##// END OF EJS Templates
frontend: introduce rhodecode-app for more complex cross element wiring
ergo -
r787:048ecbf3 default
parent child Browse files
Show More
@@ -0,0 +1,106 b''
1 <link rel="import" href="../../../../../../bower_components/polymer/polymer.html">
2 <link rel="import" href="../../../../../../bower_components/iron-ajax/iron-ajax.html">
3
4 <!--
5
6 `<channelstream-connection>` allows you to connect and interact with channelstream server
7 abstracting websocket/long-polling connections from you.
8
9 In typical use, just slap some `<channelstream-connection>` at the top of your body:
10
11 <body>
12 <channelstream-connection
13 username="{{user.username}}"
14 connect-url="http://127.0.0.1:8000/demo/connect"
15 disconnect-url="http://127.0.0.1:8000/disconnect"
16 subscribe-url="http://127.0.0.1:8000/demo/subscribe"
17 message-url="http://127.0.0.1:8000/demo/message"
18 long-poll-url="http://127.0.0.1:8000/listen"
19 websocket-url="http://127.0.0.1:8000/ws"
20 channels-url='["channel1", "channel2"]' />
21
22 Then you can do `channelstreamElem.connect()` to kick off your connection.
23 This element also handles automatic reconnections.
24
25 ## Default handlers
26
27 By default element has a listener attached that will fire `startListening()` handler on `channelstream-connected` event.
28
29 By default element has a listener attached that will fire `retryConnection()` handler on `channelstream-connect-error` event,
30 this handler will forever try to re-establish connection to the server incrementing intervals between retries up to 1 minute.
31
32 -->
33 <dom-module id="channelstream-connection">
34 <template>
35
36 <iron-ajax
37 id="ajaxConnect"
38 url=""
39 handle-as="json"
40 method="post"
41 content-type="application/json"
42 loading="{{loadingConnect}}"
43 last-response="{{connectLastResponse}}"
44 on-response="_handleConnect"
45 on-error="_handleConnectError"
46 debounce-duration="100"></iron-ajax>
47
48 <iron-ajax
49 id="ajaxDisconnect"
50 url=""
51 handle-as="json"
52 method="post"
53 content-type="application/json"
54 loading="{{loadingDisconnect}}"
55 last-response="{{_disconnectLastResponse}}"
56 on-response="_handleDisconnect"
57 debounce-duration="100"></iron-ajax>
58
59 <iron-ajax
60 id="ajaxSubscribe"
61 url=""
62 handle-as="json"
63 method="post"
64 content-type="application/json"
65 loading="{{loadingSubscribe}}"
66 last-response="{{subscribeLastResponse}}"
67 on-response="_handleSubscribe"
68 debounce-duration="100"></iron-ajax>
69
70 <iron-ajax
71 id="ajaxUnsubscribe"
72 url=""
73 handle-as="json"
74 method="post"
75 content-type="application/json"
76 loading="{{loadingUnsubscribe}}"
77 last-response="{{unsubscribeLastResponse}}"
78 on-response="_handleUnsubscribe"
79 debounce-duration="100"></iron-ajax>
80
81 <iron-ajax
82 id="ajaxMessage"
83 url=""
84 handle-as="json"
85 method="post"
86 content-type="application/json"
87 loading="{{loadingMessage}}"
88 last-response="{{messageLastResponse}}"
89 on-response="_handleMessage"
90 on-error="_handleMessageError"
91 debounce-duration="100"></iron-ajax>
92
93 <iron-ajax
94 id="ajaxListen"
95 url=""
96 handle-as="text"
97 loading="{{loadingListen}}"
98 last-response="{{listenLastResponse}}"
99 on-request="_handleListenOpen"
100 on-error="_handleListenError"
101 on-response="_handleListenMessageEvent"
102 debounce-duration="100"></iron-ajax>
103
104 </template>
105 <script src="channelstream-connection.js"></script>
106 </dom-module>
This diff has been collapsed as it changes many lines, (502 lines changed) Show them Hide them
@@ -0,0 +1,502 b''
1 Polymer({
2 is: 'channelstream-connection',
3
4 /**
5 * Fired when `channels` array changes.
6 *
7 * @event channelstream-channels-changed
8 */
9
10 /**
11 * Fired when `connect()` method succeeds.
12 *
13 * @event channelstream-connected
14 */
15
16 /**
17 * Fired when `connect` fails.
18 *
19 * @event channelstream-connect-error
20 */
21
22 /**
23 * Fired when `disconnect()` succeeds.
24 *
25 * @event channelstream-disconnected
26 */
27
28 /**
29 * Fired when `message()` succeeds.
30 *
31 * @event channelstream-message-sent
32 */
33
34 /**
35 * Fired when `message()` fails.
36 *
37 * @event channelstream-message-error
38 */
39
40 /**
41 * Fired when `subscribe()` succeeds.
42 *
43 * @event channelstream-subscribed
44 */
45
46 /**
47 * Fired when `subscribe()` fails.
48 *
49 * @event channelstream-subscribe-error
50 */
51
52 /**
53 * Fired when `unsubscribe()` succeeds.
54 *
55 * @event channelstream-unsubscribed
56 */
57
58 /**
59 * Fired when `unsubscribe()` fails.
60 *
61 * @event channelstream-unsubscribe-error
62 */
63
64 /**
65 * Fired when listening connection receives a message.
66 *
67 * @event channelstream-listen-message
68 */
69
70 /**
71 * Fired when listening connection is opened.
72 *
73 * @event channelstream-listen-opened
74 */
75
76 /**
77 * Fired when listening connection is closed.
78 *
79 * @event channelstream-listen-closed
80 */
81
82 /**
83 * Fired when listening connection suffers an error.
84 *
85 * @event channelstream-listen-error
86 */
87
88 properties: {
89 isReady: Boolean,
90 /** List of channels user should be subscribed to. */
91 channels: {
92 type: Array,
93 value: function () {
94 return []
95 },
96 notify: true
97 },
98 /** Username of connecting user. */
99 username: {
100 type: String,
101 value: 'Anonymous',
102 reflectToAttribute: true
103 },
104 /** Connection identifier. */
105 connectionId: {
106 type: String,
107 reflectToAttribute: true
108 },
109 /** Websocket instance. */
110 websocket: {
111 type: Object,
112 value: null
113 },
114 /** Websocket connection url. */
115 websocketUrl: {
116 type: String,
117 value: ''
118 },
119 /** URL used in `connect()`. */
120 connectUrl: {
121 type: String,
122 value: ''
123 },
124 /** URL used in `disconnect()`. */
125 disconnectUrl: {
126 type: String,
127 value: ''
128 },
129 /** URL used in `subscribe()`. */
130 subscribeUrl: {
131 type: String,
132 value: ''
133 },
134 /** URL used in `unsubscribe()`. */
135 unsubscribeUrl: {
136 type: String,
137 value: ''
138 },
139 /** URL used in `message()`. */
140 messageUrl: {
141 type: String,
142 value: ''
143 },
144 /** Long-polling connection url. */
145 longPollUrl: {
146 type: String,
147 value: ''
148 },
149 /** Long-polling connection url. */
150 shouldReconnect: {
151 type: Boolean,
152 value: true
153 },
154 /** Should send heartbeats. */
155 heartbeats: {
156 type: Boolean,
157 value: true
158 },
159 /** How much should every retry interval increase (in milliseconds) */
160 increaseBounceIv: {
161 type: Number,
162 value: 2000
163 },
164 _currentBounceIv: {
165 type: Number,
166 reflectToAttribute: true,
167 value: 0
168 },
169 /** Should use websockets or long-polling by default */
170 useWebsocket: {
171 type: Boolean,
172 reflectToAttribute: true,
173 value: true
174 },
175 connected: {
176 type: Boolean,
177 reflectToAttribute: true,
178 value: false
179 }
180 },
181
182 observers: [
183 '_handleChannelsChange(channels.splices)'
184 ],
185
186 listeners: {
187 'channelstream-connected': 'startListening',
188 'channelstream-connect-error': 'retryConnection',
189 },
190
191 /**
192 * Mutators hold functions that you can set locally to change the data
193 * that the client is sending to all endpoints
194 * you can call it like `elem.mutators('connect', yourFunc())`
195 * mutators will be executed in order they were pushed onto arrays
196 *
197 */
198 mutators: {
199 connect: function () {
200 return []
201 }(),
202 message: function () {
203 return []
204 }(),
205 subscribe: function () {
206 return []
207 }(),
208 unsubscribe: function () {
209 return []
210 }(),
211 disconnect: function () {
212 return []
213 }()
214 },
215 ready: function () {
216 this.isReady = true;
217 },
218
219 /**
220 * Connects user and fetches connection id from the server.
221 *
222 */
223 connect: function () {
224 var request = this.$['ajaxConnect'];
225 request.url = this.connectUrl;
226 request.body = {
227 username: this.username,
228 channels: this.channels
229 };
230 for (var i = 0; i < this.mutators.connect.length; i++) {
231 this.mutators.connect[i](request);
232 }
233 request.generateRequest()
234 },
235 /**
236 * Overwrite with custom function that will
237 */
238 addMutator: function (type, func) {
239 this.mutators[type].push(func);
240 },
241 /**
242 * Subscribes user to channels.
243 *
244 */
245 subscribe: function (channels) {
246 var request = this.$['ajaxSubscribe'];
247 request.url = this.subscribeUrl;
248 request.body = {
249 channels: channels,
250 conn_id: this.connectionId
251 };
252 for (var i = 0; i < this.mutators.subscribe.length; i++) {
253 this.mutators.subscribe[i](request);
254 }
255 if (request.body.channels.length) {
256 request.generateRequest();
257 }
258 },
259 /**
260 * Unsubscribes user from channels.
261 *
262 */
263 unsubscribe: function (unsubscribe) {
264 var request = this.$['ajaxUnsubscribe'];
265
266 request.url = this.unsubscribeUrl;
267 request.body = {
268 channels: unsubscribe,
269 conn_id: this.connectionId
270 };
271 for (var i = 0; i < this.mutators.unsubscribe.length; i++) {
272 this.mutators.unsubscribe[i](request);
273 }
274 request.generateRequest()
275 },
276
277 /**
278 * calculates list of channels we should add user to based on difference
279 * between channels property and passed channel list
280 */
281 calculateSubscribe: function (channels) {
282 var currentlySubscribed = this.channels;
283 var toSubscribe = [];
284 for (var i = 0; i < channels.length; i++) {
285 if (currentlySubscribed.indexOf(channels[i]) === -1) {
286 toSubscribe.push(channels[i]);
287 }
288 }
289 return toSubscribe
290 },
291 /**
292 * calculates list of channels we should remove user from based difference
293 * between channels property and passed channel list
294 */
295 calculateUnsubscribe: function (channels) {
296 var currentlySubscribed = this.channels;
297 var toUnsubscribe = [];
298 for (var i = 0; i < channels.length; i++) {
299 if (currentlySubscribed.indexOf(channels[i]) !== -1) {
300 toUnsubscribe.push(channels[i]);
301 }
302 }
303 return toUnsubscribe
304 },
305 /**
306 * Marks the connection as expired.
307 *
308 */
309 disconnect: function () {
310 var request = this.$['ajaxDisconnect'];
311 request.url = this.disconnectUrl;
312 request.params = {
313 conn_id: this.connectionId
314 };
315 for (var i = 0; i < this.mutators.disconnect.length; i++) {
316 this.mutators.disconnect[i](request);
317 }
318 // mark connection as expired
319 request.generateRequest();
320 // disconnect existing connection
321 this.closeConnection();
322 },
323
324 /**
325 * Sends a message to the server.
326 *
327 */
328 message: function (message) {
329 var request = this.$['ajaxMessage'];
330 request.url = this.messageUrl;
331 request.body = message;
332 for (var i = 0; i < this.mutators.message.length; i++) {
333 this.mutators.message[i](request)
334 }
335 request.generateRequest();
336 },
337 /**
338 * Opens "long lived" (websocket/longpoll) connection to the channelstream server.
339 *
340 */
341 startListening: function (event) {
342 this.fire('start-listening', {});
343 if (this.useWebsocket) {
344 this.useWebsocket = window.WebSocket ? true : false;
345 }
346 if (this.useWebsocket) {
347 this.openWebsocket();
348 }
349 else {
350 this.openLongPoll();
351 }
352 },
353 /**
354 * Opens websocket connection.
355 *
356 */
357 openWebsocket: function () {
358 var url = this.websocketUrl + '?conn_id=' + this.connectionId;
359 this.websocket = new WebSocket(url);
360 this.websocket.onopen = this._handleListenOpen.bind(this);
361 this.websocket.onclose = this._handleListenCloseEvent.bind(this);
362 this.websocket.onerror = this._handleListenErrorEvent.bind(this);
363 this.websocket.onmessage = this._handleListenMessageEvent.bind(this);
364 },
365 /**
366 * Opens long-poll connection.
367 *
368 */
369 openLongPoll: function () {
370 var request = this.$['ajaxListen'];
371 request.url = this.longPollUrl + '?conn_id=' + this.connectionId;
372 request.generateRequest()
373 },
374 /**
375 * Retries `connect()` call while incrementing interval between tries up to 1 minute.
376 *
377 */
378 retryConnection: function () {
379 if (!this.shouldReconnect) {
380 return;
381 }
382 if (this._currentBounceIv < 60000) {
383 this._currentBounceIv = this._currentBounceIv + this.increaseBounceIv;
384 }
385 else {
386 this._currentBounceIv = 60000;
387 }
388 setTimeout(this.connect.bind(this), this._currentBounceIv);
389 },
390 /**
391 * Closes listening connection.
392 *
393 */
394 closeConnection: function () {
395 var request = this.$['ajaxListen'];
396 if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
397 this.websocket.onclose = null;
398 this.websocket.onerror = null;
399 this.websocket.close();
400 }
401 if (request.loading) {
402 request.lastRequest.abort();
403 }
404 this.connected = false;
405 },
406
407 _handleChannelsChange: function (event) {
408 // do not fire the event if set() didn't mutate anything
409 // is this a reliable way to do it?
410 if (!this.isReady || event === undefined) {
411 return
412 }
413 this.fire('channelstream-channels-changed', event)
414 },
415
416 _handleListenOpen: function (event) {
417 this.connected = true;
418 this.fire('channelstream-listen-opened', event);
419 this.createHeartBeats();
420 },
421
422 createHeartBeats: function () {
423 if (typeof self._heartbeat === 'undefined' && this.websocket !== null
424 && this.heartbeats) {
425 self._heartbeat = setInterval(this._sendHeartBeat.bind(this), 10000);
426 }
427 },
428
429 _sendHeartBeat: function () {
430 if (this.websocket.readyState === WebSocket.OPEN && this.heartbeats) {
431 this.websocket.send(JSON.stringify({type: 'heartbeat'}));
432 }
433 },
434
435 _handleListenError: function (event) {
436 this.connected = false;
437 this.retryConnection();
438 },
439 _handleConnectError: function (event) {
440 this.connected = false;
441 this.fire('channelstream-connect-error', event.detail);
442 },
443
444 _handleListenMessageEvent: function (event) {
445 var data = null;
446 // comes from iron-ajax
447 if (event.detail) {
448 data = JSON.parse(event.detail.response)
449 // comes from websocket
450 setTimeout(this.openLongPoll.bind(this), 0);
451 } else {
452 data = JSON.parse(event.data)
453 }
454 this.fire('channelstream-listen-message', data);
455
456 },
457
458 _handleListenCloseEvent: function (event) {
459 this.connected = false;
460 this.fire('channelstream-listen-closed', event.detail);
461 this.retryConnection();
462 },
463
464 _handleListenErrorEvent: function (event) {
465 this.connected = false;
466 this.fire('channelstream-listen-error', {})
467 },
468
469 _handleConnect: function (event) {
470 this.currentBounceIv = 0;
471 this.connectionId = event.detail.response.conn_id;
472 this.fire('channelstream-connected', event.detail.response);
473 },
474
475 _handleDisconnect: function (event) {
476 this.connected = false;
477 this.fire('channelstream-disconnected', {});
478 },
479
480 _handleMessage: function (event) {
481 this.fire('channelstream-message-sent', event.detail.response);
482 },
483 _handleMessageError: function (event) {
484 this.fire('channelstream-message-error', event.detail);
485 },
486
487 _handleSubscribe: function (event) {
488 this.fire('channelstream-subscribed', event.detail.response);
489 },
490
491 _handleSubscribeError: function (event) {
492 this.fire('channelstream-subscribe-error', event.detail);
493 },
494
495 _handleUnsubscribe: function (event) {
496 this.fire('channelstream-unsubscribed', event.detail.response);
497 },
498
499 _handleUnsubscribeError: function (event) {
500 this.fire('channelstream-unsubscribe-error', event.detail);
501 }
502 });
@@ -0,0 +1,15 b''
1 <link rel="import" href="../../../../../../bower_components/polymer/polymer.html">
2 <link rel="import" href="../channelstream-connection/channelstream-connection.html">
3
4 <dom-module id="rhodecode-app">
5 <template>
6 <rhodecode-toast id="notifications"></rhodecode-toast>
7 <channelstream-connection
8 id="channelstream-connection"
9 on-channelstream-listen-message="receivedMessage"
10 on-channelstream-connected="handleConnected"
11 on-channelstream-subscribed="handleSubscribed">
12 </channelstream-connection>
13 </template>
14 <script src="rhodecode-app.js"></script>
15 </dom-module>
@@ -0,0 +1,127 b''
1 ccLog = Logger.get('RhodeCodeApp');
2 ccLog.setLevel(Logger.OFF);
3
4 var rhodeCodeApp = Polymer({
5 is: 'rhodecode-app',
6 created: function () {
7 ccLog.debug('rhodeCodeApp created');
8 $.Topic('/notifications').subscribe(this.handleNotifications.bind(this));
9
10 $.Topic('/plugins/__REGISTER__').subscribe(
11 this.kickoffChannelstreamPlugin.bind(this)
12 );
13
14 $.Topic('/connection_controller/subscribe').subscribe(
15 this.subscribeToChannelTopic.bind(this));
16 },
17
18 /** proxy to channelstream connection */
19 getChannelStreamConnection: function () {
20 return this.$['channelstream-connection'];
21 },
22
23 handleNotifications: function (data) {
24 this.$['notifications'].handleNotification(data);
25 },
26
27 /** opens connection to ws server */
28 kickoffChannelstreamPlugin: function (data) {
29 ccLog.debug('kickoffChannelstreamPlugin');
30 var channels = ['broadcast'];
31 var addChannels = this.checkViewChannels();
32 for (var i = 0; i < addChannels.length; i++) {
33 channels.push(addChannels[i]);
34 }
35 var channelstreamConnection = this.$['channelstream-connection'];
36 channelstreamConnection.connectUrl = CHANNELSTREAM_URLS.connect;
37 channelstreamConnection.subscribeUrl = CHANNELSTREAM_URLS.subscribe;
38 channelstreamConnection.websocketUrl = CHANNELSTREAM_URLS.ws + '/ws';
39 channelstreamConnection.longPollUrl = CHANNELSTREAM_URLS.longpoll + '/listen';
40 // some channels might already be registered by topic
41 for (var i = 0; i < channels.length; i++) {
42 channelstreamConnection.push('channels', channels[i]);
43 }
44 // append any additional channels registered in other plugins
45 $.Topic('/connection_controller/subscribe').processPrepared();
46 channelstreamConnection.connect();
47 },
48
49 checkViewChannels: function () {
50 var channels = []
51 // subscribe to PR repo channel for PR's'
52 if (templateContext.pull_request_data.pull_request_id) {
53 var channelName = '/repo$' + templateContext.repo_name + '$/pr/' +
54 String(templateContext.pull_request_data.pull_request_id);
55 channels.push(channelName);
56 }
57 return channels;
58 },
59
60 /** subscribes users from channels in channelstream */
61 subscribeToChannelTopic: function (channels) {
62 var channelstreamConnection = this.$['channelstream-connection'];
63 var toSubscribe = channelstreamConnection.calculateSubscribe(channels);
64 ccLog.debug('subscribeToChannelTopic', toSubscribe);
65 if (toSubscribe.length > 0) {
66 // if we are connected then subscribe
67 if (channelstreamConnection.connected) {
68 channelstreamConnection.subscribe(toSubscribe);
69 }
70 // not connected? just push channels onto the stack
71 else {
72 for (var i = 0; i < toSubscribe.length; i++) {
73 channelstreamConnection.push('channels', toSubscribe[i]);
74 }
75 }
76 }
77 },
78
79 /** publish received messages into correct topic */
80 receivedMessage: function (event) {
81 for (var i = 0; i < event.detail.length; i++) {
82 var message = event.detail[i];
83 if (message.message.topic) {
84 ccLog.debug('publishing', message.message.topic);
85 $.Topic(message.message.topic).publish(message);
86 }
87 else if (message.type === 'presence'){
88 $.Topic('/connection_controller/presence').publish(message);
89 }
90 else {
91 ccLog.warn('unhandled message', message);
92 }
93 }
94 },
95
96 handleConnected: function (event) {
97 var channelstreamConnection = this.$['channelstream-connection'];
98 channelstreamConnection.set('channelsState',
99 event.detail.channels_info);
100 channelstreamConnection.set('userState', event.detail.state);
101 channelstreamConnection.set('channels', event.detail.channels);
102 this.propagageChannelsState();
103 },
104 handleSubscribed: function (event) {
105 var channelstreamConnection = this.$['channelstream-connection'];
106 var channelInfo = event.detail.channels_info;
107 var channelKeys = Object.keys(event.detail.channels_info);
108 for (var i = 0; i < channelKeys.length; i++) {
109 var key = channelKeys[i];
110 channelstreamConnection.set(['channelsState', key], channelInfo[key]);
111 }
112 channelstreamConnection.set('channels', event.detail.channels);
113 this.propagageChannelsState();
114 },
115 /** propagates channel states on topics */
116 propagageChannelsState: function (event) {
117 var channelstreamConnection = this.$['channelstream-connection'];
118 var channel_data = channelstreamConnection.channelsState;
119 var channels = channelstreamConnection.channels;
120 for (var i = 0; i < channels.length; i++) {
121 var key = channels[i];
122 $.Topic('/connection_controller/channel_update').publish(
123 {channel: key, state: channel_data[key]}
124 );
125 }
126 }
127 });
@@ -1,188 +1,186 b''
1 1 {
2 2 "dirs": {
3 3 "css": {
4 4 "src":"rhodecode/public/css",
5 5 "dest":"rhodecode/public/css"
6 6 },
7 7 "js": {
8 8 "src": "rhodecode/public/js/src",
9 9 "dest": "rhodecode/public/js"
10 10 }
11 11 },
12 12 "copy": {
13 13 "main": {
14 14 "expand": true,
15 15 "cwd": "bower_components",
16 16 "src": "webcomponentsjs/webcomponents-lite.js",
17 17 "dest": "<%= dirs.js.dest %>/vendors"
18 18 }
19 19 },
20 20 "concat": {
21 21 "polymercss": {
22 22 "src": [
23 23 "<%= dirs.js.src %>/components/root-styles-prefix.html",
24 24 "<%= dirs.css.src %>/style-polymer.css",
25 25 "<%= dirs.js.src %>/components/root-styles-suffix.html"
26 26 ],
27 27 "dest": "<%= dirs.js.dest %>/src/components/root-styles.gen.html",
28 28 "nonull": true
29 29 },
30 30 "dist": {
31 31 "src": [
32 32 "<%= dirs.js.src %>/jquery-1.11.1.min.js",
33 33 "<%= dirs.js.src %>/logging.js",
34 34 "<%= dirs.js.src %>/bootstrap.js",
35 35 "<%= dirs.js.src %>/mousetrap.js",
36 36 "<%= dirs.js.src %>/moment.js",
37 37 "<%= dirs.js.src %>/appenlight-client-0.4.1.min.js",
38 38 "<%= dirs.js.src %>/i18n_utils.js",
39 39 "<%= dirs.js.src %>/deform.js",
40 40 "<%= dirs.js.src %>/plugins/jquery.pjax.js",
41 41 "<%= dirs.js.src %>/plugins/jquery.dataTables.js",
42 42 "<%= dirs.js.src %>/plugins/flavoured_checkbox.js",
43 43 "<%= dirs.js.src %>/plugins/jquery.auto-grow-input.js",
44 44 "<%= dirs.js.src %>/plugins/jquery.autocomplete.js",
45 45 "<%= dirs.js.src %>/plugins/jquery.debounce.js",
46 46 "<%= dirs.js.src %>/plugins/jquery.mark.js",
47 47 "<%= dirs.js.src %>/plugins/jquery.timeago.js",
48 48 "<%= dirs.js.src %>/plugins/jquery.timeago-extension.js",
49 49 "<%= dirs.js.src %>/select2/select2.js",
50 50 "<%= dirs.js.src %>/codemirror/codemirror.js",
51 51 "<%= dirs.js.src %>/codemirror/codemirror_loadmode.js",
52 52 "<%= dirs.js.src %>/codemirror/codemirror_hint.js",
53 53 "<%= dirs.js.src %>/codemirror/codemirror_overlay.js",
54 54 "<%= dirs.js.src %>/codemirror/codemirror_placeholder.js",
55 55 "<%= dirs.js.dest %>/mode/meta.js",
56 56 "<%= dirs.js.dest %>/mode/meta_ext.js",
57 57 "<%= dirs.js.dest %>/rhodecode/i18n/select2/translations.js",
58 58 "<%= dirs.js.src %>/rhodecode/utils/array.js",
59 59 "<%= dirs.js.src %>/rhodecode/utils/string.js",
60 60 "<%= dirs.js.src %>/rhodecode/utils/pyroutes.js",
61 61 "<%= dirs.js.src %>/rhodecode/utils/ajax.js",
62 62 "<%= dirs.js.src %>/rhodecode/utils/autocomplete.js",
63 63 "<%= dirs.js.src %>/rhodecode/utils/colorgenerator.js",
64 64 "<%= dirs.js.src %>/rhodecode/utils/ie.js",
65 65 "<%= dirs.js.src %>/rhodecode/utils/os.js",
66 66 "<%= dirs.js.src %>/rhodecode/utils/topics.js",
67 67 "<%= dirs.js.src %>/rhodecode/widgets/multiselect.js",
68 68 "<%= dirs.js.src %>/rhodecode/init.js",
69 "<%= dirs.js.src %>/rhodecode/connection_controller.js",
70 69 "<%= dirs.js.src %>/rhodecode/codemirror.js",
71 70 "<%= dirs.js.src %>/rhodecode/comments.js",
72 71 "<%= dirs.js.src %>/rhodecode/constants.js",
73 72 "<%= dirs.js.src %>/rhodecode/files.js",
74 73 "<%= dirs.js.src %>/rhodecode/followers.js",
75 74 "<%= dirs.js.src %>/rhodecode/menus.js",
76 75 "<%= dirs.js.src %>/rhodecode/notifications.js",
77 76 "<%= dirs.js.src %>/rhodecode/permissions.js",
78 77 "<%= dirs.js.src %>/rhodecode/pjax.js",
79 78 "<%= dirs.js.src %>/rhodecode/pullrequests.js",
80 79 "<%= dirs.js.src %>/rhodecode/settings.js",
81 80 "<%= dirs.js.src %>/rhodecode/select2_widgets.js",
82 81 "<%= dirs.js.src %>/rhodecode/tooltips.js",
83 82 "<%= dirs.js.src %>/rhodecode/users.js",
84 "<%= dirs.js.src %>/rhodecode/utils/notifications.js",
85 83 "<%= dirs.js.src %>/rhodecode/appenlight.js",
86 84 "<%= dirs.js.src %>/rhodecode.js"
87 85 ],
88 86 "dest": "<%= dirs.js.dest %>/scripts.js",
89 87 "nonull": true
90 88 }
91 89 },
92 90 "crisper": {
93 91 "dist": {
94 92 "options": {
95 93 "cleanup": false,
96 94 "onlySplit": true
97 95 },
98 96 "src": "<%= dirs.js.dest %>/rhodecode-components.html",
99 97 "dest": "<%= dirs.js.dest %>/rhodecode-components.js"
100 98 }
101 99 },
102 100 "less": {
103 101 "development": {
104 102 "options": {
105 103 "compress": false,
106 104 "yuicompress": false,
107 105 "optimization": 0
108 106 },
109 107 "files": {
110 108 "<%= dirs.css.dest %>/style.css": "<%= dirs.css.src %>/main.less",
111 109 "<%= dirs.css.dest %>/style-polymer.css": "<%= dirs.css.src %>/polymer.less"
112 110 }
113 111 },
114 112 "production": {
115 113 "options": {
116 114 "compress": true,
117 115 "yuicompress": true,
118 116 "optimization": 2
119 117 },
120 118 "files": {
121 119 "<%= dirs.css.dest %>/style.css": "<%= dirs.css.src %>/main.less",
122 120 "<%= dirs.css.dest %>/style-polymer.css": "<%= dirs.css.src %>/polymer.less"
123 121 }
124 122 },
125 123 "components": {
126 124 "files": [
127 125 {
128 126 "cwd": "<%= dirs.js.src %>/components/",
129 127 "dest": "<%= dirs.js.src %>/components/",
130 128 "src": [
131 129 "**/*.less"
132 130 ],
133 131 "expand": true,
134 132 "ext": ".css"
135 133 }
136 134 ]
137 135 }
138 136 },
139 137 "watch": {
140 138 "less": {
141 139 "files": [
142 140 "<%= dirs.css.src %>/**/*.less",
143 141 "<%= dirs.js.src %>/components/**/*.less"
144 142 ],
145 143 "tasks": [
146 144 "less:development",
147 145 "less:components",
148 146 "concat:polymercss",
149 147 "vulcanize"
150 148 ]
151 149 },
152 150 "js": {
153 151 "files": [
154 152 "!<%= dirs.js.src %>/components/root-styles.gen.html",
155 153 "<%= dirs.js.src %>/**/*.js",
156 154 "<%= dirs.js.src %>/components/**/*.html"
157 155 ],
158 156 "tasks": [
159 157 "less:components",
160 158 "concat:polymercss",
161 159 "vulcanize",
162 160 "crisper",
163 161 "concat:dist"
164 162 ]
165 163 }
166 164 },
167 165 "jshint": {
168 166 "rhodecode": {
169 167 "src": "<%= dirs.js.src %>/rhodecode/**/*.js",
170 168 "options": {
171 169 "jshintrc": ".jshintrc"
172 170 }
173 171 }
174 172 },
175 173 "vulcanize": {
176 174 "default": {
177 175 "options": {
178 176 "abspath": "",
179 177 "inlineScripts": true,
180 178 "inlineCss": true,
181 179 "stripComments": true
182 180 },
183 181 "files": {
184 182 "<%= dirs.js.dest %>/rhodecode-components.html": "<%= dirs.js.src %>/components/shared-components.html"
185 183 }
186 184 }
187 185 }
188 186 }
@@ -1,79 +1,81 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22
23 23 from pyramid.settings import asbool
24 24
25 25 from rhodecode.config.routing import ADMIN_PREFIX
26 26 from rhodecode.lib.ext_json import json
27 27
28 28
29 29 def url_gen(request):
30 30 urls = {
31 31 'connect': request.route_url('channelstream_connect'),
32 'subscribe': request.route_url('channelstream_subscribe')
32 'subscribe': request.route_url('channelstream_subscribe'),
33 'longpoll': request.registry.settings.get('channelstream.longpoll_url', ''),
34 'ws': request.registry.settings.get('channelstream.ws_url', '')
33 35 }
34 36 return json.dumps(urls)
35 37
36 38
37 39 PLUGIN_DEFINITION = {
38 40 'name': 'channelstream',
39 41 'config': {
40 42 'javascript': [],
41 43 'css': [],
42 44 'template_hooks': {
43 45 'plugin_init_template': 'rhodecode:templates/channelstream/plugin_init.html'
44 46 },
45 47 'url_gen': url_gen,
46 48 'static': None,
47 49 'enabled': False,
48 50 'server': '',
49 51 'secret': ''
50 52 }
51 53 }
52 54
53 55
54 56 def includeme(config):
55 57 settings = config.registry.settings
56 58 PLUGIN_DEFINITION['config']['enabled'] = asbool(
57 59 settings.get('channelstream.enabled'))
58 60 PLUGIN_DEFINITION['config']['server'] = settings.get(
59 61 'channelstream.server', '')
60 62 PLUGIN_DEFINITION['config']['secret'] = settings.get(
61 63 'channelstream.secret', '')
62 64 PLUGIN_DEFINITION['config']['history.location'] = settings.get(
63 65 'channelstream.history.location', '')
64 66 config.register_rhodecode_plugin(
65 67 PLUGIN_DEFINITION['name'],
66 68 PLUGIN_DEFINITION['config']
67 69 )
68 70 # create plugin history location
69 71 history_dir = PLUGIN_DEFINITION['config']['history.location']
70 72 if history_dir and not os.path.exists(history_dir):
71 73 os.makedirs(history_dir, 0750)
72 74
73 75 config.add_route(
74 76 name='channelstream_connect',
75 77 pattern=ADMIN_PREFIX + '/channelstream/connect')
76 78 config.add_route(
77 79 name='channelstream_subscribe',
78 80 pattern=ADMIN_PREFIX + '/channelstream/subscribe')
79 81 config.scan('rhodecode.channelstream')
@@ -1,31 +1,38 b''
1 1 Polymer({
2 2 is: 'rhodecode-toast',
3 3 properties: {
4 4 toasts: {
5 5 type: Array,
6 6 value: function(){
7 7 return []
8 8 }
9 9 }
10 10 },
11 11 observers: [
12 12 '_changedToasts(toasts.splices)'
13 13 ],
14 ready: function(){
15
16 },
17
18 14 _changedToasts: function(newValue, oldValue){
19 15 this.$['p-toast'].notifyResize();
20 16 },
21 17 dismissNotifications: function(){
22 18 this.$['p-toast'].close();
23 19 },
24 20 handleClosed: function(){
25 21 this.splice('toasts', 0);
26 22 },
27 23 open: function(){
28 24 this.$['p-toast'].open();
29 25 },
26 handleNotification: function(data){
27 if (!templateContext.rhodecode_user.notification_status && !data.message.force) {
28 // do not act if notifications are disabled
29 return
30 }
31 this.push('toasts',{
32 level: data.message.level,
33 message: data.message.message
34 });
35 this.open();
36 },
30 37 _gettext: _gettext
31 38 });
@@ -1,6 +1,8 b''
1 1 <!-- required for stamped out templates that might use common elements -->
2 2 <link rel="import" href="../../../../../bower_components/iron-ajax/iron-ajax.html">
3 3 <link rel="import" href="shared-styles.html">
4 <link rel="import" href="channelstream-connection/channelstream-connection.html">
5 <link rel="import" href="rhodecode-app/rhodecode-app.html">
4 6 <link rel="import" href="rhodecode-toast/rhodecode-toast.html">
5 7 <link rel="import" href="rhodecode-toggle/rhodecode-toggle.html">
6 8 <link rel="import" href="rhodecode-unsafe-html/rhodecode-unsafe-html.html">
@@ -1,4 +1,7 b''
1 1 /plugins/__REGISTER__ - launched after the onDomReady() code from rhodecode.js is executed
2 2 /ui/plugins/code/anchor_focus - launched when rc starts to scroll on load to anchor on PR/Codeview
3 3 /ui/plugins/code/comment_form_built - launched when injectInlineForm() is executed and the form object is created
4 /notifications - shows new event notifications No newline at end of file
4 /notifications - shows new event notifications
5 /connection_controller/subscribe - subscribes user to new channels
6 /connection_controller/presence - receives presence change messages
7 /connection_controller/channel_update - receives channel states
@@ -1,179 +1,181 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html>
3 3
4 4 <%
5 5 c.template_context['repo_name'] = getattr(c, 'repo_name', '')
6 6
7 7 if hasattr(c, 'rhodecode_db_repo'):
8 8 c.template_context['repo_type'] = c.rhodecode_db_repo.repo_type
9 9 c.template_context['repo_landing_commit'] = c.rhodecode_db_repo.landing_rev[1]
10 10
11 11 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
12 12 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
13 13 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
14 14 c.template_context['rhodecode_user']['notification_status'] = c.rhodecode_user.get_instance().user_data.get('notification_status', True)
15 15
16 16 c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default_renderer')
17 17 %>
18 18 <html xmlns="http://www.w3.org/1999/xhtml">
19 19 <head>
20 20 <title>${self.title()}</title>
21 21 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
22 22 <%def name="robots()">
23 23 <meta name="robots" content="index, nofollow"/>
24 24 </%def>
25 25 ${self.robots()}
26 26 <link rel="icon" href="${h.asset('images/favicon.ico', ver=c.rhodecode_version_hash)}" sizes="16x16 32x32" type="image/png" />
27 27
28 28 ## CSS definitions
29 29 <%def name="css()">
30 30 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
31 31 <!--[if lt IE 9]>
32 32 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css', ver=c.rhodecode_version_hash)}" media="screen"/>
33 33 <![endif]-->
34 34 ## EXTRA FOR CSS
35 35 ${self.css_extra()}
36 36 </%def>
37 37 ## CSS EXTRA - optionally inject some extra CSS stuff needed for specific websites
38 38 <%def name="css_extra()">
39 39 </%def>
40 40
41 41 ${self.css()}
42 42
43 43 ## JAVASCRIPT
44 44 <%def name="js()">
45 45 <script>
46 46 // setup Polymer options
47 47 window.Polymer = {lazyRegister: true, dom: 'shadow'};
48 48
49 49 // Load webcomponentsjs polyfill if browser does not support native Web Components
50 50 (function() {
51 51 'use strict';
52 52 var onload = function() {
53 53 // For native Imports, manually fire WebComponentsReady so user code
54 54 // can use the same code path for native and polyfill'd imports.
55 55 if (!window.HTMLImports) {
56 56 document.dispatchEvent(
57 57 new CustomEvent('WebComponentsReady', {bubbles: true})
58 58 );
59 59 }
60 60 };
61 61 var webComponentsSupported = (
62 62 'registerElement' in document
63 63 && 'import' in document.createElement('link')
64 64 && 'content' in document.createElement('template')
65 65 );
66 66 if (!webComponentsSupported) {
67 67 var e = document.createElement('script');
68 68 e.async = true;
69 69 e.src = '${h.asset('js/vendors/webcomponentsjs/webcomponents-lite.min.js', ver=c.rhodecode_version_hash)}';
70 70 document.head.appendChild(e);
71 71 } else {
72 72 onload();
73 73 }
74 74 })();
75 75 </script>
76 76
77 77 <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script>
78 78 <script type="text/javascript">
79 79 // register templateContext to pass template variables to JS
80 80 var templateContext = ${h.json.dumps(c.template_context)|n};
81 81
82 82 var REPO_NAME = "${getattr(c, 'repo_name', '')}";
83 83 %if hasattr(c, 'rhodecode_db_repo'):
84 84 var REPO_LANDING_REV = '${c.rhodecode_db_repo.landing_rev[1]}';
85 85 var REPO_TYPE = '${c.rhodecode_db_repo.repo_type}';
86 86 %else:
87 87 var REPO_LANDING_REV = '';
88 88 var REPO_TYPE = '';
89 89 %endif
90 90 var APPLICATION_URL = "${h.url('home').rstrip('/')}";
91 91 var ASSET_URL = "${h.asset('')}";
92 92 var DEFAULT_RENDERER = "${h.get_visual_attr(c, 'default_renderer')}";
93 93 var CSRF_TOKEN = "${getattr(c, 'csrf_token', '')}";
94 94 % if getattr(c, 'rhodecode_user', None):
95 95 var USER = {name:'${c.rhodecode_user.username}'};
96 96 % else:
97 97 var USER = {name:null};
98 98 % endif
99 99
100 100 var APPENLIGHT = {
101 101 enabled: ${'true' if getattr(c, 'appenlight_enabled', False) else 'false'},
102 102 key: '${getattr(c, "appenlight_api_public_key", "")}',
103 103 % if getattr(c, 'appenlight_server_url', None):
104 104 serverUrl: '${getattr(c, "appenlight_server_url", "")}',
105 105 % endif
106 106 requestInfo: {
107 107 % if getattr(c, 'rhodecode_user', None):
108 108 ip: '${c.rhodecode_user.ip_addr}',
109 109 username: '${c.rhodecode_user.username}'
110 110 % endif
111 111 }
112 112 };
113 113 </script>
114 114 <%include file="/base/plugins_base.html"/>
115 115 <!--[if lt IE 9]>
116 116 <script language="javascript" type="text/javascript" src="${h.asset('js/excanvas.min.js')}"></script>
117 117 <![endif]-->
118 118 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode/routes.js', ver=c.rhodecode_version_hash)}"></script>
119 119 <script language="javascript" type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
120 120 <script>
121 121 var e = document.createElement('script');
122 122 e.src = '${h.asset('js/rhodecode-components.js', ver=c.rhodecode_version_hash)}';
123 123 document.head.appendChild(e);
124 124 </script>
125 125 <link rel="import" href="${h.asset('js/rhodecode-components.html', ver=c.rhodecode_version_hash)}">
126 126 ## avoide escaping the %N
127 127 <script>CodeMirror.modeURL = "${h.asset('') + 'js/mode/%N/%N.js?ver='+c.rhodecode_version_hash}";</script>
128 128
129 129
130 130 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
131 131 ${self.js_extra()}
132 132
133 133 <script type="text/javascript">
134 134 $(document).ready(function(){
135 135 show_more_event();
136 136 timeagoActivate();
137 137 })
138 138 </script>
139 139
140 140 </%def>
141 141
142 142 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
143 143 <%def name="js_extra()"></%def>
144 144 ${self.js()}
145 145
146 146 <%def name="head_extra()"></%def>
147 147 ${self.head_extra()}
148 148 <script>
149 $.Topic('/plugins/__REGISTER__').prepareOrPublish({});
149 window.addEventListener("load", function(event) {
150 $.Topic('/plugins/__REGISTER__').prepareOrPublish({});
151 });
150 152 </script>
151 153 ## extra stuff
152 154 %if c.pre_code:
153 155 ${c.pre_code|n}
154 156 %endif
155 157 </head>
156 158 <body id="body">
157 159 <noscript>
158 160 <div class="noscript-error">
159 161 ${_('Please enable JavaScript to use RhodeCode Enterprise')}
160 162 </div>
161 163 </noscript>
162 164 ## IE hacks
163 165 <!--[if IE 7]>
164 166 <script>$(document.body).addClass('ie7')</script>
165 167 <![endif]-->
166 168 <!--[if IE 8]>
167 169 <script>$(document.body).addClass('ie8')</script>
168 170 <![endif]-->
169 171 <!--[if IE 9]>
170 172 <script>$(document.body).addClass('ie9')</script>
171 173 <![endif]-->
172 174
173 175 ${next.body()}
174 176 %if c.post_code:
175 177 ${c.post_code|n}
176 178 %endif
177 <rhodecode-toast id="notifications"></rhodecode-toast>
179 <rhodecode-app></rhodecode-app>
178 180 </body>
179 181 </html>
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now