Changeset 2993
- Timestamp:
- 01/07/08 08:58:56 (1 year ago)
- Files:
-
- trachacksplugin/0.11/setup.py (modified) (1 diff)
- trachacksplugin/0.11/trachacks/htdocs/css/trachacks.css (modified) (2 diffs)
- trachacksplugin/0.11/trachacks/htdocs/js/trachacks.js (modified) (1 diff)
- trachacksplugin/0.11/trachacks/htdocs/pointer.gif (added)
- trachacksplugin/0.11/trachacks/__init__.py (modified) (1 diff)
- trachacksplugin/0.11/trachacks/templates/hacks_new.html (added)
- trachacksplugin/0.11/trachacks/validate.py (added)
- trachacksplugin/0.11/trachacks/web_ui.py (moved) (moved from trachacksplugin/0.11/trachacks/trachacks.py) (6 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trachacksplugin/0.11/setup.py
r2979 r2993 13 13 ], 14 14 entry_points={ 15 'trac.plugins': 'trachacks = trachacks' 15 'trac.plugins': [ 16 'trachacks.web_ui = trachacks.web_ui', 17 ] 16 18 }, 17 19 install_requires=[ trachacksplugin/0.11/trachacks/htdocs/css/trachacks.css
r2983 r2993 3 3 4 4 .newhack { 5 width: 850px;5 width: 640px; 6 6 } 7 7 8 8 .newhack input[type="text"] { 9 width: 65%; 9 width: 90%; 10 padding: 2px; 11 margin: 0.1em 0em; 12 } 13 14 .newhack textarea { 15 width: 90%; 16 padding: 4px; 17 margin: 0.1em 0em; 10 18 } 11 19 … … 19 27 } 20 28 21 .info { 22 opacity: 0.0; 23 } 24 25 .newhack div.info { 26 width: 40ex; 29 /* TODO pointer.gif XXX XXX XXX */ 30 .newhack .hint { 27 31 font-size: xx-small; 28 float: right; 29 clear: both; 32 position: absolute; 33 right: -42ex; 34 width: 200px; 30 35 background: #b0ffb0; 31 border: 2px solid #40e040;36 border: 1px solid #40e040; 32 37 margin: 0em; 33 38 padding: 0.2em 1em; 34 39 } 40 41 .newhack div.error { 42 background: #ffb0b0; 43 border: 1px solid #e04040; 44 width: 90%; 45 text-align: center; 46 margin: 0.2em 0em; 47 padding: 2px; 48 font-weight: bold; 49 } 50 51 .newhack .hint .hint-pointer { 52 position: absolute; 53 left: -10px; 54 top: 5px; 55 width: 10px; 56 height: 19px; 57 background: url(../pointer.gif) left top no-repeat; 58 } trachacksplugin/0.11/trachacks/htdocs/js/trachacks.js
r2983 r2993 1 1 $(document).ready(function() { 2 // Move the label for each field into the infoblock.3 $('. info').each(function() {4 var info= this;5 var fieldid = info.id.slice(0, -4);2 // Move the label for each field into the hint block. 3 $('.hint').each(function() { 4 var hint = this; 5 var fieldid = hint.id.slice(0, -4); 6 6 7 $('label[@for="' + fieldid + '"]').each(function() {8 var title = $(this).text();9 10 $(info).prepend('<strong>' + title + '</strong>');11 });12 7 }); 13 8 14 // Fade all the info blocks out then focus the #name field 15 $('.info').fadeTo('fast', 0.4, function() { 9 // Handle focus/blur of input fields 10 $.fn.handleInfo = function(hint, label) { 11 return this.each(function() { 12 var hintid; 13 14 if (hint == undefined) 15 hintid = '#' + this.id + 'hint'; 16 else 17 hintid = hint; 18 19 hintid = $(hintid); 20 21 $(hintid).hide(); 22 23 $(this).focus(function() { hintid.show(); }); 24 $(this).blur(function() { hintid.hide(); }); 25 26 if (hintid.attr('copied_label') == undefined) { 27 var title = label; 28 29 hintid.attr('copied_label', true); 30 if (title == undefined) { 31 $('label[@for="' + this.id + '"]').each(function() { 32 title = $(this).text(); 33 }); 34 } 35 hintid.prepend('<strong>' + title + '</strong>' + '<span class="hint-pointer"> </span>'); 36 } 37 }); 38 } 39 40 $('#name, #title, #description, #installation').handleInfo(); 41 42 $('input[@name="type"]').handleInfo('#typehint', 'Type'); 43 $('input[@name="release"]').handleInfo('#releasehint', 'Compatibility'); 44 45 if (!$('input[@class="error"]:first').focus().size()) { 16 46 $('#name').focus(); 17 }); 18 19 20 // Handle focus/blur of input fields 21 $('input, textarea').focus(function() { 22 var id = '#' + this.id + 'info'; 23 24 $(id).fadeTo('slow', 1.0); 25 }); 26 $('input, textarea').blur(function() { 27 $('#' + this.id + 'info').fadeTo('slow', 0.4); 28 }); 47 } 29 48 }); trachacksplugin/0.11/trachacks/__init__.py
r172 r2993 1 from trachacks import *trachacksplugin/0.11/trachacks/web_ui.py
r2983 r2993 10 10 from trac.core import * 11 11 from trac.config import * 12 from trac.perm import IPermissionRequestor 13 from trac.web.chrome import Chrome 12 14 from acct_mgr.htfile import HtPasswdStore 13 15 from acct_mgr.api import IPasswordStore 14 16 from trac.wiki.model import WikiPage 15 17 from trac.util.compat import sorted 16 from trac.web.api import IRequestHandler 18 from trac.web.api import IRequestHandler, ITemplateStreamFilter 17 19 from trac.web.chrome import ITemplateProvider, INavigationContributor, \ 18 20 add_stylesheet, add_script, add_ctxtnav … … 23 25 from tracvote import VoteSystem 24 26 from genshi.builder import tag as builder 27 from genshi.filters.transform import Transformer 28 from trachacks.validate import * 25 29 26 30 … … 46 50 class TracHacksHandler(Component): 47 51 """Trac-Hacks request handler.""" 48 implements(INavigationContributor, IRequestHandler, ITemplateProvider) 52 implements(INavigationContributor, IRequestHandler, ITemplateProvider, 53 IPermissionRequestor, ITemplateStreamFilter) 49 54 50 55 limit = IntOption('trachacks', 'limit', 25, … … 53 58 path_match = re.compile(r'/hacks/?(new|cloud|list)?') 54 59 title_extract = re.compile(r'=\s+([^=]*)=', re.MULTILINE | re.UNICODE) 55 page_split = re.compile(r'(\w+)', re.I | re.UNICODE) 60 61 def __init__(self): 62 # Validate form 63 form = Form('content') 64 form.add('name', Pattern(r'[A-Z][A-Za-z0-9]+(?:[A-Z][A-Za-z0-9]+)*'), 65 'Name must be in CamelCase.') 66 form.add('title', MinLength(8), 67 'Please write a few words for the description.') 68 form.add('description', MinLength(16), 69 'Please write at least a sentence or two for the description.') 70 form.add('release', MinLength(1), 'At least one release must be checked.', 71 path='//dd[@id="release"]', where='append') 72 self.form = form 73 74 # ITemplateStreamFilter methods 75 76 def filter_stream(self, req, method, filename, stream, data): 77 errors = data.get('errors') 78 if errors and req.path_info == '/hacks/new': 79 stream |= self.form.inject_errors(errors) 80 return stream 56 81 57 82 # IRequestHandler methods 83 58 84 def match_request(self, req): 59 85 return self.path_match.match(req.path_info) … … 101 127 return self.render_cloud(req, data, hacks) 102 128 129 # INavigationContributor methods 130 def get_active_navigation_item(self, req): 131 return 'hacks' 132 133 def get_navigation_items(self, req): 134 yield ('mainnav', 'hacks', 135 builder.a('Hacks', href=req.href.hacks(), accesskey='H')) 136 137 # ITemplateProvider methods 138 def get_templates_dirs(self): 139 """ 140 Return the absolute path of the directory containing the provided 141 ClearSilver templates. 142 """ 143 from pkg_resources import resource_filename 144 return [resource_filename(__name__, 'templates')] 145 146 def get_htdocs_dirs(self): 147 """Return the absolute path of a directory containing additional 148 static resources (such as images, style sheets, etc). 149 """ 150 from pkg_resources import resource_filename 151 return [('hacks', resource_filename(__name__, 'htdocs'))] 152 153 # IPermissionRequestor methods 154 def get_permission_actions(self): 155 return ['HACK_CREATE'] 156 157 # Internal methods 103 158 def render_new(self, req, data): 159 req.perm.require('HACK_CREATE') 160 104 161 add_script(req, 'common/js/wikitoolbar.js') 162 163 data['focus'] = 'name' 164 165 # Populate data with form submission 166 if req.method == 'POST' and 'create' in req.args or 'preview' in req.args: 167 data.update(req.args) 168 169 _, errors = self.form.validate(data) 170 data['errors'] = errors 171 else: 172 data['type'] = 'plugin' 173 data['release'] = ['0.11'] 174 105 175 return 'hacks_new.html', data, None 106 176 … … 202 272 return hacks 203 273 204 # INavigationContributor methods205 def get_active_navigation_item(self, req):206 return 'hacks'207 208 def get_navigation_items(self, req):209 yield ('mainnav', 'hacks',210 builder.a('Hacks', href=req.href.hacks(), accesskey='H'))211 212 # ITemplateProvider methods213 def get_templates_dirs(self):214 """215 Return the absolute path of the directory containing the provided216 ClearSilver templates.217 """218 from pkg_resources import resource_filename219 return [resource_filename(__name__, 'templates')]220 221 def get_htdocs_dirs(self):222 """Return the absolute path of a directory containing additional223 static resources (such as images, style sheets, etc).224 """225 from pkg_resources import resource_filename226 return [('hacks', resource_filename(__name__, 'htdocs'))]227 228 274 229 275
