def publishTraverse(self, request, name): """ used for traversal via publisher, i.e. when using as a url """ stack = request.get('TraversalRequestNameStack') image = None if stack and stack[-1] not in self._ignored_stacks: # field and scale name were given... scale = stack.pop() image = self.scale(name, scale) # this is aq-wrapped elif '-' in name: # we got a uid... if '.' in name: name, ext = name.rsplit('.', 1) storage = AnnotationStorage(self.context) info = storage.get(name) if info is not None: scale_view = ImageScale(self.context, self.request, **info) alsoProvides(scale_view, IStableImageScale) return scale_view.__of__(self.context) else: # otherwise `name` must refer to a field... if '.' in name: name, ext = name.rsplit('.', 1) value = getattr(self.context, name) scale_view = ImageScale(self.context, self.request, data=value, fieldname=name) return scale_view.__of__(self.context) if image is not None: return image raise NotFound(self, name, self.request)
def publishTraverse(self, request, name): """ used for traversal via publisher, i.e. when using as a url """ stack = request.get('TraversalRequestNameStack') if stack and stack[-1] not in self._ignored_stacks: # field and scale name were given... scale = stack.pop() image = self.scale(name, scale) # this is aq-wrapped elif '.' in name: # we got a uid... uid, ext = name.rsplit('.', 1) storage = AnnotationStorage(self.context) info = storage.get(uid) image = None if info is not None: image = self.make(info).__of__(self.context) alsoProvides(image, IStableImageScale) else: # otherwise `name` must refer to a field... scale_view = self.get_image_scale_view() if not scale_view: raise NotFound(self, name, self.request) return scale_view.scale() if image is not None: return image raise NotFound(self, name, self.request)
def scale(self, fieldname=None, scale=None, height=None, width=None, direction='thumbnail', **parameters): # import pdb # pdb.set_trace() if fieldname is None: fieldname = IPrimaryFieldInfo(self.context).fieldname if scale is not None: available = self.getAvailableSizes(fieldname) if not scale in available: return None width, height = available[scale] if self.request is not None: alsoProvides(self.request, IDisableCSRFProtection) storage = AnnotationStorage(self.context, self.modified) info = storage.scale(factory=self.create, fieldname=fieldname, height=height, width=width, direction=direction, **parameters) if info is not None: info['fieldname'] = fieldname scale_view = ImageScale(self.context, self.request, **info) return scale_view.__of__(self.context)
def _invalidate_scale(self, fieldname, scale): # Call storage with actual time in milliseconds. # This always invalidates old scales scale_storage = AnnotationStorage(self.context, int(time.time())) # holzhammermethode uids = scale_storage.keys() for uid in uids: del scale_storage[uid]
def getInfo(self, fieldname=None, scale=None, height=None, width=None, **parameters): storage = AnnotationStorage(self.context, self.modified) return storage.scale( factory=self.create, fieldname=fieldname, height=height, width=width, **parameters)
def scale( self, fieldname=None, scale=None, height=None, width=None, direction="thumbnail", **parameters ): if fieldname is None: primary_field = IPrimaryFieldInfo(self.context, None) if primary_field is None: return # 404 fieldname = primary_field.fieldname if scale is not None: if width is not None or height is not None: logger.warn( "A scale name and width/heigth are given. Those are" "mutually exclusive: solved by ignoring width/heigth and " "taking name", ) available = self.available_sizes if scale not in available: return None # 404 width, height = available[scale] if IDisableCSRFProtection and self.request is not None: alsoProvides(self.request, IDisableCSRFProtection) storage = AnnotationStorage( self.context, functools.partial(self.modified, fieldname) ) info = storage.scale( fieldname=fieldname, height=height, width=width, direction=direction, scale=scale, **parameters ) if info is None: return # 404 info["srcset"] = self.calculate_srcset( fieldname=fieldname, height=height, width=width, direction=direction, scale=scale, storage=storage, **parameters ) info["fieldname"] = fieldname scale_view = self._scale_view_class(self.context, self.request, **info) return scale_view
def _remove(self, fieldname, scale): # remove info from annotation key = "%s_%s" % (fieldname, scale) if key in self._storage.keys(): del self._storage[key] # remove saved scale scale_storage = AnnotationStorage(self.context) image_scales = self.context.restrictedTraverse("@@images") image_scale = image_scales.scale(fieldname, scale=scale) del scale_storage[image_scale.uid]
def save_cropped(self, fieldname, scale, image_file): """ see interface """ sizes = getAllowedSizes() w, h = sizes[scale] def crop_factory(fieldname, **parameters): # LMU patch: remove the scale parameter _parameters = { key: value for key, value in parameters.iteritems() if key != 'scale' } result = scaleImage(image_file.read(), **_parameters) if result is not None: data, format, dimensions = result mimetype = 'image/{0:s}'.format(format.lower()) field = self.get_image_field(fieldname) value = field.__class__(data, contentType=mimetype, filename=field.filename) value.fieldname = fieldname return value, format, dimensions # call storage with actual time in milliseconds # this always invalidates old scales storage = AnnotationStorage(self.context, _millis) # We need to pass direction='thumbnail' since this is the default # used by plone.namedfile.scaling, also for retrieval of scales. # Otherwise the key under which the scaled and cropped image is # saved in plone.scale.storage.AnnotationStorage will not match the # key used for retrieval (= the cropped scaled image will not be # found) # LMU patch: add the scale parameter storage.scale( factory=crop_factory, direction='thumbnail', fieldname=fieldname, scale=scale, width=w, height=h, )
def publishTraverse(self, request, name): """ used for traversal via publisher, i.e. when using as a url """ stack = request.get('TraversalRequestNameStack') if stack: # field and scale name were given... scale = stack.pop() image = self.scale(name, scale) # this is aq-wrapped elif '.' in name: # we got a uid... uid, ext = name.rsplit('.', 1) storage = AnnotationStorage(self.context) info = storage.get(uid) image = None if info is not None: image = self.make(info).__of__(self.context) else: # otherwise `name` must refer to a field... field = self.field(name) image = field.get(self.context) # this is aq-wrapped if image is not None: return image raise NotFound(self, name, self.request)
def save_cropped(self, fieldname, scale, image_file): """ see interface """ field = self.get_image_field(fieldname) handler = IImageScaleHandler(field) sizes = field.getAvailableSizes(self.context) w, h = sizes[scale] data = handler.createScale(self.context, scale, w, h, data=image_file.read()) # store scale for classic <fieldname>_<scale> traversing handler.storeScale(self.context, scale, **data) # call plone.scale.storage.scale method in order to # provide saved scale for plone.app.imaging @@images view def crop_factory(fieldname, direction='keep', **parameters): blob = Blob() result = blob.open('w') _, image_format, dimensions = scaleImage(data['data'], result=result, **parameters) result.close() return blob, image_format, dimensions # Avoid browser cache # calling reindexObject updates the modified metadate too self.context.reindexObject() # call storage with actual time in milliseconds # this always invalidates old scales storage = AnnotationStorage(self.context, _millis) storage.scale(factory=crop_factory, fieldname=field.__name__, width=w, height=h)
def CopyToClient(survey, preview=False): """Copy the survey to the online client part of the site. :param survey: the survey to copy :param bool preview: indicates if this is a preview or a normal publication :rtype: :py:class:`euphorie.content.survey.Survey` The public area is hardcoded to be a container with id ``client`` within the site root. The ''id'' and ''title'' of the survey group will be used for the published survey. If another object with the same ''id'' already exists it will be removed first. Any missing country and sector folders are created if needed. If this is a preview (as indicated by the ``preview`` parameter) the id of the survey will be set to ``preview``, guaranteeing that an existing published survey will not be replaced. This also means only a sector can only have one preview online. This method assumes the current user has permissions to create content in the online client. This is normally done by using the :py:func:`PublishToClient` function which switches the current user for the copy operation. Returns the new public survey instance. """ # This is based on OFS.CopyContainer.manage_clone, modified to # use the sector id and title, skip security checks and remove # an existing object with the same id. client = getPortal(survey).client source = aq_inner(survey) surveygroup = aq_parent(source) sector = aq_parent(surveygroup) country = aq_parent(sector) from euphorie.content.sector import ISector assert ISector.providedBy(sector) if country.id not in client: client.invokeFactory("euphorie.clientcountry", country.id, title=country.title, country_type=country.country_type) cl_country = client[country.id] if sector.id not in cl_country: cl_country.invokeFactory("euphorie.clientsector", sector.id) target = cl_country[sector.id] target.title = sector.title target.logo = sector.logo # Clear any scaled logos AnnotationStorage(target).storage.clear() target.main_background_colour = getattr(sector, "main_colour", None) if target.main_background_colour: target.main_foreground_colour = utils.MatchColour( target.main_background_colour, 0.0, 0.6, 0.3) target.main_background_bright = \ utils.IsBright(target.main_background_colour) target.support_background_colour = getattr(sector, "support_colour", None) if target.support_background_colour: target.support_foreground_colour = \ utils.MatchColour(target.support_background_colour) target.support_background_bright = \ utils.IsBright(target.support_background_colour) copy = source._getCopy(target) if preview: copy.id = "preview" else: copy.id = surveygroup.id copy.title = surveygroup.title copy.obsolete = surveygroup.obsolete copy.evaluation_algorithm = surveygroup.evaluation_algorithm copy.version = source.id copy.published = datetime.datetime.now() copy.preview = preview if copy.id in target: # We must suppress events to prevent the can-not-delete-published- # content check from blocking us. # XXX: We need however the ObjectWillBeRemovedEvent event to be called # otherwise the removed objects are not uncatalogged. to_delete = target._getOb(copy.id) notify(ObjectWillBeRemovedEvent(to_delete, target, copy.id)) target._delObject(copy.id, suppress_events=True) target._setObject(copy.id, copy, suppress_events=True) copy = target[copy.id] copy._postCopy(target, op=0) notify(ObjectPublishedEvent(source)) return copy
def reset_scales(app, args): parser = argparse.ArgumentParser( description='Reset all scales in the application') parser.add_argument('--site', help='Add the site id', required=True) parser.add_argument( '--regenerate', help='Scale(s) you want to regnerate, multiple allowed', action='append') parser.add_argument('-c', help='stupid bug') args = parser.parse_args(args) site_name = args.site root = makerequest.makerequest(app) site = root.get(site_name, None) logger = getLogger(__name__) log = mklog(root.REQUEST) if site is None: msg = "No site called `%s` found in the database." % site_name log(msg) logger.info(msg) sys.exit(1) # Set up local site manager setHooks() setSite(site) # Set up security uf = app.acl_users user = uf.getUserById("admin") newSecurityManager(None, user) catalog = site.portal_catalog log('resetting all scales from %r:' % site) real = timer() # real time lap = timer() # real lap time (for intermediate commits) cpu = timer(clock) # cpu time processed = 0 def checkPoint(): msg = 'intermediate commit '\ '(%d objects processed, last batch in %s)...' log(msg % (processed, lap.next())) trx = get() trx.note(u'migrated %d btree-folders' % processed) trx.savepoint() cpi = checkpointIterator(checkPoint, 1000) for item in catalog(object_provides=IDexterityItem.__identifier__): o = item.getObject() storage = AnnotationStorage(o) storage.clear() msg = "Cleared storage for %s" % (item.getURL()) log(msg) try: if args.regenerate is not None: for scale in args.regenerate: if hasattr(o, 'image'): scaler = o.unrestrictedTraverse('@@images') if scaler.scale(fieldname='image', scale=scale) is not None: log("regenerated scale %s" % (scale)) else: log("error regenerating scale %s" % (scale)) except AttributeError: continue except IOError: continue processed += 1 cpi.next() checkPoint() msg = 'processed %d object(s) in %s (%s cpu time).' msg = msg % (processed, real.next(), cpu.next()) log(msg) logger.info(msg) transaction.commit()
def storage(self): from plone.scale.storage import AnnotationStorage provideAdapter(zope.annotation.attribute.AttributeAnnotations) storage = AnnotationStorage(_DummyContext()) storage.modified = lambda: 42 return storage
def object_modified_or_deleted(obj, event): storage = AnnotationStorage(obj) storage.clear()
def save_images(self, context): """Save images from ZODB to temp directory """ portal = getSite() portal_url = portal.absolute_url() if not portal_url.endswith('/'): portal_url += '/' portal_path = '/'.join(portal.getPhysicalPath()) reference_tool = getToolByName(portal, 'reference_catalog') mtool = getToolByName(portal, 'portal_membership') for filename, image in self.images: size = None # Traverse methods mess with unicode if type(image) is unicode: image = str(image) path = image.replace(portal_url, '') item = None # using uid if 'resolveuid' in image: # uid is the traversed value coming after "resolveuid/" resolveuidpath = image.split('/') resolveuid_idx = resolveuidpath.index('resolveuid') try: uuid = resolveuidpath[resolveuid_idx + 1] except IndexError: logger.error("Failed to get image uid from %s", image) continue item = reference_tool.lookupObject(uuid) if len(resolveuidpath) >= resolveuid_idx + 2: size = resolveuidpath[resolveuid_idx + 2] logger.debug("Get image from uid %s", uuid) if not item: # relative url try: item = context.restrictedTraverse(image) logger.debug("Get image from context") except Unauthorized: logger.warning( "Unauthorized to get image from context path %s", item) except: logger.debug("Failed to get image from context path %s", image) if not item: # plone.app.imaging if '@@images' in path and AnnotationStorage: context_path, images_str, uid_filename = path.rsplit( '/', 2) image_context = portal.restrictedTraverse(context_path) uid, ext = uid_filename.rsplit('.', 1) storage = AnnotationStorage(image_context) info = storage.get(uid) if info is not None: request = TestRequest() scale_view = ImageScale(image_context, request, **info) item = scale_view.__of__(image_context) if not item: # absolute url image_path = '/'.join((portal_path, path)) try: item = portal.restrictedTraverse(image_path) logger.debug("Get image from portal") except Unauthorized: logger.warning("Unauthorized to get from context path %s", image_path) except: logger.error("Failed to get image from portal path %s", image_path) continue if not mtool.checkPermission('View', item): logger.warning("Unauthorized to get image %s", item) continue if item and size: try: item = item.restrictedTraverse(size) except Unauthorized: logger.warning("Unauthorized to get size %s from image %s", size, image) except: logger.error("Failed to get size %s from image %s", size, image) pass # Eek, we should put an adapter for various image providers (overkill ?). data = get_image_data(item) if data: _write_file(data, self.fsinfo, filename) return
def storage(self): from plone.scale.storage import AnnotationStorage storage = AnnotationStorage(_DummyContext()) storage.modified = lambda: 42 storage.storage = {} return storage
for site in plones: print('') print('Handling Plone Site %s.' % site.id) setSite(site) catalog = getToolByName(site, 'portal_catalog') count = 0 purged = 0 for brain in catalog.unrestrictedSearchResults(): try: obj = brain.getObject() except: continue savepoint = transaction.savepoint() ann = AnnotationStorage(obj) try: ann.storage except TypeError: # This happens when the context cannot be annotated, for # example for a plone.app.discussion comment. continue # We want to remove all scales that are X days older than the # last modification date of the object. final_date = obj.modified() - DAYS changed = False for key, value in ann.items(): if value['modified'] < final_date.millis(): # This may easily give an error, as it tries to remove # two keys: del ann[key] del ann.storage[key]