Compile contactmenu handlebars templates

Fixes #11029
For https://github.com/orgs/nextcloud/projects/18

Ship the compiled handlebars templates. This makes it possible to have a
scricter CSP.

Signed-off-by: Roeland Jago Douma <roeland@famdouma.nl>
This commit is contained in:
Roeland Jago Douma 2018-09-27 16:20:57 +02:00
parent d984231458
commit c9e93b8084
No known key found for this signature in database
GPG Key ID: F941078878347C0C
9 changed files with 191 additions and 81 deletions

View File

@ -25,67 +25,6 @@
(function(OC, $, _, Handlebars) {
'use strict';
var MENU_TEMPLATE = ''
+ '<label class="hidden-visually" for="contactsmenu-search">' + t('core', 'Search contacts …') + '</label>'
+ '<input id="contactsmenu-search" type="search" placeholder="' + t('core', 'Search contacts …') + '" value="{{searchTerm}}">'
+ '<div class="content">'
+ '</div>';
var CONTACTS_LIST_TEMPLATE = ''
+ '{{#unless contacts.length}}'
+ '<div class="emptycontent">'
+ ' <div class="icon-search"></div>'
+ ' <h2>' + t('core', 'No contacts found') + '</h2>'
+ '</div>'
+ '{{/unless}}'
+ '<div id="contactsmenu-contacts"></div>'
+ '{{#if contactsAppEnabled}}<div class="footer"><a href="{{contactsAppURL}}">' + t('core', 'Show all contacts …') + '</a></div>{{/if}}';
var LOADING_TEMPLATE = ''
+ '<div class="emptycontent">'
+ ' <div class="icon-loading"></div>'
+ ' <h2>{{loadingText}}</h2>'
+ '</div>';
var ERROR_TEMPLATE = ''
+ '<div class="emptycontent">'
+ ' <div class="icon-search"></div>'
+ ' <h2>' + t('core', 'Could not load your contacts') + '</h2>'
+ '</div>';
var CONTACT_TEMPLATE = ''
+ '{{#if contact.avatar}}'
+ '<img src="{{contact.avatar}}&size=32" class="avatar"'
+ 'srcset="{{contact.avatar}}&size=32 1x, {{contact.avatar}}&size=64 2x, {{contact.avatar}}&size=128 4x" alt="">'
+ '{{else}}'
+ '<div class="avatar"></div>'
+ '{{/if}}'
+ '<div class="body">'
+ ' <div class="full-name">{{contact.fullName}}</div>'
+ ' <div class="last-message">{{contact.lastMessage}}</div>'
+ '</div>'
+ '{{#if contact.topAction}}'
+ '<a class="top-action" href="{{contact.topAction.hyperlink}}" title="{{contact.topAction.title}}">'
+ ' <img src="{{contact.topAction.icon}}" alt="{{contact.topAction.title}}">'
+ '</a>'
+ '{{/if}}'
+ '{{#if contact.hasTwoActions}}'
+ '<a class="second-action" href="{{contact.secondAction.hyperlink}}" title="{{contact.secondAction.title}}">'
+ ' <img src="{{contact.secondAction.icon}}" alt="{{contact.secondAction.title}}">'
+ '</a>'
+ '{{/if}}'
+ '{{#if contact.hasManyActions}}'
+ ' <span class="other-actions icon-more"></span>'
+ ' <div class="menu popovermenu">'
+ ' <ul>'
+ ' {{#each contact.actions}}'
+ ' <li>'
+ ' <a href="{{hyperlink}}">'
+ ' <img src="{{icon}}" alt="">'
+ ' <span>{{title}}</span>'
+ ' </a>'
+ ' </li>'
+ ' {{/each}}'
+ ' </ul>'
+ ' </div>'
+ '{{/if}}';
/**
* @class Contact
*/
@ -201,10 +140,7 @@
* @returns {undefined}
*/
template: function(data) {
if (!this._template) {
this._template = Handlebars.compile(CONTACT_TEMPLATE);
}
return this._template(data);
return OC.ContactsMenu.Templates['contact'](data);
},
/**
@ -314,10 +250,7 @@
* @returns {string}
*/
loadingTemplate: function(data) {
if (!this._loadingTemplate) {
this._loadingTemplate = Handlebars.compile(LOADING_TEMPLATE);
}
return this._loadingTemplate(data);
return OC.ContactsMenu.Templates['loading'](data);
},
/**
@ -325,10 +258,11 @@
* @returns {string}
*/
errorTemplate: function(data) {
if (!this._errorTemplate) {
this._errorTemplate = Handlebars.compile(ERROR_TEMPLATE);
}
return this._errorTemplate(data);
return OC.ContactsMenu.Templates['error'](
_.extend({
couldNotLoadText: t('core', 'Could not load your contacts')
}, data)
);
},
/**
@ -336,10 +270,11 @@
* @returns {string}
*/
contentTemplate: function(data) {
if (!this._contentTemplate) {
this._contentTemplate = Handlebars.compile(MENU_TEMPLATE);
}
return this._contentTemplate(data);
return OC.ContactsMenu.Templates['menu'](
_.extend({
searchContactsText: t('core', 'Search contacts …')
}, data)
);
},
/**
@ -347,10 +282,12 @@
* @returns {string}
*/
contactsTemplate: function(data) {
if (!this._contactsTemplate) {
this._contactsTemplate = Handlebars.compile(CONTACTS_LIST_TEMPLATE);
}
return this._contactsTemplate(data);
return OC.ContactsMenu.Templates['list'](
_.extend({
noContactsFoundText: t('core', 'No contacts found'),
showAllContactsText: t('core', 'Show all contacts …')
}, data)
);
},
/**

View File

@ -0,0 +1,34 @@
{{#if contact.avatar}}
<img src="{{contact.avatar}}&size=32" class="avatar" srcset="{{contact.avatar}}&size=32 1x, {{contact.avatar}}&size=64 2x, {{contact.avatar}}&size=128 4x" alt="">
{{else}}
<div class="avatar"></div>
{{/if}}
<div class="body">
<div class="full-name">{{contact.fullName}}</div>
<div class="last-message">{{contact.lastMessage}}</div>
</div>
{{#if contact.topAction}}
<a class="top-action" href="{{contact.topAction.hyperlink}}" title="{{contact.topAction.title}}">
<img src="{{contact.topAction.icon}}" alt="{{contact.topAction.title}}">
</a>
{{/if}}
{{#if contact.hasTwoActions}}
<a class="second-action" href="{{contact.secondAction.hyperlink}}" title="{{contact.secondAction.title}}">
<img src="{{contact.secondAction.icon}}" alt="{{contact.secondAction.title}}">
</a>
{{/if}}
{{#if contact.hasManyActions}}
<span class="other-actions icon-more"></span>
<div class="menu popovermenu">
<ul>
{{#each contact.actions}}
<li>
<a href="{{hyperlink}}">
<img src="{{icon}}" alt="">
<span>{{title}}</span>
</a>
</li>
{{/each}}
</ul>
</div>
{{/if}}

View File

@ -0,0 +1,4 @@
<div class="emptycontent">
<div class="icon-search"></div>
<h2>{{couldNotLoadText}}</h2>
</div>

View File

@ -0,0 +1,8 @@
{{#unless contacts.length}}
<div class="emptycontent">
<div class="icon-search"></div>
<h2>{{noContactsFoundText}}</h2>
</div>
{{/unless}}
<div id="contactsmenu-contacts"></div>
{{#if contactsAppEnabled}}<div class="footer"><a href="{{contactsAppURL}}">{{showAllContactsText}}</a></div>{{/if}}

View File

@ -0,0 +1,4 @@
<div class="emptycontent">
<div class="icon-loading"></div>
<h2>{{loadingText}}</h2>
</div>

View File

@ -0,0 +1,4 @@
<label class="hidden-visually" for="contactsmenu-search">{{searchContactsText}}</label>
<input id="contactsmenu-search" type="search" placeholder="{{searchContactsText}}" value="{{searchTerm}}">
<div class="content">
</div>

View File

@ -0,0 +1,117 @@
(function() {
var template = Handlebars.template, templates = OC.ContactsMenu.Templates = OC.ContactsMenu.Templates || {};
templates['contact'] = template({"1":function(container,depth0,helpers,partials,data) {
var stack1, alias1=container.lambda, alias2=container.escapeExpression;
return "<img src=\""
+ alias2(alias1(((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.avatar : stack1), depth0))
+ "&size=32\" class=\"avatar\" srcset=\""
+ alias2(alias1(((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.avatar : stack1), depth0))
+ "&size=32 1x, "
+ alias2(alias1(((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.avatar : stack1), depth0))
+ "&size=64 2x, "
+ alias2(alias1(((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.avatar : stack1), depth0))
+ "&size=128 4x\" alt=\"\">\n";
},"3":function(container,depth0,helpers,partials,data) {
return "<div class=\"avatar\"></div>\n";
},"5":function(container,depth0,helpers,partials,data) {
var stack1, alias1=container.lambda, alias2=container.escapeExpression;
return "<a class=\"top-action\" href=\""
+ alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.topAction : stack1)) != null ? stack1.hyperlink : stack1), depth0))
+ "\" title=\""
+ alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.topAction : stack1)) != null ? stack1.title : stack1), depth0))
+ "\">\n <img src=\""
+ alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.topAction : stack1)) != null ? stack1.icon : stack1), depth0))
+ "\" alt=\""
+ alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.topAction : stack1)) != null ? stack1.title : stack1), depth0))
+ "\">\n</a>\n";
},"7":function(container,depth0,helpers,partials,data) {
var stack1, alias1=container.lambda, alias2=container.escapeExpression;
return "<a class=\"second-action\" href=\""
+ alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.secondAction : stack1)) != null ? stack1.hyperlink : stack1), depth0))
+ "\" title=\""
+ alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.secondAction : stack1)) != null ? stack1.title : stack1), depth0))
+ "\">\n <img src=\""
+ alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.secondAction : stack1)) != null ? stack1.icon : stack1), depth0))
+ "\" alt=\""
+ alias2(alias1(((stack1 = ((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.secondAction : stack1)) != null ? stack1.title : stack1), depth0))
+ "\">\n</a>\n";
},"9":function(container,depth0,helpers,partials,data) {
var stack1;
return " <span class=\"other-actions icon-more\"></span>\n <div class=\"menu popovermenu\">\n <ul>\n"
+ ((stack1 = helpers.each.call(depth0,((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.actions : stack1),{"name":"each","hash":{},"fn":container.program(10, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ " </ul>\n </div>\n";
},"10":function(container,depth0,helpers,partials,data) {
var helper, alias1=helpers.helperMissing, alias2="function", alias3=container.escapeExpression;
return " <li>\n <a href=\""
+ alias3(((helper = (helper = helpers.hyperlink || (depth0 != null ? depth0.hyperlink : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"hyperlink","hash":{},"data":data}) : helper)))
+ "\">\n <img src=\""
+ alias3(((helper = (helper = helpers.icon || (depth0 != null ? depth0.icon : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"icon","hash":{},"data":data}) : helper)))
+ "\" alt=\"\">\n <span>"
+ alias3(((helper = (helper = helpers.title || (depth0 != null ? depth0.title : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"title","hash":{},"data":data}) : helper)))
+ "</span>\n </a>\n </li>\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
var stack1, alias1=container.lambda, alias2=container.escapeExpression;
return ((stack1 = helpers["if"].call(depth0,((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.avatar : stack1),{"name":"if","hash":{},"fn":container.program(1, data, 0),"inverse":container.program(3, data, 0),"data":data})) != null ? stack1 : "")
+ "<div class=\"body\">\n <div class=\"full-name\">"
+ alias2(alias1(((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.fullName : stack1), depth0))
+ "</div>\n <div class=\"last-message\">"
+ alias2(alias1(((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.lastMessage : stack1), depth0))
+ "</div>\n</div>\n"
+ ((stack1 = helpers["if"].call(depth0,((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.topAction : stack1),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ ((stack1 = helpers["if"].call(depth0,((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.hasTwoActions : stack1),{"name":"if","hash":{},"fn":container.program(7, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ ((stack1 = helpers["if"].call(depth0,((stack1 = (depth0 != null ? depth0.contact : depth0)) != null ? stack1.hasManyActions : stack1),{"name":"if","hash":{},"fn":container.program(9, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "");
},"useData":true});
templates['error'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
var helper;
return "<div class=\"emptycontent\">\n <div class=\"icon-search\"></div>\n <h2>"
+ container.escapeExpression(((helper = (helper = helpers.couldNotLoadText || (depth0 != null ? depth0.couldNotLoadText : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0,{"name":"couldNotLoadText","hash":{},"data":data}) : helper)))
+ "</h2>\n</div>\n";
},"useData":true});
templates['list'] = template({"1":function(container,depth0,helpers,partials,data) {
var helper;
return "<div class=\"emptycontent\">\n <div class=\"icon-search\"></div>\n <h2>"
+ container.escapeExpression(((helper = (helper = helpers.noContactsFoundText || (depth0 != null ? depth0.noContactsFoundText : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0,{"name":"noContactsFoundText","hash":{},"data":data}) : helper)))
+ "</h2>\n</div>\n";
},"3":function(container,depth0,helpers,partials,data) {
var helper, alias1=helpers.helperMissing, alias2="function", alias3=container.escapeExpression;
return "<div class=\"footer\"><a href=\""
+ alias3(((helper = (helper = helpers.contactsAppURL || (depth0 != null ? depth0.contactsAppURL : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"contactsAppURL","hash":{},"data":data}) : helper)))
+ "\">"
+ alias3(((helper = (helper = helpers.showAllContactsText || (depth0 != null ? depth0.showAllContactsText : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"showAllContactsText","hash":{},"data":data}) : helper)))
+ "</a></div>";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
var stack1;
return ((stack1 = helpers.unless.call(depth0,((stack1 = (depth0 != null ? depth0.contacts : depth0)) != null ? stack1.length : stack1),{"name":"unless","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "<div id=\"contactsmenu-contacts\"></div>\n"
+ ((stack1 = helpers["if"].call(depth0,(depth0 != null ? depth0.contactsAppEnabled : depth0),{"name":"if","hash":{},"fn":container.program(3, data, 0),"inverse":container.noop,"data":data})) != null ? stack1 : "")
+ "\n";
},"useData":true});
templates['loading'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
var helper;
return "<div class=\"emptycontent\">\n <div class=\"icon-loading\"></div>\n <h2>"
+ container.escapeExpression(((helper = (helper = helpers.loadingText || (depth0 != null ? depth0.loadingText : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0,{"name":"loadingText","hash":{},"data":data}) : helper)))
+ "</h2>\n</div>\n";
},"useData":true});
templates['menu'] = template({"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) {
var helper, alias1=helpers.helperMissing, alias2="function", alias3=container.escapeExpression;
return "<label class=\"hidden-visually\" for=\"contactsmenu-search\">"
+ alias3(((helper = (helper = helpers.searchContactsText || (depth0 != null ? depth0.searchContactsText : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"searchContactsText","hash":{},"data":data}) : helper)))
+ "</label>\n<input id=\"contactsmenu-search\" type=\"search\" placeholder=\""
+ alias3(((helper = (helper = helpers.searchContactsText || (depth0 != null ? depth0.searchContactsText : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"searchContactsText","hash":{},"data":data}) : helper)))
+ "\" value=\""
+ alias3(((helper = (helper = helpers.searchTerm || (depth0 != null ? depth0.searchTerm : depth0)) != null ? helper : alias1),(typeof helper === alias2 ? helper.call(depth0,{"name":"searchTerm","hash":{},"data":data}) : helper)))
+ "\">\n<div class=\"content\">\n</div>\n";
},"useData":true});
})();

View File

@ -42,6 +42,7 @@
"sharedialogshareelistview.js",
"octemplate.js",
"contactsmenu.js",
"contactsmenu_templates.js",
"eventsource.js",
"config.js",
"public/appconfig.js",

View File

@ -124,6 +124,7 @@ class OC_Template extends \OC\Template\Base {
OC_Util::addScript('files/fileinfo');
OC_Util::addScript('files/client');
OC_Util::addScript('contactsmenu');
OC_Util::addScript('contactsmenu_templates');
if (\OC::$server->getConfig()->getSystemValue('debug')) {
// Add the stuff we need always