diff --git a/src/IDF/Form/Admin/ProjectCreate.php b/src/IDF/Form/Admin/ProjectCreate.php index 21d3257..afe31b4 100644 --- a/src/IDF/Form/Admin/ProjectCreate.php +++ b/src/IDF/Form/Admin/ProjectCreate.php @@ -134,6 +134,18 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form 'widget' => 'Pluf_Form_Widget_TextareaInput', )); + for ($i=1;$i<7;$i++) { + $this->fields['label'.$i] = new Pluf_Form_Field_Varchar( + array('required' => false, + 'label' => __('Labels'), + 'initial' => '', + 'widget_attrs' => array( + 'maxlength' => 50, + 'size' => 20, + ), + )); + } + $projects = array('--' => '--'); foreach (Pluf::factory('IDF_Project')->getList(array('order' => 'name ASC')) as $proj) { $projects[$proj->name] = $proj->shortname; @@ -290,11 +302,29 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form if (!$this->isValid()) { throw new Exception(__('Cannot save the model from an invalid form.')); } + + // Add a tag for each label + $tagids = array(); + for ($i=1;$i<7;$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::addGlobal($name, $class); + $tagids[] = $tag->id; + } + } + $project = new IDF_Project(); $project->name = $this->cleaned_data['name']; $project->shortname = $this->cleaned_data['shortname']; $project->shortdesc = $this->cleaned_data['shortdesc']; + $tagids = array(); if ($this->cleaned_data['template'] != '--') { // Find the template project $sql = new Pluf_SQL('shortname=%s', @@ -302,11 +332,32 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form $tmpl = Pluf::factory('IDF_Project')->getOne(array('filter' => $sql->gen())); $project->private = $tmpl->private; $project->description = $tmpl->description; + + foreach ($tmpl->get_tags_list() as $tag) { + $tagids[] = $tag->id; + } } else { $project->private = $this->cleaned_data['private_project']; $project->description = __('Click on the Project Management tab to set the description of your project.'); + + // Add a tag for each label + for ($i=1;$i<7;$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::addGlobal($name, $class); + $tagids[] = $tag->id; + } + } } $project->create(); + $project->batchAssoc('IDF_Tag', $tagids); + $conf = new IDF_Conf(); $conf->setProject($project); $keys = array('scm', 'svn_remote_url', 'svn_username', diff --git a/src/IDF/Form/Admin/ProjectUpdate.php b/src/IDF/Form/Admin/ProjectUpdate.php index c7cdc07..f579381 100644 --- a/src/IDF/Form/Admin/ProjectUpdate.php +++ b/src/IDF/Form/Admin/ProjectUpdate.php @@ -70,6 +70,26 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form )); } + $tags = $this->project->get_tags_list(); + for ($i=1;$i<7;$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, + ), + )); + } $this->fields['owners'] = new Pluf_Form_Field_Varchar( array('required' => false, 'label' => __('Project owners'), @@ -132,9 +152,28 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form if (!$this->isValid()) { throw new Exception(__('Cannot save the model from an invalid form.')); } + + // Add a tag for each label + $tagids = array(); + for ($i=1;$i<7;$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::addGlobal($name, $class); + $tagids[] = $tag->id; + } + } + $this->project->batchAssoc('IDF_Tag', $tagids); + IDF_Form_MembersConf::updateMemberships($this->project, $this->cleaned_data); $this->project->membershipsUpdated(); + $this->project->name = $this->cleaned_data['name']; $this->project->shortdesc = $this->cleaned_data['shortdesc']; $this->project->update(); diff --git a/src/IDF/Form/ProjectConf.php b/src/IDF/Form/ProjectConf.php index f66f842..44f006a 100644 --- a/src/IDF/Form/ProjectConf.php +++ b/src/IDF/Form/ProjectConf.php @@ -58,6 +58,27 @@ class IDF_Form_ProjectConf extends Pluf_Form 'initial' => $conf->getVal('external_project_url'), )); + $tags = $this->project->get_tags_list(); + for ($i=1;$i<7;$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, + ), + )); + } + // Logo part $upload_path = Pluf::f('upload_path', false); if (false === $upload_path) { @@ -148,10 +169,27 @@ class IDF_Form_ProjectConf extends Pluf_Form public function save($commit=true) { + // Add a tag for each label + $tagids = array(); + for ($i=1;$i<7;$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::addGlobal($name, $class); + $tagids[] = $tag->id; + } + } + // Basic part $this->project->name = $this->cleaned_data['name']; $this->project->shortdesc = $this->cleaned_data['shortdesc']; $this->project->description = $this->cleaned_data['description']; + $this->project->batchAssoc('IDF_Tag', $tagids); $this->project->update(); $conf = $this->project->getConf(); diff --git a/src/IDF/Migrations/22ProjectTagRelationTable.php b/src/IDF/Migrations/22ProjectTagRelationTable.php new file mode 100644 index 0000000..5113af1 --- /dev/null +++ b/src/IDF/Migrations/22ProjectTagRelationTable.php @@ -0,0 +1,60 @@ +pfx.'idf_project_idf_tag_assoc'; + if (!in_array($db->engine, array('MySQL', 'PostgreSQL'))) { + throw new Exception('unsupported engine '.$engine); + } + + $intro = new Pluf_DB_Introspect($db); + if (in_array($table, $intro->listTables())) { + echo '21 skipping up migration - table already exists'."\n"; + return; + } + + $schema = Pluf::factory('Pluf_DB_Schema_'.$db->engine, $db); + $sql = $schema->getSqlCreate(new IDF_Project()); + $db->execute($sql[$table]); +} + +function IDF_Migrations_22ProjectTagRelationTable_down($params=null) +{ + $db = Pluf::db(); + $table = $db->pfx.'idf_project_idf_tag_assoc'; + if (!in_array($db->engine, array('MySQL', 'PostgreSQL'))) { + throw new Exception('unsupported engine '.$engine); + } + + $intro = new Pluf_DB_Introspect($db); + if (!in_array($table, $intro->listTables())) { + echo '22 skipping down migration - table does not exist'."\n"; + return; + } + + $schema = Pluf::factory('Pluf_DB_Schema_'.$db->engine, $db); + $sql = $schema->getSqlDelete(new IDF_Project()); + $db->execute($sql[$table]); +} diff --git a/src/IDF/Project.php b/src/IDF/Project.php index 3db4ed8..ef1d592 100644 --- a/src/IDF/Project.php +++ b/src/IDF/Project.php @@ -86,6 +86,13 @@ class IDF_Project extends Pluf_Model 'verbose' => __('description'), 'help_text' => __('The description can be extended using the Markdown syntax.'), ), + 'tags' => + array( + 'type' => 'Pluf_DB_Field_Manytomany', + 'blank' => true, + 'model' => 'IDF_Tag', + 'verbose' => __('labels'), + ), 'private' => array( 'type' => 'Pluf_DB_Field_Integer', diff --git a/src/IDF/Tag.php b/src/IDF/Tag.php index 59026c2..24441a6 100644 --- a/src/IDF/Tag.php +++ b/src/IDF/Tag.php @@ -42,16 +42,16 @@ class IDF_Tag extends Pluf_Model array( 'type' => 'Pluf_DB_Field_Sequence', //It is automatically added. - 'blank' => true, + 'blank' => true, ), - 'project' => + 'project' => array( 'type' => 'Pluf_DB_Field_Foreignkey', 'model' => 'IDF_Project', 'blank' => false, 'verbose' => __('project'), ), - 'class' => + 'class' => array( 'type' => 'Pluf_DB_Field_Varchar', 'blank' => false, @@ -59,13 +59,13 @@ class IDF_Tag extends Pluf_Model 'verbose' => __('tag class'), 'help_text' => __('The class of the tag.'), ), - 'name' => + 'name' => array( 'type' => 'Pluf_DB_Field_Varchar', 'blank' => false, 'verbose' => __('name'), ), - 'lcname' => + 'lcname' => array( 'type' => 'Pluf_DB_Field_Varchar', 'blank' => false, @@ -95,7 +95,7 @@ class IDF_Tag extends Pluf_Model } /** - * Add a tag if not already existing. + * Add a project-specific tag if not already existing. * * @param string Name of the tag. * @param IDF_Project Project of the tag. @@ -107,7 +107,7 @@ class IDF_Tag extends Pluf_Model $class = trim($class); $name = trim($name); $gtag = new IDF_Tag(); - $sql = new Pluf_SQL('class=%s AND lcname=%s AND project=%s', + $sql = new Pluf_SQL('class=%s AND lcname=%s AND project=%s', array($class, mb_strtolower($name), $project->id)); $tags = $gtag->getList(array('filter' => $sql->gen())); if ($tags->count() < 1) { @@ -122,6 +122,32 @@ class IDF_Tag extends Pluf_Model return $tags[0]; } + /** + * Add a global tag if not already existing + * + * @param string Name of the tag. + * @param string Class of the tag (IDF_TAG_DEFAULT_CLASS) + * @return IDF_Tag The tag. + */ + public static function addGlobal($name, $class=IDF_TAG_DEFAULT_CLASS) + { + $class = trim($class); + $name = trim($name); + $gtag = new IDF_Tag(); + $sql = new Pluf_SQL('class=%s AND lcname=%s AND project=0', + array($class, mb_strtolower($name))); + $tags = $gtag->getList(array('filter' => $sql->gen())); + if ($tags->count() < 1) { + // create a new tag + $tag = new IDF_Tag(); + $tag->name = $name; + $tag->class = $class; + $tag->create(); + return $tag; + } + return $tags[0]; + } + function __toString() { if ($this->class != IDF_TAG_DEFAULT_CLASS) { diff --git a/src/IDF/Views/Admin.php b/src/IDF/Views/Admin.php index 9faae89..8176fd5 100644 --- a/src/IDF/Views/Admin.php +++ b/src/IDF/Views/Admin.php @@ -140,12 +140,16 @@ class IDF_Views_Admin } else { $form = new IDF_Form_Admin_ProjectUpdate(null, $params); } + $arrays = IDF_Views_Project::autoCompleteArrays(); return Pluf_Shortcuts_RenderToResponse('idf/gadmin/projects/update.html', - array( - 'page_title' => $title, - 'project' => $project, - 'form' => $form, - ), + array_merge( + array( + 'page_title' => $title, + 'project' => $project, + 'form' => $form, + ), + $arrays + ), $request); } @@ -173,12 +177,17 @@ class IDF_Views_Admin $form = new IDF_Form_Admin_ProjectCreate(null, $extra); } $base = Pluf::f('url_base').Pluf::f('idf_base').'/p/'; + + $arrays = IDF_Views_Project::autoCompleteArrays(); return Pluf_Shortcuts_RenderToResponse('idf/gadmin/projects/create.html', - array( - 'page_title' => $title, - 'form' => $form, - 'base_url' => $base, - ), + array_merge( + array( + 'page_title' => $title, + 'form' => $form, + 'base_url' => $base, + ), + $arrays + ), $request); } diff --git a/src/IDF/Views/Project.php b/src/IDF/Views/Project.php index e4a0dc8..e83fc93 100644 --- a/src/IDF/Views/Project.php +++ b/src/IDF/Views/Project.php @@ -311,13 +311,17 @@ class IDF_Views_Project } $logo = $prj->getConf()->getVal('logo'); + $arrays = self::autoCompleteArrays(); return Pluf_Shortcuts_RenderToResponse('idf/admin/summary.html', - array( - 'page_title' => $title, - 'form' => $form, - 'project' => $prj, - 'logo' => $logo, - ), + array_merge( + array( + 'page_title' => $title, + 'form' => $form, + 'project' => $prj, + 'logo' => $logo, + ), + $arrays + ), $request); } @@ -616,4 +620,36 @@ class IDF_Views_Project ), $request); } + + /** + * Create the autocomplete arrays for the little AJAX stuff. + */ + public static function autoCompleteArrays() + { + $forge = IDF_Forge::instance(); + $labels = $forge->getProjectLabels(IDF_Form_Admin_LabelConf::init_project_labels); + + $auto = array('auto_labels' => ''); + $auto_raw = array('auto_labels' => $labels); + foreach ($auto_raw as $key => $st) { + $st = preg_split("/\015\012|\015|\012/", $st, -1, PREG_SPLIT_NO_EMPTY); + foreach ($st as $s) { + $v = ''; + $d = ''; + $_s = explode('=', $s, 2); + if (count($_s) > 1) { + $v = trim($_s[0]); + $d = trim($_s[1]); + } else { + $v = trim($_s[0]); + } + $auto[$key] .= sprintf('{ name: "%s", to: "%s" }, ', + Pluf_esc($d), + Pluf_esc($v)); + } + $auto[$key] = substr($auto[$key], 0, -2); + } + + return $auto; + } } diff --git a/src/IDF/relations.php b/src/IDF/relations.php index 797a90c..221bd81 100644 --- a/src/IDF/relations.php +++ b/src/IDF/relations.php @@ -22,7 +22,8 @@ # ***** END LICENSE BLOCK ***** */ $m = array(); -$m['IDF_Tag'] = array('relate_to' => array('IDF_Project')); +$m['IDF_Tag'] = array('relate_to' => array('IDF_Project'), + 'relate_to_many' => array('IDF_Project')); $m['IDF_Issue'] = array('relate_to' => array('IDF_Project', 'Pluf_User', 'IDF_Tag'), 'relate_to_many' => array('IDF_Tag', 'Pluf_User')); $m['IDF_IssueComment'] = array('relate_to' => array('IDF_Issue', 'Pluf_User')); diff --git a/src/IDF/templates/idf/admin/summary.html b/src/IDF/templates/idf/admin/summary.html index c75391d..2caf786 100644 --- a/src/IDF/templates/idf/admin/summary.html +++ b/src/IDF/templates/idf/admin/summary.html @@ -37,6 +37,17 @@ +{$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}
+{if $form.f.label4.errors}{$form.f.label4.fieldErrors}{/if}{$form.f.label4|unsafe} +{if $form.f.label5.errors}{$form.f.label5.fieldErrors}{/if}{$form.f.label5|unsafe} +{if $form.f.label6.errors}{$form.f.label6.fieldErrors}{/if}{$form.f.label6|unsafe} + + + {trans 'Current logo'}: {if $logo} @@ -66,6 +77,7 @@ +{include 'idf/project/js-autocomplete.html'}{/block} {/block} {block context} diff --git a/src/IDF/templates/idf/gadmin/projects/create.html b/src/IDF/templates/idf/gadmin/projects/create.html index 67b7798..fb9c85a 100644 --- a/src/IDF/templates/idf/gadmin/projects/create.html +++ b/src/IDF/templates/idf/gadmin/projects/create.html @@ -37,7 +37,7 @@ -{$form.f.external_project_url.labelTag}: +{$form.f.external_project_url.labelTag}: {if $form.f.external_project_url.errors}{$form.f.external_project_url.fieldErrors}{/if} {$form.f.external_project_url|unsafe} @@ -81,6 +81,17 @@ +{$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} +{if $form.f.label4.errors}{$form.f.label4.fieldErrors}{/if}{$form.f.label4|unsafe}
+{if $form.f.label5.errors}{$form.f.label5.fieldErrors}{/if}{$form.f.label5|unsafe} +{if $form.f.label6.errors}{$form.f.label6.fieldErrors}{/if}{$form.f.label6|unsafe} + + + {$form.f.owners.labelTag}: {if $form.f.owners.errors}{$form.f.owners.fieldErrors}{/if} @@ -109,7 +120,7 @@ - +{include 'idf/project/js-autocomplete.html'}{/block} {/block} {block context} @@ -157,7 +168,7 @@ $(document).ready(function() { } }); - // Hide if not svn + // Hide if not templated if ($("#id_template option:selected").val() == "--") { $(".no-template").show(); } else { diff --git a/src/IDF/templates/idf/gadmin/projects/update.html b/src/IDF/templates/idf/gadmin/projects/update.html index 0a64e56..8479453 100644 --- a/src/IDF/templates/idf/gadmin/projects/update.html +++ b/src/IDF/templates/idf/gadmin/projects/update.html @@ -26,7 +26,7 @@ -{$form.f.external_project_url.labelTag}: +{$form.f.external_project_url.labelTag}: {if $form.f.external_project_url.errors}{$form.f.external_project_url.fieldErrors}{/if} {$form.f.external_project_url|unsafe} @@ -41,6 +41,17 @@ {/if} +{$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} +{if $form.f.label4.errors}{$form.f.label4.fieldErrors}{/if}{$form.f.label4|unsafe}
+{if $form.f.label5.errors}{$form.f.label5.fieldErrors}{/if}{$form.f.label5|unsafe} +{if $form.f.label6.errors}{$form.f.label6.fieldErrors}{/if}{$form.f.label6|unsafe} + + + {$form.f.owners.labelTag}: {if $form.f.owners.errors}{$form.f.owners.fieldErrors}{/if} @@ -66,7 +77,9 @@ +{include 'idf/project/js-autocomplete.html'}{/block} {/block} + {block context}
{blocktrans} diff --git a/src/IDF/templates/idf/project/js-autocomplete.html b/src/IDF/templates/idf/project/js-autocomplete.html new file mode 100644 index 0000000..1799f16 --- /dev/null +++ b/src/IDF/templates/idf/project/js-autocomplete.html @@ -0,0 +1,25 @@ + + +