diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/BranchesResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/BranchesResource.java
new file mode 100644
index 0000000000..be9f61bbd7
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/BranchesResource.java
@@ -0,0 +1,519 @@
+/*
+ *
+ * BranchesResource.java - A Restlet to read Branch data from SipXecs
+ * Copyright (C) 2012 PATLive, D. Chang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.branch.Branch;
+import org.sipfoundry.sipxconfig.branch.BranchManager;
+import org.sipfoundry.sipxconfig.phonebook.Address;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.BranchRestInfoFull;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class BranchesResource extends UserResource {
+
+ private BranchManager m_branchManager;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ private enum SortField {
+ NAME, DESCRIPTION, CITY, OFFICEDESIGNATION, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve all and single Branch
+ // ------------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ BranchRestInfoFull branchRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ branchRestInfo = createBranchRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_READ_FAILED, "Read Skills failed", exception.getLocalizedMessage());
+ }
+
+ return new BranchRepresentation(variant.getMediaType(), branchRestInfo);
+ }
+
+
+ // if not single, process request for all
+ List branches = m_branchManager.getBranches();
+ List branchesRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort if specified
+ sortBranches(branches);
+
+ // set requested agents groups and get resulting metadata
+ metadataRestInfo = addBranches(branchesRestInfo, branches);
+
+ // create final restinfo
+ BranchesBundleRestInfo branchesBundleRestInfo = new BranchesBundleRestInfo(branchesRestInfo, metadataRestInfo);
+
+ return new BranchesRepresentation(variant.getMediaType(), branchesBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single Branch
+ // ---------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ BranchRepresentation representation = new BranchRepresentation(entity);
+ BranchRestInfoFull branchRestInfo = representation.getObject();
+ Branch branch = null;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(branchRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ branch = m_branchManager.getBranch(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing
+ try {
+ updateBranch(branch, branchRestInfo);
+ m_branchManager.saveBranch(branch);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Update Branch failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Updated Branch", branch.getId());
+
+ return;
+ }
+
+
+ // otherwise add new
+ try {
+ branch = createBranch(branchRestInfo);
+ m_branchManager.saveBranch(branch);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Branch failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Branch", branch.getId());
+ }
+
+
+ // DELETE - Delete single Branch
+ // -----------------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ Branch branch;
+ int idInt;
+
+ // get id then delete single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ branch = m_branchManager.getBranch(idInt); // just obtain to make sure exists, use int id for actual delete
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ List branchIds = new ArrayList();
+ branchIds.add(idInt);
+ m_branchManager.deleteBranches(branchIds);
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_DELETED, "Deleted Branch", branch.getId());
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or
+ // update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(BranchRestInfoFull restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+
+ return validationInfo;
+ }
+
+ private BranchRestInfoFull createBranchRestInfo(int id) throws ResourceException {
+ BranchRestInfoFull branchRestInfo = null;
+
+ Branch branch = m_branchManager.getBranch(id);
+ branchRestInfo = new BranchRestInfoFull(branch);
+
+ return branchRestInfo;
+ }
+
+ private MetadataRestInfo addBranches(List branchesRestInfo, List branches) {
+ BranchRestInfoFull branchRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, branches.size());
+
+ // create list of skill restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ Branch branch = branches.get(index);
+
+ branchRestInfo = new BranchRestInfoFull(branch);
+ branchesRestInfo.add(branchRestInfo);
+ }
+
+ // create metadata about agent groups
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private void sortBranches(List branches) {
+ // sort if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case CITY:
+ Collections.sort(branches, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ Branch branch1 = (Branch) object1;
+ Branch branch2 = (Branch) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(branch1.getAddress().getCity(), branch2.getAddress().getCity());
+ }
+
+ });
+ break;
+
+ case OFFICEDESIGNATION:
+ Collections.sort(branches, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ Branch branch1 = (Branch) object1;
+ Branch branch2 = (Branch) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(branch1.getAddress().getOfficeDesignation(), branch2.getAddress().getOfficeDesignation());
+ }
+
+ });
+ break;
+
+ case NAME:
+ Collections.sort(branches, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ Branch branch1 = (Branch) object1;
+ Branch branch2 = (Branch) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(branch1.getName(),branch2.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(branches, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ Branch branch1 = (Branch) object1;
+ Branch branch2 = (Branch) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(branch1.getDescription(),branch2.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case CITY:
+ Collections.sort(branches, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ Branch branch1 = (Branch) object1;
+ Branch branch2 = (Branch) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(branch2.getAddress().getCity(), branch1.getAddress().getCity());
+ }
+
+ });
+ break;
+
+ case OFFICEDESIGNATION:
+ Collections.sort(branches, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ Branch branch1 = (Branch) object1;
+ Branch branch2 = (Branch) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(branch2.getAddress().getOfficeDesignation(), branch1.getAddress().getOfficeDesignation());
+ }
+
+ });
+ break;
+
+ case NAME:
+ Collections.sort(branches, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ Branch branch1 = (Branch) object1;
+ Branch branch2 = (Branch) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(branch2.getName(),branch1.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(branches, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ Branch branch1 = (Branch) object1;
+ Branch branch2 = (Branch) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(branch2.getDescription(),branch1.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateBranch(Branch branch, BranchRestInfoFull branchRestInfo) {
+ Address address;
+ String tempString;
+
+ // do not allow empty name
+ tempString = branchRestInfo.getName();
+ if (!tempString.isEmpty()) {
+ branch.setName(tempString);
+ }
+
+ branch.setDescription(branchRestInfo.getDescription());
+ branch.setPhoneNumber(branchRestInfo.getPhoneNumber());
+ branch.setFaxNumber(branchRestInfo.getFaxNumber());
+
+ address = getAddress(branchRestInfo);
+ branch.setAddress(address);
+ }
+
+ private Branch createBranch(BranchRestInfoFull branchRestInfo) throws ResourceException {
+ Address address;
+ Branch branch = new Branch();
+
+ // copy fields from rest info
+ branch.setName(branchRestInfo.getName());
+ branch.setDescription(branchRestInfo.getDescription());
+ branch.setPhoneNumber(branchRestInfo.getPhoneNumber());
+ branch.setFaxNumber(branchRestInfo.getFaxNumber());
+
+ address = getAddress(branchRestInfo);
+ branch.setAddress(address);
+
+ return branch;
+ }
+
+ private Address getAddress(BranchRestInfoFull branchRestInfo) {
+ Address address = new Address();
+
+ address.setCity(branchRestInfo.getAddress().getCity());
+ address.setCountry(branchRestInfo.getAddress().getCountry());
+ address.setOfficeDesignation(branchRestInfo.getAddress().getOfficeDesignation());
+ address.setState(branchRestInfo.getAddress().getState());
+ address.setStreet(branchRestInfo.getAddress().getStreet());
+ address.setZip(branchRestInfo.getAddress().getZip());
+
+ return address;
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class BranchesRepresentation extends XStreamRepresentation {
+
+ public BranchesRepresentation(MediaType mediaType, BranchesBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public BranchesRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("branch", BranchesBundleRestInfo.class);
+ xstream.alias("branch", BranchRestInfoFull.class);
+ }
+ }
+
+ static class BranchRepresentation extends XStreamRepresentation {
+
+ public BranchRepresentation(MediaType mediaType, BranchRestInfoFull object) {
+ super(mediaType, object);
+ }
+
+ public BranchRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("branch", BranchRestInfoFull.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class BranchesBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_branches;
+
+ public BranchesBundleRestInfo(List branches, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_branches = branches;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getBranches() {
+ return m_branches;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setBranchManager(BranchManager branchManager) {
+ m_branchManager = branchManager;
+ }
+
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdAgentGroupsResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdAgentGroupsResource.java
new file mode 100644
index 0000000000..ed580afc8d
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdAgentGroupsResource.java
@@ -0,0 +1,566 @@
+/*
+ *
+ * OpenAcdAgentGroupsResource.java - A Restlet to read group data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, D. Chang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdAgentGroup;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdClient;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdQueue;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdSkill;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdAgentGroupRestInfoFull;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdClientRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdQueueRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdSkillRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ResponseCode;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class OpenAcdAgentGroupsResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ private enum SortField {
+ NAME, DESCRIPTION, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve Groups and single Group
+ // --------------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdAgentGroupRestInfoFull agentGroupRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ agentGroupRestInfo = createAgentGroupRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_READ_FAILED, "Read Agent Group failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdAgentGroupRepresentation(variant.getMediaType(), agentGroupRestInfo);
+ }
+
+
+ // if not single, process request for all
+ List agentGroups = m_openAcdContext.getAgentGroups();
+ List agentGroupsRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort if specified
+ sortGroups(agentGroups);
+
+ // set requested based on pagination and get resulting metadata
+ metadataRestInfo = addAgentGroups(agentGroupsRestInfo, agentGroups);
+
+ // create final restinfo
+ OpenAcdAgentGroupsBundleRestInfo agentGroupsBundleRestInfo = new OpenAcdAgentGroupsBundleRestInfo(agentGroupsRestInfo, metadataRestInfo);
+
+ return new OpenAcdAgentGroupsRepresentation(variant.getMediaType(), agentGroupsBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single Group
+ // --------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get group from body
+ OpenAcdAgentGroupRepresentation representation = new OpenAcdAgentGroupRepresentation(entity);
+ OpenAcdAgentGroupRestInfoFull agentGroupRestInfo = representation.getObject();
+ OpenAcdAgentGroup agentGroup;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(agentGroupRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update a single group
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ agentGroup = m_openAcdContext.getAgentGroupById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing group
+ try {
+ updateAgentGroup(agentGroup, agentGroupRestInfo);
+ m_openAcdContext.saveAgentGroup(agentGroup);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Update Agent Group failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Updated Agent Group", agentGroup.getId());
+
+ return;
+ }
+
+
+ // otherwise add new agent group
+ try {
+ agentGroup = createAgentGroup(agentGroupRestInfo);
+ m_openAcdContext.saveAgentGroup(agentGroup);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Agent Group failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Agent Group", agentGroup.getId());
+ }
+
+
+ // DELETE - Delete single Group
+ // --------------------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ OpenAcdAgentGroup agentGroup;
+
+ // get id then delete a single group
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ agentGroup = m_openAcdContext.getAgentGroupById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ m_openAcdContext.deleteAgentGroup(agentGroup);
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_DELETED, "Deleted Agent Group", agentGroup.getId());
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or
+ // update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdAgentGroupRestInfoFull restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+
+ String name = restInfo.getName();
+
+ for (int i = 0; i < name.length(); i++) {
+ if ((!Character.isLetterOrDigit(name.charAt(i)) && !(Character.getType(name.charAt(i)) == Character.CONNECTOR_PUNCTUATION)) && name.charAt(i) != '-') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Name' must only contain letters, numbers, dashes, and underscores";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ return validationInfo;
+ }
+
+ private OpenAcdAgentGroupRestInfoFull createAgentGroupRestInfo(int id) throws ResourceException {
+ OpenAcdAgentGroupRestInfoFull agentGroupRestInfo;
+ List skillsRestInfo;
+ List queuesRestInfo;
+ List clientsRestInfo;
+
+ OpenAcdAgentGroup agentGroup = m_openAcdContext.getAgentGroupById(id);
+
+ skillsRestInfo = createSkillsRestInfo(agentGroup);
+ queuesRestInfo = createQueuesRestInfo(agentGroup);
+ clientsRestInfo = createClientsRestInfo(agentGroup);
+ agentGroupRestInfo = new OpenAcdAgentGroupRestInfoFull(agentGroup, skillsRestInfo, queuesRestInfo, clientsRestInfo);
+
+ return agentGroupRestInfo;
+ }
+
+ private MetadataRestInfo addAgentGroups(List agentGroupsRestInfo, List agentGroups) {
+ List skillsRestInfo;
+ List queuesRestInfo;
+ List clientsRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, agentGroups.size());
+
+ // create list of group restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdAgentGroup agentGroup = agentGroups.get(index);
+ skillsRestInfo = createSkillsRestInfo(agentGroup);
+ queuesRestInfo = createQueuesRestInfo(agentGroup);
+ clientsRestInfo = createClientsRestInfo(agentGroup);
+ OpenAcdAgentGroupRestInfoFull agentGroupRestInfo = new OpenAcdAgentGroupRestInfoFull(agentGroup, skillsRestInfo, queuesRestInfo, clientsRestInfo);
+ agentGroupsRestInfo.add(agentGroupRestInfo);
+ }
+
+ // create metadata about agent groups
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private List createSkillsRestInfo(OpenAcdAgentGroup agentGroup) {
+ List skillsRestInfo;
+ OpenAcdSkillRestInfo skillRestInfo;
+
+ // create list of skill restinfos for single group
+ Set groupSkills = agentGroup.getSkills();
+ skillsRestInfo = new ArrayList(groupSkills.size());
+
+ for (OpenAcdSkill groupSkill : groupSkills) {
+ skillRestInfo = new OpenAcdSkillRestInfo(groupSkill);
+ skillsRestInfo.add(skillRestInfo);
+ }
+
+ return skillsRestInfo;
+ }
+
+ private List createQueuesRestInfo(OpenAcdAgentGroup agentGroup) {
+ List queuesRestInfo;
+ OpenAcdQueueRestInfo queueRestInfo;
+
+ // create list of queue restinfos for single group
+ Set groupQueues = agentGroup.getQueues();
+ queuesRestInfo = new ArrayList(groupQueues.size());
+
+ for (OpenAcdQueue groupQueue : groupQueues) {
+ queueRestInfo = new OpenAcdQueueRestInfo(groupQueue);
+ queuesRestInfo.add(queueRestInfo);
+ }
+
+ return queuesRestInfo;
+ }
+
+
+ private List createClientsRestInfo(OpenAcdAgentGroup agentGroup) {
+ List clientsRestInfo;
+ OpenAcdClientRestInfo clientRestInfo;
+
+ // create list of client restinfos for single group
+ Set groupClients = agentGroup.getClients();
+ clientsRestInfo = new ArrayList(groupClients.size());
+
+ for (OpenAcdClient groupClient : groupClients) {
+ clientRestInfo = new OpenAcdClientRestInfo(groupClient);
+ clientsRestInfo.add(clientRestInfo);
+ }
+
+ return clientsRestInfo;
+ }
+
+ private void sortGroups(List agentGroups) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case NAME:
+ Collections.sort(agentGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgentGroup agentGroup1 = (OpenAcdAgentGroup) object1;
+ OpenAcdAgentGroup agentGroup2 = (OpenAcdAgentGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agentGroup1.getName(), agentGroup2.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(agentGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgentGroup agentGroup1 = (OpenAcdAgentGroup) object1;
+ OpenAcdAgentGroup agentGroup2 = (OpenAcdAgentGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agentGroup1.getDescription(),agentGroup2.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case NAME:
+ Collections.sort(agentGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgentGroup agentGroup1 = (OpenAcdAgentGroup) object1;
+ OpenAcdAgentGroup agentGroup2 = (OpenAcdAgentGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agentGroup2.getName(), agentGroup1.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(agentGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgentGroup agentGroup1 = (OpenAcdAgentGroup) object1;
+ OpenAcdAgentGroup agentGroup2 = (OpenAcdAgentGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agentGroup2.getDescription(),agentGroup1.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateAgentGroup(OpenAcdAgentGroup agentGroup, OpenAcdAgentGroupRestInfoFull agentGroupRestInfo) {
+ String tempString;
+
+ // do not allow empty name
+ tempString = agentGroupRestInfo.getName();
+ if (!tempString.isEmpty()) {
+ agentGroup.setName(tempString);
+ }
+
+ agentGroup.setDescription(agentGroupRestInfo.getDescription());
+
+ addLists(agentGroup, agentGroupRestInfo);
+ }
+
+ private OpenAcdAgentGroup createAgentGroup(OpenAcdAgentGroupRestInfoFull agentGroupRestInfo) {
+ OpenAcdAgentGroup agentGroup = new OpenAcdAgentGroup();
+
+ // copy fields from rest info
+ agentGroup.setName(agentGroupRestInfo.getName());
+ agentGroup.setDescription(agentGroupRestInfo.getDescription());
+
+ addLists(agentGroup, agentGroupRestInfo);
+
+ return agentGroup;
+ }
+
+ private void addLists(OpenAcdAgentGroup agentGroup, OpenAcdAgentGroupRestInfoFull agentGroupRestInfo) {
+ // remove all current skills
+ agentGroup.getSkills().clear();
+
+ // set skills
+ OpenAcdSkill skill;
+ List skillsRestInfo = agentGroupRestInfo.getSkills();
+ for (OpenAcdSkillRestInfo skillRestInfo : skillsRestInfo) {
+ skill = m_openAcdContext.getSkillById(skillRestInfo.getId());
+ agentGroup.addSkill(skill);
+ }
+
+ // remove all current queues
+ agentGroup.getQueues().clear();
+
+ // set queues
+ OpenAcdQueue queue;
+ List queuesRestInfo = agentGroupRestInfo.getQueues();
+ for (OpenAcdQueueRestInfo queueRestInfo : queuesRestInfo) {
+ queue = m_openAcdContext.getQueueById(queueRestInfo.getId());
+ agentGroup.addQueue(queue);
+ }
+
+ // remove all current clients
+ agentGroup.getClients().clear();
+
+ // set clients
+ OpenAcdClient client;
+ List clientsRestInfo = agentGroupRestInfo.getClients();
+ for (OpenAcdClientRestInfo clientRestInfo : clientsRestInfo) {
+ client = m_openAcdContext.getClientById(clientRestInfo.getId());
+ agentGroup.addClient(client);
+ }
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdAgentGroupsRepresentation extends XStreamRepresentation {
+
+ public OpenAcdAgentGroupsRepresentation(MediaType mediaType, OpenAcdAgentGroupsBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdAgentGroupsRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-agent-group", OpenAcdAgentGroupsBundleRestInfo.class);
+ xstream.alias("group", OpenAcdAgentGroupRestInfoFull.class);
+ xstream.alias("skill", OpenAcdSkillRestInfo.class);
+ xstream.alias("queue", OpenAcdQueueRestInfo.class);
+ xstream.alias("client", OpenAcdClientRestInfo.class);
+ }
+ }
+
+ static class OpenAcdAgentGroupRepresentation extends XStreamRepresentation {
+
+ public OpenAcdAgentGroupRepresentation(MediaType mediaType, OpenAcdAgentGroupRestInfoFull object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdAgentGroupRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("group", OpenAcdAgentGroupRestInfoFull.class);
+ xstream.alias("skill", OpenAcdSkillRestInfo.class);
+ xstream.alias("queue", OpenAcdQueueRestInfo.class);
+ xstream.alias("client", OpenAcdClientRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdAgentGroupsBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_groups;
+
+ public OpenAcdAgentGroupsBundleRestInfo(List agentGroups, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_groups = agentGroups;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getGroups() {
+ return m_groups;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdAgentsResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdAgentsResource.java
new file mode 100644
index 0000000000..3e1f5b0ffe
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdAgentsResource.java
@@ -0,0 +1,750 @@
+/*
+ *
+ * OpenAcdAgentGroupsResource.java - A Restlet to read Agent data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, D. Waseem
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.data.Status;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.common.CoreContext;
+import org.sipfoundry.sipxconfig.common.User;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdAgent;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdAgentGroup;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdClient;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdQueue;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdSkill;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdAgentGroupRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdAgentRestInfoFull;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdClientRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdQueueRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdSkillRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+
+public class OpenAcdAgentsResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private CoreContext m_coreContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ private enum SortField {
+ NAME, GROUP, SECURITY, USERNAME, FIRSTNAME, LASTNAME, BRANCH, EXTENSION, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ m_coreContext = getCoreContext();
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve all and single Agent
+ // -----------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdAgentRestInfoFull agentRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ agentRestInfo = createAgentRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ agentRestInfo = createAgentRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_READ_FAILED, "Read Agent failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdAgentRepresentation(variant.getMediaType(), agentRestInfo);
+ }
+
+
+ // if not single, process request for all
+ List agents = m_openAcdContext.getAgents();
+ List agentsRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort if specified
+ sortAgents(agents);
+
+ // set requested agents groups and get resulting metadata
+ metadataRestInfo = addAgents(agentsRestInfo, agents);
+
+ // create final restinfo
+ OpenAcdAgentsBundleRestInfo agentsBundleRestInfo = new OpenAcdAgentsBundleRestInfo(agentsRestInfo, metadataRestInfo);
+
+ return new OpenAcdAgentsRepresentation(variant.getMediaType(), agentsBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single Agent
+ // --------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ OpenAcdAgentRepresentation representation = new OpenAcdAgentRepresentation(entity);
+ OpenAcdAgentRestInfoFull agentRestInfo = (OpenAcdAgentRestInfoFull) representation.getObject();
+ OpenAcdAgent agent;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(agentRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ agent = m_openAcdContext.getAgentById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing
+ try {
+ updateAgent(agent, agentRestInfo);
+ m_openAcdContext.saveAgent(agent);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Update Agent failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Updated Agent");
+ return;
+ }
+
+
+ // otherwise add new
+ try {
+ agent = createOpenAcdAgent(agentRestInfo);
+ m_openAcdContext.saveAgent(agent);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Agent failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Agent", agent.getId());
+ }
+
+
+ // DELETE - Delete single Agent
+ // ----------------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ OpenAcdAgent agent;
+
+ // get id then delete single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ agent = m_openAcdContext.getAgentById(idInt);
+ }
+ catch (Exception ex) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ m_openAcdContext.deleteAgent(agent);
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_DELETED, "Deleted Agent.", agent.getId());
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value is missing.");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdAgentRestInfoFull restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+
+ return validationInfo;
+ }
+
+ private OpenAcdAgentRestInfoFull createAgentRestInfo(int id) throws ResourceException {
+ OpenAcdAgentRestInfoFull agentRestInfo;
+ List skillsRestInfo;
+ List queuesRestInfo;
+ List clientsRestInfo;
+
+ OpenAcdAgent agent = m_openAcdContext.getAgentById(id);
+
+ skillsRestInfo = createSkillsRestInfo(agent);
+ queuesRestInfo = createQueuesRestInfo(agent);
+ clientsRestInfo = createClientRestInfo(agent);
+ agentRestInfo = new OpenAcdAgentRestInfoFull(agent, skillsRestInfo, queuesRestInfo, clientsRestInfo);
+
+ return agentRestInfo;
+ }
+
+ private MetadataRestInfo addAgents(List agentsRestInfo, List agents) {
+ List skillsRestInfo;
+ List queuesRestInfo;
+ List clientsRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, agents.size());
+
+ // create list of agent restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdAgent agent = agents.get(index);
+ skillsRestInfo = createSkillsRestInfo(agent);
+ queuesRestInfo = createQueuesRestInfo(agent);
+ clientsRestInfo = createClientRestInfo(agent);
+
+ OpenAcdAgentRestInfoFull agentRestInfo = new OpenAcdAgentRestInfoFull(agent, skillsRestInfo, queuesRestInfo, clientsRestInfo);
+ agentsRestInfo.add(agentRestInfo);
+ }
+
+ // create metadata about agent groups
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private List createSkillsRestInfo(OpenAcdAgent agent) {
+ List skillsRestInfo;
+ OpenAcdSkillRestInfo skillRestInfo;
+
+ // create list of skill restinfos for single group
+ Set groupSkills = agent.getSkills();
+ skillsRestInfo = new ArrayList(groupSkills.size());
+
+ for (OpenAcdSkill groupSkill : groupSkills) {
+ skillRestInfo = new OpenAcdSkillRestInfo(groupSkill);
+ skillsRestInfo.add(skillRestInfo);
+ }
+
+ return skillsRestInfo;
+ }
+
+ private List createQueuesRestInfo(OpenAcdAgent agent) {
+ List queuesRestInfo;
+ OpenAcdQueueRestInfo queueRestInfo;
+
+ // create list of queue restinfos for single group
+ Set groupQueues = agent.getQueues();
+ queuesRestInfo = new ArrayList(groupQueues.size());
+
+ for (OpenAcdQueue groupQueue : groupQueues) {
+ queueRestInfo = new OpenAcdQueueRestInfo(groupQueue);
+ queuesRestInfo.add(queueRestInfo);
+ }
+
+ return queuesRestInfo;
+ }
+
+ private List createClientRestInfo(OpenAcdAgent agent) {
+ List clientsRestInfo;
+ OpenAcdClientRestInfo clientRestInfo;
+
+ // create list of queue restinfos for single group
+ Set groupClients = agent.getClients();
+ clientsRestInfo = new ArrayList(groupClients.size());
+
+ for (OpenAcdClient groupClient : groupClients) {
+ clientRestInfo = new OpenAcdClientRestInfo(groupClient);
+ clientsRestInfo.add(clientRestInfo);
+ }
+
+ return clientsRestInfo;
+
+ }
+
+ private void sortAgents(List agents) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case NAME:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+
+ return RestUtilities.compareIgnoreCaseNullSafe(agent1.getName(), agent2.getName());
+ }
+
+ });
+ break;
+
+ case GROUP:
+ Collections.sort(agents, new Comparator() {
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+
+ return RestUtilities.compareIgnoreCaseNullSafe(agent1.getAgentGroup(), agent2.getAgentGroup());
+ }
+ });
+ break;
+
+ case SECURITY:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent1.getUser().getGroupsNames(), agent2.getUser().getGroupsNames());
+ }
+
+ });
+ break;
+
+ case USERNAME:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent1.getUser().getUserName(), agent2.getUser().getUserName());
+ }
+
+ });
+ break;
+
+ case FIRSTNAME:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent1.getFirstName(), agent2.getFirstName());
+ }
+
+ });
+ break;
+
+ case LASTNAME:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent1.getLastName(), agent2.getLastName());
+ }
+
+ });
+ break;
+
+ case BRANCH:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent1.getUser().getBranch().getName(), agent2.getUser().getBranch().getName());
+ }
+
+ });
+ break;
+
+ case EXTENSION:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent1.getUser().getExtension(true), agent2.getUser().getExtension(true));
+ }
+
+ });
+ break;
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case NAME:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+
+ return RestUtilities.compareIgnoreCaseNullSafe(agent2.getName(), agent1.getName());
+ }
+
+ });
+ break;
+
+ case GROUP:
+ Collections.sort(agents, new Comparator() {
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+
+ return RestUtilities.compareIgnoreCaseNullSafe(agent2.getAgentGroup(), agent1.getAgentGroup());
+ }
+ });
+ break;
+
+ case SECURITY:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent2.getUser().getGroupsNames(), agent1.getUser().getGroupsNames());
+ }
+
+ });
+ break;
+
+ case USERNAME:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent2.getUser().getUserName(), agent1.getUser().getUserName());
+ }
+
+ });
+ break;
+
+ case FIRSTNAME:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent2.getFirstName(), agent1.getFirstName());
+ }
+
+ });
+ break;
+
+ case LASTNAME:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent2.getLastName(), agent1.getLastName());
+ }
+
+ });
+ break;
+
+ case BRANCH:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent2.getUser().getBranch().getName(), agent1.getUser().getBranch().getName());
+ }
+
+ });
+ break;
+
+ case EXTENSION:
+ Collections.sort(agents, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdAgent agent1 = (OpenAcdAgent) object1;
+ OpenAcdAgent agent2 = (OpenAcdAgent) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(agent2.getUser().getExtension(true), agent1.getUser().getExtension(true));
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateAgent(OpenAcdAgent agent, OpenAcdAgentRestInfoFull agentRestInfo) throws ResourceException {
+ OpenAcdAgentGroup agentGroup;
+
+ agentGroup = getAgentGroup(agentRestInfo);
+ agent.setGroup(agentGroup);
+
+ agent.setSecurity(agentRestInfo.getSecurity());
+
+ agent.getSkills().clear();
+
+ OpenAcdSkill skill;
+ List skillsRestInfo = agentRestInfo.getSkills();
+ for (OpenAcdSkillRestInfo skillRestInfo : skillsRestInfo) {
+ skill = m_openAcdContext.getSkillById(skillRestInfo.getId());
+ agent.addSkill(skill);
+ }
+
+ agent.getQueues().clear();
+
+ OpenAcdQueue queue;
+ List queuesRestInfo = agentRestInfo.getQueues();
+ for (OpenAcdQueueRestInfo queueRestInfo : queuesRestInfo) {
+ queue = m_openAcdContext.getQueueById(queueRestInfo.getId());
+ agent.addQueue(queue);
+ }
+
+ agent.getClients().clear();
+
+ OpenAcdClient client;
+ List clientsRestInfo = agentRestInfo.getClients();
+ for (OpenAcdClientRestInfo clientRestInfo : clientsRestInfo) {
+ client = m_openAcdContext.getClientById(clientRestInfo.getId());
+ agent.addClient(client);
+ }
+
+ }
+
+ private OpenAcdAgent createOpenAcdAgent(OpenAcdAgentRestInfoFull agentRestInfo) throws ResourceException {
+ OpenAcdAgent agent = new OpenAcdAgent();
+ OpenAcdAgent duplicateAgent = null;
+ OpenAcdAgentGroup agentGroup;
+ User user;
+
+ agentGroup = getAgentGroup(agentRestInfo);
+ agent.setGroup(agentGroup);
+
+ agent.setSecurity(agentRestInfo.getSecurity());
+
+ user = m_coreContext.getUser(agentRestInfo.getUserId());
+
+ // check if user is already assigned as agent
+ duplicateAgent = m_openAcdContext.getAgentByUser(user);
+ if (duplicateAgent != null) {
+ throw new ResourceException(Status.CLIENT_ERROR_BAD_REQUEST, "User " + user.getId() + " already assigned as agent.");
+ }
+
+ agent.setUser(user);
+
+ Set skills = new LinkedHashSet();
+ List skillsRestInfo = agentRestInfo.getSkills();
+
+ for (OpenAcdSkillRestInfo skillRestInfo : skillsRestInfo) {
+ skills.add(m_openAcdContext.getSkillById(skillRestInfo.getId()));
+ }
+
+ Set queues = new LinkedHashSet();
+ List queuesRestInfo = agentRestInfo.getQueues();
+
+ for (OpenAcdQueueRestInfo queueRestInfo : queuesRestInfo) {
+ queues.add(m_openAcdContext.getQueueById(queueRestInfo.getId()));
+ }
+
+ Set clients = new LinkedHashSet();
+ List clientsRestInfo = agentRestInfo.getClients();
+
+ for (OpenAcdClientRestInfo clientRestInfo : clientsRestInfo) {
+ clients.add(m_openAcdContext.getClientById(clientRestInfo.getId()));
+ }
+
+ agent.setSkills(skills);
+ agent.setQueues(queues);
+ agent.setClients(clients);
+
+ return agent;
+ }
+
+ private OpenAcdAgentGroup getAgentGroup(OpenAcdAgentRestInfoFull agentRestInfo) throws ResourceException {
+ OpenAcdAgentGroup agentGroup;
+ int groupId = 0;
+
+ try {
+ groupId = agentRestInfo.getGroupId();
+ agentGroup = m_openAcdContext.getAgentGroupById(groupId);
+ }
+ catch (Exception ex) {
+ throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "Agent Group ID " + groupId + " not found.");
+ }
+
+ return agentGroup;
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdAgentsRepresentation extends XStreamRepresentation {
+
+ public OpenAcdAgentsRepresentation(MediaType mediaType, OpenAcdAgentsBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdAgentsRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-agent", OpenAcdAgentsBundleRestInfo.class);
+ xstream.alias("agent", OpenAcdAgentRestInfoFull.class);
+ xstream.alias("group", OpenAcdAgentGroupRestInfo.class);
+ xstream.alias("skill", OpenAcdSkillRestInfo.class);
+ xstream.alias("queue", OpenAcdQueueRestInfo.class);
+ xstream.alias("client", OpenAcdClientRestInfo.class);
+ }
+ }
+
+ static class OpenAcdAgentRepresentation extends XStreamRepresentation {
+
+ public OpenAcdAgentRepresentation(MediaType mediaType, OpenAcdAgentRestInfoFull object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdAgentRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("agent", OpenAcdAgentRestInfoFull.class);
+ xstream.alias("group", OpenAcdAgentGroupRestInfo.class);
+ xstream.alias("skill", OpenAcdSkillRestInfo.class);
+ xstream.alias("queue", OpenAcdQueueRestInfo.class);
+ xstream.alias("client", OpenAcdClientRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdAgentsBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_agents;
+
+ public OpenAcdAgentsBundleRestInfo(List agents, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_agents = agents;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getAgents() {
+ return m_agents;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdClientsResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdClientsResource.java
new file mode 100644
index 0000000000..6ed2edfea7
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdClientsResource.java
@@ -0,0 +1,461 @@
+/*
+ *
+ * OpenAcdClientsResource.java - A Restlet to read Skill data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, I. Wesson, D. Chang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.data.Status;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdClient;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdClientRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ResponseCode;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class OpenAcdClientsResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ enum SortField {
+ NAME, DESCRIPTION, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve all and single Client
+ // ------------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdClientRestInfo clientRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ clientRestInfo = createClientRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_READ_FAILED, "Read Client failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdClientRepresentation(variant.getMediaType(), clientRestInfo);
+ }
+
+
+ // if not single, process request for list
+ List clients = m_openAcdContext.getClients();
+ List clientsRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort groups if specified
+ sortClients(clients);
+
+ // set requested records and get resulting metadata
+ metadataRestInfo = addClients(clientsRestInfo, clients);
+
+ // create final restinfo
+ OpenAcdClientsBundleRestInfo clientsBundleRestInfo = new OpenAcdClientsBundleRestInfo(clientsRestInfo, metadataRestInfo);
+
+ return new OpenAcdClientsRepresentation(variant.getMediaType(), clientsBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single Client
+ // ---------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ OpenAcdClientRepresentation representation = new OpenAcdClientRepresentation(entity);
+ OpenAcdClientRestInfo clientRestInfo = representation.getObject();
+ OpenAcdClient client;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(clientRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ client = m_openAcdContext.getClientById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing
+ try {
+ updateClient(client, clientRestInfo);
+ m_openAcdContext.saveClient(client);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Update Client failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Updated Client", client.getId());
+
+ return;
+ }
+
+
+ // otherwise add new
+ try {
+ client = createClient(clientRestInfo);
+ m_openAcdContext.saveClient(client);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Client failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Client", client.getId());
+ }
+
+
+ // DELETE - Delete single Client
+ // -----------------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ OpenAcdClient client;
+
+ // get id then delete single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ client = m_openAcdContext.getClientById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ m_openAcdContext.deleteClient(client);
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_DELETED, "Deleted Client", client.getId());
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or
+ // update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdClientRestInfo restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+
+ String identity = restInfo.getIdentity();
+
+ for (int i = 0; i < identity.length(); i++) {
+ if ((!Character.isLetterOrDigit(identity.charAt(i)) && !(Character.getType(identity.charAt(i)) == Character.CONNECTOR_PUNCTUATION)) && identity.charAt(i) != '-') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Identity' must only contain letters, numbers, dashes, and underscores";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ return validationInfo;
+ }
+
+ private OpenAcdClientRestInfo createClientRestInfo(int id) throws ResourceException {
+ OpenAcdClientRestInfo clientRestInfo;
+
+ try {
+ OpenAcdClient client = m_openAcdContext.getClientById(id);
+ clientRestInfo = new OpenAcdClientRestInfo(client);
+ }
+ catch (Exception exception) {
+ throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "ID " + id + " not found.");
+ }
+
+ return clientRestInfo;
+ }
+
+ private MetadataRestInfo addClients(List clientsRestInfo, List clients) {
+ OpenAcdClientRestInfo clientRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, clients.size());
+
+ // create list of client restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdClient client = clients.get(index);
+
+ clientRestInfo = new OpenAcdClientRestInfo(client);
+ clientsRestInfo.add(clientRestInfo);
+ }
+
+ // create metadata about results
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private void sortClients(List clients) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case NAME:
+ Collections.sort(clients, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdClient client1 = (OpenAcdClient) object1;
+ OpenAcdClient client2 = (OpenAcdClient) object2;
+ return client1.getName().compareToIgnoreCase(client2.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(clients, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdClient client1 = (OpenAcdClient) object1;
+ OpenAcdClient client2 = (OpenAcdClient) object2;
+ return client1.getDescription().compareToIgnoreCase(client2.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case NAME:
+ Collections.sort(clients, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdClient client1 = (OpenAcdClient) object1;
+ OpenAcdClient client2 = (OpenAcdClient) object2;
+ return client2.getName().compareToIgnoreCase(client1.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(clients, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdClient client1 = (OpenAcdClient) object1;
+ OpenAcdClient client2 = (OpenAcdClient) object2;
+ return client2.getDescription().compareToIgnoreCase(client1.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateClient(OpenAcdClient client, OpenAcdClientRestInfo clientRestInfo) throws ResourceException {
+ String tempString;
+
+ // do not allow empty name
+ tempString = clientRestInfo.getName();
+ if (!tempString.isEmpty()) {
+ client.setName(tempString);
+ }
+
+ client.setDescription(clientRestInfo.getDescription());
+ }
+
+ private OpenAcdClient createClient(OpenAcdClientRestInfo clientRestInfo) throws ResourceException {
+ OpenAcdClient client = new OpenAcdClient();
+
+ // copy fields from rest info
+ client.setName(clientRestInfo.getName());
+ client.setDescription(clientRestInfo.getDescription());
+ client.setIdentity(clientRestInfo.getIdentity());
+
+ return client;
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdClientsRepresentation extends XStreamRepresentation {
+
+ public OpenAcdClientsRepresentation(MediaType mediaType, OpenAcdClientsBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdClientsRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-client", OpenAcdClientsBundleRestInfo.class);
+ xstream.alias("client", OpenAcdClientRestInfo.class);
+ }
+ }
+
+ static class OpenAcdClientRepresentation extends XStreamRepresentation {
+
+ public OpenAcdClientRepresentation(MediaType mediaType, OpenAcdClientRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdClientRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("client", OpenAcdClientRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdClientsBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_clients;
+
+ public OpenAcdClientsBundleRestInfo(List clients, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_clients = clients;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getClients() {
+ return m_clients;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdDialStringsResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdDialStringsResource.java
new file mode 100644
index 0000000000..4e8342ddd4
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdDialStringsResource.java
@@ -0,0 +1,540 @@
+/*
+ *
+ * OpenAcdDialStringsResource.java - A Restlet to read DialString data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, D. Waseem
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.data.Status;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.freeswitch.FreeswitchAction;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdCommand;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class OpenAcdDialStringsResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ enum SortField {
+ NAME, DESCRIPTION, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve all and single Dial Strings
+ // ------------------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdDialStringRestInfo dialStringRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ dialStringRestInfo = createDialStringRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_READ_FAILED, "Read Settings failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdDialStringRepresentation(variant.getMediaType(), dialStringRestInfo);
+ }
+
+ // if not single, process request for list
+ List dialStrings = new ArrayList(m_openAcdContext.getCommands());
+ List dialStringsRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort groups if specified
+ sortDialStrings(dialStrings);
+
+ // set requested records and get resulting metadata
+ metadataRestInfo = addDialStrings(dialStringsRestInfo, dialStrings);
+
+ // create final restinfo
+ OpenAcdDialStringsBundleRestInfo dialStringsBundleRestInfo = new OpenAcdDialStringsBundleRestInfo(dialStringsRestInfo, metadataRestInfo);
+
+ return new OpenAcdDialStringsRepresentation(variant.getMediaType(), dialStringsBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single Dial String
+ // --------------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ OpenAcdDialStringRepresentation representation = new OpenAcdDialStringRepresentation(entity);
+ OpenAcdDialStringRestInfo dialStringRestInfo = representation.getObject();
+ OpenAcdCommand dialString = null;
+
+ ValidationInfo validationInfo = validate(dialStringRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ dialString = (OpenAcdCommand) m_openAcdContext.getExtensionById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing
+ try {
+ updateDialString(dialString, dialStringRestInfo);
+ m_openAcdContext.saveExtension(dialString);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Update Setting failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Updated Settings", dialString.getId());
+
+ return;
+ }
+
+
+ // otherwise add new
+ try {
+ dialString = createDialString(dialStringRestInfo);
+ m_openAcdContext.saveExtension(dialString);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Dial String failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Setting", dialString.getId());
+ }
+
+
+ // DELETE - Delete single Dial String
+ // ----------------------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ OpenAcdCommand dialString;
+
+ // get id then delete single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ dialString = (OpenAcdCommand) m_openAcdContext.getExtensionById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ m_openAcdContext.deleteExtension(dialString);
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_DELETED, "Deleted Client", dialString.getId());
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or
+ // update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdDialStringRestInfo restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+
+ return validationInfo;
+ }
+
+ private OpenAcdDialStringRestInfo createDialStringRestInfo(int id) throws ResourceException {
+ OpenAcdDialStringRestInfo dialStringRestInfo;
+
+ try {
+ OpenAcdCommand dialString = (OpenAcdCommand) m_openAcdContext.getExtensionById(id);
+ dialStringRestInfo = createDialStringRestInfo(dialString);
+ }
+ catch (Exception exception) {
+ throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "ID " + id + " not found.");
+ }
+
+ return dialStringRestInfo;
+ }
+
+ private OpenAcdDialStringRestInfo createDialStringRestInfo(OpenAcdCommand dialString) {
+ OpenAcdDialStringRestInfo dialStringRestInfo;
+ List dialStringActionRestInfo;
+
+ dialStringActionRestInfo = createDialStringActionRestInfo(dialString);
+ dialStringRestInfo = new OpenAcdDialStringRestInfo(dialString, dialStringActionRestInfo);
+
+ return dialStringRestInfo;
+
+ }
+
+ private List createDialStringActionRestInfo(OpenAcdCommand dialString) {
+ OpenAcdDialStringActionRestInfo dialStringActionRestInfo;
+ List customActions = new ArrayList();
+
+ List actions = dialString.getLineActions();
+
+ for (FreeswitchAction action : actions) {
+ dialStringActionRestInfo = new OpenAcdDialStringActionRestInfo(action);
+ customActions.add(dialStringActionRestInfo);
+ }
+
+ return customActions;
+ }
+
+ private MetadataRestInfo addDialStrings(List dialStringsRestInfo, List dialStrings) {
+ OpenAcdDialStringRestInfo dialStringRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, dialStrings.size());
+
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdCommand dialString = dialStrings.get(index);
+
+ dialStringRestInfo = createDialStringRestInfo(dialString);
+ dialStringsRestInfo.add(dialStringRestInfo);
+ }
+
+ // create metadata about results
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private void sortDialStrings(List dialStrings) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case NAME:
+ Collections.sort(dialStrings, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdCommand dialString1 = (OpenAcdCommand) object1;
+ OpenAcdCommand dialString2 = (OpenAcdCommand) object2;
+ return dialString1.getName().compareToIgnoreCase(dialString2.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(dialStrings, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdCommand dialString1 = (OpenAcdCommand) object1;
+ OpenAcdCommand dialString2 = (OpenAcdCommand) object2;
+ return dialString1.getDescription().compareToIgnoreCase(dialString2.getDescription());
+ }
+
+ });
+ break;
+
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case NAME:
+ Collections.sort(dialStrings, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdCommand dialString1 = (OpenAcdCommand) object1;
+ OpenAcdCommand dialString2 = (OpenAcdCommand) object2;
+ return dialString2.getName().compareToIgnoreCase(dialString1.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(dialStrings, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdCommand dialString1 = (OpenAcdCommand) object1;
+ OpenAcdCommand dialString2 = (OpenAcdCommand) object2;
+ return dialString2.getDescription().compareToIgnoreCase(dialString1.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateDialString(OpenAcdCommand dialString, OpenAcdDialStringRestInfo dialStringRestInfo) throws ResourceException {
+
+ dialString.setName(dialStringRestInfo.getName());
+ dialString.setEnabled(dialStringRestInfo.getEnabled());
+ dialString.setDescription(dialStringRestInfo.getDescription());
+ dialString.getNumberCondition().setExpression(dialStringRestInfo.getExtension());
+
+ for (OpenAcdDialStringActionRestInfo actionRestInfo : dialStringRestInfo.getActions()) {
+ dialString.getNumberCondition().addAction(OpenAcdCommand.createAction(actionRestInfo.getApplication(), actionRestInfo.getApplication()));
+ }
+ }
+
+ private OpenAcdCommand createDialString(OpenAcdDialStringRestInfo dialStringRestInfo) throws ResourceException {
+ OpenAcdCommand dialString = m_openAcdContext.newOpenAcdCommand();
+ dialString.addCondition(OpenAcdCommand.createLineCondition());
+ String tempString;
+
+ if (!(tempString = dialStringRestInfo.getName()).isEmpty()) {
+ dialString.setName(tempString);
+ }
+
+ dialString.setEnabled(dialStringRestInfo.getEnabled());
+ dialString.setDescription(dialStringRestInfo.getDescription());
+ dialString.getNumberCondition().setExpression(dialStringRestInfo.getExtension());
+
+ for (OpenAcdDialStringActionRestInfo actionRestInfo : dialStringRestInfo.getActions()) {
+ dialString.getNumberCondition().addAction(OpenAcdCommand.createAction(actionRestInfo.getApplication(), actionRestInfo.getApplication()));
+ }
+
+ return dialString;
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdDialStringsRepresentation extends XStreamRepresentation {
+
+ public OpenAcdDialStringsRepresentation(MediaType mediaType, OpenAcdDialStringsBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdDialStringsRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-dial-string", OpenAcdDialStringsBundleRestInfo.class);
+ xstream.alias("dialString", OpenAcdDialStringRestInfo.class);
+ xstream.alias("action", OpenAcdDialStringActionRestInfo.class);
+ }
+ }
+
+ static class OpenAcdDialStringRepresentation extends XStreamRepresentation {
+
+ public OpenAcdDialStringRepresentation(MediaType mediaType, OpenAcdDialStringRestInfo dialStringsBundleRestInfo) {
+ super(mediaType, dialStringsBundleRestInfo);
+ }
+
+ public OpenAcdDialStringRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("dialString", OpenAcdDialStringRestInfo.class);
+ xstream.alias("action", OpenAcdDialStringActionRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdDialStringsBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_dialStrings;
+
+ public OpenAcdDialStringsBundleRestInfo(List dialStrings, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_dialStrings = dialStrings;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getDialStrings() {
+ return m_dialStrings;
+ }
+ }
+
+ static class OpenAcdDialStringRestInfo {
+ private final int m_id;
+ private final String m_name;
+ private final boolean m_enabled;
+ private final String m_description;
+ private final String m_extension;
+ private final List m_actions;
+
+ public OpenAcdDialStringRestInfo(OpenAcdCommand dial, List dialStringActionsRestInfo) {
+ m_id = dial.getId();
+ m_name = dial.getName();
+ m_enabled = dial.isEnabled();
+ m_description = dial.getDescription();
+ m_extension = dial.getExtension();
+ m_actions = dialStringActionsRestInfo;
+ }
+
+ public int getId() {
+ return m_id;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public boolean getEnabled() {
+ return m_enabled;
+ }
+
+ public String getDescription() {
+ return m_description;
+ }
+
+ public String getExtension() {
+ return m_extension;
+ }
+
+ public List getActions() {
+ return m_actions;
+ }
+ }
+
+ static class OpenAcdDialStringActionRestInfo {
+ private final String m_application;
+ private final String m_data;
+
+ public OpenAcdDialStringActionRestInfo(FreeswitchAction action) {
+ m_application = action.getApplication();
+ m_data = action.getData();
+ }
+
+ public String getApplication() {
+ return m_application;
+ }
+
+ public String getData() {
+ return m_data;
+ }
+ }
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdLinesResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdLinesResource.java
new file mode 100644
index 0000000000..2aa5f0f349
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdLinesResource.java
@@ -0,0 +1,866 @@
+/*
+ *
+ * OpenAcdLinesResource.java - A Restlet to read Skill data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, I. Wesson, D. Chang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.commons.lang.StringUtils;
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.freeswitch.FreeswitchAction;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdClient;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdLine;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdQueue;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdClientRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdQueueRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ResponseCode;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+// OpenAcdLines are different
+// --------------------------
+// Lines is inconsistent with other OpenACD objects.
+// OpenAcdContext does not contain functions for getLineById(), saveLine() or removeLine().
+// It appears the OpenAcdExtension object is used for lines, and it has all the above functions
+// so this API will appear slightly different than other APIs, although attempts have been made to preserve general structure.
+public class OpenAcdLinesResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ enum SortField {
+ NAME, DESCRIPTION, EXTENSION, DIDNUMBER, QUEUE, CLIENT, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+
+ // GET - Retrieve all and single item
+ // ----------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdLineRestInfo lineRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ lineRestInfo = createLineRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_READ_FAILED, "Read Line failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdLineRepresentation(variant.getMediaType(), lineRestInfo);
+ }
+
+
+ // if not single, process request for list
+ List lines = new ArrayList(m_openAcdContext.getLines());
+ List linesRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort groups if specified
+ sortLines(lines);
+
+ // set requested records and get resulting metadata
+ metadataRestInfo = addLines(linesRestInfo, lines);
+
+ // create final restinfo
+ OpenAcdLinesBundleRestInfo linesBundleRestInfo = new OpenAcdLinesBundleRestInfo(linesRestInfo, metadataRestInfo);
+
+ return new OpenAcdLinesRepresentation(variant.getMediaType(), linesBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single item
+ // -------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ OpenAcdLineRepresentation representation = new OpenAcdLineRepresentation(entity);
+ OpenAcdLineRestInfo lineRestInfo = representation.getObject();
+ OpenAcdLine line = null;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(lineRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ line = (OpenAcdLine) m_openAcdContext.getExtensionById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing
+ try {
+ updateLine(line, lineRestInfo);
+ m_openAcdContext.saveExtension(line);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Update Line failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Updated Line", line.getId());
+
+ return;
+ }
+
+
+ // otherwise add new
+ try {
+ line = createLine(lineRestInfo);
+ m_openAcdContext.saveExtension(line);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Line failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Line", line.getId());
+ }
+
+
+ // DELETE - Delete single item
+ // ---------------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ OpenAcdLine line;
+
+ // get id then delete single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ line = (OpenAcdLine) m_openAcdContext.getExtensionById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ m_openAcdContext.deleteExtension(line);
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_DELETED, "Deleted Line", line.getId());
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or
+ // update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdLineRestInfo restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+
+ String name = restInfo.getName();
+ String ext = restInfo.getExtension();
+ String did = restInfo.getDIDNumber();
+ String alias = restInfo.getAlias();
+
+ if (!StringUtils.isEmpty(name)) {
+ for (int i = 0; i < name.length(); i++) {
+ if (name.charAt(i) == ' ') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Name' must only contain letters, numbers, dashes, underscores, and symbols";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+ }
+
+ if (!StringUtils.isEmpty(ext)) {
+ for (int i = 0; i < ext.length(); i++) {
+ if ((!Character.isDigit(ext.charAt(i)))) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Extension' must only contain numbers";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+ }
+
+ if (!StringUtils.isEmpty(did)) {
+ for (int i = 0; i < did.length(); i++) {
+ if ((!Character.isDigit(did.charAt(i)))) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'DID Number' must only contain numbers";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+ }
+
+ if (!StringUtils.isEmpty(alias)) {
+ for (int i = 0; i < alias.length(); i++) {
+ if ((!Character.isDigit(alias.charAt(i)))) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Alias' must only contain numbers";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+ }
+
+ return validationInfo;
+ }
+
+ // parses OpenAcdLine contents instead of just an id because line is so different
+ private OpenAcdLineActionsBundleRestInfo createLineActionsBundleRestInfo(OpenAcdLine line) {
+ OpenAcdLineActionsBundleRestInfo lineActionsBundleRestInfo;
+ OpenAcdQueueRestInfo queueRestInfo;
+ OpenAcdClientRestInfo clientRestInfo;
+ List customActions = new ArrayList();
+ OpenAcdLineActionRestInfo customActionRestInfo;
+
+ OpenAcdQueue queue;
+ String queueName = "";
+ OpenAcdClient client;
+ String clientIdentity = "";
+ Boolean allowVoicemail = false;
+ String allowVoicemailString = "false";
+ Boolean isFsSet = false;
+ Boolean isAgentSet = false;
+ String answerSupervisionType = "";
+ String welcomeMessage = "";
+
+ List actions = line.getLineActions();
+ for (FreeswitchAction action : actions) {
+ String application = action.getApplication();
+ String data = action.getData();
+
+ if (StringUtils.equals(application, FreeswitchAction.PredefinedAction.answer.toString())) {
+ isFsSet = true;
+ }
+ else if (StringUtils.contains(data, OpenAcdLine.ERLANG_ANSWER)) {
+ isAgentSet = true;
+ }
+ else if (StringUtils.contains(data, OpenAcdLine.Q)) {
+ queueName = StringUtils.removeStart(data, OpenAcdLine.Q);
+ }
+ else if (StringUtils.contains(data, OpenAcdLine.BRAND)) {
+ clientIdentity = StringUtils.removeStart(data, OpenAcdLine.BRAND);
+ }
+ else if (StringUtils.contains(data, OpenAcdLine.ALLOW_VOICEMAIL)) {
+ allowVoicemailString = StringUtils.removeStart(data, OpenAcdLine.ALLOW_VOICEMAIL);
+ }
+ else if (StringUtils.equals(application, FreeswitchAction.PredefinedAction.playback.toString())) {
+ // web ui only displays filename and appends audio directory
+ //welcomeMessage = StringUtils.removeStart(data, m_openAcdContext.getSettings().getAudioDirectory() + "/");
+ welcomeMessage = data;
+ }
+ else {
+ customActionRestInfo = new OpenAcdLineActionRestInfo(action);
+ customActions.add(customActionRestInfo);
+ }
+ }
+
+ queue = m_openAcdContext.getQueueByName(queueName);
+ queueRestInfo = new OpenAcdQueueRestInfo(queue);
+
+ client = m_openAcdContext.getClientByIdentity(clientIdentity);
+ clientRestInfo = new OpenAcdClientRestInfo(client);
+
+ allowVoicemail = Boolean.parseBoolean(allowVoicemailString);
+
+ if (isFsSet) {
+ answerSupervisionType = "FS";
+ }
+ else if (isAgentSet) {
+ answerSupervisionType = "AGENT";
+ }
+ else {
+ answerSupervisionType = "ACD";
+ }
+
+ lineActionsBundleRestInfo = new OpenAcdLineActionsBundleRestInfo(queueRestInfo, clientRestInfo, allowVoicemail, customActions, answerSupervisionType, welcomeMessage);
+
+ return lineActionsBundleRestInfo;
+ }
+
+ private OpenAcdLineRestInfo createLineRestInfo(int id) {
+ OpenAcdLineRestInfo lineRestInfo;
+
+ OpenAcdLine line = (OpenAcdLine) m_openAcdContext.getExtensionById(id);
+ lineRestInfo = createLineRestInfo(line);
+
+ return lineRestInfo;
+ }
+
+ private OpenAcdLineRestInfo createLineRestInfo(OpenAcdLine line) {
+ OpenAcdLineRestInfo lineRestInfo;
+ OpenAcdLineActionsBundleRestInfo lineActionsBundleRestInfo;
+
+ lineActionsBundleRestInfo = createLineActionsBundleRestInfo(line);
+ lineRestInfo = new OpenAcdLineRestInfo(line, lineActionsBundleRestInfo);
+
+ return lineRestInfo;
+ }
+
+ private MetadataRestInfo addLines(List linesRestInfo, List lines) {
+ OpenAcdLineRestInfo lineRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, lines.size());
+
+ // create list of line restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdLine line = lines.get(index);
+
+ lineRestInfo = createLineRestInfo(line);
+ linesRestInfo.add(lineRestInfo);
+ }
+
+ // create metadata about agent groups
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private void sortLines(List lines) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case NAME:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(line1.getName(), line2.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(line1.getDescription(), line2.getDescription());
+ }
+
+ });
+ break;
+
+ case EXTENSION:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(line1.getExtension(), line2.getExtension());
+ }
+
+ });
+ break;
+
+ case DIDNUMBER:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(line1.getDid(), line2.getDid());
+ }
+
+ });
+ break;
+
+ case QUEUE:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(createLineRestInfo(line1).m_actions.getQueue().getName(), createLineRestInfo(line2).m_actions.getQueue().getName());
+ }
+
+ });
+ break;
+
+ case CLIENT:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(createLineRestInfo(line1).m_actions.getClient().getIdentity(), createLineRestInfo(line2).m_actions.getClient().getIdentity());
+ }
+
+ });
+ break;
+
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case NAME:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(line2.getName(), line1.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(line2.getDescription(), line1.getDescription());
+ }
+
+ });
+ break;
+
+ case EXTENSION:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(line2.getExtension(), line1.getExtension());
+ }
+
+ });
+ break;
+
+ case DIDNUMBER:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(line2.getDid(), line1.getDid());
+ }
+
+ });
+ break;
+
+ case QUEUE:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(createLineRestInfo(line2).m_actions.getQueue().getName(), createLineRestInfo(line1).m_actions.getQueue().getName());
+ }
+
+ });
+ break;
+
+ case CLIENT:
+ Collections.sort(lines, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdLine line1 = (OpenAcdLine) object1;
+ OpenAcdLine line2 = (OpenAcdLine) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(createLineRestInfo(line2).m_actions.getClient().getIdentity(), createLineRestInfo(line1).m_actions.getClient().getIdentity());
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateLine(OpenAcdLine line, OpenAcdLineRestInfo lineRestInfo) throws ResourceException {
+ String tempString;
+
+ // do not allow empty name
+ tempString = lineRestInfo.getName();
+ if (!tempString.isEmpty()) {
+ line.setName(tempString);
+ }
+
+ line.setDid(lineRestInfo.getDIDNumber());
+ line.setDescription(lineRestInfo.getDescription());
+ line.setAlias(lineRestInfo.getAlias());
+
+ // set standard actions
+ line.getNumberCondition().getActions().clear();
+
+ // answer supervision type is an integer code from OpenAcdLine
+ line.getNumberCondition().addAction(OpenAcdLine.createAnswerAction(getAnswerSupervisionCode(lineRestInfo.getActions().getAnswerSupervisionType())));
+ line.getNumberCondition().addAction(OpenAcdLine.createVoicemailAction(lineRestInfo.getActions().getAllowVoicemail()));
+ line.getNumberCondition().addAction(OpenAcdLine.createQueueAction(lineRestInfo.getActions().getQueue().getName()));
+ line.getNumberCondition().addAction(OpenAcdLine.createClientAction(lineRestInfo.getActions().getClient().getIdentity()));
+
+ // web ui only displays filename and appends audio directory
+ //line.getNumberCondition().addAction(OpenAcdLine.createPlaybackAction(m_openAcdContext.getSettings().getAudioDirectory() + "/" + lineRestInfo.getActions().getWelcomeMessage()));
+ line.getNumberCondition().addAction(OpenAcdLine.createPlaybackAction(lineRestInfo.getActions().getWelcomeMessage()));
+
+ // set custom actions
+ for (OpenAcdLineActionRestInfo actionRestInfo : lineRestInfo.getActions().getCustomActions()) {
+ line.getNumberCondition().addAction(OpenAcdLine.createAction(actionRestInfo.getApplication(), actionRestInfo.getData()));
+ }
+
+ // "Expression" is the extension number, which may be a regular expression if regex is set
+ line.getNumberCondition().setExpression(lineRestInfo.getExtension());
+ line.getNumberCondition().setRegex(lineRestInfo.getRegex());
+ }
+
+ private OpenAcdLine createLine(OpenAcdLineRestInfo lineRestInfo) throws ResourceException {
+ // special steps to obtain new line (cannot just "new")
+ OpenAcdLine line = m_openAcdContext.newOpenAcdLine();
+ line.addCondition(OpenAcdLine.createLineCondition());
+
+ // copy fields from rest info
+ line.setName(lineRestInfo.getName());
+ line.setDid(lineRestInfo.getDIDNumber());
+ line.setDescription(lineRestInfo.getDescription());
+ line.setAlias(lineRestInfo.getAlias());
+
+ // set standard actions
+ line.getNumberCondition().getActions().clear();
+
+ // answer supervision type is an integer code from OpenAcdLine
+ line.getNumberCondition().addAction(OpenAcdLine.createAnswerAction(getAnswerSupervisionCode(lineRestInfo.getActions().getAnswerSupervisionType())));
+ line.getNumberCondition().addAction(OpenAcdLine.createVoicemailAction(lineRestInfo.getActions().getAllowVoicemail()));
+ line.getNumberCondition().addAction(OpenAcdLine.createQueueAction(lineRestInfo.getActions().getQueue().getName()));
+ line.getNumberCondition().addAction(OpenAcdLine.createClientAction(lineRestInfo.getActions().getClient().getIdentity()));
+
+ // web ui only displays filename and appends audio directory
+ //line.getNumberCondition().addAction(OpenAcdLine.createPlaybackAction(m_openAcdContext.getSettings().getAudioDirectory() + "/" + lineRestInfo.getActions().getWelcomeMessage()));
+ line.getNumberCondition().addAction(OpenAcdLine.createPlaybackAction(lineRestInfo.getActions().getWelcomeMessage()));
+
+ // set custom actions
+ for (OpenAcdLineActionRestInfo actionRestInfo : lineRestInfo.getActions().getCustomActions()) {
+ line.getNumberCondition().addAction(OpenAcdLine.createAction(actionRestInfo.getApplication(), actionRestInfo.getData()));
+ }
+
+ // "Expression" is the extension number, which may be a regular expression if regex is set
+ line.getNumberCondition().setExpression(lineRestInfo.getExtension());
+ line.getNumberCondition().setRegex(lineRestInfo.getRegex());
+
+ return line;
+ }
+
+ private Integer getAnswerSupervisionCode(String answerSupervisionType) {
+ Integer answerSupervisionCode;
+
+ if (StringUtils.equalsIgnoreCase(answerSupervisionType, "FS")) {
+ answerSupervisionCode = OpenAcdLine.FS;
+ }
+ else if (StringUtils.equalsIgnoreCase(answerSupervisionType, "AGENT")) {
+ answerSupervisionCode = OpenAcdLine.AGENT;
+ }
+ else {
+ answerSupervisionCode = OpenAcdLine.ACD;
+ }
+
+ return answerSupervisionCode;
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdLinesRepresentation extends XStreamRepresentation {
+
+ public OpenAcdLinesRepresentation(MediaType mediaType, OpenAcdLinesBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdLinesRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-line", OpenAcdLinesBundleRestInfo.class);
+ xstream.alias("line", OpenAcdLineRestInfo.class);
+ xstream.alias("action", OpenAcdLineActionRestInfo.class);
+ }
+ }
+
+ static class OpenAcdLineRepresentation extends XStreamRepresentation {
+
+ public OpenAcdLineRepresentation(MediaType mediaType, OpenAcdLineRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdLineRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("line", OpenAcdLineRestInfo.class);
+ xstream.alias("action", OpenAcdLineActionRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdLinesBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_lines;
+
+ public OpenAcdLinesBundleRestInfo(List lines, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_lines = lines;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getLines() {
+ return m_lines;
+ }
+ }
+
+ static class OpenAcdLineRestInfo {
+ private final int m_id;
+ private final String m_name;
+ private final String m_description;
+ private final String m_extension;
+ private final boolean m_regex;
+ private final String m_didnumber;
+ private final String m_alias;
+ private final OpenAcdLineActionsBundleRestInfo m_actions;
+
+ public OpenAcdLineRestInfo(OpenAcdLine line, OpenAcdLineActionsBundleRestInfo lineActionsRestInfo) {
+ m_id = line.getId();
+ m_name = line.getName();
+ m_description = line.getDescription();
+ m_extension = line.getExtension();
+ m_regex = line.getRegex();
+ m_didnumber = line.getDid();
+ m_alias = line.getAlias();
+ m_actions = lineActionsRestInfo;
+ }
+
+ public int getId() {
+ return m_id;
+ }
+
+ public String getName() {
+ return m_name;
+ }
+
+ public String getDescription() {
+ return m_description;
+ }
+
+ public String getExtension() {
+ return m_extension;
+ }
+
+ public boolean getRegex() {
+ return m_regex;
+ }
+
+ public String getDIDNumber() {
+ return m_didnumber;
+ }
+
+ public String getAlias() {
+ return m_alias;
+ }
+
+ public OpenAcdLineActionsBundleRestInfo getActions() {
+ return m_actions;
+ }
+ }
+
+ static class OpenAcdLineActionRestInfo {
+ private final String m_application;
+ private final String m_data;
+
+ public OpenAcdLineActionRestInfo(FreeswitchAction action) {
+ m_application = action.getApplication();
+ m_data = action.getData();
+ }
+
+ public String getApplication() {
+ return m_application;
+ }
+
+ public String getData() {
+ return m_data;
+ }
+ }
+
+ static class OpenAcdLineActionsBundleRestInfo {
+ // standard (required?) actions
+ private final OpenAcdQueueRestInfo m_queue;
+ private final OpenAcdClientRestInfo m_client;
+ private final boolean m_allowVoicemail;
+ private final String m_answerSupervisionType;
+ private final String m_welcomeMessage;
+
+ // additional (custom) actions
+ private final List m_customActions;
+
+ public OpenAcdLineActionsBundleRestInfo(OpenAcdQueueRestInfo queueRestInfo, OpenAcdClientRestInfo clientRestInfo, boolean allowVoicemail, List customActions, String answerSupervisionType, String welcomeMessage) {
+ m_queue = queueRestInfo;
+ m_client = clientRestInfo;
+ m_allowVoicemail = allowVoicemail;
+ m_customActions = customActions;
+ m_answerSupervisionType = answerSupervisionType;
+ m_welcomeMessage = welcomeMessage;
+ }
+
+ public OpenAcdQueueRestInfo getQueue() {
+ return m_queue;
+ }
+
+ public OpenAcdClientRestInfo getClient() {
+ return m_client;
+ }
+
+ public boolean getAllowVoicemail() {
+ return m_allowVoicemail;
+ }
+
+ public String getAnswerSupervisionType() {
+ return m_answerSupervisionType;
+ }
+
+ public String getWelcomeMessage() {
+ return m_welcomeMessage;
+ }
+
+ public List getCustomActions() {
+ return m_customActions;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdQueueGroupsResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdQueueGroupsResource.java
new file mode 100644
index 0000000000..b6d4917e8c
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdQueueGroupsResource.java
@@ -0,0 +1,724 @@
+/*
+ *
+ * OpenAcdQueueGroupsResource.java - A Restlet to read group data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, D. Chang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdAgentGroup;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdQueueGroup;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdRecipeAction;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdRecipeCondition;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdRecipeStep;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdSkill;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdAgentGroupRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdQueueGroupRestInfoFull;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdRecipeActionRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdRecipeConditionRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdRecipeStepRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdSkillRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ResponseCode;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class OpenAcdQueueGroupsResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ private enum SortField {
+ NAME, DESCRIPTION, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve Groups and single Group
+ // --------------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdQueueGroupRestInfoFull queueGroupRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ queueGroupRestInfo = createQueueGroupRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), ResponseCode.ERROR_READ_FAILED, "Read Queue Group failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdQueueGroupRepresentation(variant.getMediaType(), queueGroupRestInfo);
+ }
+
+
+ // if not single, process request for all
+ List queueGroups = m_openAcdContext.getQueueGroups();
+ List queueGroupsRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort if specified
+ sortQueueGroups(queueGroups);
+
+ // set requested based on pagination and get resulting metadata
+ metadataRestInfo = addQueueGroups(queueGroupsRestInfo, queueGroups);
+
+ // create final restinfo
+ OpenAcdQueueGroupsBundleRestInfo queueGroupsBundleRestInfo = new OpenAcdQueueGroupsBundleRestInfo(queueGroupsRestInfo, metadataRestInfo);
+
+ return new OpenAcdQueueGroupsRepresentation(variant.getMediaType(), queueGroupsBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single Group
+ // --------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get group from body
+ OpenAcdQueueGroupRepresentation representation = new OpenAcdQueueGroupRepresentation(entity);
+ OpenAcdQueueGroupRestInfoFull queueGroupRestInfo = representation.getObject();
+ OpenAcdQueueGroup queueGroup;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(queueGroupRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update a single group
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ queueGroup = m_openAcdContext.getQueueGroupById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing group
+ try {
+ updateQueueGroup(queueGroup, queueGroupRestInfo);
+ m_openAcdContext.saveQueueGroup(queueGroup);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), ResponseCode.ERROR_WRITE_FAILED, "Update Queue Group failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), ResponseCode.SUCCESS_UPDATED, "Updated Queue", queueGroup.getId());
+
+ return;
+ }
+
+
+ // otherwise add new agent group
+ try {
+ queueGroup = createQueueGroup(queueGroupRestInfo);
+ m_openAcdContext.saveQueueGroup(queueGroup);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Queue Group failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Queue Group", queueGroup.getId());
+ }
+
+
+ // DELETE - Delete single Group
+ // --------------------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ OpenAcdQueueGroup queueGroup;
+
+ // get id then delete a single group
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ queueGroup = m_openAcdContext.getQueueGroupById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ m_openAcdContext.deleteQueueGroup(queueGroup);
+
+ RestUtilities.setResponse(getResponse(), ResponseCode.SUCCESS_DELETED, "Deleted Queue Group", queueGroup.getId());
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdQueueGroupRestInfoFull restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+ List relation = Arrays.asList("is", "isNot");
+ List condition1 = Arrays.asList("available_agents", "eligible_agents", "calls_queued", "queue_position", "hour", "weekday", "client_calls_queued");
+ List condition2 = Arrays.asList("ticks", "client", "media_type", "caller_id", "caller_name");
+ List equalityRelation = Arrays.asList("is", "greater", "less");
+ List mediaValues = Arrays.asList("voice", "email", "voicemail", "chat");
+
+ String name = restInfo.getName();
+
+ // rest mods the hours with 24 to give a new hour, so allow over 23
+
+ for (int i = 0; i < name.length(); i++) {
+ if ((!Character.isLetterOrDigit(name.charAt(i)) && !(Character.getType(name.charAt(i)) == Character.CONNECTOR_PUNCTUATION)) && name.charAt(i) != '-') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Name' must only contain letters, numbers, dashes, and underscores";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ for (int i = 0; i < restInfo.getSteps().size(); i++) {
+ if (restInfo.getSteps().get(i).getAction().getAction().isEmpty()) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Action' cannot be empty and must only contain numbers and *";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+
+ if (restInfo.getSteps().get(i).getAction().getAction().equals("announce") || restInfo.getSteps().get(i).getAction().getAction().equals("set_priority")) {
+ if (restInfo.getSteps().get(i).getAction().getActionValue().isEmpty()) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Action Value' cannot be empty and must only contain numbers and *";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ for (int j = 0; j < m_openAcdContext.getClients().size(); j++) {
+ if (m_openAcdContext.getClients().get(j).getIdentity().equals(restInfo.getSteps().get(i).getAction().getActionValue())) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: Client Does not Exist";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ if (restInfo.getSteps().get(i).getAction().getAction().equals("set_priority")) {
+ for (int j = 0; j < restInfo.getSteps().get(i).getAction().getActionValue().length(); j++) {
+ char c = restInfo.getSteps().get(i).getAction().getActionValue().charAt(j);
+ if (!Character.isDigit(c) && c != '*') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Action Value' must only contain numbers and *";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+ }
+
+ for (int k = 0; k < restInfo.getSteps().get(i).getConditions().size(); k++) {
+ if (restInfo.getSteps().get(i).getConditions().get(k).getCondition().isEmpty() || restInfo.getSteps().get(i).getConditions().get(k).getRelation().isEmpty() || restInfo.getSteps().get(i).getConditions().get(k).getValueCondition().isEmpty()) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Condtion' cannot be empty";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+
+ }
+
+ if (condition2.contains(restInfo.getSteps().get(i).getConditions().get(k).getCondition())) {
+ if (!(relation.contains(restInfo.getSteps().get(i).getConditions().get(k).getRelation()))) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Relation' must only be 'is' or 'isNot'";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+
+ }
+
+ if (condition1.contains(restInfo.getSteps().get(i).getConditions().get(k).getCondition())) {
+ if (!(equalityRelation.contains(restInfo.getSteps().get(i).getConditions().get(k).getRelation()))) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Relation' must only be 'is', 'greater', or 'less'";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ if (restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("media_type")) {
+ if (!(mediaValues.contains(restInfo.getSteps().get(i).getConditions().get(k).getValueCondition()))) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Value Condition' can only be 'voice', 'email', 'voicemail', or 'chat'";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ if (restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("weekday")) {
+ if (Integer.parseInt(restInfo.getSteps().get(i).getConditions().get(k).getValueCondition()) < 1 || Integer.parseInt(restInfo.getSteps().get(i).getConditions().get(k).getValueCondition()) > 7) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Value Condition' must be between 1 and 7";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ if (!(restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("caller_id") || restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("caller_name") || restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("media_type") || restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("client"))) {
+ for (int j = 0; j < restInfo.getSteps().get(i).getConditions().get(k).getValueCondition().length(); j++) {
+ char c = restInfo.getSteps().get(i).getConditions().get(k).getValueCondition().charAt(j);
+ if (!Character.isDigit(c) && c != '*') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Value Condition' must only contain numbers and *";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+ }
+ }
+ }
+
+ return validationInfo;
+ }
+
+ private OpenAcdQueueGroupRestInfoFull createQueueGroupRestInfo(int id) throws ResourceException {
+ OpenAcdQueueGroupRestInfoFull queueGroupRestInfo;
+ List skillsRestInfo;
+ List agentGroupRestInfo;
+ List recipeStepRestInfo;
+
+ OpenAcdQueueGroup queueGroup = m_openAcdContext.getQueueGroupById(id);
+ skillsRestInfo = createSkillsRestInfo(queueGroup.getSkills());
+ agentGroupRestInfo = createAgentGroupsRestInfo(queueGroup);
+ recipeStepRestInfo = createRecipeStepsRestInfo(queueGroup);
+ queueGroupRestInfo = new OpenAcdQueueGroupRestInfoFull(queueGroup, skillsRestInfo, agentGroupRestInfo, recipeStepRestInfo);
+
+ return queueGroupRestInfo;
+ }
+
+ private List createSkillsRestInfo(Set groupSkills) {
+ List skillsRestInfo;
+ OpenAcdSkillRestInfo skillRestInfo;
+
+ // create list of skill restinfos for single group
+ skillsRestInfo = new ArrayList(groupSkills.size());
+
+ for (OpenAcdSkill groupSkill : groupSkills) {
+ skillRestInfo = new OpenAcdSkillRestInfo(groupSkill);
+ skillsRestInfo.add(skillRestInfo);
+ }
+
+ return skillsRestInfo;
+ }
+
+ private List createAgentGroupsRestInfo(OpenAcdQueueGroup queueGroup) {
+ List agentGroupsRestInfo;
+ OpenAcdAgentGroupRestInfo agentGroupRestInfo;
+
+ // create list of agent group restinfos for single group
+ Set groupAgentGroups = queueGroup.getAgentGroups();
+ agentGroupsRestInfo = new ArrayList(groupAgentGroups.size());
+
+ for (OpenAcdAgentGroup groupAgentGroup : groupAgentGroups) {
+ agentGroupRestInfo = new OpenAcdAgentGroupRestInfo(groupAgentGroup);
+ agentGroupsRestInfo.add(agentGroupRestInfo);
+ }
+
+ return agentGroupsRestInfo;
+ }
+
+ private List createRecipeStepsRestInfo(OpenAcdQueueGroup queueGroup) {
+ List recipeStepsRestInfo;
+ OpenAcdRecipeStepRestInfo recipeStepRestInfo;
+
+ Set groupRecipeSteps = queueGroup.getSteps();
+ recipeStepsRestInfo = new ArrayList(groupRecipeSteps.size());
+
+ for (OpenAcdRecipeStep groupRecipeStep : groupRecipeSteps) {
+ recipeStepRestInfo = new OpenAcdRecipeStepRestInfo(groupRecipeStep, createRecipeActionRestInfo(groupRecipeStep), createRecipeConditionsRestInfo(groupRecipeStep));
+ recipeStepsRestInfo.add(recipeStepRestInfo);
+ }
+ return recipeStepsRestInfo;
+ }
+
+ private OpenAcdRecipeActionRestInfo createRecipeActionRestInfo(OpenAcdRecipeStep step) {
+ OpenAcdRecipeActionRestInfo recipeActionRestInfo;
+ List skillsRestInfo;
+
+ // get skills associated with action
+ skillsRestInfo = createSkillsRestInfo(step.getAction().getSkills());
+ recipeActionRestInfo = new OpenAcdRecipeActionRestInfo(step.getAction(), skillsRestInfo);
+
+ return recipeActionRestInfo;
+ }
+
+ private List createRecipeConditionsRestInfo(OpenAcdRecipeStep step) {
+ List recipeConditionsRestInfo;
+ OpenAcdRecipeConditionRestInfo recipeConditionRestInfo;
+
+
+ List groupRecipeConditions = step.getConditions();
+ recipeConditionsRestInfo = new ArrayList(groupRecipeConditions.size());
+
+ for (OpenAcdRecipeCondition groupRecipeCondition : groupRecipeConditions) {
+ recipeConditionRestInfo = new OpenAcdRecipeConditionRestInfo(groupRecipeCondition);
+ recipeConditionsRestInfo.add(recipeConditionRestInfo);
+ }
+
+ return recipeConditionsRestInfo;
+ }
+
+ private MetadataRestInfo addQueueGroups(List queueGroupsRestInfo, List queueGroups) {
+ List skillsRestInfo;
+ List agentGroupRestInfo;
+ List recipeStepRestInfo;
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, queueGroups.size());
+
+ // create list of group restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdQueueGroup queueGroup = queueGroups.get(index);
+
+ skillsRestInfo = createSkillsRestInfo(queueGroup.getSkills());
+ agentGroupRestInfo = createAgentGroupsRestInfo(queueGroup);
+ recipeStepRestInfo = createRecipeStepsRestInfo(queueGroup);
+
+ OpenAcdQueueGroupRestInfoFull queueGroupRestInfo = new OpenAcdQueueGroupRestInfoFull(queueGroup, skillsRestInfo, agentGroupRestInfo, recipeStepRestInfo);
+ queueGroupsRestInfo.add(queueGroupRestInfo);
+ }
+
+ // create metadata about agent groups
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private void sortQueueGroups(List queueGroups) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case NAME:
+ Collections.sort(queueGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueueGroup queueGroup1 = (OpenAcdQueueGroup) object1;
+ OpenAcdQueueGroup queueGroup2 = (OpenAcdQueueGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queueGroup1.getName(), queueGroup2.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(queueGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueueGroup queueGroup1 = (OpenAcdQueueGroup) object1;
+ OpenAcdQueueGroup queueGroup2 = (OpenAcdQueueGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queueGroup1.getDescription(), queueGroup2.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case NAME:
+ Collections.sort(queueGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueueGroup queueGroup1 = (OpenAcdQueueGroup) object1;
+ OpenAcdQueueGroup queueGroup2 = (OpenAcdQueueGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queueGroup2.getName(), queueGroup1.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(queueGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueueGroup queueGroup1 = (OpenAcdQueueGroup) object1;
+ OpenAcdQueueGroup queueGroup2 = (OpenAcdQueueGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queueGroup2.getDescription(), queueGroup1.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateQueueGroup(OpenAcdQueueGroup queueGroup, OpenAcdQueueGroupRestInfoFull queueGroupRestInfo) {
+ String tempString;
+
+ // do not allow empty name
+ tempString = queueGroupRestInfo.getName();
+ if (!tempString.isEmpty()) {
+ queueGroup.setName(tempString);
+ }
+
+ queueGroup.setDescription(queueGroupRestInfo.getDescription());
+
+ addLists(queueGroup, queueGroupRestInfo);
+ }
+
+ private OpenAcdQueueGroup createQueueGroup(OpenAcdQueueGroupRestInfoFull queueGroupRestInfo) {
+ OpenAcdQueueGroup queueGroup = new OpenAcdQueueGroup();
+
+ // copy fields from rest info
+ queueGroup.setName(queueGroupRestInfo.getName());
+ queueGroup.setDescription(queueGroupRestInfo.getDescription());
+
+ addLists(queueGroup, queueGroupRestInfo);
+
+ return queueGroup;
+ }
+
+ private void addLists(OpenAcdQueueGroup queueGroup, OpenAcdQueueGroupRestInfoFull queueGroupRestInfo) {
+ // remove all skills
+ queueGroup.getSkills().clear();
+
+ // set skills
+ OpenAcdSkill skill;
+ List skillsRestInfo = queueGroupRestInfo.getSkills();
+ for (OpenAcdSkillRestInfo skillRestInfo : skillsRestInfo) {
+ skill = m_openAcdContext.getSkillById(skillRestInfo.getId());
+ queueGroup.addSkill(skill);
+ }
+
+ // remove all agent groups
+ queueGroup.getAgentGroups().clear();
+
+ // set agent groups
+ OpenAcdAgentGroup agentGroup;
+ List agentGroupsRestInfo = queueGroupRestInfo.getAgentGroups();
+ for (OpenAcdAgentGroupRestInfo agentGroupRestInfo : agentGroupsRestInfo) {
+ agentGroup = m_openAcdContext.getAgentGroupById(agentGroupRestInfo.getId());
+ queueGroup.addAgentGroup(agentGroup);
+ }
+
+ // remove all current steps
+ queueGroup.getSteps().clear();
+
+ // set steps
+ OpenAcdRecipeStep step;
+ OpenAcdRecipeCondition condition;
+ OpenAcdRecipeAction action;
+ OpenAcdRecipeActionRestInfo actionRestInfo;
+
+ List recipeStepsRestInfo = queueGroupRestInfo.getSteps();
+ for (OpenAcdRecipeStepRestInfo recipeStepRestInfo : recipeStepsRestInfo) {
+ step = new OpenAcdRecipeStep();
+ step.setFrequency(recipeStepRestInfo.getFrequency());
+
+
+ // add conditions
+ step.getConditions().clear();
+ for (OpenAcdRecipeConditionRestInfo recipeConditionRestInfo : recipeStepRestInfo.getConditions()) {
+ condition = new OpenAcdRecipeCondition();
+ condition.setCondition(recipeConditionRestInfo.getCondition());
+ condition.setRelation(recipeConditionRestInfo.getRelation());
+ condition.setValueCondition(recipeConditionRestInfo.getValueCondition());
+
+ step.addCondition(condition);
+ }
+
+ // add action
+ action = new OpenAcdRecipeAction();
+ actionRestInfo = recipeStepRestInfo.getAction();
+ action.setAction(actionRestInfo.getAction());
+ action.setActionValue(actionRestInfo.getActionValue());
+
+ // set action skills (separate from skills assigned to queue
+ for (OpenAcdSkillRestInfo skillRestInfo : actionRestInfo.getSkills()) {
+ skill = m_openAcdContext.getSkillById(skillRestInfo.getId());
+ action.addSkill(skill);
+ }
+
+ step.setAction(action);
+
+ queueGroup.addStep(step);
+ }
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdQueueGroupsRepresentation extends XStreamRepresentation {
+
+ public OpenAcdQueueGroupsRepresentation(MediaType mediaType, OpenAcdQueueGroupsBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdQueueGroupsRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-queue-group", OpenAcdQueueGroupsBundleRestInfo.class);
+ xstream.alias("group", OpenAcdQueueGroupRestInfoFull.class);
+ xstream.alias("skill", OpenAcdSkillRestInfo.class);
+ xstream.alias("agentGroup", OpenAcdAgentGroupRestInfo.class);
+ xstream.alias("step", OpenAcdRecipeStepRestInfo.class);
+ xstream.alias("condition", OpenAcdRecipeConditionRestInfo.class);
+ xstream.alias("action", OpenAcdRecipeActionRestInfo.class);
+ }
+ }
+
+ static class OpenAcdQueueGroupRepresentation extends XStreamRepresentation {
+
+ public OpenAcdQueueGroupRepresentation(MediaType mediaType, OpenAcdQueueGroupRestInfoFull object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdQueueGroupRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("group", OpenAcdQueueGroupRestInfoFull.class);
+ xstream.alias("skill", OpenAcdSkillRestInfo.class);
+ xstream.alias("agentGroup", OpenAcdAgentGroupRestInfo.class);
+ xstream.alias("step", OpenAcdRecipeStepRestInfo.class);
+ xstream.alias("condition", OpenAcdRecipeConditionRestInfo.class);
+ xstream.alias("action", OpenAcdRecipeActionRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdQueueGroupsBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_groups;
+
+ public OpenAcdQueueGroupsBundleRestInfo(List queueGroups, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_groups = queueGroups;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getGroups() {
+ return m_groups;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdQueuesResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdQueuesResource.java
new file mode 100644
index 0000000000..04e2a510fc
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdQueuesResource.java
@@ -0,0 +1,776 @@
+/*
+ *
+ * OpenAcdQueuesResource.java - A Restlet to read Skill data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, I. Wesson, D. Waseem, D. Chang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Set;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.data.Status;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdAgentGroup;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdQueue;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdQueueGroup;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdRecipeAction;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdRecipeCondition;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdRecipeStep;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdSkill;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdAgentGroupRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdQueueRestInfoFull;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdRecipeActionRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdRecipeConditionRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdRecipeStepRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdSkillRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ResponseCode;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class OpenAcdQueuesResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ enum SortField {
+ NAME, GROUPNAME, DESCRIPTION, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve all and single Queue
+ // -----------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdQueueRestInfoFull queueRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ queueRestInfo = createQueueRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), ResponseCode.ERROR_READ_FAILED, "Read Queue failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdQueueRepresentation(variant.getMediaType(), queueRestInfo);
+ }
+
+
+ // if not single, process request for list
+ List queues = m_openAcdContext.getQueues();
+ List queuesRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort groups if specified
+ sortQueues(queues);
+
+ // set requested records and get resulting metadata
+ metadataRestInfo = addQueues(queuesRestInfo, queues);
+
+ // create final restinfo
+ OpenAcdQueuesBundleRestInfo queuesBundleRestInfo = new OpenAcdQueuesBundleRestInfo(queuesRestInfo, metadataRestInfo);
+
+ return new OpenAcdQueuesRepresentation(variant.getMediaType(), queuesBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single Skill
+ // --------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ OpenAcdQueueRepresentation representation = new OpenAcdQueueRepresentation(entity);
+ OpenAcdQueueRestInfoFull queueRestInfo = representation.getObject();
+ OpenAcdQueue queue;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(queueRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ queue = m_openAcdContext.getQueueById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing
+ try {
+ updateQueue(queue, queueRestInfo);
+ m_openAcdContext.saveQueue(queue);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), ResponseCode.ERROR_WRITE_FAILED, "Update Queue failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), ResponseCode.SUCCESS_UPDATED, "Updated Queue", queue.getId());
+
+ return;
+ }
+
+
+ // otherwise add new
+ try {
+ queue = createQueue(queueRestInfo);
+ m_openAcdContext.saveQueue(queue);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), ResponseCode.ERROR_WRITE_FAILED, "Create Queue failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), ResponseCode.SUCCESS_CREATED, "Created Queue", queue.getId());
+ }
+
+
+ // DELETE - Delete single Skill
+ // ----------------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ OpenAcdQueue queue;
+
+ // get id then delete single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ queue = m_openAcdContext.getQueueById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ m_openAcdContext.deleteQueue(queue);
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_DELETED, "Deleted Queue", queue.getId());
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdQueueRestInfoFull restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+ List relation = Arrays.asList("is", "isNot");
+ List condition1 = Arrays.asList("available_agents", "eligible_agents", "calls_queued", "queue_position", "hour", "weekday", "client_calls_queued");
+ List condition2 = Arrays.asList("ticks", "client", "media_type", "caller_id", "caller_name");
+ List equalityRelation = Arrays.asList("is", "greater", "less");
+ List mediaValues = Arrays.asList("voice", "email", "voicemail", "chat");
+
+ String name = restInfo.getName();
+
+ // rest mods the hours with 24 to give a new hour, so allow over 23
+
+ for (int i = 0; i < name.length(); i++) {
+ if ((!Character.isLetterOrDigit(name.charAt(i)) && !(Character.getType(name.charAt(i)) == Character.CONNECTOR_PUNCTUATION)) && name.charAt(i) != '-') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Name' must only contain letters, numbers, dashes, and underscores";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ for (int i = 0; i < restInfo.getSteps().size(); i++) {
+ if (restInfo.getSteps().get(i).getAction().getAction().isEmpty()) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Action' cannot be empty and must only contain numbers and *";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+
+ if (restInfo.getSteps().get(i).getAction().getAction().equals("announce") || restInfo.getSteps().get(i).getAction().getAction().equals("set_priority")) {
+ if (restInfo.getSteps().get(i).getAction().getActionValue().isEmpty()) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Action Value' cannot be empty and must only contain numbers and *";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ for (int j = 0; j < m_openAcdContext.getClients().size(); j++) {
+ if (m_openAcdContext.getClients().get(j).getIdentity().equals(restInfo.getSteps().get(i).getAction().getActionValue())) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: Client Does not Exist";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ if (restInfo.getSteps().get(i).getAction().getAction().equals("set_priority")) {
+ for (int j = 0; j < restInfo.getSteps().get(i).getAction().getActionValue().length(); j++) {
+ char c = restInfo.getSteps().get(i).getAction().getActionValue().charAt(j);
+ if (!Character.isDigit(c) && c != '*') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Action Value' must only contain numbers and *";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+ }
+
+ for (int k = 0; k < restInfo.getSteps().get(i).getConditions().size(); k++) {
+ if (restInfo.getSteps().get(i).getConditions().get(k).getCondition().isEmpty() || restInfo.getSteps().get(i).getConditions().get(k).getRelation().isEmpty() || restInfo.getSteps().get(i).getConditions().get(k).getValueCondition().isEmpty()) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Condtion' cannot be empty";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+
+ }
+
+ if (condition2.contains(restInfo.getSteps().get(i).getConditions().get(k).getCondition())) {
+ if (!(relation.contains(restInfo.getSteps().get(i).getConditions().get(k).getRelation()))) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Relation' must only be 'is' or 'isNot'";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+
+ }
+
+ if (condition1.contains(restInfo.getSteps().get(i).getConditions().get(k).getCondition())) {
+ if (!(equalityRelation.contains(restInfo.getSteps().get(i).getConditions().get(k).getRelation()))) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Relation' must only be 'is', 'greater', or 'less'";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ if (restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("media_type")) {
+ if (!(mediaValues.contains(restInfo.getSteps().get(i).getConditions().get(k).getValueCondition()))) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Value Condition' can only be 'voice', 'email', 'voicemail', or 'chat'";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ if (restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("weekday")) {
+ if (Integer.parseInt(restInfo.getSteps().get(i).getConditions().get(k).getValueCondition()) < 1 || Integer.parseInt(restInfo.getSteps().get(i).getConditions().get(k).getValueCondition()) > 7) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Value Condition' must be between 1 and 7";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ if (!(restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("caller_id") || restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("caller_name") || restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("media_type") || restInfo.getSteps().get(i).getConditions().get(k).getCondition().equals("client"))) {
+ for (int j = 0; j < restInfo.getSteps().get(i).getConditions().get(k).getValueCondition().length(); j++) {
+ char c = restInfo.getSteps().get(i).getConditions().get(k).getValueCondition().charAt(j);
+ if (!Character.isDigit(c) && c != '*') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Value Condition' must only contain numbers and *";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+ }
+ }
+ }
+
+ return validationInfo;
+ }
+
+ private OpenAcdQueueRestInfoFull createQueueRestInfo(int id) throws ResourceException {
+ OpenAcdQueueRestInfoFull queueRestInfo;
+ List skillsRestInfo;
+ List agentGroupsRestInfo;
+ List recipeStepRestInfo;
+
+ OpenAcdQueue queue = m_openAcdContext.getQueueById(id);
+ skillsRestInfo = createSkillsRestInfo(queue.getSkills());
+ agentGroupsRestInfo = createAgentGroupsRestInfo(queue);
+ recipeStepRestInfo = createRecipeStepsRestInfo(queue);
+ queueRestInfo = new OpenAcdQueueRestInfoFull(queue, skillsRestInfo, agentGroupsRestInfo, recipeStepRestInfo);
+
+ return queueRestInfo;
+ }
+
+ private List createRecipeStepsRestInfo(OpenAcdQueue queue) {
+ List recipeStepsRestInfo;
+ OpenAcdRecipeStepRestInfo recipeStepRestInfo;
+
+ Set groupRecipeSteps = queue.getSteps();
+ recipeStepsRestInfo = new ArrayList(groupRecipeSteps.size());
+
+ for (OpenAcdRecipeStep groupRecipeStep : groupRecipeSteps) {
+ recipeStepRestInfo = new OpenAcdRecipeStepRestInfo(groupRecipeStep, createRecipeActionRestInfo(groupRecipeStep), createRecipeConditionsRestInfo(groupRecipeStep));
+ recipeStepsRestInfo.add(recipeStepRestInfo);
+ }
+ return recipeStepsRestInfo;
+ }
+
+ private OpenAcdRecipeActionRestInfo createRecipeActionRestInfo(OpenAcdRecipeStep step) {
+ OpenAcdRecipeActionRestInfo recipeActionRestInfo;
+ List skillsRestInfo;
+
+ // get skills associated with action
+ skillsRestInfo = createSkillsRestInfo(step.getAction().getSkills());
+ recipeActionRestInfo = new OpenAcdRecipeActionRestInfo(step.getAction(), skillsRestInfo);
+
+ return recipeActionRestInfo;
+ }
+
+ private List createRecipeConditionsRestInfo(OpenAcdRecipeStep step) {
+ List recipeConditionsRestInfo;
+ OpenAcdRecipeConditionRestInfo recipeConditionRestInfo;
+
+
+ List groupRecipeConditions = step.getConditions();
+ recipeConditionsRestInfo = new ArrayList(groupRecipeConditions.size());
+
+ for (OpenAcdRecipeCondition groupRecipeCondition : groupRecipeConditions) {
+ recipeConditionRestInfo = new OpenAcdRecipeConditionRestInfo(groupRecipeCondition);
+ recipeConditionsRestInfo.add(recipeConditionRestInfo);
+ }
+
+ return recipeConditionsRestInfo;
+ }
+
+ private List createSkillsRestInfo(Set skills) {
+ List skillsRestInfo;
+ OpenAcdSkillRestInfo skillRestInfo;
+
+ // create list of skill restinfos from set of skills
+ skillsRestInfo = new ArrayList(skills.size());
+
+ for (OpenAcdSkill skill : skills) {
+ skillRestInfo = new OpenAcdSkillRestInfo(skill);
+ skillsRestInfo.add(skillRestInfo);
+ }
+
+ return skillsRestInfo;
+ }
+
+ private List createAgentGroupsRestInfo(OpenAcdQueue queue) {
+ List agentGroupsRestInfo;
+ OpenAcdAgentGroupRestInfo agentGroupRestInfo;
+
+ // create list of agent group restinfos for single group
+ Set groupAgentGroups = queue.getAgentGroups();
+ agentGroupsRestInfo = new ArrayList(groupAgentGroups.size());
+
+ for (OpenAcdAgentGroup groupAgentGroup : groupAgentGroups) {
+ agentGroupRestInfo = new OpenAcdAgentGroupRestInfo(groupAgentGroup);
+ agentGroupsRestInfo.add(agentGroupRestInfo);
+ }
+
+ return agentGroupsRestInfo;
+ }
+
+ private MetadataRestInfo addQueues(List queuesRestInfo, List queues) {
+ OpenAcdQueueRestInfoFull queueRestInfo;
+ List skillsRestInfo;
+ List agentGroupRestInfo;
+ List recipeStepRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, queues.size());
+
+ // create list of queue restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdQueue queue = queues.get(index);
+
+ skillsRestInfo = createSkillsRestInfo(queue.getSkills());
+ agentGroupRestInfo = createAgentGroupsRestInfo(queue);
+ recipeStepRestInfo = createRecipeStepsRestInfo(queue);
+ queueRestInfo = new OpenAcdQueueRestInfoFull(queue, skillsRestInfo, agentGroupRestInfo, recipeStepRestInfo);
+ queuesRestInfo.add(queueRestInfo);
+ }
+
+ // create metadata about agent groups
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private void sortQueues(List queues) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case GROUPNAME:
+ Collections.sort(queues, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueue queue1 = (OpenAcdQueue) object1;
+ OpenAcdQueue queue2 = (OpenAcdQueue) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queue1.getGroup().getName(), queue2.getGroup().getName());
+ }
+
+ });
+ break;
+
+ case NAME:
+ Collections.sort(queues, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueue queue1 = (OpenAcdQueue) object1;
+ OpenAcdQueue queue2 = (OpenAcdQueue) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queue1.getName(), queue2.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(queues, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueue queue1 = (OpenAcdQueue) object1;
+ OpenAcdQueue queue2 = (OpenAcdQueue) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queue1.getDescription(), queue2.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case GROUPNAME:
+ Collections.sort(queues, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueue queue1 = (OpenAcdQueue) object1;
+ OpenAcdQueue queue2 = (OpenAcdQueue) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queue2.getGroup().getName(), queue1.getGroup().getName());
+ }
+
+ });
+ break;
+
+ case NAME:
+ Collections.sort(queues, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueue queue1 = (OpenAcdQueue) object1;
+ OpenAcdQueue queue2 = (OpenAcdQueue) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queue2.getName(), queue1.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(queues, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdQueue queue1 = (OpenAcdQueue) object1;
+ OpenAcdQueue queue2 = (OpenAcdQueue) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(queue2.getDescription(), queue1.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateQueue(OpenAcdQueue queue, OpenAcdQueueRestInfoFull queueRestInfo) throws ResourceException {
+ String tempString;
+ OpenAcdQueueGroup queueGroup;
+
+ // do not allow empty name
+ tempString = queueRestInfo.getName();
+ if (!tempString.isEmpty()) {
+ queue.setName(tempString);
+ }
+ queueGroup = getQueueGroup(queueRestInfo);
+
+ queue.setGroup(queueGroup);
+ queue.setDescription(queueRestInfo.getDescription());
+ queue.setWeight(queueRestInfo.getWeight());
+
+ addLists(queue, queueRestInfo);
+ }
+
+ private OpenAcdQueue createQueue(OpenAcdQueueRestInfoFull queueRestInfo) throws ResourceException {
+ OpenAcdQueue queue = new OpenAcdQueue();
+ OpenAcdQueueGroup queueGroup;
+
+ // copy fields from rest info
+ queue.setName(queueRestInfo.getName());
+ queueGroup = getQueueGroup(queueRestInfo);
+
+ queue.setGroup(queueGroup);
+ queue.setDescription(queueRestInfo.getDescription());
+ queue.setWeight(queueRestInfo.getWeight());
+
+ addLists(queue, queueRestInfo);
+
+ return queue;
+ }
+
+ private void addLists(OpenAcdQueue queue, OpenAcdQueueRestInfoFull queueRestInfo) {
+ // remove all skills
+ queue.getSkills().clear();
+
+ // set skills
+ OpenAcdSkill skill;
+ List skillsRestInfo = queueRestInfo.getSkills();
+ for (OpenAcdSkillRestInfo skillRestInfo : skillsRestInfo) {
+ skill = m_openAcdContext.getSkillById(skillRestInfo.getId());
+ queue.addSkill(skill);
+ }
+
+ // remove all agent groups
+ queue.getAgentGroups().clear();
+
+ // set agent groups
+ OpenAcdAgentGroup agentGroup;
+ List agentGroupsRestInfo = queueRestInfo.getAgentGroups();
+ for (OpenAcdAgentGroupRestInfo agentGroupRestInfo : agentGroupsRestInfo) {
+ agentGroup = m_openAcdContext.getAgentGroupById(agentGroupRestInfo.getId());
+ queue.addAgentGroup(agentGroup);
+ }
+
+ // remove all current steps
+ queue.getSteps().clear();
+
+ // set steps
+ OpenAcdRecipeStep step;
+ OpenAcdRecipeCondition condition;
+ OpenAcdRecipeAction action;
+ OpenAcdRecipeActionRestInfo actionRestInfo;
+
+ List recipeStepsRestInfo = queueRestInfo.getSteps();
+ for (OpenAcdRecipeStepRestInfo recipeStepRestInfo : recipeStepsRestInfo) {
+ step = new OpenAcdRecipeStep();
+ step.setFrequency(recipeStepRestInfo.getFrequency());
+
+
+ // add conditions
+ step.getConditions().clear();
+ for (OpenAcdRecipeConditionRestInfo recipeConditionRestInfo : recipeStepRestInfo.getConditions()) {
+ condition = new OpenAcdRecipeCondition();
+ condition.setCondition(recipeConditionRestInfo.getCondition());
+ condition.setRelation(recipeConditionRestInfo.getRelation());
+ condition.setValueCondition(recipeConditionRestInfo.getValueCondition());
+
+ step.addCondition(condition);
+ }
+
+ // add action
+ action = new OpenAcdRecipeAction();
+ actionRestInfo = recipeStepRestInfo.getAction();
+ action.setAction(actionRestInfo.getAction());
+ action.setActionValue(actionRestInfo.getActionValue());
+
+ // set action skills (separate from skills assigned to queue
+ for (OpenAcdSkillRestInfo skillRestInfo : actionRestInfo.getSkills()) {
+ skill = m_openAcdContext.getSkillById(skillRestInfo.getId());
+ action.addSkill(skill);
+ }
+
+ step.setAction(action);
+
+ queue.addStep(step);
+ }
+
+ }
+
+ private OpenAcdQueueGroup getQueueGroup(OpenAcdQueueRestInfoFull queueRestInfo) throws ResourceException {
+ OpenAcdQueueGroup queueGroup;
+ int groupId = 0;
+
+ try {
+ groupId = queueRestInfo.getGroupId();
+ queueGroup = m_openAcdContext.getQueueGroupById(groupId);
+ }
+ catch (Exception exception) {
+ throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "Queue Group ID " + groupId + " not found.");
+ }
+
+ return queueGroup;
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdQueuesRepresentation extends XStreamRepresentation {
+
+ public OpenAcdQueuesRepresentation(MediaType mediaType, OpenAcdQueuesBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdQueuesRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-queue", OpenAcdQueuesBundleRestInfo.class);
+ xstream.alias("queue", OpenAcdQueueRestInfoFull.class);
+ xstream.alias("skill", OpenAcdSkillRestInfo.class);
+ xstream.alias("agentGroup", OpenAcdAgentGroupRestInfo.class);
+ xstream.alias("step", OpenAcdRecipeStepRestInfo.class);
+ xstream.alias("condition", OpenAcdRecipeConditionRestInfo.class);
+ xstream.alias("action", OpenAcdRecipeActionRestInfo.class);
+ }
+ }
+
+ static class OpenAcdQueueRepresentation extends XStreamRepresentation {
+
+ public OpenAcdQueueRepresentation(MediaType mediaType, OpenAcdQueueRestInfoFull object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdQueueRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("queue", OpenAcdQueueRestInfoFull.class);
+ xstream.alias("skill", OpenAcdSkillRestInfo.class);
+ xstream.alias("agentGroup", OpenAcdAgentGroupRestInfo.class);
+ xstream.alias("step", OpenAcdRecipeStepRestInfo.class);
+ xstream.alias("condition", OpenAcdRecipeConditionRestInfo.class);
+ xstream.alias("action", OpenAcdRecipeActionRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdQueuesBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_queues;
+
+ public OpenAcdQueuesBundleRestInfo(List queues, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_queues = queues;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getQueues() {
+ return m_queues;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdReleaseCodesResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdReleaseCodesResource.java
new file mode 100644
index 0000000000..441621654c
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdReleaseCodesResource.java
@@ -0,0 +1,465 @@
+/*
+ *
+ * OpenAcdReleaseCodesResource.java - A Restlet to read Release Code data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, D. Waseem
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.data.Status;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdReleaseCode;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdReleaseCodeRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ResponseCode;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class OpenAcdReleaseCodesResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ enum SortField {
+ LABEL, DESCRIPTION, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve all and single Release Code
+ // -------------------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdReleaseCodeRestInfo releaseCodeRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ releaseCodeRestInfo = createReleaseCodeRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_READ_FAILED, "Read Release Code failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdReleaseCodeRepresentation(variant.getMediaType(), releaseCodeRestInfo);
+ }
+
+
+ // if not single, process request for list
+ List releaseCodes = m_openAcdContext.getReleaseCodes();
+ List releaseCodesRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort groups if specified
+ sortReleaseCodes(releaseCodes);
+
+ // set requested records and get resulting metadata
+ metadataRestInfo = addReleaseCodes(releaseCodesRestInfo, releaseCodes);
+
+ // create final restinfo
+ OpenAcdReleaseCodesBundleRestInfo releaseCodesBundleRestInfo = new OpenAcdReleaseCodesBundleRestInfo(releaseCodesRestInfo, metadataRestInfo);
+
+ return new OpenAcdReleaseCodesRepresentation(variant.getMediaType(), releaseCodesBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single Skill
+ // --------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ OpenAcdReleaseCodeRepresentation representation = new OpenAcdReleaseCodeRepresentation(entity);
+ OpenAcdReleaseCodeRestInfo releaseCodeRestInfo = representation.getObject();
+ OpenAcdReleaseCode releaseCode;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(releaseCodeRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ releaseCode = m_openAcdContext.getReleaseCodeById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing
+ try {
+ updateReleaseCode(releaseCode, releaseCodeRestInfo);
+ m_openAcdContext.saveReleaseCode(releaseCode);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Update Release Code failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Updated Release Code", releaseCode.getId());
+
+ return;
+ }
+
+
+ // otherwise add new
+ try {
+ releaseCode = createReleaseCode(releaseCodeRestInfo);
+ m_openAcdContext.saveReleaseCode(releaseCode);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Release Code failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Release Code", releaseCode.getId());
+ }
+
+
+ // DELETE - Delete single Skill
+ // ----------------------------
+
+ // deleteReleaseCode() not available from openAcdContext
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ // for some reason release codes are deleted by providing collection of ids, not by providing release code object
+ Collection releaseCodeIds = new HashSet();
+
+ // get id then delete single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ releaseCodeIds.add(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ m_openAcdContext.removeReleaseCodes(releaseCodeIds);
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdReleaseCodeRestInfo restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+
+ // release code object will allow store of bias other value than -1, 0, or 1,
+ // but then current SipXconfig administrative UI will display nothing for bias name.
+ int bias = restInfo.getBias();
+ if ((bias < -1) || (bias > 1)) {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: Bias must be be -1, 0 or 1";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+
+ return validationInfo;
+ }
+
+ return validationInfo;
+ }
+
+ private OpenAcdReleaseCodeRestInfo createReleaseCodeRestInfo(int id) throws ResourceException {
+ OpenAcdReleaseCodeRestInfo releaseCodeRestInfo;
+
+ try {
+ OpenAcdReleaseCode releaseCode = m_openAcdContext.getReleaseCodeById(id);
+ releaseCodeRestInfo = new OpenAcdReleaseCodeRestInfo(releaseCode);
+ }
+ catch (Exception exception) {
+ throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "ID " + id + " not found.");
+ }
+
+ return releaseCodeRestInfo;
+ }
+
+ private MetadataRestInfo addReleaseCodes(List releaseCodesRestInfo, List releaseCodes) {
+ OpenAcdReleaseCodeRestInfo releaseCodeRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, releaseCodes.size());
+
+ // create list of releaseCode restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdReleaseCode releaseCode = releaseCodes.get(index);
+
+ releaseCodeRestInfo = new OpenAcdReleaseCodeRestInfo(releaseCode);
+ releaseCodesRestInfo.add(releaseCodeRestInfo);
+ }
+
+ // create metadata about agent groups
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private void sortReleaseCodes(List releaseCodes) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case LABEL:
+ Collections.sort(releaseCodes, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdReleaseCode releaseCode1 = (OpenAcdReleaseCode) object1;
+ OpenAcdReleaseCode releaseCode2 = (OpenAcdReleaseCode) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(releaseCode1.getLabel(), releaseCode2.getLabel());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(releaseCodes, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdReleaseCode releaseCode1 = (OpenAcdReleaseCode) object1;
+ OpenAcdReleaseCode releaseCode2 = (OpenAcdReleaseCode) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(releaseCode1.getDescription(), releaseCode2.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case LABEL:
+ Collections.sort(releaseCodes, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdReleaseCode releaseCode1 = (OpenAcdReleaseCode) object1;
+ OpenAcdReleaseCode releaseCode2 = (OpenAcdReleaseCode) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(releaseCode2.getLabel(), releaseCode1.getLabel());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(releaseCodes, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdReleaseCode releaseCode1 = (OpenAcdReleaseCode) object1;
+ OpenAcdReleaseCode releaseCode2 = (OpenAcdReleaseCode) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(releaseCode2.getDescription(), releaseCode1.getDescription());
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateReleaseCode(OpenAcdReleaseCode releaseCode, OpenAcdReleaseCodeRestInfo releaseCodeRestInfo) throws ResourceException {
+ String tempString;
+
+ // do not allow empty name
+ tempString = releaseCodeRestInfo.getLabel();
+ if (!tempString.isEmpty()) {
+ releaseCode.setName(tempString);
+ }
+
+ releaseCode.setLabel(releaseCodeRestInfo.getLabel());
+ releaseCode.setDescription(releaseCodeRestInfo.getDescription());
+ releaseCode.setBias(releaseCodeRestInfo.getBias());
+ }
+
+ private OpenAcdReleaseCode createReleaseCode(OpenAcdReleaseCodeRestInfo releaseCodeRestInfo) throws ResourceException {
+ OpenAcdReleaseCode releaseCode = new OpenAcdReleaseCode();
+
+ // copy fields from rest info
+ releaseCode.setLabel(releaseCodeRestInfo.getLabel());
+ releaseCode.setDescription(releaseCodeRestInfo.getDescription());
+ releaseCode.setBias(releaseCodeRestInfo.getBias());
+
+ return releaseCode;
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdReleaseCodesRepresentation extends XStreamRepresentation {
+
+ public OpenAcdReleaseCodesRepresentation(MediaType mediaType, OpenAcdReleaseCodesBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdReleaseCodesRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-release-code", OpenAcdReleaseCodesBundleRestInfo.class);
+ xstream.alias("release-code", OpenAcdReleaseCodeRestInfo.class);
+ }
+ }
+
+ static class OpenAcdReleaseCodeRepresentation extends XStreamRepresentation {
+
+ public OpenAcdReleaseCodeRepresentation(MediaType mediaType, OpenAcdReleaseCodeRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdReleaseCodeRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("release-code", OpenAcdReleaseCodeRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdReleaseCodesBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_releaseCodes;
+
+ public OpenAcdReleaseCodesBundleRestInfo(List releaseCodes, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_releaseCodes = releaseCodes;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getReleaseCodes() {
+ return m_releaseCodes;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdSettingsResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdSettingsResource.java
new file mode 100644
index 0000000000..c9191d7ae0
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdSettingsResource.java
@@ -0,0 +1,160 @@
+/*
+ *
+ * OpenAcdSettingsResource.java - A Restlet to read Skill data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, D. Waseem
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import org.restlet.Context;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdSettings;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class OpenAcdSettingsResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+
+ // GET - Retrieve all and single Client
+ // -----------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // if not single, process request for list
+ OpenAcdSettings settings = m_openAcdContext.getSettings();
+ OpenAcdSettingRestInfo settingsRestInfo = new OpenAcdSettingRestInfo(settings);
+
+ return new OpenAcdSettingRepresentation(variant.getMediaType(), settingsRestInfo);
+ }
+
+
+ // PUT - Update single Setting
+ // ---------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ OpenAcdSettingRepresentation representation = new OpenAcdSettingRepresentation(entity);
+ OpenAcdSettingRestInfo settingRestInfo = representation.getObject();
+ OpenAcdSettings settings;
+
+ // assign new setting
+ try {
+ settings = m_openAcdContext.getSettings();
+ updateSettings(settings, settingRestInfo);
+ m_openAcdContext.saveSettings(settings);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Assign Setting failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Assigned Setting", settings.getId());
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ private void updateSettings(OpenAcdSettings settings, OpenAcdSettingRestInfo settingRestInfo) throws ResourceException {
+ settings.setSettingValue("openacd-config/log_level", settingRestInfo.getLogLevel());
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdSettingRepresentation extends XStreamRepresentation {
+
+ public OpenAcdSettingRepresentation(MediaType mediaType, OpenAcdSettingRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdSettingRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("setting", OpenAcdSettingRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdSettingRestInfo {
+ private final int m_id;
+ private final String m_logLevel;
+
+ public OpenAcdSettingRestInfo(OpenAcdSettings setting) {
+ m_id = setting.getId();
+ m_logLevel = setting.getLogLevel();
+ }
+
+ public int getId() {
+ return m_id;
+ }
+
+ public String getLogLevel() {
+ return m_logLevel;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdSkillGroupsResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdSkillGroupsResource.java
new file mode 100644
index 0000000000..d18a4f4c7a
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdSkillGroupsResource.java
@@ -0,0 +1,501 @@
+/*
+ *
+ * OpenAcdAgentGroupsResource.java - A Restlet to read Skill data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, D. Chang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.data.Status;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdSkill;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdSkillGroup;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdSkillGroupRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ResponseCode;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class OpenAcdSkillGroupsResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ private enum SortField {
+ NAME, DESCRIPTION, NUMBERSKILLS, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve all and single Skill
+ // -----------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdSkillGroupRestInfo skillGroupRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ skillGroupRestInfo = createSkillGroupRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_READ_FAILED, "Read Skill Group failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdSkillGroupRepresentation(variant.getMediaType(), skillGroupRestInfo);
+ }
+
+
+ // if not single, process request for list
+ List skillGroups = m_openAcdContext.getSkillGroups();
+ List skillGroupsRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort groups if specified
+ sortSkillGroups(skillGroups);
+
+ // set requested records and get resulting metadata
+ metadataRestInfo = addSkillGroups(skillGroupsRestInfo, skillGroups);
+
+ // create final restinfo
+ OpenAcdSkillGroupsBundleRestInfo skillGroupsBundleRestInfo = new OpenAcdSkillGroupsBundleRestInfo(skillGroupsRestInfo, metadataRestInfo);
+
+ return new OpenAcdSkillGroupsRepresentation(variant.getMediaType(), skillGroupsBundleRestInfo);
+ }
+
+
+ // PUT - Update or Create single
+ // -----------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ OpenAcdSkillGroupRepresentation representation = new OpenAcdSkillGroupRepresentation(entity);
+ OpenAcdSkillGroupRestInfo skillGroupRestInfo = representation.getObject();
+ OpenAcdSkillGroup skillGroup;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(skillGroupRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ skillGroup = m_openAcdContext.getSkillGroupById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing
+ try {
+ updateSkillGroup(skillGroup, skillGroupRestInfo);
+ m_openAcdContext.saveSkillGroup(skillGroup);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Update Skill Group failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Updated Skill Group", skillGroup.getId());
+
+ return;
+ }
+
+
+ // otherwise add new
+ try {
+ skillGroup = createSkillGroup(skillGroupRestInfo);
+ m_openAcdContext.saveSkillGroup(skillGroup);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Skill Group failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Skill Group", skillGroup.getId());
+ }
+
+
+ // DELETE - Delete single
+ // ----------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ OpenAcdSkillGroupRestInfo skillGroupRestInfo;
+ List skills;
+
+ // skill groups are deleted by providing collection of ids, not by providing skill group object
+ Collection skillGroupIds = new HashSet();
+ int idInt;
+
+ // get id then delete single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ skillGroupIds.add(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // sipxconfig ui does not allow delete of group with existing skills
+ skills = m_openAcdContext.getSkills();
+ for (OpenAcdSkill skill : skills) {
+ if (skill.getGroup().getId() == idInt) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "Skill " + skill.getName() + " still refers to this group.");
+ return;
+ }
+ }
+
+ m_openAcdContext.removeSkillGroups(skillGroupIds);
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_DELETED, "Deleted Skill Group", idInt);
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or
+ // update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdSkillGroupRestInfo restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+
+ String name = restInfo.getName();
+
+ for (int i = 0; i < name.length(); i++) {
+ if ((!Character.isLetterOrDigit(name.charAt(i)) && !(Character.getType(name.charAt(i)) == Character.CONNECTOR_PUNCTUATION)) && name.charAt(i) != '-') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: Skill group name must only contain letters, numbers, dashes, and underscores";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ return validationInfo;
+ }
+
+ private OpenAcdSkillGroupRestInfo createSkillGroupRestInfo(int id) throws ResourceException {
+ OpenAcdSkillGroupRestInfo skillGroupRestInfo;
+
+ try {
+ OpenAcdSkillGroup skillGroup = m_openAcdContext.getSkillGroupById(id);
+ skillGroupRestInfo = new OpenAcdSkillGroupRestInfo(skillGroup);
+ }
+ catch (Exception exception) {
+ throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND, "ID " + id + " not found.");
+ }
+
+ return skillGroupRestInfo;
+ }
+
+ private MetadataRestInfo addSkillGroups(List skillsRestInfo, List skillGroups) {
+ OpenAcdSkillGroupRestInfo skillRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, skillGroups.size());
+
+ // create list of skill restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdSkillGroup skillGroup = skillGroups.get(index);
+
+ skillRestInfo = new OpenAcdSkillGroupRestInfo(skillGroup);
+ skillsRestInfo.add(skillRestInfo);
+ }
+
+ // create metadata about agent groups
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private void sortSkillGroups(List skillGroups) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case NAME:
+ Collections.sort(skillGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkillGroup skillGroup1 = (OpenAcdSkillGroup) object1;
+ OpenAcdSkillGroup skillGroup2 = (OpenAcdSkillGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skillGroup1.getName(), skillGroup2.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(skillGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkillGroup skillGroup1 = (OpenAcdSkillGroup) object1;
+ OpenAcdSkillGroup skillGroup2 = (OpenAcdSkillGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skillGroup1.getDescription(), skillGroup2.getDescription());
+ }
+
+ });
+ break;
+
+ case NUMBERSKILLS:
+ Collections.sort(skillGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkillGroup skillGroup1 = (OpenAcdSkillGroup) object1;
+ OpenAcdSkillGroup skillGroup2 = (OpenAcdSkillGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(String.valueOf(skillGroup1.getSkills().size()), String.valueOf(skillGroup2.getSkills().size()));
+ }
+
+ });
+ break;
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case NAME:
+ Collections.sort(skillGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkillGroup skillGroup1 = (OpenAcdSkillGroup) object1;
+ OpenAcdSkillGroup skillGroup2 = (OpenAcdSkillGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skillGroup2.getName(), skillGroup1.getName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(skillGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkillGroup skillGroup1 = (OpenAcdSkillGroup) object1;
+ OpenAcdSkillGroup skillGroup2 = (OpenAcdSkillGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skillGroup2.getDescription(), skillGroup1.getDescription());
+ }
+
+ });
+ break;
+
+ case NUMBERSKILLS:
+ Collections.sort(skillGroups, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkillGroup skillGroup1 = (OpenAcdSkillGroup) object1;
+ OpenAcdSkillGroup skillGroup2 = (OpenAcdSkillGroup) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(String.valueOf(skillGroup2.getSkills().size()), String.valueOf(skillGroup1.getSkills().size()));
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateSkillGroup(OpenAcdSkillGroup skillGroup, OpenAcdSkillGroupRestInfo skillGroupRestInfo) throws ResourceException {
+ String tempString;
+
+ // do not allow empty name
+ tempString = skillGroupRestInfo.getName();
+ if (!tempString.isEmpty()) {
+ skillGroup.setName(tempString);
+ }
+
+ skillGroup.setDescription(skillGroupRestInfo.getDescription());
+ }
+
+ private OpenAcdSkillGroup createSkillGroup(OpenAcdSkillGroupRestInfo skillGroupRestInfo) throws ResourceException {
+ OpenAcdSkillGroup skillGroup = new OpenAcdSkillGroup();
+
+ // copy fields from rest info
+ skillGroup.setName(skillGroupRestInfo.getName());
+ skillGroup.setDescription(skillGroupRestInfo.getDescription());
+
+ return skillGroup;
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdSkillGroupsRepresentation extends XStreamRepresentation {
+
+ public OpenAcdSkillGroupsRepresentation(MediaType mediaType, OpenAcdSkillGroupsBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdSkillGroupsRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-skill-group", OpenAcdSkillGroupsBundleRestInfo.class);
+ xstream.alias("group", OpenAcdSkillGroupRestInfo.class);
+ }
+ }
+
+ static class OpenAcdSkillGroupRepresentation extends XStreamRepresentation {
+
+ public OpenAcdSkillGroupRepresentation(MediaType mediaType, OpenAcdSkillGroupRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdSkillGroupRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("group", OpenAcdSkillGroupRestInfo.class);
+ }
+ }
+
+
+ // REST info objects
+ // -----------------
+
+ static class OpenAcdSkillGroupsBundleRestInfo {
+ private final MetadataRestInfo m_metadata;
+ private final List m_groups;
+
+ public OpenAcdSkillGroupsBundleRestInfo(List skillGroups, MetadataRestInfo metadata) {
+ m_metadata = metadata;
+ m_groups = skillGroups;
+ }
+
+ public MetadataRestInfo getMetadata() {
+ return m_metadata;
+ }
+
+ public List getGroups() {
+ return m_groups;
+ }
+ }
+
+
+ // Injected objects
+ // ----------------
+
+ @Required
+ public void setOpenAcdContext(OpenAcdContext openAcdContext) {
+ m_openAcdContext = openAcdContext;
+ }
+
+}
diff --git a/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdSkillsResource.java b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdSkillsResource.java
new file mode 100644
index 0000000000..ee532028d3
--- /dev/null
+++ b/sipXconfig/web/src/org/sipfoundry/sipxconfig/rest/OpenAcdSkillsResource.java
@@ -0,0 +1,530 @@
+/*
+ *
+ * OpenAcdAgentGroupsResource.java - A Restlet to read Skill data from OpenACD within SipXecs
+ * Copyright (C) 2012 PATLive, D. Chang
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.sipfoundry.sipxconfig.rest;
+
+import static org.restlet.data.MediaType.APPLICATION_JSON;
+import static org.restlet.data.MediaType.TEXT_XML;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.restlet.Context;
+import org.restlet.data.Form;
+import org.restlet.data.MediaType;
+import org.restlet.data.Request;
+import org.restlet.data.Response;
+import org.restlet.resource.Representation;
+import org.restlet.resource.ResourceException;
+import org.restlet.resource.Variant;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdContext;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdSkill;
+import org.sipfoundry.sipxconfig.openacd.OpenAcdSkillGroup;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.MetadataRestInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.OpenAcdSkillRestInfoFull;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.PaginationInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ResponseCode;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.SortInfo;
+import org.sipfoundry.sipxconfig.rest.RestUtilities.ValidationInfo;
+import org.springframework.beans.factory.annotation.Required;
+
+import com.thoughtworks.xstream.XStream;
+
+public class OpenAcdSkillsResource extends UserResource {
+
+ private OpenAcdContext m_openAcdContext;
+ private Form m_form;
+
+ // use to define all possible sort fields
+ private enum SortField {
+ NAME, DESCRIPTION, ATOM, GROUPNAME, NONE;
+
+ public static SortField toSortField(String fieldString) {
+ if (fieldString == null) {
+ return NONE;
+ }
+
+ try {
+ return valueOf(fieldString.toUpperCase());
+ }
+ catch (Exception ex) {
+ return NONE;
+ }
+ }
+ }
+
+
+ @Override
+ public void init(Context context, Request request, Response response) {
+ super.init(context, request, response);
+ getVariants().add(new Variant(TEXT_XML));
+ getVariants().add(new Variant(APPLICATION_JSON));
+
+ // pull parameters from url
+ m_form = getRequest().getResourceRef().getQueryAsForm();
+ }
+
+
+ // Allowed REST operations
+ // -----------------------
+
+ @Override
+ public boolean allowGet() {
+ return true;
+ }
+
+ @Override
+ public boolean allowPut() {
+ return true;
+ }
+
+ @Override
+ public boolean allowDelete() {
+ return true;
+ }
+
+ // GET - Retrieve all and single Skill
+ // -----------------------------------
+
+ @Override
+ public Representation represent(Variant variant) throws ResourceException {
+ // process request for single
+ int idInt;
+ OpenAcdSkillRestInfoFull skillRestInfo = null;
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ idInt = RestUtilities.getIntFromAttribute(idString);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ }
+
+ try {
+ skillRestInfo = createSkillRestInfo(idInt);
+ }
+ catch (Exception exception) {
+ return RestUtilities.getResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_READ_FAILED, "Read Skills failed", exception.getLocalizedMessage());
+ }
+
+ return new OpenAcdSkillRepresentation(variant.getMediaType(), skillRestInfo);
+ }
+
+
+ // if not single, process request for all
+ List skills = m_openAcdContext.getSkills();
+ List skillsRestInfo = new ArrayList();
+ MetadataRestInfo metadataRestInfo;
+
+ // sort groups if specified
+ sortSkills(skills);
+
+ // set requested agents groups and get resulting metadata
+ metadataRestInfo = addSkills(skillsRestInfo, skills);
+
+ // create final restinfo
+ OpenAcdSkillsBundleRestInfo skillsBundleRestInfo = new OpenAcdSkillsBundleRestInfo(skillsRestInfo, metadataRestInfo);
+
+ return new OpenAcdSkillsRepresentation(variant.getMediaType(), skillsBundleRestInfo);
+ }
+
+
+ // PUT - Update or Add single Skill
+ // --------------------------------
+
+ @Override
+ public void storeRepresentation(Representation entity) throws ResourceException {
+ // get from request body
+ OpenAcdSkillRepresentation representation = new OpenAcdSkillRepresentation(entity);
+ OpenAcdSkillRestInfoFull skillRestInfo = representation.getObject();
+ OpenAcdSkill skill = null;
+
+ // validate input for update or create
+ ValidationInfo validationInfo = validate(skillRestInfo);
+
+ if (!validationInfo.valid) {
+ RestUtilities.setResponseError(getResponse(), validationInfo.responseCode, validationInfo.message);
+ return;
+ }
+
+
+ // if have id then update single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ skill = m_openAcdContext.getSkillById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ // copy values over to existing
+ try {
+ updateSkill(skill, skillRestInfo);
+ m_openAcdContext.saveSkill(skill);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Update Skill failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_UPDATED, "Updated Skill", skill.getId());
+
+ return;
+ }
+
+
+ // otherwise add new
+ try {
+ skill = createSkill(skillRestInfo);
+ m_openAcdContext.saveSkill(skill);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_WRITE_FAILED, "Create Skill failed", exception.getLocalizedMessage());
+ return;
+ }
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_CREATED, "Created Skill", skill.getId());
+ }
+
+
+ // DELETE - Delete single Skill
+ // ----------------------------
+
+ @Override
+ public void removeRepresentations() throws ResourceException {
+ OpenAcdSkill skill;
+
+ // get id then delete single
+ String idString = (String) getRequest().getAttributes().get("id");
+
+ if (idString != null) {
+ try {
+ int idInt = RestUtilities.getIntFromAttribute(idString);
+ skill = m_openAcdContext.getSkillById(idInt);
+ }
+ catch (Exception exception) {
+ RestUtilities.setResponseError(getResponse(), RestUtilities.ResponseCode.ERROR_BAD_INPUT, "ID " + idString + " not found.");
+ return;
+ }
+
+ m_openAcdContext.deleteSkill(skill);
+
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.SUCCESS_DELETED, "Deleted Skill", skill.getId());
+
+ return;
+ }
+
+ // no id string
+ RestUtilities.setResponse(getResponse(), RestUtilities.ResponseCode.ERROR_MISSING_INPUT, "ID value missing");
+ }
+
+
+ // Helper functions
+ // ----------------
+
+ // basic interface level validation of data provided through REST interface for creation or
+ // update
+ // may also contain clean up of input data
+ // may create another validation function if different rules needed for update v. create
+ private ValidationInfo validate(OpenAcdSkillRestInfoFull restInfo) {
+ ValidationInfo validationInfo = new ValidationInfo();
+
+ String name = restInfo.getName();
+ String atom = restInfo.getAtom();
+
+ for (int i = 0; i < name.length(); i++) {
+ if ((!Character.isLetterOrDigit(name.charAt(i)) && !(Character.getType(name.charAt(i)) == Character.CONNECTOR_PUNCTUATION)) && name.charAt(i) != '-') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: Skill Group 'Name' must only contain letters, numbers, dashes, and underscores";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ for (int i = 0; i < atom.length(); i++) {
+ if ((!Character.isLetterOrDigit(atom.charAt(i)) && !(Character.getType(atom.charAt(i)) == Character.CONNECTOR_PUNCTUATION)) && atom.charAt(i) != '-') {
+ validationInfo.valid = false;
+ validationInfo.message = "Validation Error: 'Atom' must only contain letters, numbers, dashes, and underscores";
+ validationInfo.responseCode = ResponseCode.ERROR_BAD_INPUT;
+ }
+ }
+
+ return validationInfo;
+ }
+
+ private OpenAcdSkillRestInfoFull createSkillRestInfo(int id) throws ResourceException {
+ OpenAcdSkillRestInfoFull skillRestInfo = null;
+
+ OpenAcdSkill skill = m_openAcdContext.getSkillById(id);
+ skillRestInfo = new OpenAcdSkillRestInfoFull(skill);
+
+ return skillRestInfo;
+ }
+
+ private MetadataRestInfo addSkills(List skillsRestInfo, List skills) {
+ OpenAcdSkillRestInfoFull skillRestInfo;
+
+ // determine pagination
+ PaginationInfo paginationInfo = RestUtilities.calculatePagination(m_form, skills.size());
+
+ // create list of skill restinfos
+ for (int index = paginationInfo.startIndex; index <= paginationInfo.endIndex; index++) {
+ OpenAcdSkill skill = skills.get(index);
+
+ skillRestInfo = new OpenAcdSkillRestInfoFull(skill);
+ skillsRestInfo.add(skillRestInfo);
+ }
+
+ // create metadata about agent groups
+ MetadataRestInfo metadata = new MetadataRestInfo(paginationInfo);
+ return metadata;
+ }
+
+ private void sortSkills(List skills) {
+ // sort groups if requested
+ SortInfo sortInfo = RestUtilities.calculateSorting(m_form);
+
+ if (!sortInfo.sort) {
+ return;
+ }
+
+ SortField sortField = SortField.toSortField(sortInfo.sortField);
+
+ if (sortInfo.directionForward) {
+
+ switch (sortField) {
+ case NAME:
+ Collections.sort(skills, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkill skill1 = (OpenAcdSkill) object1;
+ OpenAcdSkill skill2 = (OpenAcdSkill) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skill1.getName(), skill2.getName());
+ }
+
+ });
+ break;
+
+ case GROUPNAME:
+ Collections.sort(skills, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkill skill1 = (OpenAcdSkill) object1;
+ OpenAcdSkill skill2 = (OpenAcdSkill) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skill1.getGroupName(), skill2.getGroupName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(skills, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkill skill1 = (OpenAcdSkill) object1;
+ OpenAcdSkill skill2 = (OpenAcdSkill) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skill1.getDescription(), skill2.getDescription());
+ }
+
+ });
+ break;
+
+
+ case ATOM:
+ Collections.sort(skills, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkill skill1 = (OpenAcdSkill) object1;
+ OpenAcdSkill skill2 = (OpenAcdSkill) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skill1.getAtom(), skill2.getAtom());
+ }
+
+ });
+ break;
+ }
+ }
+ else {
+ // must be reverse
+ switch (sortField) {
+ case NAME:
+ Collections.sort(skills, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkill skill1 = (OpenAcdSkill) object1;
+ OpenAcdSkill skill2 = (OpenAcdSkill) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skill2.getName(), skill1.getName());
+ }
+
+ });
+ break;
+
+ case GROUPNAME:
+ Collections.sort(skills, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkill skill1 = (OpenAcdSkill) object1;
+ OpenAcdSkill skill2 = (OpenAcdSkill) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skill2.getGroupName(), skill1.getGroupName());
+ }
+
+ });
+ break;
+
+ case DESCRIPTION:
+ Collections.sort(skills, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkill skill1 = (OpenAcdSkill) object1;
+ OpenAcdSkill skill2 = (OpenAcdSkill) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skill2.getDescription(), skill1.getDescription());
+ }
+
+ });
+ break;
+
+ case ATOM:
+ Collections.sort(skills, new Comparator() {
+
+ public int compare(Object object1, Object object2) {
+ OpenAcdSkill skill1 = (OpenAcdSkill) object1;
+ OpenAcdSkill skill2 = (OpenAcdSkill) object2;
+ return RestUtilities.compareIgnoreCaseNullSafe(skill2.getAtom(), skill1.getAtom());
+ }
+
+ });
+ break;
+ }
+ }
+ }
+
+ private void updateSkill(OpenAcdSkill skill, OpenAcdSkillRestInfoFull skillRestInfo) {
+ OpenAcdSkillGroup skillGroup;
+ String tempString;
+
+ // do not allow empty name
+ tempString = skillRestInfo.getName();
+ if (!tempString.isEmpty()) {
+ skill.setName(tempString);
+ }
+
+ skill.setDescription(skillRestInfo.getDescription());
+
+ skillGroup = getSkillGroup(skillRestInfo);
+ skill.setGroup(skillGroup);
+ }
+
+ private OpenAcdSkill createSkill(OpenAcdSkillRestInfoFull skillRestInfo) throws ResourceException {
+ OpenAcdSkillGroup skillGroup;
+ OpenAcdSkill skill = new OpenAcdSkill();
+
+ // copy fields from rest info
+ skill.setName(skillRestInfo.getName());
+ skill.setDescription(skillRestInfo.getDescription());
+ skill.setAtom(skillRestInfo.getAtom());
+
+ skillGroup = getSkillGroup(skillRestInfo);
+ skill.setGroup(skillGroup);
+
+ return skill;
+ }
+
+ private OpenAcdSkillGroup getSkillGroup(OpenAcdSkillRestInfoFull skillRestInfo) {
+ OpenAcdSkillGroup skillGroup;
+ int groupId = skillRestInfo.getGroupId();
+ skillGroup = m_openAcdContext.getSkillGroupById(groupId);
+
+ return skillGroup;
+ }
+
+
+ // REST Representations
+ // --------------------
+
+ static class OpenAcdSkillsRepresentation extends XStreamRepresentation {
+
+ public OpenAcdSkillsRepresentation(MediaType mediaType, OpenAcdSkillsBundleRestInfo object) {
+ super(mediaType, object);
+ }
+
+ public OpenAcdSkillsRepresentation(Representation representation) {
+ super(representation);
+ }
+
+ @Override
+ protected void configureXStream(XStream xstream) {
+ xstream.alias("openacd-skill", OpenAcdSkillsBundleRestInfo.class);
+ xstream.alias("skill", OpenAcdSkillRestInfoFull.class);
+ }
+ }
+
+ static class OpenAcdSkillRepresentation extends XStreamRepresentation