Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
e50c0efcdf
826 changed files with 24501 additions and 2626 deletions
|
|
@ -1,9 +1,9 @@
|
|||
<?php
|
||||
require_once("bbcode.php");
|
||||
require_once("datetime.php");
|
||||
require_once("conversation.php");
|
||||
require_once("oauth.php");
|
||||
require_once("html2plain.php");
|
||||
require_once("include/bbcode.php");
|
||||
require_once("include/datetime.php");
|
||||
require_once("include/conversation.php");
|
||||
require_once("include/oauth.php");
|
||||
require_once("include/html2plain.php");
|
||||
/*
|
||||
* Twitter-Like API
|
||||
*
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ require_once('include/security.php');
|
|||
require_once('include/datetime.php');
|
||||
|
||||
function nuke_session() {
|
||||
new_cookie(0); // make sure cookie is deleted on browser close, as a security measure
|
||||
|
||||
unset($_SESSION['authenticated']);
|
||||
unset($_SESSION['uid']);
|
||||
unset($_SESSION['visitor_id']);
|
||||
|
|
@ -187,18 +189,10 @@ else {
|
|||
// (i.e. expire when the browser is closed), even when there's a time expiration
|
||||
// on the cookie
|
||||
if($_POST['remember']) {
|
||||
$old_sid = session_id();
|
||||
session_set_cookie_params('31449600'); // one year
|
||||
session_regenerate_id(false);
|
||||
|
||||
q("UPDATE session SET sid = '%s' WHERE sid = '%s'", dbesc(session_id()), dbesc($old_sid));
|
||||
new_cookie(31449600); // one year
|
||||
}
|
||||
else {
|
||||
$old_sid = session_id();
|
||||
session_set_cookie_params('0');
|
||||
session_regenerate_id(false);
|
||||
|
||||
q("UPDATE session SET sid = '%s' WHERE sid = '%s'", dbesc(session_id()), dbesc($old_sid));
|
||||
new_cookie(0); // 0 means delete on browser exit
|
||||
}
|
||||
|
||||
// if we haven't failed up this point, log them in.
|
||||
|
|
@ -208,4 +202,10 @@ else {
|
|||
}
|
||||
}
|
||||
|
||||
function new_cookie($time) {
|
||||
$old_sid = session_id();
|
||||
session_set_cookie_params("$time");
|
||||
session_regenerate_id(false);
|
||||
|
||||
q("UPDATE session SET sid = '%s' WHERE sid = '%s'", dbesc(session_id()), dbesc($old_sid));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,7 +369,7 @@ if(!function_exists('conversation')) {
|
|||
function conversation(&$a, $items, $mode, $update, $preview = false) {
|
||||
|
||||
|
||||
require_once('bbcode.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
$ssl_state = ((local_user()) ? true : false);
|
||||
|
||||
|
|
@ -581,33 +581,54 @@ function conversation(&$a, $items, $mode, $update, $preview = false) {
|
|||
|
||||
list($categories, $folders) = get_cats_and_terms($item);
|
||||
|
||||
if($a->theme['template_engine'] === 'internal') {
|
||||
$profile_name_e = template_escape($profile_name);
|
||||
$item['title_e'] = template_escape($item['title']);
|
||||
$body_e = template_escape($body);
|
||||
$tags_e = template_escape($tags);
|
||||
$hashtags_e = template_escape($hashtags);
|
||||
$mentions_e = template_escape($mentions);
|
||||
$location_e = template_escape($location);
|
||||
$owner_name_e = template_escape($owner_name);
|
||||
}
|
||||
else {
|
||||
$profile_name_e = $profile_name;
|
||||
$item['title_e'] = $item['title'];
|
||||
$body_e = $body;
|
||||
$tags_e = $tags;
|
||||
$hashtags_e = $hashtags;
|
||||
$mentions_e = $mentions;
|
||||
$location_e = $location;
|
||||
$owner_name_e = $owner_name;
|
||||
}
|
||||
|
||||
$tmp_item = array(
|
||||
'template' => $tpl,
|
||||
'id' => (($preview) ? 'P0' : $item['item_id']),
|
||||
'linktitle' => sprintf( t('View %s\'s profile @ %s'), $profile_name, ((strlen($item['author-link'])) ? $item['author-link'] : $item['url'])),
|
||||
'profile_url' => $profile_link,
|
||||
'item_photo_menu' => item_photo_menu($item),
|
||||
'name' => template_escape($profile_name),
|
||||
'name' => $profile_name_e,
|
||||
'sparkle' => $sparkle,
|
||||
'lock' => $lock,
|
||||
'thumb' => $profile_avatar,
|
||||
'title' => template_escape($item['title']),
|
||||
'body' => template_escape($body),
|
||||
'tags' => template_escape($tags),
|
||||
'hashtags' => template_escape($hashtags),
|
||||
'mentions' => template_escape($mentions),
|
||||
'title' => $item['title_e'],
|
||||
'body' => $body_e,
|
||||
'tags' => $tags_e,
|
||||
'hashtags' => $hashtags_e,
|
||||
'mentions' => $mentions_e,
|
||||
'txt_cats' => t('Categories:'),
|
||||
'txt_folders' => t('Filed under:'),
|
||||
'has_cats' => ((count($categories)) ? 'true' : ''),
|
||||
'has_folders' => ((count($folders)) ? 'true' : ''),
|
||||
'categories' => $categories,
|
||||
'folders' => $folders,
|
||||
'text' => strip_tags(template_escape($body)),
|
||||
'text' => strip_tags($body_e),
|
||||
'localtime' => datetime_convert('UTC', date_default_timezone_get(), $item['created'], 'r'),
|
||||
'ago' => (($item['app']) ? sprintf( t('%s from %s'),relative_date($item['created']),$item['app']) : relative_date($item['created'])),
|
||||
'location' => template_escape($location),
|
||||
'location' => $location_e,
|
||||
'indent' => '',
|
||||
'owner_name' => template_escape($owner_name),
|
||||
'owner_name' => $owner_name_e,
|
||||
'owner_url' => $owner_url,
|
||||
'owner_photo' => $owner_photo,
|
||||
'plink' => get_plink($item),
|
||||
|
|
@ -856,12 +877,27 @@ function format_like($cnt,$arr,$type,$id) {
|
|||
if($cnt == 1)
|
||||
$o .= (($type === 'like') ? sprintf( t('%s likes this.'), $arr[0]) : sprintf( t('%s doesn\'t like this.'), $arr[0])) . EOL ;
|
||||
else {
|
||||
$spanatts = 'class="fakelink" onclick="openClose(\'' . $type . 'list-' . $id . '\');"';
|
||||
$o .= (($type === 'like') ?
|
||||
sprintf( t('<span %1$s>%2$d people</span> like this.'), $spanatts, $cnt)
|
||||
:
|
||||
sprintf( t('<span %1$s>%2$d people</span> don\'t like this.'), $spanatts, $cnt) );
|
||||
$o .= EOL ;
|
||||
//$spanatts = 'class="fakelink" onclick="openClose(\'' . $type . 'list-' . $id . '\');"';
|
||||
switch($type) {
|
||||
case 'like':
|
||||
// $phrase = sprintf( t('<span %1$s>%2$d people</span> like this.'), $spanatts, $cnt);
|
||||
$mood = t('like this');
|
||||
break;
|
||||
case 'dislike':
|
||||
// $phrase = sprintf( t('<span %1$s>%2$d people</span> don\'t like this.'), $spanatts, $cnt);
|
||||
$mood = t('don\'t like this');
|
||||
break;
|
||||
}
|
||||
$tpl = get_markup_template("voting_fakelink.tpl");
|
||||
$phrase = replace_macros($tpl, array(
|
||||
'$vote_id' => $type . 'list-' . $id,
|
||||
'$count' => $cnt,
|
||||
'$people' => t('people'),
|
||||
'$vote_mood' => $mood
|
||||
));
|
||||
$o .= $phrase;
|
||||
// $o .= EOL ;
|
||||
|
||||
$total = count($arr);
|
||||
if($total >= MAX_LIKERS)
|
||||
$arr = array_slice($arr, 0, MAX_LIKERS - 1);
|
||||
|
|
@ -885,7 +921,7 @@ function status_editor($a,$x, $notes_cid = 0, $popup=false) {
|
|||
|
||||
$o = '';
|
||||
|
||||
$geotag = (($x['allow_location']) ? get_markup_template('jot_geotag.tpl') : '');
|
||||
$geotag = (($x['allow_location']) ? replace_macros(get_markup_template('jot_geotag.tpl'), array()) : '');
|
||||
|
||||
/* $plaintext = false;
|
||||
if( local_user() && (intval(get_pconfig(local_user(),'system','plaintext')) || !feature_enabled(local_user(),'richtext')) )
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function cronhooks_run(&$argv, &$argc){
|
|||
|
||||
if(is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("dba.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,13 +12,13 @@ function delivery_run(&$argv, &$argc){
|
|||
|
||||
if(is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("dba.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
}
|
||||
|
||||
require_once("session.php");
|
||||
require_once("datetime.php");
|
||||
require_once("include/session.php");
|
||||
require_once("include/datetime.php");
|
||||
require_once('include/items.php');
|
||||
require_once('include/bbcode.php');
|
||||
require_once('include/diaspora.php');
|
||||
|
|
|
|||
|
|
@ -752,7 +752,7 @@ function diaspora_request($importer,$xml) {
|
|||
}
|
||||
|
||||
function diaspora_post_allow($importer,$contact) {
|
||||
if(($contact['blocked']) || ($contact['readonly']))
|
||||
if(($contact['blocked']) || ($contact['readonly']) || ($contact['archive']))
|
||||
return false;
|
||||
if($contact['rel'] == CONTACT_IS_SHARING || $contact['rel'] == CONTACT_IS_FRIEND)
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ function directory_run(&$argv, &$argc){
|
|||
|
||||
if(is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("dba.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ function notification($params) {
|
|||
|
||||
// send email notification if notification preferences permit
|
||||
|
||||
require_once('bbcode.php');
|
||||
require_once('include/bbcode.php');
|
||||
if((intval($params['notify_flags']) & intval($params['type'])) || $params['type'] == NOTIFY_SYSTEM) {
|
||||
|
||||
logger('notification: sending notification email');
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ function expire_run(&$argv, &$argc){
|
|||
|
||||
if(is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("dba.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ function new_contact($uid,$url,$interactive = false) {
|
|||
group_add_member($uid,'',$contact_id,$g[0]['def_gid']);
|
||||
}
|
||||
|
||||
require_once("Photo.php");
|
||||
require_once("include/Photo.php");
|
||||
|
||||
$photos = import_profile_photo($ret['photo'],$uid,$contact_id);
|
||||
|
||||
|
|
|
|||
40
include/friendica_smarty.php
Normal file
40
include/friendica_smarty.php
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
require_once("library/Smarty/libs/Smarty.class.php");
|
||||
|
||||
class FriendicaSmarty extends Smarty {
|
||||
|
||||
public $filename;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$a = get_app();
|
||||
$theme = current_theme();
|
||||
|
||||
// setTemplateDir can be set to an array, which Smarty will parse in order.
|
||||
// The order is thus very important here
|
||||
$template_dirs = array('theme' => "view/theme/$theme/smarty3/");
|
||||
if( x($a->theme_info,"extends") )
|
||||
$template_dirs = $template_dirs + array('extends' => "view/theme/".$a->theme_info["extends"]."/smarty3/");
|
||||
$template_dirs = $template_dirs + array('base' => 'view/smarty3/');
|
||||
$this->setTemplateDir($template_dirs);
|
||||
|
||||
$this->setCompileDir('view/smarty3/compiled/');
|
||||
$this->setConfigDir('view/smarty3/config/');
|
||||
$this->setCacheDir('view/smarty3/cache/');
|
||||
|
||||
$this->left_delimiter = $a->smarty3_ldelim;
|
||||
$this->right_delimiter = $a->smarty3_rdelim;
|
||||
}
|
||||
|
||||
function parsed($template = '') {
|
||||
if($template) {
|
||||
return $this->fetch('string:' . $template);
|
||||
}
|
||||
return $this->fetch('file:' . $this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -13,7 +13,7 @@ function gprobe_run(&$argv, &$argc){
|
|||
|
||||
if(is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("dba.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?php
|
||||
require_once "html2bbcode.php";
|
||||
require_once("include/html2bbcode.php");
|
||||
|
||||
function breaklines($line, $level, $wraplength = 75)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1607,7 +1607,7 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0)
|
|||
|
||||
if((is_array($contact)) && ($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $contact['avatar-date'])) {
|
||||
logger('consume_feed: Updating photo for ' . $contact['name']);
|
||||
require_once("Photo.php");
|
||||
require_once("include/Photo.php");
|
||||
$photo_failure = false;
|
||||
$have_photo = false;
|
||||
|
||||
|
|
@ -2240,7 +2240,7 @@ function local_delivery($importer,$data) {
|
|||
|
||||
if(($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $importer['avatar-date'])) {
|
||||
logger('local_delivery: Updating photo for ' . $importer['name']);
|
||||
require_once("Photo.php");
|
||||
require_once("include/Photo.php");
|
||||
$photo_failure = false;
|
||||
$have_photo = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -51,13 +51,13 @@ function notifier_run(&$argv, &$argc){
|
|||
|
||||
if(is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("dba.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
}
|
||||
|
||||
require_once("session.php");
|
||||
require_once("datetime.php");
|
||||
require_once("include/session.php");
|
||||
require_once("include/datetime.php");
|
||||
require_once('include/items.php');
|
||||
require_once('include/bbcode.php');
|
||||
require_once('include/email.php');
|
||||
|
|
|
|||
|
|
@ -150,6 +150,7 @@ class FKOAuth1 extends OAuthServer {
|
|||
$_SESSION['page_flags'] = $record['page-flags'];
|
||||
$_SESSION['my_url'] = $a->get_baseurl() . '/profile/' . $record['nickname'];
|
||||
$_SESSION['addr'] = $_SERVER['REMOTE_ADDR'];
|
||||
$_SESSION["allow_api"] = true;
|
||||
|
||||
//notice( t("Welcome back ") . $record['username'] . EOL);
|
||||
$a->user = $record;
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ function onepoll_run(&$argv, &$argc){
|
|||
|
||||
if(is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("dba.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
};
|
||||
|
|
@ -252,6 +252,9 @@ function onepoll_run(&$argv, &$argc){
|
|||
|
||||
$stat_writeable = ((($contact['notify']) && ($contact['rel'] == CONTACT_IS_FOLLOWER || $contact['rel'] == CONTACT_IS_FRIEND)) ? 1 : 0);
|
||||
|
||||
if($contact['network'] === NETWORK_OSTATUS && get_pconfig($importer_uid,'system','ostatus_autofriend'))
|
||||
$stat_writeable = 1;
|
||||
|
||||
if($stat_writeable != $contact['writable']) {
|
||||
q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d LIMIT 1",
|
||||
intval($stat_writeable),
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ function poller_run(&$argv, &$argc){
|
|||
|
||||
if(is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("dba.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,14 +11,14 @@ function queue_run(&$argv, &$argc){
|
|||
|
||||
if(is_null($db)){
|
||||
@include(".htconfig.php");
|
||||
require_once("dba.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
};
|
||||
|
||||
|
||||
require_once("session.php");
|
||||
require_once("datetime.php");
|
||||
require_once("include/session.php");
|
||||
require_once("include/datetime.php");
|
||||
require_once('include/items.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
|
|
|
|||
100
include/text.php
100
include/text.php
|
|
@ -9,19 +9,39 @@
|
|||
// depending on the order in which they were declared in the array.
|
||||
|
||||
require_once("include/template_processor.php");
|
||||
require_once("include/friendica_smarty.php");
|
||||
|
||||
if(! function_exists('replace_macros')) {
|
||||
function replace_macros($s,$r) {
|
||||
global $t;
|
||||
|
||||
//$ts = microtime();
|
||||
$r = $t->replace($s,$r);
|
||||
//$tt = microtime() - $ts;
|
||||
|
||||
//$a = get_app();
|
||||
//$a->page['debug'] .= "$tt <br>\n";
|
||||
return template_unescape($r);
|
||||
|
||||
// $ts = microtime();
|
||||
$a = get_app();
|
||||
|
||||
if($a->theme['template_engine'] === 'smarty3') {
|
||||
$template = '';
|
||||
if(gettype($s) === 'string') {
|
||||
$template = $s;
|
||||
$s = new FriendicaSmarty();
|
||||
$s->error_reporting = E_ALL & ~E_NOTICE;
|
||||
}
|
||||
foreach($r as $key=>$value) {
|
||||
if($key[0] === '$') {
|
||||
$key = substr($key, 1);
|
||||
}
|
||||
$s->assign($key, $value);
|
||||
}
|
||||
$output = $s->parsed($template);
|
||||
}
|
||||
else {
|
||||
$r = $t->replace($s,$r);
|
||||
|
||||
$output = template_unescape($r);
|
||||
}
|
||||
// $tt = microtime() - $ts;
|
||||
// $a = get_app();
|
||||
// $a->page['debug'] .= "$tt <br>\n";
|
||||
return $output;
|
||||
}}
|
||||
|
||||
|
||||
|
|
@ -421,29 +441,63 @@ if(! function_exists('get_intltext_template')) {
|
|||
function get_intltext_template($s) {
|
||||
global $lang;
|
||||
|
||||
$a = get_app();
|
||||
$engine = '';
|
||||
if($a->theme['template_engine'] === 'smarty3')
|
||||
$engine = "/smarty3";
|
||||
|
||||
if(! isset($lang))
|
||||
$lang = 'en';
|
||||
|
||||
if(file_exists("view/$lang/$s"))
|
||||
return file_get_contents("view/$lang/$s");
|
||||
elseif(file_exists("view/en/$s"))
|
||||
return file_get_contents("view/en/$s");
|
||||
if(file_exists("view/$lang$engine/$s"))
|
||||
return file_get_contents("view/$lang$engine/$s");
|
||||
elseif(file_exists("view/en$engine/$s"))
|
||||
return file_get_contents("view/en$engine/$s");
|
||||
else
|
||||
return file_get_contents("view/$s");
|
||||
return file_get_contents("view$engine/$s");
|
||||
}}
|
||||
|
||||
if(! function_exists('get_markup_template')) {
|
||||
function get_markup_template($s) {
|
||||
$a=get_app();
|
||||
$theme = current_theme();
|
||||
|
||||
if(file_exists("view/theme/$theme/$s"))
|
||||
return file_get_contents("view/theme/$theme/$s");
|
||||
elseif (x($a->theme_info,"extends") && file_exists("view/theme/".$a->theme_info["extends"]."/$s"))
|
||||
return file_get_contents("view/theme/".$a->theme_info["extends"]."/$s");
|
||||
else
|
||||
return file_get_contents("view/$s");
|
||||
function get_markup_template($s, $root = '') {
|
||||
// $ts = microtime();
|
||||
$a = get_app();
|
||||
|
||||
if($a->theme['template_engine'] === 'smarty3') {
|
||||
$template_file = get_template_file($a, 'smarty3/' . $s, $root);
|
||||
|
||||
$template = new FriendicaSmarty();
|
||||
$template->filename = $template_file;
|
||||
|
||||
// $tt = microtime() - $ts;
|
||||
// $a->page['debug'] .= "$tt <br>\n";
|
||||
return $template;
|
||||
}
|
||||
else {
|
||||
$template_file = get_template_file($a, $s, $root);
|
||||
// $file_contents = file_get_contents($template_file);
|
||||
// $tt = microtime() - $ts;
|
||||
// $a->page['debug'] .= "$tt <br>\n";
|
||||
// return $file_contents;
|
||||
return file_get_contents($template_file);
|
||||
}
|
||||
}}
|
||||
|
||||
if(! function_exists("get_template_file")) {
|
||||
function get_template_file($a, $filename, $root = '') {
|
||||
$theme = current_theme();
|
||||
|
||||
// Make sure $root ends with a slash /
|
||||
if($root !== '' && $root[strlen($root)-1] !== '/')
|
||||
$root = $root . '/';
|
||||
|
||||
if(file_exists($root . "view/theme/$theme/$filename"))
|
||||
$template_file = $root . "view/theme/$theme/$filename";
|
||||
elseif (x($a->theme_info,"extends") && file_exists($root . "view/theme/".$a->theme_info["extends"]."/$filename"))
|
||||
$template_file = $root . "view/theme/".$a->theme_info["extends"]."/$filename";
|
||||
else
|
||||
$template_file = $root . "view/$filename";
|
||||
|
||||
return $template_file;
|
||||
}}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue