cvs diff: Diffing modules/project Index: modules/project/CHANGELOG.txt =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/project/CHANGELOG.txt,v retrieving revision 1.5.2.1 diff -u -F^f -r1.5.2.1 CHANGELOG.txt --- modules/project/CHANGELOG.txt 25 Jul 2006 00:12:26 -0000 1.5.2.1 +++ modules/project/CHANGELOG.txt 13 Sep 2006 09:03:40 -0000 @@ -1,3 +1,8 @@ +XX. September 2006 +----------------- +- Split out releases into project_release.module and made them real nodes + (dww and merlinofchaos) + 21. July 2006 ----------------- - Split out issue tracking to project_issue.module (dww) Index: modules/project/project.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/project/project.inc,v retrieving revision 1.75.2.16 diff -u -F^f -r1.75.2.16 project.inc --- modules/project/project.inc 26 Jul 2006 07:18:01 -0000 1.75.2.16 +++ modules/project/project.inc 13 Sep 2006 09:03:41 -0000 @@ -173,7 +173,7 @@ function project_project_form(&$node) { '#description' => t('Link to a live demo.'), ); - if ($releases = project_release_load($node)) { + if ($releases = project_release_get_releases($node, false)) { $form['project']['version'] = array( '#type' => 'select', '#title' => t('Default version'), @@ -303,31 +303,34 @@ function project_project_view(&$node, $t $breadcrumb = project_project_set_location($node, $breadcrumb); menu_set_location($breadcrumb); - if ($releases = project_release_load($node)) { + $releases = project_release_get_releases($node); + if ($releases) { // Determine most suitable release $overview = variable_get('project_release_overview', -1); - foreach ($releases as $rid => $version) { + foreach ($releases as $rid => $release) { // If the project has a preferred release, choose that. + $version = $release->version; if ($rid == $node->version) { - $release = $rid; + $latest_release = $release; break; } // If the version matches the default on the overview, prefer the first. if (!isset($release) && strpos($version, $overview) === 0) { - $release = $rid; + $latest_release = $release; } } // Fallback to the latest - if (!isset($release)) { + if (!isset($latest_release)) { reset($releases); - list($release) = each($releases); + $latest_release = current($releases); } - $release = project_release_load($release); + $release = $latest_release; + $version = $release->version; // Download section $links = array(); - $links[] = '' . t('Download latest release (%version, %date, %size)', array('%version' => $release->version, '%date' => format_date($release->changed, 'small'), '%size' => format_size(filesize($release->path)))) . ''; + $links[] = '' . t('Download latest release (%version, %date, %size)', array('%version' => $version, '%date' => format_date($release->changed, 'small'), '%size' => format_size(filesize($release->file_path)))) . ''; if ($release->changes) { $links[] = l(t('Read release notes'), 'node/'. $node->nid .'/release', null, null, 'version-'. $release->version); @@ -337,7 +340,7 @@ function project_project_view(&$node, $t } } if (node_access('update', $node) && !variable_get('project_release_directory', '')) { - $links[] = l(t('Add new release'), 'node/'. $node->nid .'/release/add'); + $links[] = l(t('Add new release'), 'node/add/project_release/'. $node->nid); } if ($links) { $output .= theme('item_list', $links, t('Releases')); @@ -402,22 +405,6 @@ function project_project_view(&$node, $t } } -function project_project_releases() { - $node = node_load(arg(1)); - - // Breadcrumb navigation - $breadcrumb[] = array('path' => 'node/'. $node->nid .'/', 'title' => $node->title); - $breadcrumb[] = array('path' => 'node/'. arg(1) .'/release', 'title' => t('Releases')); - $breadcrumb = project_project_set_location($node, $breadcrumb); - - menu_set_location($breadcrumb); - - $output = project_release_list($node); - - drupal_set_title(check_plain($node->title)); - return $output; -} - function project_project_load($node) { $project = db_fetch_object(db_query('SELECT * FROM {project_projects} WHERE nid = %d', $node->nid)); @@ -449,17 +436,16 @@ function project_project_nodeapi(&$node, function project_project_insert($node) { db_query("INSERT INTO {project_projects} (nid, uri, homepage, changelog, cvs, demo, release_directory, mail, version, screenshots, documentation, license) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s')", $node->nid, $node->uri, $node->homepage, $node->changelog, $node->cvs, $node->demo, $node->release_directory, $node->mail, $node->version, $node->screenshots, $node->documentation, $node->license); - project_release_scan_directory($node->uri); +// project_release_scan_directory($node->uri); } function project_project_update($node) { db_query("UPDATE {project_projects} SET uri = '%s', homepage = '%s', changelog = '%s', cvs = '%s', demo = '%s', release_directory = '%s', mail = '%s', version = %d, screenshots = '%s', documentation = '%s', license = '%s' WHERE nid = %d", $node->uri, $node->homepage, $node->changelog, $node->cvs, $node->demo, $node->release_directory, $node->mail, $node->version, $node->screenshots, $node->documentation, $node->license, $node->nid); - project_release_scan_directory($node->uri); +// project_release_scan_directory($node->uri); } function project_project_delete($node) { db_query('DELETE FROM {project_projects} WHERE nid = %d', $node->nid); - db_query('DELETE FROM {project_releases} WHERE nid = %d', $node->nid); } function project_project_access($op, $node) { Index: modules/project/project.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/project/project.install,v retrieving revision 1.2.2.6 diff -u -F^f -r1.2.2.6 project.install --- modules/project/project.install 10 Sep 2006 06:37:17 -0000 1.2.2.6 +++ modules/project/project.install 13 Sep 2006 09:03:41 -0000 @@ -22,25 +22,7 @@ function project_install() { KEY project_projects_uri (uri(8)) ) TYPE=MyISAM /*!40100 DEFAULT CHARACTER SET utf8 */;"); - db_query("CREATE TABLE {project_releases} ( - rid int(10) unsigned NOT NULL default '0', - nid int(10) unsigned NOT NULL default '0', - fid int(10) unsigned NOT NULL default '0', - path varchar(255) NOT NULL default '', - created int(10) unsigned NOT NULL default '0', - version varchar(255) NOT NULL default '', - status tinyint(1) unsigned NOT NULL default '1', - changes text, - weight tinyint(3) unsigned NOT NULL default '0', - changed int(10) unsigned NOT NULL default '0', - hash varchar(32) NOT NULL default '', - UNIQUE(path), - PRIMARY KEY (`rid`), - KEY project_releases_nid (nid) - ) TYPE=MyISAM - /*!40100 DEFAULT CHARACTER SET utf8 */;"); - break; - + case 'pgsql': db_query("CREATE TABLE {project_projects} ( nid int NOT NULL default '0', @@ -57,20 +39,6 @@ function project_install() { license varchar(255) default '' not null, PRIMARY KEY (nid) );"); - db_query("CREATE TABLE {project_releases} ( - rid int NOT NULL default '0', - nid int NOT NULL default '0', - fid int NOT NULL default '0', - path varchar(255) NOT NULL default '', - created int NOT NULL default '0', - hash varchar(32) NOT NULL default '', - version varchar(255) NOT NULL default '', - changes text, - weight smallint NOT NULL default '0', - changed int NOT NULL default '0', - status smallint default '1' not null, - PRIMARY KEY (rid) - );"); break; } Index: modules/project/project.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/project/project.module,v retrieving revision 1.239.2.13 diff -u -F^f -r1.239.2.13 project.module --- modules/project/project.module 7 Sep 2006 19:25:39 -0000 1.239.2.13 +++ modules/project/project.module 13 Sep 2006 09:03:42 -0000 @@ -1,21 +1,17 @@ project_projects -// project releases -> project_releases - $path = drupal_get_path('module', 'project'); if (file_exists("$path/project.inc")) { require_once "$path/project.inc"; - require_once "$path/release.inc"; } function project_help($section) { switch ($section) { case 'admin/modules#description': - return t('Provides a project node type and manages downloads of project releases.'); + return t('Provides a project node type and browsing of projects.'); case 'node/add#project_project': - return t('A project is anything that has releases which can be downloaded'); + return t('A project is something a group is working on. It can optionally have issue tracking, integration with revision control systems, releases, and so on.' ); case 'node/add/project_project': if (project_use_taxonomy()) { $tree = taxonomy_get_tree(_project_get_vid()); @@ -89,69 +85,27 @@ function project_perm() { * Callback for the main settings page. */ function project_settings() { - $versions = array(-1 => t('all')) + project_releases_list(); $sort_methods = drupal_map_assoc(array_keys(module_invoke_all('project_sort_methods', 'methods'))); - $form['releases'] = array('#type' => 'fieldset', - '#title' => t('Releases'), - '#collapsible' => TRUE, - ); - $form['releases']['project_release_directory'] = array( - '#type' => 'textfield', - '#title' => t('Release directory'), - '#default_value' => variable_get('project_release_directory', ''), - '#size' => 50, - '#maxlength' => 255, - '#description' => t('Leave this blank if project maintainers are to create their own release packages. This is useful if releases are generated by an external tool.'), - ); - - $form['releases']['project_release_unmoderate'] = array( - '#type' => 'radios', - '#title' => t('Unmoderate projects with releases'), - '#default_value' => variable_get('project_release_unmoderate', 0), - '#options' => array('Disabled', 'Enabled'), - ); - - $form['releases']['project_browse_releases'] = array( - '#type' => 'checkbox', - '#title' => t('Browse projects by releases'), - '#default_value' => variable_get('project_browse_releases', 0), - '#description' => t('Checking this box will cause the project browsing page to have a version select.'), - ); - - $form['releases']['project_release_overview'] = array( - '#type' => 'radios', - '#title' => t('Default release overview'), - '#default_value' => variable_get('project_release_overview', -1), - '#options' => $versions, - '#description' => t('Default release version to list on the overview page'), - ); - - $form['browsing'] = array( - '#type' => 'fieldset', - '#title' => t('Browsing options'), - '#collapsible' => TRUE, - ); - if (project_use_taxonomy()) { // For now, date-based browsing doesn't work once you disable // taxonomy (only because the code involved is rather complicated // and needs to be majorly refactored and cleaned up). - $form['browsing']['project_sort_method'] = array( + $form['project_sort_method'] = array( '#type' => 'radios', '#title' => t('Default sort option'), '#default_value' => variable_get('project_sort_method', 'category'), '#options' => $sort_methods, '#description' => t('Default sorting option to use on the overview page'), ); - $form['browsing']['sort_methods'] = array( + $form['sort_methods'] = array( '#type' => 'fieldset', '#title' => t('Enabled sorting options'), ); $tree = taxonomy_get_tree(_project_get_vid(), 0 , -1, 1); foreach ($tree as $term) { - $form['browsing']['sort_methods']['project_sort_method_used_' . $term->tid] = array( + $form['sort_methods']['project_sort_method_used_' . $term->tid] = array( '#type' => 'checkboxes', '#title' => $term->name, '#default_value' => variable_get('project_sort_method_used_' . $term->tid, array_keys($sort_methods)), @@ -160,12 +114,18 @@ function project_settings() { ); } } - $form['browsing']['project_browse_nodes'] = array( + $form['project_browse_nodes'] = array( '#type' => 'select', '#title' => t('Number of projects to list in paged browsing'), '#default_value' => variable_get('project_browse_nodes', 30), '#options' => drupal_map_assoc(array(5, 10, 15, 20, 25, 30, 35, 40, 45, 50)), '#description' => t('The default maximum number of projects to list when browsing lists, e.g., by category.') ); + $form['project_browse_releases'] = array( + '#type' => 'checkbox', + '#title' => t('Browse projects by releases'), + '#default_value' => variable_get('project_browse_releases', 0), + '#description' => t('Checking this box will cause the project browsing page to have a version select.'), + ); if (module_exist('forum') && project_use_taxonomy()) { $form['project_support_forum'] = taxonomy_form(_forum_get_vid(), variable_get('project_support_forum', ''), t('Select the support forum for projects'), 'project_support_forum'); @@ -176,13 +136,6 @@ function project_settings() { return $form; } -function project_cron() { - if (variable_get('project_release_directory', '') && time() - variable_get('project_release_directory_last', 0) > variable_get('project_release_directory_interval', 6 * 60 * 60)) { - variable_set('project_release_directory_last', time()); - project_release_cron(); - } -} - function project_link($type, $node = 0, $main = 0) { $links = array(); switch ($type) { @@ -319,32 +272,9 @@ function project_menu($may_cache) { $access = project_project_access('create', NULL); $items[] = array('path' => 'node/add/project_project', 'title' => t('project'), 'callback' => 'node_page', 'access' => $access, 'type' => MENU_NORMAL_ITEM); - // Releases - if (variable_get('project_release_directory', '')) { - $access = user_access('administer projects'); - $items[] = array('path' => 'admin/settings/project/reload', 'title' => t('scan for releases'), 'callback' => 'project_release_scan', 'access' => $access, 'type' => MENU_NORMAL_ITEM); - } } else { theme_add_style(drupal_get_path('module', 'project') .'/project.css'); - if (arg(0) == 'node' && is_numeric(arg(1))) { - $node = node_load(arg(1)); - if ($node->type == 'project_project') { - $items[] = array('path' => 'node/'. arg(1) .'/release', 'title' => t('releases'), 'callback' => 'project_project_releases', 'access' => node_access('view', $node), 'weight' => 0, 'type' => MENU_CALLBACK); - - if (node_access('update', $node)) { - $items[] = array('path' => 'node/'. arg(1) .'/release/overview', 'title' => t('overview'), 'callback' => 'project_project_releases', 'access' => node_access('view', $node), 'weight' => 0, 'type' => MENU_CALLBACK); - } - - if (!variable_get('project_release_directory', '')) { - $items[] = array('path' => 'node/'. arg(1) .'/release/add', 'title' => t('add'), 'callback' => 'project_release_submit', 'access' => node_access('update', $node), 'weight' => 1, 'type' => MENU_CALLBACK); - } - if (arg(2) == 'release' && is_numeric(arg(4))) { - $items[] = array('path' => 'node/'. arg(1) .'/release/edit/'. arg(4), 'title' => t('edit'), 'callback' => 'project_release_submit', 'access' => node_access('update', $node), 'weight' => 1, 'type' => MENU_CALLBACK); - $items[] = array('path' => 'node/'. arg(1) .'/release/delete/'. arg(4), 'title' => t('edit'), 'callback' => 'project_release_submit', 'access' => node_access('update', $node), 'weight' => 1, 'type' => MENU_CALLBACK); - } - } - } } return $items; @@ -386,9 +316,6 @@ function project_form_alter($form_id, &$ /** * hook_nodeapi() implementation. This just decides what type of node * is being passed, and calls the appropriate type-specific hook. - * NOTE: there's currently only 1 value left here, but I'm planning to - * make project_release its own node-type, so we'll probably want to - * leave this plumbing in place for that... -dww * * @see project_project_nodeapi(). */ @@ -477,7 +404,8 @@ function project_page_overview($termname $tids = array(); if (variable_get('project_browse_releases', 0) && $version != -1) { // Find all terms associated with the requested version. - $result = db_query("SELECT t.tid, COUNT(DISTINCT(n.nid)) AS count FROM {term_node} t INNER JOIN {project_releases} p ON t.nid = p.nid INNER JOIN {node} n ON n.nid = t.nid WHERE (p.version LIKE '%s%%') AND (p.path <> '') AND (n.status = 1) GROUP BY t.tid", $version); + // TODO: should be conditional on project_release.module + $result = db_query("SELECT t.tid, COUNT(DISTINCT(n.nid)) AS count FROM {term_node} t INNER JOIN {project_release_nodes} p ON t.nid = p.nid INNER JOIN {node} n ON n.nid = t.nid WHERE (p.version LIKE '%s%%') AND (p.file_path <> '') AND (n.status = 1) GROUP BY t.tid", $version); $tids = array(); while ($item = db_fetch_object($result)) { $tids[$item->tid] = $item->count; @@ -575,8 +503,8 @@ function project_page_overview($termname if (variable_get('project_browse_releases', 0)) { $release_settings = array( 'fields' => array('pr.path', 'pr.version', 'MAX(pr.changed) AS changed', 'COUNT(*) AS release_count'), - 'joins' => array('INNER JOIN {project_releases} pr ON n.nid = pr.nid'), - 'wheres' => array('pr.status = 1', "pr.path <> ''"), + 'joins' => array('INNER JOIN {project_release_nodes} pr ON n.nid = pr.nid'), + 'wheres' => array("pr.path <> ''"), 'group_bys' => array('n.nid'), 'parameters' => array($version) ); @@ -660,15 +588,15 @@ function project_page_overview($termname // Make sure we have the latest release if ($project->release_count > 1) { - $latest = db_fetch_object(db_query_range("SELECT path, version FROM {project_releases} WHERE nid = %d AND version LIKE '%s%%' ORDER BY version DESC", $project->nid, $version, 0, 1)); - $project->path = $latest->path; + $latest = db_fetch_object(db_query_range("SELECT file_path, version FROM {project_release_nodes} WHERE nid = %d AND version LIKE '%s%%' ORDER BY version DESC", $project->nid, $version, 0, 1)); + $project->file_path = $latest->file_path; $project->version = $latest->version; } $project->links = array(); - if ($project->path) { - $project->links[] = '' . t('Download') . ''; + if ($project->file_path) { + $project->links[] = l(t('Download'), file_create_url($project->file_path)); } $project->links[] = l(t('Find out more'), "node/$project->nid"); @@ -733,9 +661,10 @@ function project_project_sort_methods($o 'order_bys' => array('n.sticky DESC', 'n.title ASC') ); case 'date': + // TODO: this should all be conditional on project_release.module return array( 'fields' => array('MAX(pr.changed) AS changed'), - 'joins' => array('INNER JOIN {project_releases} pr ON n.nid = pr.nid'), + 'joins' => array('INNER JOIN {project_release_nodes} pr ON n.nid = pr.nid'), 'group_bys' => array('n.nid'), 'order_bys' => array('pr.changed DESC', 'n.sticky DESC', 'n.title ASC') ); @@ -779,6 +708,7 @@ function project_project_sort_methods($o return; } +// TODO: refactor for project_release.module? Need {project_release_nodes} function project_default_tid() { static $tid; if ($tid === NULL) { Index: modules/project/project_release.install =================================================================== RCS file: modules/project/project_release.install diff -N modules/project/project_release.install --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/project/project_release.install 13 Sep 2006 09:03:42 -0000 @@ -0,0 +1,83 @@ + array('name' => t('project release'), 'base' => 'project_release'), + ); +} + +/** + * Implementation of hook_menu() + */ +function project_release_menu($may_cache) { + $items = array(); + global $user; + + if (!$may_cache) { + if (arg(0) == 'node' && is_numeric(arg(1))) { + $node = node_load(arg(1)); + if ($node->type == 'project_project') { + $items[] = array('path' => 'node/'. arg(1) .'/release', 'title' => t('releases'), 'callback' => 'project_release_project_releases', 'access' => node_access('view', $node), 'weight' => 0, 'type' => MENU_CALLBACK); + if (node_access('update', $node)) { + $items[] = array('path' => 'node/'. arg(1) .'/release/overview', 'title' => t('overview'), 'callback' => 'project_release_project_releases', 'access' => node_access('view', $node), 'weight' => 0, 'type' => MENU_CALLBACK); + } + } + } + } + return $items; +} + +/** + * Implementation of hook_access(). + * + * TODO: this needs more testing and review. Maybe we should add new + * permissions for accessing release nodes, but for now, we're just + * using the existing project perms. + */ +function project_release_access($op, $node) { + global $user; + switch ($op) { + case 'view': + return user_access('access projects') || (user_access('access own projects') && $node->uid == $user->uid); + case 'create': + if (user_access('maintain projects') || user_access('administer projects')) { + return TRUE; + } + return FALSE; + case 'update': + case 'delete': + return $user->uid && ($user->uid == $node->uid || user_access('administer projects')); + } +} + +/** + * Callback for the main settings page. + */ +function project_release_settings() { + $versions = array(-1 => t('all')) + project_releases_list(); + // TODO: do we even need the release directory setting anymore? + $form['project_release_directory'] = array( + '#type' => 'textfield', + '#title' => t('Release directory'), + '#default_value' => variable_get('project_release_directory', ''), + '#size' => 50, + '#maxlength' => 255, + '#description' => t('Leave this blank if project maintainers are to create their own release packages. This is useful if releases are generated by an external tool.'), + ); + + $form['project_release_default_version_format'] = array( + '#type' => 'textfield', + '#title' => t('Default version format string'), + '#default_value' => variable_get('project_release_default_version_format', '%major.%minor.%patch%extra'), + '#size' => 50, + '#maxlength' => 255, + '#description' => t('Customize the default format of the version strings for releases of projects on this site. Users with "administer projects" permissions can override this setting for each project.') .' '. t('Available variables are:') .' %super_major, %super_minor, %major, %minor, %patch, %extra.', + ); + + // TODO: isn't this a dead setting, too? + $form['project_release_unmoderate'] = array( + '#type' => 'radios', + '#title' => t('Unmoderate projects with releases'), + '#default_value' => variable_get('project_release_unmoderate', 0), + '#options' => array('Disabled', 'Enabled'), + ); + + $form['project_release_overview'] = array( + '#type' => 'radios', + '#title' => t('Default release overview'), + '#default_value' => variable_get('project_release_overview', -1), + '#options' => $versions, + '#description' => t('Default release version to list on the overview page'), + ); + + return $form; +} + +/** + * Implementation of hook_form(). + */ +function project_release_form(&$release, &$param) { + if (arg(1) == 'add') { + if (!is_numeric(arg(3))) { + $form['markup'] = array( + '#value' => t('You must add releases only from projects.') + ); + return $form; + } + $release->pid = arg(3); + $project = node_load($release->pid); + if (!$project) { + return drupal_not_found(); + } + $form['project'] = array( + '#type' => 'value', + '#value' => $project, + ); + } + else { + $edit = true; + } + $form['#attributes'] = array("enctype" => "multipart/form-data"); + + $form['title'] = array( + '#type' => 'value', + '#default_value' => $release->title, + ); + $form['version'] = array( + '#type' => 'value', + '#default_value' => $release->version, + ); + + // TODO: only provide UI elements for version fields that are + // specified in the active format string for this project... + + $form['version_major'] = array( + '#type' => 'textfield', + '#title' => t('Major version number'), + '#default_value' => $release->version_major, + '#size' => 20, + '#maxlength' => 10, + '#required' => TRUE, + ); + if ($edit) { + $form['version_major']['#attributes'] = array('disabled' => $edit); + } + + $form['version_minor'] = array( + '#type' => 'textfield', + '#title' => t('Minor version number'), + '#default_value' => $release->version_minor, + '#size' => 20, + '#maxlength' => 10, + '#required' => TRUE, + ); + if ($edit) { + $form['version_minor']['#attributes'] = array('disabled' => $edit); + } + + // TODO: it should be configurable which projects allow this field. + // Either as a global setting (e.g. a regexp that matches the + // project somehow) or as a setting in a {project_release_projects} + // table (which requires 'administer project' perms to see the UI + // that can alter it per-project)... + $form['version_patch'] = array( + '#type' => 'textfield', + '#title' => t('Patch-level version number'), + '#default_value' => $release->version_patch, + '#size' => 20, + '#maxlength' => 10, + ); + if ($edit) { + $form['version_patch']['#attributes'] = array('disabled' => $edit); + } + + $form['version_extra'] = array( + '#type' => 'textfield', + '#title' => t('Extra version identifier'), + '#default_value' => $release->version_extra, + '#size' => 20, + '#maxlength' => 10, + '#description' => t('Optionally specify other identifying information for this version, for example "rc1" or "dev". In most cases, this should be left blank.'), + ); + if ($edit) { + $form['version_extra']['#attributes'] = array('disabled' => $edit); + } + + if (!variable_get('project_release_directory', '') && empty($release->file_path)) { + $form['file'] = array( + '#type' => 'file', + '#title' => t('File'), + '#size' => 40, + '#description' => ($release->file_path) ? + t('A file already exists, if you upload another file the current file (%file) will be replaced.', array('%file' => $release->file_path)) : + t('Choose the file that will be associated with this release.'), + ); + } + + $form['body'] = array( + '#type' => 'textarea', + '#title' => t('Changes'), + '#default_value' => $release->body, + '#rows' => 10, + '#cols' => 40, + '#required' => TRUE, + '#description' => t('Enter a description of this version, such as a list of the major changes or updates.'), + ); + $form['format'] = filter_form($node->format); + + $form['pid'] = array( + '#type' => 'value', + '#value' => $release->pid, + ); + $form['tag'] = array( + '#type' => 'value', + '#value' => $release->tag, + ); + $form['rebuild'] = array( + '#type' => 'value', + '#value' => $release->rebuild, + ); + $form['file_hash'] = array( + '#type' => 'value', + '#value' => $release->file_hash, + ); + $form['file_date'] = array( + '#type' => 'value', + '#value' => $release->file_date, + ); + $form['file_path'] = array( + '#type' => 'value', + '#value' => $release->file_path, + ); + $form['version_super_major'] = array( + '#type' => 'value', + '#value' => $release->version_super_major, + ); + $form['version_super_minor'] = array( + '#type' => 'value', + '#value' => $release->version_super_minor, + ); + return $form; +} + +/** + * Implementation of hook_validate(). + */ +function project_release_validate(&$edit, $form) { + $result = db_query("SELECT * FROM {project_release_nodes} WHERE pid = %d AND version_major = %d AND version_minor = %d AND version_patch = %d AND version_extra = '%s' AND version_super_major = %d AND version_super_minor = %d AND nid != %d", $edit->pid, $edit->version_major, $edit->version_minor, $edit->version_patch, $edit->version_extra, $edit->version_super_major, $edit->super_version_minor, $edit->nid); + + if ($release = db_fetch_object($result)) { + form_set_error('version_minor', t('This version already exists for this project.')); + } + + if ($file = file_check_upload('file')) { + $file = file_save_upload('file', file_directory_path()); + if ($file) { + $filepath = file_create_path($file->filepath); + form_set_value($form['file_path'], $file->filepath); + form_set_value($form['file_date'], filemtime($filepath)); + form_set_value($form['file_hash'], md5_file($filepath)); + } + else { + form_set_error('file', t('There was a problem uploading the specified file.')); + } + } + if ($edit->project) { + $version = project_release_get_version((object)$edit, $edit->project); + form_set_value($form['version'], $version); + form_set_value($form['title'], t('%project %version', array('%project' => $edit->project->title, '%version' => $version))); + } +} + +/** + * Implementation of hook_load(). + */ +function project_release_load($node) { + $additions = db_fetch_object(db_query("SELECT * FROM {project_release_nodes} WHERE nid = %d", $node->nid)); + return $additions; +} + +/** + * Implementation of hook_insert(). + */ +function project_release_insert($node) { + db_query("INSERT INTO {project_release_nodes} (nid, pid, version, tag, file_path, file_date, file_hash, rebuild, version_super_major, version_super_minor, version_major, version_minor, version_patch, version_extra) VALUES (%d, %d, '%s', '%s', '%s', %d, '%s', %d, %d, %d, %d, %d, %d, '%s')", $node->nid, $node->pid, $node->version, $node->tag, $node->file_path, $node->file_date, $node->file_hash, $node->rebuild, $node->version_super_major, $node->version_super_minor, $node->version_major, $node->version_minor, $node->version_patch, $node->version_extra); +} + +/** + * Implementation of hook_update(). + */ +function project_release_update($node) { + db_query("UPDATE {project_release_nodes} SET pid = %d, version = '%s', tag = '%s', file_path = '%s', file_date = %d, file_hash = '%s', rebuild = %d, version_super_major = %d, version_super_minor = %d, version_major = %d, version_minor = %d, version_patch = %d, version_extra = '%s' WHERE nid = %d", $node->pid, $node->version, $node->tag, $node->file_path, $node->file_date, $node->file_hash, $node->rebuild, $node->version_super_major, $node->version_super_minor, $node->version_major, $node->version_minor, $node->version_patch, $node->version_extra, $node->nid); +} + +/** + * Implementation of hook_delete(). + */ +function project_release_delete($node) { + if ($node->file_path) { + file_delete(file_create_path($node->file_path)); + } + db_query("DELETE FROM {project_release_nodes} WHERE nid = %d", $node->nid); +} + +/** + * Returns the version format string for a given project + */ +function project_release_get_version_format($project) { + if (!empty($project->version_format)) { + return $project->version_format; + } + + $db_format = db_result(db_query("SELECT version_format FROM {project_release_projects} WHERE nid = %d", $project->nid)); + if (!empty($db_format)) { + return $db_format; + } + + $admin_setting = variable_get('project_release_default_version_format', FALSE); + if (!empty($admin_setting)) { + return $admin_setting; + } + + return '%major.%minor'; +} + +/** + * Returns the formatted version string for a given release node. + */ +function project_release_get_version($release, $project = NULL) { + if (isset($project)) { + $node = $project; + } + else { + $node->nid = $release->pid; + } + $version_format = project_release_get_version_format($node); + $variables = array( + '%super_major' => $release->version_super_major, + '%super_minor' => $release->version_super_minor, + '%major' => $release->version_major, + '%minor' => $release->version_minor, + '%patch' => $release->version_patch, + '%extra' => $release->version_extra, + ); + return strtr($version_format, $variables); +} + +/** + * Implementation of hook_view(). + */ +function project_release_view(&$release, $teaser = FALSE, $page = FALSE) { + $project = node_load($release->pid); + + if ($page) { + // Breadcrumb navigation + $breadcrumb[] = array('path' => 'node/'. $project->nid, 'title' => $project->title); + $breadcrumb[] = array('path' => 'node/'. $node->nid, 'title' => $node->title); + $breadcrumb = project_project_set_location($project, $breadcrumb); + menu_set_location($breadcrumb); + } + + $links = array(); + if ($release->file_path) { + $output .= '' . t('Download: %file', array('%file' => l(basename($release->file_path), file_create_url($release->file_path)))) . '
'; + $output .= '' . t('Size: %size', array('%size' => format_size(filesize(file_create_path($release->file_path))))) . '
'; + $output .= '' . t('md5_file file_hash: %file_hash', array('%file_hash' => $release->file_hash)) . '
'; + } + if ($release->created) { + $output .= '' . t('First released: %created', array('%created' => format_date($release->created))) . '
'; + } + if ($release->changed && ($release->changed != $release->created)) { + $output .= '' . t('Last updated: %changed', array('%changed' => format_date($release->changed))) . '
'; + } + + $output .= $release->changes; + $output .= theme('links', $links); + + $release->body .= '
' . $output . '
'; +} + +/** + * Display a list of releases for a given project + */ +function project_release_project_releases() { + $node = node_load(arg(1)); + + // Breadcrumb navigation + $breadcrumb[] = array('path' => 'node/'. $node->nid .'/', 'title' => $node->title); + $breadcrumb[] = array('path' => 'node/'. arg(1) .'/release', 'title' => t('Releases')); + $breadcrumb = project_project_set_location($node, $breadcrumb); + + menu_set_location($breadcrumb); + + $output = project_release_list($node); + + drupal_set_title(check_plain($node->title)); + return $output; +} + +/** + * Get an array of release nodes + */ +function project_release_get_releases($project, $nodes = true) { + $result = db_query(db_rewrite_sql("SELECT n.nid, r.* FROM {node} n INNER JOIN {project_release_nodes} r ON r.nid = n.nid WHERE r.pid = %d ORDER BY n.created DESC"), $project->nid); + $releases = array(); + while ($obj = db_fetch_object($result)) { + if ($nodes) { + $releases[$obj->nid] = node_load($obj->nid); + } + else { + $releases[$obj->nid] = $obj->version; + } + } + return $releases; +} + +function project_release_list($project, $main = 0) { + if ($releases = project_release_get_releases($project)) { + foreach ($releases as $release) { + $output .= node_view($release); + } + return $output; + } +} + +function project_releases_list($refresh = FALSE) { + if (!$refresh && ($versions = cache_get('project_releases'))) { + $versions = unserialize($versions->data); + } + else { + $result = db_query("SELECT version FROM {project_releases} WHERE status = 1 GROUP BY version, created ORDER BY created DESC;"); + $versions = array(); + while ($object = db_fetch_object($result)) { + $version = preg_replace('/^(.+?)\.?(?:\d+)?$/', '\1', $object->version); + $versions[$version] = $version == $object->version ? $version : $version .'.x'; + } + cache_set('project_releases', serialize($versions)); + menu_rebuild(); + } + + return $versions; +} + +/** + * Implementation of hook_form_alter(). + */ +function project_release_form_alter($form_id, &$form) { + if ($form_id == 'project_project_node_form') { + return project_release_alter_project_form($form); + } +} + +/** + * Alters the project_project node form to add release settings. + * @see project_release_form_alter + */ +function project_release_alter_project_form(&$form) { + $form['release'] = array( + '#type' => 'fieldset', + '#title' => t('Release information'), + '#collapsible' => TRUE, + ); + $form['release']['releases'] = array( + '#type' => 'checkbox', + '#title' => t('Enable releases'), + '#return_value' => 1, + '#weight' => -10, + '#default_value' => isset($node->releases) ? $node->releases : 1, + '#description' => t('Allow releases of this project with specific versions.'), + ); + if (user_access('administer projects')) { + $form['release']['version_format'] = array( + '#type' => 'textfield', + '#title' => t('Version format string'), + '#default_value' => $node->mail_copy, + '#size' => 50, + '#maxlength' => 255, + '#description' => t('Customize the format of the version strings for releases of this project.') .' '. t('Available variables are:') .' %super_major, %super_minor, %major, %minor, %patch, %extra.', + ); + } +} + +/** + * hook_nodeapi() implementation. This just decides what type of node + * is being passed, and calls the appropriate type-specific hook. + * + * @see project_release_project_nodeapi(). + */ +function project_release_nodeapi(&$node, $op, $arg) { + switch ($node->type) { + case 'project_project': + project_release_project_nodeapi($node, $op, $arg); + break; + } +} + +/** + * hook_nodeapi implementation specific to "project_project" nodes + * (from the project.module) + * @see project_release_nodeapi(). + */ +function project_release_project_nodeapi(&$node, $op, $arg) { + switch ($op) { + case 'load': + $project = db_fetch_object(db_query('SELECT * FROM {project_release_projects} WHERE nid = %d', $node->nid)); + foreach (array('releases', 'version_format') as $field) { + $node->$field = $project->$field; + } + break; + + case 'insert': + db_query("INSERT INTO {project_release_projects} (nid, releases, version_format) VALUES (%d, %d, '%s')", $node->nid, $node->releases, $node->version_format); + break; + + case 'update': + db_query("UPDATE {project_release_projects} SET releases = %d, version_format = '%s' WHERE nid = %d", $node->releases, $node->version_format, $node->nid); + break; + + case 'delete': + // TODO: delete all release nodes associated with this project, too. + db_query('DELETE FROM {project_release_projects} WHERE nid = %d', $node->nid); + break; + } +} Index: modules/project/project_release_scan.inc =================================================================== RCS file: modules/project/project_release_scan.inc diff -N modules/project/project_release_scan.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/project/project_release_scan.inc 13 Sep 2006 09:03:42 -0000 @@ -0,0 +1,143 @@ + 'admin/settings/project/reload', 'title' => t('scan for releases'), 'callback' => 'project_release_scan', 'access' => $access, 'type' => MENU_NORMAL_ITEM); + } + } +} + +/** + * This is not a real hook. Just saving this code since it really + * belongs in project_release_cron() if we ever want it to happen again. + */ +function project_release_scan_cron() { + if (variable_get('project_release_directory', '') && time() - variable_get('project_release_directory_last', 0) > variable_get('project_release_directory_interval', 6 * 60 * 60)) { + variable_set('project_release_directory_last', time()); + // TODO: should be able to optimize this to use less file system calls. + project_release_scan_directory(); + } +} + +function project_release_scan() { + project_release_scan_directory(); + drupal_set_title(t('Release directory scan')); + foreach (project_release_scan_directory_results() as $type => $count) { + drupal_set_message(t('Releases %type: %count.', array('%type' => $type, '%count' => $count))); + } + return t('Scan completed.'); +} + +function project_release_scan_directory($project = NULL) { + if ($dir = file_create_path(variable_get('project_release_directory', ''))) { + if ($project) { + $regexp = "($project)" .'-(.+)\.(tar.gz|zip)'; + } + else { + $regexp = '(.+)-(.+)\.(tar.gz|zip)'; + } + + file_scan_directory($dir, $regexp, array('.', '..', 'CVS'), 'project_release_scan_parse'); + + // If any releases were previously present but are no longer, unpublish them. + foreach (project_release_scan_parse() as $project) { + if ($project->releases) { + foreach ($project->releases as $rid) { + project_release_delete($rid); + project_release_scan_directory_results(t('unpublished')); + } + } + } + } + project_releases_list(TRUE); +} + +function project_release_scan_directory_results($type = NULL) { + static $results; + if ($type) { + if (!$results[$type]) { + $results[$type] = 1; + } + else { + $results[$type]++; + } + } + return $results; +} + +function project_release_scan_parse($path = NULL) { + static $projects = array(); + + if (is_null($path)) { + return $projects; + } + else { + $dir = dirname($path); + $file = basename($path); + //preg_match('/^(.+?)-([0-9.]+(?:-.*)|[^-]+)\.(tar\.gz|zip)$/', $file, $matches); + preg_match('/^(.+?)-([0-9.]+(?:-.*)|[^-]+)\.(tar.gz$|zip$)/', $file, $matches); + list($filename, $name, $version) = $matches; + + // If the project was not previously loaded, load its data, including previous releases. + if (!$projects[$name]) { + if ($project = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.type, n.moderate FROM {node} n INNER JOIN {project_projects} p ON n.nid = p.nid WHERE p.uri = '%s'"), $name))) { + if ($releases = project_release_load($project, 0)) { + // Releases are loaded in an array where the key is the rid and the value the version. + // We flip the array to more readily test for releases. + $project->releases = array_flip($releases); + } + // Unmoderate nodes that have gotten releases + if (variable_get('project_release_unmoderate', 0) && $project->moderate) { + db_query("UPDATE {node} SET moderate = 0, status = 1 WHERE nid = %d", $project->nid); + } + } + else { + // No project found for this id. + return; + } + $projects[$name] = & $project; + } + else { + $project = & $projects[$name]; + } + + $release->scan = 1; + $release->nid = $project->nid; + // If the current version was previously released, add its rid value, so that + // it will be updated rather than inserted by project_release_save(); + if (is_array($project->releases) && array_key_exists($version, $project->releases)) { + $release->rid = $project->releases[$version]; + project_release_scan_directory_results(t('updated')); + } + else { + project_release_scan_directory_results(t('created')); + } + $release->version = $version; + $release->path = $path; + project_release_save($release); + // This release version has been resaved, so take it out of the array. + // That way, any releases no longer present can be unpublished in project_release_scan_directory(). + if ($project->releases[$version]) { + unset($project->releases[$version]); + } + } +} + + Index: modules/project/project_release_update.php =================================================================== RCS file: modules/project/project_release_update.php diff -N modules/project/project_release_update.php --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ modules/project/project_release_update.php 13 Sep 2006 09:03:43 -0000 @@ -0,0 +1,160 @@ +version_major . '-'; + $tag .= $node->version_minor . '-'; + $tag .= $node->version_patch; + if (!empty($node->version_extra)) { + $tag .= '-' . strtoupper(preg_replace('/(.+)(\d+)/', '\1-\2', $node->version_extra)); + } + return $tag; +} + +function convert_all() { + $releases = db_query("SELECT pr.*, n.uid, n.title AS project_title FROM {project_releases} pr INNER JOIN {node} n ON pr.nid = n.nid"); + while ($old_release = db_fetch_object($releases)) { + convert_one($old_release); + } +} + +/** + * Determines if a given entry in the {project_releases} table + * corresponds to a real release, or a nightly development snapshot, + * and converts it into the appropriate kind of release node. + * + * BEWARE: This is drupal.org specific code. In our case, the core + * drupal system (project nid #3060) is the only project that's ever + * had "real" releases so far. Everything else has been a nightly dev + * snapshot release. If your site has a different usage, please modify + * the logic in here to meet your needs. + * + */ +function convert_one($old_release) { + // First, save everything that's shared, regardless of the version/type + + // Things that go in {node} or {node_revisions} + $node->type = 'project_release'; + $node->uid = $old_release->uid ? $old_release->uid : 1; + $node->created = $old_release->created; + $node->changed = $old_release->changed; + $node->body = $old_release->changes ? $old_release->changes : ''; + $node->teaser = node_teaser($node->body); + $node->filter = variable_get('filter_default_format', 1); + $node->status = $old_release->status; + $node->revision = 1; + $node->promote = 0; + $node->comment = 0; + + // Things that go in {project_release_nodes} (that don't depend on version) + $node->pid = $old_release->nid; + $node->file_path = $old_release->path; + $node->file_date = $old_release->changed; + $node->file_hash = $old_release->hash; + + // Now, depending on the project and version, fill in the rest. + if ($old_release->nid == 3060) { +// $node->version_super_major = -1; +// $node->version_super_minor = -1; + if ($old_release->version == 'cvs') { + $node->version_major = 5; + $node->version_minor = 0; + $node->version_patch = 0; + $node->version_extra = 'dev'; + $node->rebuild = 1; + $node->tag = 'TRUNK'; + } + else { + preg_match('/(\d+)\.(\d+)\.(\d+)(-?)(.+)?/', $old_release->version, $matches); + $node->version_major = $matches[1]; + $node->version_minor = $matches[2]; + $node->version_patch = $matches[3]; + $node->version_extra = $matches[5] ? $matches[5] : NULL; + $node->tag = generate_core_tag($node); + $node->rebuild = 0; + } + } + elseif ($old_release->version == 'cvs') { + // The "cvs" version is a nightly tarball from the trunk +// $node->version_super_major = -1; +// $node->version_super_minor = -1; + $node->version_major = 0; + $node->version_minor = 0; +// $node->version_patch = -1; + $node->version_extra = 'dev'; + $node->tag = 'TRUNK'; + $node->rebuild = 1; + } + else { + // Nightly tarball from a specific branch. + preg_match('/(\d+)\.(\d+)\.(\d+)/', $old_release->version, $matches); + if ($matches[3] != 0) { + dprint_r("warning: release $old_release->rid of $old_release->project_title has unexpected patch-level version ($matches[3])"); + } + $node->version_super_major = $matches[1]; + $node->version_super_minor = $matches[2]; + $node->version_major = 1; + $node->version_minor = 0; +// $node->version_patch = -1; + $node->version_extra = 'dev'; + $node->tag = 'DRUPAL-' . $matches[1] . '-' . $matches[2]; + $node->rebuild = 1; + } + + // Now, set the right kind of title. + $version = ''; + if ($node->tag == 'TRUNK' && !isset($node->version_patch)) { + $version = t('TRUNK'); + } + else { + $version = theme('project_release_version', $node); + } + $node->title = t('%project %version', array('%project' => $old_release->project_title, '%version' => $version)); + if ($node->rebuild) { + $node->title .= ' (' . t('nightly development snapshot') . ')'; + } + + // Now, we can actually create the node. + node_save($node); + + // Next, update all the issues with the old revision id (rid) to + // point to the new nid, instead. + $nid = $node->nid; + $rid = $old_release->rid; + db_query("UPDATE {project_issues} SET rid = %d WHERE rid = %d", $nid, $rid); + // TODO: {project_comments} !!! + // {project_comments} is much harder, since there's serialized data. :( + + // TODO: update {cvs_tags} table to put in a value for the "release + // nid" column so we know this project nid/tag combo has a release? +} + +include_once './includes/bootstrap.inc'; +drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); + +if (!module_exist('project_release')) { + print '' . t('ERROR: project_release_update.php requires that you first install the project_release.module') . ''; + exit(1); +} + +convert_all(); + +// TODO: user feedback, progress, etc. +// TODO: drop old {project_releases} table once we're convinced it worked Index: modules/project/release.inc =================================================================== RCS file: modules/project/release.inc diff -N modules/project/release.inc --- modules/project/release.inc 4 Aug 2006 03:19:50 -0000 1.70.2.3 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,437 +0,0 @@ - $count) { - drupal_set_message(t('Releases %type: %count.', array('%type' => $type, '%count' => $count))); - } - return t('Scan completed.'); -} - -function project_release_scan_directory($project = NULL) { - if ($dir = file_create_path(variable_get('project_release_directory', ''))) { - if ($project) { - $regexp = "($project)" .'-(.+)\.(tar.gz|zip)'; - } - else { - $regexp = '(.+)-(.+)\.(tar.gz|zip)'; - } - - file_scan_directory($dir, $regexp, array('.', '..', 'CVS'), 'project_release_scan_parse'); - - // If any releases were previously present but are no longer, unpublish them. - foreach (project_release_scan_parse() as $project) { - if ($project->releases) { - foreach ($project->releases as $rid) { - project_release_delete($rid); - project_release_scan_directory_results(t('unpublished')); - } - } - } - } - project_releases_list(TRUE); -} - -function project_release_scan_directory_results($type = NULL) { - static $results; - if ($type) { - if (!$results[$type]) { - $results[$type] = 1; - } - else { - $results[$type]++; - } - } - return $results; -} - -function project_release_scan_parse($path = NULL) { - static $projects = array(); - - if (is_null($path)) { - return $projects; - } - else { - $dir = dirname($path); - $file = basename($path); - //preg_match('/^(.+?)-([0-9.]+(?:-.*)|[^-]+)\.(tar\.gz|zip)$/', $file, $matches); - preg_match('/^(.+?)-([0-9.]+(?:-.*)|[^-]+)\.(tar.gz$|zip$)/', $file, $matches); - list($filename, $name, $version) = $matches; - - // If the project was not previously loaded, load its data, including previous releases. - if (!$projects[$name]) { - if ($project = db_fetch_object(db_query(db_rewrite_sql("SELECT n.nid, n.type, n.moderate FROM {node} n INNER JOIN {project_projects} p ON n.nid = p.nid WHERE p.uri = '%s'"), $name))) { - if ($releases = project_release_load($project, 0)) { - // Releases are loaded in an array where the key is the rid and the value the version. - // We flip the array to more readily test for releases. - $project->releases = array_flip($releases); - } - // Unmoderate nodes that have gotten releases - if (variable_get('project_release_unmoderate', 0) && $project->moderate) { - db_query("UPDATE {node} SET moderate = 0, status = 1 WHERE nid = %d", $project->nid); - } - } - else { - // No project found for this id. - return; - } - $projects[$name] = & $project; - } - else { - $project = & $projects[$name]; - } - - $release->scan = 1; - $release->nid = $project->nid; - // If the current version was previously released, add its rid value, so that - // it will be updated rather than inserted by project_release_save(); - if (is_array($project->releases) && array_key_exists($version, $project->releases)) { - $release->rid = $project->releases[$version]; - project_release_scan_directory_results(t('updated')); - } - else { - project_release_scan_directory_results(t('created')); - } - $release->version = $version; - $release->path = $path; - project_release_save($release); - // This release version has been resaved, so take it out of the array. - // That way, any releases no longer present can be unpublished in project_release_scan_directory(). - if ($project->releases[$version]) { - unset($project->releases[$version]); - } - } -} - -function project_release_cron() { - // TODO: should be able to optimize this to use less file system calls. - if ($dir = variable_get('project_release_directory', '')) { - project_release_scan_directory(); - } -} - -function project_release_submit() { - switch ($_POST['op'] ? $_POST['op'] : arg(3)) { - case 'add': - $node = node_load(arg(1)); - $breadcrumb = array(l(t('Home'), NULL), l(t('Projects'), 'project')); - if (project_use_taxonomy()) { - $taxonomy_terms = taxonomy_node_get_terms($node->nid); - $term = reset($taxonomy_terms); - $breadcrumb[] = l($term->name, 'project', NULL, "tid=$term->tid"); - } - $breadcrumb[] = l($node->title, "node/$node->nid"); - $error = 1; - $release->nid = $node->nid; - $form = project_release_form($release, $param); - $output = drupal_get_form('project_release_form', $form); - - drupal_set_title(t('Add release to %project', array('%project' => $node->title))); - drupal_set_breadcrumb($breadcrumb); - return $output; - break; - case 'edit': - if ($release = project_release_load(arg(4))) { - $node = node_load($release->nid); - $output = project_release_view($release); - $form = project_release_form($release, $param); - $output = drupal_get_form('project_release_form', $form); - drupal_set_title(t('Edit release %version for %project', array('%version' => $release->version, '%project' => $node->title))); - return $output; - } - break; - case 'delete': - if (($release = project_release_load(arg(4))) && $release->nid == arg(1)) { - project_release_delete(arg(4)); - drupal_goto("node/$release->nid/release"); - } - break; - case t('Submit'): - $edit = (object) $_POST['edit']; - $node = node_load($edit->nid); - if (node_access('update', $node)) { - project_release_validate($edit); - if (form_get_errors()) { - $form = project_release_form($release, $param); - $output = drupal_get_form('project_release_form', $form); - return $output; - } - else { - // Save the release and re-direct to the overview page for the project - $edit->status = 1; - project_release_save($edit); - drupal_goto("node/$node->nid/release"); - } - } - } -} - -/** - * Implementation of hook_form(). - */ -function project_release_form(&$release, &$param) { - /* TODO: pending final fileapi - // Set form parameters so we can accept file uploads. - $param['options'] = array('enctype' => 'multipart/form-data'); - */ - - $form['version'] = array( - '#type' => 'textfield', - '#title' => t('Version'), - '#default_value' => $release->version, - '#size' => 40, - '#maxlength' => 255, - '#required' => TRUE, - ); - $form['changes'] = array( - '#type' => 'textarea', - '#title' => t('Changes'), - '#default_value' => $release->changes, - '#rows' => 10, - '#cols' => 40, - '#required' => TRUE, - ); - if (!variable_get('project_release_directory', '')) { - $form['path'] = array( - '#type' => 'file', - '#title' => t('File'), - '#size' => 40, - '#description' => ($release->fid) ? - t('A file already exists, if you upload another file the current file will be replaced.') : - '' . $error['file'], - '#required' => $release->fid, - ); - } - if ($release->rid) { - $form['rid'] = array( - '#type' => 'hidden', - '#default_value' => $release->rid, - ); - } - $form['nid'] = array( - '#type' => 'hidden', - '#default_value' => $release->nid, - ); - - if (!form_get_errors()) { - $form['submit'] = array( - '#type' => 'button', - '#value' => t('Submit'), - ); - } - - return $form; -} - -function project_release_validate(&$edit) { - if ($edit->rid) { - $release = project_release_load($edit->rid); - $edit->nid = $release->nid; - $edit->fid = $release->fid; - } - - if (empty($edit->version)) { - form_set_error('version', t('You must specify a valid version.')); - } - else { - $result = db_query("SELECT * FROM {project_releases} WHERE nid = %d AND version = '%s'", $edit->nid, trim($edit->version)); - if ($release = db_fetch_object($result)) { - if ($edit->rid != $release->rid) { - if ($release->status) { - form_set_error('version', t('This version already exists for this project.')); - } - else { - $edit->rid = $release->rid; - } - } - } - } - - if (empty($edit->changes)) { - form_set_error('changes', t('You must specify changes.')); - } - - /* TODO: pending final fileapi - $file = check_file('file'); - - if ($file && !$file->fid && !empty($file->error)) { - $error['file'] = theme('error', $file->error); - } - else { - $edit->fid = $file->fid; - } - */ -} - -function project_release_view($release, $project = 0) { - if (!$project) { - $project = node_load($release->nid); - } - - $links = array(); - if ($release->rid && node_access('update', $project)) { - $links[] = l('edit', "node/$project->nid/release/edit/$release->rid"); - //$links[] = l('delete', "node/$project->nid/release/delete/$release->rid"); - } - - if ($release->path) { - $output .= '' . t('Download: %path', array('%path' => base_path() . $release->path)) . '
'; - $output .= '' . t('Size: %size', array('%size' => format_size(filesize($release->path)))) . '
'; - $output .= '' . t('md5_file hash: %hash', array('%hash' => $release->hash)) . '
'; - } - if ($release->created) { - $output .= '' . t('First released: %created', array('%created' => format_date($release->created))) . '
'; - } - if ($release->changed && ($release->changed != $release->created)) { - $output .= '' . t('Last updated: %changed', array('%changed' => format_date($release->changed))) . '
'; - } - - $output .= $release->changes; - $output .= theme('links', $links); - - return $output; -} - -function project_release_list($project, $main = 0) { - $result = db_query('SELECT * FROM {project_releases} WHERE nid = %d AND status = 1 ORDER BY created DESC', $project->nid); - if (db_num_rows($result)) { - $header = array(t('Version'), t('Released'), t('Changes')); - if (node_access('update', $project) || node_access('delete', $project)) { - $header[] = array('data' => t('Operations'), 'colspan' => 2); - } - while ($release = db_fetch_object($result)) { - $output .= theme('box', l(t('%project %version', array('%project' => $project->title, '%version' => $release->version)), 'node/'. $project->nid .'/release', array('id' => 'version-'. $release->version), NULL, 'version-'. $release->version), project_release_view($release, $project)); - } - return $output; - } -} - -function project_release_load($rid, $status = 1) { - if ($rid->type == 'project_project') { - $status = $status ? 'AND status = 1' : ''; - $result = db_query("SELECT rid,version FROM {project_releases} WHERE nid = %d $status ORDER BY version DESC", $rid->nid); - $releases = array(); - while ($release = db_fetch_object($result)) { - $releases[$release->rid] = $release->version; - } - return $releases; - } - elseif (is_numeric($rid)) { - return db_fetch_object(db_query('SELECT * FROM {project_releases} WHERE rid = %d ORDER BY created DESC', $rid)); - } -} - -function project_release_save($release) { - $fields = array('rid' => '%d', 'nid' => '%d', 'fid' => '%d', - 'path' => "'%s'", 'created' => '%d', - 'changed' => '%d', 'hash' => "'%s'", 'version' => "'%s'", - 'changes' => "'%s'", 'status' => '%d'); - - $node = node_load($release->nid); - - /* TODO: pending final fileapi - $file = check_file('file'); - */ - // If the version was not previously released, insert it. - if (empty($release->rid)) { - - // Make sure this release version doesn't already exist for the given project. - // This test should not be needed, but duplicate releases on drupal.org suggest - // that an unidentified problem is causing duplicate versions to be saved. - if (db_num_rows(db_fetch_object(db_query("SELECT rid FROM {project_releases} WHERE nid = %d AND version = '%s'", $release->nid, $release->version)))) { - return FALSE; - } - $release->rid = db_next_id('project_rid'); - if (!variable_get('project_release_directory', '') && $file->filename) { - $release->fid = drupal_file_save($file); - } - if ($release->path) { - $release->created = ($release->version == 'cvs') ? 0 : filectime($release->path); - $release->changed = filemtime($release->path); - $release->hash = md5_file($release->path); - } - else { - $release->created = ($release->version == 'cvs') ? 0 : time(); - $release->changed = time(); - } - - // Prepare the query: - foreach ($release as $key => $value) { - if (in_array($key, array_keys($fields))) { - $input[db_escape_string($key)] = $fields[$key]; - $values[] = $value; - } - } - - // Insert the node into the database: - db_query('INSERT INTO {project_releases} ('. implode(', ', array_keys($input)) .') VALUES('. implode(', ', $input) .')', $values); - watchdog('project', t("added release %version to %project.", array('%version' => theme('placeholder', $release->version), '%project' => theme('placeholder', $node->title)))); - } - // Otherwise, update the existing record. - else { - /* TODO: pending final fileapi - // Replace current file if new file upload exists. - if (!variable_get('project_release_directory', '') && $file->filename) { - $file->fid = $release->fid; - drupal_file_save($file); - } - */ - - unset($release->created); - if($release->path) { - if ($release->scan) { - // Load previous md5_file hash value, and test it agains the current one to see if the file has changed. - $previous = db_fetch_object(db_query("SELECT hash FROM {project_releases} WHERE rid = %d", $release->rid)); - if (md5_file($release->path) != $previous->hash) { - $release->hash = md5_file($release->path); - $release->changed = time(); - } - } - else { - $release->hash = md5_file($release->path); - $release->changed = filemtime($release->path); - } - } - - // Prepare the query: - foreach ($release as $key => $value) { - - if (in_array($key, array_keys($fields))) { - $q[] = db_escape_string($key) ." = " . $fields[$key]; - $v[] = $value; - } - } - $v[] = $release->nid; - $v[] = $release->rid; - - db_query('UPDATE {project_releases} SET '. implode(', ', $q) .' WHERE nid = %d AND rid = %d', $v); - watchdog('project', t("updated release %project %version", array('%project' => theme('placeholder', $node->title), '%version' => theme('placeholder', $release->version)))); - } - return $release->rid; -} - -function project_release_delete($rid) { - $release = project_release_load($rid); - watchdog('project', t("unpublished release %version", array('%version' => theme('placeholder', $release->version)))); - db_query('UPDATE {project_releases} SET status = 0 WHERE rid = %d', $rid); -} - -function project_releases_list($refresh = FALSE) { - if (!$refresh && ($versions = cache_get('project_releases'))) { - $versions = unserialize($versions->data); - } - else { - $result = db_query("SELECT version FROM {project_releases} WHERE status = 1 GROUP BY version, created ORDER BY created DESC;"); - $versions = array(); - while ($object = db_fetch_object($result)) { - $version = preg_replace('/^(.+?)\.?(?:\d+)?$/', '\1', $object->version); - $versions[$version] = $version == $object->version ? $version : $version .'.x'; - } - cache_set('project_releases', serialize($versions)); - menu_rebuild(); - } - - return $versions; -} cvs diff: Diffing modules/project/po cvs diff: Diffing modules/project/po/ar