def __call__(self, id, processing, *args, **kwargs): # Get the requested element item = resolve_object_ref(Item, id) # Make sure the selected element exists if item is None: raise cherrypy.NotFound() # Normalize the URL to use a local id if id == item.global_id: raise cherrypy.HTTPRedirect(cherrypy.url().replace( "/" + id + "/", "/%d/" % item.id)) # Handle legacy image requests (woost < 0.8) if args or kwargs or "(" in processing: raise cherrypy.HTTPError(410) # Parse the given processing string, splitting the image factory from # the image format (ie. "home_thumbnail.png" -> ("home_thumbnail", "PNG")) parts = processing.rsplit(".", 1) parameters = None if len(parts) == 2: factory_id, ext = parts format = formats_by_extension.get(ext) if format is None: raise cherrypy.HTTPError(400, "Invalid image extension: %s" % ext) else: factory_id = processing format = None factory = ImageFactory.get_instance(identifier=factory_id) if factory is None: match = generic_image_factory_pattern.match(factory_id) if match: try: factory_id = int(match.group(1)) factory = ImageFactory.require_instance(factory_id) except: pass if factory is None: raise cherrypy.HTTPError( 400, "Invalid image factory id: %s" % factory_id) # Deny access to unauthorized elements get_current_user().require_permission(RenderPermission, target=item, image_factory=factory) try: image_cache_file = require_rendering(item, factory, format, parameters) except BadRenderingRequest, ex: raise cherrypy.HTTPError(400, ex.message)
def handle_traversed(cls, event): controller = event.source # Require an edit stack with an edit node on top controller._require_edit_node() # Disable access to invisible content types if not controller.stack_node.content_type.visible: raise cherrypy.NotFound() # Restrict access if controller.stack_node.item.is_inserted: get_current_user().require_permission( ReadPermission, target=controller.stack_node.item)
def submit(self): collection = self.collection selection = self.selection parent = self.item position = self.position related_end = self.member.related_end size = len(collection) if position < 0: position = size + position position = min(position, size) if any(parent.descends_from(item) for item in selection): raise TreeCycleError() for i in range(self.MAX_TRANSACTION_ATTEMPTS): with changeset_context(get_current_user()): for item in reversed(selection): if isinstance(related_end, Reference) \ and item.get(related_end) is parent: collection.remove(item) collection.insert(position, item) try: datastore.commit() except ConflictError: datastore.abort() datastore.sync() else: break
def user_collection(self): class ChangeLogUserCollection(BackOfficeUserCollection): persistence_prefix = "changelog" schema = self.content_schema default_order = [NegativeExpression(ChangeSet.id)] allow_sorting = False allow_type_selection = False allow_language_selection = False available_languages = Configuration.instance.languages @cached_getter def available_user_filters(self): filters = BackOfficeUserCollection.available_user_filters(self) cs_action_filter = ChangeSetActionFilter() cs_action_filter.id = "member-action" cs_target_filter = ChangeSetTargetFilter() cs_target_filter.id = "member-changes" filters.append(cs_action_filter) filters.append(cs_target_filter) return filters user_collection = ChangeLogUserCollection(ChangeSet) user_collection.params.source = SessionParameterSource( key_prefix = user_collection.persistence_prefix ) user_collection.add_base_filter( ChangeSetPermissionExpression(get_current_user()) ) return user_collection
def submit(self): document = self.controller.context["publishable"] # Write to the database if document.should_save_instances: with changeset_context(get_current_user()): UploadForm.submit(self) self.instance.insert() datastore.commit() # Do not write to the database, only process form data else: UploadForm.submit(self) # Send email messages based on the submitted data for email_template in document.email_notifications: email_template.send({ "form": document, "instance": self.instance }) # Redirect the user to a confirmation page if document.redirection: raise cherrypy.HTTPRedirect(self.controller.context["cms"].uri( document.redirection, form=document.id, instance=None if not document.should_save_instances else self.instance.id))
def mailing_state(self, task_id, *args, **kwargs): cherrypy.response.headers["Content-Type"] = "application/json" task = tasks.get(int(task_id)) # If task is expired show the status from the mailing object if task is None: mailing = self.context["cms_item"] else: mailing = Mailing.get_instance(task.mailing_id) if mailing is None: return dumps({}) user = get_current_user() if not user.has_permission(ReadPermission, target = mailing): raise cherrypy.HTTPError(403, "Forbidden") tasks.remove_expired_tasks() return dumps({ "sent": len(mailing.sent), "errors": len(mailing.errors), "total": mailing.total, "completed": task and task.completed or True })
def export_form_data(self, item, form_data): """Update the edit form with the data contained on the edited item.""" self.form_adapter.export_object(item, form_data, self.content_type, self.form_schema) # Default translations if self.content_type.translated: user = get_current_user() available_languages = set( language for language in item.translations if user.has_permission(ReadTranslationPermission, language=language)) if not self._item.translations: default_language = \ Configuration.instance.get_setting("default_language") if default_language in available_languages: self._item.new_translation(default_language) self.translations = [ language for language in self._item.translations.keys() if language in available_languages ] else: self.translations = []
def submit(self): from woost.extensions.campaignmonitor import CampaignMonitorExtension extension = CampaignMonitorExtension.instance user = get_current_user() with changeset_context(author=user) as changeset: extension.synchronize_lists(restricted=True) # Report changed lists created = set() modified = set() deleted = set() for change in changeset.changes.itervalues(): if isinstance(change.target, CampaignMonitorList): action_id = change.action.identifier if action_id == "create": created.add(change.target) elif action_id == "modify": modified.add(change.target) elif action_id == "delete": deleted.add(change.target) datastore.commit() self.output["created_lists"] = created self.output["modified_lists"] = modified self.output["deleted_lists"] = deleted
def handle_producing_output(cls, event): # Set application wide output parameters cms = event.source event.output.update( cms=cms, user=get_current_user(), publishable=event.controller.context.get("publishable"))
def breakpoint(open_browser=False, stack_depth=0): """Set a `~webconsole.utils.breakpoint` that is only triggered by users with `code execution rights <WebConsolePermission>`. """ from cocktail.controllers import Location from webconsole.utils import breakpoint as webconsole_breakpoint from woost.models import get_current_user, Publishable from woost.extensions.webconsole.webconsolepermission \ import WebConsolePermission user = get_current_user() if user and user.has_permission(WebConsolePermission): def initializer(session): if open_browser: # Find the web console document webconsole = Publishable.require_instance( qname="woost.extensions.webconsole.page") # Determine the URI for the breakpoint session webconsole_location = Location.get_current_host() webconsole_location.path_info = webconsole.get_uri() webconsole_location.query_string["session_id"] = session.id # Open a web browser tab pointing at the URI from webbrowser import open open(str(webconsole_location)) return webconsole_breakpoint(initializer=initializer, stack_depth=stack_depth + 1)
def get_options(cls): for member in cls.members().itervalues(): if (member is not cls.product and member is not cls.order and member.visible and member.editable and issubclass(member.schema, ECommercePurchase) and get_current_user().has_permission( ModifyMemberPermission, member=member)): yield member
def form_errors(self): form_errors = FormControllerMixin.form_errors(self) if not form_errors and self.submitted and get_current_user().anonymous: user_param = get_parameter(schema.Member("user")) form_errors._items.append(AuthenticationFailedError(user_param)) return form_errors
def available_languages(self): user = get_current_user() return [language for language in Configuration.instance.languages if user.has_permission( ReadTranslationPermission, language = language )]
def user_views(self): user = get_current_user() views = OrderedSet() for role in user.iter_roles(): views.extend(role.user_views) return views
def allowed_destinations(): return [ destination for destination in extension.destinations if get_current_user().has_permission( ExportationPermission, destination = destination ) ]
def current_user(self): cherrypy.response.headers["Content-Type"] = "text/javascript" user = get_current_user() return "cocktail.declare('woost'); woost.user = %s;" % dumps( { "id": user.id, "label": translations(user), "identifier": user.get(app.authentication.identifier_field), "anonymous": user.anonymous } if user else None)
def validate_publishable(self, publishable): if not publishable.is_published(): raise cherrypy.NotFound() user = get_current_user() user.require_permission(ReadPermission, target=publishable) user.require_permission(ReadTranslationPermission, language=get_language())
def handle_traversed(cls, event): # Restrict access to the edited item controller = event.source item = controller.stack_node.item user = get_current_user() if item.is_inserted: user.require_permission(ModifyPermission, target=item) else: user.require_permission(CreatePermission, target=item.__class__)
def adapter(self): """The schema adapter used to produce data suitable for listing. @type: L{SchemaAdapter<cocktail.schema.adapter.SchemaAdapter>} """ user = get_current_user() adapter = schema.Adapter() adapter.exclude([ member.name for member in self.type.members().itervalues() if not member.visible or not user.has_permission(ReadMemberPermission, member=member) ]) return adapter
def available_languages(self): """The list of languages that items in the listing can be displayed in. Each language is represented using its two letter ISO code. @type: sequence of unicode """ user = get_current_user() return [ language for language in Configuration.instance.languages if user.has_permission(ReadTranslationPermission, language=language) ]
def _delete_instances(self, query): user = get_current_user() class ValidatingDeletedSet(InstrumentedSet): def item_added(self, item): user.require_permission(DeletePermission, target=item) deleted_set = ValidatingDeletedSet() with changeset_context(user): for item in list(query): item.delete(deleted_set)
def __call__(self, *args, **kwargs): node = self.stack_node previewed_item = self.previewed_item publishable = self.preview_publishable preview_language = self.preview_language user = get_current_user() # Set the language for the preview if preview_language: set_language(preview_language) # Enforce permissions user.require_permission(ReadPermission, target=previewed_item) if publishable is not previewed_item: user.require_permission(ReadPermission, target=publishable) # Disable the preview if the item's unsaved state produces validation # errors; these would usually lead to unhandled server errors during # rendering. errors = schema.ErrorList(node.iter_errors()) if errors: error_box = templates.new("cocktail.html.ErrorBox") error_box.errors = errors message = Element("div", class_name="preview-error-box", children=[ translations( "woost.backoffice invalid item preview", preview_language), error_box ]) message.add_resource("/resources/styles/backoffice.css") return message.render_page() # Update the edited item with the data to preview node.import_form_data(node.form_data, previewed_item) self.context.update(original_publishable=self.context["publishable"], publishable=publishable) controller = publishable.resolve_controller() if controller is None: raise cherrypy.NotFound() if isinstance(controller, type): controller = controller() return controller()
def _apply_https_policy(self, publishable): policy = Configuration.instance.get_setting("https_policy") website = get_current_website() if policy == "always": Location.require_https() elif policy == "never": Location.require_http() elif policy == "per_page": if publishable.requires_https or not get_current_user().anonymous: Location.require_https() elif not website.https_persistence: Location.require_http()
def get_error_page(self, error): """Produces a custom error page for the indicated exception. @param error: The exception to describe. @type error: Exception @return: A tuple comprised of a publishable item to delegate to and an HTTP status to set on the response. Either component can be None, so that no custom error page is shown, or that the status is not changed, respectively. @rtype: (L{Document<woost.models.Document>}, int) """ is_http_error = isinstance(error, cherrypy.HTTPError) config = Configuration.instance page = None page_name = None status = None # Page not found if is_http_error and error.status == 404: return config.get_setting("not_found_error_page"), 404 # Service unavailable elif is_http_error and error.status == 503: return config.get_setting("maintenance_page"), 503 # Access forbidden: # The default behavior is to show a login page for anonymous users, and # a 403 error message for authenticated users. elif ((is_http_error and error.status == 403) or isinstance(error, (AuthorizationError, AuthenticationFailedError))): if get_current_user().anonymous: publishable = self.context["publishable"] while publishable is not None: login_page = publishable.login_page if login_page is not None: return login_page, 403 publishable = publishable.parent return config.get_setting("login_page"), 403 else: return config.get_setting("forbidden_error_page"), 403 # Generic error elif (is_http_error and error.status == 500) or not is_http_error: return config.get_setting("generic_error_page"), 500 return None, None
def get_public_adapter(cls): from woost.extensions.ecommerce import ECommerceExtension user = get_current_user() adapter = schema.Adapter() adapter.exclude(["customer", "status", "purchases"]) adapter.exclude([ member.name for member in cls.members().itervalues() if not member.visible or not member.editable or not issubclass(member.schema, ECommerceOrder) or not user.has_permission(ModifyMemberPermission, member=member) ]) if len(ECommerceExtension.instance.payment_types) == 1: adapter.exclude(["payment_type"]) return adapter
def session(self): session = WebConsoleController.session(self) session.context.update(config=Configuration.instance, user=get_current_user(), language=get_language(), translations=translations, first=first, last=last) for model in PersistentObject.schema_tree(): session.context[model.name] = model return session
def allowed_publication_targets(self): from woost.extensions.facebookpublication \ import FacebookPublicationExtension user = get_current_user() return [ fb_target for fb_target in FacebookPublicationExtension.instance.targets if fb_target.auth_token and all( user.has_permission(FacebookPublicationPermission, target=publishable, publication_target=fb_target) for publishable in self.selection) ]
def _fill_entries(self): user = get_current_user() items = self.items if items is not None: items = [ item for item in self.items if user.has_permission(ReadPermission, target=item) ] if items: List._fill_entries(self) else: self.tag = "div" self.append(u"-")
def create_new_button(self): new_button = Element(class_name="ItemSelector-button new") instantiable_types = set( content_type for content_type in ([self.member.type] + list(self.member.type.derived_schemas())) if content_type.visible and content_type.instantiable and get_current_user().has_permission(CreatePermission, target=content_type)) if len(instantiable_types) > 1: new_button.add_class("selector") label = Element("span", class_name="label") new_button.append(label) container = Element(class_name="selector_content") new_button.append(container) content_type_tree = templates.new("woost.views.ContentTypeTree") content_type_tree.root = self.member.type content_type_tree.filter_item = instantiable_types.__contains__ @extend(content_type_tree) def create_label(tree, content_type): label = call_base(content_type) label.tag = "button" label["type"] = "submit" label["name"] = "relation-new" label[ "value"] = self.member.name + "-" + content_type.full_name return label container.append(content_type_tree) else: new_button.tag = "button" new_button["type"] = "submit" new_button["name"] = "relation-new" new_button["value"] = \ self.member.name + "-" + list(instantiable_types)[0].full_name label = new_button label.append(translations("woost.views.ItemSelector new")) return new_button
def _export(self, context, status_tracker=None): StaticSiteDestination._export(self, context, status_tracker=status_tracker) # Close the zip file before export it to the CMS context["zip_file"].close() upload_path = app.path("upload") with changeset_context(get_current_user()): file = File.from_path(context["temp_file"], upload_path, languages=[get_language()]) file.title = "export-%s" % date.today().strftime("%Y%m%d") file.file_name = file.title + ".zip" file.insert() context.update(file=file)