/**
* (C) Copyright 2013 Jabylon (http://www.jabylon.org) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
/**
*
*/
package org.jabylon.rest.ui.wicket.panels;
import java.text.MessageFormat;
import java.util.Locale;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.Component;
import org.apache.wicket.Session;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.repeater.Item;
import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.eclipse.emf.common.util.EList;
import org.jabylon.properties.Project;
import org.jabylon.properties.ProjectLocale;
import org.jabylon.properties.ProjectVersion;
import org.jabylon.properties.PropertiesPackage;
import org.jabylon.properties.PropertyFileDescriptor;
import org.jabylon.properties.Resolvable;
import org.jabylon.properties.ResourceFolder;
import org.jabylon.properties.Review;
import org.jabylon.properties.ReviewState;
import org.jabylon.properties.Workspace;
import org.jabylon.properties.util.PropertiesSwitch;
import org.jabylon.rest.ui.model.ComplexEObjectListDataProvider;
import org.jabylon.rest.ui.security.CDOAuthenticatedSession;
import org.jabylon.rest.ui.security.RestrictedComponent;
import org.jabylon.rest.ui.util.GlobalResources;
import org.jabylon.rest.ui.util.WicketUtil;
import org.jabylon.rest.ui.wicket.BasicResolvablePanel;
import org.jabylon.rest.ui.wicket.pages.XliffDownloadPage;
import org.jabylon.security.CommonPermissions;
import org.jabylon.users.User;
/**
* @author Johannes Utzig (jutzig.dev@googlemail.com)
*/
public class ProjectResourcePanel extends BasicResolvablePanel<Resolvable<?, ?>> implements RestrictedComponent {
private static final long serialVersionUID = 1L;
public ProjectResourcePanel(Resolvable<?, ?> object, PageParameters parameters) {
super("content", object, parameters);
if (object instanceof Project) {
Project project = (Project) object;
String announcement = project.getAnnouncement();
if(announcement!=null && !announcement.isEmpty())
info(announcement);
}
add(new Label("header", new LabelSwitch(getLocale()).doSwitch(object)));
String href = WicketUtil.getContextPath() + "/api/"+ getModelObject().toURI().appendQuery("type=file");
ExternalLink downloadLink = new ExternalLink("download.link", href);
downloadLink.setVisible(object != null && !(object instanceof Workspace) && !(object instanceof Project));
add(downloadLink);
BookmarkablePageLink<String> downloadXliff = new BookmarkablePageLink<String>(
"link-download-xliff", XliffDownloadPage.class, parameters); //$NON-NLS-1$
downloadXliff.setVisible(object instanceof ProjectVersion);
add(downloadXliff);
XliffUploadPanel panel = new XliffUploadPanel("panel-upload-xliff", getModel(), parameters);
panel.setVisible(uploadPanelVisible(object));
add(panel);
}
@Override
public void renderHead(IHeaderResponse response) {
response.render(JavaScriptHeaderItem.forReference(GlobalResources.JS_JQUERY_DATATABLES));
response.render(JavaScriptHeaderItem.forReference(GlobalResources.JS_BOOTSTRAP_DATATABLES));
response.render(JavaScriptHeaderItem.forReference(GlobalResources.JS_DATATABLES_CUSTOMSORT));
super.renderHead(response);
}
@Override
protected void onBeforeRenderPanel() {
ComplexEObjectListDataProvider<Resolvable<?, ?>> provider = new ComplexEObjectListDataProvider<Resolvable<?, ?>>(getModel(),
PropertiesPackage.Literals.RESOLVABLE__CHILDREN);
final boolean endsOnSlash = urlEndsOnSlash();
final DataView<Resolvable<?, ?>> dataView = new DataView<Resolvable<?, ?>>("children", provider) {
private static final long serialVersionUID = -3530355534807668227L;
@Override
protected void populateItem(Item<Resolvable<?, ?>> item) {
Resolvable<?, ?> resolvable = item.getModelObject();
item.setVisible(canView(resolvable));
LinkTarget target = buildLinkTarget(resolvable, endsOnSlash);
ExternalLink link = new ExternalLink("link", Model.of(target.getHref()), target.getLabel());
item.add(link);
Triplet widths = computeProgressBars(target.getEndPoint());
Label progress = new Label("progress", String.valueOf(widths.getSuccess())+"%");
progress.add(new AttributeModifier("style", "width: " + widths.getSuccess() + "%"));
Label warning = new Label("warning", "");
warning.add(new AttributeModifier("style", "width: " + widths.getWarning() + "%"));
Label danger = new Label("danger", "");
danger.add(new AttributeModifier("style", "width: " + widths.getDanger() + "%"));
item.add(progress);
item.add(warning);
item.add(danger);
new ImageSwitch(item).doSwitch(target.getEndPoint());
item.add(new Label("summary",new Summary(item).doSwitch(target.getEndPoint())));
}
};
// dataView.setItemsPerPage(10);
add(dataView);
}
protected boolean canView(Resolvable<?, ?> resolvable) {
Session session = getSession();
if (session instanceof CDOAuthenticatedSession) {
CDOAuthenticatedSession authSession = (CDOAuthenticatedSession) session;
User user = authSession.getUser();
if(user==null)
user = authSession.getAnonymousUser();
if(user!=null)
return CommonPermissions.hasViewPermission(user, resolvable);
}
return false;
}
/**
* computes the width of the two stacked progress bars
* @param resolvable
* @return
*/
protected Triplet computeProgressBars(Resolvable<?, ?> resolvable) {
int greenWidth = resolvable.getPercentComplete();
int yellowWidth = 0;
if (resolvable instanceof PropertyFileDescriptor) {
PropertyFileDescriptor descriptor = (PropertyFileDescriptor) resolvable;
int keys = descriptor.getMaster() == null ? descriptor.getKeys() : descriptor.getMaster().getKeys();
if(keys>0)
{
EList<Review> reviews = descriptor.getReviews();
int activeReviews = 0;
for (Review review : reviews) {
if(review.getState()==ReviewState.OPEN || review.getState()==ReviewState.REOPENED)
activeReviews++;
}
yellowWidth = (int) (activeReviews*100/(double)keys);
greenWidth -= yellowWidth;
}
}
return new Triplet(greenWidth, yellowWidth);
}
private LinkTarget buildLinkTarget(Resolvable<?, ?> resolvable, boolean endsOnSlash) {
StringBuilder hrefBuilder = new StringBuilder();
LabelSwitch labelSwitch = new LabelSwitch(getLocale());
StringBuilder name = new StringBuilder();
name.append(labelSwitch.doSwitch(resolvable));
if (resolvable.getParent() == null)
hrefBuilder.append("/");
else if (resolvable.getParent() instanceof Workspace)
hrefBuilder.append(endsOnSlash ? resolvable.getName() : "workspace/" + resolvable.getName());
else
hrefBuilder.append(endsOnSlash ? resolvable.getName() : resolvable.getParent().getName() + "/" + resolvable.getName());
Resolvable<?, ?> folder = resolvable;
if(folder instanceof ResourceFolder)
{
// if it is a folder, squash more children, if there is only one
while (folder.getChildren().size() == 1) {
folder = folder.getChildren().get(0);
hrefBuilder.append("/");
hrefBuilder.append(folder.getName());
name.append("/");
name.append(labelSwitch.doSwitch(folder));
}
}
LinkTarget target = new LinkTarget(Model.of(name.toString()),hrefBuilder.toString(),folder);
return target;
}
@Override
public String getRequiredPermission() {
Resolvable<?, ?> object = getModelObject();
while(object!=null) {
if (object instanceof Project) {
return CommonPermissions.constructPermissionName(object, CommonPermissions.ACTION_VIEW);
}
else if (object instanceof Workspace) {
return CommonPermissions.constructPermissionName(object, CommonPermissions.ACTION_VIEW);
}
object = object.getParent();
}
return null;
}
/**
* @return true if the user is allowed to upload/import XLIFF files for this
* {@link ProjectVersion}.<br>
*/
private boolean uploadPanelVisible(Resolvable<?, ?> object) {
if (!(object instanceof ProjectVersion)) {
return false;
}
ProjectVersion version = (ProjectVersion) object;
if (version.isReadOnly()) {
return false;
}
Session session = getSession();
if (!(session instanceof CDOAuthenticatedSession)) {
return false;
}
Project project = version.getParent();
CDOAuthenticatedSession authSession = (CDOAuthenticatedSession) session;
return authSession.hasPermission(CommonPermissions.constructPermission(CommonPermissions.PROJECT, project.getName(),
CommonPermissions.ACTION_EDIT));
}
}
class LinkTarget
{
private IModel<String> label;
private String href;
private Resolvable<?, ?> endPoint;
public LinkTarget(IModel<String> label, String href, Resolvable<?, ?> endPoint) {
super();
this.label = label;
this.href = href;
this.endPoint = endPoint;
}
public Resolvable<?, ?> getEndPoint() {
return endPoint;
}
public String getHref() {
return href;
}
public IModel<String> getLabel() {
return label;
}
}
class LabelSwitch extends PropertiesSwitch<String> {
private Locale locale;
public LabelSwitch(Locale locale) {
super();
this.locale = locale;
if(locale==null)
this.locale = Locale.getDefault();
}
@Override
public <P extends Resolvable<?, ?>, C extends Resolvable<?, ?>> String caseResolvable(Resolvable<P, C> object) {
return object.getName();
}
@Override
public String caseProjectLocale(ProjectLocale object) {
if (object.getLocale() != null)
return object.getLocale().getDisplayName(locale);
return "Template";
}
@Override
public String caseWorkspace(Workspace object) {
return "Workspace";
}
}
class Summary extends PropertiesSwitch<IModel<String>> {
private static final String NUMBER_OF_KEYS_KEY = "number.of.keys";
private static final String TRANSLATION_PERCENTAGE_KEY = "translation.percentage";
private static final String TRANSLATION_PERCENTAGE_SHORT_KEY = "translation.percentage.short";
private transient Component parent;
public Summary(Component parent) {
this.parent = parent;
}
@Override
public <P extends Resolvable<?, ?>, C extends Resolvable<?, ?>> IModel<String> caseResolvable(Resolvable<P, C> object) {
return new StringResourceModel(TRANSLATION_PERCENTAGE_SHORT_KEY, parent, null, object.getPercentComplete());
}
@Override
public IModel<String> caseProjectLocale(ProjectLocale object) {
if(object.getParent()==null && object.getParent().getTemplate()==null)
return null;
ProjectLocale template = object.getParent().getTemplate();
int propertyCount = template.getPropertyCount();
int translatedCount = object.getPropertyCount();
return new StringResourceModel(TRANSLATION_PERCENTAGE_KEY, parent, null, translatedCount,propertyCount,object.getPercentComplete());
}
@Override
public IModel<String> casePropertyFileDescriptor(PropertyFileDescriptor object) {
int propertyCount = object.getKeys();
if(object.isMaster())
{
String message = NUMBER_OF_KEYS_KEY;
message = MessageFormat.format(message, propertyCount);
return new StringResourceModel(NUMBER_OF_KEYS_KEY, parent, null, propertyCount);
}
else
{
int templateCount = object.getMaster().getKeys();
return new StringResourceModel(TRANSLATION_PERCENTAGE_KEY, parent, null, propertyCount,templateCount,object.getPercentComplete());
}
}
}
class ImageSwitch extends PropertiesSwitch<Item<?>> {
private transient Item<?> item;
public ImageSwitch(Item<?> item) {
super();
this.item = item;
}
@Override
public Item<?> caseProject(Project object) {
return addCSSIcon("icon-folder-close");
}
@Override
public Item<?> caseProjectLocale(ProjectLocale object) {
if(object.getLocale()==null || object.getLocale()==ProjectLocale.TEMPLATE_LOCALE)
return addCSSIcon("icon-book");
WebMarkupContainer markupContainer = new WebMarkupContainer("css-icon");
item.add(markupContainer);
markupContainer.setVisible(false);
Image image = new Image("regular-image", WicketUtil.getIconForLocale(object.getLocale()));
item.add(image);
return item;
}
@Override
public Item<?> caseProjectVersion(ProjectVersion object) {
return addCSSIcon("icon-time");
}
@Override
public Item<?> caseResourceFolder(ResourceFolder object) {
return addCSSIcon("icon-folder-open");
}
@Override
public <P extends Resolvable<?, ?>, C extends Resolvable<?, ?>> Item<?> caseResolvable(Resolvable<P, C> object) {
return addCSSIcon("icon-file");
}
protected Item<?> addCSSIcon(String icon) {
WebMarkupContainer markupContainer = new WebMarkupContainer("css-icon");
item.add(markupContainer);
markupContainer.add(new AttributeModifier("class", icon));
Image image = new Image("regular-image", "not-there.gif");
image.setVisible(false);
item.add(image);
return item;
}
}
class Triplet
{
int success, warning;
public Triplet(int success, int warning)
{
super();
this.success = success;
this.warning = warning;
}
public int getSuccess()
{
return success;
}
public int getWarning()
{
return warning;
}
public int getDanger()
{
int danger = 100 - success - warning;
//only show danger level if the remainder is <=5
// https://github.com/jutzig/jabylon/issues/122
if(danger<=5)
return danger;
return 0;
}
}