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

@ -38,4 +38,15 @@ public class CmsAssetEditSteps implements MvcAssetEditSteps {
return classes; 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, final String sectionIdentifier,
@PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME) @PathParam(MvcAssetEditSteps.ASSET_PATH_PATH_PARAM_NAME)
final String assetPath, final String assetPath,
@FormParam("locale") final String localeParam, @FormParam("locale") final String localeParam
@FormParam("value") final String value
) { ) {
try { try {
init(); init();
@ -186,7 +185,7 @@ public class SideNoteEditStep extends AbstractMvcAssetEditStep {
if (assetPermissionsChecker.canEditAsset(getAsset())) { if (assetPermissionsChecker.canEditAsset(getAsset())) {
final Locale locale = new Locale(localeParam); final Locale locale = new Locale(localeParam);
getSideNote().getText().addValue(locale, value); getSideNote().getText().addValue(locale, "");
assetRepo.save(getSideNote()); assetRepo.save(getSideNote());
return buildRedirectPathForStep(); 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() .getText()
.getValue(new Locale(localeParam)); .getValue(new Locale(localeParam));
final Document jsoupDoc = Jsoup.parseBodyFragment(text); 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); return Long.toString(result);
} else { } else {
throw new ForbiddenException(); throw new ForbiddenException();

View File

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

View File

@ -69,3 +69,11 @@ postal_address.description=A postal address.
side_note.label=Side Note side_note.label=Side Note
side_note.description=A side note. side_note.description=A side note.
sidenote.createform.title=Create a new 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.label=Randbemerkung
side_note.description=Eine Randbemerkung. side_note.description=Eine Randbemerkung.
sidenote.createform.title=Eine neue Randbemerkungen anlegen 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. * @param privilege The privilege granted by the permission.
* *
* @return {@code true} if the current subject has a permission granting the * @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) { public boolean isPermitted(final String privilege) {
if (subject.isAuthenticated()) { if (subject.isAuthenticated()) {
if (shiro.isSystemUser()) { if (shiro.isSystemUser()) {
return true; return true;
} else { } else {
return subject.isPermitted(generatePermissionString(privilege)); return subject.isPermitted(generatePermissionString(privilege));
} }
} else { } else {
return shiro.getPublicUser().isPermitted(generatePermissionString( return shiro.getPublicUser().isPermitted(generatePermissionString(
@ -87,11 +87,11 @@ public class PermissionChecker {
* provided {@code privilege}. * provided {@code privilege}.
* *
* @param privilege The privilege granted by the permission. * @param privilege The privilege granted by the permission.
* @param role The role to check for a permission granting the * @param role The role to check for a permission granting the
* {@code privilege}. * {@code privilege}.
* *
* @return {@code true} if the role has a permission granting the provided * @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) @Transactional(Transactional.TxType.REQUIRED)
public boolean isPermitted(final String privilege, final Role role) { public boolean isPermitted(final String privilege, final Role role) {
@ -129,22 +129,25 @@ public class PermissionChecker {
* {@code privilege} on the provided {@code object}. * {@code privilege} on the provided {@code object}.
* *
* @param privilege The granted privilege. * @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 * @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) { public boolean isPermitted(final String privilege, final CcmObject object) {
if (subject.isAuthenticated()) { if (subject.isAuthenticated()) {
if (shiro.isSystemUser()) { if (shiro.isSystemUser()) {
return true; return true;
} else { } else {
return subject.isPermitted(generatePermissionString( return subject.isPermitted(
privilege, object)); generatePermissionString(privilege, object)
);
} }
} else { } else {
return shiro.getPublicUser().isPermitted(generatePermissionString( return shiro.getPublicUser().isPermitted(
privilege, object)); generatePermissionString(privilege, object)
);
} }
} }
@ -153,13 +156,13 @@ public class PermissionChecker {
* provided {@code privilege} on the provided object. * provided {@code privilege} on the provided object.
* *
* @param privilege The granted privilege. * @param privilege The granted privilege.
* @param object The object on which the {@code privilege} is granted. * @param object The object on which the {@code privilege} is granted.
* @param role The role to check for a permission granting the * @param role The role to check for a permission granting the
* {@code privilege}. * {@code privilege}.
* *
* @return {@code true} if the there is a permission granting the provided * @return {@code true} if the there is a permission granting the provided
* {@code privilege} on the provided {@code object} to the provided * {@code privilege} on the provided {@code object} to the provided
* {@code role}. * {@code role}.
*/ */
public boolean isPermitted(final String privilege, public boolean isPermitted(final String privilege,
final CcmObject object, final CcmObject object,
@ -196,7 +199,7 @@ public class PermissionChecker {
* @param privilege The privilege to check for. * @param privilege The privilege to check for.
* *
* @throws AuthorizationException If the current subject has not permission * @throws AuthorizationException If the current subject has not permission
* granting the provided privilege. * granting the provided privilege.
*/ */
public void checkPermission(final String privilege) public void checkPermission(final String privilege)
throws AuthorizationException { throws AuthorizationException {
@ -216,10 +219,11 @@ public class PermissionChecker {
* *
* *
* @param privilege The privilege to check for. * @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 * @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, public void checkPermission(final String privilege,
final CcmObject object) final CcmObject object)
@ -243,13 +247,13 @@ public class PermissionChecker {
* placeholder object is returned with the {@link CcmObject#displayName} * placeholder object is returned with the {@link CcmObject#displayName}
* property set the {@code Access denied}. * 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 privilege The privilige to check for.
* @param object The object on which the privilege is granted. * @param object The object on which the privilege is granted.
* @param clazz The class of the object. * @param clazz The class of the object.
* *
* @return The object if the current subject is permitted to access, a * @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, public <T extends CcmObject> T checkPermission(final String privilege,
final T object, final T object,
@ -265,7 +269,7 @@ public class PermissionChecker {
* @param object The object to check. * @param object The object to check.
* *
* @return {@code true} if the object is a <i>Access denied</i> object, * @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) { public boolean isAccessDeniedObject(final CcmObject object) {
if (object == null) { if (object == null) {