1<?php
2
3/**
4 * @file
5 * Install, update, and uninstall functions for the Locale module.
6 */
7
8use Drupal\Core\File\Exception\FileException;
9use Drupal\Core\File\FileSystemInterface;
10use Drupal\Core\Link;
11use Drupal\Core\Url;
12
13/**
14 * Implements hook_install().
15 */
16function locale_install() {
17  // Create the interface translations directory and ensure it's writable.
18  if (!$directory = \Drupal::config('locale.settings')->get('translation.path')) {
19    $site_path = \Drupal::service('site.path');
20    $directory = $site_path . '/files/translations';
21    \Drupal::configFactory()->getEditable('locale.settings')->set('translation.path', $directory)->save();
22  }
23  \Drupal::service('file_system')->prepareDirectory($directory, FileSystemInterface::CREATE_DIRECTORY | FileSystemInterface::MODIFY_PERMISSIONS);
24}
25
26/**
27 * Implements hook_uninstall().
28 */
29function locale_uninstall() {
30  $config = \Drupal::config('locale.settings');
31  // Delete all JavaScript translation files.
32  $locale_js_directory = 'public://' . $config->get('javascript.directory');
33
34  if (is_dir($locale_js_directory)) {
35    $locale_javascripts = \Drupal::state()->get('locale.translation.javascript') ?: [];
36    /** @var \Drupal\Core\File\FileSystemInterface $file_system */
37    $file_system = \Drupal::service('file_system');
38    foreach ($locale_javascripts as $langcode => $file_suffix) {
39      if (!empty($file_suffix)) {
40        try {
41          $file_system->delete($locale_js_directory . '/' . $langcode . '_' . $file_suffix . '.js');
42        }
43        catch (FileException $e) {
44          // Ignore and continue.
45        }
46      }
47    }
48    // Delete the JavaScript translations directory if empty.
49    if (is_dir($locale_js_directory)) {
50      if (!$file_system->scanDirectory($locale_js_directory, '/.*/')) {
51        $file_system->rmdir($locale_js_directory);
52      }
53    }
54  }
55
56  // Clear variables.
57  \Drupal::state()->delete('system.javascript_parsed');
58  \Drupal::state()->delete('locale.translation.plurals');
59  \Drupal::state()->delete('locale.translation.javascript');
60}
61
62/**
63 * Implements hook_schema().
64 */
65function locale_schema() {
66  $schema['locales_source'] = [
67    'description' => 'List of English source strings.',
68    'fields' => [
69      'lid' => [
70        'type' => 'serial',
71        'not null' => TRUE,
72        'description' => 'Unique identifier of this string.',
73      ],
74      'source' => [
75        'type' => 'text',
76        'mysql_type' => 'blob',
77        'not null' => TRUE,
78        'description' => 'The original string in English.',
79      ],
80      'context' => [
81        'type' => 'varchar_ascii',
82        'length' => 255,
83        'not null' => TRUE,
84        'default' => '',
85        'description' => 'The context this string applies to.',
86      ],
87      'version' => [
88        'type' => 'varchar_ascii',
89        'length' => 20,
90        'not null' => TRUE,
91        'default' => 'none',
92        'description' => 'Version of Drupal where the string was last used (for locales optimization).',
93      ],
94    ],
95    'primary key' => ['lid'],
96    'indexes' => [
97      'source_context' => [['source', 30], 'context'],
98    ],
99  ];
100
101  $schema['locales_target'] = [
102    'description' => 'Stores translated versions of strings.',
103    'fields' => [
104      'lid' => [
105        'type' => 'int',
106        'not null' => TRUE,
107        'default' => 0,
108        'description' => 'Source string ID. References {locales_source}.lid.',
109      ],
110      'translation' => [
111        'type' => 'text',
112        'mysql_type' => 'blob',
113        'not null' => TRUE,
114        'description' => 'Translation string value in this language.',
115      ],
116      'language' => [
117        'type' => 'varchar_ascii',
118        'length' => 12,
119        'not null' => TRUE,
120        'default' => '',
121        'description' => 'Language code. References {language}.langcode.',
122      ],
123      'customized' => [
124        'type' => 'int',
125        'not null' => TRUE,
126        // LOCALE_NOT_CUSTOMIZED
127        'default' => 0,
128        'description' => 'Boolean indicating whether the translation is custom to this site.',
129      ],
130    ],
131    'primary key' => ['language', 'lid'],
132    'foreign keys' => [
133      'locales_source' => [
134        'table' => 'locales_source',
135        'columns' => ['lid' => 'lid'],
136      ],
137    ],
138    'indexes' => [
139      'lid' => ['lid'],
140    ],
141  ];
142
143  $schema['locales_location'] = [
144    'description' => 'Location information for source strings.',
145    'fields' => [
146      'lid' => [
147        'type' => 'serial',
148        'not null' => TRUE,
149        'description' => 'Unique identifier of this location.',
150      ],
151      'sid' => [
152        'type' => 'int',
153        'not null' => TRUE,
154        'description' => 'Unique identifier of this string.',
155      ],
156      'type' => [
157        'type' => 'varchar_ascii',
158        'length' => 50,
159        'not null' => TRUE,
160        'default' => '',
161        'description' => 'The location type (file, config, path, etc).',
162      ],
163      'name' => [
164        'type' => 'varchar',
165        'length' => 255,
166        'not null' => TRUE,
167        'default' => '',
168        'description' => 'Type dependent location information (file name, path, etc).',
169      ],
170      'version' => [
171        'type' => 'varchar_ascii',
172        'length' => 20,
173        'not null' => TRUE,
174        'default' => 'none',
175        'description' => 'Version of Drupal where the location was found.',
176      ],
177    ],
178    'primary key' => ['lid'],
179    'foreign keys' => [
180      'locales_source' => [
181        'table' => 'locales_source',
182        'columns' => ['sid' => 'lid'],
183      ],
184    ],
185    'indexes' => [
186      'string_id' => ['sid'],
187      'string_type' => ['sid', 'type'],
188    ],
189  ];
190
191  $schema['locale_file'] = [
192    'description' => 'File import status information for interface translation files.',
193    'fields' => [
194      'project' => [
195        'type' => 'varchar_ascii',
196        'length' => '255',
197        'not null' => TRUE,
198        'default' => '',
199        'description' => 'A unique short name to identify the project the file belongs to.',
200      ],
201      'langcode' => [
202        'type' => 'varchar_ascii',
203        'length' => '12',
204        'not null' => TRUE,
205        'default' => '',
206        'description' => 'Language code of this translation. References {language}.langcode.',
207      ],
208      'filename' => [
209        'type' => 'varchar',
210        'length' => 255,
211        'not null' => TRUE,
212        'default' => '',
213        'description' => 'Filename of the imported file.',
214      ],
215      'version' => [
216        'type' => 'varchar',
217        'length' => '128',
218        'not null' => TRUE,
219        'default' => '',
220        'description' => 'Version tag of the imported file.',
221      ],
222      'uri' => [
223        'type' => 'varchar',
224        'length' => 255,
225        'not null' => TRUE,
226        'default' => '',
227        'description' => 'URI of the remote file, the resulting local file or the locally imported file.',
228      ],
229      'timestamp' => [
230        'type' => 'int',
231        'not null' => FALSE,
232        'default' => 0,
233        'description' => 'Unix timestamp of the imported file.',
234      ],
235      'last_checked' => [
236        'type' => 'int',
237        'not null' => FALSE,
238        'default' => 0,
239        'description' => 'Unix timestamp of the last time this translation was confirmed to be the most recent release available.',
240      ],
241    ],
242    'primary key' => ['project', 'langcode'],
243  ];
244  return $schema;
245}
246
247/**
248 * Implements hook_requirements().
249 */
250function locale_requirements($phase) {
251  $requirements = [];
252  if ($phase == 'runtime') {
253    $available_updates = [];
254    $untranslated = [];
255    $languages = locale_translatable_language_list();
256
257    if ($languages) {
258      // Determine the status of the translation updates per language.
259      $status = locale_translation_get_status();
260      if ($status) {
261        foreach ($status as $project) {
262          foreach ($project as $langcode => $project_info) {
263            if (empty($project_info->type)) {
264              $untranslated[$langcode] = $languages[$langcode]->getName();
265            }
266            elseif ($project_info->type == LOCALE_TRANSLATION_LOCAL || $project_info->type == LOCALE_TRANSLATION_REMOTE) {
267              $available_updates[$langcode] = $languages[$langcode]->getName();
268            }
269          }
270        }
271
272        if ($available_updates || $untranslated) {
273          if ($available_updates) {
274            $requirements['locale_translation'] = [
275              'title' => t('Translation update status'),
276              'value' => Link::fromTextAndUrl(t('Updates available'), Url::fromRoute('locale.translate_status'))->toString(),
277              'severity' => REQUIREMENT_WARNING,
278              'description' => t('Updates available for: @languages. See the <a href=":updates">Available translation updates</a> page for more information.', ['@languages' => implode(', ', $available_updates), ':updates' => Url::fromRoute('locale.translate_status')->toString()]),
279            ];
280          }
281          else {
282            $requirements['locale_translation'] = [
283              'title' => t('Translation update status'),
284              'value' => t('Missing translations'),
285              'severity' => REQUIREMENT_INFO,
286              'description' => t('Missing translations for: @languages. See the <a href=":updates">Available translation updates</a> page for more information.', ['@languages' => implode(', ', $untranslated), ':updates' => Url::fromRoute('locale.translate_status')->toString()]),
287            ];
288          }
289        }
290        else {
291          $requirements['locale_translation'] = [
292            'title' => t('Translation update status'),
293            'value' => t('Up to date'),
294            'severity' => REQUIREMENT_OK,
295          ];
296        }
297      }
298      else {
299        $requirements['locale_translation'] = [
300          'title' => t('Translation update status'),
301          'value' => Link::fromTextAndUrl(t('Can not determine status'), Url::fromRoute('locale.translate_status'))->toString(),
302          'severity' => REQUIREMENT_WARNING,
303          'description' => t('No translation status is available. See the <a href=":updates">Available translation updates</a> page for more information.', [':updates' => Url::fromRoute('locale.translate_status')->toString()]),
304        ];
305      }
306    }
307  }
308  return $requirements;
309}
310
311/**
312 * Delete translation status data in state.
313 */
314function locale_update_8300() {
315  // Delete the old translation status data, it will be rebuilt and stored in
316  // the new key value collection.
317  \Drupal::state()->delete('locale.translation_status');
318}
319
320/**
321 * Update default server pattern value to use https.
322 */
323function locale_update_8500() {
324  $update_url = \Drupal::config('locale.settings')->get('translation.default_server_pattern');
325  if ($update_url == 'http://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po') {
326    \Drupal::configFactory()->getEditable('locale.settings')
327      ->set('translation.default_server_pattern', 'https://ftp.drupal.org/files/translations/%core/%project/%project-%version.%language.po')
328      ->save();
329  }
330}
331
332/**
333 * Clear Locale project storage to use new 'all' instead of 8.x in URLs.
334 */
335function locale_update_8800() {
336  \Drupal::service('locale.project')->deleteAll();
337}
338