Next part of the article text body step

pull/10/head
Jens Pelzetter 2021-05-08 20:09:41 +02:00
parent 1abf3d96a1
commit 0d5a74bfb1
14 changed files with 5492 additions and 1943 deletions

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ target
.cache
.classpath
.factorypath
.parcel-cache
.project
.settings
.tscache

View File

@ -0,0 +1,6 @@
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}

6994
ccm-cms/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,21 +5,28 @@
"main": "index.js",
"scripts": {
"build": "npm-run-all build:*:*",
"build:ccm-admin:js": "parcel build --out-dir target/generated-resources/assets/@content-sections src/main/typescript/content-sections/cms-admin.ts",
"build:ccm-admin:css": "sass src/main/scss/content-sections/cms-admin.scss target/generated-resources/assets/@content-sections/cms-admin.css"
"build:ccm-admin:js": "parcel build --target --dist-dir target/generated-resources/assets/@content-sections src/main/typescript/content-sections/cms-admin.ts",
"build:ccm-admin:css": "sass src/main/scss/content-sections/cms-admin.scss target/generated-resources/assets/@content-sections/cms-admin.css",
"build:cms-editor:js": "parcel build --target --dist-dir target/generated-resources/assets/@content-sections src/main/typescript/content-sections/cms-editor.ts"
},
"author": "Jens Pelzetter",
"license": "LGPL-3.0-or-later",
"devDependencies": {
"@parcel/transformer-typescript-tsc": "^2.0.0-beta.1",
"npm-run-all": "^4.1.5",
"parcel-bundler": "^1.12.4",
"parcel": "^2.0.0-beta.2",
"sass": "^1.32.12",
"typescript": "^4.2.4"
},
"dependencies": {
"@tiptap/core": "^2.0.0-beta.46",
"@tiptap/starter-kit": "^2.0.0-beta.43",
"bootstrap": "^4.6.0",
"bootstrap-icons": "^1.4.1",
"jquery": "^3.6.0",
"popper.js": "^1.16.1"
},
"targets": {
"main": false
}
}

View File

@ -18,6 +18,9 @@
*/
package org.librecms.ui.contenttypes;
import com.arsdigita.kernel.KernelConfig;
import org.libreccm.configuration.ConfigurationManager;
import org.libreccm.l10n.GlobalizationHelper;
import org.libreccm.l10n.LocalizedString;
import org.librecms.contentsection.ContentItemRepository;
@ -72,6 +75,9 @@ public class MvcArticleTextBodyStep extends AbstractMvcAuthoringStep {
@Inject
private ArticleMessageBundle articleMessageBundle;
@Inject
private ConfigurationManager confManager;
/**
* Used for retrieving and saving the article.
*/
@ -94,6 +100,8 @@ public class MvcArticleTextBodyStep extends AbstractMvcAuthoringStep {
private List<String> unusedLocales;
private String selectedLocale;
@Override
public Class<MvcArticleTextBodyStep> getStepClass() {
return MvcArticleTextBodyStep.class;
@ -117,29 +125,8 @@ public class MvcArticleTextBodyStep extends AbstractMvcAuthoringStep {
}
if (itemPermissionChecker.canEditItem(getArticle())) {
textValues = getArticle()
.getText()
.getValues()
.entrySet()
.stream()
.collect(
Collectors.toMap(
entry -> entry.getKey().toString(),
entry -> entry.getValue()
)
);
final Set<Locale> locales = getArticle()
.getText()
.getAvailableLocales();
unusedLocales = globalizationHelper
.getAvailableLocales()
.stream()
.filter(locale -> !locales.contains(locale))
.map(Locale::toString)
.collect(Collectors.toList());
return "org/librecms/ui/contenttypes/article/article-text.xhtml";
// return "org/librecms/ui/contenttypes/article/article-text.xhtml";
return "org/librecms/ui/contenttypes/article/article-text/available-languages.xhtml";
} else {
return documentUi.showAccessDenied(
getContentSection(),
@ -167,13 +154,16 @@ public class MvcArticleTextBodyStep extends AbstractMvcAuthoringStep {
return Collections.unmodifiableList(unusedLocales);
}
public String getSelectedLocale() {
return selectedLocale;
}
/**
* Adds a localized main text.
*
* @param sectionIdentifier
* @param documentPath
* @param localeParam The locale of the text.
* @param value The text.
*
* @return A redirect to this authoring step.
*/
@ -185,8 +175,55 @@ public class MvcArticleTextBodyStep extends AbstractMvcAuthoringStep {
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@FormParam("locale") final String localeParam,
@FormParam("value") final String value
@FormParam("locale") final String localeParam
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
final KernelConfig kernelConfig = confManager.findConfiguration(
KernelConfig.class
);
final Locale defaultLocale = kernelConfig.getDefaultLocale();
if (itemPermissionChecker.canEditItem(getArticle())) {
final String value;
if (getArticle().getText().getAvailableLocales().isEmpty()) {
value = "Lorem ipsum";
} else {
value = getArticle().getText().getValue(defaultLocale);
}
final Locale locale = new Locale(localeParam);
getArticle().getText().addValue(locale, value);
itemRepo.save(getArticle());
return String.format(
"%s/%s/@edit",
buildRedirectPathForStep(),
locale.toString()
);
} else {
return documentUi.showAccessDenied(
getContentSection(),
getArticle(),
getLabel()
);
}
}
@GET
@Path("/{locale}/@view")
@Transactional(Transactional.TxType.REQUIRED)
public String viewTextValue(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@PathParam("locale") final String localeParam
) {
try {
init();
@ -197,16 +234,46 @@ public class MvcArticleTextBodyStep extends AbstractMvcAuthoringStep {
}
if (itemPermissionChecker.canEditItem(getArticle())) {
final Locale locale = new Locale(localeParam);
getArticle().getText().addValue(locale, value);
itemRepo.save(getArticle());
selectedLocale = new Locale(localeParam).toString();
return buildRedirectPathForStep();
return "org/librecms/ui/contenttypes/article/article-text/view.xhtml";
} else {
return documentUi.showAccessDenied(
getContentSection(),
getArticle(),
getLabel()
articleMessageBundle.getMessage("article.edit.denied")
);
}
}
@GET
@Path("/{locale}/@edit")
@Transactional(Transactional.TxType.REQUIRED)
public String editTextValue(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
final String sectionIdentifier,
@PathParam(MvcAuthoringSteps.DOCUMENT_PATH_PATH_PARAM_NAME)
final String documentPath,
@PathParam("locale") final String localeParam
) {
try {
init();
} catch (ContentSectionNotFoundException ex) {
return ex.showErrorMessage();
} catch (DocumentNotFoundException ex) {
return ex.showErrorMessage();
}
if (itemPermissionChecker.canEditItem(getArticle())) {
selectedLocale = new Locale(localeParam).toString();
return "org/librecms/ui/contenttypes/article/article-text/edit.xhtml";
} else {
return documentUi.showAccessDenied(
getContentSection(),
getArticle(),
articleMessageBundle.getMessage("article.edit.denied")
);
}
}
@ -222,7 +289,7 @@ public class MvcArticleTextBodyStep extends AbstractMvcAuthoringStep {
* @return A redirect to this authoring step.
*/
@POST
@Path("/@edit/{locale}")
@Path("/{locale}/@edit")
@Transactional(Transactional.TxType.REQUIRED)
public String editTextValue(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
@ -265,7 +332,7 @@ public class MvcArticleTextBodyStep extends AbstractMvcAuthoringStep {
* @return A redirect to this authoring step.
*/
@POST
@Path("/@remove/{locale}")
@Path("/{locale}/@remove")
@Transactional(Transactional.TxType.REQUIRED)
public String removeTextValue(
@PathParam(MvcAuthoringSteps.SECTION_IDENTIFIER_PATH_PARAM)
@ -297,6 +364,36 @@ public class MvcArticleTextBodyStep extends AbstractMvcAuthoringStep {
}
}
@Override
protected void init() throws ContentSectionNotFoundException,
DocumentNotFoundException {
super.init();
if (itemPermissionChecker.canEditItem(getArticle())) {
textValues = getArticle()
.getText()
.getValues()
.entrySet()
.stream()
.collect(
Collectors.toMap(
entry -> entry.getKey().toString(),
entry -> entry.getValue()
)
);
final Set<Locale> locales = getArticle()
.getText()
.getAvailableLocales();
unusedLocales = globalizationHelper
.getAvailableLocales()
.stream()
.filter(locale -> !locales.contains(locale))
.map(Locale::toString)
.collect(Collectors.toList());
}
}
private Article getArticle() {
return (Article) getDocument();
}

View File

@ -80,5 +80,6 @@
<ui:insert name="main"></ui:insert>
</main>
<script src="#{request.contextPath}/assets/@content-sections/cms-admin.js"></script>
<ui:insert name="scripts"></ui:insert>
</body>
</html>

View File

@ -22,6 +22,13 @@
useTextarea="true"
values="#{CmsArticleTextBodyStep.textValues}"
/>
<div id="cms-editor"></div>
</ui:define>
<ui:define name="scripts">
<script src="#{request.contextPath}/assets/@content-sections/cms-editor.js"></script>
</ui:define>
</ui:composition>

View File

@ -0,0 +1,132 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml">
<ui:define name="authoringStep">
<h2>#{CmsArticleMessageBundle['textstep.header.languages']}</h2>
<c:if test="#{CmsArticleTextBodyStep.canEdit and !CmsArticleTextBodyStep.unusedLocales.isEmpty()}">
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/@add"
method="post">
<div class="d-flex justify-content-start">
<div class="form-group mr-2">
<label for="add-locale">#{CmsArticleMessageBundle['textstep.languages.add_language.label']}</label>
<select aria-describedby="add-locale-help"
name="locale"
size="1">
<c:forEach items="#{CmsArticleTextBodyStep.unusedLocales}"
var="locale">
<option value="#{locale}">#{locale}</option>
</c:forEach>
</select>
<small id="add-locale-help"
class="form-text text-muted">
#{CmsArticleMessageBundle['textstep.languages.add_language.help']}
</small>
</div>
<button class="btn btn-secondary align-self-start"
type="submit">
<bootstrap:svgIcon icon="plus-circle" />
<span>
#{CmsArticleMessageBundle['textstep.languages.add_language.submit']}
</span>
</button>
</div>
</form>
</c:if>
<c:choose>
<c:when test="#{CmsArticleTextBodyStep.textValues.isEmpty()}">
#{CmsArticleMessageBundle['textstep.languages.none']}
</c:when>
<c:otherwise>
<table>
<thead>
<tr>
<th>#{CmsArticleMessageBundle['textstep.languages.th.language']}</th>
<th>#{CmsArticleMessageBundle['textstep.languages.th.actions']}</th>
</tr>
</thead>
<tbody>
<c:forEach items="#{CmsArticleTextBodyStep.textValues}"
var="entry">
<tr>
<td>#{entry.key}</td>
<td>
<a class="btn btn-secondary"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/#{entry.key}/@view">
<bootstrap:svgIcon icon="eye" />
<span class="sr-only">#{CmsArticleMessageBundle['textstep.languages.view']}</span>
</a>
<c:if test="#{CmsArticleTextBodyStep.canEdit}">
<a class="btn btn-secondary"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/#{entry.key}/@edit">
<bootstrap:svgIcon icon="pen" />
<span class="sr-only">#{CmsArticleMessageBundle['textstep.languages.edit']}</span>
</a>
<button class="btn btn-danger"
data-target="#remove-locale-#{entry.key}-dialog"
data-toggle="modal"
type="button">
<bootstrap:svgIcon icon="x-circle" />
<span class="sr-only">#{CmsArticleMessageBundle['textstep.languages.remove']}</span>
</button>
<div aria-labelledby="remove-locale-#{entry.key}-dialog-title"
aria-hidden="true"
class="modal fade"
data-backdrop="static"
id="remove-locale-#{entry.key}-dialog"
tabindex="-1">
<div class="modal-dialog">
<form action="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/#{entry.key}/@remove"
class="modal-content"
method="post">
<div class="modal-header">
<div class="modal-title">
<h3 class="remove-locale-#{entry.key}-dialog-title">#{CmsArticleMessageBundle.getMessage('textstep.languages.remove.title', [entry.key])}</h3>
<button aria-label="#{CmsArticleMessageBundle['textstep.languages.remove.cancel']}"
class="close"
data-dismiss="modal"
type="button">
<bootstrap:svgIcon icon="x" />
</button>
</div>
</div>
<div class="modal-body">
<input name="confirmed" type="hidden" value="true" />
<p>
#{CmsArticleMessageBundle.getMessage('textstep.languages.remove.message', [entry.key])}
</p>
</div>
<div class="modal-footer">
<button class="btn btn-secondary"
data-dismiss="modal"
type="button" >
#{CmsArticleMessageBundle['textstep.languages.remove.cancel']}
</button>
<button type="submit" class="btn btn-danger">
#{CmsArticleMessageBundle['textstep.languages.remove.confirm']}
</button>
</div>
</form>
</div>
</div>
<!-- <button class="btn btn-secondary"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/#{entry.key}/@delete">
<bootstrap:svgIcon icon="eye" />
<span>{CmsArticleMessageBundle['textstep.languages.view']}</span>
</button>-->
</c:if>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</c:otherwise>
</c:choose>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml">
<ui:define name="authoringStep">
<div class="d-flex">
<a class="btn btn-secondary btn-sm mr-2"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text">
<bootstrap:svgIcon icon="caret-left-fill" />
<span class="sr-only">#{CmsArticleMessageBundle['textstep.back']}</span>
</a>
<h2>#{CmsArticleMessageBundle.getMessage('textstep.header.edit',[CmsArticleTextBodyStep.selectedLocale])}</h2>
</div>
<pre>selected locale: #{CmsArticleTextBodyStep.selectedLocale}</pre>
<c:if test="#{CmsArticleTextBodyStep.canEdit}">
<div class="cms-editor">
#{CmsArticleTextBodyStep.textValues.get(CmsArticleTextBodyStep.selectedLocale)}
</div>
</c:if>
</ui:define>
</ui:composition>
</html>

View File

@ -0,0 +1,36 @@
<!DOCTYPE html [<!ENTITY times '&#215;'>]>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
xmlns:libreccm="http://xmlns.jcp.org/jsf/composite/components/libreccm"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets">
<ui:composition template="/WEB-INF/views/org/librecms/ui/contentsection/documents/authoringstep.xhtml">
<ui:define name="authoringStep">
<div class="d-flex">
<a class="btn btn-secondary btn-sm algin-self-center mr-2"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text">
<bootstrap:svgIcon icon="caret-left-fill" />
<span class="sr-only">#{CmsArticleMessageBundle['textstep.back']}</span>
</a>
<h2>#{CmsArticleMessageBundle.getMessage('textstep.header.view',[CmsArticleTextBodyStep.selectedLocale])}</h2>
</div>
<c:if test="#{CmsArticleTextBodyStep.canEdit}">
<div class="text-right">
<a class="btn btn-secondary"
href="#{mvc.basePath}/#{ContentSectionModel.sectionName}/documents/#{CmsSelectedDocumentModel.itemPath}/@article-text/#{CmsArticleTextBodyStep.selectedLocale}/@edit">
<bootstrap:svgIcon icon="pen" />
<span class="sr-only">#{CmsArticleMessageBundle['textstep.languages.edit']}</span>
</a>
</div>
</c:if>
<div class="cms-text-preview article-text">
#{CmsArticleTextBodyStep.textValues.get(CmsArticleTextBodyStep.selectedLocale)}
</div>
</ui:define>
</ui:composition>
</html>

View File

@ -75,3 +75,20 @@ basicproperties.description.remove.cancel=Cancel
basicproperties.description.remove.submit=Remove localized summary
basicproperties.description.remove.text=Are you sure to remove the localized summary for this following locale?
basicproperties.description.remove.header=Remove localized summary
textstep.header.languages=Text of the Article - Available languages
textstep.languages.add_language.help=Select the language to add
textstep.languages.add_language.label=Add language
textstep.languages.add_language.submit=Add
textstep.languages.view=View
textstep.languages.edit=Edit
textstep.languages.remove=Remove
textstep.languages.remove.cancel=Cancel
textstep.languages.remove.confirm=Delete locale
textstep.languages.remove.title=Confirm removal of locale {0}
textstep.languages.remove.message=Are you sure to remove the text for locale {0}?
textstep.languages.th.language=Language
textstep.languages.th.actions=Actions
textstep.languages.none=No texts available
textstep.header.edit=Edit text for locale {0}
textstep.header.view=Text for locale {0}
textstep.back=Back to available languages

View File

@ -75,3 +75,20 @@ basicproperties.description.remove.cancel=Abbrechen
basicproperties.description.remove.submit=Lokalisierte Zusammenfassung entfernen
basicproperties.description.remove.text=Sind Sie sicher, dass Sie die lokalisierte Zusammenfassung f\u00fcr die folgende Sprach entfernen wollen?
basicproperties.description.remove.header=Lokalisierte Zusammenfassung entfernen
textstep.header.languages=Haupttext des Artikels - Sprachen
textstep.languages.add_language.help=W\u00e4hlen Sie die hinzuzuf\u00fcgende Sprache
textstep.languages.add_language.label=Sprache hinzuf\u00fcgen
textstep.languages.add_language.submit=Hinzuf\u00fcgen
textstep.languages.view=Ansehen
textstep.languages.edit=Bearbeiten
textstep.languages.remove=Entfernen
textstep.languages.remove.cancel=Abbrechen
textstep.languages.remove.confirm=Sprache entfernen
textstep.languages.remove.title=Entfernen der Sprache {0} best\u00e4tigen
textstep.languages.remove.message=Sind Sie sicher, dass Sie den Text f\u00fcr die Sprache {0} entfernen wollen?
textstep.languages.th.language=Sprache
textstep.languages.th.actions=Aktionen
textstep.languages.none=Keine Texte vorhandenen
textstep.header.edit=Text f\u00fcr Sprache {0} bearbeiten
textstep.header.view=Text f\u00fcr Sprache {0}
textstep.back=Zur\u00fcck zur Liste der verf\u00fcgbaren Sprachen

View File

@ -0,0 +1,15 @@
import { Editor } from "@tiptap/core";
import StarterKit from "@tiptap/starter-kit";
document.addEventListener("DOMContentLoaded", function (event) {
console.log("Starting editor");
new Editor({
element: document.querySelector('#cms-editor'),
extensions: [
StarterKit
],
content: '<h1>Hello World</h1>'
})
})

View File

@ -16,6 +16,11 @@
required="true"
shortDescription="Text of the button toggling the modal form."
type="String" />
<cc:attribute name="buttonLabelClass"
default=""
required="false"
shortDescription="Class(es) for the button text."
type="String" />
<cc:attribute name="buttonTextClass"
default=""
required="false"
@ -57,7 +62,7 @@
<c:if test="#{!cc.attrs.buttonIcon.isEmpty()}">
<bootstrap:svgIcon icon="x-circle" />
</c:if>
<span>#{cc.attrs.buttonText}</span>
<span class="#{cc.attrs.buttonLabelClass}">#{cc.attrs.buttonText}</span>
</button>
</div>
<div aria-labelledby="#{cc.attrs.dialogId}-title}"