Skip to content

Commit

Permalink
Merge pull request #75 from collective/utility
Browse files Browse the repository at this point in the history
cleanups + utility
  • Loading branch information
jensens committed Sep 23, 2022
2 parents 1357803 + 8f75e63 commit db0019d
Show file tree
Hide file tree
Showing 17 changed files with 80 additions and 121 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Expand Up @@ -6,6 +6,11 @@ Changelog

- Avoid a double loop by using set operations. [gforcada]

- Clean up old code. [gforcada]

- Turn the tool into a utility.
Fixes `issue #15 <https://github.com/collective/Products.PloneKeywordManager/issues/15>`_. [gforcada]

4.0.1 (2022-05-06)
------------------

Expand Down
22 changes: 0 additions & 22 deletions src/Products/PloneKeywordManager/__init__.py
Expand Up @@ -17,32 +17,10 @@
# http://trific.ath.cx/resources/python/levenshtein/
##
###
from AccessControl.Permission import registerPermissions
from Products.CMFCore.utils import ToolInit
from Products.PloneKeywordManager import config
from Products.PloneKeywordManager import tool
from zope.i18nmessageid import MessageFactory

import logging


global cmf_keyword_manager_globals


cmf_keyword_manager_globals = globals()

keywordmanagerMessageFactory = MessageFactory("Products.PloneKeywordManager")
logger = logging.getLogger("Products.PloneKeywordManager")


registerPermissions(
[(config.MANAGE_KEYWORDS_PERMISSION, [])], ("Manager", "Site Administrator")
)


def initialize(context):

new_tool = ToolInit(
config.TOOL_NAME, tools=(tool.PloneKeywordManager,), icon="tool.gif"
)
new_tool.initialize(context)
15 changes: 3 additions & 12 deletions src/Products/PloneKeywordManager/browser/prefs_keywords_view.pt
Expand Up @@ -179,18 +179,9 @@ nav.pagination {
onclick string:document.forms['keyword_edit_form'].changeto.value='${keyword}';; return true;;" />
<label tal:content="keyword_display" tal:attributes="for string:keyword-${keyword_id};">Keyword</label>
<span class="keyword_count"> (<tal:count tal:content="keyword_count">0101</tal:count>) </span>
<tal:p5 tal:condition="view/is_plone_5">
<a href="#" tal:attributes="href string:${navroot_url}/@@search?${field}=${keyword_quote}">
<span class="icon-search"></span></a>
</tal:p5>
<tal:p4 tal:condition="not: view/is_plone_5">
<a href="#" tal:attributes="href string:${navroot_url}/@@search?${field}=${keyword_quote}">
<img i18n:attributes="alt"
i18n:domain="plone"
tal:attributes="src string:${portal_url}/search_icon.png;"
alt="Search"/></a>
</tal:p4>

<a href="#" tal:attributes="href string:${navroot_url}/@@search?${field}=${keyword_quote}">
<span class="icon-search"></span>
</a>

<div class="simkeywords" style="display:none;">
<span tal:repeat="item python:view.getScoredMatches(keyword, batch, num_similar, score)" style="white-space: nowrap;">
Expand Down
10 changes: 4 additions & 6 deletions src/Products/PloneKeywordManager/browser/prefs_keywords_view.py
Expand Up @@ -3,14 +3,13 @@
from Products.Five import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.PloneKeywordManager import keywordmanagerMessageFactory as _
from Products.PloneKeywordManager import logger
from Products.PloneKeywordManager.interfaces import IKeywordManager
from Products.PloneKeywordManager.compat import to_str
from ZTUtils import make_query
from zope.component import getUtility

import json
import logging


logger = logging.getLogger("Products.PloneKeywordManager")


class PrefsKeywordsView(BrowserView):
Expand All @@ -22,10 +21,9 @@ class PrefsKeywordsView(BrowserView):

def __init__(self, context, request):
super().__init__(context, request)
self.pkm = api.portal.get_tool("portal_keyword_manager")
self.pkm = getUtility(IKeywordManager)

def __call__(self):
self.is_plone_5 = True
if not self.request.form.get(
"form.button.Merge", ""
) and not self.request.form.get("form.button.Delete", ""):
Expand Down
4 changes: 0 additions & 4 deletions src/Products/PloneKeywordManager/config.py
@@ -1,9 +1,5 @@
PROJECTNAME = "PloneKeywordManager"

MANAGE_KEYWORDS_PERMISSION = "Manage Keywords"

TOOL_NAME = "Plone Keyword Manager Tool"

# Meta type of the keyword indexes. If you're one of those crazy people that use
# custom indexes, you'll want to update this.
META_TYPE = "KeywordIndex"
Expand Down
3 changes: 3 additions & 0 deletions src/Products/PloneKeywordManager/configure.zcml
Expand Up @@ -35,4 +35,7 @@
factory=".setuphandlers.HiddenProfiles"
name="PloneKeywordManager-hiddenprofiles"
/>
<utility
factory=".tool.KeywordManager"
/>
</configure>
4 changes: 2 additions & 2 deletions src/Products/PloneKeywordManager/interfaces.py
Expand Up @@ -5,8 +5,8 @@
from zope.interface import Interface


class IPloneKeywordManager(Interface):
"""A portal tool for managing keywords"""
class IKeywordManager(Interface):
"""A utility that allows to manage keywords"""

def change(old_keywords, new_keyword):
"""Updates all objects using the old_keywords.
Expand Down
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<metadata>
<version>3</version>
<version>4</version>
<description>Keyword manager</description>
</metadata>
5 changes: 0 additions & 5 deletions src/Products/PloneKeywordManager/profiles/default/toolset.xml

This file was deleted.

15 changes: 3 additions & 12 deletions src/Products/PloneKeywordManager/tests/base.py
@@ -1,10 +1,9 @@
from plone import api
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
from Products.PloneKeywordManager.browser.interfaces import IPloneKeywordManagerLayer
from Products.PloneKeywordManager.testing import PLONEKEYWORDMANAGER_INTEGRATION_TESTING
from zope import interface
from Products.PloneKeywordManager.interfaces import IKeywordManager
from zope.component import getMultiAdapter
from zope.component import getUtility

import unittest

Expand All @@ -16,16 +15,8 @@ class BaseIntegrationTestCase(unittest.TestCase):
def setUp(self):
self.portal = self.layer["portal"]
self.request = self.layer["request"]
self.markRequestWithLayer()
setRoles(self.portal, TEST_USER_ID, ["Manager"])
self.pkm = api.portal.get_tool("portal_keyword_manager")

def markRequestWithLayer(self):
# to be removed when p.a.testing will fix https://dev.plone.org/ticket/11673
# Well, that's a 404, but p.a.testing 4.2.2 -> 4.2.4 has a release note that it was fixed
# Plone 4.3.3 release notes.
request = self.layer["request"]
interface.alsoProvides(request, IPloneKeywordManagerLayer)
self.pkm = getUtility(IKeywordManager)


class PKMTestCase(BaseIntegrationTestCase):
Expand Down
Expand Up @@ -6,6 +6,7 @@

import unittest


class NonAsciiKeywordsTestCase(PKMTestCase):
def setUp(self):
super().setUp()
Expand All @@ -20,23 +21,6 @@ def setUp(self):
]
self.document.reindexObject()

# def test_show_non_ascii_bugs(self):
# """
# this demonstrates two bugs in the UI, which appear only with non-ASCII keywords.
# it is commented out, as will obviously fail after the bugfixes.
# """
# self.assertRaises(
# UnicodeDecodeError,
# self._action_change,
# [u"Fr\\xfchst\\xfcck".decode("utf-8"), "Mittagessen"],
# "Abendessen",
# )
# self.assertRaises(
# UnicodeDecodeError,
# self._action_delete,
# [u"Fr\\xfchst\\xfcck".decode("utf-8")],
# )

def test_pref_keywords_action_change_keywords(self):
"""test the bugfix for prefs_keywords_action_change when keywords
contains at least one element with non ASCII characters"""
Expand Down
33 changes: 33 additions & 0 deletions src/Products/PloneKeywordManager/tests/test_setup.py
Expand Up @@ -4,6 +4,8 @@
from plone.app.testing import TEST_USER_ID
from Products.CMFPlone.utils import get_installer
from Products.PloneKeywordManager.testing import PLONEKEYWORDMANAGER_INTEGRATION_TESTING
from Products.PloneKeywordManager.setuphandlers import importKeywords
from Products.PloneKeywordManager.upgrades import to_4

import unittest

Expand Down Expand Up @@ -33,6 +35,37 @@ def test_browserlayer(self):

self.assertIn(IPloneKeywordManagerLayer, utils.registered_layers())

def test_add_keywords_from_profile(self):
"""Check that the setuphandlers code imports the keywords."""
class FakeContext:
def readDataFile(self, filename):
return "\n".join(new_keywords)

def getSite(self):
return api.portal.get()

setRoles(self.portal, TEST_USER_ID, ["Manager"])

new_keywords = ("apple", "pear", "pineapple", "cherries")
self.assertNotIn("keywords", self.portal.objectIds())
context = FakeContext()
importKeywords(context)
catalog = api.portal.get_tool("portal_catalog")
self.assertIn("keywords", self.portal.objectIds())
keywords_on_obj = self.portal.keywords.Subject()
for keyword in new_keywords:
self.assertIn(keyword, keywords_on_obj)

def test_upgrade_setp_to_4(self):
"""Check that the persistent tool is removed"""
self.portal
setRoles(self.portal, TEST_USER_ID, ["Manager"])
api.content.create(
container=self.portal, type="Document", id="portal_keyword_manager"
)
to_4(self.portal.portal_setup)
self.assertNotIn("portal_keyword_manager", self.portal.objectIds())


class TestUninstall(unittest.TestCase):

Expand Down
Binary file removed src/Products/PloneKeywordManager/tool.gif
Binary file not shown.
37 changes: 5 additions & 32 deletions src/Products/PloneKeywordManager/tool.py
@@ -1,20 +1,15 @@
# Copyright (c) 2005 gocept gmbh & co. kg
# See also LICENSE.txt
from AccessControl import ClassSecurityInfo
from AccessControl.class_init import InitializeClass
from Acquisition import aq_base
from OFS.SimpleItem import SimpleItem
from operator import itemgetter
from plone import api
from plone.app.discussion.interfaces import IComment
from plone.dexterity.interfaces import IDexterityContent
from Products.CMFCore import permissions as CMFCorePermissions
from Products.CMFCore.indexing import processQueue
from Products.CMFCore.utils import UniqueObject
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.PloneKeywordManager import config
from Products.PloneKeywordManager.compat import to_str
from Products.PloneKeywordManager.interfaces import IPloneKeywordManager
from Products.PloneKeywordManager.interfaces import IKeywordManager
from zope import interface


Expand All @@ -29,23 +24,14 @@
USE_LEVENSHTEIN = False


@interface.implementer(IPloneKeywordManager)
class PloneKeywordManager(UniqueObject, SimpleItem):
"""A portal wide tool for managing keywords within Plone."""
@interface.implementer(IKeywordManager)
class KeywordManager:
"""A utility to manage keywords within Plone."""

plone_tool = 1

id = "portal_keyword_manager"
meta_type = "Plone Keyword Manager Tool"
security = ClassSecurityInfo()

manage_options = ({"label": "Overview", "action": "manage_overview"},)

security.declareProtected(CMFCorePermissions.ManagePortal, "manage_overview")
manage_overview = PageTemplateFile(
"www/explainTool", globals(), __name__="manage_overview"
)

def _getFullIndexList(self, indexName):
idxs = set([indexName]).union(config.ALWAYS_REINDEX)
return list(idxs)
Expand Down Expand Up @@ -142,6 +128,7 @@ def getKeywords(self, indexName="Subject"):

catalog = api.portal.get_tool("portal_catalog")
keywords = catalog.uniqueValuesFor(indexName)

# Filter out Null keywords. The sorting breaks when None is indexed.
def notNone(x):
return x is not None
Expand All @@ -151,17 +138,6 @@ def notNone(x):
# can we turn this into a yield?
return list(sorted(keywords, key=lambda x: x.lower()))

def getKeywordsWithLengths(self, indexName="Subject"):
processQueue()
if indexName not in self.getKeywordIndexes():
raise ValueError(f"{indexName} is not a valid field")

catalog = api.portal.get_tool("portal_catalog")
idx = catalog._catalog.getIndex(indexName)
keywords = idx.uniqueValues(withLengths=1)

return list(sorted(keywords, key=lambda x, y: x.lower()))

def getKeywordLength(self, key, indexName="Subject"):
processQueue()
if indexName not in self.getKeywordIndexes():
Expand Down Expand Up @@ -299,6 +275,3 @@ def getFieldValue(self, obj, indexName):
return fieldVal()
else:
return fieldVal


InitializeClass(PloneKeywordManager)
10 changes: 10 additions & 0 deletions src/Products/PloneKeywordManager/upgrades.py
@@ -1,10 +1,20 @@
from plone import api
from Products.PloneKeywordManager import logger


default_profile = "profile-Products.PloneKeywordManager:default"
uninstall_profile = "profile-Products.PloneKeywordManager:uninstall"


def to_4(context):
"""Remove persistent tool"""
tool_id = "portal_keyword_manager"
portal = api.portal.get()
if tool_id in portal.objectIds():
portal.manage_delObjects([tool_id])
logger.info("Removed persistent tool")


def to_3(context):
"""
remove unused skins and add browserlayer
Expand Down
10 changes: 10 additions & 0 deletions src/Products/PloneKeywordManager/upgrades.zcml
Expand Up @@ -4,6 +4,16 @@
i18n_domain="Products.PloneKeywordManager"
>

<!-- Upgrade steps -->
<gs:upgradeStep
title="Remove tool"
description="Remove Products.PloneKeywordManager persistent tool"
profile="Products.PloneKeywordManager:default"
source="3"
destination="4"
handler=".upgrades.to_4"
/>

<!-- Upgrade steps -->
<gs:upgradeStep
title="Upgrade Products.PloneKeywordManager 3"
Expand Down
8 changes: 0 additions & 8 deletions src/Products/PloneKeywordManager/www/explainTool.zpt

This file was deleted.

0 comments on commit db0019d

Please sign in to comment.