CCM NG/ccm-cms: Editing step for editing the text of an article works now.
git-svn-id: https://svn.libreccm.org/ccm/ccm_ng@4846 8810af33-2d31-482b-a856-94f89814c4dfccm-docs
parent
9d8857ac8e
commit
7a6d194526
|
|
@ -30,11 +30,14 @@ import com.arsdigita.cms.ItemSelectionModel;
|
|||
import org.librecms.contenttypes.Article;
|
||||
|
||||
import com.arsdigita.cms.ui.workflow.WorkflowLockedComponentAccess;
|
||||
import com.arsdigita.kernel.KernelConfig;
|
||||
|
||||
import org.libreccm.cdi.utils.CdiUtil;
|
||||
import org.libreccm.l10n.LocalizedString;
|
||||
import org.librecms.contentsection.ContentItemRepository;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Displays the current text body of the article and allows the user to edit it,
|
||||
* by uploading a file or entering text in a text box.
|
||||
|
|
@ -45,29 +48,32 @@ import org.librecms.contentsection.ContentItemRepository;
|
|||
* @author Stanislav Freidin (sfreidin@arsdigita.com)
|
||||
* @author <a href="jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
public class GenericArticleBody extends TextAssetBody {
|
||||
public class ArticleTextBody extends TextBody {
|
||||
|
||||
protected AuthoringKitWizard authoringKitWizard;
|
||||
protected ItemSelectionModel itemSelectionModel;
|
||||
private final AuthoringKitWizard authoringKitWizard;
|
||||
private final ItemSelectionModel itemSelectionModel;
|
||||
private final StringParameter selectedLanguageParam;
|
||||
|
||||
/**
|
||||
* Construct a new GenericArticleBody component
|
||||
*
|
||||
* @param itemSelectionModel The {@link ItemSelectionModel} which will be
|
||||
* responsible for loading the current item
|
||||
* @param itemSelectionModel The {@link ItemSelectionModel} which will be
|
||||
* responsible for loading the current item
|
||||
*
|
||||
* @param authoringKitWizard The parent wizard which contains the form. The
|
||||
* form may use the wizard's methods, such as
|
||||
* stepForward and stepBack, in its process
|
||||
* listener.
|
||||
* @param authoringKitWizard The parent wizard which contains the form.
|
||||
* The form may use the wizard's methods, such
|
||||
* as stepForward and stepBack, in its process
|
||||
* listener.
|
||||
* @param selectedLanguageParam
|
||||
*/
|
||||
public GenericArticleBody(final ItemSelectionModel itemSelectionModel,
|
||||
final AuthoringKitWizard authoringKitWizard,
|
||||
final StringParameter selectedLanguageParam) {
|
||||
public ArticleTextBody(final ItemSelectionModel itemSelectionModel,
|
||||
final AuthoringKitWizard authoringKitWizard,
|
||||
final StringParameter selectedLanguageParam) {
|
||||
|
||||
super(new ItemAssetModel(null), selectedLanguageParam);
|
||||
super(itemSelectionModel, selectedLanguageParam);
|
||||
this.itemSelectionModel = itemSelectionModel;
|
||||
this.authoringKitWizard = authoringKitWizard;
|
||||
this.selectedLanguageParam = selectedLanguageParam;
|
||||
|
||||
// Rest the component when it is hidden
|
||||
authoringKitWizard
|
||||
|
|
@ -91,6 +97,7 @@ public class GenericArticleBody extends TextAssetBody {
|
|||
* Adds the options for the mime type select widget of
|
||||
* <code>GenericArticleForm</code> and sets the default mime type.
|
||||
*
|
||||
* @param mimeSelect
|
||||
*/
|
||||
@Override
|
||||
protected void setMimeTypeOptions(final SingleSelect mimeSelect) {
|
||||
|
|
@ -99,39 +106,26 @@ public class GenericArticleBody extends TextAssetBody {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new text asset and associate it with the current item
|
||||
* Updates the text for the currently selected locale.
|
||||
*
|
||||
* @param state the current page state
|
||||
*
|
||||
* @return a valid TextAsset
|
||||
* @param text the new text for the currently selected locale
|
||||
*/
|
||||
@Override
|
||||
protected LocalizedString createTextAsset(final PageState state) {
|
||||
protected void updateText(final PageState state,
|
||||
final String text) {
|
||||
|
||||
final Article article = getGenericArticle(state);
|
||||
final LocalizedString text = article.getText();
|
||||
final Article article = getSelectedArticle(state);
|
||||
final String selectedLanguage = (String) state.getValue(
|
||||
selectedLanguageParam);
|
||||
final Locale selectedLocale;
|
||||
if (selectedLanguage == null) {
|
||||
selectedLocale = KernelConfig.getConfig().getDefaultLocale();
|
||||
} else {
|
||||
selectedLocale = new Locale(selectedLanguage);
|
||||
}
|
||||
|
||||
// no need - cg. Text doesn't need a security context,
|
||||
// and ownership of text is recorded in text_pages
|
||||
// t.setParent(item);
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set additional parameters of a brand new text asset, such as the parent
|
||||
* ID, after the asset has been successfully uploaded
|
||||
*
|
||||
* @param state the current page state
|
||||
* @param text the new <code>TextAsset</code>
|
||||
*/
|
||||
@Override
|
||||
protected void updateTextAsset(final PageState state,
|
||||
final LocalizedString text) {
|
||||
|
||||
final Article article = getGenericArticle(state);
|
||||
|
||||
// a.setParent(t);
|
||||
article.setText(text);
|
||||
article.getText().addValue(selectedLocale, text);
|
||||
final ContentItemRepository itemRepo = CdiUtil
|
||||
.createCdiUtil()
|
||||
.findBean(ContentItemRepository.class);
|
||||
|
|
@ -139,59 +133,37 @@ public class GenericArticleBody extends TextAssetBody {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the current GenericArticle
|
||||
* Get the current {@link Article}
|
||||
*
|
||||
* @param state
|
||||
* @param state The current page state.
|
||||
*
|
||||
* @return The currently selected article.
|
||||
*/
|
||||
protected Article getGenericArticle(final PageState state) {
|
||||
protected Article getSelectedArticle(final PageState state) {
|
||||
|
||||
return (Article) itemSelectionModel.getSelectedObject(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTextAssetName() {
|
||||
return "text";
|
||||
protected String getTextPropertyName() {
|
||||
return "text";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalizedString getTextAsset(final PageState state) {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
public String getText(final PageState state) {
|
||||
|
||||
/**
|
||||
* An ACSObjectSelectionModel that selects the current text asset for the
|
||||
* text page
|
||||
*/
|
||||
private static class ItemAssetModel extends ItemSelectionModel {
|
||||
final Article article = getSelectedArticle(state);
|
||||
|
||||
private RequestLocal requestLocal;
|
||||
|
||||
public ItemAssetModel(final LongParameter parameter) {
|
||||
|
||||
super(parameter);
|
||||
|
||||
requestLocal = new RequestLocal() {
|
||||
|
||||
@Override
|
||||
protected Object initialValue(final PageState state) {
|
||||
// final Article t
|
||||
// = (Article) ((ItemSelectionModel) getSingleSelectionModel())
|
||||
// .getSelectedObject(state);
|
||||
// Assert.exists(t);
|
||||
// return t.getTextAsset();
|
||||
|
||||
throw new UnsupportedOperationException("ToDo");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isSelected(PageState s) {
|
||||
return (getSelectedObject(s) != null);
|
||||
final String selectedLanguage = (String) state
|
||||
.getValue(selectedLanguageParam);
|
||||
final Locale selectedLocale;
|
||||
if (selectedLanguage == null) {
|
||||
selectedLocale = KernelConfig.getConfig().getDefaultLocale();
|
||||
} else {
|
||||
selectedLocale = new Locale(selectedLanguage);
|
||||
}
|
||||
|
||||
return article.getText().getValue(selectedLocale);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.arsdigita.cms.ui.authoring;
|
||||
|
||||
import com.arsdigita.bebop.BoxPanel;
|
||||
import com.arsdigita.bebop.Form;
|
||||
import com.arsdigita.bebop.FormData;
|
||||
import com.arsdigita.bebop.FormProcessException;
|
||||
import com.arsdigita.bebop.PageState;
|
||||
import com.arsdigita.bebop.RequestLocal;
|
||||
import com.arsdigita.bebop.SaveCancelSection;
|
||||
import com.arsdigita.bebop.event.FormProcessListener;
|
||||
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||
import com.arsdigita.bebop.event.FormValidationListener;
|
||||
import com.arsdigita.bebop.form.FormErrorDisplay;
|
||||
import com.arsdigita.bebop.parameters.NotNullValidationListener;
|
||||
import com.arsdigita.cms.ItemSelectionModel;
|
||||
import com.arsdigita.cms.ui.FileUploadSection;
|
||||
import com.arsdigita.globalization.GlobalizedMessage;
|
||||
import com.arsdigita.util.UncheckedWrapperException;
|
||||
|
||||
import org.libreccm.l10n.LocalizedString;
|
||||
import org.librecms.CmsConstants;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import javax.activation.MimeType;
|
||||
|
||||
/**
|
||||
* A form for editing TextAsset items. Displays a "file upload" widget,
|
||||
* auto-guesses mime type
|
||||
*/
|
||||
class PageFileForm extends Form implements FormProcessListener,
|
||||
FormValidationListener {
|
||||
|
||||
private final TextBody textBody;
|
||||
|
||||
private SaveCancelSection saveCancelSection;
|
||||
private FileUploadSection fileUploadSection;
|
||||
// Variables saved by validate for processing
|
||||
private RequestLocal fileUploadContent;
|
||||
private RequestLocal fileUploadUsedINSO;
|
||||
/**
|
||||
* The text entry widget
|
||||
*/
|
||||
public static final String TEXT_ENTRY = "text_entry";
|
||||
|
||||
/**
|
||||
* Construct a new PageFileForm
|
||||
*
|
||||
* @param itemSelectionModel
|
||||
*/
|
||||
public PageFileForm(final TextBody textBody) {
|
||||
|
||||
super("PageFileUpload", new BoxPanel(BoxPanel.VERTICAL));
|
||||
|
||||
this.textBody = textBody;
|
||||
|
||||
setMethod(Form.POST);
|
||||
setEncType("multipart/form-data");
|
||||
|
||||
addWidgets();
|
||||
}
|
||||
|
||||
private void addWidgets() {
|
||||
|
||||
fileUploadSection = new FileUploadSection(
|
||||
new GlobalizedMessage("cms.ui.authoring.text.mime_type"),
|
||||
"text",
|
||||
textBody.getDefaultMimeType());
|
||||
|
||||
fileUploadSection
|
||||
.getFileUploadWidget()
|
||||
.addValidationListener(new NotNullValidationListener());
|
||||
fileUploadSection
|
||||
.getMimeTypeWidget()
|
||||
.setDefaultValue(FileUploadSection.GUESS_MIME);
|
||||
add(fileUploadSection);
|
||||
|
||||
saveCancelSection = new SaveCancelSection();
|
||||
add(saveCancelSection);
|
||||
|
||||
final FormErrorDisplay errorDisplay = new FormErrorDisplay(this);
|
||||
add(errorDisplay);
|
||||
|
||||
addValidationListener(this);
|
||||
addProcessListener(this);
|
||||
|
||||
fileUploadContent = new RequestLocal();
|
||||
fileUploadUsedINSO = new RequestLocal();
|
||||
|
||||
}
|
||||
|
||||
protected String getFileUploadContent(PageState state) {
|
||||
return (String) fileUploadContent.get(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that files of this type can be uploaded
|
||||
*/
|
||||
private void validateFileType(final MimeType mime, final boolean textType)
|
||||
throws FormProcessException {
|
||||
// boolean validType = textType || ((mime instanceof TextMimeType)
|
||||
// && ((TextMimeType) mime).
|
||||
// allowINSOConvert().booleanValue());
|
||||
boolean validType = true; //ToDo
|
||||
if (!validType) {
|
||||
throw new FormProcessException(new GlobalizedMessage(
|
||||
"cms.ui.authoring.invalid_file_type",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
// boolean insoWorks = MimeTypeStatus.getMimeTypeStatus().
|
||||
// getInsoFilterWorks().intValue() == 1;
|
||||
boolean insoWorks = true; //ToDo
|
||||
if (!textType && !insoWorks) {
|
||||
// Can't convert. inso filter is not working. Give message.
|
||||
throw new FormProcessException(new GlobalizedMessage(
|
||||
"cms.ui.authoring.couldnt_convert_missing_inso",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read in the content of the file (in bytes).
|
||||
*/
|
||||
private byte[] readFileBytes(final File file) throws FormProcessException {
|
||||
byte[] fileBytes;
|
||||
try (final FileInputStream fs = new FileInputStream(file)) {
|
||||
fileBytes = new byte[fs.available()];
|
||||
fs.read(fileBytes);
|
||||
} catch (IOException ex) {
|
||||
throw new FormProcessException(new GlobalizedMessage(
|
||||
"cms.ui.authoring.unable_to_load_file",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
return fileBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bytes to String, possibly using INSO filter to convert to HTML
|
||||
* type
|
||||
*/
|
||||
private String convertBytes(final byte[] fileBytes, final boolean textType,
|
||||
final boolean[] usedInso) throws
|
||||
FormProcessException {
|
||||
String fileContent;
|
||||
// If mime type is not text type, try to convert to html
|
||||
if (!textType) {
|
||||
fileContent = new String(fileBytes);
|
||||
if (fileContent != null) {
|
||||
// Converted successfully, flag type should be html
|
||||
usedInso[0] = true;
|
||||
} else {
|
||||
throw new FormProcessException(new GlobalizedMessage(
|
||||
"cms.ui.authoring.couldnt_convert_inso_failed",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
} else {
|
||||
// Text type, no need to convert
|
||||
final String enc = "UTF-8";
|
||||
try {
|
||||
fileContent = new String(fileBytes, enc);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new UncheckedWrapperException(
|
||||
"cannot convert to encoding " + enc, ex);
|
||||
}
|
||||
usedInso[0] = false;
|
||||
}
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the contents of the HTML Body tag. (Done to prevent base and
|
||||
* other header tags from interfering with page display).
|
||||
*/
|
||||
private String extractHTMLBody(final String htmlText) throws
|
||||
FormProcessException {
|
||||
final String lowerCase = htmlText.toLowerCase();
|
||||
int bodyStart = lowerCase.indexOf("<body");
|
||||
int bodyStart_v = lowerCase.indexOf(">", bodyStart);
|
||||
int bodyEnd = lowerCase.indexOf("</body>", bodyStart_v);
|
||||
if (bodyStart == -1 || bodyEnd == -1) {
|
||||
throw new FormProcessException(new GlobalizedMessage(
|
||||
"cms.ui.authoring.html_file_missing_body_tags",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
return htmlText.substring(bodyStart_v + 1, bodyEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate file upload
|
||||
*
|
||||
* @throws com.arsdigita.bebop.FormProcessException
|
||||
*/
|
||||
@Override
|
||||
public void validate(final FormSectionEvent event) throws
|
||||
FormProcessException {
|
||||
MimeType mime = fileUploadSection.getMimeType(event);
|
||||
// boolean textType = mime.getPrefix().equals(TextMimeType.TEXT_PREFIX);
|
||||
final boolean textType = true; //ToDo
|
||||
validateFileType(mime, textType);
|
||||
// Convert the file to HTML, if possible
|
||||
File file = fileUploadSection.getFile(event);
|
||||
byte[] file_bytes = readFileBytes(file);
|
||||
boolean[] usedInso = new boolean[1];
|
||||
String file_content = convertBytes(file_bytes, textType, usedInso);
|
||||
// ToDo if (TextMimeType.MIME_TEXT_HTML.equals(mime.getMimeType())) {
|
||||
file_content = extractHTMLBody(file_content);
|
||||
// }
|
||||
final PageState state = event.getPageState();
|
||||
fileUploadContent.set(state, file_content);
|
||||
fileUploadUsedINSO.set(state, usedInso[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process file upload. Must be validated first.
|
||||
*/
|
||||
@Override
|
||||
public void process(final FormSectionEvent event)
|
||||
throws FormProcessException {
|
||||
|
||||
final FormData data = event.getFormData();
|
||||
final PageState state = event.getPageState();
|
||||
// Get the text asset or create a new one
|
||||
final String text = textBody.getText(state);
|
||||
final File file = fileUploadSection.getFile(event);
|
||||
// Get info created during validation
|
||||
final String uploadContent = (String) fileUploadContent.get(state);
|
||||
boolean usedINSO = (Boolean) fileUploadUsedINSO.get(state);
|
||||
// Set the mime type
|
||||
// final MimeType mime = fileUploadSection.getMimeType(event);
|
||||
// if (usedINSO) {
|
||||
// mime = MimeType.loadMimeType("text/html");
|
||||
// }
|
||||
// if (mime != null) {
|
||||
// text.setMimeType(mime);
|
||||
// }
|
||||
// Save the uploaded content
|
||||
//ToDo text.setText(uploadContent);
|
||||
//t.setName(fileName); // ???
|
||||
// file = null;
|
||||
// Save everything
|
||||
textBody.updateText(state, text);
|
||||
// if (text.isNew() || text.isModified()) {
|
||||
// text.save();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the save/cancel section for this form
|
||||
*/
|
||||
public SaveCancelSection getSaveCancelSection() {
|
||||
return saveCancelSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the save/cancel section for this form
|
||||
*/
|
||||
public FileUploadSection getFileUploadSection() {
|
||||
return fileUploadSection;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.arsdigita.cms.ui.authoring;
|
||||
|
||||
import com.arsdigita.bebop.ColumnPanel;
|
||||
import com.arsdigita.bebop.Form;
|
||||
import com.arsdigita.bebop.FormData;
|
||||
import com.arsdigita.bebop.FormProcessException;
|
||||
import com.arsdigita.bebop.Label;
|
||||
import com.arsdigita.bebop.PageState;
|
||||
import com.arsdigita.bebop.SaveCancelSection;
|
||||
import com.arsdigita.bebop.event.FormInitListener;
|
||||
import com.arsdigita.bebop.event.FormProcessListener;
|
||||
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||
import com.arsdigita.bebop.event.FormSubmissionListener;
|
||||
import com.arsdigita.bebop.form.TextArea;
|
||||
import com.arsdigita.cms.ui.CMSDHTMLEditor;
|
||||
import com.arsdigita.globalization.GlobalizedMessage;
|
||||
|
||||
import org.arsdigita.cms.CMSConfig;
|
||||
import org.libreccm.l10n.LocalizedString;
|
||||
import org.librecms.CmsConstants;
|
||||
|
||||
/**
|
||||
* A form for editing the body of the text.
|
||||
*/
|
||||
public class PageTextForm
|
||||
extends Form
|
||||
implements FormInitListener,
|
||||
FormProcessListener,
|
||||
FormSubmissionListener {
|
||||
|
||||
private final TextBody textBody;
|
||||
|
||||
// private SingleSelect mimeWidget;
|
||||
// private Label mimeLabel;
|
||||
private TextArea textWidget;
|
||||
private SaveCancelSection saveCancelSection;
|
||||
/**
|
||||
* The text entry widget
|
||||
*/
|
||||
public static final String TEXT_ENTRY = "text_entry";
|
||||
/**
|
||||
* The mime type widget
|
||||
*/
|
||||
public static final String MIME_TYPE = "mime_type";
|
||||
|
||||
/**
|
||||
* Construct a new PageTextForm
|
||||
*
|
||||
*/
|
||||
public PageTextForm(final TextBody textBody) {
|
||||
|
||||
super("PageTextForm", new ColumnPanel(2));
|
||||
|
||||
this.textBody = textBody;
|
||||
|
||||
setMethod(Form.POST);
|
||||
setEncType("multipart/form-data");
|
||||
|
||||
addWidgets();
|
||||
}
|
||||
|
||||
private void addWidgets() {
|
||||
|
||||
final ColumnPanel panel = (ColumnPanel) getPanel();
|
||||
panel.setBorder(false);
|
||||
panel.setPadColor("#FFFFFF");
|
||||
panel.setColumnWidth(1, "20%");
|
||||
panel.setColumnWidth(2, "80%");
|
||||
panel.setWidth("100%");
|
||||
|
||||
add(new Label(new GlobalizedMessage(
|
||||
"cms.ui.authoring.edit_body_text",
|
||||
CmsConstants.CMS_BUNDLE)),
|
||||
ColumnPanel.LEFT | ColumnPanel.FULL_WIDTH);
|
||||
|
||||
textWidget = new CMSDHTMLEditor(PageTextForm.TEXT_ENTRY);
|
||||
textWidget.setRows(25);
|
||||
textWidget.setCols(40);
|
||||
// cg - sets editor size - closer to actual published page
|
||||
// width, and similar size as htmlarea.
|
||||
// could be configurable - unset means default 100% x 400px
|
||||
textWidget.setMetaDataAttribute("width", "575");
|
||||
textWidget.setMetaDataAttribute("height", "500");
|
||||
textWidget.setWrap(CMSDHTMLEditor.SOFT);
|
||||
add(textWidget, ColumnPanel.LEFT
|
||||
| ColumnPanel.FULL_WIDTH);
|
||||
|
||||
saveCancelSection = new SaveCancelSection();
|
||||
add(saveCancelSection, ColumnPanel.FULL_WIDTH);
|
||||
|
||||
// optionally, we clear the text of MSWord tags every time
|
||||
// the text is submitted/saved
|
||||
if (CMSConfig.getConfig().isSaveTextCleansWordTags()) {
|
||||
saveCancelSection.getSaveButton().setOnClick(
|
||||
"wordClean_"
|
||||
+ PageTextForm.TEXT_ENTRY
|
||||
+ "();");
|
||||
}
|
||||
|
||||
addInitListener(this);
|
||||
addProcessListener(this);
|
||||
|
||||
}
|
||||
|
||||
// These are here so that TemplateBody can set them.
|
||||
// public final void setMimeWidget(final SingleSelect widget) {
|
||||
// mimeWidget = widget;
|
||||
// }
|
||||
public final void setTextWidget(final TextArea widget) {
|
||||
textWidget = widget;
|
||||
}
|
||||
|
||||
public final void setSaveCancel(final SaveCancelSection widget) {
|
||||
saveCancelSection = widget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the text area with the current value.
|
||||
*
|
||||
* @param event
|
||||
*
|
||||
* @throws FormProcessException
|
||||
*/
|
||||
public void init(final FormSectionEvent event) throws FormProcessException {
|
||||
|
||||
final FormData data = event.getFormData();
|
||||
final PageState state = event.getPageState();
|
||||
|
||||
final String text = textBody.getText(state);
|
||||
|
||||
if (text != null) {
|
||||
data.put(TEXT_ENTRY, text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels streamlined editing.
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
@Override
|
||||
public void submitted(final FormSectionEvent event) {
|
||||
|
||||
if (getSaveCancelSection()
|
||||
.getCancelButton()
|
||||
.isSelected(event.getPageState())) {
|
||||
|
||||
textBody.cancelStreamlinedCreation(event.getPageState());
|
||||
}
|
||||
}
|
||||
|
||||
// process: update the mime type and content
|
||||
@Override
|
||||
public void process(final FormSectionEvent event) throws
|
||||
FormProcessException {
|
||||
|
||||
final FormData data = event.getFormData();
|
||||
final PageState state = event.getPageState();
|
||||
final String text = (String) data.get(TEXT_ENTRY);
|
||||
textBody.updateText(state, text);
|
||||
|
||||
textBody.maybeForwardToNextStep(event.getPageState());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the save/cancel section for this form
|
||||
*/
|
||||
public SaveCancelSection getSaveCancelSection() {
|
||||
return saveCancelSection;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,750 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
package com.arsdigita.cms.ui.authoring;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.arsdigita.bebop.BoxPanel;
|
||||
import com.arsdigita.bebop.ColumnPanel;
|
||||
import com.arsdigita.bebop.Form;
|
||||
import com.arsdigita.bebop.FormData;
|
||||
import com.arsdigita.bebop.FormProcessException;
|
||||
import com.arsdigita.bebop.Label;
|
||||
import com.arsdigita.bebop.Page;
|
||||
import com.arsdigita.bebop.PageState;
|
||||
import com.arsdigita.bebop.RequestLocal;
|
||||
import com.arsdigita.bebop.Resettable;
|
||||
import com.arsdigita.bebop.SaveCancelSection;
|
||||
import com.arsdigita.bebop.event.FormInitListener;
|
||||
import com.arsdigita.bebop.event.FormProcessListener;
|
||||
import com.arsdigita.bebop.event.FormSectionEvent;
|
||||
import com.arsdigita.bebop.event.FormSubmissionListener;
|
||||
import com.arsdigita.bebop.event.FormValidationListener;
|
||||
import com.arsdigita.bebop.event.RequestEvent;
|
||||
import com.arsdigita.bebop.event.RequestListener;
|
||||
import com.arsdigita.bebop.form.FormErrorDisplay;
|
||||
import com.arsdigita.bebop.form.SingleSelect;
|
||||
import com.arsdigita.bebop.form.TextArea;
|
||||
import com.arsdigita.bebop.parameters.NotNullValidationListener;
|
||||
import com.arsdigita.bebop.parameters.StringParameter;
|
||||
|
||||
import org.librecms.contentsection.ContentSection;
|
||||
|
||||
import com.arsdigita.cms.ItemSelectionModel;
|
||||
import com.arsdigita.cms.ui.CMSDHTMLEditor;
|
||||
import com.arsdigita.cms.ui.ContentItemPage;
|
||||
import com.arsdigita.cms.ui.FileUploadSection;
|
||||
import com.arsdigita.cms.ui.SecurityPropertyEditor;
|
||||
import com.arsdigita.globalization.Globalization;
|
||||
import com.arsdigita.globalization.GlobalizedMessage;
|
||||
import com.arsdigita.toolbox.ui.DomainObjectPropertySheet;
|
||||
import com.arsdigita.util.UncheckedWrapperException;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.arsdigita.cms.CMSConfig;
|
||||
import org.libreccm.l10n.LocalizedString;
|
||||
import org.librecms.CmsConstants;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.activation.MimeType;
|
||||
|
||||
/**
|
||||
* Displays the mime-type and the body of a single {@code TextAsset}. Maintains
|
||||
* a form or uploading files into the text body of the asset, and a form for
|
||||
* editing the text of the asset.
|
||||
* <p>
|
||||
* Unlike most other authoring components, this component does not require the
|
||||
* asset to exist. If the asset does not exist (i.e., if
|
||||
* <code>!m_assetModel.isSelected(state)</code>), the upload and editing forms
|
||||
* will create a new asset and set it in the model by calling
|
||||
* <code>setSelectedObject</code> on the asset selection model. Child classes
|
||||
* should override the {@link #createTextAsset(PageState)} method in to create a
|
||||
* valid text asset.
|
||||
* <p>
|
||||
* This component is used primarily in {@link GenericArticleBody} and
|
||||
* {@link com.arsdigita.cms.ui.templates.TemplateBody}
|
||||
*
|
||||
* <b>Note: </b> In CCM NG (version 7 and newer) {@code TextAsset} does not
|
||||
* longer exist. Instead fields of type {@link LocalizedString} are used. This
|
||||
* class has been adapted to use {@link LocalizedString}. The name of the class
|
||||
* etc. has been kept to make the migration easier.
|
||||
*
|
||||
* @author Stanislav Freidin (sfreidin@arsdigita.com)
|
||||
* @version <a href="jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
public abstract class TextAssetBody
|
||||
extends SecurityPropertyEditor
|
||||
implements Resettable, AuthoringStepComponent, RequestListener {
|
||||
|
||||
public static final String FILE_UPLOAD = "file";
|
||||
public static final String TEXT_ENTRY = "text";
|
||||
|
||||
private static final String STREAMLINED = "_streamlined";
|
||||
private static final String STREAMLINED_DONE = "1";
|
||||
private static final CMSConfig CMS_CONFIG = CMSConfig.getConfig();
|
||||
|
||||
private final StringParameter streamlinedCreationParam;
|
||||
private final StringParameter selectedLanguageParam;
|
||||
private ItemSelectionModel assetModel;
|
||||
|
||||
/**
|
||||
* Construct a new GenericArticleBody component
|
||||
*
|
||||
* @param assetModel The {@link ItemSelectionModel} which will be
|
||||
* responsible for maintaining the current asset
|
||||
* @param selectedLanguageParam
|
||||
*/
|
||||
public TextAssetBody(final ItemSelectionModel assetModel,
|
||||
final StringParameter selectedLanguageParam) {
|
||||
this(assetModel, null, selectedLanguageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new GenericArticleBody component
|
||||
*
|
||||
* @param assetModel The {@link ItemSelectionModel} which will be
|
||||
* responsible for maintaining the current asset
|
||||
* @param authoringKitWizard The parent wizard which contains the form. The
|
||||
* form may use the wizard's methods, such as
|
||||
* stepForward and stepBack, in its process
|
||||
* listener.
|
||||
* @param selectedLangugeParam
|
||||
*/
|
||||
public TextAssetBody(final ItemSelectionModel assetModel,
|
||||
final AuthoringKitWizard authoringKitWizard,
|
||||
final StringParameter selectedLangugeParam) {
|
||||
|
||||
super();
|
||||
this.assetModel = assetModel;
|
||||
|
||||
if (authoringKitWizard == null) {
|
||||
streamlinedCreationParam = new StringParameter("item_body_done");
|
||||
} else {
|
||||
streamlinedCreationParam = new StringParameter(
|
||||
String.format("%s_body_done",
|
||||
authoringKitWizard
|
||||
.getContentType()
|
||||
.getContentItemClass()
|
||||
.getName()));
|
||||
}
|
||||
|
||||
this.selectedLanguageParam = selectedLangugeParam;
|
||||
|
||||
if (!CMS_CONFIG.isHideTextAssetUploadFile()) {
|
||||
final PageFileForm pageFileForm = getPageFileForm();
|
||||
addFileWidgets(pageFileForm);
|
||||
add(FILE_UPLOAD,
|
||||
new GlobalizedMessage("cms.ui.upload", CmsConstants.CMS_BUNDLE),
|
||||
pageFileForm,
|
||||
pageFileForm.getSaveCancelSection().getCancelButton());
|
||||
}
|
||||
|
||||
final PageTextForm pageTextForm = new PageTextForm();
|
||||
addTextWidgets(pageTextForm);
|
||||
add(TEXT_ENTRY,
|
||||
new GlobalizedMessage("cms.ui.edit", CmsConstants.CMS_BUNDLE),
|
||||
pageTextForm,
|
||||
pageTextForm.getSaveCancelSection().getCancelButton());
|
||||
|
||||
// Specify full path to properties of the text asset
|
||||
final DomainObjectPropertySheet sheet = getBodyPropertySheet(assetModel);
|
||||
sheet.add(new GlobalizedMessage("cms.ui.authoring.body",
|
||||
CmsConstants.CMS_BUNDLE),
|
||||
getTextAssetName());
|
||||
|
||||
setDisplayComponent(sheet);
|
||||
|
||||
getDisplayPane().setClassAttr("invertedPropertyDisplay");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the name of the property holding the text.
|
||||
*
|
||||
* @return The name of the property holding the text.
|
||||
*/
|
||||
protected abstract String getTextAssetName();
|
||||
|
||||
protected DomainObjectPropertySheet getBodyPropertySheet(
|
||||
final ItemSelectionModel assetModel) {
|
||||
|
||||
return new TextAssetBodyPropertySheet(assetModel,
|
||||
selectedLanguageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the options for the mime type select widget of
|
||||
* <code>GenericArticleForm</code> and sets the default mime type.
|
||||
*
|
||||
*/
|
||||
protected void setMimeTypeOptions(SingleSelect mimeSelect) {
|
||||
FileUploadSection.addMimeOptions(mimeSelect, "text");
|
||||
mimeSelect.setOptionSelected("text/html");
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overwritten by subclasses, should return the field.
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
public abstract LocalizedString getTextAsset(final PageState state);
|
||||
|
||||
/**
|
||||
* Reset this component to its original state
|
||||
*
|
||||
* @param state the current page state
|
||||
*/
|
||||
@Override
|
||||
public void reset(final PageState state) {
|
||||
showDisplayPane(state);
|
||||
}
|
||||
|
||||
// Create a text asset if it does not exist.
|
||||
// This should probably be a method in GenericArticle ?
|
||||
protected LocalizedString createOrGetTextAsset(
|
||||
final ItemSelectionModel assetModel,
|
||||
final PageState state) {
|
||||
// Get the text asset or create a new one
|
||||
LocalizedString text = getTextAsset(state);
|
||||
|
||||
if (text == null) {
|
||||
text = createTextAsset(state);
|
||||
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a brand new <code>TextAsset</code>. Child classes should override
|
||||
* this method to do the right thing. The default implementation creates a
|
||||
* parent-less <code>TextAsset</code> with a unique name.
|
||||
*
|
||||
* @param state the current page state
|
||||
*
|
||||
* @return a valid <code>TextAsset</code>
|
||||
*/
|
||||
protected abstract LocalizedString createTextAsset(PageState state);
|
||||
|
||||
/**
|
||||
* Set additional parameters of a brand new text asset, such as the parent
|
||||
* ID, after the asset has been successfully uploaded
|
||||
*
|
||||
* @param state the current page state
|
||||
* @param text the new <code>TextAsset</code>
|
||||
*/
|
||||
protected abstract void updateTextAsset(PageState state,
|
||||
LocalizedString text);
|
||||
|
||||
/**
|
||||
* Return the <code>ItemSelectionModel</code> which will be used to maintain
|
||||
* the current text asset
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ItemSelectionModel getAssetSelectionModel() {
|
||||
return assetModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward to the next step if the streamlined creation parameter is turned
|
||||
* on _and_ the streamlined_creation global state parameter is set to
|
||||
* 'active'
|
||||
*
|
||||
* @param state the PageState
|
||||
*/
|
||||
protected void maybeForwardToNextStep(final PageState state) {
|
||||
if (ContentItemPage.isStreamlinedCreationActive(state)
|
||||
&& !STREAMLINED_DONE.
|
||||
equals(state.getValue(streamlinedCreationParam))) {
|
||||
state.setValue(streamlinedCreationParam, STREAMLINED_DONE);
|
||||
fireCompletionEvent(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel streamlined creation for this step if the streamlined creation
|
||||
* parameter is turned on _and_ the streamlined_creation global state param
|
||||
* is set to 'active'
|
||||
*
|
||||
* @param state the PageState
|
||||
*/
|
||||
protected void cancelStreamlinedCreation(final PageState state) {
|
||||
if (ContentItemPage.isStreamlinedCreationActive(state)) {
|
||||
state.setValue(streamlinedCreationParam, STREAMLINED_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the edit component if the streamlined creation parameter is turned
|
||||
* on _and_ the streamlined_creation global state param is set to 'active'
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
@Override
|
||||
public void pageRequested(RequestEvent event) {
|
||||
|
||||
final PageState state = event.getPageState();
|
||||
|
||||
if (ContentItemPage.isStreamlinedCreationActive(state)
|
||||
&& !STREAMLINED_DONE.
|
||||
equals(state.getValue(streamlinedCreationParam))) {
|
||||
showComponent(state, TEXT_ENTRY);
|
||||
}
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the form that is used to upload files. This method can be used so
|
||||
* that a subclass can use their own subclass of PageFileForm.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected PageFileForm getPageFileForm() {
|
||||
return new PageFileForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* A form for editing TextAsset items. Displays a "file upload" widget,
|
||||
* auto-guesses mime type
|
||||
*/
|
||||
public class PageFileForm extends Form
|
||||
implements FormProcessListener, FormValidationListener {
|
||||
|
||||
private SaveCancelSection saveCancelSection;
|
||||
private FileUploadSection fileUploadSection;
|
||||
// Variables saved by validate for processing
|
||||
private RequestLocal fileUploadContent;
|
||||
private RequestLocal fileUploadUsedINSO;
|
||||
/**
|
||||
* The text entry widget
|
||||
*/
|
||||
public static final String TEXT_ENTRY = "text_entry";
|
||||
|
||||
/**
|
||||
* Construct a new PageFileForm
|
||||
*/
|
||||
public PageFileForm() {
|
||||
super("PageFileUpload", new BoxPanel(BoxPanel.VERTICAL));
|
||||
setMethod(Form.POST);
|
||||
setEncType("multipart/form-data");
|
||||
}
|
||||
|
||||
protected String getFileUploadContent(PageState state) {
|
||||
return (String) fileUploadContent.get(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that files of this type can be uploaded
|
||||
*/
|
||||
private void validateFileType(final MimeType mime,
|
||||
final boolean textType)
|
||||
throws FormProcessException {
|
||||
|
||||
// boolean validType = textType || ((mime instanceof TextMimeType)
|
||||
// && ((TextMimeType) mime).
|
||||
// allowINSOConvert().booleanValue());
|
||||
boolean validType = true; //ToDo
|
||||
|
||||
if (!validType) {
|
||||
throw new FormProcessException(new GlobalizedMessage(
|
||||
"cms.ui.authoring.invalid_file_type",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
|
||||
// boolean insoWorks = MimeTypeStatus.getMimeTypeStatus().
|
||||
// getInsoFilterWorks().intValue() == 1;
|
||||
boolean insoWorks = true; //ToDo
|
||||
|
||||
if (!textType && !insoWorks) {
|
||||
// Can't convert. inso filter is not working. Give message.
|
||||
throw new FormProcessException(new GlobalizedMessage(
|
||||
"cms.ui.authoring.couldnt_convert_missing_inso",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* read in the content of the file (in bytes).
|
||||
*/
|
||||
private byte[] readFileBytes(final File file)
|
||||
throws FormProcessException {
|
||||
|
||||
byte[] fileBytes;
|
||||
try (final FileInputStream fs = new FileInputStream(file)) {
|
||||
fileBytes = new byte[fs.available()];
|
||||
fs.read(fileBytes);
|
||||
} catch (IOException ex) {
|
||||
throw new FormProcessException(new GlobalizedMessage(
|
||||
"cms.ui.authoring.unable_to_load_file",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
return fileBytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bytes to String, possibly using INSO filter to convert to
|
||||
* HTML type
|
||||
*/
|
||||
private String convertBytes(final byte[] fileBytes,
|
||||
final boolean textType,
|
||||
final boolean[] usedInso)
|
||||
throws FormProcessException {
|
||||
String fileContent;
|
||||
// If mime type is not text type, try to convert to html
|
||||
if (!textType) {
|
||||
fileContent = new String(fileBytes);
|
||||
if (fileContent != null) {
|
||||
// Converted successfully, flag type should be html
|
||||
usedInso[0] = true;
|
||||
} else {
|
||||
throw new FormProcessException(
|
||||
new GlobalizedMessage(
|
||||
"cms.ui.authoring.couldnt_convert_inso_failed",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
} else {
|
||||
// Text type, no need to convert
|
||||
final String enc = "UTF-8";
|
||||
try {
|
||||
fileContent = new String(fileBytes, enc);
|
||||
} catch (UnsupportedEncodingException ex) {
|
||||
throw new UncheckedWrapperException(
|
||||
"cannot convert to encoding "
|
||||
+ enc, ex);
|
||||
}
|
||||
usedInso[0] = false;
|
||||
}
|
||||
return fileContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the contents of the HTML Body tag. (Done to prevent base and
|
||||
* other header tags from interfering with page display).
|
||||
*/
|
||||
private String extractHTMLBody(final String htmlText)
|
||||
throws FormProcessException {
|
||||
|
||||
final String lowerCase = htmlText.toLowerCase();
|
||||
int bodyStart = lowerCase.indexOf("<body");
|
||||
int bodyStart_v = lowerCase.indexOf(">", bodyStart);
|
||||
int bodyEnd = lowerCase.indexOf("</body>", bodyStart_v);
|
||||
if (bodyStart == -1 || bodyEnd == -1) {
|
||||
throw new FormProcessException(new GlobalizedMessage(
|
||||
"cms.ui.authoring.html_file_missing_body_tags",
|
||||
CmsConstants.CMS_BUNDLE));
|
||||
}
|
||||
return htmlText.substring(bodyStart_v + 1, bodyEnd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate file upload
|
||||
*
|
||||
* @throws com.arsdigita.bebop.FormProcessException
|
||||
*/
|
||||
@Override
|
||||
public void validate(final FormSectionEvent event) throws
|
||||
FormProcessException {
|
||||
|
||||
MimeType mime = fileUploadSection.getMimeType(event);
|
||||
// boolean textType = mime.getPrefix().equals(TextMimeType.TEXT_PREFIX);
|
||||
final boolean textType = true; //ToDo
|
||||
|
||||
validateFileType(mime, textType);
|
||||
|
||||
// Convert the file to HTML, if possible
|
||||
File file = fileUploadSection.getFile(event);
|
||||
byte[] file_bytes = readFileBytes(file);
|
||||
boolean[] usedInso = new boolean[1];
|
||||
String file_content = convertBytes(file_bytes, textType, usedInso);
|
||||
|
||||
// ToDo if (TextMimeType.MIME_TEXT_HTML.equals(mime.getMimeType())) {
|
||||
file_content = extractHTMLBody(file_content);
|
||||
// }
|
||||
|
||||
final PageState state = event.getPageState();
|
||||
fileUploadContent.set(state, file_content);
|
||||
fileUploadUsedINSO.set(state, new Boolean(usedInso[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process file upload. Must be validated first.
|
||||
*/
|
||||
public void process(final FormSectionEvent event)
|
||||
throws FormProcessException {
|
||||
|
||||
final FormData data = event.getFormData();
|
||||
final PageState state = event.getPageState();
|
||||
// Get the text asset or create a new one
|
||||
final LocalizedString text = createOrGetTextAsset(assetModel, state);
|
||||
final File file = fileUploadSection.getFile(event);
|
||||
|
||||
// Get info created during validation
|
||||
final String uploadContent = (String) fileUploadContent.get(state);
|
||||
boolean usedINSO = ((Boolean) fileUploadUsedINSO
|
||||
.get(state));
|
||||
|
||||
// Set the mime type
|
||||
// final MimeType mime = fileUploadSection.getMimeType(event);
|
||||
// if (usedINSO) {
|
||||
// mime = MimeType.loadMimeType("text/html");
|
||||
// }
|
||||
// if (mime != null) {
|
||||
// text.setMimeType(mime);
|
||||
// }
|
||||
// Save the uploaded content
|
||||
//ToDo text.setText(uploadContent);
|
||||
//t.setName(fileName); // ???
|
||||
// file = null;
|
||||
// Save everything
|
||||
updateTextAsset(state, text);
|
||||
// if (text.isNew() || text.isModified()) {
|
||||
// text.save();
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the save/cancel section for this form
|
||||
*/
|
||||
public SaveCancelSection getSaveCancelSection() {
|
||||
return saveCancelSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the save/cancel section for this form
|
||||
*/
|
||||
public FileUploadSection getFileUploadSection() {
|
||||
return fileUploadSection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A form for editing the body of the text.
|
||||
*/
|
||||
public class PageTextForm
|
||||
extends Form
|
||||
implements FormInitListener,
|
||||
FormProcessListener,
|
||||
FormSubmissionListener {
|
||||
|
||||
// private SingleSelect mimeWidget;
|
||||
// private Label mimeLabel;
|
||||
private TextArea textWidget;
|
||||
private SaveCancelSection saveCancelSection;
|
||||
/**
|
||||
* The text entry widget
|
||||
*/
|
||||
public static final String TEXT_ENTRY = "text_entry";
|
||||
/**
|
||||
* The mime type widget
|
||||
*/
|
||||
public static final String MIME_TYPE = "mime_type";
|
||||
|
||||
/**
|
||||
* Construct a new PageTextForm
|
||||
*
|
||||
*/
|
||||
public PageTextForm() {
|
||||
|
||||
super("PageTextForm", new ColumnPanel(2));
|
||||
setMethod(Form.POST);
|
||||
setEncType("multipart/form-data");
|
||||
}
|
||||
|
||||
// These are here so that TemplateBody can set them.
|
||||
// public final void setMimeWidget(final SingleSelect widget) {
|
||||
// mimeWidget = widget;
|
||||
// }
|
||||
public final void setTextWidget(final TextArea widget) {
|
||||
textWidget = widget;
|
||||
}
|
||||
|
||||
public final void setSaveCancel(final SaveCancelSection widget) {
|
||||
saveCancelSection = widget;
|
||||
}
|
||||
|
||||
// Init: load the item and preset the textarea
|
||||
public void init(final FormSectionEvent event) throws
|
||||
FormProcessException {
|
||||
// Ok, we need to stream out the text somehow, but for now
|
||||
// we just fake it
|
||||
final FormData data = event.getFormData();
|
||||
final PageState state = event.getPageState();
|
||||
|
||||
// ToDo
|
||||
// final LocalizedString text = assetModel.getSelectedObject(state);
|
||||
// if (text != null) {
|
||||
// data.put(TEXT_ENTRY, text.getText());
|
||||
// MimeType m = text.getMimeType();
|
||||
// if (m != null) {
|
||||
// data.put(MIME_TYPE, m.getMimeType());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels streamlined editing.
|
||||
*/
|
||||
@Override
|
||||
public void submitted(final FormSectionEvent event) {
|
||||
if (getSaveCancelSection().getCancelButton().isSelected(event.
|
||||
getPageState())) {
|
||||
TextAssetBody.this.cancelStreamlinedCreation(event
|
||||
.getPageState());
|
||||
}
|
||||
}
|
||||
|
||||
// process: update the mime type and content
|
||||
@Override
|
||||
public void process(final FormSectionEvent event) throws
|
||||
FormProcessException {
|
||||
|
||||
final FormData data = event.getFormData();
|
||||
final PageState state = event.getPageState();
|
||||
final LocalizedString text = createOrGetTextAsset(assetModel, state);
|
||||
|
||||
// Set the mime type
|
||||
// MimeType m = MimeType.loadMimeType((String) data.get(MIME_TYPE));
|
||||
// text.setMimeType(m);
|
||||
// Get the string and normalize it
|
||||
String textStr = (String) data.get(TEXT_ENTRY);
|
||||
|
||||
if (textStr == null) {
|
||||
textStr = "";
|
||||
}
|
||||
|
||||
// ToDo textStr.setText((String) data.get(TEXT_ENTRY));
|
||||
// Save everything
|
||||
updateTextAsset(state, text);
|
||||
|
||||
TextAssetBody.this.maybeForwardToNextStep(event.getPageState());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the save/cancel section for this form
|
||||
*/
|
||||
public SaveCancelSection getSaveCancelSection() {
|
||||
return saveCancelSection;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected String getDefaultMimeType() {
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
/* overridable method to put together the PageFileForm Component */
|
||||
protected void addFileWidgets(final PageFileForm pageFileForm) {
|
||||
|
||||
pageFileForm.fileUploadSection = new FileUploadSection("Text Type:",
|
||||
"text",
|
||||
getDefaultMimeType());
|
||||
pageFileForm.fileUploadSection.getFileUploadWidget()
|
||||
.addValidationListener(
|
||||
new NotNullValidationListener());
|
||||
// Default to -guess- because want to use file extension to determine type.
|
||||
pageFileForm.fileUploadSection.getMimeTypeWidget().setDefaultValue(
|
||||
FileUploadSection.GUESS_MIME);
|
||||
pageFileForm.add(pageFileForm.fileUploadSection);
|
||||
|
||||
pageFileForm.saveCancelSection = new SaveCancelSection();
|
||||
pageFileForm.add(pageFileForm.saveCancelSection);
|
||||
|
||||
// add FormErrorDisplay component to display any error message
|
||||
FormErrorDisplay fe = new FormErrorDisplay(pageFileForm);
|
||||
pageFileForm.add(fe);
|
||||
|
||||
pageFileForm.addValidationListener(pageFileForm);
|
||||
pageFileForm.addProcessListener(pageFileForm); // process called from validationListener
|
||||
|
||||
// Storage for file upload information
|
||||
pageFileForm.fileUploadContent = new RequestLocal();
|
||||
pageFileForm.fileUploadUsedINSO = new RequestLocal();
|
||||
}
|
||||
|
||||
|
||||
/* overridable method to put together the PageTextForm Component */
|
||||
protected void addTextWidgets(final PageTextForm pageTextForm) {
|
||||
|
||||
ColumnPanel panel = (ColumnPanel) pageTextForm.getPanel();
|
||||
panel.setBorder(false);
|
||||
panel.setPadColor("#FFFFFF");
|
||||
panel.setColumnWidth(1, "20%");
|
||||
panel.setColumnWidth(2, "80%");
|
||||
panel.setWidth("100%");
|
||||
|
||||
// c.add(new Label(
|
||||
// GlobalizationUtil.globalize("cms.ui.authoring.text_type")));
|
||||
// c.mimeWidget = new SingleSelect(PageTextForm.MIME_TYPE);
|
||||
// c.mimeWidget.setClassAttr("displayOneOptionAsLabel");
|
||||
// setMimeTypeOptions(c.mimeWidget);
|
||||
// c.add(c.mimeWidget, ColumnPanel.LEFT);
|
||||
pageTextForm.add(new Label(new GlobalizedMessage(
|
||||
"cms.ui.authoring.edit_body_text",
|
||||
CmsConstants.CMS_BUNDLE)),
|
||||
ColumnPanel.LEFT | ColumnPanel.FULL_WIDTH);
|
||||
|
||||
pageTextForm.textWidget = new CMSDHTMLEditor(PageTextForm.TEXT_ENTRY);
|
||||
pageTextForm.textWidget.setRows(25);
|
||||
pageTextForm.textWidget.setCols(40);
|
||||
// cg - sets FCKEditor size - closer to actual published page
|
||||
// width, and similar size as htmlarea.
|
||||
// could be configurable - unset means default 100% x 400px
|
||||
pageTextForm.textWidget.setMetaDataAttribute("width", "575");
|
||||
pageTextForm.textWidget.setMetaDataAttribute("height", "500");
|
||||
pageTextForm.textWidget.setWrap(CMSDHTMLEditor.SOFT);
|
||||
pageTextForm.add(pageTextForm.textWidget, ColumnPanel.LEFT
|
||||
| ColumnPanel.FULL_WIDTH);
|
||||
|
||||
pageTextForm.saveCancelSection = new SaveCancelSection();
|
||||
pageTextForm.add(pageTextForm.saveCancelSection, ColumnPanel.FULL_WIDTH);
|
||||
|
||||
// optionally, we clear the text of MSWord tags every time
|
||||
// the text is submitted/saved
|
||||
if (CMSConfig.getConfig().isSaveTextCleansWordTags()) {
|
||||
pageTextForm.saveCancelSection.getSaveButton().setOnClick(
|
||||
"wordClean_"
|
||||
+ PageTextForm.TEXT_ENTRY
|
||||
+ "();");
|
||||
}
|
||||
|
||||
pageTextForm.addInitListener(pageTextForm);
|
||||
pageTextForm.addProcessListener(pageTextForm);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers global state parameter for cancelling streamlined creation
|
||||
*/
|
||||
@Override
|
||||
public void register(final Page page) {
|
||||
super.register(page);
|
||||
page.addGlobalStateParam(streamlinedCreationParam);
|
||||
page.addRequestListener(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,291 @@
|
|||
/*
|
||||
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
|
||||
*
|
||||
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
package com.arsdigita.cms.ui.authoring;
|
||||
|
||||
import com.arsdigita.bebop.Page;
|
||||
import com.arsdigita.bebop.PageState;
|
||||
import com.arsdigita.bebop.Resettable;
|
||||
import com.arsdigita.bebop.event.RequestEvent;
|
||||
import com.arsdigita.bebop.event.RequestListener;
|
||||
import com.arsdigita.bebop.form.SingleSelect;
|
||||
import com.arsdigita.bebop.parameters.StringParameter;
|
||||
|
||||
import com.arsdigita.cms.ItemSelectionModel;
|
||||
import com.arsdigita.cms.ui.ContentItemPage;
|
||||
import com.arsdigita.cms.ui.FileUploadSection;
|
||||
import com.arsdigita.cms.ui.SecurityPropertyEditor;
|
||||
import com.arsdigita.globalization.GlobalizedMessage;
|
||||
import com.arsdigita.toolbox.ui.DomainObjectPropertySheet;
|
||||
|
||||
import org.arsdigita.cms.CMSConfig;
|
||||
import org.libreccm.l10n.LocalizedString;
|
||||
import org.librecms.CmsConstants;
|
||||
|
||||
/**
|
||||
* Displays the mime-type and the body of a single {@code TextAsset}. Maintains
|
||||
* a form or uploading files into the text body of the asset, and a form for
|
||||
* editing the text of the asset.
|
||||
* <p>
|
||||
* Unlike most other authoring components, this component does not require the
|
||||
* asset to exist. If the asset does not exist (i.e., if
|
||||
* <code>!m_assetModel.isSelected(state)</code>), the upload and editing forms
|
||||
* will create a new asset and set it in the model by calling
|
||||
* <code>setSelectedObject</code> on the asset selection model. Child classes
|
||||
* should override the {@link #createTextAsset(PageState)} method in to create a
|
||||
* valid text asset.
|
||||
* <p>
|
||||
* This component is used primarily in {@link GenericArticleBody} and
|
||||
* {@link com.arsdigita.cms.ui.templates.TemplateBody}
|
||||
*
|
||||
* <b>Note: </b> In CCM NG (version 7 and newer) {@code TextAsset} does not
|
||||
* longer exist. Instead fields of type {@link LocalizedString} are used. This
|
||||
* class has been adapted to use {@link LocalizedString}. The name of the class
|
||||
* etc. has been kept to make the migration easier.
|
||||
*
|
||||
* @author Stanislav Freidin (sfreidin@arsdigita.com)
|
||||
* @version <a href="jens.pelzetter@googlemail.com">Jens Pelzetter</a>
|
||||
*/
|
||||
public abstract class TextBody
|
||||
extends SecurityPropertyEditor
|
||||
implements Resettable, AuthoringStepComponent, RequestListener {
|
||||
|
||||
public static final String FILE_UPLOAD = "file";
|
||||
public static final String TEXT_ENTRY = "text";
|
||||
|
||||
private static final String STREAMLINED_DONE = "1";
|
||||
private static final CMSConfig CMS_CONFIG = CMSConfig.getConfig();
|
||||
|
||||
private final StringParameter streamlinedCreationParam;
|
||||
private final StringParameter selectedLanguageParam;
|
||||
private final ItemSelectionModel itemSelectionModel;
|
||||
|
||||
/**
|
||||
* Construct a new GenericArticleBody component
|
||||
*
|
||||
* @param assetModel The {@link ItemSelectionModel} which will be
|
||||
* responsible for maintaining the current
|
||||
* asset
|
||||
* @param selectedLanguageParam Parameter for the currently selected locale.
|
||||
*/
|
||||
public TextBody(final ItemSelectionModel assetModel,
|
||||
final StringParameter selectedLanguageParam) {
|
||||
|
||||
this(assetModel, null, selectedLanguageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new GenericArticleBody component
|
||||
*
|
||||
* @param itemSelectionModel The {@link ItemSelectionModel} which will be
|
||||
* responsible for maintaining the current asset
|
||||
* @param authoringKitWizard The parent wizard which contains the form.
|
||||
* The form may use the wizard's methods, such
|
||||
* as stepForward and stepBack, in its process
|
||||
* listener.
|
||||
* @param selectedLangugeParam Parameter for the currently selected locale.
|
||||
*/
|
||||
public TextBody(final ItemSelectionModel itemSelectionModel,
|
||||
final AuthoringKitWizard authoringKitWizard,
|
||||
final StringParameter selectedLangugeParam) {
|
||||
|
||||
super();
|
||||
this.itemSelectionModel = itemSelectionModel;
|
||||
|
||||
if (authoringKitWizard == null) {
|
||||
streamlinedCreationParam = new StringParameter("item_body_done");
|
||||
} else {
|
||||
streamlinedCreationParam = new StringParameter(
|
||||
String.format("%s_body_done",
|
||||
authoringKitWizard
|
||||
.getContentType()
|
||||
.getContentItemClass()
|
||||
.getName()));
|
||||
}
|
||||
|
||||
this.selectedLanguageParam = selectedLangugeParam;
|
||||
|
||||
if (!CMS_CONFIG.isHideTextAssetUploadFile()) {
|
||||
final PageFileForm pageFileForm = getPageFileForm();
|
||||
add(FILE_UPLOAD,
|
||||
new GlobalizedMessage("cms.ui.upload", CmsConstants.CMS_BUNDLE),
|
||||
pageFileForm,
|
||||
pageFileForm.getSaveCancelSection().getCancelButton());
|
||||
}
|
||||
|
||||
final PageTextForm pageTextForm = new PageTextForm(this);
|
||||
add(TEXT_ENTRY,
|
||||
new GlobalizedMessage("cms.ui.edit", CmsConstants.CMS_BUNDLE),
|
||||
pageTextForm,
|
||||
pageTextForm.getSaveCancelSection().getCancelButton());
|
||||
|
||||
// Specify full path to properties of the text asset
|
||||
final DomainObjectPropertySheet sheet = getTextBodyPropertySheet(
|
||||
itemSelectionModel);
|
||||
sheet.add(new GlobalizedMessage("cms.ui.article.text",
|
||||
CmsConstants.CMS_BUNDLE),
|
||||
getTextPropertyName());
|
||||
|
||||
setDisplayComponent(sheet);
|
||||
|
||||
getDisplayPane().setClassAttr("invertedPropertyDisplay");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the name of the property holding the text.
|
||||
*
|
||||
* @return The name of the property holding the text.
|
||||
*/
|
||||
protected abstract String getTextPropertyName();
|
||||
|
||||
protected DomainObjectPropertySheet getTextBodyPropertySheet(
|
||||
final ItemSelectionModel itemSelectionModel) {
|
||||
|
||||
final DomainObjectPropertySheet sheet = new DomainObjectPropertySheet(
|
||||
itemSelectionModel);
|
||||
|
||||
return sheet;
|
||||
|
||||
// return new TextBodyPropertySheet(itemSelectionModel,
|
||||
// selectedLanguageParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the options for the mime type select widget of
|
||||
* <code>GenericArticleForm</code> and sets the default mime type.
|
||||
*
|
||||
* @param mimeSelect
|
||||
*/
|
||||
protected void setMimeTypeOptions(final SingleSelect mimeSelect) {
|
||||
FileUploadSection.addMimeOptions(mimeSelect, "text");
|
||||
mimeSelect.setOptionSelected("text/html");
|
||||
}
|
||||
|
||||
/**
|
||||
* To be overwritten by subclasses, should return the text for the currently
|
||||
* selected locale.
|
||||
*
|
||||
* @param state The current page state
|
||||
*
|
||||
* @return The text of the currently selected locale.
|
||||
*/
|
||||
public abstract String getText(final PageState state);
|
||||
|
||||
/**
|
||||
* Reset this component to its original state
|
||||
*
|
||||
* @param state the current page state
|
||||
*/
|
||||
@Override
|
||||
public void reset(final PageState state) {
|
||||
showDisplayPane(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the text for the currently selected locale.
|
||||
*
|
||||
* @param state the current page state
|
||||
* @param text the new next text for the currently selected locale.
|
||||
*/
|
||||
protected abstract void updateText(PageState state, String text);
|
||||
|
||||
/**
|
||||
* Return the <code>ItemSelectionModel</code> which will be used to maintain
|
||||
* the current text asset
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public ItemSelectionModel getItemSelectionModel() {
|
||||
return itemSelectionModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward to the next step if the streamlined creation parameter is turned
|
||||
* on _and_ the streamlined_creation global state parameter is set to
|
||||
* 'active'
|
||||
*
|
||||
* @param state the PageState
|
||||
*/
|
||||
protected void maybeForwardToNextStep(final PageState state) {
|
||||
if (ContentItemPage.isStreamlinedCreationActive(state)
|
||||
&& !STREAMLINED_DONE.
|
||||
equals(state.getValue(streamlinedCreationParam))) {
|
||||
state.setValue(streamlinedCreationParam, STREAMLINED_DONE);
|
||||
fireCompletionEvent(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel streamlined creation for this step if the streamlined creation
|
||||
* parameter is turned on _and_ the streamlined_creation global state param
|
||||
* is set to 'active'
|
||||
*
|
||||
* @param state the PageState
|
||||
*/
|
||||
protected void cancelStreamlinedCreation(final PageState state) {
|
||||
if (ContentItemPage.isStreamlinedCreationActive(state)) {
|
||||
state.setValue(streamlinedCreationParam, STREAMLINED_DONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the edit component if the streamlined creation parameter is turned
|
||||
* on _and_ the streamlined_creation global state param is set to 'active'
|
||||
*
|
||||
* @param event
|
||||
*/
|
||||
@Override
|
||||
public void pageRequested(final RequestEvent event) {
|
||||
|
||||
final PageState state = event.getPageState();
|
||||
|
||||
if (ContentItemPage.isStreamlinedCreationActive(state)
|
||||
&& !STREAMLINED_DONE.
|
||||
equals(state.getValue(streamlinedCreationParam))) {
|
||||
showComponent(state, TEXT_ENTRY);
|
||||
}
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the form that is used to upload files. This method can be used so
|
||||
* that a subclass can use their own subclass of PageFileForm.
|
||||
*
|
||||
* @return The form for uploading a text.
|
||||
*/
|
||||
protected PageFileForm getPageFileForm() {
|
||||
return new PageFileForm(this);
|
||||
}
|
||||
|
||||
protected String getDefaultMimeType() {
|
||||
return "text/plain";
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers global state parameter for cancelling streamlined creation
|
||||
*/
|
||||
@Override
|
||||
public void register(final Page page) {
|
||||
super.register(page);
|
||||
page.addGlobalStateParam(streamlinedCreationParam);
|
||||
page.addRequestListener(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -42,17 +42,17 @@ import org.libreccm.l10n.LocalizedString;
|
|||
* it because it uses so many private inner classes
|
||||
*
|
||||
*/
|
||||
public class TextAssetBodyPropertySheet extends DomainObjectPropertySheet {
|
||||
public class TextBodyPropertySheet extends DomainObjectPropertySheet {
|
||||
|
||||
/**
|
||||
* Construct a new TextAssetBodyPropertySheet
|
||||
*
|
||||
* @param objModel The selection model which feeds domain objects to this
|
||||
* property sheet.
|
||||
* @param objModel The selection model which feeds domain
|
||||
* objects to this property sheet.
|
||||
* @param selectedLanguageParam
|
||||
*
|
||||
*/
|
||||
public TextAssetBodyPropertySheet(
|
||||
public TextBodyPropertySheet(
|
||||
final ItemSelectionModel objModel,
|
||||
final StringParameter selectedLanguageParam) {
|
||||
|
||||
|
|
@ -62,8 +62,8 @@ public class TextAssetBodyPropertySheet extends DomainObjectPropertySheet {
|
|||
}
|
||||
|
||||
// Build up the object properties model from the iterator over all properties
|
||||
private static class TextAssetBodyPropertiesModel implements
|
||||
PropertySheetModel {
|
||||
private static class TextAssetBodyPropertiesModel
|
||||
implements PropertySheetModel {
|
||||
|
||||
// public final static String MIME_TYPE_KEY
|
||||
// = TextAssetBodyLabelCellRenderer.MIME_TYPE_KEY;
|
||||
|
|
@ -140,8 +140,8 @@ public class TextAssetBodyPropertySheet extends DomainObjectPropertySheet {
|
|||
public PropertySheetModel makeModel(final PropertySheet sheet,
|
||||
final PageState state) {
|
||||
|
||||
TextAssetBodyPropertySheet propSheet
|
||||
= (TextAssetBodyPropertySheet) sheet;
|
||||
TextBodyPropertySheet propSheet
|
||||
= (TextBodyPropertySheet) sheet;
|
||||
throw new UnsupportedOperationException("ToDo");
|
||||
}
|
||||
|
||||
|
|
@ -19,6 +19,7 @@
|
|||
package org.librecms.contenttypes;
|
||||
|
||||
import com.arsdigita.cms.contenttypes.ui.ArticlePropertiesStep;
|
||||
import com.arsdigita.cms.ui.authoring.ArticleTextBody;
|
||||
import com.arsdigita.cms.ui.authoring.PageCreateForm;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -57,7 +58,15 @@ import org.librecms.contentsection.ContentItem;
|
|||
descriptionBundle = "org.librecms.CmsResources",
|
||||
descriptionKey = "cms.contenttypes.shared.basic_properties"
|
||||
+ ".description",
|
||||
order = 1)
|
||||
order = 1),
|
||||
@AuthoringStep(
|
||||
component = ArticleTextBody.class,
|
||||
labelBundle = "org.librecms.CmsResources",
|
||||
labelKey = "cms.contenttypes.shared.body_text.title",
|
||||
descriptionBundle = "org.librecms.CmsResources",
|
||||
descriptionKey = "cms.contenttypes.shared.body_text.description",
|
||||
order = 2
|
||||
)
|
||||
})
|
||||
public class Article extends ContentItem implements Serializable {
|
||||
|
||||
|
|
|
|||
|
|
@ -347,3 +347,7 @@ cms.contenttypes.ui.event.event_type=Event type
|
|||
cms.contenttypes.ui.event.link_to_map=Link to map
|
||||
cms.contenttypes.ui.event.cost=Cost
|
||||
cms.contenttypes.ui.event.end_date_after_start_date=The end date must be later then the start date
|
||||
cms.ui.upload=Upload
|
||||
cms.ui.authoring.body=Text
|
||||
cms.ui.article.text=Text
|
||||
cms.ui.authoring.edit_body_text=Edit text
|
||||
|
|
|
|||
|
|
@ -344,3 +344,7 @@ cms.contenttypes.ui.event.event_type=Art der Veranstaltung
|
|||
cms.contenttypes.ui.event.link_to_map=Link zur Karte
|
||||
cms.contenttypes.ui.event.cost=Kosten
|
||||
cms.contenttypes.ui.event.end_date_after_start_date=Das End-Datum muss nach dem Start-Datum liegen.
|
||||
cms.ui.upload=Hochladen
|
||||
cms.ui.authoring.body=Text
|
||||
cms.ui.article.text=Text
|
||||
cms.ui.authoring.edit_body_text=Text bearbeiten
|
||||
|
|
|
|||
|
|
@ -303,3 +303,7 @@ cms.contenttypes.ui.event.event_type=Event type
|
|||
cms.contenttypes.ui.event.link_to_map=Link to map
|
||||
cms.contenttypes.ui.event.cost=Cost
|
||||
cms.contenttypes.ui.event.end_date_after_start_date=The end date must be later then the start date
|
||||
cms.ui.upload=Upload
|
||||
cms.ui.authoring.body=Text
|
||||
cms.ui.article.text=Text
|
||||
cms.ui.authoring.edit_body_text=Edit text
|
||||
|
|
|
|||
Loading…
Reference in New Issue