Статьи

Загрузка изображений с использованием TinyMCE в рамках Wicket Framework

Цель этой статьи — показать, как загружать изображения с помощью tinymce в рамках wicket. Я намеревался показать, как это сделать, используя только калитки без дополнительных сервлетов, html-диалоги tinymce и т. Д. Все, начиная с самого начала и до конца, делается с помощью хитростей и небольшого количества java-скриптов, которые нужны.   Я предполагаю, что люди, которые будут читать эту статью, имеют знания о структуре калитки и интегрировали их в свой проект. Эту функциональность можно разделить на три части: создание плагина tinymce, создание диалогового окна калитки и вставка вновь созданного изображения в крошечный редактор.

Создание плагина:

Для этого нам нужно создать файл сценария Java и соответствующий файл плагина Java. Файл сценария Java должен иметь имя editor_plugin_src.js и должен размещаться в ресурсах пакета, имя которого начинается с wicket.contrib.tinymce.tiny_mce.plugins. Итак, в нашем случае я создал в src / main / resources пакет wicket.contrib.tinymce.tiny_mce.plugins.imageupload и поместил туда java-скрипт, который выглядит следующим образом:

(function() {
tinymce.create('tinymce.plugins.ImageUpload', {
init : function(ed, url) {
var t = this;
t.editor = ed;
// Register command
ed.addCommand('mceImageUpload', t._showDialog, t);
// Register button
ed.addButton('upload', {title : 'Upload image', cmd : 'mceImageUpload'});
},
_showDialog : function() {
var ed = this.editor;
// TODO
}
});
// Register plugin
tinymce.PluginManager.add('imageupload', tinymce.plugins.ImageUpload);
})();

Это стандартный вид почти всех плагинов. T Хин мы больше всего интересует _showDialog функция , которая будет выполнять функцию обратного вызова для диалога открытия калиткой. Но прежде чем мы определим обратный вызов, мы должны создать плагин Java.

Плагин Java:

    public class ImageUploadPlugin extends Plugin {

private PluginButton imageUploadButton;

public ImageUploadPlugin() {
super("imageupload");
imageUploadButton = new PluginButton("upload", this);
}

public PluginButton getImageUploadButton() {
return imageUploadButton;
}
}

Чтобы показать наш плагин в крошечном, нам просто нужно добавить его в крошечные настройки.

ImageUploadPlugin imageUploadPlugin = new ImageUploadPlugin();
settings.add(imageUploadPlugin.getImageUploadButton(), TinyMCESettings.Toolbar.first, TinyMCESettings.Position.after);

Итак, у нас есть кнопка, помещенная в крошечный. Теперь нам нужно создать Wicket Behavior, который будет реагировать на нажатие нашей кнопки.

public class ImageUploadBehavior extends AbstractDefaultAjaxBehavior {

@Override
protected void respond(AjaxRequestTarget pTarget) {
//place show dialog logic here
}

public String getFunctionName() {
return "showImageUploadDialog";
}

@Override
public void renderHead(IHeaderResponse pResponse) {
String script = getFunctionName() + " = function () { "
+ getCallbackScript() + " }";
pResponse.renderOnDomReadyJavascript(script);
}
}

Поведение помещает сгенерированный обратный вызов в тело функции showImageUploadDialog . Эта функция будет отображена сразу после сборки DOM. Теперь нам нужно привязать имя функции к кнопке. Мы будем делать это переопределение definePluginSettings метод в ImageUploadPlugin.

@Override
protected void definePluginSettings(StringBuffer pBuffer) {
super.definePluginSettings(pBuffer);
pBuffer.append(",\n\tuploadimage_callback: \"" + imageUploadBehavior.getFunctionName() + "\"");
}

Теперь обратный вызов, который входит в нашу функцию, должен выполняться по нажатию кнопки. Итак, в editor_plugin_src.js к функции   _showDialog () мы добавляем:

ed.execCallback('uploadimage_callback', ed);

Вот и все. Наша кнопка связана с ImageUploadBehavior. Поскольку поведение не работает без компонента, нам нужно добавить это поведение на панель, мы сделаем это на следующем шаге.

Создание диалога:

Нам нужно модальное окно калитки с простым контентом для добавления изображений (тип ввода файла + кнопка отправки ajax). Чтобы создать модальное окно, нам нужно разместить его на панели. Итак, мы создаем панель и сразу добавляем к ней наше поведение. Я также размещу здесь ImageUploadBehavior.class и ImageUploadPlugin.class.

public class ImageUploadPanel extends Panel  {
private ModalWindow modalWindow;

public ImageUploadPanel(String pId) {
super(pId);
setOutputMarkupId(true);
add(modalWindow = new ModalWindow("imageUploadDialog"));
modalWindow.setTitle(new ResourceModel("title.label"));
modalWindow.setInitialHeight(100);
modalWindow.setInitialWidth(300);

add(imageUploadBehavior = new ImageUploadBehavior());
}

public class ImageUploadBehavior …

public class ImageUploadPlugin …
}

с соответствующей разметкой:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/">
<body>
<wicket:panel>
<div wicket:id="imageUploadDialog"></div>
</wicket:panel>
</body>
</html>

и файл свойств:
title.label = Загрузить изображение

Создается модальное окно с заголовком, начальной высотой и шириной. Теперь мы создадим панель для загрузки изображений, которая будет размещена внутри нашего модального окна. Мы создадим форму с помощью FileUploadField и AjaxButton для отправки формы и панели обратной связи, которая будет отображать ошибку, если что-то пойдет не так.

public class ImageUploadContentPanel extends Panel {
public ImageUploadContentPanel(String pId) {
super(pId);
setOutputMarkupId(true);
Form<?> form = new Form<Void>("form");
final FeedbackPanel feedback = new FeedbackPanel("feedback");
feedback.setOutputMarkupId(true);
form.add(feedback);
final FileUploadField fileUploadField = new FileUploadField("file");
fileUploadField.setLabel(new ResourceModel("required.label"));
fileUploadField.setRequired(true);
form.add(fileUploadField);
form.add(new AjaxButton("uploadButton", form) {

@Override
protected void onSubmit(AjaxRequestTarget pTarget, Form<?> pForm) {
}
@Override
protected void onError(AjaxRequestTarget pTarget, Form<?> pForm) {
pTarget.addComponent(feedback);
}
});
add(form);
}

/**
* Method invoked after image upload.
* @param pTarget - ajax target
* @param pImageName - image name
* @param pContentType – image content type
*/
public void onImageUploaded(String pImageName, String pContentType, AjaxRequestTarget pTarget) {}
}

с соответствующей разметкой:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:wicket="http://wicket.apache.org/">
<body>
<wicket:panel>
<form wicket:id="form">
<input wicket:id="file" type="file"/>
<button wicket:id="uploadButton" type="button">
<wicket:message key="upload.button.label"/>
</button>
<span class="error" wicket:id="feedback"/>
</form>
</wicket:panel>
</body>
</html>

и файл свойств:
upload.button.label =
Требуется загрузка.label = Загрузка изображения

Мы также можем добавить валидатор, который будет проверять, являются ли загруженные файлы изображениями.

public static class FileExtensionValidator implements IValidator<FileUpload>  {
private static final long serialVersionUID = -8116224338791429342L;
public static final List<String> extensions = Arrays.asList(
"jpg","gif","jpeg","png","bmp");
public void validate(IValidatable<FileUpload> pValidatable) {
FileUpload image = pValidatable.getValue();
String extension = StringUtils.getFilenameExtension(
image.getClientFileName());
if (extension!=null && !extensions.contains(
extension.toLowerCase())) {
ValidationError error = new ValidationError();
error.addMessageKey("WrongExtensionValidator");
error.setVariable("extensions", extensions.toString());
pValidatable.error(error);
}
}
}

 

свойство UploadConentPanel.properties
WrongExtensionValidator = Неверный тип файла. Разрешены следующие типы файлов: $ {extensions}

и мы должны добавить его в FileUploadField

fileUploadField.add(new FileExtensionValidator()); 

Теперь нам нужно добавить эту панель в качестве содержимого в наше модальное окно и показать ее из поведения. Таким образом, мы реализуем метод ответа в нашем поведении.

        @Override
protected void respond(AjaxRequestTarget pTarget) {
ImageUploadContentPanel content = new ImageUploadContentPanel(modalWindow.getContentId()) {
modalWindow.setContent(content);
modalWindow.show(pTarget);
}

Итак, в заключение к этому разделу — теперь у нас есть панель, показывающая, когда нажимается маленькая кнопка. Последнее, что мы должны сделать, это показать загруженное изображение в крошечном редакторе.

Вставка изображения в редактор:

Перед размещением изображения в редакторе мы должны загрузить изображение во временный каталог. Для этого мы должны реализовать метод onSubmit из AjaxButton, который находится внутри нашего ImageUploadContentPanel.

            @Override
protected void onSubmit(AjaxRequestTarget pTarget, Form<?> pForm) {
FileUpload fileUpload = fileUploadField.getFileUpload();
String fileName = fileUpload.getClientFileName();
try {
File currentEngineerDir = new File(getTemporaryDirPath());
if(!currentEngineerDir.exists()){
currentEngineerDir.mkdir();
}
fileUpload.writeTo(new File(currentEngineerDir, fileName));
} catch (IOException ex) {
ImageUploadContentPanel.this.error("Can't upload image");
pTarget.addComponent(feedback);
return;
} finally {
fileUpload.closeStreams();
}
onImageUploaded(fileName, fileUpload.getContentType(), pTarget);
}

Путь к нашему каталогу будет помещен в контекст сервлета temp dir + sessionId.

public String getTemporaryDirPath() {
ServletContext servletContext =WebApplication.get().getServletContext();
return ((File)servletContext.getAttribute("javax.servlet.context.tempdir")).getPath() +
File.separatorChar + Session.get().getId() + File.separatorChar;
}

Пожалуйста, не забудьте удалить эти временные изображения, когда сеанс признан недействительным.

Здесь идет самая сложная часть. Как я уже говорил в начале, мы хотим сделать все с помощью калиток, поэтому мы создадим тег img и сгенерируем атрибут src с путем к компоненту wicket. URL генерируется из экземпляра компонента -> метод urlFor. Он принимает в качестве параметра RequestListenerInterface или ResourceReference. Поскольку наши изображения являются динамическими, мы выберем первое решение. Мы сделаем это в нашей ImageUploadPanel, но сначала нам нужно перенести имя изображения и тип содержимого изображения с нашей модальной панели содержимого в ImageUploadPanel. Я уже сделал это, определив   метод onImageUploaded. Поэтому нам просто нужно переопределить его и создать там наш тег img (мы также можем закрыть наше модальное окно).

ImageUploadContentPanel content = new ImageUploadContentPanel(modalWindow.getContentId()) {
                @Override
                public void onImageUploaded(String pImageName, String pContentType, AjaxRequestTarget pTarget) {
                   modalWindow.close(pTarget);
                   XmlTag xmlImageTag = createImageTag(pImageName, pContentType);
		   // TODO inject tag into editor
                }
            };

    public XmlTag createImageTag(String pImageName, String pContentType) {
        XmlTag tag = new XmlTag();
        tag.setName("img");
        tag.setType(XmlTag.OPEN_CLOSE);
        CharSequence url = ImageUploadPanel.this.urlFor(IResourceListener.INTERFACE);
        StringBuilder sb = new StringBuilder(url);
        sb.append("&").append(ImageFactory.IMAGE_FILE_NAME).append("=").append(pImageName);
        sb.append("&").append(IMAGE_CONTENT_TYPE).append("=").append(pContentType);
        tag.put("src", RequestCycle.get().getOriginalResponse().encodeURL(
                Strings.replaceAll(sb.toString(), "&", "&")));
        return tag;
    }

где:

public static final String IMAGE_FILE_NAME = "filename";
public static final String IMAGE_CONTENT_TYPE = "contentType";

Те параметры, которые добавляются в URL, необходимы для поиска правильного изображения.

Таким образом, у нас есть URL, который указывает на нашу панель, но если мы хотим прослушивать запросы относительно ресурсов, мы должны реализовать IResourceListener. В реализации мы должны вызвать метод onResourceRequested () для Resource, который мы создадим. Калитки сделают все остальное;)

Код:

public class ImageUploadPanel extends Panel implements IResourceListener …

public void onResourceRequested() {
final String fileName = RequestCycle.get().getRequest().getParameter(
IMAGE_FILE_NAME);
final String contentType = RequestCycle.get().getRequest().getParameter(
IMAGE_CONTENT_TYPE);
Resource resource = new Resource() {
@Override
public IResourceStream getResourceStream() {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(getTemporaryDirPath() +"/"+fileName);
} catch(FileNotFoundException ex) {
throw new RuntimeException("Problem with getting image");
}
return new FileResourceStream(contentType, inputStream);
}
};
resource.onResourceRequested();
}

где:

public class FileResourceStream extends AbstractResourceStream {

private String contentType;
private InputStream image;

public FileResourceStream(String pContentType, InputStream pImage){
super();
this.image = pImage;
this.contentType = pContentType;
}

@Override
public String getContentType() {
return contentType;
}

public InputStream getInputStream() throws ResourceStreamNotFoundException {
return image;
}

public void close() throws IOException {
image.close();
}
}

Осталось только ввести сгенерированный img в крошечный редактор. Это должно быть сделано с помощью сценария Java. Итак, в методе onImageUploaded мы добавляем:

pTarget.appendJavascript("tinyMCE.execCommand('mceInsertContent', false, '"+xmlImageTag.toString()+"');");

И это все.

Я проверил это с Wicket 1.4.7 и FireFox.