Advancedcontentfilter honors CSP (redux) #684

Merged
MrPetovan merged 4 commits from bug/5341-advancedcontentfilter-honors-csp into develop 2018-08-05 15:00:11 +02:00
4 changed files with 199 additions and 194 deletions

File diff suppressed because one or more lines are too long

View File

@ -170,7 +170,7 @@ function advancedcontentfilter_module() {}
function advancedcontentfilter_init(App $a)
{
if ($a->argv[1] == 'api') {
if ($a->argc > 1 && $a->argv[1] == 'api') {
$slim = new \Slim\App();
require __DIR__ . '/src/middlewares.php';
@ -188,7 +188,7 @@ function advancedcontentfilter_content(App $a)
return Login::form('/' . implode('/', $a->argv));
}
if ($a->argc > 0 && $a->argv[1] == 'help') {
if ($a->argc > 1 && $a->argv[1] == 'help') {
$lang = $a->user['language'];
$default_dir = 'addon/advancedcontentfilter/doc/';
@ -208,31 +208,31 @@ function advancedcontentfilter_content(App $a)
} else {
$t = get_markup_template('settings.tpl', 'addon/advancedcontentfilter/');
return replace_macros($t, [
'$messages' => [
'backtosettings' => L10n::t('Back to Addon Settings'),
'title' => L10n::t('Advanced Content Filter'),
'add_a_rule' => L10n::t('Add a Rule'),
'help' => L10n::t('Help'),
'intro' => L10n::t('Add and manage your personal content filter rules in this screen. Rules have a name and an arbitrary expression that will be matched against post data. For a complete reference of the available operations and variables, check the help page.'),
'your_rules' => L10n::t('Your rules'),
'no_rules' => L10n::t('You have no rules yet! Start adding one by clicking on the button above next to the title.'),
'disabled' => L10n::t('Disabled'),
'enabled' => L10n::t('Enabled'),
'disable_this_rule' => L10n::t('Disable this rule'),
'enable_this_rule' => L10n::t('Enable this rule'),
'edit_this_rule' => L10n::t('Edit this rule'),
'edit_the_rule' => L10n::t('Edit the rule'),
'save_this_rule' => L10n::t('Save this rule'),
'delete_this_rule' => L10n::t('Delete this rule'),
'rule' => L10n::t('Rule'),
'close' => L10n::t('Close'),
'addtitle' => L10n::t('Add new rule'),
'rule_name' => L10n::t('Rule Name'),
'rule_expression' => L10n::t('Rule Expression'),
'cancel' => L10n::t('Cancel'),
],
'$current_theme' => $a->getCurrentTheme(),
'$backtosettings' => L10n::t('Back to Addon Settings'),
'$title' => L10n::t('Advanced Content Filter'),
'$add_a_rule' => L10n::t('Add a Rule'),
'$help' => L10n::t('Help'),
'$advanced_content_filter_intro' => addslashes(L10n::t('Add and manage your personal content filter rules in this screen. Rules have a name and an arbitrary expression that will be matched against post data. For a complete reference of the available operations and variables, check the <a href="advancedcontentfilter/help">help page</a>.')),
'$your_rules' => L10n::t('Your rules'),
'$no_rules' => L10n::t('You have no rules yet! Start adding one by clicking on the button above next to the title.'),
'$disabled' => L10n::t('Disabled'),
'$enabled' => L10n::t('Enabled'),
'$disable_this_rule' => L10n::t('Disable this rule'),
'$enable_this_rule' => L10n::t('Enable this rule'),
'$edit_this_rule' => L10n::t('Edit this rule'),
'$edit_the_rule' => L10n::t('Edit the rule'),
'$save_this_rule' => L10n::t('Save this rule'),
'$delete_this_rule' => L10n::t('Delete this rule'),
'$rule' => L10n::t('Rule'),
'$close' => L10n::t('Close'),
'$addtitle' => L10n::t('Add new rule'),
'$rule_name' => L10n::t('Rule Name'),
'$rule_expression' => L10n::t('Rule Expression'),
'$examples' => L10n::t('<p>Examples:</p><ul><li><pre>author_link == \'https://friendica.mrpetovan.com/profile/hypolite\'</pre></li><li>tags</li></ul>'),
'$cancel' => L10n::t('Cancel'),
'$rules' => advancedcontentfilter_get_rules(),
'$baseurl' => System::baseUrl(true),
'$form_security_token' => get_form_security_token()
]);
}

View File

@ -1,51 +1,52 @@
<!--
This the the source HTML for the render functions defined in settings.tpl
This the the source HTML for the render functions defined in advancedcontentfilter.js
This file is only for reference only and editing it won't change the addon display.
Here's the workflow to change the actual display:
1. Edit this file
2. Run it through https://vuejs.org/v2/guide/render-function.html#Template-Compilation
3. In the results, replace ##$ by {{$ and ## by }} in order to restore smarty variable interpolation
4. Replace the render and staticRenderFns members in settings.tpl by the contents of the output anonymous() functions
3. Replace the render and staticRenderFns members in advancedcontentfilter.js by the contents of the anonymous() functions
-->
<div id="rules">
<p><a href="settings/addon">🔙 ##$backtosettings##</a></p>
<p><a href="settings/addon">🔙 {{ messages.backtosettings }}</a></p>
<h1>
##$title##
<a href="##$baseurl##/advancedcontentfilter/help" class="btn btn-default btn-sm" title="##$help##">
{{ messages.title }}
&nbsp;
<a href="advancedcontentfilter/help" class="btn btn-default btn-sm" :title="messages.help">
<i class="fa fa-question fa-2x" aria-hidden="true"></i>
</a>
</h1>
<div>##$advanced_content_filter_intro##</div>
<div>{{ messages.intro }}</div>
<h2>
##$your_rules##
<button class="btn btn-primary btn-sm" title="##$add_a_rule##" @click="showModal = true">
{{ messages.your_rules }}
&nbsp;
<button class="btn btn-primary btn-sm" :title="messages.add_a_rule" @click="showModal = true">
<i class="fa fa-plus fa-2x" aria-hidden="true"></i>
</button>
</h2>
<div v-if="rules.length === 0" v-cloak>
##$no_rules##
{{ messages.no_rules }}
</div>
<ul class="list-group" v-cloak>
<li class="list-group-item" v-for="rule in rules">
<p class="pull-right">
<button type="button" class="btn btn-xs btn-primary" v-on:click="toggleActive(rule)" aria-label="##$disable_this_rule##" title="##$disable_this_rule##" v-if="parseInt(rule.active)">
<i class="fa fa-toggle-on" aria-hidden="true"></i> ##$enabled##
<button type="button" class="btn btn-xs btn-primary" v-on:click="toggleActive(rule)" :aria-label="messages.disable_this_rule" :title="messages.disable_this_rule" v-if="parseInt(rule.active)">
<i class="fa fa-toggle-on" aria-hidden="true"></i> {{ messages.enabled }}
</button>
<button type="button" class="btn btn-xs btn-default" v-on:click="toggleActive(rule)" aria-label="##$enable_this_rule##" title="##$enable_this_rule##" v-else>
<i class="fa fa-toggle-off" aria-hidden="true"></i> ##$disabled##
<button type="button" class="btn btn-xs btn-default" v-on:click="toggleActive(rule)" :aria-label="messages.enable_this_rule" :title="messages.enable_this_rule" v-else>
<i class="fa fa-toggle-off" aria-hidden="true"></i> {{ messages.disabled }}
</button>
<button type="button" class="btn btn-xs btn-primary" v-on:click="editRule(rule)" aria-label="##$edit_this_rule##" title="##$edit_this_rule##">
&nbsp;
<button type="button" class="btn btn-xs btn-primary" v-on:click="editRule(rule)" :aria-label="messages.edit_this_rule" :title="messages.edit_this_rule">
<i class="fa fa-pencil" aria-hidden="true"></i>
</button>
<button type="button" class="btn btn-xs btn-default" v-on:click="deleteRule(rule)" aria-label="##$delete_this_rule##" title="##$delete_this_rule##">
&nbsp;
<button type="button" class="btn btn-xs btn-default" v-on:click="deleteRule(rule)" :aria-label="messages.delete_this_rule" :title="messages.delete_this_rule">
<i class="fa fa-trash-o" aria-hidden="true"></i>
</button>
</p>
<h3 class="list-group-item-heading">
##$rule## #{{ rule.id }}: {{ rule.name }}
{{ messages.rule }} #{{ rule.id }}: {{ rule.name }}
</h3>
<pre class="list-group-item-text" v-if="rule.expression">{{ rule.expression }}</pre>
</li>
@ -55,26 +56,25 @@
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="##$close##" @click="showModal = false"><span aria-hidden="true">&times;</span></button>
<h3 v-if="rule.id">##$edit_the_rule## "{{ rule.name }}"</h3>
<h3 v-if="!rule.id">##$add_a_rule##</h3>
<button type="button" class="close" data-dismiss="modal" :aria-label="messages.close" @click="showModal = false" v-if="currentTheme === 'frio'"><span aria-hidden="true">&times;</span></button>
<h3 v-if="rule.id">{{ messages.edit_the_rule }} "{{ rule.name }}"</h3>
<h3 v-if="!rule.id">{{ messages.add_a_rule }}</h3>
</div>
<div class="modal-body">
<form>
<input type="hidden" name="form_security_token" id="csrf" value="##$form_security_token##" />
<div class="alert alert-danger" role="alert" v-if="errorMessage">{{ errorMessage }}</div>
<div class="form-group">
<input class="form-control" placeholder="##$rule_name##" v-model="rule.name">
<input class="form-control" :placeholder="messages.rule_name" v-model="rule.name">
</div>
<div class="form-group">
<input class="form-control" placeholder="##$rule_expression##" v-model="rule.expression">
<input class="form-control" :placeholder="messages.rule_expression" v-model="rule.expression">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal" aria-label="Close" @click="resetForm()">##$cancel##</button>
<button slot="button" class="btn btn-primary" type="button" v-if="rule.id" v-on:click="saveRule(rule)">##$save_this_rule##</button>
<button slot="button" class="btn btn-primary" type="button" v-if="!rule.id" v-on:click="addRule()">##$add_a_rule##</button>
<button type="button" class="btn btn-default" data-dismiss="modal" aria-label="Close" @click="resetForm()">{{ messages.cancel }}</button>
<button slot="button" class="btn btn-primary" type="button" v-if="rule.id" v-on:click="saveRule(rule)">{{ messages.save_this_rule }}</button>
<button slot="button" class="btn btn-primary" type="button" v-if="!rule.id" v-on:click="addRule()">{{ messages.add_a_rule }}</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->

View File

@ -2,148 +2,21 @@
<style>[v-cloak] { display: none; }</style>
<div id="rules"></div>
<script> var existingRules = {{$rules}};</script>
<script>
var existingRules = {{$rules}};
var messages = {
{{foreach $messages as $key => $value}}
{{$key}}: "{{$value}}",
{{/foreach}}
};
var csrfToken = "{{$form_security_token}}";
var currentTheme = "{{$current_theme}}";
</script>
<!-- JS -->
<script src="{{$baseurl}}/view/asset/vue/dist/vue.min.js"></script>
<script>
$.extend({
ajaxJSON: function(method, url, data) {
return $.ajax({
type: method.toUpperCase(),
url: url,
data: JSON.stringify(data),
contentType: 'application/json; charset=utf-8',
dataType: 'json'
});
}
});
new Vue({
el: '#rules',
data: {
showModal: false,
errorMessage: '',
editedIndex: null,
rule: {id: '', name: '', expression: '', created: ''},
rules: existingRules || [],
itemUrl: '',
itemJson: ''
},
watch: {
showModal: function () {
if (this.showModal) {
$(this.$refs.vuemodal).modal('show');
} else {
$(this.$refs.vuemodal).modal('hide');
}
}
},
mounted: function() {
$.ajaxSetup({headers: {'X-CSRF-Token': document.querySelector('#csrf').getAttribute('value')}});
},
methods: {
resetForm: function() {
this.rule = {id: '', name: '', expression: '', created: ''};
this.showModal = false;
this.editedIndex = null;
},
addRule: function () {
if (this.rule.name.trim()) {
this.errorMessage = '';
var self = this;
$.ajaxJSON('post', '/advancedcontentfilter/api/rules', this.rule)
.then(function (responseJSON) {
self.rules.push(responseJSON.rule);
self.resetForm();
}, function (response) {
self.errorMessage = response.responseJSON.message;
});
}
},
editRule: function (rule) {
this.editedIndex = this.rules.indexOf(rule);
this.rule = Object.assign({}, rule);
this.showModal = true;
},
saveRule: function (rule) {
this.errorMessage = '';
var self = this;
$.ajaxJSON('put', '/advancedcontentfilter/api/rules/' + rule.id, rule)
.then(function () {
self.rules[self.editedIndex] = rule;
self.resetForm();
}, function (response) {
self.errorMessage = response.responseJSON.message;
});
},
toggleActive: function (rule) {
var previousValue = this.rules[this.rules.indexOf(rule)].active;
var newValue = Math.abs(parseInt(rule.active) - 1);
this.rules[this.rules.indexOf(rule)].active = newValue;
var self = this;
$.ajaxJSON('put', '/advancedcontentfilter/api/rules/' + rule.id, {'active': newValue})
.fail(function (response) {
self.rules[self.rules.indexOf(rule)].active = previousValue;
console.log(response.responseJSON.message);
});
},
deleteRule: function (rule) {
if (confirm('Are you sure you want to delete this rule?')) {
var self = this;
$.ajaxJSON('delete', '/advancedcontentfilter/api/rules/' + rule.id)
.then(function () {
self.rules.splice(self.rules.indexOf(rule), 1);
}, function (response) {
console.log(response.responseJSON.message);
});
}
},
showVariables: function () {
var urlParts = this.itemUrl.split('/');
var guid = urlParts[urlParts.length - 1];
this.itemJson = '';
var self = this;
$.ajaxJSON('get', '/advancedcontentfilter/api/variables/' + guid)
.then(function (responseJSON) {
self.itemJson = responseJSON.variables;
}, function (response) {
self.itemJson = response.responseJSON.message;
});
return false;
}
},
// These render function have been compiled from templates/vue_dom.tpl, check this file out for instructions
render: function () {
with(this){return _c('div',{attrs:{"id":"rules"}},[_m(0),_m(1),_c('div',[_v("{{$advanced_content_filter_intro}}")]),_c('h2',[_v("{{$your_rules}}"),_c('button',{staticClass:"btn btn-primary btn-sm",attrs:{"title":"{{$add_a_rule}}"},on:{"click":function($event){showModal = true}}},[_c('i',{staticClass:"fa fa-plus fa-2x",attrs:{"aria-hidden":"true"}})])]),(rules.length === 0)?_c('div',{},[_v("{{$no_rules}}")]):_e(),_c('ul',{staticClass:"list-group"},_l((rules),function(rule){return _c('li',{staticClass:"list-group-item"},[_c('p',{staticClass:"pull-right"},[(parseInt(rule.active))?_c('button',{staticClass:"btn btn-xs btn-primary",attrs:{"type":"button","aria-label":"{{$disable_this_rule}}","title":"{{$disable_this_rule}}"},on:{"click":function($event){toggleActive(rule)}}},[_c('i',{staticClass:"fa fa-toggle-on",attrs:{"aria-hidden":"true"}}),_v(" {{$enabled}}")]):_c('button',{staticClass:"btn btn-xs btn-default",attrs:{"type":"button","aria-label":"{{$enable_this_rule}}","title":"{{$enable_this_rule}}"},on:{"click":function($event){toggleActive(rule)}}},[_c('i',{staticClass:"fa fa-toggle-off",attrs:{"aria-hidden":"true"}}),_v(" {{$disabled}}")]),_c('button',{staticClass:"btn btn-xs btn-primary",attrs:{"type":"button","aria-label":"{{$edit_this_rule}}","title":"{{$edit_this_rule}}"},on:{"click":function($event){editRule(rule)}}},[_c('i',{staticClass:"fa fa-pencil",attrs:{"aria-hidden":"true"}})]),_c('button',{staticClass:"btn btn-xs btn-default",attrs:{"type":"button","aria-label":"{{$delete_this_rule}}","title":"{{$delete_this_rule}}"},on:{"click":function($event){deleteRule(rule)}}},[_c('i',{staticClass:"fa fa-trash-o",attrs:{"aria-hidden":"true"}})])]),_c('h3',{staticClass:"list-group-item-heading"},[_v("{{$rule}} #"+_s(rule.id)+": "+_s(rule.name))]),(rule.expression)?_c('pre',{staticClass:"list-group-item-text"},[_v(_s(rule.expression))]):_e()])})),_c('div',{ref:"vuemodal",staticClass:"modal fade",attrs:{"tabindex":"-1","role":"dialog"}},[_c('div',{staticClass:"modal-dialog",attrs:{"role":"document"}},[_c('div',{staticClass:"modal-content"},[_c('div',{staticClass:"modal-header"},[_c('button',{staticClass:"close",attrs:{"type":"button","data-dismiss":"modal","aria-label":"{{$close}}"},on:{"click":function($event){showModal = false}}},[_c('span',{attrs:{"aria-hidden":"true"}},[_v("×")])]),(rule.id)?_c('h3',[_v("{{$edit_the_rule}} \""+_s(rule.name)+"\"")]):_e(),(!rule.id)?_c('h3',[_v("{{$add_a_rule}}")]):_e()]),_c('div',{staticClass:"modal-body"},[_c('form',[_c('input',{attrs:{"type":"hidden","name":"form_security_token","id":"csrf","value":"{{$form_security_token}}"}}),(errorMessage)?_c('div',{staticClass:"alert alert-danger",attrs:{"role":"alert"}},[_v(_s(errorMessage))]):_e(),_c('div',{staticClass:"form-group"},[_c('input',{directives:[{name:"model",rawName:"v-model",value:(rule.name),expression:"rule.name"}],staticClass:"form-control",attrs:{"placeholder":"{{$rule_name}}"},domProps:{"value":(rule.name)},on:{"input":function($event){if($event.target.composing)return;$set(rule, "name", $event.target.value)}}})]),_c('div',{staticClass:"form-group"},[_c('input',{directives:[{name:"model",rawName:"v-model",value:(rule.expression),expression:"rule.expression"}],staticClass:"form-control",attrs:{"placeholder":"{{$rule_expression}}"},domProps:{"value":(rule.expression)},on:{"input":function($event){if($event.target.composing)return;$set(rule, "expression", $event.target.value)}}})])])]),_c('div',{staticClass:"modal-footer"},[_c('button',{staticClass:"btn btn-default",attrs:{"type":"button","data-dismiss":"modal","aria-label":"Close"},on:{"click":function($event){resetForm()}}},[_v("{{$cancel}}")]),(rule.id)?_c('button',{staticClass:"btn btn-primary",attrs:{"slot":"button","type":"button"},on:{"click":function($event){saveRule(rule)}},slot:"button"},[_v("{{$save_this_rule}}")]):_e(),(!rule.id)?_c('button',{staticClass:"btn btn-primary",attrs:{"slot":"button","type":"button"},on:{"click":function($event){addRule()}},slot:"button"},[_v("{{$add_a_rule}}")]):_e()])])])]),_c('form',{staticClass:"form-inline",on:{"submit":function($event){$event.preventDefault();showVariables()}}},[_c('fieldset',[_c('legend',[_v("Show post variables")]),_c('div',{staticClass:"form-group",staticStyle:{"width":"50%"}},[_c('label',{staticClass:"sr-only",attrs:{"for":"itemUrl"}},[_v("Post URL or item guid")]),_c('input',{directives:[{name:"model",rawName:"v-model",value:(itemUrl),expression:"itemUrl"}],staticClass:"form-control",staticStyle:{"width":"100%"},attrs:{"id":"itemUrl","placeholder":"Post URL or item guid"},domProps:{"value":(itemUrl)},on:{"input":function($event){if($event.target.composing)return;itemUrl=$event.target.value}}})]),_c('button',{staticClass:"btn btn-primary",attrs:{"type":"submit"}},[_v("Show Variables")])])]),_c('pre',{},[_v(_s(itemJson))])])}
},
staticRenderFns: [
function () {
with(this){return _c('p',[_c('a',{attrs:{"href":"settings/addon"}},[_v("🔙 {{$backtosettings}}")])])}
},
function () {
with(this){return _c('h1',[_v("{{$title}}"),_c('a',{staticClass:"btn btn-default btn-sm",attrs:{"href":"{{$baseurl}}/advancedcontentfilter/help","title":"{{$help}}"}},[_c('i',{staticClass:"fa fa-question fa-2x",attrs:{"aria-hidden":"true"}})])])}
}
]
});
</script>
<script src="{{$baseurl}}/addon/advancedcontentfilter/advancedcontentfilter.js"></script>
</div>