diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f5af7cd1..b3037bab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,7 +3,6 @@ name: CI on: push: branches-ignore: - - "master" - "releases/**" jobs: @@ -12,18 +11,52 @@ jobs: strategy: fail-fast: false matrix: - plone-version: + plone-version: - '5.0' - '5.1' - '5.2' - python-version: ['2.7'] - include: - - plone-version: 5.2 + python-version: ['2.7', '3.6', '3.7', '3.8'] + layer: + - Portlet_AjaxDisabled + - Portlet_AjaxEnabled + - Tile + - unit + coverage: ['no_coverage', 'coverage'] + exclude: + - plone-version: 5.0 + layer: Tile # standardtiles 2.3.2 does support using the request to query. embed might still be possible + - plone-version: 5.0 + layer: Portlet_AjaxEnabled # passes but meaningless as it skips all the tests + # Only 5.2+ runs on py3 + - plone-version: 5.0 python-version: 3.6 - - plone-version: 5.2 + - plone-version: 5.0 python-version: 3.7 - - plone-version: 5.2 + - plone-version: 5.0 python-version: 3.8 + - plone-version: 5.1 + python-version: 3.6 + - plone-version: 5.1 + python-version: 3.7 + - plone-version: 5.1 + python-version: 3.8 + # Save some time by not running all the robot tests + - python-version: 3.6 + layer: Portlet_AjaxDisabled + - python-version: 3.7 + layer: Portlet_AjaxDisabled + - python-version: 3.6 + layer: Tile + - python-version: 3.7 + layer: Tile + # save some time only running coverage on the 5.2 tests + - plone-version: 5.0 + coverage: coverage + - plone-version: 5.1 + coverage: coverage + - plone-version: 5.2 + coverage: no_coverage + steps: - uses: actions/checkout@v2 - name: Set up Python @@ -35,13 +68,13 @@ jobs: sudo apt-get update -y # sudo apt-get install -y build-essentials sudo apt-get install -y libxml2-dev libxslt-dev python-dev - # sudo apt-get install py-pip + # sudo apt-get install py-pip pip install \ virtualenv wheel - uses: nanasess/setup-chromedriver@master # with: # # Optional: do not specify to match Chrome's version - # chromedriver-version: '77.0.3865.40' + # chromedriver-version: '77.0.3865.40' - name: Cache multiple paths uses: actions/cache@v2 with: @@ -49,10 +82,11 @@ jobs: ~/buildout-cache ~/extends ~/.cache/pip - # key: ${{ runner.os }}-buildout-${{ hashFiles('**/*.cfg') }}-${{ matrix.plone-version }}-${{ matrix.python-version }} - key: ${{ runner.os }}-buildout-${{ matrix.plone-version }}-${{ matrix.python-version }} + key: ${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.plone-version }}-${{ hashFiles('**/*.cfg') }} restore-keys: | - ${{ runner.os }}-buildout- + ${{ runner.os }}-${{ matrix.python-version }}-${{ matrix.plone-version }}- + ${{ runner.os }}-${{ matrix.python-version }}- + ${{ runner.os }}- - name: setup buildout cache run: | mkdir -p ~/buildout-cache/{eggs,downloads} @@ -69,44 +103,67 @@ jobs: bin/buildout -t 10 -Nc test-${{ matrix.plone-version }}.x.cfg bin/pip install zest.pocompile bin/pocompile src - - name: test + - name: test robot + if: ${{ matrix.coverage != 'coverage' && matrix.layer != 'unit' }} run: | export DISPLAY=:99.0 chromedriver --url-base=/wd/hub & sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional sleep 3 - bin/test --all + bin/test --all --layer ${{ matrix.layer }} --list-tests + bin/test --all --layer ${{ matrix.layer }} + - name: test unit + if: ${{ matrix.coverage != 'coverage' && matrix.layer == 'unit' }} + run: | + bin/test --unit --list-tests + bin/test --unit + - name: createcoverage + if: ${{ matrix.coverage == 'coverage' && matrix.layer != 'unit' }} + run: | + export DISPLAY=:99.0 + chromedriver --url-base=/wd/hub & + sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional + sleep 3 + bin/test --all --layer ${{ matrix.layer }} --list-tests + bin/createcoverage -t '--all --layer ${{ matrix.layer }}' + bin/coverage json -i + - name: createcoverage + if: ${{ matrix.coverage == 'coverage' && matrix.layer == 'unit' }} + run: | + bin/test --unit --list-tests + bin/createcoverage -t '--unit' + bin/coverage json -i + - name: Coveralls + if: ${{ matrix.coverage == 'coverage' }} + uses: AndreMiras/coveralls-python-action@develop + with: + parallel: true + flag-name: ${{ matrix.plone-version }}-${{ matrix.python-version }}-${{ matrix.layer }} - name: code-analysis - run: echo "${{ matrix.plone-version }}" | grep 5.1 || bin/code-analysis + if: always() + run: echo "${{ matrix.plone-version }}" | grep 5.1 || bin/code-analysis + - name: black + if: ${{ matrix.python-version == '3.8' }} + # black dropped Python2.7 support in 22.1.0 (it's first stable release) + run: | + pip install black==21.12b0 + black --check src - name: Artifact Robot Test Report if: failure() uses: actions/upload-artifact@v1 with: - name: test_results + name: test_results-${{ matrix.python-version }}-${{ matrix.plone-version }}-${{ matrix.layer }} path: ./parts/test - uses: actions/setup-python@v2 if: failure() with: - python-version: 2.7 + python-version: 2.7 - name: Robottest report + continue-on-error: true if: failure() run: | pip2.7 install popt || pip2.7 install --user popt grep --include=output.xml -Rl FAIL parts/test | xargs --no-run-if-empty -n 1 popt - - name: createcoverage - run: | - export DISPLAY=:99.0 - chromedriver --url-base=/wd/hub & - sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 & # optional - sleep 3 - bin/createcoverage -t '--all' - # bin/createcoverage - bin/coverage json -i - - name: Coveralls - uses: AndreMiras/coveralls-python-action@develop - with: - parallel: true - flag-name: ${{ matrix.plone-version }}-${{ matrix.python-version }} coveralls_finish: needs: build diff --git a/CHANGES.rst b/CHANGES.rst index 89cdcb47..01ef4c6d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,31 @@ Changelog ========= -3.6 (unreleased) +4.0 (unreleased) + +Breaking Change: + +- Add idx parameter to display_modifier call, so that we can use the index name to resolve the correct translated taxonomy titles in collective.taxonomy. This means that the display_modifier method in the groupby_modifier adapters needs to expect this parameter too! + [MrTango] + +Bug Fixes: + +- Fix edge cases where "All" wasn't translated. + [agitator] + +- Ensure a `GroupByCriteria`'s `sort_key_function` function `lower()` call gets a string. + [jensens] + +- Fixed searches for only non-alphanumeric characters causing an exception to be displayed. + [JeffersonBledsoe] + +Other: + +- Code-Style Black and Isort + [jensens] + + +3.5.1 (2021-05-26) ---------------- - Updated de and ch-de translations @@ -11,7 +35,7 @@ Changelog 3.5 (2021-05-26) ---------------- -- Use collection from context as default. `target_collection` is now used to select an alternative collection as result source. +- Use collection from context as default. `target_collection` is now used to select an alternative collection as result source. This allows to copy and paste preconfigured collections for reuse without reconfiguring each filter element. [agitator] diff --git a/README.rst b/README.rst index 5241be7e..ca2210c5 100644 --- a/README.rst +++ b/README.rst @@ -13,11 +13,11 @@ collective.collectionfilter :target: https://github.com/collective/collective.collectionfilter/actions -Faceted navigation filter for collection results. +Faceted navigation filter for collection or contentlisting tiles. -This Plone 5 addon allows you to filter collections results for additional catalog metadata. +This Plone 5 addon allows you to filter listing results for fields which are indexed in the catalog +(Plones internal search tool). For example, you can add a subject filter, but also a filter for authors or portal types. -This can also be used to build tag clouds. The filter types can be extended (see: ``collective.collectionfilter.vocabularies``). @@ -35,27 +35,42 @@ There are three portlets/tiles available for filtering: a list of indexes where the user can sort the filtered result listing -Filter Results with portlets +Filter Results of Collections ---------------------------- -Add as many of the filter portlets above to any context you want (most likely the source collection) -and assign a collection with results to it. +Add as many filter/search portlets directly to a collection. -When you select values from the filter the results are loaded asynchronously inside the container -with the selector defined in the field ``Content Selector``. Make sure the selector exists on the -source collection template and on the target page which shows the filtered results. +When you select values from the filter the results are loaded asynchronously onto the page (no page refresh). +Unless you turn off ajax loading in the registery or are using Plone 5.0. If you are using special theme or view template +you can customize ``Content Selector`` and/or ```View Template``` to ensure ajax loading works correctly. +Make sure the selector exists on the source collection template and on the target page which shows the filtered results. + +It is also possible to use filter portlets that aren't directly on a collection by specifying a target collection. +To use ajax loading you will need to ensure your content selector is visible on the page. If not using ajax loading selecting a filter +option will redirect you to the collection. Mosaic Integration ------------------ +Use the package extra to install the required dependencies:: + + [buildout] + ... + eggs += + collective.collectionfilter[mosaic] + ... + The three tiles can be added within the Mosaic editor multiple times. Just select them in the ``Insert`` menu and assign a collection to it. To show the results of the collection simply add a -``Existing Content`` tile which links to the same collection your filter tiles are assigned with. +``Content Listing`` tile. + +It's possible to use multiple content listings and multiple filters on the same page by specifying additional unique classes in +the listing tiles settings and then adding these classes to the ``Content Selector`` setting of the filter tile. + +If you want to use filter tiles with a collection then add a content listing tile with the setting to use the query from the context. +It is also possible to use the ``Embed content`` tile if there is a unique selector on your collection view. -TODO: right now the collection needs a default_view template, which wraps the result list with a unique selector -inside the ``#content-core`` container. so the collectionfilter can load the filtered result correctly from -the collection into the container inside the existing content tile. Geolocation filter support @@ -129,12 +144,12 @@ Write an adapter:: @implementer(IGroupByModifier) @adapter(IGroupByCriteria) def groupby_modifier(groupby): - groupby._groupby['Subject']['display_modifier'] = lambda x: x.upper() + groupby._groupby['Subject']['display_modifier'] = lambda x, idx: x.upper() groupby._groupby['Subject']['sort_key_function'] = subjectsort groupby._groupby['my_new_index'] = { 'index': 'my_new_index', 'metadata': 'my_new_index_metadata_colum', - 'display_modifier': lambda it: u'this is awesome: {0}'.format(it) + 'display_modifier': lambda it, idx: u'this is awesome: {0}'.format(it) } Register the adapter:: diff --git a/base.cfg b/base.cfg index e6c6ba0f..89d11bfb 100644 --- a/base.cfg +++ b/base.cfg @@ -1,14 +1,21 @@ [buildout] -extensions = mr.developer +# extensions = mr.developer versions = versions +[code-analysis] +directory = ${buildout:directory}/src/collective/collectionfilter +flake8-ignore = E501,E241,W503 + [versions] collective.collectionfilter = collective.geolocationbehavior = >=1.6.0 plone.formwidget.geolocation = >=2.2.0 plone.patternslib = >=1.1.0 +plone.app.standardtiles = >= 2.4.0 +plone.batching = >=1.1.7 # plone.app.standardtiles = 2.3.1 # fix https://github.com/plone/plone.app.standardtiles/issues/111 +pycodestyle = # Robot Testing (see buildout.coredev[5.2]/versions.cfg) plone.app.robotframework = 1.5.4 @@ -22,5 +29,25 @@ robotframework-selenium2screenshots = 0.8.1 robotsuite = 2.2.1 selenium = 3.141.0 -[code-analysis] -directory= ${buildout:directory}/src/collective/collectionfilter +[versions:python27] +# to get codeanalysis working +build = 0.1 +coverage = 5.5 + +# Required by: +# build==0.1.0 +packaging = 20.8 +contextlib2 = 0.6.0.post1 + +# Required by: +# build==0.1.0 +typing = 3.7.4.3 + + +flake8 = 3.9.2 +configparser = <5.0.0 + +flake8-pep3101 = 0.6 +flake8-commas = 0.1.6 +flake8-isort = 1.3 +flake8-deprecated = 1.0 diff --git a/buildout.cfg b/buildout.cfg index 1a65a6da..a7fcd56b 100644 --- a/buildout.cfg +++ b/buildout.cfg @@ -63,3 +63,7 @@ eggs = scripts = plone-compile-resources # zopepy + + +[instance] +eggs+= plone.staticresources \ No newline at end of file diff --git a/setup.py b/setup.py index aa9dff61..0bf3687c 100644 --- a/setup.py +++ b/setup.py @@ -52,6 +52,10 @@ def read(*rnames): "plone.app.contenttypes", ], extras_require={ + "mosaic": [ + "plone.app.mosaic >= 2.2.3", + "plone.app.standardtiles >= 2.4.0", + ], "geolocation": [ # support for latitude/longitude catalog index "collective.geolocationbehavior >= 1.6.0", diff --git a/src/collective/collectionfilter/baseviews.py b/src/collective/collectionfilter/baseviews.py index dc0ad5c6..ad59990f 100644 --- a/src/collective/collectionfilter/baseviews.py +++ b/src/collective/collectionfilter/baseviews.py @@ -2,6 +2,7 @@ from Acquisition import aq_inner from collective.collectionfilter import PLONE_VERSION from collective.collectionfilter.filteritems import get_filter_items +from collective.collectionfilter.filteritems import ICollectionish from collective.collectionfilter.interfaces import IGroupByCriteria from collective.collectionfilter.query import make_query from collective.collectionfilter.utils import base_query @@ -11,7 +12,6 @@ from collective.collectionfilter.vocabularies import TEXT_IDX from plone import api from plone.api.portal import get_registry_record as getrec -from plone.app.contenttypes.behaviors.collection import ICollection from plone.app.uuid.utils import uuidToCatalogBrain from plone.app.uuid.utils import uuidToObject from plone.i18n.normalizer.interfaces import IIDNormalizer @@ -19,7 +19,7 @@ from plone.uuid.interfaces import IUUID from Products.CMFPlone.utils import get_top_request from Products.CMFPlone.utils import safe_unicode -from six.moves.urllib.parse import urlencode +from six.moves.urllib.parse import urlencode, parse_qsl from zope.component import getUtility from zope.component import queryUtility from zope.i18n import translate @@ -98,10 +98,24 @@ def pat_options(self): "collectionUUID": self.collection_uuid, "reloadURL": self.reload_url, "ajaxLoad": self.ajax_load, - "contentSelector": self.settings.content_selector, + "contentSelector": self.content_selector, } ) + @property + def content_selector(self): + if self.settings.content_selector: + return self.settings.content_selector + + collectionish = ( + ICollectionish(self.collection.getObject()) if self.collection else None + ) + selector = collectionish.content_selector + if collectionish is None or not selector: + return u"#content-core" + else: + return selector + @property def ajax_load(self): if PLONE_VERSION < "5.1": @@ -142,6 +156,46 @@ def results(self): view_name=self.settings.view_name, cache_enabled=self.settings.cache_enabled, request_params=self.top_request.form or {}, + content_selector=self.settings.content_selector, + default_filtering_behaviour=self.settings.default_filtering_behaviour, + ) + + if ( + self.request.response.headers.get("x-tile-url") + and self.request.get("PARENT_REQUEST") is None + ): + # We are in the edit mosaic page so we don't want to redirect + return results + + # In the case of tiles we are in a subrequest (ie tile). We need to really redirect + req = self.request.get("PARENT_REQUEST", self.request) + + # In order to handle filters with no "All" option we need redirect urls that + # haven't been processed yet picking default options for those filters + if getattr(req, "collectionfilter", None) or not results: + # assume we already fixed the params + return results + + existing_query_string = req["QUERY_STRING"] + # Using `parse_qsl` then converting to a list as `parse_qs` ends up producing lists for the values + query_object = dict(parse_qsl(existing_query_string)) + + if self.settings.group_by not in query_object: + if self.settings.default_filtering_behaviour == "Select first": + query_object[self.settings.group_by] = results[0]["value"] + query_object["collectionfilter"] = 1 + + # if req['HTTP_COOKIE']: + # # when saving a tile the redirect hides teh status message. + # # The statusmessage is in HTTP_COOKIES but removed from req.cookies + # c = Cookie.Cookie() + # c.load(req['HTTP_COOKIE']) + # if "statusmessages" in c: + # # TODO: set using proper interface + # req.response.setCookie("statusmessages", c['statusmessages'].value) + + req.response.redirect( + "%s?%s" % (req["ACTUAL_URL"], urlencode(safe_encode(query_object))) ) return results @@ -217,7 +271,11 @@ def ajax_url(self): class BaseSortOnView(BaseView): def results(self): - collection = self.collection.getObject() + collection = ICollectionish(self.collection.getObject()).selectContent( + self.settings.content_selector + ) + if collection is None: + return curr_val = self.top_request.get("sort_on", collection.sort_on) curr_order = self.top_request.get( "sort_order", "descending" if collection.sort_reversed else "ascending" @@ -298,8 +356,10 @@ def locations(self): # defined by urlquery custom_query = base_query(request_params) custom_query = make_query(custom_query) - return ICollection(collection).results( - batch=False, brains=True, custom_query=custom_query + return ( + ICollectionish(collection) + .selectContent(self.settings.content_selector) + .results(custom_query, request_params) ) @property diff --git a/src/collective/collectionfilter/configure.zcml b/src/collective/collectionfilter/configure.zcml index 027c37ff..1871ad93 100644 --- a/src/collective/collectionfilter/configure.zcml +++ b/src/collective/collectionfilter/configure.zcml @@ -48,6 +48,10 @@ name="collective.collectionfilter.SortOnIndexes" /> + + Title
- +
    -
  • - +
- +
diff --git a/src/collective/collectionfilter/portlets/collectionfilter.py b/src/collective/collectionfilter/portlets/collectionfilter.py index 9301d4d9..e4073e7e 100644 --- a/src/collective/collectionfilter/portlets/collectionfilter.py +++ b/src/collective/collectionfilter/portlets/collectionfilter.py @@ -28,6 +28,7 @@ class Assignment(base.Assignment): view_name = None content_selector = "#content-core" hide_if_empty = False + default_filtering_behaviour = "Show all" # list_scaling = None def __init__( @@ -43,6 +44,7 @@ def __init__( view_name=None, content_selector="#content-core", hide_if_empty=False, + default_filtering_behaviour=True, # list_scaling=None ): self.header = header @@ -56,6 +58,7 @@ def __init__( self.view_name = view_name self.content_selector = content_selector self.hide_if_empty = hide_if_empty + self.default_filtering_behaviour = default_filtering_behaviour # self.list_scaling = list_scaling @property diff --git a/src/collective/collectionfilter/portlets/profiles/uninstall/portlets.xml b/src/collective/collectionfilter/portlets/profiles/uninstall/portlets.xml index b52499c8..eeae7405 100644 --- a/src/collective/collectionfilter/portlets/profiles/uninstall/portlets.xml +++ b/src/collective/collectionfilter/portlets/profiles/uninstall/portlets.xml @@ -9,11 +9,11 @@ addview="collective.collectionfilter.portlets.CollectionSearch" /> diff --git a/src/collective/collectionfilter/query.py b/src/collective/collectionfilter/query.py index e6fc4a43..375185d3 100644 --- a/src/collective/collectionfilter/query.py +++ b/src/collective/collectionfilter/query.py @@ -10,6 +10,8 @@ from Products.CMFPlone.browser.search import quote_chars from zope.component import getUtility +from Products.CMFPlone.UnicodeSplitter.config import rxGlob_U + try: from Products.CMFPlone.browser.search import quote @@ -28,6 +30,8 @@ def quote(term): def sanitise_search_query(query): + if not rxGlob_U.findall(query): + return u"" for char in ENCODED_BAD_CHARS: query = query.replace(char, " ") clean_query = [quote(token) for token in query.split()] diff --git a/src/collective/collectionfilter/testing.py b/src/collective/collectionfilter/testing.py index 253d8f6b..dc041935 100644 --- a/src/collective/collectionfilter/testing.py +++ b/src/collective/collectionfilter/testing.py @@ -13,6 +13,7 @@ from Products.PluginIndexes.BooleanIndex.BooleanIndex import BooleanIndex import json +import os try: @@ -89,7 +90,9 @@ def setUpPloneSite(self, portal): "Document", id="testdoc", title=u"Test Document and Document 😉", - text=RichTextValue(u"Ein heißes Test Dokument"), + text=RichTextValue( + u"Ein heißes Test Dokument", "text/plain", "text/html" + ), subject=[u"Süper", u"Dokumänt"], exclude_from_nav=False, ) @@ -97,7 +100,9 @@ def setUpPloneSite(self, portal): "Document", id="testdoc2", title=u"Page 😉", - text=RichTextValue(u"Ein heiBes Test Dokument"), + text=RichTextValue( + u"Ein heiBes Test Dokument", "text/plain", "text/html" + ), subject=[u"Dokumänt"], exclude_from_nav=True, ) @@ -137,7 +142,7 @@ def setUpPloneSite(self, portal): REMOTE_LIBRARY_BUNDLE_FIXTURE, z2.ZSERVER_FIXTURE, ), - name="CollectiveCollectionFilterLayer:AcceptanceTesting_AjaxEnabled", + name="CollectiveCollectionFilterLayer:AcceptanceTestingPortlet_AjaxEnabled", ) @@ -154,5 +159,26 @@ def setUpPloneSite(self, portal): REMOTE_LIBRARY_BUNDLE_FIXTURE, z2.ZSERVER_FIXTURE, ), - name="CollectiveCollectionFilterLayer:AcceptanceTesting_AjaxDisabled", + name="CollectiveCollectionFilterLayer:AcceptanceTestingPortlet_AjaxDisabled", +) + + +class CollectiveCollectionFilterTilesLayer(CollectiveCollectionFilterLayer): + def setUpPloneSite(self, portal): + os.environ["ROBOT_USE_TILES"] = "True" + super(CollectiveCollectionFilterTilesLayer, self).setUpPloneSite(portal) + + def tearDownPloneSite(self, portal): + super(CollectiveCollectionFilterTilesLayer, self).tearDownPloneSite(portal) + del os.environ["ROBOT_USE_TILES"] + + +TILES_FIXTURE = CollectiveCollectionFilterTilesLayer() +COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_TILES = FunctionalTesting( + bases=( + TILES_FIXTURE, + REMOTE_LIBRARY_BUNDLE_FIXTURE, + z2.ZSERVER_FIXTURE, + ), + name="CollectiveCollectionFilterLayer:AcceptanceTesting_Tiles", ) diff --git a/src/collective/collectionfilter/tests/profiles/testing/types/Collection.xml b/src/collective/collectionfilter/tests/profiles/testing/types/Collection.xml new file mode 100644 index 00000000..e0e40df1 --- /dev/null +++ b/src/collective/collectionfilter/tests/profiles/testing/types/Collection.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + diff --git a/src/collective/collectionfilter/tests/robot/keywords.robot b/src/collective/collectionfilter/tests/robot/keywords.robot index da70acc7..181678d8 100644 --- a/src/collective/collectionfilter/tests/robot/keywords.robot +++ b/src/collective/collectionfilter/tests/robot/keywords.robot @@ -4,16 +4,26 @@ Resource plone/app/robotframework/selenium.robot Resource plone/app/robotframework/keywords.robot Resource Selenium2Screenshots/keywords.robot +Library OperatingSystem + *** Variables *** ${BROWSER} chrome - *** Keywords ***************************************************************** +Default Setup +# Not passed in as variable by robotsuite as variables collected before layer setup + ${USE_TILES}= Get Environment Variable ROBOT_USE_TILES default=${False} + ${USE_TILES}= Set Test Variable ${USE_TILES} + open test browser + #Set Window Size ${1400} ${8000} + Default Teardown Run Keyword If Test Failed Capture Page Screenshot + Run Keyword If Test Failed Log Source +# Run Keyword If Test Failed Log Variables Close all browsers # --- Given ------------------------------------------------------------------ @@ -50,11 +60,69 @@ View Test Collection Log in as site owner Go to ${PLONE_URL}/testcollection +Run Keyword by label + [Arguments] ${label} ${keyword} @{args} + # Possible to get 2 mosaic overlays with the same labels on page at once + ${xpath}= set variable //*[ @id=//label[.//*[normalize-space(text())='${label}'] or normalize-space(text()) ='${label}']/@for and not(ancestor::div[contains(@class, 'mosaic-overlay')])] + Wait until page contains element xpath=${xpath} + run keyword ${keyword} xpath=${xpath} @{args} Click Input "${label}" Wait until page contains element xpath=//input[@id=//label[.//*[normalize-space(text())='${label}'] or normalize-space(text()) ='${label}']/@for] Click Element xpath=//input[@id=//label[.//*[normalize-space(text())='${label}'] or normalize-space(text()) ='${label}']/@for] +Select InAndOut + [Arguments] ${label} @{values} + Wait until page contains element xpath=//label[.//*[normalize-space(text())='${label}'] or normalize-space(text()) ='${label}'] + ${id}= Get Element Attribute xpath=//label[.//*[normalize-space(text())='${label}'] or normalize-space(text()) ='${label}'] for + FOR ${value} IN @{values} + Select from List by value css=select#${id}-from ${value} + Click element css=#${id} button[name='from2toButton'] + END + + +Select single select2 + [Arguments] ${locator} ${value} + ${select2}= get webelement ${locator} + execute javascript $(arguments[0]).select2("open") ARGUMENTS ${select2} + wait until element is visible css=.select2-result-label + Click Element xpath=//div[contains(@class,'select2-result-label') and text() = '${value}'] + + #Click link link=Select criteria + #pause + #select from list by label xpath=(//div[@class='querystring-criteria-index'])[5]//select Type + #Click Element xpath=(//div[@class='querystring-criteria-index'])[5]//*[@class='select2-arrow']//a + # $($x("(//div[@class='querystring-criteria-index'])[5]/div")).select2("val", "portal_type").trigger("change") + + +Select multi select2 + [Arguments] ${locator} @{values} + # select2-choices select2-search-field select2-input + #select from list by label xpath=(//select[@class='querystring-criteria-value-MultipleSelectionWidget'])[1] Event + #select from list by label xpath=(//select[@class='querystring-criteria-value-MultipleSelectionWidget'])[1] Page + ${select2}= get webelement ${locator} + execute javascript $(arguments[0]).select2("val",arguments[1]) ARGUMENTS ${select2} ${values} + FOR ${value} IN @{values} + # Hack to only find those in the tile popup up not the mosaic popup + #\ wait until element is visible //*[contains(@class,'plone-modal ')]//li[@class='select2-search-choice']//*[contains(text(), '${value}')] + Wait until keyword succeeds 5s 1s call method ${select2} find_elements by=xpath value=.//li[@class='select2-search-choice']//*[contains(text(), '${value}')] + END + + # Click Element xpath=(//div[@class='querystring-criteria-value'])[3]//input + # wait until element is visible css=.select2-result-label + # Click Element xpath=//div[contains(@class,'select2-result-label') and text() = 'Event'] + # Click Element xpath=(//div[@class='querystring-criteria-value'])[3]//input + # wait until element is visible css=.select2-result-label + # Click Element xpath=//div[contains(@class,'select2-result-label') and text() = 'Page'] + +select multi select2 with label + [Arguments] ${label} @{values} + # select2 don't have proper labels because the @for doesn't match an id of anything + ${xpath}= set variable //label[normalize-space(text()) ='${label}']/following-sibling::div[contains(@class,'select2-container')] + Wait until page contains element xpath=${xpath} + run keyword select multi select2 xpath=${xpath} @{values} + + Click Button with text [Arguments] ${text} ${pos}=1 Wait until page contains element xpath=(//*[@type="submit" and (normalize-space(@value)='${text}' or normalize-space(text())='${text}')])[${pos}] @@ -79,6 +147,19 @@ Select related filter collection Wait until page contains element partial link=Test Collection Click element partial link=Test Collection +_Click dropdown option "${option}" + ${value} = Get Substring ${option} 9 # 9 characters in `dropdown_` + Click element css=option[value="${value}"] + +Set Options + [Arguments] @{options} + FOR ${option} IN @{options} + Run Keyword If 'dropdown_' in '${option}' _Click dropdown option "${option}" + ... ELSE Click Input "${option}" + END + +# ---- CF stuff + Add search portlet Wait until page contains element css=select.add-portlet Select From List by label css=select.add-portlet Collection Search @@ -90,7 +171,7 @@ Add search portlet Wait until page contains element xpath=//div[@class='portletAssignments']//a[text()='Searchable Text'] Add filter portlet - [Arguments] ${group_criteria} ${filter_type} ${input_type} + [Arguments] ${group_criteria} ${filter_type} ${input_type} @{options} Wait until page contains element css=select.add-portlet Select From List by label css=select.add-portlet Collection Filter @@ -98,13 +179,25 @@ Add filter portlet Input text css=input#form-widgets-header ${group_criteria} #Select related filter collection - Select from List by value css=select#form-widgets-group_by ${group_criteria} - Click Input "Show count" - Select from List by value css=select#form-widgets-filter_type ${filter_type} - Select from List by value css=select#form-widgets-input_type ${input_type} + Set Filter Options ${group_criteria} ${filter_type} ${input_type} @{options} + # Select from List by value css=select#form-widgets-group_by ${group_criteria} + # Click Input "Show count" + # Select from List by value css=select#form-widgets-filter_type ${filter_type} + # Select from List by value css=select#form-widgets-input_type ${input_type} Click element css=.plone-modal-footer input#form-buttons-add Wait until page contains element xpath=//div[contains(@class, 'portletAssignments')]//a[text()='${group_criteria}'] + +Set Filter Options + [Arguments] ${group_by} ${filter_type} ${input_type} @{options} + + Run Keyword by label Group by Select from List by value ${group_by} + Run Keyword by label Filter Type Select from List by value ${filter_type} + Run Keyword by label Input Type Select from List by value ${input_type} + Set Options @{options} + Click Input "Show count" + + Add sorting portlet [Arguments] ${sort_on} ${input_type} @@ -113,37 +206,81 @@ Add sorting portlet Wait until element is visible css=input#form-widgets-header Input text css=input#form-widgets-header Sort on - Select from List by value css=select#form-widgets-sort_on-from ${sort_on} - Click element css=#form-widgets-sort_on button[name='from2toButton'] - Select from List by value css=select#form-widgets-input_type ${input_type} + + Set Sorting Options ${sort_on} ${input_type} + Click element css=.plone-modal-footer input#form-buttons-add Wait until page contains element xpath=//div[contains(@class, 'portletAssignments')]//a[text()='Sort on'] +Set sorting Options + [Arguments] ${sort_on} ${input_type} + #Run keyword by label Enabled sort indexes select multi select2 ${sort_on} + select multi select2 with label Enabled sort indexes ${sort_on} + Run Keyword by label Input Type Select from List by value ${input_type} + + +Add Info portlet + [Arguments] ${header} @{templates} ${hide_when}=${None} + + Wait until page contains element css=select.add-portlet + Select From List by label css=select.add-portlet Collection Filter Search Info + Wait until element is visible css=input#form-widgets-header + + Input text css=input#form-widgets-header ${header} + Set Info Settings form-widgets @{templates} hide_when=${hide_when} + Click element css=.plone-modal-footer input#form-buttons-add + Wait until page contains element xpath=//div[contains(@class, 'portletAssignments')]//a[text()='${header}'] + + + Should be ${X} filter options - Wait until keyword succeeds 5s 1s Page Should Contain Element xpath=//div[contains(@class, 'filterContent')]//*[contains(@class, 'filterItem')] limit=${X} + Wait until keyword succeeds 2s 1s Page Should Contain Element xpath=//div[contains(@class, 'filterContent')]//*[contains(@class, 'filterItem')] limit=${X} + +Should be filter options + [Arguments] @{values} + Wait until keyword succeeds 2s 1s List Labels Should Equal xpath=//div[contains(@class, 'filterContent')]//select @{values} + +Should be filter checkboxes + [Arguments] @{values} + Wait until keyword succeeds 2s 1s Labels Should Equal xpath=//div[contains(@class, 'filterContent')]//span[@class='filterLabel'] @{values} + +List Labels Should Equal + [Arguments] ${selector} @{expect} + @{options}= get list items ${selector} + Should Be Equal ${expect} ${options} + +Labels Should Equal + [Arguments] ${selector} @{expect} + @{locators}= Get Webelements ${selector} + ${result}= Create List + FOR ${locator} IN @{locators} + ${name}= Get Text ${locator} + Append To List ${result} ${name} + END + Should Be Equal ${expect} ${result} Should be ${X} collection results - Wait until element is visible css=#content-core - Wait until keyword succeeds 5s 1s Page Should Contain Element xpath=//article[@class='entry'] limit=${X} + # Wait until element is visible css=#content-core + # below should work for both collections and contentlisting tiles + Wait until keyword succeeds 5s 1s Page Should Contain Element xpath=//span[@class='summary'] limit=${X} Should be ${X} pages ${X}= evaluate ${X} + 1 # need we have next or previous Wait until keyword succeeds 5s 1s Page Should Contain Element xpath=//nav[@class='pagination']//a limit=${X} -Set Batch Size - [Arguments] ${batch_size} +Should be Info with text: ${text} + wait until element contains css=.filterInfoContent ${text} - Go to ${PLONE_URL}/testcollection/edit - Input text css=input#form-widgets-ICollection-item_count ${batch_size} - Click element css=input#form-buttons-save - Go to ${PLONE_URL}/testcollection +# TODO: there is a bug where the aside.collectionInfo is still visible on screen. +Should be no Info + wait until element is not visible css=.filterInfoContent -Set portlet "${title}" "${checkbox}" - Click Link ${title} - Click Input "${checkbox}" - Click element css=.plone-modal-footer input#form-buttons-apply - Wait until page does not contain element css=.plone-modal-dialog +# Set portlet "${title}" "${checkbox}" +# Click Link ${title} +# Click Input "${checkbox}" +# Click element css=.plone-modal-footer input#form-buttons-apply +# Wait until page does not contain element css=.plone-modal-dialog Click Page "${page}" Click element xpath=//nav[@class='pagination']//a[${page}] @@ -153,8 +290,39 @@ Ajax has completed # --- Setup ------------------------------------------------------------------- I've got a site with a collection + [arguments] ${batch}=20 Log in as site owner - Go to ${PLONE_URL}/testcollection + run keyword if ${USE_TILES} run keyword Enable mosaic layout for page ${PLONE_URL}/testdoc batch=${batch} + run keyword unless ${USE_TILES} Go to ${PLONE_URL}/testcollection + run keyword unless ${USE_TILES} Set Batch Size ${batch} + +I've got a site without a listing + I've got a site with a collection 0 + +My collection has a collection search + run keyword if ${USE_TILES} My collection has a collection search tile + run keyword unless ${USE_TILES} My collection has a collection search portlet + +My collection has a collection filter + [Arguments] ${group_by}=Subject ${op}=or ${style}=checkboxes_dropdowns @{options} + run keyword if ${USE_TILES} My collection has a collection filter tile ${group_by} ${op} ${style} @{options} + run keyword unless ${USE_TILES} My collection has a collection filter portlet ${group_by} ${op} ${style} @{options} + +My collection has a collection sorting + [Arguments] ${sort_on}=sortable_title + run keyword if ${USE_TILES} My collection has a collection sorting tile ${sort_on} + run keyword unless ${USE_TILES} My collection has a collection sorting portlet ${sort_on} + +My collection has a collection info + [Arguments] ${header}="Current Filter" @{templates} ${hide_when}=${None} + run keyword if ${USE_TILES} My collection has a collection info tile ${header} @{templates} hide_when=${hide_when} + run keyword unless ${USE_TILES} My collection has a collection info portlet ${header} @{templates} hide_when=${hide_when} + +I'm viewing the collection + run keyword if ${USE_TILES} Go to ${PLONE_URL}/testdoc + run keyword unless ${USE_TILES} Go to ${PLONE_URL}/testcollection + # Should be 3 collection results + My collection has a collection search portlet Go to ${PLONE_URL}/testcollection @@ -162,11 +330,11 @@ My collection has a collection search portlet Add search portlet My collection has a collection filter portlet - [Arguments] ${group_by}=Subject ${op}=or ${style}=checkboxes_dropdowns + [Arguments] ${group_by}=Subject ${op}=or ${style}=checkboxes_dropdowns @{options} Go to ${PLONE_URL}/testcollection Manage portlets - Add filter portlet ${group_by} ${op} ${style} + Add filter portlet ${group_by} ${op} ${style} @{options} My collection has a collection sorting portlet [Arguments] ${sort_on}=sortable_title @@ -175,11 +343,38 @@ My collection has a collection sorting portlet Manage portlets Add sorting portlet ${sort_on} links -I'm viewing the collection +My collection has a collection info portlet + [Arguments] ${header}="Current Filter" @{templates} ${hide_when}=${None} + Go to ${PLONE_URL}/testcollection - Should be 3 collection results + Manage portlets + Add info portlet ${header} @{templates} hide_when=${hide_when} + +Open collection settings + +movable removable mosaic-tile +Set Batch Size + [Arguments] ${batch_size} + + run keyword unless ${USE_TILES} Go to ${PLONE_URL}/testcollection/edit + run keyword if ${USE_TILES} Edit Listing Tile + Run keyword by label Item count Input Text ${batch_size} + Click Button Save + run keyword if ${USE_TILES} Click Button Save + # Go to ${PLONE_URL}/testcollection + +Edit Listing Tile + Go to ${PLONE_URL}/testdoc/edit + #Wait until page contains element css=.mosaic-btn-delete + #Wait until page contains element css=#mosaic-panel + Wait Until Element Is Visible css=.mosaic-toolbar + #Unselect Frame + #mouse over css=.mosaic-plone.app.standardtiles.contentlisting-tile + click element css=.contentlisting-tile + Edit Current Tile + # --- Core Functionality ------------------------------------------------------ I search for "${search}" with ajax Wait until element is not visible css=.collectionSearch button[type='submit'] timeout=5 sec @@ -203,6 +398,13 @@ I should have a portlet titled "${filter_title}" with ${number_of_results} filte Page Should Contain Element xpath=//${portlet_title_xpath} Wait until keyword succeeds 5s 1s Page Should Contain Element xpath=//${portlet_title_xpath}/parent::*[contains(@class, 'collectionFilter')]//${filter_item_xpath} limit=${number_of_results} +I should have a filter with ${number_of_results} options + Wait until keyword succeeds 5s 1s Page Should Contain Element xpath=//aside[contains(@class,'collectionFilter') and count(.//div[contains(@class, 'filterContent')]//li[contains(@class, 'filterItem')])=${number_of_results} ] limit=1 + +I should see ${number} filter options on the page + Page should contain element xpath=//aside[contains(@class,'collectionFilter') ]//div[contains(@class, 'filterContent')]//li[contains(@class, 'filterItem')] limit=${number} + + I should not have a portlet titled "${filter_title}" ${portlet_title_xpath} Convert to string header[@class='portletHeader' and descendant-or-self::*[contains(text(), '${filter_title}')]] @@ -210,32 +412,81 @@ I should not have a portlet titled "${filter_title}" I should not see any results - Sleep 1 sec - Element should be visible xpath=//*[@id="content-core"]/*[text()="No results were found."] + Wait until keyword succeeds 5s 1s Element should be visible xpath=//*[@id="content-core"]/*[text()="No results were found."] + +I should have a portlet titled "${filter_title}" with text ${text} + ${portlet_title_xpath} Convert to string header[@class='portletHeader' and contains(text(), '${filter_title}')] + ${filter_item_xpath} Convert to string div[contains(@class, 'portletContent')] + + Page Should Contain Element xpath=//${portlet_title_xpath} + Wait Until Element Contains xpath=//${portlet_title_xpath}/parent::*[contains(@class, 'collectionFilterInfo')]//${filter_item_xpath} ${text} I sort by "${sort_on}" - Wait until element is visible css=.collectionSortOn + Wait until element is visible xpath=//span[contains(normalize-space(text()), '${sort_on}')] + ${glyph}= Get Element Attribute xpath=//span[contains(normalize-space(text()), '${sort_on}')]//span class + Click Link link=${sort on} + Wait until element is not visible xpath=//span[contains(normalize-space(text()), '${sort_on}')]//span[@class='${glyph}'] + +Results Are Sorted + ${xpath}= Set Variable //span[@class='summary'] + ${count}= Get Element Count xpath=${xpath} + ${names}= Create List + FOR ${i} IN RANGE 1 ${count} + 1 + ${name}= Get Text xpath=(${xpath})[${i}] + Append To List ${names} ${name} + END + + ${sorted}= copy list ${names} False + sort list ${sorted} + log many ${sorted} ${names} + run keyword if $sorted!=$names Fail Results are not sorted ${sorted}!=${names} - Click Element css=.collectionSortOn .sortItem .${sort_on} - Wait until keyword succeeds 5s 1s Page Should Contain Element css=.collectionSortOn .sortItem.selected .${sort_on} span.glyphicon-sort-by-attributes +# --- Tiles ------------------------------------------------------------------- - Click Element css=.collectionSortOn .sortItem .${sort_on} - Wait until keyword succeeds 5s 1s Page Should Contain Element css=.collectionSortOn .sortItem.selected .${sort_on} span.glyphicon-sort-by-attributes-alt -should be no errors +My collection has a collection filter tile + [Arguments] ${group_by}=Subject ${op}=or ${style}=checkboxes_dropdowns @{options} + + Go to ${PLONE_URL}/testdoc/edit + Add filter tile ${group_by} ${op} ${style} @{options} + Save mosaic page + +My collection has a collection search tile + + Go to ${PLONE_URL}/testdoc/edit + Add search tile + Save mosaic page + +My collection has a collection info tile + [Arguments] ${header} @{templates} ${hide_when}=${None} + + Go to ${PLONE_URL}/testdoc/edit + Add info tile @{templates} hide_when=${hide_when} + Save mosaic page + +My collection has a collection sorting tile + [Arguments] ${sort_on} + Go to ${PLONE_URL}/testdoc/edit + Insert Tile "Collection Result Listing Sort" + Set Sorting Options ${sort_on} links + # Run Keyword by label Content Selector Input Text .contentlisting-tile + Click element css=.pattern-modal-buttons #buttons-save + Drag tile +# Click button Edit + Save mosaic page -# --- Tiles ------------------------------------------------------------------- Enable mosaic layout for page - [Arguments] ${page} + [Arguments] ${page}=${PLONE_URL}/testdoc ${batch}=20 + go to ${page} # Setup Mosaic display and open editor Click element link=Display Wait Until Element Is visible css=#plone-contentmenu-display-layout_view Click element link=Mosaic layout Go to ${page}/edit - # Create default layout + # Create default layout if its a Page Wait Until Element Is Visible css=.mosaic-select-layout Wait until Page contains element xpath=//a[@data-value='default/basic.html'] Click element xpath=//a[@data-value='default/basic.html'] @@ -246,60 +497,131 @@ Enable mosaic layout for page Wait Until Element Is visible css=.mosaic-button-customizelayout Click element css=.mosaic-button-customizelayout + # Add a embed content tile pointing to the collection + #Add existing content tile /testdoc + run keyword if ${batch} > 0 Add contentlisting tile ${batch} + Save mosaic page +Edit mosaic page + [Arguments] ${page}=${PLONE_URL}/testdoc + Go to ${page}/edit + Wait Until Element Is Visible css=.mosaic-toolbar + Save mosaic page Wait Until Element Is Visible css=.mosaic-button-save timeout=5 sec Click button css=.mosaic-button-save - Wait until page contains Changes saved timeout=10 sec + # HACK: Due to bug. If you save it once it works? https://github.com/plone/plone.app.mosaic/issues/421 + # You get a "do you want to leave site" popup + run keyword and ignore error alert should not be present timeout=1 sec + # Wait until page contains Changes saved timeout=2 sec + + +# Save mosaic page +# Wait Until Element Is Visible css=.mosaic-button-save timeout=5 sec +# Click button css=.mosaic-button-save +# Wait until page contains Changes saved timeout=2 sec + Add filter tile - [Arguments] ${collection_name} ${filter_type} ${input_type} + [Arguments] ${group_by} ${filter_type} ${input_type} @{options} - # Insert content filter - Wait Until Element Is Visible css=.mosaic-toolbar - Click element css=.select2-container.mosaic-menu-insert a - Wait until element is visible xpath=//li[contains(@class, "select2-result-selectable") and div/text() = "Collection Filter"] - Click element xpath=//li[contains(@class, "select2-result-selectable") and div/text() = "Collection Filter"] + Insert Tile "Collection Filter" + Drag tile + Edit Current Tile +# Wait until element is visible xpath=//div[@class='plone-modal-dialog' and .//*[contains(text(), 'Collection')]] + #run keyword if $collection_name set relateditem formfield-collective-collectionfilter-tiles-filter-target_collection ${collection_name} + + Set Filter Options ${group_by} ${filter_type} ${input_type} @{options} + # Run Keyword by label Content Selector Input Text .contentlisting-tile - # Complete filter form - Wait until element is visible xpath=//div[@class='plone-modal-dialog' and .//*[contains(text(), 'Collection')]] - Click element xpath=//div[@id='formfield-collective-collectionfilter-tiles-filter-target_collection']//ul[@class='select2-choices'] - Wait until element is visible xpath=//div[@id='select2-drop']//a[.//text() = '/${collection_name}'] - Click element xpath=//div[@id='select2-drop']//a[.//text() = '/${collection_name}'] - Select from List by value css=select#collective-collectionfilter-tiles-filter-group_by ${filter_type} - Select from List by value css=select#collective-collectionfilter-tiles-filter-input_type ${input_type} Click element css=.pattern-modal-buttons #buttons-save - Drag tile Add search tile - [Arguments] ${collection_name} + [Arguments] ${collection_name}=${None} - # Insert collection search - Wait Until Element Is Visible css=.mosaic-toolbar - Click element css=.select2-container.mosaic-menu-insert a - Wait until element is visible xpath=//li[contains(@class, "select2-result-selectable") and div/text() = "Collection Search"] - Click element xpath=//li[contains(@class, "select2-result-selectable") and div/text() = "Collection Search"] + Insert tile "Collection Search" + Drag tile + Edit Current Tile + run keyword if $collection_name set relateditem formfield-collective-collectionfilter-tiles-search-target_collection ${collection_name} + # Run Keyword by label Content Selector Input Text .contentlisting-tile + Click element css=.pattern-modal-buttons #buttons-save + + +Add info tile + [Arguments] @{templates} ${hide_when}=${None} ${collection_name}=${None} + Insert tile "Collection Filter Info" + run keyword if $collection_name set relateditem formfield-collective-collectionfilter-tiles-info-target_collection ${collection_name} # Complete filter form - Wait until element is visible xpath=//div[@class='plone-modal-dialog' and .//*[contains(text(), 'Collection')]] - Wait until element is visible css=#collective-collectionfilter-tiles-search-header - Click element xpath=//div[@id='formfield-collective-collectionfilter-tiles-search-target_collection']//ul[@class='select2-choices'] - Wait until element is visible xpath=//div[@id='select2-drop']//a[.//text() = '/${collection_name}'] - Click element xpath=//div[@id='select2-drop']//a[.//text() = '/${collection_name}'] + Set Info Settings collective-collectionfilter-tiles-info @{templates} hide_when=${hide_when} + # Run Keyword by label Content Selector Input Text .contentlisting-tile + Click element css=.pattern-modal-buttons #buttons-save + Drag tile + +Set Info Settings + [Arguments] ${prefix} @{templates} ${hide_when} + + select multi select2 with label Template Type @{templates} + Run keyword if $hide_when is not ${None} Run Keyword select multi select2 with label Hide when ${hide_when} + + + +Add existing content tile + [Arguments] ${collection_name}=${None} + Insert tile "Existing Content" + run keyword if $collection_name set relateditem formfield-plone-app-standardtiles-existingcontent-content_uid ${collection_name} + Click element css=.pattern-modal-buttons #buttons-save + + Drag tile + +Add contentlisting tile + [Arguments] ${batch}=20 + Insert tile "Content listing" Drag tile + Edit Current Tile + # TODO: test using this method + # Run Keyword by label Use query parameters from content click element + + # Since we aren't using the collection we need to recreate the same settings clickin on select2 + wait until element is visible link=Select criteria + select single select2 xpath=(//div[@id='formfield-plone-app-standardtiles-contentlisting-query']//div[@class='querystring-criteria-index'])[2]/div Type + select multi select2 xpath=(//div[@id='formfield-plone-app-standardtiles-contentlisting-query']//div[@class='querystring-criteria-value'])[2]/div Event Document + # TODO: no item count in plone 5.0 + Run Keyword by label Item count Input Text ${batch} + + Click element css=.pattern-modal-buttons #buttons-save + +Edit Current Tile + Click Button css=.mosaic-selected-tile .mosaic-btn-settings + Drag tile Wait until page contains element css=.mosaic-helper-tile-new Wait until element is visible css=.mosaic-helper-tile-new Update element style css=.mosaic-IDublinCore-description-tile .mosaic-divider-bottom display block - Mouse over css=.mosaic-IDublinCore-description-tile .mosaic-divider-bottom + Mouse over xpath=(//*[contains(@class,'movable')])[last()-1] Click element css=.mosaic-selected-divider + #Click element xpath=(//*[contains(@class,'movable')])[last()-1] Filter by [Arguments] ${filter} Wait until element is visible css=.filterContent Select from List by value xpath=//div[@class = 'filterContent']//select ${filter} + +# TODO: doesn't work yet +Set relateditem + [Arguments] ${id} ${path} + Wait until element is visible xpath=//div[@id='${id}'] + Click element xpath=//div[@id='${id}']//ul[@class='select2-choices'] + Wait until element is visible xpath=//div[@id='select2-drop']//a[.//text() = '${path}'] + Click element xpath=//div[@id='select2-drop']//a[.//text() = '${path}'] + +Insert Tile "${name}" + Wait Until Element Is Visible css=.mosaic-toolbar + Click element css=.select2-container.mosaic-menu-insert a + Wait until element is visible xpath=//li[contains(@class, "select2-result-selectable") and div/text() = "${name}"] + Click element xpath=//li[contains(@class, "select2-result-selectable") and div/text() = "${name}"] diff --git a/src/collective/collectionfilter/tests/robot/test_filterportlets.robot b/src/collective/collectionfilter/tests/robot/test_filterportlets.robot index dd610eb2..9c0e18be 100644 --- a/src/collective/collectionfilter/tests/robot/test_filterportlets.robot +++ b/src/collective/collectionfilter/tests/robot/test_filterportlets.robot @@ -5,7 +5,7 @@ Resource keywords.robot # Library Remote ${PLONE_URL}/RobotRemote -Test Setup View Test Collection +Test Setup Default Setup Test Teardown Default Teardown @@ -13,99 +13,127 @@ Test Teardown Default Teardown *** Test Cases *************************************************************** -Scenario: Add filter portlets to collection - - Manage portlets - Add search portlet - Add filter portlet Subject or checkboxes_dropdowns - Add sorting portlet sortable_title links - Go to ${PLONE_URL}/testcollection - Should be 3 collection results - - Click Input "Dokumänt (2)" - Should be 2 collection results - - Click Input "All (3)" - Should be 3 collection results +Scenario: Add filter to collection + Given I've got a site with a collection + and my collection has a collection filter Subject or checkboxes_dropdowns + When I'm viewing the collection + then Should be 3 collection results + and Should be filter checkboxes All (3) Dokumänt (2) Evänt (1) Süper (2) + When Click Input "Dokumänt (2)" + then Should be 2 collection results + and Should be filter checkboxes All (3) Dokumänt (2) Evänt (1) Süper (2) + When Click Input "All (3)" + then Should be 3 collection results + and Should be filter checkboxes All (3) Dokumänt (2) Evänt (1) Süper (2) Scenario: Test Batching - Manage portlets - Add filter portlet Subject or checkboxes_dropdowns - Go to ${PLONE_URL}/testcollection - Should be 3 collection results - - Set Batch Size 1 - - Should be 1 collection results - - Click Input "Süper (2)" - Should be 1 collection results - Should be 1 pages - - Click Page "1" - Should be 1 collection results - Should be 1 pages + Given I've got a site with a collection batch=1 + and my collection has a collection filter Subject or checkboxes_dropdowns + and I'm viewing the collection + then Should be 1 collection results + when Click Input "Süper (2)" + then Should be 1 collection results + and Should be 1 pages + when Click Page "1" + then Should be 1 collection results + then Should be 1 pages ${loc}= get location should contain ${loc} collectionfilter=1 Scenario: Hide when no options - Manage portlets - Add filter portlet author_name or checkboxes_dropdowns - Go to ${PLONE_URL}/testcollection + Given I've got a site with a collection + and my collection has a collection filter author_name or checkboxes_dropdowns Hide if empty + When I'm viewing the collection + then Should be 3 collection results + then Should be 0 filter options - Should be 3 collection results - Should be 1 filter options +Scenario: Disable all filter option - Manage portlets - Set portlet "author_name" "Hide if empty" - Go to ${PLONE_URL}/testcollection - # No idea why intermittently we get 1 filter option below instead of 0 - log source - Should be 0 filter options - Should be 3 collection results + Given I've got a site with a collection + and my collection has a collection filter Subject or checkboxes_dropdowns dropdown_Select first # The 'Enable all filter option' argument here is toggling it off, so it is disabled + When I'm viewing the collection + Then Should be 2 collection results + and Should be filter checkboxes Dokumänt (2) Evänt (1) Süper (2) Scenario: show hidden filter if just narrowed down - Given Manage portlets - and Add filter portlet Type single checkboxes_dropdowns - and Set portlet "Type" "Narrow down filter options" - and Go to ${PLONE_URL}/testcollection - and Should be 3 filter options - + Given I've got a site with a collection + and my collection has a collection filter portal_type single checkboxes_dropdowns Narrow down filter options + When I'm viewing the collection + and Should be filter options All (3) Event (1) Page (2) and Select Filter Option "Event (1)" - and Should be 2 filter options - - When Manage portlets - and Set portlet "Type" "Hide if empty" - and Go to ${PLONE_URL}/testcollection - and Should be 3 filter options + Then Should be filter options All (3) Event (1) +Scenario: don't hide hidden filter if just narrowed down + Given I've got a site with a collection + and my collection has a collection filter portal_type single checkboxes_dropdowns Narrow down filter options Hide if empty + When I'm viewing the collection + and Should be filter options All (3) Event (1) Page (2) # But if we filter it down it shouldn't disappear as then we have no way to click "All" to get back and Select Filter Option "Event (1)" - Log source - Then Should be 2 filter options + Then Should be filter options All (3) Event (1) Scenario: Displaying multiple collection filters on a single page Given I've got a site with a collection - and my collection has a collection filter portlet - and my collection has a collection filter portlet group_by=Type - When I'm viewing the collection - Then I should have a portlet titled "Subject" with 4 filter options - and I should have a portlet titled "Type" with 3 filter options + and my collection has a collection filter + and my collection has a collection filter group_by=portal_type + When I'm viewing the collection + Then I should have a filter with 4 options + and I should have a filter with 3 options + and I should see 7 filter options on the page + and Should be filter checkboxes All (3) Dokumänt (2) Evänt (1) Süper (2) All (3) Event (1) Page (2) -Scenario: Combine search and OR filter +Scenario: Combine search and AND filter Given I've got a site with a collection - and my collection has a collection search portlet - and my collection has a collection filter portlet Subject and checkboxes_dropdowns + and my collection has a collection search + and my collection has a collection filter Subject and checkboxes_dropdowns When I'm viewing the collection - and Click Input "Süper (2)" + and Should be filter checkboxes All (3) Dokumänt (2) Evänt (1) Süper (2) + Then Click Input "Süper (2)" and Should be 2 collection results - and Click Input "Evänt (1)" + and Should be filter checkboxes All (3) Dokumänt (2) Evänt (1) Süper (2) + Then Click Input "Evänt (1)" and Should be 1 collection results - and I search for "Event" + and Should be filter checkboxes All (3) Dokumänt (2) Evänt (1) Süper (2) + Then I search for "Event" and Should be 1 collection results + and Should be filter checkboxes All (1) Evänt (1) Süper (1) + + +Scenario: Search filter + Given I've got a site with a collection + and my collection has a collection search + and my collection has a collection filter + and I'm viewing the collection + When I search for "Document" + Then should be 1 collection results + and Should be filter checkboxes All (1) Dokumänt (1) Süper (1) + + When I search for "& - * $" + # Checking for no error rather than results as Plone 5.2 will display no + # results for a 'bad' query, while Plone 5.1/ 5.0 will display all of the results + Then page should not contain error + + # Searching for query keywords (https://github.com/collective/collective.collectionfilter/issues/85) + When I search for "and Document" + Then should be 1 collection results + and Should be filter checkboxes All (1) Dokumänt (1) Süper (1) + When I search for "or Document" + Then should be 0 collection results + and I should see 0 filter options on the page + When I search for "not Document" + Then should be 0 collection results + and I should see 0 filter options on the page + + # the following doesn't work ... I think no 'keyup' event is fired + # Given I'm viewing the collection + # When I search for ${EMPTY} and click search + # Then should be 2 collection results + # and should be 4 filter options + + diff --git a/src/collective/collectionfilter/tests/robot/test_filterportlets_ajaxdisabled.robot b/src/collective/collectionfilter/tests/robot/test_filterportlets_ajaxdisabled.robot deleted file mode 100644 index 6773a0a5..00000000 --- a/src/collective/collectionfilter/tests/robot/test_filterportlets_ajaxdisabled.robot +++ /dev/null @@ -1,40 +0,0 @@ - -*** Settings ***************************************************************** - -Resource keywords.robot - -# Library Remote ${PLONE_URL}/RobotRemote - -Test Setup Open test browser -Test Teardown Default Teardown - - -*** Test Cases *************************************************************** - -Scenario: Searching through a portlet with ajax disabled - Given I've got a site with a collection - and my collection has a collection search portlet - and my collection has a collection filter portlet - and I'm viewing the collection - When I search for "Document" and click search - Then should be 1 collection results - and should be 3 filter options - - # Searching for query keywords (https://github.com/collective/collective.collectionfilter/issues/85) - When I search for "and Document" and click search - Then should be 1 collection results - and I should have a portlet titled "Subject" with 3 filter options - When I search for "or Document" and click search - Then I should not see any results - and I should have a portlet titled "Subject" with 0 filter options - When I search for "not Document" and click search - Then I should not see any results - and I should have a portlet titled "Subject" with 0 filter options - - # the following doesn't work ... I think no 'keyup' event is fired - # Given I'm viewing the collection - # When I search for ${EMPTY} and click search - # Then should be 2 collection results - # and should be 4 filter options - - diff --git a/src/collective/collectionfilter/tests/robot/test_filterportlets_ajaxenabled.robot b/src/collective/collectionfilter/tests/robot/test_filterportlets_ajaxenabled.robot deleted file mode 100644 index ee718ce8..00000000 --- a/src/collective/collectionfilter/tests/robot/test_filterportlets_ajaxenabled.robot +++ /dev/null @@ -1,38 +0,0 @@ - -*** Settings ***************************************************************** - -Resource keywords.robot - -# Library Remote ${PLONE_URL}/RobotRemote - -Test Setup Open test browser -Test Teardown Default Teardown - - -*** Test Cases *************************************************************** - -Scenario: Searching through a portlet with ajax enabled - Given I've got a site with a collection - and my collection has a collection search portlet - and my collection has a collection filter portlet - and I'm viewing the collection - When I search for "Document" with ajax - Then should be 1 collection results - and should be 3 filter options - - # Searching for query keywords (https://github.com/collective/collective.collectionfilter/issues/85) - When I search for "and Document" with ajax - Then should be 1 collection results - and I should have a portlet titled "Subject" with 3 filter options - When I search for "or Document" with ajax - Then I should not see any results - and I should have a portlet titled "Subject" with 0 filter options - When I search for "not Document" with ajax - Then I should not see any results - and I should have a portlet titled "Subject" with 0 filter options - - # the following doesn't work ... I think no 'keyup' event is fired - # Given I'm viewing the collection - # When I search for ${EMPTY} with ajax - # Then should be 2 collection results - # and should be 4 filter options diff --git a/src/collective/collectionfilter/tests/robot/test_filtertiles_ajaxdisabled.robot b/src/collective/collectionfilter/tests/robot/test_filtertiles_ajaxdisabled.robot deleted file mode 100644 index 8ea2b8ff..00000000 --- a/src/collective/collectionfilter/tests/robot/test_filtertiles_ajaxdisabled.robot +++ /dev/null @@ -1,47 +0,0 @@ - -*** Settings ***************************************************************** - -Resource keywords.robot - -# Library Remote ${PLONE_URL}/RobotRemote - -Test Setup Open test browser -Test Teardown Default Teardown - -*** Variables *** - -${COLLECTION_NAME} testcollection -${TEST_PAGE} ${PLONE_URL}/testdoc - - -*** Test Cases *************************************************************** - -# Scenario: Add filter tiles to page for collection - -# Log in as site owner -# Go to ${TEST_PAGE} - -# # Setup full mosaic display and open editor -# Enable mosaic layout for page ${TEST_PAGE} - -# # Add tiles to page -# Go to ${TEST_PAGE}/edit -# Add filter tile ${COLLECTION_NAME} Subject checkboxes_dropdowns -# Add search tile ${COLLECTION_NAME} -# Save mosaic page -# Go to ${TEST_PAGE} - -# # Check collection filter filters collections -# # Broken when running with AJAX enabled -# Go to ${TEST_PAGE} -# Filter by Dokumänt -# Should be 2 collection results - -# # Check collection search filters collections -# Given go to ${TEST_PAGE} -# When I search for Document and click search -# Then should be 1 collection results - -# Given Go to ${TEST_PAGE} -# When I search for ${EMPTY} and click search -# Then should be 3 collection results diff --git a/src/collective/collectionfilter/tests/robot/test_sortingportlets.robot b/src/collective/collectionfilter/tests/robot/test_sortingportlets.robot index cc433a16..b7bd2c74 100644 --- a/src/collective/collectionfilter/tests/robot/test_sortingportlets.robot +++ b/src/collective/collectionfilter/tests/robot/test_sortingportlets.robot @@ -5,7 +5,7 @@ Resource keywords.robot # Library Remote ${PLONE_URL}/RobotRemote -Test Setup Open test browser +Test Setup Default Setup Test Teardown Default Teardown @@ -13,21 +13,22 @@ Test Teardown Default Teardown Scenario: Sorting with result sorting portlet Given I've got a site with a collection - and my collection has a collection sorting portlet + and my collection has a collection sorting and I'm viewing the collection - - I sort by "sortable_title" - and I should not have a portlet titled "Error" + and I sort by "Sortable Title" + and Page should not contain Error + and Results are Sorted Scenario: Combine sort and OR filter Given I've got a site with a collection - and my collection has a collection sorting portlet - and my collection has a collection filter portlet Subject and checkboxes_dropdowns + and my collection has a collection sorting + and my collection has a collection filter Subject and checkboxes_dropdowns When I'm viewing the collection and Click Input "Süper (2)" and Should be 2 collection results and Click Input "Evänt (1)" and Should be 1 collection results - Then I sort by "sortable_title" + Then I sort by "Sortable Title" + and Page should not contain Error and Should be 1 collection results - and I should not have a portlet titled "Error" + and Results are Sorted diff --git a/src/collective/collectionfilter/tests/robot/test_tilesonly.robot b/src/collective/collectionfilter/tests/robot/test_tilesonly.robot new file mode 100644 index 00000000..c4430a79 --- /dev/null +++ b/src/collective/collectionfilter/tests/robot/test_tilesonly.robot @@ -0,0 +1,30 @@ + +*** Settings ***************************************************************** + +Resource keywords.robot + +# Library Remote ${PLONE_URL}/RobotRemote + +Test Setup Default Setup +Test Teardown Default Teardown + + + + +*** Test Cases *************************************************************** + +Scenario: Add filter before content listing + Given I've got a site without a listing + When my collection has a collection filter + then Page should contain need to add a Content Listing + When my collection has a collection search + then Page should contain need to add a Content Listing + When my collection has a collection sorting + then Page should contain need to add a Content Listing + When edit mosaic page + and save mosaic page + then Page should contain need to add a Content Listing + When edit mosaic page + and Add contentlisting tile + and save mosaic page + then Page should not contain need to add a Content Listing diff --git a/src/collective/collectionfilter/tests/test_filteritems.py b/src/collective/collectionfilter/tests/test_filteritems.py index a4efa8e0..3dde914b 100644 --- a/src/collective/collectionfilter/tests/test_filteritems.py +++ b/src/collective/collectionfilter/tests/test_filteritems.py @@ -207,8 +207,8 @@ def test_and_filter_type(self): self.assertEqual(len(result), 4) self.assertEqual(get_data_by_val(result, "all")["count"], 3) - # TODO: I'm not sure these counts are correct. It should represent how many results you will get if you click so should be smaller than this - # but I guess you need to turn on narrow down for that? + # TODO: I'm not sure these counts are correct. It should represent how many results you will get if you click + # so should be smaller than this but I guess you need to turn on narrow down for that? self.assertEqual(get_data_by_val(result, u"Süper")["count"], 2) self.assertEqual(get_data_by_val(result, u"Evänt")["count"], 1) self.assertEqual(get_data_by_val(result, u"Dokumänt")["count"], 2) diff --git a/src/collective/collectionfilter/tests/test_robot.py b/src/collective/collectionfilter/tests/test_robot.py index 5fcc0700..92c1bd4a 100644 --- a/src/collective/collectionfilter/tests/test_robot.py +++ b/src/collective/collectionfilter/tests/test_robot.py @@ -5,6 +5,9 @@ from collective.collectionfilter.testing import ( COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_ENABLED, ) +from collective.collectionfilter.testing import ( + COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_TILES, +) from plone import api from plone.app.testing import ROBOT_TEST_LEVEL from plone.testing import layered @@ -23,28 +26,56 @@ def test_suite(): for doc in os.listdir(robot_dir) if doc.endswith(".robot") and doc.startswith("test_") ] - l1 = ROBOT_TEST_LEVEL - l2 = ROBOT_TEST_LEVEL + 1 for robot_test in robot_tests: if api.env.plone_version() < "5.1" and "ajaxenabled" in robot_test: continue - elif api.env.plone_version() < "5.1": - test_layer = ( - (l1, COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_DISABLED), - ) elif "ajaxenabled" in robot_test: test_layer = ( - (l1, COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_ENABLED), + ( + ROBOT_TEST_LEVEL, + COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_ENABLED, + ), ) elif "ajaxdisabled" in robot_test: test_layer = ( - (l1, COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_DISABLED), + ( + ROBOT_TEST_LEVEL, + COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_DISABLED, + ), + ) + elif "_tiles" in robot_test: + test_layer = ( + ( + ROBOT_TEST_LEVEL, + COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_TILES, + ), + ) + elif api.env.plone_version() < "5.1": + test_layer = ( + ( + ROBOT_TEST_LEVEL, + COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_TILES, + ), + ( + ROBOT_TEST_LEVEL, + COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_DISABLED, + ), ) else: # We will run generic tests with and without ajax to test everything test_layer = ( - (l2, COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_DISABLED), - (l1, COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_ENABLED), + ( + ROBOT_TEST_LEVEL, + COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_TILES, + ), + ( + ROBOT_TEST_LEVEL, + COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_DISABLED, + ), + ( + ROBOT_TEST_LEVEL, + COLLECTIVE_COLLECTIONFILTER_ACCEPTANCE_TESTING_AJAX_ENABLED, + ), ) for level, layer in test_layer: robottestsuite = robotsuite.RobotTestSuite(robot_test) diff --git a/src/collective/collectionfilter/tiles/__init__.py b/src/collective/collectionfilter/tiles/__init__.py index 50478f63..e6e42637 100644 --- a/src/collective/collectionfilter/tiles/__init__.py +++ b/src/collective/collectionfilter/tiles/__init__.py @@ -1,6 +1,18 @@ # -*- coding: utf-8 -*- +from collective.collectionfilter import _ +from collective.collectionfilter.filteritems import CollectionishCollection +from collective.collectionfilter.interfaces import ICollectionish from plone import api +from plone.app.blocks.layoutbehavior import ILayoutAware +from plone.app.blocks.layoutbehavior import ILayoutBehaviorAdaptable +from plone.app.contenttypes.behaviors.collection import ICollection from plone.tiles.tile import PersistentTile +from zope.component import adapter +from zope.component import getMultiAdapter +from zope.component import queryAdapter +from zope.interface import implementer + +import re class DictDataWrapper(object): @@ -17,7 +29,7 @@ def __getattr__(self, name): class BaseFilterTile(PersistentTile): def available(self): # do not render when page is ajax loaded - return "ajax_load" not in self.top_request + return "ajax_load" not in self.top_request and self.is_available @property def edit_url(self): @@ -36,3 +48,150 @@ def filter_id(self): @property def reload_url(self): return self.url + + +def findall_tiles(context, spec): + request = context.REQUEST + la = ILayoutAware(context) + layout = ( + la.customContentLayout + if la.customContentLayout is not None + else la.contentLayout + if la.contentLayout is not None + else la.content + ) + if layout is None: + return [] + urls = re.findall(r"(@@[\w\.]+/\w+)", layout) + urls = [url for url in urls if url.startswith("@@{}".format(spec))] + # TODO: maybe better to get tile data? using ITileDataManager(id)? + our_tile = request.response.headers.get("x-tile-url") + tiles = [context.unrestrictedTraverse(str(url)) for url in urls] + # mosaic get confused if we are doing this while creating a filter tile + if request.response.headers.get("x-tile-url"): + if our_tile: + request.response.headers["x-tile-url"] = our_tile + else: + del request.response.headers["x-tile-url"] + return tiles + + +@implementer(ICollectionish) +@adapter(ILayoutBehaviorAdaptable) +class CollectionishLayout(CollectionishCollection): + """Provide interface for either objects with contentlisting tiles or collections or both""" + + tile = None + collection = None + + def __init__(self, context): + """Adapt either collections or contentlisting tile. The name is sorted content selector""" + self.context = context + + self.selectContent("") # get first tile + + if self.tile is None: + # Could still be a ILayoutAware collection + try: + self.collection = ICollection(self.context) + except TypeError: + self.collection = None + else: + self.collection = self.tile # to get properties + + def selectContent(self, selector=None): + """Pick tile that selector will match, otherwise pick first one. + Return None if no listing tile or collection is suitable, else return this adapter. + """ + + if selector is None: + selector = "" + self.tile = None + tiles = findall_tiles(self.context, "plone.app.standardtiles.contentlisting") + for tile in tiles: + tile.update() + tile_classes = tile.tile_class.split() + [""] + # First tile that matches all the selector classes + if all([_class in tile_classes for _class in selector.split(".")]): + self.tile = tile + break + if tiles and self.tile is None: + # TODO: what if the class is inside a special template? Just pick first? + # none of the selectors worked. Just pick any and hope it works? + self.tile = tile + if self.tile is not None or self.collection is not None: + return self + else: + return None + + @property + def sort_reversed(self): + if self.tile is not None: + return self.sort_order == "reverse" + else: + return self.collection.sort_reversed + + @property + def content_selector(self): + """will return None if no tile or colleciton found""" + if self.collection is None: + return None + elif self.tile is None: + return super(CollectionishLayout, self).content_selector + classes = ["contentlisting-tile"] + if self.tile.tile_class: + classes += self.tile.tile_class.split() + return "." + ".".join(classes) + + def results(self, custom_query, request_params): + """Search results""" + if self.tile is None: + return super(CollectionishLayout, self).results( + custom_query, request_params + ) + + builder = getMultiAdapter( + (self.context, self.context.REQUEST), name="querybuilderresults" + ) + + # Include query parameters from request if not set to ignore + contentFilter = {} + if not getattr(self.tile, "ignore_request_params", False): + contentFilter = dict(self.context.REQUEST.get("contentFilter", {})) + + # TODO: handle events extra params + + return builder( + query=self.query, + sort_on=self.sort_on or "getObjPositionInParent", + sort_order=self.sort_order, + limit=self.limit, + batch=False, + brains=True, + custom_query=custom_query if custom_query is not None else contentFilter, + ) + + +def validateFilterTileModify(tile, event): + # TODO: is ok in the acquisiton path? + target = tile.collection + if target is not None: + target = queryAdapter(target.getObject(), ICollectionish) + if target is None or target.content_selector is None: + api.portal.show_message( + _( + u"You will need to add a Content Listing tile or target a collection to make Filters work" + ), + request=tile.context.REQUEST, + type=u"warning", + ) + return False + return True + + +def validateFilterMosaicModify(context, event): + # search the layout for any filters and then see if they have a matching listing + tiles = findall_tiles(context, "collective.collectionfilter.tiles.") + for tile in tiles: + if not validateFilterTileModify(tile, event): + break diff --git a/src/collective/collectionfilter/tiles/configure.zcml b/src/collective/collectionfilter/tiles/configure.zcml index bcf4775f..062c1b43 100644 --- a/src/collective/collectionfilter/tiles/configure.zcml +++ b/src/collective/collectionfilter/tiles/configure.zcml @@ -8,6 +8,25 @@ + + + + + + + + =3.7 # Needed for mosaic. Might also try to pin an earlier version of mosaic if this doesn't work plone.app.mosaic = 2.1.1 plone.app.blocks = 4.1.1 plone.app.drafts = 1.1.2 -plone.app.standardtiles = 2.3.0 -plone.app.tiles = 3.0.3 +plone.app.standardtiles = < 2.4.0 +plone.app.tiles = 3.1.2 plone.tiles = 2.0.0 plone.jsonserializer = 0.9.6 diff --git a/test-5.1.x.cfg b/test-5.1.x.cfg index 643dc8ee..197a209d 100644 --- a/test-5.1.x.cfg +++ b/test-5.1.x.cfg @@ -3,7 +3,6 @@ extends = https://raw.githubusercontent.com/collective/buildout.plonetest/master/test-5.1.x.cfg https://raw.githubusercontent.com/collective/buildout.plonetest/master/qa.cfg base.cfg - versions-2.7.cfg parts += test @@ -14,14 +13,11 @@ package-name = collective.collectionfilter package-extras = [test] test-eggs = -[code-analysis] -flake8-ignore = E501,E241 - [versions] setuptools = zc.buildout = configparser = 3.8.1 -coverage = >=3.7 + plone.formwidget.geolocation = < 2.2.2 # Introduced a dep on CMFPlone 5.2 diff --git a/test-5.2.x.cfg b/test-5.2.x.cfg index 6f082c72..dd888675 100644 --- a/test-5.2.x.cfg +++ b/test-5.2.x.cfg @@ -2,6 +2,7 @@ extends = https://raw.githubusercontent.com/collective/buildout.plonetest/master/test-5.2.x.cfg https://raw.githubusercontent.com/collective/buildout.plonetest/master/qa.cfg + https://raw.githubusercontent.com/plone/plone.app.mosaic/master/versions.cfg base.cfg parts += @@ -14,10 +15,8 @@ package-extras = [test] test-eggs = [code-analysis] -flake8-ignore = E501,E241 +extensions += + flake8-black -[versions] -#setuptools = -#zc.buildout = +[versions:python3] coverage = >=3.7 - diff --git a/versions-2.7.cfg b/versions-2.7.cfg deleted file mode 100644 index e8c1217a..00000000 --- a/versions-2.7.cfg +++ /dev/null @@ -1,29 +0,0 @@ -[versions] -# to get codeanalysis working -build = 0.1 - -# Required by: -# build==0.1.0 -# pep517==0.9.1 -importlib-metadata = 3.3.0 - -# Required by: -# build==0.1.0 -packaging = 20.8 - -# Required by: -# build==0.1.0 -typing = 3.7.4.3 - -# Required by: -# pep517==0.9.1 -zipp = 3.4.0 - -pycodestyle = 2.0.0 -flake8 = 2.6.2 -configparser = <5.0.0 - -flake8-pep3101 = 0.6 -flake8-commas = 0.1.6 -flake8-isort = 1.3 -flake8-deprecated = 1.0 \ No newline at end of file