Passed
Push — master ( 34a769...daf507 )
by Thomas
11:32 queued 09:02
created

LangHelper::hasFluent()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace LeKoala\Multilingual;
4
5
use Exception;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\i18n\i18n;
8
use SilverStripe\Control\Cookie;
9
use SilverStripe\Control\Session;
10
use SilverStripe\Control\Director;
11
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...
12
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...
13
use SilverStripe\Core\Config\Configurable;
14
15
/**
16
 * i18n helper class
17
 */
18
class LangHelper
19
{
20
    use Configurable;
21
22
    /**
23
     * The default key for global translation
24
     */
25
    const GLOBAL_ENTITY = 'Global';
26
27
    /**
28
     * Provision fluent locales defined in yml
29
     * Use LangHelper::provisionLocales
30
     *
31
     * eg:
32
     * LeKoala\Base\i18n\BaseI18n:
33
     *   default_locales:
34
     *     - en_US
35
     *     - fr_FR
36
     *
37
     * @var array<string>
38
     */
39
    private static $default_locales = [];
40
41
    /**
42
     * @config
43
     * @var boolean
44
     */
45
    private static $persist_cookie = true;
46
47
    /**
48
     * @var array<string,string>
49
     */
50
    protected static $locale_cache = [];
51
52
    /**
53
     * Get a global translation
54
     *
55
     * By default all global translation are stored under the Global key
56
     *
57
     * @param string $entity If no entity is specified, Global is assumed
58
     * @return string
59
     */
60
    public static function globalTranslation(string $entity): string
61
    {
62
        $parts = explode('.', $entity);
63
        if (count($parts) == 1) {
64
            array_unshift($parts, self::GLOBAL_ENTITY);
65
        }
66
        return i18n::_t(implode('.', $parts), $entity);
67
    }
68
69
    public static function hasFluent(): bool
70
    {
71
        return class_exists('\\TractorCow\\Fluent\\Middleware\\DetectLocaleMiddleware');
72
    }
73
74
    /**
75
     * Call this to make sure we are not setting any cookies that has
76
     * not been accepted
77
     */
78
    public static function persistLocaleIfCookiesAreAllowed(): void
79
    {
80
        if (headers_sent()) {
81
            return;
82
        }
83
84
        if (!self::hasFluent()) {
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 = $persist;
95
        // cookie consent from osano
96
        $status = Cookie::get('cookieconsent_status') ?? '';
97
        if (strlen($status) && $status == 'allow') {
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::persistLocale();
111
    }
112
113
    /**
114
     * Persist locale according to fluent settings
115
     */
116
    public static function persistLocale(): void
117
    {
118
        if (!self::hasFluent()) {
119
            return;
120
        }
121
122
        $curr = Controller::curr();
123
        $request = $curr->getRequest();
124
125
        $secure = Director::is_https($request)
126
            && Session::config()->get('cookie_secure');
127
128
        $persistIds = $class::config()->get('persist_ids');
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $class seems to be never defined.
Loading history...
129
        $persistKey = FluentState::singleton()->getIsFrontend()
130
            ? $persistIds['frontend']
131
            : $persistIds['cms'];
132
133
        $locale = $request->getSession()->get($persistKey);
134
        // If session is not started or set, it may not be set
135
        if (!$locale) {
136
            $locale = self::get_locale();
137
        }
138
139
        Cookie::set(
140
            $persistKey,
141
            $locale,
142
            $class::config()->get('persist_cookie_expiry'),
143
            $class::config()->get('persist_cookie_path'),
144
            $class::config()->get('persist_cookie_domain'),
145
            $secure,
146
            $class::config()->get('persist_cookie_http_only')
147
        );
148
    }
149
150
    /**
151
     * Provision locales defined in default_locales
152
     *
153
     * @return void
154
     */
155
    public static function provisionLocales()
156
    {
157
        $locales = self::config()->default_locales;
158
        if (empty($locales)) {
159
            throw new Exception("No locales defined in BaseI18n:default_locales");
160
        }
161
162
        foreach ($locales as $loc) {
163
            $Locale = Locale::get()->filter('Locale', $loc)->first();
164
            $allLocales = i18n::getData()->getLocales();
165
            if (!$Locale) {
166
                $Locale = new Locale();
167
                $Locale->Title = $allLocales[$loc];
168
                $Locale->Locale = $loc;
169
                $Locale->URLSegment = self::get_lang($loc);
170
                $Locale->IsGlobalDefault = $loc == i18n::get_locale();
171
                $Locale->write();
172
            }
173
        }
174
    }
175
176
    /**
177
     * Make sure we get a proper two characters lang
178
     *
179
     * @param string|object $lang a string or a fluent locale object
180
     * @return string a two chars lang
181
     */
182
    public static function get_lang($lang = null)
183
    {
184
        if (!$lang) {
185
            $lang = self::get_locale();
186
        }
187
        if (is_object($lang)) {
188
            $lang = $lang->Locale;
189
        }
190
        return substr($lang, 0, 2);
191
    }
192
193
    /**
194
     * Get the right locale (using fluent data if exists)
195
     *
196
     * @return string
197
     */
198
    public static function get_locale()
199
    {
200
        if (class_exists(FluentState::class)) {
201
            $locale = FluentState::singleton()->getLocale();
202
            // Locale may not be set, in tests for instance
203
            if ($locale) {
204
                return $locale;
205
            }
206
        }
207
        return i18n::get_locale();
208
    }
209
210
    /**
211
     * Get a locale from the lang
212
     *
213
     * @param string $lang
214
     * @return string
215
     */
216
    public static function get_locale_from_lang($lang)
217
    {
218
        // Use fluent data
219
        if (class_exists(Locale::class)) {
220
            if (empty(self::$locale_cache)) {
221
                $fluentLocales = Locale::getLocales();
222
                foreach ($fluentLocales as $locale) {
223
                    self::$locale_cache[self::get_lang($locale->Locale)] = $locale->Locale;
224
                }
225
            }
226
            if (isset(self::$locale_cache[$lang])) {
227
                return self::$locale_cache[$lang];
228
            }
229
        }
230
        // Guess
231
        $localesData = i18n::getData();
232
        return $localesData->localeFromLang($lang);
233
    }
234
235
    /**
236
     * Do we have the subsite module installed
237
     * TODO: check if it might be better to use module manifest instead?
238
     *
239
     * @return bool
240
     */
241
    public static function usesFluent()
242
    {
243
        return class_exists(FluentState::class);
244
    }
245
246
    /**
247
     * @return array<string>
248
     */
249
    public static function get_available_langs()
250
    {
251
        if (!self::usesFluent()) {
252
            return [
253
                self::get_lang()
254
            ];
255
        }
256
257
        $allLocales = Locale::get();
258
        $results = [];
259
        foreach ($allLocales as $locale) {
260
            $results[] = $locale->URLSegment;
261
        }
262
        return $results;
263
    }
264
265
    /**
266
     * Execute the callback in given subsite
267
     *
268
     * @param string $locale
269
     * @param callable $cb
270
     * @return mixed the callback result
271
     */
272
    public static function withLocale($locale, $cb)
273
    {
274
        if (!self::usesFluent() || !$locale) {
275
            $cb();
276
            return;
277
        }
278
        if (!is_string($locale)) {
0 ignored issues
show
introduced by
The condition is_string($locale) is always true.
Loading history...
279
            $locale = $locale->Locale;
280
        }
281
        $state = FluentState::singleton();
282
        return $state->withState(function ($state) use ($locale, $cb) {
283
            $state->setLocale($locale);
284
            return $cb();
285
        });
286
    }
287
288
    /**
289
     * Execute the callback for all locales
290
     *
291
     * @param callable $cb
292
     * @return array an array of callback results
293
     */
294
    public static function withLocales($cb)
295
    {
296
        if (!self::usesFluent()) {
297
            $cb();
298
            return [];
299
        }
300
        $allLocales = Locale::get();
301
        $results = [];
302
        foreach ($allLocales as $locale) {
303
            $results[] = self::withLocale($locale, $cb);
304
        }
305
        return $results;
306
    }
307
}
308