def get_folder(self): folder = None path = self.request.form.get('basePath', '/') if path == '/': folder = self.context else: path = path.lstrip('/') folder = self.context.restrictedTraverse(path, None) if folder is None: # check parents to see if we can auto create structure path = '/'.join(path.split('/')[:-1]) while path: folder = self.context.restrictedTraverse(path, None) if folder: if not IFolder.providedBy(folder): self.status = 'Not a valid path.' return None break path = '/'.join(path.split('/')[:-1]) if not folder: self.status = """Will automatically create folder(s) to place content in this location.""" folder = self.context elif not IFolder.providedBy(folder): self.status = 'Not a valid path.' return None return folder
def recursive_create_path(path): if path in ('/', ''): folder = site else: path = path.lstrip('/') folder = traverse(path) if folder is not None and not IFolder.providedBy(folder): api.content.delete(folder) folder = None if folder is None: # Need to create folders up to where we want content # we'll walk it up create folders as needed folder = site for part in path.split('/'): try: ob = folder[part] if not IFolder.providedBy(ob): api.content.delete(ob) raise KeyError() else: folder = ob except (KeyError, AttributeError): fpath = os.path.join(args.export_directory, relpath(folder), part, '__folder__') if os.path.exists(fpath): fi = open(fpath) data = mjson.loads(fi.read()) fi.close() importtype = get_import_type( data, fpath[len(args.export_directory):], None) creation_data = importtype.get_data() creation_data['container'] = folder creation_data['id'] = part try: folder = api.content.create(**creation_data) except api.exc.InvalidParameterError: logger.error( 'Error creating content {}'.format(fpath), exc_info=True) return importtype.post_creation(folder) if data['state']: try: api.content.transition(folder, to_state=data['state']) except: # maybe workflows do not match up pass folder.reindexObject() else: folder = api.content.create(type='Folder', id=part, title=part.capitalize(), container=folder) bdata = ILayoutAware(folder) bdata.contentLayout = '++contentlayout++castle/folder-query.html' return folder
def get_breaches_for_item(self, obj=None): """Get breaches for one object and its children. Breaches coming from the children of a folder are ignored by default. """ if obj is None: obj = self.context results = [] catalog = getToolByName(obj, 'portal_catalog') obj_path = '/'.join(obj.getPhysicalPath()) breaches = self.check_object(obj) if breaches: results.append(breaches) if IFolder.providedBy(obj): brains = catalog(path={'query': obj_path}) for brain in brains: try: child = brain.getObject() except (AttributeError, KeyError): continue if child == obj: continue breaches = self.check_object(obj=child, excluded_path=obj_path) if breaches: results.append(breaches) self.breaches = results return results
def _children_all(tree, debug=False): """returns a list of every child""" objects = [o for o in tree.objectValues() if IPersistent.providedBy(o)] child_list = [] to_crawl = deque(objects) i = 0 while to_crawl: current = to_crawl.popleft() child_list.append(current) #print "Looking at ", current.absolute_url() print ".", if IFolder.providedBy(current): node_children = [o for o in current.objectValues() if \ IAttributeAnnotatable.providedBy(o) and is_ok(o.getId())] to_crawl.extend(node_children) i += 1 if debug and (i > 1000): break return child_list
def main(): """ cleanup old files in a pre-defined container """ parser = argparse.ArgumentParser() parser.add_argument('--container', help='Folder path', dest='container') parser.add_argument('--recursive', help='Recurse through all subfolderish containers', dest='recursive') parser.add_argument('--c_type', help='Content type', dest='c_type') parser.add_argument('--prefix', help='Content type id prefix', dest='prefix') parser.add_argument('--threshold', help='Threshold for the number of old c_type objects', dest='threshold') args = parser.parse_args(sys.argv[3:]) if args.container == None or args.recursive == None or args.c_type == None or args.prefix == None or args.threshold == None: parser.print_help() sys.exit() site = get_zope_site() container = site.unrestrictedTraverse(args.container) do_cleanup(container, args.c_type, args.prefix, int(args.threshold)) if args.recursive.lower() in ['true', '1', 'on']: folderish = [obj for obj in container.objectValues() if IFolder.providedBy(obj)] for folder in folderish: do_cleanup(folder, args.c_type, args.prefix, int(args.threshold)) print "Operations completed."
def get_breaches_for_item(self, obj=None): """Get breaches for one object and its children. Breaches coming from the children of a folder are ignored by default. """ if obj is None: obj = self.context results = [] catalog = getToolByName(obj, 'portal_catalog') obj_path = '/'.join(obj.getPhysicalPath()) breaches = self.check_object(obj) if breaches: results.append(breaches) if IFolder.providedBy(obj): brains = catalog(path={'query': obj_path}) for brain in brains: child = brain.getObject() if child == obj: continue breaches = self.check_object(obj=child, excluded_path=obj_path) if breaches: results.append(breaches) self.breaches = results return results
def render(self): alsoProvides(self.context, IHomePage) if HAS_DXCT: from plone.app.contenttypes.interfaces import IFolder if IFolder.providedBy(self.context): self.context.setLayout('subhomepage') return self.request.response.redirect(self.context.absolute_url())
def get_object_data(self): is_dp = False if not IFolder.providedBy(self.obj): if defaultpage: container = aq_parent(self.obj) if container: is_dp = defaultpage.is_default_page(container, self.obj) else: is_dp = ptool.isDefaultPage(self.obj) has_sibling_pages = True if is_dp: container = aq_parent(self.obj) res = catalog( path={'query': '/'.join(container.getPhysicalPath())}) if len([b for b in res if b.UID != self.image_to_lead]) > 1: has_sibling_pages = False try: state = workflow.getInfoFor(ob=self.obj, name='review_state') except: state = None return { 'portal_type': self.obj.portal_type, 'layout': self.obj.getLayout(), 'uid': get_uid(self.obj), 'is_default_page': is_dp, 'state': state, 'has_sibling_pages': has_sibling_pages }
def _deploy_content(self, obj, is_page=True): """ Deploy object as page. """ try: new_req, orig_req = fakeRequest(obj) except AttributeError: # not a valid obj to override request with new_req = None content = self._render_obj(obj) if content is None: return filename = obj.absolute_url_path().lstrip("/") # deploy additional views for content type if PLONE_APP_BLOB_INSTALLED and isinstance(obj, ATBlob): self._deploy_views([os.path.join(filename, "view")], is_page=True) if is_page: filename = filename.rstrip("/") if self.add_index or IFolder.providedBy(obj): filename = os.path.join(filename, "index.html") elif not filename.endswith(".htm") and not filename.endswith(".html"): filename = filename + ".html" elif ( isinstance(obj, ATImage) or hasattr(obj, "getBlobWrapper") and "image" in obj.getBlobWrapper().getContentType() ): # create path to dump ATImage in original size if filename.rsplit(".", 1)[-1] in ("png", "jpg", "gif", "jpeg"): filename = os.path.join(filename, "image.%s" % (filename.rsplit(".", 1)[-1])) else: filename = os.path.join(filename, "image.jpg") filename, content = self._apply_image_transforms(filename, content) elif hasattr(obj, "getBlobWrapper") and "image" not in obj.getBlobWrapper().getContentType(): # create path like for ATImage if len(filename.rsplit(".", 1)) > 1: filename = os.path.join(filename, "file.%s" % (filename.rsplit(".", 1)[-1])) else: filename = os.path.join(filename, "file") self._write(filename, content) # deploy all sizes of images uploaded for the object if not getattr(obj, "schema", None): return for field in obj.Schema().fields(): if (PLONE_APP_BLOB_INSTALLED and IBlobImageField.providedBy(field)) or field.type == "image": self._deploy_blob_image_field(obj, field) elif PLONE_APP_BLOB_INSTALLED and IBlobField.providedBy(field): self._deploy_blob_file_field(obj, field) elif field.type == "file" and obj.meta_type not in self.file_types: self._deploy_file_field(obj, field) else: continue if new_req is not None: restoreRequest(orig_req, new_req)
def listPloneSites(zope): out = [] for item in zope.values(): if IFolder.providedBy(item): for site in item.values(): if IPloneSiteRoot.providedBy(site): out.append(site) return out
def render(self): context = aq_inner(self.context) out = [] for item in context.values(): if IFolder.providedBy(item): for site in item.values(): if IPloneSiteRoot.providedBy(site): out.append(item.id + '/' + site.id) return json.dumps(out)
def recursive_create_path(path): if path in ('/', ''): folder = site else: path = path.lstrip('/') folder = traverse(path) if folder is not None and not IFolder.providedBy(folder): api.content.delete(folder) folder = None if folder is None: # Need to create folders up to where we want content # we'll walk it up create folders as needed folder = site for part in path.split('/'): try: ob = folder[part] if not IFolder.providedBy(ob): logger.warn( 'Existing object in traversal path is not folderish. Removing it.' ) api.content.delete(ob) raise KeyError() else: folder = ob except (KeyError, AttributeError): fpath = os.path.join(args.export_directory, relpath(folder), part, '__folder__') imported = False if os.path.exists(fpath): imported = import_object(fpath) if not imported: logger.info( "Creating plain Folder (no __folder__ file found), %s" % part) folder = api.content.create(type='Folder', id=part, title=part.capitalize(), container=folder) bdata = ILayoutAware(folder) bdata.contentLayout = '++contentlayout++castle/folder-query.html' if not args.skip_transitioning: api.content.transition(folder, to_state='published') return folder
def listPloneSites(zope): """ List the available plonesites to be used by other function """ out = [] for item in zope.values(): if IFolder.providedBy(item) and not IPloneSiteRoot.providedBy(item): for site in item.values(): if IPloneSiteRoot.providedBy(site): out.append(site) elif IPloneSiteRoot.providedBy(item): out.append(item) return out
def _getFolder(self): """Return the containing folder of the current context, or the context itself if it is a folder""" context = self.context request = self.request if IFolder.providedBy(context): folder = context elif self.isFolderOrFolderDefaultPage(context, request): folder = aq_parent(aq_inner(context)) else: # don't know how to handle this folder = aq_parent(aq_inner(context)) return folder
def get_breaches(self, items=None): """Return breaches for multiple items. Breaches coming from objects in the list of items or their children (if a object is a folder) will be ignored. """ if items is None: items = [self.context] catalog = getToolByName(self.context, 'portal_catalog') results = [] uids_to_ignore = [] uids_visited = set() self.breach_count = {} for obj in items: obj_path = '/'.join(obj.getPhysicalPath()) brains_to_delete = catalog(path={'query': obj_path}) # add the current items uid and all its childrens uids to the # list of uids that are ignored uids_to_ignore.extend([i.UID for i in brains_to_delete]) for brain_to_delete in brains_to_delete: obj_to_delete = brain_to_delete.getObject() for breach in self.get_breaches_for_item(obj): add_breach = False for source in breach['sources']: # Only add the breach if one the sources is not in the # list of items that are to be deleted. if source['uid'] not in uids_to_ignore and \ source['uid'] not in uids_visited: add_breach = True uids_visited.add(source['uid']) break if add_breach: results.append(breach) if IFolder.providedBy(obj): count = len(catalog(path={'query': obj_path})) count_dirs = len(catalog(path={'query': obj_path}, is_folderish=True)) count_public = len(catalog(path={'query': obj_path}, review_state='published')) if count: self.breach_count[obj_path]=[count, count_dirs, count_public] # Cleanup: Some breaches where added before it was known # that their source will be deleted too. for result in results: for source in result['sources']: if source['uid'] in uids_to_ignore: # Drop sources that are also being deleted result['sources'].remove(source) if not result['sources']: # Remove the breach is there are no more sources # This check is necessary since there can be multiple # sources for a breach results.remove(result) return results
def _deploy_views(self, views, is_page=False): """ Deploy views of context as pages. """ for fullview_name in views: log.info('Deploying %s' % fullview_name) fullview_path = None fullview_name_args = fullview_name.split('|') if len(fullview_name_args) > 1: fullview_name = fullview_name_args[0] fullview_path = fullview_name_args[1] context = self.context context_path = os.path.dirname(fullview_name) view_name = os.path.basename(fullview_name) if context_path: context = self.context.restrictedTraverse(context_path, None) if not context: log.warning("Unable traverse to '%s'!" % context_path) continue # plone.resource file system resource if IResourceDirectory.providedBy(context): try: content_obj = context[view_name] except: log.warning("Unable traverse to '%s'!" % fullview_name) continue else: content_obj = context.restrictedTraverse(view_name, None) # get object's view content if ismethod(content_obj) or isfunction(content_obj): view = queryMultiAdapter((context, self.request), name=view_name) content_obj = view.context() content = self._render_obj(content_obj) if content is None: continue filename = fullview_name if is_page: filename = filename.rstrip('/') if self.add_index or IFolder.providedBy(content_obj): filename = os.path.join(filename, 'index.html') elif not filename.endswith('.htm') and not filename.endswith('.html'): filename = filename + '.html' # where to write view content (based on view path) path = urlparse(self.context.portal_url())[2] filename = '/'.join((path, filename)) # write view content on the disk self._write(filename, content, fullview_path) log.info('%s deployed' % fullview_name)
def noLongerProvidesITrashed(context): annotations = IAnnotations(context) infos = annotations.get(KEY, {'count': 0}) infos['count'] -= 1 annotations[KEY] = infos if infos['count'] <= 0: noLongerProvides(context, ITrashed) context.setExcludeFromNav(infos.get('ExcludeFromNav', False)) context.reindexObject(idxs=['trashed', 'object_provides']) if IFolder.providedBy(context): for obj in context.objectValues(): noLongerProvidesITrashed(obj)
def providesITrashed(context): annotations = IAnnotations(context) infos = annotations.get(KEY, {'count': 0}) infos['count'] += 1 infos['ExcludeFromNav'] = context.getExcludeFromNav() annotations[KEY] = infos alsoProvides(context, ITrashed) context.setExcludeFromNav(True) context.reindexObject(idxs=['trashed', 'object_provides']) if IFolder.providedBy(context): for obj in context.objectValues(): providesITrashed(obj)
def getMenuItems(self, context, request): """Return menu item entries in a TAL-friendly form.""" results = PloneFactoriesMenu.getMenuItems(self, context, request) # No menu customization if the product is not installed if not ICustomMenuFactoryLayer.providedBy(request): return results portal_url = getToolByName(context, 'portal_url') # First of all, get the real context of the menu if IFolder.providedBy(context): folder = context elif isFolderOrFolderDefaultPage(context, request): folder = aq_parent(aq_inner(context)) else: # don't know how to handle this folder = context data = {'context': context, 'portal_url': portal_url, 'container': folder} # If folder can't be annotable, do nothing # uncommon but may happen for old stuff like PloneGazette if not queryAdapter(folder, interface=IAnnotations): return results try: m_provider = ICustomFactoryMenuProvider(folder) except TypeError: # For any adaptation problem return results results = m_provider.getMenuCustomization(data, results) # Re-sort results.sort(lambda x, y: cmp(x['title'],y['title'])) mtool = getToolByName(context, 'portal_membership') if not mtool.isAnonymousUser() and mtool.getAuthenticatedMember().has_permission('Customize menu: factories', folder): context_url = folder.absolute_url() results.append({'title' : _(u'custommenu_manage_title', default=_(u'Customize menu\u2026')), 'description' : _(u'custommenu_manage_description', default=_(u'Manage custom elements of this menu')), 'action' : context_url+'/@@customize-factoriesmenu', 'selected' : False, 'icon' : None, 'submenu' : None, 'extra' : {'separator': 'actionSeparator', 'id': 'customize-factoriesmenu', 'class': 'customize-menu'}, }) return results
def get_relateditems_options(context, value, separator, vocabulary_name, vocabulary_view, field_name=None): if IForm.providedBy(context): context = context.context request = getRequest() site = get_top_site_from_url(context, request) options = get_ajaxselect_options(site, value, separator, vocabulary_name, vocabulary_view, field_name) nav_root = getNavigationRootObject(context, site) # basePath - start to search/browse in here. base_path_context = context if not IFolder.providedBy(base_path_context): base_path_context = aq_parent(base_path_context) if not base_path_context: base_path_context = nav_root options['basePath'] = '/'.join(base_path_context.getPhysicalPath()) # rootPath - Only display breadcrumb elements deeper than this path. options['rootPath'] = '/'.join(site.getPhysicalPath()) if site else '/' # rootUrl: Visible URL up to the rootPath. This is prepended to the # currentPath to generate submission URLs. options['rootUrl'] = site.absolute_url() if site else '' # contextPath - current edited object. Will not be available to select. options['contextPath'] = '/'.join(context.getPhysicalPath()) if base_path_context != nav_root: options['favorites'] = [ { # 'title': _(u'Current Content'), 'title': u'Aktueller Inhalt', 'path': '/'.join(base_path_context.getPhysicalPath()) }, { 'title': _(u'Start Page'), 'path': '/'.join(nav_root.getPhysicalPath()) } ] return options
def get_deleting_contents(self, items=None): if items is None: items = [self.context] catalog = getToolByName(self.context, 'portal_catalog') contents = {} for obj in items: obj_path = '/'.join(obj.getPhysicalPath()) if IFolder.providedBy(obj): count = len(catalog(path={'query': obj_path})) count_dirs = len(catalog(path={'query': obj_path}, portal_type='Folder')) count_public = len(catalog(path={'query': obj_path}, review_state='published')) has_breaches = True if count > 1: contents[obj_path]=[count, count_dirs, count_public] return contents
def _get_data(self): # First of all, get the real context of the menu context = self.context request = self.request if IFolder.providedBy(context): folder = context elif isFolderOrFolderDefaultPage(context, request): folder = aq_parent(aq_inner(context)) else: # don't know how to handle this folder = context portal_url = getToolByName(context, 'portal_url') data = {'context': context, 'portal_url': portal_url, 'container': folder} return data
def writeExport(obj, data): objpath = '/'.join(obj.getPhysicalPath()) if IFolder.providedBy(obj): objpath = os.path.join(objpath, '__folder__') else: fobj = aq_parent(obj) while not ISiteRoot.providedBy(fobj): if not os.path.exists(os.path.join('/'.join(fobj.getPhysicalPath()), '__folder__')): for eobj, edata in exportObj(fobj): writeExport(eobj, edata) fobj = aq_parent(fobj) path = createPath(objpath) fi = open(path, 'w') fi.write(dumps(data)) fi.close()
def write_export(obj, data): objpath = '/'.join(obj.getPhysicalPath()) if IFolder.providedBy(obj): objpath = os.path.join(objpath, '__folder__') else: fobj = aq_parent(obj) while not ISiteRoot.providedBy(fobj): if not os.path.exists( os.path.join('/'.join(fobj.getPhysicalPath()), '__folder__')): # noqa for eobj, edata in export_obj(fobj): write_export(eobj, edata) fobj = aq_parent(fobj) path = create_path(objpath) try: fi = open(path, 'w') fi.write(dumps(data)) fi.close() except UnicodeDecodeError: print('Error exporting {}'.format(objpath))
def _get_data(self): # First of all, get the real context of the menu context = self.context request = self.request if IFolder.providedBy(context): folder = context elif isFolderOrFolderDefaultPage(context, request): folder = aq_parent(aq_inner(context)) else: # don't know how to handle this folder = context portal_url = getToolByName(context, 'portal_url') data = { 'context': context, 'portal_url': portal_url, 'container': folder } return data
def upgrade_container(self, root, blacklist=[]): contents = [root] while contents: iterate = True content = contents.pop() if isinstance(content, Broken): # We don't upgrade broken objects. They should be # removed by their contains if needed. continue if content.meta_type not in blacklist: try: content = self.upgrade_content(content) except StopIteration: iterate = False if (iterate and IFolder.providedBy(content) and content.meta_type != "Parsed XML"): contents.extend(content.objectValues())
def __call__(self): """Return JSON structure to indicate if File or Image uploads are allowed in the current container. """ self.request.response.setHeader('Content-Type', 'application/json; charset=utf-8') context = self.context if self.request.form.get('path'): context = context.restrictedTraverse(self.request.form.get('path')) allow_images = False allow_files = False if IFolder.providedBy(context): allowed_types = [t.getId() for t in context.allowedContentTypes()] allow_images = u'Image' in allowed_types allow_files = u'File' in allowed_types return json.dumps({ 'allowUpload': allow_images or allow_files, 'allowImages': allow_images, 'allowFiles': allow_files })
def __call__(self, text, file_path=None): dutils = getUtility(IStaticDeploymentUtils) if file_path.lower().endswith('rss.xml') and dutils.rss_base_url: # will be handled differently, all urls should already # have domain ripped out base = dutils.rss_base_url.rstrip('/') + '/' return text.replace('rdf:resource="/', 'rdf:resource="' + base).replace( 'rdf:about="/', 'rdf:about="' + base).replace( '<link>/', '<link>' + base).replace( '/@@staticdeployment-controlpanel', '') if dutils.add_index: return text dom = getDom(text) if not dom: return text for link in dom.cssselect('a[href],base[href]'): link = LinkElement(link) url = link.val if not self.is_same_domain(url, file_path): continue if url == '/': # root of the site! continue url = url.rstrip('/') obj = self.context.restrictedTraverse(url.lstrip('/'), None) mt = None try: mt = obj.aq_base.meta_type except AttributeError: pass if obj and not isinstance(obj, (FSObject, File)) and \ not IFolder.providedBy(obj) and not url.endswith('.htm') \ and not url.endswith('.html') and not mt in dutils.file_types: link.set(url + '.html') return unicode(dom)
def __call__(self): """Return JSON structure to indicate if File or Image uploads are allowed in the current container. """ self.request.response.setHeader( 'Content-Type', 'application/json; charset=utf-8' ) context = self.context if self.request.form.get('path'): context = context.restrictedTraverse(self.request.form.get('path')) allow_images = False allow_files = False if IFolder.providedBy(context): allowed_types = [t.getId() for t in context.allowedContentTypes()] allow_images = u'Image' in allowed_types allow_files = u'File' in allowed_types return json.dumps({ 'allowUpload': allow_images or allow_files, 'allowImages': allow_images, 'allowFiles': allow_files })
def exportObj(obj): data = {} im_width = 0 image_to_lead = None for field in obj.Schema().fields(): fdata = field.getRaw(obj) if type(fdata) == ImplicitAcquisitionWrapper: fdata = fdata.aq_base data[field.__name__] = fdata if field.__name__ in ('image', 'file'): data[field.__name__ + '_filename'] = field.getFilename(obj) data[field.__name__ + '_contentType'] = field.getContentType(obj) if field.__name__ == 'image': im = field.get(obj) if im: im_width = im.width images = getReferencedImages(data) # try to find image to associate with it if there is none if im_width < 200: if len(images) > 0 and images[0].portal_type == 'Image': image = images[0] if len(image.getBackReferences(relationship='isReferencing')) < 2: im = image.getImage() data.update({ 'image': image.Schema().getField('image').getRaw(image), 'image_filename': image.getFilename(), 'image_contentType': image.getContentType(), 'imageCaption': image.Description() }) image_to_lead = image.UID() images = images[1:] if image_to_lead and 'text' in data: data['text'] = data['text'].replace(image_to_lead, obj.UID()) is_dp = False if not IFolder.providedBy(obj): if defaultpage: container = aq_parent(obj) if container: is_dp = defaultpage.is_default_page(container, obj) else: is_dp = ptool.isDefaultPage(obj) has_sibling_pages = True if is_dp: container = aq_parent(obj) res = catalog(path={'query': '/'.join(container.getPhysicalPath())}) if len([b for b in res if b.UID != image_to_lead]) > 1: has_sibling_pages = False try: state = workflow.getInfoFor(ob=obj, name='review_state') except: state = None alldata = { 'portal_type': obj.portal_type, 'layout': obj.getLayout(), 'data': data, 'uid': obj.UID(), 'is_default_page': is_dp, 'state': state, 'has_sibling_pages': has_sibling_pages } yield obj, alldata for ref in images: if ref.UID() == obj.UID(): continue if ref.portal_type != 'Image': continue if image_to_lead == ref.UID(): continue for o, d in exportObj(ref): if o.UID() == ref.UID() or o.UID() == obj.UID(): continue yield o, d
def isFolder(self): return IFolder.providedBy(self.context)
def process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False, validating=True): """A custom implementation for the widget form processing.""" if validating: # At the validation phase, only return the existing UID field # value or the filename of the new FileUpload object value = form.get(field.getName()) if value: return value, {} value = empty_marker files_list = form.get('%s_file' % field.getName(), []) if files_list: obj = files_list[0] value = getattr(obj, 'filename', getattr(obj, 'name', '')) return value, {} option = form.get('%s_option' % field.getName(), empty_marker) value = form.get(field.getName(), empty_marker) # In this case, just do the trivial if option == 'select': if value is empty_marker: return empty_marker if emptyReturnsMarker and value == '': return empty_marker return value, {} # In this case, we need to create new object(s) and get its UID(s) if option == 'upload': result = [] files_list = form.get('%s_file' % field.getName(), []) portal = getToolByName(instance, 'portal_url').getPortalObject() mt_tool = getToolByName(portal, 'mimetypes_registry') # Define the destination folder root = instance foldername = self.startup_directory if foldername.startswith('/'): root = portal foldername = foldername[1:] try: folder = root.restrictedTraverse(foldername) except (KeyError, AttributeError): # If the startup_directory doesn't exists, fallback to # the current instance if folderish, or its parent if IFolder.providedBy(instance): folder = instance else: folder = aq_parent(instance) for fileobj in files_list: # Normalize the filename filename = getattr(fileobj, 'filename', '') filename = filename.split('\\')[-1] if filename: # Guess the mimetype & define the content-type class content = 'File' mimetype = str(mt_tool.classify(fileobj.read(1024))) if mimetype.startswith('image'): content = 'Image' # Create the new content old_id = folder.generateUniqueId(content) new_id = folder.invokeFactory(content, id=old_id, title=filename) obj = getattr(folder, new_id) obj._renameAfterCreation() obj.unmarkCreationFlag() obj.update_data(fileobj, mimetype) result.append(obj.UID()) if field.multiValued: # Multi valued, append the old value result.extend(value) elif result: # Non multi valued, with a valid upload, return it result = result[0] else: # Non multi valued, with an invalid upload, return the old value result = value return result, {} return empty_marker
def import_object(self, filepath, container=None): fi = open(filepath) file_read = fi.read() fi.close() try: data = mjson.loads(file_read) except Exception: print("Skipping {}; Unable to read JSON data".format(filepath)) return if filepath.endswith('__folder__'): filepath = '/'.join(filepath.split('/')[:-1]) skipped = False if data['portal_type'] in skip_types: print('Skipping omitted type {type}'.format( type=data['portal_type'])) skipped = True if only_types and data['portal_type'] not in only_types: print("Skipping {type} at {path}, not in only_types.".format( type=data['portal_type'], path=filepath)) skipped = True if import_paths: do_import = False for import_path in import_paths: if filepath.startswith('{}/{}'.format(args.export_directory, import_path)): do_import = True if import_path.startswith( filepath[len(args.export_directory):].lstrip('/') + '/'): # Don't skip folders on the way to import_paths do_import = True if not do_import: print("Skipping {path}, not in import_paths".format( path=filepath)) skipped = True if skip_paths: for skip_path in skip_paths: if filepath.lower().startswith('{}/{}'.format( args.export_directory, skip_path)): print( "Skipping {path}, in skip_paths".format(path=filepath)) skipped = True if skipped: if os.path.isdir(filepath) and len(os.listdir(filepath)): logger.warn('{path} contains additional content that will be ' 'skipped.'.format(path=filepath)) return original_path = filepath[len(args.export_directory):] if retain_paths: importtype = get_import_type(data, original_path, 'retain_paths') else: importtype = get_import_type(data, original_path) path = importtype.get_path() if container is None: logger.warn( 'Skipped {} because of creation error'.format(filepath)) return _id = path.split('/')[-1] create = True if _id in container.objectIds(): if args.overwrite: existing = container[_id] if IFolder.providedBy(existing): if len(existing.objectIds()): print("OVERWRITE: Deleting non-empty container {path}". format(path=path)) else: print("OVERWRITE: Deleting content item at {path}".format( path=path)) api.content.delete(container[_id]) else: create = False creation_data = importtype.get_data() pc_data = importtype.get_post_creation_data() creation_data['container'] = container aspect = ISelectableConstrainTypes(container, None) if aspect: if (aspect.getConstrainTypesMode() != 1 or [creation_data['type']] != aspect.getImmediatelyAddableTypes()): aspect.setConstrainTypesMode(1) aspect.setImmediatelyAddableTypes([creation_data['type']]) if create: if ignore_uuids and '_plone.uuid' in creation_data: del creation_data['_plone.uuid'] obj = None if not args.overwrite and (_id in container.objectIds()): print('Skipping {path}, already exists. Use --overwrite to' ' create anyway.'.format(path=path)) return elif (not ignore_uuids and api.content.get(UID=creation_data['_plone.uuid']) is not None): logger.warn( 'Skipping {path}, content with its UUID already exists.' 'Use --ignore-uuids to create anyway.'.format(path=path)) return else: try: obj = api.content.create(safe_id=True, **creation_data) print('Created {path}'.format(path=path)) self.imported_count += 1 if self.imported_count % 50 == 0: print('%i processed, committing' % self.imported_count) transaction.commit() except api.exc.InvalidParameterError: if stop_if_exception: logger.error( 'Error creating content {}'.format(filepath), exc_info=True) if pdb_if_exception: pdb.set_trace() raise logger.error('Error creating content {}'.format(filepath), exc_info=True) return # TODO check default folder pages came over as folder with rich text tile # TODO any folder pages without default page should have content listing tile else: obj = container[_id] for key, value in creation_data.items(): if key not in ('id', 'type'): setattr(obj, key, value) if obj is not None: if path != original_path: storage = getUtility(IRedirectionStorage) rpath = os.path.join('/'.join(site.getPhysicalPath()), original_path.strip('/')) storage.add(rpath, "/".join(obj.getPhysicalPath())) obj.contentLayout = importtype.layout importtype.post_creation(obj, post_creation_data=pc_data) if not args.skip_transitioning and data['state']: # transition item only if it needs it state = api.content.get_state(obj=obj) if state != data['state']: try: print('Transitioning %s to %s' % (obj.id, data['state'])) api.content.transition(obj, to_state=data['state']) except Exception: logger.error( "Error transitioning %s to %s, maybe workflows" " don't match up" % (obj.id, data['state'])) # pass if stop_if_exception: if pdb_if_exception: pdb.set_trace() raise # set workflow / review history if 'review_history' in data: review_history = data['review_history'] wtool = api.portal.get_tool(name='portal_workflow') # loop over all workflow chains (usually only 1) for workflow_id in wtool.getChainFor(obj): obj.workflow_history[workflow_id] = review_history else: logger.warn('No review history on {obj}'.format(obj=obj)) fix_html_images(obj) obj.reindexObject() try: modification_date = data['data']['modification_date'] obj.setModificationDate(modification_date) obj.reindexObject(idxs=['modified']) logger.info(' set modification date to %s' % modification_date) except Exception: logger.info( 'Could not set modification date on {obj}'.format(obj=obj)) return obj
def process_form(self, instance, field, form, empty_marker=None, emptyReturnsMarker=False, validating=True): """A custom implementation for the widget form processing.""" if validating: # At the validation phase, only return the existing UID field # value or the filename of the new FileUpload object value = form.get(field.getName()) if value: return value, {} value = empty_marker files_list = form.get('%s_file' % field.getName(), []) if files_list: obj = files_list[0] value = getattr(obj, 'filename', getattr(obj, 'name', '')) return value, {} option = form.get('%s_option' % field.getName(), empty_marker) value = form.get(field.getName(), empty_marker) # In this case, just do the trivial if option == 'select': if value is empty_marker: return empty_marker if emptyReturnsMarker and value == '': return empty_marker return value, {} # In this case, we need to create new object(s) and get its UID(s) if option == 'upload': result = [] files_list = form.get('%s_file' % field.getName(), []) portal = getToolByName(instance, 'portal_url').getPortalObject() mt_tool = getToolByName(portal, 'mimetypes_registry') # Define the destination folder root = instance foldername = self.getStartupDirectory(instance, field) if foldername.startswith('/'): root = portal foldername = foldername[1:] try: folder = root.restrictedTraverse(foldername) except (KeyError, AttributeError): # If the startup_directory doesn't exists, fallback to # the current instance if folderish, or its parent if IFolder.providedBy(instance): folder = instance else: folder = aq_parent(instance) for fileobj in files_list: # Normalize the filename filename = getattr(fileobj, 'filename', '') filename = filename.split('\\')[-1] if filename: # Guess the mimetype & define the content-type class content = 'File' mimetype = mimetypes.guess_type(filename)[0] or "" if mimetype.startswith('image'): content = 'Image' # Create the new content old_id = folder.generateUniqueId(content) new_id = folder.invokeFactory(content, id=old_id, title=filename) obj = getattr(folder, new_id) obj._renameAfterCreation() obj.unmarkCreationFlag() obj.update_data(fileobj, mimetype) obj.reindexObject() result.append(obj.UID()) if field.multiValued: # Multi valued, append the old value result.extend(value) elif result: # Non multi valued, with a valid upload, return it result = result[0] else: # Non multi valued, with an invalid upload, return the old value result = value return result, {} return empty_marker
def _deploy_content(self, obj, is_page=True): """ Deploy object as page. """ try: new_req, orig_req = fakeRequest(obj) except AttributeError: # not a valid obj to override request with new_req = None content = self._render_obj(obj) if content is None: return filename = obj.absolute_url_path().lstrip('/') # deploy additional views for content type if PLONE_APP_BLOB_INSTALLED and isinstance(obj, ATBlob): self._deploy_views([os.path.join(filename, 'view'), ], is_page=True) if is_page: filename = filename.rstrip('/') if self.add_index or IFolder.providedBy(obj): filename = os.path.join(filename, 'index.html') elif not filename.endswith('.htm') and not filename.endswith('.html'): filename = filename + '.html' elif isinstance(obj, ATImage) or \ hasattr(obj, 'getBlobWrapper') and \ 'image' in obj.getBlobWrapper().getContentType(): # create path to dump ATImage in original size if filename.rsplit('.', 1)[-1] in ('png', 'jpg', 'gif', 'jpeg'): filename = os.path.join(filename, 'image.%s' % ( filename.rsplit('.', 1)[-1])) else: filename = os.path.join(filename, 'image.jpg') filename, content = self._apply_image_transforms(filename, content) elif (hasattr(obj, 'getBlobWrapper') and 'image' not in obj.getBlobWrapper().getContentType()): # create path like for ATImage if len(filename.rsplit('.', 1)) > 1: filename = os.path.join(filename, 'file.%s' % ( filename.rsplit('.', 1)[-1])) else: filename = os.path.join(filename, 'file') self._write(filename, content) # deploy all sizes of images uploaded for the object if not getattr(obj, 'schema', None): return # For Dexterity objects if IDexterityContent.providedBy(obj): from plone.dexterity.interfaces import IDexterityFTI from zope.component import getUtility from zope.schema import getFieldsInOrder from plone.behavior.interfaces import IBehaviorAssignable fti = getUtility(IDexterityFTI, name=obj.portal_type) schema = fti.lookupSchema() fields = getFieldsInOrder(schema) for _, field in fields: if INamedImageField.providedBy(field): self._deploy_blob_dexterity_image_field(obj, field) elif INamedFileField.providedBy(field): self._deploy_blob_dexterity_file_field(obj, field) behavior_assignable = IBehaviorAssignable(obj) if behavior_assignable: behaviors = behavior_assignable.enumerateBehaviors() for behavior in behaviors: for k, v in getFieldsInOrder(behavior.interface): pass else: for field in obj.Schema().fields(): if (PLONE_APP_BLOB_INSTALLED and IBlobImageField.providedBy(field)) or \ field.type == 'image': self._deploy_blob_image_field(obj, field) elif PLONE_APP_BLOB_INSTALLED and IBlobField.providedBy(field): self._deploy_blob_file_field(obj, field) elif field.type == 'file' and obj.portal_type not in self.file_types: self._deploy_file_field(obj, field) else: continue if new_req is not None: restoreRequest(orig_req, new_req)
def get_relateditems_options(context, value, separator, vocabulary_name, vocabulary_view, field_name=None, include_recently_added=True): if IForm.providedBy(context): context = context.context request = getRequest() site = get_top_site_from_url(context, request) options = get_ajaxselect_options( site, value, separator, vocabulary_name, vocabulary_view, field_name ) nav_root = getNavigationRootObject(context, site) if not ISimpleItem.providedBy(context): context = nav_root # basePath - start to search/browse in here. base_path_context = context if not IFolder.providedBy(base_path_context): base_path_context = aq_parent(base_path_context) if not base_path_context: base_path_context = nav_root options['basePath'] = '/'.join(base_path_context.getPhysicalPath()) # rootPath - Only display breadcrumb elements deeper than this path. options['rootPath'] = '/'.join(site.getPhysicalPath()) if site else '/' # rootUrl: Visible URL up to the rootPath. This is prepended to the # currentPath to generate submission URLs. options['rootUrl'] = site.absolute_url() if site else '' # contextPath - current edited object. Will not be available to select. options['contextPath'] = '/'.join(context.getPhysicalPath()) if base_path_context != nav_root: options['favorites'] = [ { 'title': _(u'Current Content'), 'path': '/'.join(base_path_context.getPhysicalPath()) }, { 'title': _(u'Start Page'), 'path': '/'.join(nav_root.getPhysicalPath()) } ] if include_recently_added: # Options for recently used key tool = getToolByName(context, 'portal_membership') user = tool.getAuthenticatedMember() options['recentlyUsed'] = False # Keep that off in Plone 5.1 options['recentlyUsedKey'] = (u'relateditems_recentlyused_%s_%s' % ( field_name or '', user.id )) # use string substitution with %s here for automatic str casting. return options
def get_breaches(self, items=None): """Return breaches for multiple items. Breaches coming from objects in the list of items or their children (if a object is a folder) will be ignored. """ if items is None: items = [self.context] catalog = getToolByName(self.context, 'portal_catalog') results = [] uids_to_ignore = [] uids_visited = set() self.breach_count = {} for obj in items: obj_path = '/'.join(obj.getPhysicalPath()) brains_to_delete = catalog(path={'query': obj_path}) # add the current items uid and all its childrens uids to the # list of uids that are ignored uids_to_ignore.extend([i.UID for i in brains_to_delete]) for brain_to_delete in brains_to_delete: try: obj_to_delete = brain_to_delete.getObject() # noqa except (AttributeError, KeyError): logger.exception('No object found for %s! Skipping', brain_to_delete) continue for breach in self.get_breaches_for_item(obj): add_breach = False for source in breach['sources']: # Only add the breach if one the sources is not in the # list of items that are to be deleted. if source['uid'] not in uids_to_ignore and \ source['uid'] not in uids_visited: add_breach = True uids_visited.add(source['uid']) break if add_breach: results.append(breach) if IFolder.providedBy(obj): count = len(catalog(path={'query': obj_path})) count_dirs = len( catalog(path={'query': obj_path}, is_folderish=True)) count_public = len( catalog(path={'query': obj_path}, review_state='published')) if count: self.breach_count[obj_path] = [ count, count_dirs, count_public ] # Cleanup: Some breaches where added before it was known # that their source will be deleted too. for result in results: for source in result['sources']: if source['uid'] in uids_to_ignore: # Drop sources that are also being deleted result['sources'].remove(source) if not result['sources']: # Remove the breach is there are no more sources # This check is necessary since there can be multiple # sources for a breach results.remove(result) return results
def get_relateditems_options(context, value, separator, vocabulary_name, vocabulary_view, field_name=None, include_recently_added=True): if IForm.providedBy(context): context = context.context request = getRequest() site = get_top_site_from_url(context, request) options = { 'separator': separator, } if not vocabulary_name: # we need a vocabulary! raise ValueError('RelatedItems needs a vocabulary') options['vocabularyUrl'] = '{0}/{1}?name={2}'.format( get_context_url(site), vocabulary_view, vocabulary_name, ) if field_name: options['vocabularyUrl'] += '&field={0}'.format(field_name) if value: options['initialValues'] = {} catalog = False if vocabulary_name == 'plone.app.vocabularies.Catalog': catalog = getToolByName(getSite(), 'portal_catalog') for value in value.split(separator): title = value if catalog: result = catalog(UID=value) title = result[0].Title if result else value options['initialValues'][value] = title nav_root = getNavigationRootObject(context, site) if not ISimpleItem.providedBy(context): context = nav_root # basePath - start to search/browse in here. base_path_context = context if not IFolder.providedBy(base_path_context): base_path_context = aq_parent(base_path_context) if not base_path_context: base_path_context = nav_root options['basePath'] = '/'.join(base_path_context.getPhysicalPath()) # rootPath - Only display breadcrumb elements deeper than this path. options['rootPath'] = '/'.join(site.getPhysicalPath()) if site else '/' # rootUrl: Visible URL up to the rootPath. This is prepended to the # currentPath to generate submission URLs. options['rootUrl'] = site.absolute_url() if site else '' # contextPath - current edited object. Will not be available to select. options['contextPath'] = '/'.join(context.getPhysicalPath()) if base_path_context != nav_root: options['favorites'] = [{ 'title': _(u'Current Content'), 'path': '/'.join(base_path_context.getPhysicalPath()) }, { 'title': _(u'Start Page'), 'path': '/'.join(nav_root.getPhysicalPath()) }] if include_recently_added: # Options for recently used key tool = getToolByName(context, 'portal_membership') user = tool.getAuthenticatedMember() options['recentlyUsed'] = False # Keep that off in Plone 5.1 options['recentlyUsedKey'] = ( u'relateditems_recentlyused_%s_%s' % (field_name or '', user.id) ) # use string substitution with %s here for automatic str casting. return options
def getMenuItems(self, context, request): """Return menu item entries in a TAL-friendly form.""" results = PloneFactoriesMenu.getMenuItems(self, context, request) # No menu customization if the product is not installed if not ICollectiveFactorymenuLayer.providedBy(request): return results portal_url = getToolByName(context, 'portal_url') # First of all, get the real context of the menu if IFolder.providedBy(context): folder = context elif isFolderOrFolderDefaultPage(context, request): folder = aq_parent(aq_inner(context)) else: # don't know how to handle this folder = context data = { 'context': context, 'portal_url': portal_url, 'container': folder } # If folder can't be annotable, do nothing # uncommon but may happen for old stuff like PloneGazette if not queryAdapter(folder, interface=IAnnotations): return results try: m_provider = ICustomFactoryMenuProvider(folder) except TypeError: # For any adaptation problem return results results = m_provider.getMenuCustomization(data, results) # Re-sort results.sort(lambda x, y: cmp(x['title'], y['title'])) mtool = getToolByName(context, 'portal_membership') if not mtool.isAnonymousUser() and\ mtool.getAuthenticatedMember().has_permission( 'Customize menu: factories', folder ): context_url = folder.absolute_url() results.append({ 'title': _(u'custommenu_manage_title', default=u'Customize menu\u2026'), 'description': _(u'custommenu_manage_description', default=u'Manage custom elements ' u'of this menu'), 'action': context_url + '/@@customize-factoriesmenu', 'selected': False, 'icon': None, 'submenu': None, 'extra': { 'separator': 'actionSeparator', 'id': 'customize-factoriesmenu', 'class': 'customize-menu' }, }) return results