Changeset 467
- Timestamp:
- 09/19/08 17:13:03 (5 years ago)
- Location:
- trunk/plugins
- Files:
-
- 1 added
- 13 edited
-
projectstatusplugin/setup.py (modified) (1 diff)
-
projectstatusplugin/tracprojectstatus/templates/macros_psoutline.html (modified) (1 diff)
-
projectstatusplugin/tracprojectstatus/templates/ps_index.html (modified) (3 diffs)
-
projectstatusplugin/tracprojectstatus/web_ui.py (modified) (3 diffs)
-
projectstatusplugin/tracprojectstatus/wiki.py (modified) (4 diffs)
-
resourcetoolsplugin/setup.py (modified) (2 diffs)
-
resourcetoolsplugin/tracresourcetools/api.py (modified) (3 diffs)
-
resourcetoolsplugin/tracresourcetools/model.py (modified) (4 diffs)
-
resourcetoolsplugin/tracresourcetools/tests/api.py (modified) (2 diffs)
-
resourcetoolsplugin/tracresourcetools/tests/model.py (modified) (3 diffs)
-
wikitoolsplugin/setup.py (modified) (2 diffs)
-
wikitoolsplugin/tracwikitools/api.py (added)
-
wikitoolsplugin/tracwikitools/web_ui.py (modified) (2 diffs)
-
wikitoolsplugin/tracwikitools/wiki.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/plugins/projectstatusplugin/setup.py
r447 r467 8 8 9 9 setup(name="TracProjectStatus", 10 version="0. 2",10 version="0.3", 11 11 packages=['tracprojectstatus'], 12 12 author="Catalin Balan", -
trunk/plugins/projectstatusplugin/tracprojectstatus/templates/macros_psoutline.html
r447 r467 8 8 </div> 9 9 <div> 10 <label><b>Status:</b></label><span class="status ${ps. status.upper()}"> ${ps.status}</span>10 <label><b>Status:</b></label><span class="status ${ps.ps_ryg_status.upper()}"> ${ps.ps_ryg_status}</span> 11 11 </div> 12 <div py:if="ps. state and ps.state.upper()!='ACTIVE'">13 <label><b>Document's state:</b></label><span class="${ps. state}"> ${ps.state}</span>12 <div py:if="ps.ps_state and ps.ps_state.upper()!='ACTIVE'"> 13 <label><b>Document's state:</b></label><span class="${ps.ps_state}"> ${ps.ps_state}</span> 14 14 </div> 15 <div py:if="ps. summary">16 <label><b>Summary:</b></label><span> ${ps. summary}</span>15 <div py:if="ps.ps_summary"> 16 <label><b>Summary:</b></label><span> ${ps.ps_summary}</span> 17 17 </div> 18 18 </div> -
trunk/plugins/projectstatusplugin/tracprojectstatus/templates/ps_index.html
r447 r467 40 40 <ul> 41 41 <li>Date: <a href="${ps.reports.Active[0].href}">${ps_dateformat(ps.reports.Active[0].date)}</a></li> 42 <li>Status: <span class="status_${ps.reports.Active[0]. status.upper()}"> ${ps.reports.Active[0].status} </span></li>43 <li>Summary: ${Markup(ps.reports.Active[0]. summary)}</li>42 <li>Status: <span class="status_${ps.reports.Active[0].ps_ryg_status.upper()}"> ${ps.reports.Active[0].ps_ryg_status} </span></li> 43 <li>Summary: ${Markup(ps.reports.Active[0].ps_summary)}</li> 44 44 </ul> 45 45 </py:if> … … 59 59 <tr py:for="project_status in ps.reports.Draft"> 60 60 <td><a href="${project_status.href}">${ps_dateformat(project_status.date)}</a></td> 61 <td class="status_${project_status. status.upper()}" >${project_status.status}</td>62 <td>${Markup(project_status. summary)}</td>61 <td class="status_${project_status.ps_ryg_status.upper()}" >${project_status.ps_ryg_status}</td> 62 <td>${Markup(project_status.ps_summary)}</td> 63 63 </tr> 64 64 </tbody> … … 84 84 <tr py:for="project_status in ps.reports.Active"> 85 85 <td><a href="${project_status.href}">${ps_dateformat(project_status.date)}</a></td> 86 <td class="status_${project_status. status.upper()}">${project_status.status}</td>87 <td>${Markup(project_status. summary)}</td>86 <td class="status_${project_status.ps_ryg_status.upper()}">${project_status.ps_ryg_status}</td> 87 <td>${Markup(project_status.ps_summary)}</td> 88 88 </tr> 89 89 </tbody> -
trunk/plugins/projectstatusplugin/tracprojectstatus/web_ui.py
r447 r467 15 15 from trac.resource import get_resource_url 16 16 17 from tracresourcetools.api import MetadataSystem, IMetadataHandler18 from tracwikitools. web_ui import DefaultWikiCustomFieldHandler17 from tracresourcetools.api import ResourcePropertySystem, ResourceProperty 18 from tracwikitools.api import IWikiCustomFieldsProvider 19 19 20 20 class ProjectStatusModule(Component): 21 implements(ITemplateProvider, INavigationContributor, IRequestHandler, I MetadataHandler)21 implements(ITemplateProvider, INavigationContributor, IRequestHandler, IWikiCustomFieldsProvider) 22 22 23 23 status_wikiPath = Option('status_report', 'wiki_prefix', 'StatusReport/', 24 24 """Prefix used by this component to store status reports in trac wiki system""") 25 25 26 # IMetadataHandler 27 def get_handled_metadata(self, resource, context_data={}): 28 if resource.realm == 'wiki': 29 meta=MetadataSystem(self.env) 30 page_type=meta.get_metadata(resource, 'type', 31 default_value=context_data.get('type'), 32 lookup_handler=False) 33 if page_type =='status-report': 34 yield 'ps_ryg_status' 35 yield 'ps_summary' 36 yield 'ps_state' 37 def get_supported_operations(self, metadata, context_data={}): 38 yield 'init_metadata' 39 yield 'render_metadata' 40 def init_metadata(self, metadata, context_data={}): 41 if metadata.name=='ps_ryg_status': 42 metadata.data.update(type='status-report', element_type='select', 43 label='Overall Status', options='Not_set|Green|Yellow|Red', 44 order=1) 45 elif metadata.name=='ps_summary': 46 metadata.data.update(type='status-report', element_type='textarea', 47 label='Summary', cols=100, rows=2, 48 order=2) 49 elif metadata.name=='ps_state': 50 metadata.data.update(type='status-report', element_type='select', label='State', 51 options='Draft|Active', position="after-wikitext", 52 order=3) 53 def render_metadata(self, req, metadata, context_data={}): 54 return DefaultWikiCustomFieldHandler(self.env).render_metadata(req, metadata) 55 56 26 ryg_status = ResourceProperty('ps_ryg_status', default_value= 'Not_set', 27 element_type='select', label='Overall Status', options=['Not_set','Green','Yellow','Red'], 28 order=1) 29 summary = ResourceProperty('ps_summary', 30 element_type='textarea', label='Summary', cols=100, rows=2, 31 order=2) 32 state = ResourceProperty('ps_state', 33 element_type='select', label='State', 34 options=['Draft','Active'], 35 position='after-wikitext', 36 order=3) 37 38 # IWikiCustomFieldsProvider 39 def get_wiki_custom_fields(self, resource, resource_properties_cache): 40 if resource_properties_cache.get('wiki_type', resource).value=='status-report': 41 yield self.ryg_status 42 yield self.summary 43 yield self.state 44 57 45 # INavigationContributor methods 58 46 def get_active_navigation_item(self, req): … … 94 82 95 83 # last statuses 96 data.update(reports=self._get_project s_statuses(req))84 data.update(reports=self._get_project_statuses(req)) 97 85 98 86 # adding stylesheet & js … … 105 93 return 'ps_index.html', {'ps': data}, None 106 94 107 def _get_project s_statuses(self, req, limit=None, filters={}):95 def _get_project_statuses(self, req, limit=None, filters={}): 108 96 reports = {} 109 meta = MetadataSystem(self.env)110 for res in meta.get_resources_with_metadatas(dict(type='status-report', **filters), limit=limit):97 rp_system = ResourcePropertySystem(self.env) 98 for res in rp_system.get_resources_with_properties(wiki_type='status-report', limit = limit, **filters): 111 99 report = {} 112 report.update(status=meta.get_metadata(res, 'ps_ryg_status', default_value='Not_set', lookup_handler=False), 113 summary=meta.get_metadata(res, 'ps_summary', lookup_handler=False), 114 state=meta.get_metadata(res, 'ps_state', default_value='Draft', lookup_handler=False), 115 date=res.id.replace(self.status_wikiPath,'')[:8], 100 report.update(self._get_project_status(req, res)) 101 report.update(date=res.id.replace(self.status_wikiPath,'')[:8], 116 102 href = get_resource_url(self.env, res, req.href), 117 103 name=res.id) 118 if not reports.has_key(report['state']): 119 reports[report['state']]=[] 120 reports[report['state']].append(report) 121 104 reports.setdefault(report[self.state.name],[]).append(report) 122 105 return reports 123 106 107 def _get_project_status(self, req, resource): 108 rp_cache = ResourcePropertySystem(self.env).get_properties_cache(req) 109 for prop in rp_cache.get_resource_properties(resource, self.ryg_status, self.summary, self.state): 110 yield prop.name, prop.value -
trunk/plugins/projectstatusplugin/tracprojectstatus/wiki.py
r447 r467 12 12 from trac.web.chrome import Chrome, add_stylesheet 13 13 14 from tracresourcetools.api import MetadataSystem15 14 from tracprojectstatus.web_ui import ProjectStatusModule 16 15 … … 27 26 def expand_macro(self, formatter, name, content): 28 27 req = formatter.req 29 res = formatter.resource.realm and formatter.resource or Resource('wiki', req.path_info[6:]) 30 meta = MetadataSystem(self.env) 28 resource = formatter.resource.realm and formatter.resource or Resource('wiki', req.path_info[6:]) 31 29 ps_module = ProjectStatusModule(self.env) 32 30 33 31 data={} 34 data.update(status=meta.get_metadata(res, 'ps_ryg_status', default_value=req.args.get('ps_ryg_status'),lookup_handler=False), 35 summary=Markup(meta.get_metadata(res, 'ps_summary', default_value=req.args.get('ps_summary'),lookup_handler=False)), 36 state=meta.get_metadata(res, 'ps_state', default_value='Draft', lookup_handler=False), 37 date=res.id.replace(ps_module.status_wikiPath,'')[:8]) 32 data.update(ps_module._get_project_status(req, resource)) 33 data[ps_module.summary.name] = Markup(data.get(ps_module.summary.name)) 34 data['date']=resource.id.replace(ps_module.status_wikiPath,'')[:8] 38 35 39 36 add_stylesheet(req, 'tracprojectstatus/css/project_status.css') 40 41 37 return Chrome(self.env).render_template(req, 'macros_psoutline.html', {'ps':data}) 42 38 … … 61 57 62 58 # @todo: Please fix this very inefficient last report lookup. 63 project_statuses = ProjectStatusModule(self.env)._get_project s_statuses(formatter.req, limit=1, filters={'ps_state':'Active'}).get('Active',[])59 project_statuses = ProjectStatusModule(self.env)._get_project_statuses(formatter.req, limit=1, filters={'ps_state':'Active'}).get('Active',[]) 64 60 65 61 for project_status in project_statuses: … … 70 66 add_stylesheet(formatter.req, 'tracprojectstatus/css/project_status.css') 71 67 return html.span(html.a(label, href=project_status['href']), 72 class_="status_%s"%(project_status[' status'].upper()))68 class_="status_%s"%(project_status['ps_ryg_status'].upper())) 73 69 74 70 return html.span(html.a("No status report created.", href=formatter.req.href.status('index'))) -
trunk/plugins/resourcetoolsplugin/setup.py
r447 r467 8 8 9 9 setup(name="TracResourceToolsPlugin", 10 version="0. 2",10 version="0.3", 11 11 packages=['tracresourcetools'], 12 12 url="http://code.optaros.com/trac/oforge", … … 16 16 'tracresourcetools.api = tracresourcetools.api', 17 17 'tracresourcetools.model = tracresourcetools.model']}, 18 test_suite = 'tracresourcetools.tests '18 test_suite = 'tracresourcetools.tests.suite' 19 19 ) -
trunk/plugins/resourcetoolsplugin/tracresourcetools/api.py
r447 r467 3 3 # Copyright 2008 Optaros, Inc. 4 4 # 5 import traceback6 from StringIO import StringIO7 5 8 6 from trac.core import * 9 7 from trac.resource import * 10 from trac.util.translation import _11 from trac.web.href import Href12 8 13 from tracresourcetools.model import MetadataModel9 from tracresourcetools.model import ResourcePropertyModel, StrResourcePropertyModel, ListResourcePropertyModel 14 10 15 16 class DontSaveMetadataException(TracError): 17 """Raised by a metadata handler when a metadata should not be saved.""" 18 19 class IMetadataChangeListener(Interface): 20 def metadata_changed(resource, name, old_value, new_value): 21 """Fired when MetadataSystem#set_metadata is called, after the 11 class IResourcePropertyChangeListener(Interface): 12 def resource_property_changed(resource, name, old_value, new_value): 13 """Fired when ResourcePropertySystem#set_sesource_property is called, after the 22 14 change/creation. 23 15 … … 28 20 @return: None 29 21 """ 30 def metadata_deleted(resource, name, old_value): 31 """Fired when MetadataSystem#delete_metadata is called, after 32 the metadata is deleted. 22 23 def resource_property_deleted(resource, name, old_value): 24 """Fired when ResourcePropertySystem#delete_resource_property is called, after 25 the ResourceProperty is deleted. 33 26 34 27 @param resource: Resource … … 37 30 @return: None 38 31 """ 32 33 class ResourceProperty(object): 34 """Resource property descriptor.""" 35 36 MODEL_CLASS = StrResourcePropertyModel 37 registry={} 39 38 40 class IMetadataHandler(Interface): 41 def get_handled_metadata(resource, context_data={}): 42 """Returns a list of handled metadata names 43 for a specific resource as [str, ...] 39 def __init__(self, name, env=None, **kwargs): 40 """Resource property object initialization. 41 42 @param name: str 43 @param env: Environment 44 """ 45 self.name = name 46 self.data = kwargs 47 self.env = env 48 49 self.registry[self.name]=self 50 51 def __get__(self, instance, owner): 52 """Setting current env. 53 54 @param instance: Component 55 @param owner: class 56 """ 57 if self.env is None: 58 env = getattr(instance, 'env') 59 if not env is None: 60 self.env = env 61 return self 62 63 def get(self, resource, env = None, _cache=None, req=None, **kwargs): 64 """Return the resource property model object for specified resource. 44 65 45 66 @param resource: Resource 46 @return: Generator47 67 """ 68 data = self.data.copy() 69 data.update(kwargs) 70 71 if self.env is None: 72 self.env = env 73 74 if req and _cache is None: 75 _cache = ResourcePropertySystem(self.env).get_properties_cache(req) 76 if isinstance(_cache, ResourcePropertiesCache): 77 return _cache.get(self, resource, **data) 78 79 return self.MODEL_CLASS(self.env, resource, self.name, **data) 48 80 49 def get_supported_operations(metadata, context_data={}): 50 """Return a list with supported operations for a 51 specific metadata as [str, ...]. 81 def __getitem__(self, resource): 82 return self.get(resource) 83 84 def __set__(self, instance, value): 85 raise TracError("Not implemented.") 86 def __setitem__(self, resource, value): 87 raise TracError("Not implemented.") 88 def __contains__(self, resource): 89 raise TracError("Not implemented.") 90 91 class ListResourceProperty(ResourceProperty): 92 MODEL_CLASS = ListResourcePropertyModel 93 94 class ResourcePropertiesCache(object): 95 """Caches resource properties model objects.""" 96 def __init__(self, env=None): 97 self.env = env 98 self.property_cache={} 52 99 53 @param metadata: Metadata 54 @param context_data: dict 100 def get(self, resource_property, resource, **kwargs): 101 """Returns resource property model object from cache. 102 103 @param resource_property: ResourceProperty 104 @param resource: Resource 105 @return: ResourcePropertyModel 106 """ 107 if isinstance(resource_property, str): 108 if resource_property in ResourceProperty.registry: 109 resource_property = ResourceProperty.registry[resource_property] 110 else: 111 raise TracError("Undefined resource property %s"%(resource_property)) 112 113 if not (resource_property.name, resource) in self.property_cache: 114 self.property_cache[(resource_property.name, resource)] = resource_property.get(resource, env = self.env, **kwargs) 115 return self.property_cache[(resource_property.name, resource)] 116 117 def get_resource_properties(self, resource, *args, **kwargs): 118 """Returns all defined resource property objects for 119 a specific resource. 120 121 @param resource: Resource 122 @return: Generator 123 """ 124 _cache_failed = {} 125 for resource_property in (len(args)==0 and ResourceProperty.registry.values() or args): 126 if (resource_property.name, resource) in self.property_cache: 127 yield self.property_cache[(resource_property.name, resource)] 128 else: 129 _cache_failed[resource_property.name] = resource_property 130 131 for resource_property_model in ResourcePropertyModel.get_resource_properties(self.env, resource, _cache_failed.copy(), **kwargs): 132 self.property_cache[(resource_property_model.name, resource)]=resource_property_model 133 _cache_failed.pop(resource_property_model.name) 134 yield resource_property_model 135 136 # Defaults 137 for resource_property in _cache_failed.values(): 138 model = resource_property.get(resource, env = self.env, fetch=False) 139 self.property_cache[(resource_property.name, resource)] = model 140 yield model 141 142 class ResourcePropertySystem(Component): 143 implements(IResourceManager) 144 145 change_listeners = ExtensionPoint(IResourcePropertyChangeListener) 146 147 def get_resource_property(self, name, **kwargs): 148 """Returns resource property object. 149 150 @param name: str 151 @return: ResourceProperty 152 """ 153 if name in ResourceProperty.registry: 154 if ResourceProperty.registry[name].env is None: 155 ResourceProperty.registry[name].env = self.env 156 return ResourceProperty.registry[name] 157 else: 158 raise TracError("Property not defined.") 159 160 def get_resources_with_properties(self, **kwargs): 161 """Returns all resources with resource_properties_dict. 162 163 @param resource_properties_dict: dict 55 164 @return: generator 56 165 """ 57 58 def init_metadata(metadata, context_data={}): 59 """Called by Metadata.__init__ method. metadata._fetched=True flag 60 can be used here in order to avoid MetadataModel fetch. Useful only 61 for 'virtual' metadata, when persistence is handled different 62 than 'MetadataModel' way. 63 64 @param metadata: Metadata 65 @param context_data: dict 66 @return: None 67 """ 68 69 def get_metadata(metadata, default_value=None, context_data={}): 70 """Called by Metadata.get_metadata method. 71 72 @param metadata: Metadata 73 @param default_value: str 74 @param context_data: dict 75 @return: None 76 """ 77 78 def set_metadata(metadata, value, context_data={}): 79 """Called by Metadata.set_metadata. DontSaveMetadataException exception 80 can be raised in order to avoid MetadataModel update. Useful when 81 persistence is handled different than 'MetadataModel' way. 82 83 @param metadata: Metadata 84 @param value: str 85 @param context_data: dict 86 @raise DontSaveMetadataException: can be raised in order to bypass MetadataModel update. 87 @return: None 88 """ 89 90 def render_metadata(req, metadata, context_data={}): 91 """Returns a genshi stream for handled metadata, or None. 166 return ResourcePropertyModel.get_resources_with_properties(self.env, **kwargs) 167 168 def get_properties_cache(self, req=None): 169 """Returns request properties cache. 92 170 93 171 @param req: Request 94 @param metadata: Metadata 95 @param context_data: dict 96 @return: Stream 172 @return: ResourcePropertiesCache 97 173 """ 174 if req is None: 175 return ResourcePropertiesCache(env=self.env) 98 176 99 class Metadata(object): 100 """Object representing a Resource's metadata.""" 101 102 def __init__(self, env, resource, name, handler=None, context_data={}): 103 """Metadata object initialization. 177 if 'resource_properties_cache' not in req.callbacks: 178 req.callbacks['resource_properties_cache'] = lambda req:ResourcePropertiesCache(env=self.env) 104 179 105 @param env: Environment 106 @param resource: Resource 107 @param name: str 108 @param handler: IMetadataHandler 109 @param context_data: dict 110 """ 111 self.env = env 112 self.name = name 113 self.resource = resource 114 115 # Metadata model 116 self.model = MetadataModel(env, resource, name=name, fetch=False) 117 self._fetched = False 118 119 # Dict that can be use to store various info 120 self.data={} 121 122 # IMetadataHandler implementation component 123 self.handler = handler 124 if self.is_operation_supported('init_metadata', context_data): 125 self.handler.init_metadata(self, context_data) 126 127 def set(self, value, context_data={}): 128 """Set metadata's value. 129 130 @param value: str 131 @param context_data: dict 132 @return: None 133 """ 134 self._fetch_model() 135 136 # Notify handler 137 old_value = self.model.value 138 self.model.value = value 139 if self.is_operation_supported('set_metadata', context_data): 140 try: 141 self.handler.set_metadata(self, value, context_data) 142 except DontSaveMetadataException, e: 143 return 144 145 # Update model 146 self.model.update() 147 148 # Notify change listeners about the update 149 for listener in MetadataSystem(self.env).change_listeners: 150 listener.metadata_changed(self.resource, self.name, old_value, self.model.value) 151 152 def get(self, default_value=None, context_data={}): 153 """Returns metadata's value. 154 155 @param default_value: 156 @param context_data: dict 157 @return: None 158 """ 159 self._fetch_model() 160 161 # Notify handler 162 if self.is_operation_supported('get_metadata', context_data): 163 return self.handler.get_metadata(self, default_value, context_data) 164 165 # No handler, original model value 166 if self.model.value: 167 return self.model.value 168 return default_value 169 170 def render(self, req, context_data={}): 171 """Render metadata, if operation supported by handler. 172 173 @param req: Request 174 @param context_data: dict 175 @return: Stream 176 """ 177 if self.is_render_supported(context_data): 178 return self.handler.render_metadata(req, self, context_data) 179 180 def is_render_supported(self, context_data={}): 181 """Helper method especially for templates. Returns True 182 if render operation is supported. 183 184 @param context_data: dict 185 @return: bool 186 """ 187 return self.is_operation_supported('render_metadata', context_data) 188 189 def is_operation_supported(self, operation_name, context_data={}): 190 """Returns `True` if operation_name is supported by current handler. 191 192 @param operation_name: str 193 @param context_data: dict 194 @rtype: bool 195 """ 196 if self.handler and hasattr(self.handler, 'get_supported_operations'): 197 return operation_name in self.handler.get_supported_operations(self, context_data) 198 return False 199 200 # Internals 201 def _fetch_model(self): 202 if self._fetched is False: 203 try: 204 self.model._fetch(self.model.resource.id) 205 except ResourceNotFound, e: 206 out = StringIO() 207 traceback.print_exc(file=out) 208 self.env.log.error('%s: %s\n%s' % (self.__class__.__name__, str(e), out.getvalue())) 209 self._fetched=True 210 211 class MetadataSystem(Component): 212 implements(IResourceManager) 213 214 metadata_handlers = ExtensionPoint(IMetadataHandler) 215 change_listeners = ExtensionPoint(IMetadataChangeListener) 216 217 def get_metadatas(self, resource, filter=lambda metadata:True, context_data={}): 218 """Returns all metadatas objects 219 handled by IMetadataHandler implementations. 220 221 @param resource: Resource 222 @param filter: function 223 @param context_data: dict 224 @return: Generator 225 """ 226 for handler in self.metadata_handlers: 227 for metadata_name in handler.get_handled_metadata(resource, context_data): 228 metadata = Metadata(self.env, resource, metadata_name, handler, context_data) 229 if filter(metadata): 230 yield metadata 231 232 def get_resources_with_metadatas(self, metadatas_dict, limit=None, context_data={}): 233 """Returns all resources with metadatas_dict. 234 235 @param metadatas_dict: dict 236 @param limit: int 237 @param context_data: dict 238 @return: list 239 """ 240 return MetadataModel.get_resources_with_metadatas(self.env, metadatas_dict, limit=limit) 241 242 # Metadata helpers 243 def get_metadata(self, resource, name, default_value=None, handler=None, lookup_handler=True, context_data={}): 244 """Returns metadata value. 245 246 @param resource: Resource 247 @param name: str 248 @param default_value: str 249 @param handler: IMetadataHandler 250 @param lookup_handler: bool 251 @param context_data: dict 252 @return: str 253 """ 254 if lookup_handler and handler is None: 255 for metadata in self.get_metadatas(resource, lambda x:x.name==name, context_data): 256 return metadata.get(default_value) 257 258 return Metadata(self.env, resource, name, handler=handler).get(default_value) 259 260 def set_metadata(self, resource, name, value, handler=None, lookup_handler=True, context_data={}): 261 """Update metadata's value. 262 263 @param resource: Resource 264 @param name: str 265 @param value: str 266 @param handler: IMetadataHandler 267 @param lookup_handler: bool 268 @param context_data: dict 269 @return: None 270 """ 271 if lookup_handler and handler is None: 272 for metadata in self.get_metadatas(resource, lambda x:x.name==name, context_data): 273 return metadata.set(value) 274 return Metadata(self.env, resource, name, handler=handler).set(value) 275 276 def delete_metadata(self, resource, name): 277 """Delete a resource metadata. 278 279 @param resource: Resource 280 @param name: str 281 @return: None 282 """ 283 return MetadataModel(self.env, resource, name, fetch=False).delete() 284 180 return req.resource_properties_cache 181 285 182 # IResourceManager methods 286 183 def get_resource_realms(self): 287 yield 'metadata' 288 184 yield 'property' 289 185 def get_resource_url(self, resource, href, **kwargs): 290 186 """@todo: implement IResourceMananger.get_resource_url """ -
trunk/plugins/resourcetoolsplugin/tracresourcetools/model.py
r447 r467 12 12 from trac.db import Table, Column, Index 13 13 from trac.util.translation import _ 14 15 class MetadataModel(object): 16 17 def __init__(self, env, parent_resource, name=None, db=None, default_value=None, fetch=True): 18 """MetadataModel initialization. fetch=False flag can be used 19 in order to bypass value fetch from db. 20 21 @param env: Environment 22 @param parent_resource: Resource 23 @param name: str 24 @param db: Db 25 @param default_value: str 26 @param fetch: bool 27 """ 14 from trac.util.compat import groupby 15 16 class ResourcePropertyModel(object): 17 def __init__(self, env, resource, name=None, db=None, default_value=None, fetch=True, **kwargs): 28 18 self.env = env 29 self.resource = Resource(parent_resource).child('metadata', name) 30 self.parent_realm = self.resource.parent.realm 31 self.parent_id = self.resource.parent.id 32 19 self.resource = Resource(resource).child('property', name) 20 self.resource_realm = self.resource.parent.realm 21 self.resource_id = self.resource.parent.id 33 22 self.name = name 34 23 self.value = default_value 35 self.index = None 36 self.uniq_id = None 37 24 self.data = kwargs 38 25 if self.resource.id and fetch: 39 26 try: … … 42 29 out = StringIO() 43 30 traceback.print_exc(file=out) 44 env.log.error('%s: %s\n%s' % (self.__class__.__name__, str(e), out.getvalue())) 45 31 env.log.debug('%s: %s\n%s' % (self.__class__.__name__, str(e), out.getvalue())) 32 33 # Internals 46 34 def _set_name(self, val): 47 """Updates metadata's name.48 49 @param val: str50 @return: None51 """52 35 self.resource.id = val 53 54 36 name = property(lambda self: self.resource.id, _set_name) 55 37 56 38 def _fetch(self, name, db=None): 57 """Metadata's value fetch. If index_ fields not None, then 58 value's type will be List. 59 60 @param name: str 39 raise TracError('Not implemented') 40 41 def _load(self, **kargs): 42 raise TracError('Not implemented') 43 44 def _handle_ta(self, db=None): 45 handle_ta = db is None 46 return handle_ta, handle_ta and self.env.get_db_cnx() or db 47 48 # Public methods 49 def insert(self, name=None, value=None, db=None): 50 raise TracError('Not implemented') 51 52 def update(self, db=None): 53 handle_ta, db = self._handle_ta(db) 54 self.delete(db) 55 self.insert(self.name, self.value, db) 56 if handle_ta: 57 db.commit() 58 59 def delete(self, db=None): 60 handle_ta, db = self._handle_ta(db) 61 cursor = db.cursor() 62 cursor.execute("DELETE FROM resource_custom " 63 "WHERE resource_realm=%s AND resource_id=%s " 64 "AND name=%s", (self.resource_realm, self.resource_id, self.name)) 65 if handle_ta: 66 db.commit() 67 68 def get(self): 69 return self.value 70 71 def set(self, value, db=None): 72 #notify change listeners here 73 self.value = value 74 self.update(db) 75 76 @classmethod 77 def select(cls, env, *args, **kwargs ): 78 """Returns all properties for a specific resource. 79 80 >>> select(env, resource=Resource('wiki', 'WikiStart')) # all WikiStart Properties 81 >>> select(env, type='status-report') # all properties with name='type' and value='status-report'. 82 83 @param cls: Class 84 @param env: Environment 85 @param resource: Resource 86 @return: Generator 87 """ 88 raise TracError("Not implemented.") 89 90 @classmethod 91 def delete_all(cls, env, resource, db=None): 92 """Removes all resource_custom records for a specific resource. 93 94 @param cls: Class 95 @param env: Environment 96 @param resource: Resource 61 97 @param db: Db 62 98 @return: None 63 99 """ 64 if not db: 65 db = self.env.get_db_cnx() 66 100 handle_ta = db is None 101 db = handle_ta and env.get_db_cnx() or db 102 103 cursor = db.cursor() 104 cursor.execute("DELETE FROM resource_custom " 105 "WHERE resource_realm=%s AND resource_id=%s", (resource.realm, resource.id)) 106 if handle_ta: 107 db.commit() 108 109 @classmethod 110 def get_resources_with_properties(cls, env, limit=None, **kwargs): 111 """Returns resources with properties. 112 113 >>> list(ResourcePropertyModel.get_resources_with_properties(env, prop1_name='prop_value1', prop2_name='prop_value2')) 114 [<Resource u'wiki:Page1'>, <Resource u'wiki:Page2'>] 115 116 @param cls: Type 117 @param env: Environment 118 @return: Generator 119 """ 120 db = env.get_db_cnx() 121 cursor = db.cursor() 122 123 # dict to list attr_dict = { k1:v1, k2:v2, ... } -> [k1,v1,k2,v2..., len(attr_dict)] 124 kwargs_list = [] 125 for k, v in kwargs.items(): 126 kwargs_list.append(k.startswith('NOT_') and k[4:] or k) 127 kwargs_list.append(v) 128 kwargs_list.append(len(kwargs)) 129 130 def _get_condition(k,v): 131 return "name=%s AND value" + (k.startswith('NOT_') and 'NOT' or ' ') + " LIKE %s" 132 133 cursor.execute("SELECT resource_realm, resource_id " 134 "FROM resource_custom " 135 "WHERE " + " OR ".join([ _get_condition(k,v) for k,v in kwargs.items()]) + " " 136 "GROUP BY resource_realm, resource_id " 137 "HAVING count(*)=%s " 138 "ORDER BY resource_realm, resource_id DESC" 139 +(limit and " LIMIT %s" or ""), kwargs_list+(limit and [limit] or [])) 140 141 for reasource_realm, resource_id in cursor: 142 yield Resource(reasource_realm, resource_id) 143 144 cursor.close() 145 146 @classmethod 147 def get_resource_properties(cls, env, resource, resource_properties, **kwargs): 148 """Returns resource property model objects for resource. 149 150 @param env: Environment 151 @param resource: Resource 152 @param resource_properties: dict 153 @return: Generator 154 """ 155 db = env.get_db_cnx() or db 156 cursor = db.cursor() 157 158 cursor.execute("SELECT id, name, value, property_index " 159 "FROM resource_custom " 160 "WHERE resource_realm=%s AND resource_id=%s " 161 "ORDER BY name, property_index", 162 (resource.realm, resource.id)) 163 164 kwargs['fetch'] = False 165 for name, data in groupby(cursor, lambda row: row[1]): 166 if name in resource_properties: 167 rp_model = resource_properties[name].get(resource, env=env, **kwargs) 168 169 rp_model._load(iterator = data) 170 env.log.debug("get_resource_properties: Loaded resource property: %s, value=%s"%(rp_model.resource, rp_model.value)) 171 yield rp_model 172 173 cursor.close() 174 175 class StrResourcePropertyModel(ResourcePropertyModel): 176 def __init__(self, env, resource, name=None, db=None, default_value=None, fetch=True, uniq_id=None, **kwargs): 177 self.uniq_id = uniq_id 178 ResourcePropertyModel.__init__(self, env, resource, name=name, db=db, default_value=default_value, fetch=fetch, **kwargs) 179 180 def _fetch(self, name, db=None): 181 handle_ta, db = self._handle_ta(db) 67 182 self.name = name 68 183 cursor = db.cursor() 69 cursor.execute("SELECT id, name, value, index_ " 70 "FROM metadata " 71 "WHERE parent_realm=%s AND parent_id=%s AND name=%s " 72 "ORDER BY index_", 73 (self.parent_realm, self.parent_id, name)) 74 rows = cursor.fetchall() 184 cursor.execute("SELECT id, name, value, property_index " 185 "FROM resource_custom " 186 "WHERE resource_realm=%s AND resource_id=%s AND name=%s " 187 "ORDER BY property_index", 188 (self.resource_realm, self.resource_id, name)) 189 self._load(iterator = cursor) 190 self.env.log.debug("_fetch: Fetched resource property: %s, value=%s"%(self.resource, self.value)) 75 191 cursor.close() 76 77 if not rows: 78 raise ResourceNotFound(_("Metadata '%(name)s' does not exist.", 79 name=self.name), _('Invalid metadata')) 80 if len(rows)==1 and rows[0][3] is None: 81 row = rows[0] 82 self.uniq_id = row[0] 83 self.value = row[2] 84 self.index = row[3] and int(row[3]) or 0 85 else: 86 self.value=[] 87 for row in rows: 88 self.value.append(row[2]) 89 90 91 def insert(self, name, value, db=None): 92 """Creates new metadata table record. If value's type is List, then 93 for each item in value, a new metadata record is created, where 94 index_ is item's position in value list. 95 96 @param name: str 97 @param value: str 98 @param db: Db 99 @return: None 100 """ 101 102 handle_ta = False 103 if not db: 104 db = self.env.get_db_cnx() 105 handle_ta = True 106 107 cursor = db.cursor() 108 if isinstance(value, list): 109 cursor.executemany("INSERT INTO metadata " 110 "(parent_realm, parent_id, name, value, index_) " 111 "VALUES (%s,%s,%s,%s,%s)", 112 [(self.parent_realm, self.parent_id, name, value_item, idx) for idx, value_item in enumerate(value)]) 113 else: 114 cursor.execute("INSERT INTO metadata " 115 "(parent_realm, parent_id, name, value) " 192 193 def _load(self, **kwargs): 194 if 'iterator' in kwargs: 195 for uniq_id, name, value, property_index in kwargs['iterator']: 196 self.uniq_id, self.value, self.index = uniq_id, value, property_index 197 return 198 199 self.uniq_id = kwargs.get('uniq_id', self.uniq_id) 200 self.value = kwargs.get('value', self.value) 201 self.index = kwargs.get('property_index') and int(kwargs.get('property_index')) or 0 202 203 def insert(self, name=None, value=None, db=None): 204 handle_ta, db = self._handle_ta(db) 205 cursor = db.cursor() 206 cursor.execute("INSERT INTO resource_custom " 207 "(resource_realm, resource_id, name, value) " 116 208 "VALUES (%s,%s,%s,%s)", 117 (self.parent_realm, self.parent_id, name, value)) 118 119 self.uniq_id = db.get_last_id(cursor, 'metadata') 120 209 (self.resource_realm, self.resource_id, name, value)) 210 self.uniq_id = db.get_last_id(cursor, 'resource_custom') 121 211 self.resource.id = self.name = name 122 212 self.value = value 123 self.env.log.info('New metadata: %s by %s', self.name, self.value) 124 213 self.env.log.info('New resource property: %s by %s', self.name, self.value) 125 214 if handle_ta: 126 215 db.commit() 127 216 128 217 def update(self, db=None): 129 """Update metadata's value. Update approach is used only when uniq_id is not None. 130 Delete/Insert approch is used in rest. 131 132 @param db: Db 133 @return: None 134 """ 135 if self.uniq_id is None or isinstance(self.value, list): 218 if self.uniq_id is None: 136 219 self.delete(db) 137 220 return self.insert(self.name, self.value, db) 138 139 handle_ta = False140 if not db:141 db = self.env.get_db_cnx()142 handle_ta = True143 144 cursor = db.cursor()221 handle_ta, db = self._handle_ta(db) 222 cursor = db.cursor() 223 cursor.execute("UPDATE resource_custom SET value=%s WHERE id=%s", (self.value, self.uniq_id)) 224 if handle_ta: 225 db.commit() 226 227 def delete(self, db=None): 145 228 if self.uniq_id: 146 cursor.execute("UPDATE metadata SET value=%s WHERE id=%s", (self.value, self.uniq_id)) 229 handle_ta, db = self._handle_ta(db) 230 cursor = db.cursor() 231 cursor.execute("DELETE FROM resource_custom WHERE id=%s", (self.uniq_id,)) 232 if handle_ta: 233 db.commit() 147 234 else: 148 cursor.execute("UPDATE metadata SET value=%s " 149 "WHERE parent_realm=%s AND parent_id=%s " 150 "AND name=%s", (self.value, self.parent_realm, self.parent_id, self.name)) 151 if handle_ta: 152 db.commit() 153 154 def delete(self, db=None): 155 """Remove metadata records. 156 157 @param db: Db 158 @return: None 159 """ 160 handle_ta = False 161 if not db: 162 db = self.env.get_db_cnx() 163 handle_ta = True 164 165 cursor = db.cursor() 166 if self.uniq_id: 167 cursor.execute("DELETE FROM metadata WHERE id=%s", (self.uniq_id,)) 168 else: 169 cursor.execute("DELETE FROM metadata " 170 "WHERE parent_realm=%s AND parent_id=%s " 171 "AND name=%s", (self.parent_realm, self.parent_id, self.name)) 172 if handle_ta: 173 db.commit() 174 175 # Class methods 176 def select(cls, env, parent_resource, db=None): 177 """Returns all metadatas for a specific parent_resource. 178 235 ResourcePropertyModel.delete(self, db=db) 236 237 @classmethod 238 def select(cls, env, *args, **kwargs ): 239 """Returns all properties for a specific resource. 240 @attention: Not stable, will be changed/dropped soon. 241 242 >>> select(env, resource=Resource('wiki', 'WikiStart')) # all WikiStart Properties 243 179 244 @param cls: Class 180 245 @param env: Environment 181 @param parent_resource: Resource 182 @param db: Db 246 @param resource: Resource 183 247 @return: Generator 184 248 """ 185 handle_ta = False 186 if not db: 187 db = env.get_db_cnx() 188 handle_ta = True 189 190 cursor = db.cursor() 191 cursor.execute("SELECT name " 192 "FROM metadata " 193 "WHERE parent_realm=%s AND parent_id=%s " 194 "GROUP BY name", (parent_resource.realm, parent_resource.id)) 195 for name, in cursor.fetchall(): 196 yield MetadataModel(env, parent_resource, name) 197 198 def delete_all(cls, env, parent_resource, db=None): 199 """Removes all metadata records for a specific parent_record. 200 201 @param cls: Class 202 @param env: Environment 203 @param parent_resource: Resource 204 @param db: Db 205 @return: None 206 """ 207 handle_ta = False 208 if not db: 209 db = env.get_db_cnx() 210 handle_ta = True 211 212 cursor = db.cursor() 213 cursor.execute("DELETE FROM metadata " 214 "WHERE parent_realm=%s AND parent_id=%s", (parent_resource.realm, parent_resource.id)) 215 if handle_ta: 216 db.commit() 217 218 def get_resources_with_metadatas(cls, env, metadatas_dict=None, db=None, limit=None): 219 """Returns all resources with metadata matching metadata_dict. limit flag can be used 220 in order to limit generator's size. 221 222 @param cls: Class 223 @param env: Environment 224 @param metadatas_dict: dict 225 @param db: Db 226 @param limit: int 227 @return: Generator 228 """ 229 if metadatas_dict is None: 230 return [] 231 232 handle_ta = False 233 if not db: 234 db = env.get_db_cnx() 235 handle_ta = True 236 237 # dict to list metadatas_dict = { k1:v1, k2:v2, ... } -> [k1,v1,k2,v2..., len(attr_dict)] 238 metadatas_list=[] 239 for k, v in metadatas_dict.items(): 240 metadatas_list.append(k.startswith('NOT_') and k[4:] or k) 241 metadatas_list.append(v) 242 metadatas_list.append(len(metadatas_dict)) 243 244 def _get_condition(k,v): 245 return "name=%s AND value " + (k.startswith('NOT_') and 'NOT' or '') + " LIKE %s" 246 247 cursor = db.cursor() 248 cursor.execute("SELECT parent_realm, parent_id, parent_version " 249 "FROM metadata " 250 "WHERE " + " OR ".join([ _get_condition(k,v) for k,v in metadatas_dict.items()]) +" " 251 "GROUP BY parent_realm, parent_id, parent_version " 252 "HAVING count(*)=%s " 253 "ORDER BY parent_realm, parent_id DESC" 254 +(limit and " LIMIT %s" or ""), metadatas_list+(limit and [limit] or [])) 255 256 return [Resource(parent_realm, parent_id, version=parent_version) 257 for parent_realm, parent_id, parent_version in cursor] 258 259 select = classmethod(select) 260 delete_all = classmethod(delete_all) 261 get_resources_with_metadatas = classmethod(get_resources_with_metadatas) 262 263 class MetadataModelProvider(Component): 249 resource = Resource() 250 if 'resource' in kwargs: 251 resource = kwargs['resource'] 252 253 db = env.get_db_cnx() 254 cursor = db.cursor() 255 cursor.execute("SELECT id, name, value, property_index " 256 "FROM resource_custom " 257 "WHERE resource_realm=%s AND resource_id=%s " 258 "ORDER BY name, property_index", 259 (resource.realm, resource.id)) 260 261 for uniq_id, name, value, property_index in cursor: 262 yield StrResourcePropertyModel(env, resource, name=name, default_value=value, fetch=False, uniq_id=uniq_id) 263 264 class ListResourcePropertyModel(ResourcePropertyModel): 265 def _fetch(self, name, db=None): 266 handle_ta, db = self._handle_ta(db) 267 self.name = name 268 cursor = db.cursor() 269 cursor.execute("SELECT id, name, value, property_index " 270 "FROM resource_custom " 271 "WHERE resource_realm=%s AND resource_id=%s AND name=%s " 272 "ORDER BY property_index", 273 (self.resource_realm, self.resource_id, name)) 274 self._load(iterator=cursor) 275 self.env.log.debug("_fetch: Fetched resource property: %s, value=%s"%(self.resource, self.value)) 276 cursor.close() 277 278 def _load(self, **kwargs): 279 if 'iterator' in kwargs: 280 self.value=[] 281 for uniq_id, name, value, property_index in kwargs['iterator']: 282 self.value.append(value) 283 return 284 raise ResourceNotFound(_("Property '%(name)s' does not exist.", 285 name=self.name), _('Invalid property')) 286 287 def set(self, value, db=None): 288 if isinstance(value, unicode): 289 value=value.split(' ') 290 self.value = value 291 self.update(db) 292 293 294 def insert(self, name=None, value=None, db=None): 295 handle_ta, db = self._handle_ta(db) 296 297 cursor = db.cursor() 298 cursor.executemany("INSERT INTO resource_custom " 299 "(resource_realm, resource_id, name, value, property_index) " 300 "VALUES (%s,%s,%s,%s,%s)", 301 [(self.resource_realm, self.resource_id, name, value_item, idx) 302 for idx, value_item in enumerate(value)]) 303 if handle_ta: 304 db.commit() 305 306 class ResourcePropertyModelProvider(Component): 264 307 implements(IEnvironmentSetupParticipant) 265 308 266 309 SCHEMA = [ 267 Table(' metadata', key='id')[310 Table('resource_custom', key='id')[ 268 311 Column('id', auto_increment=True), 269 Column('parent_realm'), 270 Column('parent_id'), 271 Column('parent_version'), 312 Column('resource_realm'), 313 Column('resource_id'), 272 314 Column('name'), 273 Column('index_', type='int'),274 315 Column('value'), 275 Index(['parent_realm', 'parent_id', 'parent_version', 'name']) 316 Column('property_index', type='int'), 317 Index(['resource_realm', 'resource_id', 'name']) 276 318 ] 277 319 ] … … 284 326 cursor = db.cursor() 285 327 try: 286 cursor.execute("select count(*) from metadata")328 cursor.execute("select count(*) from resource_custom") 287 329 cursor.fetchone() 288 330 return False … … 308 350 cursor.execute(stmt) 309 351 db.commit() 310 311 352 except: 312 353 db.rollback() -
trunk/plugins/resourcetoolsplugin/tracresourcetools/tests/api.py
r447 r467 13 13 from trac.test import EnvironmentStub 14 14 15 from tracresourcetools.api import Metadata16 from tracresourcetools.model import MetadataModelProvider15 from tracresourcetools.api import ResourcePropertySystem, ResourcePropertiesCache, ResourceProperty 16 from tracresourcetools.model import ResourcePropertyModelProvider 17 17 18 class MetadataSystemTestCase(unittest.TestCase):18 class ResourcePropertySystemTestCase(unittest.TestCase): 19 19 def setUp(self): 20 20 self.basedir = os.path.realpath(tempfile.mkdtemp()) 21 21 self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 22 22 23 MetadataModelProvider(self.env)._upgrade_db(self.env.get_db_cnx())23 ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 24 24 self.env.path = os.path.join(self.basedir, 'trac-tempenv') 25 25 os.mkdir(self.env.path) … … 28 28 shutil.rmtree(self.basedir) 29 29 30 def test_set_get_metadata(self): 30 def test_resource_property_registration(self): 31 resource = Resource('wiki', 'WikiStart') 32 props_in = [('test_meta_%s'%(i), 'some_value_%s'%(i)) for i in range(10)] 33 34 class InnerComponent(Component): 35 def __init__(self): 36 for prop_name, prop_value in props_in: 37 ResourceProperty(prop_name, default_value=prop_value, env=self.env) 38 39 InnerComponent(self.env) 40 41 self.assertEqual(len(ResourceProperty.registry), len(props_in)) 42 43 def test_set_get_property(self): 31 44 resource = Resource('wiki', 'WikiStart', version=1) 32 meta_name, meta_value = 'test_meta', 'some_value'45 prop_name, prop_value = 'test_meta', 'some_value' 33 46 34 Metadata(self.env, resource, meta_name).set(meta_value) 35 self.assertEqual(Metadata(self.env, resource, meta_name).get(), meta_value) 36 47 class InnerComponent(Component): 48 prop = ResourceProperty(prop_name) 49 50 prop_model = ResourcePropertySystem(self.env).get_resource_property(prop_name)[resource] 51 prop_model.value = prop_value 52 prop_model.update() 53 54 self.assertEqual(ResourcePropertySystem(self.env).get_resource_property(prop_name)[resource].value, prop_value) 55 56 def test_set_get_property_from_component(self): 57 resource = Resource('wiki', 'WikiStart', version=1) 58 prop_name, prop_value = 'test_meta', 'some_value' 59 60 class InnerComponent(Component): 61 prop = ResourceProperty(prop_name) 62 def get(self, resource): 63 return self.prop[resource].value 64 def set(self, resource, value): 65 prop_model = self.prop[resource] 66 prop_model.value = value 67 prop_model.update() 68 69 InnerComponent(self.env).set(resource, prop_value) 70 self.assertEqual(InnerComponent(self.env).get(resource), prop_value) 71 72 class ResourcePropertyCacheTestCase(unittest.TestCase): 73 def setUp(self): 74 self.basedir = os.path.realpath(tempfile.mkdtemp()) 75 self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 76 77 ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 78 self.env.path = os.path.join(self.basedir, 'trac-tempenv') 79 os.mkdir(self.env.path) 80 81 def tearDown(self): 82 shutil.rmtree(self.basedir) 83 84 def test_set_get_property(self): 85 resource = Resource('wiki', 'WikiStart') 86 prop_name, prop_value = 'test_meta', 'some_value' 87 88 class InnerComponent(Component): 89 prop = ResourceProperty(prop_name) 90 91 cache = ResourcePropertiesCache(self.env) 92 cache.get(InnerComponent(self.env).prop, resource).value = prop_value 93 cache.get(InnerComponent(self.env).prop, resource).update() 94 95 self.assertEqual(ResourcePropertySystem(self.env).get_resource_property(prop_name)[resource].value, prop_value) 96 97 def test_set_get_property(self): 98 resource = Resource('wiki', 'WikiStart') 99 prop_name, prop_value = 'test_meta', 'some_value' 100 101 class InnerComponent(Component): 102 prop = ResourceProperty(prop_name) 103 104 cache = ResourcePropertiesCache(self.env) 105 cache.get(InnerComponent(self.env).prop, resource).value = prop_value 106 cache.get(InnerComponent(self.env).prop, resource).update() 107 108 self.assertEqual(ResourcePropertySystem(self.env).get_resource_property(prop_name)[resource].value, prop_value) 109 110 def test_get_resource_properties(self): 111 resource = Resource('wiki', 'WikiStart') 112 props_in = [('test_meta_%s'%(i), 'some_value_%s'%(i)) for i in range(10)] 113 114 class InnerComponent(Component): 115 def __init__(self): 116 for prop_name, prop_value in props_in: 117 ResourceProperty(prop_name, default_value=prop_value, env=self.env) 118 119 InnerComponent(self.env) 120 121 rp_cache = ResourcePropertiesCache(self.env) 122 self.assertEqual(rp_cache.get(ResourceProperty.registry[props_in[0][0]], resource).value, props_in[0][1]) 123 self.assertEqual(len(rp_cache.property_cache),1) 124 125 props_out = [(model.name, model.value) for model in rp_cache.get_resource_properties(resource)] 126 127 for prop_in in props_in: 128 self.assertTrue(prop_in in props_out) 129 37 130 def suite(): 38 131 suite = unittest.TestSuite() 39 suite.addTest(unittest.makeSuite(MetadataSystemTestCase, 'test')) 132 suite.addTest(unittest.makeSuite(ResourcePropertySystemTestCase, 'test')) 133 suite.addTest(unittest.makeSuite(ResourcePropertyCacheTestCase, 'test')) 40 134 return suite 41 135 -
trunk/plugins/resourcetoolsplugin/tracresourcetools/tests/model.py
r447 r467 12 12 from trac.test import EnvironmentStub 13 13 14 from tracresourcetools.api import MetadataSystem 15 from tracresourcetools.model import MetadataModelProvider, MetadataModel 16 17 class MetadataModelTestCase(unittest.TestCase): 14 from tracresourcetools.api import ResourcePropertySystem 15 from tracresourcetools.model import ResourcePropertyModelProvider, StrResourcePropertyModel, ListResourcePropertyModel, ResourcePropertyModel 16 17 18 class ResourceModelModelTestCase(unittest.TestCase): 18 19 def setUp(self): 19 20 self.basedir = os.path.realpath(tempfile.mkdtemp()) 20 21 self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 21 MetadataModelProvider(self.env)._upgrade_db(self.env.get_db_cnx())22 ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 22 23 self.env.path = os.path.join(self.basedir, 'trac-tempenv') 23 24 os.mkdir(self.env.path) … … 26 27 shutil.rmtree(self.basedir) 27 28 28 def test_insert(self): 29 parent_realm, parent_id = "wiki", "WikiStart" 30 name, value = "meta_test", "some_value" 31 MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 32 33 db = self.env.get_db_cnx() 34 cursor = db.cursor() 35 cursor.execute("SELECT id, name, value, index_ " 36 "FROM metadata " 37 "WHERE parent_realm=%s " 38 "AND parent_id=%s " 39 "AND name=%s", 40 (parent_realm, parent_id, name)) 41 row = cursor.fetchone() 42 cursor.close() 43 44 self.assertTrue(row) 45 self.assertEqual(name, row[1]) 46 self.assertEqual(value, row[2]) 47 48 def test_insert_multi(self): 49 parent_realm, parent_id = "wiki", "WikiStart" 50 name, value = "meta_test", ["some_value1", "some_value2"] 51 MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 52 53 db = self.env.get_db_cnx() 54 cursor = db.cursor() 55 cursor.execute("SELECT id, name, value, index_ " 56 "FROM metadata " 57 "WHERE parent_realm=%s " 58 "AND parent_id=%s " 59 "AND name=%s", 60 (parent_realm, parent_id, name)) 61 rows = cursor.fetchall() 62 cursor.close() 63 64 self.assertEqual(len(rows),2) 65 self.assertEqual(name, rows[0][1]) 66 value_out = [row_out[2] for row_out in rows] 67 for value_item_in in value: 68 self.assertTrue(value_item_in in value_out) 69 70 def test_fetch_multi_one_item(self): 71 parent_realm, parent_id = "wiki", "WikiStart" 72 name, value = "meta_test", ["some_value"] 73 MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 74 75 self.assertEqual(value, MetadataModel(self.env, Resource(parent_realm, parent_id), name).value) 76 77 def test_update(self): 78 parent_realm, parent_id = "wiki", "WikiStart" 79 name, value, value_new = "meta_test", "some_value", "some_new_value" 80 81 # insert new value 82 MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 83 84 # retrive inserted 85 metadata = MetadataModel(self.env, Resource(parent_realm, parent_id), name) 86 metadata.value = value_new 87 metadata.update() 88 89 db = self.env.get_db_cnx() 90 cursor = db.cursor() 91 cursor.execute("SELECT id, name, value, index_ " 92 "FROM metadata " 93 "WHERE parent_realm=%s " 94 "AND parent_id=%s " 95 "AND name=%s", 96 (parent_realm, parent_id, name)) 97 row = cursor.fetchone() 98 cursor.close() 99 100 self.assertTrue(row) 101 self.assertEqual(name, row[1]) 102 self.assertEqual(value_new, row[2]) 103 104 def test_delete(self): 105 parent_realm, parent_id = "wiki", "WikiStart" 106 metas_in = [('meta%s_name'%(i), 'meta%s_value'%(i)) for i in range(10)] 107 108 # insert new value 109 for name, value in metas_in: 110 MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 111 112 # check if inserted 113 for name, value in metas_in: 114 meta = MetadataModel(self.env, Resource(parent_realm, parent_id), name) 115 self.assertEqual(value, meta.value) 116 117 # delete one 118 meta_to_delete = metas_in.pop(3) 119 MetadataModel(self.env, Resource(parent_realm, parent_id), meta_to_delete[0]).delete() 120 121 # select all contents 122 metas_out = list(MetadataModel.select(self.env, Resource(parent_realm, parent_id))) 123 124 # check if it's deleted 125 self.assertEqual(len(metas_in), len(metas_out)) 126 for meta in metas_out: 127 self.assertEqual((meta.name, meta.value) in metas_in, True ) 128 129 def test_select(self): 130 parent_realm, parent_id = "wiki", "WikiStart" 131 metas_in = [('meta%s_name'%(i), 'meta%s_value'%(i)) for i in range(10)] 132 133 # insert new value 134 for name, value in metas_in: 135 MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 136 137 # check if inserted 138 for name, value in metas_in: 139 meta = MetadataModel(self.env, Resource(parent_realm, parent_id), name) 140 self.assertEqual(value, meta.value) 141 142 # checking select 143 metas_out = list(MetadataModel.select(self.env, Resource(parent_realm, parent_id))) 144 145 self.assertEqual(len(metas_in), len(metas_out)) 146 147 for meta in metas_out: 148 self.assertEqual((meta.name, meta.value) in metas_in, True ) 149 150 def test_delete_all(self): 151 parent_realm, parent_id = "wiki", "WikiStart" 152 metas_in = [('meta%s_name'%(i), 'meta%s_value'%(i)) for i in range(10)] 153 154 # insert new value 155 for name, value in metas_in: 156 MetadataModel(self.env, Resource(parent_realm, parent_id)).insert(name, value) 157 158 # check if inserted 159 for name, value in metas_in: 160 meta = MetadataModel(self.env, Resource(parent_realm, parent_id), name) 161 self.assertEqual(value, meta.value) 162 163 MetadataModel.delete_all(self.env, Resource(parent_realm, parent_id)) 164 165 # select all contents 166 metas_out = list(MetadataModel.select(self.env, Resource(parent_realm, parent_id))) 167 self.assertEqual(0, len(metas_out)) 168 169 def test_get_resources_with_metadatas(self): 170 metas_in_filter = {'type':'status-report', 'ryg-status':'GREEN'} 29 def test_get_resources_with_properties(self): 30 props_in_filter = {'type':'status-report', 'ryg-status':'GREEN'} 171 31 resources_in_filter = [('wiki','TestPage%s'%(i)) for i in range(10)] 172 32 173 metas_in_other = {'m7':1, 'm4':2, 'm5':3}33 props_in_other = {'m7':1, 'm4':2, 'm5':3} 174 34 resources_in_other = [('wiki','TestOtherPage%s'%(i)) for i in range(10)] 175 35 176 36 # insert filter 177 37 for realm, id in resources_in_filter: 178 for k,v in metas_in_filter.items():179 MetadataModel(self.env, Resource(realm, id)).insert(k, v)38 for k,v in props_in_filter.items(): 39 StrResourcePropertyModel(self.env, Resource(realm, id)).insert(k, v) 180 40 181 41 # insert other 182 42 for realm, id in resources_in_other: 183 for k,v in metas_in_other.items():184 MetadataModel(self.env, Resource(realm, id)).insert(k, v)185 43 for k,v in props_in_other.items(): 44 StrResourcePropertyModel(self.env, Resource(realm, id)).insert(k, v) 45 186 46 # get resources 187 resources_out_filter = MetadataModel.get_resources_with_metadatas(self.env, metas_in_filter)188 resources_out_other = MetadataModel.get_resources_with_metadatas(self.env, metas_in_other)47 resources_out_filter = list(ResourcePropertyModel.get_resources_with_properties(self.env, **props_in_filter)) 48 resources_out_other = list(ResourcePropertyModel.get_resources_with_properties(self.env, **props_in_other)) 189 49 190 50 # test … … 196 56 for resource in resources_out_other: 197 57 self.assertTrue((resource.realm, resource.id) in resources_in_other) 58 59 class StrResourceModelModelTestCase(unittest.TestCase): 60 def setUp(self): 61 self.basedir = os.path.realpath(tempfile.mkdtemp()) 62 self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 63 ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 64 self.env.path = os.path.join(self.basedir, 'trac-tempenv') 65 os.mkdir(self.env.path) 66 67 def tearDown(self): 68 shutil.rmtree(self.basedir) 69 70 def test_insert(self): 71 resource_realm, resource_id = "wiki", "WikiStart" 72 name, value_in = "test_prop", "some_value" 73 prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)) 74 prop.insert(name, value_in) 75 76 db = self.env.get_db_cnx() 77 cursor = db.cursor() 78 cursor.execute("SELECT id, value " 79 "FROM resource_custom " 80 "WHERE resource_realm=%s " 81 "AND resource_id=%s " 82 "AND name=%s", 83 (resource_realm, resource_id, name)) 84 uniq_id_out, value_out = cursor.fetchone() 85 cursor.close() 86 87 self.assertEqual(value_in, value_out) 88 self.assertEqual(prop.uniq_id, uniq_id_out) 89 90 def test_update(self): 91 resource_realm, resource_id = "wiki", "WikiStart" 92 name_in, value_in, value_in_new = "testing_is_fun", "some_value", "some_new_value" 93 94 # insert new value 95 StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name_in, value_in) 96 97 # retrive inserted 98 prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name_in) 99 prop.value = value_in_new 100 prop.update() 101 102 db = self.env.get_db_cnx() 103 cursor = db.cursor() 104 cursor.execute("SELECT id, name, value " 105 "FROM resource_custom " 106 "WHERE id=%s", 107 (prop.uniq_id,)) 108 uniq_id_out, name_out, value_out = cursor.fetchone() 109 cursor.close() 110 111 self.assertEqual(name_in, name_out) 112 self.assertEqual(value_in_new, value_out) 113 114 def test_delete(self): 115 resource_realm, resource_id = "wiki", "WikiStart" 116 props_in = [('prop%s_name'%(i), 'prop%s_value'%(i)) for i in range(10)] 117 118 # insert new value 119 for name, value in props_in: 120 StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name, value) 121 122 # check if inserted 123 for name, value in props_in: 124 prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name) 125 self.assertEqual(value, prop.value) 126 127 # delete one 128 prop_to_delete = props_in.pop(3) 129 StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), prop_to_delete[0]).delete() 130 131 # select all contents 132 props_out = list(StrResourcePropertyModel.select(self.env, resource=Resource(resource_realm, resource_id))) 133 134 # check if it's deleted 135 self.assertEqual(len(props_in), len(props_out)) 136 for prop_out in props_out: 137 self.assertEqual((prop_out.name, prop_out.value) in props_in, True ) 198 138 199 def test_get_resources_with_metadatas_limit(self): 200 metas_in_filter = {'type':'status-report', 'ryg-status':'GREEN'} 201 resources_in_filter = [('wiki','TestPage%s'%(i)) for i in range(10)] 202 203 metas_in_other = {'m7':1, 'm4':2, 'm5':3} 204 resources_in_other = [('wiki','TestOtherPage%s'%(i)) for i in range(10)] 205 206 # insert filter 207 for realm, id in resources_in_filter: 208 for k,v in metas_in_filter.items(): 209 MetadataModel(self.env, Resource(realm, id)).insert(k, v) 210 211 # insert other 212 for realm, id in resources_in_other: 213 for k,v in metas_in_other.items(): 214 MetadataModel(self.env, Resource(realm, id)).insert(k, v) 215 216 # get resources 217 resources_out_filter = MetadataModel.get_resources_with_metadatas(self.env, metas_in_filter) 218 resources_out_other = MetadataModel.get_resources_with_metadatas(self.env, metas_in_other) 219 220 resources_out_filter_limit = MetadataModel.get_resources_with_metadatas(self.env, metas_in_other, limit=2) 221 222 # test 223 self.assertEqual(len(resources_in_filter), len(resources_out_filter)) 224 for resource in resources_out_filter: 225 self.assertTrue((resource.realm, resource.id) in resources_in_filter) 226 227 self.assertEqual(len(resources_in_other), len(resources_out_other)) 228 for resource in resources_out_other: 229 self.assertTrue((resource.realm, resource.id) in resources_in_other) 230 231 self.assertEqual(len(resources_out_filter_limit), 2) 139 def test_select(self): 140 resource_realm, resource_id = "wiki", "WikiStart" 141 props_in = [('prop%s_name'%(i), 'prop%s_value'%(i)) for i in range(10)] 142 143 # insert new value 144 for name, value in props_in: 145 StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name, value) 146 147 # check if inserted 148 for name, value in props_in: 149 prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name) 150 self.assertEqual(value, prop.value) 151 152 # checking select 153 props_out = list(StrResourcePropertyModel.select(self.env, resource=Resource(resource_realm, resource_id))) 154 155 self.assertEqual(len(props_in), len(props_out)) 156 157 for prop in props_out: 158 self.assertEqual((prop.name, prop.value) in props_in, True ) 159 160 def test_delete_all(self): 161 resource_realm, resource_id = "wiki", "WikiStart" 162 props_in = [('prop%s_name'%(i), 'prop%s_value'%(i)) for i in range(10)] 163 164 # insert new value 165 for name, value in props_in: 166 StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name, value) 167 168 # check if inserted 169 for name, value in props_in: 170 prop = StrResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name) 171 self.assertEqual(value, prop.value) 172 173 StrResourcePropertyModel.delete_all(self.env, Resource(resource_realm, resource_id)) 174 175 # select all contents 176 props_out = list(StrResourcePropertyModel.select(self.env, resource=Resource(resource_realm, resource_id))) 177 self.assertEqual(0, len(props_out)) 178 179 class ListResourceModelModelTestCase(unittest.TestCase): 180 def setUp(self): 181 self.basedir = os.path.realpath(tempfile.mkdtemp()) 182 self.env = EnvironmentStub(enable=['trac.*','tracresourcetools.*']) 183 ResourcePropertyModelProvider(self.env)._upgrade_db(self.env.get_db_cnx()) 184 self.env.path = os.path.join(self.basedir, 'trac-tempenv') 185 os.mkdir(self.env.path) 186 187 def tearDown(self): 188 shutil.rmtree(self.basedir) 189 190 def test_insert(self): 191 resource_realm, resource_id = "wiki", "WikiStart" 192 name, value_in = 'prop_list', ['value%s'%(i) for i in range(10)] 193 194 prop = ListResourcePropertyModel(self.env, Resource(resource_realm, resource_id)) 195 prop.insert(name, value_in) 196 197 db = self.env.get_db_cnx() 198 cursor = db.cursor() 199 cursor.execute("SELECT value, property_index " 200 "FROM resource_custom " 201 "WHERE resource_realm=%s " 202 "AND resource_id=%s " 203 "AND name=%s " 204 "ORDER BY property_index", 205 (resource_realm, resource_id, name)) 206 value_out=[value for value, idx in cursor] 207 cursor.close() 208 209 self.assertEqual(value_in, value_out) 210 211 def test_update(self): 212 resource_realm, resource_id = "wiki", "WikiStart" 213 name_in, value_in, value_in_new = "testing_is_fun", [u'value%s'%(i) for i in range(10)], [u'new value%s'%(i) for i in range(10)] 214 215 # insert new value 216 ListResourcePropertyModel(self.env, Resource(resource_realm, resource_id)).insert(name_in, value_in) 217 218 # retrive inserted 219 prop = ListResourcePropertyModel(self.env, Resource(resource_realm, resource_id), name_in) 220 prop.value = value_in_new 221 prop.update() 222 223 db = self.env.get_db_cnx() 224 cursor = db.cursor() 225 cursor.execute("SELECT value, property_index " 226 "FROM resource_custom " 227 "WHERE resource_realm=%s " 228 "AND resource_id=%s " 229 "AND name=%s " 230 "ORDER BY property_index ASC", 231 (resource_realm, resource_id, name_in)) 232 value_out=[value for value, idx in cursor] 233 cursor.close() 234 235 self.assertEqual(value_in_new, value_out) 232 236 233 237 def suite(): 234 238 suite = unittest.TestSuite() 235 suite.addTest(unittest.makeSuite(MetadataModelTestCase, 'test')) 239 suite.addTest(unittest.makeSuite(StrResourceModelModelTestCase, 'test')) 240 suite.addTest(unittest.makeSuite(ListResourceModelModelTestCase, 'test')) 241 suite.addTest(unittest.makeSuite(ResourceModelModelTestCase, 'test')) 236 242 return suite 237 243 -
trunk/plugins/wikitoolsplugin/setup.py
r447 r467 8 8 9 9 setup(name="TracWikiToolsPlugin", 10 version="0. 2",10 version="0.3", 11 11 packages=['tracwikitools'], 12 12 url="http://code.optaros.com/trac/oforge", … … 14 14 license="BSD", 15 15 entry_points={'trac.plugins': [ 16 'tracwikitools.api = tracwikitools.api', 16 17 'tracwikitools.web_ui = tracwikitools.web_ui', 17 18 'tracwikitools.wiki = tracwikitools.wiki']} -
trunk/plugins/wikitoolsplugin/tracwikitools/web_ui.py
r447 r467 6 6 from trac.core import * 7 7 from trac.web.api import ITemplateStreamFilter 8 from trac.web.main import IRequestHandler9 8 from trac.wiki.api import IWikiPageManipulator 10 from trac.util.compat import groupby11 12 9 from genshi.filters.transform import Transformer 13 10 from genshi.builder import tag 14 from tracresourcetools.api import IMetadataHandler, MetadataSystem15 11 16 class DefaultWikiCustomFieldHandler(Component): 17 implements(IMetadataHandler) 18 19 def __init__(self): 20 self._type_fields_map = None 21 self._fields = None 22 23 # IMetadataHandler 24 def get_handled_metadata(self, resource, context_data={}): 25 page_type = MetadataSystem(self.env).get_metadata(resource, 'type', 26 default_value=context_data.get('type'), 27 lookup_handler=False) 28 for field_name in self._parse_config().get(page_type,[]): 29 yield field_name 30 def get_supported_operations(self, metadata): 31 yield 'init_metadata' 32 yield 'render_metadata' 33 def init_metadata(self, metadata): 34 metadata.data.update(self._fields[metadata.name]) 35 36 def render_metadata(self, req, metadata): 37 element_type = metadata.data.get('element_type', 'text') 38 try: 39 return dict(text = self._render_text, 40 textarea = self._render_textarea, 41 select = self._render_select 42 )[element_type](metadata) 43 except KeyError, e: 44 return self._render_text(metadata) 45 46 # internals 47 def _render_text(self, metadata): 48 return tag.div( 49 tag.label(metadata.data.get('label', metadata.name), 50 tag.br(), 51 tag.input(type="text", name=metadata.name, size=metadata.data.get('size'), value=metadata.get())), 52 class_="field") 53 54 def _render_textarea(self, metadata): 55 return tag.div( 56 tag.label(metadata.data.get('label', metadata.name), 57 tag.br(), 58 tag.textarea(metadata.get(), 59 name=metadata.name, 60 rows=metadata.data.get('rows'), 61 cols=metadata.data.get('cols'), 62 style=metadata.data.get('style'))), 63 class_="field") 64 def _render_select(self, metadata): 65 return tag.div( 66 tag.label(metadata.data.get('label', metadata.name), 67 tag.br(), 68 tag.select([tag.option(opt_item, selected=metadata.get()==opt_item and 'selected' or None) 69 for opt_item in metadata.data.get('options','').split("|")], 70 name=metadata.name, 71 rows=metadata.data.get('rows'), 72 cols=metadata.data.get('cols'), 73 style=metadata.data.get('style'))), 74 class_="field") 75 76 def _parse_config(self): 77 if self._type_fields_map is None: 78 self._type_fields_map={} 79 self._fields = {} 80 81 for option, value in list(self.config.options('wiki-custom')): 82 parts = option.split('.') 83 field = parts[0] 84 if field not in self._fields: 85 self._fields[field] = {} 86 if len(parts) == 1: 87 self._fields[field]['type']=value 88 if not self._type_fields_map.has_key(value): 89 self._type_fields_map[value]=[] 90 self._type_fields_map[value].append(field) 91 else: 92 self._fields[field]['.'.join(parts[1:])] = value 93 return self._type_fields_map 12 from tracresourcetools.api import ResourcePropertySystem, ResourceProperty 13 from tracwikitools.api import WikiCustomFieldsSystem 94 14 95 15 class WikiCustomFieldModule(Component): 96 16 implements(IWikiPageManipulator, ITemplateStreamFilter) 17 page_type = ResourceProperty('wiki_type', default_value='wiki-page') 97 18 98 19 # IWikiPageManipulator methods … … 111 32 # Internals 112 33 def _set_custom_fields(self, req, page): 113 # save fields 114 meta = MetadataSystem(self.env) 115 page_type = meta.get_metadata(page.resource, 'type', req.args.get('page_type')) 116 for metadata in meta.get_metadatas(page.resource, 117 context_data=dict(type=page_type), 118 filter=lambda x: x.data.get('type') in [page_type, 'all']): 119 metadata.set(req.args.get(metadata.name).strip()) 120 # save type 121 meta.set_metadata(page.resource, 'type', req.args.get('page_type').strip()) 34 resource = page.resource 35 db = self.env.get_db_cnx() 36 37 page_type = self.page_type.get(resource, req=req, default_value=req.args.get('page_type')) 38 page_type.set(req.args.get('page_type', 'wiki-page'), db) 39 40 for prop, renderer in WikiCustomFieldsSystem(self.env).get_wiki_custom_fields(resource, req=req): 41 prop_model = prop.get(resource, req=req) 42 prop_model.set(req.args.get(prop.name, prop_model.value).strip(), db) 43 44 db.commit() 122 45 123 46 def _view_wiki_edit(self, req, method, filename, stream, data): 124 125 # collect custom wiki fields 126 meta = MetadataSystem(self.env) 127 page_type = meta.get_metadata(data['page'].resource, 'type', req.args.get('page_type')) 47 resource = data['page'].resource 128 48 129 metadatas = meta.get_metadatas(data['page'].resource, 130 context_data=dict(type=page_type), 131 filter=lambda x: x.data.get('type') in [page_type, 'all']) 132 133 # insert custom fields 134 insert_before = tag.span() 135 insert_after = tag.span() 136 137 for position, metadatas_group in groupby(metadatas, lambda x:x.data.get('position', 'before-wikitext')): 138 tag_holder = position=='before-wikitext' and insert_before or insert_after 139 for metadata in sorted(metadatas_group, key=lambda x:x.data.get('order', 9999)): 140 if metadata.is_render_supported(): 141 tag_holder.append(metadata.render(req)) 142 else: 143 tag_holder.append(tag.input(type='text', name=metadata.name, id=metadata.name, value=metadata.get())) 49 page_type = self.page_type.get(resource, req=req, default_value=req.args.get('page_type')) 50 page_type_element = tag.input(type='hidden', name='page_type', id='page_type', value=page_type.value) 51 stream = stream | Transformer('//textarea[@name="text"]').before(page_type_element) 144 52 145 page_type_element = tag.input(type='hidden', name='page_type', id='page_type', value=page_type)146 147 return stream | Transformer('//textarea[@name="text"]').before(page_type_element).before(insert_before).after(insert_after)53 for resource_property, renderer in WikiCustomFieldsSystem(self.env).get_wiki_custom_fields(resource, req=req): 54 stream = renderer.render_wiki_custom_fields(req, method, filename, stream, data, resource_property.get(resource, req=req)) 55 return stream -
trunk/plugins/wikitoolsplugin/tracwikitools/wiki.py
r447 r467 12 12 from trac.util.translation import _ 13 13 14 from trac wikitools.web_ui import DefaultWikiCustomFieldHandler15 from tracresourcetools.api import IMetadataHandler, MetadataSystem, DontSaveMetadataException 16 from trac resourcetools.model import MetadataModel14 from tracresourcetools.api import ResourcePropertySystem, ResourceProperty, ListResourceProperty 15 16 from tracwikitools.api import IWikiCustomFieldsProvider 17 17 18 18 class ShowRelatedPagesMacro(WikiMacroBase): … … 20 20 {{{ 21 21 [[ShowRelatedPages]] 22 }}} 23 22 }}} 24 23 """ 24 implements(IWikiCustomFieldsProvider) 25 25 26 26 max_levels_lookup = Option('wikitools', 'max_levels_lookup', 500, "Maximum number of tree levels lookup.") 27 28 parent_page = ResourceProperty('parent_page', label='Parent page', position='changeinfo1') 29 related_pages = ListResourceProperty('related_pages', label='Related Pages', position='changeinfo1') 27 30 28 implements(IMetadataHandler) 29 30 # IMetadataHandler 31 def get_handled_metadata(self, resource, context_data={}): 32 if resource.realm == 'wiki': 33 yield 'related_pages' 34 yield 'parent_page' 35 # virtual field used only to update child page's parent_page with current resource's name 36 yield 'child_pages' 37 def get_supported_operations(self, metadata, context_data={}): 38 yield 'init_metadata' 39 yield 'render_metadata' 40 if metadata.name in ['related_pages','child_pages']: 41 yield 'get_metadata' 42 yield 'set_metadata' 43 def get_metadata(self, metadata, default_value, context_data={}): 44 if metadata.name == 'child_pages': 45 meta = MetadataSystem(self.env) 46 child_pages = [res.id for res in meta.get_resources_with_metadatas(dict(parent_page=metadata.resource.id))] 47 return ' '.join(child_pages) 31 # IWikiCustomFieldsProvider 32 def get_wiki_custom_fields(self, resource, resource_properties_cache): 33 yield self.parent_page 34 yield self.related_pages 48 35 49 value = metadata.model.value 50 if value: 51 return ' '.join(value) 52 return default_value 53 def set_metadata(self, metadata, value, context_data={}): 54 if metadata.name=='child_pages': 55 # update child pages with parent_page 56 if value: 57 meta = MetadataSystem(self.env) 58 # delete old values 59 for res in meta.get_resources_with_metadatas(dict(parent_page=metadata.resource.id)): 60 meta.delete_metadata(res, 'parent_page') 61 # update child list 62 for child_page in value.split(' '): 63 child_resource = Resource('wiki', child_page) 64 meta.set_metadata(child_resource, 'parent_page', metadata.resource.id, lookup_handler=False) 65 raise DontSaveMetadataException('child_pages') 66 # set related pages 67 if value: 68 metadata.model.value = value.split(' ') 69 else: 70 metadata.model.value = None 71 def init_metadata(self, metadata, context_data={}): 72 metadata.data.update(type='all', 73 element_type='text', 74 size='50', 75 position = 'after-wikitext') 76 if metadata.name=='related_pages': 77 metadata.data.update(label=_('Related pages')) 78 elif metadata.name=='parent_page': 79 metadata.data.update(label=_('Parent page')) 80 elif metadata.name=='child_pages': 81 metadata.data.update(label=_('Child pages')) 82 metadata._fetched=True # Avoid model call 83 def render_metadata(self, req, metadata, context_data={}): 84 return DefaultWikiCustomFieldHandler(self.env).render_metadata(req, metadata) 85 86 # WikiMacroBase methods 36 # WikiMacroBase methods 87 37 def expand_macro(self, formatter, name, content): 88 meta = MetadataSystem(self.env) 89 related_resources = meta.get_resources_with_metadatas(dict(related_pages='%'+formatter.resource.id+'%')) 38 rp_system = ResourcePropertySystem(self.env) 39 related_resources = list(rp_system.get_resources_with_properties(related_pages=formatter.resource.id)) 40 90 41 for resource in [Resource('wiki',related) for related in 91 meta.get_metadata(formatter.resource,'related_pages','', handler=self).split(' ')]:92 if resource not in related_resources :42 self.related_pages.get(formatter.resource, req=formatter.req).value]: 43 if resource not in related_resources and resource.id: 93 44 related_resources.append(resource) 94 45 … … 99 50 related_links.append(html.li(html.a(link, href=href))) 100 51 101 102 52 _get_href = lambda res: get_resource_url(self.env, res, formatter.context.href) 103 53 _get_link = lambda res: html.a(res.id, href=_get_href(res)) 104 54 105 106 child_pages = meta.get_metadata(formatter.resource, 'child_pages', handler=self).split(' ')107 55 ancestors = html.li(html.a(html.b(formatter.resource.id), href=_get_href(formatter.resource))) 108 56 ancestors.append(html.ul( 109 57 [ html.li( _get_link(child_page)) 110 for child_page in meta.get_resources_with_metadatas(dict(parent_page=formatter.resource.id)) ]))58 for child_page in reversed(list(rp_system.get_resources_with_properties(parent_page=formatter.resource.id))) ])) 111 59 112 60 parent = formatter.resource 61 visited_parent_ids = [parent.id] 113 62 for counter in range(self.max_levels_lookup): 114 parent_id = meta.get_metadata(parent, 'parent_page', lookup_handler=False)115 if parent_id is None :63 parent_id = self.parent_page.get(parent, req=formatter.req).value 64 if parent_id is None or parent_id in visited_parent_ids: 116 65 break 117 66 parent = Resource('wiki', parent_id) … … 119 68 120 69 return html.span( html.ul(ancestors), 121 html.li(_('Related'), related_links), class_="wiki-toc") 122 70 len(related_links.children)>0 and html.ul(html.b(_('Related')), related_links) or None, class_="wiki-toc")
Note: See TracChangeset
for help on using the changeset viewer.
