2021SC@SDUSC
软件工程应用与实践——OpenMeetings项目分析(九):
本周进行web包目录下的common包下剩余类的分析;
之前的博客已经分析完了tree包以及menu包下的代码,因此上图中单独的java类都是结下来要分析的内容,由于类较多,同时web包下还有许多其他子包需要分析,因此仅对上图中有代表性的几个类进行分析;
本周从上面的具有继承关系的一系列类进行分析;
BasePanel.java
package org.apache.openmeetings.web.common;
import org.apache.openmeetings.db.util.FormatHelper;
import org.apache.openmeetings.web.app.WebSession;
import org.apache.openmeetings.web.common.menu.MenuPanel;
import org.apache.openmeetings.web.pages.BasePage;
import org.apache.openmeetings.web.pages.MainPage;
import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import com.github.openjson.JSONObject;
public abstract class BasePanel extends Panel {
private static final long serialVersionUID = 1L;
public static final String EVT_CLICK = "click";
protected static final String BASE_ROW_CLASS = "ui-widget-content";
protected static final String ROW_CLASS = BASE_ROW_CLASS + " clickable";
public BasePanel(String id) {
super(id);
setOutputMarkupId(true);
}
public BasePanel(String id, IModel<?> model) {
super(id, model);
setOutputMarkupId(true);
}
public BasePage getBasePage() {
return (BasePage)getPage();
}
public MainPanel getMainPanel() {
return findParent(MainPanel.class);
}
protected boolean isRtl() {
return FormatHelper.isRtlLanguage(WebSession.get().getLocale().toLanguageTag());
}
/**
* Overwrite this method to execute Java code after Panel is loaded by the
* {@link MenuPanel}
*
* @param handler - request handler to update menu
* @return - this for chaining
*/
public BasePanel onMenuPanelLoad(IPartialPageRequestHandler handler) {
handler.add(getBasePage().getHeader().setVisible(true), getMainPanel().getMenu().setVisible(true)
, getMainPanel().getTopLinks().setVisible(true)
, ((MainPage)getPage()).getLoader().setVisible(false));
return this;
}
/**
* This method should be overridden to perform necessary cleanup: remove timers etc.
*
* @param handler - request handler to perform cleanup
*/
public void cleanup(IPartialPageRequestHandler handler) {
}
/**
* This method should be overridden to perform after "new message" dialog was closed.
*
* @param handler - request handler to perform action after "new message" dialog was closed.
*/
public void onNewMessageClose(IPartialPageRequestHandler handler) {
}
/**
* Handler for WebSocket messages
*
* @param handler - handler to perform update
* @param o - message to process
*/
protected void process(IPartialPageRequestHandler handler, JSONObject o) {
}
}
BasePanel是一个继承自Panel类的抽象类,结构如下:
BasePanel构造方法:包括两个构造方法无返回值,通过参数列表进行区分;形参为:用来初始化类的实例,但是由于是抽象类不能实例化,因此这个构造方法大概率是被其子类利用super进行调用,达到了一次编写,多次复用的目的;
getBasePage方法:利用org.apache.wicket.Component 的getPage()方法经强转为BasePanel后直接返回;
getMainPanel方法:利用org.apache.wicket.Component 的findParent()方法传入参数为mainpanel.class后直接返回;
isRtl方法:调用org.apache.openmeetings.db.util包下的FormatHelper里的isRtlLanguage方法判断是否是RTL语言,返回值为bollean对象;
代码文档:
检查 BCP 47 / III 语言代码是否表示 RTL 语言,即: - 明确指定从右到左脚本之一的语言代码,例如“az-Arab”,或一种语言代码,指定通常以从右 到左的脚本编写的语言之一,例如“fa”(波斯语),但明确指定拉丁文或西里尔文脚本(这是通常的 LTR 替代方案)的语言代码除外。从右到左的脚 本列表出现在 http://www.unicode.org/iso15924/iso15924-num.html 中的 100-199 范围内,其中阿拉伯语和希伯来语是迄今为止使用最广泛的。 我们还认识 Thaana、N’Ko 和 Tifinagh,它们也具有重要的现代用法。 其余的(叙利亚语、撒玛利亚语、曼达语等)似乎极其有限或没有现代用法 并且不被认可。 通常以从右到左脚本编写的语言被视为带有抑制脚本的语言:http://www.iana.org/assignments/language-subtag-registry 中的 Hebr|Arab|Thaa|Nkoo|Tfng,如以及信德语(sd)和维吾尔语(ug)。 语言代码的其他子标签的存在,例如像 EG(埃及)这样的地区,将被忽 略
onMenuPanelLoad方法:在MenuPanel加载Panel后覆盖此方法执行Java代码,形参:handler - - 请求处理程序更新菜单,返回值:BasePanel这用于链接;
cleanup方法:应该重写此方法以执行必要的清理:删除计时器等。形参:handler – 请求处理程序执行清理;
onNewMessageClose方法:在“新消息”对话框关闭后,应重写此方法以执行。形参:处理程序——请求处理程序在“新消息”对话框关闭后执行操作。
process方法:针对 WebSocket 消息的行为,参数:handler - - 执行更新的处理程序,o - - 要处理的消息;
UserBasePanel.java
package org.apache.openmeetings.web.common;
import org.apache.wicket.authroles.authorization.strategies.role.annotations.AuthorizeInstantiation;
import org.apache.wicket.model.IModel;
@AuthorizeInstantiation("Dashboard")
public abstract class UserBasePanel extends BasePanel {
private static final long serialVersionUID = 1L;
public UserBasePanel(String id) {
super(id);
}
public UserBasePanel(String id, IModel<?> model) {
super(id, model);
}
}
此类继承自BasePanel类,利用super调用父类构造方法用来实例化对象,但是同样是抽象类,因此也是供其子类进行super的调用;
ImagePanel.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.web.common;
import static org.apache.openmeetings.util.OpenmeetingsVariables.ATTR_TITLE;
import static org.apache.openmeetings.util.OpenmeetingsVariables.PARAM_SRC;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.markup.html.TransparentWebMarkupContainer;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.panel.Panel;
public abstract class ImagePanel extends Panel {
private static final long serialVersionUID = 1L;
protected final WebMarkupContainer profile = new TransparentWebMarkupContainer("profile");
public ImagePanel(String id) {
super(id);
add(profile.setOutputMarkupId(true));
}
@Override
protected void onInitialize() {
super.onInitialize();
update();
}
protected abstract String getImageUrl();
protected String getTitle() {
return getString("5");
}
protected Component getImage() {
return new WebMarkupContainer("img").add(
AttributeModifier.append("alt", getTitle())
, AttributeModifier.append(ATTR_TITLE, getTitle())
, AttributeModifier.append(PARAM_SRC, getImageUrl()));
}
public void update() {
profile.addOrReplace(getImage());
}
}
ImagePanel继承自Panel类,结构如上;
构造方法:调用父类构造方法,用来实例化对象;
onInitialize方法:定义了一些初始化imagePanel对象是所要执行的一系列操作,利用super调用父类的同名方法,然后执行成员方法update更新面板状态;
getImageUrl方法:抽象方法,用以给子类去实现
getTitle方法:返回org.apache.wicket.Component类的getString的String类型的结果;
getImage方法:返回一个WebMarkupContainer类型的对象,并调用此对象的add方法,传入三个AttributeModifier类型的参数,对这三个参数对象调用append方法,添加关于getTitle方法以及getImageUrl方法的属性名等详细信息;
update方法:形参为空,返回值为空,用以更新本类实例化的对象的状态,调用成员变量profile的addOrReplace方法,根据getImage方法得到的Component数组每个元素的id是否为空,决定去替换还是增加一个子项目;
UploadableImagePanel.java
package org.apache.openmeetings.web.common;
import static org.apache.openmeetings.util.OpenmeetingsVariables.getMaxUploadSize;
import static org.apache.openmeetings.util.OpenmeetingsVariables.getWebAppRootKey;
import java.io.File;
import java.util.Optional;
import org.apache.openmeetings.util.StoredFile;
import org.apache.openmeetings.web.util.upload.BootstrapFileUploadBehavior;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
import org.apache.wicket.extensions.ajax.markup.html.form.upload.UploadProgressBar;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.upload.FileUpload;
import org.apache.wicket.markup.html.form.upload.FileUploadField;
import org.apache.wicket.model.util.ListModel;
import org.apache.wicket.util.lang.Bytes;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
public abstract class UploadableImagePanel extends ImagePanel {
private static final long serialVersionUID = 1L;
private static final Logger log = Red5LoggerFactory.getLogger(UploadableImagePanel.class, getWebAppRootKey());
private static final String HOVER = "$('.profile .ui-button-icon.ui-icon.ui-icon-closethick.remove').hover(function (e) {$(this).toggleClass('ui-widget-content', e.type === 'mouseenter');});";
private final FileUploadField fileUploadField = new FileUploadField("image", new ListModel<FileUpload>());
private final Form<Void> form = new Form<>("form");
private final boolean delayed;
public UploadableImagePanel(String id, boolean delayed) {
super(id);
this.delayed = delayed;
}
protected abstract void processImage(StoredFile sf, File f) throws Exception;
protected abstract void deleteImage() throws Exception;
@Override
protected void onInitialize() {
super.onInitialize();
form.setMultiPart(true);
form.setMaxSize(Bytes.bytes(getMaxUploadSize()));
// Model is necessary here to avoid writing image to the User object
form.add(fileUploadField);
form.add(new UploadProgressBar("progress", form, fileUploadField));
form.addOrReplace(getImage());
if (delayed) {
add(new WebMarkupContainer("remove").add(AttributeModifier.append("onclick"
, String.format("$(this).parent().find('.fileinput').fileinput('clear');", form.getMarkupId()))));
} else {
add(new ConfirmableAjaxBorder("remove", getString("80"), getString("833")) {
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget target) {
try {
deleteImage();
} catch (Exception e) {
log.error("Error", e);
}
update(Optional.of(target));
}
});
fileUploadField.add(new AjaxFormSubmitBehavior(form, "change") {
private static final long serialVersionUID = 1L;
@Override
protected void onSubmit(AjaxRequestTarget target) {
process(Optional.of(target));
}
});
}
add(form.setOutputMarkupId(true));
add(BootstrapFileUploadBehavior.INSTANCE);
}
@Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(OnDomReadyHeaderItem.forScript(HOVER));
}
@Override
public void update() {
profile.addOrReplace(new WebMarkupContainer("img").setVisible(false));
form.addOrReplace(getImage());
}
private void update(Optional<AjaxRequestTarget> target) {
update();
target.ifPresent(t -> {
t.add(profile, form);
t.appendJavaScript(HOVER);
});
}
public void process(Optional<AjaxRequestTarget> target) {
FileUpload fu = fileUploadField.getFileUpload();
if (fu != null) {
File temp = null;
try {
temp = fu.writeToTempFile();
StoredFile sf = new StoredFile(fu.getClientFileName(), temp);
if (sf.isImage()) {
processImage(sf, temp);
}
} catch (Exception e) {
log.error("Error", e);
} finally {
if (temp != null && temp.exists()) {
log.debug("Temp file was deleted ? {}", temp.delete());
}
fu.closeStreams();
fu.delete();
}
}
update(target);
}
}
结构如图所示:
此类继承自ImagePanel类,同样是抽象类,主要用于定义可上传图像的面板的相关操作的方法;
构造方法 :形参为String id, boolean delayed,作用为实例化对象,代码先用super调用了弗雷德构造方法然后给成员变量赋值;
processImage方法,deleteImage方法:抽象方法,定义一个模板留给子类去实现;
onInitiallze方法:形参返回值都为空,定义了一些初始化对象时要执行的操作,先是用super调用父类的同名方法,然后给成员变量form用一些set以及add方法添加属性;然后根据delayed成员变量的值,为真的话,调用org.apache.wicket.MarkupContainer的add方法,传入参数为WebMarkupContainer类型的对象,如果为假,则参数为ConfirmableAjaxBorder类型的对象,最后用add方法添加form变量以及BootstrapFileUploadBehavior的一个常量;
renderHead方法:形参为IHeaderResponse response,返回值为空,作用为渲染组件的头部,先调用父类的同名方法,然后传参OnDomReadyHeaderItem.forScript(HOVER)通过response对象的方法进行渲染;
update方法:形参返回值都为空,作用为更新面板状态,调用成员函数profile以及form的AddOrReplace方法,并传入WebMarkupContainer对象,以及getImage方法得到的Component对象,通过条件判断是用替换还是增加进行更新面板状态;此函数有一个通过参数列表构成的重载,利用了java8的lamda表达式,其中使用了ifpresent方法,作用为如果存在值(target不为空),则使用该值调用指定的使用者,否则不执行任何操作。
process方法:定义了用于处理上传图像的过程时后端需要执行的操作,通过fileUploadField成员变量的getFileUpload方法得到FileUpload对象,进一步取得客户端的文件类型判断是否为图像,是的话执行上传过程,同时catch定义了捕获到exception时需要执行的操作,最后进行面板的更新操作以刷新;
UploadableProfileImagePanel.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License") + you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openmeetings.web.common;
import static org.apache.openmeetings.util.OmFileHelper.EXTENSION_PNG;
import static org.apache.openmeetings.util.OmFileHelper.PROFILE_FILE_NAME;
import static org.apache.openmeetings.util.OmFileHelper.getUploadProfilesUserDir;
import static org.apache.openmeetings.web.app.Application.getBean;
import static org.apache.openmeetings.web.util.ProfileImageResourceReference.getUrl;
import java.io.File;
import org.apache.openmeetings.core.converter.ImageConverter;
import org.apache.openmeetings.util.OmFileHelper;
import org.apache.openmeetings.util.StoredFile;
public class UploadableProfileImagePanel extends UploadableImagePanel {
private static final long serialVersionUID = 1L;
private final long userId;
public UploadableProfileImagePanel(String id, final long userId) {
super(id, false);
this.userId = userId;
}
@Override
protected void processImage(StoredFile sf, File f) throws Exception {
getBean(ImageConverter.class).convertImageUserProfile(f, userId, sf.isAsIs());
}
@Override
protected void deleteImage() throws Exception {
File f = new File(getUploadProfilesUserDir(userId), OmFileHelper.getName(PROFILE_FILE_NAME, EXTENSION_PNG));
if (f.exists()) {
f.delete();
}
}
@Override
protected String getImageUrl() {
return getUrl(getRequestCycle(), userId);
}
}
此类继承自上一个类,不再是抽象类,重写了父类的几个方法,是可上传的个人资料图片面板 的类;
构造方法:利用super调用父类构造方法,同时给成员变量userid赋值;
processImage方法:形参为StoredFile sf, File f,返回值为空,作用诶处理要上传的图像,利用getBean方法取得ImageConverter的实例后进行convertImageUserProfile的调用用于转换图像用户配置文件;
deleteImage方法:用于删除已经上传的图片文件,首先获取上传配置文件用户目录,并根据取得的name创建一个file实例,然后判断不为空的话执行删除操作;
getImageUrl方法:返回值为字符串类型,直接根据请求中要获取图片的名字返回其url;
总结:本周分析完了具有继承关系的这几个类,即图片中的红方框内,由于profileimagepanle就是一个简单的面板类,方法仅有一个,代码量较少,就没再分析,下周开始,对单个类逐一进行分析;