rer.solrpush 1.3.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. rer/solrpush/__init__.py +7 -0
  2. rer/solrpush/adapters/__init__.py +1 -0
  3. rer/solrpush/adapters/configure.zcml +13 -0
  4. rer/solrpush/adapters/file.py +52 -0
  5. rer/solrpush/adapters/rss_feed.py +28 -0
  6. rer/solrpush/behaviors/__init__.py +0 -0
  7. rer/solrpush/behaviors/configure.zcml +16 -0
  8. rer/solrpush/behaviors/solr_fields.py +41 -0
  9. rer/solrpush/browser/__init__.py +0 -0
  10. rer/solrpush/browser/configure.zcml +111 -0
  11. rer/solrpush/browser/controlpanel.py +96 -0
  12. rer/solrpush/browser/elevate_settings.py +35 -0
  13. rer/solrpush/browser/maintenance.py +476 -0
  14. rer/solrpush/browser/query_debug.py +12 -0
  15. rer/solrpush/browser/querybuilder.py +168 -0
  16. rer/solrpush/browser/scales.py +80 -0
  17. rer/solrpush/browser/static/.gitkeep +0 -0
  18. rer/solrpush/browser/static/dist/dev/main.js +58240 -0
  19. rer/solrpush/browser/static/dist/dev/main.js.map +180 -0
  20. rer/solrpush/browser/static/dist/prod/main.js +9604 -0
  21. rer/solrpush/browser/static/dist/prod/main.js.map +1 -0
  22. rer/solrpush/browser/static/javascripts/FacetsContainer/index.js +46 -0
  23. rer/solrpush/browser/static/javascripts/ProgressBar/index.js +40 -0
  24. rer/solrpush/browser/static/javascripts/ProgressBarContainer/index.js +66 -0
  25. rer/solrpush/browser/static/javascripts/index.js +14 -0
  26. rer/solrpush/browser/static/package.json +13 -0
  27. rer/solrpush/browser/static/styles.css +22 -0
  28. rer/solrpush/browser/static/styles.css.map +1 -0
  29. rer/solrpush/browser/static/styles.scss +27 -0
  30. rer/solrpush/browser/static/yarn.lock +1408 -0
  31. rer/solrpush/browser/templates/controlpanel_layout.pt +49 -0
  32. rer/solrpush/browser/templates/query_debug.pt +17 -0
  33. rer/solrpush/browser/templates/reindex_solr.pt +35 -0
  34. rer/solrpush/configure.zcml +81 -0
  35. rer/solrpush/indexer.py +120 -0
  36. rer/solrpush/interfaces/__init__.py +8 -0
  37. rer/solrpush/interfaces/adapter.py +13 -0
  38. rer/solrpush/interfaces/elevate.py +98 -0
  39. rer/solrpush/interfaces/layer.py +6 -0
  40. rer/solrpush/interfaces/queueprocessor.py +6 -0
  41. rer/solrpush/interfaces/settings.py +194 -0
  42. rer/solrpush/locales/README.rst +37 -0
  43. rer/solrpush/locales/__init__.py +0 -0
  44. rer/solrpush/locales/en/LC_MESSAGES/rer.solrpush.mo +0 -0
  45. rer/solrpush/locales/en/LC_MESSAGES/rer.solrpush.po +399 -0
  46. rer/solrpush/locales/it/LC_MESSAGES/rer.solrpush.mo +0 -0
  47. rer/solrpush/locales/it/LC_MESSAGES/rer.solrpush.po +399 -0
  48. rer/solrpush/locales/rer.solrpush.pot +402 -0
  49. rer/solrpush/locales/update.py +71 -0
  50. rer/solrpush/locales/update.sh +12 -0
  51. rer/solrpush/parser.py +211 -0
  52. rer/solrpush/permissions.zcml +18 -0
  53. rer/solrpush/profiles/default/actions.xml +21 -0
  54. rer/solrpush/profiles/default/browserlayer.xml +7 -0
  55. rer/solrpush/profiles/default/catalog.xml +4 -0
  56. rer/solrpush/profiles/default/controlpanel.xml +31 -0
  57. rer/solrpush/profiles/default/metadata.xml +8 -0
  58. rer/solrpush/profiles/default/registry/bundle.xml +25 -0
  59. rer/solrpush/profiles/default/registry/criteria.xml +55 -0
  60. rer/solrpush/profiles/default/registry/settings.xml +6 -0
  61. rer/solrpush/profiles/default/rolemap.xml +13 -0
  62. rer/solrpush/profiles/default/types/Document.xml +12 -0
  63. rer/solrpush/profiles/to_1100/componentregistry.xml +9 -0
  64. rer/solrpush/profiles/to_1100/metadata.xml +5 -0
  65. rer/solrpush/profiles/to_1300/controlpanel.xml +13 -0
  66. rer/solrpush/profiles/to_1300/registry.xml +5 -0
  67. rer/solrpush/profiles/uninstall/browserlayer.xml +7 -0
  68. rer/solrpush/profiles/uninstall/controlpanel.xml +21 -0
  69. rer/solrpush/profiles/uninstall/registry.xml +17 -0
  70. rer/solrpush/restapi/__init__.py +0 -0
  71. rer/solrpush/restapi/configure.zcml +8 -0
  72. rer/solrpush/restapi/services/__init__.py +0 -0
  73. rer/solrpush/restapi/services/configure.zcml +8 -0
  74. rer/solrpush/restapi/services/elevate_schema/__init__.py +0 -0
  75. rer/solrpush/restapi/services/elevate_schema/configure.zcml +14 -0
  76. rer/solrpush/restapi/services/elevate_schema/get.py +12 -0
  77. rer/solrpush/restapi/services/solr_search/__init__.py +0 -0
  78. rer/solrpush/restapi/services/solr_search/batch.py +129 -0
  79. rer/solrpush/restapi/services/solr_search/configure.zcml +14 -0
  80. rer/solrpush/restapi/services/solr_search/get.py +25 -0
  81. rer/solrpush/restapi/services/solr_search/solr_search_handler.py +86 -0
  82. rer/solrpush/setuphandlers.py +20 -0
  83. rer/solrpush/testing.py +161 -0
  84. rer/solrpush/tests/__init__.py +0 -0
  85. rer/solrpush/tests/docs/example.docx +0 -0
  86. rer/solrpush/tests/docs/example.ods +0 -0
  87. rer/solrpush/tests/docs/example.pdf +0 -0
  88. rer/solrpush/tests/docs/plone_logo.png +0 -0
  89. rer/solrpush/tests/test_behaviors.py +61 -0
  90. rer/solrpush/tests/test_elevate.py +124 -0
  91. rer/solrpush/tests/test_events.py +181 -0
  92. rer/solrpush/tests/test_image_tags.py +85 -0
  93. rer/solrpush/tests/test_maintenance.py +165 -0
  94. rer/solrpush/tests/test_querybuilder.py +272 -0
  95. rer/solrpush/tests/test_restapi_search.py +127 -0
  96. rer/solrpush/tests/test_search.py +177 -0
  97. rer/solrpush/tests/test_setup.py +67 -0
  98. rer/solrpush/tests/test_solr_interface.py +359 -0
  99. rer/solrpush/tests/test_tika.py +85 -0
  100. rer/solrpush/tests/test_vocabularies.py +113 -0
  101. rer/solrpush/upgrades.py +76 -0
  102. rer/solrpush/upgrades.zcml +86 -0
  103. rer/solrpush/utils/__init__.py +6 -0
  104. rer/solrpush/utils/solr_common.py +122 -0
  105. rer/solrpush/utils/solr_indexer.py +295 -0
  106. rer/solrpush/utils/solr_search.py +352 -0
  107. rer/solrpush/vocabularies.py +50 -0
  108. rer.solrpush-1.3.4-py3.10-nspkg.pth +1 -0
  109. rer.solrpush-1.3.4.dist-info/LICENSE.GPL +339 -0
  110. rer.solrpush-1.3.4.dist-info/LICENSE.rst +15 -0
  111. rer.solrpush-1.3.4.dist-info/METADATA +507 -0
  112. rer.solrpush-1.3.4.dist-info/RECORD +116 -0
  113. rer.solrpush-1.3.4.dist-info/WHEEL +5 -0
  114. rer.solrpush-1.3.4.dist-info/entry_points.txt +5 -0
  115. rer.solrpush-1.3.4.dist-info/namespace_packages.txt +1 -0
  116. rer.solrpush-1.3.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,113 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Setup tests for this package."""
3
+ from plone.app.testing import setRoles
4
+ from plone.app.testing import TEST_USER_ID
5
+ from rer.solrpush.utils.solr_common import init_solr_push
6
+ from rer.solrpush.utils.solr_common import get_solr_connection
7
+ from rer.solrpush.utils import push_to_solr
8
+ from rer.solrpush.testing import RER_SOLRPUSH_API_FUNCTIONAL_TESTING
9
+ from transaction import commit
10
+ from zope.component import getUtility
11
+ from zope.schema.interfaces import IVocabularyFactory
12
+
13
+ import unittest
14
+
15
+
16
+ class TestVocabularies(unittest.TestCase):
17
+ """Test show in search field behavior."""
18
+
19
+ layer = RER_SOLRPUSH_API_FUNCTIONAL_TESTING
20
+
21
+ def setUp(self):
22
+ """
23
+ We create objects before initializing solr settings, so solr core is
24
+ always empty on setUp.
25
+ """
26
+ self.portal = self.layer["portal"]
27
+ self.request = self.layer["request"]
28
+ setRoles(self.portal, TEST_USER_ID, ["Manager"])
29
+
30
+ init_solr_push()
31
+ commit()
32
+
33
+ push_to_solr(
34
+ {
35
+ "title": "Document from site foo",
36
+ "portal_type": "Document",
37
+ "site_name": "foo",
38
+ "UID": "000",
39
+ }
40
+ )
41
+ push_to_solr(
42
+ {
43
+ "title": "Document from site foo with subject",
44
+ "portal_type": "Document",
45
+ "site_name": "foo",
46
+ "Subject": ["foo-subject"],
47
+ "UID": "111",
48
+ }
49
+ )
50
+ push_to_solr(
51
+ {
52
+ "title": "Document from site bar",
53
+ "portal_type": "Document",
54
+ "site_name": "bar",
55
+ "UID": "222",
56
+ }
57
+ )
58
+ push_to_solr(
59
+ {
60
+ "title": "News Item from site bar",
61
+ "portal_type": "News Item",
62
+ "site_name": "bar",
63
+ "Subject": ["bar-subject"],
64
+ "UID": "333",
65
+ }
66
+ )
67
+
68
+ def tearDown(self):
69
+ solr = get_solr_connection()
70
+ solr.delete(q="*:*", commit=True)
71
+ commit()
72
+
73
+ def test_sites_vocabulary_return_all_stored_ones(self):
74
+ factory = getUtility(
75
+ IVocabularyFactory, "rer.solrpush.vocabularies.AvailableSites"
76
+ )
77
+ vocabulary = factory(self.portal)
78
+
79
+ self.assertEqual(len(vocabulary), 2)
80
+ self.assertEqual([x.title for x in vocabulary], ["bar", "foo"])
81
+ self.assertEqual(vocabulary.getTerm("foo").title, "foo")
82
+ self.assertEqual(vocabulary.getTerm("bar").title, "bar")
83
+
84
+ def test_Subject_vocabulary_return_all_stored_ones(self):
85
+ factory = getUtility(
86
+ IVocabularyFactory, "rer.solrpush.vocabularies.AvailableSubjects"
87
+ )
88
+ vocabulary = factory(self.portal)
89
+
90
+ self.assertEqual(len(vocabulary), 2)
91
+ self.assertEqual(
92
+ [x.title for x in vocabulary], ["bar-subject", "foo-subject"]
93
+ )
94
+ self.assertEqual(
95
+ vocabulary.getTerm("foo-subject").title, "foo-subject"
96
+ )
97
+ self.assertEqual(
98
+ vocabulary.getTerm("bar-subject").title, "bar-subject"
99
+ )
100
+
101
+ def test_portal_type_vocabulary_return_all_stored_ones(self):
102
+ factory = getUtility(
103
+ IVocabularyFactory,
104
+ "rer.solrpush.vocabularies.AvailablePortalTypes",
105
+ )
106
+ vocabulary = factory(self.portal)
107
+
108
+ self.assertEqual(len(vocabulary), 2)
109
+ self.assertEqual(
110
+ [x.title for x in vocabulary], ["Document", "News Item"]
111
+ )
112
+ self.assertEqual(vocabulary.getTerm("Document").title, "Document")
113
+ self.assertEqual(vocabulary.getTerm("News Item").title, "News Item")
@@ -0,0 +1,76 @@
1
+ # -*- coding: utf-8 -*-
2
+ from plone import api
3
+ from plone.app.upgrade.utils import installOrReinstallProduct
4
+ from rer.solrpush.utils.solr_common import init_solr_push
5
+ from rer.solrpush.utils.solr_indexer import push_to_solr
6
+
7
+ import logging
8
+
9
+ logger = logging.getLogger(__name__)
10
+
11
+ default_profile = "profile-rer.solrpush:default"
12
+
13
+
14
+ def update_profile(context, name):
15
+ setup_tool = api.portal.get_tool(name="portal_setup")
16
+ setup_tool.runImportStepFromProfile(default_profile, name)
17
+
18
+
19
+ def update_registry(context):
20
+ update_profile(context=context, name="plone.app.registry")
21
+
22
+
23
+ def update_controlpanel(context):
24
+ update_profile(context=context, name="controlpanel")
25
+
26
+
27
+ def update_actions(context):
28
+ update_profile(context=context, name="actions")
29
+
30
+
31
+ def to_1100(context):
32
+ setup_tool = api.portal.get_tool(name="portal_setup")
33
+ setup_tool.runAllImportStepsFromProfile("profile-rer.solrpush:to_1100")
34
+ setup_tool.runImportStepFromProfile(default_profile, "plone.app.registry")
35
+
36
+
37
+ def to_1200(context):
38
+ setup_tool = api.portal.get_tool(name="portal_setup")
39
+ setup_tool.runImportStepFromProfile(default_profile, "plone.app.registry")
40
+ setup_tool.runImportStepFromProfile(default_profile, "rolemap")
41
+ setup_tool.runImportStepFromProfile(default_profile, "controlpanel")
42
+
43
+
44
+ def to_1300(context):
45
+ setup_tool = api.portal.get_tool(name="portal_setup")
46
+ setup_tool.runAllImportStepsFromProfile("profile-rer.solrpush:to_1300")
47
+
48
+
49
+ def to_1400(context):
50
+ installOrReinstallProduct(
51
+ api.portal.get(), "collective.z3cform.jsonwidget"
52
+ )
53
+ update_registry(context)
54
+ update_controlpanel(context)
55
+
56
+
57
+ def to_1600(context):
58
+ # add new criteria
59
+ update_registry(context)
60
+ # reload schema
61
+ init_solr_push()
62
+ brains = api.content.find(portal_type=["File", "Image"])
63
+ tot = len(brains)
64
+ logger.info("Reindexing {} contents for updated mime_types.".format(tot))
65
+ i = 0
66
+
67
+ for brain in brains:
68
+ i += 1
69
+ if i % 500 == 0:
70
+ logger.info("[PROGRESS] - {}/{}".format(i, tot))
71
+ item = brain.getObject()
72
+ try:
73
+ push_to_solr(item)
74
+ except Exception:
75
+ # solr can't index it, pass
76
+ continue
@@ -0,0 +1,86 @@
1
+ <configure
2
+ xmlns="http://namespaces.zope.org/zope"
3
+ xmlns:genericsetup="http://namespaces.zope.org/genericsetup">
4
+
5
+ <genericsetup:upgradeStep
6
+ source="1000"
7
+ destination="1100"
8
+ title="Remove persistent utility"
9
+ description=""
10
+ profile="rer.solrpush:default"
11
+ handler=".upgrades.to_1100"
12
+ />
13
+
14
+ <genericsetup:upgradeStep
15
+ source="1100"
16
+ destination="1200"
17
+ title="Added css resource and bundle"
18
+ description="Styles for controlpanel"
19
+ profile="rer.solrpush:default"
20
+ handler=".upgrades.to_1200"
21
+ />
22
+ <genericsetup:upgradeStep
23
+ source="1200"
24
+ destination="1300"
25
+ title="Remove unused control panel"
26
+ description=""
27
+ profile="rer.solrpush:default"
28
+ handler=".upgrades.to_1300"
29
+ />
30
+ <genericsetup:upgradeStep
31
+ source="1300"
32
+ destination="1400"
33
+ title="Remove unused control panel"
34
+ description=""
35
+ profile="rer.solrpush:default"
36
+ handler=".upgrades.to_1400"
37
+ />
38
+ <genericsetup:upgradeStep
39
+ source="1400"
40
+ destination="1500"
41
+ title="Add new field in controlpanel: force_commit"
42
+ description=""
43
+ profile="rer.solrpush:default"
44
+ handler=".upgrades.update_registry"
45
+ />
46
+ <genericsetup:upgradeStep
47
+ source="1500"
48
+ destination="1600"
49
+ title="Add new criteria and reindex files"
50
+ description=""
51
+ profile="rer.solrpush:default"
52
+ handler=".upgrades.to_1600"
53
+ />
54
+ <genericsetup:upgradeStep
55
+ source="1600"
56
+ destination="1700"
57
+ title="Add new field in controlpanel: query_debug"
58
+ description=""
59
+ profile="rer.solrpush:default"
60
+ handler=".upgrades.update_registry"
61
+ />
62
+ <genericsetup:upgradeStep
63
+ source="1700"
64
+ destination="1800"
65
+ title="Add new criteria: solr_portal_types"
66
+ description=""
67
+ profile="rer.solrpush:default"
68
+ handler=".upgrades.update_registry"
69
+ />
70
+ <genericsetup:upgradeStep
71
+ source="1800"
72
+ destination="1900"
73
+ title="Add elevate control panel link in user actions"
74
+ description=""
75
+ profile="rer.solrpush:default"
76
+ handler=".upgrades.update_actions"
77
+ />
78
+ <genericsetup:upgradeStep
79
+ source="1900"
80
+ destination="2000"
81
+ title="Add search_enabled setting field"
82
+ description=""
83
+ profile="rer.solrpush:default"
84
+ handler=".upgrades.update_registry"
85
+ />
86
+ </configure>
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ from rer.solrpush.utils.solr_common import init_solr_push # noqa
3
+ from rer.solrpush.utils.solr_indexer import push_to_solr # noqa
4
+ from rer.solrpush.utils.solr_indexer import remove_from_solr # noqa
5
+ from rer.solrpush.utils.solr_indexer import reset_solr # noqa
6
+ from rer.solrpush.utils.solr_search import search # noqa
@@ -0,0 +1,122 @@
1
+ # -*- coding: utf-8 -*-
2
+ from lxml import etree
3
+ from plone import api
4
+ from rer.solrpush import _
5
+ from rer.solrpush.interfaces.settings import IRerSolrpushSettings
6
+
7
+ import json
8
+ import pysolr
9
+ import requests
10
+ import six
11
+
12
+
13
+ def get_setting(field, interface=IRerSolrpushSettings):
14
+ return api.portal.get_registry_record(
15
+ field, interface=interface, default=False
16
+ )
17
+
18
+
19
+ def set_setting(field, value, interface=IRerSolrpushSettings):
20
+ return api.portal.set_registry_record(
21
+ field, interface=interface, value=value
22
+ )
23
+
24
+
25
+ def is_solr_active():
26
+ """Just checking if solr indexing is set to active in control panel"""
27
+ return get_setting(field="active")
28
+
29
+
30
+ def should_force_commit():
31
+ return get_setting(field="force_commit") or False
32
+
33
+
34
+ def get_index_fields():
35
+ json_str = get_setting(field="index_fields")
36
+ return json.loads(json_str)
37
+
38
+
39
+ def extract_fields(nodes):
40
+ fields = {}
41
+ for node in nodes:
42
+ field_name = node.get("name")
43
+ field_type = node.get("type")
44
+ if six.PY2:
45
+ field_name = six.text_type(field_name)
46
+ field_type = six.text_type(field_type)
47
+ fields[field_name] = {"type": field_type}
48
+ return fields
49
+
50
+
51
+ def init_solr_push():
52
+ """Inizializza la voce di registro 'index_fields'
53
+
54
+ Lo fa leggendo il file xml di SOLR.
55
+
56
+ :param solr_url: [required] L'url a cui richiedere il file xml
57
+ :type solr_url: string
58
+ :returns: Empty String if everything's good
59
+ :rtype: String
60
+ """
61
+ solr_url = get_setting(field="solr_url")
62
+
63
+ if solr_url:
64
+ if not solr_url.endswith("/"):
65
+ solr_url = solr_url + "/"
66
+ try:
67
+ respo = requests.get(solr_url + "admin/file?file=schema.xml")
68
+ except requests.exceptions.RequestException as err:
69
+ ErrorMessage = "Connection problem:\n{0}".format(err)
70
+ return ErrorMessage
71
+ if respo.status_code != 200:
72
+ ErrorMessage = "Problems fetching schema:\n{0}\n{1}".format(
73
+ respo.status_code, respo.reason
74
+ )
75
+ return ErrorMessage
76
+
77
+ root = etree.fromstring(respo.content)
78
+ chosen_fields = json.dumps(
79
+ extract_fields(nodes=root.findall(".//field"))
80
+ )
81
+ if six.PY2:
82
+ chosen_fields = chosen_fields.decode("utf-8")
83
+ set_setting(field="index_fields", value=chosen_fields)
84
+ set_setting(field="ready", value=True)
85
+ return
86
+
87
+ return _("No SOLR url provided")
88
+
89
+
90
+ def get_solr_connection(context=None, **kwargs):
91
+ # TODO: fix sporadic
92
+ # ResourceWarning: Enable tracemalloc to get the object allocation traceback
93
+ # ResourceWarning: unclosed <socket.socket ... raddr=('127.0.0.1', 8983)>
94
+ # -> avoid explicit close connection
95
+
96
+ # XXX: rivalutare il default True per always_commit !
97
+ if "always_commit" not in kwargs:
98
+ kwargs["always_commit"] = True
99
+ is_ready = get_setting(field="ready")
100
+ solr_url = get_setting(field="solr_url")
101
+ if not is_ready or not solr_url:
102
+ return
103
+ if context is None:
104
+ context = api.portal.get()
105
+ if context is None:
106
+ client = pysolr.Solr(solr_url, **kwargs)
107
+ else:
108
+ jar = getattr(context, "_p_jar", None)
109
+ oid = getattr(context, "_p_oid", None)
110
+ if jar is None or oid is None:
111
+ # object is not persistent or is not yet associated with a
112
+ # connection
113
+ cache = context._v_solr_client_cache = {}
114
+ else:
115
+ cache = getattr(jar, "foreign_connections", None)
116
+ if cache is None:
117
+ cache = jar.foreign_connections = {}
118
+ cache_key = "solr_%s_%s" % (solr_url, kwargs)
119
+ client = cache.get(cache_key)
120
+ if client is None:
121
+ client = cache[cache_key] = pysolr.Solr(solr_url, **kwargs)
122
+ return client
@@ -0,0 +1,295 @@
1
+ # -*- coding: utf-8 -*-
2
+ from DateTime import DateTime
3
+ from plone import api
4
+ from plone.indexer.interfaces import IIndexableObject
5
+ from plone.registry.interfaces import IRegistry
6
+ from rer.solrpush import _
7
+ from rer.solrpush.interfaces.adapter import IExtractFileFromTika
8
+ from six.moves import map
9
+ from zope.component import getUtility
10
+ from zope.component import queryMultiAdapter
11
+ from rer.solrpush.utils.solr_common import get_solr_connection
12
+ from rer.solrpush.utils.solr_common import get_setting
13
+ from rer.solrpush.utils.solr_common import get_index_fields
14
+ from rer.solrpush.utils.solr_common import is_solr_active
15
+ from rer.solrpush.utils.solr_common import should_force_commit
16
+
17
+ import datetime
18
+ import logging
19
+ import pysolr
20
+ import six
21
+
22
+
23
+ if six.PY2:
24
+ from ftfy import fix_text
25
+
26
+ try:
27
+ # rer.agidtheme overrides site tile field
28
+ from rer.agidtheme.base.interfaces import IRERSiteSchema as ISiteSchema
29
+ from rer.agidtheme.base.utility.interfaces import ICustomFields
30
+
31
+ RER_THEME = True
32
+ except ImportError:
33
+ from Products.CMFPlone.interfaces.controlpanel import ISiteSchema
34
+
35
+ RER_THEME = False
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+ ADDITIONAL_FIELDS = ["searchwords"]
40
+
41
+ RESTAPI_METADATA_FIELDS = ["@id", "@type", "description", "title"]
42
+
43
+ # HELPER METHODS
44
+
45
+
46
+ def get_site_title():
47
+ registry = getUtility(IRegistry)
48
+ site_settings = registry.forInterface(
49
+ ISiteSchema, prefix="plone", check=False
50
+ )
51
+ site_title = getattr(site_settings, "site_title") or ""
52
+ if RER_THEME:
53
+ site_subtitle_style = (
54
+ getattr(site_settings, "site_subtitle_style") or ""
55
+ )
56
+ fields_value = getUtility(ICustomFields)
57
+ site_title = fields_value.titleLang(site_title)
58
+ site_subtitle = fields_value.subtitleLang(
59
+ getattr(site_settings, "site_subtitle") or "{}"
60
+ )
61
+ if site_subtitle and site_subtitle_style == "subtitle-normal":
62
+ site_title += " {}".format(site_subtitle)
63
+
64
+ if six.PY2:
65
+ site_title = site_title.decode("utf-8")
66
+ return site_title
67
+
68
+
69
+ def parse_date_as_datetime(value):
70
+ """Sistemiamo le date"""
71
+ if value:
72
+ format = "%Y-%m-%dT%H:%M:%S"
73
+ return value.utcdatetime().strftime(format) + "Z"
74
+ return value
75
+
76
+
77
+ def parse_date_str(value):
78
+ if isinstance(value, datetime.date):
79
+ value = datetime.datetime.combine(value, datetime.time.min)
80
+ return parse_date_as_datetime(DateTime(value))
81
+
82
+
83
+ def attachment_to_index(item):
84
+ """
85
+ If item has a provider to extract text, return the file to be indexed
86
+ """
87
+ try:
88
+ provider = IExtractFileFromTika(item)
89
+ return provider.get_file_to_index()
90
+ except TypeError:
91
+ return None
92
+
93
+
94
+ def is_right_portal_type(item):
95
+ enabled_types = get_setting(field="enabled_types")
96
+ if not enabled_types:
97
+ return True
98
+ return item.portal_type in enabled_types
99
+
100
+
101
+ def can_index(item):
102
+ """Check if the item passed as argument can and has to be indexed"""
103
+ with api.env.adopt_roles(["Anonymous"]):
104
+ if not api.user.has_permission("View", obj=item):
105
+ return False
106
+ if not is_solr_active():
107
+ return False
108
+ return is_right_portal_type(item)
109
+
110
+
111
+ def create_index_dict(item):
112
+ """Restituisce un dizionario pronto per essere 'mandato' a SOLR per
113
+ l'indicizzazione.
114
+ """
115
+ index_fields = get_index_fields()
116
+ frontend_url = get_setting(field="frontend_url")
117
+ if frontend_url:
118
+ frontend_url = frontend_url.rstrip("/")
119
+
120
+ catalog = api.portal.get_tool(name="portal_catalog")
121
+ adapter = queryMultiAdapter((item, catalog), IIndexableObject)
122
+ index_me = {}
123
+ for field, field_infos in index_fields.items():
124
+ if field in RESTAPI_METADATA_FIELDS:
125
+ # skip. These fields are only metadata fields needed in restap-like
126
+ # repsonses and can be copied in solr configuration.
127
+ continue
128
+ field_type = field_infos.get("type")
129
+ if six.PY2:
130
+ field = field.encode("ascii")
131
+ value = getattr(adapter, field, None)
132
+ if not value and value is not False:
133
+ continue
134
+ if callable(value):
135
+ value = value()
136
+ if six.PY2:
137
+ value = fix_py2_strings(value)
138
+ if isinstance(value, DateTime):
139
+ value = parse_date_as_datetime(value)
140
+ else:
141
+ if field_type == "date":
142
+ value = parse_date_str(value)
143
+ index_me[field] = value
144
+
145
+ for field in ADDITIONAL_FIELDS:
146
+ value = getattr(item, field, None)
147
+ if value is not None:
148
+ index_me[field] = value
149
+ portal = api.portal.get()
150
+
151
+ # extra-catalog schema
152
+ index_me["site_name"] = get_site_title()
153
+ index_me["path"] = "/".join(item.getPhysicalPath())
154
+ index_me["path_depth"] = len(item.getPhysicalPath()) - 2
155
+ index_me["attachment"] = attachment_to_index(item)
156
+ index_me["getIcon"] = False
157
+ if "view_name" in index_fields.keys():
158
+ index_me["view_name"] = item.getLayout()
159
+
160
+ # convert url to frontend one
161
+ if frontend_url:
162
+ index_me["url"] = item.absolute_url().replace(
163
+ portal.portal_url(), frontend_url
164
+ )
165
+ else:
166
+ index_me["url"] = item.absolute_url()
167
+
168
+ has_image = getattr(item.aq_base, "image", None)
169
+ if has_image:
170
+ index_me["getIcon"] = True
171
+
172
+ return index_me
173
+
174
+
175
+ def fix_py2_strings(value):
176
+ """REMOVE ON PYTHON 3"""
177
+ if isinstance(value, six.string_types):
178
+ if not isinstance(value, six.text_type):
179
+ value = value.replace("\xc0?", "").decode("utf-8", "ignore")
180
+ return fix_text(value)
181
+ if isinstance(value, list):
182
+ return list(map(fix_py2_strings, value))
183
+ if isinstance(value, tuple):
184
+ return tuple(map(fix_py2_strings, value))
185
+ return value
186
+
187
+
188
+ def encode_strings_for_attachments(value):
189
+ """
190
+ Needed because attachments POST use standard requests encoding
191
+ and not utf-8
192
+ """
193
+ if isinstance(value, six.string_types):
194
+ return value.encode("ISO-8859-1", "ignore")
195
+ if isinstance(value, list):
196
+ return list(map(encode_strings_for_attachments, value))
197
+ if isinstance(value, tuple):
198
+ return tuple(map(encode_strings_for_attachments, value))
199
+ return value
200
+
201
+
202
+ def add_with_attachment(solr, attachment, fields):
203
+ params = {
204
+ "extractOnly": "false",
205
+ "lowernames": "false",
206
+ "fmap.content": "SearchableText",
207
+ "fmap.title": "SearchableText",
208
+ "literalsOverride": "true",
209
+ "commit": should_force_commit() and "true" or "false",
210
+ }
211
+ params.update(
212
+ {
213
+ "literal.{key}".format(key=key): encode_strings_for_attachments(
214
+ value
215
+ )
216
+ for (key, value) in fields.items()
217
+ }
218
+ )
219
+ path = "update/extract"
220
+ return solr._send_request(
221
+ method="post",
222
+ path=path,
223
+ body=params,
224
+ files={"file": ("extracted", attachment)},
225
+ )
226
+
227
+
228
+ # END HELPER METHODS
229
+
230
+ # LIBRARY METHODS
231
+
232
+
233
+ def push_to_solr(item_or_obj):
234
+ """
235
+ Perform push to solr
236
+ """
237
+ solr = get_solr_connection()
238
+ if not solr:
239
+ logger.error("Unable to push to solr. Configuration is incomplete.")
240
+ return
241
+ context = item_or_obj
242
+ if isinstance(item_or_obj, dict):
243
+ obj = api.content.get(UID=item_or_obj.get("UID", ""))
244
+ if obj:
245
+ context = obj
246
+ if not can_index(context):
247
+ return False
248
+ index_dict = create_index_dict(context)
249
+ attachment = index_dict.pop("attachment", None)
250
+ if attachment:
251
+ add_with_attachment(
252
+ solr=solr, attachment=attachment, fields=index_dict
253
+ )
254
+ else:
255
+ solr.add(docs=[index_dict], commit=should_force_commit())
256
+ return True
257
+
258
+
259
+ def remove_from_solr(uid):
260
+ """
261
+ Perform remove item from solr
262
+ """
263
+ if not is_solr_active():
264
+ return
265
+ solr = get_solr_connection()
266
+ portal = api.portal.get()
267
+ if not solr:
268
+ logger.error("Unable to push to solr. Configuration is incomplete.")
269
+ return
270
+ try:
271
+ solr.delete(
272
+ q="UID:{}".format(uid),
273
+ commit=should_force_commit(),
274
+ )
275
+ except (pysolr.SolrError, TypeError) as err:
276
+ logger.error(err)
277
+ message = _(
278
+ "content_remove_error",
279
+ default=u"There was a problem removing this content from SOLR. "
280
+ " Please contact site administrator.",
281
+ )
282
+ api.portal.show_message(
283
+ message=message, request=portal.REQUEST, type="warning"
284
+ )
285
+
286
+
287
+ def reset_solr():
288
+ solr = get_solr_connection()
289
+ if not solr:
290
+ logger.error("Unable to push to solr. Configuration is incomplete.")
291
+ return
292
+ solr.delete(
293
+ q='site_name:"{}"'.format(get_site_title()),
294
+ commit=should_force_commit(),
295
+ )