[advancedcontentfilter] Add addon files
- Add hooks - Add mini-API module powered by Slim - Add addon settings page powered by VueJS - Add translation strings - Add help page
This commit is contained in:
parent
20862be7d0
commit
b02724f867
9 changed files with 1396 additions and 0 deletions
24
advancedcontentfilter/LICENSE
Normal file
24
advancedcontentfilter/LICENSE
Normal file
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2011-2018 Hypolite Petovan
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Friendica nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL FRIENDICA BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
11
advancedcontentfilter/README.md
Normal file
11
advancedcontentfilter/README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
Advanced Content Filter
|
||||
=======================
|
||||
|
||||
Main author Hypolite Petovan.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
The _Advanced Content Filter_ addon is licensed under the [3-clause BSD license][2] see the LICENSE file in the addons directory.
|
||||
|
||||
[1]: http://opensource.org/licenses/BSD-3-Clause
|
113
advancedcontentfilter/advancedcontentfilter.js
Normal file
113
advancedcontentfilter/advancedcontentfilter.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
Vue.http.headers.common['X-CSRF-Token'] = document.querySelector('#csrf').getAttribute('value');
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//created: function () {
|
||||
// this.fetchRules();
|
||||
//},
|
||||
|
||||
methods: {
|
||||
resetForm: function() {
|
||||
this.rule = {id: '', name: '', expression: '', created: ''};
|
||||
this.showModal = false;
|
||||
this.editedIndex = null;
|
||||
},
|
||||
|
||||
//fetchRules: function () {
|
||||
// this.$http.get('/advancedcontentfilter/api/rules')
|
||||
// .then(function (response) {
|
||||
// this.rules = response.body;
|
||||
// }, function (err) {
|
||||
// console.log(err);
|
||||
// });
|
||||
//},
|
||||
|
||||
addRule: function () {
|
||||
if (this.rule.name.trim()) {
|
||||
this.errorMessage = '';
|
||||
this.$http.post('/advancedcontentfilter/api/rules', this.rule)
|
||||
.then(function (res) {
|
||||
this.rules.push(res.body.rule);
|
||||
this.resetForm();
|
||||
}, function (err) {
|
||||
this.errorMessage = err.body.message;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
editRule: function (rule) {
|
||||
this.editedIndex = this.rules.indexOf(rule);
|
||||
this.rule = Object.assign({}, rule);
|
||||
this.showModal = true;
|
||||
},
|
||||
|
||||
saveRule: function (rule) {
|
||||
this.errorMessage = '';
|
||||
this.$http.put('/advancedcontentfilter/api/rules/' + rule.id, rule)
|
||||
.then(function (res) {
|
||||
this.rules[this.editedIndex] = rule;
|
||||
this.resetForm();
|
||||
}, function (err) {
|
||||
this.errorMessage = err.body.message;
|
||||
});
|
||||
},
|
||||
|
||||
toggleActive: function (rule) {
|
||||
this.$http.put('/advancedcontentfilter/api/rules/' + rule.id, {'active': Math.abs(parseInt(rule.active) - 1)})
|
||||
.then(function (res) {
|
||||
this.rules[this.rules.indexOf(rule)].active = Math.abs(parseInt(rule.active) - 1);
|
||||
}, function (err) {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
|
||||
deleteRule: function (rule) {
|
||||
if (confirm('Are you sure you want to delete this rule?')) {
|
||||
this.$http.delete('/advancedcontentfilter/api/rules/' + rule.id)
|
||||
.then(function (res) {
|
||||
this.rules.splice(this.rules.indexOf(rule), 1);
|
||||
}, function (err) {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
showVariables: function () {
|
||||
var guid = '';
|
||||
|
||||
var urlParts = this.itemUrl.split('/');
|
||||
|
||||
guid = urlParts[urlParts.length - 1];
|
||||
|
||||
this.$http.get('/advancedcontentfilter/api/variables/' + guid)
|
||||
.then(function (response) {
|
||||
this.itemJson = response.bodyText;
|
||||
}, function (err) {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
420
advancedcontentfilter/advancedcontentfilter.php
Normal file
420
advancedcontentfilter/advancedcontentfilter.php
Normal file
|
@ -0,0 +1,420 @@
|
|||
<?php
|
||||
/**
|
||||
* Name: Advanced content Filter
|
||||
* Description: Expression-based content filter
|
||||
* Version: 1.0
|
||||
* Author: Hypolite Petovan <https://friendica.mrpetovan.com/profile/hypolite>
|
||||
* Maintainer: Hypolite Petovan <https://friendica.mrpetovan.com/profile/hypolite>
|
||||
*
|
||||
* Copyright (c) 2018 Hypolite Petovan
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* * copyright notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Friendica nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL FRIENDICA BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBStructure;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Symfony\Component\ExpressionLanguage;
|
||||
|
||||
require_once 'boot.php';
|
||||
require_once 'include/conversation.php';
|
||||
require_once 'include/dba.php';
|
||||
require_once 'include/security.php';
|
||||
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
|
||||
|
||||
function advancedcontentfilter_install()
|
||||
{
|
||||
Addon::registerHook('dbstructure_definition' , __FILE__, 'advancedcontentfilter_dbstructure_definition');
|
||||
Addon::registerHook('prepare_body_content_filter', __FILE__, 'advancedcontentfilter_prepare_body_content_filter');
|
||||
Addon::registerHook('addon_settings' , __FILE__, 'advancedcontentfilter_addon_settings');
|
||||
|
||||
DBStructure::update(false, true);
|
||||
|
||||
logger("installed advancedcontentfilter");
|
||||
}
|
||||
|
||||
function advancedcontentfilter_uninstall()
|
||||
{
|
||||
Addon::unregisterHook('dbstructure_definition' , __FILE__, 'advancedcontentfilter_dbstructure_definition');
|
||||
Addon::unregisterHook('prepare_body_content_filter', __FILE__, 'advancedcontentfilter_prepare_body_content_filter');
|
||||
Addon::unregisterHook('addon_settings' , __FILE__, 'advancedcontentfilter_addon_settings');
|
||||
}
|
||||
|
||||
/*
|
||||
* Hooks
|
||||
*/
|
||||
|
||||
function advancedcontentfilter_dbstructure_definition(App $a, &$database)
|
||||
{
|
||||
$database["advancedcontentfilter_rules"] = [
|
||||
"comment" => "Advancedcontentfilter addon rules",
|
||||
"fields" => [
|
||||
"id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "Auto incremented rule id"],
|
||||
"uid" => ["type" => "int unsigned", "not null" => "1", "comment" => "Owner user id"],
|
||||
"name" => ["type" => "varchar(255)", "not null" => "1", "comment" => "Rule name"],
|
||||
"expression" => ["type" => "mediumtext" , "not null" => "1", "comment" => "Expression text"],
|
||||
"serialized" => ["type" => "mediumtext" , "not null" => "1", "comment" => "Serialized parsed expression"],
|
||||
"active" => ["type" => "boolean" , "not null" => "1", "default" => "1", "comment" => "Whether the rule is active or not"],
|
||||
"created" => ["type" => "datetime" , "not null" => "1", "default" => NULL_DATE, "comment" => "Creation date"],
|
||||
],
|
||||
"indexes" => [
|
||||
"PRIMARY" => ["id"],
|
||||
"uid_active" => ["uid", "active"],
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
function advancedcontentfilter_prepare_body_content_filter(App $a, &$hook_data)
|
||||
{
|
||||
static $expressionLanguage;
|
||||
|
||||
if (is_null($expressionLanguage)) {
|
||||
$expressionLanguage = new ExpressionLanguage\ExpressionLanguage();
|
||||
}
|
||||
|
||||
if (!local_user()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$vars = [];
|
||||
foreach ($hook_data['item'] as $key => $value) {
|
||||
$vars[str_replace('-', '_', $key)] = $value;
|
||||
}
|
||||
|
||||
$rules = Friendica\Core\Cache::get('rules_' . local_user());
|
||||
if (!isset($rules)) {
|
||||
$rules = dba::inArray(dba::select(
|
||||
'advancedcontentfilter_rules',
|
||||
['name', 'expression', 'serialized'],
|
||||
['uid' => local_user(), 'active' => true]
|
||||
));
|
||||
}
|
||||
|
||||
foreach($rules as $rule) {
|
||||
try {
|
||||
$serializedParsedExpression = new ExpressionLanguage\SerializedParsedExpression(
|
||||
$rule['expression'],
|
||||
$rule['serialized']
|
||||
);
|
||||
|
||||
$found = (bool) $expressionLanguage->evaluate($serializedParsedExpression, $vars);
|
||||
} catch (Exception $e) {
|
||||
$found = false;
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
$hook_data['filter_reasons'][] = L10n::t('Filtered by rule: %s', $rule['name']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function advancedcontentfilter_addon_settings(App $a, &$s)
|
||||
{
|
||||
if (!local_user()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$advancedcontentfilter = L10n::t('Advanced Content Filter');
|
||||
|
||||
$s .= <<<HTML
|
||||
<span class="settings-block fakelink" style="display: block;"><h3><a href="advancedcontentfilter">$advancedcontentfilter <i class="glyphicon glyphicon-share"></i></a></h3></span>
|
||||
HTML;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module
|
||||
*/
|
||||
|
||||
function advancedcontentfilter_module() {}
|
||||
|
||||
function advancedcontentfilter_init(App $a)
|
||||
{
|
||||
if ($a->argv[1] == 'api') {
|
||||
$slim = new \Slim\App();
|
||||
|
||||
require __DIR__ . '/src/middlewares.php';
|
||||
|
||||
require __DIR__ . '/src/routes.php';
|
||||
$slim->run();
|
||||
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function advancedcontentfilter_content(App $a)
|
||||
{
|
||||
if (!local_user()) {
|
||||
return \Friendica\Module\Login::form('/' . implode('/', $a->argv));
|
||||
}
|
||||
|
||||
if ($a->argc > 0 && $a->argv[1] == 'help') {
|
||||
$lang = $a->user['language'];
|
||||
|
||||
$default_dir = 'addon/advancedcontentfilter/doc/';
|
||||
$help_file = 'advancedcontentfilter.md';
|
||||
$help_path = $default_dir . $help_file;
|
||||
if (file_exists($default_dir . $lang . '/' . $help_file)) {
|
||||
$help_path = $default_dir . $lang . '/' . $help_file;
|
||||
}
|
||||
|
||||
$content = file_get_contents($help_path);
|
||||
|
||||
$html = \Friendica\Content\Text\Markdown::convert($content, false);
|
||||
|
||||
$html = str_replace('code>', 'key>', $html);
|
||||
|
||||
return $html;
|
||||
} else {
|
||||
$t = get_markup_template('settings.tpl', 'addon/advancedcontentfilter/');
|
||||
return replace_macros($t, [
|
||||
'$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' => 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()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Common functions
|
||||
*/
|
||||
function advancedcontentfilter_build_fields($data)
|
||||
{
|
||||
$fields = [];
|
||||
|
||||
if (!empty($data['name'])) {
|
||||
$fields['name'] = $data['name'];
|
||||
}
|
||||
|
||||
if (!empty($data['expression'])) {
|
||||
$allowed_keys = [
|
||||
'author_id', 'author_link', 'author_name', 'author_avatar',
|
||||
'owner_id', 'owner_link', 'owner_name', 'owner_avatar',
|
||||
'contact_id', 'uid', 'id', 'parent', 'uri',
|
||||
'thr_parent', 'parent_uri',
|
||||
'content_warning',
|
||||
'commented', 'created', 'edited', 'received',
|
||||
'verb', 'object_type', 'postopts', 'plink', 'guid', 'wall', 'private', 'starred',
|
||||
'title', 'body',
|
||||
'file', 'event_id', 'location', 'coord', 'app', 'attach',
|
||||
'rendered_hash', 'rendered_html', 'object',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
|
||||
'item_id', 'item_network', 'author_thumb', 'owner_thumb',
|
||||
'network', 'url', 'name', 'writable', 'self',
|
||||
'cid', 'alias',
|
||||
'event_created', 'event_edited', 'event_start', 'event_finish', 'event_summary',
|
||||
'event_desc', 'event_location', 'event_type', 'event_nofinish', 'event_adjust', 'event_ignore',
|
||||
'children', 'pagedrop', 'tags', 'hashtags', 'mentions',
|
||||
];
|
||||
|
||||
$expressionLanguage = new ExpressionLanguage\ExpressionLanguage();
|
||||
|
||||
$parsedExpression = $expressionLanguage->parse($data['expression'], $allowed_keys);
|
||||
|
||||
$serialized = serialize($parsedExpression->getNodes());
|
||||
|
||||
$fields['expression'] = $data['expression'];
|
||||
$fields['serialized'] = $serialized;
|
||||
}
|
||||
|
||||
if (isset($data['active'])) {
|
||||
$fields['active'] = intval($data['active']);
|
||||
} else {
|
||||
$fields['active'] = 1;
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/*
|
||||
* API
|
||||
*/
|
||||
|
||||
function advancedcontentfilter_get_rules()
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
|
||||
}
|
||||
|
||||
$rules = dba::inArray(dba::select('advancedcontentfilter_rules', [], ['uid' => local_user()]));
|
||||
|
||||
return json_encode($rules);
|
||||
}
|
||||
|
||||
function advancedcontentfilter_get_rules_id(ServerRequestInterface $request, ResponseInterface $response, $args)
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
|
||||
}
|
||||
|
||||
$rule = dba::selectFirst('advancedcontentfilter_rules', [], ['id' => $args['id'], 'uid' => local_user()]);
|
||||
|
||||
return json_encode($rule);
|
||||
}
|
||||
|
||||
function advancedcontentfilter_post_rules(ServerRequestInterface $request)
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
|
||||
}
|
||||
|
||||
if (!check_form_security_token()) {
|
||||
throw new HTTPException\BadRequestException(L10n::t('Invalid form security token, please refresh the page.'));
|
||||
}
|
||||
|
||||
$data = json_decode($request->getBody(), true);
|
||||
|
||||
try {
|
||||
$fields = advancedcontentfilter_build_fields($data);
|
||||
} catch (Exception $e) {
|
||||
throw new HTTPException\BadRequestException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
if (empty($fields['name']) || empty($fields['expression'])) {
|
||||
throw new HTTPException\BadRequestException(L10n::t('The rule name and expression are required.'));
|
||||
}
|
||||
|
||||
$fields['uid'] = local_user();
|
||||
$fields['created'] = \Friendica\Util\DateTimeFormat::utcNow();
|
||||
|
||||
if (!dba::insert('advancedcontentfilter_rules', $fields)) {
|
||||
throw new HTTPException\ServiceUnavaiableException(dba::errorMessage());
|
||||
}
|
||||
|
||||
$rule = dba::selectFirst('advancedcontentfilter_rules', [], ['id' => dba::lastInsertId()]);
|
||||
|
||||
return json_encode(['message' => L10n::t('Rule successfully added'), 'rule' => $rule]);
|
||||
}
|
||||
|
||||
function advancedcontentfilter_put_rules_id(ServerRequestInterface $request, ResponseInterface $response, $args)
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
|
||||
}
|
||||
|
||||
if (!check_form_security_token()) {
|
||||
throw new HTTPException\BadRequestException(L10n::t('Invalid form security token, please refresh the page.'));
|
||||
}
|
||||
|
||||
if (!dba::exists('advancedcontentfilter_rules', ['id' => $args['id'], 'uid' => local_user()])) {
|
||||
throw new HTTPException\NotFoundException(L10n::t('Rule doesn\'t exist or doesn\'t belong to you.'));
|
||||
}
|
||||
|
||||
$data = json_decode($request->getBody(), true);
|
||||
|
||||
try {
|
||||
$fields = advancedcontentfilter_build_fields($data);
|
||||
} catch (Exception $e) {
|
||||
throw new HTTPException\BadRequestException($e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
if (!dba::update('advancedcontentfilter_rules', $fields, ['id' => $args['id']])) {
|
||||
throw new HTTPException\ServiceUnavaiableException(dba::errorMessage());
|
||||
}
|
||||
|
||||
return json_encode(['message' => L10n::t('Rule successfully updated')]);
|
||||
}
|
||||
|
||||
function advancedcontentfilter_delete_rules_id(ServerRequestInterface $request, ResponseInterface $response, $args)
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
|
||||
}
|
||||
|
||||
if (!check_form_security_token()) {
|
||||
throw new HTTPException\BadRequestException(L10n::t('Invalid form security token, please refresh the page.'));
|
||||
}
|
||||
|
||||
if (!dba::exists('advancedcontentfilter_rules', ['id' => $args['id'], 'uid' => local_user()])) {
|
||||
throw new HTTPException\NotFoundException(L10n::t('Rule doesn\'t exist or doesn\'t belong to you.'));
|
||||
}
|
||||
|
||||
if (!dba::delete('advancedcontentfilter_rules', ['id' => $args['id']])) {
|
||||
throw new HTTPException\ServiceUnavaiableException(dba::errorMessage());
|
||||
}
|
||||
|
||||
return json_encode(['message' => L10n::t('Rule successfully deleted')]);
|
||||
}
|
||||
|
||||
function advancedcontentfilter_get_variables_guid(ServerRequestInterface $request, ResponseInterface $response, $args)
|
||||
{
|
||||
if (!local_user()) {
|
||||
throw new HTTPException\UnauthorizedException(L10n::t('You must be logged in to use this method'));
|
||||
}
|
||||
|
||||
if (!isset($args['guid'])) {
|
||||
throw new HTTPException\BadRequestException(L10n::t('Missing argument: guid.'));
|
||||
}
|
||||
|
||||
$item = dba::fetch_first(item_query() . " AND `item`.`guid` = ? AND (`item`.`uid` = ? OR `item`.`uid` = 0) ORDER BY `item`.`uid` DESC", $args['guid'], local_user());
|
||||
|
||||
if (!\Friendica\Database\DBM::is_result($item)) {
|
||||
throw new HTTPException\NotFoundException(L10n::t('Unknown post with guid: %s', $args['guid']));
|
||||
}
|
||||
|
||||
$tags = \Friendica\Model\Term::populateTagsFromItem($item);
|
||||
|
||||
$item['tags'] = $tags['tags'];
|
||||
$item['hashtags'] = $tags['hashtags'];
|
||||
$item['mentions'] = $tags['mentions'];
|
||||
|
||||
$return = [];
|
||||
foreach ($item as $key => $value) {
|
||||
$return[str_replace('-', '_', $key)] = $value;
|
||||
}
|
||||
|
||||
return str_replace('\\\'', '\'', var_export($return, true));
|
||||
}
|
514
advancedcontentfilter/doc/advancedcontentfilter.md
Normal file
514
advancedcontentfilter/doc/advancedcontentfilter.md
Normal file
|
@ -0,0 +1,514 @@
|
|||
<style>
|
||||
.advancedcontentfilter-content-wrapper {
|
||||
min-height: calc(100vh - 150px);
|
||||
padding: 15px;
|
||||
padding-bottom: 20px;
|
||||
margin-bottom: 20px;
|
||||
border: none;
|
||||
/*background-color: #fff;*/
|
||||
background-color: rgba(255,255,255,0.95);
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
/*overflow: hidden;*/
|
||||
color: #555;
|
||||
box-shadow: 0 0 3px #dadada;
|
||||
-webkit-box-shadow: 0 0 3px #dadada;
|
||||
-moz-box-shadow: 0 0 3px #dadada;
|
||||
}
|
||||
</style>
|
||||
|
||||
<a href="advancedcontentfilter">🔙 Back to Addon Settings</a>
|
||||
|
||||
# Advanced Content Filter Help
|
||||
|
||||
The advanced Content Filter uses Symfony's Expression Language.
|
||||
This help page includes a summary of [the Symfony's Expression Language documentation page.](https://symfony.com/doc/current/components/expression_language/syntax.html)
|
||||
|
||||
## Basics
|
||||
|
||||
The advanced content filter matches each post that is about to be displayed against each enabled rule you set.
|
||||
|
||||
A rule is a boolean expression that should return either `true` or `false` depending on post variables.
|
||||
|
||||
If the expression using a post variables returns `true`, the post will be collapsed and the matching rule name will be displayed above the collapsed content.
|
||||
|
||||
A post will be collapsed if at least one rule matches, but all matching rule names will be displayed above the collapsed content.
|
||||
|
||||
## Expression Syntax
|
||||
|
||||
### Supported Literals
|
||||
|
||||
- **strings** - single and double quotes (e.g. `'hello'`).
|
||||
- **numbers** - e.g. `103`.
|
||||
- **arrays** - using JSON-like notation (e.g. `[1, 2]`).
|
||||
- **hashes** - using JSON-like notation (e.g. `{ foo: 'bar' }`).
|
||||
- **booleans** - `true` and `false`.
|
||||
- **null** - `null`.
|
||||
|
||||
A backslash (``\``) must be escaped by 4 backslashes (``\\\\``) in a string
|
||||
and 8 backslashes (``\\\\\\\\``) in a regex::
|
||||
|
||||
`"a\\\\b" matches "/^a\\\\\\\\b$/"`
|
||||
|
||||
Control characters (e.g. ``\n``) in expressions are replaced with
|
||||
whitespace. To avoid this, escape the sequence with a single backslash
|
||||
(e.g. ``\\n``).
|
||||
|
||||
### Supported Operators
|
||||
|
||||
The component comes with a lot of operators:
|
||||
|
||||
#### Arithmetic Operators
|
||||
|
||||
* ``+`` (addition)
|
||||
* ``-`` (subtraction)
|
||||
* ``*`` (multiplication)
|
||||
* ``/`` (division)
|
||||
* ``%`` (modulus)
|
||||
* ``**`` (pow)
|
||||
|
||||
#### Bitwise Operators
|
||||
|
||||
* ``&`` (and)
|
||||
* ``|`` (or)
|
||||
* ``^`` (xor)
|
||||
|
||||
#### Comparison Operators
|
||||
|
||||
* ``==`` (equal)
|
||||
* ``===`` (identical)
|
||||
* ``!=`` (not equal)
|
||||
* ``!==`` (not identical)
|
||||
* ``<`` (less than)
|
||||
* ``>`` (greater than)
|
||||
* ``<=`` (less than or equal to)
|
||||
* ``>=`` (greater than or equal to)
|
||||
* ``matches`` (regex match)
|
||||
|
||||
To test if a string does *not* match a regex, use the logical ``not``
|
||||
operator in combination with the ``matches`` operator:
|
||||
|
||||
'not ("foo" matches "/bar/")'
|
||||
|
||||
You must use parenthesis because the unary operator ``not`` has precedence
|
||||
over the binary operator ``matches``.
|
||||
|
||||
#### Logical Operators
|
||||
|
||||
* ``not`` or ``!``
|
||||
* ``and`` or ``&&``
|
||||
* ``or`` or ``||``
|
||||
|
||||
#### String Operators
|
||||
|
||||
* ``~`` (concatenation)
|
||||
|
||||
For example: ``firstName ~ " " ~ lastName``
|
||||
|
||||
#### Array Operators
|
||||
|
||||
* ``in`` (contain)
|
||||
* ``not in`` (does not contain)
|
||||
|
||||
For example: ``user.group in ["human_resources", "marketing"]``
|
||||
|
||||
#### Numeric Operators
|
||||
|
||||
* ``..`` (range)
|
||||
|
||||
For example: ``user.age in 18..45``
|
||||
|
||||
#### Ternary Operators
|
||||
|
||||
* ``foo ? 'yes' : 'no'``
|
||||
* ``foo ?: 'no'`` (equal to ``foo ? foo : 'no'``)
|
||||
* ``foo ? 'yes'`` (equal to ``foo ? 'yes' : ''``)
|
||||
|
||||
### Supported variables
|
||||
|
||||
Here are a sample of the available variables you can use in your expressions.
|
||||
You can also retrieve the variables of a specific post by pasting its URL below the rule list.
|
||||
|
||||
<table class="table-bordered table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Variable</th>
|
||||
<th>Type</th>
|
||||
<th>Sample Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>author-id</th>
|
||||
<td>number</td>
|
||||
<td>6</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>author-link</th>
|
||||
<td>string</td>
|
||||
<td>https://friendica.mrpetovan.com/profile/hypolite</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>author-name</th>
|
||||
<td>string</td>
|
||||
<td>Hypolite Petovan</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>author-avatar</th>
|
||||
<td>string</td>
|
||||
<td>https://friendica.mrpetovan.com/photo/41084997915a94a8c83cc39708500207-5.png</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>owner-id</th>
|
||||
<td>number</td>
|
||||
<td>6</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>owner-link</th>
|
||||
<td>string</td>
|
||||
<td>https://friendica.mrpetovan.com/profile/hypolite</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>owner-name</th>
|
||||
<td>string</td>
|
||||
<td>Hypolite Petovan</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>owner-avatar</th>
|
||||
<td>string</td>
|
||||
<td>https://friendica.mrpetovan.com/photo/41084997915a94a8c83cc39708500207-5.png</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>contact-id</th>
|
||||
<td>number</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>uid</th>
|
||||
<td>number</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>id</th>
|
||||
<td>number</td>
|
||||
<td>791875</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>parent</th>
|
||||
<td>number</td>
|
||||
<td>791875</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>uri</th>
|
||||
<td>string</td>
|
||||
<td>urn:X-dfrn:friendica.mrpetovan.com:1:twit:978740198937907200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>thr-parent</th>
|
||||
<td>string</td>
|
||||
<td>urn:X-dfrn:friendica.mrpetovan.com:1:twit:978740198937907200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>parent-uri</th>
|
||||
<td>string</td>
|
||||
<td>urn:X-dfrn:friendica.mrpetovan.com:1:twit:978740198937907200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>content-warning</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>commented</th>
|
||||
<td>date</td>
|
||||
<td>2018-03-27 21:10:18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>created</th>
|
||||
<td>date</td>
|
||||
<td>2018-03-27 21:10:18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>edited</th>
|
||||
<td>date</td>
|
||||
<td>2018-03-27 21:10:18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>received</th>
|
||||
<td>date</td>
|
||||
<td>2018-03-27 21:10:18</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>verb</th>
|
||||
<td>string</td>
|
||||
<td>http://activitystrea.ms/schema/1.0/post</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>object-type</th>
|
||||
<td>string</td>
|
||||
<td>http://activitystrea.ms/schema/1.0/bookmark</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>postopts</th>
|
||||
<td>string</td>
|
||||
<td>twitter&lang=pidgin;0.24032407407407:english;0.225:french;0.18055555555556</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>plink</th>
|
||||
<td>string</td>
|
||||
<td>https://friendica.mrpetovan.com/display/735a2029995abab33a5c006052376776</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>guid</th>
|
||||
<td>string</td>
|
||||
<td>735a2029995abab33a5c006052376776</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>wall</th>
|
||||
<td>boolean</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>private</th>
|
||||
<td>boolean</td>
|
||||
<td>0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>starred</th>
|
||||
<td>boolean</td>
|
||||
<td>0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>title</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>body</th>
|
||||
<td>string</td>
|
||||
<td>Over-compensation #[url=https://friendica.mrpetovan.com/search?tag=Street]Street[/url] #[url=https://friendica.mrpetovan.com/search?tag=Night]Night[/url] #[url=https://friendica.mrpetovan.com/search?tag=CarLights]CarLights[/url] #[url=https://friendica.mrpetovan.com/search?tag=Jeep]Jeep[/url] #[url=https://friendica.mrpetovan.com/search?tag=NoPeople]NoPeople[/url] #[url=https://friendica.mrpetovan.com/search?tag=Close]Close[/url]-up
|
||||
[attachment type='link' url='https://www.eyeem.com/p/120800309' title='Over-compensation Street Night Car Lights Jeep No | EyeEm' image='https://cdn.eyeem.com/thumb/b2f019738cbeef06e2f8c9517c6286a8adcd3a00-1522184820641/640/480']Photo by @[url=https://twitter.com/MrPetovan]MrPetovan[/url][/attachment]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>file</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-id</th>
|
||||
<td>number</td>
|
||||
<td>null
|
||||
<tr>
|
||||
<th>location</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>coord</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>app</th>
|
||||
<td>string</td>
|
||||
<td>EyeEm</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>attach</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>rendered-hash</th>
|
||||
<td>string</td>
|
||||
<td>b70abdea8b362dc5dcf63e1b2836ad89</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>rendered-html</th>
|
||||
<td>string</td>
|
||||
<td>
|
||||
Over-compensation #<a href="https://friendica.mrpetovan.com/search?tag=Street" class="tag" title="Street">Street</a> #<a href="https://friendica.mrpetovan.com/search?tag=Night" class="tag" title="Night">Night</a> #<a href="https://friendica.mrpetovan.com/search?tag=CarLights" class="tag" title="CarLights">CarLights</a> #<a href="https://friendica.mrpetovan.com/search?tag=Jeep" class="tag" title="Jeep">Jeep</a> #<a href="https://friendica.mrpetovan.com/search?tag=NoPeople" class="tag" title="NoPeople">NoPeople</a> #<a href="https://friendica.mrpetovan.com/search?tag=Close" class="tag" title="Close">Close</a>-up <div class="type-link"><a href="https://www.eyeem.com/p/120800309" target="_blank"><img src="https://friendica.mrpetovan.com/proxy/bb/aHR0cHM6Ly9jZG4uZXllZW0uY29tL3RodW1iL2IyZjAxOTczOGNiZWVmMDZlMmY4Yzk1MTdjNjI4NmE4YWRjZDNhMDAtMTUyMjE4NDgyMDY0MS82NDAvNDgw" alt="" title="Over-compensation Street Night Car Lights Jeep No | EyeEm" class="attachment-image"></a><br><h4><a href="https://www.eyeem.com/p/120800309">Over-compensation Street Night Car Lights Jeep No | EyeEm</a></h4><blockquote>Photo by @<a href="https://twitter.com/MrPetovan" class="userinfo mention" title="MrPetovan">MrPetovan</a></blockquote><sup><a href="https://www.eyeem.com/p/120800309">www.eyeem.com</a></sup></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>object</th>
|
||||
<td>string</td>
|
||||
<td>{"created_at":"Tue Mar 27 21:07:02 +0000 2018","id":978740198937907200,"id_str":"978740198937907200","full_text":"Over-compensation #Street #Night #CarLights #Jeep #NoPeople #Close-up https:\/\/t.co\/7w4ua13QA7","truncated":false,"display_text_range":[0,93],"entities":{"hashtags":[{"text":"Street","indices":[18,25]},{"text":"Night","indices":[26,32]},{"text":"CarLights","indices":[33,43]},{"text":"Jeep","indices":[44,49]},{"text":"NoPeople","indices":[50,59]},{"text":"Close","indices":[60,66]}],"symbols":[],"user_mentions":[],"urls":[{"url":"https:\/\/t.co\/7w4ua13QA7","expanded_url":"http:\/\/EyeEm.com\/p\/120800309","display_url":"EyeEm.com\/p\/120800309","indices":[70,93]}]},"source":"<a href=\"http:\/\/www.eyeem.com\" rel=\"nofollow\">EyeEm<\/a>","in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":403748896,"id_str":"403748896","name":"\ud83d\udc30yp\ud83e\udd5ali\u271d\ufe0fe Pet\ud83e\udd5avan","screen_name":"MrPetovan","location":"NYC","description":"White male form of milquetoast. Avatar by @DearMsDear inspired by @TSG_LAB.\n\nFriendica\/Diaspora\/Mastodon: hypolite@friendica.mrpetovan.com","url":"https:\/\/t.co\/PcARi5OhQO","entities":{"url":{"urls":[{"url":"https:\/\/t.co\/PcARi5OhQO","expanded_url":"https:\/\/mrpetovan.com","display_url":"mrpetovan.com","indices":[0,23]}]},"description":{"urls":[]}},"protected":false,"followers_count":182,"friends_count":146,"listed_count":15,"created_at":"Wed Nov 02 23:13:14 +0000 2011","favourites_count":45826,"utc_offset":-14400,"time_zone":"Eastern Time (US & Canada)","geo_enabled":false,"verified":false,"statuses_count":15554,"lang":"en","contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"000000","profile_background_image_url":"http:\/\/pbs.twimg.com\/profile_background_images\/370213187\/fond_twitter_mrpetovan.png","profile_background_image_url_https":"https:\/\/pbs.twimg.com\/profile_background_images\/370213187\/fond_twitter_mrpetovan.png","profile_background_tile":false,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/968008546322395136\/6qLCiu0o_normal.jpg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/968008546322395136\/6qLCiu0o_normal.jpg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/403748896\/1464321684","profile_link_color":"0084B4","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"000000","profile_use_background_image":true,"has_extended_profile":true,"default_profile":false,"default_profile_image":false,"following":false,"follow_request_sent":false,"notifications":false,"translator_type":"none"},"geo":null,"coordinates":null,"place":null,"contributors":null,"is_quote_status":false,"retweet_count":0,"favorite_count":0,"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"en"}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>allow_cid</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>allow_gid</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>deny_cid</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>deny_gid</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>item_id</th>
|
||||
<td>number</td>
|
||||
<td>791875</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>item_network</th>
|
||||
<td>string</td>
|
||||
<td>dfrn</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>author-thumb</th>
|
||||
<td>string</td>
|
||||
<td>https://friendica.mrpetovan.com/photo/0cb3d7231eb751139d7d309c7c686c49-5.png?ts=1522941604</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>owner-thumb</th>
|
||||
<td>string</td>
|
||||
<td>https://friendica.mrpetovan.com/photo/0cb3d7231eb751139d7d309c7c686c49-5.png?ts=1522941604</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>network</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>url</th>
|
||||
<td>string</td>
|
||||
<td>https://friendica.mrpetovan.com/profile/hypolite</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>name</th>
|
||||
<td>string</td>
|
||||
<td>Hypolite Petovan</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>writable</th>
|
||||
<td>boolean</td>
|
||||
<td>0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>self</th>
|
||||
<td>boolean</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>cid</th>
|
||||
<td>number</td>
|
||||
<td>1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>alias</th>
|
||||
<td>string</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-created</th>
|
||||
<td>date</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-edited</th>
|
||||
<td>date</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-start</th>
|
||||
<td>date</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-finish</th>
|
||||
<td>date</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-summary</th>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-desc</th>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-location</th>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-type</th>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-nofinish</th>
|
||||
<td>string</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-adjust</th>
|
||||
<td>boolean</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>event-ignore</th>
|
||||
<td>boolean</td>
|
||||
<td>null</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>pagedrop</th>
|
||||
<td>string</td>
|
||||
<td>true</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>tags</th>
|
||||
<td>list</td>
|
||||
<td>
|
||||
<ol start="0">
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Street" target="_blank">street</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Night" target="_blank">night</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=CarLights" target="_blank">carlights</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Jeep" target="_blank">jeep</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=NoPeople" target="_blank">nopeople</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Close" target="_blank">close</a></li>
|
||||
<li>@<a href="https://twitter.com/MrPetovan" target="_blank">mrpetovan</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Close-up" target="_blank">close-up</a></li>
|
||||
</ol>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>hashtags</th>
|
||||
<td>list</td>
|
||||
<td>
|
||||
<ol start="0">
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Street" target="_blank">street</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Night" target="_blank">night</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=CarLights" target="_blank">carlights</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Jeep" target="_blank">jeep</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=NoPeople" target="_blank">nopeople</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Close" target="_blank">close</a></li>
|
||||
<li>#<a href="https://friendica.mrpetovan.com/search?tag=Close-up" target="_blank">close-up</a></li>
|
||||
</ol>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>mentions</th>
|
||||
<td>string</td>
|
||||
<td>
|
||||
<ol start="0">
|
||||
<li>@<a href="https://twitter.com/MrPetovan" target="_blank">mrpetovan</a></li>
|
||||
</ol>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
163
advancedcontentfilter/lang/C/messages.po
Normal file
163
advancedcontentfilter/lang/C/messages.po
Normal file
|
@ -0,0 +1,163 @@
|
|||
# ADDON advancedcontentfilter
|
||||
# Copyright (C)
|
||||
# This file is distributed under the same license as the Friendica advancedcontentfilter addon package.
|
||||
#
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-04-17 04:04+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: advancedcontentfilter.php:134
|
||||
#, php-format
|
||||
msgid "Filtered by rule: %s"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:147 advancedcontentfilter.php:204
|
||||
msgid "Advanced Content Filter"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:203
|
||||
msgid "Back to Addon Settings"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:205
|
||||
msgid "Add a Rule"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:206
|
||||
msgid "Help"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:207
|
||||
msgid ""
|
||||
"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>."
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:208
|
||||
msgid "Your rules"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:209
|
||||
msgid ""
|
||||
"You have no rules yet! Start adding one by clicking on the button above next "
|
||||
"to the title."
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:210
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:211
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:212
|
||||
msgid "Disable this rule"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:213
|
||||
msgid "Enable this rule"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:214
|
||||
msgid "Edit this rule"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:215
|
||||
msgid "Edit the rule"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:216
|
||||
msgid "Save this rule"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:217
|
||||
msgid "Delete this rule"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:218
|
||||
msgid "Rule"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:219
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:220
|
||||
msgid "Add new rule"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:221
|
||||
msgid "Rule Name"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:222
|
||||
msgid "Rule Expression"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:223
|
||||
msgid ""
|
||||
"<p>Examples:</p><ul><li><pre>author_link == 'https://friendica.mrpetovan.com/"
|
||||
"profile/hypolite'</pre></li><li>tags</li></ul>"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:224
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:290 advancedcontentfilter.php:301
|
||||
#: advancedcontentfilter.php:312 advancedcontentfilter.php:346
|
||||
#: advancedcontentfilter.php:375 advancedcontentfilter.php:396
|
||||
msgid "You must be logged in to use this method"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:316 advancedcontentfilter.php:350
|
||||
#: advancedcontentfilter.php:379
|
||||
msgid "Invalid form security token, please refresh the page."
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:328
|
||||
msgid "The rule name and expression are required."
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:340
|
||||
msgid "Rule successfully added"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:354 advancedcontentfilter.php:383
|
||||
msgid "Rule doesn't exist or doesn't belong to you."
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:369
|
||||
msgid "Rule successfully updated"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:390
|
||||
msgid "Rule successfully deleted"
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:400
|
||||
msgid "Missing argument: guid."
|
||||
msgstr ""
|
||||
|
||||
#: advancedcontentfilter.php:406
|
||||
#, php-format
|
||||
msgid "Unknown post with guid: %s"
|
||||
msgstr ""
|
||||
|
||||
#: src/middlewares.php:28
|
||||
msgid "Method not found"
|
||||
msgstr ""
|
30
advancedcontentfilter/src/middlewares.php
Normal file
30
advancedcontentfilter/src/middlewares.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
$container = $slim->getContainer();
|
||||
|
||||
// Error handler based off https://stackoverflow.com/a/48135009/757392
|
||||
$container['errorHandler'] = function () {
|
||||
return function(Psr\Http\Message\RequestInterface $request, Psr\Http\Message\ResponseInterface $response, Exception $exception)
|
||||
{
|
||||
$responseCode = 500;
|
||||
|
||||
if (is_a($exception, 'Friendica\Network\HTTPException')) {
|
||||
$responseCode = $exception->httpcode;
|
||||
}
|
||||
|
||||
$errors['message'] = $exception->getMessage();
|
||||
|
||||
$errors['responseCode'] = $responseCode;
|
||||
|
||||
return $response
|
||||
->withStatus($responseCode)
|
||||
->withJson($errors);
|
||||
};
|
||||
};
|
||||
|
||||
$container['notFoundHandler'] = function () {
|
||||
return function ()
|
||||
{
|
||||
throw new \Friendica\Network\HTTPException\NotFoundException(L10n::t('Method not found'));
|
||||
};
|
||||
};
|
22
advancedcontentfilter/src/routes.php
Normal file
22
advancedcontentfilter/src/routes.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
// Routes
|
||||
|
||||
/* @var $slim Slim\App */
|
||||
$slim->group('/advancedcontentfilter/api', function () {
|
||||
/* @var $this Slim\App */
|
||||
$this->group('/rules', function () {
|
||||
/* @var $this Slim\App */
|
||||
$this->get('', 'advancedcontentfilter_get_rules');
|
||||
$this->post('', 'advancedcontentfilter_post_rules');
|
||||
|
||||
$this->get('/{id}', 'advancedcontentfilter_get_rules_id');
|
||||
$this->put('/{id}', 'advancedcontentfilter_put_rules_id');
|
||||
$this->delete('/{id}', 'advancedcontentfilter_delete_rules_id');
|
||||
});
|
||||
|
||||
$this->group('/variables', function () {
|
||||
/* @var $this Slim\App */
|
||||
$this->get('/{guid}', 'advancedcontentfilter_get_variables_guid');
|
||||
});
|
||||
});
|
99
advancedcontentfilter/templates/settings.tpl
Normal file
99
advancedcontentfilter/templates/settings.tpl
Normal file
|
@ -0,0 +1,99 @@
|
|||
<div id="adminpage">
|
||||
<style>[v-cloak] { display: none; }</style>
|
||||
<div id="rules">
|
||||
<p><a href="settings/addon">🔙 {{$backtosettings}}</a></p>
|
||||
<h1>
|
||||
{{$title}}
|
||||
|
||||
<a href="{{$baseurl}}/advancedcontentfilter/help" class="btn btn-default btn-sm" title="{{$help}}">
|
||||
<i class="fa fa-question fa-2x" aria-hidden="true"></i>
|
||||
</a>
|
||||
</h1>
|
||||
<div>{{$advanced_content_filter_intro}}</div>
|
||||
<h2>
|
||||
{{$your_rules}}
|
||||
<button class="btn btn-primary btn-sm" title="{{$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}}
|
||||
</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>
|
||||
<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>
|
||||
|
||||
<button type="button" class="btn btn-xs btn-primary" v-on:click="editRule(rule)" aria-label="{{$edit_this_rule}}" title="{{$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}}">
|
||||
<i class="fa fa-trash-o" aria-hidden="true"></i>
|
||||
</button>
|
||||
</p>
|
||||
<h3 class="list-group-item-heading">
|
||||
{{$rule}} #{{ rule.id }}: {{ rule.name }}
|
||||
</h3>
|
||||
<pre class="list-group-item-text" v-if="rule.expression">{{ rule.expression }}</pre>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="modal fade" ref="vuemodal" tabindex="-1" role="dialog" v-cloak>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
{{if current_theme() == 'frio'}}
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="{{$close}}" @click="showModal = false"><span aria-hidden="true">×</span></button>
|
||||
{{/if}}
|
||||
<h3 v-if="rule.id">{{$edit_the_rule}} "{{ rule.name }}"</h3>
|
||||
<h3 v-if="!rule.id">{{$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">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input class="form-control" placeholder="{{$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>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<form class="form-inline" v-on:submit.prevent="showVariables()">
|
||||
<fieldset>
|
||||
<legend>Show post variables</legend>
|
||||
<div class="form-group" style="width: 50%">
|
||||
<label for="itemUrl" class="sr-only">Post URL or item guid</label>
|
||||
<input class="form-control" id="itemUrl" placeholder="Post URL or item guid" v-model="itemUrl" style="width: 100%">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Show Variables</button>
|
||||
</fieldset>
|
||||
</form>
|
||||
<pre>
|
||||
{{ itemJson }}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<script> var existingRules = {{$rules}};</script>
|
||||
|
||||
<!-- JS -->
|
||||
<script src="{{$baseurl}}/addon/advancedcontentfilter/vendor/asset/vue/dist/vue.min.js"></script>
|
||||
<script src="{{$baseurl}}/addon/advancedcontentfilter/vendor/asset/vue-resource/dist/vue-resource.min.js"></script>
|
||||
<script src="{{$baseurl}}/addon/advancedcontentfilter/advancedcontentfilter.js"></script>
|
||||
</div>
|
Loading…
Reference in a new issue