def authenticate(self): """Checks the authentication credentials and sets the context user if all checks are ok. """ self.user = None # 1. Get credentials with username and token try: username, token = self.get_authentication_credentials() except ValueError as error: msg = "Authentication error : %s " % error.message log_warning(msg, domain='ikaaro.web') return if not username or not token: return # 2. Get the user user = self.root.get_user(username) if not user: return # 3. Check the token user_token = user.get_auth_token() if token == self._get_auth_token(user_token): self.user = user
def get_value(self, name, language=None): field = self.get_field(name) if field is None: msg = 'field {name} is not defined on {class_id}' log_warning(msg.format(name=name, class_id=self.class_id)) return None # Check context self.check_if_context_exists() # Check if field is obsolete if field.obsolete: msg = 'field {name} is obsolete on {class_id}' log_warning(msg.format(name=name, class_id=self.class_id)) # TODO: Use decorator for cache # TODO: Reactivate when ready #cache_key = (name, language) #if self._values.has_key(cache_key): # return self._values[cache_key] if self._brain and field.stored and not is_prototype(field.datatype, Decimal): try: value = self.get_value_from_brain(name, language) except Exception: # FIXME Sometimes we cannot get value from brain # We're tying to debug this problem msg = 'Warning: cannot get value from brain {0} {1}' msg = msg.format(self.abspath, name) print(msg) value = field.get_value(self, name, language) else: value = field.get_value(self, name, language) #self._values[cache_key] = value return value
def authenticate(self): """Checks the authentication cookie and sets the context user if all checks are ok. """ self.user = None # 1. Get the cookie cookie = self.get_cookie('iauth') if not cookie: return # 2. Parse the cookie try: username, token = decodestring(unquote(cookie)).split(':', 1) except Exception: msg = 'bad authentication cookie "%s"' % cookie log_warning(msg, domain='itools.web') return if not username or not token: return # 3. Get the user user = self.root.get_user(username) if not user: return # 4. Check the token user_token = user.get_auth_token() if token == self._get_auth_token(user_token): self.user = user
def get_products_namespace(self, context): root = context.root query = AndQuery( get_base_path_query(self.get_canonical_path()), PhraseQuery('format', 'order-product')) l = [] for brain in root.search(query).get_documents(): resource = root.get_resource(brain.abspath, soft=True) if resource is None: log_warning('ORDER-PRODUCT not found : %s' % brain.abspath) continue # Get base product namespace kw = {} for key in ['reference', 'title', 'tax', 'quantity']: kw[key] = resource.get_property(key) kw['pre_tax_price'] = resource.get_property('pre_tax_price') tax = kw['tax'] / decimal(100) + 1 kw['price_with_tax'] = get_arrondi(kw['pre_tax_price'] * tax) total_price = kw['price_with_tax'] * kw['quantity'] kw['pre_tax_price'] = self.format_price(kw['pre_tax_price']) kw['total_price'] = self.format_price(total_price) kw['price_with_tax'] = self.format_price(kw['price_with_tax']) # Get product link (if exist) abspath = resource.get_property('abspath') product = root.get_resource(abspath, soft=True) if product: # Add link only if edit allowed link = None ac = resource.get_access_control() if ac.is_allowed_to_edit(context.user, product): link = context.get_link(product) kw['link'] = link # Add product to list of products l.append(kw) return l
def handle_request(self, soup_message, path): # (1) If path is null => 400 Bad Request if path is None: log_warning('Unexpected HTTP path (null)', domain='itools.web') return set_response(soup_message, 400) # (2) Attach to the soup message and path context = self() context.soup_message = soup_message context.path = path # (3) Get the method that will handle the request method_name = soup_message.get_method() method = getattr(context, 'http_%s' % method_name.lower(), None) # 501 Not Implemented if method is None: log_warning('Unexpected "%s" HTTP method' % method_name, domain='itools.web') return set_response(soup_message, 501) # (4) Go set_context(context) try: method() except StandardError: log_error('Internal error', domain='itools.web') return set_response(soup_message, 500) finally: set_context(None)
def _update_data(self): limit = self.get_property('limit') uri = self._get_account_uri() data = None # errors errors = [] errors_str = [] # backup the default timeout default_timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(3) # timeout in seconds # TODO Use itools.vfs instead of urllib2 try: req = urllib2.Request(uri) req.add_header('User-Agent', 'itools/%s' % itools_version) response = urllib2.urlopen(req) data = response.read() except (socket.error, socket.gaierror, Exception, urllib2.HTTPError, urllib2.URLError), e: msg = '%s -- Network error: "%s"' msg = msg % (XMLContent.encode(str(uri)), e) msg = msg.encode('utf-8') errors.append(XMLParser(msg)) errors_str.append(msg) summary = 'sidebar/twitter, Error downloading feed\n' details = format_exc() log_warning(summary + details, domain='itws')
def _smtp_send(self): nb_max_mails_to_send = 2 spool = lfs.open(self.spool) def get_names(): # Find out emails to send locks = set() names = set() for name in spool.get_names(): if name == 'failed': # Skip "failed" special directory continue if name[-5:] == '.lock': locks.add(name[:-5]) else: names.add(name) names.difference_update(locks) return names # Send emails names = get_names() smtp_host = self.smtp_host for name in list(names)[:nb_max_mails_to_send]: # 1. Open connection try: smtp = SMTP(smtp_host) except gaierror, excp: log_warning('%s: "%s"' % (excp[1], smtp_host)) return 60 # 1 minute except Exception: self.smtp_log_error() return 60 # 1 minute
def get_header(self, name): name = name.lower() datatype = get_type(name) value = self.soup_message.get_header(name) if value is None: return datatype.get_default() try: return datatype.decode(value) except ValueError: log_warning('malformed header: %s: %s' % (name, value), domain='itools.web') return datatype.get_default()
def get_catalog_values(self): values = {} # Step 1. Automatically index fields root = self.get_root() languages = root.get_value('website_languages') for name, field in self.get_fields(): if not field.indexed and not field.stored: continue if field.multilingual: value = {} for language in languages: value[language] = field.get_value(self, name, language) values[name] = value else: values[name] = field.get_value(self, name) # Step 2. Index non-metadata properties # Path related fields abspath = self.abspath values['abspath'] = str(abspath) n = len(abspath) values['abspath_depth'] = n if n: values['parent_paths'] = [ str(abspath[:i]) for i in range(n) ] values['name'] = self.name # Class related fields values['format'] = self.metadata.format values['base_classes'] = self.get_base_classes() values['class_version'] = class_version_to_date(self.metadata.version) # Links to other resources values['owner'] = self.get_owner() values['share'] = self.get_share() values['links'] = list(self.get_links()) values['onchange_reindex'] = self.get_onchange_reindex() # Full text indexation (not available in icms-init.py FIXME) context = get_context() server = context.server if server and server.index_text: try: values['text'] = self.to_text() except Exception: log = 'Indexation failed: %s' % abspath log_warning(log, domain='ikaaro') # Time events for the CRON reminder, payload = self.next_time_event() values['next_time_event'] = reminder if payload: values['next_time_event_payload'] = dumps(payload) else: values['next_time_event_payload'] = None # Ok return values
def check_consistency(self, quick): log_info('Check database consistency') # Check the server is not running if self.read_only: pid = get_pid('%s/pid_ro' % self.target) else: pid = get_pid('%s/pid' % self.target) if pid is not None: msg = '[%s] The Web Server is already running.' % self.target log_warning(msg) print(msg) return False # Ok return True
def update_rss(self): handler = self.handler errors = [] errors_str = [] articles = [] feeds_summary = {} # backup the default timeout default_timeout = socket.getdefaulttimeout() socket.setdefaulttimeout(self.get_property('timeout')) # Download the feeds for uri, keywords, active in handler.get_rows(): if active is False: continue keywords = [x.strip().lower() for x in keywords.split(',')] # TODO Use itools.vfs instead of urllib2 try: req = urllib2.Request(uri) req.add_header('User-Agent', 'itools/%s' % itools_version) response = urllib2.urlopen(req) data = response.read() except (socket.error, socket.gaierror, Exception, urllib2.HTTPError), e: msg = '%s <br />-- Network error: "%s"' msg = msg % (XMLContent.encode(str(uri)), e) msg = msg.encode('utf-8') errors.append(XMLParser(msg)) errors_str.append(msg) summary = ('rssfeeds, Error downloading feed\n' 'uri: %s\n\n' % str(uri)) details = format_exc() log_warning(summary + details, domain='itws') continue # Parse try: feed = RSSFile(string=data) except Exception, e: msg = '%s <br />-- Error parsing: "%s"' msg = msg % (XMLContent.encode(str(uri)), e) msg = msg.encode('utf-8') errors.append(XMLParser(msg)) errors_str.append(msg) summary = ('rssfeeds, Error parsing feed\n' 'uri: %s\n\n' % str(uri)) details = format_exc() log_warning(summary + details, domain='itws') continue
def get_handler_class(self, key): mimetype = self.get_mimetype(key) try: return get_handler_class_by_mimetype(mimetype) except ValueError: log_warning('unknown handler class "{0}"'.format(mimetype)) if fs.is_file(key): from itools.handlers import File return File elif fs.is_folder(key): from itools.handlers import Folder return Folder raise ValueError
def get_header(self, name): name = name.lower() datatype = get_type(name) name = name.replace('-', '_') if name == 'content_type': value = self.environ.get('CONTENT_TYPE') else: value = self.environ.get('HTTP_'+ name.upper()) if value is None: return datatype.get_default() or '' try: return datatype.decode(value) or '' except ValueError: log_warning('malformed header: %s: %s' % (name, value), domain='ikaaro.web') return datatype.get_default()
def to_str(self): resource_class = self.get_resource_class(self.format) if self.version is None: lines = ['format:%s\n' % self.format] else: lines = ['format;version=%s:%s\n' % (self.version, self.format)] # Properties are to be sorted by alphabetical order properties = self.properties names = properties.keys() names.sort() # Properties for name in names: property = properties[name] # Get the field field = resource_class.get_field(name) if field is None: msg = 'unexpected field "{0}" in resource "{1}" (format "{2}")' msg = msg.format(name, self.key, self.format) if resource_class.fields_soft: log_warning(msg, domain='itools.database') continue raise ValueError, msg datatype = field.datatype params_schema = field.parameters_schema is_empty = datatype.is_empty p_type = type(property) if p_type is dict: languages = property.keys() languages.sort() lines += [ property_to_str(name, property[x], datatype, params_schema) for x in languages if not is_empty(property[x].value) ] elif p_type is list: lines += [ property_to_str(name, x, datatype, params_schema) for x in property if not is_empty(x.value) ] elif property.value is None: pass elif not is_empty(property.value): lines.append( property_to_str(name, property, datatype, params_schema)) return ''.join(lines)
def get_user_title(self, userid): if not userid: return None # Userid (abspath) or username if userid[0] != '/': userid = '/users/%s' % userid # Get user user = self.get_resource(userid, soft=True) if user is None: username = userid.rsplit('/', 1)[-1] log_warning('unkwnown user %s' % username, domain='ikaaro') return unicode(username) # Ok return user.get_title()
def path_callback(self, soup_message, path): # (1) Get the class that will handle the request method_name = soup_message.get_method() method = methods.get(method_name) # 501 Not Implemented if method is None: log_warning('Unexpected "%s" HTTP method' % method_name, domain="itools.web") return set_response(soup_message, 501) # (2) Initialize the context context = Context(soup_message, path) self.init_context(context) # (3) Pass control to the Get method class try: method.handle_request(self, context) except Exception: log_error("Failed to handle request", domain="itools.web") set_response(soup_message, 500)
def read_headers(file): entity_headers = {} # Setup line = file.readline().strip() while not line: line = file.readline().strip() # Go while line: name, value = line.split(':', 1) name = name.strip().lower() datatype = get_type(name) value = value.strip() try: value = datatype.decode(value) except Exception: log_warning("Failed to parse the '%s' header" % name, domain='itools.web') else: entity_headers[name] = value # Next line = file.readline().strip() return entity_headers
def _load_state_from_file(self, file): properties = self.properties data = file.read() parser = parse_table(data) # Read the format & version name, value, parameters = parser.next() if name != 'format': raise ValueError, 'unexpected "%s" property' % name if 'version' in parameters: version = parameters.pop('version') if len(version) > 1: raise ValueError, 'version parameter cannot be repeated' self.version = version[0] if parameters: raise ValueError, 'unexpected parameters for the format property' self.format = value # Get the schema resource_class = self.database.get_resource_class(value) # Parse for name, value, parameters in parser: if name == 'format': raise ValueError, 'unexpected "format" property' # 1. Get the field field = resource_class.get_field(name) if field is None: msg = 'unexpected field "%s"' % name if resource_class.fields_soft: log_warning(msg, domain='itools.database') field = DefaultField else: raise ValueError, msg # 2. Deserialize the parameters params_schema = field.parameters_schema params_default = field.parameters_schema_default try: deserialize_parameters(parameters, params_schema, params_default) except ValueError, e: msg = 'in class "{0}" property "{1}": {2}' raise ValueError, msg.format(resource_class, name, e) # 3. Get the datatype properties if field.multiple and field.multilingual: error = 'property "%s" is both multilingual and multiple' raise ValueError, error % name # 4. Build the property datatype = field.datatype value = datatype.decode(value) property = Property(value, **parameters) # Case 1: Multilingual if field.multilingual: language = parameters.get('lang') if language is None: err = 'multilingual property "%s" is missing the language' raise ValueError, err % name properties.setdefault(name, {})[language] = property # Case 2: multiple elif field.multiple: properties.setdefault(name, []).append(property) # Case 3: simple else: properties[name] = property
def make_namespaces(context): # Shortcuts elements = context['elements'] references = context['references'] # Find all namespaces, and fill them with elements namespaces = {} for element in elements: qnames = element['qnames'] attributes = element['attributes'] data = element['data'] is_empty = element['is_empty'] refs = element['refs'] # Replace the references of "refs" while refs: new_refs = [] for ref in refs: try: ref = references[ref] except KeyError: raise KeyError, ('the define "%s" is missing in your ' 'relax NG file') % ref attributes.extend(ref['attributes']) new_refs.extend(ref['refs']) is_empty = is_empty and ref['is_empty'] ref_data = ref['data'] if ref_data is not None: if data is not None and data != ref_data: data = '' elif data is None: data = ref_data refs = new_refs # Now, data is good if data is not None: is_empty = False # Replace the references of "attributes" for attribute in attributes: refs = attribute['refs'] while refs: new_refs = [] for ref in refs: try: ref = references[ref] except KeyError: raise KeyError, ('the define "%s" is missing in your ' 'relax NG file') % ref new_refs.extend(ref['refs']) ref_data = ref['data'] attr_data = attribute['data'] if ref_data is not None: if attr_data is not None and attr_data != ref_data: attr_data = '' elif attr_data is None: attr_data = ref_data refs = new_refs # Update the good namespaces if qnames is not None: for uri, name in element['qnames']: own, free = split_attributes(uri, attributes) # Element + its attributes namespace = namespaces.setdefault(uri, {'elements': {}, 'free_attributes': {}}) element = ElementSchema(name, default_datatype=String, is_empty=is_empty, attributes=own) namespace['elements'][name] = element # Free attributes for (uri, name), datatype in free.iteritems(): namespace = namespaces.setdefault(uri, {'elements': {}, 'free_attributes': {}}) namespace['free_attributes'][name] = datatype result = {} prefix2uri = context['prefix'] for namespace, data in namespaces.iteritems(): # Find the prefix for prefix, uri in prefix2uri.iteritems(): if uri == namespace: result[uri] = XMLNamespace(uri, prefix, data['elements'].values(), data['free_attributes'], String) break else: log_warning('relaxng: namespace "%s" not found' % namespace) return result
def warn_not_indexed(name): log_warning(MSG_NOT_INDEXED.format(name=name), 'itools.database')
def get_catalog_values(self): values = {} # Step 1. Automatically index fields languages = self.get_root().get_value('website_languages') for name, field in self.get_fields(): if not field.indexed and not field.stored: continue if field.multilingual: value = {} for language in languages: value[language] = field.get_value(self, name, language) values[name] = value else: values[name] = field.get_value(self, name) # Step 2. Index non-metadata properties # Path related fields abspath = self.abspath values['abspath'] = str(abspath) n = len(abspath) values['abspath_depth'] = n if n: values['parent_paths'] = [ str(abspath[:i]) for i in range(n) ] values['name'] = self.name values['is_content'] = self.is_content # Class related fields values['format'] = self.metadata.format values['base_classes'] = [] for cls in self.__class__.__mro__: class_id = getattr(cls, 'class_id', None) if class_id: values['base_classes'].append(class_id) # Links to other resources values['owner'] = self.get_owner() values['share'] = self.get_share() values['links'] = list(self.get_links()) # Full text context = get_context() try: server = context.server except AttributeError: server = None if server is not None and server.index_text: try: values['text'] = self.to_text() except NotImplementedError: pass except Exception: log = 'Indexation failed: %s' % abspath log_warning(log, domain='ikaaro') # Time events reminder, payload = self.next_time_event() values['next_time_event'] = reminder values['next_time_event_payload'] = dumps(payload) # Ok return values
def warn_not_indexed_nor_stored(name): log_warning(MSG_NOT_INDEXED_NOR_STORED.format(name=name))
def warn_not_stored(name): log_warning(MSG_NOT_STORED.format(name=name))
def warn_not_indexed(name): log_warning(MSG_NOT_INDEXED.format(name=name))
def _choice_widget(context, form, datatype, name, value, schema, fields, readonly, container, container_attributes, choice, choice_attributes, choice_checked, multiline, tabindex): html = [] # True -> '1' ; False -> '2' data = datatype.encode(value) if readonly: for option in datatype.get_namespace(data): attributes = merge_dicts(choice_attributes, value=option['name'], disabled=u"disabled") attributes.setdefault(u"class", []).append(u"disabled") if option['selected']: attributes[choice_checked] = choice_checked value = option['value'] if is_prototype(value, MSG): value = value.gettext() content = XMLContent.encode(value) input = make_element(choice, attributes, content) if multiline is True: # Une option par ligne html.append(make_element(u"div", content=input)) else: html.append(input) else: if type(data) == type("") or type(data) == type(u""): try: data_tmp = data.decode("utf-8").split('|') except: data_tmp = data else: data_tmp = data for option in datatype.get_namespace(data_tmp): js = [] opt_name = option['name'] attributes = merge_dicts(choice_attributes, value=opt_name) if option['selected'] or data == option['name'].strip().encode( 'utf-8'): attributes[choice_checked] = choice_checked if form.is_disabled_by_dependency(name, schema, fields): attributes[u"disabled"] = u"disabled" attributes.setdefault(u"class", []).append(u"disabled") # 0005970: Le Javascript pour (dés)activer les autres champs dep_names = form.get_dep_names(name, schema) for dep_name in dep_names: dependency = schema[dep_name].dependency if not dependency: continue # Add js action on simple boolean expressions. ie: "C11" if not Expression.is_simple(dependency): # Authorized operators are "==", "=" and "in" # ie:"C11=='others'", "C11='others'", "'others' in C11" if 'in' in dependency: dep_value = dependency.split('in', 1)[0].strip() elif '==' in dependency: exclusive, dep_value = dependency.rsplit('==', 1) elif '!=' in dependency: exclusive, dep_value = dependency.rsplit('!=', 1) else: msg = 'K col operator unknown in "%s"' % dependency log_warning(msg) continue if dep_value[:1] in ("u'", 'u"'): dep_value = dep_value[:2] dep_value = (dep_value.replace("'", '').replace('"', '')) #dep_value = checkid(dep_value) else: dep_value = 'false' js_code = ( '$("input[name=\\"%s\\"][value=\\"%s\\"]")' '.change(function(){switch_input($(this),"%s","%s");});' % (name, opt_name, dep_name, dep_value)) js.append(js_code) # 0005970 fin value = option['value'] if is_prototype(value, MSG): value = value.gettext() content = XMLContent.encode(value) input = make_element(choice, attributes, content) if multiline is True: # Une option par ligne html.append(make_element(u"div", content=input)) else: html.append(input) # Append JS code content = XMLContent.encode(u'\n'.join(js)) attributes = {u'type': "text/javascript"} html.append(make_element(u'script', attributes, content)) attributes = merge_dicts(container_attributes, id=u"field_{name}".format(name=name)) check_errors(context, form, datatype, name, data, schema, fields, readonly, attributes, tabindex=None) return make_element(container, attributes, u"".join(html))
def make_namespaces(context): # Shortcuts elements = context['elements'] references = context['references'] # Find all namespaces, and fill them with elements namespaces = {} for element in elements: qnames = element['qnames'] attributes = element['attributes'] data = element['data'] is_empty = element['is_empty'] refs = element['refs'] # Replace the references of "refs" while refs: new_refs = [] for ref in refs: try: ref = references[ref] except KeyError: raise KeyError, ('the define "%s" is missing in your ' 'relax NG file') % ref attributes.extend(ref['attributes']) new_refs.extend(ref['refs']) is_empty = is_empty and ref['is_empty'] ref_data = ref['data'] if ref_data is not None: if data is not None and data != ref_data: data = '' elif data is None: data = ref_data refs = new_refs # Now, data is good if data is not None: is_empty = False # Replace the references of "attributes" for attribute in attributes: refs = attribute['refs'] while refs: new_refs = [] for ref in refs: try: ref = references[ref] except KeyError: raise KeyError, ('the define "%s" is missing in your ' 'relax NG file') % ref new_refs.extend(ref['refs']) ref_data = ref['data'] attr_data = attribute['data'] if ref_data is not None: if attr_data is not None and attr_data != ref_data: attr_data = '' elif attr_data is None: attr_data = ref_data refs = new_refs # Update the good namespaces if qnames is not None: for uri, name in element['qnames']: own, free = split_attributes(uri, attributes) # Element + its attributes namespace = namespaces.setdefault(uri, { 'elements': {}, 'free_attributes': {} }) element = ElementSchema(name, default_datatype=String, is_empty=is_empty, attributes=own) namespace['elements'][name] = element # Free attributes for (uri, name), datatype in free.iteritems(): namespace = namespaces.setdefault(uri, { 'elements': {}, 'free_attributes': {} }) namespace['free_attributes'][name] = datatype result = {} prefix2uri = context['prefix'] for namespace, data in namespaces.iteritems(): # Find the prefix for prefix, uri in prefix2uri.iteritems(): if uri == namespace: result[uri] = XMLNamespace(uri, prefix, data['elements'].values(), data['free_attributes'], String) break else: log_warning('relaxng: namespace "%s" not found' % namespace) return result
def warn_not_stored(name): log_warning(MSG_NOT_STORED.format(name=name), 'itools.database')
def get_catalog_values(self): values = {} # Step 1. Automatically index fields languages = self.get_root().get_value('website_languages') for name, field in self.get_fields(): if not field.indexed and not field.stored: continue if field.multilingual: value = {} for language in languages: value[language] = field.get_value(self, name, language) values[name] = value else: values[name] = field.get_value(self, name) # Step 2. Index non-metadata properties # Path related fields abspath = self.abspath values['abspath'] = str(abspath) n = len(abspath) values['abspath_depth'] = n if n: values['parent_paths'] = [str(abspath[:i]) for i in range(n)] values['name'] = self.name values['is_content'] = self.is_content # Class related fields values['format'] = self.metadata.format values['base_classes'] = [] for cls in self.__class__.__mro__: class_id = getattr(cls, 'class_id', None) if class_id: values['base_classes'].append(class_id) # Links to other resources values['owner'] = self.get_owner() values['share'] = self.get_share() values['links'] = list(self.get_links()) values['onchange_reindex'] = self.get_onchange_reindex() # Full text context = get_context() try: server = context.server except AttributeError: server = None if server is not None and server.index_text: try: values['text'] = self.to_text() except NotImplementedError: pass except Exception: log = 'Indexation failed: %s' % abspath log_warning(log, domain='ikaaro') # Time events reminder, payload = self.next_time_event() values['next_time_event'] = reminder values['next_time_event_payload'] = dumps(payload) # Ok return values
def warn_not_indexed_nor_stored(name): log_warning(MSG_NOT_INDEXED_NOR_STORED.format(name=name), 'itools.database')
def get_products(self, context, product_format, categories=[], excluded_products=[]): shop = get_shop(self) table = self if self.get_property("use_shop_configuration"): table = shop.get_resource("cross-selling") if table.get_property("enabled") is False: return root = context.root products_quantity = table.get_property("products_quantity") # Base query query = [PhraseQuery("format", product_format), PhraseQuery("workflow_state", "public")] # Do not show now buyable products group_name = get_group_name(shop, context) q = PhraseQuery("not_buyable_by_groups", group_name) query.append(NotQuery(q)) # Excluded products query if excluded_products: exclude_query = [PhraseQuery("abspath", str(abspath)) for abspath in excluded_products] if len(exclude_query) > 1: exclude_query = OrQuery(*exclude_query) else: exclude_query = exclude_query[0] query.append(NotQuery(exclude_query)) # Filter on product title filter_text = table.get_property("filter_text") if filter_text: query.append(PhraseQuery("title", filter_text)) # Categories query mode_categories = table.get_property("categories") if mode_categories == "current_category": query_categorie = [PhraseQuery("parent_paths", str(x.get_abspath())) for x in categories] if len(query_categorie) > 1: query.append(OrQuery(*query_categorie)) elif len(query_categorie) == 1: query.append(query_categorie[0]) elif mode_categories == "one_category": query.append(PhraseQuery("parent_paths", table.get_property("specific_category"))) # Show reductions ? promotion = table.get_property("show_product_with_promotion") if promotion in ("0", "1"): query.append(PhraseQuery("has_reduction", bool(promotion))) # Product model product_model = table.get_property("product_model") if product_model: query.append(PhraseQuery("product_model", product_model)) # Tags if table.get_property("tags"): query.append(OrQuery(*[PhraseQuery("tags", x) for x in table.get_property("tags")])) # Selection in cross selling table handler = table.handler get_value = handler.get_record_value ids = list(handler.get_record_ids_in_order()) names = [] for id in ids[:products_quantity]: record = handler.get_record(id) path = get_value(record, "name") names.append(path) products_quantity -= 1 resource = self.get_resource(path, soft=True) if resource is None: log_warning("Error cross selling, %s" % path) elif resource.get_property("state") == "public": yield resource if products_quantity <= 0: return if names: names_query = [PhraseQuery("name", name) for name in names] if len(names_query) > 1: names_query = OrQuery(*names_query) else: names_query = names_query[0] query.append(NotQuery(names_query)) # Complete results sort = table.get_property("sort") if sort == "random": # Random selection results = root.search(AndQuery(*query)) # XXX It's not relevant to make a random cross selling # with more than 1000 products brains = list(results.get_documents(size=1000)) shuffle(brains) for brain in brains[:products_quantity]: yield root.get_resource(brain.abspath) elif sort == "last": results = root.search(AndQuery(*query)) brains = list(results.get_documents(sort_by="ctime", reverse=True, size=products_quantity)) for brain in brains: yield root.get_resource(brain.abspath)
def get_products(self, context, product_format, categories=[], excluded_products=[]): shop = get_shop(self) table = self if self.get_property('use_shop_configuration'): table = shop.get_resource('cross-selling') if table.get_property('enabled') is False: return root = context.root products_quantity = table.get_property('products_quantity') # Base query query = [ PhraseQuery('format', product_format), PhraseQuery('workflow_state', 'public') ] # Do not show now buyable products group_name = get_group_name(shop, context) q = PhraseQuery('not_buyable_by_groups', group_name) query.append(NotQuery(q)) # Excluded products query if excluded_products: exclude_query = [ PhraseQuery('abspath', str(abspath)) for abspath in excluded_products ] if len(exclude_query) > 1: exclude_query = OrQuery(*exclude_query) else: exclude_query = exclude_query[0] query.append(NotQuery(exclude_query)) # Filter on product title filter_text = table.get_property('filter_text') if filter_text: query.append(PhraseQuery('title', filter_text)) # Categories query mode_categories = table.get_property('categories') if mode_categories == 'current_category': query_categorie = [ PhraseQuery('parent_paths', str(x.get_abspath())) for x in categories ] if len(query_categorie) > 1: query.append(OrQuery(*query_categorie)) elif len(query_categorie) == 1: query.append(query_categorie[0]) elif mode_categories == 'one_category': query.append( PhraseQuery('parent_paths', table.get_property('specific_category'))) # Show reductions ? promotion = table.get_property('show_product_with_promotion') if promotion in ('0', '1'): query.append(PhraseQuery('has_reduction', bool(promotion))) # Product model product_model = table.get_property('product_model') if product_model: query.append(PhraseQuery('product_model', product_model)) # Tags if table.get_property('tags'): query.append( OrQuery(*[ PhraseQuery('tags', x) for x in table.get_property('tags') ])) # Selection in cross selling table handler = table.handler get_value = handler.get_record_value ids = list(handler.get_record_ids_in_order()) names = [] for id in ids[:products_quantity]: record = handler.get_record(id) path = get_value(record, 'name') names.append(path) products_quantity -= 1 resource = self.get_resource(path, soft=True) if resource is None: log_warning('Error cross selling, %s' % path) elif resource.get_property('state') == 'public': yield resource if products_quantity <= 0: return if names: names_query = [PhraseQuery('name', name) for name in names] if len(names_query) > 1: names_query = OrQuery(*names_query) else: names_query = names_query[0] query.append(NotQuery(names_query)) # Complete results sort = table.get_property('sort') if sort == 'random': # Random selection results = root.search(AndQuery(*query)) # XXX It's not relevant to make a random cross selling # with more than 1000 products brains = list(results.get_documents(size=1000)) shuffle(brains) for brain in brains[:products_quantity]: yield root.get_resource(brain.abspath) elif sort == 'last': results = root.search(AndQuery(*query)) brains = list( results.get_documents(sort_by='ctime', reverse=True, size=products_quantity)) for brain in brains: yield root.get_resource(brain.abspath)