Merge branch 'develop' of https://github.com/friendica/friendica into develop

This commit is contained in:
Friendica 2018-04-19 19:24:35 +00:00
commit dc4b384414
15 changed files with 610 additions and 154 deletions

View file

@ -30,6 +30,7 @@
"bower-asset/base64": "^1.0", "bower-asset/base64": "^1.0",
"bower-asset/Chart-js": "^2.7", "bower-asset/Chart-js": "^2.7",
"bower-asset/perfect-scrollbar": "^0.6", "bower-asset/perfect-scrollbar": "^0.6",
"bower-asset/vue": "^2.5",
"npm-asset/jquery": "^2.0", "npm-asset/jquery": "^2.0",
"npm-asset/jquery-colorbox": "^1.6", "npm-asset/jquery-colorbox": "^1.6",
"npm-asset/jquery-datetimepicker": "^2.4.0", "npm-asset/jquery-datetimepicker": "^2.4.0",

18
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "12b8df66213734281765cb6e2c5a786e", "content-hash": "96062c2020a40f14b52e5e91c79995a7",
"packages": [ "packages": [
{ {
"name": "asika/simple-console", "name": "asika/simple-console",
@ -133,6 +133,22 @@
"description": "Minimalistic but perfect custom scrollbar plugin", "description": "Minimalistic but perfect custom scrollbar plugin",
"time": "2017-01-10T01:04:09+00:00" "time": "2017-01-10T01:04:09+00:00"
}, },
{
"name": "bower-asset/vue",
"version": "v2.5.16",
"source": {
"type": "git",
"url": "https://github.com/vuejs/vue.git",
"reference": "25342194016dc3bcc81cb3e8e229b0fb7ba1d1d6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/vuejs/vue/zipball/25342194016dc3bcc81cb3e8e229b0fb7ba1d1d6",
"reference": "25342194016dc3bcc81cb3e8e229b0fb7ba1d1d6",
"shasum": ""
},
"type": "bower-asset-library"
},
{ {
"name": "divineomega/password_exposed", "name": "divineomega/password_exposed",
"version": "v2.5.1", "version": "v2.5.1",

View file

@ -668,33 +668,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order =
$profile_name = $item['author-link']; $profile_name = $item['author-link'];
} }
$tags = []; $tags = \Friendica\Model\Term::populateTagsFromItem($item);
$hashtags = [];
$mentions = [];
$searchpath = System::baseUrl()."/search?tag=";
$taglist = dba::select('term', ['type', 'term', 'url'],
["`otype` = ? AND `oid` = ? AND `type` IN (?, ?)", TERM_OBJ_POST, $item['id'], TERM_HASHTAG, TERM_MENTION],
['order' => ['tid']]);
while ($tag = dba::fetch($taglist)) {
if ($tag["url"] == "") {
$tag["url"] = $searchpath . strtolower($tag["term"]);
}
$tag["url"] = best_link_url($item, $sp, $tag["url"]);
if ($tag["type"] == TERM_HASHTAG) {
$hashtags[] = "#<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
$prefix = "#";
} elseif ($tag["type"] == TERM_MENTION) {
$mentions[] = "@<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
$prefix = "@";
}
$tags[] = $prefix."<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
}
dba::close($taglist);
$sp = false; $sp = false;
$profile_link = best_link_url($item, $sp); $profile_link = best_link_url($item, $sp);
@ -764,9 +738,9 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order =
} }
$body_e = $body; $body_e = $body;
$tags_e = $tags; $tags_e = $tags['tags'];
$hashtags_e = $hashtags; $hashtags_e = $tags['hashtags'];
$mentions_e = $mentions; $mentions_e = $tags['mentions'];
$location_e = $location; $location_e = $location;
$owner_name_e = $owner_name; $owner_name_e = $owner_name;

View file

@ -405,12 +405,21 @@ function get_form_security_token($typename = '')
function check_form_security_token($typename = '', $formname = 'form_security_token') function check_form_security_token($typename = '', $formname = 'form_security_token')
{ {
if (!x($_REQUEST, $formname)) { $hash = null;
return false;
}
if (!empty($_REQUEST[$formname])) {
/// @TODO Careful, not secured! /// @TODO Careful, not secured!
$hash = $_REQUEST[$formname]; $hash = $_REQUEST[$formname];
}
if (!empty($_SERVER['HTTP_X_CSRF_TOKEN'])) {
/// @TODO Careful, not secured!
$hash = $_SERVER['HTTP_X_CSRF_TOKEN'];
}
if (empty($hash)) {
return false;
}
$max_livetime = 10800; // 3 hours $max_livetime = 10800; // 3 hours

View file

@ -1234,12 +1234,6 @@ function prepare_body(array &$item, $attach = false, $is_preview = false)
$a = get_app(); $a = get_app();
Addon::callHooks('prepare_body_init', $item); Addon::callHooks('prepare_body_init', $item);
$searchpath = System::baseUrl() . "/search?tag=";
$tags = [];
$hashtags = [];
$mentions = [];
// In order to provide theme developers more possibilities, event items // In order to provide theme developers more possibilities, event items
// are treated differently. // are treated differently.
if ($item['object-type'] === ACTIVITY_OBJ_EVENT && isset($item['event-id'])) { if ($item['object-type'] === ACTIVITY_OBJ_EVENT && isset($item['event-id'])) {
@ -1247,37 +1241,11 @@ function prepare_body(array &$item, $attach = false, $is_preview = false)
return $ev; return $ev;
} }
$taglist = dba::p("SELECT `type`, `term`, `url` FROM `term` WHERE `otype` = ? AND `oid` = ? AND `type` IN (?, ?) ORDER BY `tid`", $tags = \Friendica\Model\Term::populateTagsFromItem($item);
intval(TERM_OBJ_POST), intval($item['id']), intval(TERM_HASHTAG), intval(TERM_MENTION));
while ($tag = dba::fetch($taglist)) { $item['tags'] = $tags['tags'];
if ($tag["url"] == "") { $item['hashtags'] = $tags['hashtags'];
$tag["url"] = $searchpath . strtolower($tag["term"]); $item['mentions'] = $tags['mentions'];
}
$orig_tag = $tag["url"];
$tag["url"] = best_link_url($item, $sp, $tag["url"]);
if ($tag["type"] == TERM_HASHTAG) {
if ($orig_tag != $tag["url"]) {
$item['body'] = str_replace($orig_tag, $tag["url"], $item['body']);
}
$hashtags[] = "#<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
$prefix = "#";
} elseif ($tag["type"] == TERM_MENTION) {
$mentions[] = "@<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
$prefix = "@";
}
$tags[] = $prefix . "<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
}
dba::close($taglist);
$item['tags'] = $tags;
$item['hashtags'] = $hashtags;
$item['mentions'] = $mentions;
// Compile eventual content filter reasons // Compile eventual content filter reasons
$filter_reasons = []; $filter_reasons = [];

View file

@ -50,7 +50,7 @@ class MemcachedCacheDriver extends BaseObject implements ICacheDriver
{ {
// We store with the hostname as key to avoid problems with other applications // We store with the hostname as key to avoid problems with other applications
return $this->memcached->set( return $this->memcached->set(
self::getApp()->get_hostname() . ":" . $key, self::getApp()->get_hostname() . ':' . $key,
$value, $value,
time() + $duration time() + $duration
); );
@ -58,7 +58,9 @@ class MemcachedCacheDriver extends BaseObject implements ICacheDriver
public function delete($key) public function delete($key)
{ {
return $this->memcached->delete($key); $return = $this->memcached->delete(self::getApp()->get_hostname() . ':' . $key);
return $return;
} }
public function clear() public function clear()

View file

@ -21,6 +21,7 @@ class Console extends \Asika\SimpleConsole\Console
'extract' => __NAMESPACE__ . '\Console\Extract', 'extract' => __NAMESPACE__ . '\Console\Extract',
'globalcommunityblock' => __NAMESPACE__ . '\Console\GlobalCommunityBlock', 'globalcommunityblock' => __NAMESPACE__ . '\Console\GlobalCommunityBlock',
'globalcommunitysilence' => __NAMESPACE__ . '\Console\GlobalCommunitySilence', 'globalcommunitysilence' => __NAMESPACE__ . '\Console\GlobalCommunitySilence',
'autoinstall' => __NAMESPACE__ . '\Console\AutomaticInstallation',
'maintenance' => __NAMESPACE__ . '\Console\Maintenance', 'maintenance' => __NAMESPACE__ . '\Console\Maintenance',
'newpassword' => __NAMESPACE__ . '\Console\NewPassword', 'newpassword' => __NAMESPACE__ . '\Console\NewPassword',
'php2po' => __NAMESPACE__ . '\Console\PhpToPo', 'php2po' => __NAMESPACE__ . '\Console\PhpToPo',
@ -42,6 +43,7 @@ Commands:
globalcommunityblock Block remote profile from interacting with this node globalcommunityblock Block remote profile from interacting with this node
globalcommunitysilence Silence remote profile from global community page globalcommunitysilence Silence remote profile from global community page
help Show help about a command, e.g (bin/console help config) help Show help about a command, e.g (bin/console help config)
autoinstall Starts automatic installation of friendica based on values from htconfig.php
maintenance Set maintenance mode for this node maintenance Set maintenance mode for this node
newpassword Set a new password for a given user newpassword Set a new password for a given user
php2po Generate a messages.po file from a strings.php file php2po Generate a messages.po file from a strings.php file

View file

@ -0,0 +1,164 @@
<?php
namespace Friendica\Core\Console;
use Asika\SimpleConsole\Console;
use dba;
use Friendica\App;
require_once 'mod/install.php';
require_once 'include/dba.php';
class AutomaticInstallation extends Console
{
protected function getHelp()
{
return <<<HELP
Installation - Install Friendica automatically
Synopsis
bin/console autoinstall [-h|--help|-?] [-v] [-a]
Description
Installs Friendica with data based on the htconfig.php file
Notes:
Not checking .htaccess/URL-Rewrite during CLI installation.
Options
-h|--help|-? Show help information
-v Show more debug information.
-a All setup checks are required (except .htaccess)
HELP;
}
protected function doExecute()
{
// Initialise the app
$this->out("Initializing setup...\n");
$a = get_app();
$db_host = '';
$db_user = '';
$db_pass = '';
$db_data = '';
require_once 'htconfig.php';
$this->out(" Complete!\n\n");
// Check basic setup
$this->out("Checking basic setup...\n");
$checkResults = [];
$checkResults['basic'] = $this->runBasicChecks($a);
$errorMessage = $this->extractErrors($checkResults['basic']);
if ($errorMessage !== '') {
throw new \RuntimeException($errorMessage);
}
$this->out(" Complete!\n\n");
// Check database connection
$this->out("Checking database...\n");
$checkResults['db'] = array();
$checkResults['db'][] = $this->runDatabaseCheck($db_host, $db_user, $db_pass, $db_data);
$errorMessage = $this->extractErrors($checkResults['db']);
if ($errorMessage !== '') {
throw new \RuntimeException($errorMessage);
}
$this->out(" Complete!\n\n");
// Install database
$this->out("Inserting data into database...\n");
$checkResults['data'] = load_database();
if ($checkResults['data'] !== '') {
throw new \RuntimeException("ERROR: DB Database creation error. Is the DB empty?\n");
}
$this->out(" Complete!\n\n");
// Copy config file
$this->out("Saving config file...\n");
if (!copy('htconfig.php', '.htconfig.php')) {
throw new \RuntimeException("ERROR: Saving config file failed. Please copy .htautoinstall.php to .htconfig.php manually.\n");
}
$this->out(" Complete!\n\n");
$this->out("\nInstallation is finished\n");
return 0;
}
/**
* @param App $app
* @return array
*/
private function runBasicChecks($app)
{
$checks = [];
check_funcs($checks);
check_imagik($checks);
check_htconfig($checks);
check_smarty3($checks);
check_keys($checks);
if (!empty($app->config['php_path'])) {
check_php($app->config['php_path'], $checks);
} else {
throw new \RuntimeException(" ERROR: The php_path is not set in the config. Please check the file .htconfig.php.\n");
}
$this->out(" NOTICE: Not checking .htaccess/URL-Rewrite during CLI installation.\n");
return $checks;
}
/**
* @param $db_host
* @param $db_user
* @param $db_pass
* @param $db_data
* @return array
*/
private function runDatabaseCheck($db_host, $db_user, $db_pass, $db_data)
{
$result = array(
'title' => 'MySQL Connection',
'required' => true,
'status' => true,
'help' => '',
);
if (!dba::connect($db_host, $db_user, $db_pass, $db_data, true)) {
$result['status'] = false;
$result['help'] = 'Failed, please check your MySQL settings and credentials.';
}
return $result;
}
/**
* @param array $results
* @return string
*/
private function extractErrors($results)
{
$errorMessage = '';
$allChecksRequired = $this->getOption('a') !== null;
foreach ($results as $result) {
if (($allChecksRequired || $result['required'] === true) && $result['status'] === false) {
$errorMessage .= "--------\n";
$errorMessage .= $result['title'] . ': ' . $result['help'] . "\n";
}
}
return $errorMessage;
}
}

View file

@ -24,7 +24,7 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface
public function read($session_id) public function read($session_id)
{ {
if (!x($session_id)) { if (empty($session_id)) {
return ''; return '';
} }
@ -58,9 +58,9 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface
return true; return true;
} }
Cache::set('session:' . $session_id, $session_data, Session::$expire); $return = Cache::set('session:' . $session_id, $session_data, Session::$expire);
return true; return $return;
} }
public function close() public function close()
@ -70,8 +70,9 @@ class CacheSessionHandler extends BaseObject implements SessionHandlerInterface
public function destroy($id) public function destroy($id)
{ {
Cache::delete('session:' . $id); $return = Cache::delete('session:' . $id);
return true;
return $return;
} }
public function gc($maxlifetime) public function gc($maxlifetime)

View file

@ -1803,6 +1803,8 @@ class DBStructure
] ]
]; ];
\Friendica\Core\Addon::callHooks('dbstructure_definition', $database);
return $database; return $database;
} }
} }

View file

@ -9,6 +9,7 @@ use Friendica\Database\DBM;
use dba; use dba;
require_once 'boot.php'; require_once 'boot.php';
require_once 'include/conversation.php';
require_once 'include/dba.php'; require_once 'include/dba.php';
class Term class Term
@ -168,4 +169,56 @@ class Term
} }
} }
} }
/**
* Sorts an item's tags into mentions, hashtags and other tags. Generate personalized URLs by user and modify the
* provided item's body with them.
*
* @param array $item
* @return array
*/
public static function populateTagsFromItem(&$item)
{
$return = [
'tags' => [],
'hashtags' => [],
'mentions' => [],
];
$searchpath = System::baseUrl() . "/search?tag=";
$taglist = dba::select(
'term',
['type', 'term', 'url'],
["`otype` = ? AND `oid` = ? AND `type` IN (?, ?)", TERM_OBJ_POST, $item['id'], TERM_HASHTAG, TERM_MENTION],
['order' => ['tid']]
);
while ($tag = dba::fetch($taglist)) {
if ($tag["url"] == "") {
$tag["url"] = $searchpath . strtolower($tag["term"]);
}
$orig_tag = $tag["url"];
$tag["url"] = best_link_url($item, $sp, $tag["url"]);
if ($tag["type"] == TERM_HASHTAG) {
if ($orig_tag != $tag["url"]) {
$item['body'] = str_replace($orig_tag, $tag["url"], $item['body']);
}
$return['hashtags'][] = "#<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
$prefix = "#";
} elseif ($tag["type"] == TERM_MENTION) {
$return['mentions'][] = "@<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
$prefix = "@";
}
$return['tags'][] = $prefix . "<a href=\"" . $tag["url"] . "\" target=\"_blank\">" . $tag["term"] . "</a>";
}
dba::close($taglist);
return $return;
}
} }

View file

@ -1528,36 +1528,29 @@ class Diaspora
*/ */
private static function plink($addr, $guid, $parent_guid = '') private static function plink($addr, $guid, $parent_guid = '')
{ {
$r = q("SELECT `url`, `nick`, `network` FROM `fcontact` WHERE `addr`='%s' LIMIT 1", dbesc($addr)); $contact = Contact::getDetailsByAddr($addr);
// Fallback // Fallback
if (!DBM::is_result($r)) { if (!$contact) {
if ($parent_guid != '') { if ($parent_guid != '') {
return "https://".substr($addr, strpos($addr, "@") + 1) . "/posts/" . $parent_guid . "#" . $guid; return "https://" . substr($addr, strpos($addr, "@") + 1) . "/posts/" . $parent_guid . "#" . $guid;
} else { } else {
return "https://".substr($addr, strpos($addr, "@") + 1) . "/posts/" . $guid; return "https://" . substr($addr, strpos($addr, "@") + 1) . "/posts/" . $guid;
} }
} }
// Friendica contacts are often detected as Diaspora contacts in the "fcontact" table if ($contact["network"] == NETWORK_DFRN) {
// So we try another way as well. return str_replace("/profile/" . $contact["nick"] . "/", "/display/" . $guid, $contact["url"] . "/");
$s = q("SELECT `network` FROM `gcontact` WHERE `nurl`='%s' LIMIT 1", dbesc(normalise_link($r[0]["url"])));
if (DBM::is_result($s)) {
$r[0]["network"] = $s[0]["network"];
} }
if ($r[0]["network"] == NETWORK_DFRN) { if (self::isRedmatrix($contact["url"])) {
return str_replace("/profile/".$r[0]["nick"]."/", "/display/".$guid, $r[0]["url"]."/"); return $contact["url"] . "/?f=&mid=" . $guid;
}
if (self::isRedmatrix($r[0]["url"])) {
return $r[0]["url"]."/?f=&mid=".$guid;
} }
if ($parent_guid != '') { if ($parent_guid != '') {
return "https://".substr($addr, strpos($addr, "@") + 1) . "/posts/" . $parent_guid . "#" . $guid; return "https://" . substr($addr, strpos($addr, "@") + 1) . "/posts/" . $parent_guid . "#" . $guid;
} else { } else {
return "https://".substr($addr, strpos($addr, "@") + 1) . "/posts/" . $guid; return "https://" . substr($addr, strpos($addr, "@") + 1) . "/posts/" . $guid;
} }
} }
@ -2744,35 +2737,33 @@ class Diaspora
* *
* @return array The fetched item * @return array The fetched item
*/ */
private static function originalItem($guid, $orig_author, $author) public static function originalItem($guid, $orig_author)
{ {
// Do we already have this item? // Do we already have this item?
$r = q( $fields = ['body', 'tag', 'app', 'created', 'object-type', 'uri', 'guid',
"SELECT `body`, `tag`, `app`, `created`, `object-type`, `uri`, `guid`, 'author-name', 'author-link', 'author-avatar'];
`author-name`, `author-link`, `author-avatar` $condition = ['guid' => $guid, 'visible' => true, 'deleted' => false];
FROM `item` WHERE `guid` = '%s' AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1", $item = dba::selectfirst('item', $fields, $condition);
dbesc($guid)
);
if (DBM::is_result($r)) { if (DBM::is_result($item)) {
logger("reshared message ".$guid." already exists on system."); logger("reshared message ".$guid." already exists on system.");
// Maybe it is already a reshared item? // Maybe it is already a reshared item?
// Then refetch the content, if it is a reshare from a reshare. // Then refetch the content, if it is a reshare from a reshare.
// If it is a reshared post from another network then reformat to avoid display problems with two share elements // If it is a reshared post from another network then reformat to avoid display problems with two share elements
if (self::isReshare($r[0]["body"], true)) { if (self::isReshare($item["body"], true)) {
$r = []; $r = [];
} elseif (self::isReshare($r[0]["body"], false) || strstr($r[0]["body"], "[share")) { } elseif (self::isReshare($item["body"], false) || strstr($item["body"], "[share")) {
$r[0]["body"] = Markdown::toBBCode(BBCode::toMarkdown($r[0]["body"])); $item["body"] = Markdown::toBBCode(BBCode::toMarkdown($item["body"]));
$r[0]["body"] = self::replacePeopleGuid($r[0]["body"], $r[0]["author-link"]); $item["body"] = self::replacePeopleGuid($item["body"], $item["author-link"]);
// Add OEmbed and other information to the body // Add OEmbed and other information to the body
$r[0]["body"] = add_page_info_to_body($r[0]["body"], false, true); $item["body"] = add_page_info_to_body($item["body"], false, true);
return $r[0]; return $item;
} else { } else {
return $r[0]; return $item;
} }
} }
@ -2788,21 +2779,19 @@ class Diaspora
} }
if ($item_id) { if ($item_id) {
$r = q( $fields = ['body', 'tag', 'app', 'created', 'object-type', 'uri', 'guid',
"SELECT `body`, `tag`, `app`, `created`, `object-type`, `uri`, `guid`, 'author-name', 'author-link', 'author-avatar'];
`author-name`, `author-link`, `author-avatar` $condition = ['id' => $item_id, 'visible' => true, 'deleted' => false];
FROM `item` WHERE `id` = %d AND `visible` AND NOT `deleted` AND `body` != '' LIMIT 1", $item = dba::selectfirst('item', $fields, $condition);
intval($item_id)
);
if (DBM::is_result($r)) { if (DBM::is_result($item)) {
// If it is a reshared post from another network then reformat to avoid display problems with two share elements // If it is a reshared post from another network then reformat to avoid display problems with two share elements
if (self::isReshare($r[0]["body"], false)) { if (self::isReshare($item["body"], false)) {
$r[0]["body"] = Markdown::toBBCode(BBCode::toMarkdown($r[0]["body"])); $item["body"] = Markdown::toBBCode(BBCode::toMarkdown($item["body"]));
$r[0]["body"] = self::replacePeopleGuid($r[0]["body"], $r[0]["author-link"]); $item["body"] = self::replacePeopleGuid($item["body"], $item["author-link"]);
} }
return $r[0]; return $item;
} }
} }
} }
@ -2838,7 +2827,7 @@ class Diaspora
return true; return true;
} }
$original_item = self::originalItem($root_guid, $root_author, $author); $original_item = self::originalItem($root_guid, $root_author);
if (!$original_item) { if (!$original_item) {
return false; return false;
} }
@ -3556,24 +3545,21 @@ class Diaspora
// Skip if it isn't a pure repeated messages // Skip if it isn't a pure repeated messages
// Does it start with a share? // Does it start with a share?
if ((strpos($body, "[share") > 0) && $complete) { if ((strpos($body, "[share") > 0) && $complete) {
return(false); return false;
} }
// Does it end with a share? // Does it end with a share?
if (strlen($body) > (strrpos($body, "[/share]") + 8)) { if (strlen($body) > (strrpos($body, "[/share]") + 8)) {
return(false); return false;
} }
$attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "$1", $body); $attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism", "$1", $body);
// Skip if there is no shared message in there // Skip if there is no shared message in there
if ($body == $attributes) { if ($body == $attributes) {
return(false); return false;
} }
// If we don't do the complete check we quit here // If we don't do the complete check we quit here
if (!$complete) {
return true;
}
$guid = ""; $guid = "";
preg_match("/guid='(.*?)'/ism", $attributes, $matches); preg_match("/guid='(.*?)'/ism", $attributes, $matches);
@ -3586,18 +3572,14 @@ class Diaspora
$guid = $matches[1]; $guid = $matches[1];
} }
if ($guid != "") { if (($guid != "") && $complete) {
$r = q( $condition = ['guid' => $guid, 'network' => [NETWORK_DFRN, NETWORK_DIASPORA]];
"SELECT `contact-id` FROM `item` WHERE `guid` = '%s' AND `network` IN ('%s', '%s') LIMIT 1", $item = dba::selectFirst('item', ['contact-id'], $condition);
dbesc($guid), if (DBM::is_result($item)) {
NETWORK_DFRN,
NETWORK_DIASPORA
);
if ($r) {
$ret= []; $ret= [];
$ret["root_handle"] = self::handleFromContact($r[0]["contact-id"]); $ret["root_handle"] = self::handleFromContact($item["contact-id"]);
$ret["root_guid"] = $guid; $ret["root_guid"] = $guid;
return($ret); return $ret;
} }
} }
@ -3614,28 +3596,22 @@ class Diaspora
$ret= []; $ret= [];
$ret["root_handle"] = preg_replace("=https?://(.*)/u/(.*)=ism", "$2@$1", $profile); if ($profile != "") {
if (($ret["root_handle"] == $profile) || ($ret["root_handle"] == "")) { if (Contact::getIdForURL($profile)) {
return(false); $author = Contact::getDetailsByURL($profile);
$ret["root_handle"] = $author['addr'];
}
} }
$link = ""; if (!empty($guid)) {
preg_match("/link='(.*?)'/ism", $attributes, $matches); $ret["root_guid"] = $guid;
if ($matches[1] != "") {
$link = $matches[1];
} }
preg_match('/link="(.*?)"/ism', $attributes, $matches); if (empty($ret) && !$complete) {
if ($matches[1] != "") { return true;
$link = $matches[1];
} }
$ret["root_guid"] = preg_replace("=https?://(.*)/posts/(.*)=ism", "$2", $link); return $ret;
if (($ret["root_guid"] == $link) || (trim($ret["root_guid"]) == "")) {
return(false);
}
return($ret);
} }
/** /**

View file

@ -0,0 +1,8 @@
#admin-users.adminpage { padding-left:0; padding-right: 0;}
#admin-users.adminpage > h1 { padding: 0 15px; }
#users img.icon, #deleted img.icon { height: 24px; }
.opened .caret { transform: rotate(180deg); }
tr.details td,
tr.details th
{ border-top: 0!important; }

View file

@ -20,6 +20,7 @@ $(function() {
} }
}); });
function selectall(cls) { function selectall(cls) {
$('.' + cls).prop('checked', true); $('.' + cls).prop('checked', true);
return false; return false;
@ -28,4 +29,17 @@ $(function() {
$('.' + cls).prop('checked', false); $('.' + cls).prop('checked', false);
return false; return false;
} }
}); });
// Users
function confirm_delete(msg, uname){
return confirm(msg.format(uname));
}
function details(uid) {
$("#user-"+uid+"-detail").toggleClass("hidden");
$("#user-"+uid).toggleClass("opened");
return false;
}

View file

@ -0,0 +1,266 @@
<script type="text/javascript" src="view/theme/frio/js/mod_admin.js"></script>
<link rel="stylesheet" href="view/theme/frio/css/mod_admin.css" type="text/css" media="screen"/>
<div id="admin-users" class="adminpage generic-page-wrapper">
<h1>{{$title}} - {{$page}}</h1>
<form action="{{$baseurl}}/admin/users" method="post">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<!--
**
*
* PENDING Users table
*
**
-->
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{$h_pending}}</h3></div>
{{if $pending}}
<table id="pending" class="table table-hover">
<thead>
<tr>
<th></th>
{{foreach $th_pending as $th}}<th>{{$th}}</th>{{/foreach}}
<th></th>
</tr>
</thead>
<tbody>
{{foreach $pending as $u}}
<tr>
<td><input type="checkbox" class="pending_ckbx" id="id_pending_{{$u.hash}}" name="pending[]" value="{{$u.hash}}" /></td>
<td>{{$u.created}}</td>
<td>{{$u.name}}</td>
<td>{{$u.email}}</td>
<td>
<a href="{{$baseurl}}/regmod/allow/{{$u.hash}}" title="{{$approve}}"><i class="fa fa-thumbs-up" aria-hidden="true"></i></a>
<a href="{{$baseurl}}/regmod/deny/{{$u.hash}}" title="{{$deny}}"><i class="fa fa-thumbs-down" aria-hidden="true"></i></a>
</td>
</tr>
<tr class="details">
<td></td>
<th>{{$pendingnotetext}}</th>
<td colspan="4">{{$u.note}}</td>
</tr>
{{/foreach}}
</tbody>
</table>
<div class="panel-footer">
<div class="row">
<div class="col-xs-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default selectall" data-select-all="pending_ckbx"><i class="fa fa-check-square-o" aria-hidden="true"></i></button>
<button type="button" class="btn btn-default selectnone" data-select-none="pending_ckbx"><i class="fa fa-square-o" aria-hidden="true"></i></button>
</div>
</div>
<div class="col-xs-9">
<button type="submit" name="page_users_deny" class="btn btn-primary"><i class="fa fa-thumbs-down" aria-hidden="true"></i> {{$deny}}</button>
<button type="submit" name="page_users_approve" class="btn btn-warinig"><i class="fa fa-thumbs-up" aria-hidden="true"></i> {{$approve}}</button>
</div>
</div>
</div>
{{else}}
<div class="panel-body text-center text-muted">{{$no_pending}}</div>
{{/if}}
</div>
<!--
**
*
* USERS Table
*
**
-->
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{$h_users}}</h3></div>
{{if $users}}
<table id="users" class="table table-hover">
<thead>
<tr>
<th></th>
<th></th>
{{foreach $th_users as $k=>$th}}
{{if $k < 2 || $order_users == $th.1 || ($k==5 && !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1])) }}
<th>
<a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th.1}}">
{{if $order_users == $th.1}}
{{if $order_direction_users == "+"}}
&#8595;
{{else}}
&#8593;
{{/if}}
{{else}}
&#8597;
{{/if}}
{{$th.0}}</a>
</th>
{{/if}}
{{/foreach}}
<th></th>
</tr>
</thead>
<tbody>
{{foreach $users as $u}}
<tr id="user-{{$u.uid}}">
<td>
{{if $u.is_deletable}}
<input type="checkbox" class="users_ckbx" id="id_user_{{$u.uid}}" name="user[]" value="{{$u.uid}}"/>
{{else}}
&nbsp;
{{/if}}
</td>
<td><img class="icon" src="{{$u.micro}}" title="{{$u.nickname}}"></td>
<td><a href="{{$u.url}}" title="{{$u.nickname}}"> {{$u.name}}</a></td>
<td>{{$u.email}}</td>
{{if $order_users == $th_users.2.1}}
<td>{{$u.register_date}}</td>
{{/if}}
{{if $order_users == $th_users.3.1}}
<td>{{$u.login_date}}</td>
{{/if}}
{{if $order_users == $th_users.4.1}}
<td>{{$u.lastitem_date}}</td>
{{/if}}
{{if !in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
<td>{{$u.page_flags}} {{if $u.is_admin}}({{$siteadmin}}){{/if}} {{if $u.account_expired}}({{$accountexpired}}){{/if}}</td>
{{/if}}
<td class="text-right">
<button type="button" class="btn-link" onclick="return details({{$u.uid}})"><span class="caret"></span></button>
</td>
</tr>
<tr id="user-{{$u.uid}}-detail" class="hidden details">
<td>&nbsp;</td>
<td colspan="4">
{{if $order_users != $th_users.2.1}}
<p><a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.2.1}}">
&#8597; {{$th_users.2.0}}</a> : {{$u.register_date}}</p>
{{/if}}
{{if $order_users != $th_users.3.1}}
<p><a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.3.1}}">
&#8597; {{$th_users.3.0}}</a> : {{$u.login_date}}</p>
{{/if}}
{{if $order_users != $th_users.4.1}}
<p><a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.4.1}}">
&#8597; {{$th_users.4.0}}</a> : {{$u.lastitem_date}}</p>
{{/if}}
{{if in_array($order_users,[$th_users.2.1, $th_users.3.1, $th_users.4.1]) }}
<p><a href="{{$baseurl}}/admin/users/?o={{if $order_direction_users == "+"}}-{{/if}}{{$th_users.5.1}}">
&#8597; {{$th_users.5.0}}</a> : {{$u.page_flags}} {{if $u.is_admin}}({{$siteadmin}}){{/if}} {{if $u.account_expired}}({{$accountexpired}}){{/if}}</p>
{{/if}}
</td>
<td class="text-right">
{{if $u.is_deletable}}
<a href="{{$baseurl}}/admin/users/block/{{$u.uid}}?t={{$form_security_token}}" title="{{if $u.blocked}}{{$unblock}}{{else}}{{$block}}{{/if}}">
{{if $u.blocked==0}}
<i class="fa fa-ban" aria-hidden="true"></i>
{{else}}
<i class="fa fa-circle-o" aria-hidden="true"></i>
{{/if}}
</a>
<a href="{{$baseurl}}/admin/users/delete/{{$u.uid}}?t={{$form_security_token}}" title="{{$delete}}" onclick="return confirm_delete('{{$confirm_delete}}','{{$u.name}}')"><i class="fa fa-trash" aria-hidden="true"></i></a>
{{else}}
&nbsp;
{{/if}}
</td>
</tr>
{{/foreach}}
</tbody>
</table>
<div class="panel-footer">
<div class="row">
<div class="col-xs-3">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default selectall" data-select-all="users_ckbx"><i class="fa fa-check-square-o" aria-hidden="true"></i></button>
<button type="button" class="btn btn-default selectnone" data-select-none="users_ckbx"><i class="fa fa-square-o" aria-hidden="true"></i></button>
</div>
</div>
<div class="col-xs-9 text-right">
<button type="submit" name="page_users_block" class="btn btn-warning"> <i class="fa fa-ban" aria-hidden="true"></i> {{$block}} / <i class="fa fa-circle-o" aria-hidden="true"></i> {{$unblock}}</button>
<button type="submit" name="page_users_delete" class="btn btn-danger" onclick="return confirm_delete('{{$confirm_delete_multi}}')"><i class="fa fa-trash" aria-hidden="true"></i> {{$delete}}</button>
</div>
</div>
</div>
{{else}}
<div class="panel-body text-center bg-danger">NO USERS?!?</div>
{{/if}}
</div>
</form>
<!--
**
*
* DELETED Users table
*
**
-->
{{if $deleted}}
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{$h_deleted}}</h3></div>
<table id="deleted" class="table table-hover">
<thead>
<tr>
<th></th>
{{foreach $th_deleted as $k=>$th}}
{{if in_array($k,[0,1,5])}}
<th>{{$th}}</th>
{{/if}}
{{/foreach}}
</tr>
</thead>
<tbody>
{{foreach $deleted as $u}}
<tr>
<td><img class="icon" src="{{$u.micro}}" title="{{$u.nickname}}"></td>
<td><a href="{{$u.url}}" title="{{$u.nickname}}" >{{$u.name}}</a></td>
<td>{{$u.email}}</td>
<td>{{$u.deleted}}</td>
</tr>
{{/foreach}}
</tbody>
</table>
</div>
{{/if}}
<!--
**
*
* NEW USER Form
*
**
-->
<form action="{{$baseurl}}/admin/users" method="post">
<input type="hidden" name="form_security_token" value="{{$form_security_token}}">
<div class="panel panel-default">
<div class="panel-heading"><h3 class="panel-title">{{$h_newuser}}</h3></div>
<div class="panel-body">
{{include file="field_input.tpl" field=$newusername}}
{{include file="field_input.tpl" field=$newusernickname}}
{{include file="field_input.tpl" field=$newuseremail}}
</div>
<div class="panel-footer text-right">
<button type="submit" class="btn btn-primary">{{$submit}}</button>
</form>
</div>
</form>
</div>