Thomas Vachuska

Adding server-side user preferences.

More work still needs to get done to allow client to process
server-pushed preferences updates.

Change-Id: I6e80e3f3677285cb19cfa3b6240c1b13aac56622
...@@ -23,6 +23,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode; ...@@ -23,6 +23,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
23 public interface UiConnection { 23 public interface UiConnection {
24 24
25 /** 25 /**
26 + * Returns the name of the logged-in user for which this connection exists.
27 + *
28 + * @return logged in user name
29 + */
30 + String userName();
31 +
32 + /**
26 * Sends the specified JSON message to the user interface client. 33 * Sends the specified JSON message to the user interface client.
27 * 34 *
28 * @param message message to send 35 * @param message message to send
......
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui;
18 +
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +
21 +import java.util.Map;
22 +import java.util.Set;
23 +
24 +/**
25 + * Service for tracking user interface preferences.
26 + */
27 +public interface UiPreferencesService {
28 +
29 + /**
30 + * Returns the list of user names that have user preferences available.
31 + *
32 + * @return list of user names
33 + */
34 + Set<String> getUserNames();
35 +
36 + /**
37 + * Returns an immutable copy of the preferences for the specified user.
38 + *
39 + * @param userName user name
40 + * @return map of user preferences
41 + */
42 + Map<String, ObjectNode> getPreferences(String userName);
43 +
44 + /**
45 + * Sets the named preference for the specified user.
46 + *
47 + * @param userName user name
48 + * @param preference name of the user preference
49 + * @param value preference value
50 + */
51 + void setPreference(String userName, String preference, ObjectNode value);
52 +
53 +}
...@@ -54,6 +54,11 @@ ...@@ -54,6 +54,11 @@
54 </dependency> 54 </dependency>
55 <dependency> 55 <dependency>
56 <groupId>org.onosproject</groupId> 56 <groupId>org.onosproject</groupId>
57 + <artifactId>onos-core-serializers</artifactId>
58 + <version>${project.version}</version>
59 + </dependency>
60 + <dependency>
61 + <groupId>org.onosproject</groupId>
57 <artifactId>onos-incubator-api</artifactId> 62 <artifactId>onos-incubator-api</artifactId>
58 </dependency> 63 </dependency>
59 <dependency> 64 <dependency>
......
...@@ -15,17 +15,22 @@ ...@@ -15,17 +15,22 @@
15 */ 15 */
16 package org.onosproject.ui.impl; 16 package org.onosproject.ui.impl;
17 17
18 +import com.fasterxml.jackson.databind.node.ObjectNode;
18 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableList.Builder; 20 import com.google.common.collect.ImmutableList.Builder;
20 import org.onlab.osgi.ServiceNotFoundException; 21 import org.onlab.osgi.ServiceNotFoundException;
21 import org.onosproject.rest.AbstractInjectionResource; 22 import org.onosproject.rest.AbstractInjectionResource;
22 import org.onosproject.ui.UiExtensionService; 23 import org.onosproject.ui.UiExtensionService;
24 +import org.onosproject.ui.UiPreferencesService;
23 25
24 import javax.ws.rs.GET; 26 import javax.ws.rs.GET;
25 import javax.ws.rs.Path; 27 import javax.ws.rs.Path;
26 import javax.ws.rs.Produces; 28 import javax.ws.rs.Produces;
29 +import javax.ws.rs.core.Context;
27 import javax.ws.rs.core.MediaType; 30 import javax.ws.rs.core.MediaType;
28 import javax.ws.rs.core.Response; 31 import javax.ws.rs.core.Response;
32 +import javax.ws.rs.core.SecurityContext;
33 +import java.io.ByteArrayInputStream;
29 import java.io.IOException; 34 import java.io.IOException;
30 import java.io.InputStream; 35 import java.io.InputStream;
31 import java.io.SequenceInputStream; 36 import java.io.SequenceInputStream;
...@@ -42,12 +47,21 @@ public class MainIndexResource extends AbstractInjectionResource { ...@@ -42,12 +47,21 @@ public class MainIndexResource extends AbstractInjectionResource {
42 private static final String INDEX = "index.html"; 47 private static final String INDEX = "index.html";
43 private static final String NOT_READY = "not-ready.html"; 48 private static final String NOT_READY = "not-ready.html";
44 49
50 + private static final String INJECT_USER_START = "<!-- {INJECTED-USER-START} -->";
51 + private static final String INJECT_USER_END = "<!-- {INJECTED-USER-END} -->";
52 +
45 private static final String INJECT_CSS_START = "<!-- {INJECTED-STYLESHEETS-START} -->"; 53 private static final String INJECT_CSS_START = "<!-- {INJECTED-STYLESHEETS-START} -->";
46 private static final String INJECT_CSS_END = "<!-- {INJECTED-STYLESHEETS-END} -->"; 54 private static final String INJECT_CSS_END = "<!-- {INJECTED-STYLESHEETS-END} -->";
47 55
48 private static final String INJECT_JS_START = "<!-- {INJECTED-JAVASCRIPT-START} -->"; 56 private static final String INJECT_JS_START = "<!-- {INJECTED-JAVASCRIPT-START} -->";
49 private static final String INJECT_JS_END = "<!-- {INJECTED-JAVASCRIPT-END} -->"; 57 private static final String INJECT_JS_END = "<!-- {INJECTED-JAVASCRIPT-END} -->";
50 58
59 + private static final byte[] SCRIPT_START = "\n<script>\n".getBytes();
60 + private static final byte[] SCRIPT_END = "\n</script>\n\n".getBytes();
61 +
62 + @Context
63 + private SecurityContext ctx;
64 +
51 @GET 65 @GET
52 @Produces(MediaType.TEXT_HTML) 66 @Produces(MediaType.TEXT_HTML)
53 public Response getMainIndex() throws IOException { 67 public Response getMainIndex() throws IOException {
...@@ -62,14 +76,25 @@ public class MainIndexResource extends AbstractInjectionResource { ...@@ -62,14 +76,25 @@ public class MainIndexResource extends AbstractInjectionResource {
62 InputStream indexTemplate = classLoader.getResourceAsStream(INDEX); 76 InputStream indexTemplate = classLoader.getResourceAsStream(INDEX);
63 String index = new String(toByteArray(indexTemplate)); 77 String index = new String(toByteArray(indexTemplate));
64 78
65 - int p1s = split(index, 0, INJECT_JS_START) - INJECT_JS_START.length(); 79 + int p0s = split(index, 0, INJECT_USER_START) - INJECT_USER_START.length();
80 + int p0e = split(index, p0s, INJECT_USER_END);
81 + int p1s = split(index, p0e, INJECT_JS_START) - INJECT_JS_START.length();
66 int p1e = split(index, p1s, INJECT_JS_END); 82 int p1e = split(index, p1s, INJECT_JS_END);
67 int p2s = split(index, p1e, INJECT_CSS_START) - INJECT_CSS_START.length(); 83 int p2s = split(index, p1e, INJECT_CSS_START) - INJECT_CSS_START.length();
68 int p2e = split(index, p2s, INJECT_CSS_END); 84 int p2e = split(index, p2s, INJECT_CSS_END);
69 int p3s = split(index, p2e, null); 85 int p3s = split(index, p2e, null);
70 86
87 + // FIXME: use global opaque auth token to allow secure failover
88 + String userName = ctx.getUserPrincipal().getName();
89 + String auth = "var onosAuth='" + userName + "';\n";
90 +
71 StreamEnumeration streams = 91 StreamEnumeration streams =
72 - new StreamEnumeration(of(stream(index, 0, p1s), 92 + new StreamEnumeration(of(stream(index, 0, p0s),
93 + new ByteArrayInputStream(SCRIPT_START),
94 + stream(auth, 0, auth.length()),
95 + userPreferences(userName),
96 + new ByteArrayInputStream(SCRIPT_END),
97 + stream(index, p0e, p1s),
73 includeJs(service), 98 includeJs(service),
74 stream(index, p1e, p2s), 99 stream(index, p1e, p2s),
75 includeCss(service), 100 includeCss(service),
...@@ -78,6 +103,15 @@ public class MainIndexResource extends AbstractInjectionResource { ...@@ -78,6 +103,15 @@ public class MainIndexResource extends AbstractInjectionResource {
78 return Response.ok(new SequenceInputStream(streams)).build(); 103 return Response.ok(new SequenceInputStream(streams)).build();
79 } 104 }
80 105
106 + // Produces an input stream including user preferences.
107 + private InputStream userPreferences(String userName) {
108 + UiPreferencesService service = get(UiPreferencesService.class);
109 + ObjectNode prefs = mapper().createObjectNode();
110 + service.getPreferences(userName).forEach(prefs::set);
111 + String string = "var userPrefs = " + prefs.toString() + ";";
112 + return new ByteArrayInputStream(string.getBytes());
113 + }
114 +
81 // Produces an input stream including JS injections from all extensions. 115 // Produces an input stream including JS injections from all extensions.
82 private InputStream includeJs(UiExtensionService service) { 116 private InputStream includeJs(UiExtensionService service) {
83 Builder<InputStream> builder = ImmutableList.builder(); 117 Builder<InputStream> builder = ImmutableList.builder();
......
...@@ -16,7 +16,19 @@ ...@@ -16,7 +16,19 @@
16 package org.onosproject.ui.impl; 16 package org.onosproject.ui.impl;
17 17
18 import com.fasterxml.jackson.databind.JsonNode; 18 import com.fasterxml.jackson.databind.JsonNode;
19 +import com.fasterxml.jackson.databind.ObjectMapper;
20 +import com.fasterxml.jackson.databind.node.ArrayNode;
21 +import com.fasterxml.jackson.databind.node.BooleanNode;
22 +import com.fasterxml.jackson.databind.node.DoubleNode;
23 +import com.fasterxml.jackson.databind.node.IntNode;
24 +import com.fasterxml.jackson.databind.node.JsonNodeFactory;
25 +import com.fasterxml.jackson.databind.node.LongNode;
26 +import com.fasterxml.jackson.databind.node.NullNode;
27 +import com.fasterxml.jackson.databind.node.ObjectNode;
28 +import com.fasterxml.jackson.databind.node.ShortNode;
29 +import com.fasterxml.jackson.databind.node.TextNode;
19 import com.google.common.collect.ImmutableList; 30 import com.google.common.collect.ImmutableList;
31 +import com.google.common.collect.ImmutableMap;
20 import com.google.common.collect.ImmutableSet; 32 import com.google.common.collect.ImmutableSet;
21 import com.google.common.collect.Lists; 33 import com.google.common.collect.Lists;
22 import com.google.common.collect.Maps; 34 import com.google.common.collect.Maps;
...@@ -26,40 +38,49 @@ import org.apache.felix.scr.annotations.Deactivate; ...@@ -26,40 +38,49 @@ import org.apache.felix.scr.annotations.Deactivate;
26 import org.apache.felix.scr.annotations.Reference; 38 import org.apache.felix.scr.annotations.Reference;
27 import org.apache.felix.scr.annotations.ReferenceCardinality; 39 import org.apache.felix.scr.annotations.ReferenceCardinality;
28 import org.apache.felix.scr.annotations.Service; 40 import org.apache.felix.scr.annotations.Service;
41 +import org.onlab.util.KryoNamespace;
29 import org.onosproject.mastership.MastershipService; 42 import org.onosproject.mastership.MastershipService;
43 +import org.onosproject.store.serializers.KryoNamespaces;
44 +import org.onosproject.store.service.EventuallyConsistentMap;
45 +import org.onosproject.store.service.EventuallyConsistentMapEvent;
46 +import org.onosproject.store.service.EventuallyConsistentMapListener;
47 +import org.onosproject.store.service.StorageService;
48 +import org.onosproject.store.service.WallClockTimestamp;
30 import org.onosproject.ui.UiExtension; 49 import org.onosproject.ui.UiExtension;
31 import org.onosproject.ui.UiExtensionService; 50 import org.onosproject.ui.UiExtensionService;
32 import org.onosproject.ui.UiMessageHandlerFactory; 51 import org.onosproject.ui.UiMessageHandlerFactory;
52 +import org.onosproject.ui.UiPreferencesService;
33 import org.onosproject.ui.UiTopoOverlayFactory; 53 import org.onosproject.ui.UiTopoOverlayFactory;
34 import org.onosproject.ui.UiView; 54 import org.onosproject.ui.UiView;
35 import org.onosproject.ui.UiViewHidden; 55 import org.onosproject.ui.UiViewHidden;
36 import org.slf4j.Logger; 56 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory; 57 import org.slf4j.LoggerFactory;
38 58
59 +import java.util.LinkedHashMap;
39 import java.util.List; 60 import java.util.List;
40 import java.util.Map; 61 import java.util.Map;
41 import java.util.Set; 62 import java.util.Set;
42 63
43 import static com.google.common.collect.ImmutableList.of; 64 import static com.google.common.collect.ImmutableList.of;
44 import static java.util.stream.Collectors.toSet; 65 import static java.util.stream.Collectors.toSet;
45 -import static org.onosproject.ui.UiView.Category.NETWORK;
46 -import static org.onosproject.ui.UiView.Category.PLATFORM;
47 -
48 import static org.onosproject.security.AppGuard.checkPermission; 66 import static org.onosproject.security.AppGuard.checkPermission;
49 import static org.onosproject.security.AppPermission.Type.UI_READ; 67 import static org.onosproject.security.AppPermission.Type.UI_READ;
50 import static org.onosproject.security.AppPermission.Type.UI_WRITE; 68 import static org.onosproject.security.AppPermission.Type.UI_WRITE;
69 +import static org.onosproject.ui.UiView.Category.NETWORK;
70 +import static org.onosproject.ui.UiView.Category.PLATFORM;
51 71
52 /** 72 /**
53 * Manages the user interface extensions. 73 * Manages the user interface extensions.
54 */ 74 */
55 @Component(immediate = true) 75 @Component(immediate = true)
56 @Service 76 @Service
57 -public class UiExtensionManager implements UiExtensionService, SpriteService { 77 +public class UiExtensionManager implements UiExtensionService, UiPreferencesService, SpriteService {
58 78
59 private static final ClassLoader CL = UiExtensionManager.class.getClassLoader(); 79 private static final ClassLoader CL = UiExtensionManager.class.getClassLoader();
60 private static final String CORE = "core"; 80 private static final String CORE = "core";
61 private static final String GUI_ADDED = "guiAdded"; 81 private static final String GUI_ADDED = "guiAdded";
62 private static final String GUI_REMOVED = "guiRemoved"; 82 private static final String GUI_REMOVED = "guiRemoved";
83 + private static final String UPDATE_PREFS = "updatePrefs";
63 84
64 private final Logger log = LoggerFactory.getLogger(getClass()); 85 private final Logger log = LoggerFactory.getLogger(getClass());
65 86
...@@ -72,10 +93,19 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { ...@@ -72,10 +93,19 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
72 // Core views & core extension 93 // Core views & core extension
73 private final UiExtension core = createCoreExtension(); 94 private final UiExtension core = createCoreExtension();
74 95
75 -
76 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) 96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
77 protected MastershipService mastershipService; 97 protected MastershipService mastershipService;
78 98
99 + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
100 + protected StorageService storageService;
101 +
102 + // User preferences
103 + private EventuallyConsistentMap<String, ObjectNode> prefs;
104 + private final EventuallyConsistentMapListener<String, ObjectNode> prefsListener =
105 + new InternalPrefsListener();
106 +
107 + private final ObjectMapper mapper = new ObjectMapper();
108 +
79 // Creates core UI extension 109 // Creates core UI extension
80 private UiExtension createCoreExtension() { 110 private UiExtension createCoreExtension() {
81 List<UiView> coreViews = of( 111 List<UiView> coreViews = of(
...@@ -98,6 +128,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { ...@@ -98,6 +128,7 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
98 128
99 UiMessageHandlerFactory messageHandlerFactory = 129 UiMessageHandlerFactory messageHandlerFactory =
100 () -> ImmutableList.of( 130 () -> ImmutableList.of(
131 + new UserPreferencesMessageHandler(),
101 new TopologyViewMessageHandler(), 132 new TopologyViewMessageHandler(),
102 new DeviceViewMessageHandler(), 133 new DeviceViewMessageHandler(),
103 new LinkViewMessageHandler(), 134 new LinkViewMessageHandler(),
...@@ -128,12 +159,28 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { ...@@ -128,12 +159,28 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
128 159
129 @Activate 160 @Activate
130 public void activate() { 161 public void activate() {
162 + KryoNamespace.Builder kryoBuilder = new KryoNamespace.Builder()
163 + .register(KryoNamespaces.API)
164 + .register(ObjectNode.class, ArrayNode.class,
165 + JsonNodeFactory.class, LinkedHashMap.class,
166 + TextNode.class, BooleanNode.class,
167 + LongNode.class, DoubleNode.class, ShortNode.class,
168 + IntNode.class, NullNode.class);
169 +
170 + prefs = storageService.<String, ObjectNode>eventuallyConsistentMapBuilder()
171 + .withName("onos-user-preferences")
172 + .withSerializer(kryoBuilder)
173 + .withTimestampProvider((k, v) -> new WallClockTimestamp())
174 + .withPersistence()
175 + .build();
176 + prefs.addListener(prefsListener);
131 register(core); 177 register(core);
132 log.info("Started"); 178 log.info("Started");
133 } 179 }
134 180
135 @Deactivate 181 @Deactivate
136 public void deactivate() { 182 public void deactivate() {
183 + prefs.removeListener(prefsListener);
137 UiWebSocketServlet.closeAll(); 184 UiWebSocketServlet.closeAll();
138 unregister(core); 185 unregister(core);
139 log.info("Stopped"); 186 log.info("Stopped");
...@@ -171,6 +218,27 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { ...@@ -171,6 +218,27 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
171 return views.get(viewId); 218 return views.get(viewId);
172 } 219 }
173 220
221 + @Override
222 + public Set<String> getUserNames() {
223 + ImmutableSet.Builder<String> builder = ImmutableSet.builder();
224 + prefs.keySet().forEach(k -> builder.add(userName(k)));
225 + return builder.build();
226 + }
227 +
228 + @Override
229 + public Map<String, ObjectNode> getPreferences(String userName) {
230 + ImmutableMap.Builder<String, ObjectNode> builder = ImmutableMap.builder();
231 + prefs.entrySet().stream()
232 + .filter(e -> e.getKey().startsWith(userName + "/"))
233 + .forEach(e -> builder.put(keyName(e.getKey()), e.getValue()));
234 + return builder.build();
235 + }
236 +
237 + @Override
238 + public void setPreference(String userName, String preference, ObjectNode value) {
239 + prefs.put(key(userName, preference), value);
240 + }
241 +
174 // ===================================================================== 242 // =====================================================================
175 // Provisional tracking of sprite definitions 243 // Provisional tracking of sprite definitions
176 244
...@@ -192,4 +260,33 @@ public class UiExtensionManager implements UiExtensionService, SpriteService { ...@@ -192,4 +260,33 @@ public class UiExtensionManager implements UiExtensionService, SpriteService {
192 return sprites.get(name); 260 return sprites.get(name);
193 } 261 }
194 262
263 + private String key(String userName, String keyName) {
264 + return userName + "/" + keyName;
265 + }
266 +
267 + private String userName(String key) {
268 + return key.split("/")[0];
269 + }
270 +
271 + private String keyName(String key) {
272 + return key.split("/")[1];
273 + }
274 +
275 + // Auxiliary listener to preference map events.
276 + private class InternalPrefsListener
277 + implements EventuallyConsistentMapListener<String, ObjectNode> {
278 + @Override
279 + public void event(EventuallyConsistentMapEvent<String, ObjectNode> event) {
280 + String userName = userName(event.key());
281 + if (event.type() == EventuallyConsistentMapEvent.Type.PUT) {
282 + UiWebSocketServlet.sendToUser(userName, UPDATE_PREFS, jsonPrefs());
283 + }
284 + }
285 +
286 + private ObjectNode jsonPrefs() {
287 + ObjectNode json = mapper.createObjectNode();
288 + prefs.entrySet().forEach(e -> json.set(keyName(e.getKey()), e.getValue()));
289 + return json;
290 + }
291 + }
195 } 292 }
......
...@@ -54,6 +54,7 @@ public class UiWebSocket ...@@ -54,6 +54,7 @@ public class UiWebSocket
54 54
55 private Connection connection; 55 private Connection connection;
56 private FrameConnection control; 56 private FrameConnection control;
57 + private String userName;
57 58
58 private final ObjectMapper mapper = new ObjectMapper(); 59 private final ObjectMapper mapper = new ObjectMapper();
59 60
...@@ -66,9 +67,16 @@ public class UiWebSocket ...@@ -66,9 +67,16 @@ public class UiWebSocket
66 * Creates a new web-socket for serving data to GUI. 67 * Creates a new web-socket for serving data to GUI.
67 * 68 *
68 * @param directory service directory 69 * @param directory service directory
70 + * @param userName user name of the logged-in user
69 */ 71 */
70 - public UiWebSocket(ServiceDirectory directory) { 72 + public UiWebSocket(ServiceDirectory directory, String userName) {
71 this.directory = directory; 73 this.directory = directory;
74 + this.userName = userName;
75 + }
76 +
77 + @Override
78 + public String userName() {
79 + return userName;
72 } 80 }
73 81
74 /** 82 /**
......
...@@ -70,7 +70,10 @@ public class UiWebSocketServlet extends WebSocketServlet { ...@@ -70,7 +70,10 @@ public class UiWebSocketServlet extends WebSocketServlet {
70 if (isStopped) { 70 if (isStopped) {
71 return null; 71 return null;
72 } 72 }
73 - UiWebSocket socket = new UiWebSocket(directory); 73 +
74 + // FIXME: Replace this with globally shared opaque token to allow secure failover
75 + String userName = request.getUserPrincipal().getName();
76 + UiWebSocket socket = new UiWebSocket(directory, userName);
74 synchronized (sockets) { 77 synchronized (sockets) {
75 sockets.add(socket); 78 sockets.add(socket);
76 } 79 }
...@@ -89,6 +92,20 @@ public class UiWebSocketServlet extends WebSocketServlet { ...@@ -89,6 +92,20 @@ public class UiWebSocketServlet extends WebSocketServlet {
89 } 92 }
90 } 93 }
91 94
95 + /**
96 + * Sends the specified message to all the GUI clients of the specified user.
97 + *
98 + * @param userName user name
99 + * @param type message type
100 + * @param payload message payload
101 + */
102 + static void sendToUser(String userName, String type, ObjectNode payload) {
103 + if (instance != null) {
104 + instance.sockets.stream().filter(ws -> userName.equals(ws.userName()))
105 + .forEach(ws -> ws.sendMessage(type, 0, payload));
106 + }
107 + }
108 +
92 // Task for pruning web-sockets that are idle. 109 // Task for pruning web-sockets that are idle.
93 private class Pruner extends TimerTask { 110 private class Pruner extends TimerTask {
94 @Override 111 @Override
......
1 +/*
2 + * Copyright 2016 Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.ui.impl;
18 +
19 +import com.fasterxml.jackson.databind.node.ObjectNode;
20 +import com.google.common.collect.ImmutableSet;
21 +import org.onosproject.ui.RequestHandler;
22 +import org.onosproject.ui.UiMessageHandler;
23 +import org.onosproject.ui.UiPreferencesService;
24 +
25 +import java.util.Collection;
26 +
27 +import static com.google.common.base.Strings.isNullOrEmpty;
28 +
29 +/**
30 + * Message handler for intercepting user preferences messages.
31 + */
32 +class UserPreferencesMessageHandler extends UiMessageHandler {
33 +
34 + private static final String UPDATE_PREFS_REQ = "updatePrefReq";
35 + private static final String KEY = "key";
36 + private static final String VALUE = "value";
37 +
38 + @Override
39 + protected Collection<RequestHandler> createRequestHandlers() {
40 + return ImmutableSet.of(new UpdatePreferencesRequest());
41 + }
42 +
43 + private final class UpdatePreferencesRequest extends RequestHandler {
44 + private UpdatePreferencesRequest() {
45 + super(UPDATE_PREFS_REQ);
46 + }
47 +
48 + @Override
49 + public void process(long sid, ObjectNode payload) {
50 + if (!isNullOrEmpty(connection().userName())) {
51 + UiPreferencesService service = get(UiPreferencesService.class);
52 + service.setPreference(connection().userName(),
53 + payload.get(KEY).asText(),
54 + (ObjectNode) payload.get(VALUE));
55 + }
56 + }
57 + }
58 +}
...@@ -122,9 +122,9 @@ ...@@ -122,9 +122,9 @@
122 122
123 angular.module('onosLayer') 123 angular.module('onosLayer')
124 .factory('LoadingService', 124 .factory('LoadingService',
125 - ['$log', '$timeout', 'ThemeService', 'FnService', 125 + ['$log', '$timeout', 'ThemeService', 'FnService', 'WebSocketService',
126 126
127 - function (_$log_, _$timeout_, _ts_, _fs_) { 127 + function (_$log_, _$timeout_, _ts_, _fs_, wss) {
128 $log = _$log_; 128 $log = _$log_;
129 $timeout = _$timeout_; 129 $timeout = _$timeout_;
130 ts = _ts_; 130 ts = _ts_;
...@@ -132,11 +132,13 @@ ...@@ -132,11 +132,13 @@
132 132
133 preloadImages(); 133 preloadImages();
134 134
135 - return { 135 + var self = {
136 start: start, 136 start: start,
137 stop: stop, 137 stop: stop,
138 waiting: waiting 138 waiting: waiting
139 }; 139 };
140 + wss._setLoadingDelegate(self);
141 + return self;
140 }]); 142 }]);
141 143
142 }()); 144 }());
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -79,21 +79,23 @@ ...@@ -79,21 +79,23 @@
79 79
80 angular.module('onosLayer') 80 angular.module('onosLayer')
81 .factory('VeilService', 81 .factory('VeilService',
82 - ['$log', '$route', 'FnService', 'KeyService', 'GlyphService', 82 + ['$log', '$route', 'FnService', 'KeyService', 'GlyphService', 'WebSocketService',
83 83
84 - function (_$log_, _$route_, _fs_, _ks_, _gs_) { 84 + function (_$log_, _$route_, _fs_, _ks_, _gs_, wss) {
85 $log = _$log_; 85 $log = _$log_;
86 $route = _$route_; 86 $route = _$route_;
87 fs = _fs_; 87 fs = _fs_;
88 ks = _ks_; 88 ks = _ks_;
89 gs = _gs_; 89 gs = _gs_;
90 90
91 - return { 91 + var self = {
92 init: init, 92 init: init,
93 show: show, 93 show: show,
94 hide: hide, 94 hide: hide,
95 lostServer: lostServer 95 lostServer: lostServer
96 }; 96 };
97 + wss._setVeilDelegate(self);
98 + return self;
97 }]); 99 }]);
98 100
99 }()); 101 }());
......
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
59 59
60 function handleOpen() { 60 function handleOpen() {
61 $log.info('Web socket open - ', url); 61 $log.info('Web socket open - ', url);
62 - vs.hide(); 62 + vs && vs.hide();
63 63
64 if (fs.debugOn('txrx')) { 64 if (fs.debugOn('txrx')) {
65 $log.debug('Sending ' + pendingEvents.length + ' pending event(s)...'); 65 $log.debug('Sending ' + pendingEvents.length + ' pending event(s)...');
...@@ -105,14 +105,14 @@ ...@@ -105,14 +105,14 @@
105 var gsucc; 105 var gsucc;
106 106
107 $log.info('Web socket closed'); 107 $log.info('Web socket closed');
108 - ls.stop(); 108 + ls && ls.stop();
109 wsUp = false; 109 wsUp = false;
110 110
111 if (gsucc = findGuiSuccessor()) { 111 if (gsucc = findGuiSuccessor()) {
112 createWebSocket(webSockOpts, gsucc); 112 createWebSocket(webSockOpts, gsucc);
113 } else { 113 } else {
114 // If no controllers left to contact, show the Veil... 114 // If no controllers left to contact, show the Veil...
115 - vs.show([ 115 + vs && vs.show([
116 'Oops!', 116 'Oops!',
117 'Web-socket connection to server closed...', 117 'Web-socket connection to server closed...',
118 'Try refreshing the page.' 118 'Try refreshing the page.'
...@@ -296,22 +296,29 @@ ...@@ -296,22 +296,29 @@
296 } 296 }
297 } 297 }
298 298
299 + // Binds the veil service as a delegate
300 + function setVeilDelegate(vd) {
301 + vs = vd;
302 + }
303 +
304 + // Binds the loading service as a delegate
305 + function setLoadingDelegate(ld) {
306 + ls = ld;
307 + }
308 +
299 309
300 // ============================ 310 // ============================
301 // ===== Definition of module 311 // ===== Definition of module
302 angular.module('onosRemote') 312 angular.module('onosRemote')
303 .factory('WebSocketService', 313 .factory('WebSocketService',
304 ['$log', '$location', 'FnService', 'UrlFnService', 'WSock', 314 ['$log', '$location', 'FnService', 'UrlFnService', 'WSock',
305 - 'VeilService', 'LoadingService',
306 315
307 - function (_$log_, _$loc_, _fs_, _ufs_, _wsock_, _vs_, _ls_) { 316 + function (_$log_, _$loc_, _fs_, _ufs_, _wsock_) {
308 $log = _$log_; 317 $log = _$log_;
309 $loc = _$loc_; 318 $loc = _$loc_;
310 fs = _fs_; 319 fs = _fs_;
311 ufs = _ufs_; 320 ufs = _ufs_;
312 wsock = _wsock_; 321 wsock = _wsock_;
313 - vs = _vs_;
314 - ls = _ls_;
315 322
316 bindHandlers(builtinHandlers); 323 bindHandlers(builtinHandlers);
317 324
...@@ -324,7 +331,10 @@ ...@@ -324,7 +331,10 @@
324 addOpenListener: addOpenListener, 331 addOpenListener: addOpenListener,
325 removeOpenListener: removeOpenListener, 332 removeOpenListener: removeOpenListener,
326 sendEvent: sendEvent, 333 sendEvent: sendEvent,
327 - isConnected: function () { return wsUp; } 334 + isConnected: function () { return wsUp; },
335 +
336 + _setVeilDelegate: setVeilDelegate,
337 + _setLoadingDelegate: setLoadingDelegate
328 }; 338 };
329 } 339 }
330 ]); 340 ]);
......
...@@ -21,54 +21,14 @@ ...@@ -21,54 +21,14 @@
21 'use strict'; 21 'use strict';
22 22
23 // injected refs 23 // injected refs
24 - var $log, $cookies, fs; 24 + var $log, fs, wss;
25 25
26 // internal state 26 // internal state
27 var cache = {}; 27 var cache = {};
28 28
29 - // NOTE: in Angular 1.3.5, $cookies is just a simple object, and 29 + // returns the preference by the specified name
30 - // cookie values are just strings. From the 1.3.5 docs: 30 + function getPrefs(name, defaults) {
31 - // 31 + return cache[name] || defaults;
32 - // "Only a simple Object is exposed and by adding or removing
33 - // properties to/from this object, new cookies are created/deleted
34 - // at the end of current $eval. The object's properties can only
35 - // be strings."
36 - //
37 - // We may want to upgrade the version of Angular sometime soon
38 - // since later version support objects as cookie values.
39 -
40 - // NOTE: prefs represented as simple name/value pairs
41 - // => a temporary restriction while we are encoding into cookies
42 - /*
43 - {
44 - foo: 1,
45 - bar: 0,
46 - goo: 2
47 - }
48 -
49 - stored as "foo:1,bar:0,goo:2"
50 - */
51 -
52 - // reads cookie with given name and returns an object rep of its value
53 - // or null if no such cookie is set
54 - function getPrefs(name) {
55 - var cook = $cookies[name],
56 - bits,
57 - obj = {};
58 -
59 - if (cook) {
60 - bits = cook.split(',');
61 - bits.forEach(function (value) {
62 - var x = value.split(':');
63 - obj[x[0]] = x[1];
64 - });
65 -
66 - // update the cache
67 - cache[name] = obj;
68 - return obj;
69 - }
70 - // perhaps we have a cached copy..
71 - return cache[name];
72 } 32 }
73 33
74 // converts string values to numbers for selected (or all) keys 34 // converts string values to numbers for selected (or all) keys
...@@ -89,34 +49,28 @@ ...@@ -89,34 +49,28 @@
89 } 49 }
90 50
91 function setPrefs(name, obj) { 51 function setPrefs(name, obj) {
92 - var bits = [], 52 + // keep a cached copy of the object and send an update to server
93 - str;
94 -
95 - angular.forEach(obj, function (value, key) {
96 - bits.push(key + ':' + value);
97 - });
98 - str = bits.join(',');
99 -
100 - // keep a cached copy of the object
101 cache[name] = obj; 53 cache[name] = obj;
102 - 54 + wss.sendEvent('updatePrefReq', { key: name, value: obj });
103 - // The angular way of doing this... 55 + }
104 - // $cookies[name] = str; 56 +
105 - // ...but it appears that this gets delayed, and doesn't 'stick' ?? 57 + function updatePrefs(data) {
106 - 58 + $log.info('User properties updated');
107 - // FORCE cookie to be set by writing directly to document.cookie... 59 + cache[data.key] = data.value;
108 - document.cookie = name + '=' + encodeURIComponent(str);
109 - if (fs.debugOn('prefs')) {
110 - $log.debug('<<>> Wrote cookie <'+name+'>:', str);
111 - }
112 } 60 }
113 61
114 angular.module('onosUtil') 62 angular.module('onosUtil')
115 - .factory('PrefsService', ['$log', '$cookies', 'FnService', 63 + .factory('PrefsService', ['$log', 'FnService', 'WebSocketService',
116 - function (_$log_, _$cookies_, _fs_) { 64 + function (_$log_, _fs_, _wss_) {
117 $log = _$log_; 65 $log = _$log_;
118 - $cookies = _$cookies_;
119 fs = _fs_; 66 fs = _fs_;
67 + wss = _wss_;
68 +
69 + cache = userPrefs;
70 +
71 + wss.bindHandlers({
72 + updatePrefs: updatePrefs
73 + });
120 74
121 return { 75 return {
122 getPrefs: getPrefs, 76 getPrefs: getPrefs,
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
20 (function () { 20 (function () {
21 'use strict'; 21 'use strict';
22 22
23 - var $log, fs; 23 + var $log, fs, ps;
24 24
25 var themes = ['light', 'dark'], 25 var themes = ['light', 'dark'],
26 themeStr = themes.join(' '), 26 themeStr = themes.join(' '),
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
29 nextListenerId = 1; 29 nextListenerId = 1;
30 30
31 function init() { 31 function init() {
32 - thidx = 0; 32 + thidx = ps.getPrefs('theme', { idx: 0 }).idx;
33 updateBodyClass(); 33 updateBodyClass();
34 } 34 }
35 35
...@@ -37,10 +37,11 @@ ...@@ -37,10 +37,11 @@
37 return themes[thidx]; 37 return themes[thidx];
38 } 38 }
39 39
40 - function setTheme(t) { 40 + function setTheme(t, force) {
41 var idx = themes.indexOf(t); 41 var idx = themes.indexOf(t);
42 - if (idx > -1 && idx !== thidx) { 42 + if (force || idx > -1 && idx !== thidx) {
43 thidx = idx; 43 thidx = idx;
44 + ps.setPrefs('theme', { idx: thidx });
44 updateBodyClass(); 45 updateBodyClass();
45 themeEvent('set'); 46 themeEvent('set');
46 } 47 }
...@@ -49,6 +50,7 @@ ...@@ -49,6 +50,7 @@
49 function toggleTheme() { 50 function toggleTheme() {
50 var i = thidx + 1; 51 var i = thidx + 1;
51 thidx = (i===themes.length) ? 0 : i; 52 thidx = (i===themes.length) ? 0 : i;
53 + ps.setPrefs('theme', { idx: thidx });
52 updateBodyClass(); 54 updateBodyClass();
53 themeEvent('toggle'); 55 themeEvent('toggle');
54 return getTheme(); 56 return getTheme();
...@@ -97,11 +99,11 @@ ...@@ -97,11 +99,11 @@
97 } 99 }
98 100
99 angular.module('onosUtil') 101 angular.module('onosUtil')
100 - .factory('ThemeService', ['$log', 'FnService', 102 + .factory('ThemeService', ['$log', 'FnService', 'PrefsService',
101 - function (_$log_, _fs_) { 103 + function (_$log_, _fs_, _ps_) {
102 $log = _$log_; 104 $log = _$log_;
103 fs = _fs_; 105 fs = _fs_;
104 - thidx = 0; 106 + ps = _ps_;
105 107
106 return { 108 return {
107 init: init, 109 init: init,
......
...@@ -219,6 +219,10 @@ ...@@ -219,6 +219,10 @@
219 } 219 }
220 } 220 }
221 221
222 + function isVisible() {
223 + return panel.isVisible();
224 + }
225 +
222 return { 226 return {
223 addButton: addButton, 227 addButton: addButton,
224 addToggle: addToggle, 228 addToggle: addToggle,
...@@ -228,7 +232,8 @@ ...@@ -228,7 +232,8 @@
228 232
229 show: show, 233 show: show,
230 hide: hide, 234 hide: hide,
231 - toggle: toggle 235 + toggle: toggle,
236 + isVisible: isVisible
232 }; 237 };
233 } 238 }
234 239
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
30 30
31 // references to injected services 31 // references to injected services
32 var $scope, $log, $cookies, fs, ks, zs, gs, ms, sus, flash, wss, ps, th, 32 var $scope, $log, $cookies, fs, ks, zs, gs, ms, sus, flash, wss, ps, th,
33 - tds, tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs, tspr, ttip, tov; 33 + tds, t3s, tes, tfs, tps, tis, tss, tls, tts, tos, fltr, ttbs, tspr, ttip, tov;
34 34
35 // DOM elements 35 // DOM elements
36 var ovtopo, svg, defs, zoomLayer, mapG, spriteG, forceG, noDevsLayer; 36 var ovtopo, svg, defs, zoomLayer, mapG, spriteG, forceG, noDevsLayer;
...@@ -369,16 +369,14 @@ ...@@ -369,16 +369,14 @@
369 369
370 function setUpMap($loc) { 370 function setUpMap($loc) {
371 var qp = $loc.search(), 371 var qp = $loc.search(),
372 - pr = ps.getPrefs('topo_mapid'), 372 + pr = ps.getPrefs('topo_mapid', {
373 - mi1 = qp.mapid, 373 + id: qp.mapid || 'usa',
374 - mi2 = pr && pr.id, 374 + scale: qp.mapscale || 1,
375 - mapId = mi1 || mi2 || 'usa', 375 + tint: qp.tint || 'off'
376 - ms1 = qp.mapscale, 376 + }),
377 - ms2 = pr && pr.scale, 377 + mapId = pr.id,
378 - mapScale = ms1 || ms2 || 1, 378 + mapScale = pr.scale,
379 - t1 = qp.tint, 379 + tint = pr.tint,
380 - t2 = pr && pr.tint,
381 - tint = t1 || t2 || 'off',
382 promise, 380 promise,
383 cfilter; 381 cfilter;
384 382
...@@ -441,8 +439,8 @@ ...@@ -441,8 +439,8 @@
441 439
442 function setUpSprites($loc, tspr) { 440 function setUpSprites($loc, tspr) {
443 var s1 = $loc.search().sprites, 441 var s1 = $loc.search().sprites,
444 - s2 = ps.getPrefs('topo_sprites'), 442 + s2 = ps.getPrefs('topo_sprites', { id: s1 }),
445 - sprId = s1 || (s2 && s2.id); 443 + sprId = s2.id;
446 444
447 spriteG = zoomLayer.append ('g').attr('id', 'topo-sprites'); 445 spriteG = zoomLayer.append ('g').attr('id', 'topo-sprites');
448 if (sprId) { 446 if (sprId) {
...@@ -463,7 +461,7 @@ ...@@ -463,7 +461,7 @@
463 461
464 function restoreConfigFromPrefs() { 462 function restoreConfigFromPrefs() {
465 // NOTE: toolbar will have set this for us.. 463 // NOTE: toolbar will have set this for us..
466 - prefsState = ps.asNumbers(ps.getPrefs('topo_prefs')); 464 + prefsState = ps.asNumbers(ps.getPrefs('topo_prefs', ttbs.defaultPrefs));
467 465
468 $log.debug('TOPO- Prefs State:', prefsState); 466 $log.debug('TOPO- Prefs State:', prefsState);
469 467
...@@ -476,6 +474,7 @@ ...@@ -476,6 +474,7 @@
476 togglePorts(prefsState.porthl); 474 togglePorts(prefsState.porthl);
477 toggleMap(prefsState.bg); 475 toggleMap(prefsState.bg);
478 toggleSprites(prefsState.spr); 476 toggleSprites(prefsState.spr);
477 + t3s.setDevLabIndex(prefsState.dlbls);
479 flash.enable(true); 478 flash.enable(true);
480 } 479 }
481 480
...@@ -484,7 +483,7 @@ ...@@ -484,7 +483,7 @@
484 // have opened the websocket to the server; hence this extra function 483 // have opened the websocket to the server; hence this extra function
485 // invoked after tes.start() 484 // invoked after tes.start()
486 function restoreSummaryFromPrefs() { 485 function restoreSummaryFromPrefs() {
487 - prefsState = ps.asNumbers(ps.getPrefs('topo_prefs')); 486 + prefsState = ps.asNumbers(ps.getPrefs('topo_prefs', ttbs.defaultPrefs));
488 $log.debug('TOPO- Prefs SUMMARY State:', prefsState.summary); 487 $log.debug('TOPO- Prefs SUMMARY State:', prefsState.summary);
489 488
490 flash.enable(false); 489 flash.enable(false);
...@@ -506,7 +505,7 @@ ...@@ -506,7 +505,7 @@
506 '$cookies', 'FnService', 'MastService', 'KeyService', 'ZoomService', 505 '$cookies', 'FnService', 'MastService', 'KeyService', 'ZoomService',
507 'GlyphService', 'MapService', 'SvgUtilService', 'FlashService', 506 'GlyphService', 'MapService', 'SvgUtilService', 'FlashService',
508 'WebSocketService', 'PrefsService', 'ThemeService', 507 'WebSocketService', 'PrefsService', 'ThemeService',
509 - 'TopoDialogService', 508 + 'TopoDialogService', 'TopoD3Service',
510 'TopoEventService', 'TopoForceService', 'TopoPanelService', 509 'TopoEventService', 'TopoForceService', 'TopoPanelService',
511 'TopoInstService', 'TopoSelectService', 'TopoLinkService', 510 'TopoInstService', 'TopoSelectService', 'TopoLinkService',
512 'TopoTrafficService', 'TopoObliqueService', 'TopoFilterService', 511 'TopoTrafficService', 'TopoObliqueService', 'TopoFilterService',
...@@ -515,7 +514,7 @@ ...@@ -515,7 +514,7 @@
515 514
516 function (_$scope_, _$log_, $loc, $timeout, _$cookies_, _fs_, mast, _ks_, 515 function (_$scope_, _$log_, $loc, $timeout, _$cookies_, _fs_, mast, _ks_,
517 _zs_, _gs_, _ms_, _sus_, _flash_, _wss_, _ps_, _th_, 516 _zs_, _gs_, _ms_, _sus_, _flash_, _wss_, _ps_, _th_,
518 - _tds_, _tes_, 517 + _tds_, _t3s_, _tes_,
519 _tfs_, _tps_, _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_, 518 _tfs_, _tps_, _tis_, _tss_, _tls_, _tts_, _tos_, _fltr_,
520 _ttbs_, _tspr_, _ttip_, _tov_) { 519 _ttbs_, _tspr_, _ttip_, _tov_) {
521 var params = $loc.search(), 520 var params = $loc.search(),
...@@ -545,6 +544,7 @@ ...@@ -545,6 +544,7 @@
545 ps = _ps_; 544 ps = _ps_;
546 th = _th_; 545 th = _th_;
547 tds = _tds_; 546 tds = _tds_;
547 + t3s = _t3s_;
548 tes = _tes_; 548 tes = _tes_;
549 tfs = _tfs_; 549 tfs = _tfs_;
550 // TODO: consider funnelling actions through TopoForceService... 550 // TODO: consider funnelling actions through TopoForceService...
...@@ -601,7 +601,7 @@ ...@@ -601,7 +601,7 @@
601 setUpNoDevs(); 601 setUpNoDevs();
602 setUpMap($loc).then( 602 setUpMap($loc).then(
603 function (proj) { 603 function (proj) {
604 - var z = ps.getPrefs('topo_zoom') || {tx:0, ty:0, sc:1}; 604 + var z = ps.getPrefs('topo_zoom', { tx:0, ty:0, sc:1 });
605 zoomer.panZoom([z.tx, z.ty], z.sc); 605 zoomer.panZoom([z.tx, z.ty], z.sc);
606 $log.debug('** Zoom restored:', z); 606 $log.debug('** Zoom restored:', z);
607 607
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
23 'use strict'; 23 'use strict';
24 24
25 // injected refs 25 // injected refs
26 - var $log, fs, sus, is, ts; 26 + var $log, fs, sus, is, ts, ps, ttbs;
27 27
28 // api to topoForce 28 // api to topoForce
29 var api; 29 var api;
...@@ -159,7 +159,7 @@ ...@@ -159,7 +159,7 @@
159 // ==== 159 // ====
160 160
161 function incDevLabIndex() { 161 function incDevLabIndex() {
162 - deviceLabelIndex = (deviceLabelIndex+1) % 3; 162 + setDevLabIndex(deviceLabelIndex+1);
163 switch(deviceLabelIndex) { 163 switch(deviceLabelIndex) {
164 case 0: return 'Hide device labels'; 164 case 0: return 'Hide device labels';
165 case 1: return 'Show friendly device labels'; 165 case 1: return 'Show friendly device labels';
...@@ -167,6 +167,13 @@ ...@@ -167,6 +167,13 @@
167 } 167 }
168 } 168 }
169 169
170 + function setDevLabIndex(mode) {
171 + deviceLabelIndex = mode % 3;
172 + var p = ps.getPrefs('topo_prefs', ttbs.defaultPrefs);
173 + p.dlbls = deviceLabelIndex;
174 + ps.setPrefs('topo_prefs', p);
175 + }
176 +
170 // Returns the newly computed bounding box of the rectangle 177 // Returns the newly computed bounding box of the rectangle
171 function adjustRectToFitText(n) { 178 function adjustRectToFitText(n) {
172 var text = n.select('text'), 179 var text = n.select('text'),
...@@ -599,13 +606,16 @@ ...@@ -599,13 +606,16 @@
599 angular.module('ovTopo') 606 angular.module('ovTopo')
600 .factory('TopoD3Service', 607 .factory('TopoD3Service',
601 ['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService', 608 ['$log', 'FnService', 'SvgUtilService', 'IconService', 'ThemeService',
609 + 'PrefsService', 'TopoToolbarService',
602 610
603 - function (_$log_, _fs_, _sus_, _is_, _ts_) { 611 + function (_$log_, _fs_, _sus_, _is_, _ts_, _ps_, _ttbs_) {
604 $log = _$log_; 612 $log = _$log_;
605 fs = _fs_; 613 fs = _fs_;
606 sus = _sus_; 614 sus = _sus_;
607 is = _is_; 615 is = _is_;
608 ts = _ts_; 616 ts = _ts_;
617 + ps = _ps_;
618 + ttbs = _ttbs_;
609 619
610 icfg = is.iconConfig(); 620 icfg = is.iconConfig();
611 621
...@@ -620,6 +630,7 @@ ...@@ -620,6 +630,7 @@
620 destroyD3: destroyD3, 630 destroyD3: destroyD3,
621 631
622 incDevLabIndex: incDevLabIndex, 632 incDevLabIndex: incDevLabIndex,
633 + setDevLabIndex: setDevLabIndex,
623 adjustRectToFitText: adjustRectToFitText, 634 adjustRectToFitText: adjustRectToFitText,
624 hostLabel: hostLabel, 635 hostLabel: hostLabel,
625 deviceLabel: deviceLabel, 636 deviceLabel: deviceLabel,
......
...@@ -529,6 +529,7 @@ ...@@ -529,6 +529,7 @@
529 529
530 showSummary: showSummary, 530 showSummary: showSummary,
531 toggleSummary: toggleSummary, 531 toggleSummary: toggleSummary,
532 + hideSummary: hideSummaryPanel,
532 533
533 toggleUseDetailsFlag: toggleUseDetailsFlag, 534 toggleUseDetailsFlag: toggleUseDetailsFlag,
534 displaySingle: displaySingle, 535 displaySingle: displaySingle,
...@@ -538,8 +539,6 @@ ...@@ -538,8 +539,6 @@
538 displaySomething: displaySomething, 539 displaySomething: displaySomething,
539 addAction: addAction, 540 addAction: addAction,
540 541
541 - hideSummaryPanel: hideSummaryPanel,
542 -
543 detailVisible: function () { return detail.panel().isVisible(); }, 542 detailVisible: function () { return detail.panel().isVisible(); },
544 summaryVisible: function () { return summary.panel().isVisible(); } 543 summaryVisible: function () { return summary.panel().isVisible(); }
545 }; 544 };
......
...@@ -70,11 +70,12 @@ ...@@ -70,11 +70,12 @@
70 70
71 // initial toggle state: default settings and tag to key mapping 71 // initial toggle state: default settings and tag to key mapping
72 var defaultPrefsState = { 72 var defaultPrefsState = {
73 - summary: 1,
74 insts: 1, 73 insts: 1,
74 + summary: 1,
75 detail: 1, 75 detail: 1,
76 hosts: 0, 76 hosts: 0,
77 offdev: 1, 77 offdev: 1,
78 + dlbls: 0,
78 porthl: 1, 79 porthl: 1,
79 bg: 0, 80 bg: 0,
80 spr: 0, 81 spr: 0,
...@@ -104,7 +105,7 @@ ...@@ -104,7 +105,7 @@
104 } 105 }
105 106
106 function setInitToggleState() { 107 function setInitToggleState() {
107 - cachedState = ps.asNumbers(ps.getPrefs(cooktag)); 108 + cachedState = ps.asNumbers(ps.getPrefs(cooktag, defaultPrefsState));
108 $log.debug('TOOLBAR---- read prefs state:', cachedState); 109 $log.debug('TOOLBAR---- read prefs state:', cachedState);
109 110
110 if (!cachedState) { 111 if (!cachedState) {
...@@ -264,6 +265,9 @@ ...@@ -264,6 +265,9 @@
264 265
265 function toggleToolbar() { 266 function toggleToolbar() {
266 toolbar.toggle(); 267 toolbar.toggle();
268 + var prefs = ps.getPrefs(cooktag, defaultPrefsState);
269 + prefs.toolbar = !prefs.toolbar;
270 + ps.setPrefs('topo_prefs', prefs);
267 } 271 }
268 272
269 function setDefaultOverlay() { 273 function setDefaultOverlay() {
...@@ -298,6 +302,7 @@ ...@@ -298,6 +302,7 @@
298 keyListener: keyListener, 302 keyListener: keyListener,
299 toggleToolbar: toggleToolbar, 303 toggleToolbar: toggleToolbar,
300 setDefaultOverlay: setDefaultOverlay, 304 setDefaultOverlay: setDefaultOverlay,
305 + defaultPrefs: defaultPrefsState,
301 fnkey: fnkey 306 fnkey: fnkey
302 }; 307 };
303 }]); 308 }]);
......
...@@ -38,6 +38,9 @@ ...@@ -38,6 +38,9 @@
38 <script src="tp/Chart.min.js"></script> 38 <script src="tp/Chart.min.js"></script>
39 <script src="tp/angular-chart.min.js"></script> 39 <script src="tp/angular-chart.min.js"></script>
40 40
41 + <!-- {INJECTED-USER-START} -->
42 + <!-- {INJECTED-USER-END} -->
43 +
41 <!-- ONOS UI Framework included here --> 44 <!-- ONOS UI Framework included here -->
42 <!-- TODO: use a single catenated-minified file here --> 45 <!-- TODO: use a single catenated-minified file here -->
43 <script src="onos.js"></script> 46 <script src="onos.js"></script>
......