Merge remote-tracking branch 'upstream/dev' into dev

This commit is contained in:
Manuel Jiménez Friaza 2019-02-19 12:15:59 +01:00
commit c5bb074573
80 changed files with 6204 additions and 4409 deletions

View File

@ -1,36 +1,49 @@
# Select image from https://hub.docker.com/_/php/ # Select image from https://hub.docker.com/_/php/
image: php:7.1 #image: php:7.2
# Use a prepared Hubzilla image to optimise pipeline run
image: registry.gitlab.com/dawnbreak/hubzilla/core:php7.2
stages:
- test
- deploy
# Select what we should cache # Select what we should cache
cache: cache:
paths: paths:
- vendor/ - vendor/
- .cache/
# global variables for all jobs, if no job specific variables
variables: variables:
# Configure mysql service (https://hub.docker.com/_/mysql/) # Tell composer to use the project workspace .cache folder
COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.cache/composer"
# Ignore a Composer warning
COMPOSER_ALLOW_SUPERUSER: 1
# Configure MySQL/MariaDB service (https://hub.docker.com/_/mysql/, https://hub.docker.com/_/mariadb/)
MYSQL_DATABASE: hello_world_test MYSQL_DATABASE: hello_world_test
MYSQL_ROOT_PASSWORD: mysql MYSQL_ROOT_PASSWORD: mysql
# Configure PostgreSQL service (https://hub.docker.com/_/postgres/)
POSTGRES_DB: ci-db
POSTGRES_USER: ci-user
POSTGRES_PASSWORD: ci-pass
before_script: before_script:
# prevent error installing buggy postgresql-client package
- mkdir -p /usr/share/man/man1 /usr/share/man/man7
- apt-get update -yqq
- apt-get install -yqq --no-install-recommends git mysql-client postgresql-client libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libaspell-dev libpcre3-dev libtidy-dev
# Install PHP extensions
- docker-php-ext-install mbstring mcrypt pdo_mysql pdo_pgsql curl json intl gd xml zip bz2 opcache
# Install & enable Xdebug for code coverage reports # Install & enable Xdebug for code coverage reports
- pecl install xdebug - pecl install xdebug
- docker-php-ext-enable xdebug - docker-php-ext-enable xdebug
# Install and run Composer # Install composer
- curl -sS https://getcomposer.org/installer | php - curl -sS https://getcomposer.org/installer | php
# Install dev libraries from composer # Install dev libraries from composer
- php composer.phar install --no-progress - php composer.phar install --no-progress
# Configure PHP values, needed for phpunit code coverage HTML generation
- echo "memory_limit = 256M" > /usr/local/etc/php/conf.d/hubzilla.ini
# We test PHP7 with MySQL # test PHP7 with MySQL 5.7
test:php:mysql: php7.2_mysql 1/2:
stage: test
services: services:
- mysql:5.7 - mysql:5.7
script: script:
@ -39,14 +52,38 @@ test:php:mysql:
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE" - echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text - vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
# test PHP7 with PostgreSQL
test:php:postgres: # test PHP7 with MySQL latest (8)
php7.2_mysql 2/2:
stage: test
services:
- name: mysql:latest
command: ["--default-authentication-plugin=mysql_native_password"]
script:
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
# test PHP7 with MariaDB latest (10.3)
php7.2_mariadb:
stage: test
services:
- name: mariadb:latest
alias: mysql
script:
- echo "USE $MYSQL_DATABASE; $(cat ./install/schema_mysql.sql)" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "SHOW DATABASES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- echo "USE $MYSQL_DATABASE; SHOW TABLES;" | mysql --user=root --password="$MYSQL_ROOT_PASSWORD" --host=mysql "$MYSQL_DATABASE"
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-text
# test PHP7 with PostgreSQL latest
php7.2_postgres:
stage: test
services: services:
- postgres:latest - postgres:latest
variables:
POSTGRES_DB: ci-db
POSTGRES_USER: ci-user
POSTGRES_PASSWORD: ci-pass
script: script:
- export PGPASSWORD=$POSTGRES_PASSWORD - export PGPASSWORD=$POSTGRES_PASSWORD
- psql --version - psql --version
@ -67,3 +104,23 @@ test:php:postgres:
name: "$CI_COMMIT_REF_SLUG-$CI_JOB_NAME" name: "$CI_COMMIT_REF_SLUG-$CI_JOB_NAME"
paths: paths:
- tests/results/ - tests/results/
# Generate Doxygen API Documentation and deploy it at GitLab pages
pages:
stage: deploy
cache: {}
image: php:7-cli-alpine
before_script:
- apk update
- apk add doxygen ttf-freefont graphviz
script:
- doxygen util/Doxyfile
- mv doc/html/ public/
- echo "API documentation should be accessible at https://hubzilla.frama.io/core/ soon"
artifacts:
paths:
- public
only:
# Only generate it on main repo's master branch
- master@hubzilla/core

View File

@ -1,3 +1,13 @@
Hubzilla 3.8.9 (2018-02-03)
- Fix typos in mod oep
- Fix page jumping when liking collapsed/expanded post
- Fix failure to import mail in mod import
- Fix wrong channel count in mod new_channel
- Fix diaspora addon regression
- Remove deprecated diaspora addon endpoint
- Fix wrong function call in gallery addon
Hubzilla 3.8.8 (2018-12-22) Hubzilla 3.8.8 (2018-12-22)
- Fix issue with linkinfo - Fix issue with linkinfo
- Fix cURL with HTTP/2 - Fix cURL with HTTP/2

View File

@ -0,0 +1,27 @@
<?php /** @file */
namespace Zotlabs\Daemon;
class Cache_embeds {
static public function run($argc,$argv) {
if(! $argc == 2)
return;
$c = q("select body from item where id = %d ",
dbesc(intval($argv[1]))
);
if(! $c)
return;
$item = $c[0];
// bbcode conversion by default processes embeds that aren't already cached.
// Ignore the returned html output.
bbcode($item['body']);
}
}

View File

@ -434,6 +434,8 @@ class Notifier {
$x['body'] = 'private'; $x['body'] = 'private';
logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG); logger('notifier: encoded item: ' . print_r($x,true), LOGGER_DATA, LOG_DEBUG);
//logger('notifier: encoded activity: ' . print_r($activity,true), LOGGER_DATA, LOG_DEBUG);
stringify_array_elms($recipients); stringify_array_elms($recipients);
if(! $recipients) { if(! $recipients) {
logger('no recipients'); logger('no recipients');
@ -677,6 +679,17 @@ class Notifier {
} }
$packet_type = (($upstream || $uplink) ? 'response' : 'activity'); $packet_type = (($upstream || $uplink) ? 'response' : 'activity');
// block zot private reshares from zot6, as this could cause a number of privacy issues
// due to parenting differences between the reshare implementations. In zot a reshare is
// a standalone parent activity and in zot6 it is a followup/child of the original activity.
// For public reshares, some comments to the reshare on the zot fork will not make it to zot6
// due to these different message models. This cannot be prevented at this time.
if($packet_type === 'activity' && $activity['type'] === 'Announce' && intval($target_item['item_private'])) {
continue;
}
$packet = Libzot::build_packet($channel,$packet_type,$zenv,$activity,'activitystreams',(($private) ? $hub['hubloc_sitekey'] : null),$hub['site_crypto']); $packet = Libzot::build_packet($channel,$packet_type,$zenv,$activity,'activitystreams',(($private) ? $hub['hubloc_sitekey'] : null),$hub['site_crypto']);
} }
else { else {

View File

@ -35,6 +35,9 @@ class Activity {
if($x['type'] === ACTIVITY_OBJ_EVENT) { if($x['type'] === ACTIVITY_OBJ_EVENT) {
return self::fetch_event($x); return self::fetch_event($x);
} }
if($x['type'] === ACTIVITY_OBJ_PHOTO) {
return self::fetch_image($x);
}
return $x; return $x;
@ -102,6 +105,29 @@ class Activity {
} }
} }
static function fetch_image($x) {
$ret = [
'type' => 'Image',
'id' => $x['id'],
'name' => $x['title'],
'content' => bbcode($x['body']),
'source' => [ 'mediaType' => 'text/bbcode', 'content' => $x['body'] ],
'published' => datetime_convert('UTC','UTC',$x['created'],ATOM_TIME),
'updated' => datetime_convert('UTC','UTC', $x['edited'],ATOM_TIME),
'url' => [
'type' => 'Link',
'mediaType' => $x['link'][0]['type'],
'href' => $x['link'][0]['href'],
'width' => $x['link'][0]['width'],
'height' => $x['link'][0]['height']
]
];
return $ret;
}
static function fetch_event($x) { static function fetch_event($x) {
// convert old Zot event objects to ActivityStreams Event objects // convert old Zot event objects to ActivityStreams Event objects
@ -210,7 +236,7 @@ class Activity {
$ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid'])); $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
if($i['title']) if($i['title'])
$ret['title'] = bbcode($i['title']); $ret['name'] = $i['title'];
$ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME); $ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME);
if($i['created'] !== $i['edited']) if($i['created'] !== $i['edited'])
@ -385,10 +411,17 @@ class Activity {
$ret['type'] = 'Tombstone'; $ret['type'] = 'Tombstone';
$ret['formerType'] = self::activity_obj_mapper($i['obj_type']); $ret['formerType'] = self::activity_obj_mapper($i['obj_type']);
$ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid'])); $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/item/' . urlencode($i['mid']));
$actor = self::encode_person($i['author'],false);
if($actor)
$ret['actor'] = $actor;
else
return [];
return $ret; return $ret;
} }
$ret['type'] = self::activity_mapper($i['verb']); $ret['type'] = self::activity_mapper($i['verb']);
$ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid'])); $ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid']));
if($i['title']) if($i['title'])
@ -460,6 +493,10 @@ class Activity {
if(! is_array($i['obj'])) { if(! is_array($i['obj'])) {
$i['obj'] = json_decode($i['obj'],true); $i['obj'] = json_decode($i['obj'],true);
} }
if($i['obj']['type'] === ACTIVITY_OBJ_PHOTO) {
$i['obj']['id'] = $i['id'];
}
$obj = self::encode_object($i['obj']); $obj = self::encode_object($i['obj']);
if($obj) if($obj)
$ret['object'] = $obj; $ret['object'] = $obj;
@ -1374,6 +1411,9 @@ class Activity {
$s['owner_xchan'] = $act->actor['id']; $s['owner_xchan'] = $act->actor['id'];
$s['author_xchan'] = $act->actor['id']; $s['author_xchan'] = $act->actor['id'];
// ensure we store the original actor
self::actor_store($act->actor['id'],$act->actor);
$s['mid'] = $act->obj['id']; $s['mid'] = $act->obj['id'];
$s['parent_mid'] = $act->parent_id; $s['parent_mid'] = $act->parent_id;
@ -1446,7 +1486,8 @@ class Activity {
$s['verb'] = self::activity_decode_mapper($act->type); $s['verb'] = self::activity_decode_mapper($act->type);
if($act->type === 'Tombstone') {
if($act->type === 'Tombstone' || ($act->type === 'Create' && $act->obj['type'] === 'Tombstone')) {
$s['item_deleted'] = 1; $s['item_deleted'] = 1;
} }
@ -1455,7 +1496,6 @@ class Activity {
$s['obj_type'] = ACTIVITY_OBJ_COMMENT; $s['obj_type'] = ACTIVITY_OBJ_COMMENT;
} }
if($act->obj['type'] === 'Event') { if($act->obj['type'] === 'Event') {
$s['obj'] = []; $s['obj'] = [];
$s['obj']['asld'] = $act->obj; $s['obj']['asld'] = $act->obj;
@ -1609,7 +1649,9 @@ class Activity {
} }
if($act->obj['type'] === 'Image') { // avoid double images from hubzilla to zap/osada
if($act->obj['type'] === 'Image' && strpos($s['body'],'zrl=') === false) {
$ptr = null; $ptr = null;

View File

@ -1,32 +1,34 @@
<?php /** @file */ <?php
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
/**
* Apps
*
*/
require_once('include/plugin.php'); require_once('include/plugin.php');
require_once('include/channel.php'); require_once('include/channel.php');
/**
* @brief Apps class.
*
*/
class Apps { class Apps {
static public $available_apps = null; static public $available_apps = null;
static public $installed_apps = null; static public $installed_apps = null;
static public $base_apps = null; static public $base_apps = null;
/**
* @brief
*
* @param boolean $translate (optional) default true
* @return array
*/
static public function get_system_apps($translate = true) { static public function get_system_apps($translate = true) {
$ret = [];
$ret = array();
if(is_dir('apps')) if(is_dir('apps'))
$files = glob('apps/*.apd'); $files = glob('apps/*.apd');
else else
$files = glob('app/*.apd'); $files = glob('app/*.apd');
if($files) { if($files) {
foreach($files as $f) { foreach($files as $f) {
$x = self::parse_app_description($f,$translate); $x = self::parse_app_description($f,$translate);
@ -50,10 +52,13 @@ class Apps {
} }
} }
/**
* @hooks get_system_apps
* Hook to manipulate the system apps array.
*/
call_hooks('get_system_apps', $ret); call_hooks('get_system_apps', $ret);
return $ret; return $ret;
} }
static public function get_base_apps() { static public function get_base_apps() {
@ -72,7 +77,13 @@ class Apps {
'Mail', 'Mail',
'Profile Photo' 'Profile Photo'
]); ]);
/**
* @hooks get_base_apps
* Hook to manipulate the base apps array.
*/
call_hooks('get_base_apps', $x); call_hooks('get_base_apps', $x);
return $x; return $x;
} }
@ -106,6 +117,7 @@ class Apps {
// $id will be boolean true or false to install an app, or an integer id to update an existing app // $id will be boolean true or false to install an app, or an integer id to update an existing app
if($id === false) if($id === false)
continue; continue;
if($id !== true) { if($id !== true) {
// if we already installed this app, but it changed, preserve any categories we created // if we already installed this app, but it changed, preserve any categories we created
$s = EMPTY_STR; $s = EMPTY_STR;
@ -126,7 +138,6 @@ class Apps {
$app['guid'] = hash('whirlpool',$app['name']); $app['guid'] = hash('whirlpool',$app['name']);
$app['system'] = 1; $app['system'] = 1;
self::app_install(local_channel(),$app); self::app_install(local_channel(),$app);
} }
} }
} }
@ -134,8 +145,10 @@ class Apps {
/** /**
* Install the system app if no system apps have been installed, or if a new system app * Install the system app if no system apps have been installed, or if a new system app
* is discovered, or if the version of a system app changes. * is discovered, or if the version of a system app changes.
*
* @param array $app
* @return boolean|int
*/ */
static public function check_install_system_app($app) { static public function check_install_system_app($app) {
if((! is_array(self::$available_apps)) || (! count(self::$available_apps))) { if((! is_array(self::$available_apps)) || (! count(self::$available_apps))) {
return true; return true;
@ -159,14 +172,13 @@ class Apps {
return $notfound; return $notfound;
} }
/** /**
* Install the system app if no system apps have been installed, or if a new system app * Install the personal app if no personal apps have been installed, or if a new personal app
* is discovered, or if the version of a system app changes. * is discovered, or if the version of a personal app changes.
*
* @param array $app
* @return boolean|int
*/ */
static public function check_install_personal_app($app) { static public function check_install_personal_app($app) {
$installed = false; $installed = false;
foreach(self::$installed_apps as $iapp) { foreach(self::$installed_apps as $iapp) {
@ -189,20 +201,25 @@ class Apps {
return strcasecmp($a['name'],$b['name']); return strcasecmp($a['name'],$b['name']);
} }
/**
* @brief Parse app description.
*
* @param string $f filename
* @param boolean $translate (optional) default true
* @return boolean|array
*/
static public function parse_app_description($f, $translate = true) { static public function parse_app_description($f, $translate = true) {
$ret = [];
$ret = array(); $matches = [];
$baseurl = z_root(); $baseurl = z_root();
$channel = \App::get_channel(); //$channel = \App::get_channel();
$address = (($channel) ? $channel['channel_address'] : ''); //$address = (($channel) ? $channel['channel_address'] : '');
//future expansion //future expansion
$observer = \App::get_observer(); $observer = \App::get_observer();
$lines = @file($f); $lines = @file($f);
if($lines) { if($lines) {
foreach($lines as $x) { foreach($lines as $x) {
@ -292,8 +309,10 @@ class Apps {
if($ret) { if($ret) {
if($translate) if($translate)
self::translate_system_apps($ret); self::translate_system_apps($ret);
return $ret; return $ret;
} }
return false; return false;
} }
@ -301,6 +320,7 @@ class Apps {
static public function translate_system_apps(&$arr) { static public function translate_system_apps(&$arr) {
$apps = array( $apps = array(
'Apps' => t('Apps'), 'Apps' => t('Apps'),
'Affinity Tool' => t('Affinity Tool'),
'Articles' => t('Articles'), 'Articles' => t('Articles'),
'Cards' => t('Cards'), 'Cards' => t('Cards'),
'Admin' => t('Site Admin'), 'Admin' => t('Site Admin'),
@ -371,25 +391,26 @@ class Apps {
} }
} }
} }
} }
// papp is a portable app
static public function app_render($papp,$mode = 'view') {
/** /**
* modes: * @brief
* view: normal mode for viewing an app via bbcode from a conversation or page *
* @param array $papp
* papp is a portable app
* @param string $mode (optional) default 'view'
* Render modes:
* * \b view: normal mode for viewing an app via bbcode from a conversation or page
* provides install/update button if you're logged in locally * provides install/update button if you're logged in locally
* install: like view but does not display app-bin options if they are present * * \b install: like view but does not display app-bin options if they are present
* list: normal mode for viewing an app on the app page * * \b list: normal mode for viewing an app on the app page
* no buttons are shown * no buttons are shown
* edit: viewing the app page in editing mode provides a delete button * * \b edit: viewing the app page in editing mode provides a delete button
* nav: render apps for app-bin * * \b nav: render apps for app-bin
*
* @return void|string Parsed HTML
*/ */
static public function app_render($papp, $mode = 'view') {
$installed = false; $installed = false;
if(! $papp) if(! $papp)
@ -427,7 +448,6 @@ class Apps {
$papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url']; $papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url'];
foreach($papp as $k => $v) { foreach($papp as $k => $v) {
if(strpos($v,'http') === 0 && $k != 'papp') { if(strpos($v,'http') === 0 && $k != 'papp') {
if(! (local_channel() && strpos($v,z_root()) === 0)) { if(! (local_channel() && strpos($v,z_root()) === 0)) {
@ -592,7 +612,13 @@ class Apps {
return false; return false;
} }
/**
* @brief
*
* @param mixed $uid If not set return false, otherwise no influence
* @param array $app
* @return boolean
*/
static public function can_delete($uid, $app) { static public function can_delete($uid, $app) {
if(! $uid) { if(! $uid) {
return false; return false;
@ -613,7 +639,6 @@ class Apps {
static public function app_destroy($uid,$app) { static public function app_destroy($uid,$app) {
if($uid && $app['guid']) { if($uid && $app['guid']) {
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
@ -622,7 +647,7 @@ class Apps {
if(! intval($x[0]['app_deleted'])) { if(! intval($x[0]['app_deleted'])) {
$x[0]['app_deleted'] = 1; $x[0]['app_deleted'] = 1;
if(self::can_delete($uid,$app)) { if(self::can_delete($uid,$app)) {
$r = q("delete from app where app_id = '%s' and app_channel = %d", q("delete from app where app_id = '%s' and app_channel = %d",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
); );
@ -630,10 +655,15 @@ class Apps {
intval(TERM_OBJ_APP), intval(TERM_OBJ_APP),
intval($x[0]['id']) intval($x[0]['id'])
); );
/**
* @hooks app_destroy
* Called after app entry got removed from database
* and provide app array from database.
*/
call_hooks('app_destroy', $x[0]); call_hooks('app_destroy', $x[0]);
} }
else { else {
$r = q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d", q("update app set app_deleted = 1 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
); );
@ -647,22 +677,23 @@ class Apps {
} }
} }
} }
} }
/**
* @brief Undelete a system app.
*
* @param int $uid
* @param array $app
*/
static public function app_undestroy($uid, $app) { static public function app_undestroy($uid, $app) {
// undelete a system app
if($uid && $app['guid']) { if($uid && $app['guid']) {
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1", $x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
); );
if($x) { if($x) {
if($x[0]['app_system']) { if($x[0]['app_system']) {
$r = q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d", q("update app set app_deleted = 0 where app_id = '%s' and app_channel = %d",
dbesc($app['guid']), dbesc($app['guid']),
intval($uid) intval($uid)
); );
@ -671,6 +702,14 @@ class Apps {
} }
} }
/**
* @brief
*
* @param int $uid
* @param array $app
* @param string $term
* @return void
*/
static public function app_feature($uid, $app, $term) { static public function app_feature($uid, $app, $term) {
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
dbesc($app['guid']), dbesc($app['guid']),
@ -695,6 +734,14 @@ class Apps {
} }
} }
/**
* @brief
*
* @param int $uid
* @param array $app
* @param boolean $bypass_filter (optional) default false
* @return boolean
*/
static public function app_installed($uid, $app, $bypass_filter = false) { static public function app_installed($uid, $app, $bypass_filter = false) {
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1", $r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
@ -707,11 +754,17 @@ class Apps {
'app' => $app, 'app' => $app,
'installed' => $r 'installed' => $r
]; ];
/**
* @hooks app_installed_filter
* * \e int \b uid
* * \e array \b app
* * \e mixed \b installed - return value
*/
call_hooks('app_installed_filter', $filter_arr); call_hooks('app_installed_filter', $filter_arr);
$r = $filter_arr['installed']; $r = $filter_arr['installed'];
} }
return(($r) ? true : false);
return(($r) ? true : false);
} }
@ -727,11 +780,17 @@ class Apps {
'app'=>$app, 'app'=>$app,
'installed'=>$r 'installed'=>$r
]; ];
/**
* @hooks addon_app_installed_filter
* * \e int \b uid
* * \e array \b app
* * \e mixed \b installed - return value
*/
call_hooks('addon_app_installed_filter', $filter_arr); call_hooks('addon_app_installed_filter', $filter_arr);
$r = $filter_arr['installed']; $r = $filter_arr['installed'];
} }
return(($r) ? true : false);
return(($r) ? true : false);
} }
static public function system_app_installed($uid,$app,$bypass_filter=false) { static public function system_app_installed($uid,$app,$bypass_filter=false) {
@ -746,28 +805,39 @@ class Apps {
'app'=>$app, 'app'=>$app,
'installed'=>$r 'installed'=>$r
]; ];
/**
* @hooks system_app_installed_filter
* * \e int \b uid
* * \e array \b app
* * \e mixed \b installed - return value
*/
call_hooks('system_app_installed_filter', $filter_arr); call_hooks('system_app_installed_filter', $filter_arr);
$r = $filter_arr['installed']; $r = $filter_arr['installed'];
} }
return(($r) ? true : false);
return(($r) ? true : false);
} }
/**
* @brief
*
* @param int $uid
* @param boolean $deleted
* @param array $cats
* @return boolean|array
*/
static public function app_list($uid, $deleted = false, $cats = []) { static public function app_list($uid, $deleted = false, $cats = []) {
if($deleted) if($deleted)
$sql_extra = ""; $sql_extra = '';
else else
$sql_extra = " and app_deleted = 0 "; $sql_extra = ' and app_deleted = 0 ';
if($cats) { if($cats) {
$cat_sql_extra = ' and ( ';
$cat_sql_extra = " and ( ";
foreach($cats as $cat) { foreach($cats as $cat) {
if(strpos($cat_sql_extra, 'term')) if(strpos($cat_sql_extra, 'term'))
$cat_sql_extra .= "or "; $cat_sql_extra .= 'or ';
$cat_sql_extra .= "term = '" . dbesc($cat) . "' "; $cat_sql_extra .= "term = '" . dbesc($cat) . "' ";
} }
@ -779,11 +849,13 @@ class Apps {
); );
if(! $r) if(! $r)
return $r; return $r;
$sql_extra .= " and app.id in ( ";
$sql_extra .= ' and app.id in ( ';
$s = ''; $s = '';
foreach($r as $rr) { foreach($r as $rr) {
if($s) if($s)
$s .= ','; $s .= ',';
$s .= intval($rr['oid']); $s .= intval($rr['oid']);
} }
$sql_extra .= $s . ') '; $sql_extra .= $s . ') ';
@ -794,12 +866,26 @@ class Apps {
); );
if($r) { if($r) {
$hookinfo = Array('uid'=>$uid,'deleted'=>$deleted,'cats'=>$cats,'apps'=>$r); $hookinfo = [
'uid' => $uid,
'deleted' => $deleted,
'cats' => $cats,
'apps' => $r,
];
/**
* @hooks app_list
* * \e int \b uid
* * \e boolean \b deleted
* * \e array \b cats
* * \e array \b apps - return value
*/
call_hooks('app_list', $hookinfo); call_hooks('app_list', $hookinfo);
$r = $hookinfo['apps']; $r = $hookinfo['apps'];
for($x = 0; $x < count($r); $x++) { for($x = 0; $x < count($r); $x++) {
if(! $r[$x]['app_system']) if(! $r[$x]['app_system'])
$r[$x]['type'] = 'personal'; $r[$x]['type'] = 'personal';
$r[$x]['term'] = q("select * from term where otype = %d and oid = %d", $r[$x]['term'] = q("select * from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP), intval(TERM_OBJ_APP),
intval($r[$x]['id']) intval($r[$x]['id'])
@ -807,7 +893,7 @@ class Apps {
} }
} }
return($r); return $r;
} }
static public function app_order($uid,$apps,$menu) { static public function app_order($uid,$apps,$menu) {
@ -839,13 +925,14 @@ class Apps {
$ret[] = $ap; $ret[] = $ap;
} }
} }
return $ret;
return $ret;
} }
static function find_app_in_array($name,$arr) { static function find_app_in_array($name,$arr) {
if(! $arr) if(! $arr)
return false; return false;
foreach($arr as $x) { foreach($arr as $x) {
if($x['name'] === $name) { if($x['name'] === $name) {
return $x; return $x;
@ -854,8 +941,16 @@ class Apps {
return false; return false;
} }
/**
* @brief
*
* @param int $uid
* @param int $guid
* @param string $menu
* @return void
*/
static function moveup($uid, $guid, $menu) { static function moveup($uid, $guid, $menu) {
$syslist = array(); $syslist = [];
$conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order');
@ -865,6 +960,7 @@ class Apps {
$papp = self::app_encode($li); $papp = self::app_encode($li);
if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false)
continue; continue;
$syslist[] = $papp; $syslist[] = $papp;
} }
} }
@ -877,8 +973,6 @@ class Apps {
if(! $syslist) if(! $syslist)
return; return;
$newlist = [];
foreach($syslist as $k => $li) { foreach($syslist as $k => $li) {
if($li['guid'] === $guid) { if($li['guid'] === $guid) {
$position = $k; $position = $k;
@ -887,6 +981,7 @@ class Apps {
} }
if(! $position) if(! $position)
return; return;
$dest_position = $position - 1; $dest_position = $position - 1;
$saved = $syslist[$dest_position]; $saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position]; $syslist[$dest_position] = $syslist[$position];
@ -898,11 +993,18 @@ class Apps {
} }
set_pconfig($uid,'system',$conf,implode(',',$narr)); set_pconfig($uid,'system',$conf,implode(',',$narr));
} }
/**
* @brief
*
* @param int $uid
* @param int $guid
* @param string $menu
* @return void
*/
static function movedown($uid, $guid, $menu) { static function movedown($uid, $guid, $menu) {
$syslist = array(); $syslist = [];
$conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order'); $conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order');
@ -912,6 +1014,7 @@ class Apps {
$papp = self::app_encode($li); $papp = self::app_encode($li);
if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false) if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false)
continue; continue;
$syslist[] = $papp; $syslist[] = $papp;
} }
} }
@ -924,8 +1027,6 @@ class Apps {
if(! $syslist) if(! $syslist)
return; return;
$newlist = [];
foreach($syslist as $k => $li) { foreach($syslist as $k => $li) {
if($li['guid'] === $guid) { if($li['guid'] === $guid) {
$position = $k; $position = $k;
@ -934,6 +1035,7 @@ class Apps {
} }
if($position >= count($syslist) - 1) if($position >= count($syslist) - 1)
return; return;
$dest_position = $position + 1; $dest_position = $position + 1;
$saved = $syslist[$dest_position]; $saved = $syslist[$dest_position];
$syslist[$dest_position] = $syslist[$position]; $syslist[$dest_position] = $syslist[$position];
@ -945,7 +1047,6 @@ class Apps {
} }
set_pconfig($uid,'system',$conf,implode(',',$narr)); set_pconfig($uid,'system',$conf,implode(',',$narr));
} }
static public function app_decode($s) { static public function app_decode($s) {
@ -953,7 +1054,13 @@ class Apps {
return json_decode($x,true); return json_decode($x,true);
} }
/**
* @brief
*
* @param int $uid
* @param[in,out] array $arr
* @return void
*/
static public function app_macros($uid, &$arr) { static public function app_macros($uid, &$arr) {
if(! intval($uid)) if(! intval($uid))
@ -965,18 +1072,14 @@ class Apps {
//future expansion //future expansion
$observer = \App::get_observer(); //$observer = \App::get_observer();
$arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']); $arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']);
$arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']); $arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']);
} }
static public function app_store($arr) { static public function app_store($arr) {
//logger('app_store: ' . print_r($arr,true)); //logger('app_store: ' . print_r($arr,true));
@ -1160,13 +1263,17 @@ class Apps {
} }
return $ret; return $ret;
} }
/**
* @brief
*
* @param array $app
* @param boolean $embed (optional) default false
* @return array|string
*/
static public function app_encode($app, $embed = false) { static public function app_encode($app, $embed = false) {
$ret = [];
$ret = array();
$ret['type'] = 'personal'; $ret['type'] = 'personal';
@ -1226,12 +1333,12 @@ class Apps {
foreach($app['term'] as $t) { foreach($app['term'] as $t) {
if($s) if($s)
$s .= ','; $s .= ',';
$s .= $t['term']; $s .= $t['term'];
} }
$ret['categories'] = $s; $ret['categories'] = $s;
} }
if(! $embed) if(! $embed)
return $ret; return $ret;
@ -1241,16 +1348,13 @@ class Apps {
unset($ret['categories']); unset($ret['categories']);
$j = json_encode($ret); $j = json_encode($ret);
return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]';
return '[app]' . chunk_split(base64_encode($j),72,"\n") . '[/app]';
} }
static public function papp_encode($papp) { static public function papp_encode($papp) {
return chunk_split(base64_encode(json_encode($papp)),72,"\n"); return chunk_split(base64_encode(json_encode($papp)),72,"\n");
} }
} }

View File

@ -2,11 +2,6 @@
namespace Zotlabs\Lib; namespace Zotlabs\Lib;
/**
* @brief lowlevel implementation of Zot6 protocol.
*
*/
use Zotlabs\Zot6\HTTPSig; use Zotlabs\Zot6\HTTPSig;
use Zotlabs\Access\Permissions; use Zotlabs\Access\Permissions;
use Zotlabs\Access\PermissionLimits; use Zotlabs\Access\PermissionLimits;
@ -14,14 +9,17 @@ use Zotlabs\Daemon\Master;
require_once('include/crypto.php'); require_once('include/crypto.php');
/**
* @brief Lowlevel implementation of Zot6 protocol.
*
*/
class Libzot { class Libzot {
/** /**
* @brief Generates a unique string for use as a zot guid. * @brief Generates a unique string for use as a zot guid.
* *
* Generates a unique string for use as a zot guid using our DNS-based url, the * Generates a unique string for use as a zot guid using our DNS-based url,
* channel nickname and some entropy. * the channel nickname and some entropy.
* The entropy ensures uniqueness against re-installs where the same URL and * The entropy ensures uniqueness against re-installs where the same URL and
* nickname are chosen. * nickname are chosen.
* *
@ -32,9 +30,8 @@ class Libzot {
* immediate universe. * immediate universe.
* *
* @param string $channel_nick a unique nickname of controlling entity * @param string $channel_nick a unique nickname of controlling entity
* @returns string * @return string
*/ */
static function new_uid($channel_nick) { static function new_uid($channel_nick) {
$rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand(); $rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand();
return(base64url_encode(hash('whirlpool', $rawstr, true), true)); return(base64url_encode(hash('whirlpool', $rawstr, true), true));
@ -52,8 +49,8 @@ class Libzot {
* *
* @param string $guid * @param string $guid
* @param string $pubkey * @param string $pubkey
* @return string
*/ */
static function make_xchan_hash($guid, $pubkey) { static function make_xchan_hash($guid, $pubkey) {
return base64url_encode(hash('whirlpool', $guid . $pubkey, true)); return base64url_encode(hash('whirlpool', $guid . $pubkey, true));
} }
@ -65,10 +62,8 @@ class Libzot {
* should only be used by channels which are defined on this hub. * should only be used by channels which are defined on this hub.
* *
* @param string $hash - xchan_hash * @param string $hash - xchan_hash
* @returns array of hubloc (hub location structures) * @return array of hubloc (hub location structures)
*
*/ */
static function get_hublocs($hash) { static function get_hublocs($hash) {
/* Only search for active hublocs - e.g. those that haven't been marked deleted */ /* Only search for active hublocs - e.g. those that haven't been marked deleted */
@ -92,16 +87,17 @@ class Libzot {
* packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check' * packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check'
* @param array $recipients * @param array $recipients
* envelope recipients, array of portable_id's; empty for public posts * envelope recipients, array of portable_id's; empty for public posts
* @param string msg * @param string $msg
* optional message * optional message
* @param string $encoding
* optional encoding, default 'activitystreams'
* @param string $remote_key * @param string $remote_key
* optional public site key of target hub used to encrypt entire packet * optional public site key of target hub used to encrypt entire packet
* NOTE: remote_key and encrypted packets are required for 'auth_check' packets, optional for all others * NOTE: remote_key and encrypted packets are required for 'auth_check' packets, optional for all others
* @param string $methods * @param string $methods
* optional comma separated list of encryption methods @ref self::best_algorithm() * optional comma separated list of encryption methods @ref best_algorithm()
* @returns string json encoded zot packet * @returns string json encoded zot packet
*/ */
static function build_packet($channel, $type = 'activity', $recipients = null, $msg = '', $encoding = 'activitystreams', $remote_key = null, $methods = '') { static function build_packet($channel, $type = 'activity', $recipients = null, $msg = '', $encoding = 'activitystreams', $remote_key = null, $methods = '') {
$sig_method = get_config('system','signature_algorithm','sha256'); $sig_method = get_config('system','signature_algorithm','sha256');
@ -146,11 +142,10 @@ class Libzot {
* @brief Choose best encryption function from those available on both sites. * @brief Choose best encryption function from those available on both sites.
* *
* @param string $methods * @param string $methods
* comma separated list of encryption methods * Comma separated list of encryption methods
* @return string first match from our site method preferences crypto_methods() array * @return string first match from our site method preferences crypto_methods() array
* of a method which is common to both sites; or 'aes256cbc' if no matches are found. * of a method which is common to both sites; or 'aes256cbc' if no matches are found.
*/ */
static function best_algorithm($methods) { static function best_algorithm($methods) {
$x = [ $x = [
@ -164,7 +159,6 @@ class Libzot {
* * \e string \b methods - comma separated list of encryption methods * * \e string \b methods - comma separated list of encryption methods
* * \e string \b result - the algorithm to return * * \e string \b result - the algorithm to return
*/ */
call_hooks('zot_best_algorithm', $x); call_hooks('zot_best_algorithm', $x);
if($x['result']) if($x['result'])
@ -190,7 +184,7 @@ class Libzot {
/** /**
* @brief send a zot message * @brief Send a zot message.
* *
* @see z_post_url() * @see z_post_url()
* *
@ -200,7 +194,6 @@ class Libzot {
* @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements) * @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements)
* @return array see z_post_url() for returned data format * @return array see z_post_url() for returned data format
*/ */
static function zot($url, $data, $channel = null,$crypto = null) { static function zot($url, $data, $channel = null,$crypto = null) {
if($channel) { if($channel) {
@ -227,7 +220,6 @@ class Libzot {
/** /**
* @brief Refreshes after permission changed or friending, etc. * @brief Refreshes after permission changed or friending, etc.
* *
*
* refresh is typically invoked when somebody has changed permissions of a channel and they are notified * refresh is typically invoked when somebody has changed permissions of a channel and they are notified
* to fetch new permissions via a finger/discovery operation. This may result in a new connection * to fetch new permissions via a finger/discovery operation. This may result in a new connection
* (abook entry) being added to a local channel and it may result in auto-permissions being granted. * (abook entry) being added to a local channel and it may result in auto-permissions being granted.
@ -251,7 +243,6 @@ class Libzot {
* * \b true if successful * * \b true if successful
* * otherwise \b false * * otherwise \b false
*/ */
static function refresh($them, $channel = null, $force = false) { static function refresh($them, $channel = null, $force = false) {
logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG); logger('them: ' . print_r($them,true), LOGGER_DATA, LOG_DEBUG);
@ -271,7 +262,7 @@ class Libzot {
// correct hubloc. If this doesn't work we may have to re-write this section to try them all. // correct hubloc. If this doesn't work we may have to re-write this section to try them all.
if(array_key_exists('xchan_addr',$them) && $them['xchan_addr']) { if(array_key_exists('xchan_addr',$them) && $them['xchan_addr']) {
$r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_addr = '%s' order by hubloc_id desc", $r = q("select hubloc_id_url, hubloc_primary from hubloc where hubloc_addr = '%s' and hubloc_network = 'zot6' order by hubloc_id desc",
dbesc($them['xchan_addr']) dbesc($them['xchan_addr'])
); );
} }
@ -317,7 +308,7 @@ class Libzot {
if(! $hsig_valid) { if(! $hsig_valid) {
logger('http signature not valid: ' . print_r($hsig,true)); logger('http signature not valid: ' . print_r($hsig,true));
return $result; return false;
} }
@ -356,7 +347,7 @@ class Libzot {
); );
if($r) { if($r) {
logger('4');
// connection exists // connection exists
// if the dob is the same as what we have stored (disregarding the year), keep the one // if the dob is the same as what we have stored (disregarding the year), keep the one
@ -524,9 +515,13 @@ logger('4');
return false; return false;
} }
/**
* @brief
*
* @param string $sender
* @param string $site_id
* @return null|array
*/
static function valid_hub($sender, $site_id) { static function valid_hub($sender, $site_id) {
$r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_site_id = '%s' limit 1", $r = q("select hubloc.*, site.site_crypto from hubloc left join site on hubloc_url = site_url where hubloc_hash = '%s' and hubloc_site_id = '%s' limit 1",
@ -548,7 +543,6 @@ logger('4');
} }
return $r[0]; return $r[0];
} }
/** /**
@ -559,21 +553,14 @@ logger('4');
* origination address. This will fetch the discovery packet of the sender, * origination address. This will fetch the discovery packet of the sender,
* which contains the public key we need to verify our guid and url signatures. * which contains the public key we need to verify our guid and url signatures.
* *
* @param array $arr an associative array which must contain: * @param string $id
* * \e string \b guid => guid of conversant
* * \e string \b guid_sig => guid signed with conversant's private key
* * \e string \b url => URL of the origination hub of this communication
* * \e string \b url_sig => URL signed with conversant's private key
* *
* @return array An associative array with * @return array An associative array with
* * \b success boolean true or false * * \e boolean \b success
* * \b message (optional) error string only if success is false * * \e string \b message (optional, unused) error string only if success is false
*/ */
static function register_hub($id) { static function register_hub($id) {
$id_hash = false;
$valid = false;
$hsig_valid = false; $hsig_valid = false;
$result = [ 'success' => false ]; $result = [ 'success' => false ];
@ -954,8 +941,8 @@ logger('4');
* @param string $hub - url of site we just contacted * @param string $hub - url of site we just contacted
* @param array $arr - output of z_post_url() * @param array $arr - output of z_post_url()
* @param array $outq - The queue structure attached to this request * @param array $outq - The queue structure attached to this request
* @return void
*/ */
static function process_response($hub, $arr, $outq) { static function process_response($hub, $arr, $outq) {
logger('remote: ' . print_r($arr,true),LOGGER_DATA); logger('remote: ' . print_r($arr,true),LOGGER_DATA);
@ -1083,11 +1070,6 @@ logger('4');
* *
* @param array $arr * @param array $arr
* 'pickup' structure returned from remote site * 'pickup' structure returned from remote site
* @param string $sender_url
* the url specified by the sender in the initial communication.
* We will verify the sender and url in each returned message structure and
* also verify that all the messages returned match the site url that we are
* currently processing.
* *
* @returns array * @returns array
* Suitable for logging remotely, enumerating the processing results of each message/recipient combination * Suitable for logging remotely, enumerating the processing results of each message/recipient combination
@ -1095,7 +1077,6 @@ logger('4');
* * [1] => \e string $delivery_status * * [1] => \e string $delivery_status
* * [2] => \e string $address * * [2] => \e string $address
*/ */
static function import($arr) { static function import($arr) {
$env = $arr; $env = $arr;
@ -1175,7 +1156,6 @@ logger('4');
$deliveries = self::public_recips($env,$AS); $deliveries = self::public_recips($env,$AS);
} }
$deliveries = array_unique($deliveries); $deliveries = array_unique($deliveries);
@ -1194,7 +1174,7 @@ logger('4');
//logger($AS->debug()); //logger($AS->debug());
$r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($AS->actor['id']) dbesc($AS->actor['id'])
); );
@ -1203,7 +1183,7 @@ logger('4');
} }
$s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($env['sender']) dbesc($env['sender'])
); );
@ -1218,7 +1198,7 @@ logger('4');
if($private) { if($private) {
$arr['item_private'] = true; $arr['item_private'] = true;
} }
// @fixme - spoofable /// @FIXME - spoofable
if($AS->data['hubloc']) { if($AS->data['hubloc']) {
$arr['item_verified'] = true; $arr['item_verified'] = true;
} }
@ -1252,6 +1232,13 @@ logger('4');
} }
/**
* @brief
*
* @param array $env
* @param object $act
* @return boolean
*/
static function is_top_level($env, $act) { static function is_top_level($env, $act) {
if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) { if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) {
return true; return true;
@ -1295,9 +1282,9 @@ logger('4');
* Some of these will be rejected, but this gives us a place to start. * Some of these will be rejected, but this gives us a place to start.
* *
* @param array $msg * @param array $msg
* @return NULL|array * @param object $act
* @return array
*/ */
static function public_recips($msg, $act) { static function public_recips($msg, $act) {
require_once('include/channel.php'); require_once('include/channel.php');
@ -1497,11 +1484,34 @@ logger('4');
intval($channel['channel_id']) intval($channel['channel_id'])
); );
if ($parent) { if ($parent) {
$allowed = can_comment_on_post($d,$parent[0]); $allowed = can_comment_on_post($sender,$parent[0]);
} }
} }
if ($request) { if ($request) {
// Conversation fetches (e.g. $request == true) take place for
// a) new comments on expired posts
// b) hyperdrive (friend-of-friend) conversations
// c) Repeats of posts by others
// over-ride normal connection permissions for hyperdrive (friend-of-friend) conversations
// (if hyperdrive is enabled) and repeated posts by a friend.
// If $allowed is already true, this is probably the conversation of a direct friend or a
// conversation fetch for a new comment on an expired post
// Comments of all these activities are allowed and will only be rejected (later) if the parent
// doesn't exist.
if ($perm === 'send_stream') {
if (get_pconfig($channel['channel_id'],'system','hyperdrive',false) || $arr['verb'] === ACTIVITY_SHARE) {
$allowed = true; $allowed = true;
}
}
else {
$allowed = true;
}
$friendofriend = true; $friendofriend = true;
} }
@ -1513,10 +1523,12 @@ logger('4');
} }
} }
logger('item: ' . print_r($arr,true), LOGGER_DATA); // logger('item: ' . print_r($arr,true), LOGGER_DATA);
if($arr['mid'] !== $arr['parent_mid']) { if($arr['mid'] !== $arr['parent_mid']) {
logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"'); logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"');
// check source route. // check source route.
// We are only going to accept comments from this sender if the comment has the same route as the top-level-post, // We are only going to accept comments from this sender if the comment has the same route as the top-level-post,
// this is so that permissions mismatches between senders apply to the entire conversation // this is so that permissions mismatches between senders apply to the entire conversation
@ -1537,10 +1549,7 @@ logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"')
// have the copy and we don't want the request to loop. // have the copy and we don't want the request to loop.
// Also don't do this if this comment came from a conversation request packet. // Also don't do this if this comment came from a conversation request packet.
// It's possible that comments are allowed but posting isn't and that could // It's possible that comments are allowed but posting isn't and that could
// cause a conversation fetch loop. We can detect these packets since they are // cause a conversation fetch loop.
// delivered via a 'notify' packet type that has a message_id element in the
// initial zot packet (just like the corresponding 'request' packet type which
// makes the request).
// We'll also check the send_stream permission - because if it isn't allowed, // We'll also check the send_stream permission - because if it isn't allowed,
// the top level post is unlikely to be imported and // the top level post is unlikely to be imported and
// this is just an exercise in futility. // this is just an exercise in futility.
@ -1601,7 +1610,7 @@ logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"')
$arr['route'] = $last_prior_route; $arr['route'] = $last_prior_route;
} }
} }
logger('hey');
$ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'", $ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'",
intval($channel['channel_id']), intval($channel['channel_id']),
dbesc($arr['owner_xchan']) dbesc($arr['owner_xchan'])
@ -1779,14 +1788,14 @@ logger('hey');
logger($AS->debug()); logger($AS->debug());
$r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($AS->actor['id']) dbesc($AS->actor['id'])
); );
if(! $r) { if(! $r) {
$y = import_author_xchan([ 'url' => $AS->actor['id'] ]); $y = import_author_xchan([ 'url' => $AS->actor['id'] ]);
if($y) { if($y) {
$r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $r = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($AS->actor['id']) dbesc($AS->actor['id'])
); );
} }
@ -1809,7 +1818,7 @@ logger('hey');
$arr['author_xchan'] = $r[0]['hubloc_hash']; $arr['author_xchan'] = $r[0]['hubloc_hash'];
} }
$s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' limit 1", $s = q("select hubloc_hash from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($a['signature']['signer']) dbesc($a['signature']['signer'])
); );
@ -1820,7 +1829,8 @@ logger('hey');
$arr['owner_xchan'] = $a['signature']['signer']; $arr['owner_xchan'] = $a['signature']['signer'];
} }
// @fixme - spoofable
/// @FIXME - spoofable
if($AS->data['hubloc']) { if($AS->data['hubloc']) {
$arr['item_verified'] = true; $arr['item_verified'] = true;
} }
@ -1851,8 +1861,8 @@ logger('hey');
* * \e int \b obj_type * * \e int \b obj_type
* * \e int \b mid * * \e int \b mid
* @param int $uid * @param int $uid
* @return void
*/ */
static function remove_community_tag($sender, $arr, $uid) { static function remove_community_tag($sender, $arr, $uid) {
if(! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM))) if(! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM)))
@ -1923,8 +1933,8 @@ logger('hey');
* @param array $orig * @param array $orig
* @param int $uid * @param int $uid
* @param boolean $tag_delivery * @param boolean $tag_delivery
* @return void|array
*/ */
static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) { static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) {
// If this is a comment being updated, remove any privacy information // If this is a comment being updated, remove any privacy information
@ -2143,12 +2153,13 @@ logger('hey');
* @brief Processes delivery of profile. * @brief Processes delivery of profile.
* *
* @see import_directory_profile() * @see import_directory_profile()
*
* @param array $sender an associative array * @param array $sender an associative array
* * \e string \b hash a xchan_hash * * \e string \b hash a xchan_hash
* @param array $arr * @param array $arr
* @param array $deliveries (unused) * @param array $deliveries (unused)
* @return void
*/ */
static function process_profile_delivery($sender, $arr, $deliveries) { static function process_profile_delivery($sender, $arr, $deliveries) {
logger('process_profile_delivery', LOGGER_DEBUG); logger('process_profile_delivery', LOGGER_DEBUG);
@ -2169,6 +2180,7 @@ logger('hey');
* * \e string \b hash a xchan_hash * * \e string \b hash a xchan_hash
* @param array $arr * @param array $arr
* @param array $deliveries (unused) deliveries is irrelevant * @param array $deliveries (unused) deliveries is irrelevant
* @return void
*/ */
static function process_location_delivery($sender, $arr, $deliveries) { static function process_location_delivery($sender, $arr, $deliveries) {
@ -2186,7 +2198,7 @@ logger('hey');
$x = Libsync::sync_locations($xchan,$arr,true); $x = Libsync::sync_locations($xchan,$arr,true);
logger('results: ' . print_r($x,true), LOGGER_DEBUG); logger('results: ' . print_r($x,true), LOGGER_DEBUG);
if($x['changed']) { if($x['changed']) {
$guid = random_string() . '@' . App::get_hostname(); //$guid = random_string() . '@' . App::get_hostname();
Libzotdir::update_modtime($sender,$r[0]['xchan_guid'],$arr['locations'][0]['address'],UPDATE_FLAGS_UPDATED); Libzotdir::update_modtime($sender,$r[0]['xchan_guid'],$arr['locations'][0]['address'],UPDATE_FLAGS_UPDATED);
} }
} }
@ -2210,8 +2222,8 @@ logger('hey');
* *
* @param string $sender_hash A channel hash * @param string $sender_hash A channel hash
* @param array $locations * @param array $locations
* @return void
*/ */
static function check_location_move($sender_hash, $locations) { static function check_location_move($sender_hash, $locations) {
if(! $locations) if(! $locations)
@ -2253,7 +2265,6 @@ logger('hey');
} }
/** /**
* @brief Returns an array with all known distinct hubs for this channel. * @brief Returns an array with all known distinct hubs for this channel.
* *
@ -2262,7 +2273,6 @@ logger('hey');
* * \e string \b channel_hash the hash of the channel * * \e string \b channel_hash the hash of the channel
* @return array an array with associative arrays * @return array an array with associative arrays
*/ */
static function encode_locations($channel) { static function encode_locations($channel) {
$ret = []; $ret = [];
@ -2316,10 +2326,8 @@ logger('hey');
* @brief * @brief
* *
* @param array $arr * @param array $arr
* @param string $pubkey
* @return boolean true if updated or inserted * @return boolean true if updated or inserted
*/ */
static function import_site($arr) { static function import_site($arr) {
if( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig'])) if( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig']))
@ -2594,7 +2602,7 @@ logger('hey');
$feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0); $feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0);
if($ztarget) { if($ztarget) {
$t = q("select * from hubloc where hubloc_id_url = '%s' limit 1", $t = q("select * from hubloc where hubloc_id_url = '%s' and hubloc_network = 'zot6' limit 1",
dbesc($ztarget) dbesc($ztarget)
); );
if($t) { if($t) {
@ -2834,14 +2842,20 @@ logger('hey');
$ret['locations'] = $x; $ret['locations'] = $x;
$ret['site'] = self::site_info(); $ret['site'] = self::site_info();
/**
* @hooks zotinfo
* Hook to manipulate the zotinfo array before it is returned.
*/
call_hooks('zotinfo', $ret); call_hooks('zotinfo', $ret);
return($ret); return $ret;
} }
/**
* @brief Get siteinfo.
*
* @return array
*/
static function site_info() { static function site_info() {
$signing_key = get_config('system','prvkey'); $signing_key = get_config('system','prvkey');
@ -2925,18 +2939,16 @@ logger('hey');
} }
return $ret['site']; return $ret['site'];
} }
/** /**
* @brief * @brief
* *
* @param array $hub * @param array $hub
* @param string $sitekey (optional, default empty) * @param string $site_id (optional, default empty)
* *
* @return string hubloc_url * @return string hubloc_url
*/ */
static function update_hub_connected($hub, $site_id = '') { static function update_hub_connected($hub, $site_id = '') {
if ($site_id) { if ($site_id) {
@ -2995,12 +3007,21 @@ logger('hey');
return $hub['hubloc_url']; return $hub['hubloc_url'];
} }
/**
* @brief
*
* @param string $data
* @param string $key
* @param string $alg (optional) default 'sha256'
* @return string
*/
static function sign($data,$key,$alg = 'sha256') { static function sign($data,$key,$alg = 'sha256') {
if(! $key) if(! $key)
return 'no key'; return 'no key';
$sig = ''; $sig = '';
openssl_sign($data,$sig,$key,$alg); openssl_sign($data,$sig,$key,$alg);
return $alg . '.' . base64url_encode($sig); return $alg . '.' . base64url_encode($sig);
} }
@ -3026,11 +3047,14 @@ logger('hey');
return(($verify > 0) ? true : false); return(($verify > 0) ? true : false);
} }
/**
* @brief
*
* @return boolean
*/
static function is_zot_request() { static function is_zot_request() {
$x = getBestSupportedMimeType([ 'application/x-zot+json' ]); $x = getBestSupportedMimeType([ 'application/x-zot+json' ]);
return(($x) ? true : false); return(($x) ? true : false);
} }

View File

@ -112,6 +112,8 @@ class PConfig {
* The configuration key to set * The configuration key to set
* @param string $value * @param string $value
* The value to store * The value to store
* @param string $updated (optional)
* The datetime to store
* @return mixed Stored $value or false * @return mixed Stored $value or false
*/ */
static public function Set($uid, $family, $key, $value, $updated = NULL) { static public function Set($uid, $family, $key, $value, $updated = NULL) {
@ -239,7 +241,9 @@ class PConfig {
* The category of the configuration value * The category of the configuration value
* @param string $key * @param string $key
* The configuration key to delete * The configuration key to delete
* @return mixed * @param string $updated (optional)
* The datetime to store
* @return boolean
*/ */
static public function Delete($uid, $family, $key, $updated = NULL) { static public function Delete($uid, $family, $key, $updated = NULL) {
@ -271,22 +275,13 @@ class PConfig {
dbesc($key) dbesc($key)
); );
// Synchronize delete with clones.
if ($family != 'hz_delpconfig') { if ($family != 'hz_delpconfig') {
$hash = hash('sha256',$family.':'.$key); $hash = hash('sha256',$family.':'.$key);
set_pconfig($uid,'hz_delpconfig',$hash,$updated); set_pconfig($uid,'hz_delpconfig',$hash,$updated);
} }
// Synchronize delete with clones.
if(! array_key_exists('transient', \App::$config[$uid]))
\App::$config[$uid]['transient'] = array();
if(! array_key_exists($family, \App::$config[$uid]['transient']))
\App::$config[$uid]['transient'][$family] = array();
if ($new) {
\App::$config[$uid]['transient'][$family]['pcfgdel:'.$key] = $updated;
}
return $ret; return $ret;
} }

View File

@ -54,6 +54,7 @@ class Share {
if(! $this->item) if(! $this->item)
return $obj; return $obj;
$obj['asld'] = $this->item['mid'];
$obj['type'] = $this->item['obj_type']; $obj['type'] = $this->item['obj_type'];
$obj['id'] = $this->item['mid']; $obj['id'] = $this->item['mid'];
$obj['content'] = $this->item['body']; $obj['content'] = $this->item['body'];

View File

@ -76,7 +76,7 @@ class ThreadItem {
* _ false on failure * _ false on failure
*/ */
public function get_template_data($conv_responses, $thread_level=1) { public function get_template_data($conv_responses, $thread_level=1, $conv_flags = []) {
$result = array(); $result = array();
@ -101,6 +101,7 @@ class ThreadItem {
|| strlen($item['deny_cid']) || strlen($item['deny_gid'])))) || strlen($item['deny_cid']) || strlen($item['deny_gid']))))
? t('Private Message') ? t('Private Message')
: false); : false);
$shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false); $shareable = ((($conv->get_profile_owner() == local_channel() && local_channel()) && ($item['item_private'] != 1)) ? true : false);
// allow an exemption for sharing stuff from your private feeds // allow an exemption for sharing stuff from your private feeds
@ -115,6 +116,19 @@ class ThreadItem {
$privacy_warning = true; $privacy_warning = true;
} }
if ($lock) {
if (($item['mid'] == $item['parent_mid']) && count(get_terms_oftype($item['term'],TERM_FORUM))) {
$privacy_warning = true;
$conv_flags['parent_privacy_warning'] = true;
}
}
$privacy_warning = (isset($conv_flags['parent_privacy_warning'])) ? $conv_flags['parent_privacy_warning'] : $privacy_warning;
if ($lock && $privacy_warning) {
$lock = t('Privacy conflict. Discretion advised.');
}
$mode = $conv->get_mode(); $mode = $conv->get_mode();
switch($item['item_type']) { switch($item['item_type']) {
@ -293,8 +307,15 @@ class ThreadItem {
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike")); $dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
} }
if ($shareable) if ($shareable) {
$share = array( t('Share This'), t('share')); // This actually turns out not to be possible in some protocol stacks without opening up hundreds of new issues.
// Will allow it only for uri resolvable sources.
if(strpos($item['mid'],'http') === 0) {
$share = []; //Not yet ready for primetime
//$share = array( t('Repeat This'), t('repeat'));
}
$embed = array( t('Share This'), t('share'));
}
$dreport = ''; $dreport = '';
@ -408,12 +429,13 @@ class ThreadItem {
'like' => $like, 'like' => $like,
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''), 'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
'share' => $share, 'share' => $share,
'embed' => $embed,
'rawmid' => $item['mid'], 'rawmid' => $item['mid'],
'plink' => get_plink($item), 'plink' => get_plink($item),
'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''), 'edpost' => $edpost, // ((feature_enabled($conv->get_profile_owner(),'edit_posts')) ? $edpost : ''),
'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts')) ? $star : ''), 'star' => ((feature_enabled($conv->get_profile_owner(),'star_posts') && ($item['item_type'] == ITEM_TYPE_POST)) ? $star : ''),
'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''), 'tagger' => ((feature_enabled($conv->get_profile_owner(),'commtag')) ? $tagger : ''),
'filer' => ((feature_enabled($conv->get_profile_owner(),'filing')) ? $filer : ''), 'filer' => ((feature_enabled($conv->get_profile_owner(),'filing') && ($item['item_type'] == ITEM_TYPE_POST)) ? $filer : ''),
'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''), 'bookmark' => (($conv->get_profile_owner() == local_channel() && local_channel() && $has_bookmarks) ? t('Save Bookmarks') : ''),
'addtocal' => (($has_event) ? t('Add to Calendar') : ''), 'addtocal' => (($has_event) ? t('Add to Calendar') : ''),
'drop' => $drop, 'drop' => $drop,
@ -470,7 +492,7 @@ class ThreadItem {
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) { if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
foreach($children as $child) { foreach($children as $child) {
$result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1); $result['children'][] = $child->get_template_data($conv_responses, $thread_level + 1,$conv_flags);
} }
// Collapse // Collapse
if(($nb_children > $visible_comments) || ($thread_level > 1)) { if(($nb_children > $visible_comments) || ($thread_level > 1)) {

View File

@ -0,0 +1,53 @@
<?php
namespace Zotlabs\Lib;
class ThreadListener {
static public function store($target_id,$portable_id,$ltype = 0) {
$x = self::fetch($target_id,$portable_id,$ltype = 0);
if(! $x) {
$r = q("insert into listeners ( target_id, portable_id, ltype ) values ( '%s', '%s' , %d ) ",
dbesc($target_id),
dbesc($portable_id),
intval($ltype)
);
}
}
static public function fetch($target_id,$portable_id,$ltype = 0) {
$x = q("select * from listeners where target_id = '%s' and portable_id = '%s' and ltype = %d limit 1",
dbesc($target_id),
dbesc($portable_id),
intval($ltype)
);
if($x) {
return $x[0];
}
return false;
}
static public function fetch_by_target($target_id,$ltype = 0) {
$x = q("select * from listeners where target_id = '%s' and ltype = %d",
dbesc($target_id),
intval($ltype)
);
return $x;
}
static public function delete_by_target($target_id, $ltype = 0) {
return q("delete from listeners where target_id = '%s' and ltype = %d",
dbesc($target_id),
intval($ltype)
);
}
static public function delete_by_pid($portable_id, $ltype = 0) {
return q("delete from listeners where portable_id = '%s' and ltype = %d",
dbesc($portable_id),
intval($ltype)
);
}
}

View File

@ -86,7 +86,7 @@ class Admin extends \Zotlabs\Web\Controller {
// list total user accounts, expirations etc. // list total user accounts, expirations etc.
$accounts = array(); $accounts = array();
$r = q("SELECT COUNT(*) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account", $r = q("SELECT COUNT(CASE WHEN account_id > 0 THEN 1 ELSE NULL END) AS total, COUNT(CASE WHEN account_expires > %s THEN 1 ELSE NULL END) AS expiring, COUNT(CASE WHEN account_expires < %s AND account_expires > '%s' THEN 1 ELSE NULL END) AS expired, COUNT(CASE WHEN (account_flags & %d)>0 THEN 1 ELSE NULL END) AS blocked FROM account",
db_utcnow(), db_utcnow(),
db_utcnow(), db_utcnow(),
dbesc(NULL_DATE), dbesc(NULL_DATE),

View File

@ -375,6 +375,9 @@ class Addons {
if($files) { if($files) {
foreach($files as $file) { foreach($files as $file) {
if (is_dir($file)){ if (is_dir($file)){
if($file == 'addon/addon_common/')
continue;
list($tmp, $id) = array_map('trim', explode('/', $file)); list($tmp, $id) = array_map('trim', explode('/', $file));
$info = get_plugin_info($id); $info = get_plugin_info($id);
$enabled = in_array($id,\App::$plugins); $enabled = in_array($id,\App::$plugins);

View File

@ -119,7 +119,7 @@ class Site {
del_config('system', 'admininfo'); del_config('system', 'admininfo');
} else { } else {
require_once('include/text.php'); require_once('include/text.php');
linkify_tags($a, $admininfo, local_channel()); linkify_tags($admininfo, local_channel());
set_config('system', 'admininfo', $admininfo); set_config('system', 'admininfo', $admininfo);
} }
set_config('system','siteinfo',$siteinfo); set_config('system','siteinfo',$siteinfo);

View File

@ -0,0 +1,94 @@
<?php
namespace Zotlabs\Module;
use App;
use Zotlabs\Lib\Apps;
use Zotlabs\Lib\Libsync;
class Affinity extends \Zotlabs\Web\Controller {
function post() {
if(! local_channel())
return;
if(! Apps::system_app_installed(local_channel(),'Affinity Tool'))
return;
check_form_security_token_redirectOnErr('affinity', 'affinity');
$cmax = intval($_POST['affinity_cmax']);
if($cmax < 0 || $cmax > 99)
$cmax = 99;
$cmin = intval($_POST['affinity_cmin']);
if($cmin < 0 || $cmin > 99)
$cmin = 0;
$lock = intval($_POST['affinity_lock']);
set_pconfig(local_channel(),'affinity','cmin',$cmin);
set_pconfig(local_channel(),'affinity','cmax',$cmax);
set_pconfig(local_channel(),'affinity','lock',$lock);
info( t('Affinity Tool settings updated.') . EOL);
Libsync::build_sync_packet();
}
function get() {
if(! local_channel())
return;
$desc = t('This app presents a slider control in your connection editor and also on your network page. The slider represents your degree of friendship (affinity) with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream.');
if(! Apps::system_app_installed(local_channel(),'Affinity Tool')) {
//Do not display any associated widgets at this point
App::$pdl = '';
$o = '<b>' . t('Affinity Tool App') . ' (' . t('Not Installed') . '):</b><br>';
$o .= $desc;
return $o;
}
$text = t('The numbers below represent the minimum and maximum slider default positions for your network/stream page as a percentage.');
$content = '<div class="section-content-info-wrapper">' . $text . '</div>';
$cmax = intval(get_pconfig(local_channel(),'affinity','cmax'));
$cmax = (($cmax) ? $cmax : 99);
$content .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99'))
));
$cmin = intval(get_pconfig(local_channel(),'affinity','cmin'));
$cmin = (($cmin) ? $cmin : 0);
$content .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0'))
));
$lock = intval(get_pconfig(local_channel(),'affinity','lock',1));
$content .= replace_macros(get_markup_template('field_checkbox.tpl'), array(
'$field' => array('affinity_lock', t('Persistent affinity levels'), $lock, t('If disabled the max and min levels will be reset to default after page reload'), ['No','Yes'])
));
$tpl = get_markup_template("settings_addon.tpl");
$o = replace_macros($tpl, array(
'$action_url' => 'affinity',
'$form_security_token' => get_form_security_token("affinity"),
'$title' => t('Affinity Tool Settings'),
'$content' => $content,
'$baseurl' => z_root(),
'$submit' => t('Submit'),
));
return $o;
}
}

View File

@ -10,9 +10,13 @@ require_once('include/channel.php');
require_once('include/conversation.php'); require_once('include/conversation.php');
require_once('include/acl_selectors.php'); require_once('include/acl_selectors.php');
/**
* @brief Provides the Cards module.
*
*/
class Cards extends Controller { class Cards extends Controller {
function init() { public function init() {
if(argc() > 1) if(argc() > 1)
$which = argv(1); $which = argv(1);
@ -20,14 +24,15 @@ class Cards extends Controller {
return; return;
profile_load($which); profile_load($which);
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
* @see \Zotlabs\Web\Controller::get() * @see \\Zotlabs\\Web\\Controller::get()
*
* @return string Parsed HTML from template 'cards.tpl'
*/ */
function get($update = 0, $load = false) { public function get($update = 0, $load = false) {
if(observer_prohibited(true)) { if(observer_prohibited(true)) {
return login(); return login();
@ -99,7 +104,6 @@ class Cards extends Controller {
} }
if(perm_is_allowed($owner, $ob_hash, 'write_pages')) { if(perm_is_allowed($owner, $ob_hash, 'write_pages')) {
$x = [ $x = [

View File

@ -106,7 +106,7 @@ class Chanview extends \Zotlabs\Web\Controller {
if (\App::$poi) { if (\App::$poi) {
$url = \App::$poi['xchan_url']; $url = \App::$poi['xchan_url'];
if(\App::$poi['xchan_network'] === 'zot') { if(in_array(\App::$poi['xchan_network'], ['zot', 'zot6'])) {
$is_zot = true; $is_zot = true;
} }
if(local_channel()) { if(local_channel()) {

View File

@ -710,7 +710,7 @@ class Connedit extends \Zotlabs\Web\Controller {
$tpl = get_markup_template("abook_edit.tpl"); $tpl = get_markup_template("abook_edit.tpl");
if(feature_enabled(local_channel(),'affinity')) { if(Apps::system_app_installed(local_channel(),'Affinity Tool')) {
$sections['affinity'] = [ $sections['affinity'] = [
'label' => t('Affinity'), 'label' => t('Affinity'),

View File

@ -179,7 +179,6 @@ class Display extends \Zotlabs\Web\Controller {
$static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0); $static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0);
$simple_update = (($update) ? " AND item_unseen = 1 " : ''); $simple_update = (($update) ? " AND item_unseen = 1 " : '');
if($update && $_SESSION['loadtime']) if($update && $_SESSION['loadtime'])
@ -314,7 +313,7 @@ class Display extends \Zotlabs\Web\Controller {
} }
} }
if(! $r) { if($r === null) {
// in case somebody turned off public access to sys channel content using permissions // in case somebody turned off public access to sys channel content using permissions
// make that content unsearchable by ensuring the owner_xchan can't match // make that content unsearchable by ensuring the owner_xchan can't match
if(! perm_is_allowed($sysid,$observer_hash,'view_stream')) if(! perm_is_allowed($sysid,$observer_hash,'view_stream'))

22
Zotlabs/Module/Embed.php Normal file
View File

@ -0,0 +1,22 @@
<?php
namespace Zotlabs\Module;
require_once('include/security.php');
require_once('include/bbcode.php');
class Embed extends \Zotlabs\Web\Controller {
function init() {
$post_id = ((argc() > 1) ? intval(argv(1)) : 0);
if(! $post_id)
killme();
echo '[share=' . $post_id . '][/share]';
killme();
}
}

View File

@ -3,8 +3,10 @@
namespace Zotlabs\Module; namespace Zotlabs\Module;
/** /**
* @brief * @brief Embedphoto endpoint.
* *
* Provide an AJAX endpoint to fill the embedPhotoModal with folders and photos
* selection.
*/ */
class Embedphotos extends \Zotlabs\Web\Controller { class Embedphotos extends \Zotlabs\Web\Controller {
@ -13,11 +15,11 @@ class Embedphotos extends \Zotlabs\Web\Controller {
} }
/** /**
* @brief This is the POST destination for the embedphotos button.
* *
* This is the POST destination for the embedphotos button * @return string A JSON string.
*
*/ */
function post() { public function post() {
if (argc() > 1 && argv(1) === 'album') { if (argc() > 1 && argv(1) === 'album') {
// API: /embedphotos/album // API: /embedphotos/album
$name = (x($_POST, 'name') ? $_POST['name'] : null ); $name = (x($_POST, 'name') ? $_POST['name'] : null );
@ -29,7 +31,7 @@ class Embedphotos extends \Zotlabs\Web\Controller {
} }
if (argc() > 1 && argv(1) === 'albumlist') { if (argc() > 1 && argv(1) === 'albumlist') {
// API: /embedphotos/albumlist // API: /embedphotos/albumlist
$album_list = $this->embedphotos_album_list($a); $album_list = $this->embedphotos_album_list();
json_return_and_die(array('status' => true, 'albumlist' => $album_list)); json_return_and_die(array('status' => true, 'albumlist' => $album_list));
} }
if (argc() > 1 && argv(1) === 'photolink') { if (argc() > 1 && argv(1) === 'photolink') {
@ -38,7 +40,7 @@ class Embedphotos extends \Zotlabs\Web\Controller {
if (!$href) { if (!$href) {
json_return_and_die(array('errormsg' => 'Error retrieving link ' . $href, 'status' => false)); json_return_and_die(array('errormsg' => 'Error retrieving link ' . $href, 'status' => false));
} }
$resource_id = array_pop(explode("/", $href)); $resource_id = array_pop(explode('/', $href));
$r = q("SELECT obj from item where resource_type = 'photo' and resource_id = '%s' limit 1", $r = q("SELECT obj from item where resource_type = 'photo' and resource_id = '%s' limit 1",
dbesc($resource_id) dbesc($resource_id)
); );
@ -58,47 +60,50 @@ class Embedphotos extends \Zotlabs\Web\Controller {
} }
/** /**
* Copied from include/widgets.php::widget_album() with a modification to get the profile_uid from * @brief Get photos from an album.
* the input array as in widget_item()
* *
* @param array $args * @see \\Zotlabs\\Widget\\Album::widget()
* @return string with HTML *
* @param array $args associative array with
* * \e array \b channel
* * \e string \b album
* @return string with HTML code from 'photo_album.tpl'
*/ */
function embedphotos_widget_album($args) { protected function embedphotos_widget_album($args) {
$channel_id = 0; $channel_id = 0;
if(array_key_exists('channel', $args))
if (array_key_exists('channel', $args)) {
$channel = $args['channel']; $channel = $args['channel'];
$channel_id = intval($channel['channel_id']); $channel_id = intval($channel['channel_id']);
}
if (! $channel_id) if (! $channel_id)
$channel_id = \App::$profile_uid; $channel_id = \App::$profile_uid;
if (! $channel_id) if (! $channel_id)
return ''; return '';
$owner_uid = $channel_id;
require_once('include/security.php'); require_once('include/security.php');
$sql_extra = permissions_sql($channel_id); $sql_extra = permissions_sql($channel_id);
if (! perm_is_allowed($channel_id, get_observer_hash(), 'view_storage')) if (! perm_is_allowed($channel_id, get_observer_hash(), 'view_storage'))
return ''; return '';
if($args['album']) if (isset($args['album']))
$album = (($args['album'] === '/') ? '' : $args['album']); $album = (($args['album'] === '/') ? '' : $args['album']);
if($args['title']) if (isset($args['title']))
$title = $args['title']; $title = $args['title'];
/** /**
* This may return incorrect permissions if you have multiple directories of the same name. * @note This may return incorrect permissions if you have multiple directories of the same name.
* It is a limitation of the photo table using a name for a photo album instead of a folder hash * It is a limitation of the photo table using a name for a photo album instead of a folder hash
*/ */
if ($album) { if ($album) {
require_once('include/attach.php'); require_once('include/attach.php');
$x = q("select hash from attach where filename = '%s' and uid = %d limit 1", $x = q("select hash from attach where filename = '%s' and uid = %d limit 1",
dbesc($album), dbesc($album),
intval($owner_uid) intval($channel_id)
); );
if ($x) { if ($x) {
$y = attach_can_view_folder($owner_uid,get_observer_hash(),$x[0]['hash']); $y = attach_can_view_folder($channel_id, get_observer_hash(), $x[0]['hash']);
if (! $y) if (! $y)
return ''; return '';
} }
@ -110,13 +115,13 @@ class Embedphotos extends \Zotlabs\Web\Controller {
(SELECT resource_id, max(imgscale) imgscale FROM photo WHERE uid = %d AND album = '%s' AND imgscale <= 4 AND photo_usage IN ( %d, %d ) $sql_extra GROUP BY resource_id) ph (SELECT resource_id, max(imgscale) imgscale FROM photo WHERE uid = %d AND album = '%s' AND imgscale <= 4 AND photo_usage IN ( %d, %d ) $sql_extra GROUP BY resource_id) ph
ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale) ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale)
ORDER BY created $order", ORDER BY created $order",
intval($owner_uid), intval($channel_id),
dbesc($album), dbesc($album),
intval(PHOTO_NORMAL), intval(PHOTO_NORMAL),
intval(PHOTO_PROFILE) intval(PHOTO_PROFILE)
); );
$photos = array(); $photos = [];
if (count($r)) { if (count($r)) {
$twist = 'rotright'; $twist = 'rotright';
foreach ($r as $rr) { foreach ($r as $rr) {
@ -125,15 +130,18 @@ class Embedphotos extends \Zotlabs\Web\Controller {
else else
$twist = 'rotright'; $twist = 'rotright';
$ph = photo_factory('');
$phototypes = $ph->supportedTypes();
$ext = $phototypes[$rr['mimetype']]; $ext = $phototypes[$rr['mimetype']];
$imgalt_e = $rr['filename']; $imgalt_e = $rr['filename'];
$desc_e = $rr['description']; $desc_e = $rr['description'];
$imagelink = (z_root() . '/photos/' . \App::$data['channel']['channel_address'] . '/image/' . $rr['resource_id'] $imagelink = (z_root() . '/photos/' . $channel['channel_address'] . '/image/' . $rr['resource_id']
. (($_GET['order'] === 'posted') ? '?f=&order=posted' : '')); . (($_GET['order'] === 'posted') ? '?f=&order=posted' : ''));
$photos[] = array( $photos[] = [
'id' => $rr['id'], 'id' => $rr['id'],
'twist' => ' ' . $twist . rand(2,4), 'twist' => ' ' . $twist . rand(2,4),
'link' => $imagelink, 'link' => $imagelink,
@ -143,35 +151,43 @@ class Embedphotos extends \Zotlabs\Web\Controller {
'desc'=> $desc_e, 'desc'=> $desc_e,
'ext' => $ext, 'ext' => $ext,
'hash'=> $rr['resource_id'], 'hash'=> $rr['resource_id'],
'unknown' => t('Unknown') 'unknown' => t('Unknown'),
); ];
} }
} }
$tpl = get_markup_template('photo_album.tpl'); $tpl = get_markup_template('photo_album.tpl');
$o .= replace_macros($tpl, array( $o = replace_macros($tpl, [
'$photos' => $photos, '$photos' => $photos,
'$album' => (($title) ? $title : $album), '$album' => (($title) ? $title : $album),
'$album_id' => rand(), '$album_id' => rand(),
'$album_edit' => array(t('Edit Album'), $album_edit), '$album_edit' => array(t('Edit Album'), false),
'$can_post' => false, '$can_post' => false,
'$upload' => array(t('Upload'), z_root() . '/photos/' . \App::$profile['channel_address'] . '/upload/' . bin2hex($album)), '$upload' => array(t('Upload'), z_root() . '/photos/' . \App::$profile['channel_address'] . '/upload/' . bin2hex($album)),
'$order' => false, '$order' => false,
'$upload_form' => $upload_form, '$upload_form' => '',
'$no_fullscreen_btn' => true '$no_fullscreen_btn' => true,
)); ]);
return $o; return $o;
} }
function embedphotos_album_list($a) { /**
* @brief Get albums observer is allowed to see.
*
* @see photos_albums_list()
*
* @return NULL|array
*/
protected function embedphotos_album_list() {
require_once('include/photos.php'); require_once('include/photos.php');
$p = photos_albums_list(\App::get_channel(), \App::get_observer()); $p = photos_albums_list(\App::get_channel(), \App::get_observer());
if ($p['success']) { if ($p['success']) {
return $p['albums']; return $p['albums'];
} else {
return null;
} }
return null;
} }
} }

View File

@ -97,8 +97,8 @@ class Events extends \Zotlabs\Web\Controller {
$type = escape_tags(trim($_POST['type'])); $type = escape_tags(trim($_POST['type']));
require_once('include/text.php'); require_once('include/text.php');
linkify_tags($a, $desc, local_channel()); linkify_tags($desc, local_channel());
linkify_tags($a, $location, local_channel()); linkify_tags($location, local_channel());
//$action = ($event_hash == '') ? 'new' : "event/" . $event_hash; //$action = ($event_hash == '') ? 'new' : "event/" . $event_hash;

View File

@ -66,6 +66,9 @@ class Group extends Controller {
$groupname = notags(trim($_POST['groupname'])); $groupname = notags(trim($_POST['groupname']));
$public = intval($_POST['public']); $public = intval($_POST['public']);
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ];
call_hooks ('privacygroup_extras_post',$hookinfo);
if((strlen($groupname)) && (($groupname != $group['gname']) || ($public != $group['visible']))) { if((strlen($groupname)) && (($groupname != $group['gname']) || ($public != $group['visible']))) {
$r = q("UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d", $r = q("UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d",
dbesc($groupname), dbesc($groupname),
@ -76,8 +79,6 @@ class Group extends Controller {
if($r) if($r)
info( t('Privacy group updated.') . EOL ); info( t('Privacy group updated.') . EOL );
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ];
call_hooks ('privacygroup_extras_post',$hookinfo);
build_sync_packet(local_channel(),null,true); build_sync_packet(local_channel(),null,true);
} }
@ -242,6 +243,10 @@ class Group extends Controller {
} }
} }
$hookinfo = [ 'pgrp_extras' => '', 'group'=>$group['id'] ];
call_hooks ('privacygroup_extras',$hookinfo);
$pgrp_extras = $hookinfo['pgrp_extras'];
$context = $context + array( $context = $context + array(
'$title' => sprintf(t('Privacy Group: %s'), $group['gname']), '$title' => sprintf(t('Privacy Group: %s'), $group['gname']),
'$details_label' => t('Edit'), '$details_label' => t('Edit'),
@ -252,6 +257,7 @@ class Group extends Controller {
'$form_security_token_edit' => get_form_security_token('group_edit'), '$form_security_token_edit' => get_form_security_token('group_edit'),
'$delete' => t('Delete Group'), '$delete' => t('Delete Group'),
'$form_security_token_drop' => get_form_security_token("group_drop"), '$form_security_token_drop' => get_form_security_token("group_drop"),
'$pgrp_extras' => $pgrp_extras,
); );
} }
@ -295,6 +301,7 @@ class Group extends Controller {
$context['$groupeditor'] = $groupeditor; $context['$groupeditor'] = $groupeditor;
$context['$desc'] = t('Click a channel to toggle membership'); $context['$desc'] = t('Click a channel to toggle membership');
$context['$pgrp_extras'] = $pgrp_extras;
if($change) { if($change) {
$tpl = get_markup_template('groupeditor.tpl'); $tpl = get_markup_template('groupeditor.tpl');

View File

@ -6,6 +6,13 @@ use Zotlabs\Lib\IConfig;
use Zotlabs\Lib\Enotify; use Zotlabs\Lib\Enotify;
use Zotlabs\Web\Controller; use Zotlabs\Web\Controller;
use Zotlabs\Daemon\Master; use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Activity;
use Zotlabs\Lib\ActivityStreams;
use Zotlabs\Lib\LDSignatures;
use Zotlabs\Zot6\HTTPSig;
use Zotlabs\Lib\Libzot;
use Zotlabs\Lib\ThreadListener;
use App;
require_once('include/crypto.php'); require_once('include/crypto.php');
require_once('include/items.php'); require_once('include/items.php');
@ -30,6 +37,144 @@ require_once('include/security.php');
class Item extends Controller { class Item extends Controller {
function init() {
if(Libzot::is_zot_request()) {
$conversation = false;
$item_id = argv(1);
if(! $item_id)
http_status_exit(404, 'Not found');
$portable_id = EMPTY_STR;
$sigdata = HTTPSig::verify(EMPTY_STR);
if($sigdata['portable_id'] && $sigdata['header_valid']) {
$portable_id = $sigdata['portable_id'];
}
$item_normal = " and item.item_hidden = 0 and item.item_type = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_blocked = 0 ";
$sql_extra = item_permissions_sql(0);
$r = q("select * from item where mid = '%s' $item_normal $sql_extra limit 1",
dbesc(z_root() . '/item/' . $item_id)
);
if(! $r) {
$r = q("select * from item where mid = '%s' $item_normal limit 1",
dbesc(z_root() . '/item/' . $item_id)
);
if($r) {
http_status_exit(403, 'Forbidden');
}
http_status_exit(404, 'Not found');
}
$items = q("select parent as item_id from item where mid = '%s' and uid = %d $item_normal $sql_extra ",
dbesc($r[0]['parent_mid']),
intval($r[0]['uid'])
);
if(! $items) {
http_status_exit(404, 'Not found');
}
$r = $items;
$parents_str = ids_to_querystr($r,'item_id');
$items = q("SELECT item.*, item.id AS item_id FROM item WHERE item.parent IN ( %s ) $item_normal $sql_extra ",
dbesc($parents_str)
);
if(! $items) {
http_status_exit(404, 'Not found');
}
$r = $items;
xchan_query($r,true);
$items = fetch_post_tags($r,true);
$observer = App::get_observer();
$parent = $items[0];
$recips = (($parent['owner']['xchan_network'] === 'activitypub') ? get_iconfig($parent['id'],'activitypub','recips', []) : []);
$to = (($recips && array_key_exists('to',$recips) && is_array($recips['to'])) ? $recips['to'] : null);
$nitems = [];
foreach($items as $i) {
$mids = [];
if(intval($i['item_private'])) {
if(! $observer) {
continue;
}
// ignore private reshare, possibly from hubzilla
if($i['verb'] === 'Announce') {
if(! in_array($i['thr_parent'],$mids)) {
$mids[] = $i['thr_parent'];
}
continue;
}
// also ignore any children of the private reshares
if(in_array($i['thr_parent'],$mids)) {
continue;
}
if((! $to) || (! in_array($observer['xchan_url'],$to))) {
continue;
}
}
$nitems[] = $i;
}
if(! $nitems)
http_status_exit(404, 'Not found');
$chan = channelx_by_n($nitems[0]['uid']);
if(! $chan)
http_status_exit(404, 'Not found');
if(! perm_is_allowed($chan['channel_id'],get_observer_hash(),'view_stream'))
http_status_exit(403, 'Forbidden');
$i = Activity::encode_item_collection($nitems,'conversation/' . $item_id,'OrderedCollection',( defined('NOMADIC') ? false : true));
if($portable_id) {
ThreadListener::store(z_root() . '/item/' . $item_id,$portable_id);
}
if(! $i)
http_status_exit(404, 'Not found');
$x = array_merge(['@context' => [
ACTIVITYSTREAMS_JSONLD_REV,
'https://w3id.org/security/v1',
z_root() . ZOT_APSCHEMA_REV
]], $i);
$headers = [];
$headers['Content-Type'] = 'application/x-zot+json' ;
$x['signature'] = LDSignatures::sign($x,$chan);
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];
$h = HTTPSig::create_sig($headers,$chan['channel_prvkey'],channel_url($chan));
HTTPSig::set_headers($h);
echo $ret;
killme();
}
}
function post() { function post() {
// This will change. Figure out who the observer is and whether or not // This will change. Figure out who the observer is and whether or not
@ -553,8 +698,8 @@ class Item extends Controller {
// Look for tags and linkify them // Look for tags and linkify them
$results = linkify_tags($a, $summary, ($uid) ? $uid : $profile_uid); $results = linkify_tags($summary, ($uid) ? $uid : $profile_uid);
$results = linkify_tags($a, $body, ($uid) ? $uid : $profile_uid); $results = linkify_tags($body, ($uid) ? $uid : $profile_uid);
if($results) { if($results) {
@ -639,9 +784,9 @@ class Item extends Controller {
if(preg_match_all('/(\[share=(.*?)\](.*?)\[\/share\])/',$body,$match)) { if(preg_match_all('/(\[share=(.*?)\](.*?)\[\/share\])/',$body,$match)) {
// process share by id // process share by id
$verb = ACTIVITY_SHARE;
$i = 0; $i = 0;
foreach($match[2] as $mtch) { foreach($match[2] as $mtch) {
$reshare = new \Zotlabs\Lib\Share($mtch); $reshare = new \Zotlabs\Lib\Share($mtch);

View File

@ -34,7 +34,7 @@ class Mail extends \Zotlabs\Web\Controller {
} }
else { else {
$body = cleanup_bbcode($body); $body = cleanup_bbcode($body);
$results = linkify_tags($a, $body, local_channel()); $results = linkify_tags($body, local_channel());
if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) { if(preg_match_all('/(\[attachment\](.*?)\[\/attachment\])/',$body,$match)) {
$attachments = array(); $attachments = array();
@ -111,7 +111,7 @@ class Mail extends \Zotlabs\Web\Controller {
} }
require_once('include/text.php'); require_once('include/text.php');
linkify_tags($a, $body, local_channel()); linkify_tags($body, local_channel());
if(! $recipient) { if(! $recipient) {

View File

@ -1,6 +1,8 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use Zotlabs\Lib\Group;
use Zotlabs\Lib\Apps;
use App; use App;
require_once('include/items.php'); require_once('include/items.php');
@ -114,8 +116,8 @@ class Network extends \Zotlabs\Web\Controller {
$def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>'); $def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>');
} }
$default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1)); $default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1));
$default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1)); $default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1));
$cid = ((x($_GET,'cid')) ? intval($_GET['cid']) : 0); $cid = ((x($_GET,'cid')) ? intval($_GET['cid']) : 0);
$star = ((x($_GET,'star')) ? intval($_GET['star']) : 0); $star = ((x($_GET,'star')) ? intval($_GET['star']) : 0);
@ -132,7 +134,7 @@ class Network extends \Zotlabs\Web\Controller {
$deftag = ''; $deftag = '';
if (feature_enabled(local_channel(),'affinity')) { if (Apps::system_app_installed(local_channel(),'Affinity Tool')) {
$affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1)); $affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1));
if ($affinity_locked) { if ($affinity_locked) {
set_pconfig(local_channel(),'affinity','cmin',$cmin); set_pconfig(local_channel(),'affinity','cmin',$cmin);

View File

@ -1,10 +1,13 @@
<?php <?php
namespace Zotlabs\Module; /** @file */ namespace Zotlabs\Module;
use App; use App;
use Zotlabs\Web\Controller; use Zotlabs\Web\Controller;
use Zotlabs\Lib\Apps; use Zotlabs\Lib\Apps;
/**
* @brief Notes Module controller.
*/
class Notes extends Controller { class Notes extends Controller {
function post() { function post() {
@ -40,11 +43,9 @@ class Notes extends Controller {
logger('notes saved.', LOGGER_DEBUG); logger('notes saved.', LOGGER_DEBUG);
json_return_and_die($ret); json_return_and_die($ret);
} }
function get() { function get() {
if(! local_channel()) if(! local_channel())
return EMPTY_STR; return EMPTY_STR;
@ -61,7 +62,6 @@ class Notes extends Controller {
$arr = ['app' => true]; $arr = ['app' => true];
return $w->widget($arr); return $w->widget($arr);
} }
} }

View File

@ -181,7 +181,7 @@ class Oep extends \Zotlabs\Web\Controller {
dbesc($res) dbesc($res)
); );
if($r) { if($r) {
$sql_extra = "and item.id = " . intval($r[0]['iid']) . " "; $sql_extra .= " and item.id = " . intval($r[0]['iid']) . " ";
} }
else { else {
return $ret; return $ret;
@ -194,6 +194,9 @@ class Oep extends \Zotlabs\Web\Controller {
intval(ITEM_TYPE_CARD) intval(ITEM_TYPE_CARD)
); );
if(! $r)
return;
$item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0 $item_normal = " and item.item_hidden = 0 and item.item_type in (0,6) and item.item_deleted = 0
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
and item.item_blocked = 0 "; and item.item_blocked = 0 ";
@ -255,7 +258,6 @@ class Oep extends \Zotlabs\Web\Controller {
if(! $channel) if(! $channel)
return $ret; return $ret;
if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages')) if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages'))
return $ret; return $ret;
@ -265,7 +267,7 @@ class Oep extends \Zotlabs\Web\Controller {
dbesc($res) dbesc($res)
); );
if($r) { if($r) {
$sql_extra = "and item.id = " . intval($r[0]['iid']) . " "; $sql_extra .= " and item.id = " . intval($r[0]['iid']) . " ";
} }
else { else {
return $ret; return $ret;
@ -278,6 +280,9 @@ class Oep extends \Zotlabs\Web\Controller {
intval(ITEM_TYPE_ARTICLE) intval(ITEM_TYPE_ARTICLE)
); );
if(! $r)
return;
$item_normal = " and item.item_hidden = 0 and item.item_type in (0,7) and item.item_deleted = 0 $item_normal = " and item.item_hidden = 0 and item.item_type in (0,7) and item.item_deleted = 0
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0 and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
and item.item_blocked = 0 "; and item.item_blocked = 0 ";

View File

@ -422,7 +422,7 @@ class Photos extends \Zotlabs\Web\Controller {
require_once('include/text.php'); require_once('include/text.php');
$profile_uid = \App::$profile['profile_uid']; $profile_uid = \App::$profile['profile_uid'];
$results = linkify_tags($a, $rawtags, (local_channel()) ? local_channel() : $profile_uid); $results = linkify_tags($rawtags, (local_channel()) ? local_channel() : $profile_uid);
$success = $results['success']; $success = $results['success'];
$post_tags = array(); $post_tags = array();

View File

@ -354,20 +354,20 @@ class Profiles extends \Zotlabs\Web\Controller {
require_once('include/text.php'); require_once('include/text.php');
linkify_tags($a, $likes, local_channel()); linkify_tags($likes, local_channel());
linkify_tags($a, $dislikes, local_channel()); linkify_tags($dislikes, local_channel());
linkify_tags($a, $about, local_channel()); linkify_tags($about, local_channel());
linkify_tags($a, $interest, local_channel()); linkify_tags($interest, local_channel());
linkify_tags($a, $interest, local_channel()); linkify_tags($interest, local_channel());
linkify_tags($a, $contact, local_channel()); linkify_tags($contact, local_channel());
linkify_tags($a, $channels, local_channel()); linkify_tags($channels, local_channel());
linkify_tags($a, $music, local_channel()); linkify_tags($music, local_channel());
linkify_tags($a, $book, local_channel()); linkify_tags($book, local_channel());
linkify_tags($a, $tv, local_channel()); linkify_tags($tv, local_channel());
linkify_tags($a, $film, local_channel()); linkify_tags($film, local_channel());
linkify_tags($a, $romance, local_channel()); linkify_tags($romance, local_channel());
linkify_tags($a, $work, local_channel()); linkify_tags($work, local_channel());
linkify_tags($a, $education, local_channel()); linkify_tags($education, local_channel());
$with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : ''); $with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : '');

View File

@ -10,24 +10,6 @@ class Featured {
call_hooks('feature_settings_post', $_POST); call_hooks('feature_settings_post', $_POST);
if($_POST['affinity_slider-submit']) {
$cmax = intval($_POST['affinity_cmax']);
if($cmax < 0 || $cmax > 99)
$cmax = 99;
$cmin = intval($_POST['affinity_cmin']);
if($cmin < 0 || $cmin > 99)
$cmin = 0;
$lock = ($_POST['affinity_lock']) ? intval($_POST['affinity_lock']) : 1;
set_pconfig(local_channel(),'affinity','cmin',$cmin);
set_pconfig(local_channel(),'affinity','cmax',$cmax);
set_pconfig(local_channel(),'affinity','lock',$lock);
info( t('Affinity Slider settings updated.') . EOL);
}
build_sync_packet(); build_sync_packet();
return; return;
} }
@ -41,34 +23,10 @@ class Featured {
if(! $r) if(! $r)
$settings_addons = t('No feature settings configured'); $settings_addons = t('No feature settings configured');
if(feature_enabled(local_channel(),'affinity')) {
$cmax = intval(get_pconfig(local_channel(),'affinity','cmax'));
$cmax = (($cmax) ? $cmax : 99);
$setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmax', t('Default maximum affinity level'), $cmax, t('0-99 default 99'))
));
$cmin = intval(get_pconfig(local_channel(),'affinity','cmin'));
$cmin = (($cmin) ? $cmin : 0);
$setting_fields .= replace_macros(get_markup_template('field_input.tpl'), array(
'$field' => array('affinity_cmin', t('Default minimum affinity level'), $cmin, t('0-99 - default 0'))
));
$lock = intval(get_pconfig(local_channel(),'affinity','lock',1));
$setting_fields .= replace_macros(get_markup_template('field_checkbox.tpl'), array(
'$field' => array('affinity_lock', t('Always reset on new page visit.'), $lock, t('default: yes'), Array('No','Yes'))
));
$settings_addons .= replace_macros(get_markup_template('generic_addon_settings.tpl'), array(
'$addon' => array('affinity_slider', '' . t('Affinity Slider Settings'), '', t('Submit')),
'$content' => $setting_fields
));
}
call_hooks('feature_settings', $settings_addons); call_hooks('feature_settings', $settings_addons);
$this->sortpanels($settings_addons); $this->sortpanels($settings_addons);
$tpl = get_markup_template("settings_addons.tpl"); $tpl = get_markup_template("settings_addons.tpl");
$o .= replace_macros($tpl, array( $o .= replace_macros($tpl, array(
'$form_security_token' => get_form_security_token("settings_featured"), '$form_security_token' => get_form_security_token("settings_featured"),

View File

@ -1,6 +1,11 @@
<?php <?php
namespace Zotlabs\Module; namespace Zotlabs\Module;
use App;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\Activity;
require_once('include/security.php'); require_once('include/security.php');
require_once('include/bbcode.php'); require_once('include/bbcode.php');
@ -14,23 +19,23 @@ class Share extends \Zotlabs\Web\Controller {
if(! $post_id) if(! $post_id)
killme(); killme();
echo '[share=' . $post_id . '][/share]'; if(! local_channel()) {
killme(); killme();
}
$observer = App::get_observer();
/** $channel = App::get_channel();
* The remaining code is deprecated and handled in Zotlabs/Lib/Share.php at post
* submission time.
*/
if(! (local_channel() || remote_channel()))
killme();
$r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1", $r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1",
intval($post_id) intval($post_id)
); );
if(! $r) if(! $r)
killme(); killme();
if(($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss')) if(($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss'))
killme(); killme();
@ -47,58 +52,85 @@ class Share extends \Zotlabs\Web\Controller {
if($r[0]['mimetype'] !== 'text/bbcode') if($r[0]['mimetype'] !== 'text/bbcode')
killme(); killme();
/** @FIXME eventually we want to post remotely via rpost on your home site */
// When that works remove this next bit:
if(! local_channel())
killme();
xchan_query($r); xchan_query($r);
$is_photo = (($r[0]['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false); $arr = [];
if($is_photo) {
$object = json_decode($r[0]['obj'],true);
$photo_bb = $object['body'];
}
if (strpos($r[0]['body'], "[/share]") !== false) { $item = $r[0];
$pos = strpos($r[0]['body'], "[share");
$o = substr($r[0]['body'], $pos);
} else {
$o = "[share author='" . urlencode($r[0]['author']['xchan_name']) .
"' profile='" . $r[0]['author']['xchan_url'] .
"' avatar='" . $r[0]['author']['xchan_photo_s'] .
"' link='" . $r[0]['plink'] .
"' auth='" . (($r[0]['author']['network'] === 'zot') ? 'true' : 'false') .
"' posted='" . $r[0]['created'] .
"' message_id='" . $r[0]['mid'] .
"']";
if($r[0]['title'])
$o .= '[b]'.$r[0]['title'].'[/b]'."\r\n";
$o .= (($is_photo) ? $photo_bb . "\r\n" . $r[0]['body'] : $r[0]['body']);
$o .= "[/share]";
}
if(local_channel()) { $owner_uid = $r[0]['uid'];
echo $o; $owner_aid = $r[0]['aid'];
$can_comment = false;
if((array_key_exists('owner',$item)) && intval($item['owner']['abook_self']))
$can_comment = perm_is_allowed($item['uid'],$observer['xchan_hash'],'post_comments');
else
$can_comment = can_comment_on_post($observer['xchan_hash'],$item);
if(! $can_comment) {
notice( t('Permission denied') . EOL);
killme(); killme();
} }
$observer = \App::get_observer(); $r = q("select * from xchan where xchan_hash = '%s' limit 1",
$parsed = $observer['xchan_url']; dbesc($item['owner_xchan'])
if($parsed) { );
$post_url = $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : '')
. '/rpost';
/** if($r)
* @FIXME we were probably called from JS so we don't know the return page. $thread_owner = $r[0];
* In fact we won't be able to load the remote page. else
* we might need an iframe
*/
$x = z_post_url($post_url, array('f' => '', 'body' => $o ));
killme(); killme();
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
dbesc($item['author_xchan'])
);
if($r)
$item_author = $r[0];
else
killme();
$arr['aid'] = $owner_aid;
$arr['uid'] = $owner_uid;
$arr['item_origin'] = 1;
$arr['item_wall'] = $item['item_wall'];
$arr['uuid'] = item_message_id();
$arr['mid'] = z_root() . '/activity/' . $arr['uuid'];
$arr['parent_mid'] = $item['mid'];
$mention = '@[zrl=' . $item['author']['xchan_url'] . ']' . $item['author']['xchan_name'] . '[/zrl]';
$arr['body'] = sprintf( t('&#x1f501; Repeated %1$s\'s %2$s'), $mention, Activity::activity_obj_mapper($item['obj_type']));
$arr['author_xchan'] = $channel['channel_hash'];
$arr['owner_xchan'] = $item['author_xchan'];
$arr['obj'] = Activity::encode_item($item);
$arr['obj_type'] = $item['obj_type'];
$arr['verb'] = 'Announce';
$post = item_store($arr);
$post_id = $post['item_id'];
$arr['id'] = $post_id;
call_hooks('post_local_end', $arr);
info( t('Post repeated') . EOL);
$r = q("select * from item where id = %d",
intval($post_id)
);
if($r) {
xchan_query($r);
$sync_item = fetch_post_tags($r);
build_sync_packet($channel['channel_id'], [ 'item' => [ encode_item($sync_item[0],true) ] ]);
} }
Master::Summon([ 'Notifier','like',$post_id ]);
killme();
} }
} }

View File

@ -28,7 +28,7 @@ class Viewsrc extends \Zotlabs\Web\Controller {
$item_normal = item_normal(); $item_normal = item_normal();
if(local_channel() && $item_id) { if(local_channel() && $item_id) {
$r = q("select id, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1", $r = q("select id, mid, item_flags, mimetype, item_obscured, body, llink, plink from item where uid in (%d , %d) and id = %d $item_normal limit 1",
intval(local_channel()), intval(local_channel()),
intval($sys['channel_id']), intval($sys['channel_id']),
intval($item_id) intval($item_id)
@ -53,7 +53,7 @@ class Viewsrc extends \Zotlabs\Web\Controller {
if(is_ajax()) { if(is_ajax()) {
echo '<div class="p-1">'; echo '<div class="p-1">';
echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a></div>'; echo '<div>id: ' . $r[0]['id'] . ' | <a href="' . $r[0]['plink'] . '" target="_blank">plink</a> | <a href="' . $r[0]['llink'] . '" target="_blank">llink</a><br>mid: ' . $r[0]['mid'] . '</div>';
echo '<hr>'; echo '<hr>';
echo '<pre class="p-1">' . $o . '</pre>'; echo '<pre class="p-1">' . $o . '</pre>';
echo '</div>'; echo '</div>';

View File

@ -0,0 +1,498 @@
<?php
namespace Zotlabs\Photo;
/**
* @brief Abstract photo driver class.
*
* Inheritance seems not to be the best design pattern for such photo drivers.
*/
abstract class PhotoDriver {
/**
* @brief This variable keeps the image.
*
* For GD it is a PHP image resource.
* For ImageMagick it is an \Imagick object.
*
* @var resource|\Imagick
*/
protected $image;
/**
* @var integer
*/
protected $width;
/**
* @var integer
*/
protected $height;
/**
* @var boolean
*/
protected $valid;
/**
* @brief The mimetype of the image.
*
* @var string
*/
protected $type;
/**
* @brief Supported mimetypes by the used photo driver.
*
* @var array
*/
protected $types;
/**
* @brief Return an array with supported mimetypes.
*
* @return array
* Associative array with mimetype as key and file extension as value.
*/
abstract public function supportedTypes();
abstract protected function load($data, $type);
abstract protected function destroy();
abstract protected function setDimensions();
/**
* @brief Return the current image.
*
* @fixme Shouldn't his method be protected, because outside of the current
* driver it makes no sense at all because of the different return values.
*
* @return boolean|resource|\Imagick
* false on failure, a PHP image resource for GD driver, an \Imagick object
* for ImageMagick driver.
*/
abstract public function getImage();
abstract public function doScaleImage($new_width, $new_height);
abstract public function rotate($degrees);
abstract public function flip($horiz = true, $vert = false);
/**
* @brief Crops the image.
*
* @param int $maxx width of the new image
* @param int $maxy height of the new image
* @param int $x x-offset for region
* @param int $y y-offset for region
* @param int $w width of region
* @param int $h height of region
*
* @return boolean|void false on failure
*/
abstract public function cropImageRect($maxx, $maxy, $x, $y, $w, $h);
/**
* @brief Return a binary string from the image resource.
*
* @return string A Binary String.
*/
abstract public function imageString();
abstract public function clearexif();
/**
* @brief PhotoDriver constructor.
*
* @param string $data Image
* @param string $type mimetype
*/
public function __construct($data, $type = '') {
$this->types = $this->supportedTypes();
if(! array_key_exists($type, $this->types)) {
$type = 'image/jpeg';
}
$this->type = $type;
$this->valid = false;
$this->load($data, $type);
}
public function __destruct() {
if($this->is_valid())
$this->destroy();
}
/**
* @brief Is it a valid image object.
*
* @return boolean
*/
public function is_valid() {
return $this->valid;
}
/**
* @brief Get the width of the image.
*
* @return boolean|number Width of image in pixels, or false on failure
*/
public function getWidth() {
if(! $this->is_valid())
return false;
return $this->width;
}
/**
* @brief Get the height of the image.
*
* @return boolean|number Height of image in pixels, or false on failure
*/
public function getHeight() {
if(! $this->is_valid())
return false;
return $this->height;
}
/**
* @brief Saves the image resource to a file in filesystem.
*
* @param string $path Path and filename where to save the image
* @return boolean False on failure, otherwise true
*/
public function saveImage($path) {
if(! $this->is_valid())
return false;
return (file_put_contents($path, $this->imageString()) ? true : false);
}
/**
* @brief Return mimetype of the image resource.
*
* @return boolean|string False on failure, otherwise mimetype.
*/
public function getType() {
if(! $this->is_valid())
return false;
return $this->type;
}
/**
* @brief Return file extension of the image resource.
*
* @return boolean|string False on failure, otherwise file extension.
*/
public function getExt() {
if(! $this->is_valid())
return false;
return $this->types[$this->getType()];
}
/**
* @brief Scale image to max pixel size in either dimension.
*
* @param int $max maximum pixel size in either dimension
* @param boolean $float_height (optional)
* If true allow height to float to any length on tall images, constraining
* only the width
* @return boolean|void false on failure, otherwise void
*/
public function scaleImage($max, $float_height = true) {
if(! $this->is_valid())
return false;
$width = $this->width;
$height = $this->height;
$dest_width = $dest_height = 0;
if((! $width) || (! $height))
return false;
if($width > $max && $height > $max) {
// very tall image (greater than 16:9)
// constrain the width - let the height float.
if(((($height * 9) / 16) > $width) && ($float_height)) {
$dest_width = $max;
$dest_height = intval(($height * $max) / $width);
} // else constrain both dimensions
elseif($width > $height) {
$dest_width = $max;
$dest_height = intval(($height * $max) / $width);
} else {
$dest_width = intval(($width * $max) / $height);
$dest_height = $max;
}
} else {
if($width > $max) {
$dest_width = $max;
$dest_height = intval(($height * $max) / $width);
} else {
if($height > $max) {
// very tall image (greater than 16:9)
// but width is OK - don't do anything
if(((($height * 9) / 16) > $width) && ($float_height)) {
$dest_width = $width;
$dest_height = $height;
} else {
$dest_width = intval(($width * $max) / $height);
$dest_height = $max;
}
} else {
$dest_width = $width;
$dest_height = $height;
}
}
}
$this->doScaleImage($dest_width, $dest_height);
}
public function scaleImageUp($min) {
if(! $this->is_valid()) {
return false;
}
$width = $this->width;
$height = $this->height;
$dest_width = $dest_height = 0;
if((! $width) || (! $height))
return false;
if($width < $min && $height < $min) {
if($width > $height) {
$dest_width = $min;
$dest_height = intval(($height * $min) / $width);
} else {
$dest_width = intval(($width * $min) / $height);
$dest_height = $min;
}
} else {
if($width < $min) {
$dest_width = $min;
$dest_height = intval(($height * $min) / $width);
} else {
if($height < $min) {
$dest_width = intval(($width * $min) / $height);
$dest_height = $min;
} else {
$dest_width = $width;
$dest_height = $height;
}
}
}
$this->doScaleImage($dest_width, $dest_height);
}
/**
* @brief Scales image to a square.
*
* @param int $dim Pixel of square image
* @return boolean|void false on failure, otherwise void
*/
public function scaleImageSquare($dim) {
if(! $this->is_valid())
return false;
$this->doScaleImage($dim, $dim);
}
/**
* @brief Crops a square image.
*
* @see cropImageRect()
*
* @param int $max size of the new image
* @param int $x x-offset for region
* @param int $y y-offset for region
* @param int $w width of region
* @param int $h height of region
*
* @return boolean|void false on failure
*/
public function cropImage($max, $x, $y, $w, $h) {
if(! $this->is_valid())
return false;
$this->cropImageRect($max, $max, $x, $y, $w, $h);
}
/**
* @brief Reads exif data from a given filename.
*
* @param string $filename
* @return boolean|array
*/
public function exif($filename) {
if((! function_exists('exif_read_data'))
|| (! in_array($this->getType(), ['image/jpeg', 'image/tiff'])))
{
return false;
}
/*
* PHP 7.2 allows you to use a stream resource, which should reduce/avoid
* memory exhaustion on large images.
*/
if(version_compare(PHP_VERSION, '7.2.0') >= 0) {
$f = @fopen($filename, 'rb');
} else {
$f = $filename;
}
if($f) {
return @exif_read_data($f, null, true);
}
return false;
}
/**
* @brief Orients current image based on exif orientation information.
*
* @param array $exif
* @return boolean true if oriented, otherwise false
*/
public function orient($exif) {
if(! ($this->is_valid() && $exif)) {
return false;
}
$ort = ((array_key_exists('IFD0', $exif)) ? $exif['IFD0']['Orientation'] : $exif['Orientation']);
if(! $ort) {
return false;
}
switch($ort) {
case 1 : // nothing
break;
case 2 : // horizontal flip
$this->flip();
break;
case 3 : // 180 rotate left
$this->rotate(180);
break;
case 4 : // vertical flip
$this->flip(false, true);
break;
case 5 : // vertical flip + 90 rotate right
$this->flip(false, true);
$this->rotate(-90);
break;
case 6 : // 90 rotate right
$this->rotate(-90);
break;
case 7 : // horizontal flip + 90 rotate right
$this->flip();
$this->rotate(-90);
break;
case 8 : // 90 rotate left
$this->rotate(90);
break;
default :
break;
}
return true;
}
/**
* @brief Save photo to database.
*
* @param array $arr
* @param boolean $skipcheck (optional) default false
* @return boolean|array
*/
public function save($arr, $skipcheck = false) {
if(! ($skipcheck || $this->is_valid())) {
logger('Attempt to store invalid photo.');
return false;
}
$p = [];
$p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0);
$p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0);
$p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : '');
$p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : '');
$p['filename'] = (($arr['filename']) ? $arr['filename'] : '');
$p['mimetype'] = (($arr['mimetype']) ? $arr['mimetype'] : $this->getType());
$p['album'] = (($arr['album']) ? $arr['album'] : '');
$p['imgscale'] = ((intval($arr['imgscale'])) ? intval($arr['imgscale']) : 0);
$p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : '');
$p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : '');
$p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : '');
$p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : '');
$p['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert());
$p['title'] = (($arr['title']) ? $arr['title'] : '');
$p['description'] = (($arr['description']) ? $arr['description'] : '');
$p['photo_usage'] = intval($arr['photo_usage']);
$p['os_storage'] = intval($arr['os_storage']);
$p['os_path'] = $arr['os_path'];
$p['os_syspath'] = ((array_key_exists('os_syspath', $arr)) ? $arr['os_syspath'] : '');
$p['display_path'] = (($arr['display_path']) ? $arr['display_path'] : '');
$p['width'] = (($arr['width']) ? $arr['width'] : $this->getWidth());
$p['height'] = (($arr['height']) ? $arr['height'] : $this->getHeight());
$p['expires'] = (($arr['expires']) ? $arr['expires'] : gmdate('Y-m-d H:i:s', time() + get_config('system', 'photo_cache_time', 86400)));
if(! intval($p['imgscale']))
logger('save: ' . print_r($arr, true), LOGGER_DATA);
$x = q("select id, created from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1", dbesc($p['resource_id']), intval($p['uid']), dbesc($p['xchan']), intval($p['imgscale']));
if($x) {
$p['created'] = (($x['created']) ? $x['created'] : $p['edited']);
$r = q("UPDATE photo set
aid = %d,
uid = %d,
xchan = '%s',
resource_id = '%s',
created = '%s',
edited = '%s',
filename = '%s',
mimetype = '%s',
album = '%s',
height = %d,
width = %d,
content = '%s',
os_storage = %d,
filesize = %d,
imgscale = %d,
photo_usage = %d,
title = '%s',
description = '%s',
os_path = '%s',
display_path = '%s',
allow_cid = '%s',
allow_gid = '%s',
deny_cid = '%s',
deny_gid = '%s',
expires = '%s'
where id = %d",
intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']), intval($x[0]['id']));
} else {
$p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']);
$r = q("INSERT INTO photo
( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires )
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )", intval($p['aid']), intval($p['uid']), dbesc($p['xchan']), dbesc($p['resource_id']), dbescdate($p['created']), dbescdate($p['edited']), dbesc(basename($p['filename'])), dbesc($p['mimetype']), dbesc($p['album']), intval($p['height']), intval($p['width']), (intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())), intval($p['os_storage']), (intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())), intval($p['imgscale']), intval($p['photo_usage']), dbesc($p['title']), dbesc($p['description']), dbesc($p['os_path']), dbesc($p['display_path']), dbesc($p['allow_cid']), dbesc($p['allow_gid']), dbesc($p['deny_cid']), dbesc($p['deny_gid']), dbescdate($p['expires']));
}
logger('Photo save imgscale ' . $p['imgscale'] . ' returned ' . intval($r));
return $r;
}
}

176
Zotlabs/Photo/PhotoGd.php Normal file
View File

@ -0,0 +1,176 @@
<?php
namespace Zotlabs\Photo;
/**
* @brief GD photo driver.
*
*/
class PhotoGd extends PhotoDriver {
/**
* {@inheritDoc}
* @see \Zotlabs\Photo\PhotoDriver::supportedTypes()
*/
public function supportedTypes() {
$t = [];
$t['image/jpeg'] = 'jpg';
if(imagetypes() & IMG_PNG)
$t['image/png'] = 'png';
if(imagetypes() & IMG_GIF)
$t['image/gif'] = 'gif';
return $t;
}
protected function load($data, $type) {
$this->valid = false;
if(! $data)
return;
$this->image = @imagecreatefromstring($data);
if($this->image !== false) {
$this->valid = true;
$this->setDimensions();
imagealphablending($this->image, false);
imagesavealpha($this->image, true);
}
}
protected function setDimensions() {
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
}
/**
* @brief GD driver does not preserve EXIF, so not need to clear it.
*
* @return void
*/
public function clearexif() {
return;
}
protected function destroy() {
if($this->is_valid()) {
imagedestroy($this->image);
}
}
/**
* @brief Return a PHP image resource of the current image.
*
* @see \Zotlabs\Photo\PhotoDriver::getImage()
*
* @return boolean|resource
*/
public function getImage() {
if(! $this->is_valid())
return false;
return $this->image;
}
public function doScaleImage($dest_width, $dest_height) {
$dest = imagecreatetruecolor($dest_width, $dest_height);
$width = imagesx($this->image);
$height = imagesy($this->image);
imagealphablending($dest, false);
imagesavealpha($dest, true);
if($this->type == 'image/png')
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
if($this->image)
imagedestroy($this->image);
$this->image = $dest;
$this->setDimensions();
}
public function rotate($degrees) {
if(! $this->is_valid())
return false;
$this->image = imagerotate($this->image, $degrees, 0);
$this->setDimensions();
}
public function flip($horiz = true, $vert = false) {
if(! $this->is_valid())
return false;
$w = imagesx($this->image);
$h = imagesy($this->image);
$flipped = imagecreate($w, $h);
if($horiz) {
for($x = 0; $x < $w; $x++) {
imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h);
}
}
if($vert) {
for($y = 0; $y < $h; $y++) {
imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1);
}
}
$this->image = $flipped;
$this->setDimensions(); // Shouldn't really be necessary
}
public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) {
if(! $this->is_valid())
return false;
$dest = imagecreatetruecolor($maxx, $maxy);
imagealphablending($dest, false);
imagesavealpha($dest, true);
if($this->type == 'image/png')
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h);
if($this->image)
imagedestroy($this->image);
$this->image = $dest;
$this->setDimensions();
}
/**
* {@inheritDoc}
* @see \Zotlabs\Photo\PhotoDriver::imageString()
*/
public function imageString() {
if(! $this->is_valid())
return false;
$quality = false;
ob_start();
switch($this->getType()){
case 'image/png':
$quality = get_config('system', 'png_quality');
if((! $quality) || ($quality > 9))
$quality = PNG_QUALITY;
\imagepng($this->image, NULL, $quality);
break;
case 'image/jpeg':
// gd can lack imagejpeg(), but we verify during installation it is available
default:
$quality = get_config('system', 'jpeg_quality');
if((! $quality) || ($quality > 100))
$quality = JPEG_QUALITY;
\imagejpeg($this->image, NULL, $quality);
break;
}
$string = ob_get_contents();
ob_end_clean();
return $string;
}
}

View File

@ -1,45 +1,44 @@
<?php /** @file */ <?php
namespace Zotlabs\Photo;
require_once('include/photo/photo_driver.php'); /**
* @brief ImageMagick photo driver.
*/
class PhotoImagick extends PhotoDriver {
public function supportedTypes() {
class photo_imagick extends photo_driver { return [
function supportedTypes() {
return array(
'image/jpeg' => 'jpg', 'image/jpeg' => 'jpg',
'image/png' => 'png', 'image/png' => 'png',
'image/gif' => 'gif' 'image/gif' => 'gif',
); ];
} }
public function get_FormatsMap() { private function get_FormatsMap() {
return array( return [
'image/jpeg' => 'JPG', 'image/jpeg' => 'JPG',
'image/png' => 'PNG', 'image/png' => 'PNG',
'image/gif' => 'GIF' 'image/gif' => 'GIF',
); ];
} }
function load($data, $type) { protected function load($data, $type) {
$this->valid = false; $this->valid = false;
$this->image = new Imagick(); $this->image = new \Imagick();
if(! $data) if(! $data)
return; return;
try { try {
$this->image->readImageBlob($data); $this->image->readImageBlob($data);
} } catch(\Exception $e) {
catch (Exception $e) { logger('Imagick readImageBlob() exception:' . print_r($e, true));
logger('imagick readImageBlob() exception:' . print_r($e,true));
return; return;
} }
/** /*
* Setup the image to the format it will be saved to * Setup the image to the format it will be saved to
*/ */
@ -52,19 +51,18 @@ class photo_imagick extends photo_driver {
// Always coalesce, if it is not a multi-frame image it won't hurt anyway // Always coalesce, if it is not a multi-frame image it won't hurt anyway
$this->image = $this->image->coalesceImages(); $this->image = $this->image->coalesceImages();
$this->valid = true; $this->valid = true;
$this->setDimensions(); $this->setDimensions();
/** /*
* setup the compression here, so we'll do it only once * setup the compression here, so we'll do it only once
*/ */
switch($this->getType()) { switch($this->getType()) {
case "image/png": case 'image/png':
$quality = get_config('system', 'png_quality'); $quality = get_config('system', 'png_quality');
if((! $quality) || ($quality > 9)) if((! $quality) || ($quality > 9))
$quality = PNG_QUALITY; $quality = PNG_QUALITY;
/** /*
* From http://www.imagemagick.org/script/command-line-options.php#quality: * From http://www.imagemagick.org/script/command-line-options.php#quality:
* *
* 'For the MNG and PNG image formats, the quality value sets * 'For the MNG and PNG image formats, the quality value sets
@ -75,56 +73,64 @@ class photo_imagick extends photo_driver {
$quality = $quality * 10; $quality = $quality * 10;
$this->image->setCompressionQuality($quality); $this->image->setCompressionQuality($quality);
break; break;
case "image/jpeg": case 'image/jpeg':
$quality = get_config('system', 'jpeg_quality'); $quality = get_config('system', 'jpeg_quality');
if((! $quality) || ($quality > 100)) if((! $quality) || ($quality > 100))
$quality = JPEG_QUALITY; $quality = JPEG_QUALITY;
$this->image->setCompressionQuality($quality); $this->image->setCompressionQuality($quality);
default: default:
break; break;
} }
} }
} }
public function destroy() { protected function destroy() {
if($this->is_valid()) { if($this->is_valid()) {
$this->image->clear(); $this->image->clear();
$this->image->destroy(); $this->image->destroy();
} }
} }
protected function setDimensions() {
public function setDimensions() {
$this->width = $this->image->getImageWidth(); $this->width = $this->image->getImageWidth();
$this->height = $this->image->getImageHeight(); $this->height = $this->image->getImageHeight();
} }
/**
* @brief Strips the image of all profiles and comments.
*
* Keep ICC profile for better colors.
*
* @see \Zotlabs\Photo\PhotoDriver::clearexif()
*/
public function clearexif() { public function clearexif() {
$profiles = $this->image->getImageProfiles('icc', true);
$profiles = $this->image->getImageProfiles("icc", true);
$this->image->stripImage(); $this->image->stripImage();
if(! empty($profiles)) { if(! empty($profiles)) {
$this->image->profileImage("icc", $profiles['icc']); $this->image->profileImage('icc', $profiles['icc']);
} }
} }
/**
* @brief Return a \Imagick object of the current image.
*
* @see \Zotlabs\Photo\PhotoDriver::getImage()
*
* @return boolean|\Imagick
*/
public function getImage() { public function getImage() {
if(! $this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
$this->image = $this->image->deconstructImages(); $this->image = $this->image->deconstructImages();
return $this->image; return $this->image;
} }
public function doScaleImage($dest_width, $dest_height) { public function doScaleImage($dest_width, $dest_height) {
/*
/**
* If it is not animated, there will be only one iteration here, * If it is not animated, there will be only one iteration here,
* so don't bother checking * so don't bother checking
*/ */
@ -139,12 +145,12 @@ class photo_imagick extends photo_driver {
public function rotate($degrees) { public function rotate($degrees) {
if(! $this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
$this->image->setFirstIterator(); $this->image->setFirstIterator();
do { do {
// ImageMagick rotates in the opposite direction of imagerotate() // ImageMagick rotates in the opposite direction of imagerotate()
$this->image->rotateImage(new ImagickPixel(), -$degrees); $this->image->rotateImage(new \ImagickPixel(), -$degrees);
} while($this->image->nextImage()); } while($this->image->nextImage());
$this->setDimensions(); $this->setDimensions();
@ -152,7 +158,7 @@ class photo_imagick extends photo_driver {
public function flip($horiz = true, $vert = false) { public function flip($horiz = true, $vert = false) {
if(! $this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
$this->image->setFirstIterator(); $this->image->setFirstIterator();
do { do {
@ -163,32 +169,14 @@ class photo_imagick extends photo_driver {
$this->setDimensions(); // Shouldn't really be necessary $this->setDimensions(); // Shouldn't really be necessary
} }
public function cropImage($max,$x,$y,$w,$h) {
if(!$this->is_valid())
return FALSE;
$this->image->setFirstIterator();
do {
$this->image->cropImage($w, $h, $x, $y);
/**
* We need to remove the canvas,
* or the image is not resized to the crop:
* http://php.net/manual/en/imagick.cropimage.php#97232
*/
$this->image->setImagePage(0, 0, 0, 0);
} while ($this->image->nextImage());
$this->doScaleImage($max,$max);
}
public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) { public function cropImageRect($maxx, $maxy, $x, $y, $w, $h) {
if(! $this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
$this->image->setFirstIterator(); $this->image->setFirstIterator();
do { do {
$this->image->cropImage($w, $h, $x, $y); $this->image->cropImage($w, $h, $x, $y);
/** /*
* We need to remove the canvas, * We need to remove the canvas,
* or the image is not resized to the crop: * or the image is not resized to the crop:
* http://php.net/manual/en/imagick.cropimage.php#97232 * http://php.net/manual/en/imagick.cropimage.php#97232
@ -201,13 +189,12 @@ class photo_imagick extends photo_driver {
public function imageString() { public function imageString() {
if(! $this->is_valid()) if(! $this->is_valid())
return FALSE; return false;
/* Clean it */ /* Clean it */
$this->image = $this->image->deconstructImages(); $this->image = $this->image->deconstructImages();
return $this->image->getImagesBlob(); return $this->image->getImagesBlob();
} }
} }

View File

@ -15,7 +15,7 @@ class Session {
private $handler = null; private $handler = null;
private $session_started = false; private $session_started = false;
private $custom_handler = false;
public function init() { public function init() {
$gc_probability = 50; $gc_probability = 50;
@ -24,10 +24,30 @@ class Session {
ini_set('session.use_only_cookies', 1); ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_httponly', 1); ini_set('session.cookie_httponly', 1);
$this->custom_handler = boolval(get_config('system', 'session_custom', false));
/* /*
* Set our session storage functions. * Set our session storage functions.
*/ */
if($this->custom_handler) {
/* Custom handler (files, memached, redis..) */
$session_save_handler = strval(get_config('system', 'session_save_handler', Null));
$session_save_path = strval(get_config('system', 'session_save_path', Null));
$session_gc_probability = intval(get_config('system', 'session_gc_probability', 1));
$session_gc_divisor = intval(get_config('system', 'session_gc_divisor', 100));
if(!$session_save_handler || !$session_save_path) {
logger('Session save handler or path not set.',LOGGER_NORMAL,LOG_ERR);
}
else {
ini_set('session.save_handler', $session_save_handler);
ini_set('session.save_path', $session_save_path);
ini_set('session.gc_probability', $session_gc_probability);
ini_set('session.gc_divisor', $session_gc_divisor);
}
}
else {
$handler = new \Zotlabs\Web\SessionHandler(); $handler = new \Zotlabs\Web\SessionHandler();
$this->handler = $handler; $this->handler = $handler;
@ -35,6 +55,7 @@ class Session {
$x = session_set_save_handler($handler,false); $x = session_set_save_handler($handler,false);
if(! $x) if(! $x)
logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR); logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR);
}
// Force cookies to be secure (https only) if this site is SSL enabled. // Force cookies to be secure (https only) if this site is SSL enabled.
// Must be done before session_start(). // Must be done before session_start().
@ -86,15 +107,16 @@ class Session {
$arr = session_get_cookie_params(); $arr = session_get_cookie_params();
if($this->handler && $this->session_started) { if(($this->handler || $this->custom_handler) && $this->session_started) {
session_regenerate_id(true); session_regenerate_id(true);
// force SessionHandler record creation with the new session_id // force SessionHandler record creation with the new session_id
// which occurs as a side effect of read() // which occurs as a side effect of read()
if (! $this->custom_handler) {
$this->handler->read(session_id()); $this->handler->read(session_id());
} }
}
else else
logger('no session handler'); logger('no session handler');

View File

@ -2,21 +2,24 @@
namespace Zotlabs\Widget; namespace Zotlabs\Widget;
use Zotlabs\Lib\Apps;
class Affinity { class Affinity {
function widget($arr) { function widget($arr) {
if(! local_channel()) if(! local_channel())
return ''; return;
$default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0); if(! Apps::system_app_installed(local_channel(),'Affinity Tool'))
$default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99); return;
$default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0);
$default_cmax = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99);
$cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $default_cmin); $cmin = ((x($_REQUEST,'cmin')) ? intval($_REQUEST['cmin']) : $default_cmin);
$cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $default_cmax); $cmax = ((x($_REQUEST,'cmax')) ? intval($_REQUEST['cmax']) : $default_cmax);
if(feature_enabled(local_channel(),'affinity')) {
$affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1)); $affinity_locked = intval(get_pconfig(local_channel(),'affinity','lock',1));
if ($affinity_locked) { if ($affinity_locked) {
set_pconfig(local_channel(),'affinity','cmin',$cmin); set_pconfig(local_channel(),'affinity','cmin',$cmin);
@ -31,6 +34,7 @@ class Affinity {
t('All') t('All')
); );
call_hooks('affinity_labels',$labels); call_hooks('affinity_labels',$labels);
$label_str = ''; $label_str = '';
if($labels) { if($labels) {
@ -53,9 +57,10 @@ class Affinity {
$arr = array('html' => $x); $arr = array('html' => $x);
call_hooks('main_slider',$arr); call_hooks('main_slider',$arr);
return $arr['html']; return $arr['html'];
}
return '';
} }
} }

View File

@ -42,19 +42,12 @@ class Settings_menu {
); );
$tabs[] = array( $tabs[] = array(
'label' => t('Display settings'), 'label' => t('Display settings'),
'url' => z_root().'/settings/display', 'url' => z_root().'/settings/display',
'selected' => ((argv(1) === 'display') ? 'active' : ''), 'selected' => ((argv(1) === 'display') ? 'active' : ''),
); );
$tabs[] = array(
'label' => t('Addon settings'),
'url' => z_root().'/settings/featured',
'selected' => ((argv(1) === 'featured') ? 'active' : ''),
);
if($hublocs) { if($hublocs) {
$tabs[] = array( $tabs[] = array(
'label' => t('Manage locations'), 'label' => t('Manage locations'),

View File

@ -58,10 +58,10 @@ class Zot6Handler implements IHandler {
* *
* @param array $sender * @param array $sender
* @param array $recipients * @param array $recipients
* @param array $hub
* *
* @return json_return_and_die() * @return array
*/ */
static function reply_refresh($sender, $recipients, $hub) { static function reply_refresh($sender, $recipients, $hub) {
$ret = array('success' => false); $ret = array('success' => false);
@ -70,19 +70,18 @@ class Zot6Handler implements IHandler {
// This would be a permissions update, typically for one connection // This would be a permissions update, typically for one connection
foreach ($recipients as $recip) { foreach ($recipients as $recip) {
$r = q("select channel.*,xchan.* from channel $r = q("select channel.*,xchan.* from channel
left join xchan on channel_portable_id = xchan_hash left join xchan on channel_portable_id = xchan_hash
where xchan_hash ='%s' limit 1", where xchan_hash ='%s' limit 1",
dbesc($recip) dbesc($recip)
); );
/// @FIXME $msgtype is undefined
$x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], $r[0], (($msgtype === 'force_refresh') ? true : false)); $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], $r[0], (($msgtype === 'force_refresh') ? true : false));
} }
} }
else { else {
// system wide refresh // system wide refresh
/// @FIXME $msgtype is undefined
$x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], null, (($msgtype === 'force_refresh') ? true : false)); $x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], null, (($msgtype === 'force_refresh') ? true : false));
} }
@ -105,11 +104,10 @@ class Zot6Handler implements IHandler {
* this 'message_list' at the destination and split it into individual messages which are * this 'message_list' at the destination and split it into individual messages which are
* processed/delivered in order. * processed/delivered in order.
* *
*
* @param array $data * @param array $data
* @param array $hub
* @return array * @return array
*/ */
static function reply_message_request($data, $hub) { static function reply_message_request($data, $hub) {
$ret = [ 'success' => false ]; $ret = [ 'success' => false ];
@ -153,11 +151,10 @@ class Zot6Handler implements IHandler {
/* /*
* fetch the requested conversation * fetch the requested conversation
*/ */
/// @FIXME $sender_hash is undefined
$messages = zot_feed($c[0]['channel_id'],$sender_hash, [ 'message_id' => $data['message_id'], 'encoding' => 'activitystreams' ]); $messages = zot_feed($c[0]['channel_id'],$sender_hash, [ 'message_id' => $data['message_id'], 'encoding' => 'activitystreams' ]);
return (($messages) ? : [] ); return (($messages) ? : [] );
} }
static function rekey_request($sender,$data,$hub) { static function rekey_request($sender,$data,$hub) {
@ -219,10 +216,10 @@ class Zot6Handler implements IHandler {
* *
* @param array $sender * @param array $sender
* @param array $recipients * @param array $recipients
* @param array $hub
* *
* return json_return_and_die() * @return array
*/ */
static function reply_purge($sender, $recipients, $hub) { static function reply_purge($sender, $recipients, $hub) {
$ret = array('success' => false); $ret = array('success' => false);
@ -259,9 +256,4 @@ class Zot6Handler implements IHandler {
return $ret; return $ret;
} }
} }

7
app/affinity.apd Normal file
View File

@ -0,0 +1,7 @@
version: 1
url: $baseurl/affinity
requires: local_channel
name: Affinity Tool
photo: icon:arrows-h
categories: Networking
desc: This app presents a slider control in your connection editor and also on your network page. The slider represents your degree of friendship (affinity) with each connection. It allows you to zoom in or out and display conversations from only your closest friends or everybody in your stream.

View File

@ -50,7 +50,7 @@ require_once('include/attach.php');
require_once('include/bbcode.php'); require_once('include/bbcode.php');
define ( 'PLATFORM_NAME', 'hubzilla' ); define ( 'PLATFORM_NAME', 'hubzilla' );
define ( 'STD_VERSION', '3.9.5' ); define ( 'STD_VERSION', '3.9.6' );
define ( 'ZOT_REVISION', '6.0a' ); define ( 'ZOT_REVISION', '6.0a' );
define ( 'DB_UPDATE_VERSION', 1230 ); define ( 'DB_UPDATE_VERSION', 1230 );
@ -83,7 +83,6 @@ define ( 'DIRECTORY_REALM', 'RED_GLOBAL');
define ( 'DIRECTORY_FALLBACK_MASTER', 'https://zotadel.net'); define ( 'DIRECTORY_FALLBACK_MASTER', 'https://zotadel.net');
$DIRECTORY_FALLBACK_SERVERS = array( $DIRECTORY_FALLBACK_SERVERS = array(
'https://hubzilla.zottel.net',
'https://zotadel.net', 'https://zotadel.net',
'https://zotsite.net' 'https://zotsite.net'
); );
@ -468,7 +467,7 @@ define ( 'NAMESPACE_YMEDIA', 'http://search.yahoo.com/mrss/' );
define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' ); define ( 'ACTIVITYSTREAMS_JSONLD_REV', 'https://www.w3.org/ns/activitystreams' );
define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.2' ); define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.3' );
/** /**
* activity stream defines * activity stream defines
*/ */
@ -513,6 +512,7 @@ define ( 'ACTIVITY_MOOD', NAMESPACE_ZOT . '/activity/mood' );
define ( 'ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment' ); define ( 'ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment' );
define ( 'ACTIVITY_OBJ_ACTIVITY',NAMESPACE_ACTIVITY_SCHEMA . 'activity' ); define ( 'ACTIVITY_OBJ_ACTIVITY',NAMESPACE_ACTIVITY_SCHEMA . 'activity' );
define ( 'ACTIVITY_OBJ_NOTE', NAMESPACE_ACTIVITY_SCHEMA . 'note' ); define ( 'ACTIVITY_OBJ_NOTE', NAMESPACE_ACTIVITY_SCHEMA . 'note' );
define ( 'ACTIVITY_OBJ_ARTICLE', NAMESPACE_ACTIVITY_SCHEMA . 'article' );
define ( 'ACTIVITY_OBJ_PERSON', NAMESPACE_ACTIVITY_SCHEMA . 'person' ); define ( 'ACTIVITY_OBJ_PERSON', NAMESPACE_ACTIVITY_SCHEMA . 'person' );
define ( 'ACTIVITY_OBJ_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo' ); define ( 'ACTIVITY_OBJ_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo' );
define ( 'ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo' ); define ( 'ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo' );
@ -1833,8 +1833,6 @@ function proc_run(){
$args = func_get_args(); $args = func_get_args();
$newargs = array();
if(! count($args)) if(! count($args))
return; return;
@ -2401,7 +2399,7 @@ function z_get_temp_dir() {
if(! $temp_dir) if(! $temp_dir)
$temp_dir = sys_get_temp_dir(); $temp_dir = sys_get_temp_dir();
return $upload_dir; return $temp_dir;
} }

View File

@ -0,0 +1,5 @@
[h2]jot_header_tpl_filter[/h2]
Allows addon developers to modify the values of replacements fed into jot-header.tpl
cxref: include/conversation.php

View File

@ -0,0 +1,5 @@
[h2]jot_tpl_filter[/h2]
Allows addon developers to alter the macro replacements prior to being fed into jot.tpl
cxref: include/conversation.php

View File

@ -370,6 +370,12 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the
[zrl=[baseurl]/help/hook/jot_tool]jot_tool[/zrl] [zrl=[baseurl]/help/hook/jot_tool]jot_tool[/zrl]
Deprecated and possibly obsolete. Allows one to add action buttons to the post editor. Deprecated and possibly obsolete. Allows one to add action buttons to the post editor.
[zrl=[baseurl]/help/hook/jot_tpl_filter]jot_tpl_filter[/zrl]
Called to filter template vars before replacement in jot.tpl.
[zrl=[baseurl]/help/hook/jot_header_tpl_filter]jot_header_tpl_filter[/zrl]
Called to filter template vars before replacement in jot_header.tpl.
[zrl=[baseurl]/help/hook/legal_webbie]legal_webbie[/zrl] [zrl=[baseurl]/help/hook/legal_webbie]legal_webbie[/zrl]
Called to validate a channel address Called to validate a channel address

View File

@ -137,7 +137,7 @@ function z_mime_content_type($filename) {
* @param string $hash (optional) * @param string $hash (optional)
* @param string $filename (optional) * @param string $filename (optional)
* @param string $filetype (optional) * @param string $filetype (optional)
* @return associative array with: * @return array Associative array with:
* * \e boolean \b success * * \e boolean \b success
* * \e int|boolean \b results amount of found results, or false * * \e int|boolean \b results amount of found results, or false
* * \e string \b message with error messages if any * * \e string \b message with error messages if any
@ -176,15 +176,17 @@ function attach_count_files($channel_id, $observer, $hash = '', $filename = '',
/** /**
* @brief Returns a list of files/attachments. * @brief Returns a list of files/attachments.
* *
* @param $channel_id * @param int $channel_id
* @param $observer * @param string $observer
* @param $hash (optional) * @param string $hash (optional)
* @param $filename (optional) * @param string $filename (optional)
* @param $filetype (optional) * @param string $filetype (optional)
* @param $orderby * @param string $orderby (optional)
* @param $start * @param int $start (optional)
* @param $entries * @param int $entries (optional)
* @return associative array with: * @param string $since (optional)
* @param string $until (optional)
* @return array an associative array with:
* * \e boolean \b success * * \e boolean \b success
* * \e array|boolean \b results array with results, or false * * \e array|boolean \b results array with results, or false
* * \e string \b message with error messages if any * * \e string \b message with error messages if any
@ -1429,7 +1431,16 @@ function attach_delete($channel_id, $resource, $is_photo = 0) {
if(! $r) { if(! $r) {
attach_drop_photo($channel_id,$resource); attach_drop_photo($channel_id,$resource);
$arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo' => $is_photo]; $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo' => $is_photo];
call_hooks("attach_delete",$arr);
/**
* @hooks attach_delete
* Called when deleting an attachment from channel.
* * \e int \b channel_id - the channel_id
* * \e string \b resource
* * \e int \b is_photo
*/
call_hooks('attach_delete', $arr);
return; return;
} }
@ -1489,7 +1500,14 @@ function attach_delete($channel_id, $resource, $is_photo = 0) {
); );
$arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo' => $is_photo]; $arr = ['channel_id' => $channel_id, 'resource' => $resource, 'is_photo' => $is_photo];
call_hooks("attach_delete",$arr); /**
* @hooks attach_delete
* Called when deleting an attachment from channel.
* * \e int \b channel_id - the channel_id
* * \e string \b resource
* * \e int \b is_photo
*/
call_hooks('attach_delete', $arr);
file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', true); file_activity($channel_id, $object, $object['allow_cid'], $object['allow_gid'], $object['deny_cid'], $object['deny_gid'], 'update', true);
@ -1868,7 +1886,7 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid,
* @param int $channel_id * @param int $channel_id
* @param string $hash * @param string $hash
* @param string $url * @param string $url
* @return array An associative array for the specified file. * @return array Associative array for the specified file.
*/ */
function get_file_activity_object($channel_id, $hash, $url) { function get_file_activity_object($channel_id, $hash, $url) {

View File

@ -9,7 +9,7 @@ function findpeople_widget() {
if($x || is_site_admin()) { if($x || is_site_admin()) {
App::$page['aside'] .= '<div class="side-link" id="side-invite-remain">' App::$page['aside'] .= '<div class="side-link" id="side-invite-remain">'
. sprintf( tt('%d invitation available','%d invitations available',$x), $x) . sprintf( tt('%d invitation available','%d invitations available',$x), $x)
. '</div>' . $inv; . '</div>';
} }
} }
@ -22,7 +22,7 @@ function findpeople_widget() {
'$hint' => t('Examples: Robert Morgenstein, Fishing'), '$hint' => t('Examples: Robert Morgenstein, Fishing'),
'$findthem' => t('Find'), '$findthem' => t('Find'),
'$suggest' => t('Channel Suggestions'), '$suggest' => t('Channel Suggestions'),
'$similar' => '', // FIXME and uncomment when mod/match working // t('Similar Interests'), '$similar' => '', /// @FIXME fixme and uncomment when mod/match working // t('Similar Interests'),
'$random' => t('Random Profile'), '$random' => t('Random Profile'),
'$inv' => t('Invite Friends'), '$inv' => t('Invite Friends'),
'$advanced_search' => $advanced_search, '$advanced_search' => $advanced_search,
@ -56,7 +56,6 @@ function fileas_widget($baseurl,$selected = '') {
'$all' => t('Everything'), '$all' => t('Everything'),
'$terms' => $terms, '$terms' => $terms,
'$base' => $baseurl, '$base' => $baseurl,
)); ));
} }
@ -100,7 +99,6 @@ function categories_widget($baseurl,$selected = '') {
'$all' => t('Everything'), '$all' => t('Everything'),
'$terms' => $terms, '$terms' => $terms,
'$base' => $baseurl, '$base' => $baseurl,
)); ));
} }
return ''; return '';
@ -144,9 +142,9 @@ function cardcategories_widget($baseurl,$selected = '') {
'$all' => t('Everything'), '$all' => t('Everything'),
'$terms' => $terms, '$terms' => $terms,
'$base' => $baseurl, '$base' => $baseurl,
)); ));
} }
return ''; return '';
} }
@ -189,17 +187,14 @@ function articlecategories_widget($baseurl,$selected = '') {
'$all' => t('Everything'), '$all' => t('Everything'),
'$terms' => $terms, '$terms' => $terms,
'$base' => $baseurl, '$base' => $baseurl,
)); ));
} }
return ''; return '';
} }
function common_friends_visitor_widget($profile_uid,$cnt = 25) { function common_friends_visitor_widget($profile_uid,$cnt = 25) {
if(local_channel() == $profile_uid) if(local_channel() == $profile_uid)
@ -219,16 +214,13 @@ function common_friends_visitor_widget($profile_uid,$cnt = 25) {
$r = common_friends($profile_uid,$observer_hash,0,$cnt,true); $r = common_friends($profile_uid,$observer_hash,0,$cnt,true);
return replace_macros(get_markup_template('remote_friends_common.tpl'), array( return replace_macros(get_markup_template('remote_friends_common.tpl'), [
'$desc' => t('Common Connections'), '$desc' => t('Common Connections'),
'$base' => z_root(), '$base' => z_root(),
'$uid' => $profile_uid, '$uid' => $profile_uid,
'$cid' => $observer,
'$linkmore' => (($t > $cnt) ? 'true' : ''), '$linkmore' => (($t > $cnt) ? 'true' : ''),
'$more' => sprintf( t('View all %d common connections'), $t), '$more' => sprintf( t('View all %d common connections'), $t),
'$items' => $r '$items' => $r,
)); ]);
}; };

View File

@ -1328,7 +1328,7 @@ function hz_status_editor($a, $x, $popup = false) {
$tpl = get_markup_template('jot-header.tpl'); $tpl = get_markup_template('jot-header.tpl');
App::$page['htmlhead'] .= replace_macros($tpl, array( $tplmacros = [
'$baseurl' => z_root(), '$baseurl' => z_root(),
'$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'), '$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'),
'$pretext' => ((x($x,'pretext')) ? $x['pretext'] : ''), '$pretext' => ((x($x,'pretext')) ? $x['pretext'] : ''),
@ -1349,7 +1349,10 @@ function hz_status_editor($a, $x, $popup = false) {
'$nocomment_disabled' => t('Comments disabled'), '$nocomment_disabled' => t('Comments disabled'),
'$auto_save_draft' => $feature_auto_save_draft, '$auto_save_draft' => $feature_auto_save_draft,
'$reset' => $reset '$reset' => $reset
)); ];
call_hooks('jot_header_tpl_filter',$tplmacros);
App::$page['htmlhead'] .= replace_macros($tpl, $tplmacros);
$tpl = get_markup_template('jot.tpl'); $tpl = get_markup_template('jot.tpl');
@ -1389,7 +1392,7 @@ function hz_status_editor($a, $x, $popup = false) {
$sharebutton = (x($x,'button') ? $x['button'] : t('Share')); $sharebutton = (x($x,'button') ? $x['button'] : t('Share'));
$placeholdtext = (x($x,'content_label') ? $x['content_label'] : $sharebutton); $placeholdtext = (x($x,'content_label') ? $x['content_label'] : $sharebutton);
$o .= replace_macros($tpl, array( $tplmacros = [
'$return_path' => ((x($x, 'return_path')) ? $x['return_path'] : App::$query_string), '$return_path' => ((x($x, 'return_path')) ? $x['return_path'] : App::$query_string),
'$action' => z_root() . '/item', '$action' => z_root() . '/item',
'$share' => $sharebutton, '$share' => $sharebutton,
@ -1463,9 +1466,15 @@ function hz_status_editor($a, $x, $popup = false) {
'$bbcode' => ((x($x, 'bbcode')) ? $x['bbcode'] : false), '$bbcode' => ((x($x, 'bbcode')) ? $x['bbcode'] : false),
'$parent' => ((array_key_exists('parent',$x) && $x['parent']) ? $x['parent'] : 0), '$parent' => ((array_key_exists('parent',$x) && $x['parent']) ? $x['parent'] : 0),
'$reset' => $reset, '$reset' => $reset,
'$is_owner' => ((local_channel() && (local_channel() == $x['profile_uid'])) ? true : false) '$is_owner' => ((local_channel() && (local_channel() == $x['profile_uid'])) ? true : false),
)); '$custommoretoolsdropdown' => '',
'$custommoretoolsbuttons' => '',
'$customsubmitright' => []
];
call_hooks('jot_tpl_filter',$tplmacros);
$o .= replace_macros($tpl, $tplmacros);
if ($popup === true) { if ($popup === true) {
$o = '<div id="jot-popup" style="display:none">' . $o . '</div>'; $o = '<div id="jot-popup" style="display:none">' . $o . '</div>';
} }

View File

@ -6,6 +6,10 @@
use Sabre\VObject; use Sabre\VObject;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\Exception\UnsatisfiedDependencyException;
require_once('include/bbcode.php'); require_once('include/bbcode.php');
/** /**
@ -463,8 +467,13 @@ function event_store_event($arr) {
$hash = $arr['external_id']; $hash = $arr['external_id'];
elseif(array_key_exists('event_hash',$arr)) elseif(array_key_exists('event_hash',$arr))
$hash = $arr['event_hash']; $hash = $arr['event_hash'];
else else {
$hash = random_string() . '@' . App::get_hostname(); try {
$hash = Uuid::uuid4()->toString();
} catch (UnsatisfiedDependencyException $e) {
$hash = random_string(48);
}
}
$r = q("INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,dtstart,dtend,summary,description,location,etype, $r = q("INSERT INTO event ( uid,aid,event_xchan,event_hash,created,edited,dtstart,dtend,summary,description,location,etype,
adjust,nofinish, event_status, event_status_date, event_percent, event_repeat, event_sequence, event_priority, event_vdata, allow_cid,allow_gid,deny_cid,deny_gid) adjust,nofinish, event_status, event_status_date, event_percent, event_repeat, event_sequence, event_priority, event_vdata, allow_cid,allow_gid,deny_cid,deny_gid)
@ -1126,8 +1135,8 @@ function event_store_item($arr, $event) {
} }
if(! $arr['mid']) { if(! $arr['mid']) {
$arr['uuid'] = item_message_id(); $arr['uuid'] = $event['event_hash'];
$arr['mid'] = z_root() . '/item/' . $arr['uuid']; $arr['mid'] = z_root() . '/event/' . $event['event_hash'];
} }
$item_arr['aid'] = $z[0]['channel_account_id']; $item_arr['aid'] = $z[0]['channel_account_id'];

View File

@ -360,14 +360,6 @@ function get_features($filtered = true, $level = (-1)) {
get_config('feature_lock','personal_tab') get_config('feature_lock','personal_tab')
], ],
[
'affinity',
t('Affinity Tool'),
t('Filter stream activity by depth of relationships'),
false,
get_config('feature_lock','affinity')
],
[ [
'suggest', 'suggest',
t('Suggest Channels'), t('Suggest Channels'),

View File

@ -261,13 +261,13 @@ function construct_activity_target($item) {
* @param SimplePie $item * @param SimplePie $item
* @return array $author * @return array $author
*/ */
function get_atom_author($feed, $item) { function get_atom_author($feed, $item) {
$author = []; $author = [];
$found_author = $item->get_author(); $found_author = $item->get_author();
if($found_author) { if($found_author) {
/// @FIXME $rawauthor is undefined here
if($rawauthor) { if($rawauthor) {
if($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']) if($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data'])
$author['full_name'] = unxmlify($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']); $author['full_name'] = unxmlify($rawauthor[0]['child'][NAMESPACE_POCO]['displayName'][0]['data']);
@ -398,10 +398,10 @@ function get_atom_author($feed, $item) {
'author' => $author 'author' => $author
]; ];
/** /**
* @hooks parse_atom * @hooks parse_atom_author
* * \e SimplePie \b feed - The original SimplePie feed * * \e SimplePie \b feed - The original SimplePie feed
* * \e SimplePie \b item * * \e SimplePie \b item
* * \e array \b result - the result array that will also get returned * * \e array \b author - the result array that will also get returned
*/ */
call_hooks('parse_atom_author', $arr); call_hooks('parse_atom_author', $arr);
@ -416,10 +416,8 @@ function get_atom_author($feed, $item) {
* *
* @param SimplePie $feed * @param SimplePie $feed
* @param SimplePie $item * @param SimplePie $item
* @param[out] array $author
* @return array Associative array with the parsed item data * @return array Associative array with the parsed item data
*/ */
function get_atom_elements($feed, $item) { function get_atom_elements($feed, $item) {
require_once('include/html2bbcode.php'); require_once('include/html2bbcode.php');

View File

@ -7,7 +7,8 @@ use \Michelf\MarkdownExtra;
* @brief * @brief
* *
* @param string $path * @param string $path
* @return string|unknown * @param string $suffix (optional) default null
* @return string
*/ */
function get_help_fullpath($path, $suffix = null) { function get_help_fullpath($path, $suffix = null) {
@ -49,8 +50,8 @@ function get_help_fullpath($path,$suffix=null) {
/** /**
* @brief * @brief
* *
* @param string $tocpath * @param string $tocpath (optional) default false
* @return string|unknown * @return string
*/ */
function get_help_content($tocpath = false) { function get_help_content($tocpath = false) {
@ -171,16 +172,20 @@ function preg_callback_help_include($matches) {
} }
/** /**
* @brief * @brief Determines help language.
* *
* @return boolean|array * If the language was specified in the URL, override the language preference
* of the browser. Default to English if both of these are absent.
*
* @return array Associative array with:
* * \e string \b language - 2-letter ISO 639-1 code ("en")
* * \e boolean \b from_url - true if language from URL overrides browser default
*/ */
function determine_help_language() { function determine_help_language() {
$lang_detect = new Text_LanguageDetect(); $lang_detect = new Text_LanguageDetect();
// Set this mode to recognize language by the short code like "en", "ru", etc. // Set this mode to recognize language by the short code like "en", "ru", etc.
$lang_detect->setNameMode(2); $lang_detect->setNameMode(2);
// If the language was specified in the URL, override the language preference
// of the browser. Default to English if both of these are absent.
if($lang_detect->languageExists(argv(1))) { if($lang_detect->languageExists(argv(1))) {
$lang = argv(1); $lang = argv(1);
$from_url = true; $from_url = true;
@ -211,10 +216,10 @@ function find_doc_file($s) {
} }
/** /**
* @brief * @brief Search in doc files.
* *
* @param string $s * @param string $s The search string to search for
* @return number|mixed|unknown|boolean * @return array
*/ */
function search_doc_files($s) { function search_doc_files($s) {
@ -275,7 +280,6 @@ function doc_rank_sort($s1, $s2) {
* *
* @return string * @return string
*/ */
function load_context_help() { function load_context_help() {
$path = App::$cmd; $path = App::$cmd;
@ -305,7 +309,7 @@ function load_context_help() {
* @brief * @brief
* *
* @param string $s * @param string $s
* @return void|boolean[]|number[]|string[]|unknown[] * @return void|array
*/ */
function store_doc_file($s) { function store_doc_file($s) {

View File

@ -12,6 +12,7 @@ require_once('include/perm_upgrade.php');
* @param array $channel * @param array $channel
* @param int $account_id * @param int $account_id
* @param int $seize * @param int $seize
* @param string $newname (optional)
* @return boolean|array * @return boolean|array
*/ */
function import_channel($channel, $account_id, $seize, $newname = '') { function import_channel($channel, $account_id, $seize, $newname = '') {
@ -672,7 +673,7 @@ function import_items($channel, $items, $sync = false, $relocate = null) {
/** /**
* @brief Sync items to channel. * @brief Sync items to channel.
* *
* @see import_items * @see import_items()
* *
* @param array $channel where to import to * @param array $channel where to import to
* @param array $items * @param array $items
@ -1049,7 +1050,7 @@ function import_mail($channel, $mails, $sync = false) {
/** /**
* @brief Synchronise mails. * @brief Synchronise mails.
* *
* @see import_mail * @see import_mail()
* @param array $channel * @param array $channel
* @param array $mails * @param array $mails
*/ */

View File

@ -7,6 +7,7 @@
use Zotlabs\Lib\Enotify; use Zotlabs\Lib\Enotify;
use Zotlabs\Lib\MarkdownSoap; use Zotlabs\Lib\MarkdownSoap;
use Zotlabs\Lib\MessageFilter; use Zotlabs\Lib\MessageFilter;
use Zotlabs\Lib\ThreadListener;
use Zotlabs\Lib\IConfig; use Zotlabs\Lib\IConfig;
use Zotlabs\Access\PermissionLimits; use Zotlabs\Access\PermissionLimits;
use Zotlabs\Access\AccessList; use Zotlabs\Access\AccessList;
@ -141,6 +142,22 @@ function collect_recipients($item, &$private_envelope,$include_groups = true) {
// $recipients[] = $sys['xchan_hash']; // $recipients[] = $sys['xchan_hash'];
} }
// Forward to thread listeners, *unless* there is even a remote hint that the item
// might have some privacy attached. This could be (for instance) an ActivityPub DM
// in the middle of a public thread. Unless we can guarantee beyond all doubt that
// this is public, don't allow it to go to thread listeners.
if(! intval($item['item_private'])) {
$r = ThreadListener::fetch_by_target($item['parent_mid']);
if($r) {
foreach($r as $rv) {
$recipients[] = $rv['portable_id'];
}
}
}
// Add the authors of any posts in this thread, if they are known to us. // Add the authors of any posts in this thread, if they are known to us.
// This is specifically designed to forward wall-to-wall posts to the original author, // This is specifically designed to forward wall-to-wall posts to the original author,
// in case they aren't a connection but have permission to write on our wall. // in case they aren't a connection but have permission to write on our wall.
@ -469,6 +486,8 @@ function post_activity_item($arr, $allow_code = false, $deliver = true) {
*/ */
call_hooks('post_local_end', $ret['activity']); call_hooks('post_local_end', $ret['activity']);
} }
else
return $ret;
if($post_id && $deliver) { if($post_id && $deliver) {
Master::Summon([ 'Notifier','activity',$post_id ]); Master::Summon([ 'Notifier','activity',$post_id ]);
@ -2048,6 +2067,11 @@ function item_store($arr, $allow_exec = false, $deliver = true) {
item_update_parent_commented($arr); item_update_parent_commented($arr);
if(strpos($arr['body'],'[embed]') !== false) {
Master::Summon([ 'Cache_embeds', $current_post ]);
}
// If _creating_ a deleted item, don't propagate it further or send out notifications. // If _creating_ a deleted item, don't propagate it further or send out notifications.
// We need to store the item details just in case the delete came in before the original post, // We need to store the item details just in case the delete came in before the original post,
// so that we have an item in the DB that's marked deleted and won't store a fresh post // so that we have an item in the DB that's marked deleted and won't store a fresh post
@ -2384,6 +2408,13 @@ function item_store_update($arr, $allow_exec = false, $deliver = true) {
*/ */
call_hooks('item_stored_update',$arr); call_hooks('item_stored_update',$arr);
if(strpos($arr['body'],'[embed]') !== false) {
Master::Summon([ 'Cache_embeds', $orig_post_id ]);
}
if($deliver) { if($deliver) {
send_status_notifications($orig_post_id,$arr); send_status_notifications($orig_post_id,$arr);
tag_deliver($uid,$orig_post_id); tag_deliver($uid,$orig_post_id);
@ -3004,7 +3035,9 @@ function tgroup_check($uid, $item) {
* @param array $channel * @param array $channel
* @param array $item * @param array $item
* @param int $item_id * @param int $item_id
* @param boolean $parent * @param array $parent
* @param boolean $edit (optional) default false
* @return void
*/ */
function start_delivery_chain($channel, $item, $item_id, $parent, $edit = false) { function start_delivery_chain($channel, $item, $item_id, $parent, $edit = false) {
@ -3853,6 +3886,8 @@ function delete_item_lowlevel($item, $stage = DROPITEM_NORMAL, $force = false) {
intval(TERM_OBJ_POST) intval(TERM_OBJ_POST)
); );
ThreadListener::delete_by_target($item['mid']);
/** @FIXME remove notifications for this item */ /** @FIXME remove notifications for this item */
return true; return true;
@ -4547,7 +4582,7 @@ function set_linkified_perms($linkified, &$str_contact_allow, &$str_group_allow,
$first_access_tag = true; $first_access_tag = true;
foreach($linkified as $x) { foreach($linkified as $x) {
$access_tag = $x['access_tag']; $access_tag = $x['success']['access_tag'];
if(($access_tag) && (! $parent_item)) { if(($access_tag) && (! $parent_item)) {
logger('access_tag: ' . $tag . ' ' . print_r($access_tag,true), LOGGER_DATA); logger('access_tag: ' . $tag . ' ' . print_r($access_tag,true), LOGGER_DATA);
if ($first_access_tag && (! get_pconfig($profile_uid,'system','no_private_mention_acl_override'))) { if ($first_access_tag && (! get_pconfig($profile_uid,'system','no_private_mention_acl_override'))) {

View File

@ -44,7 +44,7 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep
$body = cleanup_bbcode($body); $body = cleanup_bbcode($body);
$results = linkify_tags($a, $body, $uid); $results = linkify_tags($body, $uid);
if(! $raw) { if(! $raw) {
if(preg_match_all("/\[attachment\](.*?)\[\/attachment\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match)) { if(preg_match_all("/\[attachment\](.*?)\[\/attachment\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match)) {

View File

@ -1,18 +1,34 @@
<?php /** @file */ <?php
use Zotlabs\Photo\PhotoDriver;
use Zotlabs\Photo\PhotoGd;
use Zotlabs\Photo\PhotoImagick;
/**
* @brief Return a PhotoDriver object.
*
* Use this factory when manipulating images.
*
* Return a photo driver object implementing ImageMagick or GD.
*
* @param string $data Image data
* @param string $type Mimetype
* @return null|PhotoDriver
* NULL if unsupported image type or failure, otherwise photo driver object
*/
function photo_factory($data, $type = null) { function photo_factory($data, $type = null) {
$ph = null; $ph = null;
$m = null;
$unsupported_types = [
$unsupported_types = array(
'image/bmp', 'image/bmp',
'image/vnd.microsoft.icon', 'image/vnd.microsoft.icon',
'image/tiff', 'image/tiff',
'image/svg+xml' 'image/svg+xml',
); ];
if($type && in_array(strtolower($type), $unsupported_types)) { if($type && in_array(strtolower($type), $unsupported_types)) {
logger('photo_factory: unsupported image type'); logger('Unsupported image type ' . $type);
return null; return null;
} }
@ -22,10 +38,8 @@ function photo_factory($data, $type = null) {
$v = Imagick::getVersion(); $v = Imagick::getVersion();
preg_match('/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v['versionString'], $m); preg_match('/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v['versionString'], $m);
if(version_compare($m[1], '6.6.7') >= 0) { if(version_compare($m[1], '6.6.7') >= 0) {
require_once('include/photo/photo_imagick.php'); $ph = new PhotoImagick($data, $type);
$ph = new photo_imagick($data,$type); } else {
}
else {
// earlier imagick versions have issues with scaling png's // earlier imagick versions have issues with scaling png's
// don't log this because it will just fill the logfile. // don't log this because it will just fill the logfile.
// leave this note here so those who are looking for why // leave this note here so those who are looking for why
@ -34,458 +48,32 @@ function photo_factory($data, $type = null) {
} }
if(! $ph) { if(! $ph) {
require_once('include/photo/photo_gd.php'); $ph = new PhotoGd($data, $type);
$ph = new photo_gd($data,$type);
} }
return $ph; return $ph;
} }
abstract class photo_driver {
protected $image;
protected $width;
protected $height;
protected $valid;
protected $type;
protected $types;
abstract function supportedTypes();
abstract function load($data,$type);
abstract function destroy();
abstract function setDimensions();
abstract function getImage();
abstract function doScaleImage($new_width,$new_height);
abstract function rotate($degrees);
abstract function flip($horiz = true, $vert = false);
abstract function cropImage($max,$x,$y,$w,$h);
abstract function cropImageRect($maxx,$maxy,$x,$y,$w,$h);
abstract function imageString();
abstract function clearexif();
public function __construct($data, $type='') {
$this->types = $this->supportedTypes();
if (! array_key_exists($type,$this->types)){
$type='image/jpeg';
}
$this->type = $type;
$this->valid = false;
$this->load($data,$type);
}
public function __destruct() {
if($this->is_valid())
$this->destroy();
}
public function is_valid() {
return $this->valid;
}
public function getWidth() {
if(!$this->is_valid())
return FALSE;
return $this->width;
}
public function getHeight() {
if(!$this->is_valid())
return FALSE;
return $this->height;
}
public function saveImage($path) {
if(!$this->is_valid())
return FALSE;
return (file_put_contents($path, $this->imageString()) ? true : false);
}
public function getType() {
if(!$this->is_valid())
return FALSE;
return $this->type;
}
public function getExt() {
if(!$this->is_valid())
return FALSE;
return $this->types[$this->getType()];
}
/** /**
* @brief scale image * @brief Guess image mimetype from filename or from Content-Type header.
* int $max maximum pixel size in either dimension
* boolean $float_height - if true allow height to float to any length on tall images,
* constraining only the width
*/
public function scaleImage($max, $float_height = true) {
if(!$this->is_valid())
return FALSE;
$width = $this->width;
$height = $this->height;
$dest_width = $dest_height = 0;
if((! $width)|| (! $height))
return FALSE;
if($width > $max && $height > $max) {
// very tall image (greater than 16:9)
// constrain the width - let the height float.
if(((($height * 9) / 16) > $width) && ($float_height)) {
$dest_width = $max;
$dest_height = intval(( $height * $max ) / $width);
}
// else constrain both dimensions
elseif($width > $height) {
$dest_width = $max;
$dest_height = intval(( $height * $max ) / $width);
}
else {
$dest_width = intval(( $width * $max ) / $height);
$dest_height = $max;
}
}
else {
if( $width > $max ) {
$dest_width = $max;
$dest_height = intval(( $height * $max ) / $width);
}
else {
if( $height > $max ) {
// very tall image (greater than 16:9)
// but width is OK - don't do anything
if(((($height * 9) / 16) > $width) && ($float_height)) {
$dest_width = $width;
$dest_height = $height;
}
else {
$dest_width = intval(( $width * $max ) / $height);
$dest_height = $max;
}
}
else {
$dest_width = $width;
$dest_height = $height;
}
}
}
$this->doScaleImage($dest_width,$dest_height);
}
public function scaleImageUp($min) {
if(!$this->is_valid())
return FALSE;
$width = $this->width;
$height = $this->height;
$dest_width = $dest_height = 0;
if((! $width)|| (! $height))
return FALSE;
if($width < $min && $height < $min) {
if($width > $height) {
$dest_width = $min;
$dest_height = intval(( $height * $min ) / $width);
}
else {
$dest_width = intval(( $width * $min ) / $height);
$dest_height = $min;
}
}
else {
if( $width < $min ) {
$dest_width = $min;
$dest_height = intval(( $height * $min ) / $width);
}
else {
if( $height < $min ) {
$dest_width = intval(( $width * $min ) / $height);
$dest_height = $min;
}
else {
$dest_width = $width;
$dest_height = $height;
}
}
}
$this->doScaleImage($dest_width,$dest_height);
}
public function scaleImageSquare($dim) {
if(!$this->is_valid())
return FALSE;
$this->doScaleImage($dim,$dim);
}
/**
* @brief reads exif data from filename
*/
public function exif($filename) {
if((! function_exists('exif_read_data'))
|| (! in_array($this->getType(), [ 'image/jpeg' , 'image/tiff'] ))) {
return false;
}
/*
* PHP 7.2 allows you to use a stream resource, which should reduce/avoid
* memory exhaustion on large images.
*/
if(version_compare(PHP_VERSION,'7.2.0') >= 0) {
$f = @fopen($filename,'rb');
}
else {
$f = $filename;
}
if($f) {
return @exif_read_data($f,null,true);
}
return false;
}
/**
* @brief orients current image based on exif orientation information
*/
public function orient($exif) {
if(! ($this->is_valid() && $exif)) {
return false;
}
$ort = ((array_key_exists('IFD0',$exif)) ? $exif['IFD0']['Orientation'] : $exif['Orientation']);
if(! $ort) {
return false;
}
switch($ort) {
case 1: // nothing
break;
case 2: // horizontal flip
$this->flip();
break;
case 3: // 180 rotate left
$this->rotate(180);
break;
case 4: // vertical flip
$this->flip(false, true);
break;
case 5: // vertical flip + 90 rotate right
$this->flip(false, true);
$this->rotate(-90);
break;
case 6: // 90 rotate right
$this->rotate(-90);
break;
case 7: // horizontal flip + 90 rotate right
$this->flip();
$this->rotate(-90);
break;
case 8: // 90 rotate left
$this->rotate(90);
break;
default:
break;
}
return true;
}
public function save($arr, $skipcheck = false) {
if(! ($skipcheck || $this->is_valid())) {
logger('attempt to store invalid photo.');
return false;
}
$p = array();
$p['aid'] = ((intval($arr['aid'])) ? intval($arr['aid']) : 0);
$p['uid'] = ((intval($arr['uid'])) ? intval($arr['uid']) : 0);
$p['xchan'] = (($arr['xchan']) ? $arr['xchan'] : '');
$p['resource_id'] = (($arr['resource_id']) ? $arr['resource_id'] : '');
$p['filename'] = (($arr['filename']) ? $arr['filename'] : '');
$p['mimetype'] = (($arr['mimetype']) ? $arr['mimetype'] : $this->getType());
$p['album'] = (($arr['album']) ? $arr['album'] : '');
$p['imgscale'] = ((intval($arr['imgscale'])) ? intval($arr['imgscale']) : 0);
$p['allow_cid'] = (($arr['allow_cid']) ? $arr['allow_cid'] : '');
$p['allow_gid'] = (($arr['allow_gid']) ? $arr['allow_gid'] : '');
$p['deny_cid'] = (($arr['deny_cid']) ? $arr['deny_cid'] : '');
$p['deny_gid'] = (($arr['deny_gid']) ? $arr['deny_gid'] : '');
$p['edited'] = (($arr['edited']) ? $arr['edited'] : datetime_convert());
$p['title'] = (($arr['title']) ? $arr['title'] : '');
$p['description'] = (($arr['description']) ? $arr['description'] : '');
$p['photo_usage'] = intval($arr['photo_usage']);
$p['os_storage'] = intval($arr['os_storage']);
$p['os_path'] = $arr['os_path'];
$p['os_syspath'] = ((array_key_exists('os_syspath',$arr)) ? $arr['os_syspath'] : '');
$p['display_path'] = (($arr['display_path']) ? $arr['display_path'] : '');
$p['width'] = (($arr['width']) ? $arr['width'] : $this->getWidth());
$p['height'] = (($arr['height']) ? $arr['height'] : $this->getHeight());
$p['expires'] = (($arr['expires']) ? $arr['expires'] : gmdate('Y-m-d H:i:s', time() + get_config('system','photo_cache_time', 86400)));
if(! intval($p['imgscale']))
logger('save: ' . print_r($arr,true), LOGGER_DATA);
$x = q("select id, created from photo where resource_id = '%s' and uid = %d and xchan = '%s' and imgscale = %d limit 1",
dbesc($p['resource_id']),
intval($p['uid']),
dbesc($p['xchan']),
intval($p['imgscale'])
);
if($x) {
$p['created'] = (($x['created']) ? $x['created'] : $p['edited']);
$r = q("UPDATE photo set
aid = %d,
uid = %d,
xchan = '%s',
resource_id = '%s',
created = '%s',
edited = '%s',
filename = '%s',
mimetype = '%s',
album = '%s',
height = %d,
width = %d,
content = '%s',
os_storage = %d,
filesize = %d,
imgscale = %d,
photo_usage = %d,
title = '%s',
description = '%s',
os_path = '%s',
display_path = '%s',
allow_cid = '%s',
allow_gid = '%s',
deny_cid = '%s',
deny_gid = '%s',
expires = '%s'
where id = %d",
intval($p['aid']),
intval($p['uid']),
dbesc($p['xchan']),
dbesc($p['resource_id']),
dbescdate($p['created']),
dbescdate($p['edited']),
dbesc(basename($p['filename'])),
dbesc($p['mimetype']),
dbesc($p['album']),
intval($p['height']),
intval($p['width']),
(intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())),
intval($p['os_storage']),
(intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())),
intval($p['imgscale']),
intval($p['photo_usage']),
dbesc($p['title']),
dbesc($p['description']),
dbesc($p['os_path']),
dbesc($p['display_path']),
dbesc($p['allow_cid']),
dbesc($p['allow_gid']),
dbesc($p['deny_cid']),
dbesc($p['deny_gid']),
dbescdate($p['expires']),
intval($x[0]['id'])
);
}
else {
$p['created'] = (($arr['created']) ? $arr['created'] : $p['edited']);
$r = q("INSERT INTO photo
( aid, uid, xchan, resource_id, created, edited, filename, mimetype, album, height, width, content, os_storage, filesize, imgscale, photo_usage, title, description, os_path, display_path, allow_cid, allow_gid, deny_cid, deny_gid, expires )
VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', %d, %d, %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' )",
intval($p['aid']),
intval($p['uid']),
dbesc($p['xchan']),
dbesc($p['resource_id']),
dbescdate($p['created']),
dbescdate($p['edited']),
dbesc(basename($p['filename'])),
dbesc($p['mimetype']),
dbesc($p['album']),
intval($p['height']),
intval($p['width']),
(intval($p['os_storage']) ? dbescbin($p['os_syspath']) : dbescbin($this->imageString())),
intval($p['os_storage']),
(intval($p['os_storage']) ? @filesize($p['os_syspath']) : strlen($this->imageString())),
intval($p['imgscale']),
intval($p['photo_usage']),
dbesc($p['title']),
dbesc($p['description']),
dbesc($p['os_path']),
dbesc($p['display_path']),
dbesc($p['allow_cid']),
dbesc($p['allow_gid']),
dbesc($p['deny_cid']),
dbesc($p['deny_gid']),
dbescdate($p['expires'])
);
}
logger('photo save ' . $p['imgscale'] . ' returned ' . intval($r));
return $r;
}
}
/**
* Guess image mimetype from filename or from Content-Type header
* *
* @arg $filename string Image filename * @param string $filename
* @arg $headers string Headers to check for Content-Type (from curl request) * Image filename
* @param string $headers (optional)
* Headers to check for Content-Type (from curl request)
* @return null|string Guessed mimetype
*/ */
function guess_image_type($filename, $headers = '') { function guess_image_type($filename, $headers = '') {
// logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG); // logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG);
$type = null; $type = null;
$m = null;
if($headers) { if($headers) {
$hdrs=array(); $hdrs = [];
$h = explode("\n", $headers); $h = explode("\n", $headers);
foreach ($h as $l) { foreach ($h as $l) {
list($k,$v) = array_map("trim", explode(":", trim($l), 2)); list($k, $v) = array_map('trim', explode(':', trim($l), 2));
$hdrs[strtolower($k)] = $v; $hdrs[strtolower($k)] = $v;
} }
logger('Curl headers: ' .var_export($hdrs, true), LOGGER_DEBUG); logger('Curl headers: ' .var_export($hdrs, true), LOGGER_DEBUG);
@ -551,11 +139,17 @@ function guess_image_type($filename, $headers = '') {
} }
logger('Photo: guess_image_type: filename = ' . $filename . ' type = ' . $type, LOGGER_DEBUG); logger('Photo: guess_image_type: filename = ' . $filename . ' type = ' . $type, LOGGER_DEBUG);
return $type;
return $type;
} }
/**
* @brief Delete thing photo from database.
*
* @param string $url
* @param string $ob_hash
* @return void
*/
function delete_thing_photo($url, $ob_hash) { function delete_thing_photo($url, $ob_hash) {
$hash = basename($url); $hash = basename($url);
@ -566,18 +160,15 @@ function delete_thing_photo($url,$ob_hash) {
if((! $ob_hash) || (strlen($hash) < 16)) if((! $ob_hash) || (strlen($hash) < 16))
return; return;
$r = q("delete from photo where xchan = '%s' and photo_usage = %d and resource_id = '%s'", q("delete from photo where xchan = '%s' and photo_usage = %d and resource_id = '%s'",
dbesc($ob_hash), dbesc($ob_hash),
intval(PHOTO_THING), intval(PHOTO_THING),
dbesc($hash) dbesc($hash)
); );
} }
/** /**
* @brief fetches an photo from external site and prepares its miniatures. * @brief Fetches a photo from external site and prepares its miniatures.
* *
* @param string $photo * @param string $photo
* external URL to fetch base image * external URL to fetch base image
@ -596,30 +187,24 @@ function delete_thing_photo($url,$ob_hash) {
* * \e boolean \b 4 => TRUE if fetch failure * * \e boolean \b 4 => TRUE if fetch failure
* * \e string \b 5 => modification date * * \e string \b 5 => modification date
*/ */
function import_xchan_photo($photo, $xchan, $thing = false, $force = false) { function import_xchan_photo($photo, $xchan, $thing = false, $force = false) {
$modified = ''; $modified = '';
$o = null;
$flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN); $flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN);
$album = (($thing) ? 'Things' : 'Contact Photos'); $album = (($thing) ? 'Things' : 'Contact Photos');
logger('import_xchan_photo: updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG); logger('Updating channel photo from ' . $photo . ' for ' . $xchan, LOGGER_DEBUG);
if($thing) { if($thing) {
$hash = photo_new_resource(); $hash = photo_new_resource();
} } else {
else { $r = q("select resource_id, edited, mimetype from photo where xchan = '%s' and photo_usage = %d and imgscale = 4 limit 1", dbesc($xchan), intval(PHOTO_XCHAN));
$r = q("select resource_id, edited, mimetype from photo where xchan = '%s' and photo_usage = %d and imgscale = 4 limit 1",
dbesc($xchan),
intval(PHOTO_XCHAN)
);
if($r) { if($r) {
$hash = $r[0]['resource_id']; $hash = $r[0]['resource_id'];
$modified = $r[0]['edited']; $modified = $r[0]['edited'];
$type = $r[0]['mimetype']; $type = $r[0]['mimetype'];
} } else {
else {
$hash = photo_new_resource(); $hash = photo_new_resource();
} }
} }
@ -628,13 +213,15 @@ function import_xchan_photo($photo,$xchan,$thing = false,$force = false) {
$img_str = ''; $img_str = '';
if($photo) { if($photo) {
$filename = basename($photo);
if($force || $modified == '') { if($force || $modified == '') {
$result = z_fetch_url($photo, true); $result = z_fetch_url($photo, true);
} } else {
else { $h = [
$h = array('headers' => array("If-Modified-Since: " . gmdate("D, d M Y H:i:s", strtotime($modified . "Z")) . " GMT")); 'headers' => [
'If-Modified-Since: ' . gmdate('D, d M Y H:i:s', strtotime($modified . 'Z')) . ' GMT'
]
];
$result = z_fetch_url($photo, true, 0, $h); $result = z_fetch_url($photo, true, 0, $h);
} }
@ -644,19 +231,16 @@ function import_xchan_photo($photo,$xchan,$thing = false,$force = false) {
$modified = gmdate('Y-m-d H:i:s', (preg_match('/last-modified: (.+) \S+/i', $result['header'], $o) ? strtotime($o[1] . 'Z') : time())); $modified = gmdate('Y-m-d H:i:s', (preg_match('/last-modified: (.+) \S+/i', $result['header'], $o) ? strtotime($o[1] . 'Z') : time()));
if(is_null($type)) if(is_null($type))
$photo_failure = true; $photo_failure = true;
} } elseif($result['return_code'] == 304) {
elseif($result['return_code'] == 304) {
$photo = z_root() . '/photo/' . $hash . '-4'; $photo = z_root() . '/photo/' . $hash . '-4';
$thumb = z_root() . '/photo/' . $hash . '-5'; $thumb = z_root() . '/photo/' . $hash . '-5';
$micro = z_root() . '/photo/' . $hash . '-6'; $micro = z_root() . '/photo/' . $hash . '-6';
} } else {
else {
$photo_failure = true; $photo_failure = true;
} }
} else {
}
else
$photo_failure = true; $photo_failure = true;
}
if(!$photo_failure && $result['return_code'] != 304) { if(!$photo_failure && $result['return_code'] != 304) {
$img = photo_factory($img_str, $type); $img = photo_factory($img_str, $type);
@ -669,58 +253,48 @@ function import_xchan_photo($photo,$xchan,$thing = false,$force = false) {
// crop out the sides // crop out the sides
$margin = $width - $height; $margin = $width - $height;
$img->cropImage(300, ($margin / 2), 0, $height, $height); $img->cropImage(300, ($margin / 2), 0, $height, $height);
} } elseif(($height / $width) > 1.2) {
elseif(($height / $width) > 1.2) {
// crop out the bottom // crop out the bottom
$margin = $height - $width; $margin = $height - $width;
$img->cropImage(300, 0, 0, $width, $width); $img->cropImage(300, 0, 0, $width, $width);
} else {
}
else {
$img->scaleImageSquare(300); $img->scaleImageSquare(300);
} }
} else {
}
else
$photo_failure = true; $photo_failure = true;
}
$p = array( $p = [
'xchan' => $xchan, 'xchan' => $xchan,
'resource_id' => $hash, 'resource_id' => $hash,
'filename' => basename($photo), 'filename' => basename($photo),
'album' => $album, 'album' => $album,
'photo_usage' => $flags, 'photo_usage' => $flags,
'imgscale' => 4, 'imgscale' => 4,
'edited' => $modified 'edited' => $modified,
); ];
$r = $img->save($p); $r = $img->save($p);
if($r === false) if($r === false)
$photo_failure = true; $photo_failure = true;
$img->scaleImage(80); $img->scaleImage(80);
$p['imgscale'] = 5; $p['imgscale'] = 5;
$r = $img->save($p); $r = $img->save($p);
if($r === false) if($r === false)
$photo_failure = true; $photo_failure = true;
$img->scaleImage(48); $img->scaleImage(48);
$p['imgscale'] = 6; $p['imgscale'] = 6;
$r = $img->save($p); $r = $img->save($p);
if($r === false) if($r === false)
$photo_failure = true; $photo_failure = true;
$photo = z_root() . '/photo/' . $hash . '-4'; $photo = z_root() . '/photo/' . $hash . '-4';
$thumb = z_root() . '/photo/' . $hash . '-5'; $thumb = z_root() . '/photo/' . $hash . '-5';
$micro = z_root() . '/photo/' . $hash . '-6'; $micro = z_root() . '/photo/' . $hash . '-6';
} } else {
else { logger('Invalid image from ' . $photo);
logger('import_xchan_photo: invalid image from ' . $photo);
$photo_failure = true; $photo_failure = true;
} }
} }
@ -733,86 +307,95 @@ function import_xchan_photo($photo,$xchan,$thing = false,$force = false) {
$modified = gmdate('Y-m-d H:i:s', filemtime($default)); $modified = gmdate('Y-m-d H:i:s', filemtime($default));
} }
logger('HTTP code: ' . $result['return_code'] . '; modified: ' . $modified . '; failure: ' . ($photo_failure ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG); logger('HTTP code: ' . $result['return_code'] . '; modified: ' . $modified
return(array($photo,$thumb,$micro,$type,$photo_failure,$modified)); . '; failure: ' . ($photo_failure ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG);
return([$photo, $thumb, $micro, $type, $photo_failure, $modified]);
} }
/**
* @brief Import channel photo from a URL.
*
* @param string $photo URL to a photo
* @param int $aid
* @param int $uid channel_id
* @return null|string Guessed image mimetype or null.
*/
function import_channel_photo_from_url($photo, $aid, $uid) { function import_channel_photo_from_url($photo, $aid, $uid) {
$type = null;
if($photo) { if($photo) {
$filename = basename($photo);
$result = z_fetch_url($photo, true); $result = z_fetch_url($photo, true);
if($result['success']) { if($result['success']) {
$img_str = $result['body']; $img_str = $result['body'];
$type = guess_image_type($photo, $result['header']); $type = guess_image_type($photo, $result['header']);
if(is_null($type))
$photo_failure = true;
}
}
else {
$photo_failure = true;
}
import_channel_photo($img_str, $type, $aid, $uid); import_channel_photo($img_str, $type, $aid, $uid);
}
}
return $type; return $type;
} }
/**
* @brief Import a channel photo and prepare its miniatures.
*
* @param string $photo Image data
* @param string $type
* @param int $aid
* @param int $uid channel_id
* @return boolean|string false on failure, otherwise resource_id of photo
*/
function import_channel_photo($photo, $type, $aid, $uid) { function import_channel_photo($photo, $type, $aid, $uid) {
logger('import_channel_photo: importing channel photo for ' . $uid, LOGGER_DEBUG); logger('Importing channel photo for ' . $uid, LOGGER_DEBUG);
$hash = photo_new_resource();
$photo_failure = false; $photo_failure = false;
$hash = photo_new_resource();
$filename = $hash; $filename = $hash;
$img = photo_factory($photo, $type); $img = photo_factory($photo, $type);
if($img->is_valid()) { if($img->is_valid()) {
// config array for image save method
$p = [
'aid' => $aid,
'uid' => $uid,
'resource_id' => $hash,
'filename' => $filename,
'album' => t('Profile Photos'),
'photo_usage' => PHOTO_PROFILE,
'imgscale' => 4,
];
// photo size
$img->scaleImageSquare(300); $img->scaleImageSquare(300);
$p = array('aid' => $aid, 'uid' => $uid, 'resource_id' => $hash, 'filename' => $filename, 'album' => t('Profile Photos'), 'photo_usage' => PHOTO_PROFILE, 'imgscale' => 4);
$r = $img->save($p); $r = $img->save($p);
if($r === false) if($r === false)
$photo_failure = true; $photo_failure = true;
// thumb size
$img->scaleImage(80); $img->scaleImage(80);
$p['imgscale'] = 5; $p['imgscale'] = 5;
$r = $img->save($p); $r = $img->save($p);
if($r === false) if($r === false)
$photo_failure = true; $photo_failure = true;
// micro size
$img->scaleImage(48); $img->scaleImage(48);
$p['imgscale'] = 6; $p['imgscale'] = 6;
$r = $img->save($p); $r = $img->save($p);
if($r === false) if($r === false)
$photo_failure = true; $photo_failure = true;
} } else {
else { logger('Invalid image.');
logger('import_channel_photo: invalid image.');
$photo_failure = true; $photo_failure = true;
} }
//return(($photo_failure)? false : true);
if($photo_failure) if($photo_failure)
return false; return false;
else else
return $hash; return $hash;
} }

View File

@ -1,162 +0,0 @@
<?php /** @file */
require_once('include/photo/photo_driver.php');
class photo_gd extends photo_driver {
function supportedTypes() {
$t = array();
$t['image/jpeg'] ='jpg';
if (imagetypes() & IMG_PNG) $t['image/png'] = 'png';
if (imagetypes() & IMG_GIF) $t['image/gif'] = 'gif';
return $t;
}
function load($data, $type) {
$this->valid = false;
if(! $data)
return;
$this->image = @imagecreatefromstring($data);
if($this->image !== FALSE) {
$this->valid = true;
$this->setDimensions();
imagealphablending($this->image, false);
imagesavealpha($this->image, true);
}
}
function setDimensions() {
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
}
public function clearexif() {
return;
}
public function destroy() {
if($this->is_valid()) {
imagedestroy($this->image);
}
}
public function getImage() {
if(!$this->is_valid())
return FALSE;
return $this->image;
}
public function doScaleImage($dest_width,$dest_height) {
$dest = imagecreatetruecolor( $dest_width, $dest_height );
$width = imagesx($this->image);
$height = imagesy($this->image);
imagealphablending($dest, false);
imagesavealpha($dest, true);
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
if($this->image)
imagedestroy($this->image);
$this->image = $dest;
$this->setDimensions();
}
public function rotate($degrees) {
if(!$this->is_valid())
return FALSE;
$this->image = imagerotate($this->image,$degrees,0);
$this->setDimensions();
}
public function flip($horiz = true, $vert = false) {
if(!$this->is_valid())
return FALSE;
$w = imagesx($this->image);
$h = imagesy($this->image);
$flipped = imagecreate($w, $h);
if($horiz) {
for ($x = 0; $x < $w; $x++) {
imagecopy($flipped, $this->image, $x, 0, $w - $x - 1, 0, 1, $h);
}
}
if($vert) {
for ($y = 0; $y < $h; $y++) {
imagecopy($flipped, $this->image, 0, $y, 0, $h - $y - 1, $w, 1);
}
}
$this->image = $flipped;
$this->setDimensions(); // Shouldn't really be necessary
}
public function cropImage($max,$x,$y,$w,$h) {
if(!$this->is_valid())
return FALSE;
$dest = imagecreatetruecolor( $max, $max );
imagealphablending($dest, false);
imagesavealpha($dest, true);
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
if($this->image)
imagedestroy($this->image);
$this->image = $dest;
$this->setDimensions();
}
public function cropImageRect($maxx,$maxy,$x,$y,$w,$h) {
if(!$this->is_valid())
return FALSE;
$dest = imagecreatetruecolor( $maxx, $maxy );
imagealphablending($dest, false);
imagesavealpha($dest, true);
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $maxx, $maxy, $w, $h);
if($this->image)
imagedestroy($this->image);
$this->image = $dest;
$this->setDimensions();
}
public function imageString() {
if(!$this->is_valid())
return FALSE;
$quality = FALSE;
ob_start();
switch($this->getType()){
case "image/png":
$quality = get_config('system','png_quality');
if((! $quality) || ($quality > 9))
$quality = PNG_QUALITY;
imagepng($this->image,NULL, $quality);
break;
case "image/jpeg":
default:
$quality = get_config('system','jpeg_quality');
if((! $quality) || ($quality > 100))
$quality = JPEG_QUALITY;
imagejpeg($this->image,NULL,$quality);
break;
}
$string = ob_get_contents();
ob_end_clean();
return $string;
}
}

View File

@ -356,7 +356,7 @@ function photo_upload($channel, $observer, $args) {
$large_photos = feature_enabled($channel['channel_id'], 'large_photos'); $large_photos = feature_enabled($channel['channel_id'], 'large_photos');
linkify_tags($a, $args['body'], $channel_id); linkify_tags($args['body'], $channel_id);
if($large_photos) { if($large_photos) {
$scale = 1; $scale = 1;

View File

@ -7,11 +7,13 @@
/** /**
* @brief Handle errors in plugin calls * @brief Handle errors in plugin calls.
* *
* @param string $plugin name of the addon * @param string $plugin name of the addon
* @param string $error_text text of error * @param string $notice UI visible text of error
* @param bool $uninstall uninstall plugin * @param string $log technical error message for logging
* @param bool $uninstall (optional) default false
* uninstall plugin on error
*/ */
function handleerrors_plugin($plugin, $notice, $log, $uninstall = false){ function handleerrors_plugin($plugin, $notice, $log, $uninstall = false){
logger("Addons: [" . $plugin . "] Error: ".$log, LOGGER_ERROR); logger("Addons: [" . $plugin . "] Error: ".$log, LOGGER_ERROR);
@ -381,8 +383,6 @@ function unregister_hook($hook, $file, $function) {
* array in their theme_init() and use this to customise the app behaviour. * array in their theme_init() and use this to customise the app behaviour.
* use insert_hook($hookname,$function_name) to do this. * use insert_hook($hookname,$function_name) to do this.
*/ */
function load_hooks() { function load_hooks() {
App::$hooks = []; App::$hooks = [];
@ -1085,10 +1085,11 @@ function get_markup_template($s, $root = '') {
} }
/** /**
* @brief * @brief Test if a folder exists.
* *
* @param string $folder * @param string $folder
* @return boolean|string * @return boolean|string
* False if folder does not exist, or canonicalized absolute pathname
*/ */
function folder_exists($folder) { function folder_exists($folder) {
// Get canonicalized absolute pathname // Get canonicalized absolute pathname

View File

@ -44,8 +44,8 @@ function replace_macros($s, $r) {
try { try {
$output = $t->replace_macros($arr['template'], $arr['params']); $output = $t->replace_macros($arr['template'], $arr['params']);
} catch (Exception $e) { } catch (Exception $e) {
logger("Unable to render template: ".$e->getMessage()); logger('Unable to render template: ' . $e->getMessage());
$output = "<h3>ERROR: there was an error creating the output.</h3>"; $output = '<h3>ERROR: there was an error creating the output.</h3>';
} }
return $output; return $output;
@ -539,7 +539,14 @@ function paginate(&$a) {
return $o; return $o;
} }
/**
* @brief
*
* @param int $i
* @param string $more
* @param string $less
* @return string Parsed HTML from template 'alt_pager.tpl'
*/
function alt_pager($i, $more = '', $less = '') { function alt_pager($i, $more = '', $less = '') {
if(! $more) if(! $more)
@ -810,7 +817,7 @@ function activity_match($haystack,$needle) {
* and strip the period from any tags which end with one. * and strip the period from any tags which end with one.
* *
* @param string $s * @param string $s
* @return Returns array of tags found, or empty array. * @return array Returns an array of tags found, or empty array.
*/ */
function get_tags($s) { function get_tags($s) {
$ret = array(); $ret = array();
@ -826,6 +833,9 @@ function get_tags($s) {
// ignore anything in [color= ], because it may contain color codes which are mistaken for tags // ignore anything in [color= ], because it may contain color codes which are mistaken for tags
$s = preg_replace('/\[color=(.*?)\]/sm','',$s); $s = preg_replace('/\[color=(.*?)\]/sm','',$s);
// skip anchors in URL
$s = preg_replace('/\[url=(.*?)\]/sm','',$s);
// match any double quoted tags // match any double quoted tags
if(preg_match_all('/([@#\!]\&quot\;.*?\&quot\;)/',$s,$match)) { if(preg_match_all('/([@#\!]\&quot\;.*?\&quot\;)/',$s,$match)) {
@ -897,6 +907,7 @@ function tag_sort_length($a,$b) {
function total_sort($a,$b) { function total_sort($a,$b) {
if($a['total'] == $b['total']) if($a['total'] == $b['total'])
return 0; return 0;
return(($b['total'] > $a['total']) ? 1 : (-1)); return(($b['total'] > $a['total']) ? 1 : (-1));
} }
@ -1001,9 +1012,15 @@ function contact_block() {
'$micropro' => $micropro, '$micropro' => $micropro,
)); ));
$arr = array('contacts' => $r, 'output' => $o); $arr = ['contacts' => $r, 'output' => $o];
/**
* @hooks contact_block_end
* Called at the end of contact_block(), but can not manipulate the output.
* * \e array \b contacts - Result array from database
* * \e string \b output - the generated output
*/
call_hooks('contact_block_end', $arr); call_hooks('contact_block_end', $arr);
return $o; return $o;
} }
@ -1090,7 +1107,7 @@ function searchbox($s,$id='search-box',$url='/search',$save = false) {
* @return string * @return string
*/ */
function linkify($s, $me = false) { function linkify($s, $me = false) {
$s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\@\~\#\'\%\$\!\+\,\@]*)/u", (($me) ? ' <a href="$1" rel="me" >$1</a>' : ' <a href="$1" >$1</a>'), $s); $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\pL\:\/\-\?\&\;\.\=\_\@\~\#\'\%\$\!\+\,\@]*)/u", (($me) ? ' <a href="$1" rel="me nofollow" >$1</a>' : ' <a href="$1" >$1</a>'), $s);
$s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$s); $s = preg_replace("/\<(.*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$s);
return($s); return($s);
@ -1105,16 +1122,20 @@ function linkify($s, $me = false) {
* to a local redirector which uses https and which redirects to the selected content * to a local redirector which uses https and which redirects to the selected content
* *
* @param string $s * @param string $s
* @param int $uid
* @returns string * @returns string
*/ */
function sslify($s) { function sslify($s) {
// Local photo cache // Local photo cache
$str = array( $str = [
'body' => $s, 'body' => $s,
'uid' => local_channel() 'uid' => local_channel()
); ];
/**
* @hooks cache_body_hook
* * \e string \b body The content to parse and also the return value
* * \e int|bool \b uid
*/
call_hooks('cache_body_hook', $str); call_hooks('cache_body_hook', $str);
$s = $str['body']; $s = $str['body'];
@ -1219,7 +1240,11 @@ function get_mood_verbs() {
/** /**
* @brief Function to list all smilies, both internal and from addons. * @brief Function to list all smilies, both internal and from addons.
* *
* @return Returns array with keys 'texts' and 'icons' * @param boolean $default_only (optional) default false
* true will prevent that plugins can add smilies
* @return array Returns an associative array with:
* * \e array \b texts
* * \e array \b icons
*/ */
function list_smilies($default_only = false) { function list_smilies($default_only = false) {
@ -1297,6 +1322,11 @@ function list_smilies($default_only = false) {
if($default_only) if($default_only)
return $params; return $params;
/**
* @hooks smile
* * \e array \b texts - default values and also return value
* * \e array \b icons - default values and also return value
*/
call_hooks('smilie', $params); call_hooks('smilie', $params);
return $params; return $params;
@ -1622,6 +1652,10 @@ function generate_named_map($location) {
function prepare_body(&$item,$attach = false,$opts = false) { function prepare_body(&$item,$attach = false,$opts = false) {
/**
* @hooks prepare_body_init
* * \e array \b item
*/
call_hooks('prepare_body_init', $item); call_hooks('prepare_body_init', $item);
$s = ''; $s = '';
@ -1653,13 +1687,19 @@ function prepare_body(&$item,$attach = false,$opts = false) {
$event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false); $event = (($item['obj_type'] === ACTIVITY_OBJ_EVENT) ? format_event_obj($item['obj']) : false);
$prep_arr = array( $prep_arr = [
'item' => $item, 'item' => $item,
'html' => $event ? $event['content'] : $s, 'html' => $event ? $event['content'] : $s,
'event' => $event['header'], 'event' => $event['header'],
'photo' => $photo 'photo' => $photo
); ];
/**
* @hooks prepare_body
* * \e array \b item
* * \e string \b html - the parsed HTML to return
* * \e string \b event - the event header to return
* * \e string \b photo - the photo to return
*/
call_hooks('prepare_body', $prep_arr); call_hooks('prepare_body', $prep_arr);
$s = $prep_arr['html']; $s = $prep_arr['html'];
@ -1729,17 +1769,24 @@ function prepare_binary($item) {
/** /**
* @brief Given a text string, convert from bbcode to html and add smilie icons. * @brief Given a text string, convert from content_type to HTML.
*
* Take a text in plain text, html, markdown, bbcode, PDL or PHP and prepare
* it to return HTML.
*
* In bbcode this function will add smilie icons.
* *
* @param string $text * @param string $text
* @param string $content_type (optional) default text/bbcode * @param string $content_type (optional)
* @param boolean $cache (optional) default false * default 'text/bbcode', other values are 'text/plain', 'text/html',
* * 'text/markdown', 'application/x-pdl', 'application/x-php'
* @param boolean|array $opts (optional)
* default false, otherwise configuration array for bbcode()
* @return string * @return string
* The parsed $text as prepared HTML.
*/ */
function prepare_text($text, $content_type = 'text/bbcode', $opts = false) { function prepare_text($text, $content_type = 'text/bbcode', $opts = false) {
switch($content_type) { switch($content_type) {
case 'text/plain': case 'text/plain':
$s = escape_tags($text); $s = escape_tags($text);
@ -1780,7 +1827,7 @@ function prepare_text($text, $content_type = 'text/bbcode', $opts = false) {
require_once('include/bbcode.php'); require_once('include/bbcode.php');
if(stristr($text, '[nosmile]')) if(stristr($text, '[nosmile]'))
$s = bbcode($text, [ 'cache' => $cache ]); $s = bbcode($text, ((is_array($opts)) ? $opts : [] ));
else else
$s = smilies(bbcode($text, ((is_array($opts)) ? $opts : [] ))); $s = smilies(bbcode($text, ((is_array($opts)) ? $opts : [] )));
@ -2239,19 +2286,24 @@ function ids_to_querystr($arr,$idx = 'id',$quote = false) {
} }
/** /**
* @brief array_elm_to_str($arr,$elm,$delim = ',') extract unique individual elements from an array of arrays and return them as a string separated by a delimiter * @brief Extract unique individual elements from an array of arrays and return
* similar to ids_to_querystr, but allows a different delimiter instead of a db-quote option * them as a string separated by a delimiter.
* empty elements (evaluated after trim()) are ignored. *
* @param $arr array * Similar to ids_to_querystr, but allows a different delimiter instead of a
* @param $elm array key to extract from sub-array * db-quote option empty elements (evaluated after trim()) are ignored.
* @param $delim string default ',' *
* @param $each filter function to apply to each element before evaluation, default is 'trim'. * @see ids_to_querystr()
*
* @param array $arr
* @param string $elm key to extract from sub-array
* @param string $delim (optional) default ','
* @param string $each (optional) default is 'trim'
* Filter function to apply to each element before evaluation.
* @returns string * @returns string
*/ */
function array_elm_to_str($arr, $elm, $delim = ',', $each = 'trim') { function array_elm_to_str($arr, $elm, $delim = ',', $each = 'trim') {
$tmp = []; $tmp = [];
if($arr && is_array($arr)) { if($arr && is_array($arr)) {
foreach($arr as $x) { foreach($arr as $x) {
if(is_array($x) && array_key_exists($elm,$x)) { if(is_array($x) && array_key_exists($elm,$x)) {
@ -2262,6 +2314,7 @@ function array_elm_to_str($arr,$elm,$delim = ',',$each = 'trim') {
} }
} }
} }
return implode($delim, $tmp); return implode($delim, $tmp);
} }
@ -2486,9 +2539,9 @@ function design_tools() {
} }
/** /**
* @brief Creates website portation tools menu * @brief Creates website portation tools menu.
* *
* @return string * @return string Parsed HTML code from template 'website_portation_tools.tpl'
*/ */
function website_portation_tools() { function website_portation_tools() {
@ -2501,7 +2554,7 @@ function website_portation_tools() {
$sys = true; $sys = true;
} }
return replace_macros(get_markup_template('website_portation_tools.tpl'), array( return replace_macros(get_markup_template('website_portation_tools.tpl'), [
'$title' => t('Import'), '$title' => t('Import'),
'$import_label' => t('Import website...'), '$import_label' => t('Import website...'),
'$import_placeholder' => t('Select folder to import'), '$import_placeholder' => t('Select folder to import'),
@ -2518,7 +2571,7 @@ function website_portation_tools() {
'$cloud_export_desc' => t('/path/to/export/folder'), '$cloud_export_desc' => t('/path/to/export/folder'),
'$cloud_export_hint' => t('Enter a path to a cloud files destination.'), '$cloud_export_hint' => t('Enter a path to a cloud files destination.'),
'$cloud_export_select' => t('Specify folder'), '$cloud_export_select' => t('Specify folder'),
)); ]);
} }
/** /**
@ -2578,8 +2631,9 @@ function extra_query_args() {
* @param boolean $in_network default true * @param boolean $in_network default true
* @return boolean true if replaced, false if not replaced * @return boolean true if replaced, false if not replaced
*/ */
function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $in_network = true) { function handle_tag(&$body, &$str_tags, $profile_uid, $tag, $in_network = true) {
$channel = App::get_channel();
$replaced = false; $replaced = false;
$r = null; $r = null;
$match = array(); $match = array();
@ -2635,21 +2689,20 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
$str_tags .= $newtag; $str_tags .= $newtag;
} }
return [ return [ [
'replaced' => $replaced, 'replaced' => $replaced,
'termtype' => $termtype, 'termtype' => $termtype,
'term' => $basetag, 'term' => $basetag,
'url' => $url, 'url' => $url,
'contact' => [] 'contact' => [],
]; 'access_tag' => '',
]];
} }
// END hashtags // END hashtags
// BEGIN mentions // BEGIN mentions
if ( in_array($termtype, [ TERM_MENTION, TERM_FORUM ] )) { if ( in_array($termtype, [ TERM_MENTION, TERM_FORUM ] )) {
// The @! tag and !! tag will alter permissions // The @! tag and !! tag will alter permissions
@ -2660,7 +2713,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
$exclusive = (((strpos(substr($tag,1), '!') === 0) && $in_network) ? true : false); $exclusive = (((strpos(substr($tag,1), '!') === 0) && $in_network) ? true : false);
//is it already replaced? //is it already replaced?
if(strpos($tag,'[zrl=') || strpos($tag,'[url=')) if(strpos($tag,"[zrl=") || strpos($tag,"[url="))
return $replaced; return $replaced;
// get the channel name // get the channel name
@ -2678,7 +2731,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
$newname = substr($name,1); $newname = substr($name,1);
$newname = substr($newname,0,-1); $newname = substr($newname,0,-1);
$r = q("select * from xchan where xchan_addr = '%s' or xchan_url = '%s' limit 1", $r = q("select * from xchan where xchan_addr = '%s' or xchan_url = '%s'",
dbesc($newname), dbesc($newname),
dbesc($newname) dbesc($newname)
); );
@ -2701,7 +2754,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
// select someone from this user's contacts by name // select someone from this user's contacts by name
$r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash
WHERE xchan_name = '%s' AND abook_channel = %d LIMIT 1", WHERE xchan_name = '%s' AND abook_channel = %d ",
dbesc($newname), dbesc($newname),
intval($profile_uid) intval($profile_uid)
); );
@ -2710,7 +2763,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
if((! $r) && strpos($newname,'@')) { if((! $r) && strpos($newname,'@')) {
$r = q("SELECT * FROM xchan left join hubloc on xchan_hash = hubloc_hash $r = q("SELECT * FROM xchan left join hubloc on xchan_hash = hubloc_hash
WHERE hubloc_addr = '%s' LIMIT 1", WHERE hubloc_addr = '%s' ",
dbesc($newname) dbesc($newname)
); );
} }
@ -2719,7 +2772,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
if(! $r) { if(! $r) {
$r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash $r = q("SELECT * FROM abook left join xchan on abook_xchan = xchan_hash
WHERE xchan_addr like ('%s') AND abook_channel = %d LIMIT 1", WHERE xchan_addr like ('%s') AND abook_channel = %d ",
dbesc(((strpos($newname,'@')) ? $newname : $newname . '@%')), dbesc(((strpos($newname,'@')) ? $newname : $newname . '@%')),
intval($profile_uid) intval($profile_uid)
); );
@ -2727,45 +2780,23 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
} }
$fn_results = [];
$access_tag = EMPTY_STR;
// $r is set if we found something // $r is set if we found something
$channel = App::get_channel();
if($r) { if($r) {
$profile = $r[0]['xchan_url']; foreach($r as $xc) {
$newname = $r[0]['xchan_name']; $profile = $xc['xchan_url'];
$newname = $xc['xchan_name'];
// add the channel's xchan_hash to $access_tag if exclusive // add the channel's xchan_hash to $access_tag if exclusive
if($exclusive) { if($exclusive) {
$access_tag .= 'cid:' . $r[0]['xchan_hash']; $access_tag = 'cid:' . $xc['xchan_hash'];
}
}
else {
// check for a group/collection exclusion tag
// note that we aren't setting $replaced even though we're replacing text.
// This tag isn't going to get a term attached to it. It's only used for
// access control. The link points to out own channel just so it doesn't look
// weird - as all the other tags are linked to something.
if(local_channel() && local_channel() == $profile_uid) {
require_once('include/group.php');
$grp = group_byname($profile_uid,$name);
if($grp) {
$g = q("select hash from pgrp where id = %d and visible = 1 limit 1",
intval($grp)
);
if($g && $exclusive) {
$access_tag .= 'gid:' . $g[0]['hash'];
}
$channel = App::get_channel();
if($channel) {
$newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . z_root() . '/channel/' . $channel['channel_address'] . ']' . $newname . '[/zrl]';
$body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
}
}
}
} }
// if there is a url for this channel // if there is a url for this channel
@ -2792,32 +2823,102 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
$str_tags .= $newtag; $str_tags .= $newtag;
} }
} }
}
return [
$fn_results[] = [
'replaced' => $replaced, 'replaced' => $replaced,
'termtype' => $termtype, 'termtype' => $termtype,
'term' => $newname, 'term' => $newname,
'url' => $url, 'url' => $url,
'contact' => (($r) ? $r[0] : []) 'access_tag' => $access_tag,
'contact' => (($r) ? $xc : []),
]; ];
} }
function linkify_tags($a, &$body, $uid, $in_network = true) { }
else {
// check for a group/collection exclusion tag
// note that we aren't setting $replaced even though we're replacing text.
// This tag isn't going to get a term attached to it. It's only used for
// access control. The link points to out own channel just so it doesn't look
// weird - as all the other tags are linked to something.
if(local_channel() && local_channel() == $profile_uid) {
$grp = group_byname($profile_uid,$name);
if($grp) {
$g = q("select hash from pgrp where id = %d and visible = 1 limit 1",
intval($grp)
);
if($g && $exclusive) {
$access_tag .= 'gid:' . $g[0]['hash'];
}
$channel = App::get_channel();
if($channel) {
$newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . z_root() . '/channel/' . $channel['channel_address'] . ']' . $newname . '[/zrl]';
$body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
}
}
}
// if there is a url for this channel
if(isset($profile)) {
$replaced = true;
//create profile link
$profile = str_replace(',','%2c',$profile);
$url = $profile;
if($termtype === TERM_FORUM) {
$newtag = '!' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]';
$body = str_replace('!' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
}
else {
// ( $termtype === TERM_MENTION )
$newtag = '@' . (($exclusive) ? '!' : '') . '[zrl=' . $profile . ']' . $newname . '[/zrl]';
$body = str_replace('@' . (($exclusive) ? '!' : '') . $name, $newtag, $body);
}
// append tag to str_tags
if(! stristr($str_tags,$newtag)) {
if(strlen($str_tags))
$str_tags .= ',';
$str_tags .= $newtag;
}
}
$fn_results[] = [
'replaced' => $replaced,
'termtype' => $termtype,
'term' => $newname,
'url' => $url,
'access_tag' => $access_tag,
'contact' => [],
];
}
}
return $fn_results;
}
function linkify_tags(&$body, $uid, $in_network = true) {
$str_tags = EMPTY_STR; $str_tags = EMPTY_STR;
$tagged = [];
$results = []; $results = [];
$tags = get_tags($body); $tags = get_tags($body);
if(count($tags)) { if(count($tags)) {
foreach($tags as $tag) { foreach($tags as $tag) {
$access_tag = '';
$success = handle_tag($a, $body, $access_tag, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $in_network); $success = handle_tag($body, $str_tags, ($uid) ? $uid : App::$profile_uid , $tag, $in_network);
$results[] = array('success' => $success, 'access_tag' => $access_tag); foreach($success as $handled_tag) {
if($success['replaced']) $tagged[] = $tag; $results[] = [ 'success' => $handled_tag ];
}
} }
} }
@ -3093,7 +3194,13 @@ function pdl_selector($uid, $current='') {
intval($uid) intval($uid)
); );
$arr = array('channel_id' => $uid, 'current' => $current, 'entries' => $r); $arr = ['channel_id' => $uid, 'current' => $current, 'entries' => $r];
/**
* @hooks pdl_selector
* * \e int \b channel_id
* * \e string \b current
* * \e array \b entries - Result from database query
*/
call_hooks('pdl_selector', $arr); call_hooks('pdl_selector', $arr);
$entries = $arr['entries']; $entries = $arr['entries'];
@ -3168,7 +3275,6 @@ function text_highlight($s, $lang) {
'language' => $lang, 'language' => $lang,
'success' => false 'success' => false
]; ];
/** /**
* @hooks text_highlight * @hooks text_highlight
* * \e string \b text * * \e string \b text
@ -3369,13 +3475,17 @@ function punify($s) {
} }
// Be aware that unpunify will only convert domain names and not pathnames /**
* Be aware that unpunify() will only convert domain names and not pathnames.
*
* @param string $s
* @return string
*/
function unpunify($s) { function unpunify($s) {
require_once('vendor/simplepie/simplepie/idn/idna_convert.class.php'); require_once('vendor/simplepie/simplepie/idn/idna_convert.class.php');
$x = new idna_convert(['encoding' => 'utf8']); $x = new idna_convert(['encoding' => 'utf8']);
return $x->decode($s);
return $x->decode($s);
} }
@ -3519,4 +3629,3 @@ function new_uuid() {
return $hash; return $hash;
} }

View File

@ -174,7 +174,7 @@ function zot_build_packet($channel, $type = 'notify', $recipients = null, $remot
* packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check' * packet type: one of 'ping', 'pickup', 'purge', 'refresh', 'keychange', 'force_refresh', 'notify', 'auth_check'
* @param array $recipients * @param array $recipients
* envelope information, array ( 'guid' => string, 'guid_sig' => string ); empty for public posts * envelope information, array ( 'guid' => string, 'guid_sig' => string ); empty for public posts
* @param string msg * @param string $msg
* optional message * optional message
* @param string $remote_key * @param string $remote_key
* optional public site key of target hub used to encrypt entire packet * optional public site key of target hub used to encrypt entire packet
@ -5057,7 +5057,7 @@ function zot_reply_auth_check($data,$encrypted_packet) {
} }
// There should be exactly one recipient, the original auth requestor // There should be exactly one recipient, the original auth requestor
/// @FIXME $recipients is undefined here.
$ret['message'] .= 'recipients ' . print_r($recipients,true) . EOL; $ret['message'] .= 'recipients ' . print_r($recipients,true) . EOL;
if ($data['recipients']) { if ($data['recipients']) {

View File

@ -79,7 +79,7 @@ $HTTP["url"] =~ "\.(out|log|htaccess)$" {
url.access-deny = ("") url.access-deny = ("")
} }
$HTTP["url"] =~ "(^|/)\.git|(^|/)store" { $HTTP["url"] =~ "(^|/)\.git|(^|/)store|(^|/)util" {
url.access-deny = ("") url.access-deny = ("")
} }

View File

@ -141,5 +141,10 @@ server {
deny all; deny all;
} }
#deny access to util
location ~ /util {
deny all;
}
} }

View File

@ -0,0 +1,147 @@
<?php
namespace Zotlabs\Tests\Unit\Photo;
use Zotlabs\Photo\PhotoGd;
use phpmock\phpunit\PHPMock;
use Zotlabs\Tests\Unit\UnitTestCase;
/**
* @brief PhotoGd test case.
*
* These tests are not really useful yet, just some obvious behaviour.
*
* @todo Compare the actual results.
* @todo Test different image types.
*/
class PhotoGdTest extends UnitTestCase {
use PHPMock;
/**
* @var PhotoGd
*/
private $photoGd;
/**
* Prepares the environment before running a test.
*/
protected function setUp() {
parent::setUp();
$data = file_get_contents('images/hz-16.png');
$this->photoGd = new PhotoGd($data, 'image/png');
}
/**
* Cleans up the environment after running a test.
*/
protected function tearDown() {
$this->photoGd = null;
parent::tearDown();
}
/**
* Tests PhotoGd->supportedTypes()
*
* Without mocking gd this check is environment dependent.
*
public function testSupportedTypes() {
$sft = $this->photoGd->supportedTypes();
$this->assertArrayHasKey('image/jpeg', $sft);
$this->assertArrayHasKey('image/gif', $sft);
$this->assertArrayHasKey('image/png', $sft);
$this->assertArrayNotHasKey('image/foo', $sft);
}
*/
/**
* Tests PhotoGd->clearexif()
*/
public function testClearexifIsNotImplementedInGdAndDoesNotAlterImageOrReturnSomething() {
$data_before = $this->photoGd->getImage();
$this->assertNull($this->photoGd->clearexif());
$this->assertSame($data_before, $this->photoGd->getImage());
}
/**
* Tests PhotoGd->getImage()
*/
public function testGetimageReturnsAResource() {
$res = $this->photoGd->getImage();
$this->assertIsResource($res);
$this->assertEquals('gd', get_resource_type($res));
}
public function testGetimageReturnsFalseOnFailure() {
$this->photoGd = new PhotoGd('');
$this->assertFalse($this->photoGd->getImage());
}
/**
* Tests PhotoGd->doScaleImage()
*/
public function testDoscaleImageSetsCorrectDimensions() {
$this->photoGd->doScaleImage(5, 8);
$this->assertSame(5, $this->photoGd->getWidth());
$this->assertSame(8, $this->photoGd->getHeight());
}
/**
* Tests PhotoGd->rotate()
*/
public function testRotate360DegreesCreatesANewImage() {
$data = $this->photoGd->getImage();
$this->photoGd->rotate(360);
$this->assertNotEquals($data, $this->photoGd->getImage());
}
/**
* Tests PhotoGd->flip()
*
public function testFlip() {
// TODO Auto-generated PhotoGdTest->testFlip()
$this->markTestIncomplete("flip test not implemented");
$this->photoGd->flip();
}
*/
/**
* Tests PhotoGd->cropImageRect()
*/
public function testCropimagerectSetsCorrectDimensions() {
$this->photoGd->cropImageRect(10, 12, 1, 2, 11, 11);
$this->assertSame(10, $this->photoGd->getWidth());
$this->assertSame(12, $this->photoGd->getHeight());
}
/**
* Tests PhotoGd->imageString()
*/
public function testImagestringReturnsABinaryString() {
// Create a stub for global function get_config()
// get_config('system', 'png_quality')
// get_config('system', 'jpeg_quality');
$gc = $this->getFunctionMock('Zotlabs\Photo', 'get_config');
$gc->expects($this->once())->willReturnCallback(
function() {
switch($this->photoGd->getType()){
case 'image/png':
return 7;
case 'image/jpeg':
default:
return 70;
}
}
);
$this->assertIsString($this->photoGd->imageString());
}
}

View File

@ -106,10 +106,9 @@ class GetTagsTest extends PHPUnit_Framework_TestCase {
$tags=get_tags($text); $tags=get_tags($text);
$inform='';
$str_tags=''; $str_tags='';
foreach($tags as $tag) { foreach($tags as $tag) {
handle_tag($this->a, $text, $inform, $str_tags, 11, $tag); handle_tag($text, $str_tags, 11, $tag);
} }
//correct tags found? //correct tags found?
@ -117,7 +116,6 @@ class GetTagsTest extends PHPUnit_Framework_TestCase {
$this->assertTrue(in_array("@Mike", $tags)); $this->assertTrue(in_array("@Mike", $tags));
//correct output from handle_tag? //correct output from handle_tag?
$this->assertEquals("cid:15", $inform);
$this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags);
$this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url]", $text); $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url]", $text);
} }
@ -135,9 +133,8 @@ class GetTagsTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(1, count($tags)); $this->assertEquals(1, count($tags));
$this->assertTrue(in_array("@Mike.because", $tags)); $this->assertTrue(in_array("@Mike.because", $tags));
$inform='';
$str_tags=''; $str_tags='';
handle_tag($this->a, $text, $inform, $str_tags, 11, $tags[0]); handle_tag($text, $str_tags, 11, $tags[0]);
// (mike) - This is a tricky case. // (mike) - This is a tricky case.
// we support mentions as in @mike@example.com - which contains a period. // we support mentions as in @mike@example.com - which contains a period.
@ -149,7 +146,6 @@ class GetTagsTest extends PHPUnit_Framework_TestCase {
// $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); // $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags);
// $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url].because", $text); // $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url].because", $text);
$this->assertEquals("", $inform);
$this->assertEquals("", $str_tags); $this->assertEquals("", $str_tags);
} }
@ -195,13 +191,11 @@ class GetTagsTest extends PHPUnit_Framework_TestCase {
$this->assertTrue(in_array("@Mike This", $tags)); $this->assertTrue(in_array("@Mike This", $tags));
$this->assertTrue(in_array("#test_case", $tags)); $this->assertTrue(in_array("#test_case", $tags));
$inform='';
$str_tags=''; $str_tags='';
foreach($tags as $tag) { foreach($tags as $tag) {
handle_tag($this->a, $text, $inform, $str_tags, 11, $tag); handle_tag($text, $str_tags, 11, $tag);
} }
$this->assertEquals("cid:15", $inform);
$this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url],#[url=baseurl/search?tag=test%20case]test case[/url]", $str_tags); $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url],#[url=baseurl/search?tag=test%20case]test case[/url]", $str_tags);
$this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url] This is a #[url=baseurl/search?tag=test%20case]test case[/url]", $text); $this->assertEquals("hi @[url=http://justatest.de]Mike Lastname[/url] This is a #[url=baseurl/search?tag=test%20case]test case[/url]", $text);
@ -255,16 +249,13 @@ class GetTagsTest extends PHPUnit_Framework_TestCase {
//happens right now, but it shouldn't be necessary //happens right now, but it shouldn't be necessary
$this->assertTrue(in_array("@mike+15 id", $tags)); $this->assertTrue(in_array("@mike+15 id", $tags));
$inform='';
$str_tags=''; $str_tags='';
foreach($tags as $tag) { foreach($tags as $tag) {
handle_tag($this->a, $text, $inform, $str_tags, 11, $tag); handle_tag($text, $str_tags, 11, $tag);
} }
$this->assertEquals("Test with @[url=http://justatest.de]Mike Lastname[/url] id tag", $text); $this->assertEquals("Test with @[url=http://justatest.de]Mike Lastname[/url] id tag", $text);
$this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags); $this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags);
// this test may produce two cid:15 entries - which is OK because duplicates are pruned before delivery
$this->assertContains("cid:15",$inform);
} }
/** /**

View File

@ -0,0 +1,39 @@
<?php
namespace Zotlabs\Tests\Unit\includes;
//use Zotlabs\Photo\PhotoGd;
use Zotlabs\Tests\Unit\UnitTestCase;
//use phpmock\phpunit\PHPMock;
/**
* @brief Unit Test cases for include/photo/photo_driver.php file.
*/
class PhotodriverTest extends UnitTestCase {
//use PHPMock;
public function testPhotofactoryReturnsNullForUnsupportedType() {
// php-mock can not mock global functions which is called by a global function.
// If the calling function is in a namespace it would work.
//$logger = $this->getFunctionMock(__NAMESPACE__, 'logger');
//$logger->expects($this->once());
//$ph = \photo_factory('', 'image/bmp');
//$this->assertNull($ph);
$this->markTestIncomplete('Need to mock logger(), otherwise not unit testable.');
}
public function testPhotofactoryReturnsPhotogdIfConfigIgnore_imagickIsSet() {
// php-mock can not mock global functions which is called by a global function.
// If the calling function is in a namespace it would work.
//$gc = $this->getFunctionMock(__NAMESPACE__, 'get_config');
// simulate get_config('system', 'ignore_imagick') configured
//$gc->expects($this->once())->willReturn(1)
//$ph = \photo_factory(file_get_contents('images/hz-16.png'), 'image/png');
//$this->assertInstanceOf(PhotoGd::class, $ph);
$this->markTestIncomplete('Need to mock get_config(), otherwise not unit testable.');
}
}

View File

@ -1,7 +1,8 @@
INPUT = README.md index.php boot.php include/ install/ util/ view/ Zotlabs/ INPUT = README.md index.php boot.php include/ install/ util/ view/ Zotlabs/
RECURSIVE = YES RECURSIVE = YES
PROJECT_NAME = "The Hubzilla" PROJECT_NAME = "The Hubzilla"
PROJECT_LOGO = images/rm-64.png PROJECT_LOGO = images/hz-64.png
IMAGE_PATH = images/
EXCLUDE = .htconfig.php library/ doc/ store/ vendor/ .git/ util/zotsh/easywebdav/ util/generate-hooks-index/ EXCLUDE = .htconfig.php library/ doc/ store/ vendor/ .git/ util/zotsh/easywebdav/ util/generate-hooks-index/
EXCLUDE_PATTERNS = *smarty3* *strings.php *.out *test* EXCLUDE_PATTERNS = *smarty3* *strings.php *.out *test*
OUTPUT_DIRECTORY = doc OUTPUT_DIRECTORY = doc
@ -33,5 +34,6 @@ INTERACTIVE_SVG = YES
CLASS_GRAPH = YES CLASS_GRAPH = YES
COLLABORATION_GRAPH = NO COLLABORATION_GRAPH = NO
# fix @var (https://bugzilla.gnome.org/show_bug.cgi?id=626105) # fix @var (https://bugzilla.gnome.org/show_bug.cgi?id=626105)
# Should be obsolete with doxygen >= 1.8.15
#INPUT_FILTER = "sed -e 's/@var\s/@see /'" #INPUT_FILTER = "sed -e 's/@var\s/@see /'"
INPUT_FILTER = "php util/Doxygen_phpvarfilter.php" INPUT_FILTER = "php util/Doxygen_phpvarfilter.php"

60
util/admins Executable file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env php
<?php
if(!file_exists('include/cli_startup.php')) {
echo 'Run admins from the top level Hubzilla web directory, as util/admins <args>' . PHP_EOL;
exit(1);
}
require_once('include/cli_startup.php');
cli_startup();
$helpArgs = getopt('h', array('help'));
if (count($helpArgs) === 1) {
echo <<<'EndOfOutput'
adds, removes, or lists admins
Usage: util/admins
util/admins list
util/admins add <account_id>
util/admins remove <account_id>
EndOfOutput;
return;
}
if($argc == 1) {
$r = q("select account_id, account_roles, account_email from account");
if($r) {
foreach($r as $rr) {
echo sprintf('%4u %s %s', $rr['account_id'], $rr['account_email'],(($rr['account_roles'] & 4096) ? '*' : '')) . PHP_EOL;
}
}
}
if($argc > 1 && $argv[1] === 'list') {
$r = q("select account_id, account_roles, account_email from account where (account_roles & 4096) > 0");
if($r) {
foreach($r as $rr) {
echo sprintf('%4u %s %s', $rr['account_id'], $rr['account_email'],(($rr['account_roles'] & 4096) ? '*' : '')) . PHP_EOL;
}
}
}
if($argc > 2 && $argv[1] === 'add' && intval($argv[2])) {
$r = q("update account set account_roles = (account_roles | 4096) where account_id = %d",
intval($argv[2])
);
}
if($argc > 2 && $argv[1] === 'remove' && intval($argv[2])) {
$r = q("update account set account_roles = (account_roles - 4096) where account_id = %d and (account_roles & 4096) > 0",
intval($argv[2])
);
}

File diff suppressed because it is too large Load Diff

View File

@ -811,6 +811,7 @@ return array(
'Zotlabs\\Access\\PermissionRoles' => $baseDir . '/Zotlabs/Access/PermissionRoles.php', 'Zotlabs\\Access\\PermissionRoles' => $baseDir . '/Zotlabs/Access/PermissionRoles.php',
'Zotlabs\\Access\\Permissions' => $baseDir . '/Zotlabs/Access/Permissions.php', 'Zotlabs\\Access\\Permissions' => $baseDir . '/Zotlabs/Access/Permissions.php',
'Zotlabs\\Daemon\\Addon' => $baseDir . '/Zotlabs/Daemon/Addon.php', 'Zotlabs\\Daemon\\Addon' => $baseDir . '/Zotlabs/Daemon/Addon.php',
'Zotlabs\\Daemon\\Cache_embeds' => $baseDir . '/Zotlabs/Daemon/Cache_embeds.php',
'Zotlabs\\Daemon\\Checksites' => $baseDir . '/Zotlabs/Daemon/Checksites.php', 'Zotlabs\\Daemon\\Checksites' => $baseDir . '/Zotlabs/Daemon/Checksites.php',
'Zotlabs\\Daemon\\Cli_suggest' => $baseDir . '/Zotlabs/Daemon/Cli_suggest.php', 'Zotlabs\\Daemon\\Cli_suggest' => $baseDir . '/Zotlabs/Daemon/Cli_suggest.php',
'Zotlabs\\Daemon\\Cron' => $baseDir . '/Zotlabs/Daemon/Cron.php', 'Zotlabs\\Daemon\\Cron' => $baseDir . '/Zotlabs/Daemon/Cron.php',
@ -876,6 +877,7 @@ return array(
'Zotlabs\\Lib\\System' => $baseDir . '/Zotlabs/Lib/System.php', 'Zotlabs\\Lib\\System' => $baseDir . '/Zotlabs/Lib/System.php',
'Zotlabs\\Lib\\Techlevels' => $baseDir . '/Zotlabs/Lib/Techlevels.php', 'Zotlabs\\Lib\\Techlevels' => $baseDir . '/Zotlabs/Lib/Techlevels.php',
'Zotlabs\\Lib\\ThreadItem' => $baseDir . '/Zotlabs/Lib/ThreadItem.php', 'Zotlabs\\Lib\\ThreadItem' => $baseDir . '/Zotlabs/Lib/ThreadItem.php',
'Zotlabs\\Lib\\ThreadListener' => $baseDir . '/Zotlabs/Lib/ThreadListener.php',
'Zotlabs\\Lib\\ThreadStream' => $baseDir . '/Zotlabs/Lib/ThreadStream.php', 'Zotlabs\\Lib\\ThreadStream' => $baseDir . '/Zotlabs/Lib/ThreadStream.php',
'Zotlabs\\Lib\\Verify' => $baseDir . '/Zotlabs/Lib/Verify.php', 'Zotlabs\\Lib\\Verify' => $baseDir . '/Zotlabs/Lib/Verify.php',
'Zotlabs\\Lib\\Webfinger' => $baseDir . '/Zotlabs/Lib/Webfinger.php', 'Zotlabs\\Lib\\Webfinger' => $baseDir . '/Zotlabs/Lib/Webfinger.php',
@ -896,6 +898,7 @@ return array(
'Zotlabs\\Module\\Admin\\Security' => $baseDir . '/Zotlabs/Module/Admin/Security.php', 'Zotlabs\\Module\\Admin\\Security' => $baseDir . '/Zotlabs/Module/Admin/Security.php',
'Zotlabs\\Module\\Admin\\Site' => $baseDir . '/Zotlabs/Module/Admin/Site.php', 'Zotlabs\\Module\\Admin\\Site' => $baseDir . '/Zotlabs/Module/Admin/Site.php',
'Zotlabs\\Module\\Admin\\Themes' => $baseDir . '/Zotlabs/Module/Admin/Themes.php', 'Zotlabs\\Module\\Admin\\Themes' => $baseDir . '/Zotlabs/Module/Admin/Themes.php',
'Zotlabs\\Module\\Affinity' => $baseDir . '/Zotlabs/Module/Affinity.php',
'Zotlabs\\Module\\Api' => $baseDir . '/Zotlabs/Module/Api.php', 'Zotlabs\\Module\\Api' => $baseDir . '/Zotlabs/Module/Api.php',
'Zotlabs\\Module\\Appman' => $baseDir . '/Zotlabs/Module/Appman.php', 'Zotlabs\\Module\\Appman' => $baseDir . '/Zotlabs/Module/Appman.php',
'Zotlabs\\Module\\Apporder' => $baseDir . '/Zotlabs/Module/Apporder.php', 'Zotlabs\\Module\\Apporder' => $baseDir . '/Zotlabs/Module/Apporder.php',
@ -938,6 +941,7 @@ return array(
'Zotlabs\\Module\\Editwebpage' => $baseDir . '/Zotlabs/Module/Editwebpage.php', 'Zotlabs\\Module\\Editwebpage' => $baseDir . '/Zotlabs/Module/Editwebpage.php',
'Zotlabs\\Module\\Email_resend' => $baseDir . '/Zotlabs/Module/Email_resend.php', 'Zotlabs\\Module\\Email_resend' => $baseDir . '/Zotlabs/Module/Email_resend.php',
'Zotlabs\\Module\\Email_validation' => $baseDir . '/Zotlabs/Module/Email_validation.php', 'Zotlabs\\Module\\Email_validation' => $baseDir . '/Zotlabs/Module/Email_validation.php',
'Zotlabs\\Module\\Embed' => $baseDir . '/Zotlabs/Module/Embed.php',
'Zotlabs\\Module\\Embedphotos' => $baseDir . '/Zotlabs/Module/Embedphotos.php', 'Zotlabs\\Module\\Embedphotos' => $baseDir . '/Zotlabs/Module/Embedphotos.php',
'Zotlabs\\Module\\Events' => $baseDir . '/Zotlabs/Module/Events.php', 'Zotlabs\\Module\\Events' => $baseDir . '/Zotlabs/Module/Events.php',
'Zotlabs\\Module\\Fbrowser' => $baseDir . '/Zotlabs/Module/Fbrowser.php', 'Zotlabs\\Module\\Fbrowser' => $baseDir . '/Zotlabs/Module/Fbrowser.php',
@ -1092,6 +1096,9 @@ return array(
'Zotlabs\\Module\\Zot_probe' => $baseDir . '/Zotlabs/Module/Zot_probe.php', 'Zotlabs\\Module\\Zot_probe' => $baseDir . '/Zotlabs/Module/Zot_probe.php',
'Zotlabs\\Module\\Zotfeed' => $baseDir . '/Zotlabs/Module/Zotfeed.php', 'Zotlabs\\Module\\Zotfeed' => $baseDir . '/Zotlabs/Module/Zotfeed.php',
'Zotlabs\\Module\\Zping' => $baseDir . '/Zotlabs/Module/Zping.php', 'Zotlabs\\Module\\Zping' => $baseDir . '/Zotlabs/Module/Zping.php',
'Zotlabs\\Photo\\PhotoDriver' => $baseDir . '/Zotlabs/Photo/PhotoDriver.php',
'Zotlabs\\Photo\\PhotoGd' => $baseDir . '/Zotlabs/Photo/PhotoGd.php',
'Zotlabs\\Photo\\PhotoImagick' => $baseDir . '/Zotlabs/Photo/PhotoImagick.php',
'Zotlabs\\Render\\Comanche' => $baseDir . '/Zotlabs/Render/Comanche.php', 'Zotlabs\\Render\\Comanche' => $baseDir . '/Zotlabs/Render/Comanche.php',
'Zotlabs\\Render\\SimpleTemplate' => $baseDir . '/Zotlabs/Render/SimpleTemplate.php', 'Zotlabs\\Render\\SimpleTemplate' => $baseDir . '/Zotlabs/Render/SimpleTemplate.php',
'Zotlabs\\Render\\SmartyInterface' => $baseDir . '/Zotlabs/Render/SmartyInterface.php', 'Zotlabs\\Render\\SmartyInterface' => $baseDir . '/Zotlabs/Render/SmartyInterface.php',

View File

@ -979,6 +979,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Access\\PermissionRoles' => __DIR__ . '/../..' . '/Zotlabs/Access/PermissionRoles.php', 'Zotlabs\\Access\\PermissionRoles' => __DIR__ . '/../..' . '/Zotlabs/Access/PermissionRoles.php',
'Zotlabs\\Access\\Permissions' => __DIR__ . '/../..' . '/Zotlabs/Access/Permissions.php', 'Zotlabs\\Access\\Permissions' => __DIR__ . '/../..' . '/Zotlabs/Access/Permissions.php',
'Zotlabs\\Daemon\\Addon' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Addon.php', 'Zotlabs\\Daemon\\Addon' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Addon.php',
'Zotlabs\\Daemon\\Cache_embeds' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cache_embeds.php',
'Zotlabs\\Daemon\\Checksites' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Checksites.php', 'Zotlabs\\Daemon\\Checksites' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Checksites.php',
'Zotlabs\\Daemon\\Cli_suggest' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cli_suggest.php', 'Zotlabs\\Daemon\\Cli_suggest' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cli_suggest.php',
'Zotlabs\\Daemon\\Cron' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cron.php', 'Zotlabs\\Daemon\\Cron' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cron.php',
@ -1044,6 +1045,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Lib\\System' => __DIR__ . '/../..' . '/Zotlabs/Lib/System.php', 'Zotlabs\\Lib\\System' => __DIR__ . '/../..' . '/Zotlabs/Lib/System.php',
'Zotlabs\\Lib\\Techlevels' => __DIR__ . '/../..' . '/Zotlabs/Lib/Techlevels.php', 'Zotlabs\\Lib\\Techlevels' => __DIR__ . '/../..' . '/Zotlabs/Lib/Techlevels.php',
'Zotlabs\\Lib\\ThreadItem' => __DIR__ . '/../..' . '/Zotlabs/Lib/ThreadItem.php', 'Zotlabs\\Lib\\ThreadItem' => __DIR__ . '/../..' . '/Zotlabs/Lib/ThreadItem.php',
'Zotlabs\\Lib\\ThreadListener' => __DIR__ . '/../..' . '/Zotlabs/Lib/ThreadListener.php',
'Zotlabs\\Lib\\ThreadStream' => __DIR__ . '/../..' . '/Zotlabs/Lib/ThreadStream.php', 'Zotlabs\\Lib\\ThreadStream' => __DIR__ . '/../..' . '/Zotlabs/Lib/ThreadStream.php',
'Zotlabs\\Lib\\Verify' => __DIR__ . '/../..' . '/Zotlabs/Lib/Verify.php', 'Zotlabs\\Lib\\Verify' => __DIR__ . '/../..' . '/Zotlabs/Lib/Verify.php',
'Zotlabs\\Lib\\Webfinger' => __DIR__ . '/../..' . '/Zotlabs/Lib/Webfinger.php', 'Zotlabs\\Lib\\Webfinger' => __DIR__ . '/../..' . '/Zotlabs/Lib/Webfinger.php',
@ -1064,6 +1066,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Module\\Admin\\Security' => __DIR__ . '/../..' . '/Zotlabs/Module/Admin/Security.php', 'Zotlabs\\Module\\Admin\\Security' => __DIR__ . '/../..' . '/Zotlabs/Module/Admin/Security.php',
'Zotlabs\\Module\\Admin\\Site' => __DIR__ . '/../..' . '/Zotlabs/Module/Admin/Site.php', 'Zotlabs\\Module\\Admin\\Site' => __DIR__ . '/../..' . '/Zotlabs/Module/Admin/Site.php',
'Zotlabs\\Module\\Admin\\Themes' => __DIR__ . '/../..' . '/Zotlabs/Module/Admin/Themes.php', 'Zotlabs\\Module\\Admin\\Themes' => __DIR__ . '/../..' . '/Zotlabs/Module/Admin/Themes.php',
'Zotlabs\\Module\\Affinity' => __DIR__ . '/../..' . '/Zotlabs/Module/Affinity.php',
'Zotlabs\\Module\\Api' => __DIR__ . '/../..' . '/Zotlabs/Module/Api.php', 'Zotlabs\\Module\\Api' => __DIR__ . '/../..' . '/Zotlabs/Module/Api.php',
'Zotlabs\\Module\\Appman' => __DIR__ . '/../..' . '/Zotlabs/Module/Appman.php', 'Zotlabs\\Module\\Appman' => __DIR__ . '/../..' . '/Zotlabs/Module/Appman.php',
'Zotlabs\\Module\\Apporder' => __DIR__ . '/../..' . '/Zotlabs/Module/Apporder.php', 'Zotlabs\\Module\\Apporder' => __DIR__ . '/../..' . '/Zotlabs/Module/Apporder.php',
@ -1106,6 +1109,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Module\\Editwebpage' => __DIR__ . '/../..' . '/Zotlabs/Module/Editwebpage.php', 'Zotlabs\\Module\\Editwebpage' => __DIR__ . '/../..' . '/Zotlabs/Module/Editwebpage.php',
'Zotlabs\\Module\\Email_resend' => __DIR__ . '/../..' . '/Zotlabs/Module/Email_resend.php', 'Zotlabs\\Module\\Email_resend' => __DIR__ . '/../..' . '/Zotlabs/Module/Email_resend.php',
'Zotlabs\\Module\\Email_validation' => __DIR__ . '/../..' . '/Zotlabs/Module/Email_validation.php', 'Zotlabs\\Module\\Email_validation' => __DIR__ . '/../..' . '/Zotlabs/Module/Email_validation.php',
'Zotlabs\\Module\\Embed' => __DIR__ . '/../..' . '/Zotlabs/Module/Embed.php',
'Zotlabs\\Module\\Embedphotos' => __DIR__ . '/../..' . '/Zotlabs/Module/Embedphotos.php', 'Zotlabs\\Module\\Embedphotos' => __DIR__ . '/../..' . '/Zotlabs/Module/Embedphotos.php',
'Zotlabs\\Module\\Events' => __DIR__ . '/../..' . '/Zotlabs/Module/Events.php', 'Zotlabs\\Module\\Events' => __DIR__ . '/../..' . '/Zotlabs/Module/Events.php',
'Zotlabs\\Module\\Fbrowser' => __DIR__ . '/../..' . '/Zotlabs/Module/Fbrowser.php', 'Zotlabs\\Module\\Fbrowser' => __DIR__ . '/../..' . '/Zotlabs/Module/Fbrowser.php',
@ -1260,6 +1264,9 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Module\\Zot_probe' => __DIR__ . '/../..' . '/Zotlabs/Module/Zot_probe.php', 'Zotlabs\\Module\\Zot_probe' => __DIR__ . '/../..' . '/Zotlabs/Module/Zot_probe.php',
'Zotlabs\\Module\\Zotfeed' => __DIR__ . '/../..' . '/Zotlabs/Module/Zotfeed.php', 'Zotlabs\\Module\\Zotfeed' => __DIR__ . '/../..' . '/Zotlabs/Module/Zotfeed.php',
'Zotlabs\\Module\\Zping' => __DIR__ . '/../..' . '/Zotlabs/Module/Zping.php', 'Zotlabs\\Module\\Zping' => __DIR__ . '/../..' . '/Zotlabs/Module/Zping.php',
'Zotlabs\\Photo\\PhotoDriver' => __DIR__ . '/../..' . '/Zotlabs/Photo/PhotoDriver.php',
'Zotlabs\\Photo\\PhotoGd' => __DIR__ . '/../..' . '/Zotlabs/Photo/PhotoGd.php',
'Zotlabs\\Photo\\PhotoImagick' => __DIR__ . '/../..' . '/Zotlabs/Photo/PhotoImagick.php',
'Zotlabs\\Render\\Comanche' => __DIR__ . '/../..' . '/Zotlabs/Render/Comanche.php', 'Zotlabs\\Render\\Comanche' => __DIR__ . '/../..' . '/Zotlabs/Render/Comanche.php',
'Zotlabs\\Render\\SimpleTemplate' => __DIR__ . '/../..' . '/Zotlabs/Render/SimpleTemplate.php', 'Zotlabs\\Render\\SimpleTemplate' => __DIR__ . '/../..' . '/Zotlabs/Render/SimpleTemplate.php',
'Zotlabs\\Render\\SmartyInterface' => __DIR__ . '/../..' . '/Zotlabs/Render/SmartyInterface.php', 'Zotlabs\\Render\\SmartyInterface' => __DIR__ . '/../..' . '/Zotlabs/Render/SmartyInterface.php',

View File

@ -400,12 +400,12 @@ function viewsrc(id) {
function showHideComments(id) { function showHideComments(id) {
if( $('#collapsed-comments-' + id).is(':visible')) { if( $('#collapsed-comments-' + id).is(':visible')) {
$('#collapsed-comments-' + id + ' .autotime').timeago('dispose'); $('#collapsed-comments-' + id + ' .autotime').timeago('dispose');
$('#collapsed-comments-' + id).slideUp(); $('#collapsed-comments-' + id).hide();
$('#hide-comments-' + id).html(aStr.showmore); $('#hide-comments-' + id).html(aStr.showmore);
$('#hide-comments-total-' + id).show(); $('#hide-comments-total-' + id).show();
} else { } else {
$('#collapsed-comments-' + id + ' .autotime').timeago(); $('#collapsed-comments-' + id + ' .autotime').timeago();
$('#collapsed-comments-' + id).slideDown(); $('#collapsed-comments-' + id).show();
$('#hide-comments-' + id).html(aStr.showfewer); $('#hide-comments-' + id).html(aStr.showfewer);
$('#hide-comments-total-' + id).hide(); $('#hide-comments-total-' + id).hide();
} }
@ -734,6 +734,7 @@ function updateConvItems(mode,data) {
title.replace(/\s+$/, ''); title.replace(/\s+$/, '');
if (title) { if (title) {
savedTitle = title + " " + savedTitle; savedTitle = title + " " + savedTitle;
document.title = title;
} }
} }
} }
@ -769,7 +770,7 @@ function updateConvItems(mode,data) {
mediaPlaying = false; mediaPlaying = false;
}); });
var bimgs = ((preloadImages) ? false : $(".wall-item-body img").not(function() { return this.complete; })); var bimgs = ((preloadImages) ? false : $(".wall-item-body img, .wall-photo-item img").not(function() { return this.complete; }));
var bimgcount = bimgs.length; var bimgcount = bimgs.length;
if (bimgcount) { if (bimgcount) {
@ -845,10 +846,10 @@ function collapseHeight() {
}); });
var collapsedContentHeight = Math.ceil($("#region_2").height()); var collapsedContentHeight = Math.ceil($("#region_2").height());
contentHeightDiff = origContentHeight - collapsedContentHeight; contentHeightDiff = liking ? 0 : origContentHeight - collapsedContentHeight;
console.log('collapseHeight() - contentHeightDiff: ' + contentHeightDiff + 'px'); console.log('collapseHeight() - contentHeightDiff: ' + contentHeightDiff + 'px');
if(i){ if(i && !liking){
var sval = position - cDiff + ($(".divgrow-showmore").outerHeight() * i); var sval = position - cDiff + ($(".divgrow-showmore").outerHeight() * i);
console.log('collapsed above viewport count: ' + i); console.log('collapsed above viewport count: ' + i);
$(window).scrollTop(sval); $(window).scrollTop(sval);
@ -999,7 +1000,7 @@ function liveUpdate(notify_id) {
$("#profile-jot-text-loading").hide(); $("#profile-jot-text-loading").hide();
// adjust scroll position if new content was added above viewport // adjust scroll position if new content was added above viewport
if(update_mode === 'update') { if(update_mode === 'update' && !justifiedGalleryActive) {
$(window).scrollTop($(window).scrollTop() + $("#region_2").height() - orgHeight + contentHeightDiff); $(window).scrollTop($(window).scrollTop() + $("#region_2").height() - orgHeight + contentHeightDiff);
} }

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@ App::$strings["Set the background color of items"] = "Цвет фона элем
App::$strings["Set the background color of comments"] = "Цвет фона комментариев"; App::$strings["Set the background color of comments"] = "Цвет фона комментариев";
App::$strings["Set font-size for the entire application"] = "Установить системный размер шрифта"; App::$strings["Set font-size for the entire application"] = "Установить системный размер шрифта";
App::$strings["Examples: 1rem, 100%, 16px"] = "Например: 1rem, 100%, 16px"; App::$strings["Examples: 1rem, 100%, 16px"] = "Например: 1rem, 100%, 16px";
App::$strings["Set font-color for posts and comments"] = "Цвет шрифта для постов и комментариев"; App::$strings["Set font-color for posts and comments"] = "Цвет шрифта для публикаций и комментариев";
App::$strings["Set radius of corners"] = "Радиус скруглений"; App::$strings["Set radius of corners"] = "Радиус скруглений";
App::$strings["Example: 4px"] = "Например: 4px"; App::$strings["Example: 4px"] = "Например: 4px";
App::$strings["Set shadow depth of photos"] = "Глубина теней фотографий"; App::$strings["Set shadow depth of photos"] = "Глубина теней фотографий";
@ -444,7 +444,7 @@ App::$strings["An invitation is required."] = "Требуется приглаш
App::$strings["Invitation could not be verified."] = "Не удалось проверить приглашение."; App::$strings["Invitation could not be verified."] = "Не удалось проверить приглашение.";
App::$strings["Please enter the required information."] = "Пожалуйста, введите необходимую информацию."; App::$strings["Please enter the required information."] = "Пожалуйста, введите необходимую информацию.";
App::$strings["Failed to store account information."] = "Не удалось сохранить информацию аккаунта."; App::$strings["Failed to store account information."] = "Не удалось сохранить информацию аккаунта.";
App::$strings["Registration confirmation for %s"] = "Подтверждение регистрации для %s"; App::$strings["Registration confirmation for %s"] = "Подтверждение регистрации на %s";
App::$strings["Registration request at %s"] = "Запрос регистрации на %s"; App::$strings["Registration request at %s"] = "Запрос регистрации на %s";
App::$strings["your registration password"] = "ваш пароль регистрации"; App::$strings["your registration password"] = "ваш пароль регистрации";
App::$strings["Registration details for %s"] = "Регистрационные данные для %s"; App::$strings["Registration details for %s"] = "Регистрационные данные для %s";
@ -729,7 +729,6 @@ App::$strings["Directory Options"] = "Параметры каталога";
App::$strings["Safe Mode"] = "Безопасный режим"; App::$strings["Safe Mode"] = "Безопасный режим";
App::$strings["Public Forums Only"] = "Только публичные форумы"; App::$strings["Public Forums Only"] = "Только публичные форумы";
App::$strings["This Website Only"] = "Только этот веб-сайт"; App::$strings["This Website Only"] = "Только этот веб-сайт";
App::$strings["view full size"] = "посмотреть в полный размер";
App::$strings["Friendica"] = ""; App::$strings["Friendica"] = "";
App::$strings["OStatus"] = ""; App::$strings["OStatus"] = "";
App::$strings["GNU-Social"] = ""; App::$strings["GNU-Social"] = "";
@ -999,7 +998,7 @@ App::$strings["Privacy group: "] = "Группа безопасности: ";
App::$strings["Invalid channel."] = "Недействительный канал."; App::$strings["Invalid channel."] = "Недействительный канал.";
App::$strings["Token verification failed."] = "Не удалось выполнить проверку токена."; App::$strings["Token verification failed."] = "Не удалось выполнить проверку токена.";
App::$strings["Email Verification Required"] = "Требуется проверка адреса email"; App::$strings["Email Verification Required"] = "Требуется проверка адреса email";
App::$strings["A verification token was sent to your email address [%s]. Enter that token here to complete the account verification step. Please allow a few minutes for delivery, and check your spam folder if you do not see the message."] = "Проверочный токен был выслн на ваш адрес электронной почты [%s]. Введите этот токен здесь для завершения этапа проверки учётной записи. Пожалуйста, подождите несколько минут для завершения доставки и проверьте вашу папку \"Спам\" если вы не видите письма."; App::$strings["A verification token was sent to your email address [%s]. Enter that token here to complete the account verification step. Please allow a few minutes for delivery, and check your spam folder if you do not see the message."] = "Проверочный токен был отправлен на ваш адрес электронной почты [%s]. Введите этот токен здесь для завершения этапа проверки учётной записи. Пожалуйста, подождите несколько минут для завершения доставки и проверьте вашу папку \"Спам\" если вы не видите письма.";
App::$strings["Resend Email"] = "Выслать повторно"; App::$strings["Resend Email"] = "Выслать повторно";
App::$strings["Validation token"] = "Проверочный токен"; App::$strings["Validation token"] = "Проверочный токен";
App::$strings["No channel."] = "Канала нет."; App::$strings["No channel."] = "Канала нет.";
@ -1117,6 +1116,8 @@ App::$strings["Posts and comments"] = "Публикации и коммента
App::$strings["Only posts"] = "Только публикации"; App::$strings["Only posts"] = "Только публикации";
App::$strings["vcard"] = "vCard"; App::$strings["vcard"] = "vCard";
App::$strings["You must be logged in to see this page."] = "Вы должны авторизоваться, чтобы увидеть эту страницу."; App::$strings["You must be logged in to see this page."] = "Вы должны авторизоваться, чтобы увидеть эту страницу.";
App::$strings["&#x1f501; Repeated %1\$s's %2\$s"] = "&#x1f501; Повторил %1\$s %2\$s";
App::$strings["Post repeated"] = "Публикация повторяется";
App::$strings["No more system notifications."] = "Нет новых оповещений системы."; App::$strings["No more system notifications."] = "Нет новых оповещений системы.";
App::$strings["System Notifications"] = "Системные оповещения "; App::$strings["System Notifications"] = "Системные оповещения ";
App::$strings["%s element installed"] = "%s элемент установлен"; App::$strings["%s element installed"] = "%s элемент установлен";
@ -1549,6 +1550,8 @@ App::$strings["Default maximum affinity level"] = "Максимальный ур
App::$strings["0-99 default 99"] = "0-99 (по умолчанию 99)"; App::$strings["0-99 default 99"] = "0-99 (по умолчанию 99)";
App::$strings["Default minimum affinity level"] = "Минимальный уровень сходства по умолчанию."; App::$strings["Default minimum affinity level"] = "Минимальный уровень сходства по умолчанию.";
App::$strings["0-99 - default 0"] = "0-99 (по умолчанию 0)"; App::$strings["0-99 - default 0"] = "0-99 (по умолчанию 0)";
App::$strings["Always reset on new page visit."] = "Всегда сбрасывать при посещении новой страницы.";
App::$strings["default: yes"] = "по-умолчанию: да";
App::$strings["Affinity Slider Settings"] = "Настройки слайдера сходства"; App::$strings["Affinity Slider Settings"] = "Настройки слайдера сходства";
App::$strings["Addon Settings"] = "Настройки расширений"; App::$strings["Addon Settings"] = "Настройки расширений";
App::$strings["Please save/submit changes to any panel before opening another."] = "Пожалуйста сохраните / отправьте изменения на панели прежде чем открывать другую."; App::$strings["Please save/submit changes to any panel before opening another."] = "Пожалуйста сохраните / отправьте изменения на панели прежде чем открывать другую.";
@ -1788,6 +1791,7 @@ App::$strings["Unable to check command line PHP, as shell_exec() is disabled. Th
App::$strings["The command line version of PHP on your system does not have \"register_argc_argv\" enabled."] = "В консольной версии PHP в вашей системе отключена опция \"register_argc_argv\"."; App::$strings["The command line version of PHP on your system does not have \"register_argc_argv\" enabled."] = "В консольной версии PHP в вашей системе отключена опция \"register_argc_argv\".";
App::$strings["This is required for message delivery to work."] = "Это необходимо для функционирования доставки сообщений."; App::$strings["This is required for message delivery to work."] = "Это необходимо для функционирования доставки сообщений.";
App::$strings["PHP register_argc_argv"] = ""; App::$strings["PHP register_argc_argv"] = "";
App::$strings["This is not sufficient to upload larger images or files. You should be able to upload at least 4 MB at once."] = "Этого недостаточно для загрузки больших изображений или файлов. Вы должны иметь возможность загрузить как минимум 4 Мб за раз.";
App::$strings["Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once."] = "Максимально разрешённый общий размер загрузок установлен в %s. Максимальный размер одной загрузки установлен в %s. Вам разрешено загружать до %d файлов за один приём."; App::$strings["Your max allowed total upload size is set to %s. Maximum size of one file to upload is set to %s. You are allowed to upload up to %d files at once."] = "Максимально разрешённый общий размер загрузок установлен в %s. Максимальный размер одной загрузки установлен в %s. Вам разрешено загружать до %d файлов за один приём.";
App::$strings["You can adjust these settings in the server php.ini file."] = "Вы можете изменить эти настройки в файле php.ini на сервере."; App::$strings["You can adjust these settings in the server php.ini file."] = "Вы можете изменить эти настройки в файле php.ini на сервере.";
App::$strings["PHP upload limits"] = "Максимальный размер загрузки в PHP"; App::$strings["PHP upload limits"] = "Максимальный размер загрузки в PHP";
@ -2630,7 +2634,7 @@ App::$strings["Can view my wiki pages"] = "Может просматривать
App::$strings["Can create/edit my channel webpages"] = "Может редактировать мои веб-страницы"; App::$strings["Can create/edit my channel webpages"] = "Может редактировать мои веб-страницы";
App::$strings["Can write to my wiki pages"] = "Может редактировать мои вики-страницы"; App::$strings["Can write to my wiki pages"] = "Может редактировать мои вики-страницы";
App::$strings["Can post on my channel (wall) page"] = "Может публиковать на моей странице канала"; App::$strings["Can post on my channel (wall) page"] = "Может публиковать на моей странице канала";
App::$strings["Can comment on or like my posts"] = "Может прокомментировать или отмечать как понравившиеся мои посты"; App::$strings["Can comment on or like my posts"] = "Может прокомментировать или отмечать как понравившиеся мои публикации";
App::$strings["Can send me private mail messages"] = "Может отправлять мне личные сообщения по эл. почте"; App::$strings["Can send me private mail messages"] = "Может отправлять мне личные сообщения по эл. почте";
App::$strings["Can like/dislike profiles and profile things"] = "Может комментировать или отмечать как нравится/ненравится мой профиль"; App::$strings["Can like/dislike profiles and profile things"] = "Может комментировать или отмечать как нравится/ненравится мой профиль";
App::$strings["Can forward to all my channel connections via ! mentions in posts"] = "Может пересылать всем подписчикам моего канала используя ! в публикациях"; App::$strings["Can forward to all my channel connections via ! mentions in posts"] = "Может пересылать всем подписчикам моего канала используя ! в публикациях";
@ -2757,13 +2761,16 @@ App::$strings["Add to app-tray"] = "Добавить в app-tray";
App::$strings["Remove from app-tray"] = "Удалить из app-tray"; App::$strings["Remove from app-tray"] = "Удалить из app-tray";
App::$strings["Pin to navbar"] = "Добавить на панель навигации"; App::$strings["Pin to navbar"] = "Добавить на панель навигации";
App::$strings["Unpin from navbar"] = "Удалить с панели навигации"; App::$strings["Unpin from navbar"] = "Удалить с панели навигации";
App::$strings["I will attend"] = "Я буду присутствовать"; App::$strings["Privacy conflict. Discretion advised."] = "Конфиликт настроек конфиденциальности.";
App::$strings["I will not attend"] = "Я не буду присутствовать"; App::$strings["I will attend"] = "Я буду участвовать";
App::$strings["I will not attend"] = "Я не буду участвовать";
App::$strings["I might attend"] = "Я возможно буду присутствовать"; App::$strings["I might attend"] = "Я возможно буду присутствовать";
App::$strings["I agree"] = "Я согласен"; App::$strings["I agree"] = "Я согласен";
App::$strings["I disagree"] = "Я не согласен"; App::$strings["I disagree"] = "Я не согласен";
App::$strings["I abstain"] = "Я воздержался"; App::$strings["I abstain"] = "Я воздержался";
App::$strings["Add Tag"] = "Добавить тег"; App::$strings["Add Tag"] = "Добавить тег";
App::$strings["Repeat This"] = "Повторить это";
App::$strings["repeat"] = "повторение";
App::$strings["Share This"] = "Поделиться этим"; App::$strings["Share This"] = "Поделиться этим";
App::$strings["share"] = "поделиться"; App::$strings["share"] = "поделиться";
App::$strings["Delivery Report"] = "Отчёт о доставке"; App::$strings["Delivery Report"] = "Отчёт о доставке";
@ -2806,6 +2813,11 @@ App::$strings["This is your default setting for who can view your default channe
App::$strings["This is your default setting for who can view your connections"] = "Это настройка по умолчанию для тех, кто может просматривать ваши контакты"; App::$strings["This is your default setting for who can view your connections"] = "Это настройка по умолчанию для тех, кто может просматривать ваши контакты";
App::$strings["This is your default setting for who can view your file storage and photos"] = "Это настройка по умолчанию для тех, кто может просматривать ваше хранилище файлов и фотографий"; App::$strings["This is your default setting for who can view your file storage and photos"] = "Это настройка по умолчанию для тех, кто может просматривать ваше хранилище файлов и фотографий";
App::$strings["This is your default setting for the audience of your webpages"] = "Это настройка по умолчанию для аудитории ваших веб-страниц"; App::$strings["This is your default setting for the audience of your webpages"] = "Это настройка по умолчанию для аудитории ваших веб-страниц";
App::$strings["Likes %1\$s's %2\$s"] = "Нравится %1\$s %2\$s";
App::$strings["Doesn't like %1\$s's %2\$s"] = "Не нравится %1\$s %2\$s";
App::$strings["Will attend %1\$s's %2\$s"] = "Примет участие %1\$s %2\$s";
App::$strings["Will not attend %1\$s's %2\$s"] = "Не примет участие %1\$s %2\$s";
App::$strings["May attend %1\$s's %2\$s"] = "Возможно примет участие %1\$s %2\$s";
App::$strings["0. Beginner/Basic"] = "Начинающий / Базовый"; App::$strings["0. Beginner/Basic"] = "Начинающий / Базовый";
App::$strings["1. Novice - not skilled but willing to learn"] = "1. Новичок - не опытный, но желающий учиться"; App::$strings["1. Novice - not skilled but willing to learn"] = "1. Новичок - не опытный, но желающий учиться";
App::$strings["2. Intermediate - somewhat comfortable"] = "2. Промежуточный - более удобный"; App::$strings["2. Intermediate - somewhat comfortable"] = "2. Промежуточный - более удобный";
@ -2816,11 +2828,13 @@ App::$strings["Wiki updated successfully"] = "Wiki успешно обновле
App::$strings["Wiki files deleted successfully"] = "Wiki успешно удалена"; App::$strings["Wiki files deleted successfully"] = "Wiki успешно удалена";
App::$strings["Use markdown for editing posts"] = "Использовать язык разметки Markdown для редактирования публикаций"; App::$strings["Use markdown for editing posts"] = "Использовать язык разметки Markdown для редактирования публикаций";
App::$strings["Post to Dreamwidth"] = "Публиковать в Dreamwidth"; App::$strings["Post to Dreamwidth"] = "Публиковать в Dreamwidth";
App::$strings["Enable Dreamwidth Post Plugin"] = "Включить плагин публикаций Dreamwidth"; App::$strings["Dreamwidth Crosspost Connector Settings saved."] = "Настройки пересылки публикаций Dreamwidth сохранены.";
App::$strings["Dreamwidth Crosspost Connector App"] = "Приложение \"Пересылка публикаций Dreamwidth\"";
App::$strings["Relay public postings to Dreamwidth"] = "Пересылает общедоступные публикации в Dreamwidth";
App::$strings["Dreamwidth username"] = "Имя пользователя Dreamwidth"; App::$strings["Dreamwidth username"] = "Имя пользователя Dreamwidth";
App::$strings["Dreamwidth password"] = "Пароль Dreamwidth"; App::$strings["Dreamwidth password"] = "Пароль Dreamwidth";
App::$strings["Post to Dreamwidth by default"] = "Публиковать в Dreamwidth по умолчанию"; App::$strings["Post to Dreamwidth by default"] = "Публиковать в Dreamwidth по умолчанию";
App::$strings["Dreamwidth Post Settings"] = "Настройки публикаций в Dreamwidth"; App::$strings["Dreamwidth Crosspost Connector"] = "Пересылка публикаций Dreamwidth";
App::$strings["Project Servers and Resources"] = "Серверы и ресурсы проекта"; App::$strings["Project Servers and Resources"] = "Серверы и ресурсы проекта";
App::$strings["Project Creator and Tech Lead"] = "Создатель проекта и технический руководитель"; App::$strings["Project Creator and Tech Lead"] = "Создатель проекта и технический руководитель";
App::$strings["And the hundreds of other people and organisations who helped make the Hubzilla possible."] = "И сотни других людей и организаций которые помогали в создании Hubzilla."; App::$strings["And the hundreds of other people and organisations who helped make the Hubzilla possible."] = "И сотни других людей и организаций которые помогали в создании Hubzilla.";
@ -2842,6 +2856,8 @@ App::$strings["Minimum offset in meters"] = "Минимальное смещен
App::$strings["Maximum offset in meters"] = "Максимальное смещение в метрах"; App::$strings["Maximum offset in meters"] = "Максимальное смещение в метрах";
App::$strings["Fuzzloc Settings"] = "Настройки Fuzzloc"; App::$strings["Fuzzloc Settings"] = "Настройки Fuzzloc";
App::$strings["Allow magic authentication only to websites of your immediate connections"] = "Разрешить волшебную аутентификацию только на сайтах ваших непосредственных соединений"; App::$strings["Allow magic authentication only to websites of your immediate connections"] = "Разрешить волшебную аутентификацию только на сайтах ваших непосредственных соединений";
App::$strings["Authchoose App"] = "Приложение Authchoose";
App::$strings["Installed"] = "Установлено";
App::$strings["Authchoose"] = ""; App::$strings["Authchoose"] = "";
App::$strings["Photos imported"] = "Фотографии импортированы"; App::$strings["Photos imported"] = "Фотографии импортированы";
App::$strings["Redmatrix Photo Album Import"] = "Импортировать альбом фотографий Redmatrix"; App::$strings["Redmatrix Photo Album Import"] = "Импортировать альбом фотографий Redmatrix";
@ -2853,11 +2869,13 @@ App::$strings["Import just this album"] = "Импортировать тольк
App::$strings["Leave blank to import all albums"] = "Оставьте пустым для импорта всех альбомов"; App::$strings["Leave blank to import all albums"] = "Оставьте пустым для импорта всех альбомов";
App::$strings["Maximum count to import"] = "Максимальное количество для импорта"; App::$strings["Maximum count to import"] = "Максимальное количество для импорта";
App::$strings["0 or blank to import all available"] = "0 или пусто для импорта всех доступных"; App::$strings["0 or blank to import all available"] = "0 или пусто для импорта всех доступных";
App::$strings["Gallery App"] = "Приложение \"Галерея\"";
App::$strings["A simple gallery for your photo albums"] = "Простая галлерея для ваших фотоальбомов"; App::$strings["A simple gallery for your photo albums"] = "Простая галлерея для ваших фотоальбомов";
App::$strings["Gallery"] = "Галерея"; App::$strings["Gallery"] = "Галерея";
App::$strings["Photo Gallery"] = "Фотогалерея"; App::$strings["Photo Gallery"] = "Фотогалерея";
App::$strings["Friendica Crosspost Connector Settings saved."] = "Настройки пересылки публикаций Friendica сохранены."; App::$strings["Friendica Crosspost Connector Settings saved."] = "Настройки пересылки публикаций Friendica сохранены.";
App::$strings["Relay public postings to a connected Friendica account"] = "Ретранслировать общедоступные публикации на подключённую учётную запись Friendica"; App::$strings["Friendica Crosspost Connector App"] = "Приложение \"Пересылка публикаций Friendica\"";
App::$strings["Relay public postings to a connected Friendica account"] = "Пересылает общедоступные публикации на подключённую учётную запись Friendica";
App::$strings["Send public postings to Friendica by default"] = "Отправлять общедоступные публикации во Friendica по умолчанию"; App::$strings["Send public postings to Friendica by default"] = "Отправлять общедоступные публикации во Friendica по умолчанию";
App::$strings["Friendica API Path"] = "Путь к Friendica API"; App::$strings["Friendica API Path"] = "Путь к Friendica API";
App::$strings["https://{sitename}/api"] = ""; App::$strings["https://{sitename}/api"] = "";
@ -2869,7 +2887,8 @@ App::$strings["An account has been created for you."] = "Учётная запи
App::$strings["Authentication successful but rejected: account creation is disabled."] = "Аутентификация выполнена успешно, но отклонена: создание учетной записи отключено."; App::$strings["Authentication successful but rejected: account creation is disabled."] = "Аутентификация выполнена успешно, но отклонена: создание учетной записи отключено.";
App::$strings["Post to Insane Journal"] = "Опубликовать в Insane Journal"; App::$strings["Post to Insane Journal"] = "Опубликовать в Insane Journal";
App::$strings["Insane Journal Crosspost Connector Settings saved."] = "Настройки пересылки публикаций Insane Journal сохранены."; App::$strings["Insane Journal Crosspost Connector Settings saved."] = "Настройки пересылки публикаций Insane Journal сохранены.";
App::$strings["Relay public postings to Insane Journal"] = "Ретранслировать общедоступные публикации в Insane Journal"; App::$strings["Insane Journal Crosspost Connector App"] = "Приложение \"Пересылка публикаций Insane Journal\"";
App::$strings["Relay public postings to Insane Journal"] = "Пересылает общедоступные публикации в Insane Journal";
App::$strings["InsaneJournal username"] = "Имя пользователя Insane Journal"; App::$strings["InsaneJournal username"] = "Имя пользователя Insane Journal";
App::$strings["InsaneJournal password"] = "Пароль Insane Journal"; App::$strings["InsaneJournal password"] = "Пароль Insane Journal";
App::$strings["Post to InsaneJournal by default"] = "Публиковать в Insane Journal по умолчанию"; App::$strings["Post to InsaneJournal by default"] = "Публиковать в Insane Journal по умолчанию";
@ -2880,22 +2899,22 @@ App::$strings["Redmatrix File Storage Import"] = "Импорт файловог
App::$strings["This will import all your Redmatrix cloud files to this channel."] = "Это позволит импортировать все ваши файлы в Redmatrix в этот канал."; App::$strings["This will import all your Redmatrix cloud files to this channel."] = "Это позволит импортировать все ваши файлы в Redmatrix в этот канал.";
App::$strings["file"] = "файл"; App::$strings["file"] = "файл";
App::$strings["Post to Twitter"] = "Опубликовать в Twitter"; App::$strings["Post to Twitter"] = "Опубликовать в Twitter";
App::$strings["Submit Settings"] = "Отправить настройки";
App::$strings["Twitter settings updated."] = "Настройки Twitter обновлены"; App::$strings["Twitter settings updated."] = "Настройки Twitter обновлены";
App::$strings["Twitter Crosspost Connector App"] = "Приложение \"Пересылка публикаций Twitter\"";
App::$strings["Relay public posts to Twitter"] = "Пересылает общедоступные публикации в Twitter";
App::$strings["No consumer key pair for Twitter found. Please contact your site administrator."] = "Не найдено пары ключей для Twitter. Пожалуйста, свяжитесь с администратором сайта."; App::$strings["No consumer key pair for Twitter found. Please contact your site administrator."] = "Не найдено пары ключей для Twitter. Пожалуйста, свяжитесь с администратором сайта.";
App::$strings["At this Hubzilla instance the Twitter plugin was enabled but you have not yet connected your account to your Twitter account. To do so click the button below to get a PIN from Twitter which you have to copy into the input box below and submit the form. Only your <strong>public</strong> posts will be posted to Twitter."] = "В этой установке Hubzilla плагин Twitter был включён, однако пока он не подключён к вашему аккаунту в Twitter. Для этого нажмите на кнопку ниже для получения PIN-кода от Twitter который нужно скопировать в поле ввода и отправить форму. Только ваши <strong>общедоступные</strong> публикации будут опубликованы в Twitter."; App::$strings["At this Hubzilla instance the Twitter plugin was enabled but you have not yet connected your account to your Twitter account. To do so click the button below to get a PIN from Twitter which you have to copy into the input box below and submit the form. Only your <strong>public</strong> posts will be posted to Twitter."] = "В этой установке Hubzilla плагин Twitter был включён, однако пока он не подключён к вашему аккаунту в Twitter. Для этого нажмите на кнопку ниже для получения PIN-кода от Twitter который нужно скопировать в поле ввода и отправить форму. Только ваши <strong>общедоступные</strong> публикации будут опубликованы в Twitter.";
App::$strings["Log in with Twitter"] = "Войти в Twitter"; App::$strings["Log in with Twitter"] = "Войти в Twitter";
App::$strings["Copy the PIN from Twitter here"] = "Скопируйте PIN-код из Twitter здесь"; App::$strings["Copy the PIN from Twitter here"] = "Скопируйте PIN-код из Twitter здесь";
App::$strings["Currently connected to: "] = "В настоящее время подключён к:"; App::$strings["Currently connected to: "] = "В настоящее время подключён к:";
App::$strings["<strong>Note:</strong> Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to Twitter will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted."] = "<strong>Замечание</strong>: Из-за настроек конфиденциальности (<em>скрыть данные своего профиля от неизвестных зрителей?</em>) cсылка, потенциально включенная в общедоступные публикации, переданные в Twitter, приведет посетителя к пустой странице, информирующей его о том, что доступ к вашему профилю был ограничен."; App::$strings["<strong>Note:</strong> Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to Twitter will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted."] = "<strong>Замечание</strong>: Из-за настроек конфиденциальности (<em>скрыть данные своего профиля от неизвестных зрителей?</em>) cсылка, потенциально включенная в общедоступные публикации, переданные в Twitter, приведет посетителя к пустой странице, информирующей его о том, что доступ к вашему профилю был ограничен.";
App::$strings["Allow posting to Twitter"] = "Разрешить публиковать в Twitter";
App::$strings["If enabled your public postings can be posted to the associated Twitter account"] = "Если включено, ваши общедоступные публикации будут опубликованы в связанной учётной записи Twitter";
App::$strings["Twitter post length"] = "Длина публикации Twitter"; App::$strings["Twitter post length"] = "Длина публикации Twitter";
App::$strings["Maximum tweet length"] = "Максимальная длина твита"; App::$strings["Maximum tweet length"] = "Максимальная длина твита";
App::$strings["Send public postings to Twitter by default"] = "Отправлять общедоступные публикации в Twitter по умолчанию"; App::$strings["Send public postings to Twitter by default"] = "Отправлять общедоступные публикации в Twitter по умолчанию";
App::$strings["If enabled your public postings will be posted to the associated Twitter account by default"] = "Если включено, ваши общедоступные публикации будут опубликованы в связанной учётной записи Twitter по умолчанию"; App::$strings["If enabled your public postings will be posted to the associated Twitter account by default"] = "Если включено, ваши общедоступные публикации будут опубликованы в связанной учётной записи Twitter по умолчанию";
App::$strings["Clear OAuth configuration"] = "Очистить конфигурацию OAuth"; App::$strings["Clear OAuth configuration"] = "Очистить конфигурацию OAuth";
App::$strings["Twitter Post Settings"] = "Настройки публикаций в Twitter"; App::$strings["Twitter Crosspost Connector"] = "Пересылка публикаций Twitter";
App::$strings["Submit Settings"] = "Отправить настройки";
App::$strings["Edit your profile and change settings."] = "Отредактировать ваш профиль и изменить настройки."; App::$strings["Edit your profile and change settings."] = "Отредактировать ваш профиль и изменить настройки.";
App::$strings["Click here to see activity from your connections."] = "Нажмите сюда для отображения активности ваши контактов."; App::$strings["Click here to see activity from your connections."] = "Нажмите сюда для отображения активности ваши контактов.";
App::$strings["Click here to see your channel home."] = "Нажмите сюда чтобы увидеть главную страницу вашего канала."; App::$strings["Click here to see your channel home."] = "Нажмите сюда чтобы увидеть главную страницу вашего канала.";
@ -2913,7 +2932,7 @@ App::$strings["There are events this week. Click here too see which!"] = "На
App::$strings["You have received a new introduction. Click here to see who!"] = "Вы были представлены. Нажмите чтобы увидеть кому!"; App::$strings["You have received a new introduction. Click here to see who!"] = "Вы были представлены. Нажмите чтобы увидеть кому!";
App::$strings["There is a new system notification. Click here to see what has happened!"] = "Это новое системное уведомление. Нажмите чтобы посмотреть что случилось!"; App::$strings["There is a new system notification. Click here to see what has happened!"] = "Это новое системное уведомление. Нажмите чтобы посмотреть что случилось!";
App::$strings["Click here to share text, images, videos and sound."] = "Нажмите сюда чтобы поделиться текстом, изображениями, видео или треком."; App::$strings["Click here to share text, images, videos and sound."] = "Нажмите сюда чтобы поделиться текстом, изображениями, видео или треком.";
App::$strings["You can write an optional title for your update (good for long posts)."] = "Вы можете написать необязательный заголовок для вашей публикации (желательно для больших постов)."; App::$strings["You can write an optional title for your update (good for long posts)."] = "Вы можете написать необязательный заголовок для вашей публикации (желательно для больших публикаций).";
App::$strings["Entering some categories here makes it easier to find your post later."] = "Введите категории здесь чтобы было проще найти вашу публикацию позднее."; App::$strings["Entering some categories here makes it easier to find your post later."] = "Введите категории здесь чтобы было проще найти вашу публикацию позднее.";
App::$strings["Share photos, links, location, etc."] = "Поделиться фотографией, ссылками, местоположение и т.п."; App::$strings["Share photos, links, location, etc."] = "Поделиться фотографией, ссылками, местоположение и т.п.";
App::$strings["Only want to share content for a while? Make it expire at a certain date."] = "Хотите только поделиться временным содержимым? Установите срок его действия."; App::$strings["Only want to share content for a while? Make it expire at a certain date."] = "Хотите только поделиться временным содержимым? Установите срок его действия.";
@ -2932,6 +2951,7 @@ App::$strings["If you see this icon you can be sure that the sender is who it sa
App::$strings["Danger! It seems someone tried to forge a message! This message is not necessarily from who it says it is from!"] = "Опасность! Кажется, кто-то пытался подделать сообщение! Это сообщение не обязательно от того, от кого оно значится!"; App::$strings["Danger! It seems someone tried to forge a message! This message is not necessarily from who it says it is from!"] = "Опасность! Кажется, кто-то пытался подделать сообщение! Это сообщение не обязательно от того, от кого оно значится!";
App::$strings["Welcome to Hubzilla! Would you like to see a tour of the UI?</p> <p>You can pause it at any time and continue where you left off by reloading the page, or navigting to another page.</p><p>You can also advance by pressing the return key"] = "Добро пожаловать в Hubzilla! Желаете получить обзор пользовательского интерфейса?</p> <p>Вы можете его приостановаить и в любое время перезагрузив страницу или перейдя на другую.</p><p>Также вы можете нажать клавишу \"Назад\""; App::$strings["Welcome to Hubzilla! Would you like to see a tour of the UI?</p> <p>You can pause it at any time and continue where you left off by reloading the page, or navigting to another page.</p><p>You can also advance by pressing the return key"] = "Добро пожаловать в Hubzilla! Желаете получить обзор пользовательского интерфейса?</p> <p>Вы можете его приостановаить и в любое время перезагрузив страницу или перейдя на другую.</p><p>Также вы можете нажать клавишу \"Назад\"";
App::$strings["Block Completely"] = "Заблокировать полностью"; App::$strings["Block Completely"] = "Заблокировать полностью";
App::$strings["Superblock App"] = "Приложение Superblock";
App::$strings["Block channels"] = "Заблокировать каналы"; App::$strings["Block channels"] = "Заблокировать каналы";
App::$strings["superblock settings updated"] = "Настройки Superblock обновлены."; App::$strings["superblock settings updated"] = "Настройки Superblock обновлены.";
App::$strings["Currently blocked"] = "В настоящее время заблокирован"; App::$strings["Currently blocked"] = "В настоящее время заблокирован";
@ -2997,8 +3017,9 @@ App::$strings["Include marker on map"] = "Включите маркер на к
App::$strings["Include a marker on the map."] = "Включить маркер на карте"; App::$strings["Include a marker on the map."] = "Включить маркер на карте";
App::$strings["Photo Cache settings saved."] = "Настройки Photo Cache сохранены."; App::$strings["Photo Cache settings saved."] = "Настройки Photo Cache сохранены.";
App::$strings["Photo Cache addon saves a copy of images from external sites locally to increase your anonymity in the web."] = "Приложение Photo Cache сохраняет копию изображений с внешних сайтов локально для повышения вашей анонимности в Интернет."; App::$strings["Photo Cache addon saves a copy of images from external sites locally to increase your anonymity in the web."] = "Приложение Photo Cache сохраняет копию изображений с внешних сайтов локально для повышения вашей анонимности в Интернет.";
App::$strings["Photo Cache App"] = "Приложение Photo Cache";
App::$strings["Minimal photo size for caching"] = "Минимальный размер фотографии для кэширования"; App::$strings["Minimal photo size for caching"] = "Минимальный размер фотографии для кэширования";
App::$strings["In pixels. 0 will be replaced with system default, from 1 up to 1024 (large images will be scaled to this value)."] = "В пискелях. 0 будет заменён значением по умолчанию, от 1 до 1024 (большие изображения будут масштабированы до этого значения)."; App::$strings["In pixels. From 1 up to 1024, 0 will be replaced with system default."] = "В пикселях. От 1 до 1024, 0 будет заменён значением по умолчанию.";
App::$strings["Photo Cache"] = ""; App::$strings["Photo Cache"] = "";
App::$strings["Activate addon"] = "Активировать расширение"; App::$strings["Activate addon"] = "Активировать расширение";
App::$strings["Hide Jappixmini Chat-Widget from the webinterface"] = "Скрыть виджет чата Jappixmini из веб-интерфейса"; App::$strings["Hide Jappixmini Chat-Widget from the webinterface"] = "Скрыть виджет чата Jappixmini из веб-интерфейса";
@ -3047,24 +3068,24 @@ App::$strings["Show opt-out cookie link?"] = "Показывать ссылку
App::$strings["Asynchronous tracking"] = "Асинхронное отслеживание"; App::$strings["Asynchronous tracking"] = "Асинхронное отслеживание";
App::$strings["Enable frontend JavaScript error tracking"] = "Включить отслеживание ошибок JavaScript на фронтенде."; App::$strings["Enable frontend JavaScript error tracking"] = "Включить отслеживание ошибок JavaScript на фронтенде.";
App::$strings["This feature requires Piwik >= 2.2.0"] = "Эта функция требует версию Piwik >= 2.2.0"; App::$strings["This feature requires Piwik >= 2.2.0"] = "Эта функция требует версию Piwik >= 2.2.0";
App::$strings["You are now authenticated to pumpio."] = "Вы аутентифицированы в Pump.io"; App::$strings["Pump.io Settings saved."] = "Настройки Pump.io сохранены.";
App::$strings["return to the featured settings page"] = "Вернутся к странице настроек"; App::$strings["Pump.io Crosspost Connector App"] = "Приложение \"Пересылка публикаций Pump.io\"";
App::$strings["Post to Pump.io"] = "Опубликовать в Pump.io"; App::$strings["Relay public posts to pump.io"] = "Пересылает общедоступные публикации в Pump.io";
App::$strings["Pump.io servername"] = "Имя сервера Pump.io"; App::$strings["Pump.io servername"] = "Имя сервера Pump.io";
App::$strings["Without \"http://\" or \"https://\""] = "Без \"http://\" или \"https://\""; App::$strings["Without \"http://\" or \"https://\""] = "Без \"http://\" или \"https://\"";
App::$strings["Pump.io username"] = "Имя пользователя Pump.io"; App::$strings["Pump.io username"] = "Имя пользователя Pump.io";
App::$strings["Without the servername"] = "без имени сервера"; App::$strings["Without the servername"] = "без имени сервера";
App::$strings["You are not authenticated to pumpio"] = "Вы не аутентифицированы на Pump.io"; App::$strings["You are not authenticated to pumpio"] = "Вы не аутентифицированы на Pump.io";
App::$strings["(Re-)Authenticate your pump.io connection"] = "Аутентифицировать (повторно) ваше соединение с Pump.io"; App::$strings["(Re-)Authenticate your pump.io connection"] = "Аутентифицировать (повторно) ваше соединение с Pump.io";
App::$strings["Enable pump.io Post Plugin"] = "Включить плагин публикаций Pump.io";
App::$strings["Post to pump.io by default"] = "Публиковать в Pump.io по умолчанию"; App::$strings["Post to pump.io by default"] = "Публиковать в Pump.io по умолчанию";
App::$strings["Should posts be public"] = "Публикации должны быть общедоступными"; App::$strings["Should posts be public"] = "Публикации должны быть общедоступными";
App::$strings["Mirror all public posts"] = "Отображать все общедоступные публикации"; App::$strings["Mirror all public posts"] = "Отображать все общедоступные публикации";
App::$strings["Pump.io Post Settings"] = "Настройки публикаций в Pump.io"; App::$strings["Pump.io Crosspost Connector"] = "Пересылка публикаций Pump.io";
App::$strings["PumpIO Settings saved."] = "Настройки публикаций в Pump.io сохранены."; App::$strings["You are now authenticated to pumpio."] = "Вы аутентифицированы в Pump.io";
App::$strings["Nsabait Settings updated."] = "Настройки Nsabait обновлены"; App::$strings["return to the featured settings page"] = "Вернутся к странице настроек";
App::$strings["Enable NSAbait Plugin"] = "Включить плагин NSAbait"; App::$strings["Post to Pump.io"] = "Опубликовать в Pump.io";
App::$strings["NSAbait Settings"] = "Настройки Nsabait"; App::$strings["NSA Bait App"] = "Приложение NSA Bait";
App::$strings["Make yourself a political target"] = "Сделать себя политической мишенью";
App::$strings["Send test email"] = "Отправить тестовый email"; App::$strings["Send test email"] = "Отправить тестовый email";
App::$strings["No recipients found."] = "Получателей не найдено."; App::$strings["No recipients found."] = "Получателей не найдено.";
App::$strings["Mail sent."] = "Сообщение отправлено"; App::$strings["Mail sent."] = "Сообщение отправлено";
@ -3106,15 +3127,17 @@ App::$strings["Hubzilla Server base URL"] = "Базовый URL сервера H
App::$strings["Since modified date yyyy-mm-dd"] = "Начиная с даты изменений yyyy-mm-dd"; App::$strings["Since modified date yyyy-mm-dd"] = "Начиная с даты изменений yyyy-mm-dd";
App::$strings["Until modified date yyyy-mm-dd"] = "Заканчивая датой изменений yyyy-mm-dd"; App::$strings["Until modified date yyyy-mm-dd"] = "Заканчивая датой изменений yyyy-mm-dd";
App::$strings["Post to Livejournal"] = "Опубликовать в Livejournal"; App::$strings["Post to Livejournal"] = "Опубликовать в Livejournal";
App::$strings["Enable Livejournal Post Plugin"] = "Включить раширение публикаций в Livejournal"; App::$strings["Livejournal Crosspost Connector App"] = "Приложение \"Пересылка публикаций Livejournal\"";
App::$strings["Relay public posts to Livejournal"] = "Пересылает общедоступные публикации в Livejournal";
App::$strings["Livejournal username"] = "Имя пользователя Livejournal"; App::$strings["Livejournal username"] = "Имя пользователя Livejournal";
App::$strings["Livejournal password"] = "Пароль Livejournal"; App::$strings["Livejournal password"] = "Пароль Livejournal";
App::$strings["Post to Livejournal by default"] = "Публиковать в Livejournal по умолчанию"; App::$strings["Post to Livejournal by default"] = "Публиковать в Livejournal по умолчанию";
App::$strings["Livejournal Post Settings"] = "Настройки публикации в Livejournal"; App::$strings["Livejournal Crosspost Connector"] = "Пересылка публикаций Livejournal";
App::$strings["Please contact your site administrator.<br />The provided API URL is not valid."] = "Пожалуйста свяжитесь с администратором сайта. <br />Предоставленный URL API недействителен."; App::$strings["Please contact your site administrator.<br />The provided API URL is not valid."] = "Пожалуйста свяжитесь с администратором сайта. <br />Предоставленный URL API недействителен.";
App::$strings["We could not contact the GNU social API with the Path you entered."] = "Нам не удалось установить контакт с GNU Social API по введённому вами пути"; App::$strings["We could not contact the GNU social API with the Path you entered."] = "Нам не удалось установить контакт с GNU Social API по введённому вами пути";
App::$strings["GNU social settings updated."] = "Настройки GNU Social обновлены."; App::$strings["GNU social settings updated."] = "Настройки GNU Social обновлены.";
App::$strings["Relay public postings to a connected GNU social account (formerly StatusNet)"] = "Ретранслировать общедоступные публикации на подключённую учётную запись GNU social (бывшая StatusNet)"; App::$strings["Hubzilla Crosspost Connector App"] = "Приложение \"Пересылка публикаций Hubzilla\"";
App::$strings["Relay public postings to a connected GNU social account (formerly StatusNet)"] = "Пересылает общедоступные публикации на подключённую учётную запись GNU social (бывшая StatusNet)";
App::$strings["Globally Available GNU social OAuthKeys"] = "Глобально доступные ключи OAuthKeys GNU Social"; App::$strings["Globally Available GNU social OAuthKeys"] = "Глобально доступные ключи OAuthKeys GNU Social";
App::$strings["There are preconfigured OAuth key pairs for some GNU social servers available. If you are using one of them, please use these credentials.<br />If not feel free to connect to any other GNU social instance (see below)."] = "Существуют предварительно настроенные пары ключей OAuth для некоторых доступных серверов GNU social. Если вы используете один из них, используйте эти учетные данные. <br />Если вы не хотите подключаться к какому-либо другому серверу GNU social (см. ниже)."; App::$strings["There are preconfigured OAuth key pairs for some GNU social servers available. If you are using one of them, please use these credentials.<br />If not feel free to connect to any other GNU social instance (see below)."] = "Существуют предварительно настроенные пары ключей OAuth для некоторых доступных серверов GNU social. Если вы используете один из них, используйте эти учетные данные. <br />Если вы не хотите подключаться к какому-либо другому серверу GNU social (см. ниже).";
App::$strings["Provide your own OAuth Credentials"] = "Предоставьте ваши собственные регистрационные данные OAuth"; App::$strings["Provide your own OAuth Credentials"] = "Предоставьте ваши собственные регистрационные данные OAuth";
@ -3133,7 +3156,7 @@ App::$strings["Cancel GNU social Connection"] = "Отменить подключ
App::$strings["<strong>Note</strong>: Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to GNU social will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted."] = "<strong>Замечание</strong>: Из-за настроек конфиденциальности (<em>скрыть данные своего профиля от неизвестных зрителей?</em>) cсылка, потенциально включенная в общедоступные публикации, переданные в GNU social, приведет посетителя к пустой странице, информирующей его о том, что доступ к вашему профилю был ограничен."; App::$strings["<strong>Note</strong>: Due your privacy settings (<em>Hide your profile details from unknown viewers?</em>) the link potentially included in public postings relayed to GNU social will lead the visitor to a blank page informing the visitor that the access to your profile has been restricted."] = "<strong>Замечание</strong>: Из-за настроек конфиденциальности (<em>скрыть данные своего профиля от неизвестных зрителей?</em>) cсылка, потенциально включенная в общедоступные публикации, переданные в GNU social, приведет посетителя к пустой странице, информирующей его о том, что доступ к вашему профилю был ограничен.";
App::$strings["Post to GNU social by default"] = "Публиковать в GNU social по умолчанию"; App::$strings["Post to GNU social by default"] = "Публиковать в GNU social по умолчанию";
App::$strings["If enabled your public postings will be posted to the associated GNU-social account by default"] = "Если включено, ваши общедоступные публикации будут опубликованы в связанной учётной записи GNU social по умолчанию"; App::$strings["If enabled your public postings will be posted to the associated GNU-social account by default"] = "Если включено, ваши общедоступные публикации будут опубликованы в связанной учётной записи GNU social по умолчанию";
App::$strings["GNU-Social Crosspost Connector"] = ""; App::$strings["GNU-Social Crosspost Connector"] = "Подключение пересылки публикаций GNU Social";
App::$strings["Post to GNU social"] = "Опубликовать в GNU Social"; App::$strings["Post to GNU social"] = "Опубликовать в GNU Social";
App::$strings["API URL"] = ""; App::$strings["API URL"] = "";
App::$strings["Application name"] = "Название приложения"; App::$strings["Application name"] = "Название приложения";
@ -3172,7 +3195,8 @@ App::$strings["Birthdate"] = "Дата рождения";
App::$strings["We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID."] = "Мы столкнулись с проблемой входа с предоставленным вами OpenID. Пожалуйста, проверьте корректность его написания."; App::$strings["We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID."] = "Мы столкнулись с проблемой входа с предоставленным вами OpenID. Пожалуйста, проверьте корректность его написания.";
App::$strings["The error message was:"] = "Сообщение об ошибке было:"; App::$strings["The error message was:"] = "Сообщение об ошибке было:";
App::$strings["OpenID protocol error. No ID returned."] = "Ошибка протокола OpenID. Идентификатор не возвращён."; App::$strings["OpenID protocol error. No ID returned."] = "Ошибка протокола OpenID. Идентификатор не возвращён.";
App::$strings["Set a preferred page to load on login from home page"] = "Установить предпочтительную страницу для загрузки при входе с домашней страницы"; App::$strings["Startpage App"] = "Приложение \"Стартовая страница\"";
App::$strings["Set a preferred page to load on login from home page"] = "Устанавливает предпочтительную страницу для загрузки при входе с домашней страницы";
App::$strings["Page to load after login"] = "Страница для загрузки после входа"; App::$strings["Page to load after login"] = "Страница для загрузки после входа";
App::$strings["Examples: &quot;apps&quot;, &quot;network?f=&gid=37&quot; (privacy collection), &quot;channel&quot; or &quot;notifications/system&quot; (leave blank for default network page (grid)."] = "Примеры: &quot;apps&quot;, &quot;network?f=&gid=37&quot; (privacy collection), &quot;channel&quot; or &quot;notifications/system&quot; (оставьте пустым для для страницы сети по умолчанию)."; App::$strings["Examples: &quot;apps&quot;, &quot;network?f=&gid=37&quot; (privacy collection), &quot;channel&quot; or &quot;notifications/system&quot; (leave blank for default network page (grid)."] = "Примеры: &quot;apps&quot;, &quot;network?f=&gid=37&quot; (privacy collection), &quot;channel&quot; or &quot;notifications/system&quot; (оставьте пустым для для страницы сети по умолчанию).";
App::$strings["Startpage"] = "Стартовая страница"; App::$strings["Startpage"] = "Стартовая страница";
@ -3250,9 +3274,10 @@ App::$strings["Invalid Payment Type. Please start again."] = "Недейств
App::$strings["Order not found"] = "Заказ не найден"; App::$strings["Order not found"] = "Заказ не найден";
App::$strings["Federate"] = ""; App::$strings["Federate"] = "";
App::$strings["nofed Settings saved."] = "Настройки nofed сохранены."; App::$strings["nofed Settings saved."] = "Настройки nofed сохранены.";
App::$strings["Allow Federation Toggle"] = "Разрешить переключение федерации"; App::$strings["No Federation App"] = "Приложение No Federation";
App::$strings["Prevent posting from being federated to anybody. It will exist only on your channel page."] = "Запрещает федеративные функций для публикаций. Они будут существовать только на странице вашего канала.";
App::$strings["Federate posts by default"] = "Разрешить федерацию публикаций по умолчанию"; App::$strings["Federate posts by default"] = "Разрешить федерацию публикаций по умолчанию";
App::$strings["NoFed Settings"] = "Настройки NoFed"; App::$strings["No Federation"] = "";
App::$strings["Your channel has been upgraded to the latest \$Projectname version."] = "Ваш канал был обновлён на последнюю версию \$Projectname."; App::$strings["Your channel has been upgraded to the latest \$Projectname version."] = "Ваш канал был обновлён на последнюю версию \$Projectname.";
App::$strings["To improve usability, we have converted some features into installable stand-alone apps."] = "Чтобы улучшить удобство использования, некоторые функции теперь доступны в виде устанавливаемых автономных приложений."; App::$strings["To improve usability, we have converted some features into installable stand-alone apps."] = "Чтобы улучшить удобство использования, некоторые функции теперь доступны в виде устанавливаемых автономных приложений.";
App::$strings["Please visit the \$Projectname"] = "Пожалуйста, посетите \$Projectname"; App::$strings["Please visit the \$Projectname"] = "Пожалуйста, посетите \$Projectname";
@ -3275,13 +3300,14 @@ App::$strings["Rating of images"] = "Оценки изображений";
App::$strings["Select the appropriate avatar rating for your site. See README"] = "Выберите подходящую оценку аватара для вашего сайта (см. README)."; App::$strings["Select the appropriate avatar rating for your site. See README"] = "Выберите подходящую оценку аватара для вашего сайта (см. README).";
App::$strings["Gravatar settings updated."] = "Настройки Gravatar обновлены."; App::$strings["Gravatar settings updated."] = "Настройки Gravatar обновлены.";
App::$strings["WYSIWYG status editor"] = "WYSIWYG редактор статуса "; App::$strings["WYSIWYG status editor"] = "WYSIWYG редактор статуса ";
App::$strings["WYSIWYG Status App"] = "Приложение \"WYSIWYG статус\"";
App::$strings["WYSIWYG Status"] = "WYSIWYG статус"; App::$strings["WYSIWYG Status"] = "WYSIWYG статус";
App::$strings["Planets Settings updated."] = "Настройки Planets обновлены."; App::$strings["Random Planet App"] = "Приложение \"Случайная планета\"";
App::$strings["Enable Planets Plugin"] = "Включить плагин Planets"; App::$strings["Set a random planet from the Star Wars Empire as your location when posting"] = "Установить случайную планету из Империи Звездных Войн в качестве вашего местоположения при публикации";
App::$strings["Planets Settings"] = "Настройки Planets";
App::$strings["Add some colour to tag clouds"] = "Добавить немного цвета для облака тегов"; App::$strings["Add some colour to tag clouds"] = "Добавить немного цвета для облака тегов";
App::$strings["Rainbow Tag App"] = "Приложение \"Радуга тегов\"";
App::$strings["Rainbow Tag"] = "Радуга тегов"; App::$strings["Rainbow Tag"] = "Радуга тегов";
App::$strings["Invalid game."] = "Недействительная игра"; App::$strings["Invalid game."] = "Недействительная игра.";
App::$strings["You are not a player in this game."] = "Вы не играете в эту игру."; App::$strings["You are not a player in this game."] = "Вы не играете в эту игру.";
App::$strings["You must be a local channel to create a game."] = "Ваш канал должен быть локальным чтобы создать игру."; App::$strings["You must be a local channel to create a game."] = "Ваш канал должен быть локальным чтобы создать игру.";
App::$strings["You must select one opponent that is not yourself."] = "Вы должны выбрать противника который не является вами."; App::$strings["You must select one opponent that is not yourself."] = "Вы должны выбрать противника который не является вами.";
@ -3292,19 +3318,42 @@ App::$strings["Enable notifications"] = "Включить оповещения";
App::$strings["Follow"] = "Отслеживать"; App::$strings["Follow"] = "Отслеживать";
App::$strings["%1\$s is now following %2\$s"] = "%1\$s сейчас отслеживает %2\$s"; App::$strings["%1\$s is now following %2\$s"] = "%1\$s сейчас отслеживает %2\$s";
App::$strings["The GNU-Social protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "Протокол GNU-Social не поддерживает независимость от расположения. Ваши контакты установленные в этой сети могут быть недоступны из альтернативных мест размещения канала."; App::$strings["The GNU-Social protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "Протокол GNU-Social не поддерживает независимость от расположения. Ваши контакты установленные в этой сети могут быть недоступны из альтернативных мест размещения канала.";
App::$strings["GNU-Social Protocol App"] = "Приложение \"Протокол GNU-Social\"";
App::$strings["GNU-Social Protocol"] = "Протокол GNU-Social"; App::$strings["GNU-Social Protocol"] = "Протокол GNU-Social";
App::$strings["Message to display on every page on this server"] = "Отображаемое сообщение на каждой странице на этом сервере."; App::$strings["TOTP Two-Step Verification"] = "Двухэтапная верификация TOTP";
App::$strings["Pageheader Settings"] = "Настройки шапки страницы"; App::$strings["Enter the 2-step verification generated by your authenticator app:"] = "Введите код проверки, созданный вашим приложением для аутентификации";
App::$strings["Success!"] = "Успех!";
App::$strings["Invalid code, please try again."] = "Неверный код. Пожалуйста, попробуйте ещё раз.";
App::$strings["Too many invalid codes..."] = "Слишком много неверных кодов...";
App::$strings["Verify"] = "Проверить";
App::$strings["You haven't set a TOTP secret yet.\nPlease click the button below to generate one and register this site\nwith your preferred authenticator app."] = "Вы еще не установили секретный код TOTP. Пожалуйста, нажмите на кнопку ниже, чтобы сгенерировать его и зарегистрировать этот сайт в предпочитаемом вами приложении для аутентификации.";
App::$strings["Your TOTP secret is"] = "Ваш секретный код TOTP";
App::$strings["Be sure to save it somewhere in case you lose or replace your mobile device.\nUse your mobile device to scan the QR code below to register this site\nwith your preferred authenticator app."] = "Обязательно сохраните его где-нибудь на случай потери или замены мобильного устройства. С помощью мобильного устройства отсканируйте приведенный ниже QR-код, чтобы зарегистрировать этот сайт в предпочитаемом вами приложении для аутентификации.";
App::$strings["Test"] = "Тест";
App::$strings["Generate New Secret"] = "Сгенерировать новый секретный код";
App::$strings["Go"] = "Вперёд";
App::$strings["Enter your password"] = "Введите ваш пароль";
App::$strings["enter TOTP code from your device"] = "введите код TOTP из вашего устройства";
App::$strings["Pass!"] = "Принято!";
App::$strings["Fail"] = "Отказано";
App::$strings["Incorrect password, try again."] = "Неверный пароль, попробуйте снова.";
App::$strings["Record your new TOTP secret and rescan the QR code above."] = "Запишите ваш секретный код TOTP и повторно отсканируйте приведенный ниже QR-код.";
App::$strings["TOTP Settings"] = "Настройки TOTP";
App::$strings["pageheader Settings saved."] = "Настройки шапки страницы сохранены."; App::$strings["pageheader Settings saved."] = "Настройки шапки страницы сохранены.";
App::$strings["Page Header App"] = "Приложение \"Заголовок страницы\"";
App::$strings["Inserts a page header"] = "Вставляет заголовок страницы";
App::$strings["Message to display on every page on this server"] = "Отображаемое сообщение на каждой странице на этом сервере.";
App::$strings["Page Header"] = "Заголовок страницы";
App::$strings["text to include in all outgoing posts from this site"] = "текст, который будет добавлен во все исходящие публикации с этого сайта"; App::$strings["text to include in all outgoing posts from this site"] = "текст, который будет добавлен во все исходящие публикации с этого сайта";
App::$strings["Send email to all members"] = "Отправить email всем участникам"; App::$strings["Send email to all members"] = "Отправить email всем участникам";
App::$strings["%1\$d of %2\$d messages sent."] = "%1\$d из %2\$d сообщений отправлено."; App::$strings["%1\$d of %2\$d messages sent."] = "%1\$d из %2\$d сообщений отправлено.";
App::$strings["Send email to all hub members."] = "Отправить email всем участникам узла."; App::$strings["Send email to all hub members."] = "Отправить email всем участникам узла.";
App::$strings["Sender Email address"] = "Адрес электронной почты отправителя"; App::$strings["Sender Email address"] = "Адрес электронной почты отправителя";
App::$strings["Test mode (only send to hub administrator)"] = "Тестовый режим (отправка только администратору узла)"; App::$strings["Test mode (only send to hub administrator)"] = "Тестовый режим (отправка только администратору узла)";
App::$strings["Deactivate the feature"] = "Деактивировать функцию"; App::$strings["Smileybutton App"] = "Приложение \"Кнопка со смайликам\"";
App::$strings["Adds a smileybutton to the jot editor"] = "Добавлять кнопку со смайликами в редактор Jot";
App::$strings["Hide the button and show the smilies directly."] = "Скрыть кнопку и сразу показывать смайлики."; App::$strings["Hide the button and show the smilies directly."] = "Скрыть кнопку и сразу показывать смайлики.";
App::$strings["Smileybutton Settings"] = "Настройки кнопки смайликов"; App::$strings["Smileybutton Settings"] = "Настройки кнопки со смайликами";
App::$strings["Friendica Photo Album Import"] = "Импортировать альбом фотографий Friendica"; App::$strings["Friendica Photo Album Import"] = "Импортировать альбом фотографий Friendica";
App::$strings["This will import all your Friendica photo albums to this Red channel."] = "Это позволит импортировать все ваши альбомы фотографий Friendica в этот канал."; App::$strings["This will import all your Friendica photo albums to this Red channel."] = "Это позволит импортировать все ваши альбомы фотографий Friendica в этот канал.";
App::$strings["Friendica Server base URL"] = "Базовый URL сервера Friendica"; App::$strings["Friendica Server base URL"] = "Базовый URL сервера Friendica";
@ -3352,8 +3401,12 @@ App::$strings["If enabled, members will automatically login to an ejabberd serve
App::$strings["New registration"] = "Новая регистрация"; App::$strings["New registration"] = "Новая регистрация";
App::$strings["Message sent to %s. New account registration: %s"] = "Сообщение отправлено в %s. Регистрация нового аккаунта: %s"; App::$strings["Message sent to %s. New account registration: %s"] = "Сообщение отправлено в %s. Регистрация нового аккаунта: %s";
App::$strings["Send your identity to all websites"] = "Отправить ваши данные на все веб-сайты"; App::$strings["Send your identity to all websites"] = "Отправить ваши данные на все веб-сайты";
App::$strings["Sendzid App"] = "Приложение \"Отправить ZID\"";
App::$strings["Send ZID"] = "Отправить ZID"; App::$strings["Send ZID"] = "Отправить ZID";
App::$strings["Who likes me?"] = "Кому я нравлюсь?"; App::$strings["Who likes me?"] = "Кому я нравлюсь?";
App::$strings["Max queueworker threads"] = "Макс. количество обработчиков очереди";
App::$strings["Assume workers dead after ___ seconds"] = "Считать обработчики неактивными через секунд";
App::$strings["Queueworker Settings"] = "Настройки обработчика очереди";
App::$strings["lonely"] = "одинокий"; App::$strings["lonely"] = "одинокий";
App::$strings["drunk"] = "пьяный"; App::$strings["drunk"] = "пьяный";
App::$strings["horny"] = "возбуждённый"; App::$strings["horny"] = "возбуждённый";
@ -3376,13 +3429,9 @@ App::$strings["victorious"] = "победивший";
App::$strings["defeated"] = "проигравший"; App::$strings["defeated"] = "проигравший";
App::$strings["envious"] = "завидует"; App::$strings["envious"] = "завидует";
App::$strings["jealous"] = "ревнует"; App::$strings["jealous"] = "ревнует";
App::$strings["Who viewed my channel/profile"] = "Кто смотрел мой канал / профиль";
App::$strings["Recent Channel/Profile Viewers"] = "Последние просмотры канала / профиля"; App::$strings["Recent Channel/Profile Viewers"] = "Последние просмотры канала / профиля";
App::$strings["This plugin/addon has not been configured."] = "Это расширение не было настроено.";
App::$strings["Please visit the Visage settings on %s"] = "Пожалуйста, посетите настройки Visage на %s";
App::$strings["your feature settings page"] = "страница ваших установочных параметров";
App::$strings["No entries."] = "Нет записей."; App::$strings["No entries."] = "Нет записей.";
App::$strings["Enable Visage Visitor Logging"] = "Включить журналирование посетителей Visage";
App::$strings["Visage Settings"] = "Настройки Visage";
App::$strings["Channels to auto connect"] = "Каналы для автоматического подключения"; App::$strings["Channels to auto connect"] = "Каналы для автоматического подключения";
App::$strings["Comma separated list"] = "Список, разделённый запятыми"; App::$strings["Comma separated list"] = "Список, разделённый запятыми";
App::$strings["Popular Channels"] = "Популярные каналы"; App::$strings["Popular Channels"] = "Популярные каналы";
@ -3399,23 +3448,25 @@ App::$strings["Chord name: example: Em7"] = "Наименование аккор
App::$strings["Show for left handed stringing"] = "Показывать струны для левшей"; App::$strings["Show for left handed stringing"] = "Показывать струны для левшей";
App::$strings["Quick Reference"] = "Быстрая ссылка"; App::$strings["Quick Reference"] = "Быстрая ссылка";
App::$strings["Post to Libertree"] = "Опубликовать в Libertree"; App::$strings["Post to Libertree"] = "Опубликовать в Libertree";
App::$strings["Enable Libertree Post Plugin"] = "Включить плагин публикаций Libertree"; App::$strings["Libertree Crosspost Connector Settings saved."] = "Настройки пересылки публикаций Libertree сохранены.";
App::$strings["Libertree Crosspost Connector App"] = "Приложение \"Пересылка публикаций Libertree\"";
App::$strings["Relay public posts to Libertree"] = "Пересылает общедоступные публикации в Libertree";
App::$strings["Libertree API token"] = "Токен Libertree API"; App::$strings["Libertree API token"] = "Токен Libertree API";
App::$strings["Libertree site URL"] = "URL сайта Libertree"; App::$strings["Libertree site URL"] = "URL сайта Libertree";
App::$strings["Post to Libertree by default"] = "Публиковать в Libertree по умолчанию"; App::$strings["Post to Libertree by default"] = "Публиковать в Libertree по умолчанию";
App::$strings["Libertree Post Settings"] = "Настройки публикаций в Libertree"; App::$strings["Libertree Crosspost Connector"] = "Пересылка публикаций Libertree";
App::$strings["Libertree Settings saved."] = "Настройки Libertree сохранены.";
App::$strings["Channel is required."] = "Необходим канал."; App::$strings["Channel is required."] = "Необходим канал.";
App::$strings["Hubzilla Crosspost Connector Settings saved."] = ""; App::$strings["Hubzilla Crosspost Connector Settings saved."] = "Настройки пересылки публикаций Hubzilla сохранены.";
App::$strings["Relay public postings to another Hubzilla channel"] = "Ретранслировать общедоступные публикации в другой канал Hubzilla"; App::$strings["Relay public postings to another Hubzilla channel"] = "Пересылает общедоступные публикации в другой канал Hubzilla";
App::$strings["Send public postings to Hubzilla channel by default"] = "Отправлять общедоступные публикации в канал Hubzilla по умолчанию"; App::$strings["Send public postings to Hubzilla channel by default"] = "Отправлять общедоступные публикации в канал Hubzilla по умолчанию";
App::$strings["Hubzilla API Path"] = "Путь к Hubzilla API"; App::$strings["Hubzilla API Path"] = "Путь к Hubzilla API";
App::$strings["Hubzilla login name"] = "Имя входа Hubzilla"; App::$strings["Hubzilla login name"] = "Имя входа Hubzilla";
App::$strings["Hubzilla channel name"] = "Название канала Hubzilla"; App::$strings["Hubzilla channel name"] = "Название канала Hubzilla";
App::$strings["Hubzilla Crosspost Connector"] = ""; App::$strings["Hubzilla Crosspost Connector"] = "Пересылка публикаций Hubzilla";
App::$strings["Post to Hubzilla"] = ""; App::$strings["Post to Hubzilla"] = "Опубликовать в Hubzilla";
App::$strings["ActivityPub Protocol Settings updated."] = "Настройки протокола ActivityPub обновлены."; App::$strings["ActivityPub Protocol Settings updated."] = "Настройки протокола ActivityPub обновлены.";
App::$strings["The activitypub protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "Протокол ActivityPub не поддерживает независимость от расположения. Ваши контакты установленные в этой сети могут быть недоступны из альтернативных мест размещения канала."; App::$strings["The activitypub protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "Протокол ActivityPub не поддерживает независимость от расположения. Ваши контакты установленные в этой сети могут быть недоступны из альтернативных мест размещения канала.";
App::$strings["Activitypub Protocol App"] = "Приложение \"Протокол ActivityPub\"";
App::$strings["Deliver to ActivityPub recipients in privacy groups"] = "Доставить получателям ActivityPub в группах безопасности"; App::$strings["Deliver to ActivityPub recipients in privacy groups"] = "Доставить получателям ActivityPub в группах безопасности";
App::$strings["May result in a large number of mentions and expose all the members of your privacy group"] = "Может привести к большому количеству упоминаний и раскрытию участников группы безопасности"; App::$strings["May result in a large number of mentions and expose all the members of your privacy group"] = "Может привести к большому количеству упоминаний и раскрытию участников группы безопасности";
App::$strings["Send multi-media HTML articles"] = "Отправить HTML статьи с мультимедиа"; App::$strings["Send multi-media HTML articles"] = "Отправить HTML статьи с мультимедиа";
@ -3424,13 +3475,16 @@ App::$strings["Activitypub Protocol"] = "Протокол ActivityPub";
App::$strings["No username found in import file."] = "Имя пользователя не найдено в файле для импорта."; App::$strings["No username found in import file."] = "Имя пользователя не найдено в файле для импорта.";
App::$strings["Diaspora Protocol Settings updated."] = "Настройки протокола Diaspora обновлены."; App::$strings["Diaspora Protocol Settings updated."] = "Настройки протокола Diaspora обновлены.";
App::$strings["The diaspora protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "Протокол Diaspora не поддерживает независимость от расположения. Ваши контакты установленные в этой сети могут быть недоступны из альтернативных мест размещения канала."; App::$strings["The diaspora protocol does not support location independence. Connections you make within that network may be unreachable from alternate channel locations."] = "Протокол Diaspora не поддерживает независимость от расположения. Ваши контакты установленные в этой сети могут быть недоступны из альтернативных мест размещения канала.";
App::$strings["Diaspora Protocol App"] = "Приложение \"Протокол Diaspora\"";
App::$strings["Allow any Diaspora member to comment on your public posts"] = "Разрешить любому участнику Diaspora комментировать ваши общедоступные публикации"; App::$strings["Allow any Diaspora member to comment on your public posts"] = "Разрешить любому участнику Diaspora комментировать ваши общедоступные публикации";
App::$strings["Prevent your hashtags from being redirected to other sites"] = "Предотвратить перенаправление тегов на другие сайты"; App::$strings["Prevent your hashtags from being redirected to other sites"] = "Предотвратить перенаправление тегов на другие сайты";
App::$strings["Sign and forward posts and comments with no existing Diaspora signature"] = "Подписывать и отправлять публикации и комментарии с несуществующей подписью Diaspora"; App::$strings["Sign and forward posts and comments with no existing Diaspora signature"] = "Подписывать и отправлять публикации и комментарии с несуществующей подписью Diaspora";
App::$strings["Followed hashtags (comma separated, do not include the #)"] = "Отслеживаемые теги (через запятую, исключая #)"; App::$strings["Followed hashtags (comma separated, do not include the #)"] = "Отслеживаемые теги (через запятую, исключая #)";
App::$strings["Diaspora Protocol"] = "Протокол Diaspora"; App::$strings["Diaspora Protocol"] = "Протокол Diaspora";
App::$strings["%1\$s dislikes %2\$s's %3\$s"] = "%1\$s не нравится %2\$s's %3\$s";
App::$strings["Post to WordPress"] = "Опубликовать в WordPress"; App::$strings["Post to WordPress"] = "Опубликовать в WordPress";
App::$strings["Wordpress Settings saved."] = "Настройки WordPress сохранены."; App::$strings["Wordpress Settings saved."] = "Настройки WordPress сохранены.";
App::$strings["Wordpress Post App"] = "Приложение \"Публикация в Wordpress\"";
App::$strings["Post to WordPress or anything else which uses the wordpress XMLRPC API"] = "Опубликовать в WordPress или в чём-то ещё, поддерживающем wordpress XMLRPC API"; App::$strings["Post to WordPress or anything else which uses the wordpress XMLRPC API"] = "Опубликовать в WordPress или в чём-то ещё, поддерживающем wordpress XMLRPC API";
App::$strings["WordPress username"] = "Имя пользователя WordPress"; App::$strings["WordPress username"] = "Имя пользователя WordPress";
App::$strings["WordPress password"] = "Пароль WordPress"; App::$strings["WordPress password"] = "Пароль WordPress";
@ -3444,15 +3498,17 @@ App::$strings["Wordpress Post"] = "Публикация в WordPress";
App::$strings["Possible adult content"] = "Возможно содержимое для взрослых"; App::$strings["Possible adult content"] = "Возможно содержимое для взрослых";
App::$strings["%s - view"] = "%s - просмотр"; App::$strings["%s - view"] = "%s - просмотр";
App::$strings["NSFW Settings saved."] = "Настройки NSFW сохранены."; App::$strings["NSFW Settings saved."] = "Настройки NSFW сохранены.";
App::$strings["NSFW App"] = "Приложение NSFW";
App::$strings["Collapse content that contains predefined words"] = "Свернуть содержимое, содержащее предопределенные слова"; App::$strings["Collapse content that contains predefined words"] = "Свернуть содержимое, содержащее предопределенные слова";
App::$strings["This app looks in posts for the words/text you specify below, and collapses any content containing those keywords so it is not displayed at inappropriate times, such as sexual innuendo that may be improper in a work setting. It is polite and recommended to tag any content containing nudity with #NSFW. This filter can also match any other word/text you specify, and can thereby be used as a general purpose content filter."] = "Это приложение просматривает публикации для слов / текста, которые вы указываете ниже, и сворачивает любой контент, содержащий эти ключевые слова, поэтому он не отображается в неподходящее время, например, сексуальные инсинуации, которые могут быть неправильными в настройке работы. Например, мы рекомендуем отмечать любой контент, содержащий наготу, тегом #NSFW. Этот фильтр также способен реагировать на любое другое указанное вами слово / текст и может использоваться в качестве фильтра содержимого общего назначения."; App::$strings["This app looks in posts for the words/text you specify below, and collapses any content containing those keywords so it is not displayed at inappropriate times, such as sexual innuendo that may be improper in a work setting. It is polite and recommended to tag any content containing nudity with #NSFW. This filter can also match any other word/text you specify, and can thereby be used as a general purpose content filter."] = "Это приложение просматривает публикации для слов / текста, которые вы указываете ниже, и сворачивает любой контент, содержащий эти ключевые слова, поэтому он не отображается в неподходящее время, например, сексуальные инсинуации, которые могут быть неправильными в настройке работы. Например, мы рекомендуем отмечать любой контент, содержащий наготу, тегом #NSFW. Этот фильтр также способен реагировать на любое другое указанное вами слово / текст и может использоваться в качестве фильтра содержимого общего назначения.";
App::$strings["Comma separated list of keywords to hide"] = "Список ключевых слов для скрытия, через запятую"; App::$strings["Comma separated list of keywords to hide"] = "Список ключевых слов для скрытия, через запятую";
App::$strings["Word, /regular-expression/, lang=xx, lang!=xx"] = "слово, /регулярное_выражение/, lang=xx, lang!=xx"; App::$strings["Word, /regular-expression/, lang=xx, lang!=xx"] = "слово, /регулярное_выражение/, lang=xx, lang!=xx";
App::$strings["NSFW"] = ""; App::$strings["NSFW"] = "";
App::$strings["Skeleton App"] = "Приложение \"Скелет\"";
App::$strings["A skeleton for addons, you can copy/paste"] = "Скелет для приложений. Вы можете использовать copy/paste";
App::$strings["Some setting"] = "Некоторые настройки"; App::$strings["Some setting"] = "Некоторые настройки";
App::$strings["A setting"] = "Настройка"; App::$strings["A setting"] = "Настройка";
App::$strings["Skeleton Settings"] = "Настройки скелета"; App::$strings["Skeleton Settings"] = "Настройки скелета";
App::$strings["__ctx:opensearch__ Search %1\$s (%2\$s)"] = "Искать %1\$s (%2\$s)"; App::$strings["__ctx:opensearch__ Search %1\$s (%2\$s)"] = "Искать %1\$s (%2\$s)";
App::$strings["__ctx:opensearch__ \$Projectname"] = ""; App::$strings["__ctx:opensearch__ \$Projectname"] = "";
App::$strings["Search \$Projectname"] = "Поиск \$Projectname"; App::$strings["Search \$Projectname"] = "Поиск \$Projectname";
App::$strings["Cover Photo"] = "";

View File

@ -146,6 +146,9 @@
{{if $item.share}} {{if $item.share}}
<a class="dropdown-item" href="#" onclick="jotShare({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-retweet" title="{{$item.share.0}}"></i>{{$item.share.0}}</a> <a class="dropdown-item" href="#" onclick="jotShare({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-retweet" title="{{$item.share.0}}"></i>{{$item.share.0}}</a>
{{/if}} {{/if}}
{{if $item.embed}}
<a class="dropdown-item" href="#" onclick="jotEmbed({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-share" title="{{$item.embed.0}}"></i>{{$item.embed.0}}</a>
{{/if}}
{{if $item.plink}} {{if $item.plink}}
<a class="dropdown-item" href="{{$item.plink.href}}" title="{{$item.plink.title}}" class="u-url"><i class="generic-icons-nav fa fa-fw fa-external-link"></i>{{$item.plink.title}}</a> <a class="dropdown-item" href="{{$item.plink.href}}" title="{{$item.plink.title}}" class="u-url"><i class="generic-icons-nav fa fa-fw fa-external-link"></i>{{$item.plink.title}}</a>
{{/if}} {{/if}}

View File

@ -139,6 +139,9 @@
{{if $item.share}} {{if $item.share}}
<a class="dropdown-item" href="#" onclick="jotShare({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-retweet" title="{{$item.share.0}}"></i>{{$item.share.0}}</a> <a class="dropdown-item" href="#" onclick="jotShare({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-retweet" title="{{$item.share.0}}"></i>{{$item.share.0}}</a>
{{/if}} {{/if}}
{{if $item.embed}}
<a class="dropdown-item" href="#" onclick="jotEmbed({{$item.id}},{{$item.item_type}}); return false"><i class="generic-icons-nav fa fa-fw fa-share" title="{{$item.embed.0}}"></i>{{$item.embed.0}}</a>
{{/if}}
{{if $item.plink}} {{if $item.plink}}
<a class="dropdown-item" href="{{$item.plink.href}}" title="{{$item.plink.title}}" class="u-url"><i class="generic-icons-nav fa fa-fw fa-external-link"></i>{{$item.plink.title}}</a> <a class="dropdown-item" href="{{$item.plink.href}}" title="{{$item.plink.title}}" class="u-url"><i class="generic-icons-nav fa fa-fw fa-external-link"></i>{{$item.plink.title}}</a>
{{/if}} {{/if}}

View File

@ -13,6 +13,7 @@
<input type='hidden' name='form_security_token' value='{{$form_security_token_edit}}'> <input type='hidden' name='form_security_token' value='{{$form_security_token_edit}}'>
{{include file="field_input.tpl" field=$gname}} {{include file="field_input.tpl" field=$gname}}
{{include file="field_checkbox.tpl" field=$public}} {{include file="field_checkbox.tpl" field=$public}}
{{$pgrp_extras}}
<a href="group/drop/{{$gid}}?t={{$form_security_token_drop}}" onclick="return confirmDelete();" class="btn btn-sm btn-danger"> <a href="group/drop/{{$gid}}?t={{$form_security_token_drop}}" onclick="return confirmDelete();" class="btn btn-sm btn-danger">
{{$delete}} {{$delete}}
</a> </a>

View File

@ -198,16 +198,24 @@ var activeCommentText = '';
}) })
} }
function jotShare(id,post_type) { function jotShare(id,post_type) {
$('#like-rotator-' + id).show();
$.get('{{$baseurl}}/share/' + id, function(data) {
$('#like-rotator-' + id).hide();
updateInit();
});
}
function jotEmbed(id,post_type) {
if(post_type == 6) { if(post_type == 6) {
window.location.href = 'rpost?f=&post_id='+id; window.location.href = 'rpost?f=&post_id='+id;
} }
else { else {
if ($('#jot-popup').length != 0) $('#jot-popup').show(); if ($('#jot-popup').length != 0) $('#jot-popup').show();
$('#like-rotator-' + id).show(); $('#like-rotator-' + id).show();
$.get('{{$baseurl}}/share/' + id, function(data) { $.get('{{$baseurl}}/embed/' + id, function(data) {
if (!editor) $("#profile-jot-text").val(""); if (!editor) $("#profile-jot-text").val("");
initEditor(function(){ initEditor(function(){
addeditortext(data); addeditortext(data);

View File

@ -137,8 +137,11 @@
<i id="profile-nocomment" class="fa fa-comments jot-icons"></i> <i id="profile-nocomment" class="fa fa-comments jot-icons"></i>
</button> </button>
{{/if}} {{/if}}
{{if $custommoretoolsbuttons}}
{{$custommoretoolsbuttons}}
{{/if}}
</div> </div>
{{if $writefiles || $weblink || $setloc || $clearloc || $feature_expire || $feature_encrypt || $feature_voting}} {{if $writefiles || $weblink || $setloc || $clearloc || $feature_expire || $feature_encrypt || $feature_voting || $custommoretoolsdropdown}}
<div class="btn-group d-lg-none"> <div class="btn-group d-lg-none">
<button type="button" id="more-tools" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false"> <button type="button" id="more-tools" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
<i id="more-tools-icon" class="fa fa-cog jot-icons"></i> <i id="more-tools-icon" class="fa fa-cog jot-icons"></i>
@ -176,6 +179,8 @@
{{if $feature_nocomment}} {{if $feature_nocomment}}
<a class="dropdown-item" href="#" onclick="toggleNoComment(); return false;"><i id="profile-nocomment-sub" class="fa fa-comments"></i>&nbsp;{{$nocommenttitlesub}}</a> <a class="dropdown-item" href="#" onclick="toggleNoComment(); return false;"><i id="profile-nocomment-sub" class="fa fa-comments"></i>&nbsp;{{$nocommenttitlesub}}</a>
{{/if}} {{/if}}
<hr />
{{$custommoretoolsdropdown}}
</div> </div>
</div> </div>
{{/if}} {{/if}}
@ -186,6 +191,11 @@
</div> </div>
</div> </div>
<div id="profile-jot-submit-right" class="btn-group float-right"> <div id="profile-jot-submit-right" class="btn-group float-right">
{{foreach $customsubmitright as $csr}}
<button class="btn btn-outline-secondary btn-sm" {{$csr.buttonparams}} title="{{$csr.preview}}">
{{$csr.buttoncontent}}
</button>
{{/foreach}}
{{if $preview}} {{if $preview}}
<button class="btn btn-outline-secondary btn-sm" onclick="preview_post();return false;" title="{{$preview}}"> <button class="btn btn-outline-secondary btn-sm" onclick="preview_post();return false;" title="{{$preview}}">
<i class="fa fa-eye jot-icons" ></i> <i class="fa fa-eye jot-icons" ></i>

View File

@ -13,4 +13,3 @@
</div> </div>
{{/if}} {{/if}}
</div> </div>