Projet

Général

Profil

Paste
Télécharger (18,3 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / l10n_update / l10n_update.module @ 64ad485a

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
  $menu['l10n_client/save']['page callback'] = 'l10n_update_client_save_string';
91
}
92

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

    
106
/**
107
 * Implements hook_form_alter().
108
 */
109
function l10n_update_form_alter(&$form, $form_state, $form_id) {
110
  switch ($form_id) {
111
    case 'locale_translate_edit_form':
112
      // Replace the submit callback by our own customized version
113
      $form['#submit'] = array('l10n_update_locale_translate_edit_form_submit');
114
      break;
115
    case 'locale_languages_predefined_form':
116
    case 'locale_languages_custom_form':
117
      $form['#submit'][] = 'l10n_update_languages_changed_submit';
118
      break;
119
    case 'locale_languages_delete_form':
120
      // A language is being deleted.
121
      $form['#submit'][] = 'l10n_update_languages_delete_submit';
122
      break;
123
  }
124
}
125

    
126
/**
127
 * Implements hook_modules_enabled().
128
 *
129
 * Refresh project translation status and get translations if required.
130
 */
131
function l10n_update_modules_enabled($modules) {
132
  module_load_include('project.inc', 'l10n_update');
133
  l10n_update_project_refresh($modules);
134
}
135

    
136
/**
137
 * Implements hook_modules_uninstalled().
138
 *
139
 * Remove data of uninstalled modules from {l10n_update_file} table and
140
 * rebuild the projects cache.
141
 */
142
function l10n_update_modules_uninstalled($modules) {
143
  db_delete('l10n_update_file')
144
    ->condition('project', $modules)
145
    ->execute();
146

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

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

    
168
/**
169
 * Additional submit handler for language deletion form.
170
 *
171
 * When a language is deleted, the file history of this language is cleared.
172
 */
173
function l10n_update_languages_delete_submit($form, $form_state) {
174
  $langcode = $form_state['values']['langcode'];
175
  module_load_include('inc', 'l10n_update');
176
  l10n_update_delete_file_history($langcode);
177
}
178

    
179
/**
180
 * Replacement submit handler for translation edit form.
181
 *
182
 * Process string editing form submissions marking translations as customized.
183
 * Saves all translations of one string submitted from a form.
184
 *
185
 * @see l10n_update_form_alter()
186
 * @todo Just mark as customized when string changed.
187
 */
188
function l10n_update_locale_translate_edit_form_submit($form, &$form_state) {
189
  module_load_include('inc', 'l10n_update');
190
  $lid = $form_state['values']['lid'];
191
  foreach ($form_state['values']['translations'] as $key => $value) {
192
    $translation = db_query("SELECT translation FROM {locales_target} WHERE lid = :lid AND language = :language", array(':lid' => $lid, ':language' => $key))->fetchField();
193
    if (!empty($value)) {
194
      // Only update or insert if we have a value to use.
195
      if (!empty($translation)) {
196
        db_update('locales_target')
197
          ->fields(array(
198
            'translation' => $value,
199
            'l10n_status' => L10N_UPDATE_STRING_CUSTOM,
200
          ))
201
          ->condition('lid', $lid)
202
          ->condition('language', $key)
203
          ->execute();
204
      }
205
      else {
206
        db_insert('locales_target')
207
          ->fields(array(
208
            'lid' => $lid,
209
            'translation' => $value,
210
            'language' => $key,
211
            'l10n_status' => L10N_UPDATE_STRING_CUSTOM,
212
          ))
213
          ->execute();
214
      }
215
    }
216
    elseif (!empty($translation)) {
217
      // Empty translation entered: remove existing entry from database.
218
      db_delete('locales_target')
219
        ->condition('lid', $lid)
220
        ->condition('language', $key)
221
        ->execute();
222
    }
223

    
224
    // Force JavaScript translation file recreation for this language.
225
    _locale_invalidate_js($key);
226
  }
227

    
228
  drupal_set_message(t('The string has been saved.'));
229

    
230
  // Clear locale cache.
231
  _locale_invalidate_js();
232
  cache_clear_all('locale:', 'cache', TRUE);
233

    
234
  $form_state['redirect'] = 'admin/config/regional/translate/translate';
235
  return;
236
}
237

    
238
/**
239
 * Menu callback. Saves a string translation coming as POST data.
240
 */
241
function l10n_update_client_save_string() {
242
  global $user, $language;
243

    
244
  if (l10n_client_access()) {
245
    if (isset($_POST['source']) && isset($_POST['target']) && !empty($_POST['textgroup']) && !empty($_POST['form_token']) && drupal_valid_token($_POST['form_token'], 'l10n_client_form')) {
246
      // Ensure we have this source string before we attempt to save it.
247
      // @todo: add actual context support.
248
      $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();
249
      
250
      if (!empty($lid)) {
251
        module_load_include('inc', 'l10n_update');
252
        $report = array('skips' => 0, 'additions' => 0, 'updates' => 0, 'deletes' => 0);
253
        // @todo: add actual context support.
254
        _l10n_update_locale_import_one_string_db($report, $language->language, '', $_POST['source'], $_POST['target'], $_POST['textgroup'], NULL, LOCALE_IMPORT_OVERWRITE, L10N_UPDATE_STRING_CUSTOM);
255
        cache_clear_all('locale:', 'cache', TRUE);
256
        _locale_invalidate_js($language->language);
257
        if (!empty($report['skips'])) {
258
          $message = theme('l10n_client_message', array('message' => t('Not saved locally due to invalid HTML content.')));
259
        }
260
        elseif (!empty($report['additions']) || !empty($report['updates'])) {
261
          $message = theme('l10n_client_message', array('message' => t('Translation saved locally.'), 'level' => WATCHDOG_INFO));
262
        }
263
        elseif (!empty($report['deletes'])) {
264
          $message = theme('l10n_client_message', array('message' => t('Translation successfuly removed locally.'), 'level' => WATCHDOG_INFO));
265
        }
266
        else {
267
          $message = theme('l10n_client_message', array('message' => t('Unknown error while saving translation locally.')));
268
        }
269

    
270
        // Submit to remote server if enabled.
271
        if (variable_get('l10n_client_use_server', FALSE) && user_access('submit translations to localization server') && ($_POST['textgroup'] == 'default')) {
272
          if (!empty($user->data['l10n_client_key'])) {
273
            $remote_result = l10n_client_submit_translation($language->language, $_POST['source'], $_POST['target'], $user->data['l10n_client_key'], l10n_client_user_token($user));
274
            $message .= theme('l10n_client_message', array('message' => $remote_result[1], 'level' => $remote_result[0] ? WATCHDOG_INFO : WATCHDOG_ERROR));
275
          }
276
          else {
277
            $server_url = variable_get('l10n_client_server', 'http://localize.drupal.org');
278
            $user_edit_url = url('user/' . $user->uid . '/edit', array('absolute' => TRUE));
279
            $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));
280
          }
281
        }
282
      }
283
      else {
284
        $message = theme('l10n_client_message', array('message' => t('Not saved due to source string missing.')));
285
      }
286
    }
287
    else {
288
      $message = theme('l10n_client_message', array('message' => t('Not saved due to missing form values.')));
289
    }
290
  }
291
  else {
292
    $message = theme('l10n_client_message', array('message' => t('Not saved due to insufficient permissions.')));
293
  }
294
  drupal_json_output($message);
295
  exit;
296
}
297

    
298
/**
299
 * Get stored list of projects
300
 *
301
 * @param boolean $refresh
302
 *   TRUE = refresh the project data.
303
 * @param boolean $disabled
304
 *   TRUE = get enabled AND disabled projects.
305
 *   FALSE = get enabled projects only.
306
 *
307
 * @return array
308
 *   Array of project objects keyed by project name.
309
 */
310
function l10n_update_get_projects($refresh = FALSE, $disabled = FALSE) {
311
  static $projects, $enabled;
312

    
313
  if (!isset($projects) || $refresh) {
314
    if (variable_get('l10n_update_rebuild_projects', 0)) {
315
      module_load_include('project.inc', 'l10n_update');
316
      variable_del('l10n_update_rebuild_projects');
317
      l10n_update_build_projects();
318
    }
319
    $projects = $enabled = array();
320
    $result = db_query('SELECT * FROM {l10n_update_project}');
321
    foreach ($result as $project) {
322
      $projects[$project->name] = $project;
323
      if ($project->status) {
324
        $enabled[$project->name] = $project;
325
      }
326
    }
327
  }
328
  return $disabled ? $projects : $enabled;
329
}
330

    
331
/**
332
 * Get server information, that can come from different sources.
333
 *
334
 * - From server list provided by modules. They can provide full server information or just the url
335
 * - From server_url in a project, we'll fetch latest data from the server itself
336
 *
337
 * @param string $name
338
 *   Server name e.g. localize.drupal.org
339
 * @param string $url
340
 *   Server url
341
 * @param boolean $refresh
342
 *   TRUE = refresh the server data.
343
 *
344
 * @return array
345
 *   Array of server data.
346
 */
347
function l10n_update_server($name = NULL, $url = NULL, $refresh = FALSE) {
348
  static $info, $server_list;
349

    
350
  // Retrieve server list from modules
351
  if (!isset($server_list) || $refresh) {
352
    $server_list = module_invoke_all('l10n_servers');
353
  }
354
  // We need at least the server url to fetch all the information
355
  if (!$url && $name && isset($server_list[$name])) {
356
    $url = $server_list[$name]['server_url'];
357
  }
358
  // If we still don't have an url, cannot find this server, return false
359
  if (!$url) {
360
    return FALSE;
361
  }
362
  // Cache server information based on the url, refresh if asked
363
  $cid = 'l10n_update_server:' . $url;
364
  if ($refresh) {
365
    unset($info);
366
    cache_clear_all($cid, 'cache_l10n_update');
367
  }
368
  if (!isset($info[$url])) {
369
    if ($cache = cache_get($cid, 'cache_l10n_update')) {
370
      $info[$url] = $cache->data;
371
    }
372
    else {
373
      module_load_include('parser.inc', 'l10n_update');
374
      if ($name && !empty($server_list[$name])) {
375
        // The name is in our list, it can be full data or just an url
376
        $server = $server_list[$name];
377
      }
378
      else {
379
        // This may be a new server provided by a module / package
380
        $server = array('name' => $name, 'server_url' => $url);
381
        // If searching by name, store the name => url mapping
382
        if ($name) {
383
          $server_list[$name] = $server;
384
        }
385
      }
386
      // Now fetch server meta information form the server itself
387
      if ($server = l10n_update_get_server($server)) {
388
        cache_set($cid, $server, 'cache_l10n_update');
389
        $info[$url] = $server;
390
      }
391
      else {
392
        // If no server information, this will be FALSE. We won't search a server twice
393
        $info[$url] = FALSE;
394
      }
395
    }
396
  }
397
  return $info[$url];
398
}
399

    
400
/**
401
 * Implements hook_l10n_servers().
402
 *
403
 * @return array
404
 *   Array of server data:
405
 *     'name'       => server name
406
 *     'server_url' => server url
407
 *     'update_url' => update url
408
 */
409
function l10n_update_l10n_servers() {
410
  module_load_include('inc', 'l10n_update');
411
  $server = l10n_update_default_server();
412
  return array($server['name'] => $server);
413
}
414

    
415
/**
416
 * Get update history.
417
 *
418
 * @param boolean $refresh
419
 *   TRUE = refresh the history data.
420
 * @return
421
 *   An array of translation files indexed by project and language.
422
 */
423
function l10n_update_get_history($refresh = NULL) {
424
  static $status;
425

    
426
  if ($refresh || !isset($status)) {
427
    // Now add downloads history to projects
428
    $result = db_query("SELECT * FROM {l10n_update_file}");
429
    foreach ($result as $update) {
430
      $status[$update->project][$update->language] = $update;
431
    }
432
  }
433
  return $status;
434
}
435

    
436
/**
437
 * Get language list.
438
 *
439
 * @return array
440
 *   Array of installed language names. English is the source language and
441
 *   is therefore not included.
442
 */
443
function l10n_update_language_list() {
444
  $languages = locale_language_list('name');
445
  // Skip English language
446
  if (isset($languages['en'])) {
447
    unset($languages['en']);
448
  }
449
  return $languages;
450
}
451

    
452
/**
453
 * Implements hook_theme().
454
 */
455
function l10n_update_theme() {
456
  return array(
457
    'l10n_update_project_status' => array(
458
      'variables' => array('projects' => NULL, 'languages' => NULL, 'history' => NULL, 'available' => NULL, 'updates' => NULL),
459
      'file' => 'l10n_update.admin.inc',
460
    ),
461
    'l10n_update_single_project_wrapper' => array(
462
      'project' => array('project' => NULL, 'project_status' => NULL, 'languages' => NULL, 'history' => NULL, 'updates' => NULL),
463
      'file' => 'l10n_update.admin.inc',
464
    ),
465
    'l10n_update_single_project_status' => array(
466
      'variables' => array('project' => NULL, 'server' => NULL, 'status' => NULL),
467
      'file' => 'l10n_update.admin.inc',
468
    ),
469
    'l10n_update_current_release' => array(
470
      'variables' => array('language' => NULL, 'release' => NULL, 'status' => NULL),
471
      'file' => 'l10n_update.admin.inc',
472
    ),
473
    'l10n_update_available_release' => array(
474
      'variables' => array('release' => NULL),
475
      'file' => 'l10n_update.admin.inc',
476
    ),
477
    'l10n_update_version_status' => array(
478
      'variables' => array('status' => NULL, 'type' => NULL),
479
      'file' => 'l10n_update.admin.inc',
480
    ),
481
  );
482
}
483

    
484
/**
485
 * Build the warning message for when there is no data about available updates.
486
 *
487
 * @return sting
488
 *   Message text with links.
489
 */
490
function _l10n_update_no_data() {
491
  $destination = drupal_get_destination();
492
  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(
493
    '@run_cron' => url('admin/reports/status/run-cron', array('query' => $destination)),
494
    '@check_manually' => url('admin/config/regional/translate/update', array('query' => $destination)),
495
  ));
496
}
497

    
498
/**
499
 * Get available updates.
500
 *
501
 * @param boolean $refresh
502
 *   TRUE = refresh the history data.
503
 *
504
 * @return array
505
 *   Array of all projects for which updates are available. For each project
506
 *   an array of update objects, one per language.
507
 */
508
function l10n_update_available_updates($refresh = NULL) {
509
  module_load_include('check.inc', 'l10n_update');
510
  if ($available = l10n_update_available_releases($refresh)) {
511
    $history = l10n_update_get_history();
512
    return l10n_update_build_updates($history, $available);
513
  }
514
}
515

    
516
/**
517
 * Implements hook_flush_caches().
518
 *
519
 * Called from update.php (among others) to flush the caches.
520
 */
521
function l10n_update_flush_caches() {
522
  if (defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'update') {
523
    cache_clear_all('*', 'cache_l10n_update', TRUE);
524
    variable_set('l10n_update_rebuild_projects', 1);
525
  }
526
  return array();
527
}