def action(self, resource, context, form): email = form['email'].strip() # 1. Make the user, or get it results = context.database.search(format='user', email=email) if len(results) == 0: # New user user = context.root.make_user() for name in self.fields: field = self.get_field(name) if field and getattr(field, 'persistent', True): self.set_value(user, context, name, form) user.update_pending_key() email_id = 'user-ask-for-confirmation' else: # User already registered user = results.get_resources().next() email_id = 'register-already-registered' # 2. Send email send_email(email_id, context, email, user=user) # 3. Show message message = MSG( u'<div id="registration-end-msg">' u'An email has been sent to you, to finish the registration ' u'process follow the instructions detailed in it.</div>') return message.gettext().encode('utf-8')
def get_before_namespace(self, resource, context): # Set organizer infos in ${before} owner = resource.get_owner() owner = resource.get_resource(owner).get_title() owner_msg = MSG(u'<p id="event-owner">Created by <em>{owner}</em></p>') owner_msg = owner_msg.gettext(owner=owner).encode('utf-8') return XMLParser(owner_msg)
def get_namespace(self, resource, context): proxy = super(Shop_UserSendConfirmation, self) namespace = proxy.get_namespace(resource, context) confirm_msg = MSG(u"""Fill this form to receive a mail with the link to activate your account""") namespace['required_msg'] = (list(XMLParser(confirm_msg.gettext().encode('utf8'))) + list(XMLParser('<br/>')) + list(namespace['required_msg'])) return namespace
def get_title(self, context): if self.title is not None: return self.title cls = self._resource_class if cls: class_title = cls.class_title.gettext() title = MSG(u'Add {class_title}') return title.gettext(class_title=class_title) return MSG(u'Add resource').gettext()
def GET(self, resource, context): try: from lpod.rst2odt import rst2odt except ImportError: msg = MSG(u'<p>Please install <a href="{href}">{name}</a> ' u'for Python on the server.</p>') msg = msg.gettext(href='http://lpod-project.org/', name='LpOD') return msg.encode('utf_8') # Just to ignore pyflakes warning rst2odt proxy = super(WikiPage_ToODT, self) return proxy.GET(resource, context)
def get_options(cls): context = get_context() container = context.resource.parent options = [{'name': '', 'value': MSG(u"lpoD default template")}] for resource in container.get_resources(): if not resource.class_id in ALLOWED_FORMATS: continue msg = MSG(u'{title} (<a href="{link}">view</a>)') msg = msg.gettext(title=resource.get_title(), link=context.get_link(resource)).encode('utf_8') options.append({'name': resource.name, 'value': XMLParser(msg)}) return options
def get_payment_way_description(self, context, total_amount): msg = MSG(u"Pay {percent}% of {original_amount} now ({amount})") percent = self.get_property('percent') if self.get_property('pay_tax'): total_amount = total_amount['with_tax'].split(' ')[0] total_amount = decimal(total_amount) else: total_amount = total_amount['without_tax'].split(' ')[0] total_amount = decimal(total_amount) amount = total_amount * (percent / decimal('100.0')) msg = msg.gettext(percent=percent, original_amount=format_price(total_amount), amount=format_price(amount)) return list(XMLParser(msg.encode('utf-8'))) + self.get_property('data')
def get_message(self, context, language=None): """This function must return the tuple (subject, body) """ # Subject subject = MSG(u'[{title}] has been modified') subject = subject.gettext(title=self.get_title(), language=language) # Body message = MSG(u'DO NOT REPLY TO THIS EMAIL. To view modifications ' u'please visit:\n{resource_uri}') uri = context.get_link(self) uri = str(context.uri.resolve(uri)) uri += '/;commit_log' body = message.gettext(resource_uri=uri, language=language) # And return return subject, body
def action_postpone(self, resource, context, form): postpone = form['postpone'] alert = datetime.combine(postpone, time(9, 0)) pattern = MSG(u'<a href="{path}">{title}</a>', format='replace') missions = [] for path in form['ids']: mission = resource.get_resource(path) mission.set_property('crm_m_alert', alert) missions.append(pattern.gettext(path=path, title=mission.get_title())) postpone = context.format_date(postpone) missions = u", ".join(missions) context.message = MSG_MISSIONS_POSTPONED(postpone=postpone, missions=missions)
def get_template_title(self, context): here = context.resource # XXX Confidentiality problems if isinstance(here, ShopUser): return here.get_public_title() # In the website site_root = here.get_site_root() if site_root is here: return site_root.get_title() # Somewhere else if site_root.get_property('hide_website_title_on_meta_title'): message = MSG(u"{here_title}") else: message = MSG(u"{here_title} > {root_title}") return message.gettext(root_title=site_root.get_title(), here_title=here.get_title())
def __call__(self, **kw): if not kw: # Also skipping stl calls raise AttributeError, 'missing variables to substitute' message = MSG.gettext(self, language=None, **kw) # Send a translated copy of this instance return self.__class__(message, domain=self.domain)
def get_linked_resources_message(resource, context, state='public'): # Customize message if webpage uses private/pending resources referenced_resources = list(get_linked_resources(resource)) if len(referenced_resources) == 0: return None message = MSG(u'This {title} uses pending/private resources ' u'please go to ' u'<a href="{path}/;backlinks">backlinks interface</a>.') path = context.get_link(resource) path = XMLContent.encode(path) class_title = resource.class_title.gettext() message = message.gettext(title=class_title, path=path).encode('utf8') message = XHTMLBody.decode(message) # Return custom message return message
def _register(self, resource, context, email): site_root = context.site_root # Add the user users = site_root.get_resource('users') user = users.set_user(email, None) # Set the role to 'Member' default_role = site_root.class_roles[1] site_root.set_user_role(user.name, default_role) # Send confirmation email user.send_confirmation(context, email) # Bring the user to the login form message = MSG( u"An email has been sent to you, to finish the registration " u"process follow the instructions detailed in it.") return message.gettext().encode('utf-8')
def get_items(self, context): unit = self.get_property("unit") criterium = self.get_property("criterium") uri = context.uri options = [] get_record_value = self.handler.get_record_value # Get values values = [(None, None)] for record in self.handler.get_records(): min_value = get_record_value(record, "min") max_value = get_record_value(record, "max") values.append((min_value, max_value)) for value in values: min_value, max_value = value min_value_q = int(min_value * 100) if min_value else None max_value_q = int(max_value * 100) if max_value else None value = (min_value_q, max_value_q) name = IntegerRange.encode(value) if min_value is None and max_value: title = MSG(u"Less than {max_value} {unit}") elif min_value and max_value: title = MSG(u"From {min_value} to {max_value} {unit}") elif max_value is None and min_value: title = MSG(u"More than {min_value} {unit}") else: title = MSG(u"All") name = None value = None selected = context.query.get(criterium) == value kw = {criterium: name, "batch_start": 0} uri = uri.replace(**kw) title = title.gettext(min_value=min_value, max_value=max_value, unit=unit) options.append( { "name": name, "criterium": criterium, "query": RangeQuery(criterium, min_value_q, max_value_q), "selected": selected, "uri": uri, "css": "selected" if selected else None, "title": title, } ) return options
def get_namespace(self, resource, context): proxy = super(Shop_UserConfirmRegistration, self) namespace = proxy.get_namespace(resource, context) confirm_msg = MSG(u""" You have not yet confirmed your registration.<br/> To confirm it, please click on the confirmation link included on the registration confirmation email.<br/> You can also fill your email address and your activation key (received on the mail) in the following form.<br/> If you havn't received your registration key, <a href=";send_confirmation_view"> you can receive it again by clicking here. </a> """) namespace['required_msg'] = (list(XMLParser(confirm_msg.gettext().encode('utf8'))) + list(XMLParser('<br/>')) + list(namespace['required_msg'])) return namespace
def get_message(self, context, language=None): # Subject title=self.get_title() subject = MSG(u'The event "{title}" has been modified') subject = subject.gettext(title=title, language=language) # Body message = MSG(u'DO NOT REPLY TO THIS EMAIL.\n\n' u'The user "{last_author}" has made some modifications ' u'to the event "{title}".\n' u'To view these modifications please visit:\n' u'{resource_uri}\n') uri = context.get_link(self) uri = str(context.uri.resolve(uri)) uri += '/;edit' last_author = self.get_value('last_author') last_author = context.root.get_user_title(last_author) body = message.gettext(last_author=last_author, resource_uri=uri, title=title, language=language) # And return return subject, body
def get_template_title(self, context): """Return the title to give to the template document. """ here = context.resource root = context.root root_title = root.get_title() # Choose the template if not root.is_allowed_to_view(context.user, here): return '' elif root is here: template = MSG(u"{view_title} - {root_title}") here_title = None else: template = MSG(u"{here_title} - {view_title} - {root_title}") here_title = here.get_title() # The view view_title = context.view.get_title(context) # Ok return template.gettext(root_title=root_title, here_title=here_title, view_title=view_title)
def get_email_html(self, context, web_version=False): header = [] if web_version is False: header = MSG(u""" <center> <span style="font-size:10px;font-weight:bold; font-family:Arial,Helvetica,sans-serif;"> <a href="{page_uri}" target="_blank">Click here</a> to see this news on you browser </span> </center> """).gettext(page_uri=self.get_newsletter_uri(context)) header = HTMLParser(header.encode('utf-8')) footer = MSG(u""" <center> <span style="font-size:10px;font-weight:bold; font-family:Arial,Helvetica,sans-serif;"> <a href="{unsub_uri}" target="_blank">Click here</a> to unsubscribe </span> </center> """).gettext(unsub_uri=self.get_unsubscribe_uri(context)) footer = HTMLParser(footer.encode('utf-8')) handler = self.handler body = handler.get_body() events = (handler.events[:body.start + 1] + header + handler.events[body.start + 1:body.end] + footer + handler.events[body.end:]) # Rewrite link with scheme and autority prefix = self.get_site_root().get_pathto(self) html_data = set_prefix(events, prefix='%s/' % prefix, uri=context.uri) html_data = stl(events=html_data, mode='xhtml') return html_data
class MassSubscribeButton(Button): access = True name = 'mass_subscribe' title = MSG(u'OK')
class Observable(Resource): # Fields cc_list = Followers_Field(multiple=True, indexed=True, title=MSG(u'Followers')) confirm_register_subject = MSG(u"Confirmation required") confirm_register_text = MSG( u'To confirm subscription, click the link:\n\n {uri}\n') confirm_unregister_subject = MSG(u"Confirmation required") confirm_unregister_text = MSG( u'To confirm unsubscription, click the link:\n\n {uri}\n') invitation_subject = MSG(u'Invitation') invitation_text = MSG( u'To accept the invitation, click the link\n\n {uri}\n') def get_message(self, context, language=None): """This function must return the tuple (subject, body) """ # Subject subject = MSG(u'[{title}] has been modified') subject = subject.gettext(title=self.get_title(), language=language) # Body message = MSG(u'DO NOT REPLY TO THIS EMAIL. To view modifications ' u'please visit:\n{resource_uri}') uri = context.get_link(self) uri = str(context.uri.resolve(uri)) uri += '/;commit_log' body = message.gettext(resource_uri=uri, language=language) # And return return subject, body def get_subscribed_users(self, skip_unconfirmed=True): cc_list = self.get_property('cc_list') if not cc_list: return [] users = [] for cc in cc_list: # case 1: subscribed user or unsubscription pending user status = cc.get_parameter('status') if status in (None, 'U'): users.append(cc.value) continue # other if skip_unconfirmed is False and status == 'S': users.append(cc.value) return users def is_subscribed(self, username, skip_unconfirmed=True): return username in self.get_subscribed_users(skip_unconfirmed) def is_confirmed(self, username): for cc in self.get_property('cc_list'): if cc.value == username: status = cc.get_parameter('status') return status is None return False def is_subscription_allowed(self, username): return True def get_register_key(self, username, status='S'): for cc in self.get_property('cc_list'): if cc.value == username and cc.get_parameter('status') == status: return cc.get_parameter('key') return None def set_register_key(self, username, unregister=False): cc_list = self.get_property('cc_list') status = 'U' if unregister is True else 'S' # Find existing key for cc in cc_list: key = cc.get_parameter('key') if (cc.value == username and cc.get_parameter('status') == status and key is not None): # Reuse found key return key # Generate key key = generate_password(30) # Filter out username cc_list = [cc for cc in cc_list if cc.value != username] # Create new dict to force metadata commit cc_list.append(Property(username, status=status, key=key)) self.set_property('cc_list', cc_list) return key def reset_register_key(self, username): cc_list = self.get_property('cc_list') # Filter out username cc_list = [cc for cc in cc_list if cc.value != username] # Create new dict to force metadata commit cc_list.append(Property(username)) self.set_property('cc_list', cc_list) def subscribe_user(self, email=None, user=None): root = self.get_resource('/') # Get the user if user is None: if email is None: raise ValueError, "email or user are mandatory" user = root.get_user_from_login(email) # Create it if needed if user is None: user = root.make_user(email, password=None) # Mark it as new key = generate_password(30) user.set_property('user_state', 'pending', key=key) # Add to subscribers list self.reset_register_key(user.name) return user def unsubscribe_user(self, username): cc_list = self.get_property('cc_list') # Filter out username cc_list = [cc for cc in cc_list if cc.value != username] self.set_property('cc_list', cc_list) def after_register(self, username): pass def after_unregister(self, username): pass def send_confirm_register(self, user, context, unregister=False): username = user.name if unregister is False: key = self.set_register_key(username) view = ';confirm_register' subject = self.confirm_register_subject text = self.confirm_register_text else: key = self.set_register_key(username, unregister=True) view = ';confirm_unregister' subject = self.confirm_unregister_subject text = self.confirm_unregister_text # Build the confirmation link confirm_url = context.uri.resolve(view) email = user.get_value('email') confirm_url.query = {'key': key, 'email': email} subject = subject.gettext() text = text.gettext(uri=confirm_url) context.root.send_email(email, subject, text=text) def notify_subscribers(self, context): # 1. Check the resource has been modified # XXX This test is broken now comments are stored as separate objects # if not context.database.is_changed(self): # return # 2. Get list of subscribed users users = self.get_subscribed_users() if not users: return # 3. Build the message for each language root = context.root website_languages = root.get_value('website_languages') default_language = root.get_default_language() messages_dict = {} for language in website_languages: messages_dict[language] = self.get_message(context, language) # 4. Send the message auth_user = context.user.name if context.user else None for username in users: if username == auth_user: continue # Not confirmed yet if self.get_register_key(username) is not None: continue user = root.get_user(username) if user and user.get_value('user_state') == 'active': mail = user.get_value('email') language = user.get_value('user_language') if language not in website_languages: language = default_language subject, body = messages_dict[language] root.send_email(mail, subject, text=body) ####################################################################### # UI ####################################################################### subscribe = SubscribeForm confirm_register = ConfirmSubscription confirm_unregister = ConfirmUnsubscription accept_invitation = AcceptInvitation
class SubscribeForm(CompositeView): access = 'is_allowed_to_view' title = MSG(u'Subscriptions') subviews = [RegisterForm, ManageForm, MassSubscriptionForm]
def get_namespace(self, resource, context): path = context.query['path'] if path is None: path = Path('.') if path.startswith_slash: path.startswith_slash = False # Namespace: the location base = '/%s/;browse_tests' % context.site_root.get_pathto(resource) link = base + '?path=%s' location = [{'name': MSG(u'Test Suite'), 'link': link % '.'}] for i, name in enumerate(path): p = path[:i+1] try: test_suite.get_handler(p) except LookupError: location.append({'name': name, 'link': None}) body = MSG(u'The "{path}" resource has not been found') body = body.gettext(path=p) return {'location': location, 'body': body} else: location.append({'name': name, 'link': link % p}) # Get the handler handler = test_suite.get_handler(path) # (1) View PO file root = context.root if isinstance(handler, POFile): template = root.get_resource('/ui/odf-i18n/view_po.xml') units = handler.get_units() msgs = [ {'id': x.source, 'str': x.target} for x in units ] namespace = {'messages': msgs} body = stl(template, namespace) return {'location': location, 'body': body} # Load setup file if handler.has_handler('setup.conf'): setup = handler.get_handler('setup.conf', cls=ConfigFile) else: setup = None # (2) Browse Folders children = handler.get_handler_names() children.sort() a_handler = handler.get_handler(children[0]) if isinstance(a_handler, Folder): files = [] for child in children: child_handler = handler.get_handler(child) number = 0 for x in test_suite.database.fs.traverse(child_handler.key): if x.endswith('.po'): number += 1 files.append({'child_name': child, 'to_child': link % ('%s/%s' % (path, child)), 'number': number}) namespace = {'content': files} template = root.get_resource('/ui/odf-i18n/browse_folder.xml') body = stl(template, namespace) return {'location': location, 'body': body} # (3) Test Folder if setup is None: title = description = reference = url_reference = None else: title = setup.get_value('title') description = setup.get_value('description') reference = setup.get_value('reference') url_reference = setup.get_value('url_reference') # Format the description (may contain XML) description = XMLParser(description) files = [] for child in children: if child != 'setup.conf': child_path = '%s/%s' % (path, child) view = (link % child_path) if child.endswith('.po') else None files.append({ 'child_name': child, 'view': view, 'to_child': ';download?path=%s' % child_path}) template = root.get_resource('/ui/odf-i18n/browse_test.xml') namespace = { 'title': title, 'description': description, 'reference': reference, 'url_reference': url_reference, 'content': files} body = stl(template, namespace) return {'location': location, 'body': body}
def get_namespace(self, resource, context, query=None): """This utility method builds a namespace suitable to use to produce an HTML form. Its input data is a dictionnary that defines the form variables to consider: {'toto': Unicode(mandatory=True, multiple=False, default=u'toto'), 'tata': Unicode(mandatory=True, multiple=False, default=u'tata')} Every element specifies the datatype of the field. The output is like: {<field name>: {'value': <field value>, 'class': <CSS class>} ...} """ # Figure out whether the form has been submit or not (FIXME This # heuristic is not reliable) schema = self.get_schema(resource, context) submit = (context.method == 'POST') # Build the namespace namespace = {} for name in schema: datatype = schema[name] is_readonly = getattr(datatype, 'readonly', False) is_multilingual = getattr(datatype, 'multilingual', False) error = None if submit and not is_readonly: try: value = context.get_form_value(name, type=datatype) except FormError, err: if err.missing: error = MSG(u'This field is required.') else: error = MSG(u'Invalid value.') if issubclass(datatype, Enumerate): value = datatype.get_namespace(None) else: generic_datatype = String(multilingual=is_multilingual) value = context.get_form_value(name, type=generic_datatype) else: if issubclass(datatype, Enumerate): value = datatype.get_namespace(value) elif is_multilingual: for language in value: value[language] = datatype.encode(value[language]) else: value = datatype.encode(value) else: try: value = self.get_value(resource, context, name, datatype) except FormError, err: if err.missing: error = MSG(u'This field is required.') else: error = MSG(u'Invalid value.') if issubclass(datatype, Enumerate): value = datatype.get_namespace(None) else: value = None else:
class User(Folder): class_id = 'user' class_version = '20081217' class_title = MSG(u'User') class_icon16 = '/ui/ikaaro/icons/16x16/user.png' class_icon48 = '/ui/ikaaro/icons/48x48/user.png' class_views = [ 'profile', 'edit_account', 'edit_preferences', 'edit_password', 'edit_groups' ] # Fields firstname = Firstname_Field() lastname = Lastname_Field() email = UserEmail_Field() password = UserPassword_Field() avatar = File_Field(title=MSG(u'Avatar')) user_language = Char_Field() user_timezone = Char_Field() user_state = UserState_Field() groups = UserGroups_Field() username = Char_Field(indexed=True, stored=True) # Backwards compatibility # Remove some fields title = None description = None subject = None text = None ######################################################################## # Indexing ######################################################################## def get_catalog_values(self): proxy = super(User, self) values = proxy.get_catalog_values() # email domain email = self.get_value('email') if email and '@' in email: values['email_domain'] = email.split('@', 1)[1] # username (overrides default) values['username'] = self.get_login_name() # groups values['groups'] = self.get_value('groups') # Ok return values ######################################################################## # API / Authentication ######################################################################## def get_user_id(self): # Used by itools.web return str(self.name) def get_password(self): password = self.get_property('password') return password[-1] if password else None def get_auth_token(self): # Used by itools.web password = self.get_password() return password.value if password else None def authenticate(self, password): my_password = self.get_password() if my_password is None: return False algo = my_password.get_parameter('algo', 'sha1') salt = my_password.get_parameter('salt', '') password_hashed, salt = get_secure_hash(password, algo, salt) return password_hashed == my_password.value def _login(self, password, context): # We call this method '_login' to avoid a name clash with the login # view. if not self.authenticate(password): error = MSG_LOGIN_WRONG_NAME_OR_PASSWORD elif self.get_value('user_state') == 'inactive': error = ERROR( u'Your account has been canceled, contact the administrator ' u' if you want to get access again.') else: error = None context.login(self) # To activate this feature set the lastlog field lastlog = self.get_field('lastlog') if lastlog: success = error is None self.set_value('lastlog', context.timestamp, success=success) # Ok return error def update_pending_key(self): state = self.get_property('user_state') if state.value == 'pending': # TODO Implement expiration return state.get_parameter('key') key = generate_password(30) self.set_value('user_state', 'pending', key=key) return key ######################################################################## # API ######################################################################## def get_owner(self): return str(self.abspath) def get_title(self, language=None): firstname = self.get_value('firstname') lastname = self.get_value('lastname') if firstname: if lastname: return '%s %s' % (firstname, lastname) return firstname if lastname: return lastname return self.get_login_name().decode('utf-8') login_name_property = 'email' def get_login_name(self): return self.get_value(self.login_name_property) def get_timezone(self): return self.get_value('user_timezone') ####################################################################### # Views ####################################################################### resend_confirmation = User_ResendConfirmation() confirm_registration = User_ConfirmRegistration() change_password_forgotten = User_ChangePasswordForgotten() profile = User_Profile() edit_account = User_EditAccount() edit_preferences = User_EditPreferences() edit_password = User_EditPassword() edit_groups = AutoEdit(access='is_admin', fields=['groups'], title=MSG(u'Edit groups'))
class Payment_Widget(Widget): total_price = {'with_tax': decimal('0'), 'without_tax': decimal('0')} title1 = None title2 = MSG(u'Please choose a payment mode') template = list( XMLParser( """ <h2 stl:if="title1">${title1}</h2> <input type="text" name="amount" value="${total_price_with_tax}" size="6"/> € <h2>${title2}</h2> <table cellpadding="5px" cellspacing="0"> <tr stl:repeat="payment payments" stl:if="payment/enabled"> <td valign="top"> <input type="radio" name="payment" checked="${payment/selected}" id="payment-${payment/name}" value="${payment/name}"/> </td> <td valign="top"> ${payment/value}<br/><br/> <img stl:if="payment/logo" src="${payment/logo}/;download"/> </td> <td style="width:400px;vertical-align:top;"> ${payment/description} </td> </tr> </table> """, stl_namespaces)) def get_namespace(self, datatype, value): context = get_context() total_price = self.total_price namespace = { 'payments': [], 'title1': self.title1, 'title2': self.title2, 'total_price_with_tax': total_price['with_tax'] or None, 'total_price': total_price } payments = get_shop(context.resource).get_resource('payments') for mode in payments.search_resources(cls=PaymentWay): logo = mode.get_property('logo') if logo: logo = mode.get_resource(logo, soft=True) shipping_groups = mode.get_property('only_this_groups') user_group = context.user.get_property('user_group') if len(shipping_groups) > 0 and user_group not in shipping_groups: continue if mode.is_enabled(context) is False: continue namespace['payments'].append({ 'name': mode.name, 'value': mode.get_title(), 'description': mode.get_payment_way_description(context, total_price), 'logo': str(context.resource.get_pathto(logo)) if logo else None, 'enabled': True, 'selected': None }) # Select first mode by default if namespace['payments'] != []: namespace['payments'][0]['selected'] = True return namespace
class OrderDownButton(BrowseButton): access = 'is_allowed_to_edit' name = 'order_down' title = MSG(u'Order down')
def on_query_error_default(self, resource, context): message = MSG(u'The query could not be processed.').gettext() return message.encode('utf-8')
class AutoAdd(AutoForm): access = 'is_allowed_to_add' actions = [Button(access=True, css='button-ok', title=MSG(u'Add'))] action_goto = None goto_view = None goto_parent_view = None # DEPRECATED -> use action_goto msg_new_resource = messages.MSG_NEW_RESOURCE # Fields fields = [] def get_fields(self): cls = self._resource_class for name in self.fields: field = self.get_field(name) if not is_prototype(field, Field): field = cls.get_field(name) if not field: continue # Access control if field.access('write', cls): yield name def get_field(self, name): cls = self._resource_class field = getattr(self, name, None) if field is None or not is_prototype(field, Field): field = cls.get_field(name) return field ####################################################################### # GET ####################################################################### @proto_lazy_property def _resource_class(self): context = self.context class_id = context.query['type'] if not class_id: return None return context.database.get_resource_class(class_id) def get_title(self, context): if self.title is not None: return self.title cls = self._resource_class if cls: class_title = cls.class_title.gettext() title = MSG(u'Add {class_title}') return title.gettext(class_title=class_title) return MSG(u'Add resource').gettext() def _get_datatype(self, resource, context, name): cls = self._resource_class field = self.get_field(name) field = field(resource=cls) # bind return field.get_datatype() def get_query_schema(self): context = get_context() resource = context.resource schema = self.get_schema(resource, context) for name, datatype in schema.items(): if getattr(datatype, 'mandatory', False) is True: schema[name] = datatype(mandatory=False) schema['type'] = String return schema def get_schema(self, resource, context): schema = { 'cls_description': Unicode, 'referrer': URI} for name in self.get_fields(): datatype = self._get_datatype(resource, context, name) if datatype is None: continue # Special case: datetime elif issubclass(datatype, DateTime): schema['%s_time' % name] = Time # Special case: birthdate elif issubclass(datatype, BirthDate): schema['%s_day' % name] = Days schema['%s_month' % name] = Months schema['%s_year' % name] = Years # Standard case schema[name] = datatype return schema def _get_widget(self, resource, context, name): field = self.get_field(name) return field.get_widget(name) def get_widgets(self, resource, context): widgets = [ ReadOnlyWidget('cls_description'), HiddenWidget('referrer')] for name in self.get_fields(): widget = self._get_widget(resource, context, name) widgets.append(widget) return widgets def get_value(self, resource, context, name, datatype): if name == 'cls_description': # View cls_description value = getattr(self, name, None) if value is not None: return value.gettext() if value else u'' # Resource cls_description cls = self._resource_class value = cls.class_description return value.gettext() if value else u'' elif name == 'referrer': referrer = context.query.get('referrer') return referrer or context.get_referrer() value = context.query.get(name) if value is None: proxy = super(AutoAdd, self) return proxy.get_value(resource, context, name, datatype) if getattr(datatype, 'multilingual', False): for language in resource.get_edit_languages(context): value.setdefault(language, u'') return value ####################################################################### # POST ####################################################################### def get_container(self, resource, context, form): # Container container = resource path = str(container.abspath) # Access control class_id = context.query['type'] root = context.root if not root.has_permission(context.user, 'add', container, class_id): path = '/' if path == '.' else '/%s/' % path msg = ERROR(u'Adding resources to {path} is not allowed.') raise FormError, msg.gettext(path=path) # Ok return container automatic_resource_name = False def get_new_resource_name(self, form): if self.automatic_resource_name: return form['container'].make_resource_name() # If the name is not explicitly given, use the title name = form.get('name', '').strip() if name: return name context = get_context() lang = self.resource.get_edit_languages(context)[0] return form['title'][lang] def _get_form(self, resource, context): form = super(AutoAdd, self)._get_form(resource, context) # 1. The container container = self.get_container(resource, context, form) form['container'] = container # 2. The name name = self.get_new_resource_name(form) if not name: raise FormError, messages.MSG_NAME_MISSING try: name = checkid(name) except UnicodeEncodeError: name = None if name is None: raise FormError, messages.MSG_BAD_NAME # Check the name is free if container.get_resource(name, soft=True) is not None: raise FormError, messages.MSG_NAME_CLASH form['name'] = name # Ok return form def set_value(self, resource, context, name, form): """Return True if an error occurs otherwise False. If an error occurs, the context.message must be an ERROR instance. """ if name.endswith(('_time', '_year', '_day', '_month')): return False if resource.get_field(name) is None: return False value = form[name] if type(value) is dict: for language, data in value.iteritems(): resource.set_value(name, data, language=language) else: resource.set_value(name, value) return False def init_new_resource(self, resource, context, form): child = form['child'] schema = self.get_schema(resource, context) for name in self.get_fields(): datatype = schema.get(name) if not datatype: continue readonly = getattr(datatype, 'readonly', False) persistent = getattr(datatype, 'persistent', True) if persistent and not readonly: if self.set_value(child, context, name, form): return None return child def make_new_resource(self, resource, context, form): """Returns None if there is an error, otherwise return the new resource. """ # 1. Make the resource container = form['container'] cls = self._resource_class form['child'] = container.make_resource(form['name'], cls) # 2. Set properties return self.init_new_resource(resource, context, form) def action(self, resource, context, form): child = self.make_new_resource(resource, context, form) if child is None: return # Ok if self.action_goto: # Get same redirection from x/y/z/;edit and x/y/z and x/y/z/ goto = self.action_goto if goto[0] not in ('/', 'http://'): path = str(context.uri.path) if ('/;' not in path and '/?' not in path and not path.endswith('/')): goto = '%s/%s' % (resource.name, goto) return context.come_back(self.msg_new_resource, goto=goto) # goto_parent_view # Deprecated : To replace by action_goto goto = str(child.abspath) if self.goto_parent_view: goto = './;%s' % self.goto_parent_view # goto_view (from Child) elif self.goto_view: goto = '%s/;%s' % (child.abspath, self.goto_view) return context.come_back(self.msg_new_resource, goto=goto)
class OrderedFolder(Folder): class_title = MSG(u'Ordered Folder') class_views = ['browse_content'] # Fields order = URI_Field(title=MSG(u'Order'), multiple=True) allow_to_unorder_items = False def make_resource(self, name, cls, **kw): resource = super(OrderedFolder, self).make_resource(name, cls, **kw) if self.can_be_ordered(cls): order = self.get_value('order') order = order + [resource.name] self.set_value('order', order) return resource base_classes = None def can_be_ordered(self, cls): if not self.base_classes: return True for base_class in cls.__mro__: if getattr(base_class, 'class_id', None) in self.base_classes: return True return False def get_ordered_values(self): ordered_names = list(self.get_value('order')) # Unordered names if self.allow_to_unorder_items is False: for name in self.get_names(): if name not in ordered_names: ordered_names.append(name) return ordered_names def get_resources_in_order(self): for name in self.get_ordered_values(): yield self.get_resource(name) def order_up(self, ids): order = self.get_ordered_values() order = list(order) for id in ids: index = order.index(id) if index > 0: order.remove(id) order.insert(index - 1, id) # Update the order self.set_value('order', order) def order_down(self, ids): order = self.get_ordered_values() order = list(order) for id in ids: index = order.index(id) order.remove(id) order.insert(index + 1, id) # Update the order self.set_value('order', order) def order_top(self, ids): order = self.get_ordered_values() order = list(order) order = ids + [ id for id in order if id not in ids ] # Update the order self.set_value('order', order) def order_bottom(self, ids): order = self.get_ordered_values() order = list(order) order = [ id for id in order if id not in ids ] + ids # Update the order self.set_value('order', order) def order_add(self, ids): order = self.get_ordered_values() order = list(order) order = [ id for id in order if id not in ids ] + ids # Update the order self.set_value('order', order) def order_remove(self, ids): order = self.get_ordered_values() order = list(order) order = [ id for id in order if id not in ids ] # Update the order self.set_value('order', order) # Views browse_content = OrderedFolder_BrowseContent
class Skin(object): class_title = MSG(u'Skin') class_icon16 = '/ui/ikaaro/icons/16x16/skin.png' class_icon48 = '/ui/ikaaro/icons/48x48/skin.png' # User Interface widgets languages_template = LanguagesTemplate location_template = LocationTemplate def __init__(self, key): self.key = key def get_environment_key(self, server): """ In development environment we can use '/ui_dev/skin/' directory for skin (to avoid build JS/CSS at every changes) """ if server and server.is_development_environment(): build_path = str(server.environment['build_path']) base_path = self.key.split('/ui/')[0] new_key = self.key.replace(base_path, build_path) new_key = new_key.replace('/ui/', '/ui_dev/') if vfs.exists(new_key): # Use local skin return new_key return self.key ####################################################################### # HTML head ####################################################################### def get_template_title(self, context): """Return the title to give to the template document. """ here = context.resource root = context.root root_title = root.get_title() # Choose the template if not root.is_allowed_to_view(context.user, here): return '' elif root is here: template = MSG(u"{view_title} - {root_title}") here_title = None else: template = MSG(u"{here_title} - {view_title} - {root_title}") here_title = here.get_title() # The view view_title = context.view.get_title(context) # Ok return template.gettext(root_title=root_title, here_title=here_title, view_title=view_title) def _get_theme_file(self, context, name): theme = context.database.get_resource('/config/theme') if context.root.is_allowed_to_view(context.user, theme): value = theme.get_value(name) if value: return value return None def get_styles(self, context): # Generic styles = ['/ui/ikaaro/bo.css'] # Skin if isfile('%s/style.css' % self.key): styles.append('%s/style.css' % self.base_path) # View get_styles = getattr(context.view, 'get_styles', None) if get_styles is None: extra = getattr(context.view, 'styles', []) else: extra = get_styles(context) styles.extend(extra) # Database style if self._get_theme_file(context, 'style'): styles.append( '/config/theme/;get_file?name=style&mimetype=text/css') # Ok return styles def get_scripts(self, context): scripts = [ '/ui/ikaaro/jquery.js', '/ui/ikaaro/javascript.js'] # This skin's JavaScript if isfile('%s/javascript.js' % self.key): scripts.append('%s/javascript.js' % self.base_path) # View for script in get_view_scripts(context.view, context): if script not in scripts: scripts.append(script) # Ok return scripts def get_meta_tags(self, context): """Return a list of dict with meta tags to give to the template document. """ here = context.resource root = context.root meta = [] # Set description try: property = here.get_property('description') except ValueError: pass else: if property: meta.append({ 'name': 'description', 'lang': property.get_parameter('lang'), 'content': property.value}) # Set keywords for all languages for language in root.get_value('website_languages'): try: value = here.get_value('subject', language) except ValueError: continue if value is None: continue value = value.strip() if value: meta.append({'name': 'keywords', 'lang': language, 'content': value}) # Search engine optimization seo = root.get_resource('config/seo') for key, meta_name in [ ('google_site_verification', 'google-site-verification'), ('yahoo_site_verification', 'y_key'), ('bing_site_verification', 'msvalidate.01')]: verification_key = seo.get_value(key) if verification_key: meta.append({'name': meta_name, 'lang': None, 'content': verification_key}) # View # meta are defined as a tuple (name, content, language) extra_meta = getattr(context.view, 'meta', []) for (name, content, lang) in extra_meta: meta.append({'name': name, 'content': content, 'lang': lang}) return meta def get_favicon(self, context): # Case 1: from the database favicon = self._get_theme_file(context, 'favicon') if favicon: favicon_href = '/config/theme/;get_file?name=favicon' favicon_type = favicon.get_mimetype() return favicon_href, favicon_type # Case 2: from the skin favicon = '%s/favicon.ico' % self.base_path if context.get_template(favicon): return (favicon, 'image/x-icon') # Case 3: default return ('/ui/ikaaro/favicon.ico', 'image/x-icon') ####################################################################### # Authenticated user ####################################################################### def get_usermenu(self, context): """Return a dict {'name': ..., 'title': ..., 'home': ...} """ here = context.resource base_path = context.get_link(here) # Case 1: Anonymous user = context.user if user is None: return [{'href': '%s/;login' % base_path, 'title': MSG(u'Sign in'), 'id': 'links-menu-login'}] # Case 2: Authenticated usermenu = [ # Home {'href': '/users/%s' % user.name, 'title': user.get_title(), 'id': 'links-menu-profile'}, # Logout {'href': '%s/;logout' % base_path, 'title': MSG(u'Log out'), 'id': 'links-menu-logout'}] # Add content container = here if isinstance(here, Folder) is False: container = here.parent view = container.get_view('new_resource') if context.is_access_allowed(container, view): usermenu.append({ 'href': '%s/;new_resource' % context.get_link(container), 'title': MSG(u'Add content'), 'id': 'links-menu-new'}) # Configuration config = here.get_resource('/config') if context.root.is_allowed_to_view(user, config): usermenu.append({ 'href': '/config', 'title': MSG(u'Configuration'), 'id': 'links-menu-configuration'}) return usermenu ####################################################################### # Body ####################################################################### def _get_page_title(self, context): resource = context.resource view = context.view # Not allowed to view resource root = context.root if not root.is_allowed_to_view(context.user, resource): return '' # Page title try: get_page_title = view.get_page_title except AttributeError: return resource.get_page_title() return get_page_title(resource, context) def get_messages(self, context): """Return the message string of the last action. A list of messages is supported. """ # Text if context.message is not None: messages = context.message elif 'error' in context.uri.query: messages = ERROR(context.get_query_value('error', type=Unicode)) elif 'info' in context.uri.query: messages = INFO(context.get_query_value('info', type=Unicode)) # XXX For backwards compatibility elif 'message' in context.uri.query: messages = INFO(context.get_query_value('message', type=Unicode)) else: return None # Multiple messages: if not isinstance(messages, list): messages = [messages] messages_ns = [] for message in messages: css_class = getattr(message, 'css', 'info') messages_ns.append({'message': message, 'class': css_class}) namespace = {'messages': messages_ns} template = context.get_template('/ui/aruni/message.xml') return stl(template, namespace) def _get_context_menus(self, context): resource = context.resource # Resource for menu in resource.get_context_menus(): menu = menu(resource=resource, context=context) yield menu.render() # View get_context_menus = getattr(context.view, 'get_context_menus', None) if get_context_menus is None: menus = getattr(context.view, 'context_menus', []) else: menus = get_context_menus() for menu in menus: menu = menu(resource=resource, context=context) yield menu.render() def get_footer(self, context): footer = context.root.get_resource('config/footer') return footer.get_html_data() def get_menu_namespace(self, context): menu = context.root.get_resource('config/menu') return menu.get_menu_namespace(context) ####################################################################### # Main ####################################################################### def build_namespace(self, context): # Context menus context_menus = self._get_context_menus(context) context_menus = list(context_menus) # The favicon.ico favicon_href, favicon_type = self.get_favicon(context) # Logo logo = self._get_theme_file(context, 'logo') logo_href = '/config/theme/;get_file?name=logo' if logo else None # The document language languages = context.root.get_value('website_languages') language = context.accept_language.select_language(languages) # The base URI uri = context.uri if uri.path and not context.view_name and not uri.path.endswith_slash: uri = deepcopy(uri) uri.path.endswith_slash = True # Location template location_template = self.location_template if location_template: location_template = location_template(context=context) # Ok return { # HTML head 'language': language, 'title': self.get_template_title(context), 'base_uri': str(uri), 'canonical_uri': context.view.get_canonical_uri(context), 'styles': self.get_styles(context), 'scripts': self.get_scripts(context), 'meta_tags': self.get_meta_tags(context), 'favicon_href': favicon_href, 'favicon_type': favicon_type, # logo 'logo_href': logo_href, # Usermenu (the links at the top) 'usermenu': self.get_usermenu(context), # menu 'menu': self.get_menu_namespace(context), # Location & Views 'location': location_template, 'languages': self.languages_template(context=context), # Body 'page_title': self._get_page_title(context), 'message': self.get_messages(context), 'context_menus': context_menus, # Footer 'footer': self.get_footer(context), } def get_template(self, context): paths = [ '%s/template.xhtml' % self.base_path, '/ui/aruni/template.xhtml'] for path in paths: template = context.get_template(path) if template: return path, template raise ValueError, 'XXX' def template(self, content): context = get_context() # Build the namespace namespace = self.build_namespace(context) namespace['body'] = content # Set the encoding to UTF-8 context.content_type = 'text/html; charset=UTF-8' # Load the template prefix, handler = self.get_template(context) # Build the output s = ['<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"\n' ' "http://www.w3.org/TR/html4/strict.dtd">'] # STL data = stl(handler, namespace, prefix=prefix, mode='html') s.append(data) return ''.join(s)
class MenuItem(OrderedFolder): class_id = 'config-menu-item' class_title = MSG(u'Menu') # Fields path = URI_Field(required=True, title=MSG(u'Path'), widget=PathSelectorWidget) target = Target_Field def get_document_types(self): return [MenuItem] # Views class_views = ['edit', 'browse_content', 'add_menu', 'commit_log'] _fields = ['title', 'path', 'target'] new_instance = AutoAdd(fields=_fields) edit = AutoEdit(fields=_fields) browse_content = MenuItem_Browse add_menu = AddMenu # API def _is_allowed_to_access(self, context, uri): # Get the reference and path ref, path, view = split_reference(uri) # Broken entry if ref is None or path == '': return False # External link if ref.scheme: return True # Broken link resource = self.get_resource(path, soft=True) if resource is None: return False if view: # Remove the first '/;' of the view view = resource.get_view(view[2:], ref.query) if not view: return False # Check ACL return context.is_access_allowed(resource, view) # Check if the user can access to resource views # get_views checks ACLs with by calling is_access_allowed resource_views = list(resource.get_views()) return len(resource_views) > 0 def get_menu_namespace_level(self, context, url, use_first_child=False): menu_abspath = self.abspath here = context.resource here_abspath = here.abspath here_view_name = url[-1] here_abspath_and_view = '%s/%s' % (here_abspath, here_view_name) items = [] for resource in self.get_resources_in_order(): uri = resource.get_value('path') if not self._is_allowed_to_access(context, uri): continue ref, path, view = split_reference(uri) title = resource.get_value('title') target = resource.get_value('target') # Case 1: External link if ref.scheme: items.append({ 'id': 'menu_%s' % resource.name, 'path': str(ref), 'real_path': None, 'title': title, 'description': None, 'in_path': False, 'active': False, 'class': None, 'target': target, 'items': [] }) continue # Case 2: Internal link # Sub level subtabs = resource.get_menu_namespace_level( context, url, use_first_child) resource = self.get_resource(path, soft=True) item_id = 'menu_%s' % resource.name # Use first child by default we use the resource itself resource_path = path # Keep the real path to avoid highlight problems resource_original_path = path # use first child if use_first_child and subtabs: sub_path = subtabs[0]['real_path'] # if the first child is an external link => skip it if sub_path is not None: resource_path = sub_path # Set active, in_path active = in_path = False # add default view if view: resource_method = view[2:] item_id += '_%s' % resource_method else: resource_method = resource.get_default_view_name() resource_abspath_and_view = '%s/;%s' % (resource.abspath, resource_method) if here_abspath_and_view == resource_abspath_and_view: active = True else: # Use the original path for the highlight res_abspath = menu_abspath.resolve2(resource_original_path) common_prefix = here_abspath.get_prefix(res_abspath) # Avoid to always set the root entree 'in_path' # If common prefix equals root abspath set in_path to False # otherwise compare common_prefix and res_abspath if common_prefix != Path('/'): in_path = (common_prefix == res_abspath) # Build the new reference with the right path ref2 = deepcopy(ref) resource = self.get_resource(resource_path) ref2.path = context.get_link(resource) if view: ref2.path += view items.append({ 'id': item_id, 'path': str(ref2), 'real_path': resource.abspath, 'title': title, 'description': None, # FIXME 'in_path': active or in_path, 'active': active, 'class': None, 'target': target, 'items': subtabs }) # Set class x = None for i, item in enumerate(items): if item['active']: x = i break if item['in_path'] and x is None: x = i break if x is not None: items[x]['class'] = 'in-path' if len(items) > 0: # Add class "first" to the first item css = items[0]['class'] or '' items[0]['class'] = css + ' first' # Add class "last" to the last item css = items[-1]['class'] or '' items[-1]['class'] = css + ' last' return items
class AddMenu(NewResource_Local): title = MSG(u'Add item') def get_items(self, resource, context): return tuple(resource.get_document_types())
class SubscribeButton(BrowseButton): access = True name = 'subscribe' title = MSG(u"Subscribe")
def get_namespace(self, resource, context): resource_zones = resource.get_resource('../countries-zones') handler_countries = resource.get_resource('../countries').handler if self.show_inactive: page_title = MSG('Inactives shippings prices') else: page_title = MSG('Shippings price') namespace = { 'zones': [], 'msg_if_no_shipping': resource.get_property('msg_if_no_shipping'), 'page_title': page_title} for zone in resource_zones.handler.get_records_in_order(): countries = [] for country in handler_countries.search(zone=str(zone.id)): title = handler_countries.get_record_value(country, 'title') if handler_countries.get_record_value(country, 'enabled') is False: continue countries.append(title) if len(countries) == 0: continue zone_title = resource_zones.handler.get_record_value(zone, 'title') tarifications = [] for tarification in resource.get_resources(): # We show only active or inactives modes, depending on config if tarification.get_property('enabled') is self.show_inactive: continue mode = tarification.get_property('mode') unit = MSG(u'Kg') if mode == 'weight' else MSG(u'Unit') prices = [] min = old_price = 0 prices_resource = tarification.get_resource('prices') prices_handler = prices_resource.handler tarif_edit = context.get_link(prices_resource) tarif_edit += '/?zone=%i&search=' % zone.id records = prices_handler.search(zone=str(zone.id)) records.sort(key=lambda x: prices_handler.get_record_value(x, 'max-%s' % mode)) for price in records: max = prices_handler.get_record_value(price, 'max-%s' % mode) price = prices_handler.get_record_value(price, 'price') prices.append( {'title': '%s to %s %s' % (min, max, unit.gettext()), 'price': price, 'error': price<=old_price}) min = max old_price = price if len(prices) == 0: continue kw = { 'name': tarification.name, 'models': [], 'title': tarification.get_title(), 'img': tarification.get_property('logo'), 'is_free': tarification.get_property('is_free'), 'prices': prices, 'description': tarification.get_property('description'), 'tarif_edit': tarif_edit} models = tarification.get_property('only_this_models') for model_abspath in models: model = context.root.get_resource(model_abspath) kw['models'].append(model.get_title()) tarifications.append(kw) zone_edit = '/shop/countries?zone=%i&search=' % zone.id has_tax = resource_zones.handler.get_record_value(zone, 'has_tax') tax_image = list(bool_to_img(has_tax)) namespace['zones'].append({'title': zone_title, 'countries': countries, 'tarifications': tarifications, 'zone_edit': zone_edit, 'tax_image': tax_image}) return namespace
class ConfigMenu(MenuItem): class_id = 'config-menu' class_title = MSG(u'Configuration Menu') class_description = MSG(u'Edit the global menu.') class_icon48 = 'icons/48x48/menu.png' def init_resource(self, **kw): super(ConfigMenu, self).init_resource(**kw) # Menu order = [] menus = [('/', u'Home'), ('/;contact', u'Contact')] for path, title in menus: name = checkid(title) order.append(name) self.make_resource(name, MenuItem, path=path, title={'en': title}) self.set_property('order', order) # Configuration config_name = 'menu' config_group = 'webmaster' # Views class_views = ['browse_content', 'add_menu', 'edit', 'commit_log'] def get_menu_namespace(self, context, show_first_child=False, src=None): """Return dict with the following structure: {'items': [item_dic01, ..., item_dic0N]} Where the list of items is the first level and item_dic = {'active': True or False, 'class': 'active' or 'in_path' or None, 'description': MSG(u'About Python'), 'id': 'tab_python', 'in_path': True or False, 'items': [item_dic11, ..., item_dic1N] or None, 'name': 'python', 'path': '../python', 'target': '_top' or '_blank' or None, 'title': MSG(u'Python')} "items" contains the first level. Each item_dic contains in turn an 'items' with its children. "class" is a CSS class decorating the item when active on within the current path. "id" is a CSS id to uniquely identify the item, based on the title. "target" is the anchor target (opening a new window or not). Activate "use_first_child" to automatically point to the first child of each item instead of the item itself. """ resource = context.resource url = list(context.uri.path) if not url or url[-1][0] != ';': method = resource.get_default_view_name() url.append(';%s' % method) # Get the menu menu = self if src: menu = self.get_resource(src, soft=True) if menu is None: return [] return menu.get_menu_namespace_level(context, url, show_first_child)
class OrderButton(BrowseButton): access = 'is_allowed_to_edit' name = 'add_to_ordered' title = MSG(u'Add to ordered list')
class Application(Folder): class_id = 'application' class_title = MSG(u"Collection Application") class_description = MSG(u"Create from an OpenDocument Spreadsheet file") class_views = ['view_admin', 'edit', 'edit_ods', 'register', 'view'] # FIXME Configuration obsolete ? allowed_users = 10 # Fields subscription = Subscription_Field(required=True) data = File_Field(title=MSG(u'ODS file'), multilingual=False, required=True) filename = Char_Field() mimetype = Char_Field() def init_resource(self, *args, **kw): proxy = super(Application, self) proxy.init_resource(*args, **kw) # Initial forms answers containers self.make_resource('forms', Forms) def load_ods_file(self, data, context): if self.get_resource('model', soft=True): self.del_resource('model') context.database.save_changes() # Create model container self.make_resource('model', FormModel) model = self.get_resource('model') # Save informations filename, mimetype, body = data self.set_value('data', body) self.set_value('filename', filename) self.set_value('mimetype', mimetype) # Analyse it return model.load_ods_file(context) def get_form(self): # OBSOLETE METHOD FIXME raise NotImplementedError def search_forms(self): query = AndQuery(PhraseQuery('base_classes', 'form'), get_base_path_query(self.abspath)) return get_context().search(query) def get_forms(self): return self.search_forms().get_resources() def get_n_forms(self): return len(self.search_forms()) def get_stats(self): stats = {} stats['registered_users'] = 0 stats['unconfirmed_users'] = 0 stats['empty_forms'] = 0 stats['pending_forms'] = 0 stats['finished_forms'] = 0 users = self.get_resource('/users') for form in self.get_forms(): stats['registered_users'] += 1 user = users.get_resource(form.name, soft=True) if user is not None: if user.get_value('password') is None: stats['unconfirmed_users'] += 1 else: state = form.get_value('form_state') if state == EMPTY: stats['empty_forms'] += 1 elif state == PENDING: stats['pending_forms'] += 1 elif state == FINISHED: stats['finished_forms'] += 1 return stats def get_param_folder(self): return self def get_admin_url(self, context): base_url = context.uri.resolve(self.abspath) return base_url.resolve2(';view') def get_spread_url(self, context, email=None): base_url = context.uri.resolve(self.abspath) spread_url = base_url.resolve2(';login') if email is not None: spread_url.query['username'] = email return spread_url def subscribe_user(self, user): root = self.get_resource('/') username = user.name # Give the role "guests" to see public resources (logo, etc.) if (root.get_user_role(username) is None # Except to top-level admins and not root.is_admin(user, self)): root.set_user_role(username, 'guests') # Add the form if self.get_resource(username, soft=True) is None: self.make_resource(username, Form, title={'en': user.get_title()}, form_state=NOT_REGISTERED) # Views new_instance = Application_NewInstance() edit = Application_Edit() edit_ods = Application_EditODS() view_admin = IconsView() register = Application_Register()
class UnOrderButton(BrowseButton): access = 'is_allowed_to_edit' name = 'remove_from_ordered' title = MSG(u'Remove from ordered list')
class Payments_History_View(SearchForm): """ Shows each payment in history, with a search form. """ title = MSG(u'Payments history') access = 'is_admin' batch_msg1 = MSG(u"There is 1 payment.") batch_msg2 = MSG(u"There are {n} payments.") search_template = '/ui/backoffice/payments/history_view_search.xml' search_schema = {'ref': String, 'user': String, 'state': Boolean} search_widgets = [ TextWidget('ref', title=MSG(u'Reference')), TextWidget('user', title=MSG(u'Customer')), ] def get_search_namespace(self, resource, context): query = context.query namespace = {'widgets': []} for widget in self.search_widgets: value = context.query[widget.name] html = widget.to_html(self.search_schema[widget.name], value) namespace['widgets'].append({'title': widget.title, 'html': html}) return namespace table_columns = [('complete_id', MSG(u'Id')), ('state', u' '), ('ts', MSG(u'Date and Time')), ('user_title', MSG(u'Customer')), ('ref', MSG(u'Ref')), ('amount', MSG(u'Amount')), ('payment_mode', MSG(u'Payment mode')), ('advance_state', MSG(u'State')), ('buttons', None)] buttons_template = """ <a href=";manage_payment?payment_way={way}&id_payment={id}"> <img src="/ui/icons/16x16/view.png"/> </a> """ def get_items(self, resource, context): queries = [] for key in self.search_schema.keys(): if context.query[key]: queries.append(PhraseQuery(key, context.query[key])) return resource.get_payments_items(context, queries=queries) def get_item_value(self, resource, context, item, column): if column == 'buttons': kw = {'id': item['id'], 'way': item['payment_mode']} return XMLParser(self.buttons_template.format(**kw)) if column == 'payment_mode': payment_mode = item['payment_mode'] return PaymentWaysEnumerate.get_value(payment_mode) elif column == 'user_title': return item['user_title'], '/users/%s' % item['username'] elif column == 'state': return item[column] and MSG(u'OK') or '' return item[column] def sort_and_batch(self, resource, context, items): # Sort sort_by = context.query['sort_by'] reverse = context.query['reverse'] if sort_by: items.sort(key=itemgetter(sort_by), reverse=reverse) # Batch start = context.query['batch_start'] size = context.query['batch_size'] return items[start:start + size]
class OrderedFolder_BrowseContent(Folder_BrowseContent): query_schema = Folder_BrowseContent.query_schema.copy() query_schema['sort_by'] = query_schema['sort_by'](default='order') query_schema['reverse'] = query_schema['reverse'](default=False) table_columns = (Folder_BrowseContent.table_columns + [('order', MSG(u'Order'))]) def get_table_actions(self, resource, context): proxy = super(OrderedFolder_BrowseContent, self) buttons = proxy.get_table_actions(resource, context) buttons = list(buttons) buttons += [OrderUpButton, OrderDownButton, OrderTopButton, OrderBottomButton] if resource.allow_to_unorder_items: buttons += [OrderButton, UnOrderButton] return buttons def get_search_query(self, resource, context): proxy = super(OrderedFolder_BrowseContent, self) query = proxy.get_search_query(resource, context) if resource.base_classes: query.append(PhraseQuery('base_classes', resource.base_classes)) return query def get_key_sorted_by_order(self): context = get_context() ordered_names = list(context.resource.get_ordered_values()) nb_ordered_names = len(ordered_names) def key(item): try: return ordered_names.index(item.name) except ValueError: return nb_ordered_names return key def get_item_value(self, resource, context, item, column): if column == 'order': ordered_ids = list(resource.get_ordered_values()) if item.name in ordered_ids: return ordered_ids.index(item.name) + 1 return MSG(u'Not ordered') proxy = super(OrderedFolder_BrowseContent, self) return proxy.get_item_value(resource, context, item, column) ###################################################################### # Actions ###################################################################### def action_remove(self, resource, context, form): # Remove from ordered list resource.order_remove(form['ids']) # Super proxy = super(OrderedFolder_BrowseContent, self) return proxy.action_remove(resource, context, form) def action_order_up(self, resource, context, form): ids = form['ids'] resource.order_up(ids) context.message = INFO(u'Resources ordered up.') def action_order_down(self, resource, context, form): ids = form['ids'] resource.order_down(ids) context.message = INFO(u'Resources ordered down.') def action_order_top(self, resource, context, form): ids = form['ids'] resource.order_top(ids) context.message = INFO(u'Resources ordered on top.') def action_order_bottom(self, resource, context, form): ids = form['ids'] resource.order_bottom(ids) context.message = INFO(u'Resources ordered on bottom.') def action_add_to_ordered(self, resource, context, form): ids = form['ids'] resource.order_add(ids) context.message = INFO(u'Resources ordered on bottom.') def action_remove_from_ordered(self, resource, context, form): ids = form['ids'] resource.order_remove(ids) context.message = INFO(u'Resources unordered.')
options.append({ 'name': quote(i_abspath), 'value': item.get_title(), 'selected': i_abspath in value }) # Si 1 option, elle est sélectionnée if len(options) == 1: options[0]['selected'] = True options.sort(key=itemgetter('value')) return options ########################################################################### # Common widgets to reuse ########################################################################### title_widget = TextWidget('title', title=MSG(u'Title')) description_widget = MultilineWidget('description', title=MSG(u'Description'), rows=8) subject_widget = TextWidget('subject', title=MSG(u'Keywords'), tip=MSG(u'Separated by comma')) timestamp_widget = HiddenWidget('timestamp') file_widget = FileWidget('file', title=MSG(u'Replace file')) editarea_widget = EditAreaWidget('data', title=MSG(u'Body')) # Registry with {datatype: widget, ...} widgets_registry = { Boolean: RadioWidget, Date: DateWidget, DateTime: DatetimeWidget,
class JS(Text): class_id = 'application/x-javascript' class_title = MSG(u'Javascript') class_icon16 = '/ui/ikaaro/icons/16x16/js.png' class_icon48 = '/ui/ikaaro/icons/48x48/js.png'
class RegisterForm(AutoForm): access = 'is_allowed_to_view' title = MSG(u"Subscription") schema = freeze({'email': Email(mandatory=True)}) widgets = freeze([TextWidget('email', title=MSG(u"E-mail Address"))]) query_schema = freeze({'email': Email}) actions = [RegisterButton, UnregisterButton] # Messages msg_user_already_subscribed = MSG_USER_ALREADY_SUBSCRIBED msg_confirmation_sent = MSG_CONFIRMATION_SENT msg_user_subscribed = MSG_USER_SUBSCRIBED msg_user_already_unsubscribed = MSG_USER_ALREADY_UNSUBSCRIBED msg_user_unsubscribed = MSG_USER_UNSUBSCRIBED def get_widgets(self, resource, context): widgets = super(RegisterForm, self).get_widgets(resource, context) if context.user: # E-mail becomes hard coded widgets = list(widgets) email = widgets[0] widgets[0] = ReadOnlyWidget(name=email.name, focus=True, title=email.title) return widgets def get_value(self, resource, context, name, datatype): if name == 'email': if context.user: return context.user.get_value('email') return context.query['email'] proxy = super(RegisterForm, self) return proxy.get_value(self, resource, context, name, datatype) def action_register(self, resource, context, form): root = context.root email = form['email'] existing_user = root.get_user_from_login(email) if existing_user is not None: username = existing_user.name if resource.is_subscribed(username, skip_unconfirmed=False): context.message = self.msg_user_already_subscribed return # Create user anyhow user = resource.subscribe_user(email=email, user=existing_user) if context.user is None: # Anonymous subscription resource.send_confirm_register(user, context) context.message = self.msg_confirmation_sent else: resource.after_register(user.name) context.message = self.msg_user_subscribed def action_unregister(self, resource, context, form): user = context.root.get_user_from_login(form['email']) if user is None: context.message = self.msg_user_already_unsubscribed return username = user.name if not resource.is_subscribed(username, skip_unconfirmed=False): context.message = self.msg_user_already_unsubscribed return if context.user is None: # Anonymous subscription resource.send_confirm_register(user, context, unregister=True) context.message = self.msg_confirmation_sent else: resource.unsubscribe_user(username) resource.after_unregister(username) context.message = self.msg_user_unsubscribed
def get_title(self, context): if self.title is not None: return self.title class_title = self.add_cls.class_title.gettext() title = MSG(u'Add {class_title}') return title.gettext(class_title=class_title)
class ConfirmSubscription(AutoForm): access = 'is_allowed_to_view' title = MSG(u"Subscribe") description = MSG( u'By confirming your subscription to this resource you will' u' receive an email every time this resource is modified.') schema = freeze({ 'key': String(mandatory=True), 'email': Email(mandatory=True) }) widgets = freeze([HiddenWidget('key'), ReadOnlyWidget('email')]) actions = [Button(access=True, title=MSG(u'Confirm subscription'))] key_status = 'S' msg_already = MSG_USER_ALREADY_SUBSCRIBED def get_value(self, resource, context, name, datatype): if name in ('key', 'email'): return context.get_query_value(name) proxy = super(ConfirmSubscription, self) return proxy.get_value(resource, context, name, datatype) def get_username(self, resource, context, key): # 1. Get the user email = context.get_form_value('email') user = context.root.get_user_from_login(email) if user is None: return None, MSG(u'Bad email') # 2. Get the user key username = user.name user_key = resource.get_register_key(username, self.key_status) if user_key is None: return username, self.msg_already # 3. Check the key if user_key != key: return username, MSG_BAD_KEY # 4. Ok return username, None def get_namespace(self, resource, context): key = context.get_form_value('key') username, error = self.get_username(resource, context, key) if error: return context.come_back(error, goto='./') proxy = super(ConfirmSubscription, self) return proxy.get_namespace(resource, context) def action(self, resource, context, form): username, error = self.get_username(resource, context, form['key']) if error: context.message = error return # Ok resource.reset_register_key(username) resource.after_register(username) return context.come_back(MSG_USER_SUBSCRIBED, goto='./')
# # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # Import from standard library from datetime import datetime, timedelta from operator import itemgetter from traceback import print_exc # Import from itools from itools.database import AndQuery, PhraseQuery, RangeQuery from itools.gettext import MSG from itools.web import STLView, ERROR ERROR_MSG = MSG( u'Inconsistent class_id "{class_id}", resource version is {resource_version} but cls_version is {cls_version} ({abspath})' ) def class_version_to_date(version): return datetime.strptime(version[:8], '%Y%m%d').date() def find_versions_to_update(context, force=False): database = context.database cls_errors = [] cls_to_update = [] # Find classes for cls in database.get_resource_classes(): # Class version class_version = class_version_to_date(cls.class_version)
class MassSubscriptionForm(AutoForm): access = 'is_admin' title = MSG(u"Mass Subscription") description = MSG( u"An invitation will be sent to every address typen below, one by" u" line.") schema = freeze({'emails': MultiLinesTokens(mandatory=True)}) widgets = freeze([MultilineWidget( 'emails', focus=False, )]) actions = [MassSubscribeButton, Button] # len(actions) > 1 def get_value(self, resource, context, name, datatype): if name == 'emails': return '' proxy = super(MassSubscriptionForm, self) return proxy.get_value(resource, context, name, datatype) def action_mass_subscribe(self, resource, context, form): root = context.root already = [] unallowed = [] invited = [] invalid = [] subscribed_users = resource.get_subscribed_users() for email in form['emails']: email = email.strip() if not email: continue # Check if email is valid if not Email.is_valid(email): invalid.append(email) continue # Checks user = root.get_user_from_login(email) if user: if user.name in subscribed_users: already.append(user) continue if not resource.is_subscription_allowed(user.name): unallowed.append(user) continue # Subscribe user = resource.subscribe_user(email=email, user=user) key = resource.set_register_key(user.name) # Send invitation subject = resource.invitation_subject.gettext() confirm_url = context.uri.resolve(';accept_invitation') confirm_url.query = {'key': key, 'email': email} text = resource.invitation_text.gettext(uri=confirm_url) root.send_email(email, subject, text=text) invited.append(user) # Ok context.message = [] add_subscribed_message(MSG_ALREADY, already, context) add_subscribed_message(MSG_INVALID, invalid, context, users_is_resources=False) add_subscribed_message(MSG_INVITED, invited, context) add_subscribed_message(MSG_UNALLOWED, unallowed, context)
def gettext(self, language=None): # Gettext calling was defered return MSG.gettext(self, language=language, **self.kw)
class UnsubscribeButton(SubscribeButton): name = 'unsubscribe' title = MSG(u"Unsubscribe")
class Lastname_Field(Text_Field): title = MSG(u'Last Name') multilingual = False indexed = True stored = True
class OrderTopButton(BrowseButton): access = 'is_allowed_to_edit' name = 'order_top' title = MSG(u'Order top')
def find_versions_to_update(context, force=False): database = context.database cls_errors = [] cls_to_update = [] # Find classes for cls in database.get_resource_classes(): # Class version class_version = class_version_to_date(cls.class_version) class_version_tomorrow = class_version + timedelta(days=1) # Search for code older than the instance query = AndQuery( PhraseQuery('format', cls.class_id), RangeQuery('class_version', class_version_tomorrow, None)) search = database.search(query) if search: resource = search.get_resources().next() kw = { 'class_id': resource.class_id, 'class_title': resource.class_title, 'abspath': str(resource.abspath), 'resource_version': resource.metadata.version, 'cls_version': resource.class_version } cls_errors.append(kw) if force is False: break # Find out the versions to upgrade classes_and_versions = [] for sub_cls in cls.mro(): for name in sub_cls.__dict__.keys(): if not name.startswith('update_'): continue kk, version = name.split('_', 1) if len(version) != 8: continue if not version.isdigit(): continue class_version = class_version_to_date(version) if class_version > class_version_to_date(cls.class_version): msg = "'{0}' class_version is bad ({1} > {2})" msg = msg.format(sub_cls.class_id, version, cls.class_version) raise ValueError(msg) class_version_yesterday = class_version - timedelta(days=1) query = AndQuery( PhraseQuery('format', cls.class_id), RangeQuery('class_version', None, class_version_yesterday)) search = database.search(query) if not search: continue class_and_version = (cls.class_id, version) if class_and_version in classes_and_versions: # Overriden update method in sub classes # So update method should be done one time continue classes_and_versions.append(class_and_version) update_title_name = 'update_{0}_title'.format(version) update_title = getattr(cls, update_title_name, MSG(u'Unknow')) if isinstance(update_title, MSG): update_title = update_title.gettext() kw = { 'class_id': cls.class_id, 'class_title': cls.class_title, 'class_version': version, 'class_version_date': class_version, 'class_version_pretty': context.format_date(class_version), 'update_title': update_title, 'nb_resources': len(search) } cls_to_update.append(kw) # Sort cls_to_update.sort(key=itemgetter('class_version_date')) # Ok return {'cls_to_update': cls_to_update, 'cls_errors': cls_errors}
def say_hello(): message = MSG(u'Hello World') print message.gettext()
class OrderBottomButton(BrowseButton): access = 'is_allowed_to_edit' name = 'order_bottom' title = MSG(u'Order bottom')