diff --git a/hosting.css b/hosting.css
index fe185b8..b25c231 100644
--- a/hosting.css
+++ b/hosting.css
@@ -79,6 +79,18 @@ td.hosting-actions .hosting-button-disabled {
text-transform: none;
}
+.view-hosting-task-list .hosting-status time {
+ font-size:0.8em;
+ color: #555;
+ text-transform: uppercase;
+ padding: 0 1em 0 0;
+ white-space: nowrap;
+}
+
+.view-hosting-task-list .hosting-status time em.placeholder {
+ font-style: normal;
+}
+
/**
* Status icons, colors.
*/
diff --git a/migrate/hosting_migrate.module b/migrate/hosting_migrate.module
index 65cc62f..9342b89 100644
--- a/migrate/hosting_migrate.module
+++ b/migrate/hosting_migrate.module
@@ -49,13 +49,13 @@ function hosting_migrate_hosting_tasks() {
$tasks = array();
$tasks['site']['migrate'] = array(
- 'title' => t('Migrate'),
+ 'title' => t('Migrate Site'),
'description' => t('Move the site to a new platform.'),
'dialog' => TRUE,
);
$tasks['platform']['migrate'] = array(
- 'title' => t('Migrate'),
+ 'title' => t('Migrate Sites'),
'description' => t('Migrate sites to a new platform.'),
'page arguments' => array('hosting_migrate_platform', 1),
'dialog' => TRUE,
diff --git a/platform/hosting_platform.module b/platform/hosting_platform.module
index ca94987..d2be2bf 100644
--- a/platform/hosting_platform.module
+++ b/platform/hosting_platform.module
@@ -74,13 +74,13 @@ function hosting_platform_node_info() {
function hosting_platform_hosting_tasks() {
$tasks = array();
$tasks['platform']['verify'] = array(
- 'title' => t('Verify'),
+ 'title' => t('Verify Platform'),
'description' => t('Verify that the platform is correctly installed and working.'),
'weight' => 10,
'provision_save' => TRUE,
);
$tasks['platform']['delete'] = array(
- 'title' => t('Delete'),
+ 'title' => t('Delete Platform'),
'description' => t('Deleting this platform will completely remove it from the hosting system.
This process can not be undone. It can not be performed if you have sites currently
running on this platform.
@@ -775,8 +775,6 @@ function hosting_platform_view($node, $view_mode, $langcode = NULL) {
'changed' => $node->changed,
);
drupal_add_js($settings, array('type' => 'setting', 'scope' => JS_DEFAULT));
-
- drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
}
return $node;
diff --git a/server/hosting_server.module b/server/hosting_server.module
index cb88334..9973949 100644
--- a/server/hosting_server.module
+++ b/server/hosting_server.module
@@ -130,14 +130,14 @@ function hosting_server_menu() {
function hosting_server_hosting_tasks() {
$tasks = array();
$tasks['server']['verify'] = array(
- 'title' => t('Verify'),
+ 'title' => t('Verify Server'),
'description' => t('Verify that the server is correctly installed and working.'),
'weight' => 10,
'provision_save' => TRUE,
);
$tasks['server']['delete'] = array(
- 'title' => t('Delete'),
+ 'title' => t('Delete Server'),
'description' => t('Delete the server.'),
);
@@ -729,8 +729,6 @@ function hosting_server_view($node, $view_mode, $langcode = NULL) {
'changed' => $node->changed,
);
drupal_add_js($settings, array('type' => 'setting', 'scope' => JS_DEFAULT));
-
- drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
}
}
diff --git a/site/hosting_site.module b/site/hosting_site.module
index 38a9175..d0cdd45 100644
--- a/site/hosting_site.module
+++ b/site/hosting_site.module
@@ -294,7 +294,7 @@ function hosting_site_hosting_tasks() {
);
$tasks['site']['verify'] = array(
- 'title' => t('Verify'),
+ 'title' => t('Verify Site'),
'description' => t('Confirm that the site has been correctly installed and regenerate all configuration files to match the hosting front end.'),
'provision_save' => TRUE,
);
@@ -311,7 +311,7 @@ function hosting_site_hosting_tasks() {
It may be disabled again if needed.'),
);
$tasks['site']['delete'] = array(
- 'title' => t('Delete'),
+ 'title' => t('Delete Site'),
'description' => t('Deleting this site will completely remove it from the hosting system,
but will keep the last backup available. This process can not be undone.
Are you really sure you want to delete this site?'),
diff --git a/site/hosting_site.nodeapi.inc b/site/hosting_site.nodeapi.inc
index f7dc81f..096dd3e 100644
--- a/site/hosting_site.nodeapi.inc
+++ b/site/hosting_site.nodeapi.inc
@@ -111,8 +111,6 @@ function hosting_site_view($node, $view_mode, $langcode = NULL) {
'changed' => $node->changed,
);
drupal_add_js($settings, array('type' => 'setting', 'scope' => JS_DEFAULT));
-
- drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
}
return $node;
}
diff --git a/task/hosting_task.info b/task/hosting_task.info
index cd76d6a..3405dad 100644
--- a/task/hosting_task.info
+++ b/task/hosting_task.info
@@ -2,9 +2,11 @@ name = Hosting tasks
description = Allow Hostmaster to keep track of tasks that have been processed on the servers.
package = Hosting
dependencies[] = hosting
+dependencies[] = timeago
core = 7.x
files[] = includes/views/handlers/hosting_task_handler_field_status.inc
+files[] = includes/views/handlers/hosting_task_handler_field_type.inc
files[] = includes/views/handlers/hosting_task_handler_filter_type.inc
files[] = includes/views/handlers/hosting_task_handler_filter_status.inc
files[] = includes/views/handlers/hosting_task_handler_sort_default.inc
diff --git a/task/hosting_task.install b/task/hosting_task.install
index fadd378..0f986ed 100644
--- a/task/hosting_task.install
+++ b/task/hosting_task.install
@@ -325,4 +325,12 @@ function hosting_task_update_6200() {
*/
function hosting_task_update_7000() {
drupal_flush_all_caches();
+}
+
+/**
+ * Install timeago module and set the default format for the hosting_timeago date type.
+ */
+function hosting_task_update_7001() {
+ module_enable(array('timeago'));
+ variable_set('date_format_hosting_timeago', 'date_format_dynamic');
}
\ No newline at end of file
diff --git a/task/hosting_task.js b/task/hosting_task.js
index ae8cfe8..74c4cb0 100644
--- a/task/hosting_task.js
+++ b/task/hosting_task.js
@@ -1,91 +1,182 @@
(function($) {
-hostingTaskRefreshList = function() {
- if (!Drupal.settings.hostingTaskRefresh.nid) {
- return null;
- }
-
- var hostingTaskListRefreshCallback = function(data, responseText) {
- // If the node has been modified, reload the whole page.
- if (Drupal.settings.hostingTaskRefresh.changed < data.changed) {
- // only reload if there is no modal frame currently open
- if ($(document).data('hostingOpenModalFrame') != true) {
- // If a specific URL was specified, go there.
- if (data.navigate_url) {
- document.location = data.navigate_url;
- }
- // Fall back to just doing a reload of the current page.
- else {
- document.location.reload();
+ Drupal.behaviors.hostingTasks = {
+ attach: function (context, settings) {
+
+ // Attach to the global hosting tasks block.
+ if ($('#hostingTasks').length > 0) {
+ Drupal.settings.hostingTasks.vue = new Vue({
+ el: '#hostingTasks',
+ data: {
+ tasks: Drupal.settings.hostingTasks.tasks,
+ },
+ watch: {
+
+ // Watch tasks for changes: update timeago if timestamp changes
+ tasks: function (tasks, oldTasks) {
+ for (var i = 0, len = tasks.length; i < len; i++) {
+ var task = tasks[i];
+ if (task.timestamp != oldTasks[i].timestamp) {
+
+ // Set a tiny timeout so timeago reset happens after DOM update.
+ setTimeout(function () {
+ $("time.timeago").timeago("updateFromDOM");
+ }, 10);
+ return;
+ }
+ }
+ },
+ }
+ });
+ }
+
+ // Attach to the available_tasks block, if there is one.
+ if ($('#hostingAvailableTasks').length > 0) {
+ Drupal.settings.hostingTasks.vueAvailable = new Vue({
+ el: '#hostingAvailableTasks',
+ data: {
+ tasks: Drupal.settings.hostingAvailableTasks,
+ },
+ });
+
+ }
+
+ setTimeout("Drupal.behaviors.hostingTasks.checkTasks()", settings.hostingTasks.refreshTimeout);
+ },
+ checkTasks: function () {
+ var url = Drupal.settings.hostingTasks.url;
+ $.getJSON(url, function (data) {
+
+ // Replace vue data with new data.
+ Drupal.settings.hostingTasks.vue.tasks = data.tasks;
+ if (data.availableTasks && Drupal.settings.hostingTasks.vueAvailable) {
+ Drupal.settings.hostingTasks.vueAvailable.tasks = data.availableTasks;
+ }
+
+ // Stop if needed.
+ if (Drupal.settings.hostingTasks.halt != true) {
+ setTimeout("Drupal.behaviors.hostingTasks.checkTasks()", Drupal.settings.hostingTasks.refreshTimeout);
+ }
+ });
+ },
+ };
+
+ Drupal.behaviors.hostingTimeAgo = {
+ attach: function (context, settings) {
+ $.timeago.settings.refreshMillis = 1000;
+ $.timeago.settings.strings = {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "ago",
+ suffixFromNow: "from now",
+ inPast: 'any moment now',
+ seconds: "%d sec",
+ minute: "1 min",
+ minutes: "%d min",
+ hour: "1 hr",
+ hours: "%d hrs",
+ day: "1 day",
+ days: "%d days",
+ month: "1 month",
+ months: "%d months",
+ year: "1 year",
+ years: "%d years",
+ wordSeparator: " ",
+ numbers: []
+ }
+ $(".timeago", context).timeago();
}
- }
}
- else {
- $("#hosting-task-list").html(data.markup);
-
- hostingTaskBindButtons('#hosting-task-list');
- setTimeout("hostingTaskRefreshList()", Drupal.settings.hostingTaskRefresh.refreshTimeout);
- }
- }
-
- hostingTaskAddOverlay('#hosting-task-list');
- $.get(Drupal.settings.basePath + 'hosting/tasks/' + Drupal.settings.hostingTaskRefresh.nid + '/list', null, hostingTaskListRefreshCallback , 'json' );
-}
-
-
-function hostingTaskAddOverlay(elem) {
- $(elem).prepend('
');
-}
-
-
-hostingTaskRefreshQueueBlock = function() {
- if (Drupal.settings.hostingTaskRefresh.queueBlock != 1) {
- return null;
- }
-
- var hostingTaskQueueRefreshCallback = function(data, responseText) {
- $("#block-views-hosting-task-list-block .content").html(data.markup);
-
- hostingTaskBindButtons('#block-views-hosting-task-list-block');
- setTimeout("hostingTaskRefreshQueueBlock()", Drupal.settings.hostingTaskRefresh.refreshTimeout);
- }
-
- hostingTaskAddOverlay('#block-views-hosting-task-list-block .view-content');
- $.get(Drupal.settings.basePath + 'hosting/tasks/queue', null, hostingTaskQueueRefreshCallback , 'json');
-}
-
-$(document).ready(function() {
- $(document).data('hostingOpenModalFrame', false);
- setTimeout("hostingTaskRefreshList()", Drupal.settings.hostingTaskRefresh.refreshTimeout);
- setTimeout("hostingTaskRefreshQueueBlock()", Drupal.settings.hostingTaskRefresh.refreshTimeout);
- hostingTaskBindButtons($(this));
- $('#hosting-task-confirm-form-actions a').click(function() {
- if (parent.Drupal.modalFrame.isOpen) {
- setTimeout(function() { parent.Drupal.modalFrame.close({}, {}); }, 1);
- return false;
- }
- });
-
-});
-
-hostingTaskBindButtons = function(elem) {
- $('.hosting-button-dialog', elem).click(function() {
- $(document).data('hostingOpenModalFrame', true)
- var options = {
- url : Drupal.settings.basePath + 'hosting/js' + $(this).attr('href'),
- draggable : false,
- width : 600,
- height : 150,
- onSubmit : function() {
- $(document).data('hostingOpenModalFrame', false)
- hostingTaskRefreshQueueBlock();
- hostingTaskRefreshList();
- }
- }
- Drupal.modalFrame.open(options);
- return false;
- });
-}
-
-
-})(jQuery);
+}(jQuery));
+
+// (function($) {
+//
+// hostingTaskRefreshList = function() {
+// if (!Drupal.settings.hostingTaskRefresh.nid) {
+// return null;
+// }
+//
+// var hostingTaskListRefreshCallback = function(data, responseText) {
+// // If the node has been modified, reload the whole page.
+// if (Drupal.settings.hostingTaskRefresh.changed < data.changed) {
+// // only reload if there is no modal frame currently open
+// if ($(document).data('hostingOpenModalFrame') != true) {
+// // If a specific URL was specified, go there.
+// if (data.navigate_url) {
+// document.location = data.navigate_url;
+// }
+// // Fall back to just doing a reload of the current page.
+// else {
+// document.location.reload();
+// }
+// }
+// }
+// else {
+// $("#hosting-task-list").html(data.markup);
+//
+// hostingTaskBindButtons('#hosting-task-list');
+// setTimeout("hostingTaskRefreshList()", Drupal.settings.hostingTaskRefresh.refreshTimeout);
+// }
+// }
+//
+// hostingTaskAddOverlay('#hosting-task-list');
+// $.get(Drupal.settings.basePath + 'hosting/tasks/' + Drupal.settings.hostingTaskRefresh.nid + '/list', null, hostingTaskListRefreshCallback , 'json' );
+// }
+//
+//
+// function hostingTaskAddOverlay(elem) {
+// $(elem).prepend('');
+// }
+//
+//
+// hostingTaskRefreshQueueBlock = function() {
+// if (Drupal.settings.hostingTaskRefresh.queueBlock != 1) {
+// return null;
+// }
+//
+// var hostingTaskQueueRefreshCallback = function(data, responseText) {
+// // $("#block-views-hosting-task-list-block .content").html(data.markup);
+// //
+// // hostingTaskBindButtons('#block-views-hosting-task-list-block');
+// setTimeout("hostingTaskRefreshQueueBlock()", Drupal.settings.hostingTaskRefresh.refreshTimeout);
+// }
+//
+// // hostingTaskAddOverlay('#block-views-hosting-task-list-block .view-content');
+// $.get(Drupal.settings.basePath + 'hosting/tasks/queue', null, hostingTaskQueueRefreshCallback , 'json');
+// }
+//
+// $(document).ready(function() {
+// $(document).data('hostingOpenModalFrame', false);
+// setTimeout("hostingTaskRefreshList()", Drupal.settings.hostingTaskRefresh.refreshTimeout);
+// setTimeout("hostingTaskRefreshQueueBlock()", Drupal.settings.hostingTaskRefresh.refreshTimeout);
+// hostingTaskBindButtons($(this));
+// $('#hosting-task-confirm-form-actions a').click(function() {
+// if (parent.Drupal.modalFrame.isOpen) {
+// setTimeout(function() { parent.Drupal.modalFrame.close({}, {}); }, 1);
+// return false;
+// }
+// });
+//
+// });
+//
+// hostingTaskBindButtons = function(elem) {
+// $('.hosting-button-dialog', elem).click(function() {
+// $(document).data('hostingOpenModalFrame', true)
+// var options = {
+// url : Drupal.settings.basePath + 'hosting/js' + $(this).attr('href'),
+// draggable : false,
+// width : 600,
+// height : 150,
+// onSubmit : function() {
+// $(document).data('hostingOpenModalFrame', false)
+// hostingTaskRefreshQueueBlock();
+// hostingTaskRefreshList();
+// }
+// }
+// Drupal.modalFrame.open(options);
+// return false;
+// });
+// }
+//
+//
+// })(jQuery);
diff --git a/task/hosting_task.module b/task/hosting_task.module
index 598ae49..b09a1c4 100644
--- a/task/hosting_task.module
+++ b/task/hosting_task.module
@@ -10,10 +10,10 @@
* Adds refreshTimeout javascript variable.
*/
function hosting_task_init() {
- $settings['hostingTaskRefresh'] = array(
- 'refreshTimeout' => variable_get('hosting_task_refresh_timeout', 30000),
- );
- drupal_add_js($settings, 'setting');
+// $settings['hostingTaskRefresh'] = array(
+// 'refreshTimeout' => variable_get('hosting_task_refresh_timeout', 30000),
+// );
+// drupal_add_js($settings, 'setting');
}
@@ -48,13 +48,12 @@ function hosting_task_menu() {
}
}
- $items['hosting/tasks/%node/list'] = array(
+ $items['hosting/json/tasks'] = array(
'title' => 'Task list',
'description' => 'AJAX callback for refreshing task list',
'page callback' => 'hosting_task_ajax_list',
- 'page arguments' => array(2),
- 'access callback' => 'node_access',
- 'access arguments' => array('view', 2),
+ 'page arguments' => array(3),
+ 'access arguments' => array('access task logs'),
'type' => MENU_CALLBACK,
);
@@ -68,14 +67,6 @@ function hosting_task_menu() {
'type' => MENU_CALLBACK,
);
- $items['hosting/tasks/queue'] = array(
- 'title' => 'Task list',
- 'description' => 'AJAX callback for refreshing task queue',
- 'page callback' => 'hosting_task_ajax_queue',
- 'access arguments' => array('access task logs'),
- 'type' => MENU_CALLBACK,
- );
-
// Custom path to task node views for overlay.
// See hosting_task_overlay_paths().
$items['hosting/task/%node'] = array(
@@ -192,23 +183,20 @@ function hosting_task_ajax_command_hosting_table_check($selector, $url, $setting
/**
* Page callback to provide JSON output for a task.
*/
-function hosting_task_ajax_list($node) {
- $return['markup'] = hosting_task_table($node);
- $return['changed'] = $node->changed;
- $return['navigate_url'] = url('node/' . $node->nid);
- drupal_json_output($return);
- exit();
-}
+function hosting_task_ajax_list($nid = NULL) {
-/**
- * AJAX callback for refreshing task list.
- */
-function hosting_task_ajax_queue() {
- $view = views_get_view('hosting_task_list');
- $view->set_display('block');
- $view->pre_execute();
- $return['markup'] = $view->render('block');
+ // Load available tasks if a node is specified and accessible.
+ $node = node_load($nid);
+ if ($node && node_access('view', $node)) {
+ $return['availableTasks'] = array_values(hosting_task_fetch_tasks($node->nid));
+ $return['changed'] = $node->changed;
+ $return['navigate_url'] = url('node/' . $node->nid);
+ }
+
+ // Load global tasks for block.
+ $return['tasks'] = views_get_view_result('hosting_task_list', 'block');
+ // Output JSON
drupal_json_output($return);
exit();
}
@@ -584,7 +572,6 @@ function hosting_add_task($nid, $type, $args = NULL, $status = HOSTING_TASK_QUEU
* Implements hook_form().
*/
function hosting_task_confirm_form($form, $form_state, $node, $task) {
- drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
$tasks = hosting_available_tasks($node->type);
if (!isset($tasks[$task]['dialog']) || !$tasks[$task]['dialog']) {
@@ -798,7 +785,12 @@ function hosting_available_tasks($type = NULL, $reset = FALSE) {
drupal_alter('hosting_tasks', $cache);
}
if (isset($type)) {
- return $cache[$type];
+ if (isset($cache[$type])) {
+ return $cache[$type];
+ }
+ else {
+ return array();
+ }
}
else {
return $cache;
@@ -1459,56 +1451,12 @@ function hosting_task_list($filter_by = NULL, $filter_value = NULL) {
* simple interface.
*/
function hosting_task_table($node) {
- $output = '';
-
- $headers[] = t('Task');
- $headers[] = array(
- 'data' => t('Actions'),
- 'class' => array('hosting-actions'),
- );
-
- $tasklist = hosting_task_fetch_tasks($node->nid);
- $rows = array();
-
- foreach ($tasklist as $task => $info) {
- $row = array();
-
- if (!isset($info['nid']) && !$info['task_permitted']) {
- // Just don't show those tasks, since we'll not be able to run them.
- continue;
- }
-
- if (empty($info['title'])) {
- // Skip tasks from types that have since been removed.
- continue;
- }
-
- $row['type'] = array(
- 'data' => $info['title'],
- 'class' => array('hosting-status'),
- );
- $actions = array();
-
- if (isset($info['task_status']) && ($info['task_status'] == 0)) {
- $actions['cancel'] = _hosting_task_button(t('Cancel'), sprintf("hosting/tasks/%d/cancel", $info['nid']), t("Cancel the task and remove it from the queue"), 'hosting-button-stop', !$info['task_permitted']);
- }
- else {
- $actions['run'] = _hosting_task_button(t('Run'), sprintf("hosting_confirm/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']);
- }
-
- $actions['log'] = _hosting_task_button(t('View'), isset($info['nid']) ? 'hosting/task/' . $info['nid'] : '', t("Display the task log"), 'hosting-button-log', isset($info['nid']) && user_access('access task logs'), TRUE, FALSE);
- $row['actions'] = array(
- 'data' => implode('', $actions),
- 'class' => array('hosting-actions'),
- );
-
- $rows[] = array(
- 'data' => $row,
- 'class' => array($info['class']),
- );
- }
- $output .= theme('table', array('header' => $headers, 'rows' => $rows, 'attributes' => array('class' => array('hosting-table'))));
- return $output;
+ $tasks = hosting_task_fetch_tasks($node->nid);
+ $settings['hostingTasks']['availableTasks'] = $tasks;
+ drupal_add_js($settings, 'setting');
+ drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
+ drupal_add_js('https://npmcdn.com/vue/dist/vue.js', 'external');
+ return theme('hosting_task_table', array('tasks' => $tasks));
}
/**
@@ -1609,10 +1557,20 @@ function _hosting_task_list($filter_by, $filter_value, $count = 5, $element = 0,
}
/**
- * @todo Please document this function.
- * @see http://drupal.org/node/1354
+ * Retrieve the available tasks for a given Aegir Object. This function is used
+ * on the site/platform/server node page to provide the list of actions a user
+ * can take.
+ *
+ * @param $rid
+ * The node ID of the desired object.
+ *
+ * @return array
+ * A list of available tasks and metadata about the last task status.
+ *
+ * The keys of the array are the type of task, such as "verify" and "install".
*/
function hosting_task_fetch_tasks($rid) {
+ $return = array();
$node = node_load($rid);
$result = db_query("SELECT n.nid, t.task_type, t.task_status FROM {node} n INNER JOIN {hosting_task} t ON n.vid = t.vid
@@ -1630,6 +1588,8 @@ function hosting_task_fetch_tasks($rid) {
$tasks = hosting_available_tasks($node->type);
ksort($tasks);
+ global $user;
+
foreach ($tasks as $type => $hook_task) {
if (!isset($return[$type])) {
@@ -1641,6 +1601,8 @@ function hosting_task_fetch_tasks($rid) {
$task = array();
$task = array_merge($return[$type], $hook_task);
+ $task['task_type'] = $type;
+
$allowed = (isset($task['exists']) && !in_array($task['task_status'], array(HOSTING_TASK_QUEUED, HOSTING_TASK_PROCESSING))) || !isset($task['exists']);
if ($allowed && empty($task['hidden']) && $access_callback($node, $type)) {
$task['task_permitted'] = TRUE;
@@ -1657,12 +1619,61 @@ function hosting_task_fetch_tasks($rid) {
}
$task['class'] = hosting_task_status_class($task['task_status']);
+ // Generate Links
+ $task['view_link'] = FALSE;
+ $task['view_link_text'] = t('View');
+ $task['run_link'] = FALSE;
+ $task['cancel_link'] = FALSE;
+
+ $nid = isset($task['nid'])? $task['nid']: NULL;
+ $ref_type = $node->type;
+ $task_type = $task['task_type'];
+
+ // View Logs link
+ if (!empty($nid) && drupal_valid_path("hosting/task/{$nid}")) {
+ $task['view_link'] = array(
+ 'url' => url("hosting/task/{$nid}"),
+ 'title' => t('Display the task log.'),
+ 'text' => t('View'),
+ );
+ }
+
+ // Cancel Task link
+ if (isset($task['task_status']) && ($task['task_status'] === HOSTING_TASK_QUEUED)) {
+// $actions['cancel'] = _hosting_task_button(t('Cancel'), sprintf("hosting/tasks/%d/cancel", $info['nid']), t("Cancel the task and remove it from the queue"), 'hosting-button-stop', !$info['task_permitted']);
+
+ $task['cancel_link'] = array(
+ 'url' => url("hosting/tasks/{$nid}/cancel"),
+ 'title' => t('Cancel the task and remove it from the queue.'),
+ 'text' => t('Cancel'),
+ );
+ }
+ else {
+// $actions['run'] = _hosting_task_button(t('Run'), sprintf("hosting_confirm/%d/%s_%s", $node->nid, $node->type, $task), $info['description'], 'hosting-button-run', $info['task_permitted'], $info['dialog']);
+ $task['run_link'] = array(
+ 'url' => url("hosting_confirm/{$rid}/{$ref_type}_{$task_type}", array(
+ 'query'=> array(
+ 'token' => drupal_get_token($user->uid),
+ )
+ )),
+ 'title' => $task['description'],
+ 'text' => t('Run'),
+ );
+ }
+
+
+ if (empty($nid) && $task['task_permitted'] == FALSE) {
+ $task['hidden'] = TRUE;
+ }
+
$return[$type] = $task;
}
return $return;
}
+
+
/**
* Traslate a task status code into a css class.
*/
@@ -1692,12 +1703,35 @@ function hosting_task_status_class($status = NULL) {
}
/**
+ * Get a task's human type name from it's machine name.
+ */
+function hosting_task_type_name($task_type, $node_type = NULL) {
+
+ if ($node_type) {
+ $tasks = hosting_available_tasks($node_type);
+ }
+ else {
+ // Tasks are grouped by node type, and might vary by that even thought type value is the same.
+ $all_tasks = hosting_available_tasks();
+ $tasks = array_merge($all_tasks['server'], $all_tasks['platform'], $all_tasks['site']);
+ }
+
+ if (isset($tasks[$task_type]['title'])) {
+ return $tasks[$task_type]['title'];
+ }
+ else {
+ return $task_type;
+ }
+}
+
+/**
* Views integration.
*/
function hosting_task_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'hosting_task') . '/includes/views',
+ 'template path' => drupal_get_path('module', 'hosting_task') . '/templates',
);
}
@@ -1717,6 +1751,15 @@ function hosting_task_overlay_paths() {
}
/**
+ * Implements hook_date_format_types().
+ */
+function hosting_task_date_format_types() {
+ return array(
+ 'hosting_timeago' => t('Dynamic Time Ago'),
+ );
+}
+
+/**
* Implements hook_preprocess_views_view_table().
*/
function hosting_task_preprocess_views_view_table(&$vars) {
@@ -1724,17 +1767,68 @@ function hosting_task_preprocess_views_view_table(&$vars) {
switch ($id) {
case 'hosting_task_list-block':
- drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
-
$settings['hostingTaskRefresh'] = array(
'queueBlock' => 1,
);
- drupal_add_js($settings, 'setting');
+
break;
}
}
/**
+ * Implements hook_preprocess_HOOK().
+ */
+function hosting_task_preprocess_page(&$variables) {
+ $settings['hostingTasks'] = array(
+ 'url' => url("hosting/json/tasks"),
+ 'refreshTimeout' => 2000,
+ 'tasks' => views_get_view_result('hosting_task_list', 'block'),
+ );
+
+ // If on a hosting node page...
+ if (isset($variables['node']) && !empty($variables['node']->nid)) {
+ $availableTasks = hosting_task_fetch_tasks($variables['node']->nid);
+
+ // Get rid of array keys so it becomes an array.
+ $settings['hostingAvailableTasks'] = array_values($availableTasks);
+ $settings['hostingTasks']['url'] = url("hosting/json/tasks/" . $variables['node']->nid);
+ }
+
+ drupal_add_js($settings, 'setting');
+ drupal_add_js(drupal_get_path('module', 'hosting_task') . '/hosting_task.js');
+ drupal_add_js('https://npmcdn.com/vue/dist/vue.js', 'external');
+}
+
+/**
+ * Implements hook_views_post_execute()
+ *
+ * Alter the results of the hosting_task_list view.
+ * This view is used to generate the JSON list of tasks.
+ */
+function hosting_task_views_post_execute(&$view) {
+
+ // Parse Results on hosting_task_list view.
+ if ($view->name == 'hosting_task_list') {
+ foreach ($view->result as $i => &$result) {
+ $result->status_class = hosting_task_status_class($result->hosting_task_task_status);
+ $result->ref_url = url("node/$result->node_hosting_task_nid");
+ $result->task_url = url("hosting/task/$result->nid");
+ $result->task_link_text = t('View');
+ $result->timestamp = date('c', $result->node_created);
+ $result->timestamp_human = format_date($result->node_created);
+
+ // Load the correct task type name.
+ if (isset($result->node_hosting_task_type)) {
+ $result->task_type_name = hosting_task_type_name($result->hosting_task_task_type, $result->node_hosting_task_type);
+ }
+ else {
+ $result->task_type_name = hosting_task_type_name($result->hosting_task_task_type);
+ }
+ }
+ }
+}
+
+/**
* Set a task's status according to its log.
*
* @param object|int $task
@@ -1892,3 +1986,18 @@ function hosting_task_entity_property_info_alter(&$info) {
);
*/
}
+
+/**
+ * Implements hook_theme().
+ */
+function hosting_task_theme($existing, $type, $theme, $path) {
+ $theme = array();
+ $theme['hosting_task_table'] = array(
+ 'template' => 'hosting-task-table',
+ 'path' => drupal_get_path('module', 'hosting_task') . '/templates',
+ 'variables' => array(
+ array('tasks' => array()),
+ ),
+ );
+ return $theme;
+}
\ No newline at end of file
diff --git a/task/includes/views/handlers/hosting_task_handler_field_type.inc b/task/includes/views/handlers/hosting_task_handler_field_type.inc
new file mode 100644
index 0000000..ac0e620
--- /dev/null
+++ b/task/includes/views/handlers/hosting_task_handler_field_type.inc
@@ -0,0 +1,48 @@
+ 'text');
+ return $options;
+ }
+
+ function options_form(&$form, &$form_state) {
+ $form['type_format'] = array(
+ '#type' => 'radios',
+ '#title' => t('Display mode'),
+ '#options' => array(
+ 'raw' => t('Raw value'),
+ 'text' => t('Text value'),
+ ),
+ '#default_value' => isset($this->options['type_format'])? $this->options['type_format']: 'title',
+ '#description' => t('Output the machine-name or human name of the task type. To ensure correct name, make sure Node Type field (assigned to reference) is added to this view.'),
+ );
+ parent::options_form($form, $form_state);
+ }
+
+ function render($values) {
+ $value = $this->get_value($values);
+ switch ($this->options['type_format']) {
+ case 'text':
+
+ // Return the right type of task.
+ if (isset($values->node_hosting_task_type)) {
+ return hosting_task_type_name($value, $values->node_hosting_task_type);
+ }
+ else {
+ return hosting_task_type_name($value);
+ }
+ default:
+ return $value;
+ }
+ }
+}
+
diff --git a/task/includes/views/hosting_task.views.inc b/task/includes/views/hosting_task.views.inc
index ac324e2..99ffc6d 100644
--- a/task/includes/views/hosting_task.views.inc
+++ b/task/includes/views/hosting_task.views.inc
@@ -27,7 +27,7 @@ function hosting_task_views_data() {
'title' => t('Type'),
'help' => t('The type of task.'),
'field' => array(
- 'handler' => 'views_handler_field',
+ 'handler' => 'hosting_task_handler_field_type',
'click sortable' => TRUE,
),
'filter' => array(
diff --git a/task/includes/views/hosting_task.views_default.inc b/task/includes/views/hosting_task.views_default.inc
index 58a7d7c..a704918 100644
--- a/task/includes/views/hosting_task.views_default.inc
+++ b/task/includes/views/hosting_task.views_default.inc
@@ -101,13 +101,38 @@ function hosting_task_views_default_views() {
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['relationship'] = 'rid';
$handler->display->display_options['fields']['title']['exclude'] = TRUE;
- /* Field: Hosting Task: Type */
+ /* Field: Content: Type */
+ $handler->display->display_options['fields']['type']['id'] = 'type';
+ $handler->display->display_options['fields']['type']['table'] = 'node';
+ $handler->display->display_options['fields']['type']['field'] = 'type';
+ $handler->display->display_options['fields']['type']['relationship'] = 'rid';
+ $handler->display->display_options['fields']['type']['exclude'] = TRUE;
+ /* Field: Hosting Task: Executed */
+ $handler->display->display_options['fields']['executed']['id'] = 'executed';
+ $handler->display->display_options['fields']['executed']['table'] = 'hosting_task';
+ $handler->display->display_options['fields']['executed']['field'] = 'executed';
+ $handler->display->display_options['fields']['executed']['exclude'] = TRUE;
+ $handler->display->display_options['fields']['executed']['date_format'] = 'hosting_timeago';
+ $handler->display->display_options['fields']['executed']['second_date_format'] = 'hosting_timeago';
+ /* Field: Content: Post date */
+ $handler->display->display_options['fields']['created']['id'] = 'created';
+ $handler->display->display_options['fields']['created']['table'] = 'node';
+ $handler->display->display_options['fields']['created']['field'] = 'created';
+ $handler->display->display_options['fields']['created']['label'] = '';
+ $handler->display->display_options['fields']['created']['exclude'] = TRUE;
+ $handler->display->display_options['fields']['created']['alter']['text'] = '[created]';
+ $handler->display->display_options['fields']['created']['element_label_colon'] = FALSE;
+ $handler->display->display_options['fields']['created']['element_default_classes'] = FALSE;
+ $handler->display->display_options['fields']['created']['date_format'] = 'dynamic';
+ $handler->display->display_options['fields']['created']['second_date_format'] = 'custom';
+ /* Field: Output */
$handler->display->display_options['fields']['task_type']['id'] = 'task_type';
$handler->display->display_options['fields']['task_type']['table'] = 'hosting_task';
$handler->display->display_options['fields']['task_type']['field'] = 'task_type';
+ $handler->display->display_options['fields']['task_type']['ui_name'] = 'Output';
$handler->display->display_options['fields']['task_type']['label'] = 'Task';
$handler->display->display_options['fields']['task_type']['alter']['alter_text'] = TRUE;
- $handler->display->display_options['fields']['task_type']['alter']['text'] = '[task_type]: [title]';
+ $handler->display->display_options['fields']['task_type']['alter']['text'] = '[task_type]: [title] [created] ';
$handler->display->display_options['fields']['task_type']['element_class'] = 'hosting-status';
/* Field: Content: Nid */
$handler->display->display_options['fields']['nid']['id'] = 'nid';
@@ -120,20 +145,6 @@ function hosting_task_views_default_views() {
$handler->display->display_options['fields']['nid']['alter']['path'] = 'hosting/task/[nid]';
$handler->display->display_options['fields']['nid']['alter']['link_class'] = 'hosting-button-enabled hosting-button-log hosting-button-dialog';
$handler->display->display_options['fields']['nid']['element_class'] = 'hosting-actions';
- /* Field: Hosting Task: Executed */
- $handler->display->display_options['fields']['executed']['id'] = 'executed';
- $handler->display->display_options['fields']['executed']['table'] = 'hosting_task';
- $handler->display->display_options['fields']['executed']['field'] = 'executed';
- $handler->display->display_options['fields']['executed']['exclude'] = TRUE;
- $handler->display->display_options['fields']['executed']['date_format'] = 'long';
- /* Field: Content: Post date */
- $handler->display->display_options['fields']['created']['id'] = 'created';
- $handler->display->display_options['fields']['created']['table'] = 'node';
- $handler->display->display_options['fields']['created']['field'] = 'created';
- $handler->display->display_options['fields']['created']['label'] = '';
- $handler->display->display_options['fields']['created']['exclude'] = TRUE;
- $handler->display->display_options['fields']['created']['element_label_colon'] = FALSE;
- $handler->display->display_options['fields']['created']['date_format'] = 'long';
/* Sort criterion: Content: Updated date */
$handler->display->display_options['sorts']['changed']['id'] = 'changed';
$handler->display->display_options['sorts']['changed']['table'] = 'node';
@@ -271,12 +282,14 @@ function hosting_task_views_default_views() {
$handler->display->display_options['fields']['created']['table'] = 'node';
$handler->display->display_options['fields']['created']['field'] = 'created';
$handler->display->display_options['fields']['created']['label'] = 'Created';
- $handler->display->display_options['fields']['created']['date_format'] = 'time ago';
+ $handler->display->display_options['fields']['created']['date_format'] = 'hosting_timeago';
+ $handler->display->display_options['fields']['created']['second_date_format'] = 'long';
/* Field: Hosting Task: Executed */
$handler->display->display_options['fields']['executed']['id'] = 'executed';
$handler->display->display_options['fields']['executed']['table'] = 'hosting_task';
$handler->display->display_options['fields']['executed']['field'] = 'executed';
- $handler->display->display_options['fields']['executed']['date_format'] = 'time ago';
+ $handler->display->display_options['fields']['executed']['date_format'] = 'hosting_timeago';
+ $handler->display->display_options['fields']['executed']['second_date_format'] = 'long';
/* Field: Hosting Task: Execution time */
$handler->display->display_options['fields']['delta']['id'] = 'delta';
$handler->display->display_options['fields']['delta']['table'] = 'hosting_task';
@@ -412,7 +425,8 @@ function hosting_task_views_default_views() {
$handler->display->display_options['fields']['executed']['id'] = 'executed';
$handler->display->display_options['fields']['executed']['table'] = 'hosting_task';
$handler->display->display_options['fields']['executed']['field'] = 'executed';
- $handler->display->display_options['fields']['executed']['date_format'] = 'time ago';
+ $handler->display->display_options['fields']['executed']['date_format'] = 'dynamic';
+ $handler->display->display_options['fields']['executed']['second_date_format'] = 'long';
/* Field: Hosting Task: Execution time */
$handler->display->display_options['fields']['delta']['id'] = 'delta';
$handler->display->display_options['fields']['delta']['table'] = 'hosting_task';
diff --git a/task/js/jquery.timeago.js b/task/js/jquery.timeago.js
new file mode 100644
index 0000000..c85ebcc
--- /dev/null
+++ b/task/js/jquery.timeago.js
@@ -0,0 +1,229 @@
+/**
+ * Timeago is a jQuery plugin that makes it easy to support automatically
+ * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
+ *
+ * @name timeago
+ * @version 1.5.3
+ * @requires jQuery v1.2.3+
+ * @author Ryan McGeary
+ * @license MIT License - http://www.opensource.org/licenses/mit-license.php
+ *
+ * For usage and examples, visit:
+ * http://timeago.yarp.com/
+ *
+ * Copyright (c) 2008-2015, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
+ */
+
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else if (typeof module === 'object' && typeof module.exports === 'object') {
+ factory(require('jquery'));
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+}(function ($) {
+ $.timeago = function(timestamp) {
+ if (timestamp instanceof Date) {
+ return inWords(timestamp);
+ } else if (typeof timestamp === "string") {
+ return inWords($.timeago.parse(timestamp));
+ } else if (typeof timestamp === "number") {
+ return inWords(new Date(timestamp));
+ } else {
+ return inWords($.timeago.datetime(timestamp));
+ }
+ };
+ var $t = $.timeago;
+
+ $.extend($.timeago, {
+ settings: {
+ refreshMillis: 60000,
+ allowPast: true,
+ allowFuture: false,
+ localeTitle: false,
+ cutoff: 0,
+ autoDispose: true,
+ strings: {
+ prefixAgo: null,
+ prefixFromNow: null,
+ suffixAgo: "ago",
+ suffixFromNow: "from now",
+ inPast: 'any moment now',
+ seconds: "less than a minute",
+ minute: "about a minute",
+ minutes: "%d minutes",
+ hour: "about an hour",
+ hours: "about %d hours",
+ day: "a day",
+ days: "%d days",
+ month: "about a month",
+ months: "%d months",
+ year: "about a year",
+ years: "%d years",
+ wordSeparator: " ",
+ numbers: []
+ }
+ },
+
+ inWords: function(distanceMillis) {
+ if (!this.settings.allowPast && ! this.settings.allowFuture) {
+ throw 'timeago allowPast and allowFuture settings can not both be set to false.';
+ }
+
+ var $l = this.settings.strings;
+ var prefix = $l.prefixAgo;
+ var suffix = $l.suffixAgo;
+ if (this.settings.allowFuture) {
+ if (distanceMillis < 0) {
+ prefix = $l.prefixFromNow;
+ suffix = $l.suffixFromNow;
+ }
+ }
+
+ if (!this.settings.allowPast && distanceMillis >= 0) {
+ return this.settings.strings.inPast;
+ }
+
+ var seconds = Math.abs(distanceMillis) / 1000;
+ var minutes = seconds / 60;
+ var hours = minutes / 60;
+ var days = hours / 24;
+ var years = days / 365;
+
+ function substitute(stringOrFunction, number) {
+ var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
+ var value = ($l.numbers && $l.numbers[number]) || number;
+ return string.replace(/%d/i, value);
+ }
+
+ var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
+ seconds < 90 && substitute($l.minute, 1) ||
+ minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
+ minutes < 90 && substitute($l.hour, 1) ||
+ hours < 24 && substitute($l.hours, Math.round(hours)) ||
+ hours < 42 && substitute($l.day, 1) ||
+ days < 30 && substitute($l.days, Math.round(days)) ||
+ days < 45 && substitute($l.month, 1) ||
+ days < 365 && substitute($l.months, Math.round(days / 30)) ||
+ years < 1.5 && substitute($l.year, 1) ||
+ substitute($l.years, Math.round(years));
+
+ var separator = $l.wordSeparator || "";
+ if ($l.wordSeparator === undefined) { separator = " "; }
+ return $.trim([prefix, words, suffix].join(separator));
+ },
+
+ parse: function(iso8601) {
+ var s = $.trim(iso8601);
+ s = s.replace(/\.\d+/,""); // remove milliseconds
+ s = s.replace(/-/,"/").replace(/-/,"/");
+ s = s.replace(/T/," ").replace(/Z/," UTC");
+ s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
+ s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
+ return new Date(s);
+ },
+ datetime: function(elem) {
+ var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
+ return $t.parse(iso8601);
+ },
+ isTime: function(elem) {
+ // jQuery's `is()` doesn't play well with HTML5 in IE
+ return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
+ }
+ });
+
+ // functions that can be called via $(el).timeago('action')
+ // init is default when no action is given
+ // functions are called with context of a single element
+ var functions = {
+ init: function() {
+ var refresh_el = $.proxy(refresh, this);
+ refresh_el();
+ var $s = $t.settings;
+ if ($s.refreshMillis > 0) {
+ this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
+ }
+ },
+ update: function(timestamp) {
+ var date = (timestamp instanceof Date) ? timestamp : $t.parse(timestamp);
+ $(this).data('timeago', { datetime: date });
+ if ($t.settings.localeTitle) $(this).attr("title", date.toLocaleString());
+ refresh.apply(this);
+ },
+ updateFromDOM: function() {
+ $(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
+ refresh.apply(this);
+ },
+ dispose: function () {
+ if (this._timeagoInterval) {
+ window.clearInterval(this._timeagoInterval);
+ this._timeagoInterval = null;
+ }
+ }
+ };
+
+ $.fn.timeago = function(action, options) {
+ var fn = action ? functions[action] : functions.init;
+ if (!fn) {
+ throw new Error("Unknown function name '"+ action +"' for timeago");
+ }
+ // each over objects here and call the requested function
+ this.each(function() {
+ fn.call(this, options);
+ });
+ return this;
+ };
+
+ function refresh() {
+ var $s = $t.settings;
+
+ //check if it's still visible
+ if ($s.autoDispose && !$.contains(document.documentElement,this)) {
+ //stop if it has been removed
+ $(this).timeago("dispose");
+ return this;
+ }
+
+ var data = prepareData(this);
+
+ if (!isNaN(data.datetime)) {
+ if ( $s.cutoff == 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
+ $(this).text(inWords(data.datetime));
+ } else {
+ if ($(this).attr('title').length > 0) {
+ $(this).text($(this).attr('title'));
+ }
+ }
+ }
+ return this;
+ }
+
+ function prepareData(element) {
+ element = $(element);
+ if (!element.data("timeago")) {
+ element.data("timeago", { datetime: $t.datetime(element) });
+ var text = $.trim(element.text());
+ if ($t.settings.localeTitle) {
+ element.attr("title", element.data('timeago').datetime.toLocaleString());
+ } else if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
+ element.attr("title", text);
+ }
+ }
+ return element.data("timeago");
+ }
+
+ function inWords(date) {
+ return $t.inWords(distance(date));
+ }
+
+ function distance(date) {
+ return (new Date().getTime() - date.getTime());
+ }
+
+ // fix for IE6 suckage
+ document.createElement("abbr");
+ document.createElement("time");
+}));
diff --git a/task/templates/hosting-task-table.tpl.php b/task/templates/hosting-task-table.tpl.php
new file mode 100644
index 0000000..220e60b
--- /dev/null
+++ b/task/templates/hosting-task-table.tpl.php
@@ -0,0 +1,116 @@
+
\ No newline at end of file
diff --git a/task/templates/views-view-table--hosting-task-list--block.tpl.php b/task/templates/views-view-table--hosting-task-list--block.tpl.php
new file mode 100644
index 0000000..805435a
--- /dev/null
+++ b/task/templates/views-view-table--hosting-task-list--block.tpl.php
@@ -0,0 +1,62 @@
+
+
+