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 .= '