Move ChatView to precompiled Handlebars templates

In Nextcloud 15 the default Content Security Policy disallows unsafe
eval expressions, so Handlebars templates can no longer be compiled at
runtime.

For the time being that default Content Security Policy was lifted for
Talk so "Handlebars.compile" could still be used. However, this only
applies to Talk itself; when using Talk components in other apps they
must abide to the Content Security Policy of those apps. As ChatView is
going to be used in the Files app it has been moved to precompiled
Handlebars templates (which are still compatible with the regular Talk
UI).

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
This commit is contained in:
Daniel Calviño Sánchez 2018-11-27 11:40:12 +01:00 коммит произвёл Joas Schilling
Родитель da13fd1d8f
Коммит 252c53e780
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 7076EA9751AACDDA
9 изменённых файлов: 140 добавлений и 46 удалений

Просмотреть файл

@ -4,3 +4,5 @@
export PATH=./node_modules/.bin/:$PATH
handlebars -n OCA.VideoCalls.Admin.Templates js/admin/templates/ -f js/admin/templates.js
handlebars -n OCA.SpreedMe.Views.Templates js/views/templates/ -f js/views/templates.js

Просмотреть файл

@ -1,4 +1,4 @@
/* global autosize, Handlebars, Marionette, moment, OC, OCA, OCP */
/* global autosize, Marionette, moment, OC, OCA, OCP */
/**
*
@ -21,52 +21,12 @@
*
*/
(function(OCA, OC, OCP, Marionette, Handlebars, autosize, moment) {
(function(OCA, OC, OCP, Marionette, autosize, moment) {
'use strict';
OCA.SpreedMe = OCA.SpreedMe || {};
OCA.SpreedMe.Views = OCA.SpreedMe.Views || {};
var TEMPLATE =
'<ul class="comments">' +
'</ul>' +
'<div class="emptycontent"><div class="icon-comment"></div>' +
'<p>{{emptyResultLabel}}</p></div>' +
'<div class="loading hidden" style="height: 50px"></div>';
var ADD_COMMENT_TEMPLATE =
'<div class="newCommentRow comment">' +
' <div class="authorRow currentUser">' +
' <div class="avatar" data-user-id="{{actorId}}"></div>' +
' {{#if actorId}}' +
' <div class="author">{{actorDisplayName}}</div>' +
' {{else}}' +
' <div class="guest-name"></div>' +
' {{/if}}' +
' </div>' +
' <form class="newCommentForm">' +
' <div contentEditable="true" class="message" data-placeholder="{{newMessagePlaceholder}}">{{message}}</div>' +
' <input class="submit icon-confirm has-tooltip" type="submit" value="" title="{{submitText}}"/>' +
' <div class="submitLoading icon-loading-small hidden"></div>'+
' {{#if actorId}}' +
' <button class="share icon-add has-tooltip" title="{{shareText}}"></button>' +
' <div class="shareLoading icon-loading-small hidden"></div>'+
' {{/if}}' +
' </form>' +
'</div>';
var COMMENT_TEMPLATE =
'<li class="comment{{#if isNotSystemMessage}}{{else}} systemMessage{{/if}}" data-id="{{id}}">' +
' <div class="authorRow{{#if isUserAuthor}} currentUser{{/if}}{{#if isGuest}} guestUser{{/if}}">' +
' {{#if isNotSystemMessage}}' +
' <div class="avatar" data-user-id="{{#if isGuest}}{{else}}{{actorId}}{{/if}}" data-user-display-name="{{actorDisplayName}}"> </div>' +
' <div class="author">{{actorDisplayName}}</div>' +
' {{/if}}' +
' <div class="date has-tooltip{{#if relativeDate}} live-relative-timestamp{{/if}}" data-timestamp="{{timestamp}}" title="{{altDate}}">{{date}}</div>' +
' </div>' +
' <div class="message">{{{formattedMessage}}}</div>' +
'</li>';
var ChatView = Marionette.View.extend({
groupedMessages: 0,
@ -201,14 +161,19 @@
document.execCommand('insertText', false, text);
},
template: Handlebars.compile(TEMPLATE),
template: function(context) {
// OCA.SpreedMe.Views.Templates may not have been initialized when
// this view is initialized, so the template can not be directly
// assigned.
return OCA.SpreedMe.Views.Templates['chatview'](context);
},
templateContext: {
emptyResultLabel: t('spreed', 'No messages yet, start the conversation!')
},
addCommentTemplate: function(params) {
if (!this._addCommentTemplate) {
this._addCommentTemplate = Handlebars.compile(ADD_COMMENT_TEMPLATE);
this._addCommentTemplate = OCA.SpreedMe.Views.Templates['chatview_add_comment'];
}
return this._addCommentTemplate(_.extend({
@ -222,7 +187,7 @@
commentTemplate: function(params) {
if (!this._commentTemplate) {
this._commentTemplate = Handlebars.compile(COMMENT_TEMPLATE);
this._commentTemplate = OCA.SpreedMe.Views.Templates['chatview_comment'];
}
params = _.extend({
@ -756,4 +721,4 @@
OCA.SpreedMe.Views.ChatView = ChatView;
})(OCA, OC, OCP, Marionette, Handlebars, autosize, moment);
})(OCA, OC, OCP, Marionette, autosize, moment);

89
js/views/templates.js Normal file
Просмотреть файл

@ -0,0 +1,89 @@
(function() {
var template = Handlebars.template, templates = OCA.SpreedMe.Views.Templates = OCA.SpreedMe.Views.Templates || {};
templates['chatview'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
var helper;
return "<ul class=\"comments\"></ul>\n<div class=\"emptycontent\">\n <div class=\"icon-comment\"></div>\n <p>"
+ container.escapeExpression(((helper = (helper = helpers.emptyResultLabel || (depth0 != null ? depth0.emptyResultLabel : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"emptyResultLabel","hash":{},"data":data}) : helper)))
+ "</p>\n</div>\n<div class=\"loading hidden\" style=\"height: 50px\"></div>\n";
},"useData":true});
templates['chatview_add_comment'] = template({"1":function(container,depth0,helpers,partials,data) {
var helper;
return " <div class=\"author\">"
+ container.escapeExpression(((helper = (helper = helpers.actorDisplayName || (depth0 != null ? depth0.actorDisplayName : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"actorDisplayName","hash":{},"data":data}) : helper)))
+ "</div>\n";
},"3":function(container,depth0,helpers,partials,data) {
return " <div class=\"guest-name\"></div>\n";
},"5":function(container,depth0,helpers,partials,data) {
var helper;
return " <button class=\"share icon-add has-tooltip\" title=\""
+ container.escapeExpression(((helper = (helper = helpers.shareText || (depth0 != null ? depth0.shareText : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"shareText","hash":{},"data":data}) : helper)))
+ "\"></button>\n <div class=\"shareLoading icon-loading-small hidden\"></div>\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
return "<div class=\"newCommentRow comment\">\n <div class=\"authorRow currentUser\">\n <div class=\"avatar\" data-user-id=\""
+ alias4(((helper = (helper = helpers.actorId || (depth0 != null ? depth0.actorId : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"actorId","hash":{},"data":data}) : helper)))
+ "\"></div>\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.actorId : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.program(3, data, 0),"data":data})) != null ? stack1 : "")
+ " </div>\n <form class=\"newCommentForm\">\n <div contentEditable=\"true\" class=\"message\" data-placeholder=\""
+ alias4(((helper = (helper = helpers.newMessagePlaceholder || (depth0 != null ? depth0.newMessagePlaceholder : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"newMessagePlaceholder","hash":{},"data":data}) : helper)))
+ "\">"
+ alias4(((helper = (helper = helpers.message || (depth0 != null ? depth0.message : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"message","hash":{},"data":data}) : helper)))
+ "</div>\n <input class=\"submit icon-confirm has-tooltip\" type=\"submit\" value=\"\" title=\""
+ alias4(((helper = (helper = helpers.submitText || (depth0 != null ? depth0.submitText : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"submitText","hash":{},"data":data}) : helper)))
+ "\"/>\n <div class=\"submitLoading icon-loading-small hidden\"></div>\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.actorId : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </form>\n</div>\n";
},"useData":true});
templates['chatview_comment'] = template({"1":function(container,depth0,helpers,partials,data) {
return "";
},"3":function(container,depth0,helpers,partials,data) {
return " systemMessage";
},"5":function(container,depth0,helpers,partials,data) {
return " currentUser";
},"7":function(container,depth0,helpers,partials,data) {
return " guestUser";
},"9":function(container,depth0,helpers,partials,data) {
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
return " <div class=\"avatar\" data-user-id=\""
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isGuest : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.program(10, data, 0),"data":data})) != null ? stack1 : "")
+ "\" data-user-display-name=\""
+ alias4(((helper = (helper = helpers.actorDisplayName || (depth0 != null ? depth0.actorDisplayName : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"actorDisplayName","hash":{},"data":data}) : helper)))
+ "\"></div>\n <div class=\"author\">"
+ alias4(((helper = (helper = helpers.actorDisplayName || (depth0 != null ? depth0.actorDisplayName : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"actorDisplayName","hash":{},"data":data}) : helper)))
+ "</div>\n";
},"10":function(container,depth0,helpers,partials,data) {
var helper;
return container.escapeExpression(((helper = (helper = helpers.actorId || (depth0 != null ? depth0.actorId : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"actorId","hash":{},"data":data}) : helper)));
},"12":function(container,depth0,helpers,partials,data) {
return " live-relative-timestamp";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression;
return "<li class=\"comment"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isNotSystemMessage : depth0),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.program(3, data, 0),"data":data})) != null ? stack1 : "")
+ "\" data-id=\""
+ alias4(((helper = (helper = helpers.id || (depth0 != null ? depth0.id : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"id","hash":{},"data":data}) : helper)))
+ "\">\n <div class=\"authorRow"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isUserAuthor : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isGuest : depth0),{"name":"if","hash":{},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\">\n"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isNotSystemMessage : depth0),{"name":"if","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " <div class=\"date has-tooltip"
+ ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.relativeDate : depth0),{"name":"if","hash":{},"fn":container.program(12, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\" data-timestamp=\""
+ alias4(((helper = (helper = helpers.timestamp || (depth0 != null ? depth0.timestamp : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"timestamp","hash":{},"data":data}) : helper)))
+ "\" title=\""
+ alias4(((helper = (helper = helpers.altDate || (depth0 != null ? depth0.altDate : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"altDate","hash":{},"data":data}) : helper)))
+ "\">"
+ alias4(((helper = (helper = helpers.date || (depth0 != null ? depth0.date : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"date","hash":{},"data":data}) : helper)))
+ "</div>\n </div>\n <div class=\"message\">"
+ ((stack1 = ((helper = (helper = helpers.formattedMessage || (depth0 != null ? depth0.formattedMessage : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"formattedMessage","hash":{},"data":data}) : helper))) != null ? stack1 : "")
+ "</div>\n</li>\n";
},"useData":true});
})();

Просмотреть файл

@ -0,0 +1,6 @@
<ul class="comments"></ul>
<div class="emptycontent">
<div class="icon-comment"></div>
<p>{{emptyResultLabel}}</p>
</div>
<div class="loading hidden" style="height: 50px"></div>

Просмотреть файл

@ -0,0 +1,19 @@
<div class="newCommentRow comment">
<div class="authorRow currentUser">
<div class="avatar" data-user-id="{{actorId}}"></div>
{{#if actorId}}
<div class="author">{{actorDisplayName}}</div>
{{else}}
<div class="guest-name"></div>
{{/if}}
</div>
<form class="newCommentForm">
<div contentEditable="true" class="message" data-placeholder="{{newMessagePlaceholder}}">{{message}}</div>
<input class="submit icon-confirm has-tooltip" type="submit" value="" title="{{submitText}}"/>
<div class="submitLoading icon-loading-small hidden"></div>
{{#if actorId}}
<button class="share icon-add has-tooltip" title="{{shareText}}"></button>
<div class="shareLoading icon-loading-small hidden"></div>
{{/if}}
</form>
</div>

Просмотреть файл

@ -0,0 +1,10 @@
<li class="comment{{#if isNotSystemMessage}}{{else}} systemMessage{{/if}}" data-id="{{id}}">
<div class="authorRow{{#if isUserAuthor}} currentUser{{/if}}{{#if isGuest}} guestUser{{/if}}">
{{#if isNotSystemMessage}}
<div class="avatar" data-user-id="{{#if isGuest}}{{else}}{{actorId}}{{/if}}" data-user-display-name="{{actorDisplayName}}"></div>
<div class="author">{{actorDisplayName}}</div>
{{/if}}
<div class="date has-tooltip{{#if relativeDate}} live-relative-timestamp{{/if}}" data-timestamp="{{timestamp}}" title="{{altDate}}">{{date}}</div>
</div>
<div class="message">{{{formattedMessage}}}</div>
</li>

Просмотреть файл

@ -97,6 +97,7 @@ class TemplateLoader {
Util::addScript('spreed', 'views/roomlistview');
Util::addScript('spreed', 'views/sidebarview');
Util::addScript('spreed', 'views/tabview');
Util::addScript('spreed', 'views/templates');
Util::addScript('spreed', 'views/virtuallist');
Util::addScript('spreed', 'richobjectstringparser');
Util::addScript('spreed', 'simplewebrtc');

Просмотреть файл

@ -32,6 +32,7 @@ script(
'views/roomlistview',
'views/sidebarview',
'views/tabview',
'views/templates',
'views/virtuallist',
'richobjectstringparser',
'simplewebrtc',

Просмотреть файл

@ -31,6 +31,7 @@ script(
'views/roomlistview',
'views/sidebarview',
'views/tabview',
'views/templates',
'views/virtuallist',
'richobjectstringparser',
'simplewebrtc',