Thomas Vachuska
Committed by Gerrit Code Review

Removing a very old CORD GUI demo app.

Change-Id: I1039d5acc145f51f930dee28c5895b9593070628
Showing 91 changed files with 0 additions and 6666 deletions
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.onosproject</groupId>
<artifactId>onos-base</artifactId>
<version>1</version>
<relativePath>../../../tools/build/pom.xml</relativePath>
</parent>
<artifactId>cord-gui</artifactId>
<version>1.8.0-SNAPSHOT</version>
<packaging>war</packaging>
<description>Demo CORD Subscriber Web GUI</description>
<properties>
<web.context>/cord</web.context>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-client</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- TODO: update once following issue is fixed. -->
<!-- https://jira.codehaus.org/browse/MCOMPILER-205 -->
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import org.onosproject.cord.gui.model.Bundle;
import org.onosproject.cord.gui.model.BundleDescriptor;
import org.onosproject.cord.gui.model.BundleFactory;
import org.onosproject.cord.gui.model.JsonFactory;
import org.onosproject.cord.gui.model.SubscriberUser;
import org.onosproject.cord.gui.model.UserFactory;
import org.onosproject.cord.gui.model.XosFunction;
import org.onosproject.cord.gui.model.XosFunctionDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.cord.gui.model.XosFunctionDescriptor.URL_FILTER;
/**
* In memory cache of the model of the subscriber's account.
*/
public class CordModelCache extends JsonFactory {
private static final String KEY_SSID_MAP = "ssidmap";
private static final String KEY_SSID = "service_specific_id";
private static final String KEY_SUB_ID = "subscriber_id";
private static final int DEMO_SSID = 1234;
private static final String EMAIL_0 = "john@smith.org";
private static final String EMAIL_1 = "john@doe.org";
private static final String EMAIL = "email";
private static final String SSID = "ssid";
private static final String SUB_ID = "subId";
private static final String BUNDLE = "bundle";
private static final String USERS = "users";
private static final String LEVEL = "level";
private static final String LOGOUT = "logout";
private static final String BUNDLE_NAME = BUNDLE + "_name";
private static final String BUNDLE_DESC = BUNDLE + "_desc";
private static final Map<Integer, Integer> LOOKUP = new HashMap<>();
private String email = null;
private int subscriberId;
private int ssid;
private Bundle currentBundle;
private final Logger log = LoggerFactory.getLogger(getClass());
// NOTE: use a tree map to maintain sorted order by user ID
private final Map<Integer, SubscriberUser> userMap =
new TreeMap<Integer, SubscriberUser>();
/**
* Constructs a model cache, retrieving a mapping of SSID to XOS Subscriber
* IDs from the XOS server.
*/
CordModelCache() {
log.info("Initialize model cache");
ObjectNode map = XosManager.INSTANCE.initXosSubscriberLookups();
initLookupMap(map);
log.info("{} entries in SSID->SubID lookup map", LOOKUP.size());
}
private void initLookupMap(ObjectNode map) {
ArrayNode array = (ArrayNode) map.get(KEY_SSID_MAP);
Iterator<JsonNode> iter = array.elements();
StringBuilder msg = new StringBuilder();
while (iter.hasNext()) {
ObjectNode node = (ObjectNode) iter.next();
String ssidStr = node.get(KEY_SSID).asText();
int ssid = Integer.valueOf(ssidStr);
int subId = node.get(KEY_SUB_ID).asInt();
LOOKUP.put(ssid, subId);
msg.append(String.format("\n..binding SSID %s to sub-id %s", ssid, subId));
}
log.info(msg.toString());
}
private int lookupSubId(int ssid) {
Integer subId = LOOKUP.get(ssid);
if (subId == null) {
log.error("Unmapped SSID: {}", ssid);
return 0;
}
return subId;
}
/**
* Initializes the model for the subscriber account associated with
* the given email address.
*
* @param email the email address
*/
void init(String email) {
// defaults to the demo account
int ssid = DEMO_SSID;
this.email = email;
// obviously not scalable, but good enough for demo code...
if (EMAIL_0.equals(email)) {
ssid = 0;
} else if (EMAIL_1.equals(email)) {
ssid = 1;
}
this.ssid = ssid;
subscriberId = lookupSubId(ssid);
XosManager.INSTANCE.setXosUtilsForSubscriber(subscriberId);
// call the initdemo API to ensure users are populated in XOS
XosManager.INSTANCE.initDemoSubscriber();
// NOTE: I think the following should work for non-DEMO account...
currentBundle = new Bundle(BundleFactory.BASIC_BUNDLE);
initUsers();
}
private void initUsers() {
// start with a clean slate
userMap.clear();
ArrayNode users = XosManager.INSTANCE.getUserList();
if (users == null) {
log.warn("no user list for SSID {} (subid {})", ssid, subscriberId);
return;
}
StringBuilder sb = new StringBuilder();
for (JsonNode u: users) {
ObjectNode user = (ObjectNode) u;
int id = user.get("id").asInt();
String name = user.get("name").asText();
String mac = user.get("mac").asText();
String level = user.get("level").asText();
// NOTE: We are just storing the current "url-filter" level.
// Since we are starting with the BASIC bundle, (that does
// not include URL_FILTER), we don't yet have the URL_FILTER
// memento in which to store the level.
SubscriberUser su = createUser(id, name, mac, level);
userMap.put(id, su);
sb.append(String.format("\n..cache user %s [%d], %s, %s",
name, id, mac, level));
}
log.info(sb.toString());
}
private SubscriberUser createUser(int uid, String name, String mac,
String level) {
SubscriberUser user = new SubscriberUser(uid, name, mac, level);
for (XosFunction f: currentBundle.functions()) {
user.setMemento(f.descriptor(), f.createMemento());
}
return user;
}
/**
* Returns the currently selected bundle.
*
* @return current bundle
*/
public Bundle getCurrentBundle() {
return currentBundle;
}
/**
* Sets a new bundle.
*
* @param bundleId bundle identifier
* @throws IllegalArgumentException if bundle ID is unknown
*/
public void setCurrentBundle(String bundleId) {
log.info("set new bundle : {}", bundleId);
BundleDescriptor bd = BundleFactory.bundleFromId(bundleId);
currentBundle = new Bundle(bd);
// update the user mementos
for (SubscriberUser user: userMap.values()) {
user.clearMementos();
for (XosFunction f: currentBundle.functions()) {
user.setMemento(f.descriptor(), f.createMemento());
if (f.descriptor().equals(URL_FILTER)) {
applyUrlFilterLevel(user, user.urlFilterLevel());
}
}
}
XosManager.INSTANCE.setNewBundle(currentBundle);
}
/**
* Returns the list of current users for this subscriber account.
*
* @return the list of users
*/
public List<SubscriberUser> getUsers() {
return ImmutableList.copyOf(userMap.values());
}
/**
* Applies a function parameter change for a user, pushing that
* change through to XOS.
*
* @param userId user identifier
* @param funcId function identifier
* @param param function parameter to change
* @param value new value for function parameter
*/
public void applyPerUserParam(String userId, String funcId,
String param, String value) {
int uid = Integer.parseInt(userId);
SubscriberUser user = userMap.get(uid);
checkNotNull(user, "unknown user id: " + uid);
XosFunctionDescriptor xfd =
XosFunctionDescriptor.valueOf(funcId.toUpperCase());
XosFunction func = currentBundle.findFunction(xfd);
checkNotNull(func, "function not part of bundle: " + funcId);
applyParam(func, user, param, value, true);
}
// =============
private void applyUrlFilterLevel(SubscriberUser user, String level) {
XosFunction urlFilter = currentBundle.findFunction(URL_FILTER);
if (urlFilter != null) {
applyParam(urlFilter, user, LEVEL, level, false);
}
}
private void applyParam(XosFunction func, SubscriberUser user,
String param, String value, boolean punchThrough) {
func.applyParam(user, param, value);
if (punchThrough) {
XosManager.INSTANCE.apply(func, user);
}
}
private ArrayNode userJsonArray() {
ArrayNode userList = arrayNode();
for (SubscriberUser user: userMap.values()) {
userList.add(UserFactory.toObjectNode(user));
}
return userList;
}
// ============= generate JSON for GUI rest calls..
private void addSubId(ObjectNode root) {
root.put(SUB_ID, subscriberId);
root.put(SSID, ssid);
root.put(EMAIL, email);
}
/**
* Returns response JSON for login request.
* <p>
* Depending on which email is used, will bind the GUI to the
* appropriate XOS Subscriber ID.
*
* @param email the supplied email
* @return JSON acknowledgement
*/
public synchronized String jsonLogin(String email) {
log.info("jsonLogin(\"{}\")", email);
init(email);
ObjectNode root = objectNode();
addSubId(root);
return root.toString();
}
/**
* Returns the dashboard page data as JSON.
*
* @return dashboard page JSON data
*/
public synchronized String jsonDashboard() {
log.info("jsonDashboard()");
if (email == null) {
return jsonLogout();
}
BundleDescriptor bundleDescriptor = currentBundle.descriptor();
ObjectNode root = objectNode();
root.put(BUNDLE_NAME, bundleDescriptor.displayName());
root.put(BUNDLE_DESC, bundleDescriptor.description());
root.set(USERS, userJsonArray());
addSubId(root);
return root.toString();
}
/**
* Returns the bundle page data as JSON.
*
* @return bundle page JSON data
*/
public synchronized String jsonBundle() {
log.info("jsonBundle()");
if (email == null) {
return jsonLogout();
}
ObjectNode root = BundleFactory.toObjectNode(currentBundle);
addSubId(root);
return root.toString();
}
/**
* Returns the users page data as JSON.
*
* @return users page JSON data
*/
public synchronized String jsonUsers() {
log.info("jsonUsers()");
if (email == null) {
return jsonLogout();
}
ObjectNode root = objectNode();
root.set(USERS, userJsonArray());
addSubId(root);
return root.toString();
}
/**
* Returns logout acknowledgement as JSON.
*
* @return logout acknowledgement
*/
public synchronized String jsonLogout() {
log.info("jsonLogout()");
ObjectNode root = objectNode().put(LOGOUT, true);
addSubId(root);
email = null; // signifies no one logged in
return root.toString();
}
/**
* Singleton instance.
*/
public static final CordModelCache INSTANCE = new CordModelCache();
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.cord.gui;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* Web resource to use as the GUI back-end and as a proxy to XOS REST API.
*/
@Path("")
public class CordWebResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("login/{email}")
public Response login(@PathParam("email") String email) {
return Response.ok(CordModelCache.INSTANCE.jsonLogin(email)).build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("dashboard")
public Response dashboard() {
return Response.ok(CordModelCache.INSTANCE.jsonDashboard()).build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("bundle")
public Response bundle() {
return Response.ok(CordModelCache.INSTANCE.jsonBundle()).build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("users")
public Response users() {
return Response.ok(CordModelCache.INSTANCE.jsonUsers()).build();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("logout")
public Response logout() {
return Response.ok(CordModelCache.INSTANCE.jsonLogout()).build();
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("bundle/{id}")
public Response bundle(@PathParam("id") String bundleId) {
CordModelCache.INSTANCE.setCurrentBundle(bundleId);
return bundle();
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("users/{id}/apply/{func}/{param}/{value}")
public Response bundle(@PathParam("id") String userId,
@PathParam("func") String funcId,
@PathParam("param") String param,
@PathParam("value") String value) {
CordModelCache.INSTANCE.applyPerUserParam(userId, funcId, param, value);
return users();
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.io.InputStream;
/**
* Provides support for fake data.
*/
public class FakeUtils {
private static final ClassLoader CL = FakeUtils.class.getClassLoader();
private static final String ROOT_PATH = "/org/onosproject/cord/gui/";
private static final String UTF_8 = "UTF-8";
/**
* Returns the contents of a local file as a string.
*
* @param path file path name
* @return contents of file as a string
*/
public static String slurp(String path) {
String result = null;
InputStream is = CL.getResourceAsStream(ROOT_PATH + path);
if (is != null) {
try {
result = IOUtils.toString(is, UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.onosproject.cord.gui.model.Bundle;
import org.onosproject.cord.gui.model.SubscriberUser;
import org.onosproject.cord.gui.model.XosFunction;
import org.onosproject.cord.gui.model.XosFunctionDescriptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Set;
/**
* Encapsulation of interactions with XOS.
*/
public class XosManager {
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String HEAD_NODE_IP = "headnodeip";
private static final String HEAD_NODE_PORT = "headnodeport";
private static final int PORT_MIN = 1025;
private static final int PORT_MAX = 65535;
private static final String TEST_XOS_SERVER_IP = "10.254.1.22";
private static final String TEST_XOS_SERVER_PORT_STR = "8000";
private static final int TEST_XOS_SERVER_PORT = 8000;
private static final String URI_RS = "/rs/";
private static final String URI_SUBSCRIBER = "/rs/subscriber/%d/";
private static final String BUNDLE_URI_FORMAT = "services/%s/%s/";
private String xosServerIp;
private int xosServerPort;
private XosManagerRestUtils xosUtilsRs;
private XosManagerRestUtils xosUtils;
private final Logger log = LoggerFactory.getLogger(getClass());
/**
* No instantiation (except via unit test).
*/
XosManager() {}
private String getXosServerIp() {
return System.getProperty(HEAD_NODE_IP, TEST_XOS_SERVER_IP);
}
private int getXosServerPort() {
String p = System.getProperty(HEAD_NODE_PORT, TEST_XOS_SERVER_PORT_STR);
int port;
try {
port = Integer.valueOf(p);
} catch (NumberFormatException e) {
port = TEST_XOS_SERVER_PORT;
log.warn("Could not parse port number [{}], using {}", p, port);
}
if (port < PORT_MIN || port > PORT_MAX) {
log.warn("Bad port number [{}], using {}", port, TEST_XOS_SERVER_PORT);
port = TEST_XOS_SERVER_PORT;
}
return port;
}
/**
* Queries XOS for the Subscriber ID lookup data, and returns it.
*/
public ObjectNode initXosSubscriberLookups() {
log.info("intDemoSubscriberLookups() called");
xosServerIp = getXosServerIp();
xosServerPort = getXosServerPort();
log.info("Using XOS server at {}:{}", xosServerIp, xosServerPort);
xosUtilsRs = new XosManagerRestUtils(xosServerIp, xosServerPort, URI_RS);
// ask XOS for the subscriber ID lookup info
String result = xosUtilsRs.getRest("subidlookup/");
log.info("lookup data from XOS: {}", result);
JsonNode node;
try {
node = MAPPER.readTree(result);
} catch (IOException e) {
log.error("failed to read subscriber lookup JSON data", e);
return null;
}
return (ObjectNode) node;
}
/**
* Sets a new XOS utils object to bind URL patterns for the
* given XOS subscriber ID.
*
* @param xosSubId XOS subscriber ID
*/
public void setXosUtilsForSubscriber(int xosSubId) {
String uri = String.format(URI_SUBSCRIBER, xosSubId);
xosUtils = new XosManagerRestUtils(xosServerIp, xosServerPort, uri);
}
public void initDemoSubscriber() {
log.info("initDemoSubscriber() called");
String result = xosUtilsRs.getRest("initdemo/");
log.info("initdemo data from XOS: {}", result);
}
/**
* Returns the array of users for the subscriber.
*
* @return list of users
*/
public ArrayNode getUserList() {
log.info("getUserList() called");
String result = xosUtils.getRest("users/");
JsonNode node;
try {
node = MAPPER.readTree(result);
} catch (IOException e) {
log.error("failed to read user list JSON", e);
return null;
}
ObjectNode obj = (ObjectNode) node;
return (ArrayNode) obj.get("users");
}
/**
* Configure XOS to enable the functions that compose the given bundle,
* and disable all the others, for the given subscriber.
*
* @param bundle new bundle to set
*/
public void setNewBundle(Bundle bundle) {
log.info(">> Set New Bundle : {}", bundle.descriptor().id());
Set<XosFunctionDescriptor> inBundle = bundle.descriptor().functions();
for (XosFunctionDescriptor xfd: XosFunctionDescriptor.values()) {
// only process the functions that have a real back-end on XOS
if (xfd.backend()) {
String uri = String.format(BUNDLE_URI_FORMAT, xfd.id(),
inBundle.contains(xfd));
log.info("XOS-URI: {}", uri);
String result = xosUtils.putRest(uri);
// TODO: convert JSON result to object and check (if we care)
}
}
}
/**
* Configure XOS with new setting for given user and function, for the
* given subscriber account.
*
* @param func specific XOS function
* @param user user (containing function state)
*/
public void apply(XosFunction func, SubscriberUser user) {
log.info(">> Apply : {} for {}", func, user);
String uriPrefix = "users/" + user.id() + "/";
String uri = uriPrefix + func.xosUrlApply(user);
log.info("XOS-URI: {}", uri);
String result = xosUtils.putRest(uri);
// TODO: convert JSON result to object and check (if we care)
}
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/**
* Singleton instance.
*/
public static final XosManager INSTANCE = new XosManager();
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
import org.slf4j.Logger;
import static com.google.common.net.MediaType.JSON_UTF_8;
import static java.net.HttpURLConnection.*;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Utility RESTful methods for dealing with the XOS server.
*/
public class XosManagerRestUtils {
private static final String XOSLIB = "/xoslib";
private static final String AUTH_USER = "padmin@vicci.org";
private static final String AUTH_PASS = "letmein";
private static final String UTF_8 = JSON_UTF_8.toString();
private final Logger log = getLogger(getClass());
private final String xosServerAddress;
private final int xosServerPort;
private final String baseUri;
/**
* Constructs a utility class, using the supplied server address and port,
* using the given base URI.
* <p>
* Note that the uri should start and end with a slash; for example:
* {@code "/volttenant/"}. This example would result in URIs of the form:
* <pre>
* "http://[server]:[port]/xoslib/volttenant/"
* </pre>
*
* @param xosServerAddress server IP address
* @param xosServerPort server port
* @param baseUri base URI
*/
public XosManagerRestUtils(String xosServerAddress, int xosServerPort,
String baseUri) {
this.xosServerAddress = xosServerAddress;
this.xosServerPort = xosServerPort;
this.baseUri = baseUri;
log.info("XMRU:: {}:{}{}", xosServerAddress, xosServerPort, baseUri);
}
// build the base URL from the pieces we know...
private String baseUrl() {
return "http://" + xosServerAddress + ":" +
Integer.toString(xosServerPort) + XOSLIB + baseUri;
}
/**
* Gets a client web resource builder for the base XOS REST API
* with no additional URI.
*
* @return web resource builder
*/
public WebResource.Builder getClientBuilder() {
return getClientBuilder("");
}
/**
* Gets a client web resource builder for the base XOS REST API
* with an optional additional URI.
*
* @param uri URI suffix to append to base URI
* @return web resource builder
*/
public WebResource.Builder getClientBuilder(String uri) {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter(AUTH_USER, AUTH_PASS));
WebResource resource = client.resource(baseUrl() + uri);
log.info("XOS REST CALL>> {}", resource);
return resource.accept(UTF_8).type(UTF_8);
}
/**
* Performs a REST GET operation on the base XOS REST URI.
*
* @return JSON string fetched by the GET operation
*/
public String getRest() {
return getRest("");
}
/**
* Performs a REST GET operation on the base XOS REST URI with
* an optional additional URI.
*
* @param uri URI suffix to append to base URI
* @return JSON string fetched by the GET operation
*/
public String getRest(String uri) {
WebResource.Builder builder = getClientBuilder(uri);
ClientResponse response = builder.get(ClientResponse.class);
if (response.getStatus() != HTTP_OK) {
log.info("REST GET request returned error code {}",
response.getStatus());
}
return response.getEntity(String.class);
}
/**
* Performs a REST PUT operation on the base XOS REST URI.
*
* @return JSON string returned by the PUT operation
*/
public String putRest() {
return putRest("");
}
/**
* Performs a REST PUT operation on the base XOS REST URI with
* an optional additional URI.
*
* @param uri URI suffix to append to base URI
* @return JSON string returned by the PUT operation
*/
public String putRest(String uri) {
WebResource.Builder builder = getClientBuilder(uri);
ClientResponse response;
try {
response = builder.put(ClientResponse.class);
} catch (ClientHandlerException e) {
log.warn("Unable to contact REST server: {}", e.getMessage());
return "";
}
if (response.getStatus() != HTTP_OK) {
log.info("REST PUT request returned error code {}",
response.getStatus());
}
return response.getEntity(String.class);
}
/**
* Performs a REST POST operation of a json string on the base
* XOS REST URI with an optional additional URI.
*
* @param json JSON string to post
*/
public void postRest(String json) {
postRest("", json);
}
/**
* Performs a REST POST operation of a json string on the base
* XOS REST URI with an optional additional URI suffix.
*
* @param uri URI suffix to append to base URI
* @param json JSON string to post
*/
public void postRest(String uri, String json) {
WebResource.Builder builder = getClientBuilder(uri);
ClientResponse response;
try {
response = builder.post(ClientResponse.class, json);
} catch (ClientHandlerException e) {
log.warn("Unable to contact REST server: {}", e.getMessage());
return;
}
if (response.getStatus() != HTTP_CREATED) {
log.info("REST POST request returned error code {}",
response.getStatus());
}
}
/**
* Performs a REST DELETE operation on the base
* XOS REST URI with an optional additional URI.
*
* @param uri URI suffix to append to base URI
*/
public void deleteRest(String uri) {
WebResource.Builder builder = getClientBuilder(uri);
ClientResponse response = builder.delete(ClientResponse.class);
if (response.getStatus() != HTTP_NO_CONTENT) {
log.info("REST DELETE request returned error code {}",
response.getStatus());
}
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import com.google.common.collect.ImmutableSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* Encapsulates a bundle, including current state.
*/
public class Bundle {
private final BundleDescriptor bundleDescriptor;
private final Map<XosFunctionDescriptor, XosFunction> functionMap =
new HashMap<XosFunctionDescriptor, XosFunction>();
/**
* Constructs a new bundle instance.
*
* @param bundleDescriptor the descriptor
*/
public Bundle(BundleDescriptor bundleDescriptor) {
this.bundleDescriptor = bundleDescriptor;
initFunctions();
}
/**
* Returns the bundle descriptor.
*
* @return the descriptor
*/
public BundleDescriptor descriptor() {
return bundleDescriptor;
}
/**
* Returns the set of function instances for this bundle.
*
* @return the functions
*/
public Set<XosFunction> functions() {
return ImmutableSet.copyOf(functionMap.values());
}
/**
* Creates an initial set of function instances.
*/
private void initFunctions() {
for (XosFunctionDescriptor xfd: bundleDescriptor.functions()) {
functionMap.put(xfd, createFunction(xfd));
}
}
private XosFunction createFunction(XosFunctionDescriptor xfd) {
XosFunction func;
switch (xfd) {
case URL_FILTER:
func = new UrlFilterFunction();
break;
default:
func = new DefaultXosFunction(xfd);
break;
}
return func;
}
/**
* Returns the function instance for the specified descriptor, or returns
* null if function is not part of this bundle.
*
* @param xfd function descrriptor
* @return function instance
*/
public XosFunction findFunction(XosFunctionDescriptor xfd) {
return functionMap.get(xfd);
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import java.util.Set;
/**
* Defines a bundle of {@link XosFunctionDescriptor XOS functions}.
*/
public interface BundleDescriptor {
/**
* Bundle internal identifier.
*
* @return bundle identifier
*/
String id();
/**
* Bundle display name.
*
* @return display name
*/
String displayName();
/**
* Textual description of this bundle.
*
* @return description
*/
String description();
/**
* The set of functions in this bundle instance.
*
* @return the functions
*/
Set<XosFunctionDescriptor> functions();
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableList;
import java.util.List;
/**
* Utility factory for creating and/or operating on bundles.
*/
public class BundleFactory extends JsonFactory {
private static final String BUNDLE = "bundle";
private static final String BUNDLES = "bundles";
private static final String FUNCTIONS = "functions";
private static final String BASIC_ID = "basic";
private static final String BASIC_DISPLAY_NAME = "Basic Bundle";
private static final String BASIC_DESCRIPTION =
"If the thing that matters most to you is high speed Internet" +
" connectivity delivered at a great price, then the basic" +
" bundle is right for you.\n" +
"Starting at $30 a month for 12 months.";
private static final String FAMILY_ID = "family";
private static final String FAMILY_DISPLAY_NAME = "Family Bundle";
private static final String FAMILY_DESCRIPTION =
"Enjoy great entertainment, peace of mind and big savings when " +
"you bundle high speed Internet and Firewall with" +
" Parental Control.\n" +
"Starting at $40 a month for 12 months.";
// no instantiation
private BundleFactory() {}
/**
* Designates the BASIC bundle.
*/
public static final BundleDescriptor BASIC_BUNDLE =
new DefaultBundleDescriptor(BASIC_ID, BASIC_DISPLAY_NAME,
BASIC_DESCRIPTION,
XosFunctionDescriptor.INTERNET,
XosFunctionDescriptor.FIREWALL,
XosFunctionDescriptor.CDN);
/**
* Designates the FAMILY bundle.
*/
public static final BundleDescriptor FAMILY_BUNDLE =
new DefaultBundleDescriptor(FAMILY_ID, FAMILY_DISPLAY_NAME,
FAMILY_DESCRIPTION,
XosFunctionDescriptor.INTERNET,
XosFunctionDescriptor.FIREWALL,
XosFunctionDescriptor.CDN,
XosFunctionDescriptor.URL_FILTER);
// all bundles, in the order they should be listed in the GUI
private static final List<BundleDescriptor> ALL_BUNDLES = ImmutableList.of(
BASIC_BUNDLE,
FAMILY_BUNDLE
);
/**
* Returns the list of available bundles.
*
* @return available bundles
*/
public static List<BundleDescriptor> availableBundles() {
return ALL_BUNDLES;
}
/**
* Returns the bundle descriptor for the given identifier.
*
* @param bundleId bundle identifier
* @return bundle descriptor
* @throws IllegalArgumentException if bundle ID is unknown
*/
public static BundleDescriptor bundleFromId(String bundleId) {
for (BundleDescriptor bd : ALL_BUNDLES) {
if (bd.id().equals(bundleId)) {
return bd;
}
}
throw new IllegalArgumentException("unknown bundle: " + bundleId);
}
/**
* Returns an object node representation of the given bundle.
* Note that some functions (such as CDN) are not added to the output
* as we don't want them to appear in the GUI.
*
* @param bundle the bundle
* @return object node
*/
public static ObjectNode toObjectNode(Bundle bundle) {
ObjectNode root = objectNode();
BundleDescriptor descriptor = bundle.descriptor();
ObjectNode bnode = objectNode()
.put(ID, descriptor.id())
.put(NAME, descriptor.displayName())
.put(DESC, descriptor.description());
ArrayNode funcs = arrayNode();
for (XosFunctionDescriptor xfd: bundle.descriptor().functions()) {
if (xfd.visible()) {
funcs.add(XosFunctionFactory.toObjectNode(xfd));
}
}
bnode.set(FUNCTIONS, funcs);
root.set(BUNDLE, bnode);
ArrayNode bundles = arrayNode();
for (BundleDescriptor bd: BundleFactory.availableBundles()) {
ObjectNode bdnode = objectNode()
.put(ID, bd.id())
.put(NAME, bd.displayName())
.put(DESC, bd.description());
bundles.add(bdnode);
}
root.set(BUNDLES, bundles);
return root;
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
/**
* Base implementation of BundleDescriptor.
*/
public class DefaultBundleDescriptor implements BundleDescriptor {
private final String id;
private final String displayName;
private final String description;
private final Set<XosFunctionDescriptor> functions;
/**
* Constructs a bundle descriptor.
*
* @param id bundle identifier
* @param displayName bundle display name
* @param functions functions that make up this bundle
*/
DefaultBundleDescriptor(String id, String displayName, String description,
XosFunctionDescriptor... functions) {
this.id = id;
this.displayName = displayName;
this.description = description;
this.functions = ImmutableSet.copyOf(functions);
}
public String id() {
return id;
}
public String displayName() {
return displayName;
}
public String description() {
return description;
}
public Set<XosFunctionDescriptor> functions() {
return functions;
}
@Override
public String toString() {
return "{BundleDescriptor: " + displayName + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DefaultBundleDescriptor that = (DefaultBundleDescriptor) o;
return id.equals(that.id);
}
@Override
public int hashCode() {
return id.hashCode();
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Default XOS function implementation, that does not have any parameters
* to tweak.
*/
public class DefaultXosFunction implements XosFunction {
protected static final ObjectMapper MAPPER = new ObjectMapper();
private final XosFunctionDescriptor xfd;
public DefaultXosFunction(XosFunctionDescriptor xfd) {
this.xfd = xfd;
}
public XosFunctionDescriptor descriptor() {
return xfd;
}
/**
* {@inheritDoc}
* <p>
* This default implementation throws an exception.
*
* @param user user to apply the change to
* @param param parameter name
* @param value new parameter value
* @throws UnsupportedOperationException if invoked
*/
public void applyParam(SubscriberUser user, String param, String value) {
throw new UnsupportedOperationException();
}
public Memento createMemento() {
return null;
}
public String xosUrlApply(SubscriberUser user) {
return null;
}
@Override
public String toString() {
return "{XosFunction: " + xfd + "}";
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Base class for factories that convert objects to JSON.
*/
public abstract class JsonFactory {
private static final ObjectMapper MAPPER = new ObjectMapper();
protected static final String ID = "id";
protected static final String NAME = "name";
protected static final String DESC = "desc";
protected static final String ICON_ID = "icon_id";
/**
* Returns a freshly minted object node.
*
* @return empty object node
*/
protected static ObjectNode objectNode() {
return MAPPER.createObjectNode();
}
/**
* Returns a freshly minted array node.
*
* @return empty array node
*/
protected static ArrayNode arrayNode() {
return MAPPER.createArrayNode();
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import java.util.HashMap;
import java.util.Map;
/**
* Designates a user of a subscriber's account.
*/
public class SubscriberUser {
private final int id;
private final String name;
private final String mac;
// this is "duplicated" in the URL_FILTER memento, but, oh well...
// -- the level, as returned from XOS, when we create this user object.
private String level;
private final Map<XosFunctionDescriptor, XosFunction.Memento> mementos =
new HashMap<XosFunctionDescriptor, XosFunction.Memento>();
/**
* Constructs a subscriber user from the given parameters.
*
* @param id internal identifier
* @param name display name
* @param mac MAC address of the associated device
* @param level URL filter level
*/
public SubscriberUser(int id, String name, String mac, String level) {
this.id = id;
this.name = name;
this.mac = mac;
this.level = level;
}
/**
* Returns the internal identifier.
*
* @return the identifier
*/
public int id() {
return id;
}
/**
* Returns the display name.
*
* @return display name
*/
public String name() {
return name;
}
/**
* Returns the MAC address of the associated device.
*
* @return MAC address
*/
public String mac() {
return mac;
}
/**
* Returns the URL filter level.
*
* @return URL filter level
*/
public String urlFilterLevel() {
return level;
}
/**
* Sets the URL filter level.
*
* @param level URL filter level
*/
public void setUrlFilterLevel(String level) {
this.level = level;
}
/**
* Stores a memento for the given XOS function.
*
* @param f XOS function
* @param m memento
*/
public void setMemento(XosFunctionDescriptor f, XosFunction.Memento m) {
if (m != null) {
mementos.put(f, m);
}
}
/**
* Returns the memento stored on this user, for the given XOS function.
*
* @param f XOS function
* @return memento
*/
public XosFunction.Memento getMemento(XosFunctionDescriptor f) {
return mementos.get(f);
}
/**
* Clears the memento map.
*/
public void clearMementos() {
mementos.clear();
}
@Override
public String toString() {
return "{User: " + name + "}";
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import com.fasterxml.jackson.databind.node.ObjectNode;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Specialization of XosFunction for URL filtering.
*/
public class UrlFilterFunction extends DefaultXosFunction {
private static final String LEVEL = "level";
private static final String URI_PATTERN = "%s/%s/";
/**
* Denotes the URL filtering levels available. From most restrictive
* to least restrictive. Note: <em>NONE</em> allows nothing;
* <em>ALL</em> allows everything.
*/
public enum Level { NONE, G, PG, PG_13, R, ALL }
/**
* The default URL filtering level
*/
public static final Level DEFAULT_LEVEL = Level.G;
public UrlFilterFunction() {
super(XosFunctionDescriptor.URL_FILTER);
}
@Override
public void applyParam(SubscriberUser user, String param, String value) {
Memento memo = user.getMemento(descriptor());
checkNotNull(memo, "missing memento for " + descriptor());
UrlFilterMemento ufMemo = (UrlFilterMemento) memo;
if (LEVEL.equals(param)) {
Level newLevel = Level.valueOf(value.toUpperCase());
ufMemo.setLevel(newLevel);
// Also store the (string version) of the level
// (not in the memento). Hackish, but that's how it is for now.
user.setUrlFilterLevel(value);
}
}
@Override
public Memento createMemento() {
return new UrlFilterMemento();
}
class UrlFilterMemento implements Memento {
private Level level = DEFAULT_LEVEL;
public ObjectNode toObjectNode() {
ObjectNode node = MAPPER.createObjectNode();
node.put(LEVEL, level.name());
return node;
}
public void setLevel(Level level) {
this.level = level;
}
public String level() {
return level.toString();
}
}
@Override
public String xosUrlApply(SubscriberUser user) {
XosFunctionDescriptor xfd = XosFunctionDescriptor.URL_FILTER;
UrlFilterMemento memo = (UrlFilterMemento) user.getMemento(xfd);
return String.format(URI_PATTERN, xfd.id(), memo.level());
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.HashMap;
import java.util.Map;
/**
* Utility functions on users.
*/
public class UserFactory extends JsonFactory {
private static final String MAC = "mac";
private static final String PROFILE = "profile";
// hard-coded icons for the demo
private static final Map<String, String> ICON_LOOKUP =
new HashMap<String, String>();
static {
ICON_LOOKUP.put("Mom's PC", "mom");
ICON_LOOKUP.put("Dad's PC", "dad");
ICON_LOOKUP.put("Jack's Laptop", "boy2");
ICON_LOOKUP.put("Jill's Laptop", "girl1");
}
private static final String DEFAULT_ICON_ID = "boy1";
// no instantiation
private UserFactory() {}
/**
* Returns an object node representation of the given user.
*
* @param user the user
* @return object node
*/
public static ObjectNode toObjectNode(SubscriberUser user) {
String icon = ICON_LOOKUP.get(user.name());
icon = icon == null ? DEFAULT_ICON_ID : icon;
ObjectNode root = objectNode()
.put(ID, user.id())
.put(ICON_ID, icon)
.put(NAME, user.name())
.put(MAC, user.mac());
root.set(PROFILE, XosFunctionFactory.profileForUser(user));
return root;
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* Designates a specific instance of an XOS function.
*/
public interface XosFunction {
/**
* Returns the descriptor for this function.
*
* @return function descriptor
*/
XosFunctionDescriptor descriptor();
/**
* Applies a parameter change for the given user.
*
* @param user user to apply change to
* @param param parameter name
* @param value new parameter value
*/
void applyParam(SubscriberUser user, String param, String value);
/**
* Create an initialized memento.
* If the function maintains no state per user, return null.
*
* @return a new memento
*/
Memento createMemento();
/**
* Create the XOS specific URL suffix for applying state change for
* the given user.
*
* @param user the user
* @return URL suffix
*/
String xosUrlApply(SubscriberUser user);
/**
* Internal state memento.
*/
interface Memento {
/**
* Returns a JSON representation of this memento.
*
* @return memento state as object node
*/
ObjectNode toObjectNode();
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
/**
* Designates XOS Functions.
*/
public enum XosFunctionDescriptor {
/**
* Internet function.
*/
INTERNET("internet",
"Internet",
"Discover the joys of high-speed, reliable Internet" +
" connectivity delivered seamlessly to your home.",
false,
true),
/**
* Firewall function.
*/
FIREWALL("firewall",
"Firewall",
"Simple access control and filtering with minimal set-up.",
true,
true),
/**
* URL Filtering function (parental controls).
*/
URL_FILTER("url_filter",
"Parental Control",
"Parental Control is peace of mind that your kids are safe" +
" - whether you are around or away. Indicate with a " +
"few clicks what online content is appropriate for " +
"your children, and voila - you have control over" +
" what your kids can and cannot view.",
true,
true),
/**
* Content Distribution function.
*/
CDN("cdn",
"CDN",
"Content Distribution Network service.",
true,
false);
private final String id;
private final String displayName;
private final String description;
private final boolean backend;
private final boolean visible;
XosFunctionDescriptor(String id, String displayName, String description,
boolean backend, boolean visible) {
this.id = id;
this.displayName = displayName;
this.description = description;
this.backend = backend;
this.visible = visible;
}
/**
* Returns this function's internal identifier.
*
* @return the identifier
*/
public String id() {
return id;
}
/**
* Returns this function's display name.
*
* @return display name
*/
public String displayName() {
return displayName;
}
/**
* Returns a short, textual description of the function.
*
* @return textual description
*/
public String description() {
return description;
}
/**
* Returns true if this function is supported by the XOS backend.
*
* @return true if backend function exists
*/
public boolean backend() {
return backend;
}
/**
* Returns true if this function should be shown in the GUI, in the
* bundle listing.
*
* @return true if to be displayed
*/
public boolean visible() {
return visible;
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onosproject.cord.gui.model;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.HashMap;
import java.util.Map;
import static org.onosproject.cord.gui.model.XosFunctionDescriptor.URL_FILTER;
/**
* Utility factory for operating on XOS functions.
*/
public class XosFunctionFactory extends JsonFactory {
private static final String PARAMS = "params";
private static final String LEVEL = "level";
private static final String LEVELS = "levels";
// no instantiation
private XosFunctionFactory() {}
/**
* Produces the JSON representation of the given XOS function descriptor.
*
* @param xfd function descriptor
* @return JSON encoding
*/
public static ObjectNode toObjectNode(XosFunctionDescriptor xfd) {
ObjectNode root = objectNode()
.put(ID, xfd.id())
.put(NAME, xfd.displayName())
.put(DESC, xfd.description());
root.set(PARAMS, paramsForXfd(xfd));
return root;
}
private static ObjectNode paramsForXfd(XosFunctionDescriptor xfd) {
ParamsFactory psf = PARAM_MAP.get(xfd);
if (psf == null) {
psf = DEF_PARAMS_FACTORY;
}
return psf.params();
}
// ==== handling different parameter structures...
private static final Map<XosFunctionDescriptor, ParamsFactory>
PARAM_MAP = new HashMap<XosFunctionDescriptor, ParamsFactory>();
private static final ParamsFactory DEF_PARAMS_FACTORY = new ParamsFactory();
static {
PARAM_MAP.put(URL_FILTER, new UrlFilterParamsFactory());
}
/**
* Creates an object node representation of the profile for the
* specified user.
*
* @param user the user
* @return object node profile
*/
public static ObjectNode profileForUser(SubscriberUser user) {
ObjectNode root = objectNode();
for (XosFunctionDescriptor xfd: XosFunctionDescriptor.values()) {
XosFunction.Memento mem = user.getMemento(xfd);
if (mem != null) {
root.set(xfd.id(), mem.toObjectNode());
}
}
return root;
}
// ===================================================================
// === factories for creating parameter structures, both default
// and from a memento...
// private parameter structure creator
static class ParamsFactory {
ObjectNode params() {
return objectNode();
}
}
static class UrlFilterParamsFactory extends ParamsFactory {
@Override
ObjectNode params() {
ObjectNode result = objectNode();
result.put(LEVEL, UrlFilterFunction.DEFAULT_LEVEL.name());
ArrayNode levels = arrayNode();
for (UrlFilterFunction.Level lvl: UrlFilterFunction.Level.values()) {
levels.add(lvl.name());
}
result.set(LEVELS, levels);
return result;
}
}
}
{
"bundle": {
"id": "basic",
"name": "Basic Bundle",
"functions": [
{
"id": "internet",
"name": "Internet",
"desc": "Basic internet connectivity.",
"params": {}
},
{
"id": "firewall",
"name": "Firewall",
"desc": "Normal firewall protection.",
"params": {}
}
]
},
"bundles": [
{ "id": "basic", "name": "Basic Bundle" },
{ "id": "family", "name": "Family Bundle" }
]
}
{
"bundle": {
"id": "family",
"name": "Family Bundle",
"functions": [
{
"id": "internet",
"name": "Internet",
"desc": "Basic internet connectivity.",
"params": {}
},
{
"id": "firewall",
"name": "Firewall",
"desc": "Normal firewall protection.",
"params": {}
},
{
"id": "url_filter",
"name": "Parental Control",
"desc": "Variable levels of URL filtering.",
"params": {
"level": "PG",
"levels": [ "PG", "PG-13", "R" ]
}
}
]
},
"bundles": [
{ "id": "basic", "name": "Basic Bundle" },
{ "id": "family", "name": "Family Bundle" }
]
}
{
"bundle": "Basic Bundle",
"users": [
{ "id": 1, "name": "Mom's MacBook", "mac": "00:11:22:33:44:55" },
{ "id": 1, "name": "Dad's iPad", "mac": "00:11:22:33:44:66" },
{ "id": 1, "name": "Dick's laptop", "mac": "00:11:22:33:44:77" },
{ "id": 1, "name": "Jane's laptop", "mac": "00:11:22:33:44:88" }
]
}
{
"bundle": "Family Bundle",
"users": [
{ "id": 1, "name": "Mom's MacBook", "mac": "00:11:22:33:44:55" },
{ "id": 1, "name": "Dad's iPad", "mac": "00:11:22:33:44:66" },
{ "id": 1, "name": "Dick's laptop", "mac": "00:11:22:33:44:77" },
{ "id": 1, "name": "Jane's laptop", "mac": "00:11:22:33:44:88" }
]
}
{
"users": [
{
"id": 1,
"name": "Mom's MacBook",
"mac": "01:02:03:04:05:06",
"profile": { }
},
{
"id": 2,
"name": "Dad's iPad",
"mac": "01:02:03:04:05:77",
"profile": { }
},
{
"id": 3,
"name": "Dick's laptop",
"mac": "01:02:03:04:05:88",
"profile": { }
},
{
"id": 4,
"name": "Jane's laptop",
"mac": "01:02:03:04:05:99",
"profile": { }
}
]
}
{
"users": [
{
"id": 1,
"name": "Mom's MacBook",
"mac": "010203040506",
"profile": {
"url_filter": {
"level": "R"
}
}
},
{
"id": 2,
"name": "Dad's iPad",
"mac": "010203040507",
"profile": {
"url_filter": {
"level": "R"
}
}
},
{
"id": 3,
"name": "Dick's laptop",
"mac": "010203040508",
"profile": {
"url_filter": {
"level": "PG_13"
}
}
},
{
"id": 4,
"name": "Jane's laptop",
"mac": "010203040509",
"profile": {
"url_filter": {
"level": "PG"
}
}
}
]
}
{
"comment": [
"This is a sample JSON File",
"Which can contain fake data."
],
"fake": true,
"numbers": [ 1, 2, 3 ],
"map": {
"x": 100,
"y": 200,
"z": 32
}
}
{
"users": [
{
"id": 1,
"name": "Mom's MacBook",
"mac": "01:02:03:04:05:06"
},
{
"id": 2,
"name": "Dad's iPad",
"mac": "01:02:03:04:05:77"
},
{
"id": 3,
"name": "Dick's laptop",
"mac": "01:02:03:04:05:88"
},
{
"id": 4,
"name": "Jane's laptop",
"mac": "01:02:03:04:05:99"
}
]
}
{
"humanReadableName": "vOLT on service 16",
"id": 1,
"service_specific_id": "",
"vlan_id": null,
"vcpe_id": 2,
"sliver": 384,
"sliver_name": "onlab_vcpe",
"image": 29,
"image_name": "Ubuntu 14.04 LTS",
"firewall_enable": true,
"firewall_rules": "accept all anywhere anywhere",
"url_filter_enable": true,
"url_filter_rules": "allow all",
"url_filter_level": "PG",
"cdn_enable": true,
"vbng_id": 4,
"routeable_subnet": "1.1.1.1/4"
}
{
"users": [
{
"mac": "010203040506",
"name": "Moms laptop",
"id": 0
},
{
"mac": "010203040507",
"name": "Dads desktop",
"id": 1
},
{
"id": 2,
"mac": "010203040507",
"name": "Jacks iPad",
"level": "PG"
},
{
"mac": "010203040508",
"name": "Jills iPad",
"id": 3
}
]
}
{
"users": [
{
"mac": "010203040506",
"name": "Moms laptop",
"id": 0
},
{
"mac": "010203040507",
"name": "Dads desktop",
"id": 1
},
{
"mac": "010203040507",
"name": "Jacks iPad",
"id": 2
},
{
"mac": "010203040508",
"name": "Jills iPad",
"id": 3
}
]
}
{
"humanReadableName": "vOLT on service 1",
"id": 92,
"service_specific_id": "33445573",
"vlan_id": "77889908",
"vcpe_id": 93,
"sliver": 108,
"sliver_name": "mysite_vcpe",
"image": 1,
"image_name": "Ubuntu-14.04-LTS",
"firewall_enable": false,
"firewall_rules": "accept all anywhere anywhere",
"url_filter_enable": false,
"url_filter_rules": "allow all",
"url_filter_level": "R",
"cdn_enable": false,
"vbng_id": 94,
"routeable_subnet": "",
"nat_ip": "172.16.0.106",
"lan_ip": "12.0.0.16",
"wan_ip": "11.0.0.16",
"private_ip": "10.0.6.43"
}
{
"subscribers": [
{
"humanReadableName": "vOLT on service 1",
"id": 92,
"service_specific_id": "33445573",
"vlan_id": "77889908",
"vcpe_id": 93,
"sliver": 108,
"sliver_name": "mysite_vcpe",
"image": 1,
"image_name": "Ubuntu-14.04-LTS",
"firewall_enable": false,
"firewall_rules": "accept all anywhere anywhere",
"url_filter_enable": false,
"url_filter_rules": "allow all",
"url_filter_level": "R",
"cdn_enable": false,
"vbng_id": 94,
"routeable_subnet": "",
"nat_ip": "172.16.0.106",
"lan_ip": "12.0.0.16",
"wan_ip": "11.0.0.16",
"private_ip": "10.0.6.43"
},
{
"humanReadableName": "vOLT on service 1",
"id": 138,
"service_specific_id": "",
"vlan_id": "100",
"vcpe_id": 139,
"sliver": 106,
"sliver_name": "mysite_vcpe",
"image": 1,
"image_name": "Ubuntu-14.04-LTS",
"firewall_enable": false,
"firewall_rules": "accept all anywhere anywhere",
"url_filter_enable": false,
"url_filter_rules": "allow all",
"url_filter_level": "PG",
"cdn_enable": false,
"vbng_id": 140,
"routeable_subnet": "",
"nat_ip": "172.16.0.104",
"lan_ip": "12.0.0.14",
"wan_ip": "11.0.0.14",
"private_ip": "10.0.6.41"
},
{
"humanReadableName": "vOLT on service 1",
"id": 154,
"service_specific_id": "98765",
"vlan_id": "99",
"vcpe_id": 155,
"sliver": 117,
"sliver_name": "mysite_vcpe",
"image": 1,
"image_name": "Ubuntu-14.04-LTS",
"firewall_enable": false,
"firewall_rules": "accept all anywhere anywhere",
"url_filter_enable": false,
"url_filter_rules": "allow all",
"url_filter_level": "PG",
"cdn_enable": false,
"vbng_id": 156,
"routeable_subnet": "",
"nat_ip": "172.16.0.114",
"lan_ip": "12.0.0.24",
"wan_ip": "11.0.0.24",
"private_ip": "10.0.6.48"
},
{
"humanReadableName": "vOLT on service 1",
"id": 157,
"service_specific_id": "5678",
"vlan_id": "999",
"vcpe_id": 158,
"sliver": 118,
"sliver_name": "mysite_vcpe",
"image": 1,
"image_name": "Ubuntu-14.04-LTS",
"firewall_enable": false,
"firewall_rules": "accept all anywhere anywhere",
"url_filter_enable": false,
"url_filter_rules": "allow all",
"url_filter_level": "PG",
"cdn_enable": false,
"vbng_id": 159,
"routeable_subnet": "",
"nat_ip": "172.16.0.119",
"lan_ip": "12.0.0.29",
"wan_ip": "11.0.0.29",
"private_ip": "10.0.6.53"
},
{
"humanReadableName": "vOLT on service 1",
"id": 160,
"service_specific_id": "1",
"vlan_id": "100",
"vcpe_id": 161,
"sliver": 119,
"sliver_name": "mysite_vcpe",
"image": 1,
"image_name": "Ubuntu-14.04-LTS",
"firewall_enable": false,
"firewall_rules": "accept all anywhere anywhere",
"url_filter_enable": false,
"url_filter_rules": "allow all",
"url_filter_level": "PG",
"cdn_enable": false,
"vbng_id": 162,
"routeable_subnet": "",
"nat_ip": "172.16.0.120",
"lan_ip": "12.0.0.30",
"wan_ip": "11.0.0.30",
"private_ip": "10.0.6.54"
},
{
"humanReadableName": "vOLT on service 1",
"id": 163,
"service_specific_id": "101",
"vlan_id": "101",
"vcpe_id": 164,
"sliver": 120,
"sliver_name": "mysite_vcpe",
"image": 1,
"image_name": "Ubuntu-14.04-LTS",
"firewall_enable": false,
"firewall_rules": "accept all anywhere anywhere",
"url_filter_enable": false,
"url_filter_rules": "allow all",
"url_filter_level": "PG",
"cdn_enable": false,
"vbng_id": 165,
"routeable_subnet": "",
"nat_ip": "172.16.0.121",
"lan_ip": "12.0.0.31",
"wan_ip": "11.0.0.31",
"private_ip": "10.0.6.55"
}
]
}
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2015-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="ONOS" version="2.5">
<display-name>CORD Subscriber Web GUI</display-name>
<servlet>
<servlet-name>JAX-RS Service</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
org.onosproject.cord.gui.CordWebResource
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS Service</servlet-name>
<url-pattern>/rs/*</url-pattern>
</servlet-mapping>
</web-app>
{
"_comment_": "Parental Control Categories - data file",
"level_order": [
"NONE", "G", "PG", "PG_13", "R", "ALL"
],
"category_order": [
"Safe", "Search", "Shopping", "Sports", "Privacy", "Dating", "Games",
"Social", "Illegal", "Weapons", "Drugs", "Gambling", "Cyberbully",
"Pornography", "Adult", "Anonymizers", "Suicide", "Malware"
],
"descriptions": {
"Safe": [
"."
],
"Search": [
"Sites which provide the ability to perform searches for specific ",
"topics or websites across the entire Internet, and which display ",
"results in a multi-page format that allows material to be sorted ",
"based on content, topic, or file type."
],
"Shopping": [
"."
],
"Sports": [
"Sites which analyze, promote, or providing information about ",
"competitive sports and its fans whether official or unofficial."
],
"Privacy": [
"Sites which provide hosted online advertising intended to attract ",
"web traffic, deliver marketing messages or capture email addresses."
],
"Dating": [
"Sites which promote or provide the opportunity for establishing ",
"romantic relationship."
],
"Games": [
"Sites which related to the development, promotion, review, and ",
"enjoyment of online, PC, and console videogaming."
],
"Social": [
"Sites which facilitate online socializing and the development or ",
"maintenance of personal and professional relationships across ",
"geographical and organizational boundaries."
],
"Illegal": [
"Sites which promote or provide the means to practice illegal or ",
"unauthorized acts using computer-programming skills. And sites ",
"which offer custom academic writing services for free or for ",
"purchase. These sites are geared toward students who do not want ",
"to write their own papers, but will download or buy previously ",
"written or custom written papers."
],
"Weapons": [
"Sites which sell, manufacture, or describe the manufacture of weapons."
],
"Drugs": [
"Sites which promote, offer, sell, supply, encourage or otherwise ",
"advocate the recreational or illegal use, cultivation, manufacture, ",
"or distribution of drugs, pharmaceuticals, intoxicating plants or ",
"chemicals and their related paraphernalia. And sites which glamorize, ",
"glorify, tout or otherwise encourage the consumption of alcohol. And ",
"sites which sell, glamorize, enable or encourage the use of tobacco ",
"and tobacco-related products."
],
"Gambling": [
"."
],
"Cyberbully": [
"Sites or pages where people post targeted, deliberate and slanderous ",
"or offensive content about other people with the INTENT to torment, ",
"threaten, humiliate or defame them. Content is often sexual, ",
"malicious or hostile in nature and is submitted via interactive ",
"digital technology. And sites which advocate hostility, aggression ",
"and the denigration of an individual or group on the basis of race, ",
"religion, gender, nationality, ethnic origin, or other involuntary ",
"characteristics. Sites that use purported scientific or commonly ",
"accredited methods to justify inequality, aggression, and hostility."
],
"Pornography": [
"Sites which contain explicit material for the purpose of causing ",
"sexual excitement or arousing lascivious interest."
],
"Adult": [
"Sites which contain sexually explicit information that is not ",
"medical or scientific nature and yet are also not pornographic. ",
"And sites which feature social or family nudism/naturism, nudist ",
"camps/resorts, or „nudist-only‟ travel."
],
"Anonymizers": [
"Sites which provide anonymous access to websites through a PHP or ",
"CGI proxy, allowing users to gain access to websites blocked by ",
"corporate and school proxies as well as parental control filtering ",
"solutions."
],
"Suicide": [
"Sites which advocate, normalize, or glamourize repetitive and ",
"deliberate ways to inflict non-fatal harm to oneself. And sites ",
"advocating or glorifying suicide as well as educating people on how ",
"to commit suicide."
],
"Malware": [
"Sites where the domain was found to either contain malware or take ",
"advantage of other exploits to deliver adware, spyware or malware. ",
"And Sites that contain direct links to malware file downloads: ",
".exe, .dll, .ocx, and others. These URLs are generally highly malicious."
]
},
"_prohibited_comment_": [
"Note: Level NONE allows nothing (prohibits everything)",
" level ALL allows everything (prohibits nothing)",
" Levels G, PG, PG_13, R prohibitions listed below:"
],
"prohibited": {
"G": [
"Games", "Social", "Illegal", "Weapons", "Drugs", "Gambling",
"Cyberbully", "Pornography", "Adult", "Anonymizers", "Suicide", "Malware"
],
"PG": [
"Social", "Illegal", "Weapons", "Drugs", "Gambling",
"Cyberbully", "Pornography", "Adult", "Anonymizers", "Suicide", "Malware"
],
"PG_13": [
"Illegal", "Weapons", "Drugs", "Gambling",
"Cyberbully", "Pornography", "Adult", "Anonymizers", "Suicide", "Malware"
],
"R": [
"Pornography", "Adult", "Anonymizers", "Suicide", "Malware"
]
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
div.foot {
width: 100%;
height: 30px;
background-color: white;
position: absolute;
left: 0;
right: 0;
bottom: 0;
margin-left: auto;
margin-right: auto;
z-index: 100;
box-shadow: 0 10px 5px 10px gray;
}
.foot div {
position: absolute;
top: 50%;
transform: translate(0, -50%);
font-style: italic;
font-size: 12px;
color: #3C3C3C;
}
.foot div.left {
left: 25px;
}
.foot div.right {
right: 25px;
}
<!--Foot partial html-->
<div class="foot">
<div class="left">
</div>
<div class="right">
© ONOS Project. All rights reserved.
</div>
</div>
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
angular.module('cordFoot', [])
.directive('foot', function () {
return {
restrict: 'E',
templateUrl: 'app/fw/foot/foot.html'
};
});
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function () {
'use strict';
angular.module('cordGui')
.directive('icon', [function () {
return {
restrict: 'E',
compile: function (element, attrs) {
var html =
'<svg class="embedded-icon" width="' + attrs.size + '" ' +
'height="' + attrs.size + '" viewBox="0 0 50 50">' +
'<g class="icon">' +
'<circle cx="25" cy="25" r="25"></circle>' +
'<use width="50" height="50" class="glyph '
+ attrs.id + '" xlink:href="#' + attrs.id +
'"></use>' +
'</g>' +
'</svg>';
element.replaceWith(html);
}
};
}]);
}());
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
div.mast {
width: 100%;
height: 85px;
background-color: white;
position: relative;
box-shadow: 0 10px 5px -7px gray;
z-index: 100;
}
.mast div {
position: absolute;
top: 50%;
transform: translate(0, -50%);
}
.mast div.left {
left: 25px;
}
.mast div.right {
right: 7%;
width: 37%;
}
.mast img {
width: 220px;
}
.mast a,
.mast a:visited {
text-decoration: none;
color: #3C3C3C;
}
.mast li.logout {
list-style-type: none;
position: absolute;
right: 0;
top: 50%;
transform: translate(0, -50%);
font-size: 90%;
}
.mast li.logout:hover {
font-weight: bold;
list-style-type: none;
cursor: pointer;
}
<!--Mast HTML-->
<div class="mast" ng-controller="CordMastCtrl">
<div class="left">
<img src="/imgs/logo.png">
</div>
<div class="right">
<nav ng-show="page.curr !== 'login'"></nav>
<li class="logout"
ng-show="page.curr !== 'login'"
ng-click="logout()">LOGOUT</li>
</div>
</div>
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function () {
'use strict';
var urlSuffix = '/rs/logout';
angular.module('cordMast', [])
.controller('CordMastCtrl',
['$log','$scope', '$resource', '$location', '$window',
function ($log, $scope, $resource, $location, $window) {
var LogoutData, resource;
$scope.logout = function () {
$log.debug('Logging out...');
LogoutData = $resource($scope.shared.url + urlSuffix);
resource = LogoutData.get({},
function () {
$location.path('/login');
$window.location.href = $location.absUrl();
$log.debug('Resource received:', resource);
});
};
}])
.directive('mast', function () {
return {
restrict: 'E',
templateUrl: 'app/fw/mast/mast.html'
};
});
}());
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.nav ul {
display: table;
table-layout: fixed;
list-style-type: none;
width: 80%;
}
.nav li {
padding: 2.5% 0;
color: #3C3C3C;
}
.nav li:hover {
border-bottom: 2px solid #CE5650;
color: black;
}
.nav li.selected {
font-weight: bolder;
color: #3C3C3C;
letter-spacing: 0.03em;
border-bottom: 2px solid #CE5650;
}
.nav a,
.nav a:visited {
display: table-cell;
text-align: center;
text-decoration: none;
color: black;
}
<!--Nav HTML-->
<div class="nav">
<ul>
<a href="#/home">
<li ng-class="{selected: page.curr === 'dashboard'}"
ng-click="$route.reload()">Home</li>
</a>
<a href="#/user">
<li ng-class="{selected: page.curr === 'user'}"
ng-click="$route.reload()">Users</li>
</a>
<a href="#/bundle">
<li ng-class="{selected: page.curr === 'bundle'}"
ng-click="$route.reload()">Bundles</li>
</a>
</ul>
</div>
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
angular.module('cordNav', [])
.directive('nav', function () {
return {
restrict: 'E',
templateUrl: 'app/fw/nav/nav.html'
};
});
<div ng-cloak class="ng-hide ng-cloak" ng-show="show" id="available">
<h3>{{available.name}}</h3>
<p>{{available.desc}}</p>
<button ng-click="changeBundle(available.id)">Apply</button>
</div>
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
div#bundle div.main-left {
width: 61%;
padding: 4% 0 0 1%;
}
div#bundle div.main-right {
width: 37%;
padding-top: 4%;
}
#bundle table {
width: 95%;
margin-top: 5%;
margin-left: 2%;
border-radius: 3px;
}
#bundle td {
font-size: 90%;
}
#bundle td.icon {
text-align: center;
width: 50px;
height: 50px;
padding: 4%;
}
#bundle td.name {
border-left: solid 1px rgba(136, 0, 0, 0.25);
padding-left: 3%;
}
#bundle td.desc {
width: 60%;
text-align: left;
font-style: italic;
}
/* animation specific */
#bundle tr.fadein.ng-leave td.name,
#bundle tr.fadein.ng-leave-active td.name {
opacity: 0;
border: none;
}
#bundle img {
width: 100%;
}
#bundle h2 {
text-align: center;
padding: 3%;
font-weight: lighter;
border: 1px solid #3C3C3C;
cursor: pointer;
}
#bundle h2:hover {
color: #CE5650;
border-color: #CE5650;
}
div#bundles {
position: relative;
}
div#available.ng-hide-add.ng-hide-add-active,
div#available.ng-hide-remove.ng-hide-remove-active {
-webkit-transition: all linear 0.5s;
transition: all linear 0.5s;
}
div#available.ng-hide {
opacity: 0;
top: -80px;
}
div#available {
position: absolute;
padding: 5%;
opacity: 1;
top: -10px;
width: 100%;
}
#available p {
text-indent: initial;
text-align: initial;
}
#available button {
float: right;
width: 33%;
margin-top: 5%;
}
<!-- Bundle page partial html -->
<div id="bundle" class="container">
<div class="main-left">
<h4>You are subscribed to the</h4>
<h3>{{name}}</h3>
<p>{{desc}}</p>
<table>
<tr ng-repeat="func in funcs" class="fadein">
<td class="icon">
<img ng-src="{{'/imgs/' + func.id + '.png'}}">
</td>
<td class="name">{{func.name}}</td>
<td class="desc">{{func.desc}}</td>
</tr>
</table>
</div>
<div class="main-right">
<img src="imgs/bundle.jpg">
<div ng-click="showBundles()">
<h2>Available Bundles</h2>
</div>
<div id="bundles" bundle-available></div>
</div>
</div>
\ No newline at end of file
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function () {
'use strict';
var urlSuffix = '/rs/bundle';
var basic = 'basic',
family = 'family';
angular.module('cordBundle', [])
.controller('CordBundleCtrl', ['$log', '$scope', '$resource',
function ($log, $scope, $resource) {
var BundleData, resource,
getData;
$scope.page.curr = 'bundle';
$scope.show = false;
getData = function (id) {
if (!id) { id = ''; }
BundleData = $resource($scope.shared.url + urlSuffix + '/' + id);
resource = BundleData.get({},
// success
function () {
var current, availId;
current = resource.bundle.id;
$scope.name = resource.bundle.name;
$scope.desc = resource.bundle.desc;
$scope.funcs = resource.bundle.functions;
availId = (current === basic) ? family : basic;
resource.bundles.forEach(function (bundle) {
if (bundle.id === availId) {
$scope.available = bundle;
}
});
},
// error
function () {
$log.error('Problem with resource', resource);
});
};
getData();
$scope.changeBundle = function (id) {
getData(id);
};
$scope.showBundles = function () {
$scope.show = !$scope.show;
};
$log.debug('Cord Bundle Ctrl has been created.');
}])
.directive('bundleAvailable', [function () {
return {
templateUrl: 'app/view/bundle/available.html'
};
}]);
}());
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
[ng\:cloak], [ng-cloak], .ng-cloak {
display: none !important;
}
html, body, div#frame, div#view {
height: 100%;
}
head, body, footer,
h1, h2, h3, h4, h5, h6, p,
a, ul, li, div,
table, tr, td, th, thead, tbody,
form, select, input, option, label {
padding: 0;
margin: 0;
}
h1, h2, h3, h4, h5, h6,
p, a, li, th, td,
select, input, option, label {
font-family: sans-serif, "Droid Sans", "Lucida Grande", Arial, Helvetica;
color: #3C3C3C;
}
body {
background-color: white;
overflow: hidden;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
th, td {
color: rgba(0, 0, 0, 0.8);
}
h3 {
margin-bottom: 4%;
font-size: xx-large;
font-weight: lighter;
}
h4 {
font-size: large;
font-weight: lighter;
}
h5 {
color: rgb(107, 107, 107);
font-style: italic;
font-weight: normal;
font-size: 90%;
margin-bottom: 1%;
}
p {
font-size: 100%;
color: rgba(0,0,0, 0.8);
text-indent: 20px;
text-align: justify;
padding-right: 5%;
}
th {
background-color: #7AB6EA;
color: white;
letter-spacing: 0.05em;
font-weight: lighter;
}
button,
input[type="button"],
input[type="reset"] {
height: 30px;
box-shadow: none;
border: none;
outline: none;
cursor: pointer;
letter-spacing: 0.02em;
font-size: 14px;
background-color: lightgray;
transition: background-color 0.4s;
}
button:hover,
input[type="button"]:hover,
input[type="reset"]:hover {
color: white;
background-color: rgb(122, 188, 229);
}
button[disabled],
input[type="button"][disabled],
input[type="reset"][disabled] {
background-color: lightgray;
color: graytext;
}
button[disabled]:hover,
input[type="button"][disabled]:hover,
input[type="reset"][disabled]:hover {
cursor: default;
}
div.container {
width: 85%;
margin: 0 auto;
min-height: 100%;
}
div.main-left, div.main-right {
float: left;
}
div.main-left {
width: 37%;
padding-left: 1%;
}
div.main-right {
width: 61%;
}
svg#icon-defs {
display: none;
}
g.icon circle {
fill: none;
}
g.icon use.glyph.checkMark {
fill: rgb(68, 189, 83)
}
g.icon use.glyph.xMark {
fill: #CE5650;
}
th.user-pic {
background-color: white;
}
th.user-pic,
td.user-pic {
width: 30px;
padding-left: 4%;
}
td.user-pic img {
width: 25px;
}
/* animation */
.fadein {
transition: all linear 0.5s;
}
.fadein.ng-enter-stagger,
.fadein.ng-leave-stagger {
transition-delay: 0.2s;
animation-delay: 0.2s;
}
.fadein.ng-enter {
opacity: 0;
}
.fadein.ng-enter.ng-enter-active {
opacity: 1;
}
.fadein.ng-leave,
.fadein.ng-leave-active {
opacity: 0;
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#home div.main-left {
width: 55%;
padding: 0;
}
#home div.main-right {
padding: 1% 0 0 3%;
width: 42%;
}
#home div.move-down {
margin-top: 5%;
}
#home div.image-holder {
width: 100%;
position: relative;
}
#home div.main-left img {
width: 100%;
}
#home div.main-right div.bundle-title {
padding: 2% 0;
}
#home h4 {
padding-bottom: 2%;
}
#home p {
margin-bottom: 3%;
}
#home table {
width: 94%;
table-layout: fixed;
margin-left: 6%;
border-left: 1px solid #CE5650;
}
#home table.users th,
#home table.users td {
font-size: 90%;
}
#home td, #home th {
text-align: left;
padding: 2%;
}
<!-- Home page partial html -->
<div id="home" class="container">
<div class="main-left">
<img src="/imgs/home.jpg">
</div>
<div class="main-right">
<div class="move-down">
<div class="bundle-title">
<h4>Welcome Dad!</h4>
<h5>You are subscribed to the</h5>
<h3>{{bundle_name}}</h3>
</div>
<p>{{bundle_desc}}</p>
<h4>Users</h4>
<table class="users">
<thead>
<tr>
<th class="user-pic"></th>
<th>Name</th>
<th>Last Login</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="user in users" class="fadein">
<td class="user-pic">
<img ng-src="{{'/imgs/' + user.icon_id + '.jpg'}}">
</td>
<td>{{user.name}}</td>
<td>{{shared.userActivity[user.id]}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function () {
'use strict';
var urlSuffix = '/rs/dashboard';
function randomDate(start, end) {
return new Date(
start.getTime() + Math.random() * (end.getTime() - start.getTime())
);
}
angular.module('cordHome', [])
.controller('CordHomeCtrl', ['$log', '$scope', '$resource', '$filter',
function ($log, $scope, $resource, $filter) {
var DashboardData, resource;
$scope.page.curr = 'dashboard';
DashboardData = $resource($scope.shared.url + urlSuffix);
resource = DashboardData.get({},
// success
function () {
$scope.bundle_name = resource.bundle_name;
$scope.bundle_desc = resource.bundle_desc;
$scope.users = resource.users;
if ($.isEmptyObject($scope.shared.userActivity)) {
$scope.users.forEach(function (user) {
var date = randomDate(new Date(2015, 0, 1),
new Date());
$scope.shared.userActivity[user.id] =
$filter('date')(date, 'mediumTime');
});
}
},
// error
function () {
$log.error('Problem with resource', resource);
});
$log.debug('Resource received:', resource);
$log.debug('Cord Home Ctrl has been created.');
}]);
}());
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
div#login {
background: url("/imgs/login.jpg") no-repeat center;
background-size: contain;
position: absolute;
top: 3%;
left: 5%;
}
div#login-wrapper {
text-align: center;
}
#login h2 {
margin: 1%;
color: rgb(115, 115, 115);
font-size: xx-large;
font-weight: lighter;
text-align: left;
position: absolute;
top: -140px;
}
div#login-form {
display: inline-block;
}
#login div.outline {
position: absolute;
border: 1px solid rgba(115, 115, 115, 0.7);
background-color: white;
opacity: .6;
top: -160px;
left: -25px;
width: 300px;
height: 245px;
border-radius: 1px;
}
div#login-form {
margin-left: 2.5%;
position: relative;
width: 255px;
margin-top: 33.5%;
}
#login-form form {
line-height: 250%;
}
#login-form input {
display: block;
height: 40px;
width: 230px;
font-size: 19px;
padding: 0 5px;
margin-bottom: 3.5%;
border-radius: 1px;
position: absolute;
}
#login-form input[type="text"] {
top: -90px;
}
#login-form input[type="password"] {
top: -35px;
}
#login-form input[type="text"],
#login-form input[type="password"] {
border: 2px solid rgba(115, 115, 115, 0.7);
transition: border 0.1s;
}
#login-form input[type="text"]:focus,
#login-form input[type="password"]:focus,
#login-form input[type="button"]:focus {
outline: none;
border: solid 2px rgba(122, 188, 229, 0.5);
}
#login-form a {
text-decoration: none;
}
#login-form input[type="button"] {
top: 25px;
width: 245px;
height: 30px;
cursor: pointer;
letter-spacing: 0.02em;
font-size: 100%;
color: #3C3C3C;
background-color: lightgray;
transition: background-color 0.4s;
}
#login-form input[type="button"]:hover {
color: white;
background-color: rgb(122, 188, 229);
}
#login-form input.ng-invalid.ng-touched {
background-color: #CE5650;
color: white;
}
<!-- Login page partial html -->
<div id="login" class="container">
<div id="login-wrapper">
<div id="login-form">
<div class="outline"></div>
<h2>Subscriber Portal</h2>
<form>
<input ng-model="email" type="text" placeholder="email" required>
<input ng-model="password" type="password" placeholder="password" required>
<input ng-click="login()" type="button" value="Log In">
</form>
</div>
</div>
</div>
\ No newline at end of file
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function () {
'use strict';
var urlSuffix = '/rs/login';
angular.module('cordLogin', [])
.controller('CordLoginCtrl',
['$log', '$scope', '$resource', '$location', '$window',
function ($log, $scope, $resource, $location, $window) {
var LoginData, resource;
$scope.page.curr = 'login';
function getResource(email) {
LoginData = $resource($scope.shared.url + urlSuffix + '/' + email);
resource = LoginData.get({},
function () {
$location.url('/home');
$window.location.href = $location.absUrl();
});
}
$scope.login = function () {
if ($scope.email && $scope.password) {
getResource($scope.email);
$scope.shared.login = $scope.email;
}
};
$log.debug('Cord Login Ctrl has been created.');
}]);
}());
<!--Partial HTML for rating panel directive-->
<div id="rating-panel">
<div ng-cloak class="ng-hide ng-cloak panel" ng-show="ratingsShown">
<table>
<tr>
<th class="title">Category</th>
<th ng-repeat="rating in level_order">{{rating}}</th>
</tr>
<tr ng-repeat="cat in category_order">
<td class="title">{{cat}}</td>
<td ng-repeat="r in level_order">
<div ng-if="prohibitedSites[r][cat]">
<icon size="15" id="xMark"></icon>
</div>
<div ng-if="!prohibitedSites[r][cat]">
<icon size="15" id="checkMark"></icon>
</div>
</td>
</tr>
</table>
</div>
</div>
\ No newline at end of file
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#user div {
padding-top: 2%;
}
#user div.main-left {
width: 98%;
padding-left: 1%;
}
#user div.main-left.family {
width: 62%;
padding-left: 1%;
}
#user div.main-right {
width: 0;
}
#user div.main-right.family {
width: 37%;
}
#user table.user-info,
#user table.user-form {
float: left;
width: 100%;
}
#user table.user-info th,
#user table.user-form th {
text-align: left;
padding: 2% 1%;
}
#user span.help:hover {
cursor: pointer;
color: #CE5650;
}
#user div.main-left.family table.user-info th,
#user div.main-right.family table.user-form th {
padding: 17px;
}
#user div.main-left.family table.user-info td,
#user div.main-right.family table.user-form td {
padding: 10px;
height: 23px;
}
#user table.user-info td {
padding: 1%;
}
#user table.user-form td {
border-left: 1px solid #CE5650;
}
#user table.user-form td.buttons {
text-align: right;
border: none;
}
#user table.user-form tr.options td {
padding-left: 5%;
}
#user select,
#user select:focus {
border: none;
}
#user select {
font-size: 95%;
}
#user option,
#user option:focus {
border: none;
}
#user option[selected] {
background-color: rgb(122, 188, 229);
}
#user label {
font-weight: bold;
display: block;
text-align: center;
padding: 5%;
}
#user input[type="button"],
#user input[type="reset"] {
width: 30%;
}
#user td.buttons div {
display: inline;
}
#user td.buttons svg {
vertical-align: middle;
}
#rating-panel th,
#rating-panel td {
text-align: center;
padding: 1%;
font-weight: lighter;
}
#rating-panel th.title,
#rating-panel td.title {
width: 125px;
text-align: left;
}
#rating-panel th {
background-color: white;
padding-top: 3%;
border-bottom: 1px solid #CE5650;
color: #3C3C3C;
font-weight: normal;
}
#rating-panel tr th:first-child,
#rating-panel tr td:first-child {
padding-left: 5%;
}
#rating-panel tr th:last-child,
#rating-panel tr td:last-child {
padding-right: 5%;
}
div#rating-panel {
position: relative;
pointer-events: none;
}
#rating-panel div.ng-hide-add.ng-hide-add-active,
#rating-panel div.ng-hide-remove.ng-hide-remove-active {
-webkit-transition: all linear 0.75s;
transition: all linear 0.75s;
}
#rating-panel div.panel {
position: absolute;
top: 0;
left: -6%;
height: 545px;
overflow: auto;
padding: 0;
pointer-events: auto;
box-shadow: 0 3px 23px 7px rgb(118, 118, 118);
border-radius: 3px;
}
#rating-panel table {
table-layout: fixed;
width: 500px;
background-color: white;
opacity: 1;
}
#rating-panel div.ng-hide {
opacity: 0;
left: -55%;
}
<!-- Users page partial html -->
<div class="container">
<div id="user">
<div class="main-left" ng-class="{family: isFamily}">
<table class="user-info">
<tr>
<th class="user-pic"></th>
<th>Name</th>
<th>Last Login</th>
</tr>
<tr ng-repeat="user in users" class="fadein">
<td class="user-pic">
<img ng-src="{{'/imgs/' + user.icon_id + '.jpg'}}">
</td>
<td>{{user.name}}</td>
<td>{{shared.userActivity[user.id]}}</td>
</tr>
</table>
</div>
<div class="main-right" ng-class="{family: isFamily}">
<form ng-if="isFamily"
name="changeLevels">
<table class="user-form">
<tr>
<th>
Select Site Rating
<span class="help"
ng-click="showRatings()"> (?)</span>
</th>
</tr>
<tr ng-repeat="user in users" class="options">
<td>
<select ng-init="newLevels[user.id]=user.profile.url_filter.level"
ng-model="newLevels[user.id]"
ng-options="l for l in levels">
</select>
</td>
</tr>
<tr>
<td class="buttons">
<div ng-show="showCheck">
<icon size="20px" id="checkMark"></icon>
</div>
<input type="reset" value="Cancel"
ng-click="cancelChanges(changeLevels)"
ng-disabled="changeLevels.$pristine">
<input type="button" value="Apply"
ng-click="applyChanges(changeLevels)"
ng-disabled="changeLevels.$pristine">
</td>
</tr>
</table>
</form>
</div>
<div ng-if="isFamily">
<ratings-panel></ratings-panel>
</div>
</div>
</div>
\ No newline at end of file
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function () {
'use strict';
var bundleUrlSuffix = '/rs/bundle',
userUrlSuffix = '/rs/users',
family = 'family',
url_filter = 'url_filter';
angular.module('cordUser', [])
.controller('CordUserCtrl', ['$log', '$scope', '$resource', '$timeout',
function ($log, $scope, $resource, $timeout) {
var BundleData, bundleResource;
$scope.page.curr = 'user';
$scope.isFamily = false;
$scope.newLevels = {};
$scope.showCheck = false;
$scope.ratingsShown = false;
// === Get data functions ---
BundleData = $resource($scope.shared.url + bundleUrlSuffix);
bundleResource = BundleData.get({},
// success
function () {
var result;
$scope.isFamily = (bundleResource.bundle.id === family);
if ($scope.isFamily) {
result = $.grep(
bundleResource.bundle.functions,
function (elem) {
if (elem.id === url_filter) { return true; }
}
);
$scope.levels = result[0].params.levels;
}
},
// error
function () {
$log.error('Problem with resource', bundleResource);
}
);
function getUsers(url) {
var UserData, userResource;
UserData = $resource(url);
userResource = UserData.get({},
// success
function () {
$scope.users = userResource.users;
},
// error
function () {
$log.error('Problem with resource', userResource);
}
);
}
getUsers($scope.shared.url + userUrlSuffix);
// === Form functions ---
function levelUrl(id, level) {
return $scope.shared.url +
userUrlSuffix + '/' + id + '/apply/url_filter/level/' + level;
}
$scope.applyChanges = function (changeLevels) {
var requests = [];
if ($scope.users) {
$.each($scope.users, function (index, user) {
var id = user.id,
level = user.profile.url_filter.level;
if ($scope.newLevels[id] !== level) {
requests.push(levelUrl(id, $scope.newLevels[id]));
}
});
$.each(requests, function (index, req) {
getUsers(req);
});
}
changeLevels.$setPristine();
$scope.showCheck = true;
$timeout(function () {
$scope.showCheck = false;
}, 3000);
};
$scope.cancelChanges = function (changeLevels) {
if ($scope.users) {
$.each($scope.users, function (index, user) {
$scope.newLevels[user.id] = user.profile.url_filter.level;
});
}
changeLevels.$setPristine();
$scope.showCheck = false;
};
$scope.showRatings = function () {
$scope.ratingsShown = !$scope.ratingsShown;
};
$log.debug('Cord User Ctrl has been created.');
}])
.directive('ratingsPanel', ['$log', function ($log) {
return {
templateUrl: 'app/view/user/ratingPanel.html',
link: function (scope, elem, attrs) {
function fillSubMap(order, bool) {
var result = {};
$.each(order, function (index, cat) {
result[cat] = bool;
});
return result;
}
function processSubMap(prhbSites) {
var result = {};
$.each(prhbSites, function (index, cat) {
result[cat] = true;
});
return result;
}
function preprocess(data, order) {
return {
ALL: fillSubMap(order, false),
G: processSubMap(data.G),
PG: processSubMap(data.PG),
PG_13: processSubMap(data.PG_13),
R: processSubMap(data.R),
NONE: fillSubMap(order, true)
};
}
$.getJSON('/app/data/pc_cats.json', function (data) {
scope.level_order = data.level_order;
scope.category_order = data.category_order;
scope.prohibitedSites = preprocess(
data.prohibited, data.category_order
);
scope.$apply();
});
}
};
}]);
}());
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function () {
'use strict';
var modules = [
'ngRoute',
'ngResource',
'ngAnimate',
'cordMast',
'cordFoot',
'cordNav'
],
viewIds = [
'login',
'home',
'user',
'bundle'
],
viewDependencies = [],
dependencies;
function capitalize(word) {
return word ? word[0].toUpperCase() + word.slice(1) : word;
}
viewIds.forEach(function (id) {
if (id) {
viewDependencies.push('cord' + capitalize(id));
}
});
dependencies = modules.concat(viewDependencies);
angular.module('cordGui', dependencies)
.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.otherwise({
redirectTo: '/login'
});
function viewCtrlName(vid) {
return 'Cord' + capitalize(vid) + 'Ctrl';
}
function viewTemplateUrl(vid) {
return 'app/view/' + vid + '/' + vid + '.html';
}
viewIds.forEach(function (vid) {
if (vid) {
$routeProvider.when('/' + vid, {
controller: viewCtrlName(vid),
controllerAs: 'ctrl',
templateUrl: viewTemplateUrl(vid)
});
}
});
}])
.controller('CordCtrl', ['$scope', '$location',
function ($scope, $location) {
$scope.shared = {
url: 'http://' + $location.host() + ':' + $location.port(),
userActivity: {}
};
$scope.page = {};
}]);
}());
<!DOCTYPE html>
<!--
~ Copyright 2015-present Open Networking Laboratory
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
-->
<html>
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="imgs/bird.png">
<title>CORD Subscriber Portal</title>
<script src="tp/angular.js"></script>
<script src="tp/angular-route.js"></script>
<script src="tp/angular-animate.js"></script>
<script src="tp/angular-resource.js"></script>
<script src="tp/jquery-2.1.4.js"></script>
<script src="cord.js"></script>
<link rel="stylesheet" href="app/view/common/common.css">
<script src="app/fw/mast/mast.js"></script>
<link rel="stylesheet" href="app/fw/mast/mast.css">
<script src="app/fw/foot/foot.js"></script>
<link rel="stylesheet" href="app/fw/foot/foot.css">
<script src="app/fw/nav/nav.js"></script>
<link rel="stylesheet" href="app/fw/nav/nav.css">
<script src="app/fw/icon/icon.js"></script>
<script src="app/view/login/login.js"></script>
<link rel="stylesheet" href="app/view/login/login.css">
<script src="app/view/home/home.js"></script>
<link rel="stylesheet" href="app/view/home/home.css">
<script src="app/view/user/user.js"></script>
<link rel="stylesheet" href="app/view/user/user.css">
<script src="app/view/bundle/bundle.js"></script>
<link rel="stylesheet" href="app/view/bundle/bundle.css">
</head>
<body ng-app="cordGui">
<div id="frame" ng-controller="CordCtrl as cordCtrl">
<mast></mast>
<foot></foot>
<div id="view" ng-view></div>
<svg id="icon-defs">
<defs>
<symbol id="bird" viewBox="352 224 113 112">
<path d="M427.7,300.4 c-6.9,0.6-13.1,5-19.2,7.1c-18.1,6.2-33.9,
9.1-56.5,4.7c24.6,17.2,36.6,13,63.7,0.1c-0.5,0.6-0.7,1.3-1.3,
1.9c1.4-0.4,2.4-1.7,3.4-2.2c-0.4,0.7-0.9,1.5-1.4,1.9c2.2-0.6,
3.7-2.3,5.9-3.9c-2.4,2.1-4.2,5-6,8c-1.5,2.5-3.1,4.8-5.1,6.9c-1,
1-1.9,1.9-2.9,2.9c-1.4,1.3-2.9,2.5-5.1,2.9c1.7,0.1,3.6-0.3,6.5
-1.9c-1.6,2.4-7.1,6.2-9.9,7.2c10.5-2.6,19.2-15.9,25.7-18c18.3
-5.9,13.8-3.4,27-14.2c1.6-1.3,3-1,5.1-0.8c1.1,0.1,2.1,0.3,3.2,
0.5c0.8,0.2,1.4,0.4,2.2,0.8l1.8,0.9c-1.9-4.5-2.3-4.1-5.9-6c-2.3
-1.3-3.3-3.8-6.2-4.9c-7.1-2.6-11.9,11.7-11.7-5c0.1-8,4.2-14.4,
6.4-22c1.1-3.8,2.3-7.6,2.4-11.5c0.1-2.3,0-4.7-0.4-7c-2-11.2-8.4
-21.5-19.7-24.8c-1-0.3-1.1-0.3-0.9,0c9.6,17.1,7.2,38.3,3.1,54.2
C429.9,285.5,426.7,293.2,427.7,300.4z"></path>
</symbol>
<symbol id="checkMark" viewBox="0 0 10 10">
<path d="M2.6,4.5c0,0,0.7-0.4,1.2,0.3l1.0,1.8c0,0,2.7-5.4,2.8-5.7c
0,0,0.5-0.9,1.4-0.1c0,0,0.5,0.5,0,1.3S6.8,7.3,5.6,9.2c0,0-0.4,0.5
-1.2,0.1S2.2,5.4,2.2,5.4S2.2,4.7,2.6,4.5z"></path>
</symbol>
<symbol id="xMark" viewBox="0 0 10 10">
<path d="M9.0,7.2C8.2,6.9,7.4,6.1,6.7,5.2c0.4-0.5,0.7-0.8,0.8-1.0C
7.8,3.5,9.4,1.6,8.1,1.1C6.8,0.6,6.6,1.7,6.6,1.7C6.4,2.1,6.0,2.7,
5.4,3.4C4.9,2.5,4.5,1.9,4.5,1.9S3.8,0.2,2.9,0.7C1.9,1.1,2.3,2.3,
2.3,2.3c0.3,1.1,0.8,2.1,1.4,2.9C2.5,6.4,1.3,7.4,1.3,7.4S0.8,7.8,
0.8,8.1C0.9,8.3,0.9,9.6,2.4,9.1C3.1,8.8,4.1,7.9,5.1,7.0c1.3,1.3,
2.5,1.9,2.5,1.9s0.5,0.5,1.4-0.2C9.8,7.9,9.0,7.2,9.0,7.2z"></path>
</symbol>
</defs>
</svg>
</div>
</body>
</html>
This diff could not be displayed because it is too large.
/**
* @license AngularJS v1.3.5
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
var $resourceMinErr = angular.$$minErr('$resource');
// Helper functions and regex to lookup a dotted path on an object
// stopping at undefined/null. The path must be composed of ASCII
// identifiers (just like $parse)
var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$][0-9a-zA-Z_$]*)+$/;
function isValidDottedPath(path) {
return (path != null && path !== '' && path !== 'hasOwnProperty' &&
MEMBER_NAME_REGEX.test('.' + path));
}
function lookupDottedPath(obj, path) {
if (!isValidDottedPath(path)) {
throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
}
var keys = path.split('.');
for (var i = 0, ii = keys.length; i < ii && obj !== undefined; i++) {
var key = keys[i];
obj = (obj !== null) ? obj[key] : undefined;
}
return obj;
}
/**
* Create a shallow copy of an object and clear other fields from the destination
*/
function shallowClearAndCopy(src, dst) {
dst = dst || {};
angular.forEach(dst, function(value, key) {
delete dst[key];
});
for (var key in src) {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
return dst;
}
/**
* @ngdoc module
* @name ngResource
* @description
*
* # ngResource
*
* The `ngResource` module provides interaction support with RESTful services
* via the $resource service.
*
*
* <div doc-module-components="ngResource"></div>
*
* See {@link ngResource.$resource `$resource`} for usage.
*/
/**
* @ngdoc service
* @name $resource
* @requires $http
*
* @description
* A factory which creates a resource object that lets you interact with
* [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
*
* The returned resource object has action methods which provide high-level behaviors without
* the need to interact with the low level {@link ng.$http $http} service.
*
* Requires the {@link ngResource `ngResource`} module to be installed.
*
* By default, trailing slashes will be stripped from the calculated URLs,
* which can pose problems with server backends that do not expect that
* behavior. This can be disabled by configuring the `$resourceProvider` like
* this:
*
* ```js
app.config(['$resourceProvider', function($resourceProvider) {
// Don't strip trailing slashes from calculated URLs
$resourceProvider.defaults.stripTrailingSlashes = false;
}]);
* ```
*
* @param {string} url A parametrized URL template with parameters prefixed by `:` as in
* `/user/:username`. If you are using a URL with a port number (e.g.
* `http://example.com:8080/api`), it will be respected.
*
* If you are using a url with a suffix, just add the suffix, like this:
* `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
* or even `$resource('http://example.com/resource/:resource_id.:format')`
* If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
* collapsed down to a single `.`. If you need this sequence to appear and not collapse then you
* can escape it with `/\.`.
*
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
* `actions` methods. If any of the parameter value is a function, it will be executed every time
* when a param value needs to be obtained for a request (unless the param was overridden).
*
* Each key value in the parameter object is first bound to url template if present and then any
* excess keys are appended to the url search query after the `?`.
*
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
* URL `/path/greet?salutation=Hello`.
*
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
* from the corresponding property on the `data` object (provided when calling an action method). For
* example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
* will be `data.someProp`.
*
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
* the default set of resource actions. The declaration should be created in the format of {@link
* ng.$http#usage $http.config}:
*
* {action1: {method:?, params:?, isArray:?, headers:?, ...},
* action2: {method:?, params:?, isArray:?, headers:?, ...},
* ...}
*
* Where:
*
* - **`action`** – {string} – The name of action. This name becomes the name of the method on
* your resource object.
* - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
* `DELETE`, `JSONP`, etc).
* - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
* the parameter value is a function, it will be executed every time when a param value needs to
* be obtained for a request (unless the param was overridden).
* - **`url`** – {string} – action specific `url` override. The url templating is supported just
* like for the resource-level urls.
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
* see `returns` section.
* - **`transformRequest`** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
* transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version.
* By default, transformRequest will contain one function that checks if the request data is
* an object and serializes to using `angular.toJson`. To prevent this behavior, set
* `transformRequest` to an empty array: `transformRequest: []`
* - **`transformResponse`** –
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
* transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version.
* By default, transformResponse will contain one function that checks if the response looks like
* a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
* `transformResponse` to an empty array: `transformResponse: []`
* - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
* GET request, otherwise if a cache instance built with
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
* caching.
* - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
* should abort the request when resolved.
* - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
* XHR object. See
* [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
* for more information.
* - **`responseType`** - `{string}` - see
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
* - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
* `response` and `responseError`. Both `response` and `responseError` interceptors get called
* with `http response` object. See {@link ng.$http $http interceptors}.
*
* @param {Object} options Hash with custom settings that should extend the
* default `$resourceProvider` behavior. The only supported option is
*
* Where:
*
* - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
* slashes from any calculated URL will be stripped. (Defaults to true.)
*
* @returns {Object} A resource "class" object with methods for the default set of resource actions
* optionally extended with custom `actions`. The default set contains these actions:
* ```js
* { 'get': {method:'GET'},
* 'save': {method:'POST'},
* 'query': {method:'GET', isArray:true},
* 'remove': {method:'DELETE'},
* 'delete': {method:'DELETE'} };
* ```
*
* Calling these methods invoke an {@link ng.$http} with the specified http method,
* destination and parameters. When the data is returned from the server then the object is an
* instance of the resource class. The actions `save`, `remove` and `delete` are available on it
* as methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
* read, update, delete) on server-side data like this:
* ```js
* var User = $resource('/user/:userId', {userId:'@id'});
* var user = User.get({userId:123}, function() {
* user.abc = true;
* user.$save();
* });
* ```
*
* It is important to realize that invoking a $resource object method immediately returns an
* empty reference (object or array depending on `isArray`). Once the data is returned from the
* server the existing reference is populated with the actual data. This is a useful trick since
* usually the resource is assigned to a model which is then rendered by the view. Having an empty
* object results in no rendering, once the data arrives from the server then the object is
* populated with the data and the view automatically re-renders itself showing the new data. This
* means that in most cases one never has to write a callback function for the action methods.
*
* The action methods on the class object or instance object can be invoked with the following
* parameters:
*
* - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
* - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
*
* Success callback is called with (value, responseHeaders) arguments. Error callback is called
* with (httpResponse) argument.
*
* Class actions return empty instance (with additional properties below).
* Instance actions return promise of the action.
*
* The Resource instances and collection have these additional properties:
*
* - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
* instance or collection.
*
* On success, the promise is resolved with the same resource instance or collection object,
* updated with data from server. This makes it easy to use in
* {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
* rendering until the resource(s) are loaded.
*
* On failure, the promise is resolved with the {@link ng.$http http response} object, without
* the `resource` property.
*
* If an interceptor object was provided, the promise will instead be resolved with the value
* returned by the interceptor.
*
* - `$resolved`: `true` after first server interaction is completed (either with success or
* rejection), `false` before that. Knowing if the Resource has been resolved is useful in
* data-binding.
*
* @example
*
* # Credit card resource
*
* ```js
// Define CreditCard class
var CreditCard = $resource('/user/:userId/card/:cardId',
{userId:123, cardId:'@id'}, {
charge: {method:'POST', params:{charge:true}}
});
// We can retrieve a collection from the server
var cards = CreditCard.query(function() {
// GET: /user/123/card
// server returns: [ {id:456, number:'1234', name:'Smith'} ];
var card = cards[0];
// each item is an instance of CreditCard
expect(card instanceof CreditCard).toEqual(true);
card.name = "J. Smith";
// non GET methods are mapped onto the instances
card.$save();
// POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
// server returns: {id:456, number:'1234', name: 'J. Smith'};
// our custom method is mapped as well.
card.$charge({amount:9.99});
// POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
});
// we can create an instance as well
var newCard = new CreditCard({number:'0123'});
newCard.name = "Mike Smith";
newCard.$save();
// POST: /user/123/card {number:'0123', name:'Mike Smith'}
// server returns: {id:789, number:'0123', name: 'Mike Smith'};
expect(newCard.id).toEqual(789);
* ```
*
* The object returned from this function execution is a resource "class" which has "static" method
* for each action in the definition.
*
* Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
* `headers`.
* When the data is returned from the server then the object is an instance of the resource type and
* all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
* operations (create, read, update, delete) on server-side data.
```js
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123}, function(user) {
user.abc = true;
user.$save();
});
```
*
* It's worth noting that the success callback for `get`, `query` and other methods gets passed
* in the response that came from the server as well as $http header getter function, so one
* could rewrite the above example and get access to http headers as:
*
```js
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123}, function(u, getResponseHeaders){
u.abc = true;
u.$save(function(u, putResponseHeaders) {
//u => saved user object
//putResponseHeaders => $http header getter
});
});
```
*
* You can also access the raw `$http` promise via the `$promise` property on the object returned
*
```
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123})
.$promise.then(function(user) {
$scope.user = user;
});
```
* # Creating a custom 'PUT' request
* In this example we create a custom method on our resource to make a PUT request
* ```js
* var app = angular.module('app', ['ngResource', 'ngRoute']);
*
* // Some APIs expect a PUT request in the format URL/object/ID
* // Here we are creating an 'update' method
* app.factory('Notes', ['$resource', function($resource) {
* return $resource('/notes/:id', null,
* {
* 'update': { method:'PUT' }
* });
* }]);
*
* // In our controller we get the ID from the URL using ngRoute and $routeParams
* // We pass in $routeParams and our Notes factory along with $scope
* app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
function($scope, $routeParams, Notes) {
* // First get a note object from the factory
* var note = Notes.get({ id:$routeParams.id });
* $id = note.id;
*
* // Now call update passing in the ID first then the object you are updating
* Notes.update({ id:$id }, note);
*
* // This will PUT /notes/ID with the note object in the request payload
* }]);
* ```
*/
angular.module('ngResource', ['ng']).
provider('$resource', function() {
var provider = this;
this.defaults = {
// Strip slashes by default
stripTrailingSlashes: true,
// Default actions configuration
actions: {
'get': {method: 'GET'},
'save': {method: 'POST'},
'query': {method: 'GET', isArray: true},
'remove': {method: 'DELETE'},
'delete': {method: 'DELETE'}
}
};
this.$get = ['$http', '$q', function($http, $q) {
var noop = angular.noop,
forEach = angular.forEach,
extend = angular.extend,
copy = angular.copy,
isFunction = angular.isFunction;
/**
* We need our custom method because encodeURIComponent is too aggressive and doesn't follow
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
* (pchar) allowed in path segments:
* segment = *pchar
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* pct-encoded = "%" HEXDIG HEXDIG
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriSegment(val) {
return encodeUriQuery(val, true).
replace(/%26/gi, '&').
replace(/%3D/gi, '=').
replace(/%2B/gi, '+');
}
/**
* This method is intended for encoding *key* or *value* parts of query component. We need a
* custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
* have to be encoded per http://tools.ietf.org/html/rfc3986:
* query = *( pchar / "/" / "?" )
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
* pct-encoded = "%" HEXDIG HEXDIG
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
* / "*" / "+" / "," / ";" / "="
*/
function encodeUriQuery(val, pctEncodeSpaces) {
return encodeURIComponent(val).
replace(/%40/gi, '@').
replace(/%3A/gi, ':').
replace(/%24/g, '$').
replace(/%2C/gi, ',').
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
}
function Route(template, defaults) {
this.template = template;
this.defaults = extend({}, provider.defaults, defaults);
this.urlParams = {};
}
Route.prototype = {
setUrlParams: function(config, params, actionUrl) {
var self = this,
url = actionUrl || self.template,
val,
encodedVal;
var urlParams = self.urlParams = {};
forEach(url.split(/\W/), function(param) {
if (param === 'hasOwnProperty') {
throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
}
if (!(new RegExp("^\\d+$").test(param)) && param &&
(new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
urlParams[param] = true;
}
});
url = url.replace(/\\:/g, ':');
params = params || {};
forEach(self.urlParams, function(_, urlParam) {
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
if (angular.isDefined(val) && val !== null) {
encodedVal = encodeUriSegment(val);
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
return encodedVal + p1;
});
} else {
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
leadingSlashes, tail) {
if (tail.charAt(0) == '/') {
return tail;
} else {
return leadingSlashes + tail;
}
});
}
});
// strip trailing slashes and set the url (unless this behavior is specifically disabled)
if (self.defaults.stripTrailingSlashes) {
url = url.replace(/\/+$/, '') || '/';
}
// then replace collapse `/.` if found in the last URL path segment before the query
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
// replace escaped `/\.` with `/.`
config.url = url.replace(/\/\\\./, '/.');
// set params - delegate param encoding to $http
forEach(params, function(value, key) {
if (!self.urlParams[key]) {
config.params = config.params || {};
config.params[key] = value;
}
});
}
};
function resourceFactory(url, paramDefaults, actions, options) {
var route = new Route(url, options);
actions = extend({}, provider.defaults.actions, actions);
function extractParams(data, actionParams) {
var ids = {};
actionParams = extend({}, paramDefaults, actionParams);
forEach(actionParams, function(value, key) {
if (isFunction(value)) { value = value(); }
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
lookupDottedPath(data, value.substr(1)) : value;
});
return ids;
}
function defaultResponseInterceptor(response) {
return response.resource;
}
function Resource(value) {
shallowClearAndCopy(value || {}, this);
}
Resource.prototype.toJSON = function() {
var data = extend({}, this);
delete data.$promise;
delete data.$resolved;
return data;
};
forEach(actions, function(action, name) {
var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
Resource[name] = function(a1, a2, a3, a4) {
var params = {}, data, success, error;
/* jshint -W086 */ /* (purposefully fall through case statements) */
switch (arguments.length) {
case 4:
error = a4;
success = a3;
//fallthrough
case 3:
case 2:
if (isFunction(a2)) {
if (isFunction(a1)) {
success = a1;
error = a2;
break;
}
success = a2;
error = a3;
//fallthrough
} else {
params = a1;
data = a2;
success = a3;
break;
}
case 1:
if (isFunction(a1)) success = a1;
else if (hasBody) data = a1;
else params = a1;
break;
case 0: break;
default:
throw $resourceMinErr('badargs',
"Expected up to 4 arguments [params, data, success, error], got {0} arguments",
arguments.length);
}
/* jshint +W086 */ /* (purposefully fall through case statements) */
var isInstanceCall = this instanceof Resource;
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
var httpConfig = {};
var responseInterceptor = action.interceptor && action.interceptor.response ||
defaultResponseInterceptor;
var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
undefined;
forEach(action, function(value, key) {
if (key != 'params' && key != 'isArray' && key != 'interceptor') {
httpConfig[key] = copy(value);
}
});
if (hasBody) httpConfig.data = data;
route.setUrlParams(httpConfig,
extend({}, extractParams(data, action.params || {}), params),
action.url);
var promise = $http(httpConfig).then(function(response) {
var data = response.data,
promise = value.$promise;
if (data) {
// Need to convert action.isArray to boolean in case it is undefined
// jshint -W018
if (angular.isArray(data) !== (!!action.isArray)) {
throw $resourceMinErr('badcfg',
'Error in resource configuration for action `{0}`. Expected response to ' +
'contain an {1} but got an {2}', name, action.isArray ? 'array' : 'object',
angular.isArray(data) ? 'array' : 'object');
}
// jshint +W018
if (action.isArray) {
value.length = 0;
forEach(data, function(item) {
if (typeof item === "object") {
value.push(new Resource(item));
} else {
// Valid JSON values may be string literals, and these should not be converted
// into objects. These items will not have access to the Resource prototype
// methods, but unfortunately there
value.push(item);
}
});
} else {
shallowClearAndCopy(data, value);
value.$promise = promise;
}
}
value.$resolved = true;
response.resource = value;
return response;
}, function(response) {
value.$resolved = true;
(error || noop)(response);
return $q.reject(response);
});
promise = promise.then(
function(response) {
var value = responseInterceptor(response);
(success || noop)(value, response.headers);
return value;
},
responseErrorInterceptor);
if (!isInstanceCall) {
// we are creating instance / collection
// - set the initial promise
// - return the instance / collection
value.$promise = promise;
value.$resolved = false;
return value;
}
// instance call
return promise;
};
Resource.prototype['$' + name] = function(params, success, error) {
if (isFunction(params)) {
error = success; success = params; params = {};
}
var result = Resource[name].call(this, params, this, success, error);
return result.$promise || result;
};
});
Resource.bind = function(additionalParamDefaults) {
return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
};
return Resource;
}
return resourceFactory;
}];
});
})(window, window.angular);
/**
* @license AngularJS v1.3.5
* (c) 2010-2014 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {'use strict';
/**
* @ngdoc module
* @name ngRoute
* @description
*
* # ngRoute
*
* The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
*
* <div doc-module-components="ngRoute"></div>
*/
/* global -ngRouteModule */
var ngRouteModule = angular.module('ngRoute', ['ng']).
provider('$route', $RouteProvider),
$routeMinErr = angular.$$minErr('ngRoute');
/**
* @ngdoc provider
* @name $routeProvider
*
* @description
*
* Used for configuring routes.
*
* ## Example
* See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
*
* ## Dependencies
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*/
function $RouteProvider() {
function inherit(parent, extra) {
return angular.extend(Object.create(parent), extra);
}
var routes = {};
/**
* @ngdoc method
* @name $routeProvider#when
*
* @param {string} path Route path (matched against `$location.path`). If `$location.path`
* contains redundant trailing slash or is missing one, the route will still match and the
* `$location.path` will be updated to add or drop the trailing slash to exactly match the
* route definition.
*
* * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
* to the next slash are matched and stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain named groups starting with a colon and ending with a star:
* e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
* when the route matches.
* * `path` can contain optional named groups with a question mark: e.g.`:name?`.
*
* For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
* `/color/brown/largecode/code/with/slashes/edit` and extract:
*
* * `color: brown`
* * `largecode: code/with/slashes`.
*
*
* @param {Object} route Mapping information to be assigned to `$route.current` on route
* match.
*
* Object properties:
*
* - `controller` – `{(string|function()=}` – Controller fn that should be associated with
* newly created scope or the name of a {@link angular.Module#controller registered
* controller} if passed as a string.
* - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
* published to scope under the `controllerAs` name.
* - `template` – `{string=|function()=}` – html template as a string or a function that
* returns an html template as a string which should be used by {@link
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
* This property takes precedence over `templateUrl`.
*
* If `template` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
* template that should be used by {@link ngRoute.directive:ngView ngView}.
*
* If `templateUrl` is a function, it will be called with the following parameters:
*
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
* be injected into the controller. If any of these dependencies are promises, the router
* will wait for them all to be resolved or one to be rejected before the controller is
* instantiated.
* If all the promises are resolved successfully, the values of the resolved promises are
* injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
* fired. If any of the promises are rejected the
* {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
* is:
*
* - `key` – `{string}`: a name of a dependency to be injected into the controller.
* - `factory` - `{string|function}`: If `string` then it is an alias for a service.
* Otherwise if function, then it is {@link auto.$injector#invoke injected}
* and the return value is treated as the dependency. If the result is a promise, it is
* resolved before its value is injected into the controller. Be aware that
* `ngRoute.$routeParams` will still refer to the previous route within these resolve
* functions. Use `$route.current.params` to access the new route parameters, instead.
*
* - `redirectTo` – {(string|function())=} – value to update
* {@link ng.$location $location} path with and trigger route redirection.
*
* If `redirectTo` is a function, it will be called with the following parameters:
*
* - `{Object.<string>}` - route parameters extracted from the current
* `$location.path()` by applying the current route templateUrl.
* - `{string}` - current `$location.path()`
* - `{Object}` - current `$location.search()`
*
* The custom `redirectTo` function is expected to return a string which will be used
* to update `$location.path()` and `$location.search()`.
*
* - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
* or `$location.hash()` changes.
*
* If the option is set to `false` and url in the browser changes, then
* `$routeUpdate` event is broadcasted on the root scope.
*
* - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
*
* If the option is set to `true`, then the particular route can be matched without being
* case sensitive
*
* @returns {Object} self
*
* @description
* Adds a new route definition to the `$route` service.
*/
this.when = function(path, route) {
//copy original route object to preserve params inherited from proto chain
var routeCopy = angular.copy(route);
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
routeCopy.reloadOnSearch = true;
}
if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
}
routes[path] = angular.extend(
routeCopy,
path && pathRegExp(path, routeCopy)
);
// create redirection for trailing slashes
if (path) {
var redirectPath = (path[path.length - 1] == '/')
? path.substr(0, path.length - 1)
: path + '/';
routes[redirectPath] = angular.extend(
{redirectTo: path},
pathRegExp(redirectPath, routeCopy)
);
}
return this;
};
/**
* @ngdoc property
* @name $routeProvider#caseInsensitiveMatch
* @description
*
* A boolean property indicating if routes defined
* using this provider should be matched using a case insensitive
* algorithm. Defaults to `false`.
*/
this.caseInsensitiveMatch = false;
/**
* @param path {string} path
* @param opts {Object} options
* @return {?Object}
*
* @description
* Normalizes the given path, returning a regular expression
* and the original path.
*
* Inspired by pathRexp in visionmedia/express/lib/utils.js.
*/
function pathRegExp(path, opts) {
var insensitive = opts.caseInsensitiveMatch,
ret = {
originalPath: path,
regexp: path
},
keys = ret.keys = [];
path = path
.replace(/([().])/g, '\\$1')
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option) {
var optional = option === '?' ? option : null;
var star = option === '*' ? option : null;
keys.push({ name: key, optional: !!optional });
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (star && '(.+?)' || '([^/]+)')
+ (optional || '')
+ ')'
+ (optional || '');
})
.replace(/([\/$\*])/g, '\\$1');
ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
return ret;
}
/**
* @ngdoc method
* @name $routeProvider#otherwise
*
* @description
* Sets route definition that will be used on route change when no other route definition
* is matched.
*
* @param {Object|string} params Mapping information to be assigned to `$route.current`.
* If called with a string, the value maps to `redirectTo`.
* @returns {Object} self
*/
this.otherwise = function(params) {
if (typeof params === 'string') {
params = {redirectTo: params};
}
this.when(null, params);
return this;
};
this.$get = ['$rootScope',
'$location',
'$routeParams',
'$q',
'$injector',
'$templateRequest',
'$sce',
function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
/**
* @ngdoc service
* @name $route
* @requires $location
* @requires $routeParams
*
* @property {Object} current Reference to the current route definition.
* The route definition contains:
*
* - `controller`: The controller constructor as define in route definition.
* - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
* controller instantiation. The `locals` contain
* the resolved values of the `resolve` map. Additionally the `locals` also contain:
*
* - `$scope` - The current route scope.
* - `$template` - The current route template HTML.
*
* @property {Object} routes Object with all route configuration Objects as its properties.
*
* @description
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
* It watches `$location.url()` and tries to map the path to an existing route definition.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
*
* The `$route` service is typically used in conjunction with the
* {@link ngRoute.directive:ngView `ngView`} directive and the
* {@link ngRoute.$routeParams `$routeParams`} service.
*
* @example
* This example shows how changing the URL hash causes the `$route` to match a route against the
* URL, and the `ngView` pulls in the partial.
*
* <example name="$route-service" module="ngRouteExample"
* deps="angular-route.js" fixBase="true">
* <file name="index.html">
* <div ng-controller="MainController">
* Choose:
* <a href="Book/Moby">Moby</a> |
* <a href="Book/Moby/ch/1">Moby: Ch1</a> |
* <a href="Book/Gatsby">Gatsby</a> |
* <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
* <a href="Book/Scarlet">Scarlet Letter</a><br/>
*
* <div ng-view></div>
*
* <hr />
*
* <pre>$location.path() = {{$location.path()}}</pre>
* <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
* <pre>$route.current.params = {{$route.current.params}}</pre>
* <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
* <pre>$routeParams = {{$routeParams}}</pre>
* </div>
* </file>
*
* <file name="book.html">
* controller: {{name}}<br />
* Book Id: {{params.bookId}}<br />
* </file>
*
* <file name="chapter.html">
* controller: {{name}}<br />
* Book Id: {{params.bookId}}<br />
* Chapter Id: {{params.chapterId}}
* </file>
*
* <file name="script.js">
* angular.module('ngRouteExample', ['ngRoute'])
*
* .controller('MainController', function($scope, $route, $routeParams, $location) {
* $scope.$route = $route;
* $scope.$location = $location;
* $scope.$routeParams = $routeParams;
* })
*
* .controller('BookController', function($scope, $routeParams) {
* $scope.name = "BookController";
* $scope.params = $routeParams;
* })
*
* .controller('ChapterController', function($scope, $routeParams) {
* $scope.name = "ChapterController";
* $scope.params = $routeParams;
* })
*
* .config(function($routeProvider, $locationProvider) {
* $routeProvider
* .when('/Book/:bookId', {
* templateUrl: 'book.html',
* controller: 'BookController',
* resolve: {
* // I will cause a 1 second delay
* delay: function($q, $timeout) {
* var delay = $q.defer();
* $timeout(delay.resolve, 1000);
* return delay.promise;
* }
* }
* })
* .when('/Book/:bookId/ch/:chapterId', {
* templateUrl: 'chapter.html',
* controller: 'ChapterController'
* });
*
* // configure html5 to get links working on jsfiddle
* $locationProvider.html5Mode(true);
* });
*
* </file>
*
* <file name="protractor.js" type="protractor">
* it('should load and compile correct template', function() {
* element(by.linkText('Moby: Ch1')).click();
* var content = element(by.css('[ng-view]')).getText();
* expect(content).toMatch(/controller\: ChapterController/);
* expect(content).toMatch(/Book Id\: Moby/);
* expect(content).toMatch(/Chapter Id\: 1/);
*
* element(by.partialLinkText('Scarlet')).click();
*
* content = element(by.css('[ng-view]')).getText();
* expect(content).toMatch(/controller\: BookController/);
* expect(content).toMatch(/Book Id\: Scarlet/);
* });
* </file>
* </example>
*/
/**
* @ngdoc event
* @name $route#$routeChangeStart
* @eventType broadcast on root scope
* @description
* Broadcasted before a route change. At this point the route services starts
* resolving all of the dependencies needed for the route change to occur.
* Typically this involves fetching the view template as well as any dependencies
* defined in `resolve` route property. Once all of the dependencies are resolved
* `$routeChangeSuccess` is fired.
*
* The route change (and the `$location` change that triggered it) can be prevented
* by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
* for more details about event object.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} next Future route information.
* @param {Route} current Current route information.
*/
/**
* @ngdoc event
* @name $route#$routeChangeSuccess
* @eventType broadcast on root scope
* @description
* Broadcasted after a route dependencies are resolved.
* {@link ngRoute.directive:ngView ngView} listens for the directive
* to instantiate the controller and render the view.
*
* @param {Object} angularEvent Synthetic event object.
* @param {Route} current Current route information.
* @param {Route|Undefined} previous Previous route information, or undefined if current is
* first route entered.
*/
/**
* @ngdoc event
* @name $route#$routeChangeError
* @eventType broadcast on root scope
* @description
* Broadcasted if any of the resolve promises are rejected.
*
* @param {Object} angularEvent Synthetic event object
* @param {Route} current Current route information.
* @param {Route} previous Previous route information.
* @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
*/
/**
* @ngdoc event
* @name $route#$routeUpdate
* @eventType broadcast on root scope
* @description
*
* The `reloadOnSearch` property has been set to false, and we are reusing the same
* instance of the Controller.
*/
var forceReload = false,
preparedRoute,
preparedRouteIsUpdateOnly,
$route = {
routes: routes,
/**
* @ngdoc method
* @name $route#reload
*
* @description
* Causes `$route` service to reload the current route even if
* {@link ng.$location $location} hasn't changed.
*
* As a result of that, {@link ngRoute.directive:ngView ngView}
* creates new scope and reinstantiates the controller.
*/
reload: function() {
forceReload = true;
$rootScope.$evalAsync(function() {
// Don't support cancellation of a reload for now...
prepareRoute();
commitRoute();
});
},
/**
* @ngdoc method
* @name $route#updateParams
*
* @description
* Causes `$route` service to update the current URL, replacing
* current route parameters with those specified in `newParams`.
* Provided property names that match the route's path segment
* definitions will be interpolated into the location's path, while
* remaining properties will be treated as query params.
*
* @param {Object} newParams mapping of URL parameter names to values
*/
updateParams: function(newParams) {
if (this.current && this.current.$$route) {
var searchParams = {}, self=this;
angular.forEach(Object.keys(newParams), function(key) {
if (!self.current.pathParams[key]) searchParams[key] = newParams[key];
});
newParams = angular.extend({}, this.current.params, newParams);
$location.path(interpolate(this.current.$$route.originalPath, newParams));
$location.search(angular.extend({}, $location.search(), searchParams));
}
else {
throw $routeMinErr('norout', 'Tried updating route when with no current route');
}
}
};
$rootScope.$on('$locationChangeStart', prepareRoute);
$rootScope.$on('$locationChangeSuccess', commitRoute);
return $route;
/////////////////////////////////////////////////////
/**
* @param on {string} current url
* @param route {Object} route regexp to match the url against
* @return {?Object}
*
* @description
* Check if the route matches the current url.
*
* Inspired by match in
* visionmedia/express/lib/router/router.js.
*/
function switchRouteMatcher(on, route) {
var keys = route.keys,
params = {};
if (!route.regexp) return null;
var m = route.regexp.exec(on);
if (!m) return null;
for (var i = 1, len = m.length; i < len; ++i) {
var key = keys[i - 1];
var val = m[i];
if (key && val) {
params[key.name] = val;
}
}
return params;
}
function prepareRoute($locationEvent) {
var lastRoute = $route.current;
preparedRoute = parseRoute();
preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
&& angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
&& !preparedRoute.reloadOnSearch && !forceReload;
if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
if ($locationEvent) {
$locationEvent.preventDefault();
}
}
}
}
function commitRoute() {
var lastRoute = $route.current;
var nextRoute = preparedRoute;
if (preparedRouteIsUpdateOnly) {
lastRoute.params = nextRoute.params;
angular.copy(lastRoute.params, $routeParams);
$rootScope.$broadcast('$routeUpdate', lastRoute);
} else if (nextRoute || lastRoute) {
forceReload = false;
$route.current = nextRoute;
if (nextRoute) {
if (nextRoute.redirectTo) {
if (angular.isString(nextRoute.redirectTo)) {
$location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
.replace();
} else {
$location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
.replace();
}
}
}
$q.when(nextRoute).
then(function() {
if (nextRoute) {
var locals = angular.extend({}, nextRoute.resolve),
template, templateUrl;
angular.forEach(locals, function(value, key) {
locals[key] = angular.isString(value) ?
$injector.get(value) : $injector.invoke(value, null, null, key);
});
if (angular.isDefined(template = nextRoute.template)) {
if (angular.isFunction(template)) {
template = template(nextRoute.params);
}
} else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
if (angular.isFunction(templateUrl)) {
templateUrl = templateUrl(nextRoute.params);
}
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
if (angular.isDefined(templateUrl)) {
nextRoute.loadedTemplateUrl = templateUrl;
template = $templateRequest(templateUrl);
}
}
if (angular.isDefined(template)) {
locals['$template'] = template;
}
return $q.all(locals);
}
}).
// after route change
then(function(locals) {
if (nextRoute == $route.current) {
if (nextRoute) {
nextRoute.locals = locals;
angular.copy(nextRoute.params, $routeParams);
}
$rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
}
}, function(error) {
if (nextRoute == $route.current) {
$rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
}
});
}
}
/**
* @returns {Object} the current active route, by matching it against the URL
*/
function parseRoute() {
// Match a route
var params, match;
angular.forEach(routes, function(route, path) {
if (!match && (params = switchRouteMatcher($location.path(), route))) {
match = inherit(route, {
params: angular.extend({}, $location.search(), params),
pathParams: params});
match.$$route = route;
}
});
// No route matched; fallback to "otherwise" route
return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
}
/**
* @returns {string} interpolation of the redirect path with the parameters
*/
function interpolate(string, params) {
var result = [];
angular.forEach((string || '').split(':'), function(segment, i) {
if (i === 0) {
result.push(segment);
} else {
var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
var key = segmentMatch[1];
result.push(params[key]);
result.push(segmentMatch[2] || '');
delete params[key];
}
});
return result.join('');
}
}];
}
ngRouteModule.provider('$routeParams', $RouteParamsProvider);
/**
* @ngdoc service
* @name $routeParams
* @requires $route
*
* @description
* The `$routeParams` service allows you to retrieve the current set of route parameters.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* The route parameters are a combination of {@link ng.$location `$location`}'s
* {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
* The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
*
* In case of parameter name collision, `path` params take precedence over `search` params.
*
* The service guarantees that the identity of the `$routeParams` object will remain unchanged
* (but its properties will likely change) even when a route change occurs.
*
* Note that the `$routeParams` are only updated *after* a route change completes successfully.
* This means that you cannot rely on `$routeParams` being correct in route resolve functions.
* Instead you can use `$route.current.params` to access the new route's parameters.
*
* @example
* ```js
* // Given:
* // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
* // Route: /Chapter/:chapterId/Section/:sectionId
* //
* // Then
* $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
* ```
*/
function $RouteParamsProvider() {
this.$get = function() { return {}; };
}
ngRouteModule.directive('ngView', ngViewFactory);
ngRouteModule.directive('ngView', ngViewFillContentFactory);
/**
* @ngdoc directive
* @name ngView
* @restrict ECA
*
* @description
* # Overview
* `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
* including the rendered template of the current route into the main layout (`index.html`) file.
* Every time the current route changes, the included view changes with it according to the
* configuration of the `$route` service.
*
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*
* @animations
* enter - animation is used to bring new content into the browser.
* leave - animation is used to animate existing content away.
*
* The enter and leave animation occur concurrently.
*
* @scope
* @priority 400
* @param {string=} onload Expression to evaluate whenever the view updates.
*
* @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
* $anchorScroll} to scroll the viewport after the view is updated.
*
* - If the attribute is not set, disable scrolling.
* - If the attribute is set without value, enable scrolling.
* - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
* as an expression yields a truthy value.
* @example
<example name="ngView-directive" module="ngViewExample"
deps="angular-route.js;angular-animate.js"
animations="true" fixBase="true">
<file name="index.html">
<div ng-controller="MainCtrl as main">
Choose:
<a href="Book/Moby">Moby</a> |
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
<a href="Book/Gatsby">Gatsby</a> |
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
<a href="Book/Scarlet">Scarlet Letter</a><br/>
<div class="view-animate-container">
<div ng-view class="view-animate"></div>
</div>
<hr />
<pre>$location.path() = {{main.$location.path()}}</pre>
<pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{main.$route.current.params}}</pre>
<pre>$routeParams = {{main.$routeParams}}</pre>
</div>
</file>
<file name="book.html">
<div>
controller: {{book.name}}<br />
Book Id: {{book.params.bookId}}<br />
</div>
</file>
<file name="chapter.html">
<div>
controller: {{chapter.name}}<br />
Book Id: {{chapter.params.bookId}}<br />
Chapter Id: {{chapter.params.chapterId}}
</div>
</file>
<file name="animations.css">
.view-animate-container {
position:relative;
height:100px!important;
position:relative;
background:white;
border:1px solid black;
height:40px;
overflow:hidden;
}
.view-animate {
padding:10px;
}
.view-animate.ng-enter, .view-animate.ng-leave {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
display:block;
width:100%;
border-left:1px solid black;
position:absolute;
top:0;
left:0;
right:0;
bottom:0;
padding:10px;
}
.view-animate.ng-enter {
left:100%;
}
.view-animate.ng-enter.ng-enter-active {
left:0;
}
.view-animate.ng-leave.ng-leave-active {
left:-100%;
}
</file>
<file name="script.js">
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: 'BookCtrl',
controllerAs: 'book'
})
.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: 'ChapterCtrl',
controllerAs: 'chapter'
});
$locationProvider.html5Mode(true);
}])
.controller('MainCtrl', ['$route', '$routeParams', '$location',
function($route, $routeParams, $location) {
this.$route = $route;
this.$location = $location;
this.$routeParams = $routeParams;
}])
.controller('BookCtrl', ['$routeParams', function($routeParams) {
this.name = "BookCtrl";
this.params = $routeParams;
}])
.controller('ChapterCtrl', ['$routeParams', function($routeParams) {
this.name = "ChapterCtrl";
this.params = $routeParams;
}]);
</file>
<file name="protractor.js" type="protractor">
it('should load and compile correct template', function() {
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCtrl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: BookCtrl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
</file>
</example>
*/
/**
* @ngdoc event
* @name ngView#$viewContentLoaded
* @eventType emit on the current ngView scope
* @description
* Emitted every time the ngView content is reloaded.
*/
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
function ngViewFactory($route, $anchorScroll, $animate) {
return {
restrict: 'ECA',
terminal: true,
priority: 400,
transclude: 'element',
link: function(scope, $element, attr, ctrl, $transclude) {
var currentScope,
currentElement,
previousLeaveAnimation,
autoScrollExp = attr.autoscroll,
onloadExp = attr.onload || '';
scope.$on('$routeChangeSuccess', update);
update();
function cleanupLastView() {
if (previousLeaveAnimation) {
$animate.cancel(previousLeaveAnimation);
previousLeaveAnimation = null;
}
if (currentScope) {
currentScope.$destroy();
currentScope = null;
}
if (currentElement) {
previousLeaveAnimation = $animate.leave(currentElement);
previousLeaveAnimation.then(function() {
previousLeaveAnimation = null;
});
currentElement = null;
}
}
function update() {
var locals = $route.current && $route.current.locals,
template = locals && locals.$template;
if (angular.isDefined(template)) {
var newScope = scope.$new();
var current = $route.current;
// Note: This will also link all children of ng-view that were contained in the original
// html. If that content contains controllers, ... they could pollute/change the scope.
// However, using ng-view on an element with additional content does not make sense...
// Note: We can't remove them in the cloneAttchFn of $transclude as that
// function is called before linking the content, which would apply child
// directives to non existing elements.
var clone = $transclude(newScope, function(clone) {
$animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
}
});
cleanupLastView();
});
currentElement = clone;
currentScope = current.scope = newScope;
currentScope.$emit('$viewContentLoaded');
currentScope.$eval(onloadExp);
} else {
cleanupLastView();
}
}
}
};
}
// This directive is called during the $transclude call of the first `ngView` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngView
// is called.
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
function ngViewFillContentFactory($compile, $controller, $route) {
return {
restrict: 'ECA',
priority: -400,
link: function(scope, $element) {
var current = $route.current,
locals = current.locals;
$element.html(locals.$template);
var link = $compile($element.contents());
if (current.controller) {
locals.$scope = scope;
var controller = $controller(current.controller, locals);
if (current.controllerAs) {
scope[current.controllerAs] = controller;
}
$element.data('$ngControllerController', controller);
$element.children().data('$ngControllerController', controller);
}
link(scope);
}
};
}
})(window, window.angular);
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
cp $ONOS_ROOT/apps/demo/cord-gui/target/cord-gui-1.8.0-SNAPSHOT.war .
#######------------------------------------------------------------
# CORD Demo
# =========
export LISTENPORT=8080
export JETTY="-jar jetty-runner.jar"
export CORD=./cord-gui-1.8.0-SNAPSHOT.war
export LOGDBG=-Dorg.onosproject.cord.gui.LEVEL=DEBUG
export DEBUG="-Xdebug -Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n"
export LOG=cord.log
DBG=""
if [ "$1" = "debug" ]
then
shift
DBG=$DEBUG
fi
IP="$1"
PORT="$2"
if [ ! -z "$IP" ]
then
PARAM1="-Dheadnodeip=$IP"
else
PARAM1=""
fi
if [ ! -z "$PORT" ]
then
PARAM2="-Dheadnodeport=$PORT"
else
PARAM2=""
fi
java $PARAM1 $PARAM2 $LOGDBG $DBG $JETTY --port $LISTENPORT $CORD >$LOG 2>&1 &
echo jetty-runner started {$PARAM1:$PARAM2}
echo .. logging to $LOG
# script to stop the cord gui server
#
PID=$(ps -ef | grep jetty-runner | grep -v grep | awk '{print $2}')
if [ -z "$PID" ]
then
echo jetty-runner not running
exit 0
fi
kill $PID
sleep 1
PID=$(ps -ef | grep jetty-runner | grep -v grep | awk '{print $2}')
if [ ! -z "$PID" ]
then
echo jetty-runner still running ?
else
echo jetty-runner stopped
fi
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.onosproject.cord.gui.model.BundleFactory;
import org.onosproject.cord.gui.model.SubscriberUser;
import java.io.IOException;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Unit tests for {@link CordModelCache}.
*/
@Ignore("How to test against a live XOS system??")
public class CoreModelCacheTest {
private CordModelCache cache;
@Before
public void setUp() {
cache = new CordModelCache();
}
@Test
public void basic() {
assertEquals("wrong bundle", BundleFactory.BASIC_BUNDLE,
cache.getCurrentBundle().descriptor());
}
@Test
public void basicBundleJson() {
ObjectNode node = BundleFactory.toObjectNode(cache.getCurrentBundle());
String json = node.toString();
System.out.println(json);
assertTrue("bad basic json", sameJson(BASIC_BUNDLE_JSON, json));
}
@Test
public void chooseFamilyBundle() {
cache.setCurrentBundle("family");
assertEquals("wrong bundle", BundleFactory.FAMILY_BUNDLE,
cache.getCurrentBundle().descriptor());
}
@Test
public void familyBundleJson() {
cache.setCurrentBundle("family");
ObjectNode node = BundleFactory.toObjectNode(cache.getCurrentBundle());
String json = node.toString();
System.out.println(json);
assertTrue("bad family json", sameJson(FAMILY_BUNDLE_JSON, json));
}
@Test
public void checkUsers() {
List<SubscriberUser> users = cache.getUsers();
assertEquals("wrong # users", 4, users.size());
}
@Test
public void usersBasicJson() {
String json = cache.jsonUsers();
System.out.println(json);
assertTrue("bad users basic json", sameJson(USERS_BASIC, json));
}
@Test
public void usersFamilyJson() {
cache.setCurrentBundle("family");
String json = cache.jsonUsers();
System.out.println(json);
assertTrue("bad users family json", sameJson(USERS_FAMILY, json));
}
@Test
public void setNewLevel() {
cache.setCurrentBundle("family");
JsonNode node = fromString(cache.jsonUsers());
assertEquals("wrong level", "G", getMomsLevel(node));
cache.applyPerUserParam("1", "url_filter", "level", "R");
node = fromString(cache.jsonUsers());
assertEquals("wrong level", "R", getMomsLevel(node));
}
private String getMomsLevel(JsonNode node) {
JsonNode mom = node.get("users").elements().next();
assertEquals("wrong ID", 1, mom.get("id").asInt());
return mom.get("profile").get("url_filter").get("level").asText();
}
// =============
private JsonNode fromString(String s) {
try {
return MAPPER.readTree(s);
} catch (IOException e) {
System.out.println("Exception: " + e);
}
return null;
}
private boolean sameJson(String s1, String s2) {
try {
JsonNode tree1 = MAPPER.readTree(s1);
JsonNode tree2 = MAPPER.readTree(s2);
return tree1.equals(tree2);
} catch (IOException e) {
System.out.println("Exception: " + e);
}
return false;
}
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String BASIC_BUNDLE_JSON = "{\n" +
" \"bundle\": {\n" +
" \"id\": \"basic\",\n" +
" \"name\": \"Basic Bundle\",\n" +
" \"desc\": \"Provides basic internet and firewall functions.\",\n" +
" \"functions\": [\n" +
" {\n" +
" \"id\": \"internet\",\n" +
" \"name\": \"Internet\",\n" +
" \"desc\": \"Basic internet connectivity.\",\n" +
" \"params\": {}\n" +
" },\n" +
" {\n" +
" \"id\": \"firewall\",\n" +
" \"name\": \"Firewall\",\n" +
" \"desc\": \"Normal firewall protection.\",\n" +
" \"params\": {}\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"bundles\": [\n" +
" {\n" +
" \"id\": \"basic\",\n" +
" \"name\": \"Basic Bundle\",\n" +
" \"desc\": \"Provides basic internet and firewall functions.\"\n" +
" },\n" +
" {\n" +
" \"id\": \"family\",\n" +
" \"name\": \"Family Bundle\",\n" +
" \"desc\": \"Provides internet, firewall and parental control functions.\"\n" +
" }\n" +
" ]\n" +
"}\n";
private static final String FAMILY_BUNDLE_JSON = "{\n" +
" \"bundle\": {\n" +
" \"id\": \"family\",\n" +
" \"name\": \"Family Bundle\",\n" +
" \"desc\": \"Provides internet, firewall and parental control functions.\",\n" +
" \"functions\": [\n" +
" {\n" +
" \"id\": \"internet\",\n" +
" \"name\": \"Internet\",\n" +
" \"desc\": \"Basic internet connectivity.\",\n" +
" \"params\": {}\n" +
" },\n" +
" {\n" +
" \"id\": \"firewall\",\n" +
" \"name\": \"Firewall\",\n" +
" \"desc\": \"Normal firewall protection.\",\n" +
" \"params\": {}\n" +
" },\n" +
" {\n" +
" \"id\": \"url_filter\",\n" +
" \"name\": \"Parental Control\",\n" +
" \"desc\": \"Variable levels of URL filtering.\",\n" +
" \"params\": {\n" +
" \"level\": \"G\",\n" +
" \"levels\": [\n" +
" \"OFF\",\n" +
" \"G\",\n" +
" \"PG\",\n" +
" \"PG_13\",\n" +
" \"R\",\n" +
" \"NONE\"\n" +
" ]\n" +
" }\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"bundles\": [\n" +
" {\n" +
" \"id\": \"basic\",\n" +
" \"name\": \"Basic Bundle\",\n" +
" \"desc\": \"Provides basic internet and firewall functions.\"\n" +
" },\n" +
" {\n" +
" \"id\": \"family\",\n" +
" \"name\": \"Family Bundle\",\n" +
" \"desc\": \"Provides internet, firewall and parental control functions.\"\n" +
" }\n" +
" ]\n" +
"}\n";
private static final String USERS_BASIC = "{\n" +
" \"users\": [\n" +
" {\n" +
" \"id\": 1,\n" +
" \"name\": \"Mom's MacBook\",\n" +
" \"mac\": \"010203040506\",\n" +
" \"profile\": { }\n" +
" },\n" +
" {\n" +
" \"id\": 2,\n" +
" \"name\": \"Dad's iPad\",\n" +
" \"mac\": \"010203040507\",\n" +
" \"profile\": { }\n" +
" },\n" +
" {\n" +
" \"id\": 3,\n" +
" \"name\": \"Dick's laptop\",\n" +
" \"mac\": \"010203040508\",\n" +
" \"profile\": { }\n" +
" },\n" +
" {\n" +
" \"id\": 4,\n" +
" \"name\": \"Jane's laptop\",\n" +
" \"mac\": \"010203040509\",\n" +
" \"profile\": { }\n" +
" }\n" +
" ]\n" +
"}\n";
private static final String USERS_FAMILY = "{\n" +
" \"users\": [\n" +
" {\n" +
" \"id\": 1,\n" +
" \"name\": \"Mom's MacBook\",\n" +
" \"mac\": \"010203040506\",\n" +
" \"profile\": {\n" +
" \"url_filter\": {\n" +
" \"level\": \"G\"\n" +
" }\n" +
" }\n" +
" },\n" +
" {\n" +
" \"id\": 2,\n" +
" \"name\": \"Dad's iPad\",\n" +
" \"mac\": \"010203040507\",\n" +
" \"profile\": {\n" +
" \"url_filter\": {\n" +
" \"level\": \"G\"\n" +
" }\n" +
" }\n" +
" },\n" +
" {\n" +
" \"id\": 3,\n" +
" \"name\": \"Dick's laptop\",\n" +
" \"mac\": \"010203040508\",\n" +
" \"profile\": {\n" +
" \"url_filter\": {\n" +
" \"level\": \"G\"\n" +
" }\n" +
" }\n" +
" },\n" +
" {\n" +
" \"id\": 4,\n" +
" \"name\": \"Jane's laptop\",\n" +
" \"mac\": \"010203040509\",\n" +
" \"profile\": {\n" +
" \"url_filter\": {\n" +
" \"level\": \"G\"\n" +
" }\n" +
" }\n" +
" }\n" +
" ]\n" +
"}\n";
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import org.junit.Test;
import java.util.Set;
import static org.junit.Assert.*;
import static org.onosproject.cord.gui.model.BundleFactory.*;
import static org.onosproject.cord.gui.model.XosFunctionDescriptor.*;
/**
* Unit tests for {@link BundleFactory}.
*/
public class BundleFactoryTest {
@Test
public void bundleCount() {
assertEquals("wrong count", 2, availableBundles().size());
assertTrue("missing basic", availableBundles().contains(BASIC_BUNDLE));
assertTrue("missing family", availableBundles().contains(FAMILY_BUNDLE));
}
@Test
public void basicBundle() {
BundleDescriptor bundle = BundleFactory.BASIC_BUNDLE;
assertEquals("wrong id", "basic", bundle.id());
assertEquals("wrong id", "Basic Bundle", bundle.displayName());
Set<XosFunctionDescriptor> funcs = bundle.functions();
assertTrue("missing internet", funcs.contains(INTERNET));
assertTrue("missing firewall", funcs.contains(FIREWALL));
assertFalse("unexpected url-f", funcs.contains(URL_FILTER));
}
@Test
public void familyBundle() {
BundleDescriptor bundle = BundleFactory.FAMILY_BUNDLE;
assertEquals("wrong id", "family", bundle.id());
assertEquals("wrong id", "Family Bundle", bundle.displayName());
Set<XosFunctionDescriptor> funcs = bundle.functions();
assertTrue("missing internet", funcs.contains(INTERNET));
assertTrue("missing firewall", funcs.contains(FIREWALL));
assertTrue("missing url-f", funcs.contains(URL_FILTER));
}
@Test
public void bundleFromIdBasic() {
assertEquals("wrong bundle", BASIC_BUNDLE, bundleFromId("basic"));
}
@Test
public void bundleFromIdFamily() {
assertEquals("wrong bundle", FAMILY_BUNDLE, bundleFromId("family"));
}
@Test(expected = IllegalArgumentException.class)
public void bundleFromIdUnknown() {
bundleFromId("unknown");
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Unit tests for {@link UrlFilterFunction}.
*/
public class UrlFilterFunctionTest {
private SubscriberUser user = new SubscriberUser(1, "foo", "fooMAC", "levelX");
private UrlFilterFunction fn;
@Before
public void setUp() {
fn = new UrlFilterFunction();
}
@Test
public void basic() {
assertEquals("wrong enum const count",
6, UrlFilterFunction.Level.values().length);
}
@Test
public void memento() {
XosFunction.Memento memo = fn.createMemento();
assertTrue("wrong class", memo instanceof UrlFilterFunction.UrlFilterMemento);
UrlFilterFunction.UrlFilterMemento umemo =
(UrlFilterFunction.UrlFilterMemento) memo;
assertEquals("wrong default level", "G", umemo.level());
}
@Test
public void memoNewLevel() {
XosFunction.Memento memo = fn.createMemento();
assertTrue("wrong class", memo instanceof UrlFilterFunction.UrlFilterMemento);
UrlFilterFunction.UrlFilterMemento umemo =
(UrlFilterFunction.UrlFilterMemento) memo;
assertEquals("wrong default level", "G", umemo.level());
umemo.setLevel(UrlFilterFunction.Level.R);
assertEquals("wrong new level", "R", umemo.level());
}
@Test
public void applyMemo() {
UrlFilterFunction.UrlFilterMemento memo =
(UrlFilterFunction.UrlFilterMemento) fn.createMemento();
memo.setLevel(UrlFilterFunction.Level.PG_13);
user.setMemento(XosFunctionDescriptor.URL_FILTER, memo);
assertEquals("wrong URL suffix", "url_filter/PG_13", fn.xosUrlApply(user));
}
}
/*
* Copyright 2015-present Open Networking Laboratory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.onosproject.cord.gui.model;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.onosproject.cord.gui.model.XosFunctionDescriptor.*;
/**
* Sanity unit tests for {@link XosFunctionDescriptor}.
*/
public class XosFunctionDescriptorTest {
@Test
public void numberOfFunctions() {
assertEquals("unexpected constant count", 4, values().length);
}
@Test
public void internet() {
assertEquals("wrong id", "internet", INTERNET.id());
assertEquals("wrong display", "Internet", INTERNET.displayName());
assertTrue("wrong desc", INTERNET.description().startsWith("Basic"));
assertFalse("wrong backend", INTERNET.backend());
}
@Test
public void firewall() {
assertEquals("wrong id", "firewall", FIREWALL.id());
assertEquals("wrong display", "Firewall", FIREWALL.displayName());
assertTrue("wrong desc", FIREWALL.description().startsWith("Normal"));
assertTrue("wrong backend", FIREWALL.backend());
}
@Test
public void urlFilter() {
assertEquals("wrong id", "url_filter", URL_FILTER.id());
assertEquals("wrong display", "Parental Control", URL_FILTER.displayName());
assertTrue("wrong desc", URL_FILTER.description().startsWith("Variable"));
assertTrue("wrong backend", URL_FILTER.backend());
}
@Test
public void cdn() {
assertEquals("wrong id", "cdn", CDN.id());
assertEquals("wrong display", "CDN", CDN.displayName());
assertTrue("wrong desc", CDN.description().startsWith("Content"));
assertTrue("wrong backend", CDN.backend());
}
}