Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / locale / locale.compare.inc
1 <?php
2
3 /**
4  * @file
5  * The API for comparing project translation status with available translation.
6  */
7
8 use Drupal\Core\Utility\ProjectInfo;
9
10 /**
11  * Load common APIs.
12  */
13 // @todo Combine functions differently in files to avoid unnecessary includes.
14 // Follow-up issue: https://www.drupal.org/node/1834298.
15 require_once __DIR__ . '/locale.translation.inc';
16
17 /**
18  * Clear the project data table.
19  */
20 function locale_translation_flush_projects() {
21   \Drupal::service('locale.project')->deleteAll();
22 }
23
24 /**
25  * Builds list of projects and stores the result in the database.
26  *
27  * The project data is based on the project list supplied by the Update module.
28  * Only the properties required by Locale module is included and additional
29  * (custom) modules and translation server data is added.
30  *
31  * In case the Update module is disabled this function will return an empty
32  * array.
33  *
34  * @return array
35  *   Array of project data:
36  *   - "name": Project system name.
37  *   - "project_type": Project type, e.g. 'module', 'theme'.
38  *   - "core": Core release version, e.g. 8.x
39  *   - "version": Project release version, e.g. 8.x-1.0
40  *     See http://drupalcode.org/project/drupalorg.git/blob/refs/heads/7.x-3.x:/drupalorg_project/plugins/release_packager/DrupalorgProjectPackageRelease.class.php#l219
41  *     for how the version strings are created.
42  *   - "server_pattern": Translation server po file pattern.
43  *   - "status": Project status, 1 = enabled.
44  */
45 function locale_translation_build_projects() {
46   // Get the project list based on .info.yml files.
47   $projects = locale_translation_project_list();
48
49   // Mark all previous projects as disabled and store new project data.
50   \Drupal::service('locale.project')->disableAll();
51
52   $default_server = locale_translation_default_translation_server();
53
54   foreach ($projects as $name => $data) {
55     // For dev releases, remove the '-dev' part and trust the translation server
56     // to fall back to the latest stable release for that branch.
57     if (isset($data['info']['version']) && strpos($data['info']['version'], '-dev')) {
58       if (preg_match("/^(\d+\.x-\d+\.).*$/", $data['info']['version'], $matches)) {
59         // Example matches: 8.x-1.x-dev, 8.x-1.0-alpha1+5-dev => 8.x-1.x
60         $data['info']['version'] = $matches[1] . 'x';
61       }
62       elseif (preg_match("/^(\d+\.\d+\.).*$/", $data['info']['version'], $matches)) {
63         // Example match: 8.0.0-dev => 8.0.x (Drupal core)
64         $data['info']['version'] = $matches[1] . 'x';
65       }
66     }
67
68     // For every project store information.
69     $data += [
70       'name' => $name,
71       'version' => isset($data['info']['version']) ? $data['info']['version'] : '',
72       'core' => isset($data['info']['core']) ? $data['info']['core'] : \Drupal::CORE_COMPATIBILITY,
73       // A project can provide the path and filename pattern to download the
74       // gettext file. Use the default if not.
75       'server_pattern' => isset($data['info']['interface translation server pattern']) && $data['info']['interface translation server pattern'] ? $data['info']['interface translation server pattern'] : $default_server['pattern'],
76       'status' => !empty($data['project_status']) ? 1 : 0,
77     ];
78
79     $project = (object) $data;
80     $projects[$name] = $project;
81
82     // Create or update the project record.
83     \Drupal::service('locale.project')->set($project->name, $data);
84
85     // Invalidate the cache of translatable projects.
86     locale_translation_clear_cache_projects();
87   }
88   return $projects;
89 }
90
91 /**
92  * Fetch an array of projects for translation update.
93  *
94  * @return array
95  *   Array of project data including .info.yml file data.
96  */
97 function locale_translation_project_list() {
98   $projects = &drupal_static(__FUNCTION__, []);
99   if (empty($projects)) {
100     $projects = [];
101
102     $additional_whitelist = [
103       'interface translation project',
104       'interface translation server pattern',
105     ];
106     $module_data = _locale_translation_prepare_project_list(system_rebuild_module_data(), 'module');
107     $theme_data = _locale_translation_prepare_project_list(\Drupal::service('theme_handler')->rebuildThemeData(), 'theme');
108     $project_info = new ProjectInfo();
109     $project_info->processInfoList($projects, $module_data, 'module', TRUE, $additional_whitelist);
110     $project_info->processInfoList($projects, $theme_data, 'theme', TRUE, $additional_whitelist);
111
112     // Allow other modules to alter projects before fetching and comparing.
113     \Drupal::moduleHandler()->alter('locale_translation_projects', $projects);
114   }
115   return $projects;
116 }
117
118 /**
119  * Prepare module and theme data.
120  *
121  * Modify .info.yml file data before it is processed by
122  * \Drupal\Core\Utility\ProjectInfo->processInfoList(). In order for
123  * \Drupal\Core\Utility\ProjectInfo->processInfoList() to recognize a project,
124  * it requires the 'project' parameter in the .info.yml file data.
125  *
126  * Custom modules or themes can bring their own gettext translation file. To
127  * enable import of this file the module or theme defines "interface translation
128  * project = myproject" in its .info.yml file. This function will add a project
129  * "myproject" to the info data.
130  *
131  * @param \Drupal\Core\Extension\Extension[] $data
132  *   Array of .info.yml file data.
133  * @param string $type
134  *   The project type. i.e. module, theme.
135  *
136  * @return array
137  *   Array of .info.yml file data.
138  */
139 function _locale_translation_prepare_project_list($data, $type) {
140   foreach ($data as $name => $file) {
141     // Include interface translation projects. To allow
142     // \Drupal\Core\Utility\ProjectInfo->processInfoList() to identify this as
143     // a project the 'project' property is filled with the
144     // 'interface translation project' value.
145     if (isset($file->info['interface translation project'])) {
146       $data[$name]->info['project'] = $file->info['interface translation project'];
147     }
148   }
149   return $data;
150 }
151
152 /**
153  * Retrieve data for default server.
154  *
155  * @return array
156  *   Array of server parameters:
157  *   - "pattern": URI containing po file pattern.
158  */
159 function locale_translation_default_translation_server() {
160   $pattern = \Drupal::config('locale.settings')->get('translation.default_server_pattern');
161   // An additional check is required here. During the upgrade process
162   // \Drupal::config()->get() returns NULL. We use the defined value as
163   // fallback.
164   $pattern  = $pattern ? $pattern : LOCALE_TRANSLATION_DEFAULT_SERVER_PATTERN;
165
166   return [
167     'pattern' => $pattern,
168   ];
169 }
170
171 /**
172  * Check for the latest release of project translations.
173  *
174  * @param array $projects
175  *   Array of project names to check. Defaults to all translatable projects.
176  * @param string $langcodes
177  *   Array of language codes. Defaults to all translatable languages.
178  *
179  * @return array
180  *   Available sources indexed by project and language.
181  *
182  * @todo Return batch or NULL.
183  */
184 function locale_translation_check_projects($projects = [], $langcodes = []) {
185   if (locale_translation_use_remote_source()) {
186     // Retrieve the status of both remote and local translation sources by
187     // using a batch process.
188     locale_translation_check_projects_batch($projects, $langcodes);
189   }
190   else {
191     // Retrieve and save the status of local translations only.
192     locale_translation_check_projects_local($projects, $langcodes);
193     \Drupal::state()->set('locale.translation_last_checked', REQUEST_TIME);
194   }
195 }
196
197 /**
198  * Gets and stores the status and timestamp of remote po files.
199  *
200  * A batch process is used to check for po files at remote locations and (when
201  * configured) to check for po files in the local file system. The most recent
202  * translation source states are stored in the state variable
203  * 'locale.translation_status'.
204  *
205  * @param array $projects
206  *   Array of project names to check. Defaults to all translatable projects.
207  * @param string $langcodes
208  *   Array of language codes. Defaults to all translatable languages.
209  */
210 function locale_translation_check_projects_batch($projects = [], $langcodes = []) {
211   // Build and set the batch process.
212   $batch = locale_translation_batch_status_build($projects, $langcodes);
213   batch_set($batch);
214 }
215
216 /**
217  * Builds a batch to get the status of remote and local translation files.
218  *
219  * The batch process fetches the state of both local and (if configured) remote
220  * translation files. The data of the most recent translation is stored per
221  * per project and per language. This data is stored in a state variable
222  * 'locale.translation_status'. The timestamp it was last updated is stored
223  * in the state variable 'locale.translation_last_checked'.
224  *
225  * @param array $projects
226  *   Array of project names for which to check the state of translation files.
227  *   Defaults to all translatable projects.
228  * @param array $langcodes
229  *   Array of language codes. Defaults to all translatable languages.
230  *
231  * @return array
232  *   Batch definition array.
233  */
234 function locale_translation_batch_status_build($projects = [], $langcodes = []) {
235   $projects = $projects ? $projects : array_keys(locale_translation_get_projects());
236   $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
237   $options = _locale_translation_default_update_options();
238
239   $operations = _locale_translation_batch_status_operations($projects, $langcodes, $options);
240
241   $batch = [
242     'operations' => $operations,
243     'title' => t('Checking translations'),
244     'progress_message' => '',
245     'finished' => 'locale_translation_batch_status_finished',
246     'error_message' => t('Error checking translation updates.'),
247     'file' => drupal_get_path('module', 'locale') . '/locale.batch.inc',
248   ];
249   return $batch;
250 }
251
252 /**
253  * Helper function to construct batch operations checking remote translation
254  * status.
255  *
256  * @param array $projects
257  *   Array of project names to be processed.
258  * @param array $langcodes
259  *   Array of language codes.
260  * @param array $options
261  *   Batch processing options.
262  *
263  * @return array
264  *   Array of batch operations.
265  */
266 function _locale_translation_batch_status_operations($projects, $langcodes, $options = []) {
267   $operations = [];
268
269   foreach ($projects as $project) {
270     foreach ($langcodes as $langcode) {
271       // Check status of local and remote translation sources.
272       $operations[] = ['locale_translation_batch_status_check', [$project, $langcode, $options]];
273     }
274   }
275
276   return $operations;
277 }
278
279 /**
280  * Check and store the status and timestamp of local po files.
281  *
282  * Only po files in the local file system are checked. Any remote translation
283  * files will be ignored.
284  *
285  * Projects may contain a server_pattern option containing a pattern of the
286  * path to the po source files. If no server_pattern is defined the default
287  * translation directory is checked for the po file. When a server_pattern is
288  * defined the specified location is checked. The server_pattern can be set in
289  * the module's .info.yml file or by using
290  * hook_locale_translation_projects_alter().
291  *
292  * @param array $projects
293  *   Array of project names for which to check the state of translation files.
294  *   Defaults to all translatable projects.
295  * @param array $langcodes
296  *   Array of language codes. Defaults to all translatable languages.
297  */
298 function locale_translation_check_projects_local($projects = [], $langcodes = []) {
299   $projects = locale_translation_get_projects($projects);
300   $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
301
302   // For each project and each language we check if a local po file is
303   // available. When found the source object is updated with the appropriate
304   // type and timestamp of the po file.
305   foreach ($projects as $name => $project) {
306     foreach ($langcodes as $langcode) {
307       $source = locale_translation_source_build($project, $langcode);
308       $file = locale_translation_source_check_file($source);
309       locale_translation_status_save($name, $langcode, LOCALE_TRANSLATION_LOCAL, $file);
310     }
311   }
312 }