def reset(self): self.properties = {} self.components = {} # A Catalog "in memory" fields = { '__uid__': String(is_key_field=True, is_stored=True, is_indexed=True), 'type': String(is_indexed=True), 'dtstart': DateTime(is_stored=True, is_indexed=True), 'dtend': DateTime(is_stored=True, is_indexed=True) } self.catalog = make_catalog(None, fields)
class End_Field(Datetime_Field): datatype = DateTime(time_is_required=False) stored = True title = MSG(u'End') required = True widget = Datetime_Field.widget(value_time_default=time(10, 0))
class ShopModule_Edit(DBResource_Edit): title = MSG(u'Edit') access = 'is_allowed_to_edit' base_schema = {'title': Unicode, 'timestamp': DateTime(readonly=True)} base_widgets = [timestamp_widget, title_widget] def get_schema(self, resource, context): return merge_dicts(self.base_schema, resource.item_schema) def get_widgets(self, resource, context): return self.base_widgets + resource.item_widgets def action(self, resource, context, form): # Check edit conflict self.check_edit_conflict(resource, context, form) if context.edit_conflict: return # Save changes title = form['title'] language = resource.get_content_language(context) resource.set_property('title', title, language=language) for key, datatype in resource.item_schema.items(): if getattr(datatype, 'multilingual', False) is True: resource.set_property(key, form[key], language) else: resource.set_property(key, form[key]) # Ok context.message = messages.MSG_CHANGES_SAVED
def save_state(self): if self.incremental_save is False: File.save_state(self) self.incremental_save = True return # Incremental Save file = self.safe_open(self.key, 'a') try: # Added properties records for seq in self.added_properties: version = self.properties[seq] version = self._version_to_str(-1, seq, version) file.write(version) self.added_properties = [] # Added records for id, seq in self.added_records: version = self.records[id][seq] version = self._version_to_str(id, seq, version) file.write(version) self.added_records = [] # Removed records for id, ts in self.removed_records: file.write('id:%s/DELETED\n' % id) file.write('ts:%s\n' % DateTime.encode(ts)) file.write('\n') self.removed_records = [] finally: file.close() # Update the timestamp self.timestamp = self.database.fs.get_mtime(self.key) self.dirty = None
def get_datatype(self, name): # Table schema if name == 'ts': return DateTime(multiple=False) if name in self.schema: return self.schema[name] return String(multiple=True)
def get_record_datatype(self, name): # Record schema if name == 'ts': return DateTime(multiple=False) if name in self.record_properties: return self.record_properties[name] # FIXME Probably we should raise an exception here return String(multiple=True)
def _get_schema(self, resource, context): schema = {'timestamp': DateTime(readonly=True), 'referrer': URI} # Add schema from the resource for name in self.get_fields(): datatype = self._get_datatype(resource, context, name) # Special case: datetime if 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
class Password_Field(Metadata_Field): datatype = Password_Datatype() parameters_schema = { 'algo': String(), 'salt': String(), 'date': DateTime() } widget = PasswordWidget def access(self, mode, resource): return mode == 'write' def set_value(self, resource, name, value, language=None, **kw): if value is not None: algo = 'sha256' value, salt = get_secure_hash(value, algo) kw['algo'] = algo kw['salt'] = salt kw['date'] = get_context().timestamp # super proxy = super(Password_Field, self) return proxy.set_value(resource, name, value, language, **kw)
class_id = 'orders' class_title = MSG(u'Orders') class_views = ['view'] # 'export'] class_version = '20091127' # Views view = OrdersView() def get_document_types(self): return [Order] ############################# # Export ############################# export = Export( export_resource=Order, access='is_allowed_to_edit', file_columns=['name', 'state', 'total_price', 'creation_datetime']) # Register catalog fields register_field('customer_id', String(is_indexed=True)) register_field('is_payed', Boolean(is_stored=True)) register_field('creation_datetime', DateTime(is_stored=True, is_indexed=True)) # Register resources register_resource_class(Order) register_resource_class(Orders) register_resource_class(OrdersProducts)
def render_namespace(items, times, with_new_url, current_date): nitems, iitems = len(items), 0 # blocks = [(nrows, ncols), ..] blocks, table, state = [], [], [] nrows = 0 for itime, time in enumerate(times[:-1]): cells = [] tt_end = times[itime + 1] # add the busy cells for rowspan in state: if rowspan > 0: cells.append(Cell(Cell.busy, colspan=1)) else: cells.append(Cell(Cell.free, start=time, end=tt_end)) # add new cells icell = 0 while iitems < nitems: start, end, item, cal = items[iitems] if start != time: break # look for a free cell while icell < len(cells): if cells[icell].type == Cell.free: break icell = icell + 1 # add cell rowspan = max(times.index(end) - times.index(start), 1) cell = Cell(Cell.new, item, start, end, rowspan, 1, cal) if icell >= len(cells): state.append(rowspan) cells.append(cell) else: state[icell] = rowspan cells[icell] = cell # next item iitems = iitems + 1 ncols = len(cells) # empty row? if ncols == 0: cells.append(Cell(Cell.free, start=time, end=tt_end)) # next row, reduce the current rowspans nrows = nrows + 1 for i in range(ncols): rowspan = state[i] if rowspan > 0: state[i] = rowspan - 1 # a new block? state_0_count = state.count(0) if state_0_count == ncols: state = [] blocks.append((nrows, ncols)) nrows = 0 # Add current row cells to table table.append(cells) # calculate the number of columns total_ncols = mcm(map(lambda x: x[1], blocks)) # add colspan to each row and fill the incomplete rows with free cells base = 0 for nrows, ncols in blocks: if ncols != 0: b_colspan = total_ncols/ncols else: b_colspan = total_ncols for irow in range(nrows): cells = table[base + irow] for cell in cells: cell.colspan = b_colspan for icol in range(len(cells), ncols): cells.append(Cell(Cell.free, start=times[base+irow], colspan=b_colspan)) table[base + irow] = cells base = base + nrows #################################################################### # FOR EACH ROW for index, row_cells in enumerate(table): new_cells, free_cells = [], [] for cell in row_cells: if cell.type == Cell.new: new_cells.append(cell) elif cell.type == Cell.free: free_cells.append(cell) if free_cells == row_cells: continue # Try to extend colspan of new cells # (checking the cells below in rowspan) for cell in new_cells: colspan = cell.colspan if colspan <= 1: continue # FOR EACH LINE BELOW, USED FOR CELL TO EXTEND new_extended = [] for n in range(cell.rowspan): if colspan <= 1: break # GET CURRENT TESTED ROW row_index = index + n icells = table[row_index] # REDUCE max colspan if necessary ilen = len(icells) if ilen < colspan: colspan = ilen # TRY TO EXTEND i = row_cells.index(cell) k = 0 while k < colspan and (i+k) < ilen: if icells[i+k].type != Cell.free: colspan = k break k = k + 1 new_extended.append((row_index, i)) # Update cells below if colspan > 1: for row_index, i in new_extended: c_colspan = colspan c_row = table[row_index] l_c_row = len(c_row) for col in range(i+1, i+colspan): if col < l_c_row: current_cell = c_row[col] free_cells.remove(current_cell) current_cell.type = Cell.busy c_colspan = c_colspan + current_cell.colspan current_cell[i].colspan = c_colspan # Collapse free cells l_row_cells = len(row_cells) for icell, cell in enumerate(row_cells): if cell.type == Cell.free: jcelltest = icell + 1 start = cell.start while jcelltest < l_row_cells: if jcelltest != icell: celltest = row_cells[jcelltest] if celltest.type != Cell.free: break if celltest.start == start: celltest.type = Cell.busy cell.colspan = cell.colspan + celltest.colspan jcelltest = jcelltest + 1 ###################################################################### # render_namespace ###################################################################### url = ';new_event' ns_rows = [] for cells in table: ns_cells = [] for cell in cells: # Don't add busy cells as they don't appear in template if cell.type == Cell.busy: continue ns_cell = cell.to_dict() if with_new_url is True: # Add start time to url used to add events query = [] if cell.start is not None: x = datetime.combine(current_date, cell.start) query.append('dtstart=%s' % DateTime.encode(x)) if cell.end is not None: x = datetime.combine(current_date, cell.end) query.append('dtend=%s' % DateTime.encode(x)) ns_cell['newurl'] = '%s?%s' % (url, '&'.join(query)) ns_cells.append(ns_cell) ns_rows.append({'cells': ns_cells}) return ns_rows, total_ncols
def get_date(self): try: return DateTime.decode(self.value) except Exception: return datetime.today()
class Datetime_Field(Metadata_Field): datatype = DateTime() widget = DatetimeWidget rest_type = 'datetime'
class ShopUser(User, DynamicFolder): class_version = '20100720' class_id = 'user' # Views manage = ShopUser_Manage() profile = ShopUser_Profile() public_profile = ShopUser_PublicProfile() edit_account = ShopUser_EditAccount() edit_group = ShopUser_EditGroup() viewbox = ShopUser_Viewbox() # Confirm registration confirm_registration = Shop_UserConfirmRegistration() send_confirmation_view = Shop_UserSendConfirmation() # Orders views orders_view = ShopUser_OrdersView() order_view = ShopUser_OrderView() # Addresses views addresses_book = Addresses_Book(access='is_allowed_to_edit') edit_address = ShopUser_EditAddress() add_address = ShopUser_AddAddress() add_image = CurrentFolder_AddImage() # Base schema / widgets base_schema = merge_dicts( User.get_metadata_schema(), lastname=Unicode(title=MSG(u'Lastname')), firstname=Unicode(title=MSG(u'Firstname')), email=Email(title=MSG(u'Email')), ctime=DateTime(title=MSG(u'Register date')), last_time=DateTime(title=MSG(u'Last connection')), gender=Civilite(title=MSG(u"Civility")), phone1=String(mandatory=True, title=MSG(u'Phone1')), phone2=String(title=MSG(u'Phone2'))) base_widgets = [ TextWidget('email', title=MSG(u"Email")), SelectRadio('gender', title=MSG(u"Civility"), has_empty_option=False), TextWidget('lastname', title=MSG(u"Lastname")), TextWidget('firstname', title=MSG(u"Firstname")), TextWidget('phone1', title=MSG(u"Phone number")), TextWidget('phone2', title=MSG(u"Mobile")) ] base_items = [{ 'name': 'account', 'title': MSG(u"Edit my account"), 'href': ';edit_account', 'img': '/ui/icons/48x48/card.png' }, { 'name': 'preferences', 'title': MSG(u'Edit my preferences'), 'href': ';edit_preferences', 'img': '/ui/icons/48x48/preferences.png' }, { 'name': 'password', 'title': MSG(u'Edit my password'), 'href': ';edit_password', 'img': '/ui/icons/48x48/lock.png' }, { 'name': 'addresses', 'title': MSG(u'My addresses book'), 'href': ';addresses_book', 'img': '/ui/icons/48x48/tasks.png' }, { 'name': 'orders', 'title': MSG(u'Orders history'), 'href': ';orders_view', 'img': '/ui/shop/images/bag_green.png' }] @staticmethod def _make_resource(cls, folder, name, *args, **kw): ctime = datetime.now() User._make_resource(cls, folder, name, ctime=ctime, *args, **kw) base_class_views = [ 'profile', 'addresses_book', 'edit_account', 'orders_view', 'edit_preferences', 'edit_password' ] @property def class_views(self): context = get_context() # Back-Office hostname = context.uri.authority if hostname[:6] == 'admin.': return ['manage'] + self.base_class_views if hostname[:6] == 'www.aw': # XXX Add a configurator for public profil return ['public_profile'] + self.base_class_views return self.base_class_views @classmethod def get_metadata_schema(cls): return merge_dicts(DynamicFolder.get_metadata_schema(), cls.base_schema, is_enabled=Boolean(title=MSG(u'Enabled')), user_group=UserGroup_Enumerate(title=MSG(u'Group'))) @classmethod def get_dynamic_schema(cls): context = get_context() self = context.resource if (hasattr(context, 'view') and issubclass(context.view.__class__, RegisterForm)): group = context.view.get_group(context) return group.get_dynamic_schema() if not isinstance(self, User): self = context.user if self is None: group = context.site_root.get_resource('shop/groups/default') else: group = self.get_group(context) # By default we use default group if group is None: group = context.site_root.get_resource('shop/groups/default') return group.get_dynamic_schema() @classmethod def get_dynamic_widgets(cls): context = get_context() self = context.resource if issubclass(context.view.__class__, RegisterForm): group = context.view.get_group(context) return group.get_dynamic_widgets() if not isinstance(self, User): self = context.user if self is None: return [] group = self.get_group(context) return group.get_dynamic_widgets() def _get_catalog_values(self): values = User._get_catalog_values(self) values['ctime'] = self.get_property('ctime') values['last_time'] = self.get_property('last_time') values['user_group'] = str(self.get_property('user_group')) values['is_enabled'] = self.get_property('is_enabled') return values def get_public_title(self): try: return self.get_dynamic_property('pseudo') except: # XXX Fix that bug return MSG(u'Unknow') def get_document_types(self): return [] def save_form(self, schema, form): dynamic_schema = self.get_dynamic_schema() metadata_schema = self.get_metadata_schema() for key in schema: if key in ['password', 'user_must_confirm']: continue elif (key not in metadata_schema and key not in dynamic_schema): continue value = form[key] if value is None: self.del_property(value) continue datatype = schema[key] if issubclass(datatype, (String, Unicode)): value = value.strip() self.set_property(key, value) confirmation_txt = MSG( u"To finalize your inscription you have to confirm your identity " u"by clicking this link:" u"\n" u"\n {uri}\n" u"\n" u"(Your identity confirmation key is {key})") def send_register_confirmation(self, context, need_email_validation=False): # Get group group = self.get_group(context) # Get mail subject and body subject = group.get_register_mail_subject() text = group.get_register_mail_body() # Registration need validation ? if need_email_validation: key = generate_password(30) self.set_property('user_must_confirm', key) # Build the confirmation link confirm_url = deepcopy(context.uri) path = '/users/%s/;confirm_registration' % self.name confirm_url.path = Path(path) confirm_url.query = {'key': key, 'username': self.get_login_name()} confirm_url = str(confirm_url) text += '\n\n' text += self.confirmation_txt.gettext(uri=confirm_url, key=key) # Send mail context.root.send_email(to_addr=self.get_property('email'), subject=subject, text=text) def get_group(self, context): user_group = self.get_property('user_group') return context.root.get_resource(user_group) def to_text(self): texts = [] for key, datatype in (self.get_dynamic_schema().items() + self.get_metadata_schema().items()): if key == 'password': continue value = self.get_property(key) if value: value = datatype.encode(value) value = unicode(value, 'utf-8') texts.append(value) return u'\n'.join(texts) def get_namespace(self, context): root = context.root # Get dynamic user values dynamic_user_value = ResourceDynamicProperty() dynamic_user_value.schema = self.get_dynamic_schema() dynamic_user_value.resource = self # Module shop_module = ModuleLoader() shop_module.context = context shop_module.here = self # Get modules items modules_items = [] search = context.root.search(is_shop_user_module=True) for brain in search.get_documents(): shop_user_module = root.get_resource(brain.abspath) modules_items.append({ 'name': shop_user_module.element_name, 'title': shop_user_module.element_title, 'href': shop_user_module.element_name, 'img': shop_user_module.class_icon48 }) # Ctime ctime = self.get_property('ctime') accept = context.accept_language # ACLS ac = self.get_access_control() is_authenticated = ac.is_authenticated(context.user, self) is_owner = context.user is not None and context.user.name == self.name # Build namespace return { 'name': self.name, 'link': context.get_link(self), 'module': shop_module, 'dynamic_user_value': dynamic_user_value, 'is_owner': is_owner, 'is_authenticated': is_authenticated, 'ctime': format_datetime(ctime, accept) if ctime else None, # XXX Why ? 'items': self.base_items + modules_items } ############################# # Api ############################# def send_confirm_url(self, context, email, subject, text, view): # XXX We override send_confirm_url to send mail # without setting subject with host, to use shop_uri # and not backoffice_uri (Since webmaster click from backoffice uri) # and to use good from_addr # Set the confirmation key if self.has_property('user_must_confirm'): key = self.get_property('user_must_confirm') else: key = generate_password(30) self.set_property('user_must_confirm', key) # Build the confirmation link shop = get_shop(context.resource) base_uri = shop.get_property('shop_uri') confirm_url = get_reference(base_uri) path = '/users/%s/%s' % (self.name, view) confirm_url.path = Path(path) confirm_url.query = {'key': key, 'username': self.get_login_name()} confirm_url = str(confirm_url) text = text.gettext(uri=confirm_url, key=key) # Get from_addr site_root = context.site_root if site_root.get_property('emails_from_addr'): user_name = site_root.get_property('emails_from_addr') user = self.get_resource('/users/%s' % user_name) from_addr = user.get_title(), user.get_property('email') else: from_addr = context.server.smtp_from # Subject subject = u'[%s] %s' % (base_uri, subject.gettext()) # Send email context.root.send_email(email, subject, from_addr=from_addr, text=text, subject_with_host=False) ############################### # Computed schema ############################### computed_schema = { 'nb_orders': Integer(title=MSG(u'Nb orders (even cancel)')), 'address': Unicode(title=MSG(u'Last known user address')) } @property def nb_orders(self): # XXX Orders states root = self.get_root() queries = [ PhraseQuery('format', 'order'), PhraseQuery('customer_id', self.name) ] return len(root.search(AndQuery(*queries))) @property def address(self): # XXX We should have a default address ? context = get_context() shop = get_shop(context.resource) addresses_h = shop.get_resource('addresses').handler records = addresses_h.search(user=self.name) if len(records) == 0: return None record = records[0] addresse = addresses_h.get_record_namespace(record.id) addr = u'' for key in ['address_1', 'address_2', 'zipcode', 'town', 'country']: try: addr += u'%s ' % addresse[key] except Exception: # XXX Why ? addr += u'XXX' return addr
# Set the email and paswword if email is not None: user.set_property('email', email) if password is not None: user.set_password(password) # Set default group root = context.root query = [ PhraseQuery('format', 'user-group'), PhraseQuery('name', 'default') ] search = root.search(AndQuery(*query)) documents = search.get_documents() group = documents[0] group = root.get_resource(group.abspath) user.set_property('user_group', str(group.get_abspath())) user_is_enabled = group.get_property('user_is_enabled_when_register') user.set_property('is_enabled', user_is_enabled) # Return the user return user register_resource_class(ShopUser) register_resource_class(ShopUserFolder) register_resource_class(Customers) register_resource_class(AuthentificationLogs) register_field('last_time', DateTime(is_stored=True)) register_field('user_group', String(is_indexed=True)) register_field('is_enabled', Boolean(is_indexed=True))
rest_query = Rest_Query rest_create = Rest_Create rest_read = Rest_Read rest_update = Rest_Update rest_delete = Rest_Delete rest_schema = Rest_Schema ########################################################################### # Register read-only fields ########################################################################### # Path related fields register_field('abspath', String(indexed=True, stored=True)) register_field('abspath_depth', Integer(indexed=True, stored=True)) register_field('parent_paths', String(multiple=True, indexed=True)) register_field('name', String(stored=True, indexed=True)) # Class related fields register_field('format', String(indexed=True, stored=True)) register_field('base_classes', String(multiple=True, indexed=True)) # Referential integrity register_field('links', String(multiple=True, indexed=True)) register_field('onchange_reindex', String(multiple=True, indexed=True)) # Full text search register_field('text', Unicode(indexed=True)) # Various classifications register_field('is_content', Boolean(indexed=True)) # Time events register_field('next_time_event', DateTime(stored=True)) register_field('next_time_event_payload', String(stored=True))
stock = Products_Stock() new_product = Product_NewProduct() def can_paste(self, source): return isinstance(source, Product) def get_document_types(self): return [] # Register fields register_field('reference', String(is_indexed=True, is_stored=True)) register_field('stock_quantity', Integer(is_indexed=True, is_stored=True)) register_field('manufacturer', String(is_indexed=True)) register_field('supplier', String(is_indexed=True, multiple=True)) register_field('product_model', String(is_indexed=True, is_stored=True)) register_field('has_images', Boolean(is_indexed=True, is_stored=True)) register_field('has_reduction', Boolean(is_indexed=True)) register_field('not_buyable_by_groups', String(is_indexed=True, multiple=True)) register_field('ctime', DateTime(is_stored=True, is_indexed=True)) register_field('data', Unicode(is_indexed=True)) register_field('ht_price', Decimal(is_indexed=True, is_stored=True)) register_field('ttc_price', Decimal(is_indexed=True, is_stored=True)) # XXX xapian can't sort decimal register_field('stored_price', Integer(is_indexed=False, is_stored=True)) register_field('stored_weight', Integer(is_indexed=False, is_stored=True)) # Register resources register_resource_class(Product) register_resource_class(Products)