diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_parseSearchString.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_parseSearchString.xml new file mode 100644 index 0000000000000000000000000000000000000000..0876b7d095e1f482fb5de5e5a0a57b0616c4bc9b --- /dev/null +++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_parseSearchString.xml @@ -0,0 +1,270 @@ +<?xml version="1.0"?> +<ZopeData> + <record id="1" aka="AAAAAAAAAAE="> + <pickle> + <tuple> + <global name="PythonScript" module="Products.PythonScripts.PythonScript"/> + <tuple/> + </tuple> + </pickle> + <pickle> + <dictionary> + <item> + <key> <string>Script_magic</string> </key> + <value> <int>3</int> </value> + </item> + <item> + <key> <string>_bind_names</string> </key> + <value> + <object> + <klass> + <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/> + </klass> + <tuple/> + <state> + <dictionary> + <item> + <key> <string>_asgns</string> </key> + <value> + <dictionary> + <item> + <key> <string>name_container</string> </key> + <value> <string>container</string> </value> + </item> + <item> + <key> <string>name_context</string> </key> + <value> <string>context</string> </value> + </item> + <item> + <key> <string>name_m_self</string> </key> + <value> <string>script</string> </value> + </item> + <item> + <key> <string>name_subpath</string> </key> + <value> <string>traverse_subpath</string> </value> + </item> + </dictionary> + </value> + </item> + </dictionary> + </state> + </object> + </value> + </item> + <item> + <key> <string>_body</string> </key> + <value> <string encoding="cdata"><![CDATA[ + +"""\n +Make SQLCatalog parse given search string and generate an Abstract Syntax Tree.\n +Then, walk this tree and categorize criterion by type (and their alias, see code).\n +"""\n +from DateTime import DateTime\n +\n +def render_filetype_list(filetype_list):\n + return [\'%%.%s\' % (x, ) for x in filetype_list]\n +\n +def render_state_list(state_list):\n + # Note: also used to render type list\n + result = []\n + append = result.append\n + for state in state_list:\n + if state != \'all\':\n + append(state)\n + return result\n +\n +def render_date_range(date_range_list):\n + result = []\n + append = result.append\n + now = DateTime()\n + for date_range in date_range_list:\n + # XXX: original version used a regex, but we can\'t import\n + # "re" module here, so fallback on hand-crafted parsing.\n + # Original regex: \'(\\d)([wmy]).*\'\n + # State meaning:\n + # 0: we expect only decimals\n + # 1: we expect one of \'w\', \'m\', or \'y\'\n + state = 0\n + duration_char_list = []\n + multiplicator = None\n + for char in date_range:\n + if state == 0:\n + if \'0\' <= char <= \'9\':\n + duration_char_list.append(char)\n + else:\n + state = 1\n + if state == 1:\n + if len(duration_char_list):\n + if char == \'w\':\n + multiplicator = 7\n + elif char == \'m\':\n + multiplicator = 30\n + elif char == \'y\':\n + multiplicator = 365\n + break\n + if multiplicator is not None:\n + duration = int(\'\'.join(duration_char_list))\n + append(now - duration * multiplicator)\n + return result\n +\n +criterion_alias_dict = {\n + \'state\': (\'simulation_state\', render_state_list),\n + \'type\': (\'portal_type\', render_state_list),\n + \'filetype\': (\'source_reference\', render_filetype_list),\n + \'file\': (\'source_reference\', None),\n + \'created\': (\'creation_from\', render_date_range),\n + \'simulation_state\': (True, None),\n + \'language\': (True, None),\n + \'version\': (True, None),\n + \'reference\': (True, None),\n + \'portal_type\': (True, None),\n + \'source_reference\': (True, None),\n + \'creation_from\': (True, None),\n + \'searchabletext\': (True, None),\n +}\n +\n +DEFAULT_CRITERION_ALIAS = \'searchabletext\'\n +\n +def resolveCriterion(criterion_alias, criterion_value_list):\n + initial_criterion_alias = criterion_alias\n + # XXX: should be a set\n + seen_alias_dict = {} # Protection against endless loops\n + while True:\n + next_alias, value_list_renderer = criterion_alias_dict.get(criterion_alias, (DEFAULT_CRITERION_ALIAS, None))\n + if value_list_renderer is not None:\n + criterion_value_list = value_list_renderer(criterion_value_list)\n + if next_alias is True:\n + break\n + seen_alias_dict[criterion_alias] = None\n + if next_alias in seen_alias_dict:\n + raise Exeption, \'Endless alias loop detected: lookup of %r reached alias %r twice\' % (initial_criterion_alias, next_alias)\n + criterion_alias = next_alias\n + return criterion_alias, criterion_value_list\n +\n +def recurseSyntaxNode(node, criterion=DEFAULT_CRITERION_ALIAS):\n + if node.isColumn():\n + result = recurseSyntaxNode(node.getSubNode(), criterion=node.getColumnName())\n + else:\n + result = {}\n + if node.isLeaf():\n + result[criterion] = [node.getValue()]\n + else:\n + for subnode in node.getNodeList():\n + for criterion, value_list in recurseSyntaxNode(subnode, criterion=criterion).items():\n + result.setdefault(criterion, []).extend(value_list)\n + return result\n +\n +def acceptAllColumns(column):\n + return True\n +\n +node = context.getPortalObject().portal_catalog.getSQLCatalog().parseSearchText(searchstring, search_key=\'FullTextKey\', is_valid=acceptAllColumns)\n +result = {}\n +if node is None:\n + result[\'searchabletext\'] = searchstring\n +else:\n + for criterion, value_list in recurseSyntaxNode(node).items():\n + criterion, value_list = resolveCriterion(criterion, value_list)\n + result.setdefault(criterion, []).extend(value_list)\n + filtered_result = {}\n + for criterion, value_list in result.items():\n + if len(value_list) > 0:\n + filtered_result[criterion] = value_list\n + result = filtered_result\n + for criterion, value_list in result.items():\n + # XXX: yuck\n + if criterion == \'searchabletext\':\n + result[\'searchabletext\'] = \' \'.join(value_list)\n + if len(value_list) == 1:\n + result[criterion] = value_list[0]\n + if \'searchabletext\' not in result:\n + result[\'searchabletext\'] = \'\'\n +return result\n + + +]]></string> </value> + </item> + <item> + <key> <string>_code</string> </key> + <value> + <none/> + </value> + </item> + <item> + <key> <string>_params</string> </key> + <value> <string>searchstring</string> </value> + </item> + <item> + <key> <string>errors</string> </key> + <value> + <tuple/> + </value> + </item> + <item> + <key> <string>func_code</string> </key> + <value> + <object> + <klass> + <global name="FuncCode" module="Shared.DC.Scripts.Signature"/> + </klass> + <tuple/> + <state> + <dictionary> + <item> + <key> <string>co_argcount</string> </key> + <value> <int>1</int> </value> + </item> + <item> + <key> <string>co_varnames</string> </key> + <value> + <tuple> + <string>searchstring</string> + <string>DateTime</string> + <string>render_filetype_list</string> + <string>render_state_list</string> + <string>render_date_range</string> + <string>None</string> + <string>True</string> + <string>criterion_alias_dict</string> + <string>DEFAULT_CRITERION_ALIAS</string> + <string>resolveCriterion</string> + <string>recurseSyntaxNode</string> + <string>acceptAllColumns</string> + <string>_getattr_</string> + <string>context</string> + <string>node</string> + <string>result</string> + <string>_write_</string> + <string>_getiter_</string> + <string>criterion</string> + <string>value_list</string> + <string>filtered_result</string> + <string>len</string> + <string>_getitem_</string> + </tuple> + </value> + </item> + </dictionary> + </state> + </object> + </value> + </item> + <item> + <key> <string>func_defaults</string> </key> + <value> + <none/> + </value> + </item> + <item> + <key> <string>id</string> </key> + <value> <string>Base_parseSearchString</string> </value> + </item> + <item> + <key> <string>warnings</string> </key> + <value> + <tuple/> + </value> + </item> + </dictionary> + </pickle> + </record> +</ZopeData> diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_showFoundText.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_showFoundText.xml index 9729ca3b8146335f09c43382828b164e7ab4553f..969de4f43182d2091284615ea37b07b326458add 100644 --- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_showFoundText.xml +++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_showFoundText.xml @@ -62,23 +62,58 @@ words in the text itself.\n """\n \n +is_gadget_mode = context.REQUEST.get(\'is_gadget_mode\', 0)\n +\n +if is_gadget_mode:\n + # in gadget mode less space is available thus show less text\n + max_text_length = 100\n + max_lines = 1\n +\n +def getRandomDocumentTextExcerpt(document_text):\n + # try to get somewhat arbitrary choice of searchable attrs\n + if isinstance(document_text, str) and document_text!=\'\':\n + document_text = document_text.decode(\'utf-8\', \'ignore\')\n + start = min(len(document_text) - 300, 200)\n + return \'... %s ...\' %document_text[start:start + max_text_length]\n + else:\n + return \'\'\n +\n +# get search words from listbox selection\n +argument_names = (\'advanced_search_text\', \n + \'your_search_text\',\n + \'title\',\n + \'reference\',\n + \'searchabletext\', \n + \'searchabletext_any\',\n + \'searchabletext_all\', \n + \'searchabletext_phrase\',)\n +\n +if document_text is None:\n + document_text = context.getSearchableText()\n +\n if selection is not None:\n params = selection.getParams()\n else:\n params = context.portal_selections.getSelectionParamsFor(\'search_result_selection\')\n -search_words = params.get(\'your_search_text\')\n \n -if search_words is None:\n - return \'\'\n -else:\n +params = [params.get(name, \'\') for name in argument_names]\n +# flatten value if it is list\n +params = [(hasattr(param, \'sort\') and \' \'.join(param) or param) for param in params]\n +search_string = \' \'.join(params).strip()\n \n - if document_text is None:\n - document_text = context.getSearchableText()\n +if search_string != \'\':\n + search_argument_list = context.Base_parseSearchString(search_string)\n + search_string = search_argument_list.get(\'searchabletext\', \'\')\n \n +if search_string == \'\':\n + # the searched words are empty (e.g. because we used only parameters \n + # without pure searchable text)\n + return getRandomDocumentTextExcerpt(document_text)\n +else:\n found_text_fragments = context.Base_getExcerptText(\n context, \\\n document_text, \\\n - search_words, \\\n + search_string, \\\n tags = (\'<em>\', \'</em>\'), \\\n trail = 5, \\\n maxlines = max_lines)\n @@ -131,11 +166,21 @@ else:\n <string>selection</string> <string>max_lines</string> <string>max_text_length</string> - <string>None</string> <string>_getattr_</string> - <string>params</string> <string>context</string> - <string>search_words</string> + <string>is_gadget_mode</string> + <string>getRandomDocumentTextExcerpt</string> + <string>argument_names</string> + <string>None</string> + <string>params</string> + <string>append</string> + <string>$append0</string> + <string>_getiter_</string> + <string>name</string> + <string>param</string> + <string>hasattr</string> + <string>search_string</string> + <string>search_argument_list</string> <string>found_text_fragments</string> <string>map</string> <string>str</string> diff --git a/product/ERP5/bootstrap/erp5_core/bt/revision b/product/ERP5/bootstrap/erp5_core/bt/revision index 872362fa15d58b62629786cae412c8e6f08a1760..7ff9fa9bd870374d4773866c2746abb552fa7cf5 100644 --- a/product/ERP5/bootstrap/erp5_core/bt/revision +++ b/product/ERP5/bootstrap/erp5_core/bt/revision @@ -1 +1 @@ -1226 \ No newline at end of file +1227 \ No newline at end of file