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
<?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
  if (module_exists('l10n_client')) {
91
    $menu['l10n_client/save']['page callback'] = 'l10n_update_client_save_string';
92
  }
93
}
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
  $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
    module_load_include('check.inc', 'l10n_update');
105
    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
  }
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
    case 'i18n_string_locale_translate_edit_form':
120
      $form['#submit'][] = 'l10n_update_locale_translate_edit_form_submit';
121
      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
 * Additional submit handler for locale and i18n_string translation edit form.
188
 *
189
 * Mark locally edited translations as customized.
190
 *
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
  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
        ->condition('lid', $lid)
202
        ->condition('language', $langcode)
203
        ->execute();
204
    }
205
  }
206
}
207

    
208
/**
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

    
222
  return $wrappers;
223
}
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

    
237
      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
}