mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
AJAX Tag picker component
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@8027 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1816,6 +1816,9 @@ click_to_edit=click to edit
|
|||||||
# File Picker
|
# File Picker
|
||||||
go_up=Go up
|
go_up=Go up
|
||||||
|
|
||||||
|
# Tag picker
|
||||||
|
click_to_select_tag=Click to select tags
|
||||||
|
|
||||||
# Category browsing
|
# Category browsing
|
||||||
category_browser_plugin_label=Categories
|
category_browser_plugin_label=Categories
|
||||||
category_browser_plugin_description=Category Browsing
|
category_browser_plugin_description=Category Browsing
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"statusString":"${tagActions.resultString}",
|
"statusString":"${tagActions.resultString}",
|
||||||
"statusCode":${tagActions.resultCode?string}
|
"statusCode":${tagActions.resultCode?string},
|
||||||
|
"newTag":"${tagActions.newTag?string}"
|
||||||
}
|
}
|
@@ -1,29 +1,47 @@
|
|||||||
var action = args.a;
|
model.tagActions = tagActions(args.a, args.n, args.t);
|
||||||
|
|
||||||
/* Debug Inputs */
|
|
||||||
if (args["add"] != null) {action = "add";}
|
|
||||||
else if (args["remove"] != null) {action = "remove";}
|
|
||||||
|
|
||||||
model.tagActions = tagActions(action, args.n, args.t);
|
|
||||||
|
|
||||||
function tagActions(action, nodeId, tagName)
|
function tagActions(action, nodeId, tagName)
|
||||||
{
|
{
|
||||||
var resultString = "Action failed";
|
var resultString = "Action failed";
|
||||||
var resultCode = false;
|
var resultCode = false;
|
||||||
|
var node = null;
|
||||||
|
var newTag = null;
|
||||||
|
var newTagNodeRef = "";
|
||||||
|
|
||||||
if ((nodeId != "") && (nodeId != null) &&
|
if ((tagName != "") && (tagName != null))
|
||||||
(tagName != "") && (tagName != null))
|
{
|
||||||
|
tagName = tagName.toLowerCase();
|
||||||
|
if (action == "add")
|
||||||
|
{
|
||||||
|
// Make sure the tag is in the repo
|
||||||
|
newTag = createTag(tagName);
|
||||||
|
if (newTag != null)
|
||||||
|
{
|
||||||
|
resultString = "Tag added";
|
||||||
|
resultCode = true;
|
||||||
|
newTag.nodeRef.toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resultString = "Tag '" + tagName + "' not indexed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding the tag to a node?
|
||||||
|
if ((nodeId != "") && (nodeId != null))
|
||||||
{
|
{
|
||||||
var node = search.findNode("workspace://SpacesStore/" + nodeId);
|
var node = search.findNode("workspace://SpacesStore/" + nodeId);
|
||||||
tagName = tagName.toLowerCase();
|
|
||||||
|
|
||||||
if (node != null)
|
if (node != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var tags, newTag;
|
var tags;
|
||||||
|
|
||||||
if (action == "add")
|
if (action == "add")
|
||||||
|
{
|
||||||
|
// Must have newTag node
|
||||||
|
if (newTag != null)
|
||||||
{
|
{
|
||||||
resultString = "Already tagged with '" + tagName + "'";
|
resultString = "Already tagged with '" + tagName + "'";
|
||||||
tags = node.properties["cm:taggable"];
|
tags = node.properties["cm:taggable"];
|
||||||
@@ -45,10 +63,6 @@ function tagActions(action, nodeId, tagName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hasTag)
|
if (!hasTag)
|
||||||
{
|
|
||||||
// Make sure the tag is in the repo
|
|
||||||
newTag = createTag(tagName);
|
|
||||||
if (newTag != null)
|
|
||||||
{
|
{
|
||||||
// Add it to our node
|
// Add it to our node
|
||||||
tags.push(newTag);
|
tags.push(newTag);
|
||||||
@@ -56,13 +70,9 @@ function tagActions(action, nodeId, tagName)
|
|||||||
tagsArray["cm:taggable"] = tags;
|
tagsArray["cm:taggable"] = tags;
|
||||||
node.addAspect("cm:taggable", tagsArray);
|
node.addAspect("cm:taggable", tagsArray);
|
||||||
|
|
||||||
resultString = "Tag added";
|
resultString = "Document tagged";
|
||||||
resultCode = true;
|
resultCode = true;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
resultString = "Tag '" + tagName + "' not indexed";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (action == "remove")
|
else if (action == "remove")
|
||||||
@@ -118,11 +128,13 @@ function tagActions(action, nodeId, tagName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var result =
|
var result =
|
||||||
{
|
{
|
||||||
"resultString": resultString,
|
"resultString": resultString,
|
||||||
"resultCode": resultCode
|
"resultCode": resultCode,
|
||||||
|
"newTag": newTagNodeRef
|
||||||
};
|
};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@@ -743,7 +743,7 @@
|
|||||||
|
|
||||||
<config evaluator="aspect-name" condition="taggable">
|
<config evaluator="aspect-name" condition="taggable">
|
||||||
<property-sheet>
|
<property-sheet>
|
||||||
<show-property name="cm:taggable" display-label="Tags" show-in-edit-mode="false" />
|
<show-property name="cm:taggable" display-label="Tags" show-in-edit-mode="true" component-generator="AjaxTagPickerGenerator" />
|
||||||
</property-sheet>
|
</property-sheet>
|
||||||
</config>
|
</config>
|
||||||
|
|
||||||
|
@@ -208,6 +208,78 @@ public class PickerBean
|
|||||||
*
|
*
|
||||||
* The 16x16 pixel folder icon path is output as the 'icon' property for each child folder.
|
* The 16x16 pixel folder icon path is output as the 'icon' property for each child folder.
|
||||||
*/
|
*/
|
||||||
|
@InvokeCommand.ResponseMimetype(value=MimetypeMap.MIMETYPE_HTML)
|
||||||
|
public void getTagNodes() throws Exception
|
||||||
|
{
|
||||||
|
FacesContext fc = FacesContext.getCurrentInstance();
|
||||||
|
|
||||||
|
UserTransaction tx = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tx = Repository.getUserTransaction(FacesContext.getCurrentInstance(), true);
|
||||||
|
tx.begin();
|
||||||
|
|
||||||
|
Collection<ChildAssociationRef> childRefs;
|
||||||
|
NodeRef parentRef = null;
|
||||||
|
Map params = fc.getExternalContext().getRequestParameterMap();
|
||||||
|
String strParentRef = (String)params.get(ID_PARENT);
|
||||||
|
if (strParentRef == null || strParentRef.length() == 0)
|
||||||
|
{
|
||||||
|
childRefs = this.categoryService.getRootCategories(
|
||||||
|
Repository.getStoreRef(),
|
||||||
|
ContentModel.ASPECT_TAGGABLE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parentRef = new NodeRef(strParentRef);
|
||||||
|
childRefs = this.categoryService.getChildren(
|
||||||
|
parentRef,
|
||||||
|
CategoryService.Mode.SUB_CATEGORIES,
|
||||||
|
CategoryService.Depth.IMMEDIATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONWriter out = new JSONWriter(fc.getResponseWriter());
|
||||||
|
out.startObject();
|
||||||
|
out.startValue(ID_PARENT);
|
||||||
|
out.startObject();
|
||||||
|
if (parentRef == null)
|
||||||
|
{
|
||||||
|
out.writeNullValue(ID_ID);
|
||||||
|
out.writeValue(ID_NAME, "Tags");
|
||||||
|
out.writeValue(ID_ISROOT, true);
|
||||||
|
out.writeValue(ID_SELECTABLE, false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.writeValue(ID_ID, strParentRef);
|
||||||
|
out.writeValue(ID_NAME, Repository.getNameForNode(this.internalNodeService, parentRef));
|
||||||
|
}
|
||||||
|
out.endObject();
|
||||||
|
out.endValue();
|
||||||
|
out.startValue(ID_CHILDREN);
|
||||||
|
out.startArray();
|
||||||
|
for (ChildAssociationRef ref : childRefs)
|
||||||
|
{
|
||||||
|
NodeRef nodeRef = ref.getChildRef();
|
||||||
|
out.startObject();
|
||||||
|
out.writeValue(ID_ID, nodeRef.toString());
|
||||||
|
out.writeValue(ID_NAME, Repository.getNameForNode(this.internalNodeService, nodeRef));
|
||||||
|
out.endObject();
|
||||||
|
}
|
||||||
|
out.endArray();
|
||||||
|
out.endValue();
|
||||||
|
out.endObject();
|
||||||
|
|
||||||
|
tx.commit();
|
||||||
|
}
|
||||||
|
catch (Throwable err)
|
||||||
|
{
|
||||||
|
Utils.addErrorMessage("PickerBean exception in getTagNodes()", err);
|
||||||
|
fc.getResponseWriter().write("ERROR: " + err.getMessage());
|
||||||
|
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@InvokeCommand.ResponseMimetype(value=MimetypeMap.MIMETYPE_HTML)
|
@InvokeCommand.ResponseMimetype(value=MimetypeMap.MIMETYPE_HTML)
|
||||||
public void getFolderNodes() throws Exception
|
public void getFolderNodes() throws Exception
|
||||||
{
|
{
|
||||||
|
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
* As a special exception to the terms and conditions of version 2.0 of
|
||||||
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||||
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
|
* FLOSS exception. You should have recieved a copy of the text describing
|
||||||
|
* the FLOSS exception, and it is also available here:
|
||||||
|
* http://www.alfresco.com/legal/licensing"
|
||||||
|
*/
|
||||||
|
package org.alfresco.web.bean.generator;
|
||||||
|
|
||||||
|
import javax.faces.component.UIComponent;
|
||||||
|
import javax.faces.context.FacesContext;
|
||||||
|
|
||||||
|
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||||
|
import org.alfresco.web.app.servlet.FacesHelper;
|
||||||
|
import org.alfresco.web.ui.repo.RepoConstants;
|
||||||
|
import org.alfresco.web.ui.repo.component.UIMultiValueEditor;
|
||||||
|
import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
|
||||||
|
import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a category selector component.
|
||||||
|
*
|
||||||
|
* @author Mike Hatfield
|
||||||
|
*/
|
||||||
|
public class AjaxTagPickerGenerator extends BaseComponentGenerator
|
||||||
|
{
|
||||||
|
public UIComponent generate(FacesContext context, String id)
|
||||||
|
{
|
||||||
|
UIComponent component = context.getApplication().
|
||||||
|
createComponent(RepoConstants.ALFRESCO_FACES_AJAX_TAG_PICKER);
|
||||||
|
FacesHelper.setupComponentId(context, component, id);
|
||||||
|
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected UIComponent createComponent(FacesContext context, UIPropertySheet propertySheet,
|
||||||
|
PropertySheetItem item)
|
||||||
|
{
|
||||||
|
// the category selector component is used whatever mode the property sheet is in
|
||||||
|
return generate(context, item.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setupMandatoryValidation(FacesContext context, UIPropertySheet propertySheet,
|
||||||
|
PropertySheetItem item, UIComponent component, boolean realTimeChecking, String idSuffix)
|
||||||
|
{
|
||||||
|
if (component instanceof UIMultiValueEditor)
|
||||||
|
{
|
||||||
|
// Override the setup of the mandatory validation
|
||||||
|
// so we can send the _current_value id suffix.
|
||||||
|
// We also enable real time so the page load
|
||||||
|
// check disables the ok button if necessary, as the user
|
||||||
|
// adds or removes items from the multi value list the
|
||||||
|
// page will be refreshed and therefore re-check the status.
|
||||||
|
|
||||||
|
super.setupMandatoryValidation(context, propertySheet, item,
|
||||||
|
component, true, "_current_value");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Override the setup of the mandatory validation
|
||||||
|
// so we can send the _selected id suffix.
|
||||||
|
// We also enable real time so the page load check disabled
|
||||||
|
// the ok button if necessary, as the control is used the
|
||||||
|
// page will be refreshed and therefore re-check the status.
|
||||||
|
|
||||||
|
super.setupMandatoryValidation(context, propertySheet, item,
|
||||||
|
component, true, "_selected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ControlType getControlType()
|
||||||
|
{
|
||||||
|
return ControlType.SELECTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UIComponent setupMultiValuePropertyIfNecessary(
|
||||||
|
FacesContext context, UIPropertySheet propertySheet,
|
||||||
|
PropertySheetItem property, PropertyDefinition propertyDef,
|
||||||
|
UIComponent component)
|
||||||
|
{
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@@ -51,6 +51,7 @@ public final class RepoConstants
|
|||||||
public static final String ALFRESCO_FACES_BOOLEAN_CONVERTER = "org.alfresco.faces.BooleanLabelConverter";
|
public static final String ALFRESCO_FACES_BOOLEAN_CONVERTER = "org.alfresco.faces.BooleanLabelConverter";
|
||||||
public static final String ALFRESCO_FACES_MLTEXT_RENDERER = "org.alfresco.faces.MultilingualText";
|
public static final String ALFRESCO_FACES_MLTEXT_RENDERER = "org.alfresco.faces.MultilingualText";
|
||||||
public static final String ALFRESCO_FACES_MLTEXTAREA_RENDERER = "org.alfresco.faces.MultilingualTextArea";
|
public static final String ALFRESCO_FACES_MLTEXTAREA_RENDERER = "org.alfresco.faces.MultilingualTextArea";
|
||||||
|
public static final String ALFRESCO_FACES_AJAX_TAG_PICKER = "org.alfresco.faces.AjaxTagPicker";
|
||||||
|
|
||||||
public static final String GENERATOR_LABEL = "LabelGenerator";
|
public static final String GENERATOR_LABEL = "LabelGenerator";
|
||||||
public static final String GENERATOR_TEXT_FIELD = "TextFieldGenerator";
|
public static final String GENERATOR_TEXT_FIELD = "TextFieldGenerator";
|
||||||
|
@@ -61,15 +61,15 @@ import org.springframework.web.jsf.FacesContextUtils;
|
|||||||
*/
|
*/
|
||||||
public abstract class BaseAjaxItemPicker extends UIInput
|
public abstract class BaseAjaxItemPicker extends UIInput
|
||||||
{
|
{
|
||||||
private static final String MSG_GO_UP = "go_up";
|
protected static final String MSG_GO_UP = "go_up";
|
||||||
private static final String MSG_OK = "ok";
|
protected static final String MSG_OK = "ok";
|
||||||
private static final String MSG_CANCEL = "cancel";
|
protected static final String MSG_CANCEL = "cancel";
|
||||||
|
|
||||||
private static final String ID_ID = "id";
|
protected static final String ID_ID = "id";
|
||||||
private static final String ID_NAME = "name";
|
protected static final String ID_NAME = "name";
|
||||||
private static final String ID_ICON = "icon";
|
protected static final String ID_ICON = "icon";
|
||||||
|
|
||||||
private static final String FOLDER_IMAGE_PREFIX = "/images/icons/";
|
protected static final String FOLDER_IMAGE_PREFIX = "/images/icons/";
|
||||||
|
|
||||||
/** label to be displayed before an item is selected */
|
/** label to be displayed before an item is selected */
|
||||||
protected String label = null;
|
protected String label = null;
|
||||||
@@ -407,6 +407,10 @@ public abstract class BaseAjaxItemPicker extends UIInput
|
|||||||
{
|
{
|
||||||
this.label = (String)vb.getValue(getFacesContext());
|
this.label = (String)vb.getValue(getFacesContext());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.label = "";
|
||||||
|
}
|
||||||
|
|
||||||
return this.label;
|
return this.label;
|
||||||
}
|
}
|
||||||
@@ -420,7 +424,7 @@ public abstract class BaseAjaxItemPicker extends UIInput
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns the initial selecttion.
|
* @return Returns the initial selection.
|
||||||
*/
|
*/
|
||||||
public String getInitialSelection()
|
public String getInitialSelection()
|
||||||
{
|
{
|
||||||
|
@@ -0,0 +1,302 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
* As a special exception to the terms and conditions of version 2.0 of
|
||||||
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||||
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
|
* FLOSS exception. You should have recieved a copy of the text describing
|
||||||
|
* the FLOSS exception, and it is also available here:
|
||||||
|
* http://www.alfresco.com/legal/licensing
|
||||||
|
*/
|
||||||
|
package org.alfresco.web.ui.repo.component;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
import javax.faces.context.FacesContext;
|
||||||
|
import javax.faces.context.ResponseWriter;
|
||||||
|
import javax.transaction.UserTransaction;
|
||||||
|
|
||||||
|
import org.alfresco.model.ApplicationModel;
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
|
import org.alfresco.web.app.Application;
|
||||||
|
import org.alfresco.web.bean.repository.Repository;
|
||||||
|
import org.alfresco.web.ui.common.Utils;
|
||||||
|
import org.springframework.web.jsf.FacesContextUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mike Hatfield
|
||||||
|
*/
|
||||||
|
public class UIAjaxTagPicker extends BaseAjaxItemPicker
|
||||||
|
{
|
||||||
|
private static final String MSG_CLICK_TO_SELECT_TAG = "click_to_select_tag";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFamily()
|
||||||
|
{
|
||||||
|
return "org.alfresco.faces.AjaxTagPicker";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getServiceCall()
|
||||||
|
{
|
||||||
|
return "PickerBean.getTagNodes";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getDefaultIcon()
|
||||||
|
{
|
||||||
|
return "/images/icons/category_small.gif";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean getSingleSelect()
|
||||||
|
{
|
||||||
|
// Tag component is never in single select mode, but the base class needs to know this
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLabel()
|
||||||
|
{
|
||||||
|
// Tagger label only retrieved from a value binding when null
|
||||||
|
if (this.label == null)
|
||||||
|
{
|
||||||
|
super.getLabel();
|
||||||
|
}
|
||||||
|
return this.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
/**
|
||||||
|
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||||
|
*/
|
||||||
|
public void encodeBegin(FacesContext fc) throws IOException
|
||||||
|
{
|
||||||
|
if (isRendered() == false)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseWriter out = fc.getResponseWriter();
|
||||||
|
|
||||||
|
String formClientId = Utils.getParentForm(fc, this).getClientId(fc);
|
||||||
|
Map attrs = this.getAttributes();
|
||||||
|
ResourceBundle msg = Application.getBundle(fc);
|
||||||
|
|
||||||
|
// get values from submitted value or none selected
|
||||||
|
String selectedValues = null;
|
||||||
|
String selectedNames = null;
|
||||||
|
String selectedItems = null;
|
||||||
|
List<NodeRef> submitted = null;
|
||||||
|
|
||||||
|
submitted = (List<NodeRef>)getSubmittedValue();
|
||||||
|
if (submitted == null)
|
||||||
|
{
|
||||||
|
submitted = (List<NodeRef>)getValue();
|
||||||
|
}
|
||||||
|
// special case to submit empty lists on multi-select values
|
||||||
|
else if (submitted.equals("empty"))
|
||||||
|
{
|
||||||
|
submitted = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (submitted != null)
|
||||||
|
{
|
||||||
|
UserTransaction tx = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tx = Repository.getUserTransaction(fc, true);
|
||||||
|
tx.begin();
|
||||||
|
|
||||||
|
StringBuilder nameBuf = new StringBuilder(128);
|
||||||
|
StringBuilder valueBuf = new StringBuilder(128);
|
||||||
|
StringBuilder itemBuf = new StringBuilder(256);
|
||||||
|
NodeService nodeService = (NodeService)FacesContextUtils.getRequiredWebApplicationContext(
|
||||||
|
fc).getBean("nodeService");
|
||||||
|
for (NodeRef value : submitted)
|
||||||
|
{
|
||||||
|
String name = (String)nodeService.getProperty(value, ContentModel.PROP_NAME);
|
||||||
|
String icon = (String)nodeService.getProperty(value, ApplicationModel.PROP_ICON);
|
||||||
|
if (nameBuf.length() != 0)
|
||||||
|
{
|
||||||
|
nameBuf.append(", ");
|
||||||
|
valueBuf.append(",");
|
||||||
|
itemBuf.append(",");
|
||||||
|
}
|
||||||
|
nameBuf.append(name);
|
||||||
|
valueBuf.append(value.toString());
|
||||||
|
itemBuf.append(getItemJson(value.toString(), name, icon));
|
||||||
|
}
|
||||||
|
selectedNames = nameBuf.toString();
|
||||||
|
selectedValues = valueBuf.toString();
|
||||||
|
selectedItems = "[" + itemBuf.toString() + "]";
|
||||||
|
|
||||||
|
// commit the transaction
|
||||||
|
tx.commit();
|
||||||
|
}
|
||||||
|
catch (Throwable err)
|
||||||
|
{
|
||||||
|
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate the Ids for our script object and containing DIV element
|
||||||
|
String divId = getId();
|
||||||
|
String objId = divId + "Obj";
|
||||||
|
|
||||||
|
// generate the script to create and init our script object
|
||||||
|
String contextPath = fc.getExternalContext().getRequestContextPath();
|
||||||
|
out.write("<script type='text/javascript'>");
|
||||||
|
out.write("function init" + divId + "() {");
|
||||||
|
out.write(" window." + objId + " = new AlfTagger('" + divId + "','" + objId + "','" + getServiceCall() +
|
||||||
|
"','" + formClientId + "');");
|
||||||
|
out.write(" window." + objId + ".setChildNavigation(false);");
|
||||||
|
if (getDefaultIcon() != null)
|
||||||
|
{
|
||||||
|
out.write(" window." + objId + ".setDefaultIcon('" + getDefaultIcon() + "');");
|
||||||
|
}
|
||||||
|
if (selectedItems != null)
|
||||||
|
{
|
||||||
|
out.write(" window." + objId + ".setSelectedItems('" + selectedItems + "');");
|
||||||
|
}
|
||||||
|
out.write("}");
|
||||||
|
out.write("window.addEvent('domready', init" + divId + ");");
|
||||||
|
out.write("</script>");
|
||||||
|
|
||||||
|
// generate the DIV structure for our component as expected by the script object
|
||||||
|
out.write("<div id='" + divId + "' class='picker'>") ;
|
||||||
|
out.write(" <input id='" + getHiddenFieldName() + "' name='" + getHiddenFieldName() + "' type='hidden' value='");
|
||||||
|
if (selectedValues != null)
|
||||||
|
{
|
||||||
|
out.write(selectedValues);
|
||||||
|
}
|
||||||
|
out.write("'>");
|
||||||
|
// current selection displayed as link and message to launch the selector
|
||||||
|
out.write(" <div id='" + divId + "-noitems'");
|
||||||
|
if (attrs.get("style") != null)
|
||||||
|
{
|
||||||
|
out.write(" style=\"");
|
||||||
|
out.write((String)attrs.get("style"));
|
||||||
|
out.write('"');
|
||||||
|
}
|
||||||
|
if (attrs.get("styleClass") != null)
|
||||||
|
{
|
||||||
|
out.write(" class=");
|
||||||
|
out.write((String)attrs.get("styleClass"));
|
||||||
|
}
|
||||||
|
out.write(">");
|
||||||
|
if (isDisabled())
|
||||||
|
{
|
||||||
|
out.write(" <span>");
|
||||||
|
if (selectedNames != null)
|
||||||
|
{
|
||||||
|
out.write(selectedNames);
|
||||||
|
}
|
||||||
|
out.write(" </span>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.write(" <span class='pickerActionButton'><a href='javascript:" + objId + ".showSelector();'>");
|
||||||
|
if (selectedNames == null)
|
||||||
|
{
|
||||||
|
if (getLabel() == "")
|
||||||
|
{
|
||||||
|
setLabel(msg.getString(MSG_CLICK_TO_SELECT_TAG));
|
||||||
|
}
|
||||||
|
out.write(getLabel());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.write(selectedNames);
|
||||||
|
}
|
||||||
|
out.write(" </a></span>");
|
||||||
|
}
|
||||||
|
out.write(" </div>");
|
||||||
|
// container for item navigation
|
||||||
|
out.write(" <div id='" + divId + "-selector' class='pickerSelector'>");
|
||||||
|
out.write(" <div class='pickerResults'>");
|
||||||
|
out.write(" <div class='pickerResultsHeader'>");
|
||||||
|
out.write(" <div class='pickerNavControls'>");
|
||||||
|
out.write(" <span class='pickerNavUp'>");
|
||||||
|
out.write(" <a id='" + divId + "-nav-up' href='#'><img src='");
|
||||||
|
out.write(contextPath);
|
||||||
|
out.write("/images/icons/arrow_up.gif' border='0' alt='");
|
||||||
|
out.write(msg.getString(MSG_GO_UP));
|
||||||
|
out.write("' title='");
|
||||||
|
out.write(msg.getString(MSG_GO_UP));
|
||||||
|
out.write("'></a>");
|
||||||
|
out.write(" </span>");
|
||||||
|
out.write(" <span class='pickerNavBreadcrumb'>");
|
||||||
|
out.write(" <span id='" + divId + "-nav-txt' class='pickerNavBreadcrumbText'></span></a>");
|
||||||
|
out.write(" </span>");
|
||||||
|
out.write(" <span class='pickerNavAddTag'>");
|
||||||
|
out.write(" <span class='pickerAddTagIcon'></span>");
|
||||||
|
out.write(" <span id='" + divId + "-addTag-linkContainer' class='pickerAddTagLinkContainer'>");
|
||||||
|
out.write(" <a href='#' onclick='window." + objId + ".showAddTagForm(); return false;'>Add a tag</a>");
|
||||||
|
out.write(" </span>");
|
||||||
|
out.write(" <span id='" + divId + "-addTag-formContainer' class='pickerAddTagFormContainer'>");
|
||||||
|
out.write(" <input id='" + divId + "-addTag-box' class='pickerAddTagBox' name='" + divId + "-addTag-box' type='text'>");
|
||||||
|
out.write(" <img id='" + divId + "-addTag-ok' class='pickerAddTagImage' alt='Add' src='");
|
||||||
|
out.write(contextPath);
|
||||||
|
out.write("/images/office/action_successful.gif'>");
|
||||||
|
out.write(" <img id='" + divId + "-addTag-cancel' class='pickerAddTagImage' alt='Cancel' src='");
|
||||||
|
out.write(contextPath);
|
||||||
|
out.write("/images/office/action_failed.gif'>");
|
||||||
|
out.write(" </span>");
|
||||||
|
out.write(" </span>");
|
||||||
|
out.write(" <span id='" + divId + "-nav-add'></span>");
|
||||||
|
out.write(" </div>");
|
||||||
|
out.write(" </div>");
|
||||||
|
// container for item selection
|
||||||
|
out.write(" <div>");
|
||||||
|
out.write(" <div id='" + divId + "-ajax-wait' class='pickerAjaxWait'");
|
||||||
|
String height = getHeight();
|
||||||
|
if (height != null)
|
||||||
|
{
|
||||||
|
out.write(" style='height:" + height + "'");
|
||||||
|
}
|
||||||
|
out.write("></div>");
|
||||||
|
out.write(" <div id='" + divId + "-results-list' class='pickerResultsList'");
|
||||||
|
if (height != null)
|
||||||
|
{
|
||||||
|
out.write(" style='height:" + height + "'");
|
||||||
|
}
|
||||||
|
out.write("></div>");
|
||||||
|
out.write(" </div>");
|
||||||
|
out.write(" </div>");
|
||||||
|
// controls (OK & Cancel buttons etc.)
|
||||||
|
out.write(" <div class='pickerFinishControls'>");
|
||||||
|
out.write(" <div id='" + divId + "-finish' style='float:left' class='pickerButtons'><a href='javascript:" + objId + ".doneClicked();'>");
|
||||||
|
out.write(msg.getString(MSG_OK));
|
||||||
|
out.write("</a></div>");
|
||||||
|
out.write(" <div style='float:right' class='pickerButtons'><a href='javascript:" + objId + ".cancelClicked();'>");
|
||||||
|
out.write(msg.getString(MSG_CANCEL));
|
||||||
|
out.write("</a></div>");
|
||||||
|
out.write(" </div>");
|
||||||
|
out.write(" </div>");
|
||||||
|
// container for the selected items
|
||||||
|
out.write(" <div id='" + divId + "-selected' class='pickerSelectedItems'></div>");
|
||||||
|
out.write("</div>");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
* As a special exception to the terms and conditions of version 2.0 of
|
||||||
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||||
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||||
|
* FLOSS exception. You should have recieved a copy of the text describing
|
||||||
|
* the FLOSS exception, and it is also available here:
|
||||||
|
* http://www.alfresco.com/legal/licensing"
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Created on 25-May-2005
|
||||||
|
*/
|
||||||
|
package org.alfresco.web.ui.repo.tag;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mike Hatfield
|
||||||
|
*/
|
||||||
|
public class AjaxTagSelectorTag extends AjaxItemSelectorTag
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @see javax.faces.webapp.UIComponentTag#getComponentType()
|
||||||
|
*/
|
||||||
|
public String getComponentType()
|
||||||
|
{
|
||||||
|
return "org.alfresco.faces.AjaxTagPicker";
|
||||||
|
}
|
||||||
|
}
|
@@ -70,7 +70,8 @@ public class PageTag extends TagSupport
|
|||||||
// pop-up panel helper objects
|
// pop-up panel helper objects
|
||||||
"/scripts/ajax/summary-info.js",
|
"/scripts/ajax/summary-info.js",
|
||||||
// ajax pickers
|
// ajax pickers
|
||||||
"/scripts/ajax/picker.js"
|
"/scripts/ajax/picker.js",
|
||||||
|
"/scripts/ajax/tagger.js"
|
||||||
};
|
};
|
||||||
|
|
||||||
private final static String[] CSS =
|
private final static String[] CSS =
|
||||||
|
@@ -4327,6 +4327,15 @@
|
|||||||
<managed-bean-scope>request</managed-bean-scope>
|
<managed-bean-scope>request</managed-bean-scope>
|
||||||
</managed-bean>
|
</managed-bean>
|
||||||
|
|
||||||
|
<managed-bean>
|
||||||
|
<description>
|
||||||
|
Bean that generates an ajax tag picker component
|
||||||
|
</description>
|
||||||
|
<managed-bean-name>AjaxTagPickerGenerator</managed-bean-name>
|
||||||
|
<managed-bean-class>org.alfresco.web.bean.generator.AjaxTagPickerGenerator</managed-bean-class>
|
||||||
|
<managed-bean-scope>request</managed-bean-scope>
|
||||||
|
</managed-bean>
|
||||||
|
|
||||||
<managed-bean>
|
<managed-bean>
|
||||||
<description>
|
<description>
|
||||||
Bean that generates an association component
|
Bean that generates an association component
|
||||||
|
@@ -224,6 +224,11 @@
|
|||||||
<component-class>org.alfresco.web.ui.repo.component.UIAjaxFilePicker</component-class>
|
<component-class>org.alfresco.web.ui.repo.component.UIAjaxFilePicker</component-class>
|
||||||
</component>
|
</component>
|
||||||
|
|
||||||
|
<component>
|
||||||
|
<component-type>org.alfresco.faces.AjaxTagPicker</component-type>
|
||||||
|
<component-class>org.alfresco.web.ui.repo.component.UIAjaxTagPicker</component-class>
|
||||||
|
</component>
|
||||||
|
|
||||||
|
|
||||||
<!-- ==================== CONVERTERS ==================== -->
|
<!-- ==================== CONVERTERS ==================== -->
|
||||||
<component>
|
<component>
|
||||||
|
@@ -2462,4 +2462,76 @@
|
|||||||
</attribute>
|
</attribute>
|
||||||
</tag>
|
</tag>
|
||||||
|
|
||||||
|
<tag>
|
||||||
|
<name>ajaxTagSelector</name>
|
||||||
|
<tag-class>org.alfresco.web.ui.repo.tag.AjaxTagSelectorTag</tag-class>
|
||||||
|
<body-content>JSP</body-content>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>id</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>binding</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>rendered</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>style</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>styleClass</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>value</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>label</name>
|
||||||
|
<required>true</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>initialSelection</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>singleSelect</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>disabled</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
|
||||||
|
<attribute>
|
||||||
|
<name>height</name>
|
||||||
|
<required>false</required>
|
||||||
|
<rtexprvalue>true</rtexprvalue>
|
||||||
|
</attribute>
|
||||||
|
</tag>
|
||||||
|
|
||||||
</taglib>
|
</taglib>
|
||||||
|
@@ -99,6 +99,11 @@ div.pickerAjaxWait
|
|||||||
border-bottom: 1px solid #b2b2b2;
|
border-bottom: 1px solid #b2b2b2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pickerNavControls a img, .pickerNavControls input
|
||||||
|
{
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
img.pickerActionButton
|
img.pickerActionButton
|
||||||
{
|
{
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
@@ -106,6 +111,7 @@ img.pickerActionButton
|
|||||||
|
|
||||||
div.pickerResultsRow
|
div.pickerResultsRow
|
||||||
{
|
{
|
||||||
|
clear: left;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
@@ -177,6 +183,13 @@ div.pickerNavBreadcrumbPanel
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pickerNavBreadcrumbText
|
||||||
|
{
|
||||||
|
float: left;
|
||||||
|
height: 16px;
|
||||||
|
padding: 3px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
div.pickerNavBreadcrumbItem
|
div.pickerNavBreadcrumbItem
|
||||||
{
|
{
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
@@ -194,3 +207,46 @@ div.pickerNavBreadcrumbItem
|
|||||||
{
|
{
|
||||||
vertical-align: -4px;
|
vertical-align: -4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pickerNavAddTag
|
||||||
|
{
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerAddTagIcon
|
||||||
|
{
|
||||||
|
background: url("../images/office/new_tag.gif") no-repeat;
|
||||||
|
float: left;
|
||||||
|
height: 16px;
|
||||||
|
width: 16px;
|
||||||
|
margin: 2px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerAddTagLinkContainer
|
||||||
|
{
|
||||||
|
height: 16px;
|
||||||
|
margin: 3px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerAddTagFormContainer
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerAddTagBox
|
||||||
|
{
|
||||||
|
border: 1px solid #cccccc;
|
||||||
|
height: 16px;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 1px;
|
||||||
|
color: #0073e6;
|
||||||
|
font-family: tahoma, sans-serif;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pickerAddTagImage
|
||||||
|
{
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0px 0px -2px;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
591
source/web/scripts/ajax/tagger.js
Normal file
591
source/web/scripts/ajax/tagger.js
Normal file
@@ -0,0 +1,591 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
* This program 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 General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prerequisites: common.js
|
||||||
|
* mootools.v1.11.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Picker class definition
|
||||||
|
var AlfTagger = new Class(
|
||||||
|
{
|
||||||
|
/* id of the tagger */
|
||||||
|
id: null,
|
||||||
|
|
||||||
|
/* variable name being used */
|
||||||
|
varName: null,
|
||||||
|
|
||||||
|
/* form Id to submit when selection complete */
|
||||||
|
formClientId: null,
|
||||||
|
|
||||||
|
/* the item the tagger will start with */
|
||||||
|
startId: null,
|
||||||
|
|
||||||
|
/* list of items currently selected */
|
||||||
|
selected: null,
|
||||||
|
|
||||||
|
/* list of items pre-selected */
|
||||||
|
preselected: null,
|
||||||
|
|
||||||
|
/* the current parent being shown */
|
||||||
|
parent: null,
|
||||||
|
|
||||||
|
/* the list of items currently displayed */
|
||||||
|
items: [],
|
||||||
|
|
||||||
|
/* parent stack for the Navigate Up action*/
|
||||||
|
stack: [],
|
||||||
|
|
||||||
|
/* row type toggle */
|
||||||
|
oddRow: true,
|
||||||
|
|
||||||
|
/* ajax service call to retrieve data */
|
||||||
|
service: null,
|
||||||
|
|
||||||
|
/* default icon to use if not provided by the associated service */
|
||||||
|
defaultIcon: null,
|
||||||
|
|
||||||
|
/* initial display style of the outer div */
|
||||||
|
initialDisplayStyle: null,
|
||||||
|
|
||||||
|
/* allow child selection navigation */
|
||||||
|
allowChildNavigation: true,
|
||||||
|
|
||||||
|
initialize: function(id, varName, service, formClientId)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.varName = varName;
|
||||||
|
this.service = service;
|
||||||
|
this.formClientId = formClientId;
|
||||||
|
this.selected = [];
|
||||||
|
this.preselected = [];
|
||||||
|
|
||||||
|
// Add click event handlers for the add and cancel buttons
|
||||||
|
$(this.id + "-addTag-ok").addEvent("click", function()
|
||||||
|
{
|
||||||
|
this.addTag($(this.id + "-addTag-box").value);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
$(this.id + "-addTag-cancel").addEvent("click", function()
|
||||||
|
{
|
||||||
|
this.hideAddTagForm();
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
// Capture Enter and Esc key events from the input box
|
||||||
|
$(this.id + "-addTag-box").addEvent("keydown", function(e)
|
||||||
|
{
|
||||||
|
var e = new Event(e);
|
||||||
|
if (e.key == "enter")
|
||||||
|
{
|
||||||
|
$(this.id + "-addTag-ok").fireEvent("click");
|
||||||
|
e.stop();
|
||||||
|
}
|
||||||
|
else if (e.key == "esc")
|
||||||
|
{
|
||||||
|
$(this.id + "-addTag-cancel").fireEvent("click");
|
||||||
|
e.stop();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
// Cancel propagation of Enter keyup and keypress events to stop the JSF form submission
|
||||||
|
var fnEnterStop = function(e)
|
||||||
|
{
|
||||||
|
var e = new Event(e);
|
||||||
|
if (e.key == "enter")
|
||||||
|
{
|
||||||
|
e.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this.id + "-addTag-box").addEvents(
|
||||||
|
{
|
||||||
|
"keyup": fnEnterStop,
|
||||||
|
"keypress": fnEnterStop
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setDefaultIcon: function(icon)
|
||||||
|
{
|
||||||
|
this.defaultIcon = icon;
|
||||||
|
},
|
||||||
|
|
||||||
|
setStartId: function(id)
|
||||||
|
{
|
||||||
|
this.startId = id;
|
||||||
|
},
|
||||||
|
|
||||||
|
setChildNavigation: function(allowed)
|
||||||
|
{
|
||||||
|
this.allowChildNavigation = allowed;
|
||||||
|
},
|
||||||
|
|
||||||
|
setSelectedItems: function(jsonString)
|
||||||
|
{
|
||||||
|
this.preselected = Json.evaluate(jsonString);
|
||||||
|
},
|
||||||
|
|
||||||
|
showSelector: function()
|
||||||
|
{
|
||||||
|
// init selector state
|
||||||
|
this.selected = [];
|
||||||
|
this.stack = [];
|
||||||
|
|
||||||
|
this.initialDisplayStyle = $(this.id + "-noitems").getStyle("display");
|
||||||
|
$(this.id + "-selector").setStyle("display", "block");
|
||||||
|
$(this.id + "-selected").empty();
|
||||||
|
$(this.id + "-selected").setStyle("display", "block");
|
||||||
|
$(this.id + "-noitems").setStyle("display", "none");
|
||||||
|
|
||||||
|
this.preselected.each(function(item, i)
|
||||||
|
{
|
||||||
|
this.addSelectedItem(item);
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
this.hideAddTagForm();
|
||||||
|
|
||||||
|
// first ajax request for the children of the start item
|
||||||
|
this.getChildData(this.startId, this.populateChildren);
|
||||||
|
},
|
||||||
|
|
||||||
|
childClicked: function(index)
|
||||||
|
{
|
||||||
|
this.hidePanels();
|
||||||
|
var item = this.items[index];
|
||||||
|
// add an extra property to record the scroll position for this item
|
||||||
|
item.scrollpos = $(this.id + "-results-list").scrollTop;
|
||||||
|
this.stack.push(item); // ready for the breadcrumb redraw after the child data request
|
||||||
|
this.getChildData(item.id, this.populateChildren);
|
||||||
|
},
|
||||||
|
|
||||||
|
upClicked: function()
|
||||||
|
{
|
||||||
|
this.hidePanels();
|
||||||
|
// pop the parent off - peek for the grandparent
|
||||||
|
var parent = this.stack.pop();
|
||||||
|
var grandParent = this.stack[this.stack.length-1];
|
||||||
|
this.getChildData(grandParent != null ? grandParent.id : null, this.populateChildren, parent.scrollpos);
|
||||||
|
},
|
||||||
|
|
||||||
|
addItem: function(index)
|
||||||
|
{
|
||||||
|
var item;
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
item = this.items[index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item = this.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addSelectedItem(item);
|
||||||
|
// hide the Add button as this item is now added
|
||||||
|
$(this.id + "-add-" + item.id).setStyle("display", "none");
|
||||||
|
},
|
||||||
|
|
||||||
|
addSelectedItem: function(item)
|
||||||
|
{
|
||||||
|
// check item isn't already in the selected items list
|
||||||
|
for (var i = 0; i < this.selected.length; i++)
|
||||||
|
{
|
||||||
|
if (this.selected[i].id == item.id)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add item to list of selected items
|
||||||
|
this.selected.push(item);
|
||||||
|
|
||||||
|
// add the item to list outside the selector
|
||||||
|
var itemId = this.id + "-sel-" + item.id;
|
||||||
|
var itemDiv = new Element("div", {"id": itemId, "class": "pickerSelectedItem"});
|
||||||
|
|
||||||
|
var itemSpan = new Element("span", {"class": "pickerSelectedItemText"});
|
||||||
|
itemSpan.appendText(item.name);
|
||||||
|
itemSpan.injectInside(itemDiv);
|
||||||
|
|
||||||
|
var actionSpan = new Element("span", {"class": "pickerSelectedItemAction"});
|
||||||
|
var actionScript = "javascript:" + this.varName + ".delItem('" + item.id + "');";
|
||||||
|
var actionLink = new Element("a", {"href": actionScript});
|
||||||
|
var deleteIcon = new Element("img", {"src": getContextPath() + "/images/icons/minus.gif", "class": "pickerSelectedIcon",
|
||||||
|
"border": 0, "title": "Remove", "alt": "Remove"});
|
||||||
|
deleteIcon.injectInside(actionLink);
|
||||||
|
actionLink.injectInside(actionSpan);
|
||||||
|
actionSpan.injectInside(itemDiv);
|
||||||
|
|
||||||
|
// add mouse enter/leave enter to toggle delete icon (and toggle margin on outer div)
|
||||||
|
itemDiv.addEvent('mouseenter', function(e) {
|
||||||
|
$E('.pickerSelectedIcon', itemDiv).setStyle("opacity", 1);
|
||||||
|
});
|
||||||
|
itemDiv.addEvent('mouseleave', function(e) {
|
||||||
|
$E('.pickerSelectedIcon', itemDiv).setStyle("opacity", 0);
|
||||||
|
});
|
||||||
|
// add the item to the main selected item div
|
||||||
|
itemDiv.injectInside($(this.id + "-selected"));
|
||||||
|
|
||||||
|
// set the background image now the itemdiv has been added to the DOM (for IE)
|
||||||
|
itemDiv.setStyle("background-image", "url(" + getContextPath() + item.icon + ")");
|
||||||
|
|
||||||
|
// set opacity the style now the item has been added to the DOM (for IE)
|
||||||
|
$E('.pickerSelectedIcon', itemDiv).setStyle("opacity", 0);
|
||||||
|
|
||||||
|
// apply the effect
|
||||||
|
var fx = new Fx.Styles(itemDiv, {duration: 1000, wait: false, transition: Fx.Transitions.Quad.easeOut});
|
||||||
|
fx.start({'background-color': ['#faf7ce', '#ffffff']});
|
||||||
|
},
|
||||||
|
|
||||||
|
delItem: function(itemId)
|
||||||
|
{
|
||||||
|
// remove item from the selected items list
|
||||||
|
for (i=0; i<this.selected.length; i++)
|
||||||
|
{
|
||||||
|
if (this.selected[i].id == itemId)
|
||||||
|
{
|
||||||
|
this.selected.splice(i, 1); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the div representing the selected item
|
||||||
|
$(this.id + "-sel-" + itemId).remove();
|
||||||
|
|
||||||
|
// unhide the Add button if visible
|
||||||
|
var addBtn = $(this.id + "-add-" + itemId);
|
||||||
|
if (addBtn != null)
|
||||||
|
{
|
||||||
|
addBtn.setStyle("display", "block");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
clickBreadcumb: function(index)
|
||||||
|
{
|
||||||
|
this.hidePanels();
|
||||||
|
var item = this.stack[index];
|
||||||
|
// remove all items under this one from the navigation stack
|
||||||
|
var removeCount = (this.stack.length - index - 1);
|
||||||
|
if (removeCount != 0)
|
||||||
|
{
|
||||||
|
this.stack.splice(index + 1, removeCount);
|
||||||
|
}
|
||||||
|
this.getChildData(item.id, this.populateChildren);
|
||||||
|
},
|
||||||
|
|
||||||
|
breadcrumbToggle: function()
|
||||||
|
{
|
||||||
|
var bcpanel = $(this.id + "-nav-bread");
|
||||||
|
if (bcpanel.getChildren().length != 0)
|
||||||
|
{
|
||||||
|
if (bcpanel.getStyle("display") == "none")
|
||||||
|
{
|
||||||
|
bcpanel.setStyle("opacity", 0);
|
||||||
|
bcpanel.setStyle("display", "block");
|
||||||
|
var fx = new Fx.Styles(bcpanel, {duration: 200, wait: false, transition: Fx.Transitions.Quad.easeOut});
|
||||||
|
fx.start({'opacity': [0, 1]});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var fx = new Fx.Styles(bcpanel, {duration: 200, wait: false, transition: Fx.Transitions.Quad.easeOut,
|
||||||
|
onComplete: function() {bcpanel.setStyle("display", "none");}});
|
||||||
|
fx.start({'opacity': [1, 0]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
hidePanels: function()
|
||||||
|
{
|
||||||
|
$(this.id + "-nav-bread").setStyle("display", "none");
|
||||||
|
},
|
||||||
|
|
||||||
|
doneClicked: function()
|
||||||
|
{
|
||||||
|
var ids = "";
|
||||||
|
for (i=0; i<this.selected.length; i++)
|
||||||
|
{
|
||||||
|
if (i != 0) ids += ",";
|
||||||
|
ids += this.selected[i].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// special case for clearing out multi-select lists
|
||||||
|
if (ids == "")
|
||||||
|
{
|
||||||
|
ids = "empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
$(this.id + "-value").setProperty("value", ids);
|
||||||
|
|
||||||
|
document.forms[this.formClientId].submit();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelClicked: function()
|
||||||
|
{
|
||||||
|
$(this.id + "-selector").setStyle("display", "none");
|
||||||
|
$(this.id + "-selected").setStyle("display", "none");
|
||||||
|
$(this.id + "-noitems").setStyle("display", this.initialDisplayStyle);
|
||||||
|
},
|
||||||
|
|
||||||
|
populateChildren: function(response, tagger, scrollpos)
|
||||||
|
{
|
||||||
|
// clear any current results
|
||||||
|
var results = $(tagger.id + "-results-list");
|
||||||
|
results.empty();
|
||||||
|
|
||||||
|
// set the new parent
|
||||||
|
tagger.parent = {id: response.parent.id, name: response.parent.name};
|
||||||
|
|
||||||
|
// if nav stack is empty - add the parent item as the first entry
|
||||||
|
if (tagger.stack.length == 0)
|
||||||
|
{
|
||||||
|
tagger.stack.push(tagger.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the parent is null we're at the root so hide the up link
|
||||||
|
// otherwise we need to render it with the correct details
|
||||||
|
var upLink = $(tagger.id + "-nav-up");
|
||||||
|
if (tagger.parent.id == null || response.parent.isroot == true)
|
||||||
|
{
|
||||||
|
upLink.setStyle("display", "none");
|
||||||
|
upLink.setProperty("href", "#");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
upLink.setStyle("display", "block");
|
||||||
|
upLink.setProperty("href", "javascript:" + tagger.varName + ".upClicked('" + tagger.parent.id + "');");
|
||||||
|
}
|
||||||
|
|
||||||
|
// show what the parent next to the breadcrumb drop-down
|
||||||
|
$(tagger.id + "-nav-txt").setText(tagger.parent.name);
|
||||||
|
|
||||||
|
// render action for parent item (as it may be the root and not shown in child list!)
|
||||||
|
$(tagger.id + "-nav-add").empty();
|
||||||
|
if (response.parent.selectable != false)
|
||||||
|
{
|
||||||
|
var isSelected = false;
|
||||||
|
for (i=0; i<tagger.selected.length; i++)
|
||||||
|
{
|
||||||
|
if (tagger.selected[i].id == tagger.parent.id)
|
||||||
|
{
|
||||||
|
isSelected = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isSelected == false)
|
||||||
|
{
|
||||||
|
var actionId = tagger.id + "-add-" + tagger.parent.id;
|
||||||
|
var actionScript = "javascript:" + tagger.varName + ".addItem(-1);";
|
||||||
|
var actionLink = new Element("a", {"href": actionScript});
|
||||||
|
var actionImg = new Element("img", {"id": actionId, "src": getContextPath() + "/images/icons/plus.gif", "class": "taggerActionButton",
|
||||||
|
"border": 0, "title": "Add", "alt": "Add"});
|
||||||
|
actionImg.injectInside(actionLink);
|
||||||
|
actionLink.injectInside($(tagger.id + "-nav-add"));
|
||||||
|
// style modification for this Add button - it's inside a floating div unlike the others
|
||||||
|
if (document.all == undefined) actionImg.setStyle("vertical-align", "-18px");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through the children and render a row for each one
|
||||||
|
tagger.items = [];
|
||||||
|
tagger.oddRow = true;
|
||||||
|
|
||||||
|
for (var i=0; i<response.children.length; i++)
|
||||||
|
{
|
||||||
|
var item = response.children[i];
|
||||||
|
if (item.icon == undefined)
|
||||||
|
{
|
||||||
|
item.icon = tagger.defaultIcon;
|
||||||
|
}
|
||||||
|
tagger.items.push(item);
|
||||||
|
tagger.renderResultItem(item, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// scroll back to last position if required
|
||||||
|
results.scrollTop = (scrollpos == undefined ? 0 : scrollpos);
|
||||||
|
},
|
||||||
|
|
||||||
|
renderResultItem: function(item, index)
|
||||||
|
{
|
||||||
|
var divClass = "pickerResultsRow " + (this.oddRow ? "pickerResultsOddRow" : "pickerResultsEvenRow");
|
||||||
|
|
||||||
|
this.oddRow = !this.oddRow;
|
||||||
|
|
||||||
|
var div = new Element("div", {"class": divClass});
|
||||||
|
|
||||||
|
// render icon
|
||||||
|
var iconSpan = new Element("span", {"class": "pickerResultIcon"});
|
||||||
|
var iconImg = new Element("img", {"src": getContextPath() + item.icon});
|
||||||
|
iconImg.injectInside(iconSpan);
|
||||||
|
iconSpan.injectInside(div);
|
||||||
|
|
||||||
|
// render actions
|
||||||
|
var isSelected = false;
|
||||||
|
if (item.selectable != false)
|
||||||
|
{
|
||||||
|
var actionsSpan = new Element("span", {"class": "pickerResultActions"});
|
||||||
|
|
||||||
|
// display Add button for the item
|
||||||
|
for (i=0; i<this.selected.length; i++)
|
||||||
|
{
|
||||||
|
if (this.selected[i].id == item.id)
|
||||||
|
{
|
||||||
|
isSelected = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// even if found in the selected list, still need to generate the button - but hide it later
|
||||||
|
var actionId = this.id + "-add-" + item.id;
|
||||||
|
var actionScript = "javascript:" + this.varName + ".addItem(" + index + ");";
|
||||||
|
var actionLink = new Element("a", {"href": actionScript});
|
||||||
|
var actionImg = new Element("img", {"id": actionId, "src": getContextPath() + "/images/icons/plus.gif", "class": "pickerActionButton",
|
||||||
|
"border": 0, "title": "Add", "alt": "Add"});
|
||||||
|
actionImg.injectInside(actionLink);
|
||||||
|
actionLink.injectInside(actionsSpan);
|
||||||
|
|
||||||
|
actionsSpan.injectInside(div);
|
||||||
|
}
|
||||||
|
|
||||||
|
// render name link
|
||||||
|
var nameSpan = new Element("span", {"class": "pickerResultName"});
|
||||||
|
// child navigation allowed?
|
||||||
|
if (this.allowChildNavigation)
|
||||||
|
{
|
||||||
|
var nameLink;
|
||||||
|
if (item.url == undefined)
|
||||||
|
{
|
||||||
|
var link = "javascript:" + this.varName + ".childClicked(" + index + ");";
|
||||||
|
nameLink = new Element("a", {"href": link});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nameLink = new Element("a", {"href": getContextPath() + item.url, "target": "new"});
|
||||||
|
}
|
||||||
|
nameLink.appendText(item.name);
|
||||||
|
nameLink.injectInside(nameSpan);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nameSpan.appendText(item.name);
|
||||||
|
}
|
||||||
|
nameSpan.injectInside(div);
|
||||||
|
|
||||||
|
// add results
|
||||||
|
div.injectInside($(this.id + "-results-list"));
|
||||||
|
|
||||||
|
// hide the Add button (now this item is in the DOM) if in the selected list
|
||||||
|
if (isSelected)
|
||||||
|
{
|
||||||
|
actionImg.setStyle("display", "none");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getChildData: function(parent, callback, scrollpos)
|
||||||
|
{
|
||||||
|
// show ajax wait panel
|
||||||
|
$(this.id + '-ajax-wait').setStyle('display', 'block');
|
||||||
|
$(this.id + '-results-list').setStyle('visibility', 'hidden');
|
||||||
|
|
||||||
|
var tagger = this;
|
||||||
|
|
||||||
|
// execute ajax service call to retrieve list of child nodes as JSON response
|
||||||
|
new Ajax(getContextPath() + "/ajax/invoke/" + this.service + "?parent=" + (parent!=null ? parent : ""),
|
||||||
|
{
|
||||||
|
method: 'get',
|
||||||
|
headers: {'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT'},
|
||||||
|
async: false,
|
||||||
|
onComplete: function(r)
|
||||||
|
{
|
||||||
|
if (r.startsWith("ERROR:") == false)
|
||||||
|
{
|
||||||
|
result = Json.evaluate(r);
|
||||||
|
result.children.sort(tagger.sortByName);
|
||||||
|
|
||||||
|
callback(result, tagger, scrollpos);
|
||||||
|
|
||||||
|
// display results list again and hide ajax wait panel
|
||||||
|
$(tagger.id + '-results-list').setStyle('visibility', 'visible');
|
||||||
|
$(tagger.id + '-ajax-wait').setStyle('display', 'none');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure: function (r)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}).request();
|
||||||
|
},
|
||||||
|
|
||||||
|
sortByName: function(a, b)
|
||||||
|
{
|
||||||
|
return ((a.name < b.name) ? -1 : ((a.name > b.name) ? 1 : 0));
|
||||||
|
},
|
||||||
|
|
||||||
|
// when in tag mode - show the ad-hoc add form
|
||||||
|
showAddTagForm: function()
|
||||||
|
{
|
||||||
|
$(this.id + "-addTag-linkContainer").setStyle("display", "none");
|
||||||
|
$(this.id + "-addTag-formContainer").setStyle("display", "block");
|
||||||
|
$(this.id + "-addTag-box").focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
// hide the new tag form
|
||||||
|
hideAddTagForm: function(clearInput)
|
||||||
|
{
|
||||||
|
$(this.id + "-addTag-formContainer").setStyle("display", "none");
|
||||||
|
$(this.id + "-addTag-linkContainer").setStyle("display", "block");
|
||||||
|
if (clearInput)
|
||||||
|
{
|
||||||
|
$(this.id + "-addTag-box").value = "";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
addTag: function(tagName)
|
||||||
|
{
|
||||||
|
// Create a new item
|
||||||
|
var item =
|
||||||
|
{
|
||||||
|
id: "",
|
||||||
|
name: tagName,
|
||||||
|
icon: this.defaultIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline function to enable easy binding to "this"
|
||||||
|
var fnAfterAjax = function(item)
|
||||||
|
{
|
||||||
|
this.addSelectedItem(item);
|
||||||
|
this.hideAddTagForm(true);
|
||||||
|
this.getChildData(this.parent.id, this.populateChildren);
|
||||||
|
}.bind(this);
|
||||||
|
|
||||||
|
// execute ajax service call to retrieve list of child nodes as JSON response
|
||||||
|
new Ajax(getContextPath() + "/wcs/collaboration/tagActions?a=add&t=" + tagName,
|
||||||
|
{
|
||||||
|
method: 'post',
|
||||||
|
headers: {'If-Modified-Since': 'Sat, 1 Jan 2000 00:00:00 GMT'},
|
||||||
|
async: false,
|
||||||
|
onComplete: function(r)
|
||||||
|
{
|
||||||
|
// Pull out the nodeRef of the tag
|
||||||
|
result = Json.evaluate(r);
|
||||||
|
if (result.statusCode == true)
|
||||||
|
{
|
||||||
|
item.id = result.newTag;
|
||||||
|
fnAfterAjax(item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure: function (r)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}).request();
|
||||||
|
}
|
||||||
|
});
|
Reference in New Issue
Block a user