mirror of
				https://github.com/zulip/zulip.git
				synced 2025-10-31 03:53:50 +00:00 
			
		
		
		
	Compare commits
	
		
			10 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 76809b87a6 | ||
|  | 5baeb35ac8 | ||
|  | 75fbce0532 | ||
|  | 8ad7e08375 | ||
|  | bd01b1e2e4 | ||
|  | 58a7f6085f | ||
|  | 3367593b52 | ||
|  | 1a92ec5d86 | ||
|  | 7a8d685a71 | ||
|  | 3c3a8747c3 | 
| @@ -39,7 +39,8 @@ from typing import Any, Dict, List, Tuple, Optional, Sequence, Callable, Type, \ | |||||||
| @zulip_login_required | @zulip_login_required | ||||||
| def stats(request): | def stats(request): | ||||||
|     # type: (HttpRequest) -> HttpResponse |     # type: (HttpRequest) -> HttpResponse | ||||||
|     return render_to_response('analytics/stats.html') |     return render_to_response('analytics/stats.html', | ||||||
|  |                               context=dict(realm_name = request.user.realm.name)) | ||||||
|  |  | ||||||
| @has_request_variables | @has_request_variables | ||||||
| def get_chart_data(request, user_profile, chart_name=REQ(), | def get_chart_data(request, user_profile, chart_name=REQ(), | ||||||
|   | |||||||
| @@ -4,6 +4,16 @@ All notable changes to the Zulip server are documented in this file. | |||||||
|  |  | ||||||
| ### Unreleased | ### Unreleased | ||||||
|  |  | ||||||
|  | ### 1.5.2 -- 2017-06-01 | ||||||
|  |  | ||||||
|  | - CVE-2017-0896: Restricting inviting new users to admins was broken. | ||||||
|  | - CVE-2015-8861: Insecure old version of handlebars templating engine. | ||||||
|  |  | ||||||
|  | ### 1.5.1 -- 2017-02-07 | ||||||
|  |  | ||||||
|  | - Fix exception trying to copy node_modules during upgrade process. | ||||||
|  | - Improved styling of /stats page to remove useless login/register links. | ||||||
|  |  | ||||||
| ### 1.5.0 -- 2017-02-06 | ### 1.5.0 -- 2017-02-06 | ||||||
|  |  | ||||||
| Highlights: | Highlights: | ||||||
|   | |||||||
| @@ -288,8 +288,7 @@ function render(template_name, args) { | |||||||
|     var all_html = ''; |     var all_html = ''; | ||||||
|  |  | ||||||
|     html = render('bookend', args); |     html = render('bookend', args); | ||||||
|  |     assert.equal($(html).text().trim(), "subscribed to stream\n    \n        \n            Unsubscribe"); | ||||||
|     assert.equal($(html).text().trim(), "subscribed to stream\n    \n        \n            \n            Unsubscribe"); |  | ||||||
|  |  | ||||||
|     all_html += html; |     all_html += html; | ||||||
|  |  | ||||||
| @@ -300,7 +299,7 @@ function render(template_name, args) { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     html = render('bookend', args); |     html = render('bookend', args); | ||||||
|     assert.equal($(html).text().trim(), 'Not subscribed to stream\n    \n        \n            \n            Subscribe'); |     assert.equal($(html).text().trim(), 'Not subscribed to stream\n    \n        \n            Subscribe'); | ||||||
|  |  | ||||||
|     all_html += '<hr />'; |     all_html += '<hr />'; | ||||||
|     all_html += html; |     all_html += html; | ||||||
|   | |||||||
| @@ -5,7 +5,7 @@ | |||||||
|   "description": "", |   "description": "", | ||||||
|   "main": "", |   "main": "", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "handlebars": "1.3.0", |     "handlebars": "4.0.6", | ||||||
|     "i18next": "3.0.0", |     "i18next": "3.0.0", | ||||||
|     "i18next-parser": "0.11.1", |     "i18next-parser": "0.11.1", | ||||||
|     "i18next-xhr-backend": "0.5.4", |     "i18next-xhr-backend": "0.5.4", | ||||||
|   | |||||||
| @@ -79,8 +79,6 @@ else: | |||||||
|     logging.info("Installing static assets...") |     logging.info("Installing static assets...") | ||||||
|     subprocess.check_call(["cp", "-rT", os.path.join(deploy_path, 'prod-static/serve'), |     subprocess.check_call(["cp", "-rT", os.path.join(deploy_path, 'prod-static/serve'), | ||||||
|                            '/home/zulip/prod-static'], preexec_fn=su_to_zulip) |                            '/home/zulip/prod-static'], preexec_fn=su_to_zulip) | ||||||
|     # Sets up npm cache |  | ||||||
|     setup_node_modules(npm_args=['--production'], copy_modules=True) |  | ||||||
|  |  | ||||||
| # Our next optimization is to check whether any migrations are needed | # Our next optimization is to check whether any migrations are needed | ||||||
| # before we start the critical section of the restart.  This saves | # before we start the critical section of the restart.  This saves | ||||||
|   | |||||||
| @@ -161,10 +161,10 @@ function hover(id) { | |||||||
|             $('#hover_bots_value').hide(); |             $('#hover_bots_value').hide(); | ||||||
|         } |         } | ||||||
|         // var human_colors = data.points[0].data.x.map(function () { |         // var human_colors = data.points[0].data.x.map(function () { | ||||||
|         //     return '#1f77b4'; |         //     return '#5f6ea0'; | ||||||
|         // }); |         // }); | ||||||
|         // var bot_colors = data.points[0].data.x.map(function () { |         // var bot_colors = data.points[0].data.x.map(function () { | ||||||
|         //     return '#ff7f00'; |         //     return '#b7b867'; | ||||||
|         // }); |         // }); | ||||||
|         // human_colors[data.points[0].pointNumber] = '#185a88'; |         // human_colors[data.points[0].pointNumber] = '#185a88'; | ||||||
|         // bot_colors[data.points[0].pointNumber] = '#cc6600'; |         // bot_colors[data.points[0].pointNumber] = '#cc6600'; | ||||||
| @@ -173,12 +173,12 @@ function hover(id) { | |||||||
|         // Plotly.restyle(id, update_human, 0); |         // Plotly.restyle(id, update_human, 0); | ||||||
|         // Plotly.restyle(id, update_bot, 1); |         // Plotly.restyle(id, update_bot, 1); | ||||||
|         // var legendBoxes = document.getElementById(id).getElementsByClassName("legendbar"); |         // var legendBoxes = document.getElementById(id).getElementsByClassName("legendbar"); | ||||||
|         // Plotly.d3.select(legendBoxes[0]).style("fill", "#1f77b4"); |         // Plotly.d3.select(legendBoxes[0]).style("fill", "#5f6ea0"); | ||||||
|         // Plotly.d3.select(legendBoxes[1]).style("fill", "#ff7f00"); |         // Plotly.d3.select(legendBoxes[1]).style("fill", "#b7b867"); | ||||||
|     }); |     }); | ||||||
|     // myPlot.on('plotly_unhover', function () { |     // myPlot.on('plotly_unhover', function () { | ||||||
|     //     var update_human = {marker:{color: '#1f77b4'}}; |     //     var update_human = {marker:{color: '#5f6ea0'}}; | ||||||
|     //     var update_bot = {marker:{color: '#ff7f00'}}; |     //     var update_bot = {marker:{color: '#b7b867'}}; | ||||||
|     //     Plotly.restyle(id, update_human, 0); |     //     Plotly.restyle(id, update_human, 0); | ||||||
|     //     Plotly.restyle(id, update_bot, 1); |     //     Plotly.restyle(id, update_bot, 1); | ||||||
|     // }); |     // }); | ||||||
| @@ -186,8 +186,8 @@ function hover(id) { | |||||||
|  |  | ||||||
| function fix_legend_colors() { | function fix_legend_colors() { | ||||||
|     var legendBoxes = document.getElementById('id_messages_sent_over_time').getElementsByClassName("legendbar"); |     var legendBoxes = document.getElementById('id_messages_sent_over_time').getElementsByClassName("legendbar"); | ||||||
|     Plotly.d3.select(legendBoxes[0]).style("fill", "#1f77b4"); |     Plotly.d3.select(legendBoxes[0]).style("fill", "#5f6ea0"); | ||||||
|     Plotly.d3.select(legendBoxes[1]).style("fill", "#ff7f00"); |     Plotly.d3.select(legendBoxes[1]).style("fill", "#b7b867"); | ||||||
| } | } | ||||||
|  |  | ||||||
| function populate_messages_sent_over_time(data) { | function populate_messages_sent_over_time(data) { | ||||||
| @@ -242,13 +242,13 @@ function populate_messages_sent_over_time(data) { | |||||||
|     var date_formatter = function (date) { |     var date_formatter = function (date) { | ||||||
|         return format_date(date, true); |         return format_date(date, true); | ||||||
|     }; |     }; | ||||||
|     var hourly_traces = messages_sent_over_time_traces(start_dates, data.realm, 'bar', date_formatter, '#1f77b4', '#ff7f00'); |     var hourly_traces = messages_sent_over_time_traces(start_dates, data.realm, 'bar', date_formatter, '#5f6ea0', '#b7b867'); | ||||||
|  |  | ||||||
|     var info = aggregate_data('day'); |     var info = aggregate_data('day'); | ||||||
|     date_formatter = function (date) { |     date_formatter = function (date) { | ||||||
|         return format_date(date, false); |         return format_date(date, false); | ||||||
|     }; |     }; | ||||||
|     var daily_traces = messages_sent_over_time_traces(info.dates, info.values, 'bar', date_formatter, '#1f77b4', '#ff7f00'); |     var daily_traces = messages_sent_over_time_traces(info.dates, info.values, 'bar', date_formatter, '#5f6ea0', '#b7b867'); | ||||||
|  |  | ||||||
|     info = aggregate_data('week'); |     info = aggregate_data('week'); | ||||||
|     date_formatter = function (date) { |     date_formatter = function (date) { | ||||||
| @@ -256,14 +256,14 @@ function populate_messages_sent_over_time(data) { | |||||||
|         return "Week of " + format_date(date, false); |         return "Week of " + format_date(date, false); | ||||||
|     }; |     }; | ||||||
|     var human_colors = info.dates.map(function () { |     var human_colors = info.dates.map(function () { | ||||||
|         return '#1f77b4'; |         return '#5f6ea0'; | ||||||
|     }); |     }); | ||||||
|     var bot_colors = info.dates.map(function () { |     var bot_colors = info.dates.map(function () { | ||||||
|         return '#ff7f00'; |         return '#b7b867'; | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     human_colors[info.dates.length-1] = '#66b0e5'; |     human_colors[info.dates.length-1] = '#afb7d0'; | ||||||
|     bot_colors[info.dates.length-1] = '#ffa64d'; |     bot_colors[info.dates.length-1] = '#dbdcb3'; | ||||||
|  |  | ||||||
|     var weekly_traces = messages_sent_over_time_traces(info.dates, info.values, 'bar', date_formatter, human_colors, bot_colors); |     var weekly_traces = messages_sent_over_time_traces(info.dates, info.values, 'bar', date_formatter, human_colors, bot_colors); | ||||||
|     var dates = data.end_times.map(function (timestamp) { |     var dates = data.end_times.map(function (timestamp) { | ||||||
| @@ -273,7 +273,7 @@ function populate_messages_sent_over_time(data) { | |||||||
|     date_formatter = function (date) { |     date_formatter = function (date) { | ||||||
|         return format_date(date, true); |         return format_date(date, true); | ||||||
|     }; |     }; | ||||||
|     var cumulative_traces = messages_sent_over_time_traces(dates, values, 'scatter', date_formatter, '#1f77b4', '#ff7f00'); |     var cumulative_traces = messages_sent_over_time_traces(dates, values, 'scatter', date_formatter, '#5f6ea0', '#b7b867'); | ||||||
|  |  | ||||||
|     // Generate plot |     // Generate plot | ||||||
|     var layout = messages_sent_over_time_layout(); |     var layout = messages_sent_over_time_layout(); | ||||||
|   | |||||||
| @@ -55,7 +55,6 @@ svg { | |||||||
|  |  | ||||||
| .page-content { | .page-content { | ||||||
|     width: calc(100% - 300px - 8px); |     width: calc(100% - 300px - 8px); | ||||||
|     height: calc(100vh - 94px); |  | ||||||
|     overflow: auto; |     overflow: auto; | ||||||
|     display: inline-block; |     display: inline-block; | ||||||
|     vertical-align: top; |     vertical-align: top; | ||||||
|   | |||||||
| @@ -12,10 +12,10 @@ | |||||||
| {{/if}} | {{/if}} | ||||||
|  |  | ||||||
| <div class="recipient_row" id="{{message_group_id}}"> | <div class="recipient_row" id="{{message_group_id}}"> | ||||||
|   {{partial "recipient_row" "use_match_properties" ../../use_match_properties}} |   {{partial "recipient_row" "use_match_properties" ../use_match_properties}} | ||||||
|   {{#each message_containers}} |   {{#each message_containers}} | ||||||
|   {{#with this}} |   {{#with this}} | ||||||
|     {{partial "single_message" "use_match_properties" ../../../../use_match_properties "table_name" ../../../../table_name}} |     {{partial "single_message" "use_match_properties" ../../use_match_properties "table_name" ../../table_name}} | ||||||
|   {{/with}} |   {{/with}} | ||||||
|   {{/each}} |   {{/each}} | ||||||
| </div> | </div> | ||||||
|   | |||||||
| @@ -1,554 +0,0 @@ | |||||||
| /** @preserve |  | ||||||
|  Software from "Handlebars", an extension to the Mustache templating language, is |  | ||||||
|  Copyright (C) 2011 by Yehuda Katz and is provided under the following license: |  | ||||||
|  -- |  | ||||||
|  Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
|  of this software and associated documentation files (the "Software"), to deal |  | ||||||
|  in the Software without restriction, including without limitation the rights |  | ||||||
|  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
|  copies of the Software, and to permit persons to whom the Software is |  | ||||||
|  furnished to do so, subject to the following conditions: |  | ||||||
|  |  | ||||||
|  The above copyright notice and this permission notice shall be included in |  | ||||||
|  all copies or substantial portions of the Software. |  | ||||||
|  |  | ||||||
|  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
|  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
|  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
|  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
|  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
|  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
|  THE SOFTWARE. |  | ||||||
|  -- |  | ||||||
| */ |  | ||||||
|  |  | ||||||
| /*! |  | ||||||
|  |  | ||||||
|  handlebars v1.3.0 |  | ||||||
|  |  | ||||||
| Copyright (C) 2011 by Yehuda Katz |  | ||||||
|  |  | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a copy |  | ||||||
| of this software and associated documentation files (the "Software"), to deal |  | ||||||
| in the Software without restriction, including without limitation the rights |  | ||||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |  | ||||||
| copies of the Software, and to permit persons to whom the Software is |  | ||||||
| furnished to do so, subject to the following conditions: |  | ||||||
|  |  | ||||||
| The above copyright notice and this permission notice shall be included in |  | ||||||
| all copies or substantial portions of the Software. |  | ||||||
|  |  | ||||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |  | ||||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |  | ||||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |  | ||||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |  | ||||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |  | ||||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |  | ||||||
| THE SOFTWARE. |  | ||||||
|  |  | ||||||
| @license |  | ||||||
| */ |  | ||||||
| /* exported Handlebars */ |  | ||||||
| var Handlebars = (function() { |  | ||||||
| // handlebars/safe-string.js |  | ||||||
| var __module3__ = (function() { |  | ||||||
|   "use strict"; |  | ||||||
|   var __exports__; |  | ||||||
|   // Build out our basic SafeString type |  | ||||||
|   function SafeString(string) { |  | ||||||
|     this.string = string; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   SafeString.prototype.toString = function() { |  | ||||||
|     return "" + this.string; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   __exports__ = SafeString; |  | ||||||
|   return __exports__; |  | ||||||
| })(); |  | ||||||
|  |  | ||||||
| // handlebars/utils.js |  | ||||||
| var __module2__ = (function(__dependency1__) { |  | ||||||
|   "use strict"; |  | ||||||
|   var __exports__ = {}; |  | ||||||
|   /*jshint -W004 */ |  | ||||||
|   var SafeString = __dependency1__; |  | ||||||
|  |  | ||||||
|   var escape = { |  | ||||||
|     "&": "&", |  | ||||||
|     "<": "<", |  | ||||||
|     ">": ">", |  | ||||||
|     '"': """, |  | ||||||
|     "'": "'", |  | ||||||
|     "`": "`" |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   var badChars = /[&<>"'`]/g; |  | ||||||
|   var possible = /[&<>"'`]/; |  | ||||||
|  |  | ||||||
|   function escapeChar(chr) { |  | ||||||
|     return escape[chr] || "&"; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   function extend(obj, value) { |  | ||||||
|     for(var key in value) { |  | ||||||
|       if(Object.prototype.hasOwnProperty.call(value, key)) { |  | ||||||
|         obj[key] = value[key]; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   __exports__.extend = extend;var toString = Object.prototype.toString; |  | ||||||
|   __exports__.toString = toString; |  | ||||||
|   // Sourced from lodash |  | ||||||
|   // https://github.com/bestiejs/lodash/blob/master/LICENSE.txt |  | ||||||
|   var isFunction = function(value) { |  | ||||||
|     return typeof value === 'function'; |  | ||||||
|   }; |  | ||||||
|   // fallback for older versions of Chrome and Safari |  | ||||||
|   if (isFunction(/x/)) { |  | ||||||
|     isFunction = function(value) { |  | ||||||
|       return typeof value === 'function' && toString.call(value) === '[object Function]'; |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
|   var isFunction; |  | ||||||
|   __exports__.isFunction = isFunction; |  | ||||||
|   var isArray = Array.isArray || function(value) { |  | ||||||
|     return (value && typeof value === 'object') ? toString.call(value) === '[object Array]' : false; |  | ||||||
|   }; |  | ||||||
|   __exports__.isArray = isArray; |  | ||||||
|  |  | ||||||
|   function escapeExpression(string) { |  | ||||||
|     // don't escape SafeStrings, since they're already safe |  | ||||||
|     if (string instanceof SafeString) { |  | ||||||
|       return string.toString(); |  | ||||||
|     } else if (!string && string !== 0) { |  | ||||||
|       return ""; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Force a string conversion as this will be done by the append regardless and |  | ||||||
|     // the regex test will do this transparently behind the scenes, causing issues if |  | ||||||
|     // an object's to string has escaped characters in it. |  | ||||||
|     string = "" + string; |  | ||||||
|  |  | ||||||
|     if(!possible.test(string)) { return string; } |  | ||||||
|     return string.replace(badChars, escapeChar); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   __exports__.escapeExpression = escapeExpression;function isEmpty(value) { |  | ||||||
|     if (!value && value !== 0) { |  | ||||||
|       return true; |  | ||||||
|     } else if (isArray(value) && value.length === 0) { |  | ||||||
|       return true; |  | ||||||
|     } else { |  | ||||||
|       return false; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   __exports__.isEmpty = isEmpty; |  | ||||||
|   return __exports__; |  | ||||||
| })(__module3__); |  | ||||||
|  |  | ||||||
| // handlebars/exception.js |  | ||||||
| var __module4__ = (function() { |  | ||||||
|   "use strict"; |  | ||||||
|   var __exports__; |  | ||||||
|  |  | ||||||
|   var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; |  | ||||||
|  |  | ||||||
|   function Exception(message, node) { |  | ||||||
|     var line; |  | ||||||
|     if (node && node.firstLine) { |  | ||||||
|       line = node.firstLine; |  | ||||||
|  |  | ||||||
|       message += ' - ' + line + ':' + node.firstColumn; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     var tmp = Error.prototype.constructor.call(this, message); |  | ||||||
|  |  | ||||||
|     // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. |  | ||||||
|     for (var idx = 0; idx < errorProps.length; idx++) { |  | ||||||
|       this[errorProps[idx]] = tmp[errorProps[idx]]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (line) { |  | ||||||
|       this.lineNumber = line; |  | ||||||
|       this.column = node.firstColumn; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   Exception.prototype = new Error(); |  | ||||||
|  |  | ||||||
|   __exports__ = Exception; |  | ||||||
|   return __exports__; |  | ||||||
| })(); |  | ||||||
|  |  | ||||||
| // handlebars/base.js |  | ||||||
| var __module1__ = (function(__dependency1__, __dependency2__) { |  | ||||||
|   "use strict"; |  | ||||||
|   var __exports__ = {}; |  | ||||||
|   var Utils = __dependency1__; |  | ||||||
|   var Exception = __dependency2__; |  | ||||||
|  |  | ||||||
|   var VERSION = "1.3.0"; |  | ||||||
|   __exports__.VERSION = VERSION;var COMPILER_REVISION = 4; |  | ||||||
|   __exports__.COMPILER_REVISION = COMPILER_REVISION; |  | ||||||
|   var REVISION_CHANGES = { |  | ||||||
|     1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it |  | ||||||
|     2: '== 1.0.0-rc.3', |  | ||||||
|     3: '== 1.0.0-rc.4', |  | ||||||
|     4: '>= 1.0.0' |  | ||||||
|   }; |  | ||||||
|   __exports__.REVISION_CHANGES = REVISION_CHANGES; |  | ||||||
|   var isArray = Utils.isArray, |  | ||||||
|       isFunction = Utils.isFunction, |  | ||||||
|       toString = Utils.toString, |  | ||||||
|       objectType = '[object Object]'; |  | ||||||
|  |  | ||||||
|   function HandlebarsEnvironment(helpers, partials) { |  | ||||||
|     this.helpers = helpers || {}; |  | ||||||
|     this.partials = partials || {}; |  | ||||||
|  |  | ||||||
|     registerDefaultHelpers(this); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   __exports__.HandlebarsEnvironment = HandlebarsEnvironment;HandlebarsEnvironment.prototype = { |  | ||||||
|     constructor: HandlebarsEnvironment, |  | ||||||
|  |  | ||||||
|     logger: logger, |  | ||||||
|     log: log, |  | ||||||
|  |  | ||||||
|     registerHelper: function(name, fn, inverse) { |  | ||||||
|       if (toString.call(name) === objectType) { |  | ||||||
|         if (inverse || fn) { throw new Exception('Arg not supported with multiple helpers'); } |  | ||||||
|         Utils.extend(this.helpers, name); |  | ||||||
|       } else { |  | ||||||
|         if (inverse) { fn.not = inverse; } |  | ||||||
|         this.helpers[name] = fn; |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|  |  | ||||||
|     registerPartial: function(name, str) { |  | ||||||
|       if (toString.call(name) === objectType) { |  | ||||||
|         Utils.extend(this.partials,  name); |  | ||||||
|       } else { |  | ||||||
|         this.partials[name] = str; |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   function registerDefaultHelpers(instance) { |  | ||||||
|     instance.registerHelper('helperMissing', function(arg) { |  | ||||||
|       if(arguments.length === 2) { |  | ||||||
|         return undefined; |  | ||||||
|       } else { |  | ||||||
|         throw new Exception("Missing helper: '" + arg + "'"); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     instance.registerHelper('blockHelperMissing', function(context, options) { |  | ||||||
|       var inverse = options.inverse || function() {}, fn = options.fn; |  | ||||||
|  |  | ||||||
|       if (isFunction(context)) { context = context.call(this); } |  | ||||||
|  |  | ||||||
|       if(context === true) { |  | ||||||
|         return fn(this); |  | ||||||
|       } else if(context === false || context == null) { |  | ||||||
|         return inverse(this); |  | ||||||
|       } else if (isArray(context)) { |  | ||||||
|         if(context.length > 0) { |  | ||||||
|           return instance.helpers.each(context, options); |  | ||||||
|         } else { |  | ||||||
|           return inverse(this); |  | ||||||
|         } |  | ||||||
|       } else { |  | ||||||
|         return fn(context); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     instance.registerHelper('each', function(context, options) { |  | ||||||
|       var fn = options.fn, inverse = options.inverse; |  | ||||||
|       var i = 0, ret = "", data; |  | ||||||
|  |  | ||||||
|       if (isFunction(context)) { context = context.call(this); } |  | ||||||
|  |  | ||||||
|       if (options.data) { |  | ||||||
|         data = createFrame(options.data); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       if(context && typeof context === 'object') { |  | ||||||
|         if (isArray(context)) { |  | ||||||
|           for(var j = context.length; i<j; i++) { |  | ||||||
|             if (data) { |  | ||||||
|               data.index = i; |  | ||||||
|               data.first = (i === 0); |  | ||||||
|               data.last  = (i === (context.length-1)); |  | ||||||
|             } |  | ||||||
|             ret = ret + fn(context[i], { data: data }); |  | ||||||
|           } |  | ||||||
|         } else { |  | ||||||
|           for(var key in context) { |  | ||||||
|             if(context.hasOwnProperty(key)) { |  | ||||||
|               if(data) { |  | ||||||
|                 data.key = key; |  | ||||||
|                 data.index = i; |  | ||||||
|                 data.first = (i === 0); |  | ||||||
|               } |  | ||||||
|               ret = ret + fn(context[key], {data: data}); |  | ||||||
|               i++; |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       if(i === 0){ |  | ||||||
|         ret = inverse(this); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       return ret; |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     instance.registerHelper('if', function(conditional, options) { |  | ||||||
|       if (isFunction(conditional)) { conditional = conditional.call(this); } |  | ||||||
|  |  | ||||||
|       // Default behavior is to render the positive path if the value is truthy and not empty. |  | ||||||
|       // The `includeZero` option may be set to treat the condtional as purely not empty based on the |  | ||||||
|       // behavior of isEmpty. Effectively this determines if 0 is handled by the positive path or negative. |  | ||||||
|       if ((!options.hash.includeZero && !conditional) || Utils.isEmpty(conditional)) { |  | ||||||
|         return options.inverse(this); |  | ||||||
|       } else { |  | ||||||
|         return options.fn(this); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     instance.registerHelper('unless', function(conditional, options) { |  | ||||||
|       return instance.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn, hash: options.hash}); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     instance.registerHelper('with', function(context, options) { |  | ||||||
|       if (isFunction(context)) { context = context.call(this); } |  | ||||||
|  |  | ||||||
|       if (!Utils.isEmpty(context)) return options.fn(context); |  | ||||||
|     }); |  | ||||||
|  |  | ||||||
|     instance.registerHelper('log', function(context, options) { |  | ||||||
|       var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1; |  | ||||||
|       instance.log(level, context); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   var logger = { |  | ||||||
|     methodMap: { 0: 'debug', 1: 'info', 2: 'warn', 3: 'error' }, |  | ||||||
|  |  | ||||||
|     // State enum |  | ||||||
|     DEBUG: 0, |  | ||||||
|     INFO: 1, |  | ||||||
|     WARN: 2, |  | ||||||
|     ERROR: 3, |  | ||||||
|     level: 3, |  | ||||||
|  |  | ||||||
|     // can be overridden in the host environment |  | ||||||
|     log: function(level, obj) { |  | ||||||
|       if (logger.level <= level) { |  | ||||||
|         var method = logger.methodMap[level]; |  | ||||||
|         if (typeof console !== 'undefined' && console[method]) { |  | ||||||
|           console[method].call(console, obj); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   }; |  | ||||||
|   __exports__.logger = logger; |  | ||||||
|   function log(level, obj) { logger.log(level, obj); } |  | ||||||
|  |  | ||||||
|   __exports__.log = log;var createFrame = function(object) { |  | ||||||
|     var obj = {}; |  | ||||||
|     Utils.extend(obj, object); |  | ||||||
|     return obj; |  | ||||||
|   }; |  | ||||||
|   __exports__.createFrame = createFrame; |  | ||||||
|   return __exports__; |  | ||||||
| })(__module2__, __module4__); |  | ||||||
|  |  | ||||||
| // handlebars/runtime.js |  | ||||||
| var __module5__ = (function(__dependency1__, __dependency2__, __dependency3__) { |  | ||||||
|   "use strict"; |  | ||||||
|   var __exports__ = {}; |  | ||||||
|   var Utils = __dependency1__; |  | ||||||
|   var Exception = __dependency2__; |  | ||||||
|   var COMPILER_REVISION = __dependency3__.COMPILER_REVISION; |  | ||||||
|   var REVISION_CHANGES = __dependency3__.REVISION_CHANGES; |  | ||||||
|  |  | ||||||
|   function checkRevision(compilerInfo) { |  | ||||||
|     var compilerRevision = compilerInfo && compilerInfo[0] || 1, |  | ||||||
|         currentRevision = COMPILER_REVISION; |  | ||||||
|  |  | ||||||
|     if (compilerRevision !== currentRevision) { |  | ||||||
|       if (compilerRevision < currentRevision) { |  | ||||||
|         var runtimeVersions = REVISION_CHANGES[currentRevision], |  | ||||||
|             compilerVersions = REVISION_CHANGES[compilerRevision]; |  | ||||||
|         throw new Exception("Template was precompiled with an older version of Handlebars than the current runtime. "+ |  | ||||||
|               "Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+")."); |  | ||||||
|       } else { |  | ||||||
|         // Use the embedded version info since the runtime doesn't know about this revision yet |  | ||||||
|         throw new Exception("Template was precompiled with a newer version of Handlebars than the current runtime. "+ |  | ||||||
|               "Please update your runtime to a newer version ("+compilerInfo[1]+")."); |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   __exports__.checkRevision = checkRevision;// TODO: Remove this line and break up compilePartial |  | ||||||
|  |  | ||||||
|   function template(templateSpec, env) { |  | ||||||
|     if (!env) { |  | ||||||
|       throw new Exception("No environment passed to template"); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Note: Using env.VM references rather than local var references throughout this section to allow |  | ||||||
|     // for external users to override these as psuedo-supported APIs. |  | ||||||
|     var invokePartialWrapper = function(partial, name, context, helpers, partials, data) { |  | ||||||
|       var result = env.VM.invokePartial.apply(this, arguments); |  | ||||||
|       if (result != null) { return result; } |  | ||||||
|  |  | ||||||
|       if (env.compile) { |  | ||||||
|         var options = { helpers: helpers, partials: partials, data: data }; |  | ||||||
|         partials[name] = env.compile(partial, { data: data !== undefined }, env); |  | ||||||
|         return partials[name](context, options); |  | ||||||
|       } else { |  | ||||||
|         throw new Exception("The partial " + name + " could not be compiled when running in runtime-only mode"); |  | ||||||
|       } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     // Just add water |  | ||||||
|     var container = { |  | ||||||
|       escapeExpression: Utils.escapeExpression, |  | ||||||
|       invokePartial: invokePartialWrapper, |  | ||||||
|       programs: [], |  | ||||||
|       program: function(i, fn, data) { |  | ||||||
|         var programWrapper = this.programs[i]; |  | ||||||
|         if(data) { |  | ||||||
|           programWrapper = program(i, fn, data); |  | ||||||
|         } else if (!programWrapper) { |  | ||||||
|           programWrapper = this.programs[i] = program(i, fn); |  | ||||||
|         } |  | ||||||
|         return programWrapper; |  | ||||||
|       }, |  | ||||||
|       merge: function(param, common) { |  | ||||||
|         var ret = param || common; |  | ||||||
|  |  | ||||||
|         if (param && common && (param !== common)) { |  | ||||||
|           ret = {}; |  | ||||||
|           Utils.extend(ret, common); |  | ||||||
|           Utils.extend(ret, param); |  | ||||||
|         } |  | ||||||
|         return ret; |  | ||||||
|       }, |  | ||||||
|       programWithDepth: env.VM.programWithDepth, |  | ||||||
|       noop: env.VM.noop, |  | ||||||
|       compilerInfo: null |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return function(context, options) { |  | ||||||
|       options = options || {}; |  | ||||||
|       var namespace = options.partial ? options : env, |  | ||||||
|           helpers, |  | ||||||
|           partials; |  | ||||||
|  |  | ||||||
|       if (!options.partial) { |  | ||||||
|         helpers = options.helpers; |  | ||||||
|         partials = options.partials; |  | ||||||
|       } |  | ||||||
|       var result = templateSpec.call( |  | ||||||
|             container, |  | ||||||
|             namespace, context, |  | ||||||
|             helpers, |  | ||||||
|             partials, |  | ||||||
|             options.data); |  | ||||||
|  |  | ||||||
|       if (!options.partial) { |  | ||||||
|         env.VM.checkRevision(container.compilerInfo); |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       return result; |  | ||||||
|     }; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   __exports__.template = template;function programWithDepth(i, fn, data /*, $depth */) { |  | ||||||
|     var args = Array.prototype.slice.call(arguments, 3); |  | ||||||
|  |  | ||||||
|     var prog = function(context, options) { |  | ||||||
|       options = options || {}; |  | ||||||
|  |  | ||||||
|       return fn.apply(this, [context, options.data || data].concat(args)); |  | ||||||
|     }; |  | ||||||
|     prog.program = i; |  | ||||||
|     prog.depth = args.length; |  | ||||||
|     return prog; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   __exports__.programWithDepth = programWithDepth;function program(i, fn, data) { |  | ||||||
|     var prog = function(context, options) { |  | ||||||
|       options = options || {}; |  | ||||||
|  |  | ||||||
|       return fn(context, options.data || data); |  | ||||||
|     }; |  | ||||||
|     prog.program = i; |  | ||||||
|     prog.depth = 0; |  | ||||||
|     return prog; |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   __exports__.program = program;function invokePartial(partial, name, context, helpers, partials, data) { |  | ||||||
|     var options = { partial: true, helpers: helpers, partials: partials, data: data }; |  | ||||||
|  |  | ||||||
|     if(partial === undefined) { |  | ||||||
|       throw new Exception("The partial " + name + " could not be found"); |  | ||||||
|     } else if(partial instanceof Function) { |  | ||||||
|       return partial(context, options); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   __exports__.invokePartial = invokePartial;function noop() { return ""; } |  | ||||||
|  |  | ||||||
|   __exports__.noop = noop; |  | ||||||
|   return __exports__; |  | ||||||
| })(__module2__, __module4__, __module1__); |  | ||||||
|  |  | ||||||
| // handlebars.runtime.js |  | ||||||
| var __module0__ = (function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) { |  | ||||||
|   "use strict"; |  | ||||||
|   var __exports__; |  | ||||||
|   /*globals Handlebars: true */ |  | ||||||
|   var base = __dependency1__; |  | ||||||
|  |  | ||||||
|   // Each of these augment the Handlebars object. No need to setup here. |  | ||||||
|   // (This is done to easily share code between commonjs and browse envs) |  | ||||||
|   var SafeString = __dependency2__; |  | ||||||
|   var Exception = __dependency3__; |  | ||||||
|   var Utils = __dependency4__; |  | ||||||
|   var runtime = __dependency5__; |  | ||||||
|  |  | ||||||
|   // For compatibility and usage outside of module systems, make the Handlebars object a namespace |  | ||||||
|   var create = function() { |  | ||||||
|     var hb = new base.HandlebarsEnvironment(); |  | ||||||
|  |  | ||||||
|     Utils.extend(hb, base); |  | ||||||
|     hb.SafeString = SafeString; |  | ||||||
|     hb.Exception = Exception; |  | ||||||
|     hb.Utils = Utils; |  | ||||||
|  |  | ||||||
|     hb.VM = runtime; |  | ||||||
|     hb.template = function(spec) { |  | ||||||
|       return runtime.template(spec, hb); |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     return hb; |  | ||||||
|   }; |  | ||||||
|  |  | ||||||
|   var Handlebars = create(); |  | ||||||
|   Handlebars.create = create; |  | ||||||
|  |  | ||||||
|   __exports__ = Handlebars; |  | ||||||
|   return __exports__; |  | ||||||
| })(__module1__, __module3__, __module4__, __module2__, __module5__); |  | ||||||
|  |  | ||||||
|   return __module0__; |  | ||||||
| })(); |  | ||||||
| @@ -1,11 +1,15 @@ | |||||||
| {% extends "zerver/portico.html" %} | {% extends "zerver/base.html" %} | ||||||
| {% block portico_content %} |  | ||||||
|  | {% block customhead %} | ||||||
|  | {% stylesheet 'portico' %} | ||||||
|  | {% endblock %} | ||||||
|  |  | ||||||
|  | {% block content %} | ||||||
|  | <div class="app portico-page"> | ||||||
|  |  | ||||||
| {{ minified_js('stats')|safe }} | {{ minified_js('stats')|safe }} | ||||||
| {% stylesheet 'stats' %} | {% stylesheet 'stats' %} | ||||||
|  |  | ||||||
| <html> |  | ||||||
| <body> |  | ||||||
| <div class="sidebar"> | <div class="sidebar"> | ||||||
|     <nav class="nav"> |     <nav class="nav"> | ||||||
|         <p class="nav-subtitle">Messages Sent</p> |         <p class="nav-subtitle">Messages Sent</p> | ||||||
| @@ -18,7 +22,7 @@ | |||||||
| </div> | </div> | ||||||
| <div class="page-content"> | <div class="page-content"> | ||||||
|   <div id="id_stats_errors" class="alert alert-error"></div> |   <div id="id_stats_errors" class="alert alert-error"></div> | ||||||
|   <h1 class="analytics-page-header">Analytics</h1> |   <h1 class="analytics-page-header">Zulip Analytics for {{ realm_name }}</h1> | ||||||
|   <div class="center-container"> |   <div class="center-container"> | ||||||
|     <div class="center-block"> |     <div class="center-block"> | ||||||
|       <p class="graph-title" id="messages_timescale_anchor">Messages Sent Over Time</p> |       <p class="graph-title" id="messages_timescale_anchor">Messages Sent Over Time</p> | ||||||
| @@ -79,7 +83,6 @@ | |||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </div> | </div> | ||||||
| </body> |  | ||||||
| </html> |  | ||||||
|  |  | ||||||
|  | </div> | ||||||
| {% endblock %} | {% endblock %} | ||||||
|   | |||||||
| @@ -1,2 +1,2 @@ | |||||||
| ZULIP_VERSION = "1.5.0" | ZULIP_VERSION = "1.5.2" | ||||||
| PROVISION_VERSION = '4.4' | PROVISION_VERSION = '4.4' | ||||||
|   | |||||||
| @@ -407,6 +407,29 @@ class InviteUserTest(ZulipTestCase): | |||||||
|         self.assertTrue(find_key_by_email(email2)) |         self.assertTrue(find_key_by_email(email2)) | ||||||
|         self.check_sent_emails([email, email2]) |         self.check_sent_emails([email, email2]) | ||||||
|  |  | ||||||
|  |     def test_require_realm_admin(self): | ||||||
|  |         # type: () -> None | ||||||
|  |         """ | ||||||
|  |         The invite_by_admins_only realm setting works properly. | ||||||
|  |         """ | ||||||
|  |         realm = get_realm('zulip') | ||||||
|  |         realm.invite_by_admins_only = True | ||||||
|  |         realm.save() | ||||||
|  |  | ||||||
|  |         self.login("hamlet@zulip.com") | ||||||
|  |         email = "alice-test@zulip.com" | ||||||
|  |         email2 = "bob-test@zulip.com" | ||||||
|  |         invitee = "Alice Test <{}>, {}".format(email, email2) | ||||||
|  |         self.assert_json_error(self.invite(invitee, ["Denmark"]), | ||||||
|  |                                "Must be a realm administrator") | ||||||
|  |  | ||||||
|  |         # Now verify an administrator can do it | ||||||
|  |         self.login("iago@zulip.com") | ||||||
|  |         self.assert_json_success(self.invite(invitee, ["Denmark"])) | ||||||
|  |         self.assertTrue(find_key_by_email(email)) | ||||||
|  |         self.assertTrue(find_key_by_email(email2)) | ||||||
|  |         self.check_sent_emails([email, email2]) | ||||||
|  |  | ||||||
|     def test_invite_user_signup_initial_history(self): |     def test_invite_user_signup_initial_history(self): | ||||||
|         # type: () -> None |         # type: () -> None | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -22,6 +22,8 @@ import re | |||||||
| @has_request_variables | @has_request_variables | ||||||
| def json_invite_users(request, user_profile, invitee_emails_raw=REQ("invitee_emails")): | def json_invite_users(request, user_profile, invitee_emails_raw=REQ("invitee_emails")): | ||||||
|     # type: (HttpRequest, UserProfile, str) -> HttpResponse |     # type: (HttpRequest, UserProfile, str) -> HttpResponse | ||||||
|  |     if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin: | ||||||
|  |         return json_error(_("Must be a realm administrator")) | ||||||
|     if not invitee_emails_raw: |     if not invitee_emails_raw: | ||||||
|         return json_error(_("You must specify at least one email address.")) |         return json_error(_("You must specify at least one email address.")) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -788,7 +788,7 @@ JS_SPECS = { | |||||||
|             'third/sockjs/sockjs-0.3.4.js', |             'third/sockjs/sockjs-0.3.4.js', | ||||||
|             'node_modules/string.prototype.codepointat/codepointat.js', |             'node_modules/string.prototype.codepointat/codepointat.js', | ||||||
|             'node_modules/winchan/winchan.js', |             'node_modules/winchan/winchan.js', | ||||||
|             'third/handlebars/handlebars.runtime.js', |             'node_modules/handlebars/dist/handlebars.runtime.js', | ||||||
|             'third/marked/lib/marked.js', |             'third/marked/lib/marked.js', | ||||||
|             'generated/emoji/emoji_codes.js', |             'generated/emoji/emoji_codes.js', | ||||||
|             'templates/compiled.js', |             'templates/compiled.js', | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user