Adding server-side user preferences.
More work still needs to get done to allow client to process server-pushed preferences updates. Change-Id: I6e80e3f3677285cb19cfa3b6240c1b13aac56622
Showing
19 changed files
with
394 additions
and
122 deletions
... | @@ -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> | ... | ... |
-
Please register or login to post a comment