1.0/0002755000175000000620000000000014765775303010044 5ustar wmbstaff1.0/tickettemplate/0002755000175000000620000000000014664037202013046 5ustar wmbstaff1.0/tickettemplate/tests/0002755000175000000620000000000014664037202014210 5ustar wmbstaff1.0/tickettemplate/tests/test_tt.py0000644000175000000620000000267214664037202016255 0ustar wmbstaff# -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 Richard Liao # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # import unittest from trac.test import EnvironmentStub, Mock from .. import ttadmin class TicketTemplateTestCase(unittest.TestCase): def setUp(self): self.env = EnvironmentStub(enable=['tickettemplate']) self.tt = ttadmin.TicketTemplateModule(self.env) self.tt.upgrade_environment() def tearDown(self): self.env.shutdown() # really closes the db connections def test_match_request(self): req = Mock(path_info='/tt') self.assertEqual(True, self.tt.match_request(req)) req = Mock(path_info='/something') self.assertEqual(False, self.tt.match_request(req)) def test_load_template_text(self): templates = [ ("default2", "default text"), ("defect", "defect text"), ("enhancement", "enhancement text"), ("task", "task text"), ] self.tt._insert_templates(templates) for tt_name, tt_text in templates: self.assertEqual(tt_text, self.tt._loadTemplateText(tt_name)) def suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(TicketTemplateTestCase, 'test')) return suite if __name__ == '__main__': unittest.main() 1.0/tickettemplate/tests/__init__.py0000644000175000000620000000071114664037202016316 0ustar wmbstaff# -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 Richard Liao # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # import unittest from .tests import test_tt def suite(): suite = unittest.TestSuite() suite.addTest(test_tt.suite()) return suite if __name__ == '__main__': unittest.main(defaultTest='suite') 1.0/tickettemplate/model.py0000644000175000000620000001355614664037202014530 0ustar wmbstaff# -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 Richard Liao # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # """Model classes for objects persisted in the database.""" from trac.db import Column, Table from .utils import formatField, SYSTEM_USER class TT_Template(object): """Represents a generated tt.""" _schema = [ Table('ticket_template_store')[ Column('tt_time', type='int'), Column('tt_user'), Column('tt_name'), Column('tt_field'), Column('tt_value'), ] ] def __init__(self, env): """Initialize a new report with the specified attributes. To actually create this build log in the database, the `insert` method needs to be called. """ self.env = env exists = property(fget=lambda self: self.id is not None, doc="Whether this tt exists in the database") @classmethod def deleteCustom(cls, env, data): """Remove the tt from the database.""" env.db_transaction(""" DELETE FROM ticket_template_store WHERE tt_user=%s AND tt_name=%s """, (data['tt_user'], data['tt_name'])) @classmethod def insert(cls, env, record): """Insert a new tt into the database.""" env.db_transaction(""" INSERT INTO ticket_template_store (tt_time,tt_user,tt_name,tt_field,tt_value) VALUES (%s,%s,%s,%s,%s) """, record) @classmethod def fetchCurrent(cls, env, data): """Retrieve an existing tt from the database by ID.""" field_value_mapping = {} for tt_field, tt_value in env.db_query(""" SELECT tt_field, tt_value FROM ticket_template_store WHERE tt_user=%s AND tt_time=( SELECT max(tt_time) FROM ticket_template_store WHERE tt_name=%s) """, (data['tt_user'], data['tt_name'])): if tt_value: field_value_mapping[tt_field] = tt_value return field_value_mapping @classmethod def fetchAll(cls, env, data): """Retrieve an existing tt from the database by ID. result: { "field_value_mapping":{ "default":{ "summary":"aaa", "description":"bbb", }, }, "field_value_mapping_custom":{ "my_template":{ "summary":"ccc", "description":"ddd", }, }, } """ real_user = data.get('tt_user') req_args = data.get('req_args') field_value_mapping = {} field_value_mapping_custom = {} for tt_name, tt_field, tt_value in env.db_query(""" SELECT tt_name, tt_field, tt_value FROM ticket_template_store WHERE tt_user=%s """, (data['tt_user'],)): if tt_name not in field_value_mapping_custom: field_value_mapping_custom[tt_name] = {} if tt_value: tt_value = formatField(env.config, tt_value, real_user, req_args) field_value_mapping_custom[tt_name][tt_field] = tt_value # field_value_mapping tt_name_list = [name for name, in env.db_query(""" SELECT DISTINCT tt_name FROM ticket_template_store WHERE tt_user=%s """, (SYSTEM_USER,))] data['tt_user'] = SYSTEM_USER for tt_name in tt_name_list: data['tt_name'] = tt_name for tt_field, tt_value in env.db_query(""" SELECT tt_field, tt_value FROM ticket_template_store WHERE tt_user=%s AND tt_name=%s AND tt_time=(SELECT max(tt_time) FROM ticket_template_store WHERE tt_name=%s) """, (data['tt_user'], data['tt_name'], data['tt_name'])): if tt_name not in field_value_mapping: field_value_mapping[tt_name] = {} if tt_value: tt_value = formatField(env.config, tt_value, real_user, req_args) field_value_mapping[tt_name][tt_field] = tt_value result = { 'field_value_mapping': field_value_mapping, 'field_value_mapping_custom': field_value_mapping_custom } return result @classmethod def getCustomTemplate(cls, env, tt_user): """Retrieve from the database that match the specified criteria. """ return [name for name, in env.db_query(""" SELECT DISTINCT tt_name FROM ticket_template_store WHERE tt_user=%s ORDER BY tt_name """, (tt_user,))] @classmethod def fetch(cls, env, tt_name): """Retrieve an existing tt from the database by ID.""" tt_value = None for value, in env.db_query(""" SELECT tt_value FROM ticket_template_store WHERE tt_time=( SELECT max(tt_time) FROM ticket_template_store WHERE tt_name=%s) """, (tt_name,)): tt_value = value return tt_value @classmethod def fetchNames(cls, env): """fetch a list of existing tt names from database""" return [name for name, in env.db_query(""" SELECT DISTINCT tt_name FROM ticket_template_store """)] schema = TT_Template._schema schema_version = 4 1.0/tickettemplate/default_templates.py0000644000175000000620000000121214664037202017114 0ustar wmbstaff# -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 Richard Liao # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # DEFAULT_TEMPLATES = [ ("defect", """\ = bug description = = bug analysis = = fix recommendation ="""), ("enhancement", """\ = problem = = analysis = = enhancement recommendation ="""), ("task", """\ = phenomenon = = background analysis = = implementation recommendation ="""), ("default", """\ = phenomenon = = reason = = recommendation ="""), ] 1.0/tickettemplate/htdocs/0002755000175000000620000000000013065642300014325 5ustar wmbstaff1.0/tickettemplate/htdocs/json2.js0000644000175000000620000004267511334046722015735 0ustar wmbstaff/* http://www.JSON.org/json2.js 2008-07-15 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. See http://www.JSON.org/js.html This file creates a global JSON object containing two methods: stringify and parse. JSON.stringify(value, replacer, space) value any JavaScript value, usually an object or array. replacer an optional parameter that determines how object values are stringified for objects. It can be a function or an array. space an optional parameter that specifies the indentation of nested structures. If it is omitted, the text will be packed without extra whitespace. If it is a number, it will specify the number of spaces to indent at each level. If it is a string (such as '\t' or ' '), it contains the characters used to indent at each level. This method produces a JSON text from a JavaScript value. When an object value is found, if the object contains a toJSON method, its toJSON method will be called and the result will be stringified. A toJSON method does not serialize: it returns the value represented by the name/value pair that should be serialized, or undefined if nothing should be serialized. The toJSON method will be passed the key associated with the value, and this will be bound to the object holding the key. For example, this would serialize Dates as ISO strings. Date.prototype.toJSON = function (key) { function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } return this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z'; }; You can provide an optional replacer method. It will be passed the key and value of each member, with this bound to the containing object. The value that is returned from your method will be serialized. If your method returns undefined, then the member will be excluded from the serialization. If the replacer parameter is an array, then it will be used to select the members to be serialized. It filters the results such that only members with keys listed in the replacer array are stringified. Values that do not have JSON representations, such as undefined or functions, will not be serialized. Such values in objects will be dropped; in arrays they will be replaced with null. You can use a replacer function to replace those with JSON values. JSON.stringify(undefined) returns undefined. The optional space parameter produces a stringification of the value that is filled with line breaks and indentation to make it easier to read. If the space parameter is a non-empty string, then that string will be used for indentation. If the space parameter is a number, then the indentation will be that many spaces. Example: text = JSON.stringify(['e', {pluribus: 'unum'}]); // text is '["e",{"pluribus":"unum"}]' text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' text = JSON.stringify([new Date()], function (key, value) { return this[key] instanceof Date ? 'Date(' + this[key] + ')' : value; }); // text is '["Date(---current time---)"]' JSON.parse(text, reviver) This method parses a JSON text to produce an object or array. It can throw a SyntaxError exception. The optional reviver parameter is a function that can filter and transform the results. It receives each of the keys and values, and its return value is used instead of the original value. If it returns what it received, then the structure is not modified. If it returns undefined then the member is deleted. Example: // Parse the text. Values that look like ISO date strings will // be converted to Date objects. myData = JSON.parse(text, function (key, value) { var a; if (typeof value === 'string') { a = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); if (a) { return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6])); } } return value; }); myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { var d; if (typeof value === 'string' && value.slice(0, 5) === 'Date(' && value.slice(-1) === ')') { d = new Date(value.slice(5, -1)); if (d) { return d; } } return value; }); This is a reference implementation. You are free to copy, modify, or redistribute. This code should be minified before deployment. See http://javascript.crockford.com/jsmin.html USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO NOT CONTROL. */ /*jslint evil: true */ /*global JSON */ /*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length, parse, propertyIsEnumerable, prototype, push, replace, slice, stringify, test, toJSON, toString */ if (!this.JSON) { // Create a JSON object only if one does not already exist. We create the // object in a closure to avoid creating global variables. JSON = function () { function f(n) { // Format integers to have at least two digits. return n < 10 ? '0' + n : n; } Date.prototype.toJSON = function (key) { return this.getUTCFullYear() + '-' + f(this.getUTCMonth() + 1) + '-' + f(this.getUTCDate()) + 'T' + f(this.getUTCHours()) + ':' + f(this.getUTCMinutes()) + ':' + f(this.getUTCSeconds()) + 'Z'; }; String.prototype.toJSON = Number.prototype.toJSON = Boolean.prototype.toJSON = function (key) { return this.valueOf(); }; var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, gap, indent, meta = { // table of character substitutions '\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\' }, rep; function quote(string) { // If the string contains no control characters, no quote characters, and no // backslash characters, then we can safely slap some quotes around it. // Otherwise we must also replace the offending characters with safe escape // sequences. escapeable.lastIndex = 0; return escapeable.test(string) ? '"' + string.replace(escapeable, function (a) { var c = meta[a]; if (typeof c === 'string') { return c; } return '\\u' + ('0000' + (+(a.charCodeAt(0))).toString(16)).slice(-4); }) + '"' : '"' + string + '"'; } function str(key, holder) { // Produce a string from holder[key]. var i, // The loop counter. k, // The member key. v, // The member value. length, mind = gap, partial, value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value. if (value && typeof value === 'object' && typeof value.toJSON === 'function') { value = value.toJSON(key); } // If we were called with a replacer function, then call the replacer to // obtain a replacement value. if (typeof rep === 'function') { value = rep.call(holder, key, value); } // What happens next depends on the value's type. switch (typeof value) { case 'string': return quote(value); case 'number': // JSON numbers must be finite. Encode non-finite numbers as null. return isFinite(value) ? String(value) : 'null'; case 'boolean': case 'null': // If the value is a boolean or null, convert it to a string. Note: // typeof null does not produce 'null'. The case is included here in // the remote chance that this gets fixed someday. return String(value); // If the type is 'object', we might be dealing with an object or an array or // null. case 'object': // Due to a specification blunder in ECMAScript, typeof null is 'object', // so watch out for that case. if (!value) { return 'null'; } // Make an array to hold the partial results of stringifying this object value. gap += indent; partial = []; // If the object has a dontEnum length property, we'll treat it as an array. if (typeof value.length === 'number' && !(value.propertyIsEnumerable('length'))) { // The object is an array. Stringify every element. Use null as a placeholder // for non-JSON values. length = value.length; for (i = 0; i < length; i += 1) { partial[i] = str(i, value) || 'null'; } // Join all of the elements together, separated with commas, and wrap them in // brackets. v = partial.length === 0 ? '[]' : gap ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' : '[' + partial.join(',') + ']'; gap = mind; return v; } // If the replacer is an array, use it to select the members to be stringified. if (rep && typeof rep === 'object') { length = rep.length; for (i = 0; i < length; i += 1) { k = rep[i]; if (typeof k === 'string') { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } else { // Otherwise, iterate through all of the keys in the object. for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = str(k, value); if (v) { partial.push(quote(k) + (gap ? ': ' : ':') + v); } } } } // Join all of the member texts together, separated with commas, // and wrap them in braces. v = partial.length === 0 ? '{}' : gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' : '{' + partial.join(',') + '}'; gap = mind; return v; } } // Return the JSON object containing the stringify and parse methods. return { stringify: function (value, replacer, space) { // The stringify method takes a value and an optional replacer, and an optional // space parameter, and returns a JSON text. The replacer can be a function // that can replace values, or an array of strings that will select the keys. // A default replacer method can be provided. Use of the space parameter can // produce text that is more easily readable. var i; gap = ''; indent = ''; // If the space parameter is a number, make an indent string containing that // many spaces. if (typeof space === 'number') { for (i = 0; i < space; i += 1) { indent += ' '; } // If the space parameter is a string, it will be used as the indent string. } else if (typeof space === 'string') { indent = space; } // If there is a replacer, it must be a function or an array. // Otherwise, throw an error. rep = replacer; if (replacer && typeof replacer !== 'function' && (typeof replacer !== 'object' || typeof replacer.length !== 'number')) { throw new Error('JSON.stringify'); } // Make a fake root object containing our value under the key of ''. // Return the result of stringifying the value. return str('', {'': value}); }, parse: function (text, reviver) { // The parse method takes a text and an optional reviver function, and returns // a JavaScript value if the text is a valid JSON text. var j; function walk(holder, key) { // The walk method is used to recursively walk the resulting structure so // that modifications can be made. var k, v, value = holder[key]; if (value && typeof value === 'object') { for (k in value) { if (Object.hasOwnProperty.call(value, k)) { v = walk(value, k); if (v !== undefined) { value[k] = v; } else { delete value[k]; } } } } return reviver.call(holder, key, value); } // Parsing happens in four stages. In the first stage, we replace certain // Unicode characters with escape sequences. JavaScript handles many characters // incorrectly, either silently deleting them, or treating them as line endings. cx.lastIndex = 0; if (cx.test(text)) { text = text.replace(cx, function (a) { return '\\u' + ('0000' + (+(a.charCodeAt(0))).toString(16)).slice(-4); }); } // In the second stage, we run the text against regular expressions that look // for non-JSON patterns. We are especially concerned with '()' and 'new' // because they can cause invocation, and '=' because it can cause mutation. // But just to be safe, we want to reject all unexpected forms. // We split the second stage into 4 regexp operations in order to work around // crippling inefficiencies in IE's and Safari's regexp engines. First we // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we // replace all simple value tokens with ']' characters. Third, we delete all // open brackets that follow a colon or comma or that begin the text. Finally, // we look to see that the remaining characters are only whitespace or ']' or // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. if (/^[\],:{}\s]*$/. test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'). replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'). replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { // In the third stage we use the eval function to compile the text into a // JavaScript structure. The '{' operator is subject to a syntactic ambiguity // in JavaScript: it can begin a block or an object literal. We wrap the text // in parens to eliminate the ambiguity. j = eval('(' + text + ')'); // In the optional fourth stage, we recursively walk the new structure, passing // each name/value pair to a reviver function for possible transformation. return typeof reviver === 'function' ? walk({'': j}, '') : j; } // If the text is not JSON parseable, then a SyntaxError is thrown. throw new SyntaxError('JSON.parse'); } }; }(); } 1.0/tickettemplate/htdocs/tt.ico0000644000175000000620000000706611074573640015467 0ustar wmbstaffh& ( @ZZ88ppKK((BB{{hiaa22QQ!!          ?!??( @[[99rrKK((ffBBzz11!!bbQQlmwx//tt#      ""         #  "! ! $    #  !             !   ! $  ? ??? <   <???1.0/tickettemplate/htdocs/tt_newticket.js0000644000175000000620000002344013065642300017370 0ustar wmbstaffjQuery(document).ready(function($) { function _(message) { return message; } messages = [ _('My Template'), _('Create'), _('Delete'), _('Save template success: '), _('Delete template success: '), _('Please input template name (overwriting existing one with equal name).'), _('Sure to restore the last edited content?'), _('Submit ticket?'), _('Replace ticket content with template?'), _('') ]; var myTemplateHtml = "
" + _("My Template") + "
"; var rel_url = "tt/"; var queryResult = null; var isLoad = true; function _updateTargetElem(ticketType) { // for TracWysiwyg var wysiwyg_mode = $("#editor-wysiwyg-1:checked").length; if (wysiwyg_mode) { $("#editor-textarea-1").click(); } for (field in ticketType) { // update targetElem value var targetElem = document.getElementById('field-' + field); if (targetElem) { if (targetElem.type == 'checkbox') { targetElem.checked = ticketType[field] == 1; } else if (targetElem.type == 'select-one') { var options = targetElem.getElementsByTagName('option'); for (var i = 0; i < options.length; i++) { var elem = options[i]; if (elem.text == ticketType[field]) { elem.selected = true; } } } else if (targetElem.type == 'hidden') { // for SubcomponentsPlugin var comp_parts = ticketType[field].split('/'); for (var i = 1; i <= comp_parts.length; i++) { $('#component-selector1-' + i).val(comp_parts[i - 1]).change(); } } else { targetElem.value = ticketType[field]; } } else { var targetElems = document.getElementsByName('field_' + field); for (var i = 0; i < targetElems.length; i++) { var elem = targetElems[i]; if (elem.value == ticketType[field]) { if (elem.type == 'checkbox') { elem.checked = ticketType[field] == 1; } } } } } // for TracWysiwyg if (wysiwyg_mode) { $("#editor-wysiwyg-1").click(); } } function handleResponseQuery(result) { if (result == 0 || result == -1) { return; } queryResult = result; if (isLoad == true && queryResult.enable_custom) { // custom $("#content").prepend(myTemplateHtml); $("#tt_custom_select") .append("").val("") .change(onCustomChanged); $("#tt_custom_save").click(onCustomSave); $("#tt_custom_delete").click(onCustomDelete); $("#tt_custom_tip").click(onCustomTip); } // fill custom template select var custom_names = []; for (name in queryResult.field_value_mapping_custom) { custom_names.push(name); } custom_names.sort(); var tt_custom_select = $("#tt_custom_select"); for (var i in custom_names) { var name = custom_names[i]; tt_custom_select.append(""); } // delete edit_buffer from my template tt_custom_select.find("option:contains('edit_buffer')").remove(); if (isLoad == true) { var evt = {"type": "change"}; // trigger type change var params = location.search.slice(1).split("&"); var ticket_preset = false; for (var i = 0; i < params.length; i++) { var param = params[i]; var param_name = param.split("=")[0]; if (param_name == "description") { ticket_preset = true; break; } } if (!ticket_preset) { $("#field-type").change(); } var reapplyTemplate = result["warning"] != "1" && !preview; initTypeChanged(reapplyTemplate); } } function handleResponseSave(jsonStr) { var result = JSON.parse(jsonStr, null); if (result == 0 || result == -1) { return; } var tt_name = result.tt_name; // update field_value_mapping_custom queryResult.field_value_mapping_custom[tt_name] = result.new_template; // insert options var tt_custom_select = $("tt_custom_select"); if (!tt_custom_select.find("option:contains('" + tt_name + "')").length) { var optionNew = ''; tt_custom_select.append(optionNew); } var tt_custom_tip = $('#tt_custom_tip'); tt_custom_tip.empty(); tt_custom_tip.append(_("Save template success: ") + tt_name); } function handleResponseDelete(jsonStr) { var result = JSON.parse(jsonStr, null); if (result == 0 || result == -1) { return; } var tt_name = result.tt_name; var tt_custom_select = $("tt_custom_select"); tt_custom_select.find('option:selected').remove(); // set custom to null tt_custom_select.val(""); var tt_custom_tip = $('#tt_custom_tip'); tt_custom_tip.empty(); tt_custom_tip.append(_("Delete template success: ") + tt_name); } function onCustomChanged(evt) { var tt_name = $("#tt_custom_select").val(); if (!tt_name) { return; } if (evt.type == "change") { var answer = confirm(_("Replace ticket content with template?")); if (!answer) { return; } } // apply custom template var ticketType = queryResult.field_value_mapping_custom[tt_name]; _updateTargetElem(ticketType); } function onCustomTip(evt) { $("#tt_custom_tip").empty(); } function onCustomSave(evt) { var tt_name = prompt(_("Please input template name (overwriting existing one with equal name).")); if (!tt_name) { return; } // for TracWysiwyg var wysiwyg_mode = $("#editor-wysiwyg-1:checked").length; if (wysiwyg_mode) { $("#editor-textarea-1").click(); } // get new_template var new_template = {}; for (var i = 0; i < queryResult.field_list.length; i++) { var field = queryResult.field_list[i]; var elem = document.getElementById('field-' + field); if (elem) { if (elem.type == 'checkbox') { if (elem.checked) { new_template[field] = 1; } else { new_template[field] = 0; } } else if (elem.type == 'select-one') { var options = elem.getElementsByTagName('option'); for (var j = 0; j < options.length; j++) { if (options[j].selected) { new_template[field] = options[j].text; } } } else { new_template[field] = elem.value; } } } // for TracWysiwyg if (wysiwyg_mode) { $("#editor-wysiwyg-1").click(); } // dump json string data = { "tt_name": tt_name, "custom_template": new_template }; var json_string = JSON.stringify(data); $.ajax( { cache: false, async: true, url: rel_url + "custom_save", success: handleResponseSave, type: "POST", contentType: "application/json", data: json_string } ); } function onCustomDelete(evt) { var tt_name = $("#tt_custom_select").val(); if (!tt_name) { return; } // dump json string data = { "tt_name": tt_name }; var json_string = JSON.stringify(data); $.ajax( { cache: false, async: true, url: rel_url + "custom_delete", success: handleResponseDelete, type: "POST", contentType: "application/json", data: json_string } ); } function loadEditBuffer(evt) { var answer = confirm(_("Sure to restore the last edited content?")); if (!answer) { return; } // get last ticket field values $.getJSON(rel_url + "query", function (result) { // replace ticket fields queryResult = result; var ticketType = queryResult.field_value_mapping_custom["edit_buffer"]; _updateTargetElem(ticketType); }); } function onTypeChanged(evt) { var tt_name = $("#field-type").val(); if (!tt_name) { tt_name = $("#field-type", ttTypeCache["Default"]).val(); } if (!queryResult) { return; } if (isLoad) { return; } if (evt.type == "change" && isLoad == false) { var answer = confirm(_("Replace ticket content with template?")); if (!answer) { return; } } var ticketType = queryResult.field_value_mapping[tt_name]; if (!ticketType) { ticketType = queryResult.field_value_mapping['default']; } _updateTargetElem(ticketType); } function initTypeChanged(reapplyTemplate) { // reset isLoad isLoad = false; if (location.href.indexOf("newticket#ticket", location.href.length - "newticket#ticket".length) !== -1) { return; } var tt_name = $("#field-type").val(); if (!tt_name) { tt_name = $("#field-type", ttTypeCache["Default"]).val(); } if (!queryResult) { return; } if (reapplyTemplate) { var ticketType = queryResult.field_value_mapping[tt_name]; if (!ticketType) { ticketType = queryResult.field_value_mapping['default']; } _updateTargetElem(ticketType); } } $("#field-type").change(onTypeChanged); // requery if ($("#warning").get(0)) { $.getJSON("tt/query" + location.search, {warning: "1"}, handleResponseQuery); } else { $.getJSON("tt/query" + location.search, handleResponseQuery); } }); 1.0/tickettemplate/locale/0002755000175000000620000000000012542062701014300 5ustar wmbstaff1.0/tickettemplate/locale/messages.pot0000644000175000000620000000264712542062701016642 0ustar wmbstaff# Translations template for TracTicketTemplate. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the # TracTicketTemplate project. # FIRST AUTHOR , 2015. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: TracTicketTemplate 1.0\n" "Report-Msgid-Bugs-To: richard.liao.i@gmail.com\n" "POT-Creation-Date: 2015-06-22 11:58-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.3\n" #: tickettemplate/ttadmin.py:194 msgid "Ticket System" msgstr "" #: tickettemplate/ttadmin.py:195 msgid "Ticket Template" msgstr "" #: tickettemplate/ttadmin.py:208 msgid "default" msgstr "" #: tickettemplate/templates/admin_tickettemplate.html:20 msgid "Available Projects" msgstr "" #: tickettemplate/templates/admin_tickettemplate.html:23 msgid "Ticket: Template" msgstr "" #: tickettemplate/templates/admin_tickettemplate.html:26 msgid "Type:" msgstr "" #: tickettemplate/templates/admin_tickettemplate.html:34 msgid "Load" msgstr "" #: tickettemplate/templates/admin_tickettemplate.html:35 msgid "Apply change" msgstr "" #: tickettemplate/templates/admin_tickettemplate.html:36 msgid "Reset" msgstr "" #: tickettemplate/templates/admin_tickettemplate.html:40 msgid "Description:" msgstr "" 1.0/tickettemplate/locale/messages-js.pot0000644000175000000620000000347112542062701017250 0ustar wmbstaff# Translations template for TracTicketTemplate. # Copyright (C) 2015 ORGANIZATION # This file is distributed under the same license as the TracTicketTemplate # project. # FIRST AUTHOR , 2015. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: TracTicketTemplate 1.0\n" "Report-Msgid-Bugs-To: richard.liao.i@gmail.com\n" "POT-Creation-Date: 2015-06-22 12:34-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.3\n" #: tickettemplate/htdocs/tt_newticket.js:7 #: tickettemplate/htdocs/tt_newticket.js:19 msgid "My Template" msgstr "" #: tickettemplate/htdocs/tt_newticket.js:8 #: tickettemplate/htdocs/tt_newticket.js:19 msgid "Create" msgstr "" #: tickettemplate/htdocs/tt_newticket.js:9 #: tickettemplate/htdocs/tt_newticket.js:19 msgid "Delete" msgstr "" #: tickettemplate/htdocs/tt_newticket.js:10 #: tickettemplate/htdocs/tt_newticket.js:146 msgid "Save template success: " msgstr "" #: tickettemplate/htdocs/tt_newticket.js:11 #: tickettemplate/htdocs/tt_newticket.js:166 msgid "Delete template success: " msgstr "" #: tickettemplate/htdocs/tt_newticket.js:12 #: tickettemplate/htdocs/tt_newticket.js:193 msgid "Please input template name (overwriting existing one with equal name)." msgstr "" #: tickettemplate/htdocs/tt_newticket.js:13 #: tickettemplate/htdocs/tt_newticket.js:282 msgid "Sure to restore the last edited content?" msgstr "" #: tickettemplate/htdocs/tt_newticket.js:14 msgid "Submit ticket?" msgstr "" #: tickettemplate/htdocs/tt_newticket.js:15 #: tickettemplate/htdocs/tt_newticket.js:177 #: tickettemplate/htdocs/tt_newticket.js:312 msgid "Replace ticket content with template?" msgstr "" 1.0/tickettemplate/locale/de/0002755000175000000620000000000011376623443014702 5ustar wmbstaff1.0/tickettemplate/locale/de/LC_MESSAGES/0002755000175000000620000000000011376623443016467 5ustar wmbstaff1.0/tickettemplate/locale/de/LC_MESSAGES/tickettemplate.mo0000644000175000000620000000301211376623443022035 0ustar wmbstaffL MZmt{  F% &>(M v ]i ~"  V+- Y#g; Apply changeAvailable ProjectsCreateDeleteDelete template success: Description:LoadMy TemplatePlease input template name (overwriting existing one with equal name).Replace ticket content with template?ResetSave template success: Submit ticket?Sure to restore the last edited content?Ticket SystemTicket TemplateTicket: TemplateType:Project-Id-Version: Trac 0.12 Report-Msgid-Bugs-To: richard.liao.i@gmail.com POT-Creation-Date: 2010-05-24 09:49+0800 PO-Revision-Date: 2010-05-24 13:53+0200 Last-Translator: Steffen Hoffmann Language-Team: German > Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 1.0dev-r14358 ÜbernehmenVerfügbare ProjekteErstellenLöschenLöschen der Vorlage erfolgreich: Beschreibung:LadenMeine VorlageBitte geben Sie einen Vorlagenamen an (überschreibe vorhandene bei Namensgleichheit).Ticket-Beschreibung durch Vorlage ersetzen?ZurücksetzenSpeichern der Vorlage erfolgreich: Ticket absenden?Wollen Sie wirklich den vorherigen Inhalt wiederherstellen?Ticket-SystemTicket-VorlageTicket: VorlageTyp:1.0/tickettemplate/locale/de/LC_MESSAGES/tickettemplate.po0000644000175000000620000000532211376623443022046 0ustar wmbstaff# translation of tickettemplate.po to German # German (Germany) translations for TracTicketTemplate. # Copyright (C) 2010 # This file is distributed under the same license as the # TracTicketTemplate project. # # Steffen Hoffmann , 2010. msgid "" msgstr "" "Project-Id-Version: Trac 0.12\n" "Report-Msgid-Bugs-To: richard.liao.i@gmail.com\n" "POT-Creation-Date: 2010-05-24 09:49+0800\n" "PO-Revision-Date: 2010-05-24 13:53+0200\n" "Last-Translator: Steffen Hoffmann \n" "Language-Team: German >\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.0dev-r482\n" "X-Generator: KBabel 1.11.4\n" #: tickettemplate/ttadmin.py:137 msgid "Ticket System" msgstr "Ticket-System" #: tickettemplate/ttadmin.py:137 msgid "Ticket Template" msgstr "Ticket-Vorlage" #: tickettemplate/templates/admin_tickettemplate.html:12 msgid "Available Projects" msgstr "Verfügbare Projekte" #: tickettemplate/templates/admin_tickettemplate.html:15 msgid "Ticket: Template" msgstr "Ticket: Vorlage" #: tickettemplate/templates/admin_tickettemplate.html:20 msgid "Type:" msgstr "Typ:" #: tickettemplate/templates/admin_tickettemplate.html:28 msgid "Load" msgstr "Laden" #: tickettemplate/templates/admin_tickettemplate.html:29 msgid "Apply change" msgstr "Übernehmen" #: tickettemplate/templates/admin_tickettemplate.html:30 msgid "Reset" msgstr "Zurücksetzen" #: tickettemplate/templates/admin_tickettemplate.html:35 msgid "Description:" msgstr "Beschreibung:" #: tickettemplate/templates/tt_newticket.js:9 msgid "My Template" msgstr "Meine Vorlage" #: tickettemplate/templates/tt_newticket.js:10 msgid "Create" msgstr "Erstellen" #: tickettemplate/templates/tt_newticket.js:11 msgid "Delete" msgstr "Löschen" #: tickettemplate/templates/tt_newticket.js:12 msgid "Save template success: " msgstr "Speichern der Vorlage erfolgreich: " #: tickettemplate/templates/tt_newticket.js:13 msgid "Delete template success: " msgstr "Löschen der Vorlage erfolgreich: " #: tickettemplate/templates/tt_newticket.js:14 msgid "Please input template name (overwriting existing one with equal name)." msgstr "Bitte geben Sie einen Vorlagenamen an (überschreibe vorhandene bei Namensgleichheit)." #: tickettemplate/templates/tt_newticket.js:15 msgid "Sure to restore the last edited content?" msgstr "Wollen Sie wirklich den vorherigen Inhalt wiederherstellen?" #: tickettemplate/templates/tt_newticket.js:16 msgid "Submit ticket?" msgstr "Ticket absenden?" #: tickettemplate/templates/tt_newticket.js:17 msgid "Replace ticket content with template?" msgstr "Ticket-Beschreibung durch Vorlage ersetzen?" 1.0/tickettemplate/locale/zh_CN/0002755000175000000620000000000011376356233015313 5ustar wmbstaff1.0/tickettemplate/locale/zh_CN/LC_MESSAGES/0002755000175000000620000000000011376356233017100 5ustar wmbstaff1.0/tickettemplate/locale/zh_CN/LC_MESSAGES/tickettemplate.mo0000644000175000000620000000263311376356233022456 0ustar wmbstaffL MZmt{  F% &>(M v B O\cj O"*(A j wApply changeAvailable ProjectsCreateDeleteDelete template success: Description:LoadMy TemplatePlease input template name (overwriting existing one with equal name).Replace ticket content with template?ResetSave template success: Submit ticket?Sure to restore the last edited content?Ticket SystemTicket TemplateTicket: TemplateType:Project-Id-Version: TracTicketTemplate 0.7 Report-Msgid-Bugs-To: richard.liao.i@gmail.com POT-Creation-Date: 2010-05-24 09:49+0800 PO-Revision-Date: 2010-02-03 16:24+0800 Last-Translator: FULL NAME Language-Team: zh_CN Plural-Forms: nplurals=1; plural=0 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 1.0dev-r14358 应用变更有效项目创建删除成功删除模板: 描述:载入我的模板请输入模板名称(使用已有模板名称, 可以覆盖现有模板内容)使用模板替换传票内容吗?重置成功保存模板: 确认提交传票吗?确定使用上次编辑内容替换吗?传票系统传票模板传票: 模板类型:1.0/tickettemplate/locale/zh_CN/LC_MESSAGES/tickettemplate.po0000644000175000000620000000517411376356233022464 0ustar wmbstaff# Chinese (China) translations for TracTicketTemplate. # Copyright (C) 2010 ORGANIZATION # This file is distributed under the same license as the # TracTicketTemplate project. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: TracTicketTemplate 0.7\n" "Report-Msgid-Bugs-To: richard.liao.i@gmail.com\n" "POT-Creation-Date: 2010-05-24 09:49+0800\n" "PO-Revision-Date: 2010-02-03 16:24+0800\n" "Last-Translator: FULL NAME \n" "Language-Team: zh_CN \n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.0dev-r14358\n" #: tickettemplate/ttadmin.py:137 msgid "Ticket System" msgstr "传票系统" #: tickettemplate/ttadmin.py:137 msgid "Ticket Template" msgstr "传票模板" #: tickettemplate/templates/admin_tickettemplate.html:12 msgid "Available Projects" msgstr "有效项目" #: tickettemplate/templates/admin_tickettemplate.html:15 msgid "Ticket: Template" msgstr "传票: 模板" #: tickettemplate/templates/admin_tickettemplate.html:20 msgid "Type:" msgstr "类型:" #: tickettemplate/templates/admin_tickettemplate.html:28 msgid "Load" msgstr "载入" #: tickettemplate/templates/admin_tickettemplate.html:29 msgid "Apply change" msgstr "应用变更" #: tickettemplate/templates/admin_tickettemplate.html:30 msgid "Reset" msgstr "重置" #: tickettemplate/templates/admin_tickettemplate.html:35 msgid "Description:" msgstr "描述:" #: tickettemplate/templates/tt_newticket.js:9 msgid "My Template" msgstr "我的模板" #: tickettemplate/templates/tt_newticket.js:10 msgid "Create" msgstr "创建" #: tickettemplate/templates/tt_newticket.js:11 msgid "Delete" msgstr "删除" #: tickettemplate/templates/tt_newticket.js:12 msgid "Save template success: " msgstr "成功保存模板: " #: tickettemplate/templates/tt_newticket.js:13 msgid "Delete template success: " msgstr "成功删除模板: " #: tickettemplate/templates/tt_newticket.js:14 msgid "Please input template name (overwriting existing one with equal name)." msgstr "请输入模板名称(使用已有模板名称, 可以覆盖现有模板内容)" #: tickettemplate/templates/tt_newticket.js:15 msgid "Sure to restore the last edited content?" msgstr "确定使用上次编辑内容替换吗?" #: tickettemplate/templates/tt_newticket.js:16 msgid "Submit ticket?" msgstr "确认提交传票吗?" #: tickettemplate/templates/tt_newticket.js:17 msgid "Replace ticket content with template?" msgstr "使用模板替换传票内容吗?" 1.0/tickettemplate/locale/ja/0002755000175000000620000000000011400610762014670 5ustar wmbstaff1.0/tickettemplate/locale/ja/LC_MESSAGES/0002755000175000000620000000000011400610762016455 5ustar wmbstaff1.0/tickettemplate/locale/ja/LC_MESSAGES/tickettemplate.mo0000644000175000000620000000312611400610762022031 0ustar wmbstaffL MZmt{  F% &>(M v?Oho)v tE: )' -NApply changeAvailable ProjectsCreateDeleteDelete template success: Description:LoadMy TemplatePlease input template name (overwriting existing one with equal name).Replace ticket content with template?ResetSave template success: Submit ticket?Sure to restore the last edited content?Ticket SystemTicket TemplateTicket: TemplateType:Project-Id-Version: TracTicketTemplate 0.7 Report-Msgid-Bugs-To: richard.liao.i@gmail.com POT-Creation-Date: 2010-05-24 09:49+0800 PO-Revision-Date: 2010-05-31 09:27+0800 Last-Translator: FULL NAME Language-Team: ja Plural-Forms: nplurals=1; plural=0 MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 1.0dev-r14358 変更を適用プロジェクト一覧作成削除テンプレートを削除しました: 詳細:ロードテンプレートテンプレート名を入力してください(既に存在する名前を使うと内容は上書きされます)チケットをテンプレートの内容で置き換えますか?リセットテンプレートを保存しました: 送信しますか?編集内容で置き換えますか?チケットシステムチケットテンプレートチケット: テンプレート種別:1.0/tickettemplate/locale/ja/LC_MESSAGES/tickettemplate.po0000644000175000000620000000544311400610762022040 0ustar wmbstaff# Japanese translations for TracTicketTemplate. # Copyright (C) 2010 # This file is distributed under the same license as the # TracTicketTemplate project. # Jun Omae , 2010. # msgid "" msgstr "" "Project-Id-Version: TracTicketTemplate 0.7\n" "Report-Msgid-Bugs-To: richard.liao.i@gmail.com\n" "POT-Creation-Date: 2010-05-24 09:49+0800\n" "PO-Revision-Date: 2010-05-31 09:27+0800\n" "Last-Translator: FULL NAME \n" "Language-Team: ja \n" "Plural-Forms: nplurals=1; plural=0\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.0dev-r14358\n" #: tickettemplate/ttadmin.py:137 msgid "Ticket System" msgstr "チケットシステム" #: tickettemplate/ttadmin.py:137 msgid "Ticket Template" msgstr "チケットテンプレート" #: tickettemplate/templates/admin_tickettemplate.html:12 msgid "Available Projects" msgstr "プロジェクト一覧" #: tickettemplate/templates/admin_tickettemplate.html:15 msgid "Ticket: Template" msgstr "チケット: テンプレート" #: tickettemplate/templates/admin_tickettemplate.html:20 msgid "Type:" msgstr "種別:" #: tickettemplate/templates/admin_tickettemplate.html:28 msgid "Load" msgstr "ロード" #: tickettemplate/templates/admin_tickettemplate.html:29 msgid "Apply change" msgstr "変更を適用" #: tickettemplate/templates/admin_tickettemplate.html:30 msgid "Reset" msgstr "リセット" #: tickettemplate/templates/admin_tickettemplate.html:35 msgid "Description:" msgstr "詳細:" #: tickettemplate/templates/tt_newticket.js:9 msgid "My Template" msgstr "テンプレート" #: tickettemplate/templates/tt_newticket.js:10 msgid "Create" msgstr "作成" #: tickettemplate/templates/tt_newticket.js:11 msgid "Delete" msgstr "削除" #: tickettemplate/templates/tt_newticket.js:12 msgid "Save template success: " msgstr "テンプレートを保存しました: " #: tickettemplate/templates/tt_newticket.js:13 msgid "Delete template success: " msgstr "テンプレートを削除しました: " #: tickettemplate/templates/tt_newticket.js:14 msgid "Please input template name (overwriting existing one with equal name)." msgstr "テンプレート名を入力してください(既に存在する名前を使うと内容は上書きされます)" #: tickettemplate/templates/tt_newticket.js:15 msgid "Sure to restore the last edited content?" msgstr "編集内容で置き換えますか?" #: tickettemplate/templates/tt_newticket.js:16 msgid "Submit ticket?" msgstr "送信しますか?" #: tickettemplate/templates/tt_newticket.js:17 msgid "Replace ticket content with template?" msgstr "チケットをテンプレートの内容で置き換えますか?" 1.0/tickettemplate/locale/it/0002755000175000000620000000000011431743506014721 5ustar wmbstaff1.0/tickettemplate/locale/it/LC_MESSAGES/0002755000175000000620000000000011431743506016506 5ustar wmbstaff1.0/tickettemplate/locale/it/LC_MESSAGES/tickettemplate.mo0000644000175000000620000000307011431743506022060 0ustar wmbstaffL MZmt{  F% &>(M vl W6D{9 2Apply changeAvailable ProjectsCreateDeleteDelete template success: Description:LoadMy TemplatePlease input template name (overwriting existing one with equal name).Replace ticket content with template?ResetSave template success: Submit ticket?Sure to restore the last edited content?Ticket SystemTicket TemplateTicket: TemplateType:Project-Id-Version: TracTicketTemplate 0.7 Report-Msgid-Bugs-To: richard.liao.i@gmail.com POT-Creation-Date: 2010-05-24 09:49+0800 PO-Revision-Date: 2010-08-15 18:53+0800 Last-Translator: Roberto Longobardi seccanj@gmail.com Language-Team: Italian it_IT Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 1.0dev-r14358 Applica le modificheProgetti DisponibiliCreaEliminaRisultato dell'eliminazione: Descrizione:CaricaModello PersonalizzatoInserisci il nome del modello (sovrascriverai un eventuale modello con lo stesso nome).Sostituire il testo della segnalazione con il modello?ResettaRisultato del salvataggio: Sottomettere la segnalazione?Sei sicuro di voler ripristinare il precedente contenuto?SegnalazioniModello di SegnalazioneSegnalazione: ModelloTipo:1.0/tickettemplate/locale/it/LC_MESSAGES/tickettemplate.po0000644000175000000620000000525411431743506022071 0ustar wmbstaff# Italian (Italy) translations for TracTicketTemplate. # Copyright (C) 2010 # This file is distributed under the same license as the # TracTicketTemplate project. # Roberto Longobardi , 2010. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: TracTicketTemplate 0.7\n" "Report-Msgid-Bugs-To: richard.liao.i@gmail.com\n" "POT-Creation-Date: 2010-05-24 09:49+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Roberto Longobardi seccanj@gmail.com\n" "Language-Team: Italian it_IT \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 1.0dev-r14358\n" #: tickettemplate/ttadmin.py:137 msgid "Ticket System" msgstr "Segnalazioni" #: tickettemplate/ttadmin.py:137 msgid "Ticket Template" msgstr "Modello di Segnalazione" #: tickettemplate/templates/admin_tickettemplate.html:12 msgid "Available Projects" msgstr "Progetti Disponibili" #: tickettemplate/templates/admin_tickettemplate.html:15 msgid "Ticket: Template" msgstr "Segnalazione: Modello" #: tickettemplate/templates/admin_tickettemplate.html:20 msgid "Type:" msgstr "Tipo:" #: tickettemplate/templates/admin_tickettemplate.html:28 msgid "Load" msgstr "Carica" #: tickettemplate/templates/admin_tickettemplate.html:29 msgid "Apply change" msgstr "Applica le modifiche" #: tickettemplate/templates/admin_tickettemplate.html:30 msgid "Reset" msgstr "Resetta" #: tickettemplate/templates/admin_tickettemplate.html:35 msgid "Description:" msgstr "Descrizione:" #: tickettemplate/templates/tt_newticket.js:9 msgid "My Template" msgstr "Modello Personalizzato" #: tickettemplate/templates/tt_newticket.js:10 msgid "Create" msgstr "Crea" #: tickettemplate/templates/tt_newticket.js:11 msgid "Delete" msgstr "Elimina" #: tickettemplate/templates/tt_newticket.js:12 msgid "Save template success: " msgstr "Risultato del salvataggio: " #: tickettemplate/templates/tt_newticket.js:13 msgid "Delete template success: " msgstr "Risultato dell'eliminazione: " #: tickettemplate/templates/tt_newticket.js:14 msgid "Please input template name (overwriting existing one with equal name)." msgstr "" "Inserisci il nome del modello (sovrascriverai un eventuale modello con lo " "stesso nome)." #: tickettemplate/templates/tt_newticket.js:15 msgid "Sure to restore the last edited content?" msgstr "Sei sicuro di voler ripristinare il precedente contenuto?" #: tickettemplate/templates/tt_newticket.js:16 msgid "Submit ticket?" msgstr "Sottomettere la segnalazione?" #: tickettemplate/templates/tt_newticket.js:17 msgid "Replace ticket content with template?" msgstr "Sostituire il testo della segnalazione con il modello?" 1.0/tickettemplate/utils.py0000644000175000000620000000176412376716631014576 0ustar wmbstaff# -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 Richard Liao # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # import calendar import time calendar.setfirstweekday(calendar.MONDAY) SEC_PER_DAY = 60 * 60 * 24 SEC_PER_WEEK = 60 * 60 * 24 * 7 SYSTEM_USER = "SYSTEM" DEFAULT_FORMAT = { "now": "%Y-%m-%d %H:%M:%S", "today": "%Y-%m-%d", "this_week": "%w", "this_month": "%m", "this_year": "%Y", } def formatField(config, tt_value, user, req_args): """ format field value """ # generate format dict mapping = {} t = int(time.time()) for k, v in DEFAULT_FORMAT.items(): format = config.get('tickettemplate', k + '_format', v) mapping[k] = time.strftime(format, time.localtime(t)) mapping['user'] = user mapping.update(req_args) try: return tt_value % mapping except: return tt_value 1.0/tickettemplate/templates/0002755000175000000620000000000014664037202015044 5ustar wmbstaff1.0/tickettemplate/templates/genshi/0002755000175000000620000000000014664037202016321 5ustar wmbstaff1.0/tickettemplate/templates/genshi/admin_tickettemplate.html0000644000175000000620000000335014664037202023375 0ustar wmbstaff Available Projects

Ticket: Template

1.0/tickettemplate/templates/jinja2/0002755000175000000620000000000014664037202016221 5ustar wmbstaff1.0/tickettemplate/templates/jinja2/admin_tickettemplate.html0000644000175000000620000000275014664037202023300 0ustar wmbstaff{# Copyright (C) 2008-2013 Richard Liao All rights reserved. This software is licensed as described in the file COPYING, which you should have received as part of this distribution. #} # extends 'admin.html' # set domain = 'tickettemplate' # set _ = partial(dgettext, domain) # set admintitle = _("Ticket: Template") # block admintitle ${admintitle} # endblock admintitle # block adminpanel

${admintitle}

${jmacros.form_token_input()}

# endblock adminpanel 1.0/tickettemplate/__init__.py0000644000175000000620000000007212626050350015147 0ustar wmbstaffimport pkg_resources pkg_resources.require('Trac >= 1.0') 1.0/tickettemplate/ttadmin.py0000644000175000000620000004226314664037202015065 0ustar wmbstaff# -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 Richard Liao # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # from __future__ import with_statement import inspect import json import textwrap import time from pkg_resources import resource_exists, resource_filename from trac.admin.api import IAdminCommandProvider, IAdminPanelProvider from trac.core import Component, implements from trac.config import BoolOption, ListOption, Option from trac.db import DatabaseManager from trac.env import IEnvironmentSetupParticipant from trac.perm import IPermissionRequestor from trac.ticket import Ticket, Type as TicketType from trac.util.translation import domain_functions from trac.web.api import HTTPBadRequest, IRequestHandler, IRequestFilter, \ RequestDone from trac.web.chrome import Chrome, ITemplateProvider, add_script, \ add_script_data from .default_templates import DEFAULT_TEMPLATES from .model import TT_Template, schema, schema_version from .utils import SYSTEM_USER gettext, _, tag_, N_, add_domain = \ domain_functions('tickettemplate', 'gettext', '_', 'tag_', 'N_', 'add_domain') def _recv_json(req): type_ = req.get_header('Content-Type') if (type_ or '').lower() != 'application/json': raise HTTPBadRequest('Invalid Content-Type %s' % repr(type_)) size = req.get_header('Content-Length') if size: return req.read(int(size)) else: return req.read() def _resource_path(filename): return resource_filename(__name__, filename) _use_jinja2 = hasattr(Chrome, 'jenv') _templates_dir = _resource_path('templates/jinja2' if _use_jinja2 else 'templates/genshi') _htdocs_dir = _resource_path('htdocs') class TicketTemplateModule(Component): implements(IAdminCommandProvider, IAdminPanelProvider, IEnvironmentSetupParticipant, IPermissionRequestor, IRequestHandler, IRequestFilter, ITemplateProvider) SECTION_NAME = 'tickettemplate' enable_custom = BoolOption(SECTION_NAME, 'enable_custom', True, """Display the My Template sidebar.""") field_list = ListOption(SECTION_NAME, 'field_list', 'summary, description, reporter, owner, priority, cc, milestone, ' 'component, version, type', doc="""List of fields that can be included in the template.""") json_template_file = Option(SECTION_NAME, 'json_template_file', '', """File containing templates.""") def __init__(self): locale_dir = _resource_path('locale') add_domain(self.env.path, locale_dir) # IPermissionRequestor methods def get_permission_actions(self): actions = ['TT_USER', ('TT_ADMIN', ['TT_USER'])] return actions # IEnvironmentSetupParticipant methods def environment_created(self): # Create the required tables connector, _ = DatabaseManager(self.env).get_connector() with self.env.db_transaction as db: for table in schema: for stmt in connector.to_sql(table): db(stmt) db("""INSERT INTO system (name,value) VALUES ('tt_version', %s) """, (schema_version,)) # Create some default templates if self.json_template_file == '': # use default templates from module self._insert_templates(DEFAULT_TEMPLATES) else: self.ticket_template_import(self.json_template_file) def environment_needs_upgrade(self, db=None): for value, in self.env.db_query(""" SELECT value FROM system WHERE name='tt_version' """): return int(value) < schema_version return True def upgrade_environment(self, db=None): for value, in self.env.db_query(""" SELECT value FROM system WHERE name='tt_version' """): current_version = int(value) break else: self.environment_created() current_version = 0 from . import upgrades for version in range(current_version + 1, schema_version + 1): for function in upgrades.map.get(version): print(textwrap.fill(inspect.getdoc(function))) function(self.env, db) print('Done.') self.env.db_transaction(""" UPDATE system SET value=%s WHERE name='tt_version' """, (schema_version,)) self.log.info("Upgraded tt tables from version %d to %d", current_version, schema_version) def _insert_templates(self, templates): """ accept list of tuples called templates and insert into database. example: templates = [('tt_name','tt_value'),] """ now = int(time.time()) for tt_name, tt_value in templates: record = [ now, SYSTEM_USER, tt_name, 'description', tt_value, ] TT_Template.insert(self.env, record) # increment timestamp; other code expects it to be unique now += 1 # IAdminCommandProvider methods def get_admin_commands(self): """Implement get_admin_commands to provide two trac-admin commands: *ticket_template export* export ticket_templates as json to stdout *ticket_template import * import ticket_templates from json file specified in trac.ini """ yield ('ticket_template export', '', """export ticket templates as json to stdout""", None, self.ticket_template_export) yield ('ticket_template import', '', """import ticket templates from json file Specify json file path via: * json_template_file argument * json_template_file option in trac.ini """, None, self.ticket_template_import) def ticket_template_export(self): """export current ticket templates as json to stdout""" template_names = TT_Template.fetchNames(self.env) export_data = [] for template_name in template_names: export_datum = ( template_name, TT_Template.fetch(self.env, template_name), ) export_data.append(export_datum) print(json.dumps(export_data, indent=2)) def ticket_template_import(self, json_template_file=''): """ Import ticket templates from json file. Specify json file path via: * json_template_file argument * json_template_file option in trac.ini """ json_template_file = json_template_file or self.json_template_file if json_template_file or self.json_template_file: # convert template_file json to python data structure then insert with open(json_template_file) as f: self._insert_templates(json.load(f)) # IAdminPanelProvider methods def get_admin_panels(self, req): if 'TT_ADMIN' in req.perm: yield ('ticket', _("Ticket System"), self.SECTION_NAME, _("Ticket Template")) def render_admin_panel(self, req, cat, page, path_info): req.perm.require('TT_ADMIN') data = { 'gettext': gettext, '_': _, 'tag_': tag_, 'N_': N_, } data['options'] = [t.name for t in TicketType.select(self.env)] + \ [_("default")] data['type'] = req.args.get('type') if 'id' in req.args: # after load history id = req.args.get('id') data['tt_text'] = self._loadTemplateTextById(id) data['type'] = self._getNameById(id) elif req.method == 'POST': # Load if req.args.get('loadtickettemplate'): tt_name = req.args.get('type') data['tt_text'] = self._loadTemplateText(tt_name) # Load history if req.args.get('loadhistory'): tt_name = req.args.get('type') data['tt_name'] = tt_name tt_history = [] for id, modi_time, tt_name, tt_text \ in TT_Template.selectByName(self.env, tt_name): history = {'id': id, 'tt_name': tt_name, 'modi_time': self._formatTime(int(modi_time)), 'tt_text': tt_text, 'href': req.abs_href.admin(cat, page, {'id': id})} tt_history.append(history) data['tt_history'] = tt_history return 'loadhistory.html', data # Save elif req.args.get('savetickettemplate'): tt_text = req.args.get('description').replace('\r', '') tt_name = req.args.get('type') self._saveTemplateText(tt_name, tt_text) data['tt_text'] = tt_text # preview elif req.args.get('preview'): tt_text = req.args.get('description').replace('\r', '') tt_name = req.args.get('type') description_preview = \ self._previewTemplateText(tt_name, tt_text, req) data['tt_text'] = tt_text data['description_preview'] = description_preview return 'admin_tickettemplate.html', data # ITemplateProvider methods def get_templates_dirs(self): return [_templates_dir] def get_htdocs_dirs(self): return [('tt', _htdocs_dir)] # IRequestHandler methods def match_request(self, req): return req.path_info.startswith('/tt') def process_request(self, req): req.perm.assert_permission('TICKET_CREATE') data = { 'gettext': gettext, '_': _, 'tag_': tag_, 'N_': N_, } if req.path_info.startswith('/tt/query'): # handle XMLHTTPRequest data['req_args'] = req.args data.update({'tt_user': req.authname}) result = TT_Template.fetchAll(self.env, data) result['status'] = '1' result['field_list'] = self._getFieldList() if self.enable_custom and 'TT_USER' in req.perm: result['enable_custom'] = True else: result['enable_custom'] = False if 'warning' in req.args: result['warning'] = req.args['warning'] json_str = json.dumps(result) self._sendResponse(req, json_str) # tt_custom save elif req.path_info.startswith('/tt/custom_save'): tt_name, custom_template = self._handleCustomSave(req) result = { 'status': '1', 'tt_name': tt_name, 'new_template': custom_template } json_str = json.dumps(result) self._sendResponse(req, json_str) # tt_custom delete elif req.path_info.startswith('/tt/custom_delete'): tt_name = self._handleCustomDelete(req) result = { 'status': '1', 'tt_name': tt_name } json_str = json.dumps(result) self._sendResponse(req, json_str) elif req.path_info.startswith('/tt/edit_buffer_save'): tt_name, custom_template = self._handleCustomSave(req) result = { 'status': '1', 'tt_name': tt_name, 'new_template': custom_template } json_str = json.dumps(result) self._sendResponse(req, json_str) # IRequestFilter methods def pre_process_request(self, req, handler): return handler def post_process_request(self, req, template, data, metadata, *args): if template == 'ticket.html' \ and req.path_info.startswith('/newticket'): add_script_data(req, {'preview': 'preview' in req.args}) add_script(req, 'tt/tt_newticket.js') add_script(req, 'tt/json2.js') if req.locale and \ resource_exists('tickettemplate', 'htdocs/%s.js' % req.locale): add_script(req, 'tt/%s.js' % req.locale) return template, data, metadata # Internal methods def _handleCustomDelete(self, req): """ delete custom template """ jsonstr = _recv_json(req) custom_data = json.loads(jsonstr) tt_name = custom_data.get('tt_name') if not tt_name: return tt_user = req.authname # delete same custom template if exist delete_data = { 'tt_user': tt_user, 'tt_name': tt_name, } TT_Template.deleteCustom(self.env, delete_data) return tt_name def _handleCustomSave(self, req): """ save custom template """ jsonstr = _recv_json(req) custom_data = json.loads(jsonstr) tt_name = custom_data.get('tt_name') custom_template = custom_data.get('custom_template') if not tt_name or not custom_template: return tt_name, custom_template now = int(time.time()) tt_user = req.authname # delete same custom template if exist delete_data = { 'tt_user': tt_user, 'tt_name': tt_name, } TT_Template.deleteCustom(self.env, delete_data) # save custom template field_list = self._getFieldList() for tt_field in field_list: tt_value = custom_template.get(tt_field) if tt_value is not None: record = [ now, tt_user, tt_name, tt_field, tt_value, ] TT_Template.insert(self.env, record) return tt_name, custom_template def _getFieldList(self): """ Get available fields return: ["summary", "description", ...] """ field_list = [field.lower() for field in self.field_list] if 'description' not in field_list: field_list.append('description') return field_list def _getTTFields(self, tt_user, tt_name): """ Get all fields values return: { "summary": {"field_type":"text", "field_value": "abc"}, "description": {"field_type":"textarea", "field_value": "xyz"}, } """ result = {} # init result field_list = self._getFieldList() for field in field_list: result[field] = '' # update from db data = { 'tt_user': tt_user, 'tt_name': tt_name, } field_value_mapping = TT_Template.fetchCurrent(self.env, data) for k, v in field_value_mapping.items(): if k in field_list: result[k] = v for field in field_list: field_type = self.config.get(self.SECTION_NAME, field + '.type', 'text') field_value = field_value_mapping.get(field) field_detail = { 'field_type': field_type, 'field_value': field_value } result[field] = field_detail return result def _loadTemplateText(self, tt_name): """ get template text from tt_dict. return tt_text if found in db or default tt_text if exists or empty string if default not exists. """ tt_text = TT_Template.fetch(self.env, tt_name) if not tt_text: tt_text = TT_Template.fetch(self.env, 'default') return tt_text def _sendResponse(self, req, message): """ send response and stop request handling """ if not isinstance(message, bytes): message = message.encode('utf-8') req.send_response(200) req.send_header('Cache-control', 'no-cache') req.send_header('Expires', 'Fri, 01 Jan 1999 00:00:00 GMT') req.send_header('Content-Type', 'text/plain' + ';charset=utf-8') req.send_header('Content-Length', len(message)) req.end_headers() if req.method != 'HEAD': req.write(message) raise RequestDone def _saveTemplateText(self, tt_name, tt_text): """ save ticket template text to db. """ TT_Template.insert(self.env, (int(time.time()), 'SYSTEM', tt_name, 'description', tt_text)) def _getTicketTypeNames(self): """ get ticket type names return: ["defect", "enhancement", ..., "default"] """ options = [] ticket = Ticket(self.env) for field in ticket.fields: if field['name'] == 'type': options.extend(field['options']) options.extend(['default']) return options 1.0/tickettemplate/upgrades.py0000644000175000000620000000730214664037202015232 0ustar wmbstaff# -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 Richard Liao # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # """Automated upgrades for the tt database tables, and other data stored in the Trac environment.""" import base64 import os from stat import ST_MTIME def add_tt_table(env, db): """Migrate from template files to db.""" from .model import TT_Template # detect existing templates files allTmpls = _findAllTmpls(env) allTmpls.sort(key=_getMTime) # import into db for tt_name in allTmpls: tt_text = _loadTemplateText(env, tt_name) modi_time = _getMTime(tt_name) TT_Template.insert(env, tt_name, tt_text, modi_time) # base64 # detect existing templates files allTmpls = _findAllTmplsBase64(env) allTmpls.sort(key=_getMTimeBase64) # import into db for tt_name in allTmpls: tt_text = _loadTemplateTextBase64(env, tt_name) modi_time = _getMTimeBase64(tt_name) tt_name = base64.decodestring(tt_name).decode('utf-8') TT_Template.insert(env, tt_name, tt_text, modi_time) def _getMTime(tt_name): tt_file = _getTTFilePath(tt_name) mtime = int(os.stat(tt_file)[ST_MTIME]) return mtime def _getMTimeBase64(tt_name): tt_file = _getTTFilePath(tt_name) mtime = int(os.stat(tt_file)[ST_MTIME]) return mtime def _findAllTmpls(env): """ find all templates in trac environment """ allTmpls = [] basePath = os.path.join(env.path, 'templates') files = os.listdir(basePath) for file in files: if file.startswith('description_') and file.endswith('.tmpl'): tt_name_base64 = file.split('description_', 1)[1] \ .rsplit('.tmpl', 1)[0] # if tt_name can't decode by base64, then it's normal name try: base64.decodestring(tt_name_base64).decode('utf-8') # skip this file continue except: allTmpls.append(tt_name_base64) return allTmpls def _findAllTmplsBase64(env): """ find all templates in trac environment """ allTmplsBase64 = [] basePath = os.path.join(env.path, 'templates') files = os.listdir(basePath) for file in files: if file.startswith('description_') and file.endswith('.tmpl'): tt_name_base64 = file.split('description_', 1)[1] \ .rsplit('.tmpl', 1)[0] try: base64.decodestring(tt_name_base64).decode('utf-8') except: continue allTmplsBase64.append(tt_name_base64) return allTmplsBase64 def _getTTFilePath(env, tt_name): """ get ticket template file path """ tt_file_name = 'description_%s.tmpl' % tt_name tt_file = os.path.join(env.path, 'templates', tt_file_name) return tt_file def _loadTemplateText(env, tt_name): """ load ticket template text from file. """ tt_file = _getTTFilePath(env, tt_name) try: fp = open(tt_file, 'r') tt_text = fp.read() fp.close() except: tt_text = "" return tt_text def _loadTemplateTextBase64(env, tt_name): """ load ticket template text from file. """ tt_file = _getTTFilePath(env, tt_name) try: fp = open(tt_file, 'r') tt_text = fp.read() fp.close() except: tt_text = "" return tt_text def add_tt_custom(env, db): """Add table tt_custom.""" from trac.db import DatabaseManager connector, _ = DatabaseManager(env)._get_connector() map = { 1: [], 2: [], 3: [], 4: [], } 1.0/changelog.txt0000644000175000000620000000173612344504131012515 0ustar wmbstaffTicketTemplate 0.7 (Feb 9, 2010) * Fully i18n support with Trac 0.12dev r9098 above. * Support '''My Template'''. Everyone can manage their own templates now. * Template can include all fields, the default field is description. TicketTemplate 0.5 (April 11, 2008) * use trac db to store all templates data * implement history manipulation feature * implement upgrade template data from old version TicketTemplate 0.4 (Mar 25, 2008) * add preview to admin page * use one persistence file to store all template texts * fixed bug: leading space in template text is not stripped now * add some default template text for trac's default install: * defect * enhancement * task * default TicketTemplate 0.3 (Jan 2, 2008) * Implement replace Trac's original 'New Ticket' handler * Add tests scripts * Add documents TicketTemplate 0.2 (Dec 29, 2007) * Implement WebAdmin support TicketTemplate 0.1 (Dec 26, 2007) * First release * Support dynamic ticket template 1.0/messages-js.cfg0000644000175000000620000000036712542062701012730 0ustar wmbstaff# mapping file for extracting messages from javascript files into # tickettemplate/locale/messages-js.pot (see setup.cfg) [javascript: **.js] [extractors] javascript_script = trac.util.dist:extract_javascript_script [javascript_script: **.html] 1.0/setup.cfg0000644000175000000620000000232114664037202011644 0ustar wmbstaff[egg_info] tag_build = dev [bdist_wheel] universal = 1 [extract_messages] add_comments = TRANSLATOR: msgid_bugs_address = richard.liao.i@gmail.com output_file = tickettemplate/locale/messages.pot keywords = _ ngettext:1,2 N_ tag_ width = 72 [init_catalog] input_file = tickettemplate/locale/messages.pot output_dir = tickettemplate/locale domain = tickettemplate [compile_catalog] directory = tickettemplate/locale domain = tickettemplate [update_catalog] input_file = tickettemplate/locale/messages.pot output_dir = tickettemplate/locale domain = tickettemplate [extract_messages_js] add_comments = TRANSLATOR: msgid_bugs_address = richard.liao.i@gmail.com output_file = tickettemplate/locale/messages-js.pot keywords = _ ngettext:1,2 N_ mapping_file = messages-js.cfg [init_catalog_js] domain = tickettemplate input_file = tickettemplate/locale/messages-js.pot output_dir = tickettemplate/locale [compile_catalog_js] domain = tickettemplate directory = tickettemplate/locale [update_catalog_js] domain = tickettemplate input_file = tickettemplate/locale/messages-js.pot output_dir = tickettemplate/locale [generate_messages_js] domain = tickettemplate input_dir = tickettemplate/locale output_dir = tickettemplate/htdocs 1.0/readme.txt0000644000175000000620000000436712344504131012026 0ustar wmbstaff= About this software = * http://trac-hacks.org/wiki/TracTicketTemplatePlugin * TicketTemplate is a Trac plugin. * TicketTemplate enable users to create ticket using templates which can be customized by Trac administrator and themselves. * Trac administrator can spcify a general system level template '''default''' for all uncustomized ticket types. * System level ticket templates are ticket type specific. * User level ticket templates (ie, my template) can be managed by common users. * This version can work with Trac 0.11/0.12. = Changes in version 0.7 = * This version has fully i18n support with Trac 0.12dev r9098 above. * New feature: support '''My Template'''. Everyone can manage their own templates now. * New feature: template can include all fields, the default field is description. = Install = '''IMPORTANT''': Please BACKUP you ticket templates if you are upgrading this plugin. You can install this software as normal Trac plugin. 1. Uninstall TracTicketTemplate if you have installed before. 2. Change to the directory containning setup.py. * (Optional): If you are using Trac 0.12 with i18n, you should compile language files here: {{{ python setup.py compile_catalog -f }}} 3. If you want to install this plugin globally, that will install this plugin to the python path: * python setup.py install 4. If you want to install this plugin to trac instance only: * python setup.py bdist_egg * copy the generated egg file to the trac instance's plugin directory {{{ cp dist/*.egg /srv/trac/env/plugins }}} 5. Config trac.ini: {{{ [components] tickettemplate.* = enabled [tickettemplate] field_list = summary, description, reporter, owner, priority, cc, milestone, component, version, type enable_custom = true }}} * Set field_list to choose which field should be included in template. * Set enable_custom to false will disable My Template, which default is true. = Usage = * Trac administrator should define the template for all ticket types: * Login as administrator, open Admin -> Ticket System -> Ticket Template * 'load' the template of each ticket type, modify them and 'apply changes' * After defined ticket template, normal user can create ticket using predefined template by change ticket types dropdown list items.1.0/COPYING0000644000175000000620000000273412376716631011076 0ustar wmbstaffCopyright (C) 2010 Polar Technologies - www.polartech.es Copyright (C) 2010 Alvaro J Iradier Copyright (C) 2012 Ryan J Ollos All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.0/setup.py0000644000175000000620000000340414664037202011540 0ustar wmbstaff#!/usr/bin/env python # -*- coding: utf-8 -*- # # Copyright (C) 2008-2013 Richard Liao # All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. # import sys from setuptools import setup extra = {} try: import babel from trac.util.dist import get_l10n_js_cmdclass except ImportError: babel = None else: extra['cmdclass'] = get_l10n_js_cmdclass() extractors = [ ('**.py', 'python', None), ('**/templates/**.html', 'genshi', None), ('**/templates/**.txt', 'genshi', {'template_class': 'genshi.template:NewTextTemplate'}), ] extra['message_extractors'] = { 'tickettemplate': extractors, } setup( name='TracTicketTemplate', version='1.0.1', packages=['tickettemplate'], package_data={ 'tickettemplate': [ '*.txt', 'templates/jinja2/*.html', 'templates/genshi/*.html', 'htdocs/*.*', 'locale/*.pot', 'locale/*/LC_MESSAGES/*.mo', ], }, author="Richard Liao", author_email='richard.liao.i@gmail.com', maintainer="Richard Liao", maintainer_email="richard.liao.i@gmail.com", description="Ticket template plugin for Trac.", license="3-Clause BSD", keywords="trac ticket template", url="https://trac-hacks.org/wiki/TracTicketTemplatePlugin", classifiers=[ 'Framework :: Trac', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', ], install_requires=['Trac'], test_suite='tickettemplate.tests', entry_points={ 'trac.plugins': ['tickettemplate = tickettemplate.ttadmin'], }, **extra )