Changeset 1247 in products
- Timestamp:
- Aug 17, 2009 3:10:02 PM (17 years ago)
- Location:
- quintagroup.transmogrifier.simpleblog2quills/branches/without_image_move/quintagroup/transmogrifier/simpleblog2quills
- Files:
-
- 5 edited
-
activator.py (modified) (4 diffs)
-
adapters.py (modified) (8 diffs)
-
configure.zcml (modified) (4 diffs)
-
export.cfg (modified) (4 diffs)
-
interfaces.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
quintagroup.transmogrifier.simpleblog2quills/branches/without_image_move/quintagroup/transmogrifier/simpleblog2quills/activator.py
r638 r1247 7 7 8 8 from Products.CMFCore import utils 9 from Products.CMFCore.WorkflowCore import WorkflowException10 9 11 10 try: … … 17 16 # plone 2.1, because zcml:condition attribute in zcml doesn't work 18 17 pass 19 20 from quintagroup.transmogrifier.simpleblog2quills.adapters import IMAGE_FOLDER21 18 22 19 class BlogActivatorSection(object): … … 46 43 47 44 path = item[pathkey] 48 if type_ is None and newtype == 'Large Plone Folder':49 parts = path.rsplit('/', 1)50 if len(parts) == 2:51 parent, id_ = parts52 else:53 yield item; continue54 if id_ != IMAGE_FOLDER:55 yield item; continue56 45 57 46 obj = self.context.unrestrictedTraverse(path, None) … … 65 54 alsoProvides(obj, IWeblogEnhanced) 66 55 event.notify(WeblogActivationEvent(obj)) 67 elif type_ is None and newtype == 'Large Plone Folder':68 # pulish 'images' subfolder69 parent = self.context.unrestrictedTraverse(parent, None)70 if IWeblogEnhanced.providedBy(parent) :71 try:72 self.wftool.doActionFor(obj, 'publish')73 except WorkflowException:74 pass75 56 76 57 yield item -
quintagroup.transmogrifier.simpleblog2quills/branches/without_image_move/quintagroup/transmogrifier/simpleblog2quills/adapters.py
r1241 r1247 1 1 import re 2 2 from xml.dom import minidom 3 from types import ListType4 from types import TupleType5 3 6 from zope.interface import implements, classProvides 7 from zope.app.annotation.interfaces import IAnnotations 4 from zope.interface import implements 8 5 9 from Products.CMFPlone.Portal import PloneSite10 6 from Products.CMFCore import utils 11 12 from collective.transmogrifier.interfaces import ISection, ISectionBlueprint13 from collective.transmogrifier.utils import defaultMatcher14 7 15 8 from quintagroup.transmogrifier.interfaces import IExportDataCorrector, IImportDataCorrector 16 9 from quintagroup.transmogrifier.adapters.exporting import ReferenceExporter 17 from quintagroup.transmogrifier.manifest import ManifestExporterSection18 from quintagroup.transmogrifier.logger import VALIDATIONKEY19 10 20 from quintagroup.transmogrifier.simpleblog2quills.interfaces import IExportItemManipulator, IBlog 21 22 # URL of the site, where blog is located (this is needed to fix links in entries) 23 SITE_URLS = [] 24 IMAGE_FOLDER = 'images' 25 IMAGE_FOLDER_TYPE = 'Large Plone Folder' 26 # this registries are needed to avoid loosing images with equal ids 27 IMAGE_IDS = [] 28 IMAGE_PATHS = {} 29 30 class BlogManifest(object): 31 implements(IExportDataCorrector) 32 33 def __init__(self, context): 34 self.context = context 35 36 def __call__(self, data): 37 # flag that indicated whether 'images' folder must added to manifest 38 need_to_add = True 39 40 doc = minidom.parseString(data['data']) 41 root = doc.documentElement 42 for child in root.getElementsByTagName('record'): 43 if child.getAttribute('type') not in ('BlogEntry', 'BlogFolder'): 44 root.removeChild(child) 45 elif str(child.firstChild.nodeValue.strip()) == IMAGE_FOLDER: 46 # blog already contains object with IMAGE_FOLDER id 47 need_to_add = False 48 49 if need_to_add: 50 folder = doc.createElement('record') 51 folder.setAttribute('type', IMAGE_FOLDER_TYPE) 52 folder.appendChild(doc.createTextNode(IMAGE_FOLDER)) 53 root.appendChild(folder) 54 55 data['data'] = doc.toxml('utf-8') 56 return data 57 58 class BlogFolderManifest(object): 59 implements(IExportDataCorrector) 60 61 def __init__(self, context): 62 self.context = context 63 64 def __call__(self, data): 65 doc = minidom.parseString(data['data']) 66 root = doc.documentElement 67 for child in root.getElementsByTagName('record'): 68 if child.getAttribute('type') not in ('BlogEntry', 'BlogFolder'): 69 root.removeChild(child) 70 data['data'] = doc.toxml('utf-8') 71 return data 11 from quintagroup.transmogrifier.simpleblog2quills.interfaces import IExportItemManipulator 72 12 73 13 class BlogEntryManifest(object): … … 83 23 return item 84 24 85 def recurseToInterface(item, ifaces):86 """Recurse up the aq_chain until an object providing `iface' is found,87 and return that.88 """89 if not isinstance(ifaces, (ListType, TupleType)):90 ifaces = [ifaces]91 parent = item.aq_parent92 for iface in ifaces:93 if iface.providedBy(item):94 return item95 for iface in ifaces:96 if iface.providedBy(parent):97 return parent98 if isinstance(parent, PloneSite):99 # Stop when we get to the portal root.100 return None101 return recurseToInterface(parent, ifaces)102 103 def getUniqueId(image_id):104 """ Generate id that is unique in IMAGE_IDS registry.105 """106 if '.' in image_id:107 name, ext = image_id.rsplit('.', 1)108 ext = '.' + ext109 else:110 name, ext = image_id, ''111 if image_id in IMAGE_IDS:112 c = 1113 new_id = name + str(c) + ext114 while new_id in IMAGE_IDS:115 c += 1116 new_id = name + str(c) + ext117 image_id = new_id118 119 return image_id120 121 25 class BlogEntryExporter(ReferenceExporter): 122 26 implements(IExportDataCorrector) … … 128 32 self.portal_url = utils.getToolByName(self.context, 'portal_url') 129 33 self.portal = self.portal_url.getPortalObject() 34 35 # try to get site domain (maybe it's stored 36 # in portal_properties/site_properties/title property) 37 self.site_domains = [] 38 portal_properties = utils.getToolByName(self.context, 'portal_properties') 39 domain = portal_properties.site_properties.getProperty('title') 40 if domain is not None: 41 if domain.startswith('www.'): 42 self.site_domains.append(domain) 43 else: 44 self.site_domains.append(domain) 45 self.site_domains.append('www.' + domain) 130 46 131 47 def __call__(self, data): … … 139 55 text = elem.firstChild.nodeValue 140 56 urls = self.SRC.findall(text) 141 blog = recurseToInterface(self.context, IBlog)142 blog_path = blog.getPhysicalPath()143 57 context_path = self.context.getPhysicalPath() 58 59 # convert all links to relative and 60 # make them work without virtual hosting 144 61 for url in urls: 145 62 url = str(url) 146 image_id = url.rsplit('/', 1)[-1]147 63 # skip links with illegal url schema 148 64 if '://' in url and not url.startswith('http://'): 149 65 continue 150 # convert all all links to relative151 66 if url.startswith('http://'): 152 for site in SITE_URLS:153 if url.startswith( site):67 for domain in self.site_domains: 68 if url.startswith(domain): 154 69 # check whether image is stored in blog 155 relative_url = url[len( site):]70 relative_url = url[len(domain):] 156 71 relative_url = relative_url.strip('/') 157 72 # if link is broken we'll get an AttributeError … … 160 75 except AttributeError: 161 76 break 162 in_blog = recurseToInterface(image, IBlog) is not None and True or False 163 if in_blog: 164 image_id = self.fixImageId(image, image_id, blog_path) 165 level = len(context_path) - len(blog_path) - 1 166 new_url = '/'.join(['..' for i in range(level)]) 167 new_url = '/'.join((new_url, IMAGE_FOLDER, image_id)) 168 text = text.replace(url, new_url, 1) 169 else: 170 # find how many levels self.context is under portal root 171 level = len(context_path) - 3 172 new_url = '/'.join(['..' for i in range(level)]) 173 new_url = new_url + '/' + relative_url 174 text = text.replace(url, new_url, 1) 77 level = len(context_path) - 3 78 new_url = '/'.join(['..' for i in range(level)]) 79 new_url = new_url + '/' + relative_url 80 text = text.replace(url, new_url, 1) 175 81 break 176 82 else: 83 # if link is broken we'll get an AttributeError 177 84 if url.startswith('/'): 178 # if link is broken we'll get an AttributeError179 85 try: 180 86 image = self.portal.unrestrictedTraverse(url.strip('/')) … … 182 88 continue 183 89 else: 184 # if link is broken we'll get an AttributeError185 90 try: 186 91 image = self.context.unrestrictedTraverse(url) 187 92 except AttributeError: 188 93 continue 189 in_blog = recurseToInterface(image, IBlog) is not None and True or False 190 if in_blog: 191 image_id = self.fixImageId(image, image_id, blog_path) 192 level = len(context_path) - len(blog_path) - 1 193 new_url = '/'.join(['..' for i in range(level)]) 194 new_url = '/'.join([new_url, IMAGE_FOLDER, image_id]) 195 text = text.replace(url, new_url, 1) 196 elif url.startswith('../'): 94 95 if url.startswith('../'): 96 # BlogEntry is folderish object and links to images that 97 # are is the same folder as BlogEntry starts with '..' 197 98 # remove '../' from the start of string 198 99 new_url = url[3:] … … 209 110 data['data'] = doc.toxml('utf-8') 210 111 return data 211 212 def fixImageId(self, image, image_id, blog_path):213 """ Check whether image is good or generate new if it's bad.214 """215 image_path = '/'.join(image.getPhysicalPath())216 if image_id in IMAGE_IDS and image_path not in IMAGE_PATHS:217 image_id = getUniqueId(image_id)218 if image_id not in IMAGE_IDS:219 IMAGE_IDS.append(image_id)220 IMAGE_PATHS[image_path] = '/'.join(blog_path[2:] + (IMAGE_FOLDER, image_id))221 222 return image_id223 224 class PathRewriter(object):225 implements(IExportItemManipulator)226 227 def __init__(self, context):228 self.context = context229 230 def __call__(self, item, **kw):231 pathkey = kw.get('path')232 if pathkey is None:233 return item234 235 path = item[pathkey]236 blog = recurseToInterface(self.context, IBlog)237 if blog is None:238 return item239 240 blog_path = blog.getPhysicalPath()241 full_path = '/'.join(self.context.getPhysicalPath())242 image_id = path.rsplit('/', 1)[-1]243 modified = False244 245 if full_path in IMAGE_PATHS:246 new_path = IMAGE_PATHS[full_path]247 else:248 unique_id = getUniqueId(image_id)249 modified = image_id != unique_id250 new_path = '/'.join(blog_path[2:] + (IMAGE_FOLDER, unique_id))251 252 IMAGE_IDS.append(image_id)253 IMAGE_PATHS[full_path] = new_path254 255 # change item's path256 item[pathkey] = new_path257 item['_oldpath'] = path258 259 # now we need to fix object id in .marshall.xml260 if modified:261 if '_files' in item and 'marshall' in item['_files']:262 doc = minidom.parseString(item['_files']['marshall']['data'])263 elem = [i for i in doc.getElementsByTagName('field') if i.getAttribute('name') == 'id'][0]264 elem.firstChild.nodeValue = '\n\t\t%s\n\t' % unique_id265 item['_files']['marshall']['data'] = doc.toxml('utf-8')266 267 return item268 269 class ImageFolderSection(object):270 """ This section will generate manifest files for image folders in blog.271 """272 classProvides(ISectionBlueprint)273 implements(ISection)274 275 def __init__(self, transmogrifier, name, options, previous):276 self.previous = previous277 self.transmogrifier = transmogrifier278 279 self.flagkey = defaultMatcher(options, 'old-path-key', name, 'oldpath')280 self.typekey = defaultMatcher(options, 'type-key', name, 'type')281 self.pathkey = defaultMatcher(options, 'path-key', name, 'path')282 283 284 site_urls = options.get('site-urls', '')285 site_urls = filter(None, [i.strip() for i in site_urls.splitlines()])286 for i in site_urls:287 SITE_URLS.append(i)288 289 self.anno = IAnnotations(transmogrifier)290 291 def __iter__(self):292 folders = {}293 294 # safely get logging storage295 if VALIDATIONKEY in self.anno:296 log_storage = self.anno[VALIDATIONKEY]297 else:298 log_storage = None299 300 for item in self.previous:301 item_keys = item.keys()302 pathkey = self.pathkey(*item_keys)[0]303 typekey = self.typekey(*item_keys)[0]304 oldpathkey = self.flagkey(*item_keys)[0]305 306 # collect data about images moved to folders307 if pathkey and typekey and oldpathkey:308 path = item[pathkey]309 old_path = item[oldpathkey]310 type_ = item[typekey]311 folder_path, image_id = path.rsplit('/', 1)312 folders.setdefault(folder_path, []).append((image_id, type_))313 314 # update logging data (path) for this item315 if log_storage and log_storage[-1] == old_path:316 log_storage.pop()317 log_storage.append(path)318 319 yield item320 321 # generate manifests for those image folders322 items = []323 for folder, entries in folders.items():324 items.append({'_entries': entries, pathkey: folder})325 exporter = ManifestExporterSection(self.transmogrifier, 'manifest', {'blueprint': 'manifest'}, iter(items))326 for item in exporter:327 yield item328 329 # clean registries330 while IMAGE_IDS: IMAGE_IDS.pop()331 while SITE_URLS: SITE_URLS.pop()332 IMAGE_PATHS.clear()333 112 334 113 class WorkflowImporter(object): … … 352 131 if workflow_id == 'simple_publication_workflow': 353 132 return data 133 354 134 wh.setAttribute('id', 'simple_publication_workflow') 355 135 if workflow_id == 'simpleblog_workflow': -
quintagroup.transmogrifier.simpleblog2quills/branches/without_image_move/quintagroup/transmogrifier/simpleblog2quills/configure.zcml
r612 r1247 9 9 <include package="quintagroup.transmogrifier" file="meta.zcml" /> 10 10 11 <!-- this section is similar to datacorrector but has more power 12 to change pipeline item --> 11 13 <utility 12 14 component=".itemmanipulator.ItemManipulatorSection" … … 16 18 17 19 <configure zcml:condition="installed Products.SimpleBlog"> 18 19 <utility20 component=".adapters.ImageFolderSection"21 name="quintagroup.transmogrifier.simpleblog2quills.imagefolder"22 provides="collective.transmogrifier.interfaces.ISectionBlueprint"23 />24 20 25 21 <!-- In Plone 2.1 overrides.zcml isn't loaded, but conflicting configuration … … 36 32 37 33 <five:implements 38 class="Products.SimpleBlog.content.Blog"39 interface=".interfaces.IBlog"40 />41 42 <five:implements43 class="Products.SimpleBlog.content.BlogFolder"44 interface=".interfaces.IBlogFolder"45 />46 47 <five:implements48 34 class="Products.SimpleBlog.content.BlogEntry" 49 35 interface=".interfaces.IBlogEntry" 50 36 /> 51 37 52 <five:implements 53 class="Products.ATContentTypes.content.file.ATFile" 54 interface="quintagroup.transmogrifier.interfaces.IATFile" 55 /> 56 57 <five:implements 58 class="Products.ATContentTypes.content.image.ATImage" 59 interface="quintagroup.transmogrifier.interfaces.IATImage" 60 /> 61 62 <!-- 'datacorrector' section adapters --> 63 <adapter 64 for=".interfaces.IBlog" 65 provides="quintagroup.transmogrifier.interfaces.IExportDataCorrector" 66 factory=".adapters.BlogManifest" 67 name="manifest" 68 /> 69 70 <adapter 71 for=".interfaces.IBlogFolder" 72 provides="quintagroup.transmogrifier.interfaces.IExportDataCorrector" 73 factory=".adapters.BlogFolderManifest" 74 name="manifest" 75 /> 76 38 <!-- 'datacorrector' section adapter --> 77 39 <adapter 78 40 for=".interfaces.IBlogEntry" … … 87 49 provides=".interfaces.IExportItemManipulator" 88 50 factory=".adapters.BlogEntryManifest" 89 />90 91 <adapter92 for="quintagroup.transmogrifier.interfaces.IATFile"93 provides=".interfaces.IExportItemManipulator"94 factory=".adapters.PathRewriter"95 />96 97 <adapter98 for="quintagroup.transmogrifier.interfaces.IATImage"99 provides=".interfaces.IExportItemManipulator"100 factory=".adapters.PathRewriter"101 51 /> 102 52 -
quintagroup.transmogrifier.simpleblog2quills/branches/without_image_move/quintagroup/transmogrifier/simpleblog2quills/export.cfg
r1218 r1247 10 10 datacorrector 11 11 itemmanipulator 12 imagefolder13 12 writer 14 13 EXPORTING … … 16 15 [sitewalker] 17 16 blueprint = quintagroup.transmogrifier.sitewalker 18 excluded-types =19 ATAudio20 TrackBack21 17 22 18 [condition] … … 43 39 blueprint = quintagroup.transmogrifier.datacorrector 44 40 sources = 45 manifest46 41 marshall 47 42 … … 49 44 blueprint = quintagroup.transmogrifier.itemmanipulator 50 45 type = export 51 52 # in this section you must write you site URL53 [imagefolder]54 blueprint = quintagroup.transmogrifier.simpleblog2quills.imagefolder55 site-urls =56 46 57 47 [writer] -
quintagroup.transmogrifier.simpleblog2quills/branches/without_image_move/quintagroup/transmogrifier/simpleblog2quills/interfaces.py
r612 r1247 1 1 from zope.interface import Interface 2 3 class IBlog(Interface):4 """ Marker interface for SimpleBlog blog object.5 """6 7 class IBlogFolder(Interface):8 """ Marker interface for SimpleBlog blog folder object.9 """10 2 11 3 class IBlogEntry(Interface):
Note: See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/trac/chrome/common/qtrac_logo.png)