Цель этой статьи — показать, как загружать изображения с помощью 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.