Projet

Général

Profil

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

root / drupal7 / sites / all / modules / l10n_update / l10n_update.module @ 620f9137

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
 * Implements hook_help().
43
 */
44
function l10n_update_help($path, $arg) {
45
  switch ($path) {
46
    case 'admin/config/regional/translate/update':
47
      $output = '<p>' . t('List of latest imported translations and available updates for each enabled project and language.') . '</p>';
48
      $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>';
49
      return $output;
50
      break;
51
    case 'admin/config/regional/language/update':
52
      $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>';
53
      return $output;
54
      break;
55
  }
56
}
57

    
58
/**
59
 * Implements hook_menu().
60
 */
61
function l10n_update_menu() {
62
  $items['admin/config/regional/translate/update'] = array(
63
    'title' => 'Update',
64
    'description' => 'Available updates',
65
    'page callback' => 'l10n_update_admin_overview',
66
    'access arguments' => array('translate interface'),
67
    'file' => 'l10n_update.admin.inc',
68
    'weight' => 20,
69
    'type' => MENU_LOCAL_TASK,
70
  );
71
  $items['admin/config/regional/language/update'] = array(
72
    'title' => 'Translation updates',
73
    'description' => 'Automatic update configuration',
74
    'page callback' => 'drupal_get_form',
75
    'page arguments' => array('l10n_update_admin_settings_form'),
76
    'access arguments' => array('translate interface'),
77
    'file' => 'l10n_update.admin.inc',
78
    'weight' => 20,
79
    'type' => MENU_LOCAL_TASK,
80
  );
81
  return $items;
82
}
83

    
84
/**
85
 * Implements hook_menu_alter().
86
 */
87
function l10n_update_menu_alter(&$menu) {
88
  // Redirect l10n_client AJAX callback path for strings.
89
  if (module_exists('l10n_client')) {
90
    $menu['l10n_client/save']['page callback'] = 'l10n_update_client_save_string';
91
  }
92
}
93

    
94
/**
95
 * Implements hook_cron().
96
 *
97
 * Check one project/language at a time, download and import if update available
98
 */
99
function l10n_update_cron() {
100
  $last = variable_get('l10n_update_last_check', 0);
101
  $frequency = variable_get('l10n_update_check_frequency', 0) * 24 *3600;
102
  if ($frequency && $last < REQUEST_TIME - $frequency) {
103
    module_load_include('check.inc', 'l10n_update');
104
    list($checked, $updated) = l10n_update_check_translations(L10N_UPDATE_CRON_PROJECTS, REQUEST_TIME - $frequency, L10N_UPDATE_CRON_UPDATES);
105
    if (count($checked) || count($updated)) {
106
      watchdog('l10n_update', 'Automatically checked @checked translations, updated @updated.', array('@checked' => count($checked), '@updated' => count($updated)));
107
    }
108
    variable_set('l10n_update_last_check', REQUEST_TIME);
109
  }
110
}
111

    
112
/**
113
 * Implements hook_form_alter().
114
 */
115
function l10n_update_form_alter(&$form, $form_state, $form_id) {
116
  switch ($form_id) {
117
    case 'locale_translate_edit_form':
118
    case 'i18n_string_locale_translate_edit_form':
119
      $form['#submit'][] = 'l10n_update_locale_translate_edit_form_submit';
120
      break;
121
    case 'locale_languages_predefined_form':
122
    case 'locale_languages_custom_form':
123
      $form['#submit'][] = 'l10n_update_languages_changed_submit';
124
      break;
125
    case 'locale_languages_delete_form':
126
      // A language is being deleted.
127
      $form['#submit'][] = 'l10n_update_languages_delete_submit';
128
      break;
129
  }
130
}
131

    
132
/**
133
 * Implements hook_modules_enabled().
134
 *
135
 * Refresh project translation status and get translations if required.
136
 */
137
function l10n_update_modules_enabled($modules) {
138
  module_load_include('project.inc', 'l10n_update');
139
  l10n_update_project_refresh($modules);
140
}
141

    
142
/**
143
 * Implements hook_modules_uninstalled().
144
 *
145
 * Remove data of uninstalled modules from {l10n_update_file} table and
146
 * rebuild the projects cache.
147
 */
148
function l10n_update_modules_uninstalled($modules) {
149
  db_delete('l10n_update_file')
150
    ->condition('project', $modules)
151
    ->execute();
152

    
153
  // Rebuild {l10n_update_project} table.
154
  // Just like the system table, the project table holds both enabled and
155
  // disabled projects. Full control over its content is not possible.
156
  // To minimize polution we flush it here. The cost of rebuilding is small
157
  // compared to the {l10n_update_file} table.
158
  db_delete('l10n_update_project')->execute();
159
  module_load_include('project.inc', 'l10n_update');
160
  l10n_update_build_projects();
161
}
162

    
163
/**
164
 * Aditional submit handler for language forms
165
 *
166
 * We need to refresh status when a new language is enabled / disabled
167
 */
168
function l10n_update_languages_changed_submit($form, $form_state) {
169
  module_load_include('check.inc', 'l10n_update');
170
  $langcode = $form_state['values']['langcode'];
171
  l10n_update_language_refresh(array($langcode));
172
}
173

    
174
/**
175
 * Additional submit handler for language deletion form.
176
 *
177
 * When a language is deleted, the file history of this language is cleared.
178
 */
179
function l10n_update_languages_delete_submit($form, $form_state) {
180
  $langcode = $form_state['values']['langcode'];
181
  module_load_include('inc', 'l10n_update');
182
  l10n_update_delete_file_history($langcode);
183
}
184

    
185
/**
186
 * Additional submit handler for locale and i18n_string translation edit form.
187
 *
188
 * Mark locally edited translations as customized.
189
 *
190
 * @see l10n_update_form_alter()
191
 */
192
function l10n_update_locale_translate_edit_form_submit($form, &$form_state) {
193
  module_load_include('inc', 'l10n_update');
194
  $lid = $form_state['values']['lid'];
195
  foreach ($form_state['values']['translations'] as $langcode => $value) {
196
    if (!empty($value) && $value != $form_state['complete form']['translations'][$langcode]['#default_value']) {
197
      // An update has been made, mark the string as customized.
198
      db_update('locales_target')
199
        ->fields(array('l10n_status' => L10N_UPDATE_STRING_CUSTOM))
200
        ->condition('lid', $lid)
201
        ->condition('language', $langcode)
202
        ->execute();
203
    }
204
  }
205
}
206

    
207
/**
208
 * Menu callback. Saves a string translation coming as POST data.
209
 */
210
function l10n_update_client_save_string() {
211
  global $user, $language;
212

    
213
  if (l10n_client_access()) {
214
    if (isset($_POST['source']) && isset($_POST['target']) && !empty($_POST['textgroup']) && !empty($_POST['form_token']) && drupal_valid_token($_POST['form_token'], 'l10n_client_form')) {
215
      // Ensure we have this source string before we attempt to save it.
216
      // @todo: add actual context support.
217
      $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();
218

    
219
      if (!empty($lid)) {
220
        module_load_include('inc', 'l10n_update');
221
        $report = array('skips' => 0, 'additions' => 0, 'updates' => 0, 'deletes' => 0);
222
        // @todo: add actual context support.
223
        _l10n_update_locale_import_one_string_db($report, $language->language, '', $_POST['source'], $_POST['target'], $_POST['textgroup'], NULL, LOCALE_IMPORT_OVERWRITE, L10N_UPDATE_STRING_CUSTOM);
224
        cache_clear_all('locale:', 'cache', TRUE);
225
        _locale_invalidate_js($language->language);
226
        if (!empty($report['skips'])) {
227
          $message = theme('l10n_client_message', array('message' => t('Not saved locally due to invalid HTML content.')));
228
        }
229
        elseif (!empty($report['additions']) || !empty($report['updates'])) {
230
          $message = theme('l10n_client_message', array('message' => t('Translation saved locally.'), 'level' => WATCHDOG_INFO));
231
        }
232
        elseif (!empty($report['deletes'])) {
233
          $message = theme('l10n_client_message', array('message' => t('Translation successfuly removed locally.'), 'level' => WATCHDOG_INFO));
234
        }
235
        else {
236
          $message = theme('l10n_client_message', array('message' => t('Unknown error while saving translation locally.')));
237
        }
238

    
239
        // Submit to remote server if enabled.
240
        if (variable_get('l10n_client_use_server', FALSE) && user_access('submit translations to localization server') && ($_POST['textgroup'] == 'default')) {
241
          if (!empty($user->data['l10n_client_key'])) {
242
            $remote_result = l10n_client_submit_translation($language->language, $_POST['source'], $_POST['target'], $user->data['l10n_client_key'], l10n_client_user_token($user));
243
            $message .= theme('l10n_client_message', array('message' => $remote_result[1], 'level' => $remote_result[0] ? WATCHDOG_INFO : WATCHDOG_ERROR));
244
          }
245
          else {
246
            $server_url = variable_get('l10n_client_server', 'http://localize.drupal.org');
247
            $user_edit_url = url('user/' . $user->uid . '/edit', array('absolute' => TRUE));
248
            $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));
249
          }
250
        }
251
      }
252
      else {
253
        $message = theme('l10n_client_message', array('message' => t('Not saved due to source string missing.')));
254
      }
255
    }
256
    else {
257
      $message = theme('l10n_client_message', array('message' => t('Not saved due to missing form values.')));
258
    }
259
  }
260
  else {
261
    $message = theme('l10n_client_message', array('message' => t('Not saved due to insufficient permissions.')));
262
  }
263
  drupal_json_output($message);
264
  exit;
265
}
266

    
267
/**
268
 * Get stored list of projects
269
 *
270
 * @param boolean $refresh
271
 *   TRUE = refresh the project data.
272
 * @param boolean $disabled
273
 *   TRUE = get enabled AND disabled projects.
274
 *   FALSE = get enabled projects only.
275
 *
276
 * @return array
277
 *   Array of project objects keyed by project name.
278
 */
279
function l10n_update_get_projects($refresh = FALSE, $disabled = FALSE) {
280
  static $projects, $enabled;
281

    
282
  if (!isset($projects) || $refresh) {
283
    if (variable_get('l10n_update_rebuild_projects', 0)) {
284
      module_load_include('project.inc', 'l10n_update');
285
      variable_del('l10n_update_rebuild_projects');
286
      l10n_update_build_projects();
287
    }
288
    $projects = $enabled = array();
289
    $result = db_query('SELECT * FROM {l10n_update_project}');
290
    foreach ($result as $project) {
291
      $projects[$project->name] = $project;
292
      if ($project->status) {
293
        $enabled[$project->name] = $project;
294
      }
295
    }
296
  }
297
  return $disabled ? $projects : $enabled;
298
}
299

    
300
/**
301
 * Get server information, that can come from different sources.
302
 *
303
 * - From server list provided by modules. They can provide full server information or just the url
304
 * - From server_url in a project, we'll fetch latest data from the server itself
305
 *
306
 * @param string $name
307
 *   Server name e.g. localize.drupal.org
308
 * @param string $url
309
 *   Server url
310
 * @param boolean $refresh
311
 *   TRUE = refresh the server data.
312
 *
313
 * @return array
314
 *   Array of server data.
315
 */
316
function l10n_update_server($name = NULL, $url = NULL, $refresh = FALSE) {
317
  static $info, $server_list;
318

    
319
  // Retrieve server list from modules
320
  if (!isset($server_list) || $refresh) {
321
    $server_list = module_invoke_all('l10n_servers');
322
  }
323
  // We need at least the server url to fetch all the information
324
  if (!$url && $name && isset($server_list[$name])) {
325
    $url = $server_list[$name]['server_url'];
326
  }
327
  // If we still don't have an url, cannot find this server, return false
328
  if (!$url) {
329
    return FALSE;
330
  }
331
  // Cache server information based on the url, refresh if asked
332
  $cid = 'l10n_update_server:' . $url;
333
  if ($refresh) {
334
    unset($info);
335
    cache_clear_all($cid, 'cache_l10n_update');
336
  }
337
  if (!isset($info[$url])) {
338
    if ($cache = cache_get($cid, 'cache_l10n_update')) {
339
      $info[$url] = $cache->data;
340
    }
341
    else {
342
      module_load_include('parser.inc', 'l10n_update');
343
      if ($name && !empty($server_list[$name])) {
344
        // The name is in our list, it can be full data or just an url
345
        $server = $server_list[$name];
346
      }
347
      else {
348
        // This may be a new server provided by a module / package
349
        $server = array('name' => $name, 'server_url' => $url);
350
        // If searching by name, store the name => url mapping
351
        if ($name) {
352
          $server_list[$name] = $server;
353
        }
354
      }
355
      // Now fetch server meta information form the server itself
356
      if ($server = l10n_update_get_server($server)) {
357
        cache_set($cid, $server, 'cache_l10n_update');
358
        $info[$url] = $server;
359
      }
360
      else {
361
        // If no server information, this will be FALSE. We won't search a server twice
362
        $info[$url] = FALSE;
363
      }
364
    }
365
  }
366
  return $info[$url];
367
}
368

    
369
/**
370
 * Implements hook_l10n_servers().
371
 *
372
 * @return array
373
 *   Array of server data:
374
 *     'name'       => server name
375
 *     'server_url' => server url
376
 *     'update_url' => update url
377
 */
378
function l10n_update_l10n_servers() {
379
  module_load_include('inc', 'l10n_update');
380
  $server = l10n_update_default_server();
381
  return array($server['name'] => $server);
382
}
383

    
384
/**
385
 * Get update history.
386
 *
387
 * @param boolean $refresh
388
 *   TRUE = refresh the history data.
389
 * @return
390
 *   An array of translation files indexed by project and language.
391
 */
392
function l10n_update_get_history($refresh = NULL) {
393
  static $status;
394

    
395
  if ($refresh || !isset($status)) {
396
    // Now add downloads history to projects
397
    $result = db_query("SELECT * FROM {l10n_update_file}");
398
    foreach ($result as $update) {
399
      $status[$update->project][$update->language] = $update;
400
    }
401
  }
402
  return $status;
403
}
404

    
405
/**
406
 * Get language list.
407
 *
408
 * @return array
409
 *   Array of installed language names. English is the source language and
410
 *   is therefore not included.
411
 */
412
function l10n_update_language_list() {
413
  $languages = locale_language_list('name');
414
  // Skip English language
415
  if (isset($languages['en'])) {
416
    unset($languages['en']);
417
  }
418
  return $languages;
419
}
420

    
421
/**
422
 * Implements hook_theme().
423
 */
424
function l10n_update_theme() {
425
  return array(
426
    'l10n_update_project_status' => array(
427
      'variables' => array('projects' => NULL, 'languages' => NULL, 'history' => NULL, 'available' => NULL, 'updates' => NULL),
428
      'file' => 'l10n_update.admin.inc',
429
    ),
430
    'l10n_update_single_project_wrapper' => array(
431
      'project' => array('project' => NULL, 'project_status' => NULL, 'languages' => NULL, 'history' => NULL, 'updates' => NULL),
432
      'file' => 'l10n_update.admin.inc',
433
    ),
434
    'l10n_update_single_project_status' => array(
435
      'variables' => array('project' => NULL, 'server' => NULL, 'status' => NULL),
436
      'file' => 'l10n_update.admin.inc',
437
    ),
438
    'l10n_update_current_release' => array(
439
      'variables' => array('language' => NULL, 'release' => NULL, 'status' => NULL),
440
      'file' => 'l10n_update.admin.inc',
441
    ),
442
    'l10n_update_available_release' => array(
443
      'variables' => array('release' => NULL),
444
      'file' => 'l10n_update.admin.inc',
445
    ),
446
    'l10n_update_version_status' => array(
447
      'variables' => array('status' => NULL, 'type' => NULL),
448
      'file' => 'l10n_update.admin.inc',
449
    ),
450
  );
451
}
452

    
453
/**
454
 * Build the warning message for when there is no data about available updates.
455
 *
456
 * @return sting
457
 *   Message text with links.
458
 */
459
function _l10n_update_no_data() {
460
  $destination = drupal_get_destination();
461
  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(
462
    '@run_cron' => url('admin/reports/status/run-cron', array('query' => $destination)),
463
    '@check_manually' => url('admin/config/regional/translate/update', array('query' => $destination)),
464
  ));
465
}
466

    
467
/**
468
 * Get available updates.
469
 *
470
 * @param boolean $refresh
471
 *   TRUE = refresh the history data.
472
 *
473
 * @return array
474
 *   Array of all projects for which updates are available. For each project
475
 *   an array of update objects, one per language.
476
 */
477
function l10n_update_available_updates($refresh = NULL) {
478
  module_load_include('check.inc', 'l10n_update');
479
  if ($available = l10n_update_available_releases($refresh)) {
480
    $history = l10n_update_get_history();
481
    return l10n_update_build_updates($history, $available);
482
  }
483
}
484

    
485
/**
486
 * Implements hook_flush_caches().
487
 *
488
 * Called from update.php (among others) to flush the caches.
489
 */
490
function l10n_update_flush_caches() {
491
  if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') {
492
    cache_clear_all('*', 'cache_l10n_update', TRUE);
493
    variable_set('l10n_update_rebuild_projects', 1);
494
  }
495
  return array();
496
}
497

    
498
/**
499
 * Creates a .htaccess file in the translations directory if it is missing.
500
 */
501
function l10n_update_ensure_htaccess() {
502
  $directory = variable_get('l10n_update_download_store', '');
503
  if ($directory) {
504
    file_create_htaccess($directory, FALSE);
505
  }
506
}