SideNoteEditStep bugfixes

pull/10/head
Jens Pelzetter 2021-05-18 20:29:55 +02:00
parent 0cce876a5b
commit 5711a086ea
8 changed files with 285 additions and 114 deletions

View File

@ -37,5 +37,16 @@ public class CmsAssetEditSteps implements MvcAssetEditSteps {
return classes;
}
@Override
public Set<Class<?>> getResourceClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(SideNoteEditStepResources.class);
return classes;
}
}

View File

@ -173,8 +173,7 @@ public class SideNoteEditStep extends AbstractMvcAssetEditStep {
final String sectionIdentifier,
@PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
final String assetPath,
@FormParam("locale") final String localeParam,
@FormParam("value") final String value
@FormParam("locale") final String localeParam
) {
try {
init();
@ -186,7 +185,7 @@ public class SideNoteEditStep extends AbstractMvcAssetEditStep {
if (assetPermissionsChecker.canEditAsset(getAsset())) {
final Locale locale = new Locale(localeParam);
getSideNote().getText().addValue(locale, value);
getSideNote().getText().addValue(locale, "");
assetRepo.save(getSideNote());
return buildRedirectPathForStep();

View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2021 LibreCCM Foundation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
package org.librecms.ui.contentsections.assets;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.librecms.assets.SideNote;
import org.librecms.contentsection.Asset;
import org.librecms.contentsection.AssetRepository;
import org.librecms.contentsection.ContentSection;
import org.librecms.ui.contentsections.AssetPermissionsChecker;
import org.librecms.ui.contentsections.ContentSectionsUi;
import java.util.Locale;
import java.util.StringTokenizer;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.GET;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
*
* @author <a href="mailto:jens.pelzetter@googlemail.com">Jens Pelzetter</a>
*/
@RequestScoped
@Path(MvcAssetEditSteps.PATH_PREFIX + "sidenote-edit-resources")
public class SideNoteEditStepResources {
@Inject
private AssetRepository assetRepo;
@Inject
private ContentSectionsUi sectionsUi;
@Inject
private AssetPermissionsChecker assetPermissionsChecker;
@GET
@Path("/variants/wordcount/{locale}")
@Produces(MediaType.TEXT_HTML)
@Transactional(Transactional.TxType.REQUIRED)
public String getWordCount(
@PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
final String assetPathParam,
@PathParam("locale") final String localeParam
) {
final ContentSection contentSection = sectionsUi
.findContentSection(sectionIdentifier)
.orElseThrow(
() -> new NotFoundException()
);
final Asset asset = assetRepo
.findByPath(contentSection, assetPathParam)
.orElseThrow(() -> new NotFoundException());
if (!(asset instanceof SideNote)) {
throw new NotFoundException();
}
final SideNote sideNote = (SideNote) asset;
if (assetPermissionsChecker.canEditAsset(asset)) {
final String text = sideNote
.getText()
.getValue(new Locale(localeParam));
final Document jsoupDoc = Jsoup.parseBodyFragment(text);
final long result = new StringTokenizer(
jsoupDoc.body().text()
).countTokens();
return Long.toString(result);
} else {
throw new ForbiddenException();
}
}
@GET
@Path("/variants/{locale}")
@Produces(MediaType.TEXT_HTML)
@Transactional(Transactional.TxType.REQUIRED)
public String viewTextValue(
@PathParam(MvcAssetEditSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
final String assetPathParam,
@PathParam("locale") final String localeParam
) {
final ContentSection contentSection = sectionsUi
.findContentSection(sectionIdentifier)
.orElseThrow(
() -> new NotFoundException()
);
final Asset asset = assetRepo
.findByPath(contentSection, assetPathParam)
.orElseThrow(() -> new NotFoundException());
if (!(asset instanceof SideNote)) {
throw new NotFoundException();
}
final SideNote sideNote = (SideNote) asset;
if (assetPermissionsChecker.canEditAsset(asset)) {
return sideNote.getText().getValue(new Locale(localeParam));
} else {
throw new ForbiddenException();
}
}
}

View File

@ -95,7 +95,9 @@ public class MvcArticleTextBodyStepResources {
.getText()
.getValue(new Locale(localeParam));
final Document jsoupDoc = Jsoup.parseBodyFragment(text);
long result = new StringTokenizer(jsoupDoc.body().text()).countTokens();
final long result = new StringTokenizer(
jsoupDoc.body().text()
).countTokens();
return Long.toString(result);
} else {
throw new ForbiddenException();

View File

@ -11,74 +11,74 @@
<ui:define name="editStep">
<div class="container">
<h2>#{CmsAssetsStepsDefaultMessagesBundle.getMessage('sidenote.editstep.header', [CmsSideNoteEditStep.name])}</h2>
</div>
<h3>#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.header']}</h3>
<div class="d-flex">
<pre class="mr-2">#{CmsSideNoteEditStep.name}</pre>
<c:if test="#{CmsSideNoteEditStep.canEdit}">
<button class="btn btn-primary btn-sm"
data-toggle="modal"
data-target="#name-edit-dialog"
type="button">
<bootstrap:svgIcon icon="pen" />
<span class="sr-only">
#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.edit']}
</span>
</button>
</c:if>
</div>
<c:if test="#{CmsSideNoteEditStep.canEdit}">
<div aria-hidden="true"
aria-labelledby="name-edit-dialog-title"
class="modal fade"
id="name-edit-dialog"
tabindex="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSelectedAssetModel.assetPath}/@sidenote-edit/name"
class="modal-content"
method="post">
<div class="modal-header">
<h4 class="modal-title"
id="name-edit-dialog-title">
#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.edit.title']}
</h4>
<button aria-label="#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.edit.close']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x" />
</button>
</div>
<div class="modal-body">
<bootstrap:formGroupText
help="#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.help']}"
inputId="name"
label="#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.label']}"
name="name"
pattern="^([a-zA-Z0-9_-]*)$"
required="true"
value="#{CmsSideNoteEditStep.name}"
/>
</div>
<div class="modal-footer">
<button class="btn btn-warning"
data-dismiss="modal"
type="button">
#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.close']}
</button>
<button class="btn btn-success"
type="submit">
#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.submit']}
</button>
</div>
</form>
</div>
<h3>#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.header']}</h3>
<div class="d-flex">
<pre class="mr-2">#{CmsSideNoteEditStep.name}</pre>
<c:if test="#{CmsSideNoteEditStep.canEdit}">
<button class="btn btn-primary btn-sm"
data-toggle="modal"
data-target="#name-edit-dialog"
type="button">
<bootstrap:svgIcon icon="pen" />
<span class="sr-only">
#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.edit']}
</span>
</button>
</c:if>
</div>
</c:if>
<libreccm:localizedStringEditor
<c:if test="#{CmsSideNoteEditStep.canEdit}">
<div aria-hidden="true"
aria-labelledby="name-edit-dialog-title"
class="modal fade"
id="name-edit-dialog"
tabindex="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSelectedAssetModel.assetPath}/@sidenote-edit/name"
class="modal-content"
method="post">
<div class="modal-header">
<h4 class="modal-title"
id="name-edit-dialog-title">
#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.edit.title']}
</h4>
<button aria-label="#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.edit.close']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x" />
</button>
</div>
<div class="modal-body">
<bootstrap:formGroupText
help="#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.help']}"
inputId="name"
label="#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.label']}"
name="name"
pattern="^([a-zA-Z0-9_-]*)$"
required="true"
value="#{CmsSideNoteEditStep.name}"
/>
</div>
<div class="modal-footer">
<button class="btn btn-warning"
data-dismiss="modal"
type="button">
#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.close']}
</button>
<button class="btn btn-success"
type="submit">
#{CmsAssetsStepsDefaultMessagesBundle['editstep.name.submit']}
</button>
</div>
</form>
</div>
</div>
</c:if>
<libreccm:localizedStringEditor
addButtonLabel="#{CmsAssetsStepsDefaultMessagesBundle['editstep.title.add_button.label']}"
addDialogCancelLabel="#{CmsAssetsStepsDefaultMessagesBundle['editstep.title.add.cancel']}"
addDialogLocaleSelectHelp="#{CmsAssetsStepsDefaultMessagesBundle['editstep.title.add.locale.help']}"
@ -111,28 +111,34 @@
values="#{CmsSideNoteEditStep.titleValues}"
/>
<h3>#{CmsAssetsStepsDefaultMessagesBundle['sidenote.editstep.text.header']}</h3>
<c:if test="#{CmsPostalAddressEditStep.canEdit}">
<librecms:cmsEditor addButtonLabel="#{CmsAssetsStepsDefaultMessagesBundle['sidenote.sidenote.text.editor.add_variant']}"
addDialogLocaleSelectHelp="#{CmsAdminMessages['sidenote.text.editor.add.locale.help']}"
addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@edit-sidenote/text/add"
editDialogValueHelp="#{CmsAdminMessages['sidenote.text.editor.edit.value.help']}"
editDialogValueLabel="#{CmsAdminMessages['sidenote.text.editor.edit.value.label']}"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@edit-sidenote/text/edit"
editorId="sidenote-text-editor"
hasUnusedLocales="#{!CmsSideNoteEditStep.unusedTextLocales.isEmpty()}"
headingLevel="3"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@edit-sidenote/text/remove"
title="#{CmsAssetsStepsDefaultMessagesBundle['sidenote.text.editor.header']}"
unusedLocales="#{CmsArticleTextBodyStep.unusedLocales}"
variantUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@edit-sidenote/text/variants"
variants="#{CmsSideNoteEditStep.variants}"
wordCountUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@edit-sidenote/text/variants/wordcount"
/>
</c:if>
<c:if test="#{CmsSideNoteEditStep.canEdit}">
<librecms:cmsEditor
addButtonLabel="#{CmsAssetsStepsDefaultMessagesBundle['sidenote.sidenote.text.editor.add_variant']}"
addDialogLocaleSelectHelp="#{CmsAssetsStepsDefaultMessagesBundle['sidenote.text.editor.add.locale.help']}"
addDialogLocaleSelectLabel="#{CmsAssetsStepsDefaultMessagesBundle['sidenote.text.editor.add.locale.label']}"
addMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@sidenote-edit/text/add"
editDialogValueHelp="#{CmsAssetsStepsDefaultMessagesBundle['sidenote.text.editor.edit.value.help']}"
editDialogValueLabel="#{CmsAssetsStepsDefaultMessagesBundle['sidenote.text.editor.edit.value.label']}"
editMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@sidenote-edit/text/edit"
editorId="sidenote-text-editor"
hasUnusedLocales="#{!CmsSideNoteEditStep.unusedTextLocales.isEmpty()}"
headingLevel="3"
objectIdentifier="#{CmsSelectedDocumentModel.itemPath}"
removeMethod="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@sidenote-edit/text/remove"
title="#{CmsAssetsStepsDefaultMessagesBundle['sidenote.editstep.text.header']}"
unusedLocales="#{CmsSideNoteEditStep.unusedTextLocales}"
variantUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@sidenote-edit-resources/text/variants"
variants="#{CmsSideNoteEditStep.variants}"
wordCountUrl="#{mvc.basePath}/#{ContentSectionModel.sectionName}/assets/#{CmsSideNoteEditStep.assetPath}/@sidenote-edit-resources/text/variants/wordcount"
/>
</c:if>
</div>
</ui:define>
<ui:define name="scripts">
<script src="#{request.contextPath}/assets/@content-sections/cms-editor.js"></script>
</ui:define>
</ui:composition>
</html>

View File

@ -69,3 +69,11 @@ postal_address.description=A postal address.
side_note.label=Side Note
side_note.description=A side note.
sidenote.createform.title=Create a new side note
sidenote.editstep.header=Edit Side Note {0}
sidenote.editstep.text.header=Text
sidenote.sidenote.text.editor.add_variant=Add localized text
sidenote.text.editor.add.locale.help=The locale of the text variant.
sidenote.text.editor.add.locale.label=Locale
sidenote.text.editor.edit.value.help=The localized text.
sidenote.text.editor.edit.value.label=Text
sidenote.text.editor.header=Edit Side Note Text

View File

@ -69,3 +69,11 @@ postal_address.description=Eine Postanschrift.
side_note.label=Randbemerkung
side_note.description=Eine Randbemerkung.
sidenote.createform.title=Eine neue Randbemerkungen anlegen
sidenote.editstep.header=Randbemerkung {0} bearbeiten
sidenote.editstep.text.header=Text
sidenote.sidenote.text.editor.add_variant=\u00dcbersetzung hinzuf\u00fcgen
sidenote.text.editor.add.locale.help=Die Sprache der \u00dcbersetzung.
sidenote.text.editor.add.locale.label=Sprache
sidenote.text.editor.edit.value.help=The translated text.
sidenote.text.editor.edit.value.label=Text
sidenote.text.editor.header=Text der Randbemerkung bearbeiten

View File

@ -67,14 +67,14 @@ public class PermissionChecker {
* @param privilege The privilege granted by the permission.
*
* @return {@code true} if the current subject has a permission granting the
* provided {@code privilege}, {@code false} otherwise.
* provided {@code privilege}, {@code false} otherwise.
*/
public boolean isPermitted(final String privilege) {
if (subject.isAuthenticated()) {
if (shiro.isSystemUser()) {
return true;
} else {
return subject.isPermitted(generatePermissionString(privilege));
return subject.isPermitted(generatePermissionString(privilege));
}
} else {
return shiro.getPublicUser().isPermitted(generatePermissionString(
@ -87,11 +87,11 @@ public class PermissionChecker {
* provided {@code privilege}.
*
* @param privilege The privilege granted by the permission.
* @param role The role to check for a permission granting the
* {@code privilege}.
* @param role The role to check for a permission granting the
* {@code privilege}.
*
* @return {@code true} if the role has a permission granting the provided
* {@code privilege}, {@code false} otherwise.
* {@code privilege}, {@code false} otherwise.
*/
@Transactional(Transactional.TxType.REQUIRED)
public boolean isPermitted(final String privilege, final Role role) {
@ -129,22 +129,25 @@ public class PermissionChecker {
* {@code privilege} on the provided {@code object}.
*
* @param privilege The granted privilege.
* @param object The object on which the privilege is granted.
* @param object The object on which the privilege is granted.
*
* @return {@code true} if the there is a permission granting the provided
* {@code privilege} on the provided {@code object} to the current subject.
* {@code privilege} on the provided {@code object} to the current
* subject.
*/
public boolean isPermitted(final String privilege, final CcmObject object) {
if (subject.isAuthenticated()) {
if (shiro.isSystemUser()) {
return true;
} else {
return subject.isPermitted(generatePermissionString(
privilege, object));
return subject.isPermitted(
generatePermissionString(privilege, object)
);
}
} else {
return shiro.getPublicUser().isPermitted(generatePermissionString(
privilege, object));
return shiro.getPublicUser().isPermitted(
generatePermissionString(privilege, object)
);
}
}
@ -153,13 +156,13 @@ public class PermissionChecker {
* provided {@code privilege} on the provided object.
*
* @param privilege The granted privilege.
* @param object The object on which the {@code privilege} is granted.
* @param role The role to check for a permission granting the
* {@code privilege}.
* @param object The object on which the {@code privilege} is granted.
* @param role The role to check for a permission granting the
* {@code privilege}.
*
* @return {@code true} if the there is a permission granting the provided
* {@code privilege} on the provided {@code object} to the provided
* {@code role}.
* {@code privilege} on the provided {@code object} to the provided
* {@code role}.
*/
public boolean isPermitted(final String privilege,
final CcmObject object,
@ -196,7 +199,7 @@ public class PermissionChecker {
* @param privilege The privilege to check for.
*
* @throws AuthorizationException If the current subject has not permission
* granting the provided privilege.
* granting the provided privilege.
*/
public void checkPermission(final String privilege)
throws AuthorizationException {
@ -216,10 +219,11 @@ public class PermissionChecker {
*
*
* @param privilege The privilege to check for.
* @param object The object on which the privilege is granted.
* @param object The object on which the privilege is granted.
*
* @throws AuthorizationException If there is no permission granting the
* provided privilege to the current subject on the provided object..
* provided privilege to the current subject
* on the provided object..
*/
public void checkPermission(final String privilege,
final CcmObject object)
@ -243,13 +247,13 @@ public class PermissionChecker {
* placeholder object is returned with the {@link CcmObject#displayName}
* property set the {@code Access denied}.
*
* @param <T> The type of the object to check.
* @param <T> The type of the object to check.
* @param privilege The privilige to check for.
* @param object The object on which the privilege is granted.
* @param clazz The class of the object.
* @param object The object on which the privilege is granted.
* @param clazz The class of the object.
*
* @return The object if the current subject is permitted to access, a
* placeholder object if not.
* placeholder object if not.
*/
public <T extends CcmObject> T checkPermission(final String privilege,
final T object,
@ -265,7 +269,7 @@ public class PermissionChecker {
* @param object The object to check.
*
* @return {@code true} if the object is a <i>Access denied</i> object,
* {@code false} if not.
* {@code false} if not.
*/
public boolean isAccessDeniedObject(final CcmObject object) {
if (object == null) {