Projet

Général

Profil

Paste
Télécharger (17,8 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / l10n_update / l10n_update.module @ 503b3f7b

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file
5
 *   Download translations from remote localization server.
6
 *
7
 * @todo Fetch information from info files.
8
 */
9
10
/**
11
 * Update mode: Remote server.
12
 */
13
define('L10N_UPDATE_CHECK_REMOTE', 1);
14
15
/**
16
 * Update mode: Local server.
17
 */
18
define('L10N_UPDATE_CHECK_LOCAL', 2);
19
20
/**
21
 * Update mode: both.
22
 */
23
define('L10N_UPDATE_CHECK_ALL', L10N_UPDATE_CHECK_REMOTE | L10N_UPDATE_CHECK_LOCAL);
24
25
/**
26
 * Translation import mode keeping translations which are edited after enabling
27
 * Locale Update module an only override default (un-edited) translations.
28
 */
29
define('LOCALE_UPDATE_OVERRIDE_DEFAULT', 2);
30
31
/**
32
 * The maximum number of projects which are checked for available translations each cron run.
33
 */
34
define('L10N_UPDATE_CRON_PROJECTS', 10);
35
36
/**
37
 * The maximum number of projects which are updated each cron run.
38
 */
39
define('L10N_UPDATE_CRON_UPDATES', 2);
40
41
42
/**
43
 * Implements hook_help().
44
 */
45
function l10n_update_help($path, $arg) {
46
  switch ($path) {
47
    case 'admin/config/regional/translate/update':
48
      $output = '<p>' . t('List of latest imported translations and available updates for each enabled project and language.') . '</p>';
49
      $output .= '<p>' . t('If there are available updates you can click on Update for them to be downloaded and imported now or you can edit the configuration for them to be updated automatically on the <a href="@update-settings">Update settings page</a>', array('@update-settings' => url('admin/config/regional/language/update'))) . '</p>';
50
      return $output;
51
      break;
52
    case 'admin/config/regional/language/update':
53
      $output = '<p>' . t('These are the settings for the translation update system. To update your translations now, check out the <a href="@update-admin">Translation update administration page</a>.', array('@update-admin' => url('admin/config/regional/translate/update'))) . '</p>';
54
      return $output;
55
      break;
56
  }
57
}
58
59
/**
60
 * Implements hook_menu().
61
 */
62
function l10n_update_menu() {
63
  $items['admin/config/regional/translate/update'] = array(
64
    'title' => 'Update',
65
    'description' => 'Available updates',
66
    'page callback' => 'l10n_update_admin_overview',
67
    'access arguments' => array('translate interface'),
68
    'file' => 'l10n_update.admin.inc',
69
    'weight' => 20,
70
    'type' => MENU_LOCAL_TASK,
71
  );
72
  $items['admin/config/regional/language/update'] = array(
73
    'title' => 'Translation updates',
74
    'description' => 'Automatic update configuration',
75
    'page callback' => 'drupal_get_form',
76
    'page arguments' => array('l10n_update_admin_settings_form'),
77
    'access arguments' => array('translate interface'),
78
    'file' => 'l10n_update.admin.inc',
79
    'weight' => 20,
80
    'type' => MENU_LOCAL_TASK,
81
  );
82
  return $items;
83
}
84
85
/**
86
 * Implements hook_menu_alter().
87
 */
88
function l10n_update_menu_alter(&$menu) {
89
  // Redirect l10n_client AJAX callback path for strings.
90 503b3f7b Assos Assos
  if (module_exists('l10n_client')) {
91
    $menu['l10n_client/save']['page callback'] = 'l10n_update_client_save_string';
92
  }
93 85ad3d82 Assos Assos
}
94
95
/**
96
 * Implements hook_cron().
97
 *
98
 * Check one project/language at a time, download and import if update available
99
 */
100
function l10n_update_cron() {
101 503b3f7b Assos Assos
  $last = variable_get('l10n_update_last_check', 0);
102
  $frequency = variable_get('l10n_update_check_frequency', 0) * 24 *3600;
103
  if ($frequency && $last < REQUEST_TIME - $frequency) {
104 85ad3d82 Assos Assos
    module_load_include('check.inc', 'l10n_update');
105 503b3f7b Assos Assos
    list($checked, $updated) = l10n_update_check_translations(L10N_UPDATE_CRON_PROJECTS, REQUEST_TIME - $frequency, L10N_UPDATE_CRON_UPDATES);
106
    if (count($checked) || count($updated)) {
107
      watchdog('l10n_update', 'Automatically checked @checked translations, updated @updated.', array('@checked' => count($checked), '@updated' => count($updated)));
108
    }
109
    variable_set('l10n_update_last_check', REQUEST_TIME);
110 85ad3d82 Assos Assos
  }
111
}
112
113
/**
114
 * Implements hook_form_alter().
115
 */
116
function l10n_update_form_alter(&$form, $form_state, $form_id) {
117
  switch ($form_id) {
118
    case 'locale_translate_edit_form':
119 503b3f7b Assos Assos
    case 'i18n_string_locale_translate_edit_form':
120
      $form['#submit'][] = 'l10n_update_locale_translate_edit_form_submit';
121 85ad3d82 Assos Assos
      break;
122
    case 'locale_languages_predefined_form':
123
    case 'locale_languages_custom_form':
124
      $form['#submit'][] = 'l10n_update_languages_changed_submit';
125
      break;
126
    case 'locale_languages_delete_form':
127
      // A language is being deleted.
128
      $form['#submit'][] = 'l10n_update_languages_delete_submit';
129
      break;
130
  }
131
}
132
133
/**
134
 * Implements hook_modules_enabled().
135
 *
136
 * Refresh project translation status and get translations if required.
137
 */
138
function l10n_update_modules_enabled($modules) {
139
  module_load_include('project.inc', 'l10n_update');
140
  l10n_update_project_refresh($modules);
141
}
142
143
/**
144
 * Implements hook_modules_uninstalled().
145
 *
146
 * Remove data of uninstalled modules from {l10n_update_file} table and
147
 * rebuild the projects cache.
148
 */
149
function l10n_update_modules_uninstalled($modules) {
150
  db_delete('l10n_update_file')
151
    ->condition('project', $modules)
152
    ->execute();
153
154
  // Rebuild {l10n_update_project} table.
155
  // Just like the system table, the project table holds both enabled and
156
  // disabled projects. Full control over its content is not possible.
157
  // To minimize polution we flush it here. The cost of rebuilding is small
158
  // compared to the {l10n_update_file} table.
159
  db_delete('l10n_update_project')->execute();
160
  module_load_include('project.inc', 'l10n_update');
161
  l10n_update_build_projects();
162
}
163
164
/**
165
 * Aditional submit handler for language forms
166
 *
167
 * We need to refresh status when a new language is enabled / disabled
168
 */
169
function l10n_update_languages_changed_submit($form, $form_state) {
170
  module_load_include('check.inc', 'l10n_update');
171
  $langcode = $form_state['values']['langcode'];
172
  l10n_update_language_refresh(array($langcode));
173
}
174
175
/**
176
 * Additional submit handler for language deletion form.
177
 *
178
 * When a language is deleted, the file history of this language is cleared.
179
 */
180
function l10n_update_languages_delete_submit($form, $form_state) {
181
  $langcode = $form_state['values']['langcode'];
182
  module_load_include('inc', 'l10n_update');
183
  l10n_update_delete_file_history($langcode);
184
}
185
186
/**
187 503b3f7b Assos Assos
 * Additional submit handler for locale and i18n_string translation edit form.
188 85ad3d82 Assos Assos
 *
189 503b3f7b Assos Assos
 * Mark locally edited translations as customized.
190 85ad3d82 Assos Assos
 *
191
 * @see l10n_update_form_alter()
192
 */
193
function l10n_update_locale_translate_edit_form_submit($form, &$form_state) {
194
  module_load_include('inc', 'l10n_update');
195
  $lid = $form_state['values']['lid'];
196 503b3f7b Assos Assos
  foreach ($form_state['values']['translations'] as $langcode => $value) {
197
    if (!empty($value) && $value != $form_state['complete form']['translations'][$langcode]['#default_value']) {
198
      // An update has been made, mark the string as customized.
199
      db_update('locales_target')
200
        ->fields(array('l10n_status' => L10N_UPDATE_STRING_CUSTOM))
201 85ad3d82 Assos Assos
        ->condition('lid', $lid)
202 503b3f7b Assos Assos
        ->condition('language', $langcode)
203 85ad3d82 Assos Assos
        ->execute();
204
    }
205
  }
206 503b3f7b Assos Assos
}
207 85ad3d82 Assos Assos
208 503b3f7b Assos Assos
/**
209
 * Implements hook_stream_wrappers().
210
 *
211
 * Add a stream wrapper. The wrapper is not used in 7.x-1.x but is required to
212
 * make upgrade to 7.x-2.x painless.
213
 */
214
function l10n_update_stream_wrappers() {
215
  $wrappers['translations'] = array(
216
    'name' => t('Translation files'),
217
    'class' => 'TranslationsStreamWrapper',
218
    'description' => t('Translation files.'),
219
    'type' => STREAM_WRAPPERS_LOCAL_HIDDEN,
220
  );
221 85ad3d82 Assos Assos
222 503b3f7b Assos Assos
  return $wrappers;
223 85ad3d82 Assos Assos
}
224
225
/**
226
 * Menu callback. Saves a string translation coming as POST data.
227
 */
228
function l10n_update_client_save_string() {
229
  global $user, $language;
230
231
  if (l10n_client_access()) {
232
    if (isset($_POST['source']) && isset($_POST['target']) && !empty($_POST['textgroup']) && !empty($_POST['form_token']) && drupal_valid_token($_POST['form_token'], 'l10n_client_form')) {
233
      // Ensure we have this source string before we attempt to save it.
234
      // @todo: add actual context support.
235
      $lid = db_query("SELECT lid FROM {locales_source} WHERE source = :source AND context = :context AND textgroup = :textgroup", array(':source' => $_POST['source'], ':context' => '', ':textgroup' => $_POST['textgroup']))->fetchField();
236 503b3f7b Assos Assos
237 85ad3d82 Assos Assos
      if (!empty($lid)) {
238
        module_load_include('inc', 'l10n_update');
239
        $report = array('skips' => 0, 'additions' => 0, 'updates' => 0, 'deletes' => 0);
240
        // @todo: add actual context support.
241
        _l10n_update_locale_import_one_string_db($report, $language->language, '', $_POST['source'], $_POST['target'], $_POST['textgroup'], NULL, LOCALE_IMPORT_OVERWRITE, L10N_UPDATE_STRING_CUSTOM);
242
        cache_clear_all('locale:', 'cache', TRUE);
243
        _locale_invalidate_js($language->language);
244
        if (!empty($report['skips'])) {
245
          $message = theme('l10n_client_message', array('message' => t('Not saved locally due to invalid HTML content.')));
246
        }
247
        elseif (!empty($report['additions']) || !empty($report['updates'])) {
248
          $message = theme('l10n_client_message', array('message' => t('Translation saved locally.'), 'level' => WATCHDOG_INFO));
249
        }
250
        elseif (!empty($report['deletes'])) {
251
          $message = theme('l10n_client_message', array('message' => t('Translation successfuly removed locally.'), 'level' => WATCHDOG_INFO));
252
        }
253
        else {
254
          $message = theme('l10n_client_message', array('message' => t('Unknown error while saving translation locally.')));
255
        }
256
257
        // Submit to remote server if enabled.
258
        if (variable_get('l10n_client_use_server', FALSE) && user_access('submit translations to localization server') && ($_POST['textgroup'] == 'default')) {
259
          if (!empty($user->data['l10n_client_key'])) {
260
            $remote_result = l10n_client_submit_translation($language->language, $_POST['source'], $_POST['target'], $user->data['l10n_client_key'], l10n_client_user_token($user));
261
            $message .= theme('l10n_client_message', array('message' => $remote_result[1], 'level' => $remote_result[0] ? WATCHDOG_INFO : WATCHDOG_ERROR));
262
          }
263
          else {
264
            $server_url = variable_get('l10n_client_server', 'http://localize.drupal.org');
265
            $user_edit_url = url('user/' . $user->uid . '/edit', array('absolute' => TRUE));
266
            $message .= theme('l10n_client_message', array('message' => t('You could share your work with !l10n_server if you set your API key at !user_link.', array('!l10n_server' => l($server_url, $server_url), '!user_link' => l($user_edit_url, 'user/' . $user->uid . '/edit'))), 'level' => WATCHDOG_WARNING));
267
          }
268
        }
269
      }
270
      else {
271
        $message = theme('l10n_client_message', array('message' => t('Not saved due to source string missing.')));
272
      }
273
    }
274
    else {
275
      $message = theme('l10n_client_message', array('message' => t('Not saved due to missing form values.')));
276
    }
277
  }
278
  else {
279
    $message = theme('l10n_client_message', array('message' => t('Not saved due to insufficient permissions.')));
280
  }
281
  drupal_json_output($message);
282
  exit;
283
}
284
285
/**
286
 * Get stored list of projects
287
 *
288
 * @param boolean $refresh
289
 *   TRUE = refresh the project data.
290
 * @param boolean $disabled
291
 *   TRUE = get enabled AND disabled projects.
292
 *   FALSE = get enabled projects only.
293
 *
294
 * @return array
295
 *   Array of project objects keyed by project name.
296
 */
297
function l10n_update_get_projects($refresh = FALSE, $disabled = FALSE) {
298
  static $projects, $enabled;
299
300
  if (!isset($projects) || $refresh) {
301
    if (variable_get('l10n_update_rebuild_projects', 0)) {
302
      module_load_include('project.inc', 'l10n_update');
303
      variable_del('l10n_update_rebuild_projects');
304
      l10n_update_build_projects();
305
    }
306
    $projects = $enabled = array();
307
    $result = db_query('SELECT * FROM {l10n_update_project}');
308
    foreach ($result as $project) {
309
      $projects[$project->name] = $project;
310
      if ($project->status) {
311
        $enabled[$project->name] = $project;
312
      }
313
    }
314
  }
315
  return $disabled ? $projects : $enabled;
316
}
317
318
/**
319
 * Get server information, that can come from different sources.
320
 *
321
 * - From server list provided by modules. They can provide full server information or just the url
322
 * - From server_url in a project, we'll fetch latest data from the server itself
323
 *
324
 * @param string $name
325
 *   Server name e.g. localize.drupal.org
326
 * @param string $url
327
 *   Server url
328
 * @param boolean $refresh
329
 *   TRUE = refresh the server data.
330
 *
331
 * @return array
332
 *   Array of server data.
333
 */
334
function l10n_update_server($name = NULL, $url = NULL, $refresh = FALSE) {
335
  static $info, $server_list;
336
337
  // Retrieve server list from modules
338
  if (!isset($server_list) || $refresh) {
339
    $server_list = module_invoke_all('l10n_servers');
340
  }
341
  // We need at least the server url to fetch all the information
342
  if (!$url && $name && isset($server_list[$name])) {
343
    $url = $server_list[$name]['server_url'];
344
  }
345
  // If we still don't have an url, cannot find this server, return false
346
  if (!$url) {
347
    return FALSE;
348
  }
349
  // Cache server information based on the url, refresh if asked
350
  $cid = 'l10n_update_server:' . $url;
351
  if ($refresh) {
352
    unset($info);
353
    cache_clear_all($cid, 'cache_l10n_update');
354
  }
355
  if (!isset($info[$url])) {
356
    if ($cache = cache_get($cid, 'cache_l10n_update')) {
357
      $info[$url] = $cache->data;
358
    }
359
    else {
360
      module_load_include('parser.inc', 'l10n_update');
361
      if ($name && !empty($server_list[$name])) {
362
        // The name is in our list, it can be full data or just an url
363
        $server = $server_list[$name];
364
      }
365
      else {
366
        // This may be a new server provided by a module / package
367
        $server = array('name' => $name, 'server_url' => $url);
368
        // If searching by name, store the name => url mapping
369
        if ($name) {
370
          $server_list[$name] = $server;
371
        }
372
      }
373
      // Now fetch server meta information form the server itself
374
      if ($server = l10n_update_get_server($server)) {
375
        cache_set($cid, $server, 'cache_l10n_update');
376
        $info[$url] = $server;
377
      }
378
      else {
379
        // If no server information, this will be FALSE. We won't search a server twice
380
        $info[$url] = FALSE;
381
      }
382
    }
383
  }
384
  return $info[$url];
385
}
386
387
/**
388
 * Implements hook_l10n_servers().
389
 *
390
 * @return array
391
 *   Array of server data:
392
 *     'name'       => server name
393
 *     'server_url' => server url
394
 *     'update_url' => update url
395
 */
396
function l10n_update_l10n_servers() {
397
  module_load_include('inc', 'l10n_update');
398
  $server = l10n_update_default_server();
399
  return array($server['name'] => $server);
400
}
401
402
/**
403
 * Get update history.
404
 *
405
 * @param boolean $refresh
406
 *   TRUE = refresh the history data.
407
 * @return
408
 *   An array of translation files indexed by project and language.
409
 */
410
function l10n_update_get_history($refresh = NULL) {
411
  static $status;
412
413
  if ($refresh || !isset($status)) {
414
    // Now add downloads history to projects
415
    $result = db_query("SELECT * FROM {l10n_update_file}");
416
    foreach ($result as $update) {
417
      $status[$update->project][$update->language] = $update;
418
    }
419
  }
420
  return $status;
421
}
422
423
/**
424
 * Get language list.
425
 *
426
 * @return array
427
 *   Array of installed language names. English is the source language and
428
 *   is therefore not included.
429
 */
430
function l10n_update_language_list() {
431
  $languages = locale_language_list('name');
432
  // Skip English language
433
  if (isset($languages['en'])) {
434
    unset($languages['en']);
435
  }
436
  return $languages;
437
}
438
439
/**
440
 * Implements hook_theme().
441
 */
442
function l10n_update_theme() {
443
  return array(
444
    'l10n_update_project_status' => array(
445
      'variables' => array('projects' => NULL, 'languages' => NULL, 'history' => NULL, 'available' => NULL, 'updates' => NULL),
446
      'file' => 'l10n_update.admin.inc',
447
    ),
448
    'l10n_update_single_project_wrapper' => array(
449
      'project' => array('project' => NULL, 'project_status' => NULL, 'languages' => NULL, 'history' => NULL, 'updates' => NULL),
450
      'file' => 'l10n_update.admin.inc',
451
    ),
452
    'l10n_update_single_project_status' => array(
453
      'variables' => array('project' => NULL, 'server' => NULL, 'status' => NULL),
454
      'file' => 'l10n_update.admin.inc',
455
    ),
456
    'l10n_update_current_release' => array(
457
      'variables' => array('language' => NULL, 'release' => NULL, 'status' => NULL),
458
      'file' => 'l10n_update.admin.inc',
459
    ),
460
    'l10n_update_available_release' => array(
461
      'variables' => array('release' => NULL),
462
      'file' => 'l10n_update.admin.inc',
463
    ),
464
    'l10n_update_version_status' => array(
465
      'variables' => array('status' => NULL, 'type' => NULL),
466
      'file' => 'l10n_update.admin.inc',
467
    ),
468
  );
469
}
470
471
/**
472
 * Build the warning message for when there is no data about available updates.
473
 *
474
 * @return sting
475
 *   Message text with links.
476
 */
477
function _l10n_update_no_data() {
478
  $destination = drupal_get_destination();
479
  return t('No information is available about potential new and updated translations for currently installed modules and themes. To check for updates, you may need to <a href="@run_cron">run cron</a> or you can <a href="@check_manually">check manually</a>. Please note that checking for available updates can take a long time, so please be patient.', array(
480
    '@run_cron' => url('admin/reports/status/run-cron', array('query' => $destination)),
481
    '@check_manually' => url('admin/config/regional/translate/update', array('query' => $destination)),
482
  ));
483
}
484
485
/**
486
 * Get available updates.
487
 *
488
 * @param boolean $refresh
489
 *   TRUE = refresh the history data.
490
 *
491
 * @return array
492
 *   Array of all projects for which updates are available. For each project
493
 *   an array of update objects, one per language.
494
 */
495
function l10n_update_available_updates($refresh = NULL) {
496
  module_load_include('check.inc', 'l10n_update');
497
  if ($available = l10n_update_available_releases($refresh)) {
498
    $history = l10n_update_get_history();
499
    return l10n_update_build_updates($history, $available);
500
  }
501
}
502
503
/**
504
 * Implements hook_flush_caches().
505
 *
506
 * Called from update.php (among others) to flush the caches.
507
 */
508
function l10n_update_flush_caches() {
509
  if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') {
510
    cache_clear_all('*', 'cache_l10n_update', TRUE);
511
    variable_set('l10n_update_rebuild_projects', 1);
512
  }
513
  return array();
514
}