From 6caef4c9f0cb14426e4695abbca0c749cd7e7d03 Mon Sep 17 00:00:00 2001
From: Pradeep CE (cep) <breathingcode@gmail.com>
Date: Jan 17 2017 19:02:42 +0000
Subject: [PATCH 1/9] Add Description for Tags


---

diff --git a/pagure/forms.py b/pagure/forms.py
index 576896f..bb57357 100644
--- a/pagure/forms.py
+++ b/pagure/forms.py
@@ -239,6 +239,10 @@ class DeleteIssueTagForm(PagureForm):
 
 class AddIssueTagForm(DeleteIssueTagForm):
     ''' Form to add a tag to a project. '''
+    tag_description = wtforms.TextField(
+        'tag_description',
+        [wtforms.validators.Optional()],
+    )
     tag_color = wtforms.TextField(
         'tag_color',
         [wtforms.validators.Required()],
@@ -634,4 +638,3 @@ class SubscribtionForm(PagureForm):
         [wtforms.validators.optional()],
         false_values=('false', '', False, 'False', 0, '0'),
     )
-
diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py
index 65c61bf..09b95f9 100644
--- a/pagure/lib/__init__.py
+++ b/pagure/lib/__init__.py
@@ -736,7 +736,7 @@ def remove_tags_obj(session, obj, tags, ticketfolder, user):
 
 
 def edit_issue_tags(
-        session, project, old_tag, new_tag,
+        session, project, old_tag, new_tag, new_tag_description,
         new_tag_color, ticketfolder, user):
     ''' Removes the specified tag of a project. '''
     user_obj = get_user(session, user)
@@ -750,12 +750,18 @@ def edit_issue_tags(
             'No tag "%s" found related to this project' % (old_tag_name))
 
     old_tag_name = old_tag.tag
+    old_tag_description = old_tag.tag_description
     old_tag_color = old_tag.tag_color
-    if old_tag.tag == new_tag and old_tag_color == new_tag_color:
-        # check for change
+
+    # check for change
+    no_change_in_tag = old_tag.tag == new_tag and \
+                       old_tag_description == new_tag_description  and \
+                       old_tag_color == new_tag_color
+    if no_change_in_tag:
         raise pagure.exceptions.PagureException(
-            'No change.  Old tag "%s(%s)" is the same as new tag "%s(%s)"'
-            % (old_tag, old_tag_color, new_tag, new_tag_color))
+            'No change.  Old tag "%s(%s)[%s]" is the same as new tag "%s(%s)[%s]"'
+            % (old_tag, old_tag_description, old_tag_color, new_tag,
+            new_tag_description, new_tag_color))
     elif old_tag.tag != new_tag:
         # Check if new tag already exists
         existing_tag = get_tag(session, new_tag, project.id)
@@ -772,6 +778,7 @@ def edit_issue_tags(
     ).update(
         {
             model.TagColored.tag: new_tag,
+            model.TagColored.tag_description: new_tag_description,
             model.TagColored.tag_color: new_tag_color
         }
     )
@@ -789,16 +796,19 @@ def edit_issue_tags(
             issue, repo=issue.project, repofolder=ticketfolder)
 
     msgs = []
-    msgs.append('Edited tag: %s(%s) to %s(%s)' %
-                (old_tag_name, old_tag_color, new_tag, new_tag_color))
+    msgs.append('Edited tag: %s(%s)[%s] to %s(%s)[%s]' %
+                (old_tag_name, old_tag_description, old_tag_color,
+                new_tag, new_tag_description, new_tag_color))
     pagure.lib.notify.log(
         project,
         topic='project.tag.edited',
         msg=dict(
             project=project.to_json(public=True),
             old_tag=old_tag.tag,
+            old_tag_description=old_tag_description,
             old_tag_color=old_tag_color,
             new_tag=new_tag,
+            new_tag_description=new_tag_description,
             new_tag_color=new_tag_color,
             agent=user_obj.username,
         ),
@@ -1372,10 +1382,11 @@ def new_pull_request(session, branch_from,
     return request
 
 
-def new_tag(session, tag_name, tag_color, project_id):
+def new_tag(session, tag_name, tag_description, tag_color, project_id):
     ''' Return a new tag object '''
     tagobj = model.TagColored(
         tag=tag_name,
+        tag_description=tag_description,
         tag_color=tag_color,
         project_id=project_id
     )
diff --git a/pagure/lib/model.py b/pagure/lib/model.py
index 34d4aff..e532d6d 100644
--- a/pagure/lib/model.py
+++ b/pagure/lib/model.py
@@ -1089,6 +1089,7 @@ class TagColored(BASE):
 
     id = sa.Column(sa.Integer, primary_key=True)
     tag = sa.Column(sa.String(255), nullable=False)
+    tag_description = sa.Column(sa.String(255))
     project_id = sa.Column(
         sa.Integer,
         sa.ForeignKey(
diff --git a/pagure/templates/edit_tag.html b/pagure/templates/edit_tag.html
index 6775017..5789cfa 100644
--- a/pagure/templates/edit_tag.html
+++ b/pagure/templates/edit_tag.html
@@ -23,6 +23,7 @@
               namespace=repo.namespace) }}" >
             {{ form.csrf_token }}
           {{ render_bootstrap_field(form.tag) }}
+          {{ render_bootstrap_field(form.tag_description) }}
           <fieldset class="form-group {% if form.tag.errors %}has-error{% endif %}">
             {% set formclasses = "form-control c-select"%}
             {% if form.tag_color.errors %} {% set formclasses = formclasses + " form-control-error" %} {% endif %}
diff --git a/pagure/templates/issue.html b/pagure/templates/issue.html
index 4e5a186..56120fa 100644
--- a/pagure/templates/issue.html
+++ b/pagure/templates/issue.html
@@ -159,7 +159,7 @@
             <label><strong>Tags</strong></label>
               <h4 id="taglist">
                 {% for tag in issue.tags %}
-                  <a id="tag-{{ tag.tag }}" style="background-color:{{ tag.tag_color}}" class="label label-default"
+                  <a id="tag-{{ tag.tag }}" title="{{ tag.tag_description }}" style="background-color:{{ tag.tag_color}}" class="label label-default"
                     href="{{ url_for('view_issues',
                         repo=repo.name,
                         username=username,
diff --git a/pagure/templates/settings.html b/pagure/templates/settings.html
index c28f43d..45ccd12 100644
--- a/pagure/templates/settings.html
+++ b/pagure/templates/settings.html
@@ -765,6 +765,7 @@
               <span class="oi" data-glyph="tag"></span>&nbsp; {{ tag.tag }}
             </a>
             &nbsp<span class="label label-info" style="background-color:{{tag.tag_color}}">{{tag.tag}}</span>
+            &nbsp;<span class="text-muted">{{ tag.tag_description }}</span>
             <div class="pull-xs-right">
             <form class="icon del_icon pull-xs-right" method="POST"
               action="{{ url_for(
@@ -1003,10 +1004,13 @@ $('#new_tag').click(function(e) {
     // Only display the Tag row the first time Add New Tag is clicked
     $('#tagcolor').append(
      '<div class="row">\
-        <div class="col-sm-6">\
+        <div class="col-sm-4">\
           <strong>New Tag</strong>\
         </div>\
         <div class="col-sm-6">\
+          <strong>Description</strong>\
+        </div>\
+        <div class="col-sm-2">\
           <strong>Tag Color</strong>\
         </div>\
       </div>');
@@ -1014,11 +1018,15 @@ $('#new_tag').click(function(e) {
   }
   $('#tagcolor').append(
    '<div class="row p-t-1"> \
-        <div class="col-sm-6 p-r-0">\
+        <div class="col-sm-4 p-r-0">\
           <input type="text" name="tag"\
             value="" size="3" class="form-control"/>\
         </div>\
         <div class="col-sm-6 p-r-0">\
+          <input type="text" name="tag_description" placeholder="(optional)"\
+            value="" size="15" class="form-control"/>\
+        </div>\
+        <div class="col-sm-2 p-r-0">\
           <input type="color" name="tag_color" class="form-control c-select" \
               onchange="clickColor(0, -1, -1, 5)" /> \
         </div>\
diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py
index 631f579..8ccb272 100644
--- a/pagure/ui/issues.py
+++ b/pagure/ui/issues.py
@@ -392,6 +392,7 @@ def edit_tag(repo, tag, username=None, namespace=None):
     form = pagure.forms.AddIssueTagForm()
     if form.validate_on_submit():
         new_tag = form.tag.data
+        new_tag_description = form.tag_description.data
         new_tag_color = form.tag_color.data
 
         msgs = pagure.lib.edit_issue_tags(
@@ -399,6 +400,7 @@ def edit_tag(repo, tag, username=None, namespace=None):
             repo,
             tagobj,
             new_tag,
+            new_tag_description,
             new_tag_color,
             user=flask.g.fas_user.username,
             ticketfolder=APP.config['TICKETS_FOLDER']
@@ -418,6 +420,7 @@ def edit_tag(repo, tag, username=None, namespace=None):
             namespace=repo.namespace))
     elif flask.request.method == 'GET':
         form.tag_color.data = tagobj.tag_color
+        form.tag_description.data = tagobj.tag_description
         form.tag.data = tag
 
     return flask.render_template(
@@ -457,6 +460,14 @@ def update_tags(repo, username=None, namespace=None):
             for tag in flask.request.form.getlist('tag')
             if tag.strip() and tag.strip() not in seen and not seen.add(tag.strip())
         ]
+
+        seen = set()
+        tag_descriptions = [
+            desc.strip()
+            for desc in flask.request.form.getlist('tag_description')
+            if desc.strip() and desc.strip() not in seen and not seen.add(desc.strip())
+        ]
+
         # Uniquify and order preserving
         seen = set()
         colors = [

From 3080f62b60ac19add8a88a3f1b65fc8840e9d42b Mon Sep 17 00:00:00 2001
From: Pradeep CE (cep) <breathingcode@gmail.com>
Date: Jan 17 2017 19:02:42 +0000
Subject: [PATCH 2/9] Fix tag creation breaking on empty description


Also check if descriptions (empty or otherwise) are present for
every tag created.

---

diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py
index 8ccb272..19a90c6 100644
--- a/pagure/ui/issues.py
+++ b/pagure/ui/issues.py
@@ -461,12 +461,7 @@ def update_tags(repo, username=None, namespace=None):
             if tag.strip() and tag.strip() not in seen and not seen.add(tag.strip())
         ]
 
-        seen = set()
-        tag_descriptions = [
-            desc.strip()
-            for desc in flask.request.form.getlist('tag_description')
-            if desc.strip() and desc.strip() not in seen and not seen.add(desc.strip())
-        ]
+        tag_descriptions = flask.request.form.getlist('tag_description')
 
         # Uniquify and order preserving
         seen = set()
@@ -484,9 +479,9 @@ def update_tags(repo, username=None, namespace=None):
                     'error')
                 error = True
 
-        if len(tags) != len(colors):
+        if not (len(tags) == len(colors) == len(tag_descriptions)):
             flask.flash(
-                'tags and tag colors are not of the same length', 'error')
+                'tags, tag descriptions and tag colors are not of the same length', 'error')
             error = True
 
         if not error:
@@ -495,6 +490,7 @@ def update_tags(repo, username=None, namespace=None):
                     pagure.lib.new_tag(
                         SESSION,
                         tag,
+                        tag_descriptions[idx],
                         colors[idx],
                         repo.id)
                     SESSION.commit()

From 9a63dd15faa7aceb0a1e49fb95e3fccdb7476343 Mon Sep 17 00:00:00 2001
From: Pradeep CE (cep) <breathingcode@gmail.com>
Date: Jan 17 2017 19:02:42 +0000
Subject: [PATCH 3/9] Add DB revision for tag description column


---

diff --git a/alembic/versions/6addaed6008e_add_description_for_tags.py b/alembic/versions/6addaed6008e_add_description_for_tags.py
new file mode 100644
index 0000000..588ea6a
--- /dev/null
+++ b/alembic/versions/6addaed6008e_add_description_for_tags.py
@@ -0,0 +1,26 @@
+"""Add description for tags
+
+Revision ID: 6addaed6008e
+Revises: 208b0cd232ab
+Create Date: 2017-01-14 23:38:15.631750
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '6addaed6008e'
+down_revision = '208b0cd232ab'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    ''' Add the column notification to the table pull_request_comments.
+    '''
+    op.add_column(
+        'tags_colored',
+        sa.Column('tag_description', sa.String(255))
+    )
+
+def downgrade():
+    op.drop_column('tags_colored', 'tag_description')

From a0815ce9867f80b1a127076ec18c0679e7a668c4 Mon Sep 17 00:00:00 2001
From: Pradeep CE (cep) <breathingcode@gmail.com>
Date: Jan 17 2017 19:02:42 +0000
Subject: [PATCH 4/9] Extend render_bootstrap_field macro with field_label


---

diff --git a/pagure/templates/_formhelper.html b/pagure/templates/_formhelper.html
index ae5da5b..117b6f7 100644
--- a/pagure/templates/_formhelper.html
+++ b/pagure/templates/_formhelper.html
@@ -12,7 +12,9 @@
 </div>
 {% endmacro %}
 
-{% macro render_bootstrap_field(field, field_description="", formclass="") %}
+{% macro render_bootstrap_field(field, field_label, field_description="", formclass="") %}
+  {% if field_label is not defined %} {% set field_label = field.label %} {% endif %}
+
   <fieldset class="form-group {% if field.errors %}has-error{% endif %} {% if formclass %}issue-metadata-form{% endif %}">
     {% set formclasses = "form-control"%}
     {% if field.errors %} {% set formclasses = formclasses + " form-control-error" %} {% endif %}
@@ -23,14 +25,14 @@
         <label class="c-input c-checkbox">
           {{ field(type='checkbox', **kwargs) }}
           <span class="c-indicator"></span>
-          <strong>{{ field.label }}</strong>
+          <strong>{{ field_label }}</strong>
         </label>
         <br />
         {% if field_description %}
           <small class="text-muted">{{ field_description }}</small>
         {% endif %}
     {% else %}
-      <strong>{{ field.label }}</strong>
+      <strong>{{ field_label }}</strong>
       {{ field(class_=formclasses)|safe }}
       <div>
         {% if field_description %}

From a976106f79e8c4f70cb7570cc0950601c8dbe757 Mon Sep 17 00:00:00 2001
From: Pradeep CE (cep) <breathingcode@gmail.com>
Date: Jan 17 2017 19:02:42 +0000
Subject: [PATCH 5/9] Show proper labels in edit_tag page


---

diff --git a/pagure/templates/edit_tag.html b/pagure/templates/edit_tag.html
index 5789cfa..f7e61e7 100644
--- a/pagure/templates/edit_tag.html
+++ b/pagure/templates/edit_tag.html
@@ -22,13 +22,13 @@
               tag=tagname,
               namespace=repo.namespace) }}" >
             {{ form.csrf_token }}
-          {{ render_bootstrap_field(form.tag) }}
-          {{ render_bootstrap_field(form.tag_description) }}
+          {{ render_bootstrap_field(form.tag, field_label="Tag") }}
+          {{ render_bootstrap_field(form.tag_description, field_label="Tag Description") }}
           <fieldset class="form-group {% if form.tag.errors %}has-error{% endif %}">
             {% set formclasses = "form-control c-select"%}
             {% if form.tag_color.errors %} {% set formclasses = formclasses + " form-control-error" %} {% endif %}
 
-            <strong>{{ form.tag_color.label }}</strong>
+            <strong>Tag Color</strong>
             <input type="color" name="tag_color" class="form-control c-select"
               onchange="clickColor(0, -1, -1, 5)" value="{{ form.tag_color.data }}">
 

From dc15c98d94a93373fddf1228500601077835cdea Mon Sep 17 00:00:00 2001
From: Pradeep CE (cep) <breathingcode@gmail.com>
Date: Jan 17 2017 19:02:42 +0000
Subject: [PATCH 6/9] Fix tests after description for tags feature


 - Default empty value for descriptions
 - Strip description before creating tag
 - Update DB migration for default empty value
 - Some other minor fixes

---

diff --git a/alembic/versions/6addaed6008e_add_description_for_tags.py b/alembic/versions/6addaed6008e_add_description_for_tags.py
index 588ea6a..14411bc 100644
--- a/alembic/versions/6addaed6008e_add_description_for_tags.py
+++ b/alembic/versions/6addaed6008e_add_description_for_tags.py
@@ -19,7 +19,7 @@ def upgrade():
     '''
     op.add_column(
         'tags_colored',
-        sa.Column('tag_description', sa.String(255))
+        sa.Column('tag_description', sa.String(255), default="")
     )
 
 def downgrade():
diff --git a/pagure/lib/model.py b/pagure/lib/model.py
index e532d6d..f982f9e 100644
--- a/pagure/lib/model.py
+++ b/pagure/lib/model.py
@@ -1089,7 +1089,7 @@ class TagColored(BASE):
 
     id = sa.Column(sa.Integer, primary_key=True)
     tag = sa.Column(sa.String(255), nullable=False)
-    tag_description = sa.Column(sa.String(255))
+    tag_description = sa.Column(sa.String(255), default="")
     project_id = sa.Column(
         sa.Integer,
         sa.ForeignKey(
@@ -1109,8 +1109,8 @@ class TagColored(BASE):
     )
 
     def __repr__(self):
-        return 'TagColored(id: %s, tag:%s, color:%s)' % (
-            self.id, self.tag, self.tag_color)
+        return 'TagColored(id: %s, tag:%s, tag_description:%s, color:%s)' % (
+            self.id, self.tag, self.tag_description, self.tag_color)
 
 
 class TagIssueColored(BASE):
diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py
index 19a90c6..f5e6085 100644
--- a/pagure/ui/issues.py
+++ b/pagure/ui/issues.py
@@ -461,7 +461,10 @@ def update_tags(repo, username=None, namespace=None):
             if tag.strip() and tag.strip() not in seen and not seen.add(tag.strip())
         ]
 
-        tag_descriptions = flask.request.form.getlist('tag_description')
+        tag_descriptions = [
+            desc.strip()
+            for desc in flask.request.form.getlist('tag_description')
+        ]
 
         # Uniquify and order preserving
         seen = set()
diff --git a/tests/test_pagure_flask_ui_issues.py b/tests/test_pagure_flask_ui_issues.py
index c71bd30..d7b169d 100644
--- a/tests/test_pagure_flask_ui_issues.py
+++ b/tests/test_pagure_flask_ui_issues.py
@@ -1518,6 +1518,7 @@ class PagureFlaskIssuestests(tests.Modeltests):
                 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
 
             data = {'tag': 'tag2',
+                    'tag_description': 'lorem ipsum',
                     'tag_color': 'DeepSkyBlue'}
 
             output = self.app.post('/test/tag/tag1/edit', data=data)
@@ -1532,7 +1533,19 @@ class PagureFlaskIssuestests(tests.Modeltests):
                 'Settings - test - Pagure', output.data)
             self.assertIn(
                 '</button>\n                      '
-                'Edited tag: tag1(DeepSkyBlue) to tag2(DeepSkyBlue)',
+                'Edited tag: tag1()[DeepSkyBlue] to tag2(lorem ipsum)[DeepSkyBlue]',
+                output.data)
+
+            # update tag with empty description
+            data['tag_description'] = ''
+            output = self.app.post(
+                '/test/tag/tag2/edit', data=data, follow_redirects=True)
+            self.assertEqual(output.status_code, 200)
+            self.assertIn(
+                'Settings - test - Pagure', output.data)
+            self.assertIn(
+                '</button>\n                      '
+                'Edited tag: tag2(lorem ipsum)[DeepSkyBlue] to tag2()[DeepSkyBlue]',
                 output.data)
 
         # After edit, list tags
@@ -2013,6 +2026,7 @@ class PagureFlaskIssuestests(tests.Modeltests):
             # No CSRF
             data = {
                 'tag': 'red',
+                'tag_description': 'lorem ipsum',
                 'tag_color': '#ff0000'
             }
             output = self.app.post(
@@ -2030,6 +2044,7 @@ class PagureFlaskIssuestests(tests.Modeltests):
             # Invalid color
             data = {
                 'tag': 'red',
+                'tag_description': 'lorem ipsum',
                 'tag_color': 'red',
                 'csrf_token': csrf_token,
             }
@@ -2049,6 +2064,7 @@ class PagureFlaskIssuestests(tests.Modeltests):
             # Inconsistent length color
             data = {
                 'tag': ['red', 'blue'],
+                'tag_description': ['lorem ipsum', 'foo bar'],
                 'tag_color': 'red',
                 'csrf_token': csrf_token,
             }
@@ -2062,15 +2078,35 @@ class PagureFlaskIssuestests(tests.Modeltests):
                 'Color: red does not match the expected pattern',
                 output.data)
             self.assertIn(
-                '</button>\n                      tags and tag colors'
-                ' are not of the same length', output.data)
+                '</button>\n                      tags, tag descriptions and'
+                ' tag colors are not of the same length', output.data)
             self.assertIn(
                 '        <ul class="list-group list-group-flush">'
                 '\n        </ul>', output.data)
 
-            # Valid query
+            # Inconsistent length description
             data = {
                 'tag': ['red', 'blue'],
+                'tag_description': 'lorem ipsum',
+                'tag_color': ['#ff0000', '#003cff'],
+                'csrf_token': csrf_token,
+            }
+            output = self.app.post(
+                '/test/update/tags', data=data, follow_redirects=True)
+            self.assertEqual(output.status_code, 200)
+            self.assertIn(
+                '<title>Settings - test - Pagure</title>', output.data)
+            self.assertIn(
+                '</button>\n                      tags, tag descriptions and'
+                ' tag colors are not of the same length', output.data)
+            self.assertIn(
+                '        <ul class="list-group list-group-flush">'
+                '\n        </ul>', output.data)
+
+            # consistent length, but empty description
+            data = {
+                'tag': ['red', 'blue'],
+                'tag_description': ['lorem ipsum', ''],
                 'tag_color': ['#ff0000', '#003cff'],
                 'csrf_token': csrf_token,
             }
@@ -2081,20 +2117,53 @@ class PagureFlaskIssuestests(tests.Modeltests):
                 '<title>Settings - test - Pagure</title>', output.data)
             self.assertIn(
                 '<span class="label label-info" style="background-color:'
-                '#003cff">blue</span>', output.data)
+                '#003cff">blue</span>\n'
+                '            &nbsp;<span class="text-muted">'
+                '</span>', output.data)
             self.assertIn(
                 '<input type="hidden" value="blue" name="tag" />',
                 output.data)
             self.assertIn(
                 '<span class="label label-info" style="background-color:'
-                '#ff0000">red</span>', output.data)
+                '#ff0000">red</span>\n'
+                '            &nbsp;<span class="text-muted">lorem ipsum'
+                '</span>', output.data)
+            self.assertIn(
+                '<input type="hidden" value="red" name="tag" />',
+                output.data)
+
+            # Valid query
+            data = {
+                'tag': ['red', 'green'],
+                'tag_description': ['lorem ipsum', 'sample description'],
+                'tag_color': ['#ff0000', '#00ff00'],
+                'csrf_token': csrf_token,
+            }
+            output = self.app.post(
+                '/test/update/tags', data=data, follow_redirects=True)
+            self.assertEqual(output.status_code, 200)
+            self.assertIn(
+                '<title>Settings - test - Pagure</title>', output.data)
+            self.assertIn(
+                '<span class="label label-info" style="background-color:'
+                '#00ff00">green</span>\n'
+                '            &nbsp;<span class="text-muted">sample description'
+                '</span>', output.data)
+            self.assertIn(
+                '<input type="hidden" value="green" name="tag" />',
+                output.data)
+            self.assertIn(
+                '<span class="label label-info" style="background-color:'
+                '#ff0000">red</span>\n'
+                '            &nbsp;<span class="text-muted">lorem ipsum'
+                '</span>', output.data)
             self.assertIn(
                 '<input type="hidden" value="red" name="tag" />',
                 output.data)
 
         # After update, list tags
         tags = pagure.lib.get_tags_of_project(self.session, repo)
-        self.assertEqual([tag.tag for tag in tags], ['blue', 'red'])
+        self.assertEqual([tag.tag for tag in tags], ['blue', 'green', 'red'])
 
 
 if __name__ == '__main__':
diff --git a/tests/test_pagure_lib.py b/tests/test_pagure_lib.py
index b73ad40..9bfb92e 100644
--- a/tests/test_pagure_lib.py
+++ b/tests/test_pagure_lib.py
@@ -500,6 +500,7 @@ class PagureLibtests(tests.Modeltests):
             project=repo,
             old_tag='foo',
             new_tag='bar',
+            new_tag_description='lorem ipsum',
             new_tag_color='black',
             user='pingou',
             ticketfolder=None,
@@ -512,6 +513,7 @@ class PagureLibtests(tests.Modeltests):
             project=repo,
             old_tag=None,
             new_tag='bar',
+            new_tag_description='lorem ipsum',
             new_tag_color='black',
             user='pingou',
             ticketfolder=None,
@@ -522,6 +524,7 @@ class PagureLibtests(tests.Modeltests):
             project=repo,
             old_tag='tag1',
             new_tag='tag2',
+            new_tag_description='lorem ipsum',
             new_tag_color='black',
             user='pingou',
             ticketfolder=None,
@@ -529,7 +532,7 @@ class PagureLibtests(tests.Modeltests):
         self.session.commit()
         self.assertEqual(
             msgs,
-            ['Edited tag: tag1(DeepSkyBlue) to tag2(black)']
+            ['Edited tag: tag1()[DeepSkyBlue] to tag2(lorem ipsum)[black]']
         )
 
         # Add a new tag
@@ -551,6 +554,7 @@ class PagureLibtests(tests.Modeltests):
             project=repo,
             old_tag='tag2',
             new_tag='tag3',
+            new_tag_description='lorem ipsum',
             new_tag_color='red',
             user='pingou',
             ticketfolder=None,
@@ -562,12 +566,13 @@ class PagureLibtests(tests.Modeltests):
             project=repo,
             old_tag='tag2',
             new_tag='tag4',
+            new_tag_description='ipsum lorem',
             new_tag_color='purple',
             user='pingou',
             ticketfolder=None,
         )
         self.session.commit()
-        self.assertEqual(msgs, ['Edited tag: tag2(black) to tag4(purple)'])
+        self.assertEqual(msgs, ['Edited tag: tag2(lorem ipsum)[black] to tag4(ipsum lorem)[purple]'])
 
     @patch('pagure.lib.git.update_git')
     @patch('pagure.lib.notify.send_email')
diff --git a/tests/test_pagure_lib_model.py b/tests/test_pagure_lib_model.py
index 696ea42..1acfd16 100644
--- a/tests/test_pagure_lib_model.py
+++ b/tests/test_pagure_lib_model.py
@@ -153,6 +153,7 @@ class PagureLibModeltests(tests.Modeltests):
 
         item = pagure.lib.model.TagColored(
             tag='foo',
+            tag_description='bar',
             tag_color='DeepSkyBlue',
             project_id=repo.id)
         self.session.add(item)

From 57c2a1f9705441416dcb52925b2799f452566b05 Mon Sep 17 00:00:00 2001
From: Pradeep CE (cep) <breathingcode@gmail.com>
Date: Jan 17 2017 19:02:42 +0000
Subject: [PATCH 7/9] More helpful error messages for creating tags


Also update the tests for the same.

---

diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py
index f5e6085..393e773 100644
--- a/pagure/ui/issues.py
+++ b/pagure/ui/issues.py
@@ -483,9 +483,21 @@ def update_tags(repo, username=None, namespace=None):
                 error = True
 
         if not (len(tags) == len(colors) == len(tag_descriptions)):
-            flask.flash(
-                'tags, tag descriptions and tag colors are not of the same length', 'error')
             error = True
+            # store the lengths because we are going to use them a lot
+            len_tags = len(tags)
+            len_tag_descriptions = len(tag_descriptions)
+            len_colors = len(colors)
+            error_message = 'Error: Incomplete request. '
+
+            if len_colors > len_tags or len_tag_descriptions > len_tags:
+                error_message += 'One or more tag fields missing.'
+            elif len_colors < len_tags:
+                error_message += 'One or more tag color fields missing.'
+            elif len_tag_descriptions < len_tags:
+                error_message += 'One or more tag description fields missing.'
+
+            flask.flash(error_message, 'error')
 
         if not error:
             for idx, tag in enumerate(tags):
diff --git a/tests/test_pagure_flask_ui_issues.py b/tests/test_pagure_flask_ui_issues.py
index d7b169d..3451282 100644
--- a/tests/test_pagure_flask_ui_issues.py
+++ b/tests/test_pagure_flask_ui_issues.py
@@ -2061,6 +2061,25 @@ class PagureFlaskIssuestests(tests.Modeltests):
                 '        <ul class="list-group list-group-flush">'
                 '\n        </ul>', output.data)
 
+            # Inconsistent length tags (missing tag field)
+            data = {
+                'tag': 'red',
+                'tag_description': ['lorem ipsum', 'foo bar'],
+                'tag_color': ['#ff0000', '#003cff'],
+                'csrf_token': csrf_token,
+            }
+            output = self.app.post(
+                '/test/update/tags', data=data, follow_redirects=True)
+            self.assertEqual(output.status_code, 200)
+            self.assertIn(
+                '<title>Settings - test - Pagure</title>', output.data)
+            self.assertIn(
+                '</button>\n                      Error: Incomplete request. '
+                'One or more tag fields missing.', output.data)
+            self.assertIn(
+                '        <ul class="list-group list-group-flush">'
+                '\n        </ul>', output.data)
+
             # Inconsistent length color
             data = {
                 'tag': ['red', 'blue'],
@@ -2078,8 +2097,8 @@ class PagureFlaskIssuestests(tests.Modeltests):
                 'Color: red does not match the expected pattern',
                 output.data)
             self.assertIn(
-                '</button>\n                      tags, tag descriptions and'
-                ' tag colors are not of the same length', output.data)
+                '</button>\n                      Error: Incomplete request. '
+                'One or more tag color fields missing.', output.data)
             self.assertIn(
                 '        <ul class="list-group list-group-flush">'
                 '\n        </ul>', output.data)
@@ -2097,8 +2116,8 @@ class PagureFlaskIssuestests(tests.Modeltests):
             self.assertIn(
                 '<title>Settings - test - Pagure</title>', output.data)
             self.assertIn(
-                '</button>\n                      tags, tag descriptions and'
-                ' tag colors are not of the same length', output.data)
+                '</button>\n                      Error: Incomplete request. '
+                'One or more tag description fields missing.', output.data)
             self.assertIn(
                 '        <ul class="list-group list-group-flush">'
                 '\n        </ul>', output.data)

From 28c4763363ccd50d499f1cd71b636470840661b8 Mon Sep 17 00:00:00 2001
From: Pradeep CE (cep) <breathingcode@gmail.com>
Date: Jan 17 2017 19:02:42 +0000
Subject: [PATCH 8/9] Minor fixes for feature: Description for Tags


Discussion for the same can be found at
https://pagure.io/pagure/pull-request/1757

---

diff --git a/alembic/versions/6addaed6008e_add_description_for_tags.py b/alembic/versions/6addaed6008e_add_description_for_tags.py
index 14411bc..47b0070 100644
--- a/alembic/versions/6addaed6008e_add_description_for_tags.py
+++ b/alembic/versions/6addaed6008e_add_description_for_tags.py
@@ -15,7 +15,7 @@ import sqlalchemy as sa
 
 
 def upgrade():
-    ''' Add the column notification to the table pull_request_comments.
+    ''' Add the column tag_description to the table tags_colored.
     '''
     op.add_column(
         'tags_colored',
@@ -23,4 +23,6 @@ def upgrade():
     )
 
 def downgrade():
+    ''' Remove column tag_description from the table tags_colored.
+    '''
     op.drop_column('tags_colored', 'tag_description')
diff --git a/pagure/forms.py b/pagure/forms.py
index bb57357..579e047 100644
--- a/pagure/forms.py
+++ b/pagure/forms.py
@@ -228,7 +228,7 @@ class RemoteRequestPullForm(RequestPullForm):
 class DeleteIssueTagForm(PagureForm):
     ''' Form to remove a tag to from a project. '''
     tag = wtforms.TextField(
-        'tag',
+        'Tag',
         [
             wtforms.validators.Optional(),
             wtforms.validators.Regexp(TAGS_REGEX, flags=re.IGNORECASE),
@@ -240,11 +240,11 @@ class DeleteIssueTagForm(PagureForm):
 class AddIssueTagForm(DeleteIssueTagForm):
     ''' Form to add a tag to a project. '''
     tag_description = wtforms.TextField(
-        'tag_description',
+        'Tag Description',
         [wtforms.validators.Optional()],
     )
     tag_color = wtforms.TextField(
-        'tag_color',
+        'Tag Color',
         [wtforms.validators.Required()],
     )
 
diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py
index 09b95f9..d28b2af 100644
--- a/pagure/lib/__init__.py
+++ b/pagure/lib/__init__.py
@@ -754,14 +754,15 @@ def edit_issue_tags(
     old_tag_color = old_tag.tag_color
 
     # check for change
-    no_change_in_tag = old_tag.tag == new_tag and \
-                       old_tag_description == new_tag_description  and \
-                       old_tag_color == new_tag_color
+    no_change_in_tag = old_tag.tag == new_tag \
+                       and old_tag_description == new_tag_description \
+                       and old_tag_color == new_tag_color
     if no_change_in_tag:
         raise pagure.exceptions.PagureException(
-            'No change.  Old tag "%s(%s)[%s]" is the same as new tag "%s(%s)[%s]"'
-            % (old_tag, old_tag_description, old_tag_color, new_tag,
-            new_tag_description, new_tag_color))
+            'No change.  Old tag "%s(%s)[%s]" is the same as '
+            'new tag "%s(%s)[%s]"' % (
+                old_tag, old_tag_description, old_tag_color, new_tag,
+                new_tag_description, new_tag_color))
     elif old_tag.tag != new_tag:
         # Check if new tag already exists
         existing_tag = get_tag(session, new_tag, project.id)
diff --git a/pagure/templates/_formhelper.html b/pagure/templates/_formhelper.html
index 117b6f7..ae5da5b 100644
--- a/pagure/templates/_formhelper.html
+++ b/pagure/templates/_formhelper.html
@@ -12,9 +12,7 @@
 </div>
 {% endmacro %}
 
-{% macro render_bootstrap_field(field, field_label, field_description="", formclass="") %}
-  {% if field_label is not defined %} {% set field_label = field.label %} {% endif %}
-
+{% macro render_bootstrap_field(field, field_description="", formclass="") %}
   <fieldset class="form-group {% if field.errors %}has-error{% endif %} {% if formclass %}issue-metadata-form{% endif %}">
     {% set formclasses = "form-control"%}
     {% if field.errors %} {% set formclasses = formclasses + " form-control-error" %} {% endif %}
@@ -25,14 +23,14 @@
         <label class="c-input c-checkbox">
           {{ field(type='checkbox', **kwargs) }}
           <span class="c-indicator"></span>
-          <strong>{{ field_label }}</strong>
+          <strong>{{ field.label }}</strong>
         </label>
         <br />
         {% if field_description %}
           <small class="text-muted">{{ field_description }}</small>
         {% endif %}
     {% else %}
-      <strong>{{ field_label }}</strong>
+      <strong>{{ field.label }}</strong>
       {{ field(class_=formclasses)|safe }}
       <div>
         {% if field_description %}
diff --git a/pagure/templates/edit_tag.html b/pagure/templates/edit_tag.html
index f7e61e7..5789cfa 100644
--- a/pagure/templates/edit_tag.html
+++ b/pagure/templates/edit_tag.html
@@ -22,13 +22,13 @@
               tag=tagname,
               namespace=repo.namespace) }}" >
             {{ form.csrf_token }}
-          {{ render_bootstrap_field(form.tag, field_label="Tag") }}
-          {{ render_bootstrap_field(form.tag_description, field_label="Tag Description") }}
+          {{ render_bootstrap_field(form.tag) }}
+          {{ render_bootstrap_field(form.tag_description) }}
           <fieldset class="form-group {% if form.tag.errors %}has-error{% endif %}">
             {% set formclasses = "form-control c-select"%}
             {% if form.tag_color.errors %} {% set formclasses = formclasses + " form-control-error" %} {% endif %}
 
-            <strong>Tag Color</strong>
+            <strong>{{ form.tag_color.label }}</strong>
             <input type="color" name="tag_color" class="form-control c-select"
               onchange="clickColor(0, -1, -1, 5)" value="{{ form.tag_color.data }}">
 
diff --git a/pagure/templates/issue.html b/pagure/templates/issue.html
index 56120fa..a2ba336 100644
--- a/pagure/templates/issue.html
+++ b/pagure/templates/issue.html
@@ -159,7 +159,9 @@
             <label><strong>Tags</strong></label>
               <h4 id="taglist">
                 {% for tag in issue.tags %}
-                  <a id="tag-{{ tag.tag }}" title="{{ tag.tag_description }}" style="background-color:{{ tag.tag_color}}" class="label label-default"
+                  <a id="tag-{{ tag.tag }}" title="{{ tag.tag_description }}"
+                    style="background-color:{{ tag.tag_color}}"
+                    class="label label-default"
                     href="{{ url_for('view_issues',
                         repo=repo.name,
                         username=username,
diff --git a/pagure/ui/issues.py b/pagure/ui/issues.py
index 393e773..574474b 100644
--- a/pagure/ui/issues.py
+++ b/pagure/ui/issues.py
@@ -484,7 +484,7 @@ def update_tags(repo, username=None, namespace=None):
 
         if not (len(tags) == len(colors) == len(tag_descriptions)):
             error = True
-            # store the lengths because we are going to use them a lot
+            # Store the lengths because we are going to use them a lot
             len_tags = len(tags)
             len_tag_descriptions = len(tag_descriptions)
             len_colors = len(colors)

From 4462a426e1169f1cbbe07db74604647dbde17033 Mon Sep 17 00:00:00 2001
From: Pradeep CE (cep) <breathingcode@gmail.com>
Date: Jan 17 2017 19:37:57 +0000
Subject: [PATCH 9/9] Add tag description tooltip in issues page


---

diff --git a/pagure/templates/issues.html b/pagure/templates/issues.html
index d609b3c..342bcc9 100644
--- a/pagure/templates/issues.html
+++ b/pagure/templates/issues.html
@@ -231,7 +231,9 @@
             </span>
               {% endif %}
               {% for tag in issue.tags %}
-                <span class="label label-info" style="background-color:{{tag.tag_color}}">{{tag.tag}}</span>
+                <span class="label label-info"
+                  style="background-color:{{tag.tag_color}}"
+                  title="{{ tag.tag_description or '' }}">{{tag.tag}}</span>
               {% endfor%}
             </td>
             <td class="td-open_date nowrap">
diff --git a/pagure/templates/settings.html b/pagure/templates/settings.html
index 45ccd12..5955328 100644
--- a/pagure/templates/settings.html
+++ b/pagure/templates/settings.html
@@ -765,7 +765,7 @@
               <span class="oi" data-glyph="tag"></span>&nbsp; {{ tag.tag }}
             </a>
             &nbsp<span class="label label-info" style="background-color:{{tag.tag_color}}">{{tag.tag}}</span>
-            &nbsp;<span class="text-muted">{{ tag.tag_description }}</span>
+            &nbsp;<span class="text-muted">{{ tag.tag_description or '' }}</span>
             <div class="pull-xs-right">
             <form class="icon del_icon pull-xs-right" method="POST"
               action="{{ url_for(

