You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by da...@apache.org on 2014/07/29 22:49:38 UTC

svn commit: r1614483 - in /bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme: htdocs/js/batchcreate.js theme.py

Author: dammina
Date: Tue Jul 29 20:49:38 2014
New Revision: 1614483

URL: http://svn.apache.org/r1614483
Log:
The new functionality provides the users the freedom to change the number of tickets at any moment.

Modified:
    bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
    bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py

Modified: bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js?rev=1614483&r1=1614482&r2=1614483&view=diff
==============================================================================
--- bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js (original)
+++ bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js Tue Jul 29 20:49:38 2014
@@ -175,7 +175,26 @@ function emptyTable(products,href,token)
 	}
 	table.appendChild(tbody);
 	form.appendChild(table);
+
+	remove_row_button = document.createElement("button");
+	remove_row_button.setAttribute("class","btn pull-right");
+	remove_row_button.setAttribute("type","button");
+	remove_row_button.setAttribute("onclick","remove_row_btn_action()");
+	remove_row_button.setAttribute("id","bct-rmv-empty-row");
+	remove_row_button.appendChild(document.createTextNode("-"));
+	form.appendChild(remove_row_button);
 	
+	add_row_button = document.createElement("button");
+	add_row_button.setAttribute("class","btn pull-right");
+	add_row_button.setAttribute("type","button");
+	add_row_button.addEventListener("click", function(event) {
+  		add_row_btn_action(products);
+  		event.preventDefault();
+	});
+	add_row_button.setAttribute("id","bct-add-empty-row");
+	add_row_button.appendChild(document.createTextNode("+"));
+	form.appendChild(add_row_button);	
+
     submit_button = document.createElement("button");
 	submit_button.setAttribute("class","btn pull-right");
 	submit_button.setAttribute("type","button");
@@ -282,3 +301,144 @@ function submit_btn_action() {
 			contentDiv.appendChild(div);     
         });
 }
+
+function add_row_btn_action(products){
+	// alert("1");
+
+	var headers = {"summary":"Summary","description":"Description","product":"Product","status":"Status","priority":"Priority"}
+	var statuses = ["accepted", "assigned", "closed", "new", "reopened"];
+	var priorities = ["blocker", "critical", "major", "minor", "trivial"];
+	var types = ["defect", "enhancement", "task"];
+
+    tr_rows = document.createElement("tr");
+
+    for (header in headers){
+		if (header == "summary"){
+				td_row = document.createElement("td");
+				input_summary = document.createElement("input");
+				input_summary.setAttribute("type","text");
+				input_summary.setAttribute("id","field-summary"+i);
+				input_summary.setAttribute("class","input-block-level");
+				input_summary.setAttribute("name","field_summary"+i);
+				td_row.appendChild(input_summary);
+				tr_rows.appendChild(td_row);
+			}
+			else if (header == "description") {
+				td_row = document.createElement("td");
+				input_description = document.createElement("textarea");
+				input_description.setAttribute("id","field-description"+i);
+				input_description.setAttribute("class","input-block-level");
+				input_description.setAttribute("name","field_description"+i);
+				input_description.setAttribute("rows","2");
+				input_description.setAttribute("cols","28");
+				td_row.appendChild(input_description);
+				tr_rows.appendChild(td_row);
+			}
+			else if (header == "status") {
+				td_row = document.createElement("td");
+				input_status = document.createElement("select");
+				input_status.setAttribute("id","field-status"+i);
+				input_status.setAttribute("class","input-block-level");
+				input_status.setAttribute("name","field_status"+i);
+				for (status in statuses){
+					option = document.createElement("option");
+					option.setAttribute("value",statuses[status]);
+					option.appendChild(document.createTextNode(statuses[status]));
+					input_status.appendChild(option);
+				}
+				td_row.appendChild(input_status);
+				tr_rows.appendChild(td_row);
+			}
+			else if (header == "priority") {
+				td_row = document.createElement("td");
+				input_priority = document.createElement("select");
+				input_priority.setAttribute("id","field-priority"+i);
+				input_priority.setAttribute("class","input-block-level");
+				input_priority.setAttribute("name","field_priority"+i);
+				for (priority in priorities){
+					option = document.createElement("option");
+					option.setAttribute("value",priorities[priority]);
+					option.appendChild(document.createTextNode(priorities[priority]));
+					input_priority.appendChild(option);
+				}
+				td_row.appendChild(input_priority);
+				tr_rows.appendChild(td_row);
+			}
+			/*else if (header == "type") {
+				td_row = document.createElement("td");
+				input_type = document.createElement("select");
+				input_type.setAttribute("id","field-type"+i);
+				input_type.setAttribute("class","input-block-level");
+				input_type.setAttribute("name","field_type"+i);
+				for (type in types){
+					option = document.createElement("option");
+					option.setAttribute("value",types[type]);
+					option.appendChild(document.createTextNode(types[type]));
+					input_type.appendChild(option);
+				}
+				td_row.appendChild(input_type);
+				tr_rows.appendChild(td_row);
+			}*/
+			else if (header == "product") {
+				td_row = document.createElement("td");
+				field_product = document.createElement("select");
+				field_product.setAttribute("id","field-product"+i);
+				field_product.setAttribute("class","input-block-level");
+				field_product.setAttribute("name","field_product"+i);
+				for (product in products){
+					option = document.createElement("option");
+					option.setAttribute("value",(products[product])[0]);
+					option.appendChild(document.createTextNode((products[product])[1]));
+					field_product.appendChild(option);
+				}
+				td_row.appendChild(field_product);
+				tr_rows.appendChild(td_row);
+			}
+			/*else if (header == "owner"){
+				td_row = document.createElement("td");
+				input_owner = document.createElement("input");
+				input_owner.setAttribute("type","text");
+				input_owner.setAttribute("id","field-owner"+i);
+				input_owner.setAttribute("class","input-block-level");
+				input_owner.setAttribute("name","field_owner"+i);
+				td_row.appendChild(input_owner);
+				tr_rows.appendChild(td_row);
+			}*/
+			/*else if (header == "cc"){
+				td_row = document.createElement("td");
+				input_cc = document.createElement("input");
+				input_cc.setAttribute("type","text");
+				input_cc.setAttribute("id","field-cc"+i);
+				input_cc.setAttribute("class","input-block-level");
+				input_cc.setAttribute("name","field_cc"+i);
+				td_row.appendChild(input_cc);
+				tr_rows.appendChild(td_row);
+			}*/
+			/*else if (header == "milestone"){
+				td_row = document.createElement("td");
+				input_milestone = document.createElement("input");
+				input_milestone.setAttribute("type","text");
+				input_milestone.setAttribute("id","field-milestone"+i);
+				input_milestone.setAttribute("class","input-block-level");
+				input_milestone.setAttribute("name","field_milestone"+i);
+				td_row.appendChild(input_milestone);
+				tr_rows.appendChild(td_row);
+			}*/
+			/*else if (header == "keywords"){
+				td_row = document.createElement("td");
+				input_keywords = document.createElement("input");
+				input_keywords.setAttribute("type","text");
+				input_keywords.setAttribute("id","field-keywords"+i);
+				input_keywords.setAttribute("class","input-block-level");
+				input_keywords.setAttribute("name","field_keywords"+i);
+				td_row.appendChild(input_keywords);
+				tr_rows.appendChild(td_row);
+			}*/
+
+	}
+	document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].appendChild(tr_rows);
+}
+
+function remove_row_btn_action(){
+	document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].lastChild.remove();
+}

Modified: bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py?rev=1614483&r1=1614482&r2=1614483&view=diff
==============================================================================
--- bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py (original)
+++ bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py Tue Jul 29 20:49:38 2014
@@ -36,14 +36,8 @@ from trac.util.compat import set
 from trac.util.presentation import to_json
 from trac.versioncontrol.web_ui.browser import BrowserModule
 from trac.web.api import IRequestFilter, ITemplateStreamFilter
-from trac.web.chrome import (
-    add_stylesheet,
-    add_warning,
-    INavigationContributor,
-    ITemplateProvider,
-    prevnext_nav,
-    Chrome,
-    add_script)
+from trac.web.chrome import (add_stylesheet, add_warning, INavigationContributor,
+                             ITemplateProvider, prevnext_nav, Chrome, add_script)
 from trac.web.main import IRequestHandler
 from trac.wiki.admin import WikiAdmin
 from trac.wiki.formatter import format_to_html
@@ -63,9 +57,7 @@ try:
 except ImportError:
     ProductTicketModule = None
 
-
 class BloodhoundTheme(ThemeBase):
-
     """Look and feel of Bloodhound issue tracker.
     """
     template = htdocs = css = screenshot = disable_trac_css = True
@@ -168,34 +160,24 @@ class BloodhoundTheme(ThemeBase):
     )
 
     labels_application_short = Option('labels', 'application_short',
-                                      'Bloodhound', """A short version of application name most commonly
+        'Bloodhound', """A short version of application name most commonly
         displayed in text, titles and labels""", doc_domain='bhtheme')
 
     labels_application_full = Option('labels', 'application_full',
-                                     'Apache Bloodhound', """This is full name with trade mark and
+        'Apache Bloodhound', """This is full name with trade mark and
         everything, it is currently used in footers and about page only""",
                                      doc_domain='bhtheme')
 
-    labels_footer_left_prefix = Option(
-        'labels',
-        'footer_left_prefix',
-        '',
+    labels_footer_left_prefix = Option('labels', 'footer_left_prefix', '',
         """Text to display before full application name in footers""",
-        doc_domain='bhtheme')
+                                       doc_domain='bhtheme')
 
-    labels_footer_left_postfix = Option(
-        'labels',
-        'footer_left_postfix',
-        '',
+    labels_footer_left_postfix = Option('labels', 'footer_left_postfix', '',
         """Text to display after full application name in footers""",
-        doc_domain='bhtheme')
+                                        doc_domain='bhtheme')
 
-    labels_footer_right = Option(
-        'labels',
-        'footer_right',
-        '',
-        """Text to use as the right aligned footer""",
-        doc_domain='bhtheme')
+    labels_footer_right = Option('labels', 'footer_right', '',
+        """Text to use as the right aligned footer""", doc_domain='bhtheme')
 
     _wiki_pages = None
     Chrome.default_html_doctype = DocType.HTML5
@@ -365,13 +347,7 @@ class BloodhoundTheme(ThemeBase):
 
     # Request modifiers
 
-    def _modify_search_data(
-            self,
-            req,
-            template,
-            data,
-            content_type,
-            is_active):
+    def _modify_search_data(self, req, template, data, content_type, is_active):
         """Insert breadcumbs and context navigation items in search web UI
         """
         if is_active:
@@ -405,7 +381,7 @@ class BloodhoundTheme(ThemeBase):
         self._modify_resource_breadcrumb(req, template, data, content_type,
                                          is_active)
 
-        # add a creation event to the changelog if the ticket exists
+        #add a creation event to the changelog if the ticket exists
         ticket = data['ticket']
         if ticket.exists:
             data['changes'] = [{'comment': '',
@@ -417,7 +393,7 @@ class BloodhoundTheme(ThemeBase):
                                 'date': ticket['time'],
                                 },
                                ] + data['changes']
-        # and set default order
+        #and set default order
         if not req.session.get('ticket_comments_order'):
             req.session['ticket_comments_order'] = 'newest'
 
@@ -440,13 +416,7 @@ class BloodhoundTheme(ThemeBase):
             if mname:
                 data['milestone'] = Milestone(self.env, mname)
 
-    def _modify_admin_breadcrumb(
-            self,
-            req,
-            template,
-            data,
-            content_type,
-            is_active):
+    def _modify_admin_breadcrumb(self, req, template, data, content_type, is_active):
         # override 'normal' product list with the admin one
 
         def admin_url(prefix):
@@ -495,7 +465,7 @@ class BloodhoundTheme(ThemeBase):
                 SELECT product, value FROM bloodhound_productconfig
                 WHERE product IN (%s) AND section='project' AND
                 option='icon'""" % ', '.join(["%s"] * len(products)),
-                               tuple(p.prefix for p in products))
+                tuple(p.prefix for p in products))
         icons = dict(icons)
         data['thumbsize'] = 64
         # FIXME: Gray icon for missing products
@@ -510,29 +480,29 @@ class BloodhoundTheme(ThemeBase):
                                                    product_ctx(product),
                                                    product.description),
                         links={'extras': (([{'href': req.href.products(
-                            product.prefix, action='edit'),
-                            'title': _('Edit product %(prefix)s',
-                                       prefix=product.prefix),
-                            'icon': tag.i(class_='icon-edit'),
-                            'label': _('Edit')}, ]
-                            if 'PRODUCT_MODIFY' in req.perm
-                            else []) +
-                            [{'href': product.href(),
-                              'title': _('Home page'),
-                              'icon': tag.i(class_='icon-home'),
-                              'label': _('Home')},
-                             {'href': product.href.dashboard(),
-                              'title': _('Tickets dashboard'),
-                              'icon': tag.i(class_='icon-tasks'),
-                              'label': _('Tickets')},
-                             {'href': product.href.wiki(),
-                              'title': _('Wiki'),
-                              'icon': tag.i(class_='icon-book'),
-                              'label': _('Wiki')}]),
-                'main': {'href': product.href(),
-                         'title': None,
-                         'icon': tag.i(class_='icon-chevron-right'),
-                         'label': _('Browse')}})
+                                                product.prefix, action='edit'),
+                                             'title': _('Edit product %(prefix)s',
+                                                        prefix=product.prefix),
+                                             'icon': tag.i(class_='icon-edit'),
+                                             'label': _('Edit')},]
+                                           if 'PRODUCT_MODIFY' in req.perm
+                                           else []) +
+                                          [{'href': product.href(),
+                                            'title': _('Home page'),
+                                            'icon': tag.i(class_='icon-home'),
+                                            'label': _('Home')},
+                                           {'href': product.href.dashboard(),
+                                            'title': _('Tickets dashboard'),
+                                            'icon': tag.i(class_='icon-tasks'),
+                                            'label': _('Tickets')},
+                                           {'href': product.href.wiki(),
+                                            'title': _('Wiki'),
+                                            'icon': tag.i(class_='icon-book'),
+                                            'label': _('Wiki')}]),
+                               'main': {'href': product.href(),
+                                        'title': None,
+                                        'icon': tag.i(class_='icon-chevron-right'),
+                                        'label': _('Browse')}})
 
         data['products'] = [product_media_data(icons, product)
                             for product in products]
@@ -550,16 +520,29 @@ class BloodhoundTheme(ThemeBase):
                        tag.a(_('Source'),
                              href=req.href.wiki('TracRepositoryAdmin')))
 
+class QCTSelectFieldUpdate(Component):
+    implements(IRequestHandler)
+
+    def match_request(self, req):
+        return req.path_info == '/update-menus'
+
+    def process_request(self, req):
+        product = req.args.get('product')
+        fields_to_update = req.args.get('fields_to_update[]');
+        env = ProductEnvironment(self.env.parent, req.args.get('product'))
+        ticket_fields = TicketSystem(env).get_ticket_fields()
+        data = dict([f['name'], f['options']]  for f in ticket_fields
+            if f['type'] == 'select' and f['name'] in fields_to_update)
+        req.send(to_json(data), 'application/json')
+
 
 class QuickCreateTicketDialog(Component):
     implements(IRequestFilter, IRequestHandler)
 
-    qct_fields = ListOption(
-        'ticket',
-        'quick_create_fields',
-        'product, version, type',
+    qct_fields = ListOption('ticket', 'quick_create_fields',
+                            'product, version, type',
         doc="""Multiple selection fields displayed in create ticket menu""",
-        doc_domain='bhtheme')
+                            doc_domain='bhtheme')
 
     def __init__(self, *args, **kwargs):
         import pkg_resources
@@ -610,8 +593,8 @@ class QuickCreateTicketDialog(Component)
                          new_ticket_url=dum_req.href.products(p, 'newticket'),
                          description=ProductEnvironment.lookup_env(self.env, p)
                                                        .product.name
-                         )
-                    for p in product_field['options']
+                    )
+                for p in product_field['options']
                     if req.perm.has_permission('TICKET_CREATE',
                                                Neighborhood('product', p)
                                                .child(None, None))]
@@ -628,7 +611,7 @@ class QuickCreateTicketDialog(Component)
                 'fields': [all_fields[k] for k in self.qct_fields
                            if k in all_fields],
                 'hidden_fields': [all_fields[k] for k in all_fields.keys()
-                                  if k not in self.qct_fields]}
+                                  if k not in self.qct_fields] }
         return template, data, content_type
 
     # IRequestHandler methods
@@ -652,7 +635,7 @@ class QuickCreateTicketDialog(Component)
             attrs = dict([k[6:], v] for k, v in req.args.iteritems()
                          if k.startswith('field_'))
             product, tid = self.create(req, summary, desc, attrs, True)
-        except Exception as exc:
+        except Exception, exc:
             self.log.exception("BH: Quick create ticket failed %s" % (exc,))
             req.send(str(exc), 'plain/text', 500)
         else:
@@ -700,7 +683,7 @@ class QuickCreateTicketDialog(Component)
             try:
                 tn = TicketNotifyEmail(env)
                 tn.notify(t, newticket=True)
-            except Exception as e:
+            except Exception, e:
                 self.log.exception("Failure sending notification on creation "
                                    "of ticket #%s: %s" % (t.id, e))
         return t['product'], t.id
@@ -708,7 +691,6 @@ class QuickCreateTicketDialog(Component)
 from pkg_resources import get_distribution
 application_version = get_distribution('BloodhoundTheme').version
 
-
 class BatchCreateTicketDialog(Component):
     implements(
         IRequestFilter,



Re: svn commit: r1614483 - in /bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme: htdocs/js/batchcreate.js theme.py

Posted by Dammina Sahabandu <dm...@gmail.com>.
Hi Gary,
I have deferred the issue and modified the feature as you suggested.
I'll sort out the issue as soon as possible. Please review the
modifications. And FYI even though the GSoC timeline ends this month,
I hope to keep contributing to bh :)

Thanks.
Dammina


On Sat, Aug 2, 2014 at 2:37 AM, Gary Martin <ga...@wandisco.com> wrote:
> Hi,
>
> Ah, well that reminds me of another complication! Effectively milestones
> and components (among other fields) are associated with a specific
> product. You could get all the milestones and components but really you
> want to make sure that you are only allowing appropriate ones for the
> chosen product. One way to avoid this issue would be to lock down the
> creation of tickets to a specific product (based on the currently active
> product) and defer the problem until later.
>
> This is also related to why you are not getting the list of milestones
> and components out of the query. If you try the same query when you are
> in a product, you will find that the query does return information.
>
> If you want to defer the issue, you are just looking at disabling the
> functionality when self.env.product is None. Otherwise you will need to
> determine the appropriate valid set of values that fields can take as
> the product changes.
>
> I think if I were you I would defer the issue and look at sorting out
> the issue at some point shortly after the end of the GSoC project. What
> do you think?
>
> Cheers,
>     Gary
>
> On 01/08/14 20:36, Dammina Sahabandu wrote:
>> Hi Gary,
>> I started working on this improvements. There is an issue, when I
>> tried to retrieve the information in the component/milestone  tables
>> in the DB it does return an empty list(even though there are data in
>> the tables). The following are the queries that I used to retrieve
>> information.
>> products = self.env.db_query("SELECT * FROM bloodhound_product")
>> milestones = self.env.db_query("SELECT * FROM milestone")
>> components = self.env.db_query("SELECT * FROM component")
>>
>> Though it returns the product information correctly it does not return
>> component or milestone information. What can be the reason for this
>> issue?
>>
>> Thanks
>>
>> On Fri, Aug 1, 2014 at 11:15 PM, Gary Martin <ga...@wandisco.com> wrote:
>>> OK, that looks useful but I have some observations/requests
>>>
>>>  * If as a logging in user I create a table with N tickets and add
>>> information to M of the rows (where N > M) and then hit the save button, I
>>> get N - M tickets created. It would seem better to ignore the creation of
>>> the 'empty' rows. If we define the empty rows as those that do not have a
>>> summary, that might be enough.
>>>
>>>  * With the ability to remove rows you also need to be careful with any rows
>>> that have data that you are effectively removing in case it was not
>>> intended. Perhaps it would be better to give controls that allow you remove
>>> specific rows instead?
>>>
>>>  * Given the ability to add rows, it might also be sensible provide a means
>>> to clone details from the preceding row as an alternative row addition. Here
>>> I am not thinking of the summary and description which should probably be
>>> expected to be more variable than other fields but if there were milestone
>>> and component fields, these are things that might stay the same along with
>>> the product. The priority might not be so important to clone but I would say
>>> it wouldn't hurt too much. There are of course other possible ways to go
>>> that would help reduce a user's need to edit.
>>>
>>>  * The status of tickets is somewhat tied to workflow and so really the
>>> state should only be new. There may be a case for allowing it to be
>>> different but workflow is complicated so I would probably leave that until
>>> later. There is certainly a temptation to be able to provide an initial
>>> assignment of the ticket to a user which might suggest that assigned is also
>>> a valid starting state but workflow really is a bit of a minefield where you
>>> may not be able to make any certain predictions of what is definitely
>>> required at the moment.
>>>
>>>  * If you decide to drop status you could consider replacing it with
>>> component and/or milestone. In the long run it might well be useful to have
>>> a way of setting the fields that can be set but I would suspect there is no
>>> time for that.
>>>
>>> Anyway, pick things that you think you can improve on, add testing if you
>>> can and tidy up as best you can. The firm pencils down deadline looks like
>>> it is 18th August and I would hope that you really are only writing tests
>>> and improving documentation in the week before that.
>>>
>>> Cheers,
>>>     Gary
>>>
>>>
>>> On 29/07/14 21:51, Dammina Sahabandu wrote:
>>>> Earlier the batch create functionality allowed the users to batch
>>>> create certain number of tickets. But they needed to decide the number
>>>> that they are going to create initially. After that they can't change
>>>> the number. If they need they have to do the process from the
>>>> beginning. The new functionality provides the users the freedom to
>>>> change the number of tickets at any moment.
>>>>
>>>> On Wed, Jul 30, 2014 at 2:19 AM,  <da...@apache.org> wrote:
>>>>> Author: dammina
>>>>> Date: Tue Jul 29 20:49:38 2014
>>>>> New Revision: 1614483
>>>>>
>>>>> URL: http://svn.apache.org/r1614483
>>>>> Log:
>>>>> The new functionality provides the users the freedom to change the number
>>>>> of tickets at any moment.
>>>>>
>>>>> Modified:
>>>>>
>>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>>>>
>>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>>>>
>>>>> Modified:
>>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>>>> URL:
>>>>> http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js?rev=1614483&r1=1614482&r2=1614483&view=diff
>>>>>
>>>>> ==============================================================================
>>>>> ---
>>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>>>> (original)
>>>>> +++
>>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>>>> Tue Jul 29 20:49:38 2014
>>>>> @@ -175,7 +175,26 @@ function emptyTable(products,href,token)
>>>>>          }
>>>>>          table.appendChild(tbody);
>>>>>          form.appendChild(table);
>>>>> +
>>>>> +       remove_row_button = document.createElement("button");
>>>>> +       remove_row_button.setAttribute("class","btn pull-right");
>>>>> +       remove_row_button.setAttribute("type","button");
>>>>> +
>>>>> remove_row_button.setAttribute("onclick","remove_row_btn_action()");
>>>>> +       remove_row_button.setAttribute("id","bct-rmv-empty-row");
>>>>> +       remove_row_button.appendChild(document.createTextNode("-"));
>>>>> +       form.appendChild(remove_row_button);
>>>>>
>>>>> +       add_row_button = document.createElement("button");
>>>>> +       add_row_button.setAttribute("class","btn pull-right");
>>>>> +       add_row_button.setAttribute("type","button");
>>>>> +       add_row_button.addEventListener("click", function(event) {
>>>>> +               add_row_btn_action(products);
>>>>> +               event.preventDefault();
>>>>> +       });
>>>>> +       add_row_button.setAttribute("id","bct-add-empty-row");
>>>>> +       add_row_button.appendChild(document.createTextNode("+"));
>>>>> +       form.appendChild(add_row_button);
>>>>> +
>>>>>       submit_button = document.createElement("button");
>>>>>          submit_button.setAttribute("class","btn pull-right");
>>>>>          submit_button.setAttribute("type","button");
>>>>> @@ -282,3 +301,144 @@ function submit_btn_action() {
>>>>>                          contentDiv.appendChild(div);
>>>>>           });
>>>>>   }
>>>>> +
>>>>> +function add_row_btn_action(products){
>>>>> +       // alert("1");
>>>>> +
>>>>> +       var headers =
>>>>> {"summary":"Summary","description":"Description","product":"Product","status":"Status","priority":"Priority"}
>>>>> +       var statuses = ["accepted", "assigned", "closed", "new",
>>>>> "reopened"];
>>>>> +       var priorities = ["blocker", "critical", "major", "minor",
>>>>> "trivial"];
>>>>> +       var types = ["defect", "enhancement", "task"];
>>>>> +
>>>>> +    tr_rows = document.createElement("tr");
>>>>> +
>>>>> +    for (header in headers){
>>>>> +               if (header == "summary"){
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               input_summary =
>>>>> document.createElement("input");
>>>>> +
>>>>> input_summary.setAttribute("type","text");
>>>>> +
>>>>> input_summary.setAttribute("id","field-summary"+i);
>>>>> +
>>>>> input_summary.setAttribute("class","input-block-level");
>>>>> +
>>>>> input_summary.setAttribute("name","field_summary"+i);
>>>>> +                               td_row.appendChild(input_summary);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }
>>>>> +                       else if (header == "description") {
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               input_description =
>>>>> document.createElement("textarea");
>>>>> +
>>>>> input_description.setAttribute("id","field-description"+i);
>>>>> +
>>>>> input_description.setAttribute("class","input-block-level");
>>>>> +
>>>>> input_description.setAttribute("name","field_description"+i);
>>>>> +
>>>>> input_description.setAttribute("rows","2");
>>>>> +
>>>>> input_description.setAttribute("cols","28");
>>>>> +                               td_row.appendChild(input_description);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }
>>>>> +                       else if (header == "status") {
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               input_status =
>>>>> document.createElement("select");
>>>>> +
>>>>> input_status.setAttribute("id","field-status"+i);
>>>>> +
>>>>> input_status.setAttribute("class","input-block-level");
>>>>> +
>>>>> input_status.setAttribute("name","field_status"+i);
>>>>> +                               for (status in statuses){
>>>>> +                                       option =
>>>>> document.createElement("option");
>>>>> +
>>>>> option.setAttribute("value",statuses[status]);
>>>>> +
>>>>> option.appendChild(document.createTextNode(statuses[status]));
>>>>> +                                       input_status.appendChild(option);
>>>>> +                               }
>>>>> +                               td_row.appendChild(input_status);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }
>>>>> +                       else if (header == "priority") {
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               input_priority =
>>>>> document.createElement("select");
>>>>> +
>>>>> input_priority.setAttribute("id","field-priority"+i);
>>>>> +
>>>>> input_priority.setAttribute("class","input-block-level");
>>>>> +
>>>>> input_priority.setAttribute("name","field_priority"+i);
>>>>> +                               for (priority in priorities){
>>>>> +                                       option =
>>>>> document.createElement("option");
>>>>> +
>>>>> option.setAttribute("value",priorities[priority]);
>>>>> +
>>>>> option.appendChild(document.createTextNode(priorities[priority]));
>>>>> +
>>>>> input_priority.appendChild(option);
>>>>> +                               }
>>>>> +                               td_row.appendChild(input_priority);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }
>>>>> +                       /*else if (header == "type") {
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               input_type =
>>>>> document.createElement("select");
>>>>> +
>>>>> input_type.setAttribute("id","field-type"+i);
>>>>> +
>>>>> input_type.setAttribute("class","input-block-level");
>>>>> +
>>>>> input_type.setAttribute("name","field_type"+i);
>>>>> +                               for (type in types){
>>>>> +                                       option =
>>>>> document.createElement("option");
>>>>> +
>>>>> option.setAttribute("value",types[type]);
>>>>> +
>>>>> option.appendChild(document.createTextNode(types[type]));
>>>>> +                                       input_type.appendChild(option);
>>>>> +                               }
>>>>> +                               td_row.appendChild(input_type);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }*/
>>>>> +                       else if (header == "product") {
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               field_product =
>>>>> document.createElement("select");
>>>>> +
>>>>> field_product.setAttribute("id","field-product"+i);
>>>>> +
>>>>> field_product.setAttribute("class","input-block-level");
>>>>> +
>>>>> field_product.setAttribute("name","field_product"+i);
>>>>> +                               for (product in products){
>>>>> +                                       option =
>>>>> document.createElement("option");
>>>>> +
>>>>> option.setAttribute("value",(products[product])[0]);
>>>>> +
>>>>> option.appendChild(document.createTextNode((products[product])[1]));
>>>>> +
>>>>> field_product.appendChild(option);
>>>>> +                               }
>>>>> +                               td_row.appendChild(field_product);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }
>>>>> +                       /*else if (header == "owner"){
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               input_owner =
>>>>> document.createElement("input");
>>>>> +                               input_owner.setAttribute("type","text");
>>>>> +
>>>>> input_owner.setAttribute("id","field-owner"+i);
>>>>> +
>>>>> input_owner.setAttribute("class","input-block-level");
>>>>> +
>>>>> input_owner.setAttribute("name","field_owner"+i);
>>>>> +                               td_row.appendChild(input_owner);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }*/
>>>>> +                       /*else if (header == "cc"){
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               input_cc =
>>>>> document.createElement("input");
>>>>> +                               input_cc.setAttribute("type","text");
>>>>> +                               input_cc.setAttribute("id","field-cc"+i);
>>>>> +
>>>>> input_cc.setAttribute("class","input-block-level");
>>>>> +
>>>>> input_cc.setAttribute("name","field_cc"+i);
>>>>> +                               td_row.appendChild(input_cc);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }*/
>>>>> +                       /*else if (header == "milestone"){
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               input_milestone =
>>>>> document.createElement("input");
>>>>> +
>>>>> input_milestone.setAttribute("type","text");
>>>>> +
>>>>> input_milestone.setAttribute("id","field-milestone"+i);
>>>>> +
>>>>> input_milestone.setAttribute("class","input-block-level");
>>>>> +
>>>>> input_milestone.setAttribute("name","field_milestone"+i);
>>>>> +                               td_row.appendChild(input_milestone);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }*/
>>>>> +                       /*else if (header == "keywords"){
>>>>> +                               td_row = document.createElement("td");
>>>>> +                               input_keywords =
>>>>> document.createElement("input");
>>>>> +
>>>>> input_keywords.setAttribute("type","text");
>>>>> +
>>>>> input_keywords.setAttribute("id","field-keywords"+i);
>>>>> +
>>>>> input_keywords.setAttribute("class","input-block-level");
>>>>> +
>>>>> input_keywords.setAttribute("name","field_keywords"+i);
>>>>> +                               td_row.appendChild(input_keywords);
>>>>> +                               tr_rows.appendChild(td_row);
>>>>> +                       }*/
>>>>> +
>>>>> +       }
>>>>> +
>>>>> document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].appendChild(tr_rows);
>>>>> +}
>>>>> +
>>>>> +function remove_row_btn_action(){
>>>>> +
>>>>> document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].lastChild.remove();
>>>>> +}
>>>>>
>>>>> Modified:
>>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>>>> URL:
>>>>> http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py?rev=1614483&r1=1614482&r2=1614483&view=diff
>>>>>
>>>>> ==============================================================================
>>>>> ---
>>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>>>> (original)
>>>>> +++
>>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>>>> Tue Jul 29 20:49:38 2014
>>>>> @@ -36,14 +36,8 @@ from trac.util.compat import set
>>>>>   from trac.util.presentation import to_json
>>>>>   from trac.versioncontrol.web_ui.browser import BrowserModule
>>>>>   from trac.web.api import IRequestFilter, ITemplateStreamFilter
>>>>> -from trac.web.chrome import (
>>>>> -    add_stylesheet,
>>>>> -    add_warning,
>>>>> -    INavigationContributor,
>>>>> -    ITemplateProvider,
>>>>> -    prevnext_nav,
>>>>> -    Chrome,
>>>>> -    add_script)
>>>>> +from trac.web.chrome import (add_stylesheet, add_warning,
>>>>> INavigationContributor,
>>>>> +                             ITemplateProvider, prevnext_nav, Chrome,
>>>>> add_script)
>>>>>   from trac.web.main import IRequestHandler
>>>>>   from trac.wiki.admin import WikiAdmin
>>>>>   from trac.wiki.formatter import format_to_html
>>>>> @@ -63,9 +57,7 @@ try:
>>>>>   except ImportError:
>>>>>       ProductTicketModule = None
>>>>>
>>>>> -
>>>>>   class BloodhoundTheme(ThemeBase):
>>>>> -
>>>>>       """Look and feel of Bloodhound issue tracker.
>>>>>       """
>>>>>       template = htdocs = css = screenshot = disable_trac_css = True
>>>>> @@ -168,34 +160,24 @@ class BloodhoundTheme(ThemeBase):
>>>>>       )
>>>>>
>>>>>       labels_application_short = Option('labels', 'application_short',
>>>>> -                                      'Bloodhound', """A short version
>>>>> of application name most commonly
>>>>> +        'Bloodhound', """A short version of application name most
>>>>> commonly
>>>>>           displayed in text, titles and labels""", doc_domain='bhtheme')
>>>>>
>>>>>       labels_application_full = Option('labels', 'application_full',
>>>>> -                                     'Apache Bloodhound', """This is
>>>>> full name with trade mark and
>>>>> +        'Apache Bloodhound', """This is full name with trade mark and
>>>>>           everything, it is currently used in footers and about page
>>>>> only""",
>>>>>                                        doc_domain='bhtheme')
>>>>>
>>>>> -    labels_footer_left_prefix = Option(
>>>>> -        'labels',
>>>>> -        'footer_left_prefix',
>>>>> -        '',
>>>>> +    labels_footer_left_prefix = Option('labels', 'footer_left_prefix',
>>>>> '',
>>>>>           """Text to display before full application name in footers""",
>>>>> -        doc_domain='bhtheme')
>>>>> +                                       doc_domain='bhtheme')
>>>>>
>>>>> -    labels_footer_left_postfix = Option(
>>>>> -        'labels',
>>>>> -        'footer_left_postfix',
>>>>> -        '',
>>>>> +    labels_footer_left_postfix = Option('labels', 'footer_left_postfix',
>>>>> '',
>>>>>           """Text to display after full application name in footers""",
>>>>> -        doc_domain='bhtheme')
>>>>> +                                        doc_domain='bhtheme')
>>>>>
>>>>> -    labels_footer_right = Option(
>>>>> -        'labels',
>>>>> -        'footer_right',
>>>>> -        '',
>>>>> -        """Text to use as the right aligned footer""",
>>>>> -        doc_domain='bhtheme')
>>>>> +    labels_footer_right = Option('labels', 'footer_right', '',
>>>>> +        """Text to use as the right aligned footer""",
>>>>> doc_domain='bhtheme')
>>>>>
>>>>>       _wiki_pages = None
>>>>>       Chrome.default_html_doctype = DocType.HTML5
>>>>> @@ -365,13 +347,7 @@ class BloodhoundTheme(ThemeBase):
>>>>>
>>>>>       # Request modifiers
>>>>>
>>>>> -    def _modify_search_data(
>>>>> -            self,
>>>>> -            req,
>>>>> -            template,
>>>>> -            data,
>>>>> -            content_type,
>>>>> -            is_active):
>>>>> +    def _modify_search_data(self, req, template, data, content_type,
>>>>> is_active):
>>>>>           """Insert breadcumbs and context navigation items in search web
>>>>> UI
>>>>>           """
>>>>>           if is_active:
>>>>> @@ -405,7 +381,7 @@ class BloodhoundTheme(ThemeBase):
>>>>>           self._modify_resource_breadcrumb(req, template, data,
>>>>> content_type,
>>>>>                                            is_active)
>>>>>
>>>>> -        # add a creation event to the changelog if the ticket exists
>>>>> +        #add a creation event to the changelog if the ticket exists
>>>>>           ticket = data['ticket']
>>>>>           if ticket.exists:
>>>>>               data['changes'] = [{'comment': '',
>>>>> @@ -417,7 +393,7 @@ class BloodhoundTheme(ThemeBase):
>>>>>                                   'date': ticket['time'],
>>>>>                                   },
>>>>>                                  ] + data['changes']
>>>>> -        # and set default order
>>>>> +        #and set default order
>>>>>           if not req.session.get('ticket_comments_order'):
>>>>>               req.session['ticket_comments_order'] = 'newest'
>>>>>
>>>>> @@ -440,13 +416,7 @@ class BloodhoundTheme(ThemeBase):
>>>>>               if mname:
>>>>>                   data['milestone'] = Milestone(self.env, mname)
>>>>>
>>>>> -    def _modify_admin_breadcrumb(
>>>>> -            self,
>>>>> -            req,
>>>>> -            template,
>>>>> -            data,
>>>>> -            content_type,
>>>>> -            is_active):
>>>>> +    def _modify_admin_breadcrumb(self, req, template, data,
>>>>> content_type, is_active):
>>>>>           # override 'normal' product list with the admin one
>>>>>
>>>>>           def admin_url(prefix):
>>>>> @@ -495,7 +465,7 @@ class BloodhoundTheme(ThemeBase):
>>>>>                   SELECT product, value FROM bloodhound_productconfig
>>>>>                   WHERE product IN (%s) AND section='project' AND
>>>>>                   option='icon'""" % ', '.join(["%s"] * len(products)),
>>>>> -                               tuple(p.prefix for p in products))
>>>>> +                tuple(p.prefix for p in products))
>>>>>           icons = dict(icons)
>>>>>           data['thumbsize'] = 64
>>>>>           # FIXME: Gray icon for missing products
>>>>> @@ -510,29 +480,29 @@ class BloodhoundTheme(ThemeBase):
>>>>>
>>>>> product_ctx(product),
>>>>>
>>>>> product.description),
>>>>>                           links={'extras': (([{'href': req.href.products(
>>>>> -                            product.prefix, action='edit'),
>>>>> -                            'title': _('Edit product %(prefix)s',
>>>>> -                                       prefix=product.prefix),
>>>>> -                            'icon': tag.i(class_='icon-edit'),
>>>>> -                            'label': _('Edit')}, ]
>>>>> -                            if 'PRODUCT_MODIFY' in req.perm
>>>>> -                            else []) +
>>>>> -                            [{'href': product.href(),
>>>>> -                              'title': _('Home page'),
>>>>> -                              'icon': tag.i(class_='icon-home'),
>>>>> -                              'label': _('Home')},
>>>>> -                             {'href': product.href.dashboard(),
>>>>> -                              'title': _('Tickets dashboard'),
>>>>> -                              'icon': tag.i(class_='icon-tasks'),
>>>>> -                              'label': _('Tickets')},
>>>>> -                             {'href': product.href.wiki(),
>>>>> -                              'title': _('Wiki'),
>>>>> -                              'icon': tag.i(class_='icon-book'),
>>>>> -                              'label': _('Wiki')}]),
>>>>> -                'main': {'href': product.href(),
>>>>> -                         'title': None,
>>>>> -                         'icon': tag.i(class_='icon-chevron-right'),
>>>>> -                         'label': _('Browse')}})
>>>>> +                                                product.prefix,
>>>>> action='edit'),
>>>>> +                                             'title': _('Edit product
>>>>> %(prefix)s',
>>>>> +
>>>>> prefix=product.prefix),
>>>>> +                                             'icon':
>>>>> tag.i(class_='icon-edit'),
>>>>> +                                             'label': _('Edit')},]
>>>>> +                                           if 'PRODUCT_MODIFY' in
>>>>> req.perm
>>>>> +                                           else []) +
>>>>> +                                          [{'href': product.href(),
>>>>> +                                            'title': _('Home page'),
>>>>> +                                            'icon':
>>>>> tag.i(class_='icon-home'),
>>>>> +                                            'label': _('Home')},
>>>>> +                                           {'href':
>>>>> product.href.dashboard(),
>>>>> +                                            'title': _('Tickets
>>>>> dashboard'),
>>>>> +                                            'icon':
>>>>> tag.i(class_='icon-tasks'),
>>>>> +                                            'label': _('Tickets')},
>>>>> +                                           {'href': product.href.wiki(),
>>>>> +                                            'title': _('Wiki'),
>>>>> +                                            'icon':
>>>>> tag.i(class_='icon-book'),
>>>>> +                                            'label': _('Wiki')}]),
>>>>> +                               'main': {'href': product.href(),
>>>>> +                                        'title': None,
>>>>> +                                        'icon':
>>>>> tag.i(class_='icon-chevron-right'),
>>>>> +                                        'label': _('Browse')}})
>>>>>
>>>>>           data['products'] = [product_media_data(icons, product)
>>>>>                               for product in products]
>>>>> @@ -550,16 +520,29 @@ class BloodhoundTheme(ThemeBase):
>>>>>                          tag.a(_('Source'),
>>>>>
>>>>> href=req.href.wiki('TracRepositoryAdmin')))
>>>>>
>>>>> +class QCTSelectFieldUpdate(Component):
>>>>> +    implements(IRequestHandler)
>>>>> +
>>>>> +    def match_request(self, req):
>>>>> +        return req.path_info == '/update-menus'
>>>>> +
>>>>> +    def process_request(self, req):
>>>>> +        product = req.args.get('product')
>>>>> +        fields_to_update = req.args.get('fields_to_update[]');
>>>>> +        env = ProductEnvironment(self.env.parent,
>>>>> req.args.get('product'))
>>>>> +        ticket_fields = TicketSystem(env).get_ticket_fields()
>>>>> +        data = dict([f['name'], f['options']]  for f in ticket_fields
>>>>> +            if f['type'] == 'select' and f['name'] in fields_to_update)
>>>>> +        req.send(to_json(data), 'application/json')
>>>>> +
>>>>>
>>>>>   class QuickCreateTicketDialog(Component):
>>>>>       implements(IRequestFilter, IRequestHandler)
>>>>>
>>>>> -    qct_fields = ListOption(
>>>>> -        'ticket',
>>>>> -        'quick_create_fields',
>>>>> -        'product, version, type',
>>>>> +    qct_fields = ListOption('ticket', 'quick_create_fields',
>>>>> +                            'product, version, type',
>>>>>           doc="""Multiple selection fields displayed in create ticket
>>>>> menu""",
>>>>> -        doc_domain='bhtheme')
>>>>> +                            doc_domain='bhtheme')
>>>>>
>>>>>       def __init__(self, *args, **kwargs):
>>>>>           import pkg_resources
>>>>> @@ -610,8 +593,8 @@ class QuickCreateTicketDialog(Component)
>>>>>                            new_ticket_url=dum_req.href.products(p,
>>>>> 'newticket'),
>>>>>
>>>>> description=ProductEnvironment.lookup_env(self.env, p)
>>>>>                                                          .product.name
>>>>> -                         )
>>>>> -                    for p in product_field['options']
>>>>> +                    )
>>>>> +                for p in product_field['options']
>>>>>                       if req.perm.has_permission('TICKET_CREATE',
>>>>>                                                  Neighborhood('product',
>>>>> p)
>>>>>                                                  .child(None, None))]
>>>>> @@ -628,7 +611,7 @@ class QuickCreateTicketDialog(Component)
>>>>>                   'fields': [all_fields[k] for k in self.qct_fields
>>>>>                              if k in all_fields],
>>>>>                   'hidden_fields': [all_fields[k] for k in
>>>>> all_fields.keys()
>>>>> -                                  if k not in self.qct_fields]}
>>>>> +                                  if k not in self.qct_fields] }
>>>>>           return template, data, content_type
>>>>>
>>>>>       # IRequestHandler methods
>>>>> @@ -652,7 +635,7 @@ class QuickCreateTicketDialog(Component)
>>>>>               attrs = dict([k[6:], v] for k, v in req.args.iteritems()
>>>>>                            if k.startswith('field_'))
>>>>>               product, tid = self.create(req, summary, desc, attrs, True)
>>>>> -        except Exception as exc:
>>>>> +        except Exception, exc:
>>>>>               self.log.exception("BH: Quick create ticket failed %s" %
>>>>> (exc,))
>>>>>               req.send(str(exc), 'plain/text', 500)
>>>>>           else:
>>>>> @@ -700,7 +683,7 @@ class QuickCreateTicketDialog(Component)
>>>>>               try:
>>>>>                   tn = TicketNotifyEmail(env)
>>>>>                   tn.notify(t, newticket=True)
>>>>> -            except Exception as e:
>>>>> +            except Exception, e:
>>>>>                   self.log.exception("Failure sending notification on
>>>>> creation "
>>>>>                                      "of ticket #%s: %s" % (t.id, e))
>>>>>           return t['product'], t.id
>>>>> @@ -708,7 +691,6 @@ class QuickCreateTicketDialog(Component)
>>>>>   from pkg_resources import get_distribution
>>>>>   application_version = get_distribution('BloodhoundTheme').version
>>>>>
>>>>> -
>>>>>   class BatchCreateTicketDialog(Component):
>>>>>       implements(
>>>>>           IRequestFilter,
>>>>>
>>>>>
>>>>
>>
>>
>



-- 
Dammina Sahabandu.
Committer for ASF (Apache Bloodhound)
Undergraduate Department of Computer Science and Engineering
University of Moratuwa
Sri Lanka.

About multiproduct ticket id

Posted by "tianhongjie07@gmail.com" <ti...@gmail.com>.
hello all,

I want to add new column [product_ticket_id] in ticket table as group id for product.
Now wo have  three product: @, product_a, product_b, wo save data in ticket table as below:

Ticket_id .... product_ticket_id product
1                    1                         @
2                    2                         @
3                    1                          product_a
4                    1                          product_b
5                    2                          product_b
6                    3                          product_b
7                    4                          product_b

I could use database triggers to do it. 
I want to know if there are some good ideas to do it.


Thanks, 
Charlie Tian

Re: svn commit: r1614483 - in /bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme: htdocs/js/batchcreate.js theme.py

Posted by Gary Martin <ga...@wandisco.com>.
Hi,

Ah, well that reminds me of another complication! Effectively milestones
and components (among other fields) are associated with a specific
product. You could get all the milestones and components but really you
want to make sure that you are only allowing appropriate ones for the
chosen product. One way to avoid this issue would be to lock down the
creation of tickets to a specific product (based on the currently active
product) and defer the problem until later.

This is also related to why you are not getting the list of milestones
and components out of the query. If you try the same query when you are
in a product, you will find that the query does return information.

If you want to defer the issue, you are just looking at disabling the
functionality when self.env.product is None. Otherwise you will need to
determine the appropriate valid set of values that fields can take as
the product changes.

I think if I were you I would defer the issue and look at sorting out
the issue at some point shortly after the end of the GSoC project. What
do you think?

Cheers,
    Gary

On 01/08/14 20:36, Dammina Sahabandu wrote:
> Hi Gary,
> I started working on this improvements. There is an issue, when I
> tried to retrieve the information in the component/milestone  tables
> in the DB it does return an empty list(even though there are data in
> the tables). The following are the queries that I used to retrieve
> information.
> products = self.env.db_query("SELECT * FROM bloodhound_product")
> milestones = self.env.db_query("SELECT * FROM milestone")
> components = self.env.db_query("SELECT * FROM component")
>
> Though it returns the product information correctly it does not return
> component or milestone information. What can be the reason for this
> issue?
>
> Thanks
>
> On Fri, Aug 1, 2014 at 11:15 PM, Gary Martin <ga...@wandisco.com> wrote:
>> OK, that looks useful but I have some observations/requests
>>
>>  * If as a logging in user I create a table with N tickets and add
>> information to M of the rows (where N > M) and then hit the save button, I
>> get N - M tickets created. It would seem better to ignore the creation of
>> the 'empty' rows. If we define the empty rows as those that do not have a
>> summary, that might be enough.
>>
>>  * With the ability to remove rows you also need to be careful with any rows
>> that have data that you are effectively removing in case it was not
>> intended. Perhaps it would be better to give controls that allow you remove
>> specific rows instead?
>>
>>  * Given the ability to add rows, it might also be sensible provide a means
>> to clone details from the preceding row as an alternative row addition. Here
>> I am not thinking of the summary and description which should probably be
>> expected to be more variable than other fields but if there were milestone
>> and component fields, these are things that might stay the same along with
>> the product. The priority might not be so important to clone but I would say
>> it wouldn't hurt too much. There are of course other possible ways to go
>> that would help reduce a user's need to edit.
>>
>>  * The status of tickets is somewhat tied to workflow and so really the
>> state should only be new. There may be a case for allowing it to be
>> different but workflow is complicated so I would probably leave that until
>> later. There is certainly a temptation to be able to provide an initial
>> assignment of the ticket to a user which might suggest that assigned is also
>> a valid starting state but workflow really is a bit of a minefield where you
>> may not be able to make any certain predictions of what is definitely
>> required at the moment.
>>
>>  * If you decide to drop status you could consider replacing it with
>> component and/or milestone. In the long run it might well be useful to have
>> a way of setting the fields that can be set but I would suspect there is no
>> time for that.
>>
>> Anyway, pick things that you think you can improve on, add testing if you
>> can and tidy up as best you can. The firm pencils down deadline looks like
>> it is 18th August and I would hope that you really are only writing tests
>> and improving documentation in the week before that.
>>
>> Cheers,
>>     Gary
>>
>>
>> On 29/07/14 21:51, Dammina Sahabandu wrote:
>>> Earlier the batch create functionality allowed the users to batch
>>> create certain number of tickets. But they needed to decide the number
>>> that they are going to create initially. After that they can't change
>>> the number. If they need they have to do the process from the
>>> beginning. The new functionality provides the users the freedom to
>>> change the number of tickets at any moment.
>>>
>>> On Wed, Jul 30, 2014 at 2:19 AM,  <da...@apache.org> wrote:
>>>> Author: dammina
>>>> Date: Tue Jul 29 20:49:38 2014
>>>> New Revision: 1614483
>>>>
>>>> URL: http://svn.apache.org/r1614483
>>>> Log:
>>>> The new functionality provides the users the freedom to change the number
>>>> of tickets at any moment.
>>>>
>>>> Modified:
>>>>
>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>>>
>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>>>
>>>> Modified:
>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>>> URL:
>>>> http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js?rev=1614483&r1=1614482&r2=1614483&view=diff
>>>>
>>>> ==============================================================================
>>>> ---
>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>>> (original)
>>>> +++
>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>>> Tue Jul 29 20:49:38 2014
>>>> @@ -175,7 +175,26 @@ function emptyTable(products,href,token)
>>>>          }
>>>>          table.appendChild(tbody);
>>>>          form.appendChild(table);
>>>> +
>>>> +       remove_row_button = document.createElement("button");
>>>> +       remove_row_button.setAttribute("class","btn pull-right");
>>>> +       remove_row_button.setAttribute("type","button");
>>>> +
>>>> remove_row_button.setAttribute("onclick","remove_row_btn_action()");
>>>> +       remove_row_button.setAttribute("id","bct-rmv-empty-row");
>>>> +       remove_row_button.appendChild(document.createTextNode("-"));
>>>> +       form.appendChild(remove_row_button);
>>>>
>>>> +       add_row_button = document.createElement("button");
>>>> +       add_row_button.setAttribute("class","btn pull-right");
>>>> +       add_row_button.setAttribute("type","button");
>>>> +       add_row_button.addEventListener("click", function(event) {
>>>> +               add_row_btn_action(products);
>>>> +               event.preventDefault();
>>>> +       });
>>>> +       add_row_button.setAttribute("id","bct-add-empty-row");
>>>> +       add_row_button.appendChild(document.createTextNode("+"));
>>>> +       form.appendChild(add_row_button);
>>>> +
>>>>       submit_button = document.createElement("button");
>>>>          submit_button.setAttribute("class","btn pull-right");
>>>>          submit_button.setAttribute("type","button");
>>>> @@ -282,3 +301,144 @@ function submit_btn_action() {
>>>>                          contentDiv.appendChild(div);
>>>>           });
>>>>   }
>>>> +
>>>> +function add_row_btn_action(products){
>>>> +       // alert("1");
>>>> +
>>>> +       var headers =
>>>> {"summary":"Summary","description":"Description","product":"Product","status":"Status","priority":"Priority"}
>>>> +       var statuses = ["accepted", "assigned", "closed", "new",
>>>> "reopened"];
>>>> +       var priorities = ["blocker", "critical", "major", "minor",
>>>> "trivial"];
>>>> +       var types = ["defect", "enhancement", "task"];
>>>> +
>>>> +    tr_rows = document.createElement("tr");
>>>> +
>>>> +    for (header in headers){
>>>> +               if (header == "summary"){
>>>> +                               td_row = document.createElement("td");
>>>> +                               input_summary =
>>>> document.createElement("input");
>>>> +
>>>> input_summary.setAttribute("type","text");
>>>> +
>>>> input_summary.setAttribute("id","field-summary"+i);
>>>> +
>>>> input_summary.setAttribute("class","input-block-level");
>>>> +
>>>> input_summary.setAttribute("name","field_summary"+i);
>>>> +                               td_row.appendChild(input_summary);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }
>>>> +                       else if (header == "description") {
>>>> +                               td_row = document.createElement("td");
>>>> +                               input_description =
>>>> document.createElement("textarea");
>>>> +
>>>> input_description.setAttribute("id","field-description"+i);
>>>> +
>>>> input_description.setAttribute("class","input-block-level");
>>>> +
>>>> input_description.setAttribute("name","field_description"+i);
>>>> +
>>>> input_description.setAttribute("rows","2");
>>>> +
>>>> input_description.setAttribute("cols","28");
>>>> +                               td_row.appendChild(input_description);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }
>>>> +                       else if (header == "status") {
>>>> +                               td_row = document.createElement("td");
>>>> +                               input_status =
>>>> document.createElement("select");
>>>> +
>>>> input_status.setAttribute("id","field-status"+i);
>>>> +
>>>> input_status.setAttribute("class","input-block-level");
>>>> +
>>>> input_status.setAttribute("name","field_status"+i);
>>>> +                               for (status in statuses){
>>>> +                                       option =
>>>> document.createElement("option");
>>>> +
>>>> option.setAttribute("value",statuses[status]);
>>>> +
>>>> option.appendChild(document.createTextNode(statuses[status]));
>>>> +                                       input_status.appendChild(option);
>>>> +                               }
>>>> +                               td_row.appendChild(input_status);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }
>>>> +                       else if (header == "priority") {
>>>> +                               td_row = document.createElement("td");
>>>> +                               input_priority =
>>>> document.createElement("select");
>>>> +
>>>> input_priority.setAttribute("id","field-priority"+i);
>>>> +
>>>> input_priority.setAttribute("class","input-block-level");
>>>> +
>>>> input_priority.setAttribute("name","field_priority"+i);
>>>> +                               for (priority in priorities){
>>>> +                                       option =
>>>> document.createElement("option");
>>>> +
>>>> option.setAttribute("value",priorities[priority]);
>>>> +
>>>> option.appendChild(document.createTextNode(priorities[priority]));
>>>> +
>>>> input_priority.appendChild(option);
>>>> +                               }
>>>> +                               td_row.appendChild(input_priority);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }
>>>> +                       /*else if (header == "type") {
>>>> +                               td_row = document.createElement("td");
>>>> +                               input_type =
>>>> document.createElement("select");
>>>> +
>>>> input_type.setAttribute("id","field-type"+i);
>>>> +
>>>> input_type.setAttribute("class","input-block-level");
>>>> +
>>>> input_type.setAttribute("name","field_type"+i);
>>>> +                               for (type in types){
>>>> +                                       option =
>>>> document.createElement("option");
>>>> +
>>>> option.setAttribute("value",types[type]);
>>>> +
>>>> option.appendChild(document.createTextNode(types[type]));
>>>> +                                       input_type.appendChild(option);
>>>> +                               }
>>>> +                               td_row.appendChild(input_type);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }*/
>>>> +                       else if (header == "product") {
>>>> +                               td_row = document.createElement("td");
>>>> +                               field_product =
>>>> document.createElement("select");
>>>> +
>>>> field_product.setAttribute("id","field-product"+i);
>>>> +
>>>> field_product.setAttribute("class","input-block-level");
>>>> +
>>>> field_product.setAttribute("name","field_product"+i);
>>>> +                               for (product in products){
>>>> +                                       option =
>>>> document.createElement("option");
>>>> +
>>>> option.setAttribute("value",(products[product])[0]);
>>>> +
>>>> option.appendChild(document.createTextNode((products[product])[1]));
>>>> +
>>>> field_product.appendChild(option);
>>>> +                               }
>>>> +                               td_row.appendChild(field_product);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }
>>>> +                       /*else if (header == "owner"){
>>>> +                               td_row = document.createElement("td");
>>>> +                               input_owner =
>>>> document.createElement("input");
>>>> +                               input_owner.setAttribute("type","text");
>>>> +
>>>> input_owner.setAttribute("id","field-owner"+i);
>>>> +
>>>> input_owner.setAttribute("class","input-block-level");
>>>> +
>>>> input_owner.setAttribute("name","field_owner"+i);
>>>> +                               td_row.appendChild(input_owner);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }*/
>>>> +                       /*else if (header == "cc"){
>>>> +                               td_row = document.createElement("td");
>>>> +                               input_cc =
>>>> document.createElement("input");
>>>> +                               input_cc.setAttribute("type","text");
>>>> +                               input_cc.setAttribute("id","field-cc"+i);
>>>> +
>>>> input_cc.setAttribute("class","input-block-level");
>>>> +
>>>> input_cc.setAttribute("name","field_cc"+i);
>>>> +                               td_row.appendChild(input_cc);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }*/
>>>> +                       /*else if (header == "milestone"){
>>>> +                               td_row = document.createElement("td");
>>>> +                               input_milestone =
>>>> document.createElement("input");
>>>> +
>>>> input_milestone.setAttribute("type","text");
>>>> +
>>>> input_milestone.setAttribute("id","field-milestone"+i);
>>>> +
>>>> input_milestone.setAttribute("class","input-block-level");
>>>> +
>>>> input_milestone.setAttribute("name","field_milestone"+i);
>>>> +                               td_row.appendChild(input_milestone);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }*/
>>>> +                       /*else if (header == "keywords"){
>>>> +                               td_row = document.createElement("td");
>>>> +                               input_keywords =
>>>> document.createElement("input");
>>>> +
>>>> input_keywords.setAttribute("type","text");
>>>> +
>>>> input_keywords.setAttribute("id","field-keywords"+i);
>>>> +
>>>> input_keywords.setAttribute("class","input-block-level");
>>>> +
>>>> input_keywords.setAttribute("name","field_keywords"+i);
>>>> +                               td_row.appendChild(input_keywords);
>>>> +                               tr_rows.appendChild(td_row);
>>>> +                       }*/
>>>> +
>>>> +       }
>>>> +
>>>> document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].appendChild(tr_rows);
>>>> +}
>>>> +
>>>> +function remove_row_btn_action(){
>>>> +
>>>> document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].lastChild.remove();
>>>> +}
>>>>
>>>> Modified:
>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>>> URL:
>>>> http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py?rev=1614483&r1=1614482&r2=1614483&view=diff
>>>>
>>>> ==============================================================================
>>>> ---
>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>>> (original)
>>>> +++
>>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>>> Tue Jul 29 20:49:38 2014
>>>> @@ -36,14 +36,8 @@ from trac.util.compat import set
>>>>   from trac.util.presentation import to_json
>>>>   from trac.versioncontrol.web_ui.browser import BrowserModule
>>>>   from trac.web.api import IRequestFilter, ITemplateStreamFilter
>>>> -from trac.web.chrome import (
>>>> -    add_stylesheet,
>>>> -    add_warning,
>>>> -    INavigationContributor,
>>>> -    ITemplateProvider,
>>>> -    prevnext_nav,
>>>> -    Chrome,
>>>> -    add_script)
>>>> +from trac.web.chrome import (add_stylesheet, add_warning,
>>>> INavigationContributor,
>>>> +                             ITemplateProvider, prevnext_nav, Chrome,
>>>> add_script)
>>>>   from trac.web.main import IRequestHandler
>>>>   from trac.wiki.admin import WikiAdmin
>>>>   from trac.wiki.formatter import format_to_html
>>>> @@ -63,9 +57,7 @@ try:
>>>>   except ImportError:
>>>>       ProductTicketModule = None
>>>>
>>>> -
>>>>   class BloodhoundTheme(ThemeBase):
>>>> -
>>>>       """Look and feel of Bloodhound issue tracker.
>>>>       """
>>>>       template = htdocs = css = screenshot = disable_trac_css = True
>>>> @@ -168,34 +160,24 @@ class BloodhoundTheme(ThemeBase):
>>>>       )
>>>>
>>>>       labels_application_short = Option('labels', 'application_short',
>>>> -                                      'Bloodhound', """A short version
>>>> of application name most commonly
>>>> +        'Bloodhound', """A short version of application name most
>>>> commonly
>>>>           displayed in text, titles and labels""", doc_domain='bhtheme')
>>>>
>>>>       labels_application_full = Option('labels', 'application_full',
>>>> -                                     'Apache Bloodhound', """This is
>>>> full name with trade mark and
>>>> +        'Apache Bloodhound', """This is full name with trade mark and
>>>>           everything, it is currently used in footers and about page
>>>> only""",
>>>>                                        doc_domain='bhtheme')
>>>>
>>>> -    labels_footer_left_prefix = Option(
>>>> -        'labels',
>>>> -        'footer_left_prefix',
>>>> -        '',
>>>> +    labels_footer_left_prefix = Option('labels', 'footer_left_prefix',
>>>> '',
>>>>           """Text to display before full application name in footers""",
>>>> -        doc_domain='bhtheme')
>>>> +                                       doc_domain='bhtheme')
>>>>
>>>> -    labels_footer_left_postfix = Option(
>>>> -        'labels',
>>>> -        'footer_left_postfix',
>>>> -        '',
>>>> +    labels_footer_left_postfix = Option('labels', 'footer_left_postfix',
>>>> '',
>>>>           """Text to display after full application name in footers""",
>>>> -        doc_domain='bhtheme')
>>>> +                                        doc_domain='bhtheme')
>>>>
>>>> -    labels_footer_right = Option(
>>>> -        'labels',
>>>> -        'footer_right',
>>>> -        '',
>>>> -        """Text to use as the right aligned footer""",
>>>> -        doc_domain='bhtheme')
>>>> +    labels_footer_right = Option('labels', 'footer_right', '',
>>>> +        """Text to use as the right aligned footer""",
>>>> doc_domain='bhtheme')
>>>>
>>>>       _wiki_pages = None
>>>>       Chrome.default_html_doctype = DocType.HTML5
>>>> @@ -365,13 +347,7 @@ class BloodhoundTheme(ThemeBase):
>>>>
>>>>       # Request modifiers
>>>>
>>>> -    def _modify_search_data(
>>>> -            self,
>>>> -            req,
>>>> -            template,
>>>> -            data,
>>>> -            content_type,
>>>> -            is_active):
>>>> +    def _modify_search_data(self, req, template, data, content_type,
>>>> is_active):
>>>>           """Insert breadcumbs and context navigation items in search web
>>>> UI
>>>>           """
>>>>           if is_active:
>>>> @@ -405,7 +381,7 @@ class BloodhoundTheme(ThemeBase):
>>>>           self._modify_resource_breadcrumb(req, template, data,
>>>> content_type,
>>>>                                            is_active)
>>>>
>>>> -        # add a creation event to the changelog if the ticket exists
>>>> +        #add a creation event to the changelog if the ticket exists
>>>>           ticket = data['ticket']
>>>>           if ticket.exists:
>>>>               data['changes'] = [{'comment': '',
>>>> @@ -417,7 +393,7 @@ class BloodhoundTheme(ThemeBase):
>>>>                                   'date': ticket['time'],
>>>>                                   },
>>>>                                  ] + data['changes']
>>>> -        # and set default order
>>>> +        #and set default order
>>>>           if not req.session.get('ticket_comments_order'):
>>>>               req.session['ticket_comments_order'] = 'newest'
>>>>
>>>> @@ -440,13 +416,7 @@ class BloodhoundTheme(ThemeBase):
>>>>               if mname:
>>>>                   data['milestone'] = Milestone(self.env, mname)
>>>>
>>>> -    def _modify_admin_breadcrumb(
>>>> -            self,
>>>> -            req,
>>>> -            template,
>>>> -            data,
>>>> -            content_type,
>>>> -            is_active):
>>>> +    def _modify_admin_breadcrumb(self, req, template, data,
>>>> content_type, is_active):
>>>>           # override 'normal' product list with the admin one
>>>>
>>>>           def admin_url(prefix):
>>>> @@ -495,7 +465,7 @@ class BloodhoundTheme(ThemeBase):
>>>>                   SELECT product, value FROM bloodhound_productconfig
>>>>                   WHERE product IN (%s) AND section='project' AND
>>>>                   option='icon'""" % ', '.join(["%s"] * len(products)),
>>>> -                               tuple(p.prefix for p in products))
>>>> +                tuple(p.prefix for p in products))
>>>>           icons = dict(icons)
>>>>           data['thumbsize'] = 64
>>>>           # FIXME: Gray icon for missing products
>>>> @@ -510,29 +480,29 @@ class BloodhoundTheme(ThemeBase):
>>>>
>>>> product_ctx(product),
>>>>
>>>> product.description),
>>>>                           links={'extras': (([{'href': req.href.products(
>>>> -                            product.prefix, action='edit'),
>>>> -                            'title': _('Edit product %(prefix)s',
>>>> -                                       prefix=product.prefix),
>>>> -                            'icon': tag.i(class_='icon-edit'),
>>>> -                            'label': _('Edit')}, ]
>>>> -                            if 'PRODUCT_MODIFY' in req.perm
>>>> -                            else []) +
>>>> -                            [{'href': product.href(),
>>>> -                              'title': _('Home page'),
>>>> -                              'icon': tag.i(class_='icon-home'),
>>>> -                              'label': _('Home')},
>>>> -                             {'href': product.href.dashboard(),
>>>> -                              'title': _('Tickets dashboard'),
>>>> -                              'icon': tag.i(class_='icon-tasks'),
>>>> -                              'label': _('Tickets')},
>>>> -                             {'href': product.href.wiki(),
>>>> -                              'title': _('Wiki'),
>>>> -                              'icon': tag.i(class_='icon-book'),
>>>> -                              'label': _('Wiki')}]),
>>>> -                'main': {'href': product.href(),
>>>> -                         'title': None,
>>>> -                         'icon': tag.i(class_='icon-chevron-right'),
>>>> -                         'label': _('Browse')}})
>>>> +                                                product.prefix,
>>>> action='edit'),
>>>> +                                             'title': _('Edit product
>>>> %(prefix)s',
>>>> +
>>>> prefix=product.prefix),
>>>> +                                             'icon':
>>>> tag.i(class_='icon-edit'),
>>>> +                                             'label': _('Edit')},]
>>>> +                                           if 'PRODUCT_MODIFY' in
>>>> req.perm
>>>> +                                           else []) +
>>>> +                                          [{'href': product.href(),
>>>> +                                            'title': _('Home page'),
>>>> +                                            'icon':
>>>> tag.i(class_='icon-home'),
>>>> +                                            'label': _('Home')},
>>>> +                                           {'href':
>>>> product.href.dashboard(),
>>>> +                                            'title': _('Tickets
>>>> dashboard'),
>>>> +                                            'icon':
>>>> tag.i(class_='icon-tasks'),
>>>> +                                            'label': _('Tickets')},
>>>> +                                           {'href': product.href.wiki(),
>>>> +                                            'title': _('Wiki'),
>>>> +                                            'icon':
>>>> tag.i(class_='icon-book'),
>>>> +                                            'label': _('Wiki')}]),
>>>> +                               'main': {'href': product.href(),
>>>> +                                        'title': None,
>>>> +                                        'icon':
>>>> tag.i(class_='icon-chevron-right'),
>>>> +                                        'label': _('Browse')}})
>>>>
>>>>           data['products'] = [product_media_data(icons, product)
>>>>                               for product in products]
>>>> @@ -550,16 +520,29 @@ class BloodhoundTheme(ThemeBase):
>>>>                          tag.a(_('Source'),
>>>>
>>>> href=req.href.wiki('TracRepositoryAdmin')))
>>>>
>>>> +class QCTSelectFieldUpdate(Component):
>>>> +    implements(IRequestHandler)
>>>> +
>>>> +    def match_request(self, req):
>>>> +        return req.path_info == '/update-menus'
>>>> +
>>>> +    def process_request(self, req):
>>>> +        product = req.args.get('product')
>>>> +        fields_to_update = req.args.get('fields_to_update[]');
>>>> +        env = ProductEnvironment(self.env.parent,
>>>> req.args.get('product'))
>>>> +        ticket_fields = TicketSystem(env).get_ticket_fields()
>>>> +        data = dict([f['name'], f['options']]  for f in ticket_fields
>>>> +            if f['type'] == 'select' and f['name'] in fields_to_update)
>>>> +        req.send(to_json(data), 'application/json')
>>>> +
>>>>
>>>>   class QuickCreateTicketDialog(Component):
>>>>       implements(IRequestFilter, IRequestHandler)
>>>>
>>>> -    qct_fields = ListOption(
>>>> -        'ticket',
>>>> -        'quick_create_fields',
>>>> -        'product, version, type',
>>>> +    qct_fields = ListOption('ticket', 'quick_create_fields',
>>>> +                            'product, version, type',
>>>>           doc="""Multiple selection fields displayed in create ticket
>>>> menu""",
>>>> -        doc_domain='bhtheme')
>>>> +                            doc_domain='bhtheme')
>>>>
>>>>       def __init__(self, *args, **kwargs):
>>>>           import pkg_resources
>>>> @@ -610,8 +593,8 @@ class QuickCreateTicketDialog(Component)
>>>>                            new_ticket_url=dum_req.href.products(p,
>>>> 'newticket'),
>>>>
>>>> description=ProductEnvironment.lookup_env(self.env, p)
>>>>                                                          .product.name
>>>> -                         )
>>>> -                    for p in product_field['options']
>>>> +                    )
>>>> +                for p in product_field['options']
>>>>                       if req.perm.has_permission('TICKET_CREATE',
>>>>                                                  Neighborhood('product',
>>>> p)
>>>>                                                  .child(None, None))]
>>>> @@ -628,7 +611,7 @@ class QuickCreateTicketDialog(Component)
>>>>                   'fields': [all_fields[k] for k in self.qct_fields
>>>>                              if k in all_fields],
>>>>                   'hidden_fields': [all_fields[k] for k in
>>>> all_fields.keys()
>>>> -                                  if k not in self.qct_fields]}
>>>> +                                  if k not in self.qct_fields] }
>>>>           return template, data, content_type
>>>>
>>>>       # IRequestHandler methods
>>>> @@ -652,7 +635,7 @@ class QuickCreateTicketDialog(Component)
>>>>               attrs = dict([k[6:], v] for k, v in req.args.iteritems()
>>>>                            if k.startswith('field_'))
>>>>               product, tid = self.create(req, summary, desc, attrs, True)
>>>> -        except Exception as exc:
>>>> +        except Exception, exc:
>>>>               self.log.exception("BH: Quick create ticket failed %s" %
>>>> (exc,))
>>>>               req.send(str(exc), 'plain/text', 500)
>>>>           else:
>>>> @@ -700,7 +683,7 @@ class QuickCreateTicketDialog(Component)
>>>>               try:
>>>>                   tn = TicketNotifyEmail(env)
>>>>                   tn.notify(t, newticket=True)
>>>> -            except Exception as e:
>>>> +            except Exception, e:
>>>>                   self.log.exception("Failure sending notification on
>>>> creation "
>>>>                                      "of ticket #%s: %s" % (t.id, e))
>>>>           return t['product'], t.id
>>>> @@ -708,7 +691,6 @@ class QuickCreateTicketDialog(Component)
>>>>   from pkg_resources import get_distribution
>>>>   application_version = get_distribution('BloodhoundTheme').version
>>>>
>>>> -
>>>>   class BatchCreateTicketDialog(Component):
>>>>       implements(
>>>>           IRequestFilter,
>>>>
>>>>
>>>
>
>


Re: svn commit: r1614483 - in /bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme: htdocs/js/batchcreate.js theme.py

Posted by Dammina Sahabandu <dm...@gmail.com>.
Hi Gary,
I started working on this improvements. There is an issue, when I
tried to retrieve the information in the component/milestone  tables
in the DB it does return an empty list(even though there are data in
the tables). The following are the queries that I used to retrieve
information.
products = self.env.db_query("SELECT * FROM bloodhound_product")
milestones = self.env.db_query("SELECT * FROM milestone")
components = self.env.db_query("SELECT * FROM component")

Though it returns the product information correctly it does not return
component or milestone information. What can be the reason for this
issue?

Thanks

On Fri, Aug 1, 2014 at 11:15 PM, Gary Martin <ga...@wandisco.com> wrote:
> OK, that looks useful but I have some observations/requests
>
>  * If as a logging in user I create a table with N tickets and add
> information to M of the rows (where N > M) and then hit the save button, I
> get N - M tickets created. It would seem better to ignore the creation of
> the 'empty' rows. If we define the empty rows as those that do not have a
> summary, that might be enough.
>
>  * With the ability to remove rows you also need to be careful with any rows
> that have data that you are effectively removing in case it was not
> intended. Perhaps it would be better to give controls that allow you remove
> specific rows instead?
>
>  * Given the ability to add rows, it might also be sensible provide a means
> to clone details from the preceding row as an alternative row addition. Here
> I am not thinking of the summary and description which should probably be
> expected to be more variable than other fields but if there were milestone
> and component fields, these are things that might stay the same along with
> the product. The priority might not be so important to clone but I would say
> it wouldn't hurt too much. There are of course other possible ways to go
> that would help reduce a user's need to edit.
>
>  * The status of tickets is somewhat tied to workflow and so really the
> state should only be new. There may be a case for allowing it to be
> different but workflow is complicated so I would probably leave that until
> later. There is certainly a temptation to be able to provide an initial
> assignment of the ticket to a user which might suggest that assigned is also
> a valid starting state but workflow really is a bit of a minefield where you
> may not be able to make any certain predictions of what is definitely
> required at the moment.
>
>  * If you decide to drop status you could consider replacing it with
> component and/or milestone. In the long run it might well be useful to have
> a way of setting the fields that can be set but I would suspect there is no
> time for that.
>
> Anyway, pick things that you think you can improve on, add testing if you
> can and tidy up as best you can. The firm pencils down deadline looks like
> it is 18th August and I would hope that you really are only writing tests
> and improving documentation in the week before that.
>
> Cheers,
>     Gary
>
>
> On 29/07/14 21:51, Dammina Sahabandu wrote:
>>
>> Earlier the batch create functionality allowed the users to batch
>> create certain number of tickets. But they needed to decide the number
>> that they are going to create initially. After that they can't change
>> the number. If they need they have to do the process from the
>> beginning. The new functionality provides the users the freedom to
>> change the number of tickets at any moment.
>>
>> On Wed, Jul 30, 2014 at 2:19 AM,  <da...@apache.org> wrote:
>>>
>>> Author: dammina
>>> Date: Tue Jul 29 20:49:38 2014
>>> New Revision: 1614483
>>>
>>> URL: http://svn.apache.org/r1614483
>>> Log:
>>> The new functionality provides the users the freedom to change the number
>>> of tickets at any moment.
>>>
>>> Modified:
>>>
>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>>
>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>>
>>> Modified:
>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>> URL:
>>> http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js?rev=1614483&r1=1614482&r2=1614483&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>> (original)
>>> +++
>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>> Tue Jul 29 20:49:38 2014
>>> @@ -175,7 +175,26 @@ function emptyTable(products,href,token)
>>>          }
>>>          table.appendChild(tbody);
>>>          form.appendChild(table);
>>> +
>>> +       remove_row_button = document.createElement("button");
>>> +       remove_row_button.setAttribute("class","btn pull-right");
>>> +       remove_row_button.setAttribute("type","button");
>>> +
>>> remove_row_button.setAttribute("onclick","remove_row_btn_action()");
>>> +       remove_row_button.setAttribute("id","bct-rmv-empty-row");
>>> +       remove_row_button.appendChild(document.createTextNode("-"));
>>> +       form.appendChild(remove_row_button);
>>>
>>> +       add_row_button = document.createElement("button");
>>> +       add_row_button.setAttribute("class","btn pull-right");
>>> +       add_row_button.setAttribute("type","button");
>>> +       add_row_button.addEventListener("click", function(event) {
>>> +               add_row_btn_action(products);
>>> +               event.preventDefault();
>>> +       });
>>> +       add_row_button.setAttribute("id","bct-add-empty-row");
>>> +       add_row_button.appendChild(document.createTextNode("+"));
>>> +       form.appendChild(add_row_button);
>>> +
>>>       submit_button = document.createElement("button");
>>>          submit_button.setAttribute("class","btn pull-right");
>>>          submit_button.setAttribute("type","button");
>>> @@ -282,3 +301,144 @@ function submit_btn_action() {
>>>                          contentDiv.appendChild(div);
>>>           });
>>>   }
>>> +
>>> +function add_row_btn_action(products){
>>> +       // alert("1");
>>> +
>>> +       var headers =
>>> {"summary":"Summary","description":"Description","product":"Product","status":"Status","priority":"Priority"}
>>> +       var statuses = ["accepted", "assigned", "closed", "new",
>>> "reopened"];
>>> +       var priorities = ["blocker", "critical", "major", "minor",
>>> "trivial"];
>>> +       var types = ["defect", "enhancement", "task"];
>>> +
>>> +    tr_rows = document.createElement("tr");
>>> +
>>> +    for (header in headers){
>>> +               if (header == "summary"){
>>> +                               td_row = document.createElement("td");
>>> +                               input_summary =
>>> document.createElement("input");
>>> +
>>> input_summary.setAttribute("type","text");
>>> +
>>> input_summary.setAttribute("id","field-summary"+i);
>>> +
>>> input_summary.setAttribute("class","input-block-level");
>>> +
>>> input_summary.setAttribute("name","field_summary"+i);
>>> +                               td_row.appendChild(input_summary);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }
>>> +                       else if (header == "description") {
>>> +                               td_row = document.createElement("td");
>>> +                               input_description =
>>> document.createElement("textarea");
>>> +
>>> input_description.setAttribute("id","field-description"+i);
>>> +
>>> input_description.setAttribute("class","input-block-level");
>>> +
>>> input_description.setAttribute("name","field_description"+i);
>>> +
>>> input_description.setAttribute("rows","2");
>>> +
>>> input_description.setAttribute("cols","28");
>>> +                               td_row.appendChild(input_description);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }
>>> +                       else if (header == "status") {
>>> +                               td_row = document.createElement("td");
>>> +                               input_status =
>>> document.createElement("select");
>>> +
>>> input_status.setAttribute("id","field-status"+i);
>>> +
>>> input_status.setAttribute("class","input-block-level");
>>> +
>>> input_status.setAttribute("name","field_status"+i);
>>> +                               for (status in statuses){
>>> +                                       option =
>>> document.createElement("option");
>>> +
>>> option.setAttribute("value",statuses[status]);
>>> +
>>> option.appendChild(document.createTextNode(statuses[status]));
>>> +                                       input_status.appendChild(option);
>>> +                               }
>>> +                               td_row.appendChild(input_status);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }
>>> +                       else if (header == "priority") {
>>> +                               td_row = document.createElement("td");
>>> +                               input_priority =
>>> document.createElement("select");
>>> +
>>> input_priority.setAttribute("id","field-priority"+i);
>>> +
>>> input_priority.setAttribute("class","input-block-level");
>>> +
>>> input_priority.setAttribute("name","field_priority"+i);
>>> +                               for (priority in priorities){
>>> +                                       option =
>>> document.createElement("option");
>>> +
>>> option.setAttribute("value",priorities[priority]);
>>> +
>>> option.appendChild(document.createTextNode(priorities[priority]));
>>> +
>>> input_priority.appendChild(option);
>>> +                               }
>>> +                               td_row.appendChild(input_priority);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }
>>> +                       /*else if (header == "type") {
>>> +                               td_row = document.createElement("td");
>>> +                               input_type =
>>> document.createElement("select");
>>> +
>>> input_type.setAttribute("id","field-type"+i);
>>> +
>>> input_type.setAttribute("class","input-block-level");
>>> +
>>> input_type.setAttribute("name","field_type"+i);
>>> +                               for (type in types){
>>> +                                       option =
>>> document.createElement("option");
>>> +
>>> option.setAttribute("value",types[type]);
>>> +
>>> option.appendChild(document.createTextNode(types[type]));
>>> +                                       input_type.appendChild(option);
>>> +                               }
>>> +                               td_row.appendChild(input_type);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }*/
>>> +                       else if (header == "product") {
>>> +                               td_row = document.createElement("td");
>>> +                               field_product =
>>> document.createElement("select");
>>> +
>>> field_product.setAttribute("id","field-product"+i);
>>> +
>>> field_product.setAttribute("class","input-block-level");
>>> +
>>> field_product.setAttribute("name","field_product"+i);
>>> +                               for (product in products){
>>> +                                       option =
>>> document.createElement("option");
>>> +
>>> option.setAttribute("value",(products[product])[0]);
>>> +
>>> option.appendChild(document.createTextNode((products[product])[1]));
>>> +
>>> field_product.appendChild(option);
>>> +                               }
>>> +                               td_row.appendChild(field_product);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }
>>> +                       /*else if (header == "owner"){
>>> +                               td_row = document.createElement("td");
>>> +                               input_owner =
>>> document.createElement("input");
>>> +                               input_owner.setAttribute("type","text");
>>> +
>>> input_owner.setAttribute("id","field-owner"+i);
>>> +
>>> input_owner.setAttribute("class","input-block-level");
>>> +
>>> input_owner.setAttribute("name","field_owner"+i);
>>> +                               td_row.appendChild(input_owner);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }*/
>>> +                       /*else if (header == "cc"){
>>> +                               td_row = document.createElement("td");
>>> +                               input_cc =
>>> document.createElement("input");
>>> +                               input_cc.setAttribute("type","text");
>>> +                               input_cc.setAttribute("id","field-cc"+i);
>>> +
>>> input_cc.setAttribute("class","input-block-level");
>>> +
>>> input_cc.setAttribute("name","field_cc"+i);
>>> +                               td_row.appendChild(input_cc);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }*/
>>> +                       /*else if (header == "milestone"){
>>> +                               td_row = document.createElement("td");
>>> +                               input_milestone =
>>> document.createElement("input");
>>> +
>>> input_milestone.setAttribute("type","text");
>>> +
>>> input_milestone.setAttribute("id","field-milestone"+i);
>>> +
>>> input_milestone.setAttribute("class","input-block-level");
>>> +
>>> input_milestone.setAttribute("name","field_milestone"+i);
>>> +                               td_row.appendChild(input_milestone);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }*/
>>> +                       /*else if (header == "keywords"){
>>> +                               td_row = document.createElement("td");
>>> +                               input_keywords =
>>> document.createElement("input");
>>> +
>>> input_keywords.setAttribute("type","text");
>>> +
>>> input_keywords.setAttribute("id","field-keywords"+i);
>>> +
>>> input_keywords.setAttribute("class","input-block-level");
>>> +
>>> input_keywords.setAttribute("name","field_keywords"+i);
>>> +                               td_row.appendChild(input_keywords);
>>> +                               tr_rows.appendChild(td_row);
>>> +                       }*/
>>> +
>>> +       }
>>> +
>>> document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].appendChild(tr_rows);
>>> +}
>>> +
>>> +function remove_row_btn_action(){
>>> +
>>> document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].lastChild.remove();
>>> +}
>>>
>>> Modified:
>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>> URL:
>>> http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py?rev=1614483&r1=1614482&r2=1614483&view=diff
>>>
>>> ==============================================================================
>>> ---
>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>> (original)
>>> +++
>>> bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>> Tue Jul 29 20:49:38 2014
>>> @@ -36,14 +36,8 @@ from trac.util.compat import set
>>>   from trac.util.presentation import to_json
>>>   from trac.versioncontrol.web_ui.browser import BrowserModule
>>>   from trac.web.api import IRequestFilter, ITemplateStreamFilter
>>> -from trac.web.chrome import (
>>> -    add_stylesheet,
>>> -    add_warning,
>>> -    INavigationContributor,
>>> -    ITemplateProvider,
>>> -    prevnext_nav,
>>> -    Chrome,
>>> -    add_script)
>>> +from trac.web.chrome import (add_stylesheet, add_warning,
>>> INavigationContributor,
>>> +                             ITemplateProvider, prevnext_nav, Chrome,
>>> add_script)
>>>   from trac.web.main import IRequestHandler
>>>   from trac.wiki.admin import WikiAdmin
>>>   from trac.wiki.formatter import format_to_html
>>> @@ -63,9 +57,7 @@ try:
>>>   except ImportError:
>>>       ProductTicketModule = None
>>>
>>> -
>>>   class BloodhoundTheme(ThemeBase):
>>> -
>>>       """Look and feel of Bloodhound issue tracker.
>>>       """
>>>       template = htdocs = css = screenshot = disable_trac_css = True
>>> @@ -168,34 +160,24 @@ class BloodhoundTheme(ThemeBase):
>>>       )
>>>
>>>       labels_application_short = Option('labels', 'application_short',
>>> -                                      'Bloodhound', """A short version
>>> of application name most commonly
>>> +        'Bloodhound', """A short version of application name most
>>> commonly
>>>           displayed in text, titles and labels""", doc_domain='bhtheme')
>>>
>>>       labels_application_full = Option('labels', 'application_full',
>>> -                                     'Apache Bloodhound', """This is
>>> full name with trade mark and
>>> +        'Apache Bloodhound', """This is full name with trade mark and
>>>           everything, it is currently used in footers and about page
>>> only""",
>>>                                        doc_domain='bhtheme')
>>>
>>> -    labels_footer_left_prefix = Option(
>>> -        'labels',
>>> -        'footer_left_prefix',
>>> -        '',
>>> +    labels_footer_left_prefix = Option('labels', 'footer_left_prefix',
>>> '',
>>>           """Text to display before full application name in footers""",
>>> -        doc_domain='bhtheme')
>>> +                                       doc_domain='bhtheme')
>>>
>>> -    labels_footer_left_postfix = Option(
>>> -        'labels',
>>> -        'footer_left_postfix',
>>> -        '',
>>> +    labels_footer_left_postfix = Option('labels', 'footer_left_postfix',
>>> '',
>>>           """Text to display after full application name in footers""",
>>> -        doc_domain='bhtheme')
>>> +                                        doc_domain='bhtheme')
>>>
>>> -    labels_footer_right = Option(
>>> -        'labels',
>>> -        'footer_right',
>>> -        '',
>>> -        """Text to use as the right aligned footer""",
>>> -        doc_domain='bhtheme')
>>> +    labels_footer_right = Option('labels', 'footer_right', '',
>>> +        """Text to use as the right aligned footer""",
>>> doc_domain='bhtheme')
>>>
>>>       _wiki_pages = None
>>>       Chrome.default_html_doctype = DocType.HTML5
>>> @@ -365,13 +347,7 @@ class BloodhoundTheme(ThemeBase):
>>>
>>>       # Request modifiers
>>>
>>> -    def _modify_search_data(
>>> -            self,
>>> -            req,
>>> -            template,
>>> -            data,
>>> -            content_type,
>>> -            is_active):
>>> +    def _modify_search_data(self, req, template, data, content_type,
>>> is_active):
>>>           """Insert breadcumbs and context navigation items in search web
>>> UI
>>>           """
>>>           if is_active:
>>> @@ -405,7 +381,7 @@ class BloodhoundTheme(ThemeBase):
>>>           self._modify_resource_breadcrumb(req, template, data,
>>> content_type,
>>>                                            is_active)
>>>
>>> -        # add a creation event to the changelog if the ticket exists
>>> +        #add a creation event to the changelog if the ticket exists
>>>           ticket = data['ticket']
>>>           if ticket.exists:
>>>               data['changes'] = [{'comment': '',
>>> @@ -417,7 +393,7 @@ class BloodhoundTheme(ThemeBase):
>>>                                   'date': ticket['time'],
>>>                                   },
>>>                                  ] + data['changes']
>>> -        # and set default order
>>> +        #and set default order
>>>           if not req.session.get('ticket_comments_order'):
>>>               req.session['ticket_comments_order'] = 'newest'
>>>
>>> @@ -440,13 +416,7 @@ class BloodhoundTheme(ThemeBase):
>>>               if mname:
>>>                   data['milestone'] = Milestone(self.env, mname)
>>>
>>> -    def _modify_admin_breadcrumb(
>>> -            self,
>>> -            req,
>>> -            template,
>>> -            data,
>>> -            content_type,
>>> -            is_active):
>>> +    def _modify_admin_breadcrumb(self, req, template, data,
>>> content_type, is_active):
>>>           # override 'normal' product list with the admin one
>>>
>>>           def admin_url(prefix):
>>> @@ -495,7 +465,7 @@ class BloodhoundTheme(ThemeBase):
>>>                   SELECT product, value FROM bloodhound_productconfig
>>>                   WHERE product IN (%s) AND section='project' AND
>>>                   option='icon'""" % ', '.join(["%s"] * len(products)),
>>> -                               tuple(p.prefix for p in products))
>>> +                tuple(p.prefix for p in products))
>>>           icons = dict(icons)
>>>           data['thumbsize'] = 64
>>>           # FIXME: Gray icon for missing products
>>> @@ -510,29 +480,29 @@ class BloodhoundTheme(ThemeBase):
>>>
>>> product_ctx(product),
>>>
>>> product.description),
>>>                           links={'extras': (([{'href': req.href.products(
>>> -                            product.prefix, action='edit'),
>>> -                            'title': _('Edit product %(prefix)s',
>>> -                                       prefix=product.prefix),
>>> -                            'icon': tag.i(class_='icon-edit'),
>>> -                            'label': _('Edit')}, ]
>>> -                            if 'PRODUCT_MODIFY' in req.perm
>>> -                            else []) +
>>> -                            [{'href': product.href(),
>>> -                              'title': _('Home page'),
>>> -                              'icon': tag.i(class_='icon-home'),
>>> -                              'label': _('Home')},
>>> -                             {'href': product.href.dashboard(),
>>> -                              'title': _('Tickets dashboard'),
>>> -                              'icon': tag.i(class_='icon-tasks'),
>>> -                              'label': _('Tickets')},
>>> -                             {'href': product.href.wiki(),
>>> -                              'title': _('Wiki'),
>>> -                              'icon': tag.i(class_='icon-book'),
>>> -                              'label': _('Wiki')}]),
>>> -                'main': {'href': product.href(),
>>> -                         'title': None,
>>> -                         'icon': tag.i(class_='icon-chevron-right'),
>>> -                         'label': _('Browse')}})
>>> +                                                product.prefix,
>>> action='edit'),
>>> +                                             'title': _('Edit product
>>> %(prefix)s',
>>> +
>>> prefix=product.prefix),
>>> +                                             'icon':
>>> tag.i(class_='icon-edit'),
>>> +                                             'label': _('Edit')},]
>>> +                                           if 'PRODUCT_MODIFY' in
>>> req.perm
>>> +                                           else []) +
>>> +                                          [{'href': product.href(),
>>> +                                            'title': _('Home page'),
>>> +                                            'icon':
>>> tag.i(class_='icon-home'),
>>> +                                            'label': _('Home')},
>>> +                                           {'href':
>>> product.href.dashboard(),
>>> +                                            'title': _('Tickets
>>> dashboard'),
>>> +                                            'icon':
>>> tag.i(class_='icon-tasks'),
>>> +                                            'label': _('Tickets')},
>>> +                                           {'href': product.href.wiki(),
>>> +                                            'title': _('Wiki'),
>>> +                                            'icon':
>>> tag.i(class_='icon-book'),
>>> +                                            'label': _('Wiki')}]),
>>> +                               'main': {'href': product.href(),
>>> +                                        'title': None,
>>> +                                        'icon':
>>> tag.i(class_='icon-chevron-right'),
>>> +                                        'label': _('Browse')}})
>>>
>>>           data['products'] = [product_media_data(icons, product)
>>>                               for product in products]
>>> @@ -550,16 +520,29 @@ class BloodhoundTheme(ThemeBase):
>>>                          tag.a(_('Source'),
>>>
>>> href=req.href.wiki('TracRepositoryAdmin')))
>>>
>>> +class QCTSelectFieldUpdate(Component):
>>> +    implements(IRequestHandler)
>>> +
>>> +    def match_request(self, req):
>>> +        return req.path_info == '/update-menus'
>>> +
>>> +    def process_request(self, req):
>>> +        product = req.args.get('product')
>>> +        fields_to_update = req.args.get('fields_to_update[]');
>>> +        env = ProductEnvironment(self.env.parent,
>>> req.args.get('product'))
>>> +        ticket_fields = TicketSystem(env).get_ticket_fields()
>>> +        data = dict([f['name'], f['options']]  for f in ticket_fields
>>> +            if f['type'] == 'select' and f['name'] in fields_to_update)
>>> +        req.send(to_json(data), 'application/json')
>>> +
>>>
>>>   class QuickCreateTicketDialog(Component):
>>>       implements(IRequestFilter, IRequestHandler)
>>>
>>> -    qct_fields = ListOption(
>>> -        'ticket',
>>> -        'quick_create_fields',
>>> -        'product, version, type',
>>> +    qct_fields = ListOption('ticket', 'quick_create_fields',
>>> +                            'product, version, type',
>>>           doc="""Multiple selection fields displayed in create ticket
>>> menu""",
>>> -        doc_domain='bhtheme')
>>> +                            doc_domain='bhtheme')
>>>
>>>       def __init__(self, *args, **kwargs):
>>>           import pkg_resources
>>> @@ -610,8 +593,8 @@ class QuickCreateTicketDialog(Component)
>>>                            new_ticket_url=dum_req.href.products(p,
>>> 'newticket'),
>>>
>>> description=ProductEnvironment.lookup_env(self.env, p)
>>>                                                          .product.name
>>> -                         )
>>> -                    for p in product_field['options']
>>> +                    )
>>> +                for p in product_field['options']
>>>                       if req.perm.has_permission('TICKET_CREATE',
>>>                                                  Neighborhood('product',
>>> p)
>>>                                                  .child(None, None))]
>>> @@ -628,7 +611,7 @@ class QuickCreateTicketDialog(Component)
>>>                   'fields': [all_fields[k] for k in self.qct_fields
>>>                              if k in all_fields],
>>>                   'hidden_fields': [all_fields[k] for k in
>>> all_fields.keys()
>>> -                                  if k not in self.qct_fields]}
>>> +                                  if k not in self.qct_fields] }
>>>           return template, data, content_type
>>>
>>>       # IRequestHandler methods
>>> @@ -652,7 +635,7 @@ class QuickCreateTicketDialog(Component)
>>>               attrs = dict([k[6:], v] for k, v in req.args.iteritems()
>>>                            if k.startswith('field_'))
>>>               product, tid = self.create(req, summary, desc, attrs, True)
>>> -        except Exception as exc:
>>> +        except Exception, exc:
>>>               self.log.exception("BH: Quick create ticket failed %s" %
>>> (exc,))
>>>               req.send(str(exc), 'plain/text', 500)
>>>           else:
>>> @@ -700,7 +683,7 @@ class QuickCreateTicketDialog(Component)
>>>               try:
>>>                   tn = TicketNotifyEmail(env)
>>>                   tn.notify(t, newticket=True)
>>> -            except Exception as e:
>>> +            except Exception, e:
>>>                   self.log.exception("Failure sending notification on
>>> creation "
>>>                                      "of ticket #%s: %s" % (t.id, e))
>>>           return t['product'], t.id
>>> @@ -708,7 +691,6 @@ class QuickCreateTicketDialog(Component)
>>>   from pkg_resources import get_distribution
>>>   application_version = get_distribution('BloodhoundTheme').version
>>>
>>> -
>>>   class BatchCreateTicketDialog(Component):
>>>       implements(
>>>           IRequestFilter,
>>>
>>>
>>
>>
>



-- 
Dammina Sahabandu.
Committer for ASF (Apache Bloodhound)
Undergraduate Department of Computer Science and Engineering
University of Moratuwa
Sri Lanka.

Re: svn commit: r1614483 - in /bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme: htdocs/js/batchcreate.js theme.py

Posted by Gary Martin <ga...@wandisco.com>.
OK, that looks useful but I have some observations/requests

  * If as a logging in user I create a table with N tickets and add 
information to M of the rows (where N > M) and then hit the save button, 
I get N - M tickets created. It would seem better to ignore the creation 
of the 'empty' rows. If we define the empty rows as those that do not 
have a summary, that might be enough.

  * With the ability to remove rows you also need to be careful with any 
rows that have data that you are effectively removing in case it was not 
intended. Perhaps it would be better to give controls that allow you 
remove specific rows instead?

  * Given the ability to add rows, it might also be sensible provide a 
means to clone details from the preceding row as an alternative row 
addition. Here I am not thinking of the summary and description which 
should probably be expected to be more variable than other fields but if 
there were milestone and component fields, these are things that might 
stay the same along with the product. The priority might not be so 
important to clone but I would say it wouldn't hurt too much. There are 
of course other possible ways to go that would help reduce a user's need 
to edit.

  * The status of tickets is somewhat tied to workflow and so really the 
state should only be new. There may be a case for allowing it to be 
different but workflow is complicated so I would probably leave that 
until later. There is certainly a temptation to be able to provide an 
initial assignment of the ticket to a user which might suggest that 
assigned is also a valid starting state but workflow really is a bit of 
a minefield where you may not be able to make any certain predictions of 
what is definitely required at the moment.

  * If you decide to drop status you could consider replacing it with 
component and/or milestone. In the long run it might well be useful to 
have a way of setting the fields that can be set but I would suspect 
there is no time for that.

Anyway, pick things that you think you can improve on, add testing if 
you can and tidy up as best you can. The firm pencils down deadline 
looks like it is 18th August and I would hope that you really are only 
writing tests and improving documentation in the week before that.

Cheers,
     Gary

On 29/07/14 21:51, Dammina Sahabandu wrote:
> Earlier the batch create functionality allowed the users to batch
> create certain number of tickets. But they needed to decide the number
> that they are going to create initially. After that they can't change
> the number. If they need they have to do the process from the
> beginning. The new functionality provides the users the freedom to
> change the number of tickets at any moment.
>
> On Wed, Jul 30, 2014 at 2:19 AM,  <da...@apache.org> wrote:
>> Author: dammina
>> Date: Tue Jul 29 20:49:38 2014
>> New Revision: 1614483
>>
>> URL: http://svn.apache.org/r1614483
>> Log:
>> The new functionality provides the users the freedom to change the number of tickets at any moment.
>>
>> Modified:
>>      bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>>      bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>>
>> Modified: bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>> URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js?rev=1614483&r1=1614482&r2=1614483&view=diff
>> ==============================================================================
>> --- bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js (original)
>> +++ bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js Tue Jul 29 20:49:38 2014
>> @@ -175,7 +175,26 @@ function emptyTable(products,href,token)
>>          }
>>          table.appendChild(tbody);
>>          form.appendChild(table);
>> +
>> +       remove_row_button = document.createElement("button");
>> +       remove_row_button.setAttribute("class","btn pull-right");
>> +       remove_row_button.setAttribute("type","button");
>> +       remove_row_button.setAttribute("onclick","remove_row_btn_action()");
>> +       remove_row_button.setAttribute("id","bct-rmv-empty-row");
>> +       remove_row_button.appendChild(document.createTextNode("-"));
>> +       form.appendChild(remove_row_button);
>>
>> +       add_row_button = document.createElement("button");
>> +       add_row_button.setAttribute("class","btn pull-right");
>> +       add_row_button.setAttribute("type","button");
>> +       add_row_button.addEventListener("click", function(event) {
>> +               add_row_btn_action(products);
>> +               event.preventDefault();
>> +       });
>> +       add_row_button.setAttribute("id","bct-add-empty-row");
>> +       add_row_button.appendChild(document.createTextNode("+"));
>> +       form.appendChild(add_row_button);
>> +
>>       submit_button = document.createElement("button");
>>          submit_button.setAttribute("class","btn pull-right");
>>          submit_button.setAttribute("type","button");
>> @@ -282,3 +301,144 @@ function submit_btn_action() {
>>                          contentDiv.appendChild(div);
>>           });
>>   }
>> +
>> +function add_row_btn_action(products){
>> +       // alert("1");
>> +
>> +       var headers = {"summary":"Summary","description":"Description","product":"Product","status":"Status","priority":"Priority"}
>> +       var statuses = ["accepted", "assigned", "closed", "new", "reopened"];
>> +       var priorities = ["blocker", "critical", "major", "minor", "trivial"];
>> +       var types = ["defect", "enhancement", "task"];
>> +
>> +    tr_rows = document.createElement("tr");
>> +
>> +    for (header in headers){
>> +               if (header == "summary"){
>> +                               td_row = document.createElement("td");
>> +                               input_summary = document.createElement("input");
>> +                               input_summary.setAttribute("type","text");
>> +                               input_summary.setAttribute("id","field-summary"+i);
>> +                               input_summary.setAttribute("class","input-block-level");
>> +                               input_summary.setAttribute("name","field_summary"+i);
>> +                               td_row.appendChild(input_summary);
>> +                               tr_rows.appendChild(td_row);
>> +                       }
>> +                       else if (header == "description") {
>> +                               td_row = document.createElement("td");
>> +                               input_description = document.createElement("textarea");
>> +                               input_description.setAttribute("id","field-description"+i);
>> +                               input_description.setAttribute("class","input-block-level");
>> +                               input_description.setAttribute("name","field_description"+i);
>> +                               input_description.setAttribute("rows","2");
>> +                               input_description.setAttribute("cols","28");
>> +                               td_row.appendChild(input_description);
>> +                               tr_rows.appendChild(td_row);
>> +                       }
>> +                       else if (header == "status") {
>> +                               td_row = document.createElement("td");
>> +                               input_status = document.createElement("select");
>> +                               input_status.setAttribute("id","field-status"+i);
>> +                               input_status.setAttribute("class","input-block-level");
>> +                               input_status.setAttribute("name","field_status"+i);
>> +                               for (status in statuses){
>> +                                       option = document.createElement("option");
>> +                                       option.setAttribute("value",statuses[status]);
>> +                                       option.appendChild(document.createTextNode(statuses[status]));
>> +                                       input_status.appendChild(option);
>> +                               }
>> +                               td_row.appendChild(input_status);
>> +                               tr_rows.appendChild(td_row);
>> +                       }
>> +                       else if (header == "priority") {
>> +                               td_row = document.createElement("td");
>> +                               input_priority = document.createElement("select");
>> +                               input_priority.setAttribute("id","field-priority"+i);
>> +                               input_priority.setAttribute("class","input-block-level");
>> +                               input_priority.setAttribute("name","field_priority"+i);
>> +                               for (priority in priorities){
>> +                                       option = document.createElement("option");
>> +                                       option.setAttribute("value",priorities[priority]);
>> +                                       option.appendChild(document.createTextNode(priorities[priority]));
>> +                                       input_priority.appendChild(option);
>> +                               }
>> +                               td_row.appendChild(input_priority);
>> +                               tr_rows.appendChild(td_row);
>> +                       }
>> +                       /*else if (header == "type") {
>> +                               td_row = document.createElement("td");
>> +                               input_type = document.createElement("select");
>> +                               input_type.setAttribute("id","field-type"+i);
>> +                               input_type.setAttribute("class","input-block-level");
>> +                               input_type.setAttribute("name","field_type"+i);
>> +                               for (type in types){
>> +                                       option = document.createElement("option");
>> +                                       option.setAttribute("value",types[type]);
>> +                                       option.appendChild(document.createTextNode(types[type]));
>> +                                       input_type.appendChild(option);
>> +                               }
>> +                               td_row.appendChild(input_type);
>> +                               tr_rows.appendChild(td_row);
>> +                       }*/
>> +                       else if (header == "product") {
>> +                               td_row = document.createElement("td");
>> +                               field_product = document.createElement("select");
>> +                               field_product.setAttribute("id","field-product"+i);
>> +                               field_product.setAttribute("class","input-block-level");
>> +                               field_product.setAttribute("name","field_product"+i);
>> +                               for (product in products){
>> +                                       option = document.createElement("option");
>> +                                       option.setAttribute("value",(products[product])[0]);
>> +                                       option.appendChild(document.createTextNode((products[product])[1]));
>> +                                       field_product.appendChild(option);
>> +                               }
>> +                               td_row.appendChild(field_product);
>> +                               tr_rows.appendChild(td_row);
>> +                       }
>> +                       /*else if (header == "owner"){
>> +                               td_row = document.createElement("td");
>> +                               input_owner = document.createElement("input");
>> +                               input_owner.setAttribute("type","text");
>> +                               input_owner.setAttribute("id","field-owner"+i);
>> +                               input_owner.setAttribute("class","input-block-level");
>> +                               input_owner.setAttribute("name","field_owner"+i);
>> +                               td_row.appendChild(input_owner);
>> +                               tr_rows.appendChild(td_row);
>> +                       }*/
>> +                       /*else if (header == "cc"){
>> +                               td_row = document.createElement("td");
>> +                               input_cc = document.createElement("input");
>> +                               input_cc.setAttribute("type","text");
>> +                               input_cc.setAttribute("id","field-cc"+i);
>> +                               input_cc.setAttribute("class","input-block-level");
>> +                               input_cc.setAttribute("name","field_cc"+i);
>> +                               td_row.appendChild(input_cc);
>> +                               tr_rows.appendChild(td_row);
>> +                       }*/
>> +                       /*else if (header == "milestone"){
>> +                               td_row = document.createElement("td");
>> +                               input_milestone = document.createElement("input");
>> +                               input_milestone.setAttribute("type","text");
>> +                               input_milestone.setAttribute("id","field-milestone"+i);
>> +                               input_milestone.setAttribute("class","input-block-level");
>> +                               input_milestone.setAttribute("name","field_milestone"+i);
>> +                               td_row.appendChild(input_milestone);
>> +                               tr_rows.appendChild(td_row);
>> +                       }*/
>> +                       /*else if (header == "keywords"){
>> +                               td_row = document.createElement("td");
>> +                               input_keywords = document.createElement("input");
>> +                               input_keywords.setAttribute("type","text");
>> +                               input_keywords.setAttribute("id","field-keywords"+i);
>> +                               input_keywords.setAttribute("class","input-block-level");
>> +                               input_keywords.setAttribute("name","field_keywords"+i);
>> +                               td_row.appendChild(input_keywords);
>> +                               tr_rows.appendChild(td_row);
>> +                       }*/
>> +
>> +       }
>> +       document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].appendChild(tr_rows);
>> +}
>> +
>> +function remove_row_btn_action(){
>> +       document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].lastChild.remove();
>> +}
>>
>> Modified: bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>> URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py?rev=1614483&r1=1614482&r2=1614483&view=diff
>> ==============================================================================
>> --- bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py (original)
>> +++ bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py Tue Jul 29 20:49:38 2014
>> @@ -36,14 +36,8 @@ from trac.util.compat import set
>>   from trac.util.presentation import to_json
>>   from trac.versioncontrol.web_ui.browser import BrowserModule
>>   from trac.web.api import IRequestFilter, ITemplateStreamFilter
>> -from trac.web.chrome import (
>> -    add_stylesheet,
>> -    add_warning,
>> -    INavigationContributor,
>> -    ITemplateProvider,
>> -    prevnext_nav,
>> -    Chrome,
>> -    add_script)
>> +from trac.web.chrome import (add_stylesheet, add_warning, INavigationContributor,
>> +                             ITemplateProvider, prevnext_nav, Chrome, add_script)
>>   from trac.web.main import IRequestHandler
>>   from trac.wiki.admin import WikiAdmin
>>   from trac.wiki.formatter import format_to_html
>> @@ -63,9 +57,7 @@ try:
>>   except ImportError:
>>       ProductTicketModule = None
>>
>> -
>>   class BloodhoundTheme(ThemeBase):
>> -
>>       """Look and feel of Bloodhound issue tracker.
>>       """
>>       template = htdocs = css = screenshot = disable_trac_css = True
>> @@ -168,34 +160,24 @@ class BloodhoundTheme(ThemeBase):
>>       )
>>
>>       labels_application_short = Option('labels', 'application_short',
>> -                                      'Bloodhound', """A short version of application name most commonly
>> +        'Bloodhound', """A short version of application name most commonly
>>           displayed in text, titles and labels""", doc_domain='bhtheme')
>>
>>       labels_application_full = Option('labels', 'application_full',
>> -                                     'Apache Bloodhound', """This is full name with trade mark and
>> +        'Apache Bloodhound', """This is full name with trade mark and
>>           everything, it is currently used in footers and about page only""",
>>                                        doc_domain='bhtheme')
>>
>> -    labels_footer_left_prefix = Option(
>> -        'labels',
>> -        'footer_left_prefix',
>> -        '',
>> +    labels_footer_left_prefix = Option('labels', 'footer_left_prefix', '',
>>           """Text to display before full application name in footers""",
>> -        doc_domain='bhtheme')
>> +                                       doc_domain='bhtheme')
>>
>> -    labels_footer_left_postfix = Option(
>> -        'labels',
>> -        'footer_left_postfix',
>> -        '',
>> +    labels_footer_left_postfix = Option('labels', 'footer_left_postfix', '',
>>           """Text to display after full application name in footers""",
>> -        doc_domain='bhtheme')
>> +                                        doc_domain='bhtheme')
>>
>> -    labels_footer_right = Option(
>> -        'labels',
>> -        'footer_right',
>> -        '',
>> -        """Text to use as the right aligned footer""",
>> -        doc_domain='bhtheme')
>> +    labels_footer_right = Option('labels', 'footer_right', '',
>> +        """Text to use as the right aligned footer""", doc_domain='bhtheme')
>>
>>       _wiki_pages = None
>>       Chrome.default_html_doctype = DocType.HTML5
>> @@ -365,13 +347,7 @@ class BloodhoundTheme(ThemeBase):
>>
>>       # Request modifiers
>>
>> -    def _modify_search_data(
>> -            self,
>> -            req,
>> -            template,
>> -            data,
>> -            content_type,
>> -            is_active):
>> +    def _modify_search_data(self, req, template, data, content_type, is_active):
>>           """Insert breadcumbs and context navigation items in search web UI
>>           """
>>           if is_active:
>> @@ -405,7 +381,7 @@ class BloodhoundTheme(ThemeBase):
>>           self._modify_resource_breadcrumb(req, template, data, content_type,
>>                                            is_active)
>>
>> -        # add a creation event to the changelog if the ticket exists
>> +        #add a creation event to the changelog if the ticket exists
>>           ticket = data['ticket']
>>           if ticket.exists:
>>               data['changes'] = [{'comment': '',
>> @@ -417,7 +393,7 @@ class BloodhoundTheme(ThemeBase):
>>                                   'date': ticket['time'],
>>                                   },
>>                                  ] + data['changes']
>> -        # and set default order
>> +        #and set default order
>>           if not req.session.get('ticket_comments_order'):
>>               req.session['ticket_comments_order'] = 'newest'
>>
>> @@ -440,13 +416,7 @@ class BloodhoundTheme(ThemeBase):
>>               if mname:
>>                   data['milestone'] = Milestone(self.env, mname)
>>
>> -    def _modify_admin_breadcrumb(
>> -            self,
>> -            req,
>> -            template,
>> -            data,
>> -            content_type,
>> -            is_active):
>> +    def _modify_admin_breadcrumb(self, req, template, data, content_type, is_active):
>>           # override 'normal' product list with the admin one
>>
>>           def admin_url(prefix):
>> @@ -495,7 +465,7 @@ class BloodhoundTheme(ThemeBase):
>>                   SELECT product, value FROM bloodhound_productconfig
>>                   WHERE product IN (%s) AND section='project' AND
>>                   option='icon'""" % ', '.join(["%s"] * len(products)),
>> -                               tuple(p.prefix for p in products))
>> +                tuple(p.prefix for p in products))
>>           icons = dict(icons)
>>           data['thumbsize'] = 64
>>           # FIXME: Gray icon for missing products
>> @@ -510,29 +480,29 @@ class BloodhoundTheme(ThemeBase):
>>                                                      product_ctx(product),
>>                                                      product.description),
>>                           links={'extras': (([{'href': req.href.products(
>> -                            product.prefix, action='edit'),
>> -                            'title': _('Edit product %(prefix)s',
>> -                                       prefix=product.prefix),
>> -                            'icon': tag.i(class_='icon-edit'),
>> -                            'label': _('Edit')}, ]
>> -                            if 'PRODUCT_MODIFY' in req.perm
>> -                            else []) +
>> -                            [{'href': product.href(),
>> -                              'title': _('Home page'),
>> -                              'icon': tag.i(class_='icon-home'),
>> -                              'label': _('Home')},
>> -                             {'href': product.href.dashboard(),
>> -                              'title': _('Tickets dashboard'),
>> -                              'icon': tag.i(class_='icon-tasks'),
>> -                              'label': _('Tickets')},
>> -                             {'href': product.href.wiki(),
>> -                              'title': _('Wiki'),
>> -                              'icon': tag.i(class_='icon-book'),
>> -                              'label': _('Wiki')}]),
>> -                'main': {'href': product.href(),
>> -                         'title': None,
>> -                         'icon': tag.i(class_='icon-chevron-right'),
>> -                         'label': _('Browse')}})
>> +                                                product.prefix, action='edit'),
>> +                                             'title': _('Edit product %(prefix)s',
>> +                                                        prefix=product.prefix),
>> +                                             'icon': tag.i(class_='icon-edit'),
>> +                                             'label': _('Edit')},]
>> +                                           if 'PRODUCT_MODIFY' in req.perm
>> +                                           else []) +
>> +                                          [{'href': product.href(),
>> +                                            'title': _('Home page'),
>> +                                            'icon': tag.i(class_='icon-home'),
>> +                                            'label': _('Home')},
>> +                                           {'href': product.href.dashboard(),
>> +                                            'title': _('Tickets dashboard'),
>> +                                            'icon': tag.i(class_='icon-tasks'),
>> +                                            'label': _('Tickets')},
>> +                                           {'href': product.href.wiki(),
>> +                                            'title': _('Wiki'),
>> +                                            'icon': tag.i(class_='icon-book'),
>> +                                            'label': _('Wiki')}]),
>> +                               'main': {'href': product.href(),
>> +                                        'title': None,
>> +                                        'icon': tag.i(class_='icon-chevron-right'),
>> +                                        'label': _('Browse')}})
>>
>>           data['products'] = [product_media_data(icons, product)
>>                               for product in products]
>> @@ -550,16 +520,29 @@ class BloodhoundTheme(ThemeBase):
>>                          tag.a(_('Source'),
>>                                href=req.href.wiki('TracRepositoryAdmin')))
>>
>> +class QCTSelectFieldUpdate(Component):
>> +    implements(IRequestHandler)
>> +
>> +    def match_request(self, req):
>> +        return req.path_info == '/update-menus'
>> +
>> +    def process_request(self, req):
>> +        product = req.args.get('product')
>> +        fields_to_update = req.args.get('fields_to_update[]');
>> +        env = ProductEnvironment(self.env.parent, req.args.get('product'))
>> +        ticket_fields = TicketSystem(env).get_ticket_fields()
>> +        data = dict([f['name'], f['options']]  for f in ticket_fields
>> +            if f['type'] == 'select' and f['name'] in fields_to_update)
>> +        req.send(to_json(data), 'application/json')
>> +
>>
>>   class QuickCreateTicketDialog(Component):
>>       implements(IRequestFilter, IRequestHandler)
>>
>> -    qct_fields = ListOption(
>> -        'ticket',
>> -        'quick_create_fields',
>> -        'product, version, type',
>> +    qct_fields = ListOption('ticket', 'quick_create_fields',
>> +                            'product, version, type',
>>           doc="""Multiple selection fields displayed in create ticket menu""",
>> -        doc_domain='bhtheme')
>> +                            doc_domain='bhtheme')
>>
>>       def __init__(self, *args, **kwargs):
>>           import pkg_resources
>> @@ -610,8 +593,8 @@ class QuickCreateTicketDialog(Component)
>>                            new_ticket_url=dum_req.href.products(p, 'newticket'),
>>                            description=ProductEnvironment.lookup_env(self.env, p)
>>                                                          .product.name
>> -                         )
>> -                    for p in product_field['options']
>> +                    )
>> +                for p in product_field['options']
>>                       if req.perm.has_permission('TICKET_CREATE',
>>                                                  Neighborhood('product', p)
>>                                                  .child(None, None))]
>> @@ -628,7 +611,7 @@ class QuickCreateTicketDialog(Component)
>>                   'fields': [all_fields[k] for k in self.qct_fields
>>                              if k in all_fields],
>>                   'hidden_fields': [all_fields[k] for k in all_fields.keys()
>> -                                  if k not in self.qct_fields]}
>> +                                  if k not in self.qct_fields] }
>>           return template, data, content_type
>>
>>       # IRequestHandler methods
>> @@ -652,7 +635,7 @@ class QuickCreateTicketDialog(Component)
>>               attrs = dict([k[6:], v] for k, v in req.args.iteritems()
>>                            if k.startswith('field_'))
>>               product, tid = self.create(req, summary, desc, attrs, True)
>> -        except Exception as exc:
>> +        except Exception, exc:
>>               self.log.exception("BH: Quick create ticket failed %s" % (exc,))
>>               req.send(str(exc), 'plain/text', 500)
>>           else:
>> @@ -700,7 +683,7 @@ class QuickCreateTicketDialog(Component)
>>               try:
>>                   tn = TicketNotifyEmail(env)
>>                   tn.notify(t, newticket=True)
>> -            except Exception as e:
>> +            except Exception, e:
>>                   self.log.exception("Failure sending notification on creation "
>>                                      "of ticket #%s: %s" % (t.id, e))
>>           return t['product'], t.id
>> @@ -708,7 +691,6 @@ class QuickCreateTicketDialog(Component)
>>   from pkg_resources import get_distribution
>>   application_version = get_distribution('BloodhoundTheme').version
>>
>> -
>>   class BatchCreateTicketDialog(Component):
>>       implements(
>>           IRequestFilter,
>>
>>
>
>


Re: svn commit: r1614483 - in /bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme: htdocs/js/batchcreate.js theme.py

Posted by Dammina Sahabandu <dm...@gmail.com>.
Earlier the batch create functionality allowed the users to batch
create certain number of tickets. But they needed to decide the number
that they are going to create initially. After that they can't change
the number. If they need they have to do the process from the
beginning. The new functionality provides the users the freedom to
change the number of tickets at any moment.

On Wed, Jul 30, 2014 at 2:19 AM,  <da...@apache.org> wrote:
> Author: dammina
> Date: Tue Jul 29 20:49:38 2014
> New Revision: 1614483
>
> URL: http://svn.apache.org/r1614483
> Log:
> The new functionality provides the users the freedom to change the number of tickets at any moment.
>
> Modified:
>     bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
>     bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
>
> Modified: bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js
> URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js?rev=1614483&r1=1614482&r2=1614483&view=diff
> ==============================================================================
> --- bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js (original)
> +++ bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/htdocs/js/batchcreate.js Tue Jul 29 20:49:38 2014
> @@ -175,7 +175,26 @@ function emptyTable(products,href,token)
>         }
>         table.appendChild(tbody);
>         form.appendChild(table);
> +
> +       remove_row_button = document.createElement("button");
> +       remove_row_button.setAttribute("class","btn pull-right");
> +       remove_row_button.setAttribute("type","button");
> +       remove_row_button.setAttribute("onclick","remove_row_btn_action()");
> +       remove_row_button.setAttribute("id","bct-rmv-empty-row");
> +       remove_row_button.appendChild(document.createTextNode("-"));
> +       form.appendChild(remove_row_button);
>
> +       add_row_button = document.createElement("button");
> +       add_row_button.setAttribute("class","btn pull-right");
> +       add_row_button.setAttribute("type","button");
> +       add_row_button.addEventListener("click", function(event) {
> +               add_row_btn_action(products);
> +               event.preventDefault();
> +       });
> +       add_row_button.setAttribute("id","bct-add-empty-row");
> +       add_row_button.appendChild(document.createTextNode("+"));
> +       form.appendChild(add_row_button);
> +
>      submit_button = document.createElement("button");
>         submit_button.setAttribute("class","btn pull-right");
>         submit_button.setAttribute("type","button");
> @@ -282,3 +301,144 @@ function submit_btn_action() {
>                         contentDiv.appendChild(div);
>          });
>  }
> +
> +function add_row_btn_action(products){
> +       // alert("1");
> +
> +       var headers = {"summary":"Summary","description":"Description","product":"Product","status":"Status","priority":"Priority"}
> +       var statuses = ["accepted", "assigned", "closed", "new", "reopened"];
> +       var priorities = ["blocker", "critical", "major", "minor", "trivial"];
> +       var types = ["defect", "enhancement", "task"];
> +
> +    tr_rows = document.createElement("tr");
> +
> +    for (header in headers){
> +               if (header == "summary"){
> +                               td_row = document.createElement("td");
> +                               input_summary = document.createElement("input");
> +                               input_summary.setAttribute("type","text");
> +                               input_summary.setAttribute("id","field-summary"+i);
> +                               input_summary.setAttribute("class","input-block-level");
> +                               input_summary.setAttribute("name","field_summary"+i);
> +                               td_row.appendChild(input_summary);
> +                               tr_rows.appendChild(td_row);
> +                       }
> +                       else if (header == "description") {
> +                               td_row = document.createElement("td");
> +                               input_description = document.createElement("textarea");
> +                               input_description.setAttribute("id","field-description"+i);
> +                               input_description.setAttribute("class","input-block-level");
> +                               input_description.setAttribute("name","field_description"+i);
> +                               input_description.setAttribute("rows","2");
> +                               input_description.setAttribute("cols","28");
> +                               td_row.appendChild(input_description);
> +                               tr_rows.appendChild(td_row);
> +                       }
> +                       else if (header == "status") {
> +                               td_row = document.createElement("td");
> +                               input_status = document.createElement("select");
> +                               input_status.setAttribute("id","field-status"+i);
> +                               input_status.setAttribute("class","input-block-level");
> +                               input_status.setAttribute("name","field_status"+i);
> +                               for (status in statuses){
> +                                       option = document.createElement("option");
> +                                       option.setAttribute("value",statuses[status]);
> +                                       option.appendChild(document.createTextNode(statuses[status]));
> +                                       input_status.appendChild(option);
> +                               }
> +                               td_row.appendChild(input_status);
> +                               tr_rows.appendChild(td_row);
> +                       }
> +                       else if (header == "priority") {
> +                               td_row = document.createElement("td");
> +                               input_priority = document.createElement("select");
> +                               input_priority.setAttribute("id","field-priority"+i);
> +                               input_priority.setAttribute("class","input-block-level");
> +                               input_priority.setAttribute("name","field_priority"+i);
> +                               for (priority in priorities){
> +                                       option = document.createElement("option");
> +                                       option.setAttribute("value",priorities[priority]);
> +                                       option.appendChild(document.createTextNode(priorities[priority]));
> +                                       input_priority.appendChild(option);
> +                               }
> +                               td_row.appendChild(input_priority);
> +                               tr_rows.appendChild(td_row);
> +                       }
> +                       /*else if (header == "type") {
> +                               td_row = document.createElement("td");
> +                               input_type = document.createElement("select");
> +                               input_type.setAttribute("id","field-type"+i);
> +                               input_type.setAttribute("class","input-block-level");
> +                               input_type.setAttribute("name","field_type"+i);
> +                               for (type in types){
> +                                       option = document.createElement("option");
> +                                       option.setAttribute("value",types[type]);
> +                                       option.appendChild(document.createTextNode(types[type]));
> +                                       input_type.appendChild(option);
> +                               }
> +                               td_row.appendChild(input_type);
> +                               tr_rows.appendChild(td_row);
> +                       }*/
> +                       else if (header == "product") {
> +                               td_row = document.createElement("td");
> +                               field_product = document.createElement("select");
> +                               field_product.setAttribute("id","field-product"+i);
> +                               field_product.setAttribute("class","input-block-level");
> +                               field_product.setAttribute("name","field_product"+i);
> +                               for (product in products){
> +                                       option = document.createElement("option");
> +                                       option.setAttribute("value",(products[product])[0]);
> +                                       option.appendChild(document.createTextNode((products[product])[1]));
> +                                       field_product.appendChild(option);
> +                               }
> +                               td_row.appendChild(field_product);
> +                               tr_rows.appendChild(td_row);
> +                       }
> +                       /*else if (header == "owner"){
> +                               td_row = document.createElement("td");
> +                               input_owner = document.createElement("input");
> +                               input_owner.setAttribute("type","text");
> +                               input_owner.setAttribute("id","field-owner"+i);
> +                               input_owner.setAttribute("class","input-block-level");
> +                               input_owner.setAttribute("name","field_owner"+i);
> +                               td_row.appendChild(input_owner);
> +                               tr_rows.appendChild(td_row);
> +                       }*/
> +                       /*else if (header == "cc"){
> +                               td_row = document.createElement("td");
> +                               input_cc = document.createElement("input");
> +                               input_cc.setAttribute("type","text");
> +                               input_cc.setAttribute("id","field-cc"+i);
> +                               input_cc.setAttribute("class","input-block-level");
> +                               input_cc.setAttribute("name","field_cc"+i);
> +                               td_row.appendChild(input_cc);
> +                               tr_rows.appendChild(td_row);
> +                       }*/
> +                       /*else if (header == "milestone"){
> +                               td_row = document.createElement("td");
> +                               input_milestone = document.createElement("input");
> +                               input_milestone.setAttribute("type","text");
> +                               input_milestone.setAttribute("id","field-milestone"+i);
> +                               input_milestone.setAttribute("class","input-block-level");
> +                               input_milestone.setAttribute("name","field_milestone"+i);
> +                               td_row.appendChild(input_milestone);
> +                               tr_rows.appendChild(td_row);
> +                       }*/
> +                       /*else if (header == "keywords"){
> +                               td_row = document.createElement("td");
> +                               input_keywords = document.createElement("input");
> +                               input_keywords.setAttribute("type","text");
> +                               input_keywords.setAttribute("id","field-keywords"+i);
> +                               input_keywords.setAttribute("class","input-block-level");
> +                               input_keywords.setAttribute("name","field_keywords"+i);
> +                               td_row.appendChild(input_keywords);
> +                               tr_rows.appendChild(td_row);
> +                       }*/
> +
> +       }
> +       document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].appendChild(tr_rows);
> +}
> +
> +function remove_row_btn_action(){
> +       document.getElementById("empty-table").childNodes[1].childNodes[1].childNodes[1].lastChild.remove();
> +}
>
> Modified: bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py
> URL: http://svn.apache.org/viewvc/bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py?rev=1614483&r1=1614482&r2=1614483&view=diff
> ==============================================================================
> --- bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py (original)
> +++ bloodhound/branches/bep_0011_batch_create_tickets/bloodhound_theme/bhtheme/theme.py Tue Jul 29 20:49:38 2014
> @@ -36,14 +36,8 @@ from trac.util.compat import set
>  from trac.util.presentation import to_json
>  from trac.versioncontrol.web_ui.browser import BrowserModule
>  from trac.web.api import IRequestFilter, ITemplateStreamFilter
> -from trac.web.chrome import (
> -    add_stylesheet,
> -    add_warning,
> -    INavigationContributor,
> -    ITemplateProvider,
> -    prevnext_nav,
> -    Chrome,
> -    add_script)
> +from trac.web.chrome import (add_stylesheet, add_warning, INavigationContributor,
> +                             ITemplateProvider, prevnext_nav, Chrome, add_script)
>  from trac.web.main import IRequestHandler
>  from trac.wiki.admin import WikiAdmin
>  from trac.wiki.formatter import format_to_html
> @@ -63,9 +57,7 @@ try:
>  except ImportError:
>      ProductTicketModule = None
>
> -
>  class BloodhoundTheme(ThemeBase):
> -
>      """Look and feel of Bloodhound issue tracker.
>      """
>      template = htdocs = css = screenshot = disable_trac_css = True
> @@ -168,34 +160,24 @@ class BloodhoundTheme(ThemeBase):
>      )
>
>      labels_application_short = Option('labels', 'application_short',
> -                                      'Bloodhound', """A short version of application name most commonly
> +        'Bloodhound', """A short version of application name most commonly
>          displayed in text, titles and labels""", doc_domain='bhtheme')
>
>      labels_application_full = Option('labels', 'application_full',
> -                                     'Apache Bloodhound', """This is full name with trade mark and
> +        'Apache Bloodhound', """This is full name with trade mark and
>          everything, it is currently used in footers and about page only""",
>                                       doc_domain='bhtheme')
>
> -    labels_footer_left_prefix = Option(
> -        'labels',
> -        'footer_left_prefix',
> -        '',
> +    labels_footer_left_prefix = Option('labels', 'footer_left_prefix', '',
>          """Text to display before full application name in footers""",
> -        doc_domain='bhtheme')
> +                                       doc_domain='bhtheme')
>
> -    labels_footer_left_postfix = Option(
> -        'labels',
> -        'footer_left_postfix',
> -        '',
> +    labels_footer_left_postfix = Option('labels', 'footer_left_postfix', '',
>          """Text to display after full application name in footers""",
> -        doc_domain='bhtheme')
> +                                        doc_domain='bhtheme')
>
> -    labels_footer_right = Option(
> -        'labels',
> -        'footer_right',
> -        '',
> -        """Text to use as the right aligned footer""",
> -        doc_domain='bhtheme')
> +    labels_footer_right = Option('labels', 'footer_right', '',
> +        """Text to use as the right aligned footer""", doc_domain='bhtheme')
>
>      _wiki_pages = None
>      Chrome.default_html_doctype = DocType.HTML5
> @@ -365,13 +347,7 @@ class BloodhoundTheme(ThemeBase):
>
>      # Request modifiers
>
> -    def _modify_search_data(
> -            self,
> -            req,
> -            template,
> -            data,
> -            content_type,
> -            is_active):
> +    def _modify_search_data(self, req, template, data, content_type, is_active):
>          """Insert breadcumbs and context navigation items in search web UI
>          """
>          if is_active:
> @@ -405,7 +381,7 @@ class BloodhoundTheme(ThemeBase):
>          self._modify_resource_breadcrumb(req, template, data, content_type,
>                                           is_active)
>
> -        # add a creation event to the changelog if the ticket exists
> +        #add a creation event to the changelog if the ticket exists
>          ticket = data['ticket']
>          if ticket.exists:
>              data['changes'] = [{'comment': '',
> @@ -417,7 +393,7 @@ class BloodhoundTheme(ThemeBase):
>                                  'date': ticket['time'],
>                                  },
>                                 ] + data['changes']
> -        # and set default order
> +        #and set default order
>          if not req.session.get('ticket_comments_order'):
>              req.session['ticket_comments_order'] = 'newest'
>
> @@ -440,13 +416,7 @@ class BloodhoundTheme(ThemeBase):
>              if mname:
>                  data['milestone'] = Milestone(self.env, mname)
>
> -    def _modify_admin_breadcrumb(
> -            self,
> -            req,
> -            template,
> -            data,
> -            content_type,
> -            is_active):
> +    def _modify_admin_breadcrumb(self, req, template, data, content_type, is_active):
>          # override 'normal' product list with the admin one
>
>          def admin_url(prefix):
> @@ -495,7 +465,7 @@ class BloodhoundTheme(ThemeBase):
>                  SELECT product, value FROM bloodhound_productconfig
>                  WHERE product IN (%s) AND section='project' AND
>                  option='icon'""" % ', '.join(["%s"] * len(products)),
> -                               tuple(p.prefix for p in products))
> +                tuple(p.prefix for p in products))
>          icons = dict(icons)
>          data['thumbsize'] = 64
>          # FIXME: Gray icon for missing products
> @@ -510,29 +480,29 @@ class BloodhoundTheme(ThemeBase):
>                                                     product_ctx(product),
>                                                     product.description),
>                          links={'extras': (([{'href': req.href.products(
> -                            product.prefix, action='edit'),
> -                            'title': _('Edit product %(prefix)s',
> -                                       prefix=product.prefix),
> -                            'icon': tag.i(class_='icon-edit'),
> -                            'label': _('Edit')}, ]
> -                            if 'PRODUCT_MODIFY' in req.perm
> -                            else []) +
> -                            [{'href': product.href(),
> -                              'title': _('Home page'),
> -                              'icon': tag.i(class_='icon-home'),
> -                              'label': _('Home')},
> -                             {'href': product.href.dashboard(),
> -                              'title': _('Tickets dashboard'),
> -                              'icon': tag.i(class_='icon-tasks'),
> -                              'label': _('Tickets')},
> -                             {'href': product.href.wiki(),
> -                              'title': _('Wiki'),
> -                              'icon': tag.i(class_='icon-book'),
> -                              'label': _('Wiki')}]),
> -                'main': {'href': product.href(),
> -                         'title': None,
> -                         'icon': tag.i(class_='icon-chevron-right'),
> -                         'label': _('Browse')}})
> +                                                product.prefix, action='edit'),
> +                                             'title': _('Edit product %(prefix)s',
> +                                                        prefix=product.prefix),
> +                                             'icon': tag.i(class_='icon-edit'),
> +                                             'label': _('Edit')},]
> +                                           if 'PRODUCT_MODIFY' in req.perm
> +                                           else []) +
> +                                          [{'href': product.href(),
> +                                            'title': _('Home page'),
> +                                            'icon': tag.i(class_='icon-home'),
> +                                            'label': _('Home')},
> +                                           {'href': product.href.dashboard(),
> +                                            'title': _('Tickets dashboard'),
> +                                            'icon': tag.i(class_='icon-tasks'),
> +                                            'label': _('Tickets')},
> +                                           {'href': product.href.wiki(),
> +                                            'title': _('Wiki'),
> +                                            'icon': tag.i(class_='icon-book'),
> +                                            'label': _('Wiki')}]),
> +                               'main': {'href': product.href(),
> +                                        'title': None,
> +                                        'icon': tag.i(class_='icon-chevron-right'),
> +                                        'label': _('Browse')}})
>
>          data['products'] = [product_media_data(icons, product)
>                              for product in products]
> @@ -550,16 +520,29 @@ class BloodhoundTheme(ThemeBase):
>                         tag.a(_('Source'),
>                               href=req.href.wiki('TracRepositoryAdmin')))
>
> +class QCTSelectFieldUpdate(Component):
> +    implements(IRequestHandler)
> +
> +    def match_request(self, req):
> +        return req.path_info == '/update-menus'
> +
> +    def process_request(self, req):
> +        product = req.args.get('product')
> +        fields_to_update = req.args.get('fields_to_update[]');
> +        env = ProductEnvironment(self.env.parent, req.args.get('product'))
> +        ticket_fields = TicketSystem(env).get_ticket_fields()
> +        data = dict([f['name'], f['options']]  for f in ticket_fields
> +            if f['type'] == 'select' and f['name'] in fields_to_update)
> +        req.send(to_json(data), 'application/json')
> +
>
>  class QuickCreateTicketDialog(Component):
>      implements(IRequestFilter, IRequestHandler)
>
> -    qct_fields = ListOption(
> -        'ticket',
> -        'quick_create_fields',
> -        'product, version, type',
> +    qct_fields = ListOption('ticket', 'quick_create_fields',
> +                            'product, version, type',
>          doc="""Multiple selection fields displayed in create ticket menu""",
> -        doc_domain='bhtheme')
> +                            doc_domain='bhtheme')
>
>      def __init__(self, *args, **kwargs):
>          import pkg_resources
> @@ -610,8 +593,8 @@ class QuickCreateTicketDialog(Component)
>                           new_ticket_url=dum_req.href.products(p, 'newticket'),
>                           description=ProductEnvironment.lookup_env(self.env, p)
>                                                         .product.name
> -                         )
> -                    for p in product_field['options']
> +                    )
> +                for p in product_field['options']
>                      if req.perm.has_permission('TICKET_CREATE',
>                                                 Neighborhood('product', p)
>                                                 .child(None, None))]
> @@ -628,7 +611,7 @@ class QuickCreateTicketDialog(Component)
>                  'fields': [all_fields[k] for k in self.qct_fields
>                             if k in all_fields],
>                  'hidden_fields': [all_fields[k] for k in all_fields.keys()
> -                                  if k not in self.qct_fields]}
> +                                  if k not in self.qct_fields] }
>          return template, data, content_type
>
>      # IRequestHandler methods
> @@ -652,7 +635,7 @@ class QuickCreateTicketDialog(Component)
>              attrs = dict([k[6:], v] for k, v in req.args.iteritems()
>                           if k.startswith('field_'))
>              product, tid = self.create(req, summary, desc, attrs, True)
> -        except Exception as exc:
> +        except Exception, exc:
>              self.log.exception("BH: Quick create ticket failed %s" % (exc,))
>              req.send(str(exc), 'plain/text', 500)
>          else:
> @@ -700,7 +683,7 @@ class QuickCreateTicketDialog(Component)
>              try:
>                  tn = TicketNotifyEmail(env)
>                  tn.notify(t, newticket=True)
> -            except Exception as e:
> +            except Exception, e:
>                  self.log.exception("Failure sending notification on creation "
>                                     "of ticket #%s: %s" % (t.id, e))
>          return t['product'], t.id
> @@ -708,7 +691,6 @@ class QuickCreateTicketDialog(Component)
>  from pkg_resources import get_distribution
>  application_version = get_distribution('BloodhoundTheme').version
>
> -
>  class BatchCreateTicketDialog(Component):
>      implements(
>          IRequestFilter,
>
>



-- 
Dammina Sahabandu.
Committer for ASF (Apache Bloodhound)
Undergraduate Department of Computer Science and Engineering
University of Moratuwa
Sri Lanka.