Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/test-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ jobs:
- ["ubuntu", "ubuntu-latest"]
config:
# [Python version, visual name, tox env]
- ["3.14", "6.2 on py3.14", "py314-plone62"]
- ["3.10", "6.2 on py3.10", "py310-plone62"]
- ["3.14", "6.3 on py3.14", "py314-plone63"]
- ["3.10", "6.3 on py3.10", "py310-plone63"]

runs-on: ${{ matrix.os[1] }}
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
Expand Down
4 changes: 2 additions & 2 deletions .meta.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# See the inline comments on how to expand/tweak this configuration file
[meta]
template = "default"
commit-id = "2.8.1.dev0"
commit-id = "2.9.0"

[pyproject]
dependencies_ignores = "['plone.app.relationfield', 'plone.app.multilingual', 'Products.CMFPlone']"
Expand All @@ -14,4 +14,4 @@ extra_lines = """
"""

[tox]
test_matrix = { "6.2" = ["*"] }
test_matrix = { "6.3" = ["*"] }
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repos:
hooks:
- id: isort
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 26.3.1
rev: 26.5.1
hooks:
- id: black
- repo: https://github.com/collective/zpretty
Expand Down
10 changes: 10 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ Changelog

.. towncrier release notes start

7.0.0a1 (2026-05-27)
--------------------

Breaking changes:


- Moved plone.app.i18n.browser.languageselector template to plone.app.layout.viewlets
[agitator] (#3953)


6.0.1 (2026-05-07)
------------------

Expand Down
2 changes: 2 additions & 0 deletions news/+meta.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Update configuration files.
[plone devs]
1 change: 1 addition & 0 deletions news/+sitemapcache.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Take into account the value of request parameter page for caching the generated sitemap @erral
2 changes: 2 additions & 0 deletions news/3953.breaking.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Move ``plone.app.linkintegrity`` template/views to ``plone.app.layout.views.linkintegrity``
[petschki]
8 changes: 7 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pathlib import Path
from setuptools import setup

version = "6.0.2.dev0"
version = "7.0.0a2.dev0"

long_description = (
f"{Path('README.rst').read_text()}\n{Path('CHANGES.rst').read_text()}"
Expand Down Expand Up @@ -42,6 +42,7 @@
install_requires=[
"plone.app.content",
"plone.app.dexterity",
"plone.app.linkintegrity",
"plone.app.relationfield",
"plone.app.uuid",
"plone.app.viewletmanager >=1.2",
Expand All @@ -61,13 +62,18 @@
extras_require=dict(
test=[
"plone.app.contenttypes[test]",
"plone.app.linkintegrity",
"plone.app.relationfield",
"plone.app.robotframework",
"plone.app.testing",
"plone.app.textfield",
"plone.browserlayer",
"plone.dexterity",
"plone.locking",
"plone.testing",
"robotsuite",
"z3c.relationfield",
"zc.relation",
"zope.intid",
]
),
Expand Down
4 changes: 2 additions & 2 deletions src/plone/app/layout/analytics/tests/test_doctests.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from plone.app.layout.testing import FUNCTIONAL_TESTING
from plone.app.layout.testing import PLONE_APP_LAYOUT_FUNCTIONAL_TESTING
from plone.testing import layered

import doctest
Expand All @@ -22,7 +22,7 @@ def test_suite():
test,
optionflags=OPTIONFLAGS,
),
layer=FUNCTIONAL_TESTING,
layer=PLONE_APP_LAYOUT_FUNCTIONAL_TESTING,
)
for test in normal_testfiles
]
Expand Down
17 changes: 17 additions & 0 deletions src/plone/app/layout/controlpanels/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,21 @@
xmlns:zcml="http://namespaces.zope.org/zcml"
>

<browser:page
name="delete_confirmation_info"
for="*"
class=".linkintegrity.DeleteConfirmationInfo"
permission="zope2.Public"
layer="plone.app.layout.interfaces.IPloneAppLayoutLayer"
/>

<browser:page
name="updateLinkIntegrityInformation"
for="plone.base.interfaces.IPloneSiteRoot"
class=".linkintegrity.UpdateView"
template="templates/linkintegrity_update.pt"
permission="cmf.ManagePortal"
layer="plone.app.layout.interfaces.IPloneAppLayoutLayer"
/>

</configure>
76 changes: 76 additions & 0 deletions src/plone/app/layout/controlpanels/linkintegrity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from Acquisition import aq_inner
from datetime import datetime
from datetime import timedelta
from plone.app.linkintegrity.browser.info import (
DeleteConfirmationInfo as DeleteConfirmationInfoAPI,
)
from plone.app.linkintegrity.handlers import modifiedContent
from plone.base import PloneMessageFactory as _
from Products.CMFCore.utils import getToolByName
from Products.Five import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from Products.statusmessages.interfaces import IStatusMessage
from transaction import savepoint
from zExceptions import NotFound

import logging

logger = logging.getLogger(__name__)


class DeleteConfirmationInfo(DeleteConfirmationInfoAPI):
template = ViewPageTemplateFile(
"templates/linkintegrity_delete_confirmation_info.pt"
)


class UpdateView(BrowserView):
"""Iterate over all catalogued items and update linkintegrity-information."""

def __call__(self):
context = aq_inner(self.context)
request = aq_inner(self.request)
if "update" in request.form or "delete_all" in request.form:
starttime = datetime.now()
count = self.update()
duration = timedelta(seconds=(datetime.now() - starttime).seconds)
msg = _(
"linkintegrity_update_info",
default="Link integrity information updated for ${count} "
+ "items in ${time} seconds.",
mapping={"count": count, "time": str(duration)},
)
IStatusMessage(request).add(msg, type="info")
msg = "Updated {} items in {} seconds".format(
count,
str(duration),
)
logger.info(msg)
request.RESPONSE.redirect(getToolByName(context, "portal_url")())
elif "cancel" in request.form:
msg = _("Update cancelled.")
IStatusMessage(request).add(msg, type="info")
request.RESPONSE.redirect(getToolByName(context, "portal_url")())
else:
return self.index()

def update(self):
catalog = getToolByName(self.context, "portal_catalog")
count = 0

for brain in catalog():
try:
obj = brain.getObject()
except (AttributeError, NotFound, KeyError):
msg = "Catalog inconsistency: {0} not found!"
logger.error(msg.format(brain.getPath()), exc_info=1)
continue
try:
modifiedContent(obj, "dummy event parameter")
count += 1
except Exception:
msg = "Error updating linkintegrity-info for {0}."
logger.error(msg.format(obj.absolute_url()), exc_info=1)
if count % 1000 == 0:
savepoint(optimistic=True)
return count
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
xmlns:metal="http://xml.zope.org/namespaces/metal"
xmlns:tal="http://xml.zope.org/namespaces/tal"
lang="en"
xml:lang="en"
tal:omit-tag=""
i18n:domain="plone"
>
<body tal:define="
breaches python:view.breaches;
"
tal:omit-tag=""
>

<h2 tal:condition="breaches"
i18n:translate="linkintegrity_breaches_title"
>Potential link breakage</h2>

<div id="content-core">

<p tal:condition="breaches"
i18n:translate="linkintegrity_instructions"
>
By deleting this item, you will break links that exist in the items listed
below. If this is indeed what you want to do, we recommend that you remove
these references first.
</p>

<div tal:define="
token context/@@authenticator/token;
">
<article class="breach-container"
tal:repeat="breach breaches"
>

<tal:target tal:define="
target breach/target;
">
<header><a href="${target/url}"
tal:content="target/title"
></a></header>
<p>
<span i18n:translate="linkintegrity_is_referenced">
This
<span tal:content="target/type_title"
i18n:name="portal_type"
></span>
is referenced by the following items:
</span>
</p>
</tal:target>

<ul>
<li class="breach-item"
tal:repeat="source python:breach['sources']"
>
<tal:visible condition="source/accessible">
<a tal:content="source/title"
tal:attributes="
href source/url;
"
></a>
[<a target="_blank"
tal:attributes="
href string:${source/url}/edit?_authenticator=${token};
"
i18n:translate="linkintegrity_edit_in_new_window"
>Edit in new window</a>]
</tal:visible>
<tal:private condition="not: source/accessible"
i18n:translate="linkintegrity_item_not_accessible"
>
The item is not accessible.
</tal:private>
</li>
</ul>

</article>

<div tal:define="
breach_count view/breach_count;
"
tal:condition="breach_count"
>

<h2 i18n:translate="deleting_overview">Deleting overview</h2>
<p>
<span tal:define="
refs python:len(breach_count);
"
i18n:translate="selected_folders_with_content"
>
Number of selected, non-empty folders:
<strong><span tal:replace="refs"
i18n:name="refs"
></span></strong>
</span>
</p>
<ul>
<li tal:repeat="content python:breach_count">
<span i18n:translate="deleting_contents">
Following content within
<strong><span tal:replace="content"
i18n:name="content"
></span></strong>
will also be deleted:
</span><br />
<ul>
<li tal:define="
objects view/objects;
"
tal:repeat="item python:range(3) "
>
<span tal:replace="python: breach_count[content][item]"></span>
<span tal:replace="python: objects[item]"></span>
</li>
</ul>
</li>
</ul>

</div>

<p tal:condition="breaches"
i18n:translate="linkintegrity_delete_anyway"
>
Would you like to delete it anyway?
</p>

</div>

</div>

</body>
</html>
Loading