class ShopModule_ProductImagesSlider(ShopModule): class_id = 'shop_module_product_images_slider' class_title = MSG(u'Product images slider') class_views = ['edit'] class_description = MSG(u'Product images slider') item_schema = {'big_img_width': Integer(default=500), 'big_img_height': Integer(default=600), 'thumb_img_width': Integer(default=90), 'thumb_img_height': Integer(default=90), 'show_cover': Boolean, 'show_loupe': Boolean, 'change_img_on_hover': Boolean, 'activate_lightbox': Boolean} item_widgets = [ TextWidget('big_img_width', title=MSG(u'Width of big image')), TextWidget('big_img_height', title=MSG(u'Height of big image')), TextWidget('thumb_img_width', title=MSG(u'Width of thumb image')), TextWidget('thumb_img_height', title=MSG(u'Height of thumb image')), BooleanRadio('show_cover', title=MSG(u'Show cover ?')), BooleanRadio('show_loupe', title=MSG(u'Show loupe ?')), BooleanRadio('change_img_on_hover', title=MSG(u'Change img on hover ?')), BooleanRadio('activate_lightbox', title=MSG(u'Activate lightbox ?')), ] def render(self, resource, context): return ShopModule_ProductImagesSlider_View().GET(self, context)
class ServerConfig(ConfigFile): schema = { 'modules': Tokens, 'listen-address': String(default=''), 'listen-port': Integer(default=None), # Mailing 'smtp-host': String(default=''), 'smtp-from': String(default=''), 'smtp-login': String(default=''), 'smtp-password': String(default=''), # Logging 'log-level': String(default='warning'), 'log-email': Email(default=''), # Time events 'cron-interval': Integer(default=0), # Security 'session-timeout': ExpireValue(default=timedelta(0)), # Tuning 'database-size': String(default='19500:20500'), 'database-readonly': Boolean(default=False), 'index-text': Boolean(default=True), 'max-width': Integer(default=None), 'max-height': Integer(default=None), }
def get_query_schema(self): # Define a huge batch limit, and the image size parameter schema = super(Folder_PreviewContent, self).get_query_schema() return merge_dicts(schema, batch_size=Integer(default=0), size=Integer(default=128), width=String, height=String)
class Product_View(STLForm): access = 'is_allowed_to_view' title = MSG(u'View') scripts = ['/ui/shop/js/declinations.js'] def get_template(self, resource, context): product_model = resource.get_product_model() if product_model is not None: return get_skin_template(context, '/product/product_view_%s.xml' % product_model.name, '/product/product_view.xml') return get_skin_template(context, '/product/product_view.xml') def get_schema(self, resource, context): return merge_dicts(STLForm.get_schema(self, resource, context), resource.get_purchase_options_schema()) def get_namespace(self, resource, context): context.scripts.append('/ui/shop/js/declinations.js') declinations = list(resource.search_resources(cls=Declination)) javascript_products = resource.get_javascript_namespace(declinations) purchase_options = resource.get_purchase_options_namespace(declinations) return merge_dicts(resource.get_namespace(context), javascript_products=javascript_products, purchase_options=purchase_options) action_add_to_cart_schema = {'quantity': Integer(default=1)} def action_add_to_cart(self, resource, context, form): """ Add to cart """ cart = ProductCart(context) # Check if we can add to cart if not resource.is_buyable(context): msg = MSG(u"This product isn't buyable") return context.come_back(msg) # Get purchase options declination = None kw = {} for key in resource.get_purchase_options_schema(): if form[key] is not None: kw[key] = form[key] if kw: declination = resource.get_declination(kw) if declination is None: context.message = ERROR(u'Declination not exist') return # Check if product is in stock cart_quantity = cart.get_product_quantity_in_cart(resource.name) total_quantity = cart_quantity + form['quantity'] if not resource.is_in_stock_or_ignore_stock(total_quantity, declination): msg = u"Quantity in stock insufficient." return context.come_back(MSG(msg)) # Add to cart cart.add_product(resource, form['quantity'], declination) # Information message context.message = INFO(u'Product added to cart !')
class Payments_EditablePayment(object): action_edit_payment_schema = { 'payment_way': String, # XXX PaymentWaysEnumerate(mandatory=True), 'id_payment': Integer(mandatory=True) } def action_edit_payment(self, resource, context, form): shop = get_shop(resource) # We get shipping way payment_way = shop.get_resource('payments/%s/' % form['payment_way']) # We get order_edit_view view = payment_way.order_edit_view # We get schema schema = view.schema # We get form try: form = process_form(context.get_form_value, schema) except FormError, error: context.form_error = error return self.on_form_error(resource, context) # Instanciate view payment_table = payment_way.get_resource('payments').handler record = payment_table.get_record(form['id_payment']) view = view(payment_way=payment_way, payment_table=payment_table, record=record, id_payment=form['id_payment']) # Do actions return view.action_edit_payment(resource, context, form)
class Document(CatalogAware): fields = { 'name': String(is_key_field=True, is_stored=True, is_indexed=True), 'title': Unicode(is_stored=True, is_indexed=True), 'data': Unicode(is_indexed=True), 'size': Integer(is_indexed=True), 'about_wolf': Boolean(is_indexed=True), 'is_long': Boolean(is_indexed=False, is_stored=True) } def __init__(self, abspath): self.abspath = abspath def get_catalog_values(self): data = lfs.open(self.abspath).read() return { 'name': basename(self.abspath), 'title': data.splitlines()[0], 'data': data, 'size': len(data), 'about_wolf': re.search('wolf', data, re.I) is not None, 'is_long': len(data) > 1024, }
class ShippingPricesTable(BaseTable): record_properties = { 'zone': CountriesZonesEnumerate(mandatory=True, is_indexed=True), 'max-weight': Decimal(is_indexed=True), 'max-quantity': Integer(is_indexed=True), 'price': Decimal(mandatory=True)}
class CheckPaymentBaseTable(PaymentWayBaseTable): record_properties = merge_dicts( PaymentWayBaseTable.record_properties, check_number=Integer(title=MSG(u'Check number')), bank=Unicode(title=MSG(u'Bank')), account_holder=Unicode(title=MSG(u'Account holder')), advance_state=CheckStates(title=MSG(u'Advance State')))
def decode(cls, data): data = data.strip() if not data: # Turn it into default value at the time of writing return None try: value = Integer.decode(data) except ValueError: value = data return value
def get_metadata_schema(cls): return merge_dicts(Folder.get_metadata_schema(), WorkflowAware.get_metadata_schema(), ctime=DateTime, note=Integer(default=0), remote_ip=String, author=String, advantages=Unicode, disadvantages=Unicode, images=String, recommended=RecommandationEnumerate)
def get_int_value(value, default=0): """ Return the interger representation of value is his decoding succeed otherwise the default value """ if not value: return default try: return Integer.decode(value) except ValueError: return default
def GET(self, resource, context): default_icon = context.get_template(self.default_icon) if PILImage is None: # Full size but better than nothing data = default_icon.to_str() format = 'png' else: # Default icon for empty or inaccessible folders width = context.get_form_value('width', type=Integer(), default=48) height = context.get_form_value('height', type=Integer(), default=48) data, format = default_icon.get_thumbnail(width, height) # XXX Don't cache nothing here # The image thumbnail was cached in the image handler # The folder thumbnail was cached in the folder handler # Accessible images depend on too many parameters context.content_type = 'image/%s' % format return data
def reset(self): self.properties = None self.records = [] self.added_properties = [] self.added_records = [] self.removed_records = [] # The catalog (for index and search) fields = merge_dicts(self.record_properties, __id__=Integer(is_key_field=True, is_stored=True, is_indexed=True)) self.catalog = make_catalog(None, fields)
class Document_2(CatalogAware): fields = { 'id': Integer(is_key_field=True, is_stored=True, is_indexed=True), 'data': Unicode(is_stored=True, is_indexed=True) } def __init__(self, id, data): self.id = id self.data = data def get_catalog_values(self): return {'id': self.id, 'data': self.data}
class SideBarProductCrossSellingBox(Box): class_id = 'sidebar-product-cross-selling-box' class_version = '20090122' class_title = MSG(u'Vertical item cross selling (product)') class_description = MSG(u"""Show on sidebar the cross selling configure in product""") view = SideBarCrossSellingBox_View() # XXX Need ? edit_schema = { 'thumb_width': Integer(mandatory=True), 'thumb_height': Integer(mandatory=True) } edit_widgets = [ TextWidget('thumb_width', size=3, title=MSG(u'Largeur des miniatures')), TextWidget('thumb_height', size=3, title=MSG(u'Hauteur des miniatures')) ]
class DBResource_GetImage(DBResource_GetFile): query_schema = { 'name': String(), 'width': Integer(), 'height': Integer(), 'language': String(), 'fit': Boolean(default=False), 'lossy': Boolean(default=False) } def GET(self, resource, context): field_name = self.get_field_name(context) language = context.query['language'] handler = self.get_handler(resource, field_name, language) image_width, image_height = handler.get_size() fit = context.query['fit'] lossy = context.query['lossy'] width = context.query['width'] or image_width height = context.query['height'] or image_height format = 'jpeg' if lossy is False: format = handler.get_mimetype().split('/')[1] data, format = handler.get_thumbnail(width, height, format, fit) if data is None: default = context.get_template('/ui/ikaaro/icons/48x48/image.png') data = default.to_str() format = 'png' # Headers context.set_content_type('image/%s' % format) # Ok return data
class Document(Resource): fields = { 'abspath': String(indexed=True, stored=True), 'name': String(indexed=True), 'lang': String(indexed=True, stored=True), 'title': Unicode(stored=True, indexed=True), 'data': Unicode(indexed=True), 'about_wolf': Boolean(indexed=True), 'is_long': Boolean(indexed=False, stored=True), 'count': Integer(stored=True, multiple=True), } def __init__(self, abspath): self.abspath = abspath def get_catalog_values(self): # Filename filename = basename(self.abspath) name, ext, lang = FileName.decode(filename) # Content with open(self.abspath) as f: data = f.read() data = unicode(data, 'utf-8') title = data.splitlines()[0] wolf = re.search('wolf', data, re.I) is not None count = [ len(data.splitlines()), # lines len(data.split()), # words len(data) ] # chars # Multilingual if lang: data = {lang: data} title = {lang: title} # Ok return { 'abspath': filename, # oid 'name': name, 'lang': lang, 'title': title, 'data': data, 'count': count, 'about_wolf': wolf, 'is_long': count[2] > 1024 }
def reset(self): self.lines = [] self.n_lines = 0 # Initialize the catalog if needed (Index&Search) # (we look if we have at least one indexed field in schema/columns) self.catalog = None schema = self.schema if schema is not None: for name in self.columns: field_cls = schema[name] if getattr(field_cls, 'is_indexed', False): fields = merge_dicts(schema, __number__=Integer(is_key_field=True, is_stored=True, is_indexed=True)) self.catalog = make_catalog(None, fields) break
def get_metadata_schema(cls): schema = ShopFolder.get_metadata_schema() schema['shop_uri'] = String schema['shop_backoffice_uri'] = String schema['order_notification_mails'] = Email(multiple=True) schema['shop_default_zone'] = CountriesZonesEnumerate(default=0) schema['shop_sort_by'] = SortBy_Enumerate schema['shop_sort_reverse'] = Boolean schema['categories_batch_size'] = Integer(default=20) schema['devise'] = Devises(default='978') schema['bill_logo'] = ImagePathDataType schema['pdf_signature'] = Unicode schema['barcode_format'] = BarcodesFormat schema['show_sub_categories'] = Boolean schema['hide_not_buyable_products'] = Boolean schema['product_cover_is_mandatory'] = Boolean schema['log_authentification'] = Boolean schema['registration_need_email_validation'] = Boolean return schema
class DBResource_Links(Folder_BrowseContent): """Links are the list of resources used by this resource.""" access = 'is_allowed_to_view' title = MSG(u"Links") icon = 'rename.png' query_schema = merge_dicts(Folder_BrowseContent.query_schema, batch_size=Integer(default=0)) search_schema = {} search_widgets = [] table_actions = [] def get_table_columns(self, resource, context): proxy = super(DBResource_Links, self) cols = proxy.get_table_columns(resource, context) return [x for x in cols if x[0] != 'checkbox'] def get_items(self, resource, context): links = resource.get_links() query = OrQuery(*[PhraseQuery('abspath', link) for link in links]) return context.database.search(query)
# Import from shop from enumerate import DeclinationImpact from dynamic_folder import DynamicFolder from widgets import DeclinationPricesWidget from shop.enumerate_table import EnumerateTable_to_Enumerate from shop.utils import get_shop declination_schema = {#General informations 'reference': String, 'title': Unicode, # Default declination ? 'is_default': Boolean, # Stock 'stock-quantity': Integer(default=0), # Weight 'impact-on-weight': DeclinationImpact, 'weight-impact-value': Decimal(default=decimal(0)), # Price 'impact_on_price': Decimal(default=decimal(0)), 'pro-impact_on_price': Decimal(default=decimal(0)), # Associated images #'associated-image': ImagesEnumerate # XXX Old to delete 'pro-price-impact-value': Decimal(default=decimal(0)), 'impact-on-price': DeclinationImpact, 'price-impact-value': Decimal(default=decimal(0)), } declination_widgets = [
def render_for_user(self, resource, context): # Get review that belong to user query = [ PhraseQuery('shop_module_review_author', resource.name), PhraseQuery('workflow_state', 'public'), PhraseQuery('format', 'shop_module_a_review') ] search = context.root.search(AndQuery(*query)) brains = list(search.get_documents(sort_by='mtime', reverse=True)) nb_reviews = len(brains) # Get viewboxes viewboxes = [] for brain in brains[:5]: review = context.root.get_resource(brain.abspath) viewbox = Review_Viewbox().GET(review, context) viewboxes.append(viewbox) # Return namespace return {'nb_reviews': nb_reviews, 'viewboxes': viewboxes} def get_document_types(self): return [] register_resource_class(ShopModule_Review) register_resource_class(ShopModule_AReview) register_field('shop_module_review_author', String(is_indexed=True)) register_field('shop_module_review_note', Integer(is_indexed=True, is_stored=True)) register_field('shop_module_review_description', Unicode(is_stored=True))
class icalendarTable(BaseCalendar, Table): """An icalendarTable is a handler for calendar data, generally used as an ical file but here as a table object. """ record_class = Record record_properties = merge_dicts(record_properties, type=String(is_indexed=True), inner=Integer(multiple=True)) ######################################################################### # API ######################################################################### def load_state_from_ical_file(self, file): """Load state from the given ical file. """ self.reset() self.set_changed() components = {} # Read the data data = file.read() # Parse lines = [] for name, value, parameters in parse_table(data): # Timestamp (ts), Schema, or Something else datatype = self.get_record_datatype(name) value = datatype.decode(value) property = Property(value, **parameters) # Append lines.append((name, property)) # Read first line first = lines[0] if (first[0] != 'BEGIN' or first[1].value != 'VCALENDAR' or len(first[1].parameters) != 0): raise ValueError, 'icalendar must begin with BEGIN:VCALENDAR' lines = lines[1:] ################################################################### # Skip properties # TODO Currently tables are not able to handler global properties, # we must implement this feature to be able to load from ical files. n_line = 0 for name, value in lines: if name == 'BEGIN': break elif name == 'END': break n_line += 1 lines = lines[n_line:] ################################################################### # Read components c_type = None c_inner_type = None uid = None records = self.records record_properties = self.record_properties id = 0 uids = {} for prop_name, prop_value in lines[:-1]: if prop_name in ('PRODID', 'VERSION'): raise ValueError, 'PRODID and VERSION must appear before '\ 'any component' if prop_name == 'BEGIN': if c_type is None: c_type = prop_value.value c_properties = {} c_inner_components = [] else: # Inner component like DAYLIGHT or STANDARD c_inner_type = prop_value.value c_inner_properties = {} continue if prop_name == 'END': value = prop_value.value if value == c_type: if uid is None: raise ValueError, 'UID is not present' record = self.get_record(id) or Record( id, record_properties) c_properties['type'] = Property(c_type) c_properties['UID'] = Property(uid) sequence = c_properties.get('SEQUENCE', None) c_properties['SEQUENCE'] = sequence or Property(0) c_properties['ts'] = Property(datetime.now()) # Add ids of inner components if c_inner_components: c_inner_components = [ Property(x) for x in c_inner_components ] c_properties['inner'] = c_inner_components record.append(c_properties) if uid in uids: n = uids[uid] + 1 uids[uid] = n else: n = 0 uids[uid] = 0 self.added_records.append((id, n)) records.append(record) # Next c_type = None uid = None if n == 0: id = id + 1 # Inner component elif value == c_inner_type: record = self.get_record(id) or Record( id, record_properties) c_inner_properties['type'] = Property(c_inner_type) sequence = c_inner_properties.get('SEQUENCE', None) c_inner_properties['SEQUENCE'] = sequence or Property(0) c_inner_properties['ts'] = Property(datetime.now()) record.append(c_inner_properties) c_inner_components.append(id) self.added_records.append((id, 0)) records.append(record) # Next c_inner_type = None id = id + 1 else: raise ValueError, 'Component %s found, %s expected' \ % (value, c_inner_type) else: datatype = self.get_record_datatype(prop_name) if c_inner_type is None: if prop_name in ('UID', 'TZID'): uid = prop_value.value else: if getattr(datatype, 'multiple', False) is True: value = c_properties.setdefault(prop_name, []) value.append(prop_value) else: # Check the property has not yet being found if prop_name in c_properties: raise ValueError, \ "property '%s' can occur only once" % name # Set the property c_properties[prop_name] = prop_value else: # Inner component properties if getattr(datatype, 'multiple', False) is True: value = c_inner_properties.setdefault(prop_name, []) value.append(prop_value) else: # Check the property has not yet being found if prop_name in c_inner_properties: msg = ('the property %s can be assigned only one' ' value' % prop_name) raise ValueError, msg # Set the property c_inner_properties[prop_name] = prop_value # Index the records for record in records: if record is not None: self.catalog.index_document(record) def to_ical(self): """Serialize as an ical file, generally named .ics """ lines = [] line = 'BEGIN:VCALENDAR\n' lines.append(Unicode.encode(line)) # Calendar properties properties = (('VERSION', u'2.0'), ('PRODID', u'-//itaapy.com/NONSGML ikaaro icalendar V1.0//EN')) for name, value in properties: value = Property(value) line = self.encode_property(name, value) lines.append(line[0]) # Calendar components for record in self.records: if record is not None: seq = 0 c_type = record.type # Ignore some components (like DAYLIGHT, STANDARD, ...) # keeping only VEVENT, VTIMEZONE, V.., and x-name ones if not c_type.startswith('V') and not c_type.startswith('X'): continue for version in record: line = 'BEGIN:%s\n' % c_type lines.append(Unicode.encode(line)) line = '' # Properties names = version.keys() names.sort() for name in names: if name in ('id', 'ts', 'type'): continue elif name == 'DTSTAMP': value = version['ts'] else: value = version[name] if name == 'SEQUENCE': value.value += seq # Insert inner components elif name == 'inner': line = self.encode_inner_components(name, value) else: name = name.upper() line = self.encode_property(name, value) lines.extend(line) line = 'END:%s\n' % c_type lines.append(Unicode.encode(line)) seq += 1 line = 'END:VCALENDAR\n' lines.append(Unicode.encode(line)) return ''.join(lines) def add_record(self, kw): if 'UID' not in kw: type = kw.get('type', 'UNKNOWN') kw['UID'] = self.generate_uid(type) id = len(self.records) record = Record(id, self.record_properties) version = self.properties_to_dict(kw) version['ts'] = Property(datetime.now()) record.append(version) # Change self.set_changed() self.added_records.append((id, 0)) self.records.append(record) self.catalog.index_document(record) # Back return record def get_component_by_uid(self, uid): """Return components with the given uid, None if it doesn't appear. """ return self.search(UID=uid) # Deprecated def get_components(self, type=None): """Return a dict {component_type: Record[], ...} or Return Record[] of given type. """ if type is None: return self.records return self.search(type=type) # Get some events corresponding to arguments def search_events(self, subset=None, **kw): """Return a list of Record objects of type 'VEVENT' corresponding to the given filters. It should be used like this, for example: events = cal.search_events( STATUS='TENTATIVE', PRIORITY=1, ATTENDEE=['mailto:[email protected]', 'mailto:[email protected]']) ** With a list of values, events match if at least one value matches It searches into all components or in the provided subset list of components. """ res_events = [] # Get the list of differents property names used to filter filters = kw.keys() # For each event events = subset or self.search(type='VEVENT') for event in events: if event in res_events: continue version = self.get_record(id=event.id)[-1] # For each filter for filter in filters: # If filter not in component, go to next one if filter not in version: break # Test filter expected = kw.get(filter) value = version[filter] datatype = self.get_record_datatype(filter) if getattr(datatype, 'multiple', False) is True: value = [ isinstance(x, Property) and x or Property(x) for x in value ] if not isinstance(expected, list): expected = [ expected, ] for item in value: if item.value in expected: break elif item.value == expected: break else: break else: if not isinstance(value, Property): value = Property(value) if value.value != expected: break else: res_events.append(event) return res_events def search_events_in_range(self, dtstart, dtend, sortby=None, **kw): """Return a list of Records objects of type 'VEVENT' matching the given dates range and sorted if requested. If kw is filled, it calls search_events on the found subset to return only components matching filters. RangeSearch is [left, right[ """ # Check type of dates, we need datetime for method in_range if not isinstance(dtstart, datetime): dtstart = datetime(dtstart.year, dtstart.month, dtstart.day) if not isinstance(dtend, datetime): dtend = datetime(dtend.year, dtend.month, dtend.day) # dtend is include into range dtend = dtend + timedelta(days=1) - resolution # Get only the events which matches dtstart_limit = dtstart + resolution dtend_limit = dtend + resolution dtstart = dtstart dtend = dtend query = AndQuery( PhraseQuery('type', 'VEVENT'), OrQuery( RangeQuery('DTSTART', dtstart, dtend), RangeQuery('DTEND', dtstart_limit, dtend_limit), AndQuery(RangeQuery('DTSTART', None, dtstart), RangeQuery('DTEND', dtend, None)))) results = self.search(query) if results == []: return [] # Check filters if kw: results = self.search_events(subset=results, **kw) # Nothing to sort or inactive if sortby is None or len(results) <= 1: return results # Get results as a dict to sort them res_events = [] for event in results: version = event[-1] value = { 'dtstart': version['DTSTART'].value, 'dtend': version['DTEND'].value, 'event': event } res_events.append(value) # Sort by dtstart res_events = sorted(res_events, key=itemgetter('dtstart')) # Sort by dtend res = [] current = [res_events[0]] for e in res_events[1:]: if e['dtstart'] == current[0]['dtstart']: current.append(e) else: res.extend(x['event'] for x in sorted(current, key=itemgetter('dtend'))) current = [e] res.extend(x['event'] for x in sorted(current, key=itemgetter('dtend'))) return res def search_events_in_date(self, date, sortby=None, **kw): """Return a list of Component objects of type 'VEVENT' matching the given date and sorted if requested. """ dtstart = datetime(date.year, date.month, date.day) dtend = dtstart + timedelta(days=1) - resolution return self.search_events_in_range(dtstart, dtend, sortby=sortby, **kw) # Test if any event corresponds to a given date def has_event_in_date(self, date): """Return True if there is at least one event matching the given date. """ return self.search_events_in_date(date) != [] def get_conflicts(self, start_date, end_date=None): """Returns a list of uid couples which happen at the same time. We check only last occurrence of events. """ if end_date is not None: events = self.search_events_in_range(start_date, end_date) else: events = self.search_events_in_date(start_date) if len(events) <= 1: return None conflicts = [] # We take each event as a reference for i, event_ref in enumerate(events): version = event_ref[-1] dtstart_ref = version['DTSTART'].value dtend_ref = version['DTEND'].value # For each other event, we test if there is a conflict for j, event in enumerate(events): if j <= i: continue version = event[-1] dtstart = version['DTSTART'].value dtend = version['DTEND'].value if dtstart >= dtend_ref or dtend <= dtstart_ref: continue conflicts.append((i, j)) # Replace index of components by their UID if conflicts != []: for index, (i, j) in enumerate(conflicts): conflicts[index] = (events[i].id, events[j].id) return conflicts
rest_login = Rest_Login 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))
class CSV_View(BrowseForm): # FIXME We need different permissions for GET and POST access = 'is_allowed_to_edit' title = MSG(u'View') schema = { 'ids': Integer(mandatory=True, multiple=True), } search_schema = {} search_widgets = [] def get_items(self, resource, context): return list(resource.handler.get_rows()) def sort_and_batch(self, resource, context, items): # Sort sort_by = context.query['sort_by'] reverse = context.query['reverse'] if sort_by: handler = resource.handler if handler.schema is None: sort_by = int(sort_by) else: sort_by = handler.columns.index(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] def get_table_columns(self, resource, context): columns = resource.get_columns() columns.insert(0, ('checkbox', None)) columns.insert(1, ('index', None)) return columns def get_item_value(self, resource, context, item, column): if column == 'checkbox': return item.number, False elif column == 'index': index = item.number return index, ';edit_row?index=%s' % index # A value from the schema handler = resource.handler datatype = handler.get_datatype(column) if handler.schema is None: value = item[int(column)] else: value = item.get_value(column) # Columns is_enumerate = getattr(datatype, 'is_enumerate', False) if is_enumerate: return datatype.get_value(value) return value table_actions = [Remove_BrowseButton] def action_remove(self, resource, context, form): ids = form['ids'] resource.handler.del_rows(ids) # Ok context.message = INFO(u'Row deleted.')
class Shop_Configure(DBResource_Edit): access = 'is_admin' title = MSG(u'Configure shop') schema = { 'shop_uri': String(mandatory=True), 'shop_backoffice_uri': String(mandatory=True), 'order_notification_mails': MultiLinesTokens(mandatory=True), 'shop_default_zone': CountriesZonesEnumerate(mandatory=True), 'shop_sort_by': SortBy_Enumerate(mandatory=True), 'shop_sort_reverse': Boolean(mandatory=True), 'devise': Devises(mandatory=True), 'hide_not_buyable_products': Boolean(mandatory=True), 'categories_batch_size': Integer(mandatory=True), 'show_sub_categories': Boolean, 'product_cover_is_mandatory': Boolean, 'log_authentification': Boolean, 'registration_need_email_validation': Boolean, 'bill_logo': ImagePathDataType, 'pdf_signature': Unicode, 'barcode_format': BarcodesFormat } widgets = [ TextWidget('shop_uri', title=MSG(u'Website uri')), TextWidget('shop_backoffice_uri', title=MSG(u'Website backoffice uri')), MultilineWidget( 'order_notification_mails', title=MSG(u'New order notification emails (1 per line)'), rows=8, cols=50), MultilineWidget('pdf_signature', title=MSG(u'PDF signature'), rows=8, cols=50), SelectWidget('shop_default_zone', title=MSG(u'Shop default zone')), SelectWidget('devise', title=MSG(u'Devises'), has_empty_option=False), TextWidget('categories_batch_size', title=MSG(u'Batch size for categories ?')), SelectWidget('shop_sort_by', title=MSG(u'Sort products by ...'), has_empty_option=False), BooleanRadio('shop_sort_reverse', title=MSG(u'Reverse sort ?')), BooleanRadio('show_sub_categories', title=MSG(u'Show sub categories ?')), BooleanRadio('product_cover_is_mandatory', title=MSG(u'Product cover is mandatory ?')), BooleanRadio('hide_not_buyable_products', title=MSG(u'Hide not buyable products ?')), BooleanRadio('log_authentification', title=MSG(u'Log users authentification ?')), BooleanRadio('registration_need_email_validation', title=MSG(u'Ask for mail validation on registration ?')), ImageSelectorWidget('bill_logo', title=MSG(u'Bill logo')), SelectWidget('barcode_format', title=MSG(u'Barcode format'), has_empty_option=False), ] submit_value = MSG(u'Edit configuration') def action(self, resource, context, form): for key in self.schema.keys(): if key == 'order_notification_mails': continue resource.set_property(key, form[key]) values = [x.strip() for x in form['order_notification_mails']] resource.set_property('order_notification_mails', values) context.message = MSG_CHANGES_SAVED return
def test_Integer(self): for x in range(-10,11): data = Integer.encode(x) self.assertEqual(x, Integer.decode(data))
'cellpadding': String, 'cellspacing': String, 'char': String, 'charoff': String, 'charset': String, 'checked': Boolean, 'cite': URI, 'class': String, 'classid': URI, 'clear': String, 'code': String, 'codebase': URI, 'codetype': String, 'color': String, 'cols': Integer, 'colspan': Integer(default=1), 'compact': Boolean, 'content': String, 'coords': String, 'data': URI, 'datetime': String, 'declare': Boolean, 'defer': Boolean, 'dir': String, 'disabled': Boolean, 'enctype': String, 'face': String, 'for': String, 'frame': String, 'frameborder': String, 'headers': String,
class BrowseForm(STLView): template = '/ui/ikaaro/generic/browse.xml' query_schema = { 'batch_start': Integer(default=0), 'batch_size': Integer(default=20), 'sort_by': String, 'reverse': Boolean(default=False)} # Batch batch = Batch # Search configuration search_form_id = 'form-search' search_form_css = '' search_template = '/ui/ikaaro/auto_form.xml' search_template_field = '/ui/ikaaro/auto_form_field.xml' search_schema = {} search_widgets = [] # Content table_template = '/ui/ikaaro/generic/browse_table.xml' table_form_id = 'form-table' table_css = None table_columns = [] table_actions = [] # Actions are external to current form external_form = False # Keep the batch in the canonical URL canonical_query_parameters = (STLView.canonical_query_parameters + ['batch_start']) def get_query_schema(self): proxy = super(BrowseForm, self) return merge_dicts(proxy.get_query_schema(), self.search_schema) def get_namespace(self, resource, context): batch = None table = None # Batch items = self.get_items(resource, context) if self.batch is not None: total = len(items) batch = self.batch(context=context, total=total).render() # Content items = self.sort_and_batch(resource, context, items) if self.table_template is not None: template = context.get_template(self.table_template) namespace = self.get_table_namespace(resource, context, items) table = stl(template, namespace) # The Search Form search = None if self.search_widgets: search = self.get_search_namespace(resource, context) return {'batch': batch, 'table': table, 'search': search} ################################################## # Search ################################################## def get_search_actions(self, resource, context): search_button = Button(access=True, resource=resource, context=context, css='button-search', title=MSG(u'Search')) return [search_button] def get_search_namespace(self, resource, context): form = AutoForm( form_id=self.search_form_id, form_css=self.search_form_css, template=self.search_template, template_field=self.search_template_field, title=MSG(u'Search'), method='get', schema=self.search_schema, widgets=self.search_widgets, actions=self.get_search_actions(resource, context)) return form.GET(resource, context) ####################################################################### # Private API (to override) def get_table_columns(self, resource, context): return self.table_columns def _get_table_columns(self, resource, context): """ Always return a tuple of 4 elements. """ table_columns = [] for column in self.get_table_columns(resource, context): if len(column) == 2: name, title = column column = (name, title, True, None) elif len(column) == 3: name, title, sortable = column column = (name, title, sortable, None) table_columns.append(column) return table_columns def get_items(self, resource, context): name = 'get_items' raise NotImplementedError, "the '%s' method is not defined" % name def sort_and_batch(self, resource, context, items): name = 'sort_and_batch' raise NotImplementedError, "the '%s' method is not defined" % name def get_item_value(self, resource, context, item, column): if column == 'row_css': return None # Default raise ValueError, 'unexpected "%s"' % column def get_table_actions(self, resource, context): return self.table_actions ####################################################################### # Table def get_table_head(self, resource, context, items): actions = self.actions_namespace # Get from the query query = context.query sort_by = query['sort_by'] reverse = query['reverse'] columns = self._get_table_columns(resource, context) columns_ns = [] for name, title, sortable, css in columns: if name == 'checkbox': # Type: checkbox if self.external_form or actions: columns_ns.append({'is_checkbox': True}) elif title is None or not sortable: # Type: nothing or not sortable columns_ns.append({ 'is_checkbox': False, 'title': title, 'css': 'thead-%s' % name.replace('_', '-'), 'href': None, 'sortable': False}) else: # Type: normal base_href = context.uri.replace(sort_by=name, batch_start=None) if name == sort_by: sort_up_active = reverse is False sort_down_active = reverse is True else: sort_up_active = sort_down_active = False columns_ns.append({ 'is_checkbox': False, 'title': title, 'css': 'thead-%s' % name.replace('_', '-'), 'sortable': True, 'href': context.uri.path, 'href_up': base_href.replace(reverse=0), 'href_down': base_href.replace(reverse=1), 'sort_up_active': sort_up_active, 'sort_down_active': sort_down_active }) return columns_ns @proto_lazy_property def actions_namespace(self): resource = self.resource context = self.context items = self._items actions = [] for button in self.get_table_actions(resource, context): button = button(resource=resource, context=context, items=items) if button.show: actions.append(button) return actions def get_column_css(self, resource, context, column): return None def get_table_namespace(self, resource, context, items): # (1) Actions (submit buttons) self._items = items actions = self.actions_namespace # (2) Table Head: columns table_head = self.get_table_head(resource, context, items) # (3) Table Body: rows columns = self.get_table_columns(resource, context) rows = [] for item in items: row_columns = [] for column in columns: column = column[0] # Skip the checkbox column if there are not any actions if column == 'checkbox': if not self.external_form and not actions: continue value = self.get_item_value(resource, context, item, column) column_ns = { 'is_checkbox': False, 'is_icon': False, 'is_link': False, 'css': self.get_column_css(resource, context, column), } # Type: empty if value is None: pass # Type: checkbox elif column == 'checkbox': value, checked = value column_ns['is_checkbox'] = True column_ns['value'] = value column_ns['checked'] = checked # Type: icon elif column == 'icon': column_ns['is_icon'] = True column_ns['src'] = value # Type: normal else: column_ns['is_link'] = True if type(value) is tuple: value, href = value else: href = None column_ns['value'] = value column_ns['href'] = href row_columns.append(column_ns) # Row css row_css = self.get_item_value(resource, context, item, 'row_css') # Append rows.append({'css': row_css, 'columns': row_columns}) # Ok return { 'css': self.table_css, 'form_id': self.table_form_id, 'columns': table_head, 'rows': rows, 'actions': actions, 'external_form': self.external_form or not actions}
class TimetablesForm(STLView): access = 'is_allowed_to_edit' title = MSG(u'Timetables') template = '/ui/ikaaro/agenda/edit_timetables.xml' styles = ['/ui/ikaaro/agenda/style.css'] def get_namespace(self, resource, context): # Show current timetables only if previously set in metadata if resource.has_property('timetables'): timetables = resource.get_value('timetables') timetables_ns = [{ 'index': index, 'startname': '%s_start' % index, 'endname': '%s_end' % index, 'start': Time.encode(start), 'end': Time.encode(end) } for index, (start, end) in enumerate(timetables)] else: timetables_ns = [] # Ok return {'timetables': timetables_ns} action_add_schema = {'new_start': Time, 'new_end': Time} def action_add(self, resource, context, form): # Check start time is before end time start = form['new_start'] end = form['new_end'] if start >= end: message = ERROR(u'Start time must be earlier than end time.') context.message = message return # Check the given range is not defined yet timetables = resource.get_value('timetables') if (start, end) in timetables: context.message = ERROR(u'The given range is already defined.') return # Add new range timetables.append((start, end)) timetables.sort() resource.set_value('timetables', timetables) # Ok context.message = messages.MSG_CHANGES_SAVED action_remove_schema = {'ids': Integer(multiple=True)} def action_remove(self, resource, context, form): ids = form['ids'] if len(ids) == 0: context.message = ERROR(u'Nothing to remove.') return # New timetables timetables = resource.get_value('timetables') timetables = [ timetable for index, timetable in enumerate(timetables) if index not in ids ] resource.set_property('timetables', timetables) # Ok context.message = INFO(u'Timetable(s) removed successfully.') def action_update(self, resource, context, form): timetables = resource.get_value('timetables') if len(timetables) == 0: context.message = ERROR(u'Nothing to change.') return # Update timetable or just set index to next index new_timetables = [] for index in range(len(timetables)): try: start = context.get_form_value('%s_start' % index, type=Time) end = context.get_form_value('%s_end' % index, type=Time) except: context.message = ERROR(u'Wrong time selection (HH:MM).') return if start >= end: message = ERROR(u'Start time must be earlier than end time.') context.message = message return new_timetables.append((start, end)) new_timetables.sort() resource.set_property('timetables', new_timetables) # Ok context.message = messages.MSG_CHANGES_SAVED