Passed
Push — master ( 180d95...160cd0 )
by Thomas
03:30 queued 01:00
created

LangHelper::persistLocaleIfCookiesAreAllowed()   B

Complexity

Conditions 9
Paths 11

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 9
eloc 18
c 1
b 0
f 1
nc 11
nop 0
dl 0
loc 34
rs 8.0555
1
<?php
2
3
namespace LeKoala\Multilingual;
4
5
use Exception;
6
use SilverStripe\Admin\LeftAndMain;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\i18n\i18n;
9
use SilverStripe\Control\Cookie;
10
use SilverStripe\Control\Session;
11
use SilverStripe\Control\Director;
12
use TractorCow\Fluent\Model\Locale;
0 ignored issues
show
Bug introduced by
The type TractorCow\Fluent\Model\Locale was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use SilverStripe\Core\Config\Config;
14
use TractorCow\Fluent\State\FluentState;
0 ignored issues
show
Bug introduced by
The type TractorCow\Fluent\State\FluentState was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use SilverStripe\Core\Config\Configurable;
16
17
/**
18
 * i18n helper class
19
 */
20
class LangHelper
21
{
22
    use Configurable;
23
24
    /**
25
     * The default key for global translation
26
     */
27
    const GLOBAL_ENTITY = 'Global';
28
29
    /**
30
     * Provision fluent locales defined in yml
31
     * Use LangHelper::provisionLocales
32
     *
33
     * eg:
34
     * LeKoala\Base\i18n\BaseI18n:
35
     *   default_locales:
36
     *     - en_US
37
     *     - fr_FR
38
     * @config
39
     * @var array
40
     */
41
    private static $default_locales = [];
42
43
    /**
44
     * @config
45
     * @var boolean
46
     */
47
    private static $persist_cookie = true;
48
49
    /**
50
     * @var array
51
     */
52
    protected static $locale_cache = [];
53
54
    /**
55
     * Get a global translation
56
     *
57
     * By default all global translation are stored under the Global key
58
     *
59
     * @param string $entity If no entity is specified, Global is assumed
60
     * @return string
61
     */
62
    public static function globalTranslation($entity)
63
    {
64
        $parts = explode('.', $entity);
65
        if (count($parts) == 1) {
66
            array_unshift($parts, self::GLOBAL_ENTITY);
67
        }
68
        return i18n::_t(implode('.', $parts), $entity);
69
    }
70
71
    /**
72
     * Call this to make sure we are not setting any cookies that has
73
     * not been accepted
74
     *
75
     * @return void
76
     */
77
    public static function persistLocaleIfCookiesAreAllowed()
78
    {
79
        if (headers_sent()) {
80
            return;
81
        }
82
83
        $class = \TractorCow\Fluent\Middleware\DetectLocaleMiddleware::class;
0 ignored issues
show
Bug introduced by
The type TractorCow\Fluent\Middle...\DetectLocaleMiddleware was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
84
        if (!class_exists($class)) {
85
            return;
86
        }
87
88
        $persist = static::config()->persist_cookie;
89
        if (!$persist) {
90
            return;
91
        }
92
93
        // If we choose to persist cookies, we should check our cookie consent first
94
        $dont_persist = true;
95
        // cookie consent from osano
96
        $status = Cookie::get('cookieconsent_status');
97
        if (strlen($status) && $status == 'allow') {
0 ignored issues
show
Bug introduced by
It seems like $status can also be of type null; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

97
        if (strlen(/** @scrutinizer ignore-type */ $status) && $status == 'allow') {
Loading history...
98
            $dont_persist = false;
99
        }
100
        // cookie from cookieconsent
101
        $status = Cookie::get('cookie_consent_user_accepted');
102
        if (strlen($status) && $status == 'true') {
103
            $dont_persist = false;
104
        }
105
106
        if ($dont_persist) {
107
            return;
108
        }
109
110
        self::persistLocaleIfCookiesAreAllowed();
111
    }
112
113
    /**
114
     * Persist locale according to fluent settings
115
     *
116
     * @return void
117
     */
118
    public static function persistLocale()
119
    {
120
        $class = \TractorCow\Fluent\Middleware\DetectLocaleMiddleware::class;
121
        if (!class_exists($class)) {
122
            return;
123
        }
124
125
        $curr = Controller::curr();
126
        $request = $curr->getRequest();
127
128
        $secure = Director::is_https($request)
129
            && Session::config()->get('cookie_secure');
130
131
        $persistIds = $class::config()->get('persist_ids');
132
        $persistKey = FluentState::singleton()->getIsFrontend()
133
            ? $persistIds['frontend']
134
            : $persistIds['cms'];
135
136
        $locale = $request->getSession()->get($persistKey);
137
        // If session is not started or set, it may not be set
138
        if (!$locale) {
139
            $locale = self::get_locale();
140
        }
141
142
        Cookie::set(
143
            $persistKey,
144
            $locale,
145
            $class::config()->get('persist_cookie_expiry'),
146
            $class::config()->get('persist_cookie_path'),
147
            $class::config()->get('persist_cookie_domain'),
148
            $secure,
149
            $class::config()->get('persist_cookie_http_only')
150
        );
151
    }
152
153
    /**
154
     * Provision locales defined in default_locales
155
     *
156
     * @return void
157
     */
158
    public static function provisionLocales()
159
    {
160
        $locales = self::config()->default_locales;
161
        if (empty($locales)) {
162
            throw new Exception("No locales defined in BaseI18n:default_locales");
163
        }
164
165
        foreach ($locales as $loc) {
166
            $Locale = Locale::get()->filter('Locale', $loc)->first();
167
            $allLocales = i18n::getData()->getLocales();
168
            if (!$Locale) {
169
                $Locale = new Locale();
170
                $Locale->Title = $allLocales[$loc];
171
                $Locale->Locale = $loc;
172
                $Locale->URLSegment = self::get_lang($loc);
173
                $Locale->IsGlobalDefault = $loc == i18n::get_locale();
174
                $Locale->write();
175
            }
176
        }
177
    }
178
179
    /**
180
     * Make sure we get a proper two characters lang
181
     *
182
     * @param string|object $lang a string or a fluent locale object
183
     * @return string a two chars lang
184
     */
185
    public static function get_lang($lang = null)
186
    {
187
        if (!$lang) {
188
            $lang = self::get_locale();
189
        }
190
        if (is_object($lang)) {
191
            $lang = $lang->Locale;
192
        }
193
        return substr($lang, 0, 2);
194
    }
195
196
    /**
197
     * Get the right locale (using fluent data if exists)
198
     *
199
     * @return string
200
     */
201
    public static function get_locale()
202
    {
203
        if (class_exists(FluentState::class)) {
204
            $locale = FluentState::singleton()->getLocale();
205
            // Locale may not be set, in tests for instance
206
            if ($locale) {
207
                return $locale;
208
            }
209
        }
210
        return i18n::get_locale();
211
    }
212
213
    /**
214
     * Get a locale from the lang
215
     *
216
     * @param string $lang
217
     * @return string
218
     */
219
    public static function get_locale_from_lang($lang)
220
    {
221
        // Use fluent data
222
        if (class_exists(Locale::class)) {
223
            if (empty(self::$locale_cache)) {
224
                $fluentLocales = Locale::getLocales();
225
                foreach ($fluentLocales as $locale) {
226
                    self::$locale_cache[self::get_lang($locale->Locale)] = $locale->Locale;
227
                }
228
            }
229
            if (isset(self::$locale_cache[$lang])) {
230
                return self::$locale_cache[$lang];
231
            }
232
        }
233
        // Guess
234
        $localesData = i18n::getData();
235
        return $localesData->localeFromLang($lang);
236
    }
237
238
    /**
239
     * Do we have the subsite module installed
240
     * TODO: check if it might be better to use module manifest instead?
241
     *
242
     * @return bool
243
     */
244
    public static function usesFluent()
245
    {
246
        return class_exists(FluentState::class);
247
    }
248
249
    /**
250
     * @return array
251
     */
252
    public static function get_available_langs()
253
    {
254
        if (!self::usesFluent()) {
255
            return [
256
                self::get_lang()
257
            ];
258
        }
259
260
        $allLocales = Locale::get();
261
        $results = [];
262
        foreach ($allLocales as $locale) {
263
            $results[] = $locale->URLSegment;
264
        }
265
        return $results;
266
    }
267
268
    /**
269
     * Execute the callback in given subsite
270
     *
271
     * @param string $locale
272
     * @param callable $cb
273
     * @return mixed the callback result
274
     */
275
    public static function withLocale($locale, $cb)
276
    {
277
        if (!self::usesFluent() || !$locale) {
278
            $cb();
279
            return;
280
        }
281
        if (!is_string($locale)) {
0 ignored issues
show
introduced by
The condition is_string($locale) is always true.
Loading history...
282
            $locale = $locale->Locale;
283
        }
284
        $state = FluentState::singleton();
285
        return $state->withState(function ($state) use ($locale, $cb) {
286
            $state->setLocale($locale);
287
            return $cb();
288
        });
289
    }
290
291
    /**
292
     * Execute the callback for all locales
293
     *
294
     * @param callable $cb
295
     * @return array an array of callback results
296
     */
297
    public static function withLocales($cb)
298
    {
299
        if (!self::usesFluent()) {
300
            $cb();
301
            return [];
302
        }
303
        $allLocales = Locale::get();
304
        $results = [];
305
        foreach ($allLocales as $locale) {
306
            $results[] = self::withLocale($locale, $cb);
307
        }
308
        return $results;
309
    }
310
}
311