From 7a5bb7345de1512a420d0272e335e3a6c514d17c Mon Sep 17 00:00:00 2001 From: Loic d'Anterroches Date: Mon, 4 Aug 2008 00:42:05 +0200 Subject: [PATCH] Added a download area to the forge. --- src/IDF/Form/UpdateUpload.php | 140 ++++++++++++ src/IDF/Form/Upload.php | 152 +++++++++++++ src/IDF/Form/UploadConf.php | 70 ++++++ src/IDF/Migrations/1Download.php | 52 +++++ src/IDF/Migrations/Install.php | 2 + src/IDF/Precondition.php | 21 ++ src/IDF/Upload.php | 143 +++++++++++++ src/IDF/Views/Download.php | 200 ++++++++++++++++++ src/IDF/Views/Project.php | 45 +++- src/IDF/conf/views.php | 24 +++ src/IDF/templates/admin/base.html | 3 +- src/IDF/templates/admin/downloads.html | 34 +++ src/IDF/templates/base.html | 1 + src/IDF/templates/downloads/base.html | 8 + src/IDF/templates/downloads/index.html | 13 ++ .../templates/downloads/js-autocomplete.html | 27 +++ src/IDF/templates/downloads/submit.html | 57 +++++ src/IDF/templates/downloads/view.html | 63 ++++++ src/IDF/templates/issues/view.html | 2 +- www/media/idf/css/style.css | 17 ++ www/media/idf/img/down-large.png | Bin 0 -> 874 bytes 21 files changed, 1071 insertions(+), 3 deletions(-) create mode 100644 src/IDF/Form/UpdateUpload.php create mode 100644 src/IDF/Form/Upload.php create mode 100644 src/IDF/Form/UploadConf.php create mode 100644 src/IDF/Migrations/1Download.php create mode 100644 src/IDF/Upload.php create mode 100644 src/IDF/Views/Download.php create mode 100644 src/IDF/templates/admin/downloads.html create mode 100644 src/IDF/templates/downloads/base.html create mode 100644 src/IDF/templates/downloads/index.html create mode 100644 src/IDF/templates/downloads/js-autocomplete.html create mode 100644 src/IDF/templates/downloads/submit.html create mode 100644 src/IDF/templates/downloads/view.html create mode 100644 www/media/idf/img/down-large.png diff --git a/src/IDF/Form/UpdateUpload.php b/src/IDF/Form/UpdateUpload.php new file mode 100644 index 0000000..6620faf --- /dev/null +++ b/src/IDF/Form/UpdateUpload.php @@ -0,0 +1,140 @@ +user = $extra['user']; + $this->project = $extra['project']; + $this->upload = $extra['upload']; + + $this->fields['summary'] = new Pluf_Form_Field_Varchar( + array('required' => true, + 'label' => __('Summary'), + 'initial' => $this->upload->summary, + 'widget_attrs' => array( + 'maxlength' => 200, + 'size' => 67, + ), + )); + $tags = $this->upload->get_tags_list(); + for ($i=1;$i<4;$i++) { + $initial = ''; + if (isset($tags[$i-1])) { + if ($tags[$i-1]->class != 'Other') { + $initial = (string) $tags[$i-1]; + } else { + $initial = $tags[$i-1]->name; + } + } + $this->fields['label'.$i] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Labels'), + 'initial' => $initial, + 'widget_attrs' => array( + 'maxlength' => 50, + 'size' => 20, + ), + )); + } + } + + /** + * Validate the interconnection in the form. + */ + public function clean() + { + $conf = new IDF_Conf(); + $conf->setProject($this->project); + $onemax = array(); + foreach (split(',', $conf->getVal('labels_download_one_max', IDF_Form_UploadConf::init_one_max)) as $class) { + if (trim($class) != '') { + $onemax[] = mb_strtolower(trim($class)); + } + } + $count = array(); + for ($i=1;$i<4;$i++) { + $this->cleaned_data['label'.$i] = trim($this->cleaned_data['label'.$i]); + if (strpos($this->cleaned_data['label'.$i], ':') !== false) { + list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2); + list($class, $name) = array(mb_strtolower(trim($class)), + trim($name)); + } else { + $class = 'other'; + $name = $this->cleaned_data['label'.$i]; + } + if (!isset($count[$class])) $count[$class] = 1; + else $count[$class] += 1; + if (in_array($class, $onemax) and $count[$class] > 1) { + if (!isset($this->errors['label'.$i])) $this->errors['label'.$i] = array(); + $this->errors['label'.$i][] = sprintf(__('You cannot provide more than label from the %s class to an issue.'), $class); + throw new Pluf_Form_Invalid(__('You provided an invalid label.')); + } + } + return $this->cleaned_data; + } + + /** + * Save the model in the database. + * + * @param bool Commit in the database or not. If not, the object + * is returned but not saved in the database. + * @return Object Model with data set from the form. + */ + function save($commit=true) + { + if (!$this->isValid()) { + throw new Exception(__('Cannot save the model from an invalid form.')); + } + // Add a tag for each label + $tags = array(); + for ($i=1;$i<4;$i++) { + if (strlen($this->cleaned_data['label'.$i]) > 0) { + if (strpos($this->cleaned_data['label'.$i], ':') !== false) { + list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2); + list($class, $name) = array(trim($class), trim($name)); + } else { + $class = 'Other'; + $name = trim($this->cleaned_data['label'.$i]); + } + $tag = IDF_Tag::add($name, $this->project, $class); + $tags[] = $tag->id; + } + } + // Create the upload + $this->upload->summary = trim($this->cleaned_data['summary']); + $this->upload->update(); + $this->upload->batchAssoc('IDF_Tag', $tags); + return $this->upload; + } +} + diff --git a/src/IDF/Form/Upload.php b/src/IDF/Form/Upload.php new file mode 100644 index 0000000..c547ae2 --- /dev/null +++ b/src/IDF/Form/Upload.php @@ -0,0 +1,152 @@ +user = $extra['user']; + $this->project = $extra['project']; + + $this->fields['summary'] = new Pluf_Form_Field_Varchar( + array('required' => true, + 'label' => __('Summary'), + 'initial' => '', + 'widget_attrs' => array( + 'maxlength' => 200, + 'size' => 67, + ), + )); + $this->fields['file'] = new Pluf_Form_Field_File( + array('required' => true, + 'label' => __('File'), + 'initial' => '', + 'move_function_params' => array('upload_path' => Pluf::f('upload_path').'/'.$this->project->shortname.'/files', + 'upload_path_create' => true), + + )); + for ($i=1;$i<4;$i++) { + $this->fields['label'.$i] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Labels'), + 'widget_attrs' => array( + 'maxlength' => 50, + 'size' => 20, + ), + )); + } + } + + + public function clean_file() + { + if (!preg_match('/\.(png|jpg|jpeg|gif|bmp|psd|tif|aiff|asf|avi|bz2|css|doc|eps|gz|mdtext|mid|mov|mp3|mpg|ogg|pdf|ppt|ps|qt|ra|ram|rm|rtf|sdd|sdw|sit|sxi|sxw|swf|tgz|txt|wav|xls|xml|wmv|zip)$/i', $this->cleaned_data['file'])) { + throw new Pluf_Form_Invalid(__('For security reason, you cannot upload a file with this extension.')); + } + return $this->cleaned_data['file']; + } + + /** + * Validate the interconnection in the form. + */ + public function clean() + { + $conf = new IDF_Conf(); + $conf->setProject($this->project); + $onemax = array(); + foreach (split(',', $conf->getVal('labels_download_one_max', IDF_Form_UploadConf::init_one_max)) as $class) { + if (trim($class) != '') { + $onemax[] = mb_strtolower(trim($class)); + } + } + $count = array(); + for ($i=1;$i<4;$i++) { + $this->cleaned_data['label'.$i] = trim($this->cleaned_data['label'.$i]); + if (strpos($this->cleaned_data['label'.$i], ':') !== false) { + list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2); + list($class, $name) = array(mb_strtolower(trim($class)), + trim($name)); + } else { + $class = 'other'; + $name = $this->cleaned_data['label'.$i]; + } + if (!isset($count[$class])) $count[$class] = 1; + else $count[$class] += 1; + if (in_array($class, $onemax) and $count[$class] > 1) { + if (!isset($this->errors['label'.$i])) $this->errors['label'.$i] = array(); + $this->errors['label'.$i][] = sprintf(__('You cannot provide more than label from the %s class to an issue.'), $class); + throw new Pluf_Form_Invalid(__('You provided an invalid label.')); + } + } + return $this->cleaned_data; + } + + /** + * Save the model in the database. + * + * @param bool Commit in the database or not. If not, the object + * is returned but not saved in the database. + * @return Object Model with data set from the form. + */ + function save($commit=true) + { + if (!$this->isValid()) { + throw new Exception(__('Cannot save the model from an invalid form.')); + } + // Add a tag for each label + $tags = array(); + for ($i=1;$i<4;$i++) { + if (strlen($this->cleaned_data['label'.$i]) > 0) { + if (strpos($this->cleaned_data['label'.$i], ':') !== false) { + list($class, $name) = explode(':', $this->cleaned_data['label'.$i], 2); + list($class, $name) = array(trim($class), trim($name)); + } else { + $class = 'Other'; + $name = trim($this->cleaned_data['label'.$i]); + } + $tags[] = IDF_Tag::add($name, $this->project, $class); + } + } + // Create the upload + $upload = new IDF_Upload(); + $upload->project = $this->project; + $upload->submitter = $this->user; + $upload->summary = trim($this->cleaned_data['summary']); + $upload->file = $this->cleaned_data['file']; + $upload->filesize = filesize(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->cleaned_data['file']); + $upload->downloads = 0; + $upload->create(); + foreach ($tags as $tag) { + $upload->setAssoc($tag); + } + return $upload; + } +} + diff --git a/src/IDF/Form/UploadConf.php b/src/IDF/Form/UploadConf.php new file mode 100644 index 0000000..bee8701 --- /dev/null +++ b/src/IDF/Form/UploadConf.php @@ -0,0 +1,70 @@ +fields['labels_download_predefined'] = new Pluf_Form_Field_Varchar( + array('required' => true, + 'label' => __('Predefined download labels'), + 'initial' => self::init_predefined, + 'widget_attrs' => array('rows' => 13, + 'cols' => 75), + 'widget' => 'Pluf_Form_Widget_TextareaInput', + )); + + $this->fields['labels_download_one_max'] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Each download may have at most one label with each of these classes'), + 'initial' => self::init_one_max, + 'widget_attrs' => array('size' => 60), + )); + + } +} + + diff --git a/src/IDF/Migrations/1Download.php b/src/IDF/Migrations/1Download.php new file mode 100644 index 0000000..dcda585 --- /dev/null +++ b/src/IDF/Migrations/1Download.php @@ -0,0 +1,52 @@ +model = new $model(); + $schema->createTables(); + } +} + +function IDF_Migrations_1Download_down($params=null) +{ + $models = array( + 'IDF_Upload', + ); + $db = Pluf::db(); + $schema = new Pluf_DB_Schema($db); + foreach ($models as $model) { + $schema->model = new $model(); + $schema->dropTables(); + } +} \ No newline at end of file diff --git a/src/IDF/Migrations/Install.php b/src/IDF/Migrations/Install.php index 4cc6d26..d255ed8 100644 --- a/src/IDF/Migrations/Install.php +++ b/src/IDF/Migrations/Install.php @@ -35,6 +35,7 @@ function IDF_Migrations_Install_setup($params=null) 'IDF_Issue', 'IDF_IssueComment', 'IDF_Conf', + 'IDF_Upload', ); $db = Pluf::db(); $schema = new Pluf_DB_Schema($db); @@ -64,6 +65,7 @@ function IDF_Migrations_Install_teardown($params=null) $perm = Pluf_Permission::getFromString('IDF.project-owner'); if ($perm) $perm->delete(); $models = array( + 'IDF_Upload', 'IDF_Conf', 'IDF_IssueComment', 'IDF_Issue', diff --git a/src/IDF/Precondition.php b/src/IDF/Precondition.php index 9fc1f76..fb07330 100644 --- a/src/IDF/Precondition.php +++ b/src/IDF/Precondition.php @@ -40,4 +40,25 @@ class IDF_Precondition } return new Pluf_HTTP_Response_Forbidden($request); } + + /** + * Check if the user is project owner or member. + * + * @param Pluf_HTTP_Request + * @return mixed + */ + static public function projectMemberOrOwner($request) + { + $res = Pluf_Precondition::loginRequired($request); + if (true !== $res) { + return $res; + } + if ($request->user->hasPerm('IDF.project-owner', $request->project) + or + $request->user->hasPerm('IDF.project-member', $request->project) + ) { + return true; + } + return new Pluf_HTTP_Response_Forbidden($request); + } } \ No newline at end of file diff --git a/src/IDF/Upload.php b/src/IDF/Upload.php new file mode 100644 index 0000000..2031fdd --- /dev/null +++ b/src/IDF/Upload.php @@ -0,0 +1,143 @@ +_a['table'] = 'idf_uploads'; + $this->_a['model'] = __CLASS__; + $this->_a['cols'] = array( + // It is mandatory to have an "id" column. + 'id' => + array( + 'type' => 'Pluf_DB_Field_Sequence', + 'blank' => true, + ), + 'project' => + array( + 'type' => 'Pluf_DB_Field_Foreignkey', + 'model' => 'IDF_Project', + 'blank' => false, + 'verbose' => __('project'), + 'relate_name' => 'issues', + ), + 'summary' => + array( + 'type' => 'Pluf_DB_Field_Varchar', + 'blank' => false, + 'size' => 250, + 'verbose' => __('summary'), + ), + 'file' => + array( + 'type' => 'Pluf_DB_Field_File', + 'blank' => false, + 'default' => 0, + 'verbose' => __('file'), + 'help_text' => __('The path is relative to the upload path.'), + ), + 'filesize' => + array( + 'type' => 'Pluf_DB_Field_Integer', + 'blank' => false, + 'default' => 0, + 'verbose' => __('file size in bytes'), + ), + 'submitter' => + array( + 'type' => 'Pluf_DB_Field_Foreignkey', + 'model' => 'Pluf_User', + 'blank' => false, + 'verbose' => __('submitter'), + 'relate_name' => 'submitted_issue', + ), + 'tags' => + array( + 'type' => 'Pluf_DB_Field_Manytomany', + 'blank' => true, + 'model' => 'IDF_Tag', + 'verbose' => __('labels'), + ), + 'downloads' => + array( + 'type' => 'Pluf_DB_Field_Integer', + 'blank' => false, + 'default' => 0, + 'verbose' => __('number of downloads'), + ), + 'creation_dtime' => + array( + 'type' => 'Pluf_DB_Field_Datetime', + 'blank' => true, + 'verbose' => __('creation date'), + ), + 'modif_dtime' => + array( + 'type' => 'Pluf_DB_Field_Datetime', + 'blank' => true, + 'verbose' => __('modification date'), + ), + ); + $this->_a['idx'] = array( + 'modif_dtime_idx' => + array( + 'col' => 'modif_dtime', + 'type' => 'normal', + ), + ); + $table = $this->_con->pfx.'idf_tag_idf_upload_assoc'; + $this->_a['views'] = array( + 'join_tags' => + array( + 'join' => 'LEFT JOIN '.$table + .' ON idf_upload_id=id', + ), + ); + } + + function __toString() + { + return $this->file; + } + + function _toIndex() + { + return ''; + } + + function preSave() + { + if ($this->id == '') { + $this->creation_dtime = gmdate('Y-m-d H:i:s'); + } + $this->modif_dtime = gmdate('Y-m-d H:i:s'); + } +} \ No newline at end of file diff --git a/src/IDF/Views/Download.php b/src/IDF/Views/Download.php new file mode 100644 index 0000000..acd3ecc --- /dev/null +++ b/src/IDF/Views/Download.php @@ -0,0 +1,200 @@ +project; + $title = sprintf(__('%s Downloads'), (string) $prj); + // Paginator to paginate the files to download. + $pag = new Pluf_Paginator(new IDF_Upload()); + $pag->class = 'recent-issues'; + $pag->item_extra_props = array('project_m' => $prj, + 'shortname' => $prj->shortname); + $pag->summary = __('This table shows the files to download.'); + $pag->action = array('IDF_Views_Download::index', array($prj->shortname)); + $pag->edit_action = array('IDF_Views_Download::view', 'shortname', 'id'); + $list_display = array( + 'file' => __('File'), + array('summary', 'IDF_Views_Download_SummaryAndLabels', __('Summary')), + array('filesize', 'IDF_Views_Download_Size', __('Size')), + array('modif_dtime', 'Pluf_Paginator_DateYMD', __('Uploaded')), + ); + $pag->configure($list_display, array(), array('file', 'filesize', 'modif_dtime')); + $pag->items_per_page = 10; + $pag->no_results_text = __('No downloads were found.'); + $pag->sort_order = array('file', 'ASC'); + $pag->setFromRequest($request); + return Pluf_Shortcuts_RenderToResponse('downloads/index.html', + array('project' => $prj, + 'page_title' => $title, + 'downloads' => $pag, + ), + $request); + + } + + /** + * View details of a file. + */ + public function view($request, $match) + { + $prj = $request->project; + $upload = Pluf_Shortcuts_GetObjectOr404('IDF_Upload', $match[2]); + $title = sprintf(__('Download %s'), $upload->summary); + $form = false; + if ($request->method == 'POST' and + true === IDF_Precondition::projectMemberOrOwner($request)) { + + $form = new IDF_Form_UpdateUpload($request->POST, + array('project' => $prj, + 'upload' => $upload, + 'user' => $request->user)); + if ($form->isValid()) { + $upload = $form->save(); + $urlfile = Pluf_HTTP_URL_urlForView('IDF_Views_Download::view', + array($prj->shortname, $upload->id)); + $request->user->setMessage(sprintf(__('The file %2$s has been updated.'), $urlfile, Pluf_esc($upload->file))); + $url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index', + array($prj->shortname)); + return new Pluf_HTTP_Response_Redirect($url); + } + } elseif (true === IDF_Precondition::projectMemberOrOwner($request)) { + $form = new IDF_Form_UpdateUpload(null, + array('upload' => $upload, + 'project' => $prj, + 'user' => $request->user)); + } + return Pluf_Shortcuts_RenderToResponse('downloads/view.html', + array( + 'file' => $upload, + 'auto_labels' => self::autoCompleteArrays($prj), + 'page_title' => $title, + 'form' => $form, + ), + $request); + } + + /** + * Submit a new file for download. + */ + public $submit_precond = array('IDF_Precondition::projectMemberOrOwner'); + public function submit($request, $match) + { + $prj = $request->project; + $title = __('New Download'); + if ($request->method == 'POST') { + $form = new IDF_Form_Upload(array_merge($request->POST, $request->FILES), + array('project' => $prj, + 'user' => $request->user)); + if ($form->isValid()) { + $upload = $form->save(); + $urlfile = Pluf_HTTP_URL_urlForView('IDF_Views_Download::view', + array($prj->shortname, $upload->id)); + $request->user->setMessage(sprintf(__('The file has been uploaded.'), $urlfile)); + $url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index', + array($prj->shortname)); + return new Pluf_HTTP_Response_Redirect($url); + } + } else { + $form = new IDF_Form_Upload(null, + array('project' => $prj, + 'user' => $request->user)); + } + return Pluf_Shortcuts_RenderToResponse('downloads/submit.html', + array( + 'auto_labels' => self::autoCompleteArrays($prj), + 'page_title' => $title, + 'form' => $form, + ), + $request); + } + + /** + * Create the autocomplete arrays for the little AJAX stuff. + */ + public static function autoCompleteArrays($project) + { + $conf = new IDF_Conf(); + $conf->setProject($project); + $st = preg_split("/\015\012|\015|\012/", + $conf->getVal('labels_downloads_predefined', IDF_Form_UploadConf::init_predefined), -1, PREG_SPLIT_NO_EMPTY); + $auto = ''; + foreach ($st as $s) { + $v = ''; + $d = ''; + $_s = split('=', $s, 2); + if (count($_s) > 1) { + $v = trim($_s[0]); + $d = trim($_s[1]); + } else { + $v = trim($_s[0]); + } + $auto .= sprintf('{ name: "%s", to: "%s" }, ', + Pluf_esc($d), Pluf_esc($v)); + } + return substr($auto, 0, -1); + } +} + +/** + * Display the summary of a download, then on a new line, display the + * list of labels. + * + * The summary of the download is linking to the download. + */ +function IDF_Views_Download_SummaryAndLabels($field, $down, $extra='') +{ + //$edit = Pluf_HTTP_URL_urlForView('IDF_Views_Download::view', + // array($down->shortname, $down->id)); + $tags = array(); + foreach ($down->get_tags_list() as $tag) { + $tags[] = sprintf('%s', Pluf_esc((string) $tag)); + } + $out = ''; + if (count($tags)) { + $out = '
'.implode(', ', $tags).''; + } + return Pluf_esc($down->summary).$out; +} + +function IDF_Views_Download_Size($field, $down) +{ + return Pluf_Utils::prettySize($down->$field); +} \ No newline at end of file diff --git a/src/IDF/Views/Project.php b/src/IDF/Views/Project.php index 32180eb..ef6ac62 100644 --- a/src/IDF/Views/Project.php +++ b/src/IDF/Views/Project.php @@ -82,7 +82,7 @@ class IDF_Views_Project /** * Administrate the issue tracking of a project. */ - public $adminIssueTracking_precond = array('IDF_Precondition::projectOwner'); + public $adminIssues_precond = array('IDF_Precondition::projectOwner'); public function adminIssues($request, $match) { $prj = $request->project; @@ -123,6 +123,49 @@ class IDF_Views_Project $request); } + /** + * Administrate the downloads of a project. + */ + public $adminDownloads_precond = array('IDF_Precondition::projectOwner'); + public function adminDownloads($request, $match) + { + $prj = $request->project; + $title = sprintf(__('%s Downloads Configuration'), (string) $prj); + $conf = new IDF_Conf(); + $conf->setProject($prj); + if ($request->method == 'POST') { + $form = new IDF_Form_UploadConf($request->POST); + if ($form->isValid()) { + foreach ($form->cleaned_data as $key=>$val) { + $conf->setVal($key, $val); + } + $request->user->setMessage(__('The downloads configuration has been saved.')); + $url = Pluf_HTTP_URL_urlForView('IDF_Views_Project::adminDownloads', + array($prj->shortname)); + return new Pluf_HTTP_Response_Redirect($url); + } + } else { + $params = array(); + $keys = array('labels_download_predefined', 'labels_download_one_max'); + foreach ($keys as $key) { + $_val = $conf->getVal($key, false); + if ($_val !== false) { + $params[$key] = $_val; + } + } + if (count($params) == 0) { + $params = null; //Nothing in the db, so new form. + } + $form = new IDF_Form_UploadConf($params); + } + return Pluf_Shortcuts_RenderToResponse('admin/downloads.html', + array( + 'page_title' => $title, + 'form' => $form, + ), + $request); + } + /** * Administrate the members of a project. */ diff --git a/src/IDF/conf/views.php b/src/IDF/conf/views.php index d47cc6a..10741f2 100644 --- a/src/IDF/conf/views.php +++ b/src/IDF/conf/views.php @@ -147,6 +147,24 @@ $ctl[] = array('regex' => '#^/p/(\w+)/source/download/(\w+)/$#', 'model' => 'IDF_Views_Source', 'method' => 'download'); +$ctl[] = array('regex' => '#^/p/(\w+)/downloads/$#', + 'base' => $base, + 'priority' => 4, + 'model' => 'IDF_Views_Download', + 'method' => 'index'); + +$ctl[] = array('regex' => '#^/p/(\w+)/downloads/(\d+)/$#', + 'base' => $base, + 'priority' => 4, + 'model' => 'IDF_Views_Download', + 'method' => 'view'); + +$ctl[] = array('regex' => '#^/p/(\w+)/downloads/create/$#', + 'base' => $base, + 'priority' => 4, + 'model' => 'IDF_Views_Download', + 'method' => 'submit'); + // ---------- ADMIN -------------------------------------- @@ -162,6 +180,12 @@ $ctl[] = array('regex' => '#^/p/(\w+)/admin/issues/$#', 'model' => 'IDF_Views_Project', 'method' => 'adminIssues'); +$ctl[] = array('regex' => '#^/p/(\w+)/admin/downloads/$#', + 'base' => $base, + 'priority' => 4, + 'model' => 'IDF_Views_Project', + 'method' => 'adminDownloads'); + $ctl[] = array('regex' => '#^/p/(\w+)/admin/members/$#', 'base' => $base, 'priority' => 4, diff --git a/src/IDF/templates/admin/base.html b/src/IDF/templates/admin/base.html index 3670329..0cae4d8 100644 --- a/src/IDF/templates/admin/base.html +++ b/src/IDF/templates/admin/base.html @@ -4,6 +4,7 @@
{trans 'Project Summary'} | {trans 'Project Members'} | -{trans 'Issue Tracking'} +{trans 'Issue Tracking'} | +{trans 'Downloads'}
{/block} diff --git a/src/IDF/templates/admin/downloads.html b/src/IDF/templates/admin/downloads.html new file mode 100644 index 0000000..3c2c1b5 --- /dev/null +++ b/src/IDF/templates/admin/downloads.html @@ -0,0 +1,34 @@ +{extends "admin/base.html"} +{block docclass}yui-t1{assign $inDownloads = true}{/block} +{block body} +
+ + + + + + + + + + +
{$form.f.labels_download_predefined.labelTag}:
+{if $form.f.labels_download_predefined.errors}{$form.f.labels_download_predefined.fieldErrors}{/if} +{$form.f.labels_download_predefined|unsafe} +
{$form.f.labels_download_one_max.labelTag}:
+{if $form.f.labels_download_one_max.errors}{$form.f.labels_download_one_max.fieldErrors}{/if} +{$form.f.labels_download_one_max|unsafe} +
+ +
+
+{/block} +{block context} +
+{blocktrans} +

Instructions:

+

List one status value per line in desired sort-order.

+

Optionally, use an equals-sign to document the meaning of each status value.

+{/blocktrans} +
+{/block} diff --git a/src/IDF/templates/base.html b/src/IDF/templates/base.html index 7a2ba69..3aaebd6 100644 --- a/src/IDF/templates/base.html +++ b/src/IDF/templates/base.html @@ -43,6 +43,7 @@ {if $project} {trans 'Project Home'} {trans 'Issues'} + {trans 'Downloads'} {trans 'Source'} {if $isOwner} {trans 'Administer'}{/if}{/if} diff --git a/src/IDF/templates/downloads/base.html b/src/IDF/templates/downloads/base.html new file mode 100644 index 0000000..b8dbe87 --- /dev/null +++ b/src/IDF/templates/downloads/base.html @@ -0,0 +1,8 @@ +{extends "base.html"} +{block tabdownloads} class="active"{/block} +{block subtabs} +
+{trans 'Downloads'} {if $isOwner or $isMember}| {trans 'New Download'} {/if} +{superblock} +
+{/block} diff --git a/src/IDF/templates/downloads/index.html b/src/IDF/templates/downloads/index.html new file mode 100644 index 0000000..4ac7e82 --- /dev/null +++ b/src/IDF/templates/downloads/index.html @@ -0,0 +1,13 @@ +{extends "downloads/base.html"} +{block docclass}yui-t1{assign $inDownloads=true}{/block} +{block body} +{$downloads.render} +{if $isOwner or $isMember} +{aurl 'url', 'IDF_Views_Download::submit', array($project.shortname)} +

+ {trans 'New Download'}

{/if} + +{/block} +{block context} +

{trans 'Number of files:'} {$downloads.nb_items}

+{/block} + diff --git a/src/IDF/templates/downloads/js-autocomplete.html b/src/IDF/templates/downloads/js-autocomplete.html new file mode 100644 index 0000000..0d00d55 --- /dev/null +++ b/src/IDF/templates/downloads/js-autocomplete.html @@ -0,0 +1,27 @@ +{if $isOwner or $isMember} + + + +{/if} diff --git a/src/IDF/templates/downloads/submit.html b/src/IDF/templates/downloads/submit.html new file mode 100644 index 0000000..663b39a --- /dev/null +++ b/src/IDF/templates/downloads/submit.html @@ -0,0 +1,57 @@ +{extends "downloads/base.html"} +{block docclass}yui-t3{assign $inSubmit=true}{/block} +{block body} +{if $form.errors} +
+

{trans 'The form contains some errors. Please correct them to submit the file.'}

+{if $form.get_top_errors} +{$form.render_top_errors|unsafe} +{/if} +
+{/if} + +
+ + + + + + + + + + + + + + + + + +
{$form.f.summary.labelTag}:{if $form.f.summary.errors}{$form.f.summary.fieldErrors}{/if} +{$form.f.summary|unsafe} +
{$form.f.file.labelTag}:{if $form.f.file.errors}{$form.f.file.fieldErrors}{/if} +{$form.f.file|unsafe} +
{$form.f.label1.labelTag}: +{if $form.f.label1.errors}{$form.f.label1.fieldErrors}{/if}{$form.f.label1|unsafe} +{if $form.f.label2.errors}{$form.f.label2.fieldErrors}{/if}{$form.f.label2|unsafe} +{if $form.f.label3.errors}{$form.f.label3.fieldErrors}{/if}{$form.f.label3|unsafe} +
  | {trans 'Cancel'} +
+
+{/block} +{block context} +
+

{trans 'Instructions'}

+ +

{blocktrans}Each file must have a distinct name and file contents +cannot be changed, so be sure to include release numbers in each file +name.{/blocktrans}

+
+{/block} +{block javascript} + +{include 'downloads/js-autocomplete.html'}{/block} + diff --git a/src/IDF/templates/downloads/view.html b/src/IDF/templates/downloads/view.html new file mode 100644 index 0000000..0a49126 --- /dev/null +++ b/src/IDF/templates/downloads/view.html @@ -0,0 +1,63 @@ +{extends "downloads/base.html"} +{block docclass}yui-t3{assign $inDownloads=true}{/block} +{block body} + +
+{$file} - {$file.filesize|size} +
+ + +{if $form} +{if $form.errors} +
+

{trans 'The form contains some errors. Please correct them to update the file.'}

+{if $form.get_top_errors} +{$form.render_top_errors|unsafe} +{/if} +
+{/if} + +
+ + + + + + + + + + + + + +
{$form.f.summary.labelTag}:{if $form.f.summary.errors}{$form.f.summary.fieldErrors}{/if} +{$form.f.summary|unsafe} +
{$form.f.label1.labelTag}: +{if $form.f.label1.errors}{$form.f.label1.fieldErrors}{/if}{$form.f.label1|unsafe} +{if $form.f.label2.errors}{$form.f.label2.fieldErrors}{/if}{$form.f.label2|unsafe} +{if $form.f.label3.errors}{$form.f.label3.fieldErrors}{/if}{$form.f.label3|unsafe} +
  | {trans 'Cancel'} +
+
+{/if} +{/block} +{block context} +{assign $submitter = $file.get_submitter()} +

{trans 'Uploaded:'} {$file.creation_dtime|dateago} {blocktrans}by {$submitter}{/blocktrans}

+

+{trans 'Updated:'} {$file.modif_dtime|dateago}

+{assign $tags = $file.get_tags_list()}{if $tags.count()} +

+{trans 'Labels:'}
+{foreach $tags as $tag} +{$tag.class}:{$tag.name}
+{/foreach} +

{/if} +{/block} +{block javascript}{if $form} + +{include 'downloads/js-autocomplete.html'}{/if}{/block} + diff --git a/src/IDF/templates/issues/view.html b/src/IDF/templates/issues/view.html index 8477084..4e57081 100644 --- a/src/IDF/templates/issues/view.html +++ b/src/IDF/templates/issues/view.html @@ -101,7 +101,7 @@

{assign $tags = $issue.get_tags_list()}{if $tags.count()}

{trans 'Labels:'}
-{foreach $issue.get_tags_list() as $tag}{aurl 'url', 'IDF_Views_Issue::listLabel', array($project.shortname, $tag.id, 'open')} +{foreach $tags as $tag}{aurl 'url', 'IDF_Views_Issue::listLabel', array($project.shortname, $tag.id, 'open')} {$tag.class}:{$tag.name}
{/foreach}

{/if} diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css index f8c7563..89c37ba 100644 --- a/www/media/idf/css/style.css +++ b/www/media/idf/css/style.css @@ -444,3 +444,20 @@ table.diff tr.diff-next { table.diff tr.diff-next td { padding: 1px 5px; } + +/** + * Download + */ +div.download-file { + padding: 1em 1em 1em 3em; + background: url("../img/down-large.png"); + background-repeat: no-repeat; + background-position: 1em 1em; + font-size: 140%; + margin-bottom: 3em; + background-color: #bbe394; + width: 40%; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + +} diff --git a/www/media/idf/img/down-large.png b/www/media/idf/img/down-large.png new file mode 100644 index 0000000000000000000000000000000000000000..af237881737d5135e8e8422acb00c75bf0682c58 GIT binary patch literal 874 zcmV-w1C{)VP)b zluv9FRUF4Z@6AkiR?`^3q=kcRIEa9rOf+pFX*408I6yEl9E>rfUNj~&C3;1pG!{>C zRudZpBQ~)nM3E3^i6RM6G%A%1Aqd7&s<7SJd4GN$c6K{mc3U~{l3(8M&Ai{|_uhAY zzfmH>{TTYbP=T=Bi-D2o2A-~Eqcwj`jhgOz0^{j=d*~yqH#l#xKE-*9^9Jh;sY^I? z_^q~y+k*j6oc`koRl7jdE|9vU;Xk)=&kC2h$DvUFYu^fBL0GIBk=k0svr;uJ3tohH z1Ydh1f^pRqPu$RW%Sd!mJcEslp-M!2EaLHaywogp6%t!+>=_u27LTquH8pAmmw>&K zv7YWnpWd_m&0-itII*$w7B3dGM~8y<2?@7|3K|{2E4$u^MZlOU6DP(OW^es@u+CF? ztprS@z?B?XuR|}V~&+MNb zHSf06cVxO13Kb>zMdVFu3ol3{eJG}%Dj8A znWI{j&6a(tQ>o~}_Ge${4IirLi}S|`LX8d;O2Ol+wo#eWGBJ7dx-<5P>4P%Y%xGC| zXAa11ZOq{0iKG7%+$uJ#=_l+^LmGcpW`)fh{(QV#P0h|T2a?~~#kZ6qKL$p^ovS<7e7yVlmmUZr#V4PASiU)X zbKmLF^n>N%mVlK~3J7wh0p0gnu=kPP-r=C2RB5JkGPrVwv69l^J{2?=xrkD(9GpGejHe0~d!BGy_>^HAX4r1D@(|SwSuh9HYtY1@S zmi1jjg60#aKq?~su8-GF=ghAstHWImE|P=&2Oa>1Tp;d96#xJL07*qoM6N<$g3Ao0 Am;e9( literal 0 HcmV?d00001