'admin/build/deploy', 'title' => t('Deployment'), 'callback' => 'deploy_overview', 'access' => user_access('administer deployment'), 'description' => t('Deployment'), ); $items[] = array( 'path' => 'admin/build/deploy/plans', 'title' => t('Plans'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'access' => user_access('administer deployment'), ); $items[] = array( 'path' => 'admin/build/deploy/servers', 'title' => t('Servers'), 'description' => t('Manage deployment servers'), 'callback' => 'deploy_server_overview', 'access' => user_access('administer deployment'), 'type' => MENU_LOCAL_TASK, 'weight' => 3 ); $items[] = array( 'path' => 'admin/build/deploy/logs', 'title' => t('Deployment Log'), 'description' => t('View logs of past deployments'), 'callback' => 'deploy_logs_overview', 'access' => user_access('administer deployment'), 'type' => MENU_LOCAL_TASK, 'weight' => 4 ); $items[] = array( 'path' => 'admin/build/deploy/logs/details', 'title' => t('Deployment Log Details'), 'description' => t('View detailed logs of a past deployment'), 'callback' => 'deploy_logs_details', 'access' => user_access('administer deployment'), 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => 'admin/build/deploy/add', 'title' => t('Add a deployment plan'), 'description' => t('Add a deployment plan.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('deploy_plan_form'), 'access' => user_access('administer deployment'), 'type' => MENU_CALLBACK, 'weight' => 2 ); $items[] = array( 'path' => 'admin/build/deploy/plan', 'title' => t('Edit plan'), 'callback' => 'drupal_get_form', 'callback arguments' => array('deploy_plan_form'), 'access' => user_access('administer deployment'), 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => 'admin/build/deploy/server/add', 'title' => t('Add server'), 'description' => t('Add a deployment server.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('deploy_server_form'), 'access' => user_access('administer deployment'), 'type' => MENU_CALLBACK, 'weight' => 2 ); $items[] = array( 'path' => 'admin/build/deploy/server', 'title' => t('Edit server'), 'description' => t('Edit a deployment server.'), 'callback' => 'drupal_get_form', 'callback arguments' => array('deploy_server_form'), 'access' => user_access('administer deployment'), 'type' => MENU_CALLBACK, ); $items[] = array( 'path' => 'admin/build/deploy/push', 'title' => t('Push a plan live'), 'callback' => 'deploy_push', 'type' => MENU_CALLBACK, 'access' => user_access('deploy items'), 'weight' => 1, ); $items[] = array( 'path' => 'admin/build/deploy/push/results', 'title' => t('Push results'), 'callback' => 'deploy_push_results', 'type' => MENU_CALLBACK, 'access' => user_access('deploy items'), 'weight' => 1, ); $items[] = array( 'path' => 'admin/build/deploy/list', 'title' => t('View deployment plan items'), 'callback' => 'drupal_get_form', 'callback arguments' => array('deploy_list'), 'type' => MENU_CALLBACK, 'access' => user_access('administer deployment'), 'weight' => 1, ); $items[] = array( 'path' => 'admin/build/deploy/delete/item', 'title' => t('Delete a deployment plan item'), 'callback' => 'drupal_get_form', 'callback arguments' => array('deploy_delete_item_form'), 'type' => MENU_CALLBACK, 'access' => user_access('administer deployment'), 'weight' => 1, ); $items[] = array( 'path' => 'admin/build/deploy/delete/plan', 'title' => t('Delete a deployment plan'), 'callback' => 'drupal_get_form', 'callback arguments' => array('deploy_delete_plan_form'), 'type' => MENU_CALLBACK, 'access' => user_access('administer deployment'), 'weight' => 1, ); $items[] = array( 'path' => 'admin/build/deploy/delete/server', 'title' => t('Delete a server'), 'callback' => 'drupal_get_form', 'callback arguments' => array('deploy_delete_server_form'), 'type' => MENU_CALLBACK, 'access' => user_access('administer deployment'), 'weight' => 1, ); return $items; } /** * deployment overview page menu callback */ function deploy_overview() { $result = db_query("select * from {deploy_plan}"); while ($plan = db_fetch_array($result)) { $items = deploy_get_plan_items($plan['pid']); $plan_entry = l($plan['name'], 'admin/build/deploy/list/'. $plan['pid']); if (empty($items)) { $plan_entry = check_plain($plan['name']); } $row = array( $plan_entry, check_plain($plan['description']), l(t('edit'), 'admin/build/deploy/plan/'. $plan['pid']), l(t('delete'), 'admin/build/deploy/delete/plan/'. $plan['pid']), l(t('push'), 'admin/build/deploy/push/'. $plan['pid']), ); $rows[] = $row; } if (empty($rows)) { $rows[] = array(array('data' => t('No deployment plans available.'), 'colspan' => '4', 'class' => 'message')); } $header = array( t('Name'), t('Description'), array( 'data' => t('Operations'), 'colspan' => 3, ) ); $output = theme('table', $header, $rows); $output .= l(t("Add a New Deployment Plan"), "/admin/build/deploy/add", array()); return $output; } /** * add/edit deployment plan form */ function deploy_plan_form($pid = NULL) { $plan = NULL; if (!is_null($pid)) { $plan = deploy_get_plan($pid); $form['pid'] = array( '#type' => 'hidden', '#default_value' => $pid, ); } $form['plan_name'] = array( '#title' => t('Name'), '#type' => 'textfield', '#size' => 30, '#maxlength' => 50, '#required' => TRUE, '#default_value' => $plan['name'], '#description' => t('A unique name for this deployment plan.'), ); $form['plan_description'] = array( '#title' => t('Description'), '#type' => 'textarea', '#default_value' => $plan['description'], '#description' => t('Information about this deployment plan, and the items being deployed.'), ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); return $form; } /** * add deployment plan form submit function */ function deploy_plan_form_submit($form_id, $form_values) { global $user; // If $form_values has 'pid', then this is an update. Otherwise it is an // insert. if (array_key_exists('pid', $form_values)) { db_query("update {deploy_plan} set name = '%s', description = '%s' where pid = %d", $form_values['plan_name'], $form_values['plan_description'], $form_values['pid']); drupal_set_message(t('Plan updated')); } else { $pid = db_next_id('{deploy_plan}_pid'); db_query("insert into {deploy_plan} (pid, name, description) values (%d, '%s', '%s')", $pid, $form_values['plan_name'], $form_values['plan_description']); drupal_set_message(t('Plan added')); } return 'admin/build/deploy'; } /** * deployment servers overview page menu callback */ function deploy_server_overview() { $header = array(t('Name'), t('URL'), array('data' => t('Operations'), 'colspan' => 2)); $result = db_query("select * from {deploy_servers}"); while ($row = db_fetch_array($result)) { $row = array(check_plain($row['description']), check_plain($row['url']), l(t('edit'), 'admin/build/deploy/server/'. $row['sid']), l(t('delete'), 'admin/build/deploy/delete/server/'. $row['sid'])); $rows[] = $row; } if (empty($rows)) { $rows[] = array(array('data' => t('No deployment servers available.'), 'colspan' => '4', 'class' => 'message')); } $output = theme('table', $header, $rows); $output .= l(t("Add a New Server"), "/admin/build/deploy/server/add", array()); return $output; } /** * add deployment server form */ function deploy_server_form($sid = NULL) { $server = NULL; if (!is_null($sid)) { $server = deploy_get_server($sid); $form['sid'] = array( '#type' => 'hidden', '#default_value' => $sid, ); } $form['description'] = array( '#title' => t('Name'), '#type' => 'textfield', '#size' => 50, '#maxlength' => 100, '#default_value' => $server['description'], '#description' => t('Description of this server.'), ); $form['url'] = array( '#title' => t('URL'), '#type' => 'textfield', '#size' => 50, '#maxlength' => 100, '#default_value' => $server['url'], '#description' => t('Domain name and path to xmlrpc service.'), ); $form['api_key'] = array( '#title' => t('API key'), '#type' => 'textfield', '#size' => 32, '#maxlength' => 32, '#default_value' => $server['api_key'], '#description' => t('API key (if needed)'), ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); return $form; } /** * add deployment server form validator */ function deploy_server_form_validate($form_id, $form_values) { if ($form_values['description'] == '') { form_set_error('description', t('Server description is required')); } if ($form_values['url'] == '') { form_set_error('url', t('URL is required')); } } /** * add deployment server form submit function */ function deploy_server_form_submit($form_id, $form_values) { $url = $form_values['url']; $description = $form_values['description']; $api_key = $form_values['api_key']; if (array_key_exists('sid', $form_values)) { $sid = $form_values['sid']; db_query("update {deploy_servers} set description = '%s', url = '%s', api_key = '%s' where sid = %d", $description, $url, $api_key, $sid); drupal_set_message(t('Deployment server updated')); } else { $sid = db_next_id('{deploy_servers}_sid'); db_query("insert into {deploy_servers} (sid, description, url, api_key) values (%d, '%s', '%s', '%s')", $sid, $description, $url, $api_key); drupal_set_message(t('Deployment server added')); } return 'admin/build/deploy/servers'; } /** * list deployment plan items form */ function deploy_list($pid = NULL) { $items = deploy_get_plan_items($pid); if (is_null($pid) || empty($items)) { drupal_goto('/admin/build/deploy'); } // Build form tree $form['#tree'] = TRUE; foreach ($items as $i => $item) { $account = user_load(array('uid' => $item['uid'])); $added_by = $account->name ." on ". format_date($item['ts'], 'small'); $form[$i]['module'] = array('#type' => 'item', '#value' => $item['module']); $form[$i]['description'] = array('#type' => item, '#value' => $item['description']); $form[$i]['added_by'] = array('#type' => item, '#value' => $added_by); $form[$i]['weight'] = array('#type' => 'weight', '#default_value' => $item['weight']); $form[$i]['iid'] = array('#type' => 'hidden', '#value' => $item['iid']); } $form['pid'] = array('#type' => 'hidden', '#value' => $pid); $form['submit'] = array('#type' => 'submit', '#value' => t('Save plan')); return $form; } /** * theme the deploy plan items form * some drag-and-dropification would be nice here */ function theme_deploy_list($form) { $header = array(t('Module'), t('Description'), t('Added by'), t('Weight'), t('Operations')); $rows = array(); foreach (element_children($form) as $i) { $item = &$form[$i]; if (is_array($item['module'])) { $rows[] = array(drupal_render($item['module']), drupal_render($item['description']), drupal_render($item['added_by']), drupal_render($item['weight']), l(t('Delete'), 'admin/build/deploy/delete/item/'. $item['iid']['#value']), ); } } $output = theme('table', $header, $rows, array('id' => 'plans')); $output .= drupal_render($form); return $output; } /** * deploy plan items form submit (just updates the weightings) */ function deploy_list_submit($form_id, $form_values) { foreach ($form_values as $i => $item) { if (is_array($item)) { db_query("update {deploy_plan_items} set weight = %d where iid = %d", $item['weight'], $item['iid']); } } drupal_set_message(t('Deployment plan saved.')); return 'admin/build/deploy/'; } /** * return an assoc array of deployment plans of format pid => name */ function deploy_get_plans() { $result = db_query("select * from {deploy_plan} order by name"); $plans = array(); while ($plan = db_fetch_array($result)) { $plans[$plan['pid']] = $plan['name']; } return $plans; } /** * delete a deployment plan confirmation form */ function deploy_delete_plan_form($pid = NULL) { $plan = deploy_get_plan($pid); if (!$plan) { drupal_goto('admin/build/deploy'); } $form['pid'] = array('#type' => 'value', '#value' => $pid); $form = confirm_form( $form, t('Are you sure you want to delete the deployment plan %plan_name?', array('%plan_name' => $plan['name'])), 'admin/build/deploy', t('This action cannot be undone.'), t('Delete'), t('Cancel') ); return $form; } /** * delete a deployment plan submit handler. */ function deploy_delete_plan_form_submit($form_id, $form_values) { db_query("delete from {deploy_plan_items} where pid = %d", $form_values['pid']); db_query("delete from {deploy_plan} where pid = %d", $form_values['pid']); drupal_set_message(t('Plan Deleted')); drupal_goto("/admin/build/deploy/"); } /** * delete a deployment plan confirmation form */ function deploy_delete_server_form($sid = NULL) { $server = deploy_get_server($sid); if (!$server) { drupal_goto('admin/build/deploy/servers'); } $form['sid'] = array('#type' => 'value', '#value' => $sid); $form = confirm_form( $form, t('Are you sure you want to delete the server %server_name?', array('%server_name' => $server['description'])), 'admin/build/deploy/servers', t('This action cannot be undone.'), t('Delete'), t('Cancel') ); return $form; } /** * delete a deployment server form submit handler. */ function deploy_delete_server_form_submit($form_id, $form_values) { db_query("delete from {deploy_servers} where sid = %d", $form_values['sid']); drupal_set_message(t('Server Deleted')); drupal_goto("/admin/build/deploy/servers"); } /** * delete an item from a deployment plan confirmation form. */ function deploy_delete_item_form($iid = NULL) { $item = deploy_get_plan_item($iid); if (!$item) { drupal_goto('admin/build/deploy'); } $form['iid'] = array('#type' => 'value', '#value' => $iid); $form['pid'] = array('#type' => 'value', '#value' => $item['pid']); $form = confirm_form( $form, t('Are you sure you want to delete the deployment plan item %item_name?', array('%item_name' => $item['description'])), 'admin/build/deploy/list/'. $item['pid'], t('This action cannot be undone.'), t('Delete'), t('Cancel') ); return $form; } /** * delete an item from a deployment plan form submit handler. */ function deploy_delete_item_form_submit($form_id, $form_values) { db_query("delete from {deploy_plan_items} where iid = %d", $form_values['iid']); drupal_set_message(t('Item Deleted')); drupal_goto("/admin/build/deploy/list/". $form_values['pid']); } /** * hook_xmlrpc deploy.install callback */ function deploy_install_items($module, $form_id, $data) { watchdog('install', $module); if (module_exists($module)) { $data = unserialize($data); drupal_execute($form_id, $data); } else { return "Module not enabled: $module"; } } /** * add data to a deployment plan */ function deploy_add_to_plan($pid, $module, $description, $data) { global $user; $iid = db_next_id('{deploy_plan_items}_iid'); $result = db_query("insert into {deploy_plan_items} (iid, pid, uid, ts, module, description, data) values (%d, %d, %d, %d, '%s', '%s', '%s')", $iid, $pid, $user->uid, time(), $module, $description, $data); if (!$result) { drupal_set_message(t("An error occured.")); } else { drupal_set_message("$description successfully added to plan"); } } /** * push a deployment plan live */ function deploy_push($pid) { if (is_null($pid)) { drupal_goto('/admin/build/deploy'); } $output = t('Choose a deployment server'); $output .= drupal_get_form('deploy_pushform', $pid); return $output; } function deploy_pushform($pid) { $servers = _deploy_get_servers(); if (empty($servers)) { drupal_set_message(t("There are no servers defined. Please define a server using the Servers tab before pushing your deployment plan.")); drupal_goto("admin/build/deploy"); } $form['pid'] = array( '#type' => 'hidden', '#value' => $pid, ); $form['sid'] = array( '#title' => t('Server'), '#type' => 'select', '#options' => $servers, '#description' => t('The server you want to deploy to'), ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); return $form; } /** * submit handler for deployment push form */ function deploy_pushform_submit($form_id, $form_values) { global $user; $pid = $form_values['pid']; $plan = deploy_get_plan($pid); $sid = $form_values['sid']; $server = deploy_get_server($sid); $url = $server['url']; $api_key = $server['api_key']; $deploy_fatal = FALSE; // rather than save foreign keys out to the server/plan/user in the log, // I'm saving actual identifying data. This keeps the log pure in case // associated data gets deleted. $dlid = db_next_id('{deploy_log}_dlid'); db_query("insert into {deploy_log} (dlid, plan, server, username, ts) values (%d, '%s', '%s', '%s', %d)", $dlid, $plan['name'], $server['description'], $user->name, time()); $items = db_query("select * from {deploy_plan_items} where pid = %d order by weight", $pid); while ($item = db_fetch_array($items)) { if (!$deploy_fatal) { $xmlrpc_result = call_user_func($item['module'] .'_deploy', $url, $api_key, $item['data']); if ($xmlrpc_result === FALSE) { $deploy_fatal = TRUE; $result = t('Error'); $message = xmlrpc_error_msg(); } else { $result = t('Success'); $message = ''; } } else { $result = t('Not Sent'); $message = t('Item not sent due to prior fatal error.'); } $dldid = db_next_id('{deploy_log_details}_dldid'); db_query("insert into {deploy_log_details} (dldid, dlid, module, description, result, message) values (%d, %d, '%s', '%s', '%s', '%s')", $dldid, $dlid, $item['module'], $item['description'], $result, $message); } return "/admin/build/deploy/logs/details/$dlid"; } /** * return an assoc array of servers of format sid => description */ function _deploy_get_servers() { $result = db_query("select * from {deploy_servers} order by description"); $servers = array(); while ($server = db_fetch_array($result)) { $servers[$server['sid']] = $server['description']; } return $servers; } /** * update a single item in a deployment plan with new data */ function deploy_update_item($iid, $data) { db_query("update {deploy_plan_items} set data = '%s' where iid = %d", $data, $iid); } /** * retrieve an array of a deployment plan */ function deploy_get_plan($pid) { $result = db_query("select * from {deploy_plan} where pid = %d", $pid); return db_fetch_array($result); } /** * retrieve an array of a deployment plan server */ function deploy_get_server($sid) { $result = db_query("select * from {deploy_servers} where sid = %d", $sid); return db_fetch_array($result); } /** * retrieve a deployment plan item as an array. */ function deploy_get_plan_item($iid) { $result = db_query("select * from {deploy_plan_items} where iid = %d", $iid); return db_fetch_array($result); } /** * retrieve all the plan items for a specified plan * takes an optional module name, to return just the items for * that module */ function deploy_get_plan_items($pid, $module = NULL) { $items = array(); $sql = "select * from {deploy_plan_items} where pid = %d"; if (!is_null($module)) { $sql .= " and module = '%s'"; } $sql .= " order by weight"; $result = db_query($sql, $pid, $module); while ($item = db_fetch_array($result)) { $items[] = $item; } return $items; } /** * deployment logs overview page menu callback */ function deploy_logs_overview() { $result = pager_query("select * from {deploy_log} order by ts desc", 20); while ($log = db_fetch_array($result)) { $row = array( l($log['plan'], 'admin/build/deploy/logs/details/'. $log['dlid']), $log['username'], format_date($log['ts'], 'small'), $log['server'], ); $rows[] = $row; } if (empty($rows)) { $rows[] = array(array('data' => t('No deployment plans have been pushed.'), 'colspan' => '4', 'class' => 'message')); } $header = array( t('Plan'), t('Pushed By'), t('When'), t('To Server') ); $output = theme('table', $header, $rows); return $output; } /** * view the results of a push */ function deploy_logs_details($dlid) { $rows = array(); $styles = array( 'Error' => 'background-color: #fcc', 'Not Sent' => 'background-color: #ffd', 'Success' => '', ); $result = db_query("select * from {deploy_log_details} where dlid = %d", $dlid); while ($log_detail = db_fetch_array($result)) { $row = array('data' => array( $log_detail['module'], $log_detail['description'], $log_detail['result'], $log_detail['message'], ), 'style' => $styles[$log_detail['result']], ); $rows[] = $row; } $header = array( t('Module'), t('Description'), t('Result'), t('Message') ); return theme('table', $header, $rows); }