l10n_update.check.inc in Localization update 6
Same filename and directory in other branches
Reusable API for l10n remote updates using $source objects
These functions may not be safe for the installer as they use variables and report using watchdog
File
l10n_update.check.incView source
<?php
/**
* @file
* Reusable API for l10n remote updates using $source objects
*
* These functions may not be safe for the installer as they use variables and report using watchdog
*/
/**
* Threshold for timestamp comparison.
*
* Eliminates a difference between the download time
* (Database: l10n_update_file.timestamp) and the actual .po file timestamp.
*/
define('L10N_UPDATE_TIMESTAMP_THRESHOLD', 2);
require_once 'l10n_update.inc';
/**
* Fetch update information for all projects / all languages.
*
* @param boolean $refresh
* TRUE = refresh the release data.
* We refresh anyway if the data is older than a day.
*
* @return array
* Available releases indexed by project and language.
*/
function l10n_update_available_releases($refresh = FALSE) {
$frequency = variable_get('l10n_update_check_frequency', 0) * 24 * 3600;
if (!$refresh && ($cache = cache_get('l10n_update_available_releases', 'cache_l10n_update')) && (!$frequency || $cache->created > time() - $frequency)) {
return $cache->data;
}
else {
$projects = l10n_update_get_projects(TRUE);
$languages = l10n_update_language_list();
$local = variable_get('l10n_update_check_mode', L10N_UPDATE_CHECK_ALL) & L10N_UPDATE_CHECK_LOCAL;
$remote = variable_get('l10n_update_check_mode', L10N_UPDATE_CHECK_ALL) & L10N_UPDATE_CHECK_REMOTE;
$available = l10n_update_check_projects($projects, array_keys($languages), $local, $remote);
cache_set('l10n_update_available_releases', $available, 'cache_l10n_update', $frequency ? time() + $frequency : CACHE_PERMANENT);
return $available;
}
}
/**
* Check latest release for project, language.
*
* @param $projects
* Projects to check (objects).
* @param $languages
* Array of language codes to check, none to check all.
* @param $check_local
* Check local translation file.
* @param $check_remote
* Check remote translation file.
*
* @return array
* Available sources indexed by project, language.
*/
function l10n_update_check_projects($projects, $languages = NULL, $check_local = TRUE, $check_remote = TRUE) {
$languages = $languages ? $languages : array_keys(l10n_update_language_list());
$result = array();
foreach ($projects as $name => $project) {
foreach ($languages as $lang) {
$source = l10n_update_source_build($project, $lang);
if ($update = l10n_update_source_check($source, $check_local, $check_remote)) {
$result[$name][$lang] = $update;
}
}
}
return $result;
}
/**
* Compare available releases with history and get list of downloadable updates.
*
* @param $history
* Update history of projects.
* @param $available
* Available project releases.
* @return array
* Projects to be updated: 'not yet downloaded', 'newer timestamp available',
* 'new version available'.
* Up to date projects are not included in the array.
*/
function l10n_update_build_updates($history, $available) {
$updates = array();
foreach ($available as $name => $project_updates) {
foreach ($project_updates as $lang => $update) {
if (!empty($update->timestamp)) {
$current = !empty($history[$name][$lang]) ? $history[$name][$lang] : NULL;
// Add when not current, timestamp newer or version difers (newer version)
if (_l10n_update_source_compare($current, $update) == -1 || $current->version != $update->version) {
$updates[$name][$lang] = $update;
}
}
}
}
return $updates;
}
/**
* Check updates for active projects and languages.
*
* @param $count
* Number of package translations to check.
* @param $before
* Unix timestamp, check only updates that haven't been checked for this time.
* @param $limit
* Maximum number of updates to do. We check $count translations
* but we stop after we do $limit updates.
* @return array
*/
function l10n_update_check_translations($count, $before, $limit = 1) {
$projects = l10n_update_get_projects();
$updated = $checked = array();
// Select active projects x languages ordered by last checked time
$sql = "SELECT p.name, l.language AS lang, f.* FROM {l10n_update_project} p";
$sql .= " LEFT JOIN {l10n_update_file} f ON p.name = f.project";
$sql .= " INNER JOIN {languages} l ON l.language = f.language";
$sql .= " WHERE p.status = 1 AND l.enabled = 1 AND (f.status IS NULL OR f.status = 1 AND f.last_checked < %d) ORDER BY last_checked";
$result = db_query_range($sql, $before, 0, $count);
if ($result) {
$local = variable_get('l10n_update_check_mode', L10N_UPDATE_CHECK_ALL) & L10N_UPDATE_CHECK_LOCAL;
$remote = variable_get('l10n_update_check_mode', L10N_UPDATE_CHECK_ALL) & L10N_UPDATE_CHECK_REMOTE;
while (($check = db_fetch_object($result)) && count($updated) < $limit) {
$checked[] = $check;
if (!empty($projects[$check->name])) {
$project = $projects[$check->name];
$update = NULL;
$source = l10n_update_source_build($project, $check->lang);
$current = $check->filename ? $check : NULL;
if ($available = l10n_update_source_check($source, $local, $remote)) {
if (!$current || _l10n_update_source_compare($current, $available) == -1 || $current->version != $available->version) {
$update = $available;
}
}
if ($update) {
// The update functions will update data and timestamps too
l10n_update_source_update($update, variable_get('l10n_update_import_mode', LOCALE_IMPORT_KEEP));
$updated[] = $update;
}
elseif ($current) {
// No update available, just update timestamp for this row
db_query("UPDATE {l10n_update_file} SET last_checked = %d WHERE project = '%s' AND language = '%s'", time(), $current->project, $current->language);
}
elseif ($source) {
// Create a new record just for keeping last checked time
$source->last_checked = time();
drupal_write_record('l10n_update_file', $source);
}
}
}
}
return array(
$checked,
$updated,
);
}
/**
* Build abstract translation source, to be mapped to a file or a download.
*
* @param $project
* Project object.
* @param $langcode
* Language code.
* @param $filename
* File name of translation file. May contains placeholders.
* @return object
* Source object, which may have these properties:
* - 'project': Project name.
* - 'language': Language code.
* - 'type': Source type 'download' or 'localfile'.
* - 'filepath': Local file path.
* - 'fileurl': Remote file URL for downloads.
* - 'filename': File name.
* - 'keep': TRUE to keep the downloaded file.
* - 'timestamp': Last update time of the file.
*/
function l10n_update_source_build($project, $langcode, $filename = L10N_UPDATE_DEFAULT_FILENAME) {
$source = clone $project;
$source->project = $project->name;
$source->language = $langcode;
$source->filename = l10n_update_build_string($source, $filename);
return $source;
}
/**
* Check local and remote sources for the file.
*
* @param $source
* Translation source object.
* @see l10n_update_source_build()
* @param $check_local
* File object of local translation file.
* @param $check_remote
* File object of remote translation file.
* @return object
* File object of most recent translation; local or remote.
*/
function l10n_update_source_check($source, $check_local = TRUE, $check_remote = TRUE) {
$local = $remote = NULL;
if ($check_local) {
$check = clone $source;
if (l10n_update_source_check_file($check)) {
$local = $check;
}
}
if ($check_remote) {
$check = clone $source;
if (l10n_update_source_check_download($check)) {
$remote = $check;
}
}
// Get remote if newer than local only, they both can be empty
return _l10n_update_source_compare($local, $remote) < 0 ? $remote : $local;
}
/**
* Check remote file object.
*
* @param $source
* Remote translation file object. The object will be update
* with data of the remote file:
* - 'type': Fixed value 'download'.
* - 'fileurl': File name and path.
* - 'timestamp': Last updated time.
* @see l10n_update_source_build()
* @return object
* An object containing the HTTP request headers, response code, headers,
* data, redirect status and updated timestamp.
* NULL if failure.
*/
function l10n_update_source_check_download($source) {
$url = l10n_update_build_string($source, $source->l10n_path);
$result = l10n_update_http_check($url);
if ($result && !empty($result->updated)) {
$source->type = 'download';
// There may have been redirects so we store the resulting url
$source->fileurl = $result->url;
$source->timestamp = $result->updated;
return $result;
}
}
/**
* Check whether we've got the file in the filesystem under 'translations'.
*
* It will search, similar to modules and themes:
* - translations
* - sites/all/translations
* - sites/mysite/translations
*
* Using name as the key will return just the last one found.
*
* @param $source
* Translation file object. The object will be updated with data of local file.
* - 'type': Fixed value 'localfile'.
* - 'filepath': File name and path.
* - 'timestamp': Last updated time.
* @see l10n_update_source_build()
* @param $directory
* Files directory.
* @return Object
* File object (filename, basename, name)
* NULL if failure.
*/
function l10n_update_source_check_file($source, $directory = 'translations') {
$filename = preg_quote($source->filename) . '$';
// Using the 'name' key will return
if ($files = drupal_system_listing($filename, $directory, 'name', 0)) {
$file = current($files);
$source->type = 'localfile';
$source->filepath = $file->filename;
$source->timestamp = filemtime($file->filename);
return $file;
}
}
/**
* Download and import or just import source, depending on type.
*
* @param $source
* Translation source object with information about the file location.
* Object will be updated with :
* - 'last_updated': Timestamp of current time;
* - 'import_date': Timestamp of current time;
* @param $mode
* Download mode. How to treat exising and modified translations.
* @return boolean
* TRUE on success, NULL on failure.
*/
function l10n_update_source_update($source, $mode) {
if ($source->type == 'localfile' || l10n_update_source_download($source)) {
if (l10n_update_source_import($source, $mode)) {
l10n_update_source_history($source);
return TRUE;
}
}
}
/**
* Import source into locales table.
*
* @param $source
* Translation source object with information about the file location.
* Object will be updated with :
* - 'last_updated': Timestamp of current time;
* - 'import_date': Timestamp of current time;
* @param $mode
* Download mode. How to treat exising and modified translations.
* @return boolean
* Result array on success, FALSE on failure.
*/
function l10n_update_source_import($source, $mode) {
if (!empty($source->filepath) && ($result = l10n_update_import_file($source->filepath, $source->language, $mode))) {
$source->last_checked = time();
// We override the file timestamp here. The default file time stamp is the
// release date from the l.d.o server. We change the timestamp to the
// creation time on the webserver. On multi sites that share a common
// sites/all/translations directory, the sharing sites use the local file
// creation date as release date. Without this correction the local
// file is always newer than the l.d.o. file, which results in unnecessary
// translation import.
$source->timestamp = time();
return $result;
}
return FALSE;
}
/**
* Download source file from remote server.
*
* If succesful this function returns the downloaded file in two ways:
* - As a temporary $file object
* - As a file path on the $source->filepath property.
*
* @param $source
* Source object with all parameters
* - 'fileurl': url to download.
* - 'filepath': alternate destination. If not present a temporary file
* will be used and the path returned here.
* @return object
* $file object if download successful.
*/
function l10n_update_source_download($source) {
if (!empty($source->filepath)) {
$destination = $source->filepath;
}
elseif ($directory = variable_get('l10n_update_download_store', '')) {
$destination = $directory . '/' . $source->filename;
}
else {
$destination = NULL;
}
if ($file = l10n_update_download_file($source->fileurl, $destination)) {
$source->filepath = $file;
return $file;
}
}
/**
* Update the file history table and delete the file if temporary.
*
* @param $file
* Source object representing the file just imported or downloaded.
*/
function l10n_update_source_history($file) {
// Update history table
l10n_update_file_history($file);
// If it's a downloaded file and not marked for keeping, delete the file.
if ($file->type == 'download' && empty($file->keep)) {
file_delete($file->filepath);
$file->filepath = '';
}
}
/**
* Compare two update sources, looking for the newer one (bigger timestamp).
*
* This function can be used as a callback to compare two source objects.
*
* @param $current
* Source object of current project.
* @param $update
* Source object of available update.
* @return integer
* - '-1': $current < $updated OR $current is missing
* - '0': $current == $updated OR both $current and $updated are missing
* - '1': $current > $updated OR $updated is missing
*/
function _l10n_update_source_compare($current, $update) {
if ($current && $update) {
if (abs($current->timestamp - $update->timestamp) < L10N_UPDATE_TIMESTAMP_THRESHOLD) {
return 0;
}
else {
return $current->timestamp > $update->timestamp ? 1 : -1;
}
}
elseif ($current && !$update) {
return 1;
}
elseif (!$current && $update) {
return -1;
}
else {
return 0;
}
}
/**
* Prepare update list.
*
* @param $updates
* Array of update sources that may be indexed in multiple ways.
* @param $projects
* Array of project names to be included, others will be filtered out.
* @param $languages
* Array of language codes to be included, others will be filtered out.
* @return array
* Plain array of filtered updates with directory applied.
*/
function _l10n_update_prepare_updates($updates, $projects = NULL, $languages = NULL) {
$result = array();
foreach ($updates as $key => $update) {
if (is_array($update)) {
// It is a sub array of updates yet, process and merge
$result = array_merge($result, _l10n_update_prepare_updates($update, $projects, $languages));
}
elseif ((!$projects || in_array($update->project, $projects)) && (!$languages || in_array($update->language, $languages))) {
$directory = variable_get('l10n_update_download_store', '');
if ($directory && empty($update->filepath)) {
// If we have a destination folder set just if we have no filepath
if (empty($update->filepath)) {
$update->filepath = $directory . '/' . $update->filename;
$update->keep = TRUE;
}
}
$result[] = $update;
}
}
return $result;
}
/**
* Language refresh. Runs a batch for loading the selected languages.
*
* To be used after adding a new language.
*
* @param $languages
* Array of language codes to check and download.
*/
function l10n_update_language_refresh($languages) {
$projects = l10n_update_get_projects();
if ($available = l10n_update_check_projects($projects, $languages)) {
$history = l10n_update_get_history();
if ($updates = l10n_update_build_updates($history, $available)) {
module_load_include('batch.inc', 'l10n_update');
// Filter out updates in other languages. If no languages, all of them will be updated
$updates = _l10n_update_prepare_updates($updates);
$batch = l10n_update_batch_multiple($updates, variable_get('l10n_update_import_mode', LOCALE_IMPORT_KEEP));
batch_set($batch);
}
}
}
Functions
Name | Description |
---|---|
l10n_update_available_releases | Fetch update information for all projects / all languages. |
l10n_update_build_updates | Compare available releases with history and get list of downloadable updates. |
l10n_update_check_projects | Check latest release for project, language. |
l10n_update_check_translations | Check updates for active projects and languages. |
l10n_update_language_refresh | Language refresh. Runs a batch for loading the selected languages. |
l10n_update_source_build | Build abstract translation source, to be mapped to a file or a download. |
l10n_update_source_check | Check local and remote sources for the file. |
l10n_update_source_check_download | Check remote file object. |
l10n_update_source_check_file | Check whether we've got the file in the filesystem under 'translations'. |
l10n_update_source_download | Download source file from remote server. |
l10n_update_source_history | Update the file history table and delete the file if temporary. |
l10n_update_source_import | Import source into locales table. |
l10n_update_source_update | Download and import or just import source, depending on type. |
_l10n_update_prepare_updates | Prepare update list. |
_l10n_update_source_compare | Compare two update sources, looking for the newer one (bigger timestamp). |
Constants
Name | Description |
---|---|
L10N_UPDATE_TIMESTAMP_THRESHOLD | Threshold for timestamp comparison. |