updated elysia_cron, elfinder, metatag, libraries, email_registration, migrate, nodeform_cols

This commit is contained in:
Bachir Soussi Chiadmi 2019-05-13 18:03:41 +02:00
parent e08a2639c6
commit 58cd990c8c
346 changed files with 8636 additions and 4770 deletions

View File

@ -5,9 +5,9 @@ dependencies[] = biblio
core = 7.x core = 7.x
package = Node form columns package = Node form columns
; Information added by Drupal.org packaging script on 2014-01-15 ; Information added by Drupal.org packaging script on 2016-09-16
version = "7.x-1.0" version = "7.x-1.1"
core = "7.x" core = "7.x"
project = "nodeformcols" project = "nodeformcols"
datestamp = "1389798806" datestamp = "1474036440"

View File

@ -19,7 +19,7 @@ function nfcbiblio_nodeformcols_variants_alter(&$variants, $type) {
WHERE tid >= :tid WHERE tid >= :tid
AND visible = :visible AND visible = :visible
ORDER BY weight ASC', array(':tid' => 0, ':visible' => 1)); ORDER BY weight ASC', array(':tid' => 0, ':visible' => 1));
while ($option = db_fetch_object($res)) { while ($option = $res->fetchObject()) {
$variants[$option->tid] = $option->name; $variants[$option->tid] = $option->name;
} }
} }
@ -35,8 +35,8 @@ function _nfcbiblio_top_visible_type() {
return db_query_range("SELECT tid return db_query_range("SELECT tid
FROM {biblio_types} FROM {biblio_types}
WHERE visible = :visible WHERE visible = :visible
AND tid> = :tid> AND tid >= :tid
ORDER BY weight ASC", array(':visible' => 1, ':tid>' => 0))->fetchField(); ORDER BY weight ASC", 0, 1, array(':visible' => 1, ':tid' => 0))->fetchField();
} }
/** /**

View File

@ -5,9 +5,9 @@ dependencies[] = captcha
core = 7.x core = 7.x
package = Node form columns package = Node form columns
; Information added by Drupal.org packaging script on 2014-01-15 ; Information added by Drupal.org packaging script on 2016-09-16
version = "7.x-1.0" version = "7.x-1.1"
core = "7.x" core = "7.x"
project = "nodeformcols" project = "nodeformcols"
datestamp = "1389798806" datestamp = "1474036440"

View File

@ -21,6 +21,20 @@ function _nodeformcols_get_node_type_form($type) {
); );
$fs += form_state_defaults(); $fs += form_state_defaults();
$nf = drupal_retrieve_form($nfid, $fs); $nf = drupal_retrieve_form($nfid, $fs);
// Add in field_groups if they exist.
if (module_exists('field_group') && !empty($nf['#groups'])) {
foreach ($nf['#groups'] as $group) {
$group_title = $group->label . ' (' . t('Field group') . ')';
$nf[$group->group_name] = array(
'#group' => $group,
'#weight' => $group->weight,
'#title' => $group_title,
);
foreach ($nf['#groups'][$group->group_name]->children as $group_field) {
unset($nf[$group_field]);
}
}
}
drupal_prepare_form($nfid, $nf, $fs); drupal_prepare_form($nfid, $nf, $fs);
drupal_process_form($nfid, $nf, $fs); drupal_process_form($nfid, $nf, $fs);
return $nf; return $nf;
@ -35,8 +49,10 @@ function nodeformcols_update_placements($type, $variant, &$placements) {
$field = $form[$key]; $field = $form[$key];
if ( if (
substr($key, 0, 8) == 'section_' || substr($key, 0, 8) == 'section_' ||
// Exclude hidden fields.
(isset($field['#type']) && in_array($field['#type'], array('value', 'hidden', 'token'))) || (isset($field['#type']) && in_array($field['#type'], array('value', 'hidden', 'token'))) ||
(isset($field['#type'], $field['#group']) && $field['#type'] === 'fieldset') // Exclude the vertical tabs.
(isset($field['#group']) && $field['#group'] == 'additional_settings')
) { ) {
// Remove placements that meet exclusion rules. // Remove placements that meet exclusion rules.
if (isset($placements[$key])) { if (isset($placements[$key])) {
@ -128,7 +144,7 @@ function nodeformcols_configuration_form($form, $form_state, $node_type, $varian
$form['variant'] = array( $form['variant'] = array(
'#type' => 'item', '#type' => 'item',
'#title' => t('Select a form variant'), '#title' => t('Select a form variant'),
'#value' => theme('links', array('links' => $variant_links)), '#markup' => theme('links', array('links' => $variant_links)),
); );
} }

View File

@ -3,9 +3,9 @@ description = Separates the node forms into two columns and a footer.
core = 7.x core = 7.x
package = Node form columns package = Node form columns
; Information added by Drupal.org packaging script on 2014-01-15 ; Information added by Drupal.org packaging script on 2016-09-16
version = "7.x-1.0" version = "7.x-1.1"
core = "7.x" core = "7.x"
project = "nodeformcols" project = "nodeformcols"
datestamp = "1389798806" datestamp = "1474036440"

View File

@ -160,8 +160,8 @@ function template_preprocess_node_form(&$aVars) {
} }
} }
foreach ($has_elements as $name => $has) { foreach ($regions as $name => $data) {
if ($has) { if (!empty($has_elements[$name])) {
$class[] = 'node-form-has-region-' . $name; $class[] = 'node-form-has-region-' . $name;
$form['nodeformcols_region_' . $name] = $regions[$name]; $form['nodeformcols_region_' . $name] = $regions[$name];
} }
@ -191,8 +191,8 @@ function nodeformcols_node_type_update($info) {
$base = 'nodeformscols_field_placements_' . $info->old_type; $base = 'nodeformscols_field_placements_' . $info->old_type;
$new_base = 'nodeformscols_field_placements_' . $info->type; $new_base = 'nodeformscols_field_placements_' . $info->type;
$result = db_select('variable') $result = db_select('variable')
->condition('name', $base . '%', 'LIKE')
->fields('variable', array('name')) ->fields('variable', array('name'))
->condition('name', $base . '%', 'LIKE')
->execute(); ->execute();
foreach ($result as $row) { foreach ($result as $row) {
$value = variable_get($row->name, NULL); $value = variable_get($row->name, NULL);

View File

@ -312,7 +312,8 @@ class _DiffEngine {
} }
$matches = $ymatches[$line]; $matches = $ymatches[$line];
reset($matches); reset($matches);
while (list ($junk, $y) = each($matches)) { while ($y = current($matches)) {
next($matches);
if (empty($this->in_seq[$y])) { if (empty($this->in_seq[$y])) {
$k = $this->_lcs_pos($y); $k = $this->_lcs_pos($y);
USE_ASSERTS && assert($k > 0); USE_ASSERTS && assert($k > 0);
@ -320,7 +321,8 @@ class _DiffEngine {
break; break;
} }
} }
while (list ($junk, $y) = each($matches)) { while ($y = current($matches)) {
next($matches);
if ($y > $this->seq[$k-1]) { if ($y > $this->seq[$k-1]) {
USE_ASSERTS && assert($y < $this->seq[$k]); USE_ASSERTS && assert($y < $this->seq[$k]);
// Optimization: this is a common case: // Optimization: this is a common case:
@ -454,7 +456,7 @@ class _DiffEngine {
$i = 0; $i = 0;
$j = 0; $j = 0;
USE_ASSERTS && assert('sizeof($lines) == sizeof($changed)'); USE_ASSERTS && assert(sizeof($lines) == sizeof($changed));
$len = sizeof($lines); $len = sizeof($lines);
$other_len = sizeof($other_changed); $other_len = sizeof($other_changed);
@ -474,7 +476,7 @@ class _DiffEngine {
$j++; $j++;
} }
while ($i < $len && ! $changed[$i]) { while ($i < $len && ! $changed[$i]) {
USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]'); USE_ASSERTS && assert($j < $other_len && ! $other_changed[$j]);
$i++; $i++;
$j++; $j++;
while ($j < $other_len && $other_changed[$j]) { while ($j < $other_len && $other_changed[$j]) {
@ -510,11 +512,11 @@ class _DiffEngine {
while ($start > 0 && $changed[$start - 1]) { while ($start > 0 && $changed[$start - 1]) {
$start--; $start--;
} }
USE_ASSERTS && assert('$j > 0'); USE_ASSERTS && assert($j > 0);
while ($other_changed[--$j]) { while ($other_changed[--$j]) {
continue; continue;
} }
USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]'); USE_ASSERTS && assert($j >= 0 && !$other_changed[$j]);
} }
/* /*
@ -537,7 +539,7 @@ class _DiffEngine {
while ($i < $len && $changed[$i]) { while ($i < $len && $changed[$i]) {
$i++; $i++;
} }
USE_ASSERTS && assert('$j < $other_len && ! $other_changed[$j]'); USE_ASSERTS && assert($j < $other_len && ! $other_changed[$j]);
$j++; $j++;
if ($j < $other_len && $other_changed[$j]) { if ($j < $other_len && $other_changed[$j]) {
$corresponding = $i; $corresponding = $i;
@ -555,11 +557,11 @@ class _DiffEngine {
while ($corresponding < $i) { while ($corresponding < $i) {
$changed[--$start] = 1; $changed[--$start] = 1;
$changed[--$i] = 0; $changed[--$i] = 0;
USE_ASSERTS && assert('$j > 0'); USE_ASSERTS && assert($j > 0);
while ($other_changed[--$j]) { while ($other_changed[--$j]) {
continue; continue;
} }
USE_ASSERTS && assert('$j >= 0 && !$other_changed[$j]'); USE_ASSERTS && assert($j >= 0 && !$other_changed[$j]);
} }
} }
} }
@ -742,8 +744,8 @@ class MappedDiff extends Diff {
* have the same number of elements as $to_lines. * have the same number of elements as $to_lines.
*/ */
function __construct($from_lines, $to_lines, $mapped_from_lines, $mapped_to_lines) { function __construct($from_lines, $to_lines, $mapped_from_lines, $mapped_to_lines) {
assert(sizeof($from_lines) == sizeof($mapped_from_lines)); USE_ASSERTS && assert(sizeof($from_lines) == sizeof($mapped_from_lines));
assert(sizeof($to_lines) == sizeof($mapped_to_lines)); USE_ASSERTS && assert(sizeof($to_lines) == sizeof($mapped_to_lines));
parent::__construct($mapped_from_lines, $mapped_to_lines); parent::__construct($mapped_from_lines, $mapped_to_lines);
@ -1000,7 +1002,7 @@ class _HWLDF_WordAccumulator {
$this->_flushLine($tag); $this->_flushLine($tag);
$word = drupal_substr($word, 1); $word = drupal_substr($word, 1);
} }
assert(!strstr($word, "\n")); USE_ASSERTS && assert(!strstr($word, "\n"));
$this->_group .= $word; $this->_group .= $word;
} }
} }

View File

@ -112,7 +112,12 @@ function diff_admin_field_overview() {
'#markup' => '<p>' . t('This table provides a summary of the field type support found on the system. It is recommended that you use global settings whenever possible to configure field comparison settings.') . '</p>', '#markup' => '<p>' . t('This table provides a summary of the field type support found on the system. It is recommended that you use global settings whenever possible to configure field comparison settings.') . '</p>',
); );
$header = array(t('Type'), t('Module'), t('Operations')); $header = array(
t('Type'),
t('Machine name'),
t('Module'),
t('Operations'),
);
$rows = array(); $rows = array();
// Skip field types which have no widget types. // Skip field types which have no widget types.
@ -129,10 +134,8 @@ function diff_admin_field_overview() {
foreach ($field_types as $field_name => $field_type) { foreach ($field_types as $field_name => $field_type) {
if (!empty($widgets[$field_name])) { if (!empty($widgets[$field_name])) {
$row = array(); $row = array();
$row[] = t('@field_label (%field_type)', array( $row[] = $field_type['label'];
'@field_label' => $field_type['label'], $row[] = $field_name;
'%field_type' => $field_name,
));
$row[] = $field_type['module']; $row[] = $field_type['module'];
$row[] = l(t('Global settings'), 'admin/config/content/diff/fields/' . $field_name); $row[] = l(t('Global settings'), 'admin/config/content/diff/fields/' . $field_name);
$rows[] = $row; $rows[] = $row;

View File

@ -108,6 +108,22 @@ function hook_entity_diff($old_entity, $new_entity, $context) {
* @see hook_entity_diff() * @see hook_entity_diff()
*/ */
function hook_entity_diff_alter(&$entity_diffs, $context) { function hook_entity_diff_alter(&$entity_diffs, $context) {
if ($context['entity_type'] == 'node') {
$old_entity = $context['old_entity'];
$new_entity = $context['new_entity'];
$entity_diffs['custom_vid'] = array(
'#name' => t('Second VID'),
'#old' => array($old_entity->vid),
'#new' => array($new_entity->vid),
'#weight' => 5,
);
$entity_diffs['custom_log'] = array(
'#name' => t('Second log'),
'#old' => array($old_entity->log),
'#new' => array($new_entity->log),
'#weight' => 6,
);
}
} }
/** /**

View File

@ -5,9 +5,8 @@ configure = admin/config/content/diff
files[] = DiffEngine.php files[] = DiffEngine.php
; Information added by Drupal.org packaging script on 2016-12-20 ; Information added by Drupal.org packaging script on 2018-11-05
version = "7.x-3.3" version = "7.x-3.4"
core = "7.x" core = "7.x"
project = "diff" project = "diff"
datestamp = "1482211686" datestamp = "1541401388"

View File

@ -133,9 +133,14 @@ function diff_update_7306() {
} }
/** /**
* Grants access to the Diff "View Changes" button permission to all users. * Grants access to the Diff "View Changes" button permission to all users
* if the Diff module is enabled. You need to manually update the permissions
* if the module is currently disabled.
*/ */
function diff_update_7307() { function diff_update_7307() {
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('diff view changes')); // The update hooks run even if disabled, and this throws an error.
user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('diff view changes')); if (module_exists('diff')) {
user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('diff view changes'));
user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('diff view changes'));
}
} }

View File

@ -475,7 +475,8 @@ function diff_node_form_build_preview_changes($form, &$form_state) {
$node = node_form_submit_build_node($form, $form_state); $node = node_form_submit_build_node($form, $form_state);
// Create diff of old node and edited node. // Create diff of old node and edited node.
$rows = _diff_body_rows($old_node, $node); $state = variable_get('diff_default_state_node', 'raw');
$rows = _diff_body_rows($old_node, $node, $state);
$header = _diff_default_header(t('Original'), t('Changes')); $header = _diff_default_header(t('Original'), t('Changes'));
$changes = theme('table__diff__preview', array( $changes = theme('table__diff__preview', array(

View File

@ -153,6 +153,8 @@ Options:
--verbose, but without enabling drush --verbose, but without enabling drush
verbose mode) verbose mode)
--ignore-disable run channels/tasks even if disabled --ignore-disable run channels/tasks even if disabled
--ignore-maintenance run channels/tasks even if
maintenance mode is enabled
--ignore-running run channels/tasks even --ignore-running run channels/tasks even
if already running if already running
--ignore-time run channels/tasks even if not ready --ignore-time run channels/tasks even if not ready

View File

@ -10,6 +10,9 @@
* *
* @return array * @return array
* Renderable array. * Renderable array.
*
* @throws Exception
* Exceptions from theme().
*/ */
function elysia_cron_admin_page() { function elysia_cron_admin_page() {
$aoutput = array(); $aoutput = array();
@ -151,10 +154,26 @@ function elysia_cron_admin_page() {
$output .= '<br />'; $output .= '<br />';
$legend_icons = array( $legend_icons = array(
'idle' => theme('image', array('path' => $ipath . 'idle.png')), 'idle' => theme('image', array(
'waiting' => theme('image', array('path' => $ipath . 'waiting.png')), 'path' => $ipath . 'idle.png',
'running' => theme('image', array('path' => $ipath . 'running.png')), 'alt' => t('Idle'),
'disabled' => theme('image', array('path' => $ipath . 'disabled.png')), 'title' => t('Idle'),
)),
'waiting' => theme('image', array(
'path' => $ipath . 'waiting.png',
'alt' => t('Waiting for execution'),
'title' => t('Waiting for execution'),
)),
'running' => theme('image', array(
'path' => $ipath . 'running.png',
'alt' => t('Running'),
'title' => t('Running'),
)),
'disabled' => theme('image', array(
'path' => $ipath . 'disabled.png',
'alt' => t('Disabled'),
'title' => t('Disabled'),
)),
); );
$output .= theme('table', array( $output .= theme('table', array(
'header' => array(t('Legend')), 'header' => array(t('Legend')),
@ -187,6 +206,7 @@ function elysia_cron_settings_form() {
elysia_cron_initialize(); elysia_cron_initialize();
$form = array(); $form = array();
$form['#attached']['js'][] = drupal_get_path('module', 'elysia_cron') . '/js/elysia_cron.js';
$form['prefix_1'] = array( $form['prefix_1'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
@ -429,14 +449,6 @@ EOT
'#collapsible' => TRUE, '#collapsible' => TRUE,
); );
$jobchannels = array(
'#title' => t('Job channel associations'),
'#description' => t('Leave empty for default channel'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
foreach ($_elysia_cron_settings_by_channel as $channel => $cconf) { foreach ($_elysia_cron_settings_by_channel as $channel => $cconf) {
foreach ($cconf as $job => $conf) { foreach ($cconf as $job => $conf) {
if ($job != '#data' && empty($conf['expression'])) { if ($job != '#data' && empty($conf['expression'])) {
@ -546,12 +558,6 @@ EOT
function theme_elysia_cron_settings_form(array &$variables) { function theme_elysia_cron_settings_form(array &$variables) {
$form = &$variables['form']; $form = &$variables['form'];
$output = '<script type="text/javascript"><!--' . "\n" .
'function _ec_select(key, select) { if (select.value == \'custom\') {' .
'$ = jQuery; $("#_ec_select_"+key).hide();$("#_ec_custom_"+key).show();$("#_ec_custom_"+key).focus();' .
'}}' .
"\n" . '--></script>';
$coutput = '<table>'; $coutput = '<table>';
$i = 0; $i = 0;
@ -579,7 +585,8 @@ function theme_elysia_cron_settings_form(array &$variables) {
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#suffix'] = '</span>'; $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#suffix'] = '</span>';
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#title'] = NULL; $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#title'] = NULL;
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#description'] = NULL; $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#description'] = NULL;
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#attributes']['onchange'] = '_ec_select(\'' . $key . '\', this)'; $form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#attributes']['class'][] = 'ec-select';
$form['single_job'][$c]['_elysia_cron_seljob_rule_' . $key]['#attributes']['data-key'] = $key;
$form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#title'] = NULL; $form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#title'] = NULL;
$form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#description'] = NULL; $form['single_job'][$c]['_elysia_cron_job_weight_' . $key]['#description'] = NULL;
@ -631,7 +638,7 @@ function theme_elysia_cron_settings_form(array &$variables) {
$form['channels']['#children'] = $coutput; $form['channels']['#children'] = $coutput;
return $output . drupal_render_children($form); return drupal_render_children($form);
} }
/** /**

View File

@ -5,6 +5,8 @@
* Ctools integration. * Ctools integration.
*/ */
define('ELYSIA_CRON_CTOOLS_LINEBREAK', "\n");
/** /**
* Get default cron jobs. * Get default cron jobs.
* *
@ -203,13 +205,13 @@ function elysia_cron_ctools_export_callback($object, $indent) {
$schema = ctools_export_get_schema($table); $schema = ctools_export_get_schema($table);
$identifier = $schema['export']['identifier']; $identifier = $schema['export']['identifier'];
$output = $indent . '$' . $identifier . ' = new ' . get_class($object) . ';' . PHP_EOL; $output = $indent . '$' . $identifier . ' = new ' . get_class($object) . '();' . ELYSIA_CRON_CTOOLS_LINEBREAK;
if ($schema['export']['can disable']) { if ($schema['export']['can disable']) {
$output .= $indent . '$' . $identifier . '->disabled = FALSE; /* Edit this to true to make a default ' . $identifier . ' disabled initially */' . PHP_EOL; $output .= $indent . '$' . $identifier . '->disabled = FALSE; /* Edit this to true to make a default ' . $identifier . ' disabled initially */' . ELYSIA_CRON_CTOOLS_LINEBREAK;
} }
if (!empty($schema['export']['api']['current_version'])) { if (!empty($schema['export']['api']['current_version'])) {
$output .= $indent . '$' . $identifier . '->api_version = ' . $schema['export']['api']['current_version'] . ';' . PHP_EOL; $output .= $indent . '$' . $identifier . '->api_version = ' . $schema['export']['api']['current_version'] . ';' . ELYSIA_CRON_CTOOLS_LINEBREAK;
} }
$fields = $schema['fields']; $fields = $schema['fields'];
@ -222,7 +224,7 @@ function elysia_cron_ctools_export_callback($object, $indent) {
if (!is_null($value) && $info['type'] == 'int') { if (!is_null($value) && $info['type'] == 'int') {
$value = (isset($info['size']) && $info['size'] == 'tiny') ? (bool) $value : (int) $value; $value = (isset($info['size']) && $info['size'] == 'tiny') ? (bool) $value : (int) $value;
} }
$output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ';' . PHP_EOL; $output .= $indent . '$' . $identifier . '->' . $field . ' = ' . ctools_var_export($value, $indent) . ';' . ELYSIA_CRON_CTOOLS_LINEBREAK;
} }
return $output; return $output;
@ -253,19 +255,19 @@ function elysia_cron_ctools_to_hook_code(array $names, $name) {
$objects = array_intersect_key($objects, array_flip($names)); $objects = array_intersect_key($objects, array_flip($names));
if ($objects) { if ($objects) {
$output = '/**' . PHP_EOL; $output = '/**' . ELYSIA_CRON_CTOOLS_LINEBREAK;
$output .= " * Implementation of hook_{$export['default hook']}()" . PHP_EOL; $output .= " * Implementation of hook_{$export['default hook']}()" . ELYSIA_CRON_CTOOLS_LINEBREAK;
$output .= ' */' . PHP_EOL; $output .= ' */' . ELYSIA_CRON_CTOOLS_LINEBREAK;
$output .= "function " . $name . "_{$export['default hook']}() {" . PHP_EOL; $output .= "function " . $name . "_{$export['default hook']}() {" . ELYSIA_CRON_CTOOLS_LINEBREAK;
$output .= " \${$export['identifier']}s = array();" . PHP_EOL . PHP_EOL; $output .= " \${$export['identifier']}s = array();" . ELYSIA_CRON_CTOOLS_LINEBREAK . ELYSIA_CRON_CTOOLS_LINEBREAK;
foreach ($objects as $object) { foreach ($objects as $object) {
$output .= ctools_export_crud_export($table, $object, ' '); $output .= ctools_export_crud_export($table, $object, ' ');
$output .= " \${$export['identifier']}s['" . check_plain($object->{$export['key']}) . "'] = \${$export['identifier']};" . PHP_EOL . PHP_EOL; $output .= " \${$export['identifier']}s['" . check_plain($object->{$export['key']}) . "'] = \${$export['identifier']};" . ELYSIA_CRON_CTOOLS_LINEBREAK . ELYSIA_CRON_CTOOLS_LINEBREAK;
} }
$output .= " return \${$export['identifier']}s;" . PHP_EOL; $output .= " return \${$export['identifier']}s;" . ELYSIA_CRON_CTOOLS_LINEBREAK;
$output .= '}' . PHP_EOL; $output .= '}' . ELYSIA_CRON_CTOOLS_LINEBREAK;
} }
return $output; return $output;

View File

@ -63,7 +63,9 @@ function elysia_cron_drush_command() {
'quiet' => 'suppress all output', 'quiet' => 'suppress all output',
'verbose' => 'enable extended output', 'verbose' => 'enable extended output',
'elysia-cron-verbose' => 'enable extended output (the same as --verbose, but without enabling drush verbose mode)', 'elysia-cron-verbose' => 'enable extended output (the same as --verbose, but without enabling drush verbose mode)',
'force' => 'run channels/tasks even if it disabled/not ready for execution/already running',
'ignore-disable' => 'run channels/tasks even if disabled', 'ignore-disable' => 'run channels/tasks even if disabled',
'ignore-maintenance' => 'run channels/tasks even if maintenance mode is enabled',
'ignore-time' => 'run channels/tasks even if not ready for execution', 'ignore-time' => 'run channels/tasks even if not ready for execution',
'ignore-running' => 'run channels/tasks even if already running', 'ignore-running' => 'run channels/tasks even if already running',
), ),
@ -79,8 +81,10 @@ function elysia_cron_drush_command() {
function drush_elysia_cron_run_wrapper($op = FALSE, $target = FALSE) { function drush_elysia_cron_run_wrapper($op = FALSE, $target = FALSE) {
global $_elysia_cron_drush; global $_elysia_cron_drush;
$force = drush_get_option('force', FALSE);
if (variable_get('maintenance_mode', FALSE)) { if (variable_get('maintenance_mode', FALSE)) {
if (!variable_get('elysia_cron_run_maintenance', FALSE)) { $ignore_maintenance = $force || drush_get_option('ignore-maintenance', FALSE);
if (!$ignore_maintenance && !variable_get('elysia_cron_run_maintenance', FALSE)) {
drush_set_error('Cron run is not allowed in maintenance mode'); drush_set_error('Cron run is not allowed in maintenance mode');
return; return;
} }
@ -91,7 +95,16 @@ function drush_elysia_cron_run_wrapper($op = FALSE, $target = FALSE) {
if (!$verbose) { if (!$verbose) {
$verbose = drush_get_option('elysia-cron-verbose', FALSE); $verbose = drush_get_option('elysia-cron-verbose', FALSE);
} }
$_elysia_cron_drush = $quiet ? 1 : !$verbose ? 2 : 3;
if ($quiet) {
$_elysia_cron_drush = 1;
}
elseif ($verbose) {
$_elysia_cron_drush = 3;
}
else {
$_elysia_cron_drush = 2;
}
switch ($op) { switch ($op) {
case 'list': case 'list':
@ -140,13 +153,18 @@ function drush_elysia_cron_run_wrapper($op = FALSE, $target = FALSE) {
break; break;
case 'run': case 'run':
// Collect options.
$ignore_disable = $force || drush_get_option('ignore-disable', FALSE);
$ignore_time = $force || drush_get_option('ignore-time', FALSE);
$ignore_running = $force || drush_get_option('ignore-running', FALSE);
if (empty($target)) { if (empty($target)) {
elysia_cron_run(FALSE, drush_get_option('ignore-disable', FALSE), drush_get_option('ignore-time', FALSE), drush_get_option('ignore-running', FALSE)); elysia_cron_run(FALSE, $ignore_disable, $ignore_time, $ignore_running);
} }
elseif (strpos($target, '@') === 0) { elseif (strpos($target, '@') === 0) {
elysia_cron_initialize(); elysia_cron_initialize();
if (elysia_cron_channel_exists(substr($target, 1))) { if (elysia_cron_channel_exists(substr($target, 1))) {
elysia_cron_run_channel(substr($target, 1), drush_get_option('ignore-disable', FALSE), drush_get_option('ignore-time', FALSE), drush_get_option('ignore-running', FALSE)); elysia_cron_run_channel(substr($target, 1), $ignore_disable, $ignore_time, $ignore_running);
} }
else { else {
drush_set_error('Channel ' . substr($target, 1) . ' does not exists'); drush_set_error('Channel ' . substr($target, 1) . ' does not exists');
@ -155,7 +173,7 @@ function drush_elysia_cron_run_wrapper($op = FALSE, $target = FALSE) {
else { else {
elysia_cron_initialize(); elysia_cron_initialize();
if (elysia_cron_job_exists($target)) { if (elysia_cron_job_exists($target)) {
elysia_cron_run_job($target, drush_get_option('ignore-disable', FALSE), drush_get_option('ignore-time', FALSE), drush_get_option('ignore-running', FALSE)); elysia_cron_run_job($target, $ignore_disable, $ignore_time, $ignore_running);
} }
else { else {
drush_set_error('Job ' . $target . ' does not exists'); drush_set_error('Job ' . $target . ' does not exists');

View File

@ -4,9 +4,8 @@ core = 7.x
configure = admin/config/system/cron configure = admin/config/system/cron
; Information added by Drupal.org packaging script on 2016-11-23 ; Information added by Drupal.org packaging script on 2019-04-24
version = "7.x-2.4" version = "7.x-2.7"
core = "7.x" core = "7.x"
project = "elysia_cron" project = "elysia_cron"
datestamp = "1479877741" datestamp = "1556076200"

View File

@ -64,14 +64,15 @@ function elysia_cron_menu() {
*/ */
function elysia_cron_menu_alter(&$items) { function elysia_cron_menu_alter(&$items) {
// Override default cron page. // Override default cron page.
$items['admin/config/system/cron'] = array( $items['admin/config/system/cron']['title'] = 'Cron Settings';
'title' => 'Cron Settings', $items['admin/config/system/cron']['description'] = 'View and manage cron table';
'description' => 'View and manage cron table', $items['admin/config/system/cron']['page callback'] = 'elysia_cron_admin_page';
'page callback' => 'elysia_cron_admin_page', $items['admin/config/system/cron']['access callback'] = 'elysia_cron_access';
'access callback' => 'elysia_cron_access', $items['admin/config/system/cron']['access arguments'] = array('view elysia_cron');
'access arguments' => array('view elysia_cron'), $items['admin/config/system/cron']['file'] = NULL;
'file' => NULL,
) + $items['admin/config/system/cron']; $items['admin/reports/status/run-cron']['page callback'] = 'elysia_cron_run_manually';
$items['admin/reports/status/run-cron']['file'] = NULL;
} }
/** /**
@ -113,7 +114,7 @@ function elysia_cron_permission() {
'administer elysia_cron' => array( 'administer elysia_cron' => array(
'title' => t('Administer elysia cron'), 'title' => t('Administer elysia cron'),
'description' => t('Perform changes to cron jobs timings, disable cron or single jobs and access cron execution statistics'), 'description' => t('Perform changes to cron jobs timings, disable cron or single jobs and access cron execution statistics'),
'restrict access' => TRUE, 'restrict access' => TRUE,
), ),
'execute elysia_cron' => array( 'execute elysia_cron' => array(
'title' => t('Execute elysia cron jobs'), 'title' => t('Execute elysia cron jobs'),
@ -143,6 +144,12 @@ function elysia_cron_exit() {
function elysia_cron_cron() { function elysia_cron_cron() {
global $_elysia_cron_exit_phase, $_elysia_cron_drush; global $_elysia_cron_exit_phase, $_elysia_cron_drush;
// First cron run is executed in standard drupal way.
// This is to enable the use of install profiles.
if (variable_get('cron_last', 0) <= variable_get('install_time', 0)) {
return;
}
// If cron has been executed via "drush core-cron" or any other custom drush // If cron has been executed via "drush core-cron" or any other custom drush
// command then we run internal cron handler which is designed to handle // command then we run internal cron handler which is designed to handle
// cron executions from drush. // cron executions from drush.
@ -150,11 +157,6 @@ function elysia_cron_cron() {
elysia_cron_drush_invoke(); elysia_cron_drush_invoke();
} }
// First cron run is executed in standard drupal way.
// This is to enable the use of install profiles.
if (variable_get('cron_last', 0) <= variable_get('install_time', 0)) {
return;
}
// If the path is 'admin/*', or if the user is not anonymous, // If the path is 'admin/*', or if the user is not anonymous,
// this is a manual cron run (probably by admin/logs/status), // this is a manual cron run (probably by admin/logs/status),
// but not if we are in the exit phase (= this a "poormanscron" run). // but not if we are in the exit phase (= this a "poormanscron" run).
@ -187,7 +189,7 @@ function elysia_cron_cron() {
variable_set('cron_last', time()); variable_set('cron_last', time());
} }
exit(); drupal_exit();
} }
/** /**
@ -238,8 +240,11 @@ $GLOBALS['_ec_variables_allowed'] = array(
*/ */
function _ec_variable_init() { function _ec_variable_init() {
global $_ec_variables, $_ec_variables_allowed; global $_ec_variables, $_ec_variables_allowed;
$_ec_variables = array(); $_ec_variables = array();
$_ec_variables = array_map('unserialize', db_query("SELECT name, value FROM {variable} where name like '" . implode("' or name like '", $_ec_variables_allowed) . "'")->fetchAllKeyed()); foreach ($_ec_variables_allowed as $name) {
$_ec_variables[$name] = variable_get($name);
}
} }
/** /**
@ -925,7 +930,7 @@ function elysia_cron_debug($message, $vars = array(), $type = WATCHDOG_NOTICE) {
if ($type < WATCHDOG_NOTICE || variable_get('elysia_cron_debug_messages', 0)) { if ($type < WATCHDOG_NOTICE || variable_get('elysia_cron_debug_messages', 0)) {
watchdog('cron', $message, $vars, $type); watchdog('cron', $message, $vars, $type);
} }
if (!empty($_elysia_cron_drush) && $_elysia_cron_drush >= 2 && ($type >= WATCHDOG_NOTICE || !drush_get_option("verbose", FALSE))) { if (!empty($_elysia_cron_drush) && $_elysia_cron_drush >= 2 && ($type >= WATCHDOG_NOTICE || drush_get_option("verbose", FALSE))) {
if ($type >= WATCHDOG_NOTICE) { if ($type >= WATCHDOG_NOTICE) {
drush_print(strip_tags(dt($message, $vars))); drush_print(strip_tags(dt($message, $vars)));
} }
@ -1143,7 +1148,7 @@ function elysia_cron_prepare_run($manual_run, $start = TRUE) {
// Try to allocate enough time to run all the hook_cron implementations. // Try to allocate enough time to run all the hook_cron implementations.
if (!ini_get('safe_mode') && _elysia_cron_function_available('set_time_limit')) { if (!ini_get('safe_mode') && _elysia_cron_function_available('set_time_limit')) {
set_time_limit(variable_get('elysia_cron_time_limit', 240)); @set_time_limit(variable_get('elysia_cron_time_limit', 240));
} }
// Prevent session information from being saved while cron is running. // Prevent session information from being saved while cron is running.
@ -1294,6 +1299,21 @@ function elysia_cron_run($manual_run = FALSE, $ignore_disable = FALSE, $ignore_t
return $execute; return $execute;
} }
/**
* Menu callback: run cron manually.
*/
function elysia_cron_run_manually() {
// Run cron manually
if (elysia_cron_run(TRUE)) {
drupal_set_message(t('Cron ran successfully.'));
}
else {
drupal_set_message(t('Cron run failed.'), 'error');
}
drupal_goto('admin/reports/status');
}
/** /**
* Public function to execute all jobs in a channel. * Public function to execute all jobs in a channel.
* *
@ -1303,6 +1323,9 @@ function elysia_cron_run($manual_run = FALSE, $ignore_disable = FALSE, $ignore_t
* Run channel (and all it's jobs) job even if not ready. * Run channel (and all it's jobs) job even if not ready.
* @param bool $ignore_running * @param bool $ignore_running
* Run the channel (and all it's jobs) even if already running. * Run the channel (and all it's jobs) even if already running.
*
* @return bool
* Result of execution.
*/ */
function elysia_cron_run_channel($channel, $ignore_disable = FALSE, $ignore_time = FALSE, $ignore_running = FALSE) { function elysia_cron_run_channel($channel, $ignore_disable = FALSE, $ignore_time = FALSE, $ignore_running = FALSE) {
global $_elysia_cron_settings_by_channel; global $_elysia_cron_settings_by_channel;
@ -1530,7 +1553,8 @@ function elysia_cron_check_run_job($job) {
/** /**
* Find an idle channel (not running, or stuck). If found one, set it as running and returns available jobs. * Find an idle channel (not running, or stuck). If found one, set it as running and returns available jobs.
* *
* @return if found returns array { 'name' => name of channel, 'jobs' => array of active jobs }, else return FALSE * @return array|bool
* If found returns array { 'name' => name of channel, 'jobs' => array of active jobs }, else return FALSE.
*/ */
function elysia_cron_run_available_channel($ignore_disable = FALSE, $ignore_time = FALSE, $ignore_running = FALSE) { function elysia_cron_run_available_channel($ignore_disable = FALSE, $ignore_time = FALSE, $ignore_running = FALSE) {
global $_elysia_cron_settings_by_channel; global $_elysia_cron_settings_by_channel;
@ -1592,7 +1616,7 @@ function elysia_cron_execute_aborted($channel) {
* Used for unexpected termination of code. * Used for unexpected termination of code.
*/ */
function elysia_cron_internal_execute_channel_cleanup() { function elysia_cron_internal_execute_channel_cleanup() {
global $_elysia_cron_settings, $_elysia_cron_current_channel, $_cron_completed, $_cron_completed_time; global $_elysia_cron_current_channel, $_cron_completed;
if ($_cron_completed) { if ($_cron_completed) {
return; return;
@ -1825,16 +1849,16 @@ function elysia_cron_ping_page() {
$max_interval = variable_get('elysia_cron_alert_interval', 60) * 60; $max_interval = variable_get('elysia_cron_alert_interval', 60) * 60;
if ($diff > $max_interval) { if ($diff > $max_interval) {
return drupal_not_found(); drupal_not_found();
} drupal_exit();
else {
$aoutput = array();
$aoutput[] = array(
'#type' => 'markup',
'#markup' => t('Cron has been called within maximum lapse time.'),
);
return $aoutput;
} }
$aoutput = array();
$aoutput[] = array(
'#type' => 'markup',
'#markup' => t('Cron has been called within maximum lapse time.'),
);
return $aoutput;
} }
/******************************************************************************* /*******************************************************************************
@ -1842,6 +1866,9 @@ function elysia_cron_ping_page() {
* ONLY FOR D7 * ONLY FOR D7
******************************************************************************/ ******************************************************************************/
/**
* Implements hook_cronapi().
*/
function elysia_cron_cronapi($op, $job = FALSE) { function elysia_cron_cronapi($op, $job = FALSE) {
$items = array(); $items = array();
@ -1879,6 +1906,14 @@ function elysia_cron_cronapi($op, $job = FALSE) {
return $items; return $items;
} }
/**
* Callback for cronapi.
*
* @param string $queue_name
* Queue name.
* @param array $info
* Info about queue.
*/
function elysia_cron_queue_exec($queue_name, $info) { function elysia_cron_queue_exec($queue_name, $info) {
$function = $info['worker callback']; $function = $info['worker callback'];
$end = time() + (isset($info['time']) ? $info['time'] : 15); $end = time() + (isset($info['time']) ? $info['time'] : 15);
@ -1908,6 +1943,9 @@ function elysia_cron_queue_exec($queue_name, $info) {
* Array of links under admin/* that were removed by * Array of links under admin/* that were removed by
* admin_menu_adjust_items(). If one of these links is added back, * admin_menu_adjust_items(). If one of these links is added back,
* it should be removed from the array. * it should be removed from the array.
*
* @return array
* Links array.
*/ */
function elysia_cron_admin_menu(&$deleted) { function elysia_cron_admin_menu(&$deleted) {
$links = array(); $links = array();

View File

@ -9,8 +9,6 @@
* Function for cron run schedule. * Function for cron run schedule.
*/ */
function elysia_cron_should_run($conf, $now = -1, $ignore_disable = FALSE, $ignore_time = FALSE) { function elysia_cron_should_run($conf, $now = -1, $ignore_disable = FALSE, $ignore_time = FALSE) {
// What time SHOULD the job be executed last time.
$prev_rule_run = 0;
if (!$ignore_disable && $conf['disabled']) { if (!$ignore_disable && $conf['disabled']) {
return FALSE; return FALSE;
} }
@ -53,17 +51,17 @@ function _elysia_cron_next_run($conf) {
$rule = array($rules[1], $rules[2], array($rules[3], $rules[5]), $rules[4]); $rule = array($rules[1], $rules[2], array($rules[3], $rules[5]), $rules[4]);
$ruledec = array(); $ruledec = array();
$date = __cronDecodeDate($conf['last_run'], 1); $date = _cronDecodeDate($conf['last_run'], 1);
$expected_date = __cronDecodeDate(!empty($conf['last_run_expected']) ? $conf['last_run_expected'] : 0); $expected_date = _cronDecodeDate(!empty($conf['last_run_expected']) ? $conf['last_run_expected'] : 0);
for ($i = 0; $i < 4; $i++) { for ($i = 0; $i < 4; $i++) {
if ($i != 2) { if ($i != 2) {
// Standard scheme for mins, hours, month. // Standard scheme for mins, hours, month.
$ruledec[$i] = __cronDecodeRule($rule[$i], $ranges[$i][0], $ranges[$i][1]); $ruledec[$i] = _cronDecodeRule($rule[$i], $ranges[$i][0], $ranges[$i][1]);
} }
else { else {
// For mday+week we follow another scheme. // For mday+week we follow another scheme.
$ruledec[$i] = __cronDecodeRuleMday($rule[2], $date[3], $date[4]); $ruledec[$i] = _cronDecodeRuleMday($rule[2], $date[3], $date[4]);
} }
$r = $ruledec[$i]; $r = $ruledec[$i];
$new = $date[$i]; $new = $date[$i];
@ -74,7 +72,7 @@ function _elysia_cron_next_run($conf) {
$new = $expected_date[$i] + ceil(($date[$i] - $expected_date[$i]) / $r['d']) * $r['d']; $new = $expected_date[$i] + ceil(($date[$i] - $expected_date[$i]) / $r['d']) * $r['d'];
} }
elseif ($r['n']) { elseif ($r['n']) {
$new = __cronNextOrEqual($date[$i], $r['n'], $ranges[$i][0], $ranges[$i][1]); $new = _cronNextOrEqual($date[$i], $r['n'], $ranges[$i][0], $ranges[$i][1]);
} }
if ($new != $date[$i]) { if ($new != $date[$i]) {
$date[$i] = $new; $date[$i] = $new;
@ -85,7 +83,7 @@ function _elysia_cron_next_run($conf) {
for ($j = 0; $j < $i; $j++) { for ($j = 0; $j < $i; $j++) {
if ($j == 2) { if ($j == 2) {
// For mday+week decoded rule could be changed (by month+year) // For mday+week decoded rule could be changed (by month+year)
$ruledec[$j] = __cronDecodeRuleMday($rule[2], $date[3], $date[4]); $ruledec[$j] = _cronDecodeRuleMday($rule[2], $date[3], $date[4]);
} }
$date[$j] = $ruledec[$j]['d'] ? ($ranges[$j][0] == 0 ? 0 : $ruledec[$j]['d']) : ($ruledec[$j]['n'] ? reset($ruledec[$j]['n']) : $ranges[$j][0]); $date[$j] = $ruledec[$j]['d'] ? ($ranges[$j][0] == 0 ? 0 : $ruledec[$j]['d']) : ($ruledec[$j]['n'] ? reset($ruledec[$j]['n']) : $ranges[$j][0]);
$expected_date[$j] = 0; $expected_date[$j] = 0;
@ -93,13 +91,13 @@ function _elysia_cron_next_run($conf) {
} }
} }
return __cronEncodeDate($date); return _cronEncodeDate($date);
} }
/** /**
* Helper function for _elysia_cron_next_run(). * Helper function for _elysia_cron_next_run().
*/ */
function __cronDecodeDate($timestamp, $min_diff = 0) { function _cronDecodeDate($timestamp, $min_diff = 0) {
$time = floor($timestamp / 60); $time = floor($timestamp / 60);
$time += $min_diff; $time += $min_diff;
@ -117,14 +115,14 @@ function __cronDecodeDate($timestamp, $min_diff = 0) {
/** /**
* Helper function for _elysia_cron_next_run(). * Helper function for _elysia_cron_next_run().
*/ */
function __cronEncodeDate($date) { function _cronEncodeDate($date) {
return mktime($date[1], $date[0], 0, $date[3], $date[2], $date[4]); return mktime($date[1], $date[0], 0, $date[3], $date[2], $date[4]);
} }
/** /**
* Helper function for _elysia_cron_next_run(). * Helper function for _elysia_cron_next_run().
*/ */
function __cronNextOrEqual($el, $arr, $range_start, $range_end) { function _cronNextOrEqual($el, $arr, $range_start, $range_end) {
if (empty($arr)) { if (empty($arr)) {
return $el; return $el;
} }
@ -139,7 +137,7 @@ function __cronNextOrEqual($el, $arr, $range_start, $range_end) {
/** /**
* Helper function for _elysia_cron_next_run(). * Helper function for _elysia_cron_next_run().
*/ */
function __cronDecodeRule($rule, $min, $max) { function _cronDecodeRule($rule, $min, $max) {
if ($rule == '*') { if ($rule == '*') {
return array('n' => array(), 'd' => 0); return array('n' => array(), 'd' => 0);
} }
@ -163,11 +161,11 @@ function __cronDecodeRule($rule, $min, $max) {
/** /**
* Helper function for _elysia_cron_next_run(). * Helper function for _elysia_cron_next_run().
*/ */
function __cronDecodeRuleMday($rule, $month, $year) { function _cronDecodeRuleMday($rule, $month, $year) {
$range_from = 1; $range_from = 1;
$range_to = $month != 2 ? (in_array($month, array(4, 6, 9, 11)) ? 30 : 31) : ($year % 4 == 0 ? 29 : 28); $range_to = $month != 2 ? (in_array($month, array(4, 6, 9, 11)) ? 30 : 31) : ($year % 4 == 0 ? 29 : 28);
$r1 = __cronDecodeRule($rule[0], $range_from, $range_to); $r1 = _cronDecodeRule($rule[0], $range_from, $range_to);
$r2 = __cronDecodeRule($rule[1], $range_from, $range_to); $r2 = _cronDecodeRule($rule[1], $range_from, $range_to);
if ($r2['d']) { if ($r2['d']) {
for ($i = 0; $i < 7; $i++) { for ($i = 0; $i < 7; $i++) {
if ($i % $r2['d'] == 0) { if ($i % $r2['d'] == 0) {
@ -178,7 +176,7 @@ function __cronDecodeRuleMday($rule, $month, $year) {
if ($r2['n']) { if ($r2['n']) {
$r2['n'] = array_unique($r2['n']); $r2['n'] = array_unique($r2['n']);
// Use always "31" and not $range_to, see http://drupal.org/node/1668302. // Use always "31" and not $range_to, see http://drupal.org/node/1668302.
$r1['n'] = array_merge($r1['n'], __cronMonDaysFromWeekDays($year, $month, $r2['n']), __cronMonDaysFromWeekDays($year, $month + 1, $r2['n'], 31)); $r1['n'] = array_merge($r1['n'], _cronMonDaysFromWeekDays($year, $month, $r2['n']), _cronMonDaysFromWeekDays($year, $month + 1, $r2['n'], 31));
} }
return $r1; return $r1;
} }
@ -186,7 +184,7 @@ function __cronDecodeRuleMday($rule, $month, $year) {
/** /**
* Helper function for _elysia_cron_next_run(). * Helper function for _elysia_cron_next_run().
*/ */
function __cronMonDaysFromWeekDays($year, $mon, $weekdays, $offset = 0) { function _cronMonDaysFromWeekDays($year, $mon, $weekdays, $offset = 0) {
if ($mon > 12) { if ($mon > 12) {
$year++; $year++;
$mon = $mon - 12; $mon = $mon - 12;
@ -350,15 +348,15 @@ function test_elysia_cron_should_run() {
'last_run' => mktime(23, 59, 0, 2, 29, 2008), 'last_run' => mktime(23, 59, 0, 2, 29, 2008),
), mktime(23, 59, 0, 2, 6, 2009)))); ), mktime(23, 59, 0, 2, 6, 2009))));
dprint("34." . (TRUE == elysia_cron_should_run(array( dprint("34." . (TRUE == elysia_cron_should_run(array(
'rule' => '59 23 *' . '/10 * *', 'rule' => '59 23 */10 * *',
'last_run' => mktime(23, 58, 0, 1, 10, 2008), 'last_run' => mktime(23, 58, 0, 1, 10, 2008),
), mktime(23, 59, 0, 1, 10, 2008)))); ), mktime(23, 59, 0, 1, 10, 2008))));
dprint("35." . (FALSE == elysia_cron_should_run(array( dprint("35." . (FALSE == elysia_cron_should_run(array(
'rule' => '59 23 *' . '/10 * *', 'rule' => '59 23 */10 * *',
'last_run' => mktime(23, 59, 0, 1, 10, 2008), 'last_run' => mktime(23, 59, 0, 1, 10, 2008),
), mktime(23, 59, 0, 1, 11, 2008)))); ), mktime(23, 59, 0, 1, 11, 2008))));
dprint("36." . (TRUE == elysia_cron_should_run(array( dprint("36." . (TRUE == elysia_cron_should_run(array(
'rule' => '59 23 *' . '/10 * *', 'rule' => '59 23 */10 * *',
'last_run' => mktime(23, 59, 0, 1, 10, 2008), 'last_run' => mktime(23, 59, 0, 1, 10, 2008),
), mktime(23, 59, 0, 1, 20, 2008)))); ), mktime(23, 59, 0, 1, 20, 2008))));
dprint("37." . (TRUE == elysia_cron_should_run(array( dprint("37." . (TRUE == elysia_cron_should_run(array(

View File

@ -0,0 +1,16 @@
(function ($) {
Drupal.behaviors.elysiaCron = {
attach: function (context, settings) {
$('.ec-select').once().change(function () {
if (this.value === 'custom') {
var key = $(this).data('key');
$("#_ec_select_" + key).hide();
$("#_ec_custom_" + key).show();
}
});
}
}
})(jQuery);

View File

@ -1,4 +1,21 @@
Libraries 7.x-2.5, 2018-10-5
-----------------------------
#2815965 by plach, Manav, tstoeckler, Proteo: Base theme is not loaded when checking for theme library info
#2999116 by Anghelu, joshbrown81: Finds no library after update
Libraries 7.x-2.4, 2018-09-10
-----------------------------
#2779591 by mark_fullmer, cglauren, improved PHP 7.x.x support.
#2699799 by flaviovs, hanoii: Support reading version from package.json.
#2816781 by joelstein: Add a 'access library reports' permission.
#2823735 by amanaplan, tstoeckler: Add admin_menu cache clear integration.
#2745763 by Albert Volkman, tstoeckler: Allow downloading all libraries at once.
#2310753 by tstoeckler: Avoid libraries_get_libraries() scanning the root.
#2341955 by sadashiv, tstoeckler: Clear library cache on library report.
#819610 by tstoeckler: Show variants and dependencies in the UI.
#2724925 by ron_s, tstoeckler: Separate installed from uninstalled libraries.
Libraries 7.x-2.3, 2016-05-12 Libraries 7.x-2.3, 2016-05-12
----------------------------- -----------------------------
#1884246 by BR0kEN, tstoeckler et al: Allow downloading libraries via Drush. #1884246 by BR0kEN, tstoeckler et al: Allow downloading libraries via Drush.
@ -94,17 +111,11 @@ by sun: Fixed testbot breaks upon .info file without .module file.
#719896 by tstoeckler, sun: Added starting point for hook_libraries_info(). #719896 by tstoeckler, sun: Added starting point for hook_libraries_info().
Libraries 7.x-1.x, xxxx-xx-xx
-----------------------------
Libraries 7.x-1.0, 2010-01-27 Libraries 7.x-1.0, 2010-01-27
----------------------------- -----------------------------
#743522 by sun: Ported to D7. #743522 by sun: Ported to D7.
Libraries 6.x-1.x, xxxx-xx-xx
-----------------------------
Libraries 6.x-1.0, 2010-01-27 Libraries 6.x-1.0, 2010-01-27
----------------------------- -----------------------------
#1028744 by tstoeckler: Code clean-up. #1028744 by tstoeckler: Code clean-up.

View File

@ -0,0 +1,3 @@
.libraries-table {
margin-bottom: 2em;
}

View File

@ -20,41 +20,112 @@
* The form array for the overview form. * The form array for the overview form.
*/ */
function libraries_admin_overview(array $form, array &$form_state) { function libraries_admin_overview(array $form, array &$form_state) {
$header = array(t('Name'), t('Status'), t('Installed version'), t('Provider'), t('Links')); // Only show variants for installed libraries.
$rows = array(); $header_installed = array(t('Name'), t('Version'), t('Variants'), t('Dependencies'), t('Provider'), t('Links'));
// Only show status for libraries with an error.
$header_error = array(t('Name'), t('Status'), t('Version'), t('Dependencies'), t('Provider'), t('Links'));
// For unregistered libraries the only information we can show is the path.
$header_unregistered = array(t('Name'), t('Path'));
$libraries = libraries_detect(); $rows_installed = array();
uasort($libraries, 'libraries_admin_sort_title'); $rows_error = array();
$rows_unregistered = array();
foreach ($libraries as $machine_name => $library) { // Registered libraries: we prefer to use libraries_detect() since it provides
// library metadata.
$libraries_registered = libraries_detect();
uasort($libraries_registered, 'libraries_admin_sort_title');
// Unregistered libraries: modules can depend on Libraries API without sharing
// metadata by using libraries_get_path(). Libraries can also be placed in the
// filesystem that are incorrectly installed, a wrong version, or a standalone
// not connected to any module. In these cases, libraries_get_libraries()
// provides a full library list. Libraries found by libraries_get_libraries(),
// but not identified by libraries_detect, are displayed in a separate table.
$libraries_unregistered = libraries_get_libraries();
natcasesort($libraries_unregistered);
foreach ($libraries_registered as $machine_name => $library) {
$actions = array(); $actions = array();
$row = array();
if ($library['vendor url']) { if ($library['vendor url']) {
$actions[] = l('Homepage', $library['vendor url']); $actions[] = l(t('Homepage'), $library['vendor url']);
} }
if ($library['download url']) { if ($library['download url']) {
$actions[] = l('Download', $library['download url']); $actions[] = l(t('Download'), $library['download url']);
} }
$rows[] = array( $row['data'][] = l($library['name'], 'admin/reports/libraries/' . $machine_name);
'data' => array( // Only show status for libraries with an error. See above.
l($library['name'], 'admin/reports/libraries/' . $machine_name), if (!$library['installed']) {
($library['installed'] ? t('OK') : drupal_ucfirst($library['error'])), $row['data'][] = drupal_ucfirst($library['error']);
(isset($library['version']) ? $library['version'] : ''), }
libraries_admin_get_provider_with_type($library), $row['data'][] = isset($library['version']) ? $library['version'] : '';
implode(' | ', $actions), if ($library['installed']) {
), $row['data'][] = implode(', ', array_keys($library['variants']));
'class' => ($library['installed'] ? array('ok') : array('error')), }
); $row['data'][] = libraries_admin_get_dependencies($library);
$row['data'][] = libraries_admin_get_provider_with_type($library);
$row['data'][] = implode(' | ', $actions);
$row['class'] = $library['installed'] ? array('ok') : array('warning');
if ($library['installed']) {
$rows_installed[] = $row;
}
else {
$rows_error[] = $row;
}
// Filter registered libraries from unregistered libraries.
unset($libraries_unregistered[$library['machine name']]);
} }
$form['libraries']['list'] = array( // Build table of registered libraries with installed status.
'#theme' => 'table', $form['libraries']['installed'] = array(
'#header' => $header, '#theme' => 'libraries_table_with_title',
'#rows' => $rows, '#title' => t('Installed'),
'#empty' => t('There are currently no libraries installed'), '#header' => $header_installed,
'#rows' => $rows_installed,
'#description' => t('These libraries are registered and installed correctly.'),
'#empty' => t('There are currently no libraries that are registered and installed.'),
); );
// Build table of registered libraries with error status.
$form['libraries']['error'] = array(
'#theme' => 'libraries_table_with_title',
'#title' => t('Uninstalled'),
'#header' => $header_error,
'#rows' => $rows_error,
'#description' => t('These libraries are registered but not installed. They may not need to be installed in case a module or theme provides optional integration with a library.'),
'#empty' => t('There are currently no libraries that are registered but not installed.'),
);
// Build table of unregistered libraries.
foreach ($libraries_unregistered as $name => $path) {
$rows_unregistered[] = array(
'data' => array(
$name,
$path,
),
);
}
$form['libraries']['unregistered'] = array(
'#theme' => 'libraries_table_with_title',
'#title' => t('Unregistered'),
'#header' => $header_unregistered,
'#rows' => $rows_unregistered,
'#description' => t('These libraries were found in the filesystem but there is no metadata about them.'),
// Do not show the table at all, if there are no unregistered libraries.
'#access' => (bool) $libraries_unregistered,
);
// Clear the cached library information so that the library can be loaded if
// it was just downloaded. Because these instructions use libraries_detect()
// directly, they will never use the cached information, but this avoids the
// overview showing a library as installed but it not being loadable.
libraries_cache_clear();
return $form; return $form;
} }
@ -99,11 +170,11 @@ function libraries_admin_library_status_form(array $form, array &$form_state, $l
break; break;
case 'missing dependency': case 'missing dependency':
$form['instructions']['instruction']['#markup'] = t('There a missing dependency in your configuration that prevent this library to work properly.') . '<br>'; $form['instructions']['instruction']['#markup'] = t('There is a missing dependency in your configuration that prevents this library from working properly.') . '<br>';
break; break;
case 'incompatible dependency': case 'incompatible dependency':
$form['instructions']['instruction']['#markup'] = t('There an incompatible dependency in your configuration that prevent this library to work properly.') . '<br>'; $form['instructions']['instruction']['#markup'] = t('There is an incompatible dependency in your configuration that prevents this library from working properly.') . '<br>';
break; break;
} }
} }
@ -483,6 +554,28 @@ function libraries_admin_sort_title(array $a, array $b) {
return strnatcasecmp($a['name'], $b['name']); return strnatcasecmp($a['name'], $b['name']);
} }
/**
* Returns the library's dependencies, if any.
*
* @param array $library
* A library information array.
*
* @return string
* The dependencies.
*/
function libraries_admin_get_dependencies($library) {
$dependencies = array();
foreach ($library['dependencies'] as $dependency_name) {
if ($dependency = libraries_info($dependency_name)) {
$dependencies[] = $dependency['name'];
}
else {
$dependencies[] = $dependency_name;
}
}
return implode(', ', $dependencies);
}
/** /**
* Returns the library's provider. * Returns the library's provider.
* *

View File

@ -47,6 +47,8 @@
* Unless 'version' is declared or libraries_get_version() is being used as * Unless 'version' is declared or libraries_get_version() is being used as
* a version callback, 'version callback' must be declared. In the latter * a version callback, 'version callback' must be declared. In the latter
* case, however, 'version arguments' must be declared in the specified way. * case, however, 'version arguments' must be declared in the specified way.
* For libraries that provide a package.json file, use
* 'libraries_get_package_json_version' as the version callback.
* - version arguments: (optional) A list of arguments to pass to the version * - version arguments: (optional) A list of arguments to pass to the version
* callback. Version arguments can be declared either as an associative * callback. Version arguments can be declared either as an associative
* array whose keys are the argument names or as an indexed array without * array whose keys are the argument names or as an indexed array without

View File

@ -23,7 +23,9 @@ function libraries_drush_command() {
'arguments' => array( 'arguments' => array(
'libraries' => 'A comma delimited list of library machine names.', 'libraries' => 'A comma delimited list of library machine names.',
), ),
'required-arguments' => TRUE, 'options' => array(
'all' => 'Download all registered libraries.',
),
); );
return $items; return $items;
@ -42,10 +44,7 @@ function libraries_drush_cache_clear(array &$types) {
* Clears the library cache. * Clears the library cache.
*/ */
function libraries_drush_invalidate_cache() { function libraries_drush_invalidate_cache() {
// @see drupal_flush_all_caches() libraries_cache_clear();
foreach (libraries_flush_caches() as $table) {
cache_clear_all('*', $table, TRUE);
}
} }
/** /**
@ -109,28 +108,61 @@ function drush_libraries_list() {
function drush_libraries_download() { function drush_libraries_download() {
drush_command_include('pm-download'); drush_command_include('pm-download');
$libraries = libraries_info(); $all_libraries = libraries_detect();
// @todo Consider supporting downloading all downloadable libraries. // Prepare a list of names of downloadable libraries.
// @todo Consider offering a selection if no library is specified. $downloadable_names = array();
foreach (pm_parse_arguments(func_get_args(), FALSE) as $machine_name) { foreach ($all_libraries as $machine_name => $library) {
if (!isset($libraries[$machine_name])) { // Skip libraries that are already installed.
$message = dt("The !library library is not registered with Libraries API.\n", array('!library' => $machine_name)); // @todo Allow (optionally) re-downloading installing libraries.
$message .= dt("Provide an info file for it or implement hook_libraries_info().\n"); if (!empty($library['download file url']) && !$library['installed']) {
$message .= dt("See hook_libraries_info() for more information.\n"); $downloadable_names[] = $machine_name;
drush_set_error('DRUSH_LIBRARY_UKNOWN', $message);
continue;
} }
$library = $libraries[$machine_name]; }
if (empty($library['download file url'])) { // Gather a list of libraries to download. If '--all' was specified, that
$message = dt("The !library library cannot be downloaded.\n", array('!library' => $machine_name)); // takes precedence over any other arguments. Otherwise and if no arguments
$message .= dt("Libraries need to specify a download file URL to support being downloaded via Drush.\n"); // are specified, we present a choice of all downloadable libraries.
$message .= dt("See hook_libraries_info() for more information.\n"); if (drush_get_option('all', FALSE) && $downloadable_names) {
drush_set_error('DRUSH_LIBRARY_NOT_DOWNLOADABLE', $message); $machine_names = $downloadable_names;
continue; }
elseif (pm_parse_arguments(func_get_args(), FALSE)) {
$machine_names = array();
foreach (pm_parse_arguments(func_get_args(), FALSE) as $machine_name) {
// If there was an error with with one of the libraries, continue to try
// to install any remaining libraries.
if (!isset($all_libraries[$machine_name])) {
$message = dt("The !library library is not registered with Libraries API.\n", array('!library' => $machine_name));
$message .= dt("Provide an info file for it or implement hook_libraries_info().\n");
$message .= dt("See hook_libraries_info() for more information.\n");
drush_set_error('DRUSH_LIBRARY_UKNOWN', $message);
continue;
}
if (empty($all_libraries[$machine_name]['download file url'])) {
$message = dt("The !library library cannot be downloaded.\n", array('!library' => $machine_name));
$message .= dt("Libraries need to specify a download file URL to support being downloaded via Drush.\n");
$message .= dt("See hook_libraries_info() for more information.\n");
drush_set_error('DRUSH_LIBRARY_NOT_DOWNLOADABLE', $message);
continue;
}
$machine_names[] = $machine_name;
} }
$download_url = $library['download file url']; }
elseif ($downloadable_names) {
$machine_names = drush_choice_multiple(drupal_map_assoc($downloadable_names), FALSE, 'Select which libraries to download.');
// If the operation was cancelled by the user, or if no libraries were
// selected, bail out without any further error message.
if (!$machine_names) {
return;
}
}
else {
drush_log(dt('There are no registered, uninstalled libraries that can be downloaded.'), 'warning');
return;
}
foreach ($machine_names as $machine_name) {
$download_url = $all_libraries[$machine_name]['download file url'];
drush_log(dt('Downloading library !name ...', array('!name' => $machine_name))); drush_log(dt('Downloading library !name ...', array('!name' => $machine_name)));
@ -205,7 +237,7 @@ function drush_libraries_download() {
drush_delete_dir($install_location, TRUE); drush_delete_dir($install_location, TRUE);
} }
else { else {
drush_log(dt("Skip installation of !project to !dest.", array('!project' => $library['machine name'], '!dest' => $install_location)), 'warning'); drush_log(dt("Skip installation of !project to !dest.", array('!project' => $machine_name, '!dest' => $install_location)), 'warning');
continue; continue;
} }
} }

View File

@ -8,9 +8,8 @@ files[] = tests/LibrariesLoadWebTest.test
files[] = tests/LibrariesUnitTest.test files[] = tests/LibrariesUnitTest.test
files[] = tests/LibrariesWebTestBase.test files[] = tests/LibrariesWebTestBase.test
; Information added by Drupal.org packaging script on 2016-05-12 ; Information added by Drupal.org packaging script on 2018-10-05
version = "7.x-2.3" version = "7.x-2.5"
core = "7.x" core = "7.x"
project = "libraries" project = "libraries"
datestamp = "1463077450" datestamp = "1538770685"

View File

@ -34,3 +34,13 @@ function libraries_update_7201() {
// during the 7.x-2.x cycle. // during the 7.x-2.x cycle.
registry_rebuild(); registry_rebuild();
} }
/**
* Grant the "View library reports" permission to roles with the "View site reports" permission.
*/
function libraries_update_7202() {
$rids = array_keys(user_roles(FALSE, 'access site reports'));
foreach ($rids as $rid) {
_update_7000_user_role_grant_permissions($rid, array('access library reports'), 'libraries');
}
}

View File

@ -25,6 +25,29 @@ function libraries_flush_caches() {
} }
} }
/**
* Implements hook_admin_menu_cache_info().
*/
function libraries_admin_menu_cache_info() {
$caches['libraries'] = array(
'title' => t('Libraries'),
'callback' => 'libraries_cache_clear',
);
return $caches;
}
/**
* Clears the cached library information.
*/
function libraries_cache_clear() {
foreach (libraries_flush_caches() as $bin) {
// Using the wildcard argument leads to DrupalDatabaseCache::clear()
// truncating the libraries cache table which is more performant that
// deleting the rows.
cache_clear_all('*', $bin, TRUE);
}
}
/** /**
* Gets the path of a library. * Gets the path of a library.
* *
@ -56,6 +79,52 @@ function libraries_get_path($name, $base_path = FALSE) {
return $path; return $path;
} }
/**
* Returns all enabled themes.
*
* Themes are sorted so that base themes always precede their child themes.
*
* @return array
* An associative array of theme objects keyed by theme name.
*/
function libraries_get_enabled_themes() {
$themes = array();
foreach (list_themes() as $name => $theme) {
if ($theme->status) {
$themes[$name] = $theme;
}
}
return libraries_sort_themes($themes);
}
/**
* Sort a themes array.
*
* @param array $themes
* Array of themes as objects, keyed by theme name.
* @param string $base
* A base theme (internal use only).
*
* @return array
* A similar array to $themes, but sorted in such a way that subthemes are
* always located after its base theme.
*/
function libraries_sort_themes($themes, $base = '') {
$output = array();
foreach ($themes as $name => $theme) {
if (!isset($theme->base_theme) || $theme->base_theme == $base) {
$output[$name] = $theme;
unset($themes[$name]);
$subthemes = libraries_sort_themes($themes, $name);
foreach ($subthemes as $sub_name => $subtheme) {
$output[$sub_name] = $subtheme;
}
}
}
return $output;
}
/** /**
* Returns an array of library directories. * Returns an array of library directories.
* *
@ -77,6 +146,13 @@ function libraries_get_libraries() {
$profile = drupal_get_path('profile', drupal_get_profile()); $profile = drupal_get_path('profile', drupal_get_profile());
$config = conf_path(); $config = conf_path();
// $config and $profile should never be empty in a proper Drupal setup.
// However, we should never search into the root filesystem under any
// circumstances, so just bail out in that case.
if (!$profile && !$config) {
return array();
}
// Similar to 'modules' and 'themes' directories in the root directory, // Similar to 'modules' and 'themes' directories in the root directory,
// certain distributions may want to place libraries into a 'libraries' // certain distributions may want to place libraries into a 'libraries'
// directory in Drupal's root directory. // directory in Drupal's root directory.
@ -358,10 +434,12 @@ function &libraries_info($name = NULL) {
} }
} }
// Gather information from hook_libraries_info() in enabled themes. // Gather information from hook_libraries_info() in enabled themes. Themes
// are sorted to ensure that a base theme's template.php is included before
// its children's ones.
$themes = array(); $themes = array();
foreach (list_themes() as $theme_name => $theme_info) { foreach (libraries_get_enabled_themes() as $theme_name => $theme_info) {
if ($theme_info->status && file_exists(drupal_get_path('theme', $theme_name) . '/template.php')) { if (file_exists(drupal_get_path('theme', $theme_name) . '/template.php')) {
// Collect a list of viable themes for re-use when calling the alter // Collect a list of viable themes for re-use when calling the alter
// hook. // hook.
$themes[] = $theme_name; $themes[] = $theme_name;
@ -546,7 +624,7 @@ function libraries_detect($name = NULL) {
$library['version'] = call_user_func_array($library['version callback'], array_merge(array($library), $library['version arguments'])); $library['version'] = call_user_func_array($library['version callback'], array_merge(array($library), $library['version arguments']));
} }
else { else {
$library['version'] = call_user_func($library['version callback'], $library, $library['version arguments']); $library['version'] = call_user_func_array($library['version callback'], array(&$library, $library['version arguments']));
} }
if (empty($library['version'])) { if (empty($library['version'])) {
$library['error'] = 'not detected'; $library['error'] = 'not detected';
@ -887,16 +965,56 @@ function libraries_get_version($library, $options) {
fclose($file); fclose($file);
} }
/**
* Gets the version information from a library's package.json file.
*
* @param $library
* An associative array containing all information about the library.
* @param $options
* This callback expects no option.
* @return
* A string containing the version of the library.
*
* @see libraries_get_path()
*/
function libraries_get_package_json_version($library, $options) {
$file = DRUPAL_ROOT . '/' . $library['library path'] . '/package.json';
if (!file_exists($file)) {
return;
}
$content = file_get_contents($file);
if (!$content) {
return;
}
$data = drupal_json_decode($content);
if (isset($data['version'])) {
return $data['version'];
}
}
/** /**
* Implements hook_help(). * Implements hook_help().
*/ */
function libraries_help($path, $arg) { function libraries_help($path, $arg) {
switch ($path) { switch ($path) {
case 'admin/reports/libraries': case 'admin/reports/libraries':
return t('Click on a library for a status report or detailed installation instructions in case the library is not installed correctly.'); return t('Click on a library for a status report or detailed installation instructions.');
} }
} }
/**
* Implements hook_permission().
*/
function libraries_permission() {
return array(
'access library reports' => array(
'title' => t('View library reports'),
),
);
}
/** /**
* Implements hook_menu(). * Implements hook_menu().
*/ */
@ -907,7 +1025,7 @@ function libraries_menu() {
'description' => 'An overview of libraries installed on this site.', 'description' => 'An overview of libraries installed on this site.',
'page callback' => 'drupal_get_form', 'page callback' => 'drupal_get_form',
'page arguments' => array('libraries_admin_overview'), 'page arguments' => array('libraries_admin_overview'),
'access arguments' => array('access site reports'), 'access arguments' => array('access library reports'),
'file' => 'libraries.admin.inc' 'file' => 'libraries.admin.inc'
); );
$items['admin/reports/libraries/%libraries_ui'] = array( $items['admin/reports/libraries/%libraries_ui'] = array(
@ -915,7 +1033,7 @@ function libraries_menu() {
'description' => 'Status overview for a single library', 'description' => 'Status overview for a single library',
'page callback' => 'drupal_get_form', 'page callback' => 'drupal_get_form',
'page arguments' => array('libraries_admin_library_status_form', 3), 'page arguments' => array('libraries_admin_library_status_form', 3),
'access arguments' => array('access site reports'), 'access arguments' => array('access library reports'),
'file' => 'libraries.admin.inc' 'file' => 'libraries.admin.inc'
); );
return $items; return $items;
@ -944,3 +1062,19 @@ function libraries_menu() {
function libraries_ui_load($name) { function libraries_ui_load($name) {
return libraries_detect($name); return libraries_detect($name);
} }
/**
* Implements hook_theme().
*/
function libraries_theme($existing, $type, $theme, $path) {
// Because we extend the 'table' theme function, fetch the respective
// variables dynamically.
$common_theme = drupal_common_theme();
$variables = $common_theme['table']['variables'] + array('title' => '', 'description' => '');
return array(
'libraries_table_with_title' => array(
'variables' => $variables,
'file' => 'libraries.theme.inc',
),
);
}

View File

@ -0,0 +1,36 @@
<?php
/**
* @file
* Provides theme and preprocess functions for Libraries API.
*/
/**
* Prepare variables for theming a table with a title.
*
* @param array $variables
* An array of theme variables, passed by reference.
*/
function template_preprocess_libraries_table_with_title(&$variables) {
drupal_add_css(drupal_get_path('module', 'libraries') . '/css/libraries.admin.css');
$variables['attributes'] += array('class' => array());
$variables['attributes']['class'][] = 'libraries-table';
}
/**
* Returns HTML for a table with a title.
*
* @param array $variables
* An array theme variables.
*
* @return string
* The HTML output for this table with a title.
*/
function theme_libraries_table_with_title(array $variables) {
$output = '';
$output .= '<h2>' . $variables['title'] . '</h2>';
$output .= '<div class="description">' . $variables['description'] . '</div>';
$output .= theme_table($variables);
return $output;
}

View File

@ -40,8 +40,20 @@ class LibrariesAdminWebTest extends LibrariesWebTestBase {
* Tests the libraries report at /admin/reports/libraries. * Tests the libraries report at /admin/reports/libraries.
*/ */
public function testLibrariesReportOverview() { public function testLibrariesReportOverview() {
$this->getWithPermissions(array('access site reports'), 'admin/reports/libraries'); $this->getWithPermissions(array('access library reports'), 'admin/reports/libraries');
$this->assertRaw('Libraries'); // Assert the page title and table titles show up.
$this->assertText('Libraries');
$this->assertRaw('<h2>Installed</h2>');
$this->assertRaw('<h2>Uninstalled</h2>');
// Make sure the table headings show up.
$this->assertText('Name');
$this->assertText('Status');
$this->assertText('Version');
$this->assertText('Variants');
$this->assertText('Dependencies');
$this->assertText('Provider');
$this->assertText('Links');
// Make sure that all the libraries are listed. // Make sure that all the libraries are listed.
$libraries = libraries_info(); $libraries = libraries_info();
@ -51,8 +63,7 @@ class LibrariesAdminWebTest extends LibrariesWebTestBase {
$this->assertLinkByHref('admin/reports/libraries/' . $library['machine name']); $this->assertLinkByHref('admin/reports/libraries/' . $library['machine name']);
} }
// Make sure that all possible statuses are displayed. // Make sure that all possible error statuses are displayed.
$this->assertText('OK');
$this->assertText('Not found'); $this->assertText('Not found');
$this->assertText('Not detected'); $this->assertText('Not detected');
$this->assertText('Not supported'); $this->assertText('Not supported');
@ -73,7 +84,7 @@ class LibrariesAdminWebTest extends LibrariesWebTestBase {
* Tests the libraries report for an installed library. * Tests the libraries report for an installed library.
*/ */
public function testLibrariesReportInstalled() { public function testLibrariesReportInstalled() {
$this->getWithPermissions(array('access site reports'), 'admin/reports/libraries/example_files'); $this->getWithPermissions(array('access library reports'), 'admin/reports/libraries/example_files');
$this->assertRaw('Status report for library <em class="placeholder">Example files</em>'); $this->assertRaw('Status report for library <em class="placeholder">Example files</em>');
$this->assertRaw('The <em class="placeholder">Example files</em> library is installed correctly.'); $this->assertRaw('The <em class="placeholder">Example files</em> library is installed correctly.');
// Check that the information in the status report is displayed. // Check that the information in the status report is displayed.
@ -88,7 +99,7 @@ class LibrariesAdminWebTest extends LibrariesWebTestBase {
* Tests the libraries report for a missing library. * Tests the libraries report for a missing library.
*/ */
public function testLibrariesReportMissing() { public function testLibrariesReportMissing() {
$this->getWithPermissions(array('access site reports'), 'admin/reports/libraries/example_missing'); $this->getWithPermissions(array('access library reports'), 'admin/reports/libraries/example_missing');
$this->assertRaw('Status report for library <em class="placeholder">Example missing</em>'); $this->assertRaw('Status report for library <em class="placeholder">Example missing</em>');
$this->assertRaw('The <em class="placeholder">Example missing</em> library could not be found.'); $this->assertRaw('The <em class="placeholder">Example missing</em> library could not be found.');
// Check that the download link is being displayed. // Check that the download link is being displayed.
@ -100,7 +111,7 @@ class LibrariesAdminWebTest extends LibrariesWebTestBase {
* Tests the libraries report for a missing library. * Tests the libraries report for a missing library.
*/ */
public function testLibrariesReportNotDetected() { public function testLibrariesReportNotDetected() {
$this->getWithPermissions(array('access site reports'), 'admin/reports/libraries/example_undetected_version'); $this->getWithPermissions(array('access library reports'), 'admin/reports/libraries/example_undetected_version');
$this->assertRaw('Status report for library <em class="placeholder">Example undetected version</em>'); $this->assertRaw('Status report for library <em class="placeholder">Example undetected version</em>');
$this->assertRaw('The version of the <em class="placeholder">Example undetected version</em> library could not be detected.'); $this->assertRaw('The version of the <em class="placeholder">Example undetected version</em> library could not be detected.');
} }
@ -109,7 +120,7 @@ class LibrariesAdminWebTest extends LibrariesWebTestBase {
* Tests the libraries report for a missing library. * Tests the libraries report for a missing library.
*/ */
public function testLibrariesReportNotSupported() { public function testLibrariesReportNotSupported() {
$this->getWithPermissions(array('access site reports'), 'admin/reports/libraries/example_unsupported_version'); $this->getWithPermissions(array('access library reports'), 'admin/reports/libraries/example_unsupported_version');
$this->assertRaw('Status report for library <em class="placeholder">Example unsupported version</em>'); $this->assertRaw('Status report for library <em class="placeholder">Example unsupported version</em>');
$this->assertRaw('The installed version <em class="placeholder">1</em> of the <em class="placeholder">Example unsupported version</em> library is not supported.'); $this->assertRaw('The installed version <em class="placeholder">1</em> of the <em class="placeholder">Example unsupported version</em> library is not supported.');
// Check that the download link is being displayed. // Check that the download link is being displayed.

View File

@ -2,9 +2,8 @@
name = Example info file name = Example info file
; Information added by Drupal.org packaging script on 2016-05-12 ; Information added by Drupal.org packaging script on 2018-10-05
version = "7.x-2.3" version = "7.x-2.5"
core = "7.x" core = "7.x"
project = "libraries" project = "libraries"
datestamp = "1463077450" datestamp = "1538770685"

View File

@ -5,9 +5,8 @@ package = Testing
dependencies[] = libraries dependencies[] = libraries
hidden = TRUE hidden = TRUE
; Information added by Drupal.org packaging script on 2016-05-12 ; Information added by Drupal.org packaging script on 2018-10-05
version = "7.x-2.3" version = "7.x-2.5"
core = "7.x" core = "7.x"
project = "libraries" project = "libraries"
datestamp = "1463077450" datestamp = "1538770685"

View File

@ -3,9 +3,8 @@ description = Tests that themes can provide and alter library information.
core = 7.x core = 7.x
hidden = TRUE hidden = TRUE
; Information added by Drupal.org packaging script on 2016-05-12 ; Information added by Drupal.org packaging script on 2018-10-05
version = "7.x-2.3" version = "7.x-2.5"
core = "7.x" core = "7.x"
project = "libraries" project = "libraries"
datestamp = "1463077450" datestamp = "1538770685"

View File

@ -1,6 +1,6 @@
2.x-dev 2.x-dev
------- -------
- Now requires elFinder 2.0.9+ - Now requires elFinder 2.0.9+ or 2.1.38+
- Added multiple root directories support - Added multiple root directories support
- Added Drupal file_managed table synchronization - Added Drupal file_managed table synchronization
- Added per-role configurable profiles support - Added per-role configurable profiles support
@ -8,4 +8,7 @@
- Added CKEditor and FCKeditor Upload tab support - Added CKEditor and FCKeditor Upload tab support
- New library search algoritm with install profiles and multiple sites support - New library search algoritm with install profiles and multiple sites support
- Search support - Search support
- Inline preview support for PSD, md, html, office docs (by Google/Microsoft online tools), CAD (by sharecad.org online tools), etc (disabled by default, be careful)
- Auto fit window
- File owner available in list view

View File

@ -1,339 +1,14 @@
GNU GENERAL PUBLIC LICENSE elFinder Integration
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
The licenses for most software are designed to take away your 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid 3. Neither the name of the Alexey Sukhotin nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL "ALEXEY SUKHOTIN" OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
gratis or for a fee, you must give the recipients all the rights that LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -1,83 +1,79 @@
elFinder file manager integration module for Drupal elFinder file manager integration module for Drupal
=================================================== ===================================================
System Requirements: System Requirements:
- Drupal 7.x - Drupal 7.x
- elFinder 2.0.9+ - elFinder 2.0.9+ or 2.1.38+
Note: elFinder 2.0rc1 no more supported - use 2.x dev build from Note: elFinder 2.0rc1 no more supported - use 2.0.9+ or 2.1.38+ dev build from
http://sourceforge.net/projects/drupal-elfinder/files/Library/ http://elfinder.org
or use production version if it is available or use production version if it is available
Optional: Optional:
- Wysiwyg module - Wysiwyg module
- Wysiwyg editor: - Wysiwyg editor:
- CKEditor - CKEditor
- FCKeditor - FCKeditor
- TinyMCE 3 - TinyMCE 3
- BUEdtor - BUEdtor
- jWYSIWYG (jWYSIWYG 0.93+, jquery 1.3+, jquery ui 1.7+) - jWYSIWYG (jWYSIWYG 0.93+, jquery 1.3+, jquery ui 1.7+)
- YUI Editor - YUI Editor
- WYMeditor - WYMeditor
Installation: Installation:
1. Unpack archive contents into modules/elfinder 1. Unpack archive contents into modules/elfinder
2. Get latest elFinder at http://sourceforge.net/projects/drupal-elfinder/files/Library/ and it's contents to sites/all/libraries/elfinder 2. Get latest elFinder at http://elfinder.org and it's contents to sites/all/libraries/elfinder
3. REMOVE following files from library directory if it exists to avoid security hole: 3. REMOVE following files from library directory if it exists to avoid security hole:
sites/all/libraries/elfinder/elfinder.php.html sites/all/libraries/elfinder/connectors/php/connector.php
sites/all/libraries/elfinder/connectors/php/connector.php sites/all/libraries/elfinder/php/connector.php
sites/all/libraries/elfinder/php/connector.php
4. Enable elFinder module in Modules > List menu
4. Enable elFinder module in Modules > List menu 5. Add 'use file manager' permission to users who will be able to use elFinder at Modules > elFinder > Permissions menu
5. Add 'use file manager' permission to users who will be able to use elFinder at Modules > elFinder > Permissions menu 6. Enable elFinder checkbox in your editor Wysiwyg profile at Configuration > Wysiwyg profiles > <Editor Profile> Buttons and Plugins
6. Enable elFinder checkbox in your editor Wysiwyg profile at Configuration > Wysiwyg profiles > <Editor Profile> Buttons and Plugins
Usage:
Usage:
1. Administration backend
1. Administration backend a. Open /elfinder url (or ?q=elfinder if seo-capable urls disabled)
b. Go to Administer page. Click 'Files' under Content section.
a. Open /elfinder url (or ?q=elfinder if seo-capable urls disabled)
b. Go to Administer page. Click 'Files' under Content section. 2. Inside CKEditor (FCKeditor)
2.1. Open Image Properties dialog in editor
2. Inside CKEditor (FCKeditor) 2.2. Click Browse Server button near image url
2.1. Open Image Properties dialog in editor
2.2. Click Browse Server button near image url 3. Inside TinyMCE
3.1. Open Insert/edit image dialog
3. Inside TinyMCE 3.2. Click Browse button near image url
3.1. Open Insert/edit image dialog
3.2. Click Browse button near image url 4. Inside BUEditor
4.1. Click 'Insert/edit image'
4. Inside BUEditor 4.2. Click 'Browse'
4.1. Click 'Insert/edit image' 4.3. Select file
4.2. Click 'Browse' 4.4. Enter alt text then click OK
4.3. Select file
4.4. Enter alt text then click OK 5. Inside jWYSIWYG
5.1. Make sure that you have jWYSYWIG 0.93+ (http://github.com/akzhan/jwysiwyg/), jQuery 1.3+ and jQuery UI 1.7+ installed
5. Inside jWYSIWYG 5.2. Click 'Insert image' button
5.1. Make sure that you have jWYSYWIG 0.93+ (http://github.com/akzhan/jwysiwyg/), jQuery 1.3+ and jQuery UI 1.7+ installed 5.3. Click 'Browse Server' button near image url
5.2. Click 'Insert image' button
5.3. Click 'Browse Server' button near image url 6. Inside YUI Editor
6.1. Click 'Insert Image' button
6. Inside YUI Editor 6.2. Click 'Browse Server' button near image url
6.1. Click 'Insert Image' button
6.2. Click 'Browse Server' button near image url 7. Inside WYMeditor
7.1. Click 'Image' button
7. Inside WYMeditor 7.2. Click 'Browse Server' button near image url
7.1. Click 'Image' button
7.2. Click 'Browse Server' button near image url
Known Issues:
- Not all editors supported
Known Issues: - Conflicting with some devel module features
- Not all editors supported
- Conflicting with some devel module features

View File

@ -1,5 +1,11 @@
.elfinder-field-wrapper-volume-path { /*
float:left; * elFinder Integration
width:300px; *
bborder:#f00 1px solid; * Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
.elfinder-field-wrapper-volume-path {
float: left;
width: 300px;
bborder: #f00 1px solid;
} }

View File

@ -1,27 +1,48 @@
/*
.el-finder-nav ul li { * elFinder Integration
background:none; *
padding:0; * Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
margin:0; */
}
.elfinder-cwd, .elfinder-navbar { #finder .elfinder-statusbar {
font-size:13px!important; padding: .2em 1em;
} }
.elfinder-cwd-view-icons .elfinder-cwd-file { #finder fieldset.elfinder-resize-preset-container {
height: 150px; padding: .5em;
width: 100px; }
}
#finder .ui-dialog input[type='text'] {
.elfinder-cwd-view-icons .elfinder-cwd-filename { padding: 1px 2px;
white-space: normal; }
overflow:visible;
} #finder .ui-dialog tr td {
border-right: none;
body.elfinder #finder { vertical-align: top;
border: none; }
}
#finder .ui-dialog textarea#elfinder-fm-file-desc {
body.elfinder .elfinder-toolbar, body.elfinder .elfinder-statusbar { background: #FFF;
border-radius: 0 !important; }
}
#finder .elfinder-cwd-view-icons .elfinder-cwd-icon.elfinder-cwd-bgurl:before {
font-family: inherit;
}
/* elfinder popup window */
body.elfinder #finder {
border: none;
}
body.elfinder .elfinder-toolbar,
body.elfinder .elfinder-statusbar {
border-radius: 0 !important;
}
#elfinder-messages {
display: none;
}
#elfinder-messages.legacy {
display: block;
}

View File

@ -1,6 +1,11 @@
/*
.el-finder-nav ul li { * elFinder Integration
background:none; *
padding:0; * Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
margin:0; */
.el-finder-nav ul li {
background: none;
padding: 0;
margin: 0;
} }

View File

@ -1,9 +0,0 @@
.el-finder-nav ul {
list-style-type:none!important;
}
.el-finder-cwd label {
font-weight:normal!important;
overflow:visible!important;
}

View File

@ -1,3 +1,8 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
@import url('jquery-ui.min.css'); @import url('jquery-ui.min.css');
@import url('jquery-ui.structure.min.css'); @import url('jquery-ui.structure.min.css');
@import url('jquery-ui.theme.min.css'); @import url('jquery-ui.theme.min.css');

File diff suppressed because one or more lines are too long

View File

@ -1 +1,19 @@
// $Id$ function elfinder_bueditor_callback(url) { var img = jQuery('<img/>').attr('src', url).css('display', 'none').insertBefore(jQuery('#finder')); parent.jQuery("#bue-tgd-form input[name=attr_width]").val(img.width()); parent.jQuery("#bue-tgd-form input[name=attr_height]").val(img.height()); parent.BUE.imce.target.value = url; parent.BUE.popups['bue-imce-pop'].close(); } function elfinder_bue_callback(url) { elfinder_bueditor_callback(url); } /*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
function elfinder_bueditor_callback(url) {
var img = jQuery('<img/>').attr('src', url).css('display', 'none').insertBefore(jQuery('#finder'));
parent.jQuery("#bue-tgd-form input[name=attr_width]").val(img.width());
parent.jQuery("#bue-tgd-form input[name=attr_height]").val(img.height());
parent.BUE.imce.target.value = url;
parent.BUE.popups['bue-imce-pop'].close();
}
function elfinder_bue_callback(url) {
elfinder_bueditor_callback(url);
}

View File

@ -1,23 +1,29 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
/** /**
* @file * @file
* BUEditor integration plugin * BUEditor integration plugin
*/ */
/** /**
* Pseudo-hook for elfinder hook_wysiwyg_plugin implementation * Pseudo-hook for elfinder hook_wysiwyg_plugin implementation
*/ */
function elfinder_bueditor_elfinder_editor_plugin($options) { function elfinder_bueditor_elfinder_editor_plugin($options) {
drupal_add_js(array('elfinder' => array('moduleUrl' => url('elfinder'))), 'setting'); drupal_add_js(array('elfinder' => array('moduleUrl' => url('elfinder'))), 'setting');
drupal_add_js(array('elfinder' => array('moduleUrl' => url('elfinder'))), 'setting'); drupal_add_js(array('elfinder' => array('moduleUrl' => url('elfinder'))), 'setting');
drupal_add_js($options['plugin_url_base'] . '/bueditor.js'); drupal_add_js($options['plugin_url_base'] . '/bueditor.js');
return array( return array(
'elfinder' => array( 'elfinder' => array(
'extensions' => array('elfinder' => t('elFinder')), 'extensions' => array('elfinder' => t('elFinder')),
'url' => $options['homepage_url'], 'url' => $options['homepage_url'],
'options' => array(), 'options' => array(),
'load' => FALSE, 'load' => FALSE,
), ),
); );
} }

View File

@ -1,12 +1,18 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id: elfinder.bueditor.js,v 1.1 2010/10/06 09:44:51 ph0enix Exp $ // $Id: elfinder.bueditor.js,v 1.1 2010/10/06 09:44:51 ph0enix Exp $
Drupal.elfinder.editor.bueditor = { Drupal.elfinder.editor.bueditor = {
fn : { fn: {
attach: Drupal.wysiwyg.editor.attach.bueditor attach: Drupal.wysiwyg.editor.attach.bueditor
} }
} }
Drupal.wysiwyg.editor.attach.bueditor = function(context, params, settings) { Drupal.wysiwyg.editor.attach.bueditor = function(context, params, settings) {
Drupal.settings.BUE.imceURL = Drupal.settings.elfinder.moduleUrl; Drupal.settings.BUE.imceURL = Drupal.settings.elfinder.moduleUrl;
Drupal.elfinder.editor.bueditor.fn.attach.apply(this, arguments); Drupal.elfinder.editor.bueditor.fn.attach.apply(this, arguments);
} }

View File

@ -1,4 +1,8 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2019, Alexey Sukhotin. All rights reserved.
*/
function elfinder_ckeditor_callback(arg1) { function elfinder_ckeditor_callback(arg1) {
@ -7,13 +11,23 @@ function elfinder_ckeditor_callback(arg1) {
if (typeof arg1 == 'object') { if (typeof arg1 == 'object') {
url = arg1.url; url = arg1.url;
} }
funcNum = window.location.search.replace(/^.*CKEditorFuncNum=(\d+).*$/, "$1"); funcNum = window.location.search.replace(/^.*CKEditorFuncNum=(\d+).*$/, "$1");
window.opener.CKEDITOR.tools.callFunction(funcNum, url, function() { window.opener.CKEDITOR.tools.callFunction(funcNum, url, function() {
// adapted from http://docs.ckeditor.com/#!/guide/dev_file_browser_api // adapted from http://docs.ckeditor.com/#!/guide/dev_file_browser_api
var dialog = this.getDialog(); var dialog = this.getDialog();
if (dialog.getName() == 'link' ) {
if (dialog.getName() == 'link') {
var element = dialog.getContentElement('info', 'linkDisplayText'); var element = dialog.getContentElement('info', 'linkDisplayText');
var display_text = element.getValue(); var display_text = null;
if (element) {
display_text = element.getValue();
}
// If display text is blank, insert the filename. // If display text is blank, insert the filename.
if (element && !display_text) { if (element && !display_text) {
element.setValue(arg1.name); element.setValue(arg1.name);
@ -24,7 +38,7 @@ function elfinder_ckeditor_callback(arg1) {
// Avoid beforeunload event when selecting an image. // Avoid beforeunload event when selecting an image.
// See https://github.com/Studio-42/elFinder/issues/1340 // See https://github.com/Studio-42/elFinder/issues/1340
// Maybe remove this when elfinder js library gets updated. // Maybe remove this when elfinder js library gets updated.
$(window).off('beforeunload'); jQuery(window).off('beforeunload');
window.close(); window.close();
} }

View File

@ -1,4 +1,9 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
/** /**
* @file * @file
@ -6,8 +11,8 @@
*/ */
/** /**
* Pseudo-hook for elfinder hook_wysiwyg_plugin implementation * Pseudo-hook for elfinder hook_wysiwyg_plugin implementation
*/ */
function elfinder_ckeditor_elfinder_editor_plugin($options) { function elfinder_ckeditor_elfinder_editor_plugin($options) {
return array( return array(
'elfinder' => array( 'elfinder' => array(
@ -18,6 +23,7 @@ function elfinder_ckeditor_elfinder_editor_plugin($options) {
'filebrowserUploadUrl' => url('elfinder/upload/ckeditor'), 'filebrowserUploadUrl' => url('elfinder/upload/ckeditor'),
'filebrowserImageUploadUrl' => url('elfinder/upload/ckeditor'), 'filebrowserImageUploadUrl' => url('elfinder/upload/ckeditor'),
'filebrowserFlashUploadUrl' => url('elfinder/upload/ckeditor'), 'filebrowserFlashUploadUrl' => url('elfinder/upload/ckeditor'),
//'filebrowserBrowseUrl' => 'javascript:alert(12345);return false;//',
), ),
'load' => FALSE, 'load' => FALSE,
), ),

View File

@ -1,36 +1,42 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
/** /**
* @file * @file
* *
* CKeditor Upload tab support * CKeditor Upload tab support
*/ */
function elfinder_editor_upload_ckeditor() { function elfinder_editor_upload_ckeditor() {
$p = elfinder_get_user_profile(); $p = elfinder_get_user_profile();
$dest = file_build_uri(''); $dest = file_build_uri('');
if (!strpos($p->settings['ckeditor_upload_directory'], '://')) { if (!strpos($p->settings['ckeditor_upload_directory'], '://')) {
$dest .= $p->settings['ckeditor_upload_directory']; $dest .= $p->settings['ckeditor_upload_directory'];
} else { } else {
$dest = $p->settings['ckeditor_upload_directory']; $dest = $p->settings['ckeditor_upload_directory'];
} }
$dest = elfinder_parse_path_tokens($dest); $dest = elfinder_parse_path_tokens($dest);
$destabs = drupal_realpath($dest); $destabs = drupal_realpath($dest);
if (!file_prepare_directory($destabs, FILE_CREATE_DIRECTORY)) { if (!file_prepare_directory($destabs, FILE_CREATE_DIRECTORY)) {
drupal_set_message(t('Error. Cannot initialize directory %dir', array('%dir' => $destabs)), 'error'); drupal_set_message(t('Error. Cannot initialize directory %dir', array('%dir' => $destabs)), 'error');
} }
$tmpf = $_FILES; $tmpf = $_FILES;
foreach (array_keys($_FILES['upload']) as $key) { foreach (array_keys($_FILES['upload']) as $key) {
$tmpf['files'][$key]['upload'] = $_FILES['upload'][$key]; $tmpf['files'][$key]['upload'] = $_FILES['upload'][$key];
} }
$_FILES = $tmpf; $_FILES = $tmpf;
$file = file_save_upload('upload', array(), $dest); $file = file_save_upload('upload', array(), $dest);
@ -38,7 +44,7 @@ function elfinder_editor_upload_ckeditor() {
file_save($file); file_save($file);
header('Content-Type: text/html'); header('Content-Type: text/html');
print '<script type="text/javascript">'; print '<script type="text/javascript">';
if ($file) { if ($file) {

View File

@ -1,5 +1,10 @@
// $Id$ /*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
function elfinder_drupalnodeattach_callback(url) { function elfinder_drupalnodeattach_callback(url) {
parent.jQuery('input#edit-attach-url').val(url); parent.jQuery('input#edit-attach-url').val(url);
alert(url);
} }

View File

@ -1 +1,10 @@
// $Id$ function elfinder_fckeditor_callback(url) { window.opener.SetUrl(url) ; window.close(); } /*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
function elfinder_fckeditor_callback(url) {
window.opener.SetUrl(url);
window.close();
}

View File

@ -1,34 +1,40 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
/** /**
* @file * @file
* FCKeditor integration plugin * FCKeditor integration plugin
*/ */
/** /**
* Pseudo-hook for elfinder hook_wysiwyg_plugin implementation * Pseudo-hook for elfinder hook_wysiwyg_plugin implementation
*/ */
function elfinder_fckeditor_elfinder_editor_plugin($options) { function elfinder_fckeditor_elfinder_editor_plugin($options) {
return array( return array(
'elfinder' => array( 'elfinder' => array(
'extensions' => array('elfinder' => t('elFinder')), 'extensions' => array('elfinder' => t('elFinder')),
'url' => $options['homepage_url'], 'url' => $options['homepage_url'],
'options' => array( 'options' => array(
'LinkBrowser' => TRUE, 'LinkBrowser' => TRUE,
'LinkBrowserURL' => $options['elfinder_url'], 'LinkBrowserURL' => $options['elfinder_url'],
'ImageBrowser' => TRUE, 'ImageBrowser' => TRUE,
'ImageBrowserURL' => $options['elfinder_url'], 'ImageBrowserURL' => $options['elfinder_url'],
'FlashBrowser' => TRUE, 'FlashBrowser' => TRUE,
'FlashBrowserURL' => $options['elfinder_url'], 'FlashBrowserURL' => $options['elfinder_url'],
'ImageUpload' => TRUE, 'ImageUpload' => TRUE,
'ImageUploadURL' => url('elfinder/upload/fckeditor'), 'ImageUploadURL' => url('elfinder/upload/fckeditor'),
'FlashUpload' => TRUE, 'FlashUpload' => TRUE,
// 'FlashUploadURL' => $options['elfinder_connector_url'], // 'FlashUploadURL' => $options['elfinder_connector_url'],
'LinkUpload' => TRUE, 'LinkUpload' => TRUE,
// 'LinkUploadURL' => $options['elfinder_connector_url'], // 'LinkUploadURL' => $options['elfinder_connector_url'],
),
'load' => FALSE, ),
), 'load' => FALSE,
); ),
);
} }

View File

@ -1,53 +1,59 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
/** /**
* @file * @file
* *
* FCKeditor Upload tab support * FCKeditor Upload tab support
*/ */
function elfinder_editor_upload_fckeditor() { function elfinder_editor_upload_fckeditor() {
$p = elfinder_get_user_profile(); $p = elfinder_get_user_profile();
$dest = file_build_uri(''); $dest = file_build_uri('');
if (!strpos($p->settings['ckeditor_upload_directory'], '://')) { if (!strpos($p->settings['ckeditor_upload_directory'], '://')) {
$dest .= $p->settings['ckeditor_upload_directory']; $dest .= $p->settings['ckeditor_upload_directory'];
} else { } else {
$dest = $p->settings['ckeditor_upload_directory']; $dest = $p->settings['ckeditor_upload_directory'];
} }
$dest = elfinder_parse_path_tokens($dest); $dest = elfinder_parse_path_tokens($dest);
$destabs = drupal_realpath($dest); $destabs = drupal_realpath($dest);
if (!file_prepare_directory($destabs, FILE_CREATE_DIRECTORY)) { if (!file_prepare_directory($destabs, FILE_CREATE_DIRECTORY)) {
drupal_set_message(t('Error. Cannot initialize directory %dir', array('%dir' => $destabs)), 'error'); drupal_set_message(t('Error. Cannot initialize directory %dir', array('%dir' => $destabs)), 'error');
} }
$tmpf = $_FILES; $tmpf = $_FILES;
foreach (array_keys($_FILES['NewFile']) as $key) { foreach (array_keys($_FILES['NewFile']) as $key) {
$tmpf['files'][$key]['NewFile'] = $_FILES['NewFile'][$key]; $tmpf['files'][$key]['NewFile'] = $_FILES['NewFile'][$key];
} }
$_FILES = $tmpf; $_FILES = $tmpf;
$file = file_save_upload('NewFile', array(), $dest); $file = file_save_upload('NewFile', array(), $dest);
$file->status = FILE_STATUS_PERMANENT; $file->status = FILE_STATUS_PERMANENT;
file_save($file); file_save($file);
header('Content-Type: text/html'); header('Content-Type: text/html');
print '<script type="text/javascript">'; print '<script type="text/javascript">';
if ($file) { if ($file) {
print "window.parent.OnUploadCompleted(0, '" . file_create_url($file->uri) . "', '" . $file->filename . "', '') ;"; print "window.parent.OnUploadCompleted(0, '" . file_create_url($file->uri) . "', '" . $file->filename . "', '') ;";
} else { } else {
print 'window.parent.OnUploadCompleted(1,"","", "' . t('Error uploading file!') . '") ;'; print 'window.parent.OnUploadCompleted(1,"","", "' . t('Error uploading file!') . '") ;';
} }
print '</script>'; print '</script>';
exit(); exit();

View File

@ -1 +1,10 @@
// $Id$ function elfinder_jwysiwyg_callback(url) { window.opener.jQuery('.ui-dialog input[type=text][name=url]').val(url); window.close(); } /*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
function elfinder_jwysiwyg_callback(url) {
window.opener.jQuery('.ui-dialog input[type=text][name=url]').val(url);
window.close();
}

View File

@ -1,29 +1,39 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id$
/** /**
* @file * @file
* jWYSIWYG integration plugin * jWYSIWYG integration plugin
*/ */
/** /**
* Pseudo-hook for elfinder hook_wysiwyg_plugin implementation * Pseudo-hook for elfinder hook_wysiwyg_plugin implementation
*/ */
function elfinder_jwysiwyg_elfinder_editor_plugin($options) { function elfinder_jwysiwyg_elfinder_editor_plugin($options) {
if (module_exists('jquery_update') && module_exists('jquery_ui')) { if (VERSION < 7) {
drupal_add_css(JQUERY_UI_PATH . '/themes/base/ui.all.css'); if (module_exists('jquery_update') && module_exists('jquery_ui')) {
jquery_ui_add(array('ui.dialog', 'ui.accordion', 'ui.draggable', 'ui.droppable', 'ui.selectable')); drupal_add_css(JQUERY_UI_PATH . '/themes/base/ui.all.css');
} else { jquery_ui_add(array('ui.dialog', 'ui.accordion', 'ui.draggable', 'ui.droppable', 'ui.selectable'));
drupal_set_message(t('elFinder requires jQuery 1.3+ and jQuery UI 1.7+ for jWYSIWYG support. Please install and enable <a href="http://drupal.org/project/jquery_update">jquery_update</a> and <a href="http://drupal.org/project/jquery_ui">jquery_ui</a> modules.'), 'error'); } else {
} drupal_set_message(t('elFinder requires jQuery 1.3+ and jQuery UI 1.7+ for jWYSIWYG support. Please install and enable <a href="http://drupal.org/project/jquery_update">jquery_update</a> and <a href="http://drupal.org/project/jquery_ui">jquery_ui</a> modules.'), 'error');
}
drupal_add_js(array('elfinder' => array('file_browser_url' => $options['elfinder_url'])), 'setting'); }
drupal_add_js($options['plugin_url_base'] . '/jwysiwyg.js');
drupal_add_js(array('elfinder' => array('file_browser_url' => $options['elfinder_url'])), 'setting');
return array( drupal_add_js($options['plugin_url_base'] . '/jwysiwyg.js');
'elfinder' => array(
'extensions' => array('elfinder' => t('elFinder')), return array(
'url' => $options['homepage_url'], 'elfinder' => array(
'options' => array(), 'extensions' => array('elfinder' => t('elFinder')),
'load' => FALSE, 'url' => $options['homepage_url'],
), 'options' => array(),
); 'load' => FALSE,
),
);
} }

View File

@ -1,34 +1,40 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id: elfinder.jwysiwyg.js,v 1.1 2010/10/06 09:44:51 ph0enix Exp $ // $Id: elfinder.jwysiwyg.js,v 1.1 2010/10/06 09:44:51 ph0enix Exp $
(function($) { (function($) {
Drupal.elfinder.editor.jwysiwyg = { Drupal.elfinder.editor.jwysiwyg = {
fn : { fn: {
attach: Drupal.wysiwyg.editor.attach.jwysiwyg attach: Drupal.wysiwyg.editor.attach.jwysiwyg
} }
}
Drupal.wysiwyg.editor.attach.jwysiwyg = function(context, params, settings) {
var imgHtml = null;
if (jQuery.fn.wysiwyg.defaults) {
imgHtml = jQuery.fn.wysiwyg.defaults.formImageHtml;
}
var oImgHtml = $('<div>'+imgHtml+'</div>');
var oUrl = $("input[name=url]", oImgHtml);
var browsebutton = $('<input type="button">');
browsebutton.attr('value', Drupal.t('Browse Server'));
browsebutton.attr('onclick', 'var w = window; w.open("' + Drupal.settings.elfinder.file_browser_url + '","","toolbar=no,menubar=no,width=600,height=600")');
browsebutton.insertAfter(oUrl);
if (jQuery.fn.wysiwyg.defaults) {
jQuery.fn.wysiwyg.defaults.formImageHtml = oImgHtml.html();
} }
Drupal.elfinder.editor.jwysiwyg.fn.attach.apply(this, arguments); Drupal.wysiwyg.editor.attach.jwysiwyg = function(context, params, settings) {
}
var imgHtml = null;
if (jQuery.fn.wysiwyg.defaults) {
imgHtml = jQuery.fn.wysiwyg.defaults.formImageHtml;
}
var oImgHtml = $('<div>' + imgHtml + '</div>');
var oUrl = $("input[name=url]", oImgHtml);
var browsebutton = $('<input type="button">');
browsebutton.attr('value', Drupal.t('Browse Server'));
browsebutton.attr('onclick', 'var w = window; w.open("' + Drupal.settings.elfinder.file_browser_url + '","","toolbar=no,menubar=no,width=600,height=600")');
browsebutton.insertAfter(oUrl);
if (jQuery.fn.wysiwyg.defaults) {
jQuery.fn.wysiwyg.defaults.formImageHtml = oImgHtml.html();
}
Drupal.elfinder.editor.jwysiwyg.fn.attach.apply(this, arguments);
}
})(jQuery); })(jQuery);

View File

@ -1 +1,38 @@
// $Id$ var elfinder_tinymce_callback = function (arg1) { var url = arg1; if (typeof arg1 == 'object') { url = arg1.url; } /* window.tinymceFileWin.document.forms[0].elements[window.tinymceFileField].value = url; window.tinymceFileWin.focus(); window.close();*/ //make inline popup work var win = tinyMCEPopup.getWindowArg("window"); // insert information now win.document.getElementById(tinyMCEPopup.getWindowArg("input")).value = url; // are we an image browser if (typeof (win.ImageDialog) != "undefined") { // we are, so update image dimensions... if (win.ImageDialog.getImageData) win.ImageDialog.getImageData(); // ... and preview if necessary if (win.ImageDialog.showPreviewImage) win.ImageDialog.showPreviewImage(url); } // close popup window tinyMCEPopup.close(); } /*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
var elfinder_tinymce_callback = function(arg1) {
var url = arg1;
if (typeof arg1 == 'object') {
url = arg1.url;
}
/* window.tinymceFileWin.document.forms[0].elements[window.tinymceFileField].value = url;
window.tinymceFileWin.focus();
window.close();*/
//make inline popup work
var win = tinyMCEPopup.getWindowArg("window");
// insert information now
win.document.getElementById(tinyMCEPopup.getWindowArg("input")).value = url;
// are we an image browser
if (typeof (win.ImageDialog) != "undefined") {
// we are, so update image dimensions...
if (win.ImageDialog.getImageData)
win.ImageDialog.getImageData();
// ... and preview if necessary
if (win.ImageDialog.showPreviewImage)
win.ImageDialog.showPreviewImage(url);
}
// close popup window
tinyMCEPopup.close();
}

View File

@ -1,24 +1,30 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
/** /**
* @file * @file
* TinyMCE integration plugin * TinyMCE integration plugin
*/ */
/** /**
* Pseudo-hook for elfinder hook_wysiwyg_plugin implementation * Pseudo-hook for elfinder hook_wysiwyg_plugin implementation
*/ */
function elfinder_tinymce_elfinder_editor_plugin($options) { function elfinder_tinymce_elfinder_editor_plugin($options) {
drupal_add_js($options['plugin_url_base'] . '/tinymce.js'); drupal_add_js($options['plugin_url_base'] . '/tinymce.js');
return array( return array(
'elfinder' => array( 'elfinder' => array(
'extensions' => array('elfinder' => t('elFinder')), 'extensions' => array('elfinder' => t('elFinder')),
'url' => $options['homepage_url'], 'url' => $options['homepage_url'],
'options' => array( 'options' => array(
'file_browser_callback' => 'elfinder_tinymce_browse_callback', 'file_browser_callback' => 'elfinder_tinymce_browse_callback',
'file_browser_url' => $options['elfinder_url'], // non standard TinyMCE configuration variable to pass source application to elFinder 'file_browser_url' => $options['elfinder_url'], // non standard TinyMCE configuration variable to pass source application to elFinder
'inline_styles' => TRUE, 'inline_styles' => TRUE,
), ),
'load' => FALSE, 'load' => FALSE,
), ),
); );
} }

View File

@ -1,27 +1,46 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id$
/*function elfinder_tinymce_browse_callback(field_name, url, type, win) {
var w = window.open(tinymce.settings.file_browser_url, null, 'toolbar=yes,menubar=yes,width=600,height=500, inline=yes');
w.tinymceFileField = field_name;
w.tinymceFileWin = win;
}*/
// MAKE INLINE POPUP WORK
function elfinder_tinymce_browse_callback(field_name, url, type, win) { function elfinder_tinymce_browse_callback(field_name, url, type, win) {
/*var w = window.open(tinymce.settings.file_browser_url, null, 'toolbar=yes,menubar=yes,width=600,height=500, inline=yes');
w.tinymceFileField = field_name;
w.tinymceFileWin = win;*/
var cmsURL = tinymce.settings.file_browser_url; // script URL - use an absolute path! var cmsURL = tinymce.settings.file_browser_url; // script URL - use an absolute path!
if (cmsURL.indexOf("?") < 0) { if (cmsURL.indexOf("?") < 0) {
//add the type as the only query parameter //add the type as the only query parameter
cmsURL = cmsURL + "?type=" + type; cmsURL = cmsURL + "?type=" + type;
} else { } else {
//add the type as an additional query parameter //add the type as an additional query parameter
// (PHP session ID is now included if there is one at all) // (PHP session ID is now included if there is one at all)
cmsURL = cmsURL + "&type=" + type; cmsURL = cmsURL + "&type=" + type;
} }
tinyMCE.activeEditor.windowManager.open({ tinyMCE.activeEditor.windowManager.open({
file: cmsURL, file: cmsURL,
title: 'File Manager', title: 'File Manager',
width: 900, width: 900,
height: 450, height: 450,
resizable: "yes", resizable: "yes",
inline: "yes", // This parameter only has an effect if you use the inlinepopups plugin! inline: "yes", // This parameter only has an effect if you use the inlinepopups plugin!
popup_css: false, // Disable TinyMCE's default popup CSS popup_css: false, // Disable TinyMCE's default popup CSS
close_previous: "no" close_previous: "no"
}, { }, {
window: win, window: win,
input: field_name input: field_name
}); });
return false; return false;
} }

View File

@ -1,4 +1,10 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
function elfinder_wymeditor_callback(url) { function elfinder_wymeditor_callback(url) {
window.opener.jQuery('input.wym_src').val(url); window.opener.jQuery('input.wym_src').val(url);
window.close(); window.close();
} }

View File

@ -1,4 +1,10 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id$ // $Id$
/** /**
@ -7,17 +13,17 @@
*/ */
/** /**
* Pseudo-hook for elfinder hook_wysiwyg_plugin implementation * Pseudo-hook for elfinder hook_wysiwyg_plugin implementation
*/ */
function elfinder_wymeditor_elfinder_editor_plugin($options) { function elfinder_wymeditor_elfinder_editor_plugin($options) {
drupal_add_js($options['plugin_url_base'] . '/wymeditor.js'); drupal_add_js($options['plugin_url_base'] . '/wymeditor.js');
drupal_add_js(array('elfinder' => array('file_browser_url' => $options['elfinder_url'])), 'setting'); drupal_add_js(array('elfinder' => array('file_browser_url' => $options['elfinder_url'])), 'setting');
return array( return array(
'elfinder' => array( 'elfinder' => array(
'extensions' => array('elfinder' => t('elFinder')), 'extensions' => array('elfinder' => t('elFinder')),
'url' => $options['homepage_url'], 'url' => $options['homepage_url'],
'options' => array(), 'options' => array(),
'load' => FALSE, 'load' => FALSE,
), ),
); );
} }

View File

@ -1,52 +1,64 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id: elfinder.wymeditor.js,v 1.1.2.2 2010/12/11 11:53:49 ph0enix Exp $ // $Id: elfinder.wymeditor.js,v 1.1.2.2 2010/12/11 11:53:49 ph0enix Exp $
(function($) { (function($) {
Drupal.elfinder.editor.wymeditor = { Drupal.elfinder.editor.wymeditor = {
fn : { fn: {
attach: Drupal.wysiwyg.editor.attach.wymeditor attach: Drupal.wysiwyg.editor.attach.wymeditor
} }
} }
Drupal.wysiwyg.editor.attach.wymeditor = function(context, params, settings) { Drupal.wysiwyg.editor.attach.wymeditor = function(context, params, settings) {
Drupal.elfinder.editor.wymeditor.fn.attach.apply(this, arguments); Drupal.elfinder.editor.wymeditor.fn.attach.apply(this, arguments);
var $field = $('#' + params.field); var $field = $('#' + params.field);
var index = $field.data(WYMeditor.WYM_INDEX); var index = $field.data(WYMeditor.WYM_INDEX);
if (typeof index != 'undefined') { if (typeof index != 'undefined') {
var instance = WYMeditor.INSTANCES[index]; var instance = WYMeditor.INSTANCES[index];
var imgHtml = instance._options.dialogImageHtml; var imgHtml = instance._options.dialogImageHtml;
var oImgHtml = $('<div>'+imgHtml+'</div>'); var oImgHtml = $('<div>' + imgHtml + '</div>');
var oUrl = $("input.wym_src", oImgHtml);
var browsebutton = $('<input type="button">'); //$('body', oImgHtml).addClass('wym_dialog wym_dialog_image');
browsebutton.attr('value', Drupal.t('Browse Server')); //$('body', oImgHtml).attr('omload', 'WYMeditor.INIT_DIALOG(' + index + ')');
/* FIXME: url parameters duplicates for some reason */ var oUrl = $("input.wym_src", oImgHtml);
var tmp = Drupal.settings.elfinder.file_browser_url; var browsebutton = $('<input type="button">');
var bugpos = tmp.indexOf(","); browsebutton.attr('value', Drupal.t('Browse Server'));
var elfinderUrl = ''; /* FIXME: url parameters duplicates for some reason */
var tmp = Drupal.settings.elfinder.file_browser_url;
if (bugpos != -1) { var bugpos = tmp.indexOf(",");
elfinderUrl = tmp.slice(0, bugpos);
var elfinderUrl = '';
if (bugpos != -1) {
elfinderUrl = tmp.slice(0, bugpos);
}
else {
elfinderUrl = tmp;
}
/* */
browsebutton.attr('onclick', 'var w = window; w.open("' + elfinderUrl + '","","toolbar=no,menubar=no,width=600,height=600")');
browsebutton.insertAfter(oUrl);
ImgHtml = '<body class="wym_dialog wym_dialog_image" onload="WYMeditor.INIT_DIALOG(' + index + ')">' + oImgHtml.html() + '</body>';
instance._options.dialogImageHtml = ImgHtml;
} }
else {
elfinderUrl = tmp;
}
browsebutton.attr('onclick', 'var w = window; w.open("' + elfinderUrl + '","","toolbar=no,menubar=no,width=600,height=600")');
browsebutton.insertAfter(oUrl);
ImgHtml = '<body class="wym_dialog wym_dialog_image" onload="WYMeditor.INIT_DIALOG(' + index + ')">' + oImgHtml.html() + '</body>';
instance._options.dialogImageHtml = ImgHtml;
} }
}
})(jQuery); })(jQuery);

View File

@ -1,5 +1,11 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
function elfinder_yui_callback(url) { function elfinder_yui_callback(url) {
var editorId = window.opener.Drupal.wysiwyg.activeId; var editorId = window.opener.Drupal.wysiwyg.activeId;
window.opener.jQuery('input#' + editorId + '_insertimage_url').val(url); window.opener.jQuery('input#' + editorId + '_insertimage_url').val(url);
window.close(); window.close();
} }

View File

@ -1,21 +1,29 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id$
/** /**
* @file * @file
* YUI editor integration plugin * YUI editor integration plugin
*/ */
/** /**
* Pseudo-hook for elfinder hook_wysiwyg_plugin implementation * Pseudo-hook for elfinder hook_wysiwyg_plugin implementation
*/ */
function elfinder_yui_elfinder_editor_plugin($options) { function elfinder_yui_elfinder_editor_plugin($options) {
drupal_add_js($options['plugin_url_base'] . '/yui.js'); drupal_add_js($options['plugin_url_base'] . '/yui.js');
drupal_add_js(array('elfinder' => array('file_browser_url' => $options['elfinder_url'])), 'setting'); drupal_add_js(array('elfinder' => array('file_browser_url' => $options['elfinder_url'])), 'setting');
return array( return array(
'elfinder' => array( 'elfinder' => array(
'extensions' => array('elfinder' => t('elFinder')), 'extensions' => array('elfinder' => t('elFinder')),
'url' => $options['homepage_url'], 'url' => $options['homepage_url'],
'options' => array(), 'options' => array(),
'load' => FALSE, 'load' => FALSE,
), ),
); );
} }

View File

@ -1,33 +1,41 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id: elfinder.yui.js,v 1.1 2010/10/06 09:44:51 ph0enix Exp $ // $Id: elfinder.yui.js,v 1.1 2010/10/06 09:44:51 ph0enix Exp $
(function($) { (function($) {
Drupal.elfinder.editor.yui = { Drupal.elfinder.editor.yui = {
fn : { fn: {
attach: Drupal.wysiwyg.editor.attach.yui attach: Drupal.wysiwyg.editor.attach.yui
}
} }
}
Drupal.wysiwyg.editor.attach.yui = function(context, params, settings) { Drupal.wysiwyg.editor.attach.yui = function(context, params, settings) {
$('#' + params.field).parent().addClass('yui-skin-' + settings.theme);
// Attach editor.
var editor = new YAHOO.widget.Editor(params.field, settings);
editor.on('windowInsertImageRender', function(e) {
var dialogcode = $('#' + params.field + '-panel');
var oUrl = $('input#' + params.field + '_insertimage_url');
var browsebutton = $('<input type="button">');
browsebutton.attr('value', Drupal.t('Browse Server')); $('#' + params.field).parent().addClass('yui-skin-' + settings.theme);
browsebutton.attr('onclick', 'var w = window; w.open("' + Drupal.settings.elfinder.file_browser_url + '","","toolbar=no,menubar=no,width=600,height=600")'); // Attach editor.
browsebutton.insertAfter(oUrl); var editor = new YAHOO.widget.Editor(params.field, settings);
}, editor, true); editor.on('windowInsertImageRender', function(e) {
editor.render(); var dialogcode = $('#' + params.field + '-panel');
}
//var oImgHtml = $('<div>'+imgHtml+'</div>');
var oUrl = $('input#' + params.field + '_insertimage_url');
var browsebutton = $('<input type="button">');
browsebutton.attr('value', Drupal.t('Browse Server'));
browsebutton.attr('onclick', 'var w = window; w.open("' + Drupal.settings.elfinder.file_browser_url + '","","toolbar=no,menubar=no,width=600,height=600")');
browsebutton.insertAfter(oUrl);
}, editor, true);
editor.render();
}
})(jQuery); })(jQuery);

View File

@ -6,8 +6,9 @@ configure = "admin/config/media/elfinder"
core = 7.x core = 7.x
project status url = https://drupal-elfinder.sourceforge.io/release-history.php project status url = https://drupal-elfinder.sourceforge.io/release-history.php
; Information added by Drupal.org packaging script on 2018-12-03 ; Information added by Drupal.org packaging script on 2017-11-16
version = "7.x-2.x-dev" version = "7.x-2.x-dev"
core = "7.x" core = "7.x"
project = "elfinder" project = "elfinder"
datestamp = "1543843700" datestamp = "1555577224"

View File

@ -1,4 +1,10 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2019, Alexey Sukhotin. All rights reserved.
*/
/** /**
* @file * @file
* Installation file for elfinder. * Installation file for elfinder.
@ -11,77 +17,74 @@ function elfinder_requirements($phase) {
require_once drupal_get_path('module', 'elfinder') . "/elfinder.module"; require_once drupal_get_path('module', 'elfinder') . "/elfinder.module";
$requirements = array(); $requirements = array();
$ver_major = 0; $ver_major = 0;
$ver_minor = 0; $ver_minor = 0;
$ver_release = 0; $ver_release = 0;
$min_major = 2; $min_major = 2;
$min_minor = 0; $min_minor = 0;
$min_ver = "$min_major.$min_minor"; $min_ver = "$min_major.$min_minor";
$libpath = elfinder_lib_path(); $libpath = elfinder_lib_path();
$ver = ''; $ver = '';
$install_t = t('Please download it from <a href="@url">@url</a> and install to @libpath.', array('@url' => 'http://elfinder.org', '@libpath' => $libpath)); $install_t = t('Please download it from <a href="@url">@url</a> and install to @libpath.', array('@url' => elfinder_download_url(), '@libpath' => $libpath));
if ($phase == 'runtime' || $phase == 'install' || $phase == 'update') { if ($phase == 'runtime' || $phase == 'install' || $phase == 'update') {
$description = t('elFinder library was not found.') . ' ' . $install_t; $description = t('elFinder library was not found.') . ' ' . $install_t;
$severity = ''; $severity = '';
$value = t('Not found'); $value = t('Not found');
if ( (is_readable($libpath . '/connectors/php/elFinder.class.php') || is_readable($libpath . '/php/elFinder.class.php') ) && is_readable($libpath . '/js/elfinder.min.js') ) {
$editor_file_content = file_get_contents($libpath . '/js/elfinder.min.js'); if ((is_readable($libpath . '/connectors/php/elFinder.class.php') || is_readable($libpath . '/php/elFinder.class.php')) && is_readable($libpath . '/js/elfinder.min.js')) {
$editor_file_content = file_get_contents($libpath . '/js/elfinder.min.js');
$value = t('Exists'); $value = t('Exists');
if (preg_match("/(?:this|elFinder\.prototype|\.prototype)\.version\s*=\s*[\"\']([^\"\']+)[\"\']/", $editor_file_content, $matches)) { if (preg_match("/(?:this|elFinder\.prototype|\.prototype)\.version\s*=\s*[\"\']([^\"\']+)[\"\']/", $editor_file_content, $matches)) {
$ver = $matches[1]; $ver = $matches[1];
$value = t('@ver', array('@ver' => $ver)); $value = t('@ver', array('@ver' => $ver));
} }
$description = ''; $description = '';
if (preg_match("/^(\d+)\.(\d+|x)(\.(\d+))?/", $ver, $matches)) { if (preg_match("/^(\d+)\.(\d+|x)(\.(\d+))?/", $ver, $matches)) {
$ver_major = (int)$matches[1]; $ver_major = (int)$matches[1];
$ver_minor = $matches[2]; $ver_minor = $matches[2];
$ver = "$ver_major.$ver_minor"; $ver = "$ver_major.$ver_minor";
if (count($matches) == 5) { if (count($matches) == 5) {
$ver_release = (int)$matches[4]; $ver_release = (int)$matches[4];
$ver = "$ver_major.$ver_minor.$ver_release"; $ver = "$ver_major.$ver_minor.$ver_release";
} }
} }
if (($ver_major < $min_major) || ($ver_major == 2 && $ver_minor == '1' && $ver_release < 38)) {
$description = t('Not supported elFinder library. Please upgrade to @minver.', array('@ver' => $ver, '@minver' => $min_ver)) . ' ' . $install_t;
if (($ver_major < $min_major) || ($ver_major == 2 && $ver_minor == '1' && $ver_release < 38)) { $severity = REQUIREMENT_ERROR;
$description = t('Not supported elFinder library. Please upgrade to @minver.', array('@ver' => $ver, '@minver' => $min_ver)) . ' ' . $install_t; } else {
$severity = REQUIREMENT_ERROR; $severity = REQUIREMENT_OK;
} else { }
$severity = REQUIREMENT_OK;
} $badpaths_check = elfinder_check_badpaths();
$badpaths_check = elfinder_check_badpaths(); if ($badpaths_check['result'] == FALSE) {
$severity = REQUIREMENT_ERROR;
if ($badpaths_check['result'] == FALSE) { $description = $badpaths_check['message'];
$severity = REQUIREMENT_ERROR; }
$description = $badpaths_check['message'];
} } else {
}
else {
$severity = REQUIREMENT_ERROR; $severity = REQUIREMENT_ERROR;
} }
$requirements['elfinder'] = array( $requirements['elfinder'] = array(
'title' => 'elFinder', 'title' => 'elFinder',
'description' => $description, 'description' => $description,
'value' => $value, 'value' => $value,
'severity' => $severity 'severity' => $severity
); );
} }
@ -102,47 +105,48 @@ function elfinder_schema() {
$schema['elfinder_file_extinfo'] = array( $schema['elfinder_file_extinfo'] = array(
'description' => 'Stores additional filesystem attributes', 'description' => 'Stores additional filesystem attributes',
'fields' => array( 'fields' => array(
'extid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), 'extid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
'fid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE), 'fid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE),
'description' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'default' => ''), 'description' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'default' => ''),
), ),
'primary key' => array('extid'), 'primary key' => array('extid'),
); );
$schema['elfinder_profile'] = array( $schema['elfinder_profile'] = array(
'description' => 'Stores configuration profiles', 'description' => 'Stores configuration profiles',
'fields' => array( 'fields' => array(
'pid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE), 'pid' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
'name' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'default' => ''), 'name' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'default' => ''),
'description' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'default' => ''), 'description' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'default' => ''),
'settings' => array('type' => 'text', 'not null' => FALSE), 'settings' => array('type' => 'text', 'not null' => FALSE),
), ),
'primary key' => array('pid'), 'primary key' => array('pid'),
); );
return $schema; return $schema;
} }
function elfinder_update_7101() { function elfinder_update_7101() {
// drupal_install_schema('elfinder');
} }
function elfinder_update_7102() { function elfinder_update_7102() {
$ret = array(); $ret = array();
if (db_table_exists('elfinder_profile') == FALSE) { if (db_table_exists('elfinder_profile') == FALSE) {
drupal_install_schema('elfinder'); drupal_install_schema('elfinder');
} }
if (!db_field_exists('elfinder_profile', 'settings')) { if (!db_field_exists('elfinder_profile', 'settings')) {
db_add_field($ret, 'elfinder_profile', 'settings', array('type' => 'text', 'not null' => FALSE)); db_add_field($ret, 'elfinder_profile', 'settings', array('type' => 'text', 'not null' => FALSE));
} }
return $ret; return $ret;
} }
function elfinder_update_7103() { function elfinder_update_7103() {
drupal_flush_all_caches(); drupal_flush_all_caches();
} }
/** /**
@ -150,13 +154,12 @@ function elfinder_update_7103() {
*/ */
function elfinder_update_7104() { function elfinder_update_7104() {
$results = db_query("SELECT pid, name, description, settings from {elfinder_profile}"); $results = db_query("SELECT pid, name, description, settings from {elfinder_profile}");
foreach($results as $row) { foreach ($results as $row) {
$settings = unserialize($row->settings); $settings = unserialize($row->settings);
if (!is_array($settings['profile_role'])) { if (!is_array($settings['profile_role'])) {
if (isset($settings['profile_role']) && $settings['profile_role'] > 0) { if (isset($settings['profile_role']) && $settings['profile_role'] > 0) {
$settings['profile_role'] = array($settings['profile_role'] => $settings['profile_role']); $settings['profile_role'] = array($settings['profile_role'] => $settings['profile_role']);
} } else {
else {
$settings['profile_role'] = array(); $settings['profile_role'] = array();
} }
db_update('elfinder_profile') db_update('elfinder_profile')

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,9 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
/** /**
* @file * @file
@ -28,30 +33,29 @@ function elfinder_admin_profile_links($profile_name) {
} }
/** /**
* Settings form definition * Settings form definition
*/ */
function elfinder_admin_form(&$form_state) { function elfinder_admin_form(&$form_state) {
global $user, $language; global $user, $language;
$path = drupal_get_path('module', 'elfinder'); $path = drupal_get_path('module', 'elfinder');
// require_once $path .'/inc/' . 'elfinder.admin.profiles.inc'; // require_once $path .'/inc/' . 'elfinder.admin.profiles.inc';
$langCode = isset($language->language) ? $language->language : 'en'; $langCode = isset($language->language) ? $language->language : 'en';
$form['profiles'] = array( $form['profiles'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Profiles'), '#title' => t('Profiles'),
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => FALSE, '#collapsed' => FALSE,
); );
$profiles = elfinder_admin_profile_get(); $profiles = elfinder_admin_profile_get();
$roles = user_roles(); $roles = user_roles();
$rows = array(); $rows = array();
if ($profiles) { if ($profiles) {
if (is_array($profiles)) { if (is_array($profiles)) {
foreach ($profiles as $profile) { foreach ($profiles as $profile) {
@ -61,21 +65,21 @@ function elfinder_admin_form(&$form_state) {
$profile = $profiles; $profile = $profiles;
$rows[] = array($profile->pid, $profile->name, $profile->description, implode(', ', array_intersect_key($roles, $profile->settings['profile_role'])), elfinder_admin_profile_links($profile->name)); $rows[] = array($profile->pid, $profile->name, $profile->description, implode(', ', array_intersect_key($roles, $profile->settings['profile_role'])), elfinder_admin_profile_links($profile->name));
} }
} }
$profile_table = theme('table', array( $profile_table = theme('table', array(
'header' => array(t('Id'), t('Name'), t('Description'), t('Role'), ''), 'header' => array(t('Id'), t('Name'), t('Description'), t('Role'), ''),
'rows' => $rows 'rows' => $rows
)); ));
$addprofile = l(t('Add profile'), 'admin/config/media/elfinder/profile'); $addprofile = l(t('Add profile'), 'admin/config/media/elfinder/profile');
$form['profiles']['profile_list'] = array( $form['profiles']['profile_list'] = array(
'#type' => 'markup', '#type' => 'markup',
'#markup' => "$profile_table<br/>$addprofile", '#markup' => "$profile_table<br/>$addprofile",
); );
$form['filesystem_settings'] = array( $form['filesystem_settings'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('File system settings'), '#title' => t('File system settings'),
@ -110,7 +114,7 @@ function elfinder_admin_form(&$form_state) {
'#default_value' => variable_get('elfinder_settings_filesystem_unmanaged_root_label', ''), '#default_value' => variable_get('elfinder_settings_filesystem_unmanaged_root_label', ''),
'#description' => t('Root directory label in directory tree'), '#description' => t('Root directory label in directory tree'),
); );
$form['filesystem_settings']['filesystem_root_custom'] = array( $form['filesystem_settings']['filesystem_root_custom'] = array(
'#prefix' => '<div class="custom-container">', '#prefix' => '<div class="custom-container">',
'#suffix' => '</div>', '#suffix' => '</div>',
@ -137,7 +141,7 @@ function elfinder_admin_form(&$form_state) {
'auto' => t('Automatical detection'), 'auto' => t('Automatical detection'),
), ),
); );
$form['filesystem_settings']['filesystem_allowed_extensions'] = array( $form['filesystem_settings']['filesystem_allowed_extensions'] = array(
'#prefix' => '<div class="custom-container">', '#prefix' => '<div class="custom-container">',
'#suffix' => '</div>', '#suffix' => '</div>',
@ -147,7 +151,7 @@ function elfinder_admin_form(&$form_state) {
'#default_value' => variable_get('elfinder_settings_filesystem_allowed_extensions', ''), '#default_value' => variable_get('elfinder_settings_filesystem_allowed_extensions', ''),
'#description' => t('Separate extensions with a space or comma and do not include the leading dot.'), '#description' => t('Separate extensions with a space or comma and do not include the leading dot.'),
); );
if (function_exists('finfo_open')) { if (function_exists('finfo_open')) {
$form['filesystem_settings']['mime_detect']['#options']['finfo'] = t('php finfo'); $form['filesystem_settings']['mime_detect']['#options']['finfo'] = t('php finfo');
} }
@ -160,8 +164,41 @@ function elfinder_admin_form(&$form_state) {
$form['filesystem_settings']['mime_detect']['#options']['bsd'] = t('file -Ib (bsd)'); $form['filesystem_settings']['mime_detect']['#options']['bsd'] = t('file -Ib (bsd)');
$form['filesystem_settings']['mime_detect']['#options']['internal'] = t('By file extension (built-in)'); $form['filesystem_settings']['mime_detect']['#options']['internal'] = t('By file extension (built-in)');
$form['filesystem_settings']['mime_detect']['#options']['drupal'] = t('Drupal API'); $form['filesystem_settings']['mime_detect']['#options']['drupal'] = t('Drupal API');
$form['filesystem_settings']['filesystem_inline_preview'] = array(
'#type' => 'radios',
'#title' => t('Preview'),
'#default_value' => variable_get('elfinder_settings_filesystem_inline_preview', 'default'),
'#description' => t('File types allowed to display in preview'),
'#options' => array(
'default' => t('Default - images, video, audio, pdf, text'),
'all' => t('All supported for preview files - additional: md, psd, html, archives, swf, sharecad.org, MS Office Online, Google Docs - be careful'),
'custom' => t('Custom regex'),
'disabled' => t('Disabled'),
),
);
$form['filesystem_settings']['filesystem_inline_preview_custom'] = array(
'#prefix' => '<div class="custom-container">',
'#suffix' => '</div>',
'#type' => 'textfield',
'#title' => t('Custom preview match regex'),
'#default_value' => variable_get('elfinder_settings_filesystem_inlinepreviewcustom', '^(?:(?:image|video|audio)|application/(?:x-mpegURL|dash\\+xml)|(?:text/plain|application/pdf)$)'),
'#description' => t('Custom mime type match regex for preview'),
);
$form['filesystem_settings']['filesystem_external_preview'] = array(
'#type' => 'radios',
'#title' => t('External Service Preview'),
'#default_value' => variable_get('elfinder_settings_filesystem_external_preview', 'disabled'),
'#description' => t('Use Microsoft, Google and other online services to preview some office documents. <b>Warning!</b> By previewing document with external services <b>YOU ARE ULOADING</b> the document to them. Google, Microsoft and other service owners usually <b>TRACK</b> your activity and <b>share it with Sales, CIA, FSB (KGB), FBI, governors, etc.</b>'),
'#options' => array(
'default' => t('Use Microsoft Office and Google Docs for preview'),
'disabled' => t('Disabled'),
),
);
$form['filesystem_settings']['file_url_type'] = array( $form['filesystem_settings']['file_url_type'] = array(
'#type' => 'radios', '#type' => 'radios',
'#title' => t('Selected file url type'), '#title' => t('Selected file url type'),
@ -170,7 +207,7 @@ function elfinder_admin_form(&$form_state) {
'true' => t('Absolute'), 'true' => t('Absolute'),
'false' => t('Relative'), 'false' => t('Relative'),
), ),
); );
$form['filesystem_settings']['file_perm'] = array( $form['filesystem_settings']['file_perm'] = array(
'#type' => 'textfield', '#type' => 'textfield',
@ -195,7 +232,7 @@ function elfinder_admin_form(&$form_state) {
'#size' => 10, '#size' => 10,
'#weight' => 5, '#weight' => 5,
); );
$form['filesystem_settings']['max_filecount'] = array( $form['filesystem_settings']['max_filecount'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Maximum folder size'), '#title' => t('Maximum folder size'),
@ -204,7 +241,7 @@ function elfinder_admin_form(&$form_state) {
'#size' => 10, '#size' => 10,
'#weight' => 5, '#weight' => 5,
); );
$form['filesystem_settings']['handleprivate'] = array( $form['filesystem_settings']['handleprivate'] = array(
'#type' => 'radios', '#type' => 'radios',
'#title' => t('Handle private downloads'), '#title' => t('Handle private downloads'),
@ -222,7 +259,7 @@ function elfinder_admin_form(&$form_state) {
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => FALSE, '#collapsed' => FALSE,
); );
$form['thumbnail_settings']['tmbsize'] = array( $form['thumbnail_settings']['tmbsize'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Thumbnail size'), '#title' => t('Thumbnail size'),
@ -244,7 +281,7 @@ function elfinder_admin_form(&$form_state) {
'#options' => array( '#options' => array(
'auto' => t('Automatical detection'), 'auto' => t('Automatical detection'),
'imagick' => t('Image Magick'), 'imagick' => t('Image Magick'),
'gd' => t('GD'), 'gd' => t('GD'),
), ),
); );
@ -265,7 +302,7 @@ function elfinder_admin_form(&$form_state) {
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => FALSE, '#collapsed' => FALSE,
); );
$form['misc_settings']['rememberlastdir'] = array( $form['misc_settings']['rememberlastdir'] = array(
'#type' => 'radios', '#type' => 'radios',
'#title' => t('Remember last opened directory'), '#title' => t('Remember last opened directory'),
@ -312,7 +349,7 @@ function elfinder_admin_form(&$form_state) {
$form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration')); $form['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
$form['#submit'][] = 'elfinder_admin_submit'; $form['#submit'][] = 'elfinder_admin_submit';
return $form; return $form;
} }
@ -344,11 +381,25 @@ function elfinder_admin_submit($form, &$form_state) {
variable_set('elfinder_settings_filesystem_public_root_label', $form_state['values']['filesystem_public_root_label']); variable_set('elfinder_settings_filesystem_public_root_label', $form_state['values']['filesystem_public_root_label']);
variable_set('elfinder_settings_filesystem_private_root_label', $form_state['values']['filesystem_private_root_label']); variable_set('elfinder_settings_filesystem_private_root_label', $form_state['values']['filesystem_private_root_label']);
variable_set('elfinder_settings_filesystem_unmanaged_root_label', $form_state['values']['filesystem_unmanaged_root_label']); variable_set('elfinder_settings_filesystem_unmanaged_root_label', $form_state['values']['filesystem_unmanaged_root_label']);
variable_set('elfinder_settings_misc_manager_width', $form_state['values']['manager_width']); variable_set('elfinder_settings_misc_manager_width', $form_state['values']['manager_width']);
variable_set('elfinder_settings_misc_manager_height', $form_state['values']['manager_height']); variable_set('elfinder_settings_misc_manager_height', $form_state['values']['manager_height']);
variable_set('elfinder_settings_misc_hidden_folders', $form_state['values']['hidden_folders']); variable_set('elfinder_settings_misc_hidden_folders', $form_state['values']['hidden_folders']);
variable_set('elfinder_settings_filesystem_allowed_extensions', $form_state['values']['filesystem_allowed_extensions']); variable_set('elfinder_settings_filesystem_allowed_extensions', $form_state['values']['filesystem_allowed_extensions']);
if ($form_state['values']['filesystem_inline_preview'] == 'default') {
variable_set('elfinder_settings_filesystem_inlinepreviewregex', '^(?:(?:image|video|audio)|application/(?:x-mpegURL|dash\\+xml)|(?:text/plain|application/pdf)$)');
} else if ($form_state['values']['filesystem_inline_preview'] == 'all') {
variable_set('elfinder_settings_filesystem_inlinepreviewregex', '.');
} else if ($form_state['values']['filesystem_inline_preview'] == 'custom') {
variable_set('elfinder_settings_filesystem_inlinepreviewregex', variable_get('elfinder_settings_filesystem_inlinepreviewcustom', '^$'));
} else {
variable_set('elfinder_settings_filesystem_inlinepreviewregex', '^$');
}
variable_set('elfinder_settings_filesystem_inline_preview', $form_state['values']['filesystem_inline_preview']);
variable_set('elfinder_settings_filesystem_inlinepreviewcustom', $form_state['values']['filesystem_inline_preview_custom']);
variable_set('elfinder_settings_filesystem_external_preview', $form_state['values']['filesystem_external_preview']);
drupal_set_message(t('Changes have been saved.')); drupal_set_message(t('Changes have been saved.'));
} }
@ -398,4 +449,4 @@ function elfinder_admin_form_validate($form, &$form_state) {
if ($tmbsize && !is_numeric($tmbsize)) { if ($tmbsize && !is_numeric($tmbsize)) {
form_set_error('tmbsize', t('Thumbnail size should be a number')); form_set_error('tmbsize', t('Thumbnail size should be a number'));
} }
} }

View File

@ -1,4 +1,9 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
/** /**
* @file * @file
@ -31,69 +36,69 @@ function elfinder_admin_profile($form, &$form_state) {
if ($action == 'profile_add') { if ($action == 'profile_add') {
$form['profile_name'] = array( $form['profile_name'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Profile Name'), '#title' => t('Profile Name'),
'#default_value' => '', '#default_value' => '',
'#size' => 14, '#size' => 14,
); );
} else { } else {
$form['profile_name_label'] = array( $form['profile_name_label'] = array(
'#title' => t('Profile Name'), '#title' => t('Profile Name'),
'#markup' => $profile_name, '#markup' => $profile_name,
'#size' => 14, '#size' => 14,
'#type' => 'item', '#type' => 'item',
); );
$form['profile_name'] = array( $form['profile_name'] = array(
'#default_value' => $profile_name, '#default_value' => $profile_name,
'#type' => 'hidden', '#type' => 'hidden',
); );
} }
$form['profile_description'] = array( $form['profile_description'] = array(
'#type' => 'textarea', '#type' => 'textarea',
'#title' => t('Description'), '#title' => t('Description'),
'#default_value' => isset($p->description) ? $p->description : '', '#default_value' => isset($p->description) ? $p->description : '',
); );
$form['profile_role'] = array( $form['profile_role'] = array(
'#type' => 'checkboxes', '#type' => 'checkboxes',
'#title' => t('Roles'), '#title' => t('Roles'),
'#default_value' => isset($profile['profile_role']) ? array_keys($profile['profile_role']) : array(), '#default_value' => isset($profile['profile_role']) ? array_keys($profile['profile_role']) : array(),
'#options' => user_roles(), '#options' => user_roles(),
'#description' => t('Roles for which profile settings will be applied'), '#description' => t('Roles for which profile settings will be applied'),
); );
$form['profile_action'] = array( $form['profile_action'] = array(
'#default_value' => $action, '#default_value' => $action,
'#type' => 'hidden', '#type' => 'hidden',
); );
$form['filesystem_settings'] = array( $form['filesystem_settings'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('File system'), '#title' => t('File system'),
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => TRUE, '#collapsed' => TRUE,
); );
$form['volumes'] = array( $form['volumes'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Volumes'), '#title' => t('Volumes'),
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => FALSE, '#collapsed' => FALSE,
); );
$form['volumes']['volumes_wrapper'] = array( $form['volumes']['volumes_wrapper'] = array(
'#weight' => -4, '#weight' => -4,
'#prefix' => '<div class="clear-block" id="poll-choice-wrapper">', '#prefix' => '<div class="clear-block" id="poll-choice-wrapper">',
'#suffix' => '</div>', '#suffix' => '</div>',
); );
$form['volumes']['volumes_wrapper']['volume'] = array( $form['volumes']['volumes_wrapper']['volume'] = array(
'#prefix' => '<div id="profile-volumes">', '#prefix' => '<div id="profile-volumes">',
'#suffix' => '</div>', '#suffix' => '</div>',
'#type' => 'markup', '#type' => 'markup',
'#value' => '&nbsp;', '#value' => '&nbsp;',
); );
@ -129,35 +134,35 @@ function elfinder_admin_profile($form, &$form_state) {
foreach ($form['volumes']['volumes_wrapper']['volume'] as $volume) { foreach ($form['volumes']['volumes_wrapper']['volume'] as $volume) {
if (is_array($volume) && isset($volume['volume_settings'])) { if (is_array($volume) && isset($volume['volume_settings'])) {
$ckeditor_volumes[] = t('Volume @i (@path)', array( $ckeditor_volumes[] = t('Volume @i (@path)', array(
'@i' => $i, '@i' => $i,
'@path' => $volume['volume_settings']['path']['#default_value'], '@path' => $volume['volume_settings']['path']['#default_value'],
)); ));
$i++; $i++;
} }
} }
$form['volumes']['volumes_wrapper']['volume_add'] = array( $form['volumes']['volumes_wrapper']['volume_add'] = array(
'#type' => 'submit', '#type' => 'submit',
'#value' => t('Add volume'), '#value' => t('Add volume'),
'#weight' => 1, '#weight' => 1,
'#name' => 'addfield', '#name' => 'addfield',
'#submit' => array('elfinder_admin_profile_change_volume'), '#submit' => array('elfinder_admin_profile_change_volume'),
'#ajax' => array( '#ajax' => array(
'wrapper' => 'profile-volumes', 'wrapper' => 'profile-volumes',
'callback' => 'elfinder_admin_profile_callback', 'callback' => 'elfinder_admin_profile_callback',
'method' => 'replace', 'method' => 'replace',
'effect' => 'fade', 'effect' => 'fade',
), ),
); );
$form['filesystem_settings']['mime_detect'] = array( $form['filesystem_settings']['mime_detect'] = array(
'#type' => 'radios', '#type' => 'radios',
'#title' => t('File type detection'), '#title' => t('File type detection'),
'#default_value' => isset($profile['mimedetect']) ? $profile['mimedetect'] : 'auto', '#default_value' => isset($profile['mimedetect']) ? $profile['mimedetect'] : 'auto',
'#options' => array( '#options' => array(
'auto' => t('Automatical detection'), 'auto' => t('Automatical detection'),
), ),
); );
if (function_exists('finfo_open')) { if (function_exists('finfo_open')) {
@ -175,144 +180,144 @@ function elfinder_admin_profile($form, &$form_state) {
$form['filesystem_settings']['file_url_type'] = array( $form['filesystem_settings']['file_url_type'] = array(
'#type' => 'radios', '#type' => 'radios',
'#title' => t('Selected file url type'), '#title' => t('Selected file url type'),
'#default_value' => $profile['file_url_type'] == 'true' ? 'true' : 'false', '#default_value' => $profile['file_url_type'] == 'true' ? 'true' : 'false',
'#options' => array( '#options' => array(
'true' => t('Absolute'), 'true' => t('Absolute'),
'false' => t('Relative'), 'false' => t('Relative'),
), ),
); );
$form['filesystem_settings']['file_perm'] = array( $form['filesystem_settings']['file_perm'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Created file permissions'), '#title' => t('Created file permissions'),
'#default_value' => isset($profile['file_perm']) ? $profile['file_perm'] : '0666', '#default_value' => isset($profile['file_perm']) ? $profile['file_perm'] : '0666',
'#size' => 4, '#size' => 4,
); );
$form['filesystem_settings']['dir_perm'] = array( $form['filesystem_settings']['dir_perm'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Created directory permissions'), '#title' => t('Created directory permissions'),
'#default_value' => isset($profile['dir_perm']) ? $profile['dir_perm'] : '0777', '#default_value' => isset($profile['dir_perm']) ? $profile['dir_perm'] : '0777',
'#size' => 4, '#size' => 4,
); );
$form['filesystem_settings']['max_filesize'] = array( $form['filesystem_settings']['max_filesize'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Maximum upload size'), '#title' => t('Maximum upload size'),
'#default_value' => isset($profile['max_filesize']) ? $profile['max_filesize'] : '', '#default_value' => isset($profile['max_filesize']) ? $profile['max_filesize'] : '',
'#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes will be limited only by PHP\'s maximum post and file upload sizes (current limit <strong>%limit</strong>).', array('%limit' => format_size(file_upload_max_size()))), '#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes will be limited only by PHP\'s maximum post and file upload sizes (current limit <strong>%limit</strong>).', array('%limit' => format_size(file_upload_max_size()))),
'#size' => 10, '#size' => 10,
'#weight' => 5, '#weight' => 5,
); );
$form['filesystem_settings']['user_quota'] = array( $form['filesystem_settings']['user_quota'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('User quota'), '#title' => t('User quota'),
'#default_value' => isset($profile['user_quota']) ? $profile['user_quota'] : '', '#default_value' => isset($profile['user_quota']) ? $profile['user_quota'] : '',
'#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes will be unlimited.'), '#description' => t('Enter a value like "512" (bytes), "80 KB" (kilobytes) or "50 MB" (megabytes) in order to restrict the allowed file size. If left empty the file sizes will be unlimited.'),
'#size' => 10, '#size' => 10,
'#weight' => 5, '#weight' => 5,
); );
$form['thumbnail_settings'] = array( $form['thumbnail_settings'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Thumbnails'), '#title' => t('Thumbnails'),
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => TRUE, '#collapsed' => TRUE,
); );
$form['thumbnail_settings']['tmbsize'] = array( $form['thumbnail_settings']['tmbsize'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Thumbnail size'), '#title' => t('Thumbnail size'),
'#default_value' => isset($profile['tmbsize']) ? $profile['tmbsize'] : '48', '#default_value' => isset($profile['tmbsize']) ? $profile['tmbsize'] : '48',
'#size' => 4, '#size' => 4,
); );
$form['thumbnail_settings']['tmbdirname'] = array( $form['thumbnail_settings']['tmbdirname'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Thumbnail directory name'), '#title' => t('Thumbnail directory name'),
'#default_value' => isset($profile['tmbdirname']) ? $profile['tmbdirname'] : 'tmb', '#default_value' => isset($profile['tmbdirname']) ? $profile['tmbdirname'] : 'tmb',
'#size' => 10, '#size' => 10,
); );
$form['thumbnail_settings']['imglib'] = array( $form['thumbnail_settings']['imglib'] = array(
'#type' => 'radios', '#type' => 'radios',
'#title' => t('Image manipulation library'), '#title' => t('Image manipulation library'),
'#default_value' => isset($profile['imglib']) ? $profile['imglib'] : 'auto', '#default_value' => isset($profile['imglib']) ? $profile['imglib'] : 'auto',
'#options' => array( '#options' => array(
'auto' => t('Automatical detection'), 'auto' => t('Automatical detection'),
'imagick' => t('Image Magick'), 'imagick' => t('Image Magick'),
'gd' => t('GD'), 'gd' => t('GD'),
), ),
); );
$form['thumbnail_settings']['tmbcrop'] = array( $form['thumbnail_settings']['tmbcrop'] = array(
'#type' => 'radios', '#type' => 'radios',
'#title' => t('Image crop'), '#title' => t('Image crop'),
'#default_value' => $profile['tmbcrop'] == 'true' ? 'true' : 'false', '#default_value' => $profile['tmbcrop'] == 'true' ? 'true' : 'false',
'#options' => array( '#options' => array(
'true' => t('Yes'), 'true' => t('Yes'),
'false' => t('No'), 'false' => t('No'),
), ),
'#description' => t('Crop image to fit thumbnail size. Yes - crop, No - scale image to fit thumbnail size.'), '#description' => t('Crop image to fit thumbnail size. Yes - crop, No - scale image to fit thumbnail size.'),
); );
$form['misc_settings'] = array( $form['misc_settings'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Miscellaneous'), '#title' => t('Miscellaneous'),
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => TRUE, '#collapsed' => TRUE,
); );
$form['misc_settings']['rememberlastdir'] = array( $form['misc_settings']['rememberlastdir'] = array(
'#type' => 'radios', '#type' => 'radios',
'#title' => t('Remember last opened directory'), '#title' => t('Remember last opened directory'),
'#default_value' => $profile['rememberlastdir'] == 'true' ? 'true' : 'false', '#default_value' => $profile['rememberlastdir'] == 'true' ? 'true' : 'false',
'#options' => array( '#options' => array(
'true' => t('Yes'), 'true' => t('Yes'),
'false' => t('No'), 'false' => t('No'),
), ),
'#description' => t('Creates a cookie. Disable if you have issues with caching.'), '#description' => t('Creates a cookie. Disable if you have issues with caching.'),
); );
$form['misc_settings']['manager_width'] = array( $form['misc_settings']['manager_width'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('File manager width'), '#title' => t('File manager width'),
'#default_value' => isset($profile['manager_width']) ? $profile['manager_width'] : '', '#default_value' => isset($profile['manager_width']) ? $profile['manager_width'] : '',
'#size' => 4, '#size' => 4,
); );
$form['misc_settings']['manager_height'] = array( $form['misc_settings']['manager_height'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('File manager height'), '#title' => t('File manager height'),
'#default_value' => isset($profile['manager_height']) ? $profile['manager_height'] : '', '#default_value' => isset($profile['manager_height']) ? $profile['manager_height'] : '',
'#size' => 4, '#size' => 4,
); );
$form['misc_settings']['ckeditor_upload_settings'] = array( $form['misc_settings']['ckeditor_upload_settings'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('CKEditor/FCKeditor Upload Settings'), '#title' => t('CKEditor/FCKeditor Upload Settings'),
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => TRUE, '#collapsed' => TRUE,
'#description' => t('CKEditor and FCKeditor allowing to upload and insert image files directly from Insert Image dialog.'), '#description' => t('CKEditor and FCKeditor allowing to upload and insert image files directly from Insert Image dialog.'),
); );
$form['misc_settings']['ckeditor_upload_settings']['ckeditor_upload_directory'] = array( $form['misc_settings']['ckeditor_upload_settings']['ckeditor_upload_directory'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Path'), '#title' => t('Path'),
'#default_value' => isset($profile['ckeditor_upload_directory']) ? $profile['ckeditor_upload_directory'] : '', '#default_value' => isset($profile['ckeditor_upload_directory']) ? $profile['ckeditor_upload_directory'] : '',
'#size' => 40, '#size' => 40,
'#description' => t('Image upload path. Default file uri is used if no uri prefix specified. Examples: public://ckeditor - image will be uploaded into public://ckeditor; images/ckeditor - image will be uploaded to :uriimages/ckeditor', array(':uri' => file_build_uri(''))), '#description' => t('Image upload path. Default file uri is used if no uri prefix specified. Examples: public://ckeditor - image will be uploaded into public://ckeditor; images/ckeditor - image will be uploaded to :uriimages/ckeditor', array(':uri' => file_build_uri(''))),
); );
$form['submit'] = array( $form['submit'] = array(
'#type' => 'submit', '#type' => 'submit',
'#name' => 'save_profile', '#name' => 'save_profile',
'#value' => t('Save configuration') '#value' => t('Save configuration')
); );
$form['#submit'][] = 'elfinder_admin_submit'; $form['#submit'][] = 'elfinder_admin_submit';
@ -343,8 +348,8 @@ function elfinder_admin_profiles($action = '', $profile_name = '') {
function elfinder_admin_profiles_delete_form($form_id, &$form_state, $action, $profile_name) { function elfinder_admin_profiles_delete_form($form_id, &$form_state, $action, $profile_name) {
$form = array(); $form = array();
$form['profile_name'] = array( $form['profile_name'] = array(
'#type' => 'hidden', '#type' => 'hidden',
'#default_value' => $profile_name, '#default_value' => $profile_name,
); );
@ -381,53 +386,53 @@ function elfinder_admin_dir_form($delta, $values = array(), $votes = 0) {
$form['volume_settings'] = array( $form['volume_settings'] = array(
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('Volume @n (@p)', array('@n' => ($delta + 1), '@p' => $values['path'])), '#title' => t('Volume @n (@p)', array('@n' => ($delta + 1), '@p' => isset($values['path']) ? $values['path'] : '')),
'#collapsible' => TRUE, '#collapsible' => TRUE,
'#collapsed' => FALSE, '#collapsed' => FALSE,
); );
$form['volume_settings']['path'] = array( $form['volume_settings']['path'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Path'), '#title' => t('Path'),
'#default_value' => isset($values['path']) ? $values['path'] : '', '#default_value' => isset($values['path']) ? $values['path'] : '',
'#parents' => array('volume', $delta, 'path'), '#parents' => array('volume', $delta, 'path'),
'#prefix' => '<div class="elfinder-field-wrapper-volume-path">', '#prefix' => '<div class="elfinder-field-wrapper-volume-path">',
'#suffix' => '</div>', '#suffix' => '</div>',
'#size' => 40, '#size' => 40,
); );
$form['volume_settings']['label'] = array( $form['volume_settings']['label'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('Label'), '#title' => t('Label'),
'#size' => 15, '#size' => 15,
'#default_value' => isset($values['label']) ? $values['label'] : '', '#default_value' => isset($values['label']) ? $values['label'] : '',
'#description' => t('Root directory label in directory tree'), '#description' => t('Root directory label in directory tree'),
'#parents' => array('volume', $delta, 'label'), '#parents' => array('volume', $delta, 'label'),
); );
$form['volume_settings']['url'] = array( $form['volume_settings']['url'] = array(
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('URL'), '#title' => t('URL'),
'#default_value' => isset($values['url']) ? $values['url'] : '', '#default_value' => isset($values['url']) ? $values['url'] : '',
'#parents' => array('volume', $delta, 'url'), '#parents' => array('volume', $delta, 'url'),
'#prefix' => '<div class="elfinder-field-wrapper-volume-path">', '#prefix' => '<div class="elfinder-field-wrapper-volume-path">',
'#suffix' => '</div>', '#suffix' => '</div>',
'#description' => t('Custom URL prefix (default %def)', array('%def' => $defaulturl)), '#description' => t('Custom URL prefix (default %def)', array('%def' => $defaulturl)),
'#size' => 40, '#size' => 40,
); );
$form['volume_settings']['delete'] = array( $form['volume_settings']['delete'] = array(
'#type' => 'submit', '#type' => 'submit',
'#value' => t('Delete'), '#value' => t('Delete'),
'#name' => 'rmfield-' . $delta, '#name' => 'rmfield-' . $delta,
'#submit' => array('elfinder_admin_profile_change_volume'), '#submit' => array('elfinder_admin_profile_change_volume'),
'#ajax' => array( '#ajax' => array(
'callback' => 'elfinder_admin_profile_callback', 'callback' => 'elfinder_admin_profile_callback',
'wrapper' => 'profile-volumes', 'wrapper' => 'profile-volumes',
'method' => 'replace', 'method' => 'replace',
'effect' => 'fade', 'effect' => 'fade',
), ),
); );
return $form; return $form;
@ -466,28 +471,28 @@ function elfinder_admin_submit($form, &$form_state) {
foreach ($settings as $setting) { foreach ($settings as $setting) {
if (isset($form_state['values'][$setting])) { if (isset($form_state['values'][$setting])) {
/* Excluding empty volumes */ /* Excluding empty volumes */
if ($setting == 'volume') { if ($setting == 'volume') {
for ($i = 0; $i < count($form_state['values']['volume']); $i++) { for ($i = 0; $i < count($form_state['values']['volume']); $i++) {
if (empty($form_state['values']['volume'][$i]['path'])) { if (empty($form_state['values']['volume'][$i]['path'])) {
unset($form_state['values']['volume'][$i]); unset($form_state['values']['volume'][$i]);
} }
} }
} }
$profile_settings[$setting] = $form_state['values'][$setting]; $profile_settings[$setting] = $form_state['values'][$setting];
} }
} }
// Save roles. // Save roles.
$profile_settings['profile_role'] = array(); $profile_settings['profile_role'] = array();
foreach($form_state['values']['profile_role'] as $rid) { foreach ($form_state['values']['profile_role'] as $rid) {
if ($rid != 0) { if ($rid != 0) {
$profile_settings['profile_role'][$rid] = $rid; $profile_settings['profile_role'][$rid] = $rid;
} }
} }
$profile_name = $form_state['values']['profile_name']; $profile_name = $form_state['values']['profile_name'];
$profile_description = $form_state['values']['profile_description']; $profile_description = $form_state['values']['profile_description'];

View File

@ -1,265 +1,285 @@
<?php <?php
/** /**
* @file * elFinder Integration
* *
* elFinder conenctor class * Copyright (c) 2010-2019, Alexey Sukhotin. All rights reserved.
*/ */
class elFinderDrupal extends elFinder { /**
public function __construct($opts) { * @file
if (is_callable(array($this, 'version'))) { *
* elFinder conenctor class
$ver = floatval($this->version()); */
class elFinderDrupal extends elFinder {
if (isset(elFinder::$ApiVersion)) {
$ver = elFinder::$ApiVersion; protected $elFinderDrupalLibVersion;
} protected $elFinderDrupalLibRevision;
if ($ver == 2.1 && is_callable(array($this, 'revision')) && $this->revision() < 37) { public function __construct($opts) {
$this->connector_unsupported_error(); if (is_callable(array($this, 'version'))) {
}
$this->elFinderDrupalLibVersion = floatval($this->version());
parent::__construct($opts);
if (isset(elFinder::$ApiVersion)) {
$this->commands['desc'] = array('target' => TRUE, 'content' => FALSE); $this->elFinderDrupalLibVersion = elFinder::$ApiVersion;
$this->commands['owner'] = array('target' => TRUE, 'content' => FALSE); }
$this->commands['downloadcount'] = array('target' => TRUE);
if (is_callable(array($this, 'revision'))) {
} else { $this->elFinderDrupalLibRevision = $this->revision();
$this->connector_unsupported_error(); }
}
if ($this->elFinderDrupalLibVersion == 2.1 && is_callable(array($this, 'revision')) && $this->revision() < 37) {
} $this->connector_unsupported_error();
}
public function connector_unsupported_error() { parent::__construct($opts);
$this->connector_error(t('Unsupported elFinder library version. Please upgrade.'));
} $this->commands['desc'] = array('target' => TRUE, 'content' => FALSE);
$this->commands['owner'] = array('target' => TRUE, 'content' => FALSE);
public function connector_error($message) { $this->commands['downloadcount'] = array('target' => TRUE);
exit(drupal_json_encode(array('error' => array(strip_tags($message)))));
} } else {
$this->connector_unsupported_error();
/* Overriding search query argument name 'q' since it's already used in Drupal */ }
public function commandArgsList($cmd) { }
$this->commands['search']['elfinder_search_q'] = TRUE;
return $this->commandExists($cmd) ? $this->commands[$cmd] : array(); public function connector_unsupported_error() {
} $this->connector_error(t('Unsupported elFinder library version. Please upgrade.'));
}
protected function search($args) {
$q = trim($args['elfinder_search_q']); public function connector_error($message) {
$mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array(); exit(drupal_json_encode(array('error' => array(strip_tags($message)))));
$target = !empty($args['target']) ? $args['target'] : null; }
$result = array();
$errors = array(); /* Overriding search query argument name 'q' since it's already used in Drupal */
if ($target) { public function commandArgsList($cmd) {
if ($volume = $this->volume($target)) { $this->commands['search']['elfinder_search_q'] = TRUE;
$result = $volume->search($q, $mimes, $target); return $this->commandExists($cmd) ? $this->commands[$cmd] : array();
$errors = array_merge($errors, $volume->error()); }
}
} else { protected function search($args) {
foreach ($this->volumes as $volume) { $q = trim($args['elfinder_search_q']);
$result = array_merge($result, $volume->search($q, $mimes)); $mimes = !empty($args['mimes']) && is_array($args['mimes']) ? $args['mimes'] : array();
$errors = array_merge($errors, $volume->error()); $target = !empty($args['target']) ? $args['target'] : null;
} $result = array();
} $errors = array();
// Exclude hidden folders from search results. if ($target) {
$hidden_folders = variable_get('elfinder_settings_misc_hidden_folders', ''); if ($volume = $this->volume($target)) {
if($hidden_folders) { $result = $volume->search($q, $mimes, $target);
$hidden_folders = explode(',', $hidden_folders); $errors = array_merge($errors, $volume->error());
foreach($result AS $key => $file) { }
$parts = explode('/', $file['path']); } else {
if(in_array($parts[1], $hidden_folders)) { foreach ($this->volumes as $volume) {
unset($result[$key]); $result = array_merge($result, $volume->search($q, $mimes));
} $errors = array_merge($errors, $volume->error());
} }
} }
$result = array('files' => $result); // Exclude hidden folders from search results.
if ($errors) { $hidden_folders = variable_get('elfinder_settings_misc_hidden_folders', '');
$result['warning'] = $errors; if($hidden_folders) {
} $hidden_folders = explode(',', $hidden_folders);
return $result; foreach($result AS $key => $file) {
} $parts = explode('/', $file['path']);
if(in_array($parts[1], $hidden_folders)) {
protected function desc($args) { unset($result[$key]);
$target = $args['target']; }
$desc = $args['content']; }
$error = array(self::ERROR_UNKNOWN, '#' . $target); }
if (($volume = $this->volume($target)) == FALSE $result = array('files' => $result);
|| ($file = $volume->file($target)) == FALSE) { if ($errors) {
return array('error' => $this->error($error, self::ERROR_FILE_NOT_FOUND)); $result['warning'] = $errors;
} }
return $result;
$error[1] = $file['name']; }
if ($volume->driverId() == 'f') { protected function desc($args) {
return array('desc' => ''); $target = $args['target'];
} $desc = $args['content'];
$error = array(self::ERROR_UNKNOWN, '#' . $target);
if ($volume->commandDisabled('desc')) {
return array('error' => $this->error($error, self::ERROR_ACCESS_DENIED)); if (($volume = $this->volume($target)) == FALSE
} || ($file = $volume->file($target)) == FALSE) {
return array('error' => $this->error($error, self::ERROR_FILE_NOT_FOUND));
if (($desc = $volume->desc($target, $desc)) == -1) { }
return array('error' => $this->error($error, $volume->error()));
} $error[1] = $file['name'];
return array('desc' => $desc); if ($volume->driverId() == 'f') {
} return array('desc' => '');
}
protected function owner($args) {
$target = $args['target']; if ($volume->commandDisabled('desc')) {
return array('error' => $this->error($error, self::ERROR_ACCESS_DENIED));
$error = array(self::ERROR_UNKNOWN, '#' . $target); }
if (($volume = $this->volume($target)) == FALSE if (($desc = $volume->desc($target, $desc)) == -1) {
|| ($file = $volume->file($target)) == FALSE) { return array('error' => $this->error($error, $volume->error()));
return array('error' => $this->error($error, self::ERROR_FILE_NOT_FOUND)); }
}
return array('desc' => $desc);
$error[1] = $file['name']; }
if ($volume->driverId() == 'f') { protected function owner($args) {
return array('owner' => ''); $target = $args['target'];
}
$error = array(self::ERROR_UNKNOWN, '#' . $target);
if ($volume->commandDisabled('owner')) {
return array('error' => $this->error($error, self::ERROR_ACCESS_DENIED)); if (($volume = $this->volume($target)) == FALSE
} || ($file = $volume->file($target)) == FALSE) {
return array('error' => $this->error($error, self::ERROR_FILE_NOT_FOUND));
if (($owner = $volume->owner($target)) == FALSE) { }
return array('error' => $this->error($error, $volume->error()));
} $error[1] = $file['name'];
return array('owner' => $owner); if ($volume->driverId() == 'f') {
} return array('owner' => '');
}
protected function downloadcount($args) {
$target = $args['target']; if ($volume->commandDisabled('owner')) {
return array('error' => $this->error($error, self::ERROR_ACCESS_DENIED));
$error = array(self::ERROR_UNKNOWN, '#' . $target); }
if (($volume = $this->volume($target)) == FALSE if (($owner = $volume->owner($target)) == FALSE) {
|| ($file = $volume->file($target)) == FALSE) { return array('error' => $this->error($error, $volume->error()));
return array('error' => $this->error($error, self::ERROR_FILE_NOT_FOUND)); }
}
return array('owner' => $owner);
$error[1] = $file['name']; }
if ($volume->driverId() == 'f') { protected function downloadcount($args) {
return array('downloadcount' => ''); $target = $args['target'];
}
$error = array(self::ERROR_UNKNOWN, '#' . $target);
if ($volume->commandDisabled('downloadcount')) {
return array('error' => $this->error($error, self::ERROR_ACCESS_DENIED)); if (($volume = $this->volume($target)) == FALSE
} || ($file = $volume->file($target)) == FALSE) {
return array('error' => $this->error($error, self::ERROR_FILE_NOT_FOUND));
if (($downloadcount = $volume->downloadcount($target)) == FALSE) { }
return array('error' => $this->error($error, $volume->error()));
} $error[1] = $file['name'];
return array('downloadcount' => $downloadcount); if ($volume->driverId() == 'f') {
} return array('downloadcount' => '');
}
/**
* Required to output file in browser when volume URL is not set if ($volume->commandDisabled('downloadcount')) {
* Return array contains opened file pointer, root itself and required headers return array('error' => $this->error($error, self::ERROR_ACCESS_DENIED));
* }
* @param array command arguments
* @return array if (($downloadcount = $volume->downloadcount($target)) == FALSE) {
* @author Dmitry (dio) Levashov return array('error' => $this->error($error, $volume->error()));
* */ }
protected function file($args) {
$target = $args['target']; return array('downloadcount' => $downloadcount);
$download = !empty($args['download']); }
$h403 = 'HTTP/1.x 403 Access Denied';
$h404 = 'HTTP/1.x 404 Not Found'; /**
* Required to output file in browser when volume URL is not set
if (($volume = $this->volume($target)) == FALSE) { * Return array contains opened file pointer, root itself and required headers
return array('error' => self::$errors[self::ERROR_FILE_NOT_FOUND], 'header' => $h404, 'raw' => TRUE); *
} * @param array command arguments
* @return array
if (($file = $volume->file($target)) == FALSE) { * @author Dmitry (dio) Levashov
return array('error' => self::$errors[self::ERROR_FILE_NOT_FOUND], 'header' => $h404, 'raw' => TRUE); * */
} protected function file($args) {
if (!$file['read']) { if ($this->elFinderDrupalLibVersion >= 2.1 && $this->elFinderDrupalLibRevision > 38) {
return array('error' => self::$errors[self::ERROR_ACCESS_DENIED], 'header' => $h403, 'raw' => TRUE); return parent::file($args);
} }
if ($volume->driverId() != 'f' && (($fp = $volume->open($target)) == FALSE)) { $target = $args['target'];
return array('error' => self::$errors[self::ERROR_FILE_NOT_FOUND], 'header' => $h404, 'raw' => TRUE); $download = !empty($args['download']);
} $h403 = 'HTTP/1.x 403 Access Denied';
$h404 = 'HTTP/1.x 404 Not Found';
$mime = ($download) ? 'application/octet-stream' : $file['mime'];
if (($volume = $this->volume($target)) == FALSE) {
$result = array( return array('error' => self::$errors[self::ERROR_FILE_NOT_FOUND], 'header' => $h404, 'raw' => TRUE);
'volume' => $volume, }
'pointer' => $fp,
'info' => $file, if (($file = $volume->file($target)) == FALSE) {
'header' => array( return array('error' => self::$errors[self::ERROR_FILE_NOT_FOUND], 'header' => $h404, 'raw' => TRUE);
"Content-Type: " . $mime, }
"Content-Disposition: " . $this->GetContentDisposition($file['name'], $mime, $download),
"Content-Location: " . $file['name'], if (!$file['read']) {
'Content-Transfer-Encoding: binary', return array('error' => self::$errors[self::ERROR_ACCESS_DENIED], 'header' => $h403, 'raw' => TRUE);
"Content-Length: " . $file['size'], }
"Connection: close"
) if ($volume->driverId() != 'f' && (($fp = $volume->open($target)) == FALSE)) {
); return array('error' => self::$errors[self::ERROR_FILE_NOT_FOUND], 'header' => $h404, 'raw' => TRUE);
}
$real_path = $this->realpath($target);
module_invoke_all('file_download', $volume->drupalpathtouri($real_path)); $mime = ($download) ? 'application/octet-stream' : $file['mime'];
return $result; $result = array(
} 'volume' => $volume,
'pointer' => $fp,
/** 'info' => $file,
* Generating Content-Disposition HTTP header 'header' => array(
* "Content-Type: " . $mime,
* @param string $file Filename "Content-Disposition: " . $this->GetContentDisposition($file['name'], $mime, $download),
* @param string $filemime MIME Type "Content-Location: " . $file['name'],
* @param bool $download Disposition type (true = download file, false = open file in browser) 'Content-Transfer-Encoding: binary',
* @return string "Content-Length: " . $file['size'],
* @author Dmitry (dio) Levashov, Alexey Sukhotin "Connection: close"
* */ )
public static function GetContentDisposition($file, $filemime, $download = FALSE) { );
$disp = ''; $real_path = $this->realpath($target);
$filename = $file; module_invoke_all('file_download', $volume->drupalpathtouri($real_path));
$ua = $_SERVER["HTTP_USER_AGENT"];
$mime = $filemime; return $result;
}
if ($download) {
$disp = 'attachment'; /**
$mime = 'application/octet-stream'; * Generating Content-Disposition HTTP header
} else { *
$disp = preg_match('/^(image|text)/i', $mime) * @param string $file Filename
|| $mime == 'application/x-shockwave-flash' ? 'inline' : 'attachment'; * @param string $filemime MIME Type
} * @param bool $download Disposition type (true = download file, false = open file in browser)
* @return string
$disp .= '; '; * @author Dmitry (dio) Levashov, Alexey Sukhotin
* */
if (preg_match("/MSIE ([0-9]{1,}[\.0-9]{0,})/", $ua)) { public static function GetContentDisposition($file, $filemime, $download = FALSE) {
$filename = rawurlencode($filename);
$filename = str_replace("+", "%20", $filename); $disp = '';
//$filename = str_replace(" ", "%20", $filename); $filename = $file;
$disp .= "filename=" . $filename; $ua = $_SERVER["HTTP_USER_AGENT"];
} elseif (preg_match("/Firefox\/(\d+)/", $ua, $m)) { $mime = $filemime;
if ($m[1] >= 8) {
$disp .= "filename*=?UTF-8''" . rawurlencode($filename); if ($download) {
} else { $disp = 'attachment';
$disp .= "filename*=\"?UTF-8''" . rawurlencode($filename) . "\";"; $mime = 'application/octet-stream';
} } else {
} else { $disp = preg_match('/^(image|text)/i', $mime)
$disp .= "filename=" . $filename; || $mime == 'application/x-shockwave-flash' ? 'inline' : 'attachment';
} }
return $disp; $disp .= '; ';
}
if (preg_match("/MSIE ([0-9]{1,}[\.0-9]{0,})/", $ua)) {
} $filename = rawurlencode($filename);
$filename = str_replace("+", "%20", $filename);
//$filename = str_replace(" ", "%20", $filename);
$disp .= "filename=" . $filename;
} elseif (preg_match("/Firefox\/(\d+)/", $ua, $m)) {
if ($m[1] >= 8) {
$disp .= "filename*=?UTF-8''" . rawurlencode($filename);
} else {
$disp .= "filename*=\"?UTF-8''" . rawurlencode($filename) . "\";";
}
} elseif (preg_match("/Chrome\/(\d+)/", $ua, $m)) {
$disp .= "filename=\"" . $filename . "\"";
} else {
$disp .= "filename=" . $filename;
}
return $disp;
}
}

View File

@ -1,108 +1,114 @@
<?php <?php
/** /**
* @file * elFinder Integration
* elfinder ACL class *
*/ * Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
class elFinderDrupalACL { */
public function fsAccessPublic($attr, $path, $data, $volume) { /**
* @file
if (strpos(basename($path), '.') === 0 && $attr == 'hidden') { * elfinder ACL class
return TRUE; */
} class elFinderDrupalACL {
/* Hiding thumbnail folder */ public function fsAccessPublic($attr, $path, $data, $volume) {
if (strstr($path, DIRECTORY_SEPARATOR . variable_get('elfinder_settings_thumbnail_dirname', 'tmb')) && $attr == 'hidden') {
return TRUE; if (strpos(basename($path), '.') === 0 && $attr == 'hidden') {
} return TRUE;
}
// Hide folders hidden by config settings.
$f = drupal_realpath("public://") . "/"; /* Hiding thumbnail folder */
$folder = str_replace($f, "", $path); if (strstr($path, DIRECTORY_SEPARATOR . variable_get('elfinder_settings_thumbnail_dirname', 'tmb')) && $attr == 'hidden') {
$hidden_folder = preg_split("/[\s,]+/", variable_get('elfinder_settings_misc_hidden_folders', '')); return TRUE;
if (in_array($folder, $hidden_folder) && $attr == 'hidden') { }
return TRUE;
} // Hide folders hidden by config settings.
$f = drupal_realpath("public://") . "/";
if (strstr($path, DIRECTORY_SEPARATOR . '.quarantine') && $attr == 'hidden') { $folder = str_replace($f, "", $path);
return TRUE; $hidden_folder = preg_split("/[\s,]+/", variable_get('elfinder_settings_misc_hidden_folders', ''));
} if (in_array($folder, $hidden_folder) && $attr == 'hidden') {
return TRUE;
if ($attr == 'read') { }
return TRUE;
} if (strstr($path, DIRECTORY_SEPARATOR . '.quarantine') && $attr == 'hidden') {
return TRUE;
if ($attr == 'write') { }
return TRUE;
} if ($attr == 'read') {
return TRUE;
/* if ($attr == 'write' && user_access('write public files')) { }
return TRUE;
} if ($attr == 'write') {
return TRUE;
if ($attr == 'locked' && !user_access('write public files')) { }
return TRUE;
} */ /* if ($attr == 'write' && user_access('write public files')) {
return TRUE;
return FALSE; }
}
if ($attr == 'locked' && !user_access('write public files')) {
public function fsAccessPrivate($attr, $path, $data, $volume) { return TRUE;
} */
if (strpos(basename($path), '.') === 0 && $attr == 'hidden') {
return TRUE; return FALSE;
} }
/* Hiding thumbnail folder */ public function fsAccessPrivate($attr, $path, $data, $volume) {
if (strstr($path, DIRECTORY_SEPARATOR . variable_get('elfinder_settings_thumbnail_dirname', 'tmb')) && $attr == 'hidden') {
return TRUE; if (strpos(basename($path), '.') === 0 && $attr == 'hidden') {
} return TRUE;
}
if (strstr($path, DIRECTORY_SEPARATOR . '.quarantine') && $attr == 'hidden') {
return TRUE; /* Hiding thumbnail folder */
} if (strstr($path, DIRECTORY_SEPARATOR . variable_get('elfinder_settings_thumbnail_dirname', 'tmb')) && $attr == 'hidden') {
return TRUE;
if ($attr == 'read') { }
return TRUE;
} if (strstr($path, DIRECTORY_SEPARATOR . '.quarantine') && $attr == 'hidden') {
return TRUE;
}
if ($attr == 'write') {
return TRUE; if ($attr == 'read') {
} return TRUE;
}
/* if ($attr == 'write' && user_access('write public files')) {
return TRUE;
} if ($attr == 'write') {
return TRUE;
if ($attr == 'locked' && !user_access('write public files')) { }
return TRUE;
} */ /* if ($attr == 'write' && user_access('write public files')) {
return TRUE;
}
return FALSE;
} if ($attr == 'locked' && !user_access('write public files')) {
return TRUE;
public function fsAccessUnmanaged($attr, $path, $data, $volume) { } */
if (strpos(basename($path), '.') === 0 && $attr == 'hidden') {
return TRUE; return FALSE;
} }
/* Hiding thumbnail folder */ public function fsAccessUnmanaged($attr, $path, $data, $volume) {
if (strstr($path, DIRECTORY_SEPARATOR . variable_get('elfinder_settings_thumbnail_dirname', 'tmb')) && $attr == 'hidden') {
return TRUE; if (strpos(basename($path), '.') === 0 && $attr == 'hidden') {
} return TRUE;
}
if (strstr($path, DIRECTORY_SEPARATOR . '.quarantine') && $attr == 'hidden') {
return TRUE; /* Hiding thumbnail folder */
} if (strstr($path, DIRECTORY_SEPARATOR . variable_get('elfinder_settings_thumbnail_dirname', 'tmb')) && $attr == 'hidden') {
return TRUE;
if ($attr == 'read') { }
return TRUE;
} if (strstr($path, DIRECTORY_SEPARATOR . '.quarantine') && $attr == 'hidden') {
return TRUE;
return FALSE; }
}
if ($attr == 'read') {
return TRUE;
}
return FALSE;
}
} }

View File

@ -40,15 +40,16 @@ class elFinderVolumeDrupal extends elFinderVolumeLocalFileSystem {
$pvtpath = drupal_realpath('private://'); $pvtpath = drupal_realpath('private://');
$pubpath = drupal_realpath('public://'); $pubpath = drupal_realpath('public://');
$tmppath = drupal_realpath('temporary://'); $tmppath = drupal_realpath('temporary://');
$final_path = DIRECTORY_SEPARATOR !== '/' ? str_replace(DIRECTORY_SEPARATOR, '/', $path) : $path;
$uri = ''; $uri = '';
if (strpos($path, $pvtpath) === 0) { if (strpos($final_path, $pvtpath) === 0) {
$uri = 'private://' . substr($path, strlen($pvtpath) + 1); $uri = 'private://' . substr($final_path, strlen($pvtpath) + 1);
} elseif (strpos($path, $tmppath) === 0) { } elseif (strpos($final_path, $tmppath) === 0) {
$uri = 'temporary://' . substr($path, strlen($tmppath) + 1); $uri = 'temporary://' . substr($final_path, strlen($tmppath) + 1);
} else { } else {
$uri = 'public://' . substr($path, strlen($pubpath) + 1); $uri = 'public://' . substr($final_path, strlen($pubpath) + 1);
} }
return @file_stream_wrapper_uri_normalize($uri); return @file_stream_wrapper_uri_normalize($uri);
@ -357,7 +358,8 @@ class elFinderVolumeDrupal extends elFinderVolumeLocalFileSystem {
$this->setError(strip_tags(implode(' ', $validation_errors))); $this->setError(strip_tags(implode(' ', $validation_errors)));
return FALSE; return FALSE;
} }
} else { }
else {
watchdog('elfinder', 'File upload "' . $name . '" not found in $_FILES'); watchdog('elfinder', 'File upload "' . $name . '" not found in $_FILES');
} }
return TRUE; return TRUE;
@ -405,6 +407,12 @@ class elFinderVolumeDrupal extends elFinderVolumeLocalFileSystem {
return FALSE; return FALSE;
} }
public static function stat_corrector(&$stat, $path, $statOwner, $volumeDriveInstance) {
if (method_exists($volumeDriveInstance, 'owner')) {
$stat['owner'] = $volumeDriveInstance->owner($volumeDriveInstance->encode($path));
}
}
public function desc($target, $newdesc = NULL) { public function desc($target, $newdesc = NULL) {
$path = $this->decode($target); $path = $this->decode($target);

View File

@ -1,239 +1,414 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id: elfinder.callback.js 106 2011-02-26 08:19:56Z ph0enix $ // $Id: elfinder.callback.js 106 2011-02-26 08:19:56Z ph0enix $
(function($) { (function($) {
/** /**
* @class elFinder command "search" * @class elFinder command "search"
* Find files * Find files
* *
* @author Dmitry (dio) Levashov * @author Dmitry (dio) Levashov
**/ **/
elFinder.prototype.commands.search = function() { elFinder.prototype.commands.search = function() {
this.title = 'Find files'; this.title = 'Find files';
this.options = {ui : 'searchbutton'} this.options = {ui: 'searchbutton'}
this.alwaysEnabled = true; this.alwaysEnabled = true;
this.updateOnSelect = false; this.updateOnSelect = false;
/**
* Return command status.
* Search does not support old api.
*
* @return Number
**/
this.getstate = function() {
return 0;
}
/**
* Send search request to backend.
*
* @param String search string
* @return $.Deferred
**/
this.exec = function(q) {
var fm = this.fm;
if (typeof(q) == 'string' && q) {
return fm.request({
data : {cmd : 'search', elfinder_search_q : q},
notify : {type : 'search', cnt : 1, hideCnt : true}
});
}
fm.getUI('toolbar').find('.'+fm.res('class', 'searchbtn')+' :text').focus();
return $.Deferred().reject();
}
} /**
* Return command status.
* Search does not support old api.
*
* @return Number
**/
this.getstate = function() {
return 0;
}
elFinder.prototype.commands.test134 = function() { /**
this.title = 'Test Command'; * Send search request to backend.
//this.options = {ui : 'uploadbutton'} *
this.alwaysEnabled = true; * @param String search string
this.updateOnSelect = false; * @return $.Deferred
this.state = 0; **/
this.exec = function(q) {
this.getstate = function() { var fm = this.fm;
return 0;
}
/**
* Send search request to backend.
*
* @param String search string
* @return $.Deferred
**/
this.exec = function(q) {
var fm = this.fm;
alert(Drupal.t('test command. arg=') + q);
return $.Deferred().reject();
}
} if (typeof(q) == 'string' && q) {
return fm.request({
data: {cmd: 'search', elfinder_search_q: q},
notify: {type: 'search', cnt: 1, hideCnt: true}
});
}
fm.getUI('toolbar').find('.' + fm.res('class', 'searchbtn') + ' :text').focus();
return $.Deferred().reject();
}
}
elFinder.prototype.commands.test134 = function() {
this.title = 'Test Command';
//this.options = {ui : 'uploadbutton'}
this.alwaysEnabled = true;
this.updateOnSelect = false;
this.state = 0;
this.getstate = function() {
return 0;
}
/**
* Send search request to backend.
*
* @param String search string
* @return $.Deferred
**/
this.exec = function(q) {
var fm = this.fm;
alert(Drupal.t('test command. arg=') + q);
return $.Deferred().reject();
}
}
$().ready(function() { $().ready(function() {
var uiopts = elFinder.prototype._options.uiOptions.toolbar; var uiopts = elFinder.prototype._options.uiOptions.toolbar;
var newOpts = new Array(); var newOpts = new Array();
var disabledCommands = Drupal.settings.elfinder.disabledCommands; var disabledCommands = Drupal.settings.elfinder.disabledCommands;
for (var i in uiopts) {
var optsGroup = uiopts[i];
var newOptsGroup = Array();
for (var j in optsGroup) {
for (var i in uiopts) {
var optsGroup = uiopts[i];
var newOptsGroup = Array();
for (var j in optsGroup) {
var found = false;
for (var k in disabledCommands) {
if (disabledCommands[k] == optsGroup[j]) {
found = true;
}
}
if (found == false) {
newOptsGroup.push(optsGroup[j]);
}
}
if (i == 0) {
newOptsGroup.push('up');
}
if (newOptsGroup.length >= 1) {
newOpts.push(newOptsGroup);
}
}
/*elFinder.prototype._options.contextmenu.files.push('|');
elFinder.prototype._options.contextmenu.files.push('rename'); */
var contextMenuCwd = elFinder.prototype._options.contextmenu.cwd;
var contextMenuFiles = elFinder.prototype._options.contextmenu.files;
var contextMenuNavbar = elFinder.prototype._options.contextmenu.navbar;
var newContextMenuCwd = Array();
var newContextMenuFiles = Array();
var newContextMenuNavbar = Array();
for (var i in contextMenuCwd) {
var found = false; var found = false;
for (var k in disabledCommands) { for (var k in disabledCommands) {
if (disabledCommands[k] == optsGroup[j]) { if (disabledCommands[k] == contextMenuCwd[i]) {
found = true; found = true;
} }
} }
if (found == false) { if (found == false && contextMenuCwd[i] != '|') {
newOptsGroup.push(optsGroup[j]); newContextMenuCwd.push(contextMenuCwd[i]);
}
}
if (i == 0) {
newOptsGroup.push('up');
}
if (newOptsGroup.length >= 1) {
newOpts.push(newOptsGroup);
}
}
var contextMenuCwd = elFinder.prototype._options.contextmenu.cwd;
var contextMenuFiles = elFinder.prototype._options.contextmenu.files;
var contextMenuNavbar = elFinder.prototype._options.contextmenu.navbar;
var newContextMenuCwd = Array();
var newContextMenuFiles = Array();
var newContextMenuNavbar = Array();
for (var i in contextMenuCwd) {
var found = false;
for (var k in disabledCommands) {
if (disabledCommands[k] == contextMenuCwd[i]) {
found = true;
} }
} }
if (found == false && contextMenuCwd[i] != '|') { for (var i in contextMenuFiles) {
newContextMenuCwd.push(contextMenuCwd[i]); var found = false;
} for (var k in disabledCommands) {
} if (disabledCommands[k] == contextMenuFiles[i]) {
found = true;
for (var i in contextMenuFiles) { }
var found = false; }
for (var k in disabledCommands) {
if (disabledCommands[k] == contextMenuFiles[i]) { if (found == false && contextMenuFiles[i] != '|') {
found = true; newContextMenuFiles.push(contextMenuFiles[i]);
} }
} }
if (found == false && contextMenuFiles[i] != '|') { for (var i in contextMenuNavbar) {
newContextMenuFiles.push(contextMenuFiles[i]); var found = false;
} for (var k in disabledCommands) {
} if (disabledCommands[k] == contextMenuNavbar[i]) {
found = true;
for (var i in contextMenuNavbar) { }
var found = false; }
for (var k in disabledCommands) {
if (disabledCommands[k] == contextMenuNavbar[i]) { if (found == false && contextMenuNavbar[i] != '|') {
found = true; newContextMenuNavbar.push(contextMenuNavbar[i]);
} }
} }
elFinder.prototype._options.uiOptions.toolbar = newOpts;
if (found == false && contextMenuNavbar[i] != '|') { elFinder.prototype._options.contextmenu.cwd = newContextMenuCwd;
newContextMenuNavbar.push(contextMenuNavbar[i]); elFinder.prototype._options.contextmenu.files = newContextMenuFiles;
} elFinder.prototype._options.contextmenu.navbar = newContextMenuNavbar;
}
elFinder.prototype._options.uiOptions.toolbar = newOpts;
elFinder.prototype._options.contextmenu.cwd = newContextMenuCwd;
elFinder.prototype._options.contextmenu.files = newContextMenuFiles;
elFinder.prototype._options.contextmenu.navbar = newContextMenuNavbar;
}); //elFinder.prototype._options.ui.push('mouseover');
});
$().ready(function() {
if (Drupal.settings.elfinder) {
var editorApp = Drupal.settings.elfinder.editorApp;
$().ready(function() { var elfinderOpts = {
if (Drupal.settings.elfinder) { url: Drupal.settings.elfinder.connectorUrl,
var editorApp = Drupal.settings.elfinder.editorApp; lang: Drupal.settings.elfinder.langCode,
rememberLastDir: Drupal.settings.elfinder.rememberLastDir,
closeOnEditorCallback: false,
customData: {token: Drupal.settings.elfinder.token},
commandsOptions_: {}
}
var elfinderOpts = { // help tab rendering issues in admin theme
url : Drupal.settings.elfinder.connectorUrl, if (Drupal.settings.elfinder.browserMode != 'default' && elFinder.prototype._options.commandsOptions.help) {
lang : Drupal.settings.elfinder.langCode, final_options = [];
rememberLastDir : Drupal.settings.elfinder.rememberLastDir,
closeOnEditorCallback : false,
customData : {token: Drupal.settings.elfinder.token}
}
/* Pushing all settings to elFinder */
$.extend(elfinderOpts, Drupal.settings.elfinder);
if (editorApp && typeof window[Drupal.settings.elfinder.editorCallback] == 'function') {
elfinderOpts.editorCallback = window[Drupal.settings.elfinder.editorCallback];
}
if (editorApp && typeof window[Drupal.settings.elfinder.editorCallback] == 'function') { for (var i in elFinder.prototype._options.commandsOptions.help.view) {
elfinderOpts.getFileCallback = window[Drupal.settings.elfinder.editorCallback]; var option = elFinder.prototype._options.commandsOptions.help.view[i];
} if (option != 'help') {
final_options.push(option);
if (elfinderOpts.api21) { }
//alert('api21'); }
elFinder.prototype._options.commandsOptions.help['view'] = final_options;
}
/* Pushing all settings to elFinder */
$.extend(elfinderOpts, Drupal.settings.elfinder);
if (editorApp && typeof window[Drupal.settings.elfinder.editorCallback] == 'function') {
elfinderOpts.editorCallback = window[Drupal.settings.elfinder.editorCallback];
}
if (editorApp && typeof window[Drupal.settings.elfinder.editorCallback] == 'function') {
elfinderOpts.getFileCallback = window[Drupal.settings.elfinder.editorCallback];
}
if (elfinderOpts.api21) {
console.log('2.1 api'); console.log('2.1 api');
elfinderOpts['commandsOptions'] = { elfinderOpts['commandsOptions']['info'] = {
info: { custom: {}
// Key is the same as your command name
desc : { };
// Field label
label : 'Description1', /* elfinderOpts['commandsOptions']['quicklook'] = {
officeOnlineMimes : ['application/msword', 'application/vnd.ms-word', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
// HTML Template }*/
tpl : '<div class="elfinder-info-desc"><span class="elfinder-info-spinner"></span></div>',
var disabledCommands = Drupal.settings.elfinder.disabledCommands,
// Action that sends the request to the server and get the description viewDesc = $.inArray('desc', disabledCommands) == -1 ? true : false,
action : function(file, filemanager, dialog) { editDesc = $.inArray('editdesc', disabledCommands) == -1 ? true : false,
// Use the @filemanager object to issue a request viewOwner = $.inArray('owner', disabledCommands) == -1 ? true : false,
filemanager.request({ viewDownloads = $.inArray('downloadcount', disabledCommands) == -1 ? true : false;
// Issuing the custom 'desc' command, targetting the selected file
data : { cmd: 'desc', target: file.hash, },
preventDefault: true, if (viewDesc || editDesc) {
})
// If the request fails, populate the field with 'Unknown' // Key is the same as your command name
.fail(function() { elfinderOpts['commandsOptions']['info']['custom']['desc'] = {
dialog.find('.elfinder-info-desc').html(filemanager.i18n('unknown')); // Field label
}) label: Drupal.t('Description'),
// When the request is successful, show the description
.done(function(data) { // HTML Template
dialog.find('.elfinder-info-desc').html(data.desc); tpl: '<div class="elfinder-info-desc"><span class="elfinder-info-spinner"></span></div><div class="elfinder-info-save"></div>',
});
}, // Action that sends the request to the server and get the description
}, action: function(file, filemanager, dialog) {
console.log('desc action');
console.log('fm');
console.log(file.mime);
// Use the @filemanager object to issue a request
filemanager.request({
// Issuing the custom 'desc' command, targetting the selected file
data: {cmd: 'desc', target: file.hash,},
preventDefault: true,
})
// If the request fails, populate the field with 'Unknown'
.fail(function() {
console.log('desc fail');
dialog.find('.elfinder-info-desc').html(filemanager.i18n('unknown'));
})
// When the request is successful, show the description
.done(function(data) {
console.log('desc done');
dialog.find('.elfinder-info-desc').html(data.desc);
if (editDesc) {
//filemanager.lockfiles({files : [file.hash]})
dialog.find('.elfinder-info-desc').html('<textarea cols="20" rows="5" id="elfinder-fm-file-desc" class="ui-widget ui-widget-content">' + data.desc + '</textarea>');
$('.elfinder-info-save').append('<input type="button" id="elfinder-fm-file-desc-btn-save" class="ui-widget ui-button" value="' + filemanager.i18n('btnSave') + '" />');
var btnSave = $('#elfinder-fm-file-desc-btn-save', dialog).button();
console.log(btnSave);
btnSave.click(function() {
filemanager.lockfiles({files: [file.hash]});
filemanager.request({
data: {cmd: 'desc', target: file.hash, content: $('#elfinder-fm-file-desc').val()},
notify: {type: 'desc', cnt: 1}
})
.always(function() {
filemanager.unlockfiles({files: [file.hash]});
});
});
}
});
}
};
}
if (viewOwner) {
// Key is the same as your command name
elfinderOpts['commandsOptions']['info']['custom']['owner'] = {
// Field label
label: Drupal.t('Owner'),
// HTML Template
tpl: '<div class="elfinder-info-owner"><span class="elfinder-info-spinner"></span></div>',
// Action that sends the request to the server and get the description
action: function(file, filemanager, dialog) {
console.log('owner action');
// Use the @filemanager object to issue a request
filemanager.request({
// Issuing the custom 'desc' command, targetting the selected file
data: {cmd: 'owner', target: file.hash,},
preventDefault: true,
})
// If the request fails, populate the field with 'Unknown'
.fail(function() {
console.log('owner fail');
dialog.find('.elfinder-info-owner').html(filemanager.i18n('unknown'));
})
// When the request is successful, show the description
.done(function(data) {
console.log('owner done');
dialog.find('.elfinder-info-owner').html(data.owner);
});
}
};
elFinder.prototype._options.uiOptions.cwd.listView.columns.push('owner');
elFinder.prototype._options.uiOptions.cwd.listView.columnsCustomName['owner'] = Drupal.t('Owner');
}
if (viewDownloads) {
elfinderOpts['commandsOptions']['info']['custom']['downloadcount'] = {
// Field label
label: Drupal.t('Downloads'),
// HTML Template
tpl: '<div class="elfinder-info-downloadcount"><span class="elfinder-info-spinner"></span></div>',
// Action that sends the request to the server and get the description
action: function(file, filemanager, dialog) {
// Use the @filemanager object to issue a request
filemanager.request({
// Issuing the custom 'desc' command, targetting the selected file
data: {cmd: 'downloadcount', target: file.hash,},
preventDefault: true,
})
// If the request fails, populate the field with 'Unknown'
.fail(function() {
dialog.find('.elfinder-info-downloadcount').html(0);
})
// When the request is successful, show the description
.done(function(data) {
dialog.find('.elfinder-info-downloadcount').html(data.desc);
});
}
};
}
// console.log(elfinderOpts);
} }
};
} var fm = $('#finder').elfinder(elfinderOpts);
$('#finder').elfinder(elfinderOpts); var instance = fm.elfinder('instance');
// console.log(typeof instance == 'object' && typeof instance.toast == 'function');
if (typeof instance == 'object' && typeof instance.toast == 'function') {
instance.bind('load', function(event) {
var messages = '#elfinder-messages .messages';
$(messages).each(function(index, value) {
var mode = 'info';
if ($(value).hasClass('warning')) {
mode = 'warning';
} else if ($(value).hasClass('error')) {
mode = 'error';
}
instance.toast({
msg: $(value).html(),
hideDuration: 500,
showDuration: 300,
timeOut: 1000,
mode: mode
});
});
});
} else {
$('#elfinder-messages').addClass('legacy');
}
//$('#finder').toast({msg: '123'});
var h;
if (elfinderOpts.browserMode != 'backend') {
h = ($(window).height());
} else {
h = ($('#page').height());
}
if(elfinderOpts.browserMode != 'backend') {
// If this is a popup, add an event so that elfinder fills the window.
$(window).resize(function() { $(window).resize(function() {
var h = ($(window).height()); if ($('#finder').height() != h) {
if($('#finder').height() != h) {
$('#finder').height(h).resize(); $('#finder').height(h).resize();
} }
}); });
$(window).trigger('resize');
} }
} });
});
})(jQuery); })(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,9 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
Drupal.elfinder = { Drupal.elfinder = {
editor: {} editor: {}

View File

@ -1,3 +1,9 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id: helper.js 100 2011-02-20 19:14:25Z ph0enix $ // $Id: helper.js 100 2011-02-20 19:14:25Z ph0enix $
Drupal.elfinder = { Drupal.elfinder = {

View File

@ -1,84 +1,90 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
"use strict"; "use strict";
/** /**
* @class elFinder command "info". * @class elFinder command "info".
* Display dialog with file properties. * Display dialog with file properties.
* *
* @author Dmitry (dio) Levashov, dio@std42.ru * @author Dmitry (dio) Levashov, dio@std42.ru
**/ **/
(function($) { (function($) {
elFinder.prototype.commands.info = function() { elFinder.prototype.commands.info = function() {
var m = 'msg', var m = 'msg',
fm = this.fm, fm = this.fm,
spclass = 'elfinder-info-spinner', spclass = 'elfinder-info-spinner',
msg = { msg = {
calc : fm.i18n('calc'), calc: fm.i18n('calc'),
size : fm.i18n('size'), size: fm.i18n('size'),
unknown : fm.i18n('unknown'), unknown: fm.i18n('unknown'),
path : fm.i18n('path'), path: fm.i18n('path'),
aliasfor : fm.i18n('aliasfor'), aliasfor: fm.i18n('aliasfor'),
modify : fm.i18n('modify'), modify: fm.i18n('modify'),
perms : fm.i18n('perms'), perms: fm.i18n('perms'),
locked : fm.i18n('locked'), locked: fm.i18n('locked'),
dim : fm.i18n('dim'), dim: fm.i18n('dim'),
kind : fm.i18n('kind'), kind: fm.i18n('kind'),
files : fm.i18n('files'), files: fm.i18n('files'),
folders : fm.i18n('folders'), folders: fm.i18n('folders'),
items : fm.i18n('items'), items: fm.i18n('items'),
yes : fm.i18n('yes'), yes: fm.i18n('yes'),
no : fm.i18n('no'), no: fm.i18n('no'),
link : fm.i18n('link'), link: fm.i18n('link'),
owner : fm.i18n('owner'), owner: fm.i18n('owner'),
desc : fm.i18n('description'), desc: fm.i18n('description'),
downloads: fm.i18n('downloads') downloads: fm.i18n('downloads')
}; };
this.tpl = {
main : '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div><table class="elfinder-info-tb">{content}</table>',
itemTitle : '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>',
groupTitle : '<strong>{items}: {num}</strong>',
row : '<tr><td>{label} : </td><td>{value}</td></tr>',
spinner : '<span>{text}</span> <span class="'+spclass+' '+spclass+'-'+'{id}"/>'
}
this.alwaysEnabled = true;
this.updateOnSelect = false;
this.shortcuts = [{
pattern : 'ctrl+i'
}];
this.init = function() {
$.each(msg, function(k, v) {
msg[k] = fm.i18n(v);
});
}
this.getstate = function() {
return 0;
}
this.exec = function(hashes) {
var self = this, this.tpl = {
fm = this.fm, main: '<div class="ui-helper-clearfix elfinder-info-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}</div><table class="elfinder-info-tb">{content}</table>',
tpl = this.tpl, itemTitle: '<strong>{name}</strong><span class="elfinder-info-kind">{kind}</span>',
row = tpl.row, groupTitle: '<strong>{items}: {num}</strong>',
files = this.files(hashes), row: '<tr><td>{label} : </td><td>{value}</td></tr>',
cnt = files.length, spinner: '<span>{text}</span> <span class="' + spclass + ' ' + spclass + '-' + '{id}"/>'
content = [], }
view = tpl.main,
l = '{label}', this.alwaysEnabled = true;
v = '{value}', this.updateOnSelect = false;
opts = { this.shortcuts = [{
title : this.title, pattern: 'ctrl+i'
width : 'auto', }];
modal : true,
this.init = function() {
$.each(msg, function(k, v) {
msg[k] = fm.i18n(v);
});
}
this.getstate = function() {
return 0;
}
this.exec = function(hashes) {
var self = this,
fm = this.fm,
tpl = this.tpl,
row = tpl.row,
files = this.files(hashes),
cnt = files.length,
content = [],
view = tpl.main,
l = '{label}',
v = '{value}',
opts = {
title: this.title,
width: 'auto',
modal: true,
close: function() { close: function() {
$(this).elfinderdialog('destroy'); $(this).elfinderdialog('destroy');
} }
}, },
count = [], count = [],
replSpinner = function(msg) { replSpinner = function(msg) {
dialog.find('.' + spclass).parent().text(msg); dialog.find('.' + spclass).parent().text(msg);
}, },
@ -88,208 +94,208 @@ elFinder.prototype.commands.info = function() {
id = fm.namespace + '-info-' + $.map(files, function(f) { id = fm.namespace + '-info-' + $.map(files, function(f) {
return f.hash return f.hash
}).join('-'), }).join('-'),
dialog = fm.getUI().find('#'+id), dialog = fm.getUI().find('#' + id),
size, tmb, file, title, dcnt; size, tmb, file, title, dcnt;
var disabledCommands = Drupal.settings.elfinder.disabledCommands, var disabledCommands = Drupal.settings.elfinder.disabledCommands,
viewDesc = $.inArray('desc', disabledCommands) == -1 ? true : false, viewDesc = $.inArray('desc', disabledCommands) == -1 ? true : false,
editDesc = $.inArray('editdesc', disabledCommands) == -1 ? true : false, editDesc = $.inArray('editdesc', disabledCommands) == -1 ? true : false,
viewOwner = $.inArray('owner', disabledCommands) == -1 ? true : false, viewOwner = $.inArray('owner', disabledCommands) == -1 ? true : false,
viewDownloads = $.inArray('downloadcount', disabledCommands) == -1 ? true : false; viewDownloads = $.inArray('downloadcount', disabledCommands) == -1 ? true : false;
if (!cnt) {
return $.Deferred().reject();
}
if (dialog.length) {
dialog.elfinderdialog('toTop');
return $.Deferred().resolve();
}
if (cnt == 1) {
file = files[0];
view = view.replace('{class}', fm.mime2class(file.mime));
title = tpl.itemTitle.replace('{name}', file.name).replace('{kind}', fm.mime2kind(file));
if (file.tmb) { if (!cnt) {
tmb = fm.option('tmbUrl')+file.tmb; return $.Deferred().reject();
}
if (!file.read) {
size = msg.unknown;
} else if (file.mime != 'directory' || file.alias) {
size = fm.formatSize(file.size);
} else {
/* adding spinner id to separate field updates */
size = tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'size');
count.push(file.hash);
}
content.push(row.replace(l, msg.size).replace(v, size));
file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias));
content.push(row.replace(l, msg.path).replace(v, fm.escape(fm.path(file.hash))));
file.read && content.push(row.replace(l, msg.link).replace(v, '<a href="'+fm.url(file.hash)+'" target="_blank">'+file.name+'</a>'));
if (file.dim) { // old api
content.push(row.replace(l, msg.dim).replace(v, file.dim));
} else if (file.mime.indexOf('image') !== -1) {
content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'dim')));
fm.request({
data : {cmd : 'dim', target : file.hash},
preventDefault : true
})
.fail(function() {
replSpinnerById(msg.unknown, 'dim');
})
.done(function(data) {
replSpinnerById(data.dim || msg.unknown, 'dim');
});
}
content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file)));
content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file)));
content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no));
/* Don't show fields if command not allowed */
if (viewOwner) {
content.push(row.replace(l, Drupal.t('Owner')).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'owner')));
fm.request({
data : {cmd : 'owner', target : file.hash},
preventDefault : true
})
.fail(function() {
replSpinnerById(msg.unknown, 'owner');
})
.done(function(data) {
replSpinnerById(data.owner || msg.unknown, 'owner');
});
} }
/* Don't show fields if command not allowed */ if (dialog.length) {
if (viewDownloads) { dialog.elfinderdialog('toTop');
content.push(row.replace(l, Drupal.t('Downloads')).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'downloads'))); return $.Deferred().resolve();
fm.request({
data : {cmd : 'downloadcount', target : file.hash},
preventDefault : true
})
.fail(function() {
replSpinnerById(msg.unknown, 'downloads');
})
.done(function(data) {
replSpinnerById(data.downloadcount || msg.unknown, 'downloads');
});
} }
/* Don't show fields if command not allowed */
if (viewDesc || editDesc) { if (cnt == 1) {
file = files[0];
var desc = '<div id="elfinder-fm-file-desc">' + msg.calc + '</div>'; view = view.replace('{class}', fm.mime2class(file.mime));
title = tpl.itemTitle.replace('{name}', file.name).replace('{kind}', fm.mime2kind(file));
if (editDesc) {
if (file.tmb) {
desc = '<textarea cols="20" rows="5" id="elfinder-fm-file-desc" class="ui-widget ui-widget-content" disabled="true" >' + msg.calc + '</textarea><input type="button" id="elfinder-fm-file-desc-btn-save" value="' + fm.i18n('btnSave') + '" />'; tmb = fm.option('tmbUrl') + file.tmb;
} }
content.push(row.replace(l, Drupal.t('Description')).replace(v , desc)); if (!file.read) {
size = msg.unknown;
fm.request({ } else if (file.mime != 'directory' || file.alias) {
data : {cmd : 'desc', target : file.hash}, size = fm.formatSize(file.size);
preventDefault : true } else {
}) /* adding spinner id to separate field updates */
.done(function(data) { size = tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'size');
var fieldDesc = dialog.find('#elfinder-fm-file-desc'); count.push(file.hash);
}
if (editDesc) {
fieldDesc.val(data.desc || ''); content.push(row.replace(l, msg.size).replace(v, size));
fieldDesc.removeAttr('disabled'); file.alias && content.push(row.replace(l, msg.aliasfor).replace(v, file.alias));
} else { content.push(row.replace(l, msg.path).replace(v, fm.escape(fm.path(file.hash))));
fieldDesc.empty(); file.read && content.push(row.replace(l, msg.link).replace(v, '<a href="' + fm.url(file.hash) + '" target="_blank">' + file.name + '</a>'));
fieldDesc.html(data.desc || '');
} if (file.dim) { // old api
}); content.push(row.replace(l, msg.dim).replace(v, file.dim));
} else if (file.mime.indexOf('image') !== -1) {
} content.push(row.replace(l, msg.dim).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'dim')));
fm.request({
} else { data: {cmd: 'dim', target: file.hash},
view = view.replace('{class}', 'elfinder-cwd-icon-group'); preventDefault: true
title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt); })
.fail(function() {
replSpinnerById(msg.unknown, 'dim');
})
.done(function(data) {
replSpinnerById(data.dim || msg.unknown, 'dim');
});
}
content.push(row.replace(l, msg.modify).replace(v, fm.formatDate(file)));
content.push(row.replace(l, msg.perms).replace(v, fm.formatPermissions(file)));
content.push(row.replace(l, msg.locked).replace(v, file.locked ? msg.yes : msg.no));
/* Don't show fields if command not allowed */
if (viewOwner) {
content.push(row.replace(l, Drupal.t('Owner')).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'owner')));
fm.request({
data: {cmd: 'owner', target: file.hash},
preventDefault: true
})
.fail(function() {
replSpinnerById(msg.unknown, 'owner');
})
.done(function(data) {
replSpinnerById(data.owner || msg.unknown, 'owner');
});
}
/* Don't show fields if command not allowed */
if (viewDownloads) {
content.push(row.replace(l, Drupal.t('Downloads')).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'downloads')));
fm.request({
data: {cmd: 'downloadcount', target: file.hash},
preventDefault: true
})
.fail(function() {
replSpinnerById(msg.unknown, 'downloads');
})
.done(function(data) {
replSpinnerById(data.downloadcount || msg.unknown, 'downloads');
});
}
/* Don't show fields if command not allowed */
if (viewDesc || editDesc) {
var desc = '<div id="elfinder-fm-file-desc">' + msg.calc + '</div>';
if (editDesc) {
desc = '<textarea cols="20" rows="5" id="elfinder-fm-file-desc" class="ui-widget ui-widget-content" disabled="true" >' + msg.calc + '</textarea><input type="button" id="elfinder-fm-file-desc-btn-save" value="' + fm.i18n('btnSave') + '" />';
}
content.push(row.replace(l, Drupal.t('Description')).replace(v, desc));
fm.request({
data: {cmd: 'desc', target: file.hash},
preventDefault: true
})
.done(function(data) {
var fieldDesc = dialog.find('#elfinder-fm-file-desc');
if (editDesc) {
fieldDesc.val(data.desc || '');
fieldDesc.removeAttr('disabled');
} else {
fieldDesc.empty();
fieldDesc.html(data.desc || '');
}
});
}
} else {
view = view.replace('{class}', 'elfinder-cwd-icon-group');
title = tpl.groupTitle.replace('{items}', msg.items).replace('{num}', cnt);
dcnt = $.map(files, function(f) { dcnt = $.map(files, function(f) {
return f.mime == 'directory' ? 1 : null return f.mime == 'directory' ? 1 : null
}).length; }).length;
if (!dcnt) { if (!dcnt) {
size = 0; size = 0;
$.each(files, function(h, f) { $.each(files, function(h, f) {
var s = parseInt(f.size); var s = parseInt(f.size);
if (s >= 0 && size >= 0) { if (s >= 0 && size >= 0) {
size += s; size += s;
} else { } else {
size = 'unknown'; size = 'unknown';
} }
}); });
content.push(row.replace(l, msg.kind).replace(v, msg.files)); content.push(row.replace(l, msg.kind).replace(v, msg.files));
content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size))); content.push(row.replace(l, msg.size).replace(v, fm.formatSize(size)));
} else { } else {
content.push(row.replace(l, msg.kind).replace(v, dcnt == cnt ? msg.folders : msg.folders+' '+dcnt+', '+msg.files+' '+(cnt-dcnt))) content.push(row.replace(l, msg.kind).replace(v, dcnt == cnt ? msg.folders : msg.folders + ' ' + dcnt + ', ' + msg.files + ' ' + (cnt - dcnt)))
content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'size'))); content.push(row.replace(l, msg.size).replace(v, tpl.spinner.replace('{text}', msg.calc).replace('{id}', 'size')));
count = $.map(files, function(f) { count = $.map(files, function(f) {
return f.hash return f.hash
}); });
} }
} }
view = view.replace('{title}', title).replace('{content}', content.join('')); view = view.replace('{title}', title).replace('{content}', content.join(''));
dialog = fm.dialog(view, opts); dialog = fm.dialog(view, opts);
dialog.attr('id', id); dialog.attr('id', id);
if (editDesc) { if (editDesc) {
var inputDesc = $('#elfinder-fm-file-desc', dialog); var inputDesc = $('#elfinder-fm-file-desc', dialog);
var btnSave = $('#elfinder-fm-file-desc-btn-save', dialog).button(); var btnSave = $('#elfinder-fm-file-desc-btn-save', dialog).button();
btnSave.click(function() { btnSave.click(function() {
fm.lockfiles({files : [file.hash]}); fm.lockfiles({files: [file.hash]});
fm.request({ fm.request({
data : {cmd : 'desc', target : file.hash, content : inputDesc.val()}, data: {cmd: 'desc', target: file.hash, content: inputDesc.val()},
notify : {type : 'desc', cnt : 1} notify: {type: 'desc', cnt: 1}
}) })
.always(function() { .always(function() {
fm.unlockfiles({files : [file.hash]}) fm.unlockfiles({files: [file.hash]})
}); });
}); });
} }
// load thumbnail // load thumbnail
if (tmb) { if (tmb) {
$('<img/>') $('<img/>')
.load(function() { .load(function() {
dialog.find('.elfinder-cwd-icon').css('background', 'url("' + tmb + '") center center no-repeat'); dialog.find('.elfinder-cwd-icon').css('background', 'url("' + tmb + '") center center no-repeat');
}) })
.attr('src', tmb); .attr('src', tmb);
} }
// send request to count total size // send request to count total size
if (count.length) { if (count.length) {
fm.request({ fm.request({
data : {cmd : 'size', targets : count}, data: {cmd: 'size', targets: count},
preventDefault : true preventDefault: true
}) })
.fail(function() { .fail(function() {
replSpinnerById(msg.unknown, 'size'); replSpinnerById(msg.unknown, 'size');
}) })
.done(function(data) { .done(function(data) {
var size = parseInt(data.size); var size = parseInt(data.size);
fm.log(data.size) fm.log(data.size)
replSpinnerById(size >= 0 ? fm.formatSize(size) : msg.unknown, 'size'); replSpinnerById(size >= 0 ? fm.formatSize(size) : msg.unknown, 'size');
}); });
} }
} }
} }
})(jQuery); })(jQuery);

View File

@ -3,12 +3,14 @@ name = elFinder BUEditor integration
description = Adding elFinder to BUEditor Insert Image dialog description = Adding elFinder to BUEditor Insert Image dialog
package = User interface package = User interface
core = 7.x core = 7.x
project status url = https://drupal-elfinder.sourceforge.io/release-history.php
dependencies[] = bueditor dependencies[] = bueditor
dependencies[] = elfinder dependencies[] = elfinder
; Information added by Drupal.org packaging script on 2018-12-03 ; Information added by Drupal.org packaging script on 2017-11-16
version = "7.x-2.x-dev" version = "7.x-2.x-dev"
core = "7.x" core = "7.x"
project = "elfinder" project = "elfinder"
datestamp = "1543843700" datestamp = "1555577224"

View File

@ -1,15 +1,16 @@
<?php <?php
// $Id: elfinder.module 102 2011-02-20 20:11:52Z ph0enix $
/** /**
* BUEditor support for elFinder * elFinder Integration
* Copyright (c) 2011, Alexey Sukhotin *
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/ */
// $Id: elfinder.module 102 2011-02-20 20:11:52Z ph0enix $
function elfinder_bueditor_elfinder_js_settings($settings) { function elfinder_bueditor_elfinder_js_settings($settings) {
$newsettings['editorCallback'] = 'elfinder_bue_callback'; $newsettings['editorCallback'] = 'elfinder_bue_callback';
$newsettings['browserscripts'][] = drupal_get_path('module', 'elfinder') . '/editors/bueditor/bueditor.callback.js'; $newsettings['browserscripts'][] = drupal_get_path('module', 'elfinder') . '/editors/bueditor/bueditor.callback.js';
return array('bue' => $newsettings, 'bueditor' => $newsettings); return array('bue' => $newsettings, 'bueditor' => $newsettings);
@ -18,7 +19,7 @@ function elfinder_bueditor_elfinder_js_settings($settings) {
/* D7 Support */ /* D7 Support */
function elfinder_bueditor_init() { function elfinder_bueditor_init() {
$settings = array(); $settings = array();
$settings['BUE']['imceURL'] = url('elfinder', array('query' => array('app' => 'bueditor' ))); $settings['BUE']['imceURL'] = url('elfinder', array('query' => array('app' => 'bueditor')));
drupal_add_js($settings, 'setting'); drupal_add_js($settings, 'setting');
} }

View File

@ -2,12 +2,14 @@ name = elFinder File Field Source
description = File Field Sources elFinder connector description = File Field Sources elFinder connector
package = User interface package = User interface
core = 7.x core = 7.x
project status url = https://drupal-elfinder.sourceforge.io/release-history.php
dependencies[] = filefield_sources dependencies[] = filefield_sources
dependencies[] = elfinder dependencies[] = elfinder
; Information added by Drupal.org packaging script on 2018-12-03 ; Information added by Drupal.org packaging script on 2017-11-16
version = "7.x-2.x-dev" version = "7.x-2.x-dev"
core = "7.x" core = "7.x"
project = "elfinder" project = "elfinder"
datestamp = "1543843700" datestamp = "1555577224"

View File

@ -1,4 +1,9 @@
<?php <?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// Main hooks to tell FielField Sources about our source // Main hooks to tell FielField Sources about our source
function elfinder_ffsel_filefield_sources_info() { function elfinder_ffsel_filefield_sources_info() {
@ -52,11 +57,11 @@ function filefield_source_elfinder_info() {
function filefield_source_elfinder_menu() { function filefield_source_elfinder_menu() {
$items = array(); $items = array();
$items['filefield/elfinder/%/%'] = array( $items['filefield/elfinder/%/%'] = array(
'page callback' => 'filefield_source_elfinder_page', 'page callback' => 'filefield_source_elfinder_page',
'page arguments' => array(2, 3), 'page arguments' => array(2, 3),
'access callback' => 'filefield_edit_access', 'access callback' => 'filefield_edit_access',
'access arguments' => array(2, 3), 'access arguments' => array(2, 3),
'type' => MENU_CALLBACK, 'type' => MENU_CALLBACK,
); );
return $items; return $items;
} }
@ -66,9 +71,9 @@ function filefield_source_elfinder_menu() {
*/ */
function elfinder_ffsel_theme() { function elfinder_ffsel_theme() {
return array( return array(
'filefield_source_elfinder_element' => array( 'filefield_source_elfinder_element' => array(
'render element' => 'element', 'render element' => 'element',
), ),
); );
} }
@ -90,58 +95,58 @@ function filefield_source_elfinder_process($element, &$form_state, $form) {
$instance = field_widget_instance($element, $form_state); $instance = field_widget_instance($element, $form_state);
$element['filefield_elfinder'] = array( $element['filefield_elfinder'] = array(
'#weight' => 100.5, '#weight' => 100.5,
'#theme' => 'filefield_source_elfinder_element', '#theme' => 'filefield_source_elfinder_element',
'#filefield_source' => TRUE, // Required for proper theming. '#filefield_source' => TRUE, // Required for proper theming.
'#description' => filefield_sources_element_validation_help($element['#upload_validators']), '#description' => filefield_sources_element_validation_help($element['#upload_validators']),
); );
$filepath_id = $element['#id'] . '-elfinder-path'; $filepath_id = $element['#id'] . '-elfinder-path';
$display_id = $element['#id'] . '-elfinder-display'; $display_id = $element['#id'] . '-elfinder-display';
$select_id = $element['#id'] . '-elfinder-select'; $select_id = $element['#id'] . '-elfinder-select';
$element['filefield_elfinder']['file_path'] = array( $element['filefield_elfinder']['file_path'] = array(
'#type' => 'hidden', '#type' => 'hidden',
'#attributes' => array( '#attributes' => array(
'id' => $filepath_id, 'id' => $filepath_id,
'onchange' => "if (!jQuery('#$select_id').attr('disabled')) { jQuery('#$select_id').mousedown().attr('disabled', true); jQuery('#$display_id').html(this.value); }", 'onchange' => "if (!jQuery('#$select_id').attr('disabled')) { jQuery('#$select_id').mousedown().attr('disabled', true); jQuery('#$display_id').html(this.value); }",
), ),
'#value_callback' => 'elfinder_ffsel_element_value', '#value_callback' => 'elfinder_ffsel_element_value',
'#upload_validators' => $element['#upload_validators'], '#upload_validators' => $element['#upload_validators'],
'#maxlength' => NULL, '#maxlength' => NULL,
); );
$width = variable_get('elfinder_settings_misc_manager_width', ''); $width = variable_get('elfinder_settings_misc_manager_width', '');
if(!$width) { if (!$width) {
$width = ELFINDER_POPUP_WIDTH; $width = ELFINDER_POPUP_WIDTH;
} }
$height = variable_get('elfinder_settings_misc_manager_height', ''); $height = variable_get('elfinder_settings_misc_manager_height', '');
if(!$height) { if (!$height) {
$height = ELFINDER_POPUP_HEIGHT; $height = ELFINDER_POPUP_HEIGHT;
} }
$elfinder_function = 'var left = window.screenX + (window.innerWidth / 2) - (' . $width . ' / 2); var top = window.screenY + (window.innerHeight / 2) - (' . $height . ' / 2); window.open(\'' . url('elfinder', array('query' => array('app' => 'ffs', 'field_name' => $element['#field_name'], 'filepath_id' => $filepath_id, 'select_id' => $select_id))) . '\', \'\', \'width=' . $width . ',height=' . $height . ',top=\'+top+\',left=\'+left+\',resizable=1\'); return false;'; $elfinder_function = 'var left = window.screenX + (window.innerWidth / 2) - (' . $width . ' / 2); var top = window.screenY + (window.innerHeight / 2) - (' . $height . ' / 2); window.open(\'' . url('elfinder', array('query' => array('app' => 'ffs', 'field_name' => $element['#field_name'], 'filepath_id' => $filepath_id, 'select_id' => $select_id))) . '\', \'\', \'width=' . $width . ',height=' . $height . ',top=\'+top+\',left=\'+left+\',resizable=1\'); return false;';
$element['filefield_elfinder']['display_path'] = array( $element['filefield_elfinder']['display_path'] = array(
'#type' => 'markup', '#type' => 'markup',
'#markup' => '<span id="' . $display_id . '" class="filefield-sources-elfinder-display">' . t('No file selected') . '</span> (<a class="filefield-sources-elfinder-browse" href="#" onclick="' . $elfinder_function . '">' . t('browse') . '</a>)', '#markup' => '<span id="' . $display_id . '" class="filefield-sources-elfinder-display">' . t('No file selected') . '</span> (<a class="filefield-sources-elfinder-browse" href="#" onclick="' . $elfinder_function . '">' . t('browse') . '</a>)',
); );
$element['filefield_elfinder']['upload_button'] = array( $element['filefield_elfinder']['upload_button'] = array(
'#name' => implode('_', $element['#array_parents']) . '_elfinder_select', '#name' => implode('_', $element['#array_parents']) . '_elfinder_select',
'#type' => 'submit', '#type' => 'submit',
'#value' => t('Select'), '#value' => t('Select'),
'#validate' => array(), '#validate' => array(),
'#submit' => array('filefield_sources_field_submit'), '#submit' => array('filefield_sources_field_submit'),
'#limit_validation_errors' => array($element['#parents']), '#limit_validation_errors' => array($element['#parents']),
'#name' => $element['#name'] . '[filefield_elfinder][button]', '#name' => $element['#name'] . '[filefield_elfinder][button]',
'#id' => $select_id, '#id' => $select_id,
'#attributes' => array('style' => 'display: none;'), '#attributes' => array('style' => 'display: none;'),
'#ajax' => array( '#ajax' => array(
'path' => 'file/ajax/' . implode('/', $element['#array_parents']) . '/' . $form['form_build_id']['#value'], 'path' => 'file/ajax/' . implode('/', $element['#array_parents']) . '/' . $form['form_build_id']['#value'],
'wrapper' => $element['upload_button']['#ajax']['wrapper'], 'wrapper' => $element['upload_button']['#ajax']['wrapper'],
'method' => 'replace', 'method' => 'replace',
'effect' => 'fade', 'effect' => 'fade',
), ),
); );
return $element; return $element;
@ -163,21 +168,21 @@ function filefield_source_elfinder_value($element, &$item) {
// Resolve the file path to an FID. // Resolve the file path to an FID.
$fid = db_select('file_managed', 'f') $fid = db_select('file_managed', 'f')
->condition('uri', rawurldecode($uri)) ->condition('uri', rawurldecode($uri))
->fields('f', array('fid')) ->fields('f', array('fid'))
->execute() ->execute()
->fetchField(); ->fetchField();
if ($fid) { if ($fid) {
$file = file_load($fid); $file = file_load($fid);
if (filefield_sources_element_validate($element, $file)) { if (filefield_sources_element_validate($element, $file)) {
$item = array_merge($item, (array) $file); $item = array_merge($item, (array)$file);
} }
} else { } else {
$local_root = elfinder_document_root() . '/' . $file_directory_prefix . '/'; $local_root = elfinder_document_root() . '/' . $file_directory_prefix . '/';
$file_path = preg_replace('/.*(' . preg_quote('/' . $file_directory_prefix . '/', '/') . ')/', $local_root, $item['filefield_elfinder']['file_path']); $file_path = preg_replace('/.*(' . preg_quote('/' . $file_directory_prefix . '/', '/') . ')/', $local_root, $item['filefield_elfinder']['file_path']);
if ($file = filefield_sources_save_file($file_path, $element['#upload_validators'], $element['#upload_location'], FILE_EXISTS_REPLACE)) { if ($file = filefield_sources_save_file($file_path, $element['#upload_validators'], $element['#upload_location'], FILE_EXISTS_REPLACE)) {
$item = array_merge($item, (array) $file); $item = array_merge($item, (array)$file);
drupal_set_message(t('File created')); drupal_set_message(t('File created'));
} else { } else {
form_error($element, t('The selected file could not be used because the file does not exist in the database.')); form_error($element, t('The selected file could not be used because the file does not exist in the database.'));
@ -237,7 +242,7 @@ function elfinder_ffsel_elfinder_js_settings($settings) {
*/ */
function elfinder_ffsel_element_value($element, $input = FALSE, &$form_state = NULL) { function elfinder_ffsel_element_value($element, $input = FALSE, &$form_state = NULL) {
if(empty($input)) { if (empty($input)) {
return array(); return array();
} }
@ -265,22 +270,22 @@ function elfinder_ffsel_element_value($element, $input = FALSE, &$form_state = N
$files = explode('%%', $input); $files = explode('%%', $input);
foreach($files AS $delta => $file) { foreach ($files AS $delta => $file) {
// Respect the field's count limit. // Respect the field's count limit.
if($upload_delta == $field['cardinality']) { if ($upload_delta == $field['cardinality']) {
break; break;
} }
$uri = preg_replace('/.*(' . preg_quote('/' . $file_directory_prefix . '/', '/') . ')/', $scheme . '://', $file); $uri = preg_replace('/.*(' . preg_quote('/' . $file_directory_prefix . '/', '/') . ')/', $scheme . '://', $file);
// Resolve the file path to an FID. // Resolve the file path to an FID.
$fid = db_select('file_managed', 'f') $fid = db_select('file_managed', 'f')
->condition('uri', rawurldecode($uri)) ->condition('uri', rawurldecode($uri))
->fields('f', array('fid')) ->fields('f', array('fid'))
->execute() ->execute()
->fetchField(); ->fetchField();
if ($fid) { if ($fid) {
$file = file_load($fid); $file = file_load($fid);
if (filefield_sources_element_validate($element, $file)) { if (filefield_sources_element_validate($element, $file)) {
$items[$upload_delta] = (array) $file; $items[$upload_delta] = (array)$file;
$items[$upload_delta]['_weight'] = $upload_delta; $items[$upload_delta]['_weight'] = $upload_delta;
$upload_delta++; $upload_delta++;
} }
@ -289,7 +294,7 @@ function elfinder_ffsel_element_value($element, $input = FALSE, &$form_state = N
$file_path = preg_replace('/.*(' . preg_quote('/' . $file_directory_prefix . '/', '/') . ')/', $local_root, $file); $file_path = preg_replace('/.*(' . preg_quote('/' . $file_directory_prefix . '/', '/') . ')/', $local_root, $file);
if ($file = filefield_sources_save_file($file_path, $element['#upload_validators'], $element['#upload_location'], FILE_EXISTS_REPLACE)) { if ($file = filefield_sources_save_file($file_path, $element['#upload_validators'], $element['#upload_location'], FILE_EXISTS_REPLACE)) {
$items[$upload_delta] = (array) $file; $items[$upload_delta] = (array)$file;
$items[$upload_delta]['_weight'] = $upload_delta; $items[$upload_delta]['_weight'] = $upload_delta;
$upload_delta++; $upload_delta++;
drupal_set_message(t('File created')); drupal_set_message(t('File created'));

View File

@ -1,26 +1,39 @@
/*
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
function elfinder_ffs_callback(arg1) { function elfinder_ffs_callback(arg1) {
var fieldName = Drupal.settings.elfinder.field_name;
var fieldId = Drupal.settings.elfinder.filepath_id;
var fieldName = Drupal.settings.elfinder.field_name; var url = arg1;
var fieldId = Drupal.settings.elfinder.filepath_id;
var url = arg1; if (typeof arg1 == 'object') {
if ($.isArray(arg1)) {
url = arg1[0];
} else {
url = arg1.url;
}
}
if (typeof arg1 == 'object') { var filePath = url;
url = arg1.url;
}
var filePath = url;
/* Needs rework: must support both classic single file selection and multiple selection */
//var filePath = arg1.join('%%');
window.opener.jQuery('input#'+fieldId).val(filePath).change(); /* Needs rework: must support both classic single file selection and multiple selection */
window.opener.focus(); //var filePath = arg1.join('%%');
// Avoid beforeunload event when selecting an image. if (typeof filePath == 'undefined') {
// https://github.com/Studio-42/elFinder/issues/1340 console.log('Undefined filePath, please check integration');
// Maybe remove this when elfinder js library gets updated. }
//$(window).off('beforeunload');
window.close(); window.opener.jQuery('input#' + fieldId).val(filePath).change();
window.opener.focus();
// Avoid beforeunload event when selecting an image.
// https://github.com/Studio-42/elFinder/issues/1340
// Maybe remove this when elfinder js library gets updated.
//$(window).off('beforeunload');
window.close();
} }

View File

@ -0,0 +1,14 @@
name = elFinder TinyMCE integration
description = Extended tinyMCE integration
package = User interface
core = 7.x
project status url = https://drupal-elfinder.sourceforge.io/release-history.php
dependencies[] = elfinder
; Information added by Drupal.org packaging script on 2017-11-16
version = "7.x-2.x-dev"
core = "7.x"
project = "elfinder"
datestamp = "1555577224"

View File

@ -0,0 +1,24 @@
<?php
/**
* elFinder Integration
*
* Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/
// $Id: elfinder.module 102 2011-02-20 20:11:52Z ph0enix $
function elfinder_tinymce_elfinder_js_settings($settings) {
// $newsettings['editorCallback'] = 'elfinder_bue_callback';
$newsettings['browserscripts'][] = drupal_get_path('module', 'elfinder') . '/editors/bueditor/bueditor.callback.js';
return array('bue' => $newsettings, 'bueditor' => $newsettings);
}
/* D7 Support */
function elfinder_tinymce_init() {
$settings = array();
drupal_add_js($settings, 'setting');
}

View File

@ -1,9 +1,9 @@
<?php <?php
/** /**
* @file * elFinder Integration
* elFinder file browser page template *
* Copyright (c) 2010, Alexey Sukhotin * Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/ */
?> ?>
<div id="finder"></div> <div id="finder"></div>

View File

@ -1,26 +1,26 @@
<?php <?php
/** /**
* @file * elFinder Integration
* elFinder file browser page template *
* Copyright (c) 2010, Alexey Sukhotin * Copyright (c) 2010-2018, Alexey Sukhotin. All rights reserved.
*/ */
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> ?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" <html xmlns="http://www.w3.org/1999/xhtml"
lang="<?php print isset($language->language) ? $language->language : 'en'; ?>" lang="<?php print isset($language->language) ? $language->language : 'en'; ?>"
xml:lang="<?php print isset($language->language) ? $language->language : 'en'; ?>"> xml:lang="<?php print isset($language->language) ? $language->language : 'en'; ?>">
<head> <head>
<title><?php print t('File Browser'); ?></title> <title><?php print t('File Browser'); ?></title>
<?php <?php
print drupal_get_html_head(); print drupal_get_html_head();
print elfinder_browser_css(); print elfinder_browser_css();
print elfinder_browser_js(); print elfinder_browser_js();
?> ?>
<!-- make inline popup WORK --> <!-- make inline popup WORK -->
</head> </head>
<body class="elfinder"> <body class="elfinder">
<div id="elfinder-messages"><?php print theme('status_messages'); ?></div> <div id="elfinder-messages"><?php print theme('status_messages'); ?></div>
<div id="finder"></div> <div id="finder"></div>
<?php print drupal_get_js('footer'); ?> <?php print drupal_get_js('footer'); ?>
</body> </body>
</html> </html>

View File

@ -1,3 +1,29 @@
Next release
============
Features and enhancements
- #2552189 - Return migration status from drush migrate-import.
- #2550793 - Option to always use chunk IDs in MigrateContentParser.
- #2505683 - Pass item ID to MigrateContentParser implementations.
- #2532222 - Allow spreadsheet source data on a row different that 1.
- #1406802 - Add callback() field mapping method, to pass additional arguments
to callbacks.
- #2516828 - Add mdreg alias for migrate-deregister.
- #2504517 - Add timezone handling to timestamp().
Bug fixes
- #2612110 - Notice when units omitted on --limit.
- #2597606 - Escape MySQL database names.
- #2577091 - Do strict type-check on XML ids.
- #2537206 - copy() return not checked.
- #2536616 - Improve message when sourceMigration not found.
- #2578391 - Improve message when class not found.
- #2565043 - Handle empty idlist in MigrateSourceMultiItems.
- #2510010 - Stop stripping group name prefixes from migration names.
- #2542520 - Fallback for missing source key description.
- #2499861 - Properly save/disable/restore mail system.
- #2541996 - Prevent NULL file_mask being passed to file_scan_directory().
Migrate 2.8 Migrate 2.8
=========== ===========

View File

@ -1,15 +1,17 @@
The Migrate module provides a flexible framework for migrating content into
The Migrate module provides a flexible framework for migrating content into Drupal Drupal from other sources
from other sources (e.g., when converting a web site from another CMS to Drupal). (e.g., when converting a web site from another CMS to Drupal).
Out-of-the-box, support for creating Drupal nodes, taxonomy terms, comments, and Out-of-the-box, support for creating Drupal nodes, taxonomy terms, comments, and
users are included. Plugins permit migration of other types of content. users are included. Plugins permit migration of other types of content.
Usage Usage
----- -----
Documentation is at http://drupal.org/migrate. To get started, enable the Documentation is at http://drupal.org/migrate. To get started, enable the
migrate_example module and browse to admin/content/migrate to see its dashboard. migrate_example module, enable migrate_ui, and then browse to
The code for this migration is in migrate_example/beer.inc (advanced examples are admin/content/migrate to see its dashboard.
in wine.inc). Mimic that file in order to specify your own migrations. The code for this migration is in migrate_example/beer.inc
(advanced examples are in wine.inc). Mimic that file in order to specify your
own migrations.
The Migrate module itself has support for migration into core objects. Support The Migrate module itself has support for migration into core objects. Support
for migration involving contrib modules is in the migrate_extras module. for migration involving contrib modules is in the migrate_extras module.
@ -17,8 +19,8 @@ for migration involving contrib modules is in the migrate_extras module.
Known issues Known issues
------------ ------------
A user migration with systemOfRecord == DESTINATION will drop pictures from user A user migration with systemOfRecord == DESTINATION will drop pictures from user
records due to core bug http://drupal.org/node/935592 - the simpletests report an records due to core bug http://drupal.org/node/935592 - the simpletests report
error reflecting this. We have not developed a work-around. an error reflecting this. We have not developed a work-around.
Upgrading Upgrading
--------- ---------
@ -30,9 +32,9 @@ projects.
Acknowledgements Acknowledgements
---------------- ----------------
Much of the Migrate module functionality was sponsored by Cyrve, for its clients GenomeWeb Much of the Migrate module functionality was sponsored by Cyrve, for its clients
(http://www.genomeweb.com), The Economist (http://www.economist.com), and Examiner.com GenomeWeb (http://www.genomeweb.com), The Economist (http://www.economist.com),
(http://www.examiner.com). and Examiner.com (http://www.examiner.com).
Authors Authors
------- -------

View File

@ -8,11 +8,12 @@
/** /**
* The base class for all objects representing distinct steps in a migration * The base class for all objects representing distinct steps in a migration
* process. Most commonly these will be Migration objects which actually import * process. Most commonly these will be Migration objects which actually import
* data from a source into a Drupal destination, but by deriving classes directly * data from a source into a Drupal destination, but by deriving classes
* from MigrationBase one can have other sorts of tasks (e.g., enabling/disabling * directly from MigrationBase one can have other sorts of tasks (e.g.,
* of modules) occur during the migration process. * enabling/disabling of modules) occur during the migration process.
*/ */
abstract class MigrationBase { abstract class MigrationBase {
/** /**
* Track the migration currently running, so handlers can easily determine it * Track the migration currently running, so handlers can easily determine it
* without having to pass a Migration object everywhere. * without having to pass a Migration object everywhere.
@ -20,18 +21,21 @@ abstract class MigrationBase {
* @var Migration * @var Migration
*/ */
protected static $currentMigration; protected static $currentMigration;
public static function currentMigration() { public static function currentMigration() {
return self::$currentMigration; return self::$currentMigration;
} }
/** /**
* The machine name of this Migration object, derived by removing the 'Migration' * The machine name of this Migration object, derived by removing the
* suffix from the class name. Used to construct default map/message table names, * 'Migration' suffix from the class name. Used to construct default
* displayed in drush migrate-status, key to migrate_status table... * map/message table names, displayed in drush migrate-status, key to
* migrate_status table...
* *
* @var string * @var string
*/ */
protected $machineName; protected $machineName;
public function getMachineName() { public function getMachineName() {
return $this->machineName; return $this->machineName;
} }
@ -42,6 +46,7 @@ abstract class MigrationBase {
* @var MigrateGroup * @var MigrateGroup
*/ */
protected $group; protected $group;
public function getGroup() { public function getGroup() {
return $this->group; return $this->group;
} }
@ -52,18 +57,22 @@ abstract class MigrationBase {
* @var string * @var string
*/ */
protected $description; protected $description;
public function getDescription() { public function getDescription() {
return $this->description; return $this->description;
} }
public function setDescription($description) { public function setDescription($description) {
$this->description = $description; $this->description = $description;
} }
/** /**
* Save options passed to current operation * Save options passed to current operation
*
* @var array * @var array
*/ */
protected $options; protected $options;
public function getOption($option_name) { public function getOption($option_name) {
if (isset($this->options[$option_name])) { if (isset($this->options[$option_name])) {
return $this->options[$option_name]; return $this->options[$option_name];
@ -72,18 +81,20 @@ abstract class MigrationBase {
return NULL; return NULL;
} }
} }
public function getItemLimit() { public function getItemLimit() {
if (isset($this->options['limit']) && if (isset($this->options['limit']) &&
($this->options['limit']['unit'] == 'items' || $this->options['limit']['unit'] == 'item')) { ($this->options['limit']['unit'] == 'items' || $this->options['limit']['unit'] == 'item')) {
return $this->options['limit']['value']; return $this->options['limit']['value'];
} }
else { else {
return NULL; return NULL;
} }
} }
public function getTimeLimit() { public function getTimeLimit() {
if (isset($this->options['limit']) && if (isset($this->options['limit']) &&
($this->options['limit']['unit'] == 'seconds' || $this->options['limit']['unit'] == 'second')) { ($this->options['limit']['unit'] == 'seconds' || $this->options['limit']['unit'] == 'second')) {
return $this->options['limit']['value']; return $this->options['limit']['value'];
} }
else { else {
@ -108,6 +119,7 @@ abstract class MigrationBase {
/** /**
* When the current operation started. * When the current operation started.
*
* @var int * @var int
*/ */
protected $starttime; protected $starttime;
@ -137,41 +149,51 @@ abstract class MigrationBase {
/** /**
* List of other Migration classes which should be imported before this one. * List of other Migration classes which should be imported before this one.
* E.g., a comment migration class would typically have node and user migrations * E.g., a comment migration class would typically have node and user
* as dependencies. * migrations as dependencies.
* *
* @var array * @var array
*/ */
protected $dependencies = array(), $softDependencies = array(); protected $dependencies = array(), $softDependencies = array();
public function getHardDependencies() { public function getHardDependencies() {
return $this->dependencies; return $this->dependencies;
} }
public function setHardDependencies(array $dependencies) { public function setHardDependencies(array $dependencies) {
$this->dependencies = $dependencies; $this->dependencies = $dependencies;
} }
public function addHardDependencies(array $dependencies) { public function addHardDependencies(array $dependencies) {
$this->dependencies = array_merge($this->dependencies, $dependencies); $this->dependencies = array_merge($this->dependencies, $dependencies);
} }
public function getSoftDependencies() { public function getSoftDependencies() {
return $this->softDependencies; return $this->softDependencies;
} }
public function setSoftDependencies(array $dependencies) { public function setSoftDependencies(array $dependencies) {
$this->softDependencies = $dependencies; $this->softDependencies = $dependencies;
} }
public function addSoftDependencies(array $dependencies) { public function addSoftDependencies(array $dependencies) {
$this->softDependencies = array_merge($this->softDependencies, $dependencies); $this->softDependencies = array_merge($this->softDependencies, $dependencies);
} }
public function getDependencies() { public function getDependencies() {
return array_merge($this->dependencies, $this->softDependencies); return array_merge($this->dependencies, $this->softDependencies);
} }
/** /**
* Name of a function for displaying feedback. It must take the message to display * Name of a function for displaying feedback. It must take the message to
* as its first argument, and a (string) message type as its second argument * display as its first argument, and a (string) message type as its second
* argument
* (see drush_log()). * (see drush_log()).
*
* @var string * @var string
*/ */
protected static $displayFunction; protected static $displayFunction;
public static function setDisplayFunction($display_function) { public static function setDisplayFunction($display_function) {
self::$displayFunction = $display_function; self::$displayFunction = $display_function;
} }
@ -230,9 +252,11 @@ abstract class MigrationBase {
* @var array * @var array
*/ */
protected $team = array(); protected $team = array();
public function getTeam() { public function getTeam() {
return $this->team; return $this->team;
} }
public function setTeam(array $team) { public function setTeam(array $team) {
$this->team = $team; $this->team = $team;
} }
@ -244,9 +268,11 @@ abstract class MigrationBase {
* @var string * @var string
*/ */
protected $issuePattern; protected $issuePattern;
public function getIssuePattern() { public function getIssuePattern() {
return $this->issuePattern; return $this->issuePattern;
} }
public function setIssuePattern($issue_pattern) { public function setIssuePattern($issue_pattern) {
$this->issuePattern = $issue_pattern; $this->issuePattern = $issue_pattern;
} }
@ -265,12 +291,15 @@ abstract class MigrationBase {
* @var array * @var array
*/ */
protected $arguments = array(); protected $arguments = array();
public function getArguments() { public function getArguments() {
return $this->arguments; return $this->arguments;
} }
public function setArguments(array $arguments) { public function setArguments(array $arguments) {
$this->arguments = $arguments; $this->arguments = $arguments;
} }
public function addArguments(array $arguments) { public function addArguments(array $arguments) {
$this->arguments = array_merge($this->arguments, $arguments); $this->arguments = array_merge($this->arguments, $arguments);
} }
@ -282,9 +311,11 @@ abstract class MigrationBase {
* @var boolean * @var boolean
*/ */
protected $enabled = TRUE; protected $enabled = TRUE;
public function getEnabled() { public function getEnabled() {
return $this->enabled; return $this->enabled;
} }
public function setEnabled($enabled) { public function setEnabled($enabled) {
$this->enabled = $enabled; $this->enabled = $enabled;
} }
@ -294,9 +325,11 @@ abstract class MigrationBase {
* *
* @var array * @var array
* Key: Hook name (e.g., 'node_insert') * Key: Hook name (e.g., 'node_insert')
* Value: Array of modules for which to disable this hook (e.g., array('pathauto')). * Value: Array of modules for which to disable this hook (e.g.,
* array('pathauto')).
*/ */
protected $disableHooks = array(); protected $disableHooks = array();
public function getDisableHooks() { public function getDisableHooks() {
return $this->disableHooks; return $this->disableHooks;
} }
@ -304,26 +337,33 @@ abstract class MigrationBase {
/** /**
* An array to track 'mail_system' variable if disabled. * An array to track 'mail_system' variable if disabled.
*/ */
protected $mailSystem = array(); protected $mailSystem;
/** /**
* Have we already warned about obsolete constructor argumentss on this request? * Have we already warned about obsolete constructor argumentss on this
* request?
* *
* @var bool * @var bool
*/ */
static protected $groupArgumentWarning = FALSE; static protected $groupArgumentWarning = FALSE;
static protected $emptyArgumentsWarning = FALSE; static protected $emptyArgumentsWarning = FALSE;
/** /**
* Codes representing the result of a rollback or import process. * Codes representing the result of a rollback or import process.
*/ */
const RESULT_COMPLETED = 1; // All records have been processed const RESULT_COMPLETED = 1; // All records have been processed
const RESULT_INCOMPLETE = 2; // The process has interrupted itself (e.g., the const RESULT_INCOMPLETE = 2; // The process has interrupted itself (e.g., the
// memory limit is approaching)
// memory limit is approaching)
const RESULT_STOPPED = 3; // The process was stopped externally (e.g., via const RESULT_STOPPED = 3; // The process was stopped externally (e.g., via
// drush migrate-stop)
// drush migrate-stop)
const RESULT_FAILED = 4; // The process had a fatal error const RESULT_FAILED = 4; // The process had a fatal error
const RESULT_SKIPPED = 5; // Dependencies are unfulfilled - skip the process const RESULT_SKIPPED = 5; // Dependencies are unfulfilled - skip the process
const RESULT_DISABLED = 6; // This migration is disabled, skipping const RESULT_DISABLED = 6; // This migration is disabled, skipping
/** /**
@ -331,20 +371,27 @@ abstract class MigrationBase {
* migrate_status table. * migrate_status table.
*/ */
const STATUS_IDLE = 0; const STATUS_IDLE = 0;
const STATUS_IMPORTING = 1; const STATUS_IMPORTING = 1;
const STATUS_ROLLING_BACK = 2; const STATUS_ROLLING_BACK = 2;
const STATUS_STOPPING = 3; const STATUS_STOPPING = 3;
const STATUS_DISABLED = 4; const STATUS_DISABLED = 4;
/** /**
* Message types to be passed to saveMessage() and saved in message tables. * Message types to be passed to saveMessage() and saved in message tables.
* MESSAGE_INFORMATIONAL represents a condition that did not prevent the operation * MESSAGE_INFORMATIONAL represents a condition that did not prevent the
* from succeeding - all others represent different severities of conditions * operation from succeeding - all others represent different severities of
* resulting in a source record not being imported. * conditions resulting in a source record not being imported.
*/ */
const MESSAGE_ERROR = 1; const MESSAGE_ERROR = 1;
const MESSAGE_WARNING = 2; const MESSAGE_WARNING = 2;
const MESSAGE_NOTICE = 3; const MESSAGE_NOTICE = 3;
const MESSAGE_INFORMATIONAL = 4; const MESSAGE_INFORMATIONAL = 4;
/** /**
@ -374,7 +421,7 @@ abstract class MigrationBase {
$this->group = $arguments; $this->group = $arguments;
$this->arguments['group_name'] = $arguments->getName(); $this->arguments['group_name'] = $arguments->getName();
if (!self::$groupArgumentWarning && if (!self::$groupArgumentWarning &&
variable_get('migrate_deprecation_warnings', 1)) { variable_get('migrate_deprecation_warnings', 1)) {
self::displayMessage(t('Passing a group object to a migration constructor is now deprecated - pass through the arguments array passed to the leaf class instead.')); self::displayMessage(t('Passing a group object to a migration constructor is now deprecated - pass through the arguments array passed to the leaf class instead.'));
self::$groupArgumentWarning = TRUE; self::$groupArgumentWarning = TRUE;
} }
@ -383,7 +430,7 @@ abstract class MigrationBase {
if (empty($arguments)) { if (empty($arguments)) {
$this->arguments = array(); $this->arguments = array();
if (!self::$emptyArgumentsWarning && if (!self::$emptyArgumentsWarning &&
variable_get('migrate_deprecation_warnings', 1)) { variable_get('migrate_deprecation_warnings', 1)) {
self::displayMessage(t('Passing an empty first parameter to a migration constructor is now deprecated - pass through the arguments array passed to the leaf class instead.')); self::displayMessage(t('Passing an empty first parameter to a migration constructor is now deprecated - pass through the arguments array passed to the leaf class instead.'));
self::$emptyArgumentsWarning = TRUE; self::$emptyArgumentsWarning = TRUE;
} }
@ -417,7 +464,8 @@ abstract class MigrationBase {
} }
else { else {
if (!is_numeric($limit)) { if (!is_numeric($limit)) {
$last = drupal_strtolower($limit[strlen($limit)-1]); $last = drupal_strtolower($limit[strlen($limit) - 1]);
$limit = substr($limit, 0, -1);
switch ($last) { switch ($last) {
case 'g': case 'g':
$limit *= 1024; $limit *= 1024;
@ -437,18 +485,12 @@ abstract class MigrationBase {
// Record the time limit // Record the time limit
$this->timeLimit = ini_get('max_execution_time'); $this->timeLimit = ini_get('max_execution_time');
// Save the current mail system, prior to disabling emails.
$this->saveMailSystem();
// Prevent emails from being sent out during migrations.
$this->disableMailSystem();
// Make sure we clear our semaphores in case of abrupt exit // Make sure we clear our semaphores in case of abrupt exit
drupal_register_shutdown_function(array($this, 'endProcess')); drupal_register_shutdown_function(array($this, 'endProcess'));
// Save any hook disablement information. // Save any hook disablement information.
if (isset($this->arguments['disable_hooks']) && if (isset($this->arguments['disable_hooks']) &&
is_array($this->arguments['disable_hooks'])) { is_array($this->arguments['disable_hooks'])) {
$this->disableHooks = $this->arguments['disable_hooks']; $this->disableHooks = $this->arguments['disable_hooks'];
} }
} }
@ -477,7 +519,7 @@ abstract class MigrationBase {
* @param array $arguments * @param array $arguments
*/ */
static public function registerMigration($class_name, $machine_name = NULL, static public function registerMigration($class_name, $machine_name = NULL,
array $arguments = array()) { array $arguments = array()) {
// Support for legacy migration code - in later releases, the machine_name // Support for legacy migration code - in later releases, the machine_name
// should always be explicit. // should always be explicit.
if (!$machine_name) { if (!$machine_name) {
@ -486,7 +528,7 @@ abstract class MigrationBase {
if (!preg_match('|^[a-z0-9_]+$|i', $machine_name)) { if (!preg_match('|^[a-z0-9_]+$|i', $machine_name)) {
throw new Exception(t('!name is not a valid Migration machine name. Use only alphanumeric or underscore characters.', throw new Exception(t('!name is not a valid Migration machine name. Use only alphanumeric or underscore characters.',
array('!name' => $machine_name))); array('!name' => $machine_name)));
} }
// We no longer have any need to store the machine_name in the arguments. // We no longer have any need to store the machine_name in the arguments.
@ -509,10 +551,10 @@ abstract class MigrationBase {
db_merge('migrate_status') db_merge('migrate_status')
->key(array('machine_name' => $machine_name)) ->key(array('machine_name' => $machine_name))
->fields(array( ->fields(array(
'class_name' => $class_name, 'class_name' => $class_name,
'group_name' => $group_name, 'group_name' => $group_name,
'arguments' => serialize($arguments) 'arguments' => serialize($arguments),
)) ))
->execute(); ->execute();
} }
@ -524,8 +566,8 @@ abstract class MigrationBase {
*/ */
static public function deregisterMigration($machine_name) { static public function deregisterMigration($machine_name) {
$rows_deleted = db_delete('migrate_status') $rows_deleted = db_delete('migrate_status')
->condition('machine_name', $machine_name) ->condition('machine_name', $machine_name)
->execute(); ->execute();
// Make sure the group gets deleted if we were the only member. // Make sure the group gets deleted if we were the only member.
MigrateGroup::deleteOrphans(); MigrateGroup::deleteOrphans();
} }
@ -583,10 +625,10 @@ abstract class MigrationBase {
if (!isset($migrations[$machine_name_key])) { if (!isset($migrations[$machine_name_key])) {
// See if we know about this migration // See if we know about this migration
$row = db_select('migrate_status', 'ms') $row = db_select('migrate_status', 'ms')
->fields('ms', array('class_name', 'group_name', 'arguments')) ->fields('ms', array('class_name', 'group_name', 'arguments'))
->condition('machine_name', $machine_name) ->condition('machine_name', $machine_name)
->execute() ->execute()
->fetchObject(); ->fetchObject();
if ($row) { if ($row) {
$class_name = $row->class_name; $class_name = $row->class_name;
$arguments = unserialize($row->arguments); $arguments = unserialize($row->arguments);
@ -603,8 +645,7 @@ abstract class MigrationBase {
if (class_exists($class_name)) { if (class_exists($class_name)) {
try { try {
$migrations[$machine_name_key] = new $class_name($arguments); $migrations[$machine_name_key] = new $class_name($arguments);
} } catch (Exception $e) {
catch (Exception $e) {
self::displayMessage(t('Migration !machine could not be constructed.', self::displayMessage(t('Migration !machine could not be constructed.',
array('!machine' => $machine_name))); array('!machine' => $machine_name)));
self::displayMessage($e->getMessage()); self::displayMessage($e->getMessage());
@ -665,12 +706,14 @@ abstract class MigrationBase {
} }
/** /**
* Output the given message appropriately (drush_print/drupal_set_message/etc.) * Output the given message appropriately
* (drush_print/drupal_set_message/etc.)
* *
* @param string $message * @param string $message
* The message to output. * The message to output.
* @param int $level * @param int $level
* Optional message severity as understood by drupal_set_message and drush_log * Optional message severity as understood by drupal_set_message and
* drush_log
* (defaults to 'error'). * (defaults to 'error').
*/ */
static public function displayMessage($message, $level = 'error') { static public function displayMessage($message, $level = 'error') {
@ -690,12 +733,13 @@ abstract class MigrationBase {
* @param $line * @param $line
* The line number the error was raised at. * The line number the error was raised at.
* @param $context * @param $context
* An array that points to the active symbol table at the point the error occurred. * An array that points to the active symbol table at the point the error
* occurred.
*/ */
public function errorHandler($error_level, $message, $filename, $line, $context) { public function errorHandler($error_level, $message, $filename, $line, $context) {
if ($error_level & error_reporting()) { if ($error_level & error_reporting()) {
$message .= "\n" . t('File !file, line !line', $message .= "\n" . t('File !file, line !line',
array('!line' => $line, '!file' => $filename)); array('!line' => $line, '!file' => $filename));
// Record notices and continue // Record notices and continue
if ($error_level == E_NOTICE || $error_level == E_USER_NOTICE) { if ($error_level == E_NOTICE || $error_level == E_USER_NOTICE) {
$this->saveMessage($message . "(file: $filename, line $line)", MigrationBase::MESSAGE_INFORMATIONAL); $this->saveMessage($message . "(file: $filename, line $line)", MigrationBase::MESSAGE_INFORMATIONAL);
@ -703,20 +747,21 @@ abstract class MigrationBase {
// Simply ignore strict and deprecated errors // Simply ignore strict and deprecated errors
// Note DEPRECATED constants introduced in PHP 5.3 // Note DEPRECATED constants introduced in PHP 5.3
elseif (!($error_level == E_STRICT || $error_level == 8192 || elseif (!($error_level == E_STRICT || $error_level == 8192 ||
$error_level == 16384)) { $error_level == 16384)) {
throw new MigrateException($message, MigrationBase::MESSAGE_ERROR); throw new MigrateException($message, MigrationBase::MESSAGE_ERROR);
} }
} }
} }
/** /**
* Takes an Exception object and both saves and displays it, pulling additional * Takes an Exception object and both saves and displays it, pulling
* information on the location triggering the exception. * additional information on the location triggering the exception.
* *
* @param Exception $exception * @param Exception $exception
* Object representing the exception. * Object representing the exception.
* @param boolean $save * @param boolean $save
* Whether to save the message in the migration's mapping table. Set to FALSE * Whether to save the message in the migration's mapping table. Set to
* FALSE
* in contexts where this doesn't make sense. * in contexts where this doesn't make sense.
*/ */
public function handleException($exception, $save = TRUE) { public function handleException($exception, $save = TRUE) {
@ -730,6 +775,7 @@ abstract class MigrationBase {
/** /**
* Check the current status of a migration. * Check the current status of a migration.
*
* @return int * @return int
* One of the MigrationBase::STATUS_* constants * One of the MigrationBase::STATUS_* constants
*/ */
@ -738,10 +784,10 @@ abstract class MigrationBase {
return MigrationBase::STATUS_DISABLED; return MigrationBase::STATUS_DISABLED;
} }
$status = db_select('migrate_status', 'ms') $status = db_select('migrate_status', 'ms')
->fields('ms', array('status')) ->fields('ms', array('status'))
->condition('machine_name', $this->machineName) ->condition('machine_name', $this->machineName)
->execute() ->execute()
->fetchField(); ->fetchField();
if (!isset($status)) { if (!isset($status)) {
$status = MigrationBase::STATUS_IDLE; $status = MigrationBase::STATUS_IDLE;
} }
@ -750,19 +796,20 @@ abstract class MigrationBase {
/** /**
* Retrieve the last time an import operation completed successfully. * Retrieve the last time an import operation completed successfully.
*
* @return string * @return string
* Date/time string, formatted... How? Default DB server format? * Date/time string, formatted... How? Default DB server format?
*/ */
public function getLastImported() { public function getLastImported() {
$last_imported = db_select('migrate_log', 'ml') $last_imported = db_select('migrate_log', 'ml')
->fields('ml', array('endtime')) ->fields('ml', array('endtime'))
->condition('machine_name', $this->machineName) ->condition('machine_name', $this->machineName)
->isNotNull('endtime') ->isNotNull('endtime')
->orderBy('endtime', 'DESC') ->orderBy('endtime', 'DESC')
->execute() ->execute()
->fetchField(); ->fetchField();
if ($last_imported) { if ($last_imported) {
$last_imported = date('Y-m-d H:i:s', $last_imported/1000); $last_imported = date('Y-m-d H:i:s', $last_imported / 1000);
} }
else { else {
$last_imported = ''; $last_imported = '';
@ -778,10 +825,10 @@ abstract class MigrationBase {
*/ */
public function getHighwater() { public function getHighwater() {
$highwater = db_select('migrate_status', 'ms') $highwater = db_select('migrate_status', 'ms')
->fields('ms', array('highwater')) ->fields('ms', array('highwater'))
->condition('machine_name', $this->machineName) ->condition('machine_name', $this->machineName)
->execute() ->execute()
->fetchField(); ->fetchField();
return $highwater; return $highwater;
} }
@ -796,8 +843,8 @@ abstract class MigrationBase {
protected function saveHighwater($highwater, $force = FALSE) { protected function saveHighwater($highwater, $force = FALSE) {
if (!isset($this->options['idlist'])) { if (!isset($this->options['idlist'])) {
$query = db_update('migrate_status') $query = db_update('migrate_status')
->fields(array('highwater' => $highwater)) ->fields(array('highwater' => $highwater))
->condition('machine_name', $this->machineName); ->condition('machine_name', $this->machineName);
if (!$force) { if (!$force) {
if (!empty($this->highwaterField['type']) && $this->highwaterField['type'] == 'int') { if (!empty($this->highwaterField['type']) && $this->highwaterField['type'] == 'int') {
// If the highwater is an integer type, we need to force the DB server // If the highwater is an integer type, we need to force the DB server
@ -808,9 +855,8 @@ abstract class MigrationBase {
$query->where('(CASE WHEN highwater=\'\' THEN 0 ELSE CAST(highwater AS INTEGER) END) < :highwater', array(':highwater' => intval($highwater))); $query->where('(CASE WHEN highwater=\'\' THEN 0 ELSE CAST(highwater AS INTEGER) END) < :highwater', array(':highwater' => intval($highwater)));
break; break;
default: default:
// CAST(highwater AS INTEGER) would be ideal, but won't // MySQL casts as integers as SIGNED or UNSIGNED.
// work in MySQL. This hack is thought to be portable. $query->where('(CASE WHEN highwater=\'\' THEN 0 ELSE CAST(highwater AS SIGNED) END) < :highwater', array(':highwater' => intval($highwater)));
$query->where('(highwater+0) < :highwater', array(':highwater' => $highwater));
} }
} }
else { else {
@ -823,20 +869,21 @@ abstract class MigrationBase {
/** /**
* Retrieve the last throughput for current Migration (items / minute). * Retrieve the last throughput for current Migration (items / minute).
*
* @return integer * @return integer
*/ */
public function getLastThroughput() { public function getLastThroughput() {
$last_throughput = 0; $last_throughput = 0;
$row = db_select('migrate_log', 'ml') $row = db_select('migrate_log', 'ml')
->fields('ml', array('starttime', 'endtime', 'numprocessed')) ->fields('ml', array('starttime', 'endtime', 'numprocessed'))
->condition('machine_name', $this->machineName) ->condition('machine_name', $this->machineName)
->condition('process_type', 1) ->condition('process_type', 1)
->isNotNull('endtime') ->isNotNull('endtime')
->orderBy('starttime', 'DESC') ->orderBy('starttime', 'DESC')
->execute() ->execute()
->fetchObject(); ->fetchObject();
if ($row) { if ($row) {
$elapsed = ($row->endtime - $row->starttime)/1000; $elapsed = ($row->endtime - $row->starttime) / 1000;
if ($elapsed > 0) { if ($elapsed > 0) {
$last_throughput = round(($row->numprocessed / $elapsed) * 60); $last_throughput = round(($row->numprocessed / $elapsed) * 60);
} }
@ -846,8 +893,9 @@ abstract class MigrationBase {
/** /**
* Reports whether this migration process is complete. For a Migration, for * Reports whether this migration process is complete. For a Migration, for
* example, this would be whether all available source rows have been processed. * example, this would be whether all available source rows have been
* Other MigrationBase classes will need to return TRUE/FALSE appropriately. * processed. Other MigrationBase classes will need to return TRUE/FALSE
* appropriately.
*/ */
abstract public function isComplete(); abstract public function isComplete();
@ -904,6 +952,12 @@ abstract class MigrationBase {
// Try to make the semaphore handling atomic (depends on DB support) // Try to make the semaphore handling atomic (depends on DB support)
$transaction = db_transaction(); $transaction = db_transaction();
// Save the current mail system, prior to disabling emails.
$this->saveMailSystem();
// Prevent emails from being sent out during migrations.
$this->disableMailSystem();
$this->starttime = microtime(TRUE); $this->starttime = microtime(TRUE);
// Check to make sure there's no process already running for this migration // Check to make sure there's no process already running for this migration
@ -922,19 +976,22 @@ abstract class MigrationBase {
// Set an error handler for imports // Set an error handler for imports
if ($newStatus == MigrationBase::STATUS_IMPORTING) { if ($newStatus == MigrationBase::STATUS_IMPORTING) {
$this->previousErrorHandler = set_error_handler(array($this, 'errorHandler')); $this->previousErrorHandler = set_error_handler(array(
$this,
'errorHandler',
));
} }
// Save the initial history record // Save the initial history record
if ($this->logHistory) { if ($this->logHistory) {
$this->logID = db_insert('migrate_log') $this->logID = db_insert('migrate_log')
->fields(array( ->fields(array(
'machine_name' => $this->machineName, 'machine_name' => $this->machineName,
'process_type' => $newStatus, 'process_type' => $newStatus,
'starttime' => round(microtime(TRUE) * 1000), 'starttime' => round(microtime(TRUE) * 1000),
'initialHighwater' => $this->getHighwater(), 'initialHighwater' => $this->getHighwater(),
)) ))
->execute(); ->execute();
} }
// If we're disabling any hooks, reset the static module_implements cache so // If we're disabling any hooks, reset the static module_implements cache so
@ -950,8 +1007,8 @@ abstract class MigrationBase {
} }
/** /**
* End a rollback or import process, releasing the semaphore. Note that it must * End a rollback or import process, releasing the semaphore. Note that it
* be public to be callable as the shutdown function. * must be public to be callable as the shutdown function.
*/ */
public function endProcess() { public function endProcess() {
if ($this->previousErrorHandler) { if ($this->previousErrorHandler) {
@ -960,7 +1017,14 @@ abstract class MigrationBase {
} }
if ($this->processing) { if ($this->processing) {
$this->status = MigrationBase::STATUS_IDLE; $this->status = MigrationBase::STATUS_IDLE;
$fields = array('class_name' => get_class($this), 'status' => MigrationBase::STATUS_IDLE);
// Restore the previous mail handler.
$this->restoreMailSystem();
$fields = array(
'class_name' => get_class($this),
'status' => MigrationBase::STATUS_IDLE,
);
db_merge('migrate_status') db_merge('migrate_status')
->key(array('machine_name' => $this->machineName)) ->key(array('machine_name' => $this->machineName))
->fields($fields) ->fields($fields)
@ -977,10 +1041,9 @@ abstract class MigrationBase {
'numprocessed' => $this->total_processed, 'numprocessed' => $this->total_processed,
)) ))
->execute(); ->execute();
} } catch (PDOException $e) {
catch (PDOException $e) {
Migration::displayMessage(t('Could not log operation on migration !name - possibly MigrationBase::beginProcess() was not called', Migration::displayMessage(t('Could not log operation on migration !name - possibly MigrationBase::beginProcess() was not called',
array('!name' => $this->machineName))); array('!name' => $this->machineName)));
} }
} }
@ -1035,8 +1098,7 @@ abstract class MigrationBase {
$this->beginProcess(MigrationBase::STATUS_ROLLING_BACK); $this->beginProcess(MigrationBase::STATUS_ROLLING_BACK);
try { try {
$return = $this->rollback(); $return = $this->rollback();
} } catch (Exception $exception) {
catch (Exception $exception) {
// If something bad happened, make sure we clear the semaphore // If something bad happened, make sure we clear the semaphore
$this->endProcess(); $this->endProcess();
throw $exception; throw $exception;
@ -1071,8 +1133,7 @@ abstract class MigrationBase {
$this->beginProcess(MigrationBase::STATUS_IMPORTING); $this->beginProcess(MigrationBase::STATUS_IMPORTING);
try { try {
$return = $this->import(); $return = $this->import();
} } catch (Exception $exception) {
catch (Exception $exception) {
// If something bad happened, make sure we clear the semaphore // If something bad happened, make sure we clear the semaphore
$this->endProcess(); $this->endProcess();
throw $exception; throw $exception;
@ -1117,43 +1178,50 @@ abstract class MigrationBase {
*/ */
/** /**
* Test whether we've exceeded the desired memory threshold. If so, output a message. * Test whether we've exceeded the desired memory threshold. If so, output a
* message.
* *
* @return boolean * @return boolean
* TRUE if the threshold is exceeded, FALSE if not. * TRUE if the threshold is exceeded, FALSE if not.
*/ */
protected function memoryExceeded() { protected function memoryExceeded() {
$usage = memory_get_usage(); $usage = memory_get_usage();
$pct_memory = $usage/$this->memoryLimit; $pct_memory = $usage / $this->memoryLimit;
if ($pct_memory > $this->memoryThreshold) { if ($pct_memory > $this->memoryThreshold) {
self::displayMessage( self::displayMessage(
t('Memory usage is !usage (!pct% of limit !limit), resetting statics', t('Memory usage is !usage (!pct% of limit !limit), resetting statics',
array('!pct' => round($pct_memory*100), array(
'!usage' => format_size($usage), '!pct' => round($pct_memory * 100),
'!limit' => format_size($this->memoryLimit))), '!usage' => format_size($usage),
'!limit' => format_size($this->memoryLimit),
)),
'warning'); 'warning');
// First, try resetting Drupal's static storage - this frequently releases // First, try resetting Drupal's static storage - this frequently releases
// plenty of memory to continue // plenty of memory to continue
drupal_static_reset(); drupal_static_reset();
$usage = memory_get_usage(); $usage = memory_get_usage();
$pct_memory = $usage/$this->memoryLimit; $pct_memory = $usage / $this->memoryLimit;
// Use a lower threshold - we don't want to be in a situation where we keep // Use a lower threshold - we don't want to be in a situation where we keep
// coming back here and trimming a tiny amount // coming back here and trimming a tiny amount
if ($pct_memory > (.90 * $this->memoryThreshold)) { if ($pct_memory > (.90 * $this->memoryThreshold)) {
self::displayMessage( self::displayMessage(
t('Memory usage is now !usage (!pct% of limit !limit), not enough reclaimed, starting new batch', t('Memory usage is now !usage (!pct% of limit !limit), not enough reclaimed, starting new batch',
array('!pct' => round($pct_memory*100), array(
'!usage' => format_size($usage), '!pct' => round($pct_memory * 100),
'!limit' => format_size($this->memoryLimit))), '!usage' => format_size($usage),
'!limit' => format_size($this->memoryLimit),
)),
'warning'); 'warning');
return TRUE; return TRUE;
} }
else { else {
self::displayMessage( self::displayMessage(
t('Memory usage is now !usage (!pct% of limit !limit), reclaimed enough, continuing', t('Memory usage is now !usage (!pct% of limit !limit), reclaimed enough, continuing',
array('!pct' => round($pct_memory*100), array(
'!usage' => format_size($usage), '!pct' => round($pct_memory * 100),
'!limit' => format_size($this->memoryLimit))), '!usage' => format_size($usage),
'!limit' => format_size($this->memoryLimit),
)),
'warning'); 'warning');
return FALSE; return FALSE;
} }
@ -1204,37 +1272,23 @@ abstract class MigrationBase {
/** /**
* Encrypt an incoming value. Detects for existence of the Drupal 'Encrypt' * Encrypt an incoming value. Detects for existence of the Drupal 'Encrypt'
* module or the mcrypt PHP extension. * module.
* *
* @param string $value * @param string $value
*
* @return string The encrypted value. * @return string The encrypted value.
*/ */
static public function encrypt($value) { static public function encrypt($value) {
if (module_exists('encrypt')) { if (module_exists('encrypt')) {
$value = encrypt($value); $value = encrypt($value);
} }
else if (extension_loaded('mcrypt')) {
// Mimic encrypt module to ensure compatibility
$key = drupal_substr(variable_get('drupal_private_key', 'no_key'), 0, 32);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$value = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $value,
MCRYPT_MODE_ECB, $iv);
$encryption_array['text'] = $value;
// For forward compatibility with the encrypt module.
$encryption_array['method'] = 'mcrypt_rij_256';
$encryption_array['key_name'] = 'drupal_private_key';
$value = serialize($encryption_array);
}
else { else {
if (self::$showEncryptionWarning) { if (self::$showEncryptionWarning) {
MigrationBase::displayMessage(t('Encryption of secure migration information is not supported. Ensure the <a href="@encrypt">Encrypt module</a> or <a href="mcrypt">mcrypt PHP extension</a> is installed for this functionality.', MigrationBase::displayMessage(t('Encryption of secure migration information is not supported. Ensure the <a href="@encrypt">Encrypt module</a> is installed for this functionality.',
array( array(
'@encrypt' => 'http://drupal.org/project/encrypt', '@encrypt' => 'http://drupal.org/project/encrypt',
'@mcrypt' => 'http://php.net/manual/en/book.mcrypt.php', )
) ),
),
'warning'); 'warning');
self::$showEncryptionWarning = FALSE; self::$showEncryptionWarning = FALSE;
} }
@ -1246,33 +1300,20 @@ abstract class MigrationBase {
* Decrypt an incoming value. * Decrypt an incoming value.
* *
* @param string $value * @param string $value
*
* @return string The encrypted value * @return string The encrypted value
*/ */
static public function decrypt($value) { static public function decrypt($value) {
if (module_exists('encrypt')) { if (module_exists('encrypt')) {
$value = decrypt($value); $value = decrypt($value);
} }
else if (extension_loaded('mcrypt')) {
// Mimic encrypt module to ensure compatibility
$encryption_array = unserialize($value);
$method = $encryption_array['method']; // Not used right now
$text = $encryption_array['text'];
$key_name = $encryption_array['key_name']; // Not used right now
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$key = drupal_substr(variable_get('drupal_private_key', 'no_key'), 0, 32);
$value = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $text,
MCRYPT_MODE_ECB, $iv);
}
else { else {
if (self::$showEncryptionWarning) { if (self::$showEncryptionWarning) {
MigrationBase::displayMessage(t('Encryption of secure migration information is not supported. Ensure the <a href="@encrypt">Encrypt module</a> or <a href="mcrypt">mcrypt PHP extension</a> is installed for this functionality.', MigrationBase::displayMessage(t('Encryption of secure migration information is not supported. Ensure the <a href="@encrypt">Encrypt module</a> is installed for this functionality.',
array( array(
'@encrypt' => 'http://drupal.org/project/encrypt', '@encrypt' => 'http://drupal.org/project/encrypt',
'@mcrypt' => 'http://php.net/manual/en/book.mcrypt.php', )
) ),
),
'warning'); 'warning');
self::$showEncryptionWarning = FALSE; self::$showEncryptionWarning = FALSE;
} }
@ -1329,12 +1370,18 @@ abstract class MigrationBase {
} }
/** /**
* Convert an incoming string (which may be a UNIX timestamp, or an arbitrarily-formatted * Convert an incoming string (which may be a UNIX timestamp, or an
* date/time string) to a UNIX timestamp. * arbitrarily-formatted date/time string) to a UNIX timestamp.
* *
* @param string $value * @param string $value
* The time string to convert.
* @param string $timezone
* Optional timezone for the time string. NULL to leave the timezone unset.
*
* @return string
* The UNIX timestamp.
*/ */
static public function timestamp($value) { static public function timestamp($value, $timezone = NULL) {
// Does it look like it's already a timestamp? Just return it // Does it look like it's already a timestamp? Just return it
if (is_numeric($value)) { if (is_numeric($value)) {
return $value; return $value;
@ -1345,7 +1392,10 @@ abstract class MigrationBase {
return time(); return time();
} }
$date = new DateTime($value); if (isset($timezone)) {
$timezone = new DateTimeZone($timezone);
}
$date = new DateTime($value, $timezone);
$time = $date->format('U'); $time = $date->format('U');
if ($time == FALSE) { if ($time == FALSE) {
// Handles form YYYY-MM-DD HH:MM:SS.garbage // Handles form YYYY-MM-DD HH:MM:SS.garbage
@ -1359,14 +1409,9 @@ abstract class MigrationBase {
/** /**
* Saves the current mail system, or set a system default if there is none. * Saves the current mail system, or set a system default if there is none.
*/ */
protected function saveMailSystem() { public function saveMailSystem() {
global $conf; global $conf;
if (empty($conf['mail_system'])) { $this->mailSystem = empty($conf['mail_system']) ? NULL : $conf['mail_system'];
$conf['mail_system']['default-system'] = 'MigrateMailIgnore';
}
else {
$this->mailSystem = $conf['mail_system'];
}
} }
/** /**
@ -1379,6 +1424,9 @@ abstract class MigrationBase {
$conf['mail_system'][$system] = 'MigrateMailIgnore'; $conf['mail_system'][$system] = 'MigrateMailIgnore';
} }
} }
else {
$conf['mail_system'] = array('default-system' => 'MigrateMailIgnore');
}
} }
/** /**

View File

@ -13,11 +13,12 @@
* MigrateDestinationEntity for an example. * MigrateDestinationEntity for an example.
*/ */
abstract class MigrateDestination { abstract class MigrateDestination {
/** /**
* To support MigrateSQLMap maps, derived destination classes should return * To support MigrateSQLMap maps, derived destination classes should return
* schema field definition(s) corresponding to the primary key of the destination * schema field definition(s) corresponding to the primary key of the
* being implemented. These are used to construct the destination key fields * destination being implemented. These are used to construct the destination
* of the map table for a migration using this destination. * key fields of the map table for a migration using this destination.
* *
* abstract static public function getKeySchema() * abstract static public function getKeySchema()
*/ */
@ -36,6 +37,7 @@ abstract class MigrateDestination {
* *
* @param Migration $migration * @param Migration $migration
* Optionally, the migration containing this destination. * Optionally, the migration containing this destination.
*
* @return array * @return array
* Keys: machine names of the fields (to be passed to addFieldMapping) * Keys: machine names of the fields (to be passed to addFieldMapping)
* Values: Human-friendly descriptions of the fields. * Values: Human-friendly descriptions of the fields.
@ -43,9 +45,9 @@ abstract class MigrateDestination {
abstract public function fields(); abstract public function fields();
/** /**
* Derived classes must implement either bulkRollback or rollback() according to * Derived classes must implement either bulkRollback or rollback() according
* the signatures below, to rollback (usually by deletion) previously-migrated * to the signatures below, to rollback (usually by deletion)
* items. * previously-migrated items.
* *
* $ids is an array of single-field keys to be deleted * $ids is an array of single-field keys to be deleted
* abstract public function bulkRollback(array $ids); * abstract public function bulkRollback(array $ids);
@ -55,9 +57,10 @@ abstract class MigrateDestination {
*/ */
/** /**
* Derived classes must implement import(), to construct one new object (pre-pppulated * Derived classes must implement import(), to construct one new object
* using field mappings in the Migration). It is expected to call prepare and * (pre-pppulated using field mappings in the Migration). It is expected to
* complete handlers, passing them $row (the raw data from the source). * call prepare and complete handlers, passing them $row (the raw data from
* the source).
*/ */
abstract public function import(stdClass $object, stdClass $row); abstract public function import(stdClass $object, stdClass $row);
@ -78,10 +81,13 @@ abstract class MigrateDestination {
* @var int * @var int
*/ */
protected $numCreated = 0; protected $numCreated = 0;
public function getCreated() { public function getCreated() {
return $this->numCreated; return $this->numCreated;
} }
protected $numUpdated = 0; protected $numUpdated = 0;
public function getUpdated() { public function getUpdated() {
return $this->numUpdated; return $this->numUpdated;
} }
@ -105,6 +111,7 @@ abstract class MigrateDestination {
* All destination handlers should be derived from MigrateDestinationHandler * All destination handlers should be derived from MigrateDestinationHandler
*/ */
abstract class MigrateDestinationHandler extends MigrateHandler { abstract class MigrateDestinationHandler extends MigrateHandler {
// Any one or more of these methods may be implemented // Any one or more of these methods may be implemented
/** /**
@ -116,6 +123,7 @@ abstract class MigrateDestinationHandler extends MigrateHandler {
* The bundle (article, blog, etc.), if any, for which to list fields. * The bundle (article, blog, etc.), if any, for which to list fields.
* @param Migration $migration * @param Migration $migration
* Optionally, the migration providing the context. * Optionally, the migration providing the context.
*
* @return array * @return array
* An array keyed by field name, with field descriptions as values. * An array keyed by field name, with field descriptions as values.
*/ */

View File

@ -6,11 +6,14 @@
*/ */
class MigrateException extends Exception { class MigrateException extends Exception {
/** /**
* The level of the error being reported (a Migration::MESSAGE_* constant) * The level of the error being reported (a Migration::MESSAGE_* constant)
*
* @var int * @var int
*/ */
protected $level; protected $level;
public function getLevel() { public function getLevel() {
return $this->level; return $this->level;
} }
@ -22,6 +25,7 @@ class MigrateException extends Exception {
* @var int * @var int
*/ */
protected $status; protected $status;
public function getStatus() { public function getStatus() {
return $this->status; return $this->status;
} }

View File

@ -7,6 +7,7 @@
*/ */
class MigrateFieldMapping { class MigrateFieldMapping {
/** /**
* Destination field name for the mapping. If empty, the mapping is just a * Destination field name for the mapping. If empty, the mapping is just a
* stub for annotating the source field. * stub for annotating the source field.
@ -14,6 +15,7 @@ class MigrateFieldMapping {
* @var string * @var string
*/ */
protected $destinationField; protected $destinationField;
public function getDestinationField() { public function getDestinationField() {
return $this->destinationField; return $this->destinationField;
} }
@ -25,6 +27,7 @@ class MigrateFieldMapping {
* @var string * @var string
*/ */
protected $sourceField; protected $sourceField;
public function getSourceField() { public function getSourceField() {
return $this->sourceField; return $this->sourceField;
} }
@ -33,11 +36,15 @@ class MigrateFieldMapping {
* @var int * @var int
*/ */
const MAPPING_SOURCE_CODE = 1; const MAPPING_SOURCE_CODE = 1;
const MAPPING_SOURCE_DB = 2; const MAPPING_SOURCE_DB = 2;
protected $mappingSource = self::MAPPING_SOURCE_CODE; protected $mappingSource = self::MAPPING_SOURCE_CODE;
public function getMappingSource() { public function getMappingSource() {
return $this->mappingSource; return $this->mappingSource;
} }
public function setMappingSource($mapping_source) { public function setMappingSource($mapping_source) {
$this->mappingSource = $mapping_source; $this->mappingSource = $mapping_source;
} }
@ -50,6 +57,7 @@ class MigrateFieldMapping {
* @var mixed * @var mixed
*/ */
protected $defaultValue; protected $defaultValue;
public function getDefaultValue() { public function getDefaultValue() {
return $this->defaultValue; return $this->defaultValue;
} }
@ -61,6 +69,7 @@ class MigrateFieldMapping {
* @var string * @var string
*/ */
protected $separator; protected $separator;
public function getSeparator() { public function getSeparator() {
return $this->separator; return $this->separator;
} }
@ -74,6 +83,7 @@ class MigrateFieldMapping {
* An array of source migrations, or string for a single migration. * An array of source migrations, or string for a single migration.
*/ */
protected $sourceMigration; protected $sourceMigration;
public function getSourceMigration() { public function getSourceMigration() {
return $this->sourceMigration; return $this->sourceMigration;
} }
@ -84,6 +94,7 @@ class MigrateFieldMapping {
* @var string * @var string
*/ */
protected $callbacks = array(); protected $callbacks = array();
public function getCallbacks() { public function getCallbacks() {
return $this->callbacks; return $this->callbacks;
} }
@ -99,6 +110,7 @@ class MigrateFieldMapping {
* @var string * @var string
*/ */
protected $dedupe; protected $dedupe;
public function getDedupe() { public function getDedupe() {
return $this->dedupe; return $this->dedupe;
} }
@ -106,41 +118,49 @@ class MigrateFieldMapping {
/** /**
* Argument overrides. If present this will be an array, keyed by * Argument overrides. If present this will be an array, keyed by
* a field API array key, with one or both of these entries: * a field API array key, with one or both of these entries:
* 'source_field' - Name of the source field in the incoming row containing the * 'source_field' - Name of the source field in the incoming row containing
* value to be assigned * the value to be assigned
* 'default_value' - A constant value to be assigned in the absence of source_field * 'default_value' - A constant value to be assigned in the absence of
* Deprecated - subfield notation is now preferred. * source_field Deprecated - subfield notation is now preferred.
* *
* @var array * @var array
*/ */
protected $arguments; protected $arguments;
public function getArguments() { public function getArguments() {
return $this->arguments; return $this->arguments;
} }
protected $description = ''; protected $description = '';
public function getDescription() { public function getDescription() {
return $this->description; return $this->description;
} }
protected $issueGroup; protected $issueGroup;
public function getIssueGroup() { public function getIssueGroup() {
return $this->issueGroup; return $this->issueGroup;
} }
protected $issueNumber; protected $issueNumber;
public function getIssueNumber() { public function getIssueNumber() {
return $this->issueNumber; return $this->issueNumber;
} }
protected $issuePriority = self::ISSUE_PRIORITY_OK; protected $issuePriority = self::ISSUE_PRIORITY_OK;
public function getIssuePriority() { public function getIssuePriority() {
return $this->issuePriority; return $this->issuePriority;
} }
const ISSUE_PRIORITY_OK = 1; const ISSUE_PRIORITY_OK = 1;
const ISSUE_PRIORITY_LOW = 2; const ISSUE_PRIORITY_LOW = 2;
const ISSUE_PRIORITY_MEDIUM = 3; const ISSUE_PRIORITY_MEDIUM = 3;
const ISSUE_PRIORITY_BLOCKER = 4; const ISSUE_PRIORITY_BLOCKER = 4;
public static $priorities = array(); public static $priorities = array();
@ -177,7 +197,17 @@ class MigrateFieldMapping {
} }
public function callbacks($callbacks) { public function callbacks($callbacks) {
$this->callbacks = func_get_args(); foreach (func_get_args() as $callback) {
$this->callback($callback);
}
return $this;
}
public function callback($callback) {
$this->callbacks[] = array(
'callback' => $callback,
'params' => array_slice(func_get_args(), 1),
);
return $this; return $this;
} }

View File

@ -6,12 +6,14 @@
*/ */
class MigrateGroup { class MigrateGroup {
/** /**
* The machine name of the group - used to identify it in drush commands. * The machine name of the group - used to identify it in drush commands.
* *
* @var string * @var string
*/ */
protected $name; protected $name;
public function getName() { public function getName() {
return $this->name; return $this->name;
} }
@ -22,6 +24,7 @@ class MigrateGroup {
* @var string * @var string
*/ */
protected $title; protected $title;
public function getTitle() { public function getTitle() {
return $this->title; return $this->title;
} }
@ -33,6 +36,7 @@ class MigrateGroup {
* @var array * @var array
*/ */
protected $arguments = array(); protected $arguments = array();
public function getArguments() { public function getArguments() {
return $this->arguments; return $this->arguments;
} }
@ -43,6 +47,7 @@ class MigrateGroup {
* @var array * @var array
*/ */
protected $dependencies = array(); protected $dependencies = array();
public function getDependencies() { public function getDependencies() {
return $this->dependencies; return $this->dependencies;
} }
@ -53,6 +58,7 @@ class MigrateGroup {
* @var array * @var array
*/ */
static protected $groupList = array(); static protected $groupList = array();
static public function groups() { static public function groups() {
$groups = array(); $groups = array();
$dependent_groups = array(); $dependent_groups = array();
@ -110,10 +116,10 @@ class MigrateGroup {
static public function getInstance($name, $dependencies = array()) { static public function getInstance($name, $dependencies = array()) {
if (empty(self::$groupList[$name])) { if (empty(self::$groupList[$name])) {
$row = db_select('migrate_group', 'mg') $row = db_select('migrate_group', 'mg')
->fields('mg') ->fields('mg')
->condition('name', $name) ->condition('name', $name)
->execute() ->execute()
->fetchObject(); ->fetchObject();
if ($row) { if ($row) {
$arguments = unserialize($row->arguments); $arguments = unserialize($row->arguments);
$arguments = MigrationBase::decryptArguments($arguments); $arguments = MigrationBase::decryptArguments($arguments);
@ -141,8 +147,8 @@ class MigrateGroup {
* A user-visible title for the group. Defaults to the machine name. * A user-visible title for the group. Defaults to the machine name.
* *
* @param array $arguments * @param array $arguments
* An array of group arguments - generally data that applies to all migrations * An array of group arguments - generally data that applies to all
* in the group. * migrations in the group.
*/ */
static public function register($name, $title = NULL, array $arguments = array()) { static public function register($name, $title = NULL, array $arguments = array()) {
if (!$title) { if (!$title) {
@ -156,9 +162,9 @@ class MigrateGroup {
db_merge('migrate_group') db_merge('migrate_group')
->key(array('name' => $name)) ->key(array('name' => $name))
->fields(array( ->fields(array(
'title' => $title, 'title' => $title,
'arguments' => serialize($arguments) 'arguments' => serialize($arguments),
)) ))
->execute(); ->execute();
} }
@ -172,9 +178,9 @@ class MigrateGroup {
*/ */
static public function deregister($name) { static public function deregister($name) {
$result = db_select('migrate_status', 'ms') $result = db_select('migrate_status', 'ms')
->fields('ms', array('machine_name')) ->fields('ms', array('machine_name'))
->condition('group_name', $name) ->condition('group_name', $name)
->execute(); ->execute();
foreach ($result as $row) { foreach ($result as $row) {
Migration::deregisterMigration($row->machine_name); Migration::deregisterMigration($row->machine_name);
} }

View File

@ -10,12 +10,15 @@
* to implement appropriate methods (e.g., prepare, complete, or fields). * to implement appropriate methods (e.g., prepare, complete, or fields).
*/ */
abstract class MigrateHandler { abstract class MigrateHandler {
/** /**
* List of other handler classes which should be invoked before the current one. * List of other handler classes which should be invoked before the current
* one.
* *
* @var array * @var array
*/ */
protected $dependencies = array(); protected $dependencies = array();
public function getDependencies() { public function getDependencies() {
return $this->dependencies; return $this->dependencies;
} }
@ -27,6 +30,7 @@ abstract class MigrateHandler {
* @var array * @var array
*/ */
protected $typesHandled = array(); protected $typesHandled = array();
public function getTypesHandled() { public function getTypesHandled() {
return $this->typesHandled; return $this->typesHandled;
} }

View File

@ -10,12 +10,16 @@
* for the purpose of rollback. * for the purpose of rollback.
*/ */
abstract class MigrateMap implements Iterator { abstract class MigrateMap implements Iterator {
/** /**
* Codes reflecting the current status of a map row. * Codes reflecting the current status of a map row.
*/ */
const STATUS_IMPORTED = 0; const STATUS_IMPORTED = 0;
const STATUS_NEEDS_UPDATE = 1; const STATUS_NEEDS_UPDATE = 1;
const STATUS_IGNORED = 2; const STATUS_IGNORED = 2;
const STATUS_FAILED = 3; const STATUS_FAILED = 3;
/** /**
@ -23,6 +27,7 @@ abstract class MigrateMap implements Iterator {
* *
*/ */
const ROLLBACK_DELETE = 0; const ROLLBACK_DELETE = 0;
const ROLLBACK_PRESERVE = 1; const ROLLBACK_PRESERVE = 1;
/** /**
@ -32,7 +37,9 @@ abstract class MigrateMap implements Iterator {
* @var array * @var array
*/ */
protected $sourceKey, $destinationKey; protected $sourceKey, $destinationKey;
abstract public function getSourceKey(); abstract public function getSourceKey();
abstract public function getDestinationKey(); abstract public function getDestinationKey();
/** /**
@ -56,9 +63,11 @@ abstract class MigrateMap implements Iterator {
* @var boolean * @var boolean
*/ */
protected $trackLastImported = FALSE; protected $trackLastImported = FALSE;
public function getTrackLastImported() { public function getTrackLastImported() {
return $this->trackLastImported; return $this->trackLastImported;
} }
public function setTrackLastImported($trackLastImported) { public function setTrackLastImported($trackLastImported) {
if (is_bool($trackLastImported)) { if (is_bool($trackLastImported)) {
$this->trackLastImported = $trackLastImported; $this->trackLastImported = $trackLastImported;
@ -76,8 +85,8 @@ abstract class MigrateMap implements Iterator {
* @param $hash * @param $hash
*/ */
abstract public function saveIDMapping(stdClass $source_row, array $dest_ids, abstract public function saveIDMapping(stdClass $source_row, array $dest_ids,
$status = MigrateMap::STATUS_IMPORTED, $status = MigrateMap::STATUS_IMPORTED,
$rollback_action = MigrateMap::ROLLBACK_DELETE, $hash = NULL); $rollback_action = MigrateMap::ROLLBACK_DELETE, $hash = NULL);
/** /**
* Record a message related to a source record * Record a message related to a source record
@ -147,6 +156,7 @@ abstract class MigrateMap implements Iterator {
* Retrieve map data for a given source or destination item * Retrieve map data for a given source or destination item
*/ */
abstract public function getRowBySource(array $source_id); abstract public function getRowBySource(array $source_id);
abstract public function getRowByDestination(array $destination_id); abstract public function getRowByDestination(array $destination_id);
/** /**
@ -155,29 +165,32 @@ abstract class MigrateMap implements Iterator {
abstract public function getRowsNeedingUpdate($count); abstract public function getRowsNeedingUpdate($count);
/** /**
* Given a (possibly multi-field) destination key, return the (possibly multi-field) * Given a (possibly multi-field) destination key, return the (possibly
* source key mapped to it. * multi-field) source key mapped to it.
* *
* @param array $destination_id * @param array $destination_id
* Array of destination key values. * Array of destination key values.
*
* @return array * @return array
* Array of source key values, or NULL on failure. * Array of source key values, or NULL on failure.
*/ */
abstract public function lookupSourceID(array $destination_id); abstract public function lookupSourceID(array $destination_id);
/** /**
* Given a (possibly multi-field) source key, return the (possibly multi-field) * Given a (possibly multi-field) source key, return the (possibly
* destination key it is mapped to. * multi-field) destination key it is mapped to.
* *
* @param array $source_id * @param array $source_id
* Array of source key values. * Array of source key values.
*
* @return array * @return array
* Array of destination key values, or NULL on failure. * Array of destination key values, or NULL on failure.
*/ */
abstract public function lookupDestinationID(array $source_id); abstract public function lookupDestinationID(array $source_id);
/** /**
* Remove any persistent storage used by this map (e.g., map and message tables) * Remove any persistent storage used by this map (e.g., map and message
* tables)
*/ */
abstract public function destroy(); abstract public function destroy();
} }

View File

@ -8,20 +8,24 @@
/** /**
* The base class for all import objects. This is where most of the smarts * The base class for all import objects. This is where most of the smarts
* of the migrate module resides. Migrations are created by deriving from this * of the migrate module resides. Migrations are created by deriving from this
* class, and in the constructor (after calling parent::__construct()) initializing * class, and in the constructor (after calling parent::__construct())
* at a minimum the name, description, source, and destination properties. The constructor * initializing at a minimum the name, description, source, and destination
* will also usually make several calls to addFieldMapping(). * properties. The constructor will also usually make several calls to
* addFieldMapping().
*/ */
abstract class Migration extends MigrationBase { abstract class Migration extends MigrationBase {
/** /**
* Source object for the migration, derived from MigrateSource. * Source object for the migration, derived from MigrateSource.
* *
* @var MigrateSource * @var MigrateSource
*/ */
protected $source; protected $source;
public function getSource() { public function getSource() {
return $this->source; return $this->source;
} }
public function setSource(MigrateSource $source) { public function setSource(MigrateSource $source) {
$this->source = $source; $this->source = $source;
} }
@ -32,9 +36,11 @@ abstract class Migration extends MigrationBase {
* @var MigrateDestination * @var MigrateDestination
*/ */
protected $destination; protected $destination;
public function getDestination() { public function getDestination() {
return $this->destination; return $this->destination;
} }
public function setDestination(MigrateDestination $destination) { public function setDestination(MigrateDestination $destination) {
$this->destination = $destination; $this->destination = $destination;
} }
@ -45,9 +51,11 @@ abstract class Migration extends MigrationBase {
* @var MigrateMap * @var MigrateMap
*/ */
protected $map; protected $map;
public function getMap() { public function getMap() {
return $this->map; return $this->map;
} }
public function setMap(MigrateMap $map) { public function setMap(MigrateMap $map) {
$this->map = $map; $this->map = $map;
} }
@ -63,11 +71,15 @@ abstract class Migration extends MigrationBase {
* @var int * @var int
*/ */
const SOURCE = 1; const SOURCE = 1;
const DESTINATION = 2; const DESTINATION = 2;
protected $systemOfRecord = Migration::SOURCE; protected $systemOfRecord = Migration::SOURCE;
public function getSystemOfRecord() { public function getSystemOfRecord() {
return $this->systemOfRecord; return $this->systemOfRecord;
} }
public function setSystemOfRecord($system_of_record) { public function setSystemOfRecord($system_of_record) {
$this->systemOfRecord = $system_of_record; $this->systemOfRecord = $system_of_record;
} }
@ -87,9 +99,11 @@ abstract class Migration extends MigrationBase {
* @var int * @var int
*/ */
protected $defaultRollbackAction = MigrateMap::ROLLBACK_DELETE; protected $defaultRollbackAction = MigrateMap::ROLLBACK_DELETE;
public function getDefaultRollbackAction() { public function getDefaultRollbackAction() {
return $this->defaultRollbackAction; return $this->defaultRollbackAction;
} }
public function setDefaultRollbackAction($rollback_action) { public function setDefaultRollbackAction($rollback_action) {
$this->defaultRollbackAction = $rollback_action; $this->defaultRollbackAction = $rollback_action;
} }
@ -107,7 +121,9 @@ abstract class Migration extends MigrationBase {
* @var array * @var array
*/ */
protected $storedFieldMappings = array(); protected $storedFieldMappings = array();
protected $storedFieldMappingsRetrieved = FALSE; protected $storedFieldMappingsRetrieved = FALSE;
public function getStoredFieldMappings() { public function getStoredFieldMappings() {
if (!$this->storedFieldMappingsRetrieved) { if (!$this->storedFieldMappingsRetrieved) {
$this->loadFieldMappings(); $this->loadFieldMappings();
@ -122,6 +138,7 @@ abstract class Migration extends MigrationBase {
* @var array * @var array
*/ */
protected $codedFieldMappings = array(); protected $codedFieldMappings = array();
public function getCodedFieldMappings() { public function getCodedFieldMappings() {
return $this->codedFieldMappings; return $this->codedFieldMappings;
} }
@ -133,10 +150,11 @@ abstract class Migration extends MigrationBase {
* @var array * @var array
*/ */
protected $allFieldMappings = array(); protected $allFieldMappings = array();
public function getFieldMappings() { public function getFieldMappings() {
if (empty($allFieldMappings)) { if (empty($allFieldMappings)) {
$this->allFieldMappings = array_merge($this->getCodedFieldMappings(), $this->allFieldMappings = array_merge($this->getCodedFieldMappings(),
$this->getStoredFieldMappings()); $this->getStoredFieldMappings());
// If there are multiple mappings of a given source field to no // If there are multiple mappings of a given source field to no
// destination field, keep only the last (so the UI can override a source // destination field, keep only the last (so the UI can override a source
// field DNM that was defined in code). // field DNM that was defined in code).
@ -193,21 +211,25 @@ abstract class Migration extends MigrationBase {
* @var array * @var array
*/ */
protected $highwaterField = array(); protected $highwaterField = array();
public function getHighwaterField() { public function getHighwaterField() {
return $this->highwaterField; return $this->highwaterField;
} }
public function setHighwaterField(array $highwater_field) { public function setHighwaterField(array $highwater_field) {
$this->highwaterField = $highwater_field; $this->highwaterField = $highwater_field;
} }
/** /**
* The object currently being constructed * The object currently being constructed
*
* @var stdClass * @var stdClass
*/ */
protected $destinationValues; protected $destinationValues;
/** /**
* The current data row retrieved from the source. * The current data row retrieved from the source.
*
* @var stdClass * @var stdClass
*/ */
protected $sourceValues; protected $sourceValues;
@ -238,7 +260,7 @@ abstract class Migration extends MigrationBase {
* @param array $arguments * @param array $arguments
*/ */
static public function registerMigration($class_name, $machine_name = NULL, static public function registerMigration($class_name, $machine_name = NULL,
array $arguments = array()) { array $arguments = array()) {
// Record any field mappings provided via arguments. // Record any field mappings provided via arguments.
if (isset($arguments['field_mappings'])) { if (isset($arguments['field_mappings'])) {
self::saveFieldMappings($machine_name, $arguments['field_mappings']); self::saveFieldMappings($machine_name, $arguments['field_mappings']);
@ -270,14 +292,13 @@ abstract class Migration extends MigrationBase {
// Remove stored field mappings for this migration // Remove stored field mappings for this migration
$rows_deleted = db_delete('migrate_field_mapping') $rows_deleted = db_delete('migrate_field_mapping')
->condition('machine_name', $machine_name) ->condition('machine_name', $machine_name)
->execute(); ->execute();
// Call the parent deregistration (which clears migrate_status) last, the // Call the parent deregistration (which clears migrate_status) last, the
// above will reference it. // above will reference it.
parent::deregisterMigration($machine_name); parent::deregisterMigration($machine_name);
} } catch (Exception $e) {
catch (Exception $e) {
// Fail silently if it's already gone // Fail silently if it's already gone
} }
} }
@ -298,11 +319,11 @@ abstract class Migration extends MigrationBase {
$source_field = $field_mapping->getSourceField(); $source_field = $field_mapping->getSourceField();
db_insert('migrate_field_mapping') db_insert('migrate_field_mapping')
->fields(array( ->fields(array(
'machine_name' => $machine_name, 'machine_name' => $machine_name,
'destination_field' => is_null($destination_field) ? '' : $destination_field, 'destination_field' => is_null($destination_field) ? '' : $destination_field,
'source_field' => is_null($source_field) ? '' : $source_field, 'source_field' => is_null($source_field) ? '' : $source_field,
'options' => serialize($field_mapping) 'options' => serialize($field_mapping),
)) ))
->execute(); ->execute();
} }
} }
@ -312,9 +333,9 @@ abstract class Migration extends MigrationBase {
*/ */
public function loadFieldMappings() { public function loadFieldMappings() {
$result = db_select('migrate_field_mapping', 'mfm') $result = db_select('migrate_field_mapping', 'mfm')
->fields('mfm', array('destination_field', 'source_field', 'options')) ->fields('mfm', array('destination_field', 'source_field', 'options'))
->condition('machine_name', $this->machineName) ->condition('machine_name', $this->machineName)
->execute(); ->execute();
foreach ($result as $row) { foreach ($result as $row) {
$field_mapping = unserialize($row->options); $field_mapping = unserialize($row->options);
$field_mapping->setMappingSource(MigrateFieldMapping::MAPPING_SOURCE_DB); $field_mapping->setMappingSource(MigrateFieldMapping::MAPPING_SOURCE_DB);
@ -346,11 +367,14 @@ abstract class Migration extends MigrationBase {
$warn_on_override = TRUE) { $warn_on_override = TRUE) {
// Warn of duplicate mappings // Warn of duplicate mappings
if ($warn_on_override && !is_null($destination_field) && if ($warn_on_override && !is_null($destination_field) &&
isset($this->codedFieldMappings[$destination_field])) { isset($this->codedFieldMappings[$destination_field])) {
self::displayMessage( self::displayMessage(
t('!name addFieldMapping: !dest was previously mapped from !source, overridden', t('!name addFieldMapping: !dest was previously mapped from !source, overridden',
array('!name' => $this->machineName, '!dest' => $destination_field, array(
'!source' => $this->codedFieldMappings[$destination_field]->getSourceField())), '!name' => $this->machineName,
'!dest' => $destination_field,
'!source' => $this->codedFieldMappings[$destination_field]->getSourceField(),
)),
'warning'); 'warning');
} }
$mapping = new MigrateFieldMapping($destination_field, $source_field); $mapping = new MigrateFieldMapping($destination_field, $source_field);
@ -413,7 +437,7 @@ abstract class Migration extends MigrationBase {
} }
foreach ($fields as $field) { foreach ($fields as $field) {
$this->addFieldMapping($field, NULL, $warn_on_override) $this->addFieldMapping($field, NULL, $warn_on_override)
->issueGroup($issue_group); ->issueGroup($issue_group);
} }
} }
@ -433,7 +457,7 @@ abstract class Migration extends MigrationBase {
} }
foreach ($fields as $field) { foreach ($fields as $field) {
$this->addFieldMapping(NULL, $field, $warn_on_override) $this->addFieldMapping(NULL, $field, $warn_on_override)
->issueGroup($issue_group); ->issueGroup($issue_group);
} }
} }
@ -464,7 +488,7 @@ abstract class Migration extends MigrationBase {
// Do some standard setup // Do some standard setup
if (isset($this->options['feedback']) && isset($this->options['feedback']['value']) && if (isset($this->options['feedback']) && isset($this->options['feedback']['value']) &&
isset($this->options['feedback']['unit'])) { isset($this->options['feedback']['unit'])) {
$this->feedback = $this->options['feedback']['value']; $this->feedback = $this->options['feedback']['value'];
$this->feedback_unit = $this->options['feedback']['unit']; $this->feedback_unit = $this->options['feedback']['unit'];
if ($this->feedback_unit == 'item') { if ($this->feedback_unit == 'item') {
@ -477,7 +501,7 @@ abstract class Migration extends MigrationBase {
$this->lastfeedback = $this->starttime; $this->lastfeedback = $this->starttime;
$this->total_processed = $this->total_successes = $this->total_processed = $this->total_successes =
$this->processed_since_feedback = $this->successes_since_feedback = 0; $this->processed_since_feedback = $this->successes_since_feedback = 0;
// Call pre-process methods // Call pre-process methods
if ($this->status == Migration::STATUS_IMPORTING) { if ($this->status == Migration::STATUS_IMPORTING) {
@ -572,7 +596,7 @@ abstract class Migration extends MigrationBase {
// Note that bulk rollback is only supported for single-column keys // Note that bulk rollback is only supported for single-column keys
$sourceids[] = $current_source_key; $sourceids[] = $current_source_key;
if (!empty($destination_key->destid1)) { if (!empty($destination_key->destid1)) {
$map_row = $this->map->getRowByDestination((array)$destination_key); $map_row = $this->map->getRowByDestination((array) $destination_key);
if ($map_row['rollback_action'] == MigrateMap::ROLLBACK_DELETE) { if ($map_row['rollback_action'] == MigrateMap::ROLLBACK_DELETE) {
$destids[] = $destination_key->destid1; $destids[] = $destination_key->destid1;
} }
@ -594,8 +618,7 @@ abstract class Migration extends MigrationBase {
migrate_instrument_stop('rollback map/message update'); migrate_instrument_stop('rollback map/message update');
$this->total_successes += $batch_count; $this->total_successes += $batch_count;
$this->successes_since_feedback += $batch_count; $this->successes_since_feedback += $batch_count;
} } catch (Exception $e) {
catch (Exception $e) {
$this->handleException($e, FALSE); $this->handleException($e, FALSE);
migrate_instrument_stop('bulkRollback'); migrate_instrument_stop('bulkRollback');
migrate_instrument_stop('rollback map/message update'); migrate_instrument_stop('rollback map/message update');
@ -656,10 +679,10 @@ abstract class Migration extends MigrationBase {
} }
} }
if (!$skip) { if (!$skip) {
$map_row = $this->map->getRowByDestination((array)$destination_key); $map_row = $this->map->getRowByDestination((array) $destination_key);
if ($map_row['rollback_action'] == MigrateMap::ROLLBACK_DELETE) { if ($map_row['rollback_action'] == MigrateMap::ROLLBACK_DELETE) {
migrate_instrument_start('destination rollback'); migrate_instrument_start('destination rollback');
$this->destination->rollback((array)$destination_key); $this->destination->rollback((array) $destination_key);
migrate_instrument_stop('destination rollback'); migrate_instrument_stop('destination rollback');
} }
} }
@ -670,8 +693,7 @@ abstract class Migration extends MigrationBase {
migrate_instrument_stop('rollback map/message update'); migrate_instrument_stop('rollback map/message update');
$this->total_successes++; $this->total_successes++;
$this->successes_since_feedback++; $this->successes_since_feedback++;
} } catch (Exception $e) {
catch (Exception $e) {
// TODO: At least count failures // TODO: At least count failures
continue; continue;
} }
@ -701,11 +723,15 @@ abstract class Migration extends MigrationBase {
try { try {
$this->source->rewind(); $this->source->rewind();
} } catch (Exception $e) {
catch (Exception $e) {
self::displayMessage( self::displayMessage(
t('Migration failed with source plugin exception: %e, in %file:%line', t('Migration for %class failed with source plugin exception: %e, in %file:%line',
array('%e' => $e->getMessage(), '%file' => $e->getFile(), '%line' => $e->getLine()))); array(
'%class' => get_class($this),
'%e' => $e->getMessage(),
'%file' => $e->getFile(),
'%line' => $e->getLine(),
)));
return MigrationBase::RESULT_FAILED; return MigrationBase::RESULT_FAILED;
} }
while ($this->source->valid()) { while ($this->source->valid()) {
@ -732,24 +758,22 @@ abstract class Migration extends MigrationBase {
else { else {
$this->map->saveIDMapping($this->sourceValues, array(), $this->map->saveIDMapping($this->sourceValues, array(),
MigrateMap::STATUS_FAILED, $this->rollbackAction, MigrateMap::STATUS_FAILED, $this->rollbackAction,
$data_row->migrate_map_hash); NULL);
if ($this->map->messageCount() == 0) { if ($this->map->messageCount() == 0) {
$message = t('New object was not saved, no error provided'); $message = t('New object was not saved, no error provided');
$this->saveMessage($message); $this->saveMessage($message);
self::displayMessage($message); self::displayMessage($message);
} }
} }
} } catch (MigrateException $e) {
catch (MigrateException $e) {
$this->map->saveIDMapping($this->sourceValues, array(), $this->map->saveIDMapping($this->sourceValues, array(),
$e->getStatus(), $this->rollbackAction, $data_row->migrate_map_hash); $e->getStatus(), $this->rollbackAction, $data_row->migrate_map_hash);
$this->saveMessage($e->getMessage(), $e->getLevel()); $this->saveMessage($e->getMessage(), $e->getLevel());
self::displayMessage($e->getMessage()); self::displayMessage($e->getMessage());
} } catch (Exception $e) {
catch (Exception $e) {
$this->map->saveIDMapping($this->sourceValues, array(), $this->map->saveIDMapping($this->sourceValues, array(),
MigrateMap::STATUS_FAILED, $this->rollbackAction, MigrateMap::STATUS_FAILED, $this->rollbackAction,
$data_row->migrate_map_hash); NULL);
$this->handleException($e); $this->handleException($e);
} }
$this->total_processed++; $this->total_processed++;
@ -779,11 +803,15 @@ abstract class Migration extends MigrationBase {
} }
try { try {
$this->source->next(); $this->source->next();
} } catch (Exception $e) {
catch (Exception $e) {
self::displayMessage( self::displayMessage(
t('Migration failed with source plugin exception: %e, in %file:%line', t('Migration for %class failed with source plugin exception: %e, in %file:%line',
array('%e' => $e->getMessage(), '%file' => $e->getFile(), '%line' => $e->getLine()))); array(
'%class' => get_class($this),
'%e' => $e->getMessage(),
'%file' => $e->getFile(),
'%line' => $e->getLine(),
)));
return MigrationBase::RESULT_FAILED; return MigrationBase::RESULT_FAILED;
} }
} }
@ -805,8 +833,7 @@ abstract class Migration extends MigrationBase {
self::$currentMigration = $this; self::$currentMigration = $this;
try { try {
$this->source->rewind(); $this->source->rewind();
} } catch (Exception $e) {
catch (Exception $e) {
self::displayMessage( self::displayMessage(
t('Migration analysis failed with source plugin exception: !e', t('Migration analysis failed with source plugin exception: !e',
array('!e' => $e->getMessage()))); array('!e' => $e->getMessage())));
@ -901,8 +928,7 @@ abstract class Migration extends MigrationBase {
try { try {
$this->source->next(); $this->source->next();
} } catch (Exception $e) {
catch (Exception $e) {
self::displayMessage( self::displayMessage(
t('Migration analysis failed with source plugin exception: !e. Partial results follow:', t('Migration analysis failed with source plugin exception: !e. Partial results follow:',
array('!e' => $e->getMessage()))); array('!e' => $e->getMessage())));
@ -940,17 +966,18 @@ abstract class Migration extends MigrationBase {
public function prepareKey($source_key, $row) { public function prepareKey($source_key, $row) {
$key = array(); $key = array();
foreach ($source_key as $field_name => $field_schema) { foreach ($source_key as $field_name => $field_schema) {
$key[$field_name] = $row->$field_name; $key[$field_name] = $row->{$field_name};
} }
return $key; return $key;
} }
/** /**
* Default implementation of prepareRow(). This method is called from the source * Default implementation of prepareRow(). This method is called from the
* plugin upon first pulling the raw data from the source. * source plugin upon first pulling the raw data from the source.
* *
* @param $row * @param $row
* Object containing raw source data. * Object containing raw source data.
*
* @return bool * @return bool
* TRUE to process this row, FALSE to have the source skip it. * TRUE to process this row, FALSE to have the source skip it.
*/ */
@ -971,8 +998,7 @@ abstract class Migration extends MigrationBase {
public function sourceCount($refresh = FALSE) { public function sourceCount($refresh = FALSE) {
try { try {
$count = $this->source->count($refresh); $count = $this->source->count($refresh);
} } catch (Exception $e) {
catch (Exception $e) {
$count = t('N/A'); $count = t('N/A');
self::displayMessage($e->getMessage()); self::displayMessage($e->getMessage());
} }
@ -981,14 +1007,14 @@ abstract class Migration extends MigrationBase {
/** /**
* Get the number of source records processed. * Get the number of source records processed.
*
* @return int * @return int
* Number of processed records. * Number of processed records.
*/ */
public function processedCount() { public function processedCount() {
try { try {
$count = $this->map->processedCount(); $count = $this->map->processedCount();
} } catch (Exception $e) {
catch (Exception $e) {
$count = t('N/A'); $count = t('N/A');
self::displayMessage($e->getMessage()); self::displayMessage($e->getMessage());
} }
@ -997,14 +1023,14 @@ abstract class Migration extends MigrationBase {
/** /**
* Get the number of records successfully imported. * Get the number of records successfully imported.
*
* @return int * @return int
* Number of imported records. * Number of imported records.
*/ */
public function importedCount() { public function importedCount() {
try { try {
$count = $this->map->importedCount(); $count = $this->map->importedCount();
} } catch (Exception $e) {
catch (Exception $e) {
$count = t('N/A'); $count = t('N/A');
self::displayMessage($e->getMessage()); self::displayMessage($e->getMessage());
} }
@ -1013,13 +1039,13 @@ abstract class Migration extends MigrationBase {
/** /**
* Get the number of records marked as needing update. * Get the number of records marked as needing update.
*
* @return int * @return int
*/ */
public function updateCount() { public function updateCount() {
try { try {
$count = $this->map->updateCount(); $count = $this->map->updateCount();
} } catch (Exception $e) {
catch (Exception $e) {
$count = t('N/A'); $count = t('N/A');
self::displayMessage($e->getMessage()); self::displayMessage($e->getMessage());
} }
@ -1072,15 +1098,17 @@ abstract class Migration extends MigrationBase {
} }
/** /**
* Outputs a progress message, reflecting the current status of a migration process. * Outputs a progress message, reflecting the current status of a migration
* process.
* *
* @param int $result * @param int $result
* Status of the process, represented by one of the Migration::RESULT_* constants. * Status of the process, represented by one of the Migration::RESULT_*
* constants.
*/ */
protected function progressMessage($result) { protected function progressMessage($result) {
$time = microtime(TRUE) - $this->lastfeedback; $time = microtime(TRUE) - $this->lastfeedback;
if ($time > 0) { if ($time > 0) {
$perminute = round(60*$this->processed_since_feedback/$time); $perminute = round(60 * $this->processed_since_feedback / $time);
$time = round($time, 1); $time = round($time, 1);
} }
else { else {
@ -1129,31 +1157,37 @@ abstract class Migration extends MigrationBase {
} }
$numitems = $this->processed_since_feedback + $this->source->getIgnored(); $numitems = $this->processed_since_feedback + $this->source->getIgnored();
$message = t($basetext, $message = t($basetext,
array('!numitems' => $numitems, array(
'!successes' => $this->successes_since_feedback, '!numitems' => $numitems,
'!failed' => $this->processed_since_feedback - $this->successes_since_feedback, '!successes' => $this->successes_since_feedback,
'!created' => $this->destination->getCreated(), '!failed' => $this->processed_since_feedback - $this->successes_since_feedback,
'!updated' => $this->destination->getUpdated(), '!created' => $this->destination->getCreated(),
'!ignored' => $this->source->getIgnored(), '!updated' => $this->destination->getUpdated(),
'!time' => $time, '!ignored' => $this->source->getIgnored(),
'!perminute' => $perminute, '!time' => $time,
'!name' => $this->machineName)); '!perminute' => $perminute,
'!name' => $this->machineName,
));
self::displayMessage($message, $type); self::displayMessage($message, $type);
// Report on lookup_cache hit rate. Only visible at 'debug' level. // Report on lookup_cache hit rate. Only visible at 'debug' level.
if ($result != Migration::RESULT_INCOMPLETE && !empty($this->counts['lookup_cache'])) { if ($result != Migration::RESULT_INCOMPLETE && !empty($this->counts['lookup_cache'])) {
foreach ($this->counts['lookup_cache'] as $name => $tallies) { foreach ($this->counts['lookup_cache'] as $name => $tallies) {
$tallies += array('hit' => 0, 'miss_hit' => 0, 'miss_miss' => 0); // Set defaults to avoid NOTICE. $tallies += array(
$sum = $tallies['hit']+$tallies['miss_hit']+$tallies['miss_miss']; 'hit' => 0,
'miss_hit' => 0,
'miss_miss' => 0,
); // Set defaults to avoid NOTICE.
$sum = $tallies['hit'] + $tallies['miss_hit'] + $tallies['miss_miss'];
self::displayMessage( self::displayMessage(
t('Lookup cache: !mn SM=!name !hit hit, !miss_hit miss_hit, !miss_miss miss_miss (!total total).', array( t('Lookup cache: !mn SM=!name !hit hit, !miss_hit miss_hit, !miss_miss miss_miss (!total total).', array(
'!mn' => $this->machineName, '!mn' => $this->machineName,
'!name' => $name, '!name' => $name,
'!hit' => round((100*$tallies['hit'])/$sum) . '%', '!hit' => round((100 * $tallies['hit']) / $sum) . '%',
'!miss_hit' => round((100*$tallies['miss_hit'])/$sum) . '%', '!miss_hit' => round((100 * $tallies['miss_hit']) / $sum) . '%',
'!miss_miss' => round((100*$tallies['miss_miss'])/$sum) . '%', '!miss_miss' => round((100 * $tallies['miss_miss']) / $sum) . '%',
'!total' => $sum '!total' => $sum,
)), 'debug'); )), 'debug');
} }
$this->counts['lookup_cache'] = array(); $this->counts['lookup_cache'] = array();
} }
@ -1182,7 +1216,7 @@ abstract class Migration extends MigrationBase {
// If feedback is requested, produce a progress message at the proper time // If feedback is requested, produce a progress message at the proper time
if (isset($this->feedback)) { if (isset($this->feedback)) {
if (($this->feedback_unit == 'seconds' && time() - $this->lastfeedback >= $this->feedback) || if (($this->feedback_unit == 'seconds' && time() - $this->lastfeedback >= $this->feedback) ||
($this->feedback_unit == 'items' && $this->processed_since_feedback >= $this->feedback)) { ($this->feedback_unit == 'items' && $this->processed_since_feedback >= $this->feedback)) {
$this->progressMessage(MigrationBase::RESULT_INCOMPLETE); $this->progressMessage(MigrationBase::RESULT_INCOMPLETE);
} }
} }
@ -1214,7 +1248,7 @@ abstract class Migration extends MigrationBase {
// If there's a source mapping, and a source value in the data row, copy // If there's a source mapping, and a source value in the data row, copy
// to the destination // to the destination
if ($source && isset($this->sourceValues->{$source})) { if ($source && isset($this->sourceValues->{$source})) {
$destination_values = $this->sourceValues->$source; $destination_values = $this->sourceValues->{$source};
} }
// Otherwise, apply the default value (if any) // Otherwise, apply the default value (if any)
elseif (!is_null($default)) { elseif (!is_null($default)) {
@ -1225,7 +1259,25 @@ abstract class Migration extends MigrationBase {
// will be populated as an array exploded from the source value // will be populated as an array exploded from the source value
$separator = $mapping->getSeparator(); $separator = $mapping->getSeparator();
if ($separator && isset($destination_values)) { if ($separator && isset($destination_values)) {
$destination_values = explode($separator, $destination_values); if (is_array($separator)) {
if (isset($separator['group separator'])) {
$destination_values = explode($separator['group separator'], $destination_values);
}
else {
$destination_values = array($destination_values);
}
foreach ($destination_values as $group => $value) {
if (isset($separator['key separator'])) {
$destination_values[$group] = explode($separator['key separator'], $value);
}
else {
$destination_values[$group] = array($value);
}
}
}
else {
$destination_values = explode($separator, $destination_values);
}
} }
// If a source migration is supplied, use the current value for this field // If a source migration is supplied, use the current value for this field
@ -1239,7 +1291,7 @@ abstract class Migration extends MigrationBase {
$callbacks = $mapping->getCallbacks(); $callbacks = $mapping->getCallbacks();
foreach ($callbacks as $callback) { foreach ($callbacks as $callback) {
if (isset($destination_values)) { if (isset($destination_values)) {
$destination_values = call_user_func($callback, $destination_values); $destination_values = call_user_func_array($callback['callback'], array_merge(array($destination_values), $callback['params']));
} }
} }
@ -1262,7 +1314,7 @@ abstract class Migration extends MigrationBase {
$destination_values['arguments'] = array(); $destination_values['arguments'] = array();
foreach ($arguments as $argname => $destarg) { foreach ($arguments as $argname => $destarg) {
if (is_array($destarg) && isset($destarg['source_field']) && property_exists($this->sourceValues, $destarg['source_field'])) { if (is_array($destarg) && isset($destarg['source_field']) && property_exists($this->sourceValues, $destarg['source_field'])) {
$destination_values['arguments'][$argname] = $this->sourceValues->$destarg['source_field']; $destination_values['arguments'][$argname] = $this->sourceValues->{$destarg['source_field']};
} }
elseif (is_array($destarg) && isset($destarg['default_value'])) { elseif (is_array($destarg) && isset($destarg['default_value'])) {
$destination_values['arguments'][$argname] = $destarg['default_value']; $destination_values['arguments'][$argname] = $destarg['default_value'];
@ -1281,47 +1333,47 @@ abstract class Migration extends MigrationBase {
// last one. // last one.
$destination_count = count($destination); $destination_count = count($destination);
$destination_field = $destination[0]; $destination_field = $destination[0];
if ($destination_count == 2) { if ($destination_count == 2) {
$subfield = $destination[1]; $subfield = $destination[1];
// We're processing the subfield before the primary value, initialize it // We're processing the subfield before the primary value, initialize it
if (!property_exists($this->destinationValues, $destination_field)) { if (!property_exists($this->destinationValues, $destination_field)) {
$this->destinationValues->$destination_field = array(); $this->destinationValues->{$destination_field} = array();
} }
// We have a value, and need to convert to an array so we can add // We have a value, and need to convert to an array so we can add
// arguments. // arguments.
elseif (!is_array($this->destinationValues->$destination_field)) { elseif (!is_array($this->destinationValues->{$destination_field})) {
$this->destinationValues->$destination_field = array($this->destinationValues->$destination_field); $this->destinationValues->{$destination_field} = array($this->destinationValues->{$destination_field});
} }
// Add the subfield value to the arguments array. // Add the subfield value to the arguments array.
$this->destinationValues->{$destination_field}['arguments'][$subfield] = $destination_values; $this->destinationValues->{$destination_field}['arguments'][$subfield] = $destination_values;
} }
elseif ($destination_count == 3) { elseif ($destination_count == 3) {
$subfield2 = $destination[2]; $subfield2 = $destination[2];
// We're processing the subfield before the primary value, initialize it // We're processing the subfield before the primary value, initialize it
if (!property_exists($this->destinationValues, $destination_field)) { if (!property_exists($this->destinationValues, $destination_field)) {
$this->destinationValues->$destination_field = array(); $this->destinationValues->{$destination_field} = array();
} }
// We have a value, and need to convert to an array so we can add // We have a value, and need to convert to an array so we can add
// arguments. // arguments.
elseif (!is_array($this->destinationValues->$destination_field)) { elseif (!is_array($this->destinationValues->{$destination_field})) {
$this->destinationValues->$destination_field = array($this->destinationValues->$destination_field); $this->destinationValues->{$destination_field} = array($this->destinationValues->{$destination_field});
} }
if (!is_array($this->destinationValues->{$destination_field}['arguments'][$destination[1]])) { if (!is_array($this->destinationValues->{$destination_field}['arguments'][$destination[1]])) {
// Convert first subfield level to an array so we can add to it. // Convert first subfield level to an array so we can add to it.
$this->destinationValues->{$destination_field}['arguments'][$destination[1]] = array( $this->destinationValues->{$destination_field}['arguments'][$destination[1]] ); $this->destinationValues->{$destination_field}['arguments'][$destination[1]] = array($this->destinationValues->{$destination_field}['arguments'][$destination[1]]);
} }
// Add the subfield value to the arguments array. // Add the subfield value to the arguments array.
$this->destinationValues->{$destination_field}['arguments'][$destination[1]]['arguments'][$subfield2] = $destination_values; $this->destinationValues->{$destination_field}['arguments'][$destination[1]]['arguments'][$subfield2] = $destination_values;
} }
// Just the primary value, the first time through for this field, simply // Just the primary value, the first time through for this field, simply
// set it. // set it.
elseif (!property_exists($this->destinationValues, $destination_field)) { elseif (!property_exists($this->destinationValues, $destination_field)) {
$this->destinationValues->$destination_field = $destination_values; $this->destinationValues->{$destination_field} = $destination_values;
} }
// We've seen a subfield, so add as an array value. // We've seen a subfield, so add as an array value.
else { else {
$this->destinationValues->{$destination_field} = array_merge( $this->destinationValues->{$destination_field} = array_merge(
(array)$destination_values, $this->destinationValues->{$destination_field}); (array) $destination_values, $this->destinationValues->{$destination_field});
} }
} }
} }
@ -1333,14 +1385,15 @@ abstract class Migration extends MigrationBase {
* @param mixed $source_migrations * @param mixed $source_migrations
* An array of source migrations, or string for a single migration. * An array of source migrations, or string for a single migration.
* @param mixed $source_keys * @param mixed $source_keys
* Key(s) to be looked up against the source migration(s). This may be a simple * Key(s) to be looked up against the source migration(s). This may be a
* value (one single-field key), an array of values (multiple single-field keys * simple value (one single-field key), an array of values (multiple
* to each be looked up), or an array of arrays (multiple multi-field keys to * single-field keys to each be looked up), or an array of arrays (multiple
* each be looked up). * multi-field keys to each be looked up).
* @param mixed $default * @param mixed $default
* The default value, if no ID was found. * The default value, if no ID was found.
* @param $migration * @param $migration
* The implementing migration. * The implementing migration.
*
* @return * @return
* Destination value(s) from the source migration(s), as a single value if * Destination value(s) from the source migration(s), as a single value if
* a single key was passed in, or an array of values if there were multiple * a single key was passed in, or an array of values if there were multiple
@ -1377,6 +1430,11 @@ abstract class Migration extends MigrationBase {
// Instantiate each migration, and store back in the array. // Instantiate each migration, and store back in the array.
foreach ($source_migrations as $key => $source_migration) { foreach ($source_migrations as $key => $source_migration) {
$source_migrations[$key] = Migration::getInstance($source_migration); $source_migrations[$key] = Migration::getInstance($source_migration);
if (!isset($source_migrations[$key])) {
MigrationBase::displayMessage(t('The @source cannot be resolved to a migration instance.',
array('@source' => $source_migration)));
unset($source_migrations[$key]);
}
} }
$results = array(); $results = array();
@ -1398,7 +1456,8 @@ abstract class Migration extends MigrationBase {
// Loop through each source migration, checking for an existing dest ID. // Loop through each source migration, checking for an existing dest ID.
foreach ($source_migrations as $source_migration) { foreach ($source_migrations as $source_migration) {
// Break out of the loop as soon as a destination ID is found. // Break out of the loop as soon as a destination ID is found.
if ($destids = $source_migration->getMap()->lookupDestinationID($source_key)) { if ($destids = $source_migration->getMap()
->lookupDestinationID($source_key)) {
if (!empty($destids['destid1'])) { if (!empty($destids['destid1'])) {
break; break;
} }
@ -1448,16 +1507,18 @@ abstract class Migration extends MigrationBase {
} }
/** /**
* For fields which require uniqueness, assign a new unique value if necessary. * For fields which require uniqueness, assign a new unique value if
* necessary.
* *
* @param array $dedupe * @param array $dedupe
* An array with two keys, 'table' the name of the Drupal table and 'column' * An array with two keys, 'table' the name of the Drupal table and 'column'
* the column within that table where uniqueness must be maintained. * the column within that table where uniqueness must be maintained.
* @param $original * @param $original
* The value coming in, which must be checked for uniqueness. * The value coming in, which must be checked for uniqueness.
*
* @return string * @return string
* The value to use - either the original, or a variation created by appending * The value to use - either the original, or a variation created by
* a sequence number. * appending a sequence number.
*/ */
protected function handleDedupe($dedupe, $original) { protected function handleDedupe($dedupe, $original) {
// If we're remigrating a previously-existing value, simply running through // If we're remigrating a previously-existing value, simply running through
@ -1468,11 +1529,11 @@ abstract class Migration extends MigrationBase {
if (isset($this->sourceValues->migrate_map_destid1)) { if (isset($this->sourceValues->migrate_map_destid1)) {
$key_field = key($this->destination->getKeySchema()); $key_field = key($this->destination->getKeySchema());
$existing_value = db_select($dedupe['table'], 't') $existing_value = db_select($dedupe['table'], 't')
->fields('t', array($dedupe['column'])) ->fields('t', array($dedupe['column']))
->range(0, 1) ->range(0, 1)
->condition($key_field, $this->sourceValues->migrate_map_destid1) ->condition($key_field, $this->sourceValues->migrate_map_destid1)
->execute() ->execute()
->fetchField(); ->fetchField();
// Note that if, for some reason, we don't find a value, fall through // Note that if, for some reason, we don't find a value, fall through
// to the normal deduping process // to the normal deduping process
if ($existing_value) { if ($existing_value) {
@ -1481,12 +1542,12 @@ abstract class Migration extends MigrationBase {
} }
$i = 1; $i = 1;
$candidate = $original; $candidate = $original;
while ($candidate_found = db_select($dedupe['table'], 't') while (db_select($dedupe['table'], 't')
->fields('t', array($dedupe['column'])) ->fields('t', array($dedupe['column']))
->range(0, 1) ->range(0, 1)
->condition('t.' . $dedupe['column'], $candidate) ->condition('t.' . $dedupe['column'], $candidate)
->execute() ->execute()
->fetchField()) { ->rowCount() > 0) {
// We already have the candidate value. Find a non-existing value. // We already have the candidate value. Find a non-existing value.
$i++; $i++;
// @TODO: support custom replacement pattern instead of just append. // @TODO: support custom replacement pattern instead of just append.
@ -1494,9 +1555,11 @@ abstract class Migration extends MigrationBase {
} }
if ($i > 1) { if ($i > 1) {
$message = t('Replacing !column !original with !candidate', $message = t('Replacing !column !original with !candidate',
array('!column' => $dedupe['column'], array(
'!original' => $original, '!column' => $dedupe['column'],
'!candidate' => $candidate)); '!original' => $original,
'!candidate' => $candidate,
));
$migration = Migration::currentMigration(); $migration = Migration::currentMigration();
$migration->saveMessage($message, Migration::MESSAGE_INFORMATIONAL); $migration->saveMessage($message, Migration::MESSAGE_INFORMATIONAL);
} }
@ -1515,7 +1578,7 @@ abstract class Migration extends MigrationBase {
$data_row = new stdClass; $data_row = new stdClass;
$i = 0; $i = 0;
foreach ($map_source_key as $key => $definition) { foreach ($map_source_key as $key => $definition) {
$data_row->$key = $source_key[$i++]; $data_row->{$key} = $source_key[$i++];
} }
$this->map->saveIDMapping($data_row, $destids, $this->map->saveIDMapping($data_row, $destids,
MigrateMap::STATUS_NEEDS_UPDATE, $this->defaultRollbackAction); MigrateMap::STATUS_NEEDS_UPDATE, $this->defaultRollbackAction);
@ -1577,15 +1640,18 @@ abstract class Migration extends MigrationBase {
* Migration instead. * Migration instead.
*/ */
abstract class DynamicMigration extends Migration { abstract class DynamicMigration extends Migration {
static $deprecationWarning = FALSE; static $deprecationWarning = FALSE;
public function __construct($arguments) { public function __construct($arguments) {
parent::__construct($arguments); parent::__construct($arguments);
if (variable_get('migrate_deprecation_warnings', 1) && if (variable_get('migrate_deprecation_warnings', 1) &&
!self::$deprecationWarning) { !self::$deprecationWarning) {
self::displayMessage(t('The DynamicMigration class is no longer necessary and is now deprecated - please derive your migration classes directly from Migration.')); self::displayMessage(t('The DynamicMigration class is no longer necessary and is now deprecated - please derive your migration classes directly from Migration.'));
self::$deprecationWarning = TRUE; self::$deprecationWarning = TRUE;
} }
} }
/** /**
* Overrides default of FALSE * Overrides default of FALSE
*/ */

View File

@ -13,6 +13,7 @@
* MigrateSourceSQL for an example. * MigrateSourceSQL for an example.
*/ */
abstract class MigrateSource implements Iterator { abstract class MigrateSource implements Iterator {
/** /**
* The current row from the quey * The current row from the quey
* *
@ -26,6 +27,7 @@ abstract class MigrateSource implements Iterator {
* @var array * @var array
*/ */
protected $currentKey; protected $currentKey;
public function getCurrentKey() { public function getCurrentKey() {
return $this->currentKey; return $this->currentKey;
} }
@ -50,6 +52,7 @@ abstract class MigrateSource implements Iterator {
* @var int * @var int
*/ */
protected $numIgnored = 0; protected $numIgnored = 0;
public function getIgnored() { public function getIgnored() {
return $this->numIgnored; return $this->numIgnored;
} }
@ -60,6 +63,7 @@ abstract class MigrateSource implements Iterator {
* @var int * @var int
*/ */
protected $numProcessed = 0; protected $numProcessed = 0;
public function getProcessed() { public function getProcessed() {
return $this->numProcessed; return $this->numProcessed;
} }
@ -160,7 +164,7 @@ abstract class MigrateSource implements Iterator {
} }
if (!isset($this->cacheKey)) { if (!isset($this->cacheKey)) {
$this->cacheKey = md5((string)$this); $this->cacheKey = md5((string) $this);
} }
// If a refresh is requested, or we're not caching counts, ask the derived // If a refresh is requested, or we're not caching counts, ask the derived
@ -227,17 +231,18 @@ abstract class MigrateSource implements Iterator {
} }
/** /**
* Implementation of Iterator::key - called when entering a loop iteration, returning * Implementation of Iterator::key - called when entering a loop iteration,
* the key of the current row. It must be a scalar - we will serialize * returning the key of the current row. It must be a scalar - we will
* to fulfill the requirement, but using getCurrentKey() is preferable. * serialize to fulfill the requirement, but using getCurrentKey() is
* preferable.
*/ */
public function key() { public function key() {
return serialize($this->currentKey); return serialize($this->currentKey);
} }
/** /**
* Implementation of Iterator::valid() - called at the top of the loop, returning * Implementation of Iterator::valid() - called at the top of the loop,
* TRUE to process the loop and FALSE to terminate it * returning TRUE to process the loop and FALSE to terminate it
*/ */
public function valid() { public function valid() {
return !is_null($this->currentRow); return !is_null($this->currentRow);
@ -292,7 +297,7 @@ abstract class MigrateSource implements Iterator {
if ($map_row) { if ($map_row) {
foreach ($map_row as $field => $value) { foreach ($map_row as $field => $value) {
$field = 'migrate_map_' . $field; $field = 'migrate_map_' . $field;
$row->$field = $value; $row->{$field} = $value;
} }
} }
} }
@ -422,12 +427,12 @@ abstract class MigrateSource implements Iterator {
// so we need to provide them with the necessary information (before and // so we need to provide them with the necessary information (before and
// after hashes). // after hashes).
if ($this->trackChanges) { if ($this->trackChanges) {
$unhashed_row = clone ($row); $unhashed_row = clone $row;
// Remove all map data, otherwise we'll have a false positive on the // Remove all map data, otherwise we'll have a false positive on the
// second import (attempt) on a row. // second import (attempt) on a row.
foreach ($unhashed_row as $field => $data) { foreach ($unhashed_row as $field => $data) {
if (strpos($field, 'migrate_map_') === 0) { if (strpos($field, 'migrate_map_') === 0) {
unset($unhashed_row->$field); unset($unhashed_row->{$field});
} }
} }
$row->migrate_map_original_hash = isset($row->migrate_map_hash) ? $row->migrate_map_original_hash = isset($row->migrate_map_hash) ?

Some files were not shown because too many files have changed in this diff Show More