Merge remote-tracking branch 'upstream/dev' into dev
This commit is contained in:
commit
c5bb074573
@ -1,36 +1,49 @@
|
||||
# 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
|
||||
cache:
|
||||
paths:
|
||||
- vendor/
|
||||
- .cache/
|
||||
|
||||
|
||||
# global variables for all jobs, if no job specific 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_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:
|
||||
# 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
|
||||
- pecl install xdebug
|
||||
- docker-php-ext-enable xdebug
|
||||
# Install and run Composer
|
||||
# Install composer
|
||||
- curl -sS https://getcomposer.org/installer | php
|
||||
# Install dev libraries from composer
|
||||
- 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:php:mysql:
|
||||
# test PHP7 with MySQL 5.7
|
||||
php7.2_mysql 1/2:
|
||||
stage: test
|
||||
services:
|
||||
- mysql:5.7
|
||||
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"
|
||||
- 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:
|
||||
- postgres:latest
|
||||
variables:
|
||||
POSTGRES_DB: ci-db
|
||||
POSTGRES_USER: ci-user
|
||||
POSTGRES_PASSWORD: ci-pass
|
||||
script:
|
||||
- export PGPASSWORD=$POSTGRES_PASSWORD
|
||||
- psql --version
|
||||
@ -67,3 +104,23 @@ test:php:postgres:
|
||||
name: "$CI_COMMIT_REF_SLUG-$CI_JOB_NAME"
|
||||
paths:
|
||||
- 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
|
||||
|
10
CHANGELOG
10
CHANGELOG
@ -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)
|
||||
- Fix issue with linkinfo
|
||||
- Fix cURL with HTTP/2
|
||||
|
27
Zotlabs/Daemon/Cache_embeds.php
Normal file
27
Zotlabs/Daemon/Cache_embeds.php
Normal 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']);
|
||||
}
|
||||
}
|
@ -434,6 +434,8 @@ class Notifier {
|
||||
$x['body'] = 'private';
|
||||
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);
|
||||
if(! $recipients) {
|
||||
logger('no recipients');
|
||||
@ -677,6 +679,17 @@ class Notifier {
|
||||
}
|
||||
|
||||
$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']);
|
||||
}
|
||||
else {
|
||||
|
@ -35,6 +35,9 @@ class Activity {
|
||||
if($x['type'] === ACTIVITY_OBJ_EVENT) {
|
||||
return self::fetch_event($x);
|
||||
}
|
||||
if($x['type'] === ACTIVITY_OBJ_PHOTO) {
|
||||
return self::fetch_image($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) {
|
||||
|
||||
// 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']));
|
||||
|
||||
if($i['title'])
|
||||
$ret['title'] = bbcode($i['title']);
|
||||
$ret['name'] = $i['title'];
|
||||
|
||||
$ret['published'] = datetime_convert('UTC','UTC',$i['created'],ATOM_TIME);
|
||||
if($i['created'] !== $i['edited'])
|
||||
@ -385,10 +411,17 @@ class Activity {
|
||||
$ret['type'] = 'Tombstone';
|
||||
$ret['formerType'] = self::activity_obj_mapper($i['obj_type']);
|
||||
$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;
|
||||
}
|
||||
|
||||
$ret['type'] = self::activity_mapper($i['verb']);
|
||||
|
||||
|
||||
$ret['id'] = ((strpos($i['mid'],'http') === 0) ? $i['mid'] : z_root() . '/activity/' . urlencode($i['mid']));
|
||||
|
||||
if($i['title'])
|
||||
@ -460,6 +493,10 @@ class Activity {
|
||||
if(! is_array($i['obj'])) {
|
||||
$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']);
|
||||
if($obj)
|
||||
$ret['object'] = $obj;
|
||||
@ -1374,6 +1411,9 @@ class Activity {
|
||||
$s['owner_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['parent_mid'] = $act->parent_id;
|
||||
|
||||
@ -1446,7 +1486,8 @@ class Activity {
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
@ -1455,7 +1496,6 @@ class Activity {
|
||||
$s['obj_type'] = ACTIVITY_OBJ_COMMENT;
|
||||
}
|
||||
|
||||
|
||||
if($act->obj['type'] === 'Event') {
|
||||
$s['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;
|
||||
|
||||
|
@ -1,32 +1,34 @@
|
||||
<?php /** @file */
|
||||
<?php
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* Apps
|
||||
*
|
||||
*/
|
||||
|
||||
require_once('include/plugin.php');
|
||||
require_once('include/channel.php');
|
||||
|
||||
|
||||
/**
|
||||
* @brief Apps class.
|
||||
*
|
||||
*/
|
||||
class Apps {
|
||||
|
||||
static public $available_apps = null;
|
||||
static public $installed_apps = null;
|
||||
|
||||
static public $base_apps = null;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param boolean $translate (optional) default true
|
||||
* @return array
|
||||
*/
|
||||
static public function get_system_apps($translate = true) {
|
||||
$ret = [];
|
||||
|
||||
$ret = array();
|
||||
if(is_dir('apps'))
|
||||
$files = glob('apps/*.apd');
|
||||
else
|
||||
$files = glob('app/*.apd');
|
||||
|
||||
if($files) {
|
||||
foreach($files as $f) {
|
||||
$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);
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
static public function get_base_apps() {
|
||||
@ -72,7 +77,13 @@ class Apps {
|
||||
'Mail',
|
||||
'Profile Photo'
|
||||
]);
|
||||
|
||||
/**
|
||||
* @hooks get_base_apps
|
||||
* Hook to manipulate the base apps array.
|
||||
*/
|
||||
call_hooks('get_base_apps', $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
|
||||
if($id === false)
|
||||
continue;
|
||||
|
||||
if($id !== true) {
|
||||
// if we already installed this app, but it changed, preserve any categories we created
|
||||
$s = EMPTY_STR;
|
||||
@ -126,7 +138,6 @@ class Apps {
|
||||
$app['guid'] = hash('whirlpool',$app['name']);
|
||||
$app['system'] = 1;
|
||||
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
|
||||
* 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) {
|
||||
if((! is_array(self::$available_apps)) || (! count(self::$available_apps))) {
|
||||
return true;
|
||||
@ -159,14 +172,13 @@ class Apps {
|
||||
return $notfound;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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 personal app changes.
|
||||
*
|
||||
* @param array $app
|
||||
* @return boolean|int
|
||||
*/
|
||||
|
||||
|
||||
|
||||
static public function check_install_personal_app($app) {
|
||||
$installed = false;
|
||||
foreach(self::$installed_apps as $iapp) {
|
||||
@ -189,20 +201,25 @@ class Apps {
|
||||
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) {
|
||||
|
||||
$ret = array();
|
||||
$ret = [];
|
||||
$matches = [];
|
||||
|
||||
$baseurl = z_root();
|
||||
$channel = \App::get_channel();
|
||||
$address = (($channel) ? $channel['channel_address'] : '');
|
||||
//$channel = \App::get_channel();
|
||||
//$address = (($channel) ? $channel['channel_address'] : '');
|
||||
|
||||
//future expansion
|
||||
|
||||
$observer = \App::get_observer();
|
||||
|
||||
|
||||
$lines = @file($f);
|
||||
if($lines) {
|
||||
foreach($lines as $x) {
|
||||
@ -292,8 +309,10 @@ class Apps {
|
||||
if($ret) {
|
||||
if($translate)
|
||||
self::translate_system_apps($ret);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -301,6 +320,7 @@ class Apps {
|
||||
static public function translate_system_apps(&$arr) {
|
||||
$apps = array(
|
||||
'Apps' => t('Apps'),
|
||||
'Affinity Tool' => t('Affinity Tool'),
|
||||
'Articles' => t('Articles'),
|
||||
'Cards' => t('Cards'),
|
||||
'Admin' => t('Site Admin'),
|
||||
@ -371,25 +391,26 @@ class Apps {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// papp is a portable app
|
||||
|
||||
static public function app_render($papp,$mode = 'view') {
|
||||
|
||||
/**
|
||||
* modes:
|
||||
* view: normal mode for viewing an app via bbcode from a conversation or page
|
||||
* @brief
|
||||
*
|
||||
* @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
|
||||
* 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 install: like view but does not display app-bin options if they are present
|
||||
* * \b list: normal mode for viewing an app on the app page
|
||||
* no buttons are shown
|
||||
* edit: viewing the app page in editing mode provides a delete button
|
||||
* nav: render apps for app-bin
|
||||
* * \b edit: viewing the app page in editing mode provides a delete button
|
||||
* * \b nav: render apps for app-bin
|
||||
*
|
||||
* @return void|string Parsed HTML
|
||||
*/
|
||||
|
||||
static public function app_render($papp, $mode = 'view') {
|
||||
$installed = false;
|
||||
|
||||
if(! $papp)
|
||||
@ -427,7 +448,6 @@ class Apps {
|
||||
$papp['url'] = z_root() . ((strpos($papp['url'],'/') === 0) ? '' : '/') . $papp['url'];
|
||||
|
||||
|
||||
|
||||
foreach($papp as $k => $v) {
|
||||
if(strpos($v,'http') === 0 && $k != 'papp') {
|
||||
if(! (local_channel() && strpos($v,z_root()) === 0)) {
|
||||
@ -592,7 +612,13 @@ class Apps {
|
||||
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) {
|
||||
if(! $uid) {
|
||||
return false;
|
||||
@ -613,7 +639,6 @@ class Apps {
|
||||
static public function app_destroy($uid,$app) {
|
||||
|
||||
if($uid && $app['guid']) {
|
||||
|
||||
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($app['guid']),
|
||||
intval($uid)
|
||||
@ -622,7 +647,7 @@ class Apps {
|
||||
if(! intval($x[0]['app_deleted'])) {
|
||||
$x[0]['app_deleted'] = 1;
|
||||
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']),
|
||||
intval($uid)
|
||||
);
|
||||
@ -630,10 +655,15 @@ class Apps {
|
||||
intval(TERM_OBJ_APP),
|
||||
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]);
|
||||
}
|
||||
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']),
|
||||
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) {
|
||||
|
||||
// undelete a system app
|
||||
|
||||
if($uid && $app['guid']) {
|
||||
|
||||
$x = q("select * from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
dbesc($app['guid']),
|
||||
intval($uid)
|
||||
);
|
||||
if($x) {
|
||||
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']),
|
||||
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) {
|
||||
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
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) {
|
||||
|
||||
$r = q("select id from app where app_id = '%s' and app_channel = %d limit 1",
|
||||
@ -707,11 +754,17 @@ class Apps {
|
||||
'app' => $app,
|
||||
'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);
|
||||
$r = $filter_arr['installed'];
|
||||
}
|
||||
return(($r) ? true : false);
|
||||
|
||||
return(($r) ? true : false);
|
||||
}
|
||||
|
||||
|
||||
@ -727,11 +780,17 @@ class Apps {
|
||||
'app'=>$app,
|
||||
'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);
|
||||
$r = $filter_arr['installed'];
|
||||
}
|
||||
return(($r) ? true : false);
|
||||
|
||||
return(($r) ? true : false);
|
||||
}
|
||||
|
||||
static public function system_app_installed($uid,$app,$bypass_filter=false) {
|
||||
@ -746,28 +805,39 @@ class Apps {
|
||||
'app'=>$app,
|
||||
'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);
|
||||
$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 = []) {
|
||||
if($deleted)
|
||||
$sql_extra = "";
|
||||
$sql_extra = '';
|
||||
else
|
||||
$sql_extra = " and app_deleted = 0 ";
|
||||
$sql_extra = ' and app_deleted = 0 ';
|
||||
|
||||
if($cats) {
|
||||
|
||||
$cat_sql_extra = " and ( ";
|
||||
$cat_sql_extra = ' and ( ';
|
||||
|
||||
foreach($cats as $cat) {
|
||||
if(strpos($cat_sql_extra, 'term'))
|
||||
$cat_sql_extra .= "or ";
|
||||
$cat_sql_extra .= 'or ';
|
||||
|
||||
$cat_sql_extra .= "term = '" . dbesc($cat) . "' ";
|
||||
}
|
||||
@ -779,11 +849,13 @@ class Apps {
|
||||
);
|
||||
if(! $r)
|
||||
return $r;
|
||||
$sql_extra .= " and app.id in ( ";
|
||||
|
||||
$sql_extra .= ' and app.id in ( ';
|
||||
$s = '';
|
||||
foreach($r as $rr) {
|
||||
if($s)
|
||||
$s .= ',';
|
||||
|
||||
$s .= intval($rr['oid']);
|
||||
}
|
||||
$sql_extra .= $s . ') ';
|
||||
@ -794,12 +866,26 @@ class Apps {
|
||||
);
|
||||
|
||||
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);
|
||||
$r = $hookinfo['apps'];
|
||||
|
||||
for($x = 0; $x < count($r); $x++) {
|
||||
if(! $r[$x]['app_system'])
|
||||
$r[$x]['type'] = 'personal';
|
||||
|
||||
$r[$x]['term'] = q("select * from term where otype = %d and oid = %d",
|
||||
intval(TERM_OBJ_APP),
|
||||
intval($r[$x]['id'])
|
||||
@ -807,7 +893,7 @@ class Apps {
|
||||
}
|
||||
}
|
||||
|
||||
return($r);
|
||||
return $r;
|
||||
}
|
||||
|
||||
static public function app_order($uid,$apps,$menu) {
|
||||
@ -839,13 +925,14 @@ class Apps {
|
||||
$ret[] = $ap;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
static function find_app_in_array($name,$arr) {
|
||||
if(! $arr)
|
||||
return false;
|
||||
|
||||
foreach($arr as $x) {
|
||||
if($x['name'] === $name) {
|
||||
return $x;
|
||||
@ -854,8 +941,16 @@ class Apps {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param int $uid
|
||||
* @param int $guid
|
||||
* @param string $menu
|
||||
* @return void
|
||||
*/
|
||||
static function moveup($uid, $guid, $menu) {
|
||||
$syslist = array();
|
||||
$syslist = [];
|
||||
|
||||
$conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order');
|
||||
|
||||
@ -865,6 +960,7 @@ class Apps {
|
||||
$papp = self::app_encode($li);
|
||||
if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false)
|
||||
continue;
|
||||
|
||||
$syslist[] = $papp;
|
||||
}
|
||||
}
|
||||
@ -877,8 +973,6 @@ class Apps {
|
||||
if(! $syslist)
|
||||
return;
|
||||
|
||||
$newlist = [];
|
||||
|
||||
foreach($syslist as $k => $li) {
|
||||
if($li['guid'] === $guid) {
|
||||
$position = $k;
|
||||
@ -887,6 +981,7 @@ class Apps {
|
||||
}
|
||||
if(! $position)
|
||||
return;
|
||||
|
||||
$dest_position = $position - 1;
|
||||
$saved = $syslist[$dest_position];
|
||||
$syslist[$dest_position] = $syslist[$position];
|
||||
@ -898,11 +993,18 @@ class Apps {
|
||||
}
|
||||
|
||||
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) {
|
||||
$syslist = array();
|
||||
$syslist = [];
|
||||
|
||||
$conf = (($menu === 'nav_featured_app') ? 'app_order' : 'app_pin_order');
|
||||
|
||||
@ -912,6 +1014,7 @@ class Apps {
|
||||
$papp = self::app_encode($li);
|
||||
if($menu !== 'nav_pinned_app' && strpos($papp['categories'],'nav_pinned_app') !== false)
|
||||
continue;
|
||||
|
||||
$syslist[] = $papp;
|
||||
}
|
||||
}
|
||||
@ -924,8 +1027,6 @@ class Apps {
|
||||
if(! $syslist)
|
||||
return;
|
||||
|
||||
$newlist = [];
|
||||
|
||||
foreach($syslist as $k => $li) {
|
||||
if($li['guid'] === $guid) {
|
||||
$position = $k;
|
||||
@ -934,6 +1035,7 @@ class Apps {
|
||||
}
|
||||
if($position >= count($syslist) - 1)
|
||||
return;
|
||||
|
||||
$dest_position = $position + 1;
|
||||
$saved = $syslist[$dest_position];
|
||||
$syslist[$dest_position] = $syslist[$position];
|
||||
@ -945,7 +1047,6 @@ class Apps {
|
||||
}
|
||||
|
||||
set_pconfig($uid,'system',$conf,implode(',',$narr));
|
||||
|
||||
}
|
||||
|
||||
static public function app_decode($s) {
|
||||
@ -953,7 +1054,13 @@ class Apps {
|
||||
return json_decode($x,true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param int $uid
|
||||
* @param[in,out] array $arr
|
||||
* @return void
|
||||
*/
|
||||
static public function app_macros($uid, &$arr) {
|
||||
|
||||
if(! intval($uid))
|
||||
@ -965,18 +1072,14 @@ class Apps {
|
||||
|
||||
//future expansion
|
||||
|
||||
$observer = \App::get_observer();
|
||||
//$observer = \App::get_observer();
|
||||
|
||||
$arr['url'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['url']);
|
||||
$arr['photo'] = str_replace(array('$baseurl','$nick'),array($baseurl,$address),$arr['photo']);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static public function app_store($arr) {
|
||||
|
||||
//logger('app_store: ' . print_r($arr,true));
|
||||
@ -1160,13 +1263,17 @@ class Apps {
|
||||
}
|
||||
|
||||
return $ret;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param array $app
|
||||
* @param boolean $embed (optional) default false
|
||||
* @return array|string
|
||||
*/
|
||||
static public function app_encode($app, $embed = false) {
|
||||
|
||||
$ret = array();
|
||||
$ret = [];
|
||||
|
||||
$ret['type'] = 'personal';
|
||||
|
||||
@ -1226,12 +1333,12 @@ class Apps {
|
||||
foreach($app['term'] as $t) {
|
||||
if($s)
|
||||
$s .= ',';
|
||||
|
||||
$s .= $t['term'];
|
||||
}
|
||||
$ret['categories'] = $s;
|
||||
}
|
||||
|
||||
|
||||
if(! $embed)
|
||||
return $ret;
|
||||
|
||||
@ -1241,16 +1348,13 @@ class Apps {
|
||||
unset($ret['categories']);
|
||||
|
||||
$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) {
|
||||
return chunk_split(base64_encode(json_encode($papp)),72,"\n");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,11 +2,6 @@
|
||||
|
||||
namespace Zotlabs\Lib;
|
||||
|
||||
/**
|
||||
* @brief lowlevel implementation of Zot6 protocol.
|
||||
*
|
||||
*/
|
||||
|
||||
use Zotlabs\Zot6\HTTPSig;
|
||||
use Zotlabs\Access\Permissions;
|
||||
use Zotlabs\Access\PermissionLimits;
|
||||
@ -14,14 +9,17 @@ use Zotlabs\Daemon\Master;
|
||||
|
||||
require_once('include/crypto.php');
|
||||
|
||||
|
||||
/**
|
||||
* @brief Lowlevel implementation of Zot6 protocol.
|
||||
*
|
||||
*/
|
||||
class Libzot {
|
||||
|
||||
/**
|
||||
* @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
|
||||
* channel nickname and some entropy.
|
||||
* Generates a unique string for use as a zot guid using our DNS-based url,
|
||||
* the channel nickname and some entropy.
|
||||
* The entropy ensures uniqueness against re-installs where the same URL and
|
||||
* nickname are chosen.
|
||||
*
|
||||
@ -32,9 +30,8 @@ class Libzot {
|
||||
* immediate universe.
|
||||
*
|
||||
* @param string $channel_nick a unique nickname of controlling entity
|
||||
* @returns string
|
||||
* @return string
|
||||
*/
|
||||
|
||||
static function new_uid($channel_nick) {
|
||||
$rawstr = z_root() . '/' . $channel_nick . '.' . mt_rand();
|
||||
return(base64url_encode(hash('whirlpool', $rawstr, true), true));
|
||||
@ -52,8 +49,8 @@ class Libzot {
|
||||
*
|
||||
* @param string $guid
|
||||
* @param string $pubkey
|
||||
* @return string
|
||||
*/
|
||||
|
||||
static function make_xchan_hash($guid, $pubkey) {
|
||||
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.
|
||||
*
|
||||
* @param string $hash - xchan_hash
|
||||
* @returns array of hubloc (hub location structures)
|
||||
*
|
||||
* @return array of hubloc (hub location structures)
|
||||
*/
|
||||
|
||||
static function get_hublocs($hash) {
|
||||
|
||||
/* 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'
|
||||
* @param array $recipients
|
||||
* envelope recipients, array of portable_id's; empty for public posts
|
||||
* @param string msg
|
||||
* @param string $msg
|
||||
* optional message
|
||||
* @param string $encoding
|
||||
* optional encoding, default 'activitystreams'
|
||||
* @param string $remote_key
|
||||
* 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
|
||||
* @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
|
||||
*/
|
||||
|
||||
static function build_packet($channel, $type = 'activity', $recipients = null, $msg = '', $encoding = 'activitystreams', $remote_key = null, $methods = '') {
|
||||
|
||||
$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.
|
||||
*
|
||||
* @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
|
||||
* of a method which is common to both sites; or 'aes256cbc' if no matches are found.
|
||||
*/
|
||||
|
||||
static function best_algorithm($methods) {
|
||||
|
||||
$x = [
|
||||
@ -164,7 +159,6 @@ class Libzot {
|
||||
* * \e string \b methods - comma separated list of encryption methods
|
||||
* * \e string \b result - the algorithm to return
|
||||
*/
|
||||
|
||||
call_hooks('zot_best_algorithm', $x);
|
||||
|
||||
if($x['result'])
|
||||
@ -190,7 +184,7 @@ class Libzot {
|
||||
|
||||
|
||||
/**
|
||||
* @brief send a zot message
|
||||
* @brief Send a zot message.
|
||||
*
|
||||
* @see z_post_url()
|
||||
*
|
||||
@ -200,7 +194,6 @@ class Libzot {
|
||||
* @param array $crypto (required if encrypted httpsig, requires hubloc_sitekey and site_crypto elements)
|
||||
* @return array see z_post_url() for returned data format
|
||||
*/
|
||||
|
||||
static function zot($url, $data, $channel = null,$crypto = null) {
|
||||
|
||||
if($channel) {
|
||||
@ -227,7 +220,6 @@ class Libzot {
|
||||
/**
|
||||
* @brief Refreshes after permission changed or friending, etc.
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* (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
|
||||
* * otherwise \b false
|
||||
*/
|
||||
|
||||
static function refresh($them, $channel = null, $force = false) {
|
||||
|
||||
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.
|
||||
|
||||
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'])
|
||||
);
|
||||
}
|
||||
@ -317,7 +308,7 @@ class Libzot {
|
||||
|
||||
if(! $hsig_valid) {
|
||||
logger('http signature not valid: ' . print_r($hsig,true));
|
||||
return $result;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -356,7 +347,7 @@ class Libzot {
|
||||
);
|
||||
|
||||
if($r) {
|
||||
logger('4');
|
||||
|
||||
// connection exists
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param string $sender
|
||||
* @param string $site_id
|
||||
* @return null|array
|
||||
*/
|
||||
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",
|
||||
@ -548,7 +543,6 @@ logger('4');
|
||||
}
|
||||
|
||||
return $r[0];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -559,21 +553,14 @@ logger('4');
|
||||
* 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.
|
||||
*
|
||||
* @param array $arr an associative array which must contain:
|
||||
* * \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
|
||||
* @param string $id
|
||||
*
|
||||
* @return array An associative array with
|
||||
* * \b success boolean true or false
|
||||
* * \b message (optional) error string only if success is false
|
||||
* * \e boolean \b success
|
||||
* * \e string \b message (optional, unused) error string only if success is false
|
||||
*/
|
||||
|
||||
static function register_hub($id) {
|
||||
|
||||
$id_hash = false;
|
||||
$valid = false;
|
||||
$hsig_valid = false;
|
||||
|
||||
$result = [ 'success' => false ];
|
||||
@ -954,8 +941,8 @@ logger('4');
|
||||
* @param string $hub - url of site we just contacted
|
||||
* @param array $arr - output of z_post_url()
|
||||
* @param array $outq - The queue structure attached to this request
|
||||
* @return void
|
||||
*/
|
||||
|
||||
static function process_response($hub, $arr, $outq) {
|
||||
|
||||
logger('remote: ' . print_r($arr,true),LOGGER_DATA);
|
||||
@ -1083,11 +1070,6 @@ logger('4');
|
||||
*
|
||||
* @param array $arr
|
||||
* '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
|
||||
* Suitable for logging remotely, enumerating the processing results of each message/recipient combination
|
||||
@ -1095,7 +1077,6 @@ logger('4');
|
||||
* * [1] => \e string $delivery_status
|
||||
* * [2] => \e string $address
|
||||
*/
|
||||
|
||||
static function import($arr) {
|
||||
|
||||
$env = $arr;
|
||||
@ -1175,7 +1156,6 @@ logger('4');
|
||||
|
||||
$deliveries = self::public_recips($env,$AS);
|
||||
|
||||
|
||||
}
|
||||
|
||||
$deliveries = array_unique($deliveries);
|
||||
@ -1194,7 +1174,7 @@ logger('4');
|
||||
|
||||
//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'])
|
||||
);
|
||||
|
||||
@ -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'])
|
||||
);
|
||||
|
||||
@ -1218,7 +1198,7 @@ logger('4');
|
||||
if($private) {
|
||||
$arr['item_private'] = true;
|
||||
}
|
||||
// @fixme - spoofable
|
||||
/// @FIXME - spoofable
|
||||
if($AS->data['hubloc']) {
|
||||
$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) {
|
||||
if($env['encoding'] === 'zot' && array_key_exists('flags',$env) && in_array('thread_parent', $env['flags'])) {
|
||||
return true;
|
||||
@ -1295,9 +1282,9 @@ logger('4');
|
||||
* Some of these will be rejected, but this gives us a place to start.
|
||||
*
|
||||
* @param array $msg
|
||||
* @return NULL|array
|
||||
* @param object $act
|
||||
* @return array
|
||||
*/
|
||||
|
||||
static function public_recips($msg, $act) {
|
||||
|
||||
require_once('include/channel.php');
|
||||
@ -1497,11 +1484,34 @@ logger('4');
|
||||
intval($channel['channel_id'])
|
||||
);
|
||||
if ($parent) {
|
||||
$allowed = can_comment_on_post($d,$parent[0]);
|
||||
$allowed = can_comment_on_post($sender,$parent[0]);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$allowed = 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']) {
|
||||
|
||||
logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"');
|
||||
|
||||
// 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,
|
||||
// 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.
|
||||
// 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
|
||||
// cause a conversation fetch loop. We can detect these packets since they are
|
||||
// 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).
|
||||
// cause a conversation fetch loop.
|
||||
// We'll also check the send_stream permission - because if it isn't allowed,
|
||||
// the top level post is unlikely to be imported and
|
||||
// this is just an exercise in futility.
|
||||
@ -1601,7 +1610,7 @@ logger('checking source: "' . $arr['mid'] . '" != "' . $arr['parent_mid'] . '"')
|
||||
$arr['route'] = $last_prior_route;
|
||||
}
|
||||
}
|
||||
logger('hey');
|
||||
|
||||
$ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'",
|
||||
intval($channel['channel_id']),
|
||||
dbesc($arr['owner_xchan'])
|
||||
@ -1779,14 +1788,14 @@ logger('hey');
|
||||
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'])
|
||||
);
|
||||
|
||||
if(! $r) {
|
||||
$y = import_author_xchan([ 'url' => $AS->actor['id'] ]);
|
||||
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'])
|
||||
);
|
||||
}
|
||||
@ -1809,7 +1818,7 @@ logger('hey');
|
||||
$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'])
|
||||
);
|
||||
|
||||
@ -1820,7 +1829,8 @@ logger('hey');
|
||||
$arr['owner_xchan'] = $a['signature']['signer'];
|
||||
}
|
||||
|
||||
// @fixme - spoofable
|
||||
|
||||
/// @FIXME - spoofable
|
||||
if($AS->data['hubloc']) {
|
||||
$arr['item_verified'] = true;
|
||||
}
|
||||
@ -1851,8 +1861,8 @@ logger('hey');
|
||||
* * \e int \b obj_type
|
||||
* * \e int \b mid
|
||||
* @param int $uid
|
||||
* @return void
|
||||
*/
|
||||
|
||||
static function remove_community_tag($sender, $arr, $uid) {
|
||||
|
||||
if(! (activity_match($arr['verb'], ACTIVITY_TAG) && ($arr['obj_type'] == ACTIVITY_OBJ_TAGTERM)))
|
||||
@ -1923,8 +1933,8 @@ logger('hey');
|
||||
* @param array $orig
|
||||
* @param int $uid
|
||||
* @param boolean $tag_delivery
|
||||
* @return void|array
|
||||
*/
|
||||
|
||||
static function update_imported_item($sender, $item, $orig, $uid, $tag_delivery) {
|
||||
|
||||
// If this is a comment being updated, remove any privacy information
|
||||
@ -2143,12 +2153,13 @@ logger('hey');
|
||||
* @brief Processes delivery of profile.
|
||||
*
|
||||
* @see import_directory_profile()
|
||||
*
|
||||
* @param array $sender an associative array
|
||||
* * \e string \b hash a xchan_hash
|
||||
* @param array $arr
|
||||
* @param array $deliveries (unused)
|
||||
* @return void
|
||||
*/
|
||||
|
||||
static function process_profile_delivery($sender, $arr, $deliveries) {
|
||||
|
||||
logger('process_profile_delivery', LOGGER_DEBUG);
|
||||
@ -2169,6 +2180,7 @@ logger('hey');
|
||||
* * \e string \b hash a xchan_hash
|
||||
* @param array $arr
|
||||
* @param array $deliveries (unused) deliveries is irrelevant
|
||||
* @return void
|
||||
*/
|
||||
static function process_location_delivery($sender, $arr, $deliveries) {
|
||||
|
||||
@ -2186,7 +2198,7 @@ logger('hey');
|
||||
$x = Libsync::sync_locations($xchan,$arr,true);
|
||||
logger('results: ' . print_r($x,true), LOGGER_DEBUG);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -2210,8 +2222,8 @@ logger('hey');
|
||||
*
|
||||
* @param string $sender_hash A channel hash
|
||||
* @param array $locations
|
||||
* @return void
|
||||
*/
|
||||
|
||||
static function check_location_move($sender_hash, $locations) {
|
||||
|
||||
if(! $locations)
|
||||
@ -2253,7 +2265,6 @@ logger('hey');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @return array an array with associative arrays
|
||||
*/
|
||||
|
||||
static function encode_locations($channel) {
|
||||
$ret = [];
|
||||
|
||||
@ -2316,10 +2326,8 @@ logger('hey');
|
||||
* @brief
|
||||
*
|
||||
* @param array $arr
|
||||
* @param string $pubkey
|
||||
* @return boolean true if updated or inserted
|
||||
*/
|
||||
|
||||
static function import_site($arr) {
|
||||
|
||||
if( (! is_array($arr)) || (! $arr['url']) || (! $arr['site_sig']))
|
||||
@ -2594,7 +2602,7 @@ logger('hey');
|
||||
$feed = ((x($arr,'feed')) ? intval($arr['feed']) : 0);
|
||||
|
||||
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)
|
||||
);
|
||||
if($t) {
|
||||
@ -2834,14 +2842,20 @@ logger('hey');
|
||||
$ret['locations'] = $x;
|
||||
|
||||
$ret['site'] = self::site_info();
|
||||
|
||||
/**
|
||||
* @hooks zotinfo
|
||||
* Hook to manipulate the zotinfo array before it is returned.
|
||||
*/
|
||||
call_hooks('zotinfo', $ret);
|
||||
|
||||
return($ret);
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get siteinfo.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static function site_info() {
|
||||
|
||||
$signing_key = get_config('system','prvkey');
|
||||
@ -2925,18 +2939,16 @@ logger('hey');
|
||||
}
|
||||
|
||||
return $ret['site'];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param array $hub
|
||||
* @param string $sitekey (optional, default empty)
|
||||
* @param string $site_id (optional, default empty)
|
||||
*
|
||||
* @return string hubloc_url
|
||||
*/
|
||||
|
||||
static function update_hub_connected($hub, $site_id = '') {
|
||||
|
||||
if ($site_id) {
|
||||
@ -2995,12 +3007,21 @@ logger('hey');
|
||||
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') {
|
||||
if(! $key)
|
||||
return 'no key';
|
||||
|
||||
$sig = '';
|
||||
openssl_sign($data,$sig,$key,$alg);
|
||||
|
||||
return $alg . '.' . base64url_encode($sig);
|
||||
}
|
||||
|
||||
@ -3026,11 +3047,14 @@ logger('hey');
|
||||
return(($verify > 0) ? true : false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
static function is_zot_request() {
|
||||
|
||||
$x = getBestSupportedMimeType([ 'application/x-zot+json' ]);
|
||||
|
||||
return(($x) ? true : false);
|
||||
}
|
||||
|
||||
|
@ -112,6 +112,8 @@ class PConfig {
|
||||
* The configuration key to set
|
||||
* @param string $value
|
||||
* The value to store
|
||||
* @param string $updated (optional)
|
||||
* The datetime to store
|
||||
* @return mixed Stored $value or false
|
||||
*/
|
||||
static public function Set($uid, $family, $key, $value, $updated = NULL) {
|
||||
@ -239,7 +241,9 @@ class PConfig {
|
||||
* The category of the configuration value
|
||||
* @param string $key
|
||||
* 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) {
|
||||
|
||||
@ -271,22 +275,13 @@ class PConfig {
|
||||
dbesc($key)
|
||||
);
|
||||
|
||||
// Synchronize delete with clones.
|
||||
|
||||
if ($family != 'hz_delpconfig') {
|
||||
$hash = hash('sha256',$family.':'.$key);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,7 @@ class Share {
|
||||
if(! $this->item)
|
||||
return $obj;
|
||||
|
||||
$obj['asld'] = $this->item['mid'];
|
||||
$obj['type'] = $this->item['obj_type'];
|
||||
$obj['id'] = $this->item['mid'];
|
||||
$obj['content'] = $this->item['body'];
|
||||
|
@ -76,7 +76,7 @@ class ThreadItem {
|
||||
* _ 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();
|
||||
|
||||
@ -101,6 +101,7 @@ class ThreadItem {
|
||||
|| strlen($item['deny_cid']) || strlen($item['deny_gid']))))
|
||||
? t('Private Message')
|
||||
: 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
|
||||
@ -115,6 +116,19 @@ class ThreadItem {
|
||||
$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();
|
||||
|
||||
switch($item['item_type']) {
|
||||
@ -293,8 +307,15 @@ class ThreadItem {
|
||||
$dislike = array( t("I don't like this \x28toggle\x29"), t("dislike"));
|
||||
}
|
||||
|
||||
if ($shareable)
|
||||
$share = array( t('Share This'), t('share'));
|
||||
if ($shareable) {
|
||||
// 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 = '';
|
||||
|
||||
@ -408,12 +429,13 @@ class ThreadItem {
|
||||
'like' => $like,
|
||||
'dislike' => ((feature_enabled($conv->get_profile_owner(),'dislike')) ? $dislike : ''),
|
||||
'share' => $share,
|
||||
'embed' => $embed,
|
||||
'rawmid' => $item['mid'],
|
||||
'plink' => get_plink($item),
|
||||
'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 : ''),
|
||||
'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') : ''),
|
||||
'addtocal' => (($has_event) ? t('Add to Calendar') : ''),
|
||||
'drop' => $drop,
|
||||
@ -470,7 +492,7 @@ class ThreadItem {
|
||||
|
||||
if(($this->get_display_mode() === 'normal') && ($nb_children > 0)) {
|
||||
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
|
||||
if(($nb_children > $visible_comments) || ($thread_level > 1)) {
|
||||
|
53
Zotlabs/Lib/ThreadListener.php
Normal file
53
Zotlabs/Lib/ThreadListener.php
Normal 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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
@ -86,7 +86,7 @@ class Admin extends \Zotlabs\Web\Controller {
|
||||
|
||||
// list total user accounts, expirations etc.
|
||||
$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(),
|
||||
dbesc(NULL_DATE),
|
||||
|
@ -375,6 +375,9 @@ class Addons {
|
||||
if($files) {
|
||||
foreach($files as $file) {
|
||||
if (is_dir($file)){
|
||||
if($file == 'addon/addon_common/')
|
||||
continue;
|
||||
|
||||
list($tmp, $id) = array_map('trim', explode('/', $file));
|
||||
$info = get_plugin_info($id);
|
||||
$enabled = in_array($id,\App::$plugins);
|
||||
|
@ -119,7 +119,7 @@ class Site {
|
||||
del_config('system', 'admininfo');
|
||||
} else {
|
||||
require_once('include/text.php');
|
||||
linkify_tags($a, $admininfo, local_channel());
|
||||
linkify_tags($admininfo, local_channel());
|
||||
set_config('system', 'admininfo', $admininfo);
|
||||
}
|
||||
set_config('system','siteinfo',$siteinfo);
|
||||
|
94
Zotlabs/Module/Affinity.php
Normal file
94
Zotlabs/Module/Affinity.php
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -10,9 +10,13 @@ require_once('include/channel.php');
|
||||
require_once('include/conversation.php');
|
||||
require_once('include/acl_selectors.php');
|
||||
|
||||
/**
|
||||
* @brief Provides the Cards module.
|
||||
*
|
||||
*/
|
||||
class Cards extends Controller {
|
||||
|
||||
function init() {
|
||||
public function init() {
|
||||
|
||||
if(argc() > 1)
|
||||
$which = argv(1);
|
||||
@ -20,14 +24,15 @@ class Cards extends Controller {
|
||||
return;
|
||||
|
||||
profile_load($which);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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)) {
|
||||
return login();
|
||||
@ -99,7 +104,6 @@ class Cards extends Controller {
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(perm_is_allowed($owner, $ob_hash, 'write_pages')) {
|
||||
|
||||
$x = [
|
||||
|
@ -106,7 +106,7 @@ class Chanview extends \Zotlabs\Web\Controller {
|
||||
|
||||
if (\App::$poi) {
|
||||
$url = \App::$poi['xchan_url'];
|
||||
if(\App::$poi['xchan_network'] === 'zot') {
|
||||
if(in_array(\App::$poi['xchan_network'], ['zot', 'zot6'])) {
|
||||
$is_zot = true;
|
||||
}
|
||||
if(local_channel()) {
|
||||
|
@ -710,7 +710,7 @@ class Connedit extends \Zotlabs\Web\Controller {
|
||||
|
||||
$tpl = get_markup_template("abook_edit.tpl");
|
||||
|
||||
if(feature_enabled(local_channel(),'affinity')) {
|
||||
if(Apps::system_app_installed(local_channel(),'Affinity Tool')) {
|
||||
|
||||
$sections['affinity'] = [
|
||||
'label' => t('Affinity'),
|
||||
|
@ -179,7 +179,6 @@ class Display extends \Zotlabs\Web\Controller {
|
||||
|
||||
$static = ((array_key_exists('static',$_REQUEST)) ? intval($_REQUEST['static']) : 0);
|
||||
|
||||
|
||||
$simple_update = (($update) ? " AND item_unseen = 1 " : '');
|
||||
|
||||
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
|
||||
// make that content unsearchable by ensuring the owner_xchan can't match
|
||||
if(! perm_is_allowed($sysid,$observer_hash,'view_stream'))
|
||||
|
22
Zotlabs/Module/Embed.php
Normal file
22
Zotlabs/Module/Embed.php
Normal 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -3,8 +3,10 @@
|
||||
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 {
|
||||
|
||||
@ -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') {
|
||||
// API: /embedphotos/album
|
||||
$name = (x($_POST, 'name') ? $_POST['name'] : null );
|
||||
@ -29,7 +31,7 @@ class Embedphotos extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
if (argc() > 1 && argv(1) === '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));
|
||||
}
|
||||
if (argc() > 1 && argv(1) === 'photolink') {
|
||||
@ -38,7 +40,7 @@ class Embedphotos extends \Zotlabs\Web\Controller {
|
||||
if (!$href) {
|
||||
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",
|
||||
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
|
||||
* the input array as in widget_item()
|
||||
* @brief Get photos from an album.
|
||||
*
|
||||
* @param array $args
|
||||
* @return string with HTML
|
||||
* @see \\Zotlabs\\Widget\\Album::widget()
|
||||
*
|
||||
* @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;
|
||||
if(array_key_exists('channel', $args))
|
||||
|
||||
if (array_key_exists('channel', $args)) {
|
||||
$channel = $args['channel'];
|
||||
$channel_id = intval($channel['channel_id']);
|
||||
}
|
||||
if (! $channel_id)
|
||||
$channel_id = \App::$profile_uid;
|
||||
if (! $channel_id)
|
||||
return '';
|
||||
|
||||
$owner_uid = $channel_id;
|
||||
require_once('include/security.php');
|
||||
$sql_extra = permissions_sql($channel_id);
|
||||
|
||||
if (! perm_is_allowed($channel_id, get_observer_hash(), 'view_storage'))
|
||||
return '';
|
||||
|
||||
if($args['album'])
|
||||
if (isset($args['album']))
|
||||
$album = (($args['album'] === '/') ? '' : $args['album']);
|
||||
if($args['title'])
|
||||
if (isset($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
|
||||
*/
|
||||
if ($album) {
|
||||
require_once('include/attach.php');
|
||||
$x = q("select hash from attach where filename = '%s' and uid = %d limit 1",
|
||||
dbesc($album),
|
||||
intval($owner_uid)
|
||||
intval($channel_id)
|
||||
);
|
||||
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)
|
||||
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
|
||||
ON (p.resource_id = ph.resource_id AND p.imgscale = ph.imgscale)
|
||||
ORDER BY created $order",
|
||||
intval($owner_uid),
|
||||
intval($channel_id),
|
||||
dbesc($album),
|
||||
intval(PHOTO_NORMAL),
|
||||
intval(PHOTO_PROFILE)
|
||||
);
|
||||
|
||||
$photos = array();
|
||||
$photos = [];
|
||||
if (count($r)) {
|
||||
$twist = 'rotright';
|
||||
foreach ($r as $rr) {
|
||||
@ -125,15 +130,18 @@ class Embedphotos extends \Zotlabs\Web\Controller {
|
||||
else
|
||||
$twist = 'rotright';
|
||||
|
||||
$ph = photo_factory('');
|
||||
$phototypes = $ph->supportedTypes();
|
||||
|
||||
$ext = $phototypes[$rr['mimetype']];
|
||||
|
||||
$imgalt_e = $rr['filename'];
|
||||
$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' : ''));
|
||||
|
||||
$photos[] = array(
|
||||
$photos[] = [
|
||||
'id' => $rr['id'],
|
||||
'twist' => ' ' . $twist . rand(2,4),
|
||||
'link' => $imagelink,
|
||||
@ -143,35 +151,43 @@ class Embedphotos extends \Zotlabs\Web\Controller {
|
||||
'desc'=> $desc_e,
|
||||
'ext' => $ext,
|
||||
'hash'=> $rr['resource_id'],
|
||||
'unknown' => t('Unknown')
|
||||
);
|
||||
'unknown' => t('Unknown'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$tpl = get_markup_template('photo_album.tpl');
|
||||
$o .= replace_macros($tpl, array(
|
||||
$o = replace_macros($tpl, [
|
||||
'$photos' => $photos,
|
||||
'$album' => (($title) ? $title : $album),
|
||||
'$album_id' => rand(),
|
||||
'$album_edit' => array(t('Edit Album'), $album_edit),
|
||||
'$album_edit' => array(t('Edit Album'), false),
|
||||
'$can_post' => false,
|
||||
'$upload' => array(t('Upload'), z_root() . '/photos/' . \App::$profile['channel_address'] . '/upload/' . bin2hex($album)),
|
||||
'$order' => false,
|
||||
'$upload_form' => $upload_form,
|
||||
'$no_fullscreen_btn' => true
|
||||
));
|
||||
'$upload_form' => '',
|
||||
'$no_fullscreen_btn' => true,
|
||||
]);
|
||||
|
||||
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');
|
||||
$p = photos_albums_list(\App::get_channel(), \App::get_observer());
|
||||
|
||||
if ($p['success']) {
|
||||
return $p['albums'];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -97,8 +97,8 @@ class Events extends \Zotlabs\Web\Controller {
|
||||
$type = escape_tags(trim($_POST['type']));
|
||||
|
||||
require_once('include/text.php');
|
||||
linkify_tags($a, $desc, local_channel());
|
||||
linkify_tags($a, $location, local_channel());
|
||||
linkify_tags($desc, local_channel());
|
||||
linkify_tags($location, local_channel());
|
||||
|
||||
//$action = ($event_hash == '') ? 'new' : "event/" . $event_hash;
|
||||
|
||||
|
@ -66,6 +66,9 @@ class Group extends Controller {
|
||||
$groupname = notags(trim($_POST['groupname']));
|
||||
$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']))) {
|
||||
$r = q("UPDATE pgrp SET gname = '%s', visible = %d WHERE uid = %d AND id = %d",
|
||||
dbesc($groupname),
|
||||
@ -76,8 +79,6 @@ class Group extends Controller {
|
||||
if($r)
|
||||
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);
|
||||
}
|
||||
@ -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(
|
||||
'$title' => sprintf(t('Privacy Group: %s'), $group['gname']),
|
||||
'$details_label' => t('Edit'),
|
||||
@ -252,6 +257,7 @@ class Group extends Controller {
|
||||
'$form_security_token_edit' => get_form_security_token('group_edit'),
|
||||
'$delete' => t('Delete Group'),
|
||||
'$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['$desc'] = t('Click a channel to toggle membership');
|
||||
$context['$pgrp_extras'] = $pgrp_extras;
|
||||
|
||||
if($change) {
|
||||
$tpl = get_markup_template('groupeditor.tpl');
|
||||
|
@ -6,6 +6,13 @@ use Zotlabs\Lib\IConfig;
|
||||
use Zotlabs\Lib\Enotify;
|
||||
use Zotlabs\Web\Controller;
|
||||
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/items.php');
|
||||
@ -30,6 +37,144 @@ require_once('include/security.php');
|
||||
|
||||
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() {
|
||||
|
||||
// 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
|
||||
|
||||
$results = linkify_tags($a, $summary, ($uid) ? $uid : $profile_uid);
|
||||
$results = linkify_tags($a, $body, ($uid) ? $uid : $profile_uid);
|
||||
$results = linkify_tags($summary, ($uid) ? $uid : $profile_uid);
|
||||
$results = linkify_tags($body, ($uid) ? $uid : $profile_uid);
|
||||
|
||||
if($results) {
|
||||
|
||||
@ -639,9 +784,9 @@ class Item extends Controller {
|
||||
|
||||
|
||||
if(preg_match_all('/(\[share=(.*?)\](.*?)\[\/share\])/',$body,$match)) {
|
||||
|
||||
// process share by id
|
||||
|
||||
$verb = ACTIVITY_SHARE;
|
||||
$i = 0;
|
||||
foreach($match[2] as $mtch) {
|
||||
$reshare = new \Zotlabs\Lib\Share($mtch);
|
||||
|
@ -34,7 +34,7 @@ class Mail extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
else {
|
||||
$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)) {
|
||||
$attachments = array();
|
||||
@ -111,7 +111,7 @@ class Mail extends \Zotlabs\Web\Controller {
|
||||
}
|
||||
|
||||
require_once('include/text.php');
|
||||
linkify_tags($a, $body, local_channel());
|
||||
linkify_tags($body, local_channel());
|
||||
|
||||
|
||||
if(! $recipient) {
|
||||
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use Zotlabs\Lib\Group;
|
||||
use Zotlabs\Lib\Apps;
|
||||
use App;
|
||||
|
||||
require_once('include/items.php');
|
||||
@ -114,8 +116,8 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
$def_acl = array('allow_gid' => '<' . $r[0]['hash'] . '>');
|
||||
}
|
||||
|
||||
$default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-1));
|
||||
$default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : (-1));
|
||||
$default_cmin = ((Apps::system_app_installed(local_channel(),'Affinity Tool')) ? get_pconfig(local_channel(),'affinity','cmin',0) : (-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);
|
||||
$star = ((x($_GET,'star')) ? intval($_GET['star']) : 0);
|
||||
@ -132,7 +134,7 @@ class Network extends \Zotlabs\Web\Controller {
|
||||
|
||||
$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));
|
||||
if ($affinity_locked) {
|
||||
set_pconfig(local_channel(),'affinity','cmin',$cmin);
|
||||
|
@ -1,10 +1,13 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module; /** @file */
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Web\Controller;
|
||||
use Zotlabs\Lib\Apps;
|
||||
|
||||
/**
|
||||
* @brief Notes Module controller.
|
||||
*/
|
||||
class Notes extends Controller {
|
||||
|
||||
function post() {
|
||||
@ -40,11 +43,9 @@ class Notes extends Controller {
|
||||
|
||||
logger('notes saved.', LOGGER_DEBUG);
|
||||
json_return_and_die($ret);
|
||||
|
||||
}
|
||||
|
||||
function get() {
|
||||
|
||||
if(! local_channel())
|
||||
return EMPTY_STR;
|
||||
|
||||
@ -61,7 +62,6 @@ class Notes extends Controller {
|
||||
$arr = ['app' => true];
|
||||
|
||||
return $w->widget($arr);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ class Oep extends \Zotlabs\Web\Controller {
|
||||
dbesc($res)
|
||||
);
|
||||
if($r) {
|
||||
$sql_extra = "and item.id = " . intval($r[0]['iid']) . " ";
|
||||
$sql_extra .= " and item.id = " . intval($r[0]['iid']) . " ";
|
||||
}
|
||||
else {
|
||||
return $ret;
|
||||
@ -194,6 +194,9 @@ class Oep extends \Zotlabs\Web\Controller {
|
||||
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
|
||||
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
|
||||
and item.item_blocked = 0 ";
|
||||
@ -255,7 +258,6 @@ class Oep extends \Zotlabs\Web\Controller {
|
||||
if(! $channel)
|
||||
return $ret;
|
||||
|
||||
|
||||
if(! perm_is_allowed($channel['channel_id'],get_observer_hash(),'view_pages'))
|
||||
return $ret;
|
||||
|
||||
@ -265,7 +267,7 @@ class Oep extends \Zotlabs\Web\Controller {
|
||||
dbesc($res)
|
||||
);
|
||||
if($r) {
|
||||
$sql_extra = "and item.id = " . intval($r[0]['iid']) . " ";
|
||||
$sql_extra .= " and item.id = " . intval($r[0]['iid']) . " ";
|
||||
}
|
||||
else {
|
||||
return $ret;
|
||||
@ -278,6 +280,9 @@ class Oep extends \Zotlabs\Web\Controller {
|
||||
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
|
||||
and item.item_unpublished = 0 and item.item_delayed = 0 and item.item_pending_remove = 0
|
||||
and item.item_blocked = 0 ";
|
||||
|
@ -422,7 +422,7 @@ class Photos extends \Zotlabs\Web\Controller {
|
||||
require_once('include/text.php');
|
||||
$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'];
|
||||
$post_tags = array();
|
||||
|
@ -354,20 +354,20 @@ class Profiles extends \Zotlabs\Web\Controller {
|
||||
|
||||
|
||||
require_once('include/text.php');
|
||||
linkify_tags($a, $likes, local_channel());
|
||||
linkify_tags($a, $dislikes, local_channel());
|
||||
linkify_tags($a, $about, local_channel());
|
||||
linkify_tags($a, $interest, local_channel());
|
||||
linkify_tags($a, $interest, local_channel());
|
||||
linkify_tags($a, $contact, local_channel());
|
||||
linkify_tags($a, $channels, local_channel());
|
||||
linkify_tags($a, $music, local_channel());
|
||||
linkify_tags($a, $book, local_channel());
|
||||
linkify_tags($a, $tv, local_channel());
|
||||
linkify_tags($a, $film, local_channel());
|
||||
linkify_tags($a, $romance, local_channel());
|
||||
linkify_tags($a, $work, local_channel());
|
||||
linkify_tags($a, $education, local_channel());
|
||||
linkify_tags($likes, local_channel());
|
||||
linkify_tags($dislikes, local_channel());
|
||||
linkify_tags($about, local_channel());
|
||||
linkify_tags($interest, local_channel());
|
||||
linkify_tags($interest, local_channel());
|
||||
linkify_tags($contact, local_channel());
|
||||
linkify_tags($channels, local_channel());
|
||||
linkify_tags($music, local_channel());
|
||||
linkify_tags($book, local_channel());
|
||||
linkify_tags($tv, local_channel());
|
||||
linkify_tags($film, local_channel());
|
||||
linkify_tags($romance, local_channel());
|
||||
linkify_tags($work, local_channel());
|
||||
linkify_tags($education, local_channel());
|
||||
|
||||
|
||||
$with = ((x($_POST,'with')) ? escape_tags(trim($_POST['with'])) : '');
|
||||
|
@ -10,24 +10,6 @@ class Featured {
|
||||
|
||||
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();
|
||||
return;
|
||||
}
|
||||
@ -41,34 +23,10 @@ class Featured {
|
||||
if(! $r)
|
||||
$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);
|
||||
|
||||
$this->sortpanels($settings_addons);
|
||||
|
||||
|
||||
$tpl = get_markup_template("settings_addons.tpl");
|
||||
$o .= replace_macros($tpl, array(
|
||||
'$form_security_token' => get_form_security_token("settings_featured"),
|
||||
|
@ -1,6 +1,11 @@
|
||||
<?php
|
||||
namespace Zotlabs\Module;
|
||||
|
||||
use App;
|
||||
use Zotlabs\Daemon\Master;
|
||||
use Zotlabs\Lib\Activity;
|
||||
|
||||
|
||||
require_once('include/security.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
@ -14,23 +19,23 @@ class Share extends \Zotlabs\Web\Controller {
|
||||
if(! $post_id)
|
||||
killme();
|
||||
|
||||
echo '[share=' . $post_id . '][/share]';
|
||||
if(! local_channel()) {
|
||||
killme();
|
||||
}
|
||||
|
||||
$observer = App::get_observer();
|
||||
|
||||
/**
|
||||
* The remaining code is deprecated and handled in Zotlabs/Lib/Share.php at post
|
||||
* submission time.
|
||||
*/
|
||||
|
||||
if(! (local_channel() || remote_channel()))
|
||||
killme();
|
||||
$channel = App::get_channel();
|
||||
|
||||
$r = q("SELECT * from item left join xchan on author_xchan = xchan_hash WHERE id = %d LIMIT 1",
|
||||
intval($post_id)
|
||||
);
|
||||
if(! $r)
|
||||
killme();
|
||||
|
||||
|
||||
|
||||
|
||||
if(($r[0]['item_private']) && ($r[0]['xchan_network'] !== 'rss'))
|
||||
killme();
|
||||
|
||||
@ -47,58 +52,85 @@ class Share extends \Zotlabs\Web\Controller {
|
||||
if($r[0]['mimetype'] !== 'text/bbcode')
|
||||
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);
|
||||
|
||||
$is_photo = (($r[0]['obj_type'] === ACTIVITY_OBJ_PHOTO) ? true : false);
|
||||
if($is_photo) {
|
||||
$object = json_decode($r[0]['obj'],true);
|
||||
$photo_bb = $object['body'];
|
||||
}
|
||||
$arr = [];
|
||||
|
||||
if (strpos($r[0]['body'], "[/share]") !== false) {
|
||||
$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]";
|
||||
}
|
||||
$item = $r[0];
|
||||
|
||||
if(local_channel()) {
|
||||
echo $o;
|
||||
$owner_uid = $r[0]['uid'];
|
||||
$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();
|
||||
}
|
||||
|
||||
$observer = \App::get_observer();
|
||||
$parsed = $observer['xchan_url'];
|
||||
if($parsed) {
|
||||
$post_url = $parsed['scheme'] . '://' . $parsed['host'] . (($parsed['port']) ? ':' . $parsed['port'] : '')
|
||||
. '/rpost';
|
||||
$r = q("select * from xchan where xchan_hash = '%s' limit 1",
|
||||
dbesc($item['owner_xchan'])
|
||||
);
|
||||
|
||||
/**
|
||||
* @FIXME we were probably called from JS so we don't know the return page.
|
||||
* In fact we won't be able to load the remote page.
|
||||
* we might need an iframe
|
||||
*/
|
||||
|
||||
$x = z_post_url($post_url, array('f' => '', 'body' => $o ));
|
||||
if($r)
|
||||
$thread_owner = $r[0];
|
||||
else
|
||||
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('🔁 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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
$item_normal = item_normal();
|
||||
|
||||
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($sys['channel_id']),
|
||||
intval($item_id)
|
||||
@ -53,7 +53,7 @@ class Viewsrc extends \Zotlabs\Web\Controller {
|
||||
|
||||
if(is_ajax()) {
|
||||
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 '<pre class="p-1">' . $o . '</pre>';
|
||||
echo '</div>';
|
||||
|
498
Zotlabs/Photo/PhotoDriver.php
Normal file
498
Zotlabs/Photo/PhotoDriver.php
Normal 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
176
Zotlabs/Photo/PhotoGd.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
||||
|
||||
class photo_imagick extends photo_driver {
|
||||
|
||||
|
||||
function supportedTypes() {
|
||||
return array(
|
||||
public function supportedTypes() {
|
||||
return [
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/png' => 'png',
|
||||
'image/gif' => 'gif'
|
||||
);
|
||||
'image/gif' => 'gif',
|
||||
];
|
||||
}
|
||||
|
||||
public function get_FormatsMap() {
|
||||
return array(
|
||||
private function get_FormatsMap() {
|
||||
return [
|
||||
'image/jpeg' => 'JPG',
|
||||
'image/png' => 'PNG',
|
||||
'image/gif' => 'GIF'
|
||||
);
|
||||
'image/gif' => 'GIF',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function load($data, $type) {
|
||||
protected function load($data, $type) {
|
||||
$this->valid = false;
|
||||
$this->image = new Imagick();
|
||||
$this->image = new \Imagick();
|
||||
|
||||
if(! $data)
|
||||
return;
|
||||
|
||||
try {
|
||||
$this->image->readImageBlob($data);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
logger('imagick readImageBlob() exception:' . print_r($e,true));
|
||||
} catch(\Exception $e) {
|
||||
logger('Imagick readImageBlob() exception:' . print_r($e, true));
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* 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
|
||||
$this->image = $this->image->coalesceImages();
|
||||
|
||||
|
||||
$this->valid = true;
|
||||
$this->setDimensions();
|
||||
|
||||
/**
|
||||
/*
|
||||
* setup the compression here, so we'll do it only once
|
||||
*/
|
||||
switch($this->getType()) {
|
||||
case "image/png":
|
||||
case 'image/png':
|
||||
$quality = get_config('system', 'png_quality');
|
||||
if((! $quality) || ($quality > 9))
|
||||
$quality = PNG_QUALITY;
|
||||
/**
|
||||
/*
|
||||
* From http://www.imagemagick.org/script/command-line-options.php#quality:
|
||||
*
|
||||
* 'For the MNG and PNG image formats, the quality value sets
|
||||
@ -75,56 +73,64 @@ class photo_imagick extends photo_driver {
|
||||
$quality = $quality * 10;
|
||||
$this->image->setCompressionQuality($quality);
|
||||
break;
|
||||
case "image/jpeg":
|
||||
case 'image/jpeg':
|
||||
$quality = get_config('system', 'jpeg_quality');
|
||||
if((! $quality) || ($quality > 100))
|
||||
$quality = JPEG_QUALITY;
|
||||
$this->image->setCompressionQuality($quality);
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy() {
|
||||
protected function destroy() {
|
||||
if($this->is_valid()) {
|
||||
$this->image->clear();
|
||||
$this->image->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function setDimensions() {
|
||||
protected function setDimensions() {
|
||||
$this->width = $this->image->getImageWidth();
|
||||
$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() {
|
||||
|
||||
$profiles = $this->image->getImageProfiles("icc", true);
|
||||
$profiles = $this->image->getImageProfiles('icc', true);
|
||||
|
||||
$this->image->stripImage();
|
||||
|
||||
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() {
|
||||
if(! $this->is_valid())
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
$this->image = $this->image->deconstructImages();
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
public function doScaleImage($dest_width, $dest_height) {
|
||||
|
||||
/**
|
||||
/*
|
||||
* If it is not animated, there will be only one iteration here,
|
||||
* so don't bother checking
|
||||
*/
|
||||
@ -139,12 +145,12 @@ class photo_imagick extends photo_driver {
|
||||
|
||||
public function rotate($degrees) {
|
||||
if(! $this->is_valid())
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
$this->image->setFirstIterator();
|
||||
do {
|
||||
// ImageMagick rotates in the opposite direction of imagerotate()
|
||||
$this->image->rotateImage(new ImagickPixel(), -$degrees);
|
||||
$this->image->rotateImage(new \ImagickPixel(), -$degrees);
|
||||
} while($this->image->nextImage());
|
||||
|
||||
$this->setDimensions();
|
||||
@ -152,7 +158,7 @@ class photo_imagick extends photo_driver {
|
||||
|
||||
public function flip($horiz = true, $vert = false) {
|
||||
if(! $this->is_valid())
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
$this->image->setFirstIterator();
|
||||
do {
|
||||
@ -163,32 +169,14 @@ class photo_imagick extends photo_driver {
|
||||
$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) {
|
||||
if(! $this->is_valid())
|
||||
return FALSE;
|
||||
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
|
||||
@ -201,13 +189,12 @@ class photo_imagick extends photo_driver {
|
||||
|
||||
public function imageString() {
|
||||
if(! $this->is_valid())
|
||||
return FALSE;
|
||||
return false;
|
||||
|
||||
/* Clean it */
|
||||
$this->image = $this->image->deconstructImages();
|
||||
|
||||
return $this->image->getImagesBlob();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -15,7 +15,7 @@ class Session {
|
||||
|
||||
private $handler = null;
|
||||
private $session_started = false;
|
||||
|
||||
private $custom_handler = false;
|
||||
public function init() {
|
||||
|
||||
$gc_probability = 50;
|
||||
@ -24,10 +24,30 @@ class Session {
|
||||
ini_set('session.use_only_cookies', 1);
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
|
||||
$this->custom_handler = boolval(get_config('system', 'session_custom', false));
|
||||
|
||||
/*
|
||||
* 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();
|
||||
|
||||
$this->handler = $handler;
|
||||
@ -35,6 +55,7 @@ class Session {
|
||||
$x = session_set_save_handler($handler,false);
|
||||
if(! $x)
|
||||
logger('Session save handler initialisation failed.',LOGGER_NORMAL,LOG_ERR);
|
||||
}
|
||||
|
||||
// Force cookies to be secure (https only) if this site is SSL enabled.
|
||||
// Must be done before session_start().
|
||||
@ -86,15 +107,16 @@ class Session {
|
||||
|
||||
$arr = session_get_cookie_params();
|
||||
|
||||
if($this->handler && $this->session_started) {
|
||||
if(($this->handler || $this->custom_handler) && $this->session_started) {
|
||||
|
||||
session_regenerate_id(true);
|
||||
|
||||
// force SessionHandler record creation with the new session_id
|
||||
// which occurs as a side effect of read()
|
||||
|
||||
if (! $this->custom_handler) {
|
||||
$this->handler->read(session_id());
|
||||
}
|
||||
}
|
||||
else
|
||||
logger('no session handler');
|
||||
|
||||
|
@ -2,21 +2,24 @@
|
||||
|
||||
namespace Zotlabs\Widget;
|
||||
|
||||
use Zotlabs\Lib\Apps;
|
||||
|
||||
class Affinity {
|
||||
|
||||
function widget($arr) {
|
||||
|
||||
if(! local_channel())
|
||||
return '';
|
||||
return;
|
||||
|
||||
$default_cmin = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmin',0) : 0);
|
||||
$default_cmax = ((feature_enabled(local_channel(),'affinity')) ? get_pconfig(local_channel(),'affinity','cmax',99) : 99);
|
||||
if(! Apps::system_app_installed(local_channel(),'Affinity Tool'))
|
||||
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);
|
||||
$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));
|
||||
if ($affinity_locked) {
|
||||
set_pconfig(local_channel(),'affinity','cmin',$cmin);
|
||||
@ -31,6 +34,7 @@ class Affinity {
|
||||
t('All')
|
||||
);
|
||||
call_hooks('affinity_labels',$labels);
|
||||
|
||||
$label_str = '';
|
||||
|
||||
if($labels) {
|
||||
@ -53,9 +57,10 @@ class Affinity {
|
||||
|
||||
$arr = array('html' => $x);
|
||||
call_hooks('main_slider',$arr);
|
||||
|
||||
return $arr['html'];
|
||||
}
|
||||
return '';
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,19 +42,12 @@ class Settings_menu {
|
||||
|
||||
);
|
||||
|
||||
|
||||
$tabs[] = array(
|
||||
'label' => t('Display settings'),
|
||||
'url' => z_root().'/settings/display',
|
||||
'selected' => ((argv(1) === 'display') ? 'active' : ''),
|
||||
);
|
||||
|
||||
$tabs[] = array(
|
||||
'label' => t('Addon settings'),
|
||||
'url' => z_root().'/settings/featured',
|
||||
'selected' => ((argv(1) === 'featured') ? 'active' : ''),
|
||||
);
|
||||
|
||||
if($hublocs) {
|
||||
$tabs[] = array(
|
||||
'label' => t('Manage locations'),
|
||||
|
@ -58,10 +58,10 @@ class Zot6Handler implements IHandler {
|
||||
*
|
||||
* @param array $sender
|
||||
* @param array $recipients
|
||||
* @param array $hub
|
||||
*
|
||||
* @return json_return_and_die()
|
||||
* @return array
|
||||
*/
|
||||
|
||||
static function reply_refresh($sender, $recipients, $hub) {
|
||||
$ret = array('success' => false);
|
||||
|
||||
@ -70,19 +70,18 @@ class Zot6Handler implements IHandler {
|
||||
// This would be a permissions update, typically for one connection
|
||||
|
||||
foreach ($recipients as $recip) {
|
||||
|
||||
$r = q("select channel.*,xchan.* from channel
|
||||
left join xchan on channel_portable_id = xchan_hash
|
||||
where xchan_hash ='%s' limit 1",
|
||||
dbesc($recip)
|
||||
);
|
||||
|
||||
/// @FIXME $msgtype is undefined
|
||||
$x = Libzot::refresh( [ 'hubloc_id_url' => $hub['hubloc_id_url'] ], $r[0], (($msgtype === 'force_refresh') ? true : false));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// system wide refresh
|
||||
|
||||
/// @FIXME $msgtype is undefined
|
||||
$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
|
||||
* processed/delivered in order.
|
||||
*
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $hub
|
||||
* @return array
|
||||
*/
|
||||
|
||||
static function reply_message_request($data, $hub) {
|
||||
$ret = [ 'success' => false ];
|
||||
|
||||
@ -153,11 +151,10 @@ class Zot6Handler implements IHandler {
|
||||
/*
|
||||
* 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' ]);
|
||||
|
||||
return (($messages) ? : [] );
|
||||
|
||||
}
|
||||
|
||||
static function rekey_request($sender,$data,$hub) {
|
||||
@ -219,10 +216,10 @@ class Zot6Handler implements IHandler {
|
||||
*
|
||||
* @param array $sender
|
||||
* @param array $recipients
|
||||
* @param array $hub
|
||||
*
|
||||
* return json_return_and_die()
|
||||
* @return array
|
||||
*/
|
||||
|
||||
static function reply_purge($sender, $recipients, $hub) {
|
||||
|
||||
$ret = array('success' => false);
|
||||
@ -259,9 +256,4 @@ class Zot6Handler implements IHandler {
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
7
app/affinity.apd
Normal file
7
app/affinity.apd
Normal 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.
|
10
boot.php
10
boot.php
@ -50,7 +50,7 @@ require_once('include/attach.php');
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
define ( 'PLATFORM_NAME', 'hubzilla' );
|
||||
define ( 'STD_VERSION', '3.9.5' );
|
||||
define ( 'STD_VERSION', '3.9.6' );
|
||||
define ( 'ZOT_REVISION', '6.0a' );
|
||||
|
||||
define ( 'DB_UPDATE_VERSION', 1230 );
|
||||
@ -83,7 +83,6 @@ define ( 'DIRECTORY_REALM', 'RED_GLOBAL');
|
||||
define ( 'DIRECTORY_FALLBACK_MASTER', 'https://zotadel.net');
|
||||
|
||||
$DIRECTORY_FALLBACK_SERVERS = array(
|
||||
'https://hubzilla.zottel.net',
|
||||
'https://zotadel.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 ( 'ZOT_APSCHEMA_REV', '/apschema/v1.2' );
|
||||
define ( 'ZOT_APSCHEMA_REV', '/apschema/v1.3' );
|
||||
/**
|
||||
* 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_ACTIVITY',NAMESPACE_ACTIVITY_SCHEMA . 'activity' );
|
||||
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_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo' );
|
||||
define ( 'ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo' );
|
||||
@ -1833,8 +1833,6 @@ function proc_run(){
|
||||
|
||||
$args = func_get_args();
|
||||
|
||||
$newargs = array();
|
||||
|
||||
if(! count($args))
|
||||
return;
|
||||
|
||||
@ -2401,7 +2399,7 @@ function z_get_temp_dir() {
|
||||
if(! $temp_dir)
|
||||
$temp_dir = sys_get_temp_dir();
|
||||
|
||||
return $upload_dir;
|
||||
return $temp_dir;
|
||||
}
|
||||
|
||||
|
||||
|
5
doc/hook/jot_header_tpl_filter.bb
Normal file
5
doc/hook/jot_header_tpl_filter.bb
Normal 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
|
5
doc/hook/jot_tpl_filter.bb
Normal file
5
doc/hook/jot_tpl_filter.bb
Normal 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
|
@ -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]
|
||||
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]
|
||||
Called to validate a channel address
|
||||
|
||||
|
@ -137,7 +137,7 @@ function z_mime_content_type($filename) {
|
||||
* @param string $hash (optional)
|
||||
* @param string $filename (optional)
|
||||
* @param string $filetype (optional)
|
||||
* @return associative array with:
|
||||
* @return array Associative array with:
|
||||
* * \e boolean \b success
|
||||
* * \e int|boolean \b results amount of found results, or false
|
||||
* * \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.
|
||||
*
|
||||
* @param $channel_id
|
||||
* @param $observer
|
||||
* @param $hash (optional)
|
||||
* @param $filename (optional)
|
||||
* @param $filetype (optional)
|
||||
* @param $orderby
|
||||
* @param $start
|
||||
* @param $entries
|
||||
* @return associative array with:
|
||||
* @param int $channel_id
|
||||
* @param string $observer
|
||||
* @param string $hash (optional)
|
||||
* @param string $filename (optional)
|
||||
* @param string $filetype (optional)
|
||||
* @param string $orderby (optional)
|
||||
* @param int $start (optional)
|
||||
* @param int $entries (optional)
|
||||
* @param string $since (optional)
|
||||
* @param string $until (optional)
|
||||
* @return array an associative array with:
|
||||
* * \e boolean \b success
|
||||
* * \e array|boolean \b results array with results, or false
|
||||
* * \e string \b message with error messages if any
|
||||
@ -1429,7 +1431,16 @@ function attach_delete($channel_id, $resource, $is_photo = 0) {
|
||||
if(! $r) {
|
||||
attach_drop_photo($channel_id,$resource);
|
||||
$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;
|
||||
}
|
||||
|
||||
@ -1489,7 +1500,14 @@ function attach_delete($channel_id, $resource, $is_photo = 0) {
|
||||
);
|
||||
|
||||
$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);
|
||||
|
||||
@ -1868,7 +1886,7 @@ function file_activity($channel_id, $object, $allow_cid, $allow_gid, $deny_cid,
|
||||
* @param int $channel_id
|
||||
* @param string $hash
|
||||
* @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) {
|
||||
|
||||
|
@ -9,7 +9,7 @@ function findpeople_widget() {
|
||||
if($x || is_site_admin()) {
|
||||
App::$page['aside'] .= '<div class="side-link" id="side-invite-remain">'
|
||||
. 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'),
|
||||
'$findthem' => t('Find'),
|
||||
'$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'),
|
||||
'$inv' => t('Invite Friends'),
|
||||
'$advanced_search' => $advanced_search,
|
||||
@ -56,7 +56,6 @@ function fileas_widget($baseurl,$selected = '') {
|
||||
'$all' => t('Everything'),
|
||||
'$terms' => $terms,
|
||||
'$base' => $baseurl,
|
||||
|
||||
));
|
||||
}
|
||||
|
||||
@ -100,7 +99,6 @@ function categories_widget($baseurl,$selected = '') {
|
||||
'$all' => t('Everything'),
|
||||
'$terms' => $terms,
|
||||
'$base' => $baseurl,
|
||||
|
||||
));
|
||||
}
|
||||
return '';
|
||||
@ -144,9 +142,9 @@ function cardcategories_widget($baseurl,$selected = '') {
|
||||
'$all' => t('Everything'),
|
||||
'$terms' => $terms,
|
||||
'$base' => $baseurl,
|
||||
|
||||
));
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@ -189,17 +187,14 @@ function articlecategories_widget($baseurl,$selected = '') {
|
||||
'$all' => t('Everything'),
|
||||
'$terms' => $terms,
|
||||
'$base' => $baseurl,
|
||||
|
||||
));
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function common_friends_visitor_widget($profile_uid,$cnt = 25) {
|
||||
|
||||
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);
|
||||
|
||||
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'),
|
||||
'$base' => z_root(),
|
||||
'$uid' => $profile_uid,
|
||||
'$cid' => $observer,
|
||||
'$linkmore' => (($t > $cnt) ? 'true' : ''),
|
||||
'$more' => sprintf( t('View all %d common connections'), $t),
|
||||
'$items' => $r
|
||||
));
|
||||
'$items' => $r,
|
||||
]);
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -1328,7 +1328,7 @@ function hz_status_editor($a, $x, $popup = false) {
|
||||
|
||||
$tpl = get_markup_template('jot-header.tpl');
|
||||
|
||||
App::$page['htmlhead'] .= replace_macros($tpl, array(
|
||||
$tplmacros = [
|
||||
'$baseurl' => z_root(),
|
||||
'$editselect' => (($plaintext) ? 'none' : '/(profile-jot-text|prvmail-text)/'),
|
||||
'$pretext' => ((x($x,'pretext')) ? $x['pretext'] : ''),
|
||||
@ -1349,7 +1349,10 @@ function hz_status_editor($a, $x, $popup = false) {
|
||||
'$nocomment_disabled' => t('Comments disabled'),
|
||||
'$auto_save_draft' => $feature_auto_save_draft,
|
||||
'$reset' => $reset
|
||||
));
|
||||
];
|
||||
|
||||
call_hooks('jot_header_tpl_filter',$tplmacros);
|
||||
App::$page['htmlhead'] .= replace_macros($tpl, $tplmacros);
|
||||
|
||||
$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'));
|
||||
$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),
|
||||
'$action' => z_root() . '/item',
|
||||
'$share' => $sharebutton,
|
||||
@ -1463,9 +1466,15 @@ function hz_status_editor($a, $x, $popup = false) {
|
||||
'$bbcode' => ((x($x, 'bbcode')) ? $x['bbcode'] : false),
|
||||
'$parent' => ((array_key_exists('parent',$x) && $x['parent']) ? $x['parent'] : 0),
|
||||
'$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) {
|
||||
$o = '<div id="jot-popup" style="display:none">' . $o . '</div>';
|
||||
}
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Ramsey\Uuid\Exception\UnsatisfiedDependencyException;
|
||||
|
||||
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
/**
|
||||
@ -463,8 +467,13 @@ function event_store_event($arr) {
|
||||
$hash = $arr['external_id'];
|
||||
elseif(array_key_exists('event_hash',$arr))
|
||||
$hash = $arr['event_hash'];
|
||||
else
|
||||
$hash = random_string() . '@' . App::get_hostname();
|
||||
else {
|
||||
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,
|
||||
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']) {
|
||||
$arr['uuid'] = item_message_id();
|
||||
$arr['mid'] = z_root() . '/item/' . $arr['uuid'];
|
||||
$arr['uuid'] = $event['event_hash'];
|
||||
$arr['mid'] = z_root() . '/event/' . $event['event_hash'];
|
||||
}
|
||||
|
||||
$item_arr['aid'] = $z[0]['channel_account_id'];
|
||||
|
@ -360,14 +360,6 @@ function get_features($filtered = true, $level = (-1)) {
|
||||
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',
|
||||
t('Suggest Channels'),
|
||||
|
@ -261,13 +261,13 @@ function construct_activity_target($item) {
|
||||
* @param SimplePie $item
|
||||
* @return array $author
|
||||
*/
|
||||
|
||||
function get_atom_author($feed, $item) {
|
||||
|
||||
$author = [];
|
||||
|
||||
$found_author = $item->get_author();
|
||||
if($found_author) {
|
||||
/// @FIXME $rawauthor is undefined here
|
||||
if($rawauthor) {
|
||||
if($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
|
||||
];
|
||||
/**
|
||||
* @hooks parse_atom
|
||||
* @hooks parse_atom_author
|
||||
* * \e SimplePie \b feed - The original SimplePie feed
|
||||
* * \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);
|
||||
|
||||
@ -416,10 +416,8 @@ function get_atom_author($feed, $item) {
|
||||
*
|
||||
* @param SimplePie $feed
|
||||
* @param SimplePie $item
|
||||
* @param[out] array $author
|
||||
* @return array Associative array with the parsed item data
|
||||
*/
|
||||
|
||||
function get_atom_elements($feed, $item) {
|
||||
|
||||
require_once('include/html2bbcode.php');
|
||||
|
@ -7,7 +7,8 @@ use \Michelf\MarkdownExtra;
|
||||
* @brief
|
||||
*
|
||||
* @param string $path
|
||||
* @return string|unknown
|
||||
* @param string $suffix (optional) default null
|
||||
* @return string
|
||||
*/
|
||||
function get_help_fullpath($path, $suffix = null) {
|
||||
|
||||
@ -49,8 +50,8 @@ function get_help_fullpath($path,$suffix=null) {
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* @param string $tocpath
|
||||
* @return string|unknown
|
||||
* @param string $tocpath (optional) default false
|
||||
* @return string
|
||||
*/
|
||||
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() {
|
||||
$lang_detect = new Text_LanguageDetect();
|
||||
// Set this mode to recognize language by the short code like "en", "ru", etc.
|
||||
$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))) {
|
||||
$lang = argv(1);
|
||||
$from_url = true;
|
||||
@ -211,10 +216,10 @@ function find_doc_file($s) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @brief Search in doc files.
|
||||
*
|
||||
* @param string $s
|
||||
* @return number|mixed|unknown|boolean
|
||||
* @param string $s The search string to search for
|
||||
* @return array
|
||||
*/
|
||||
function search_doc_files($s) {
|
||||
|
||||
@ -275,7 +280,6 @@ function doc_rank_sort($s1, $s2) {
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
|
||||
function load_context_help() {
|
||||
|
||||
$path = App::$cmd;
|
||||
@ -305,7 +309,7 @@ function load_context_help() {
|
||||
* @brief
|
||||
*
|
||||
* @param string $s
|
||||
* @return void|boolean[]|number[]|string[]|unknown[]
|
||||
* @return void|array
|
||||
*/
|
||||
function store_doc_file($s) {
|
||||
|
||||
|
@ -12,6 +12,7 @@ require_once('include/perm_upgrade.php');
|
||||
* @param array $channel
|
||||
* @param int $account_id
|
||||
* @param int $seize
|
||||
* @param string $newname (optional)
|
||||
* @return boolean|array
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @see import_items
|
||||
* @see import_items()
|
||||
*
|
||||
* @param array $channel where to import to
|
||||
* @param array $items
|
||||
@ -1049,7 +1050,7 @@ function import_mail($channel, $mails, $sync = false) {
|
||||
/**
|
||||
* @brief Synchronise mails.
|
||||
*
|
||||
* @see import_mail
|
||||
* @see import_mail()
|
||||
* @param array $channel
|
||||
* @param array $mails
|
||||
*/
|
||||
|
@ -7,6 +7,7 @@
|
||||
use Zotlabs\Lib\Enotify;
|
||||
use Zotlabs\Lib\MarkdownSoap;
|
||||
use Zotlabs\Lib\MessageFilter;
|
||||
use Zotlabs\Lib\ThreadListener;
|
||||
use Zotlabs\Lib\IConfig;
|
||||
use Zotlabs\Access\PermissionLimits;
|
||||
use Zotlabs\Access\AccessList;
|
||||
@ -141,6 +142,22 @@ function collect_recipients($item, &$private_envelope,$include_groups = true) {
|
||||
// $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.
|
||||
// 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.
|
||||
@ -469,6 +486,8 @@ function post_activity_item($arr, $allow_code = false, $deliver = true) {
|
||||
*/
|
||||
call_hooks('post_local_end', $ret['activity']);
|
||||
}
|
||||
else
|
||||
return $ret;
|
||||
|
||||
if($post_id && $deliver) {
|
||||
Master::Summon([ 'Notifier','activity',$post_id ]);
|
||||
@ -2048,6 +2067,11 @@ function item_store($arr, $allow_exec = false, $deliver = true) {
|
||||
|
||||
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.
|
||||
// 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
|
||||
@ -2384,6 +2408,13 @@ function item_store_update($arr, $allow_exec = false, $deliver = true) {
|
||||
*/
|
||||
call_hooks('item_stored_update',$arr);
|
||||
|
||||
if(strpos($arr['body'],'[embed]') !== false) {
|
||||
Master::Summon([ 'Cache_embeds', $orig_post_id ]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if($deliver) {
|
||||
send_status_notifications($orig_post_id,$arr);
|
||||
tag_deliver($uid,$orig_post_id);
|
||||
@ -3004,7 +3035,9 @@ function tgroup_check($uid, $item) {
|
||||
* @param array $channel
|
||||
* @param array $item
|
||||
* @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) {
|
||||
|
||||
@ -3853,6 +3886,8 @@ function delete_item_lowlevel($item, $stage = DROPITEM_NORMAL, $force = false) {
|
||||
intval(TERM_OBJ_POST)
|
||||
);
|
||||
|
||||
ThreadListener::delete_by_target($item['mid']);
|
||||
|
||||
/** @FIXME remove notifications for this item */
|
||||
|
||||
return true;
|
||||
@ -4547,7 +4582,7 @@ function set_linkified_perms($linkified, &$str_contact_allow, &$str_group_allow,
|
||||
$first_access_tag = true;
|
||||
|
||||
foreach($linkified as $x) {
|
||||
$access_tag = $x['access_tag'];
|
||||
$access_tag = $x['success']['access_tag'];
|
||||
if(($access_tag) && (! $parent_item)) {
|
||||
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'))) {
|
||||
|
@ -44,7 +44,7 @@ function send_message($uid = 0, $recipient = '', $body = '', $subject = '', $rep
|
||||
|
||||
|
||||
$body = cleanup_bbcode($body);
|
||||
$results = linkify_tags($a, $body, $uid);
|
||||
$results = linkify_tags($body, $uid);
|
||||
|
||||
if(! $raw) {
|
||||
if(preg_match_all("/\[attachment\](.*?)\[\/attachment\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match)) {
|
||||
|
@ -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) {
|
||||
$ph = null;
|
||||
$m = null;
|
||||
|
||||
|
||||
$unsupported_types = array(
|
||||
$unsupported_types = [
|
||||
'image/bmp',
|
||||
'image/vnd.microsoft.icon',
|
||||
'image/tiff',
|
||||
'image/svg+xml'
|
||||
);
|
||||
'image/svg+xml',
|
||||
];
|
||||
|
||||
if($type && in_array(strtolower($type), $unsupported_types)) {
|
||||
logger('photo_factory: unsupported image type');
|
||||
logger('Unsupported image type ' . $type);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -22,10 +38,8 @@ function photo_factory($data, $type = null) {
|
||||
$v = Imagick::getVersion();
|
||||
preg_match('/ImageMagick ([0-9]+\.[0-9]+\.[0-9]+)/', $v['versionString'], $m);
|
||||
if(version_compare($m[1], '6.6.7') >= 0) {
|
||||
require_once('include/photo/photo_imagick.php');
|
||||
$ph = new photo_imagick($data,$type);
|
||||
}
|
||||
else {
|
||||
$ph = new PhotoImagick($data, $type);
|
||||
} else {
|
||||
// earlier imagick versions have issues with scaling png's
|
||||
// don't log this because it will just fill the logfile.
|
||||
// leave this note here so those who are looking for why
|
||||
@ -34,458 +48,32 @@ function photo_factory($data, $type = null) {
|
||||
}
|
||||
|
||||
if(! $ph) {
|
||||
require_once('include/photo/photo_gd.php');
|
||||
$ph = new photo_gd($data,$type);
|
||||
$ph = new PhotoGd($data, $type);
|
||||
}
|
||||
|
||||
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
|
||||
* 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
|
||||
* @brief Guess image mimetype from filename or from Content-Type header.
|
||||
*
|
||||
* @arg $filename string Image filename
|
||||
* @arg $headers string Headers to check for Content-Type (from curl request)
|
||||
* @param string $filename
|
||||
* 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 = '') {
|
||||
// logger('Photo: guess_image_type: '.$filename . ($headers?' from curl headers':''), LOGGER_DEBUG);
|
||||
$type = null;
|
||||
$m = null;
|
||||
|
||||
if($headers) {
|
||||
$hdrs=array();
|
||||
$hdrs = [];
|
||||
$h = explode("\n", $headers);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
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) {
|
||||
|
||||
$hash = basename($url);
|
||||
@ -566,18 +160,15 @@ function delete_thing_photo($url,$ob_hash) {
|
||||
if((! $ob_hash) || (strlen($hash) < 16))
|
||||
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),
|
||||
intval(PHOTO_THING),
|
||||
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
|
||||
* 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 string \b 5 => modification date
|
||||
*/
|
||||
|
||||
function import_xchan_photo($photo, $xchan, $thing = false, $force = false) {
|
||||
|
||||
$modified = '';
|
||||
$o = null;
|
||||
|
||||
$flags = (($thing) ? PHOTO_THING : PHOTO_XCHAN);
|
||||
$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) {
|
||||
$hash = photo_new_resource();
|
||||
}
|
||||
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)
|
||||
);
|
||||
} 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));
|
||||
if($r) {
|
||||
$hash = $r[0]['resource_id'];
|
||||
$modified = $r[0]['edited'];
|
||||
$type = $r[0]['mimetype'];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$hash = photo_new_resource();
|
||||
}
|
||||
}
|
||||
@ -628,13 +213,15 @@ function import_xchan_photo($photo,$xchan,$thing = false,$force = false) {
|
||||
$img_str = '';
|
||||
|
||||
if($photo) {
|
||||
$filename = basename($photo);
|
||||
|
||||
if($force || $modified == '') {
|
||||
$result = z_fetch_url($photo, true);
|
||||
}
|
||||
else {
|
||||
$h = array('headers' => array("If-Modified-Since: " . gmdate("D, d M Y H:i:s", strtotime($modified . "Z")) . " GMT"));
|
||||
} else {
|
||||
$h = [
|
||||
'headers' => [
|
||||
'If-Modified-Since: ' . gmdate('D, d M Y H:i:s', strtotime($modified . 'Z')) . ' GMT'
|
||||
]
|
||||
];
|
||||
$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()));
|
||||
if(is_null($type))
|
||||
$photo_failure = true;
|
||||
}
|
||||
elseif($result['return_code'] == 304) {
|
||||
} elseif($result['return_code'] == 304) {
|
||||
$photo = z_root() . '/photo/' . $hash . '-4';
|
||||
$thumb = z_root() . '/photo/' . $hash . '-5';
|
||||
$micro = z_root() . '/photo/' . $hash . '-6';
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$photo_failure = true;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
} else {
|
||||
$photo_failure = true;
|
||||
}
|
||||
|
||||
if(!$photo_failure && $result['return_code'] != 304) {
|
||||
$img = photo_factory($img_str, $type);
|
||||
@ -669,58 +253,48 @@ function import_xchan_photo($photo,$xchan,$thing = false,$force = false) {
|
||||
// crop out the sides
|
||||
$margin = $width - $height;
|
||||
$img->cropImage(300, ($margin / 2), 0, $height, $height);
|
||||
}
|
||||
elseif(($height / $width) > 1.2) {
|
||||
} elseif(($height / $width) > 1.2) {
|
||||
// crop out the bottom
|
||||
$margin = $height - $width;
|
||||
$img->cropImage(300, 0, 0, $width, $width);
|
||||
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$img->scaleImageSquare(300);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
} else {
|
||||
$photo_failure = true;
|
||||
}
|
||||
|
||||
$p = array(
|
||||
$p = [
|
||||
'xchan' => $xchan,
|
||||
'resource_id' => $hash,
|
||||
'filename' => basename($photo),
|
||||
'album' => $album,
|
||||
'photo_usage' => $flags,
|
||||
'imgscale' => 4,
|
||||
'edited' => $modified
|
||||
);
|
||||
'edited' => $modified,
|
||||
];
|
||||
|
||||
$r = $img->save($p);
|
||||
|
||||
if($r === false)
|
||||
$photo_failure = true;
|
||||
|
||||
$img->scaleImage(80);
|
||||
$p['imgscale'] = 5;
|
||||
|
||||
$r = $img->save($p);
|
||||
|
||||
if($r === false)
|
||||
$photo_failure = true;
|
||||
|
||||
$img->scaleImage(48);
|
||||
$p['imgscale'] = 6;
|
||||
|
||||
$r = $img->save($p);
|
||||
|
||||
if($r === false)
|
||||
$photo_failure = true;
|
||||
|
||||
$photo = z_root() . '/photo/' . $hash . '-4';
|
||||
$thumb = z_root() . '/photo/' . $hash . '-5';
|
||||
$micro = z_root() . '/photo/' . $hash . '-6';
|
||||
}
|
||||
else {
|
||||
logger('import_xchan_photo: invalid image from ' . $photo);
|
||||
} else {
|
||||
logger('Invalid image from ' . $photo);
|
||||
$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));
|
||||
}
|
||||
|
||||
logger('HTTP code: ' . $result['return_code'] . '; modified: ' . $modified . '; failure: ' . ($photo_failure ? 'yes' : 'no') . '; URL: ' . $photo, LOGGER_DEBUG);
|
||||
return(array($photo,$thumb,$micro,$type,$photo_failure,$modified));
|
||||
logger('HTTP code: ' . $result['return_code'] . '; modified: ' . $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) {
|
||||
$type = null;
|
||||
|
||||
if($photo) {
|
||||
$filename = basename($photo);
|
||||
|
||||
$result = z_fetch_url($photo, true);
|
||||
|
||||
if($result['success']) {
|
||||
$img_str = $result['body'];
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
logger('import_channel_photo: importing channel photo for ' . $uid, LOGGER_DEBUG);
|
||||
|
||||
$hash = photo_new_resource();
|
||||
logger('Importing channel photo for ' . $uid, LOGGER_DEBUG);
|
||||
|
||||
$photo_failure = false;
|
||||
|
||||
|
||||
$hash = photo_new_resource();
|
||||
$filename = $hash;
|
||||
|
||||
$img = photo_factory($photo, $type);
|
||||
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);
|
||||
|
||||
$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);
|
||||
|
||||
if($r === false)
|
||||
$photo_failure = true;
|
||||
|
||||
// thumb size
|
||||
$img->scaleImage(80);
|
||||
$p['imgscale'] = 5;
|
||||
|
||||
$r = $img->save($p);
|
||||
|
||||
if($r === false)
|
||||
$photo_failure = true;
|
||||
|
||||
// micro size
|
||||
$img->scaleImage(48);
|
||||
$p['imgscale'] = 6;
|
||||
|
||||
$r = $img->save($p);
|
||||
|
||||
if($r === false)
|
||||
$photo_failure = true;
|
||||
|
||||
}
|
||||
else {
|
||||
logger('import_channel_photo: invalid image.');
|
||||
} else {
|
||||
logger('Invalid image.');
|
||||
$photo_failure = true;
|
||||
}
|
||||
|
||||
//return(($photo_failure)? false : true);
|
||||
|
||||
if($photo_failure)
|
||||
return false;
|
||||
else
|
||||
return $hash;
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -356,7 +356,7 @@ function photo_upload($channel, $observer, $args) {
|
||||
|
||||
$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) {
|
||||
$scale = 1;
|
||||
|
@ -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 $error_text text of error
|
||||
* @param bool $uninstall uninstall plugin
|
||||
* @param string $notice UI visible text of error
|
||||
* @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){
|
||||
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.
|
||||
* use insert_hook($hookname,$function_name) to do this.
|
||||
*/
|
||||
|
||||
|
||||
function load_hooks() {
|
||||
|
||||
App::$hooks = [];
|
||||
@ -1085,10 +1085,11 @@ function get_markup_template($s, $root = '') {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @brief Test if a folder exists.
|
||||
*
|
||||
* @param string $folder
|
||||
* @return boolean|string
|
||||
* False if folder does not exist, or canonicalized absolute pathname
|
||||
*/
|
||||
function folder_exists($folder) {
|
||||
// Get canonicalized absolute pathname
|
||||
|
295
include/text.php
295
include/text.php
@ -44,8 +44,8 @@ function replace_macros($s, $r) {
|
||||
try {
|
||||
$output = $t->replace_macros($arr['template'], $arr['params']);
|
||||
} catch (Exception $e) {
|
||||
logger("Unable to render template: ".$e->getMessage());
|
||||
$output = "<h3>ERROR: there was an error creating the output.</h3>";
|
||||
logger('Unable to render template: ' . $e->getMessage());
|
||||
$output = '<h3>ERROR: there was an error creating the output.</h3>';
|
||||
}
|
||||
|
||||
return $output;
|
||||
@ -539,7 +539,14 @@ function paginate(&$a) {
|
||||
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 = '') {
|
||||
|
||||
if(! $more)
|
||||
@ -810,7 +817,7 @@ function activity_match($haystack,$needle) {
|
||||
* and strip the period from any tags which end with one.
|
||||
*
|
||||
* @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) {
|
||||
$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
|
||||
$s = preg_replace('/\[color=(.*?)\]/sm','',$s);
|
||||
|
||||
// skip anchors in URL
|
||||
$s = preg_replace('/\[url=(.*?)\]/sm','',$s);
|
||||
|
||||
// match any double quoted tags
|
||||
|
||||
if(preg_match_all('/([@#\!]\"\;.*?\"\;)/',$s,$match)) {
|
||||
@ -897,6 +907,7 @@ function tag_sort_length($a,$b) {
|
||||
function total_sort($a,$b) {
|
||||
if($a['total'] == $b['total'])
|
||||
return 0;
|
||||
|
||||
return(($b['total'] > $a['total']) ? 1 : (-1));
|
||||
}
|
||||
|
||||
@ -1001,9 +1012,15 @@ function contact_block() {
|
||||
'$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);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
@ -1090,7 +1107,7 @@ function searchbox($s,$id='search-box',$url='/search',$save = false) {
|
||||
* @return string
|
||||
*/
|
||||
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)=(.*?)\&\;(.*?)\>/ism",'<$1$2=$3&$4>',$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
|
||||
*
|
||||
* @param string $s
|
||||
* @param int $uid
|
||||
* @returns string
|
||||
*/
|
||||
function sslify($s) {
|
||||
|
||||
// Local photo cache
|
||||
$str = array(
|
||||
$str = [
|
||||
'body' => $s,
|
||||
'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);
|
||||
|
||||
$s = $str['body'];
|
||||
@ -1219,7 +1240,11 @@ function get_mood_verbs() {
|
||||
/**
|
||||
* @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) {
|
||||
|
||||
@ -1297,6 +1322,11 @@ function list_smilies($default_only = false) {
|
||||
if($default_only)
|
||||
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);
|
||||
|
||||
return $params;
|
||||
@ -1622,6 +1652,10 @@ function generate_named_map($location) {
|
||||
|
||||
function prepare_body(&$item,$attach = false,$opts = false) {
|
||||
|
||||
/**
|
||||
* @hooks prepare_body_init
|
||||
* * \e array \b item
|
||||
*/
|
||||
call_hooks('prepare_body_init', $item);
|
||||
|
||||
$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);
|
||||
|
||||
$prep_arr = array(
|
||||
$prep_arr = [
|
||||
'item' => $item,
|
||||
'html' => $event ? $event['content'] : $s,
|
||||
'event' => $event['header'],
|
||||
'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);
|
||||
|
||||
$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 $content_type (optional) default text/bbcode
|
||||
* @param boolean $cache (optional) default false
|
||||
*
|
||||
* @param string $content_type (optional)
|
||||
* 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
|
||||
* The parsed $text as prepared HTML.
|
||||
*/
|
||||
function prepare_text($text, $content_type = 'text/bbcode', $opts = false) {
|
||||
|
||||
|
||||
switch($content_type) {
|
||||
case 'text/plain':
|
||||
$s = escape_tags($text);
|
||||
@ -1780,7 +1827,7 @@ function prepare_text($text, $content_type = 'text/bbcode', $opts = false) {
|
||||
require_once('include/bbcode.php');
|
||||
|
||||
if(stristr($text, '[nosmile]'))
|
||||
$s = bbcode($text, [ 'cache' => $cache ]);
|
||||
$s = bbcode($text, ((is_array($opts)) ? $opts : [] ));
|
||||
else
|
||||
$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
|
||||
* similar to ids_to_querystr, but allows a different delimiter instead of a db-quote option
|
||||
* empty elements (evaluated after trim()) are ignored.
|
||||
* @param $arr array
|
||||
* @param $elm array key to extract from sub-array
|
||||
* @param $delim string default ','
|
||||
* @param $each filter function to apply to each element before evaluation, default is 'trim'.
|
||||
* @brief Extract unique individual elements from an array of arrays and return
|
||||
* them as a string separated by a delimiter.
|
||||
*
|
||||
* Similar to ids_to_querystr, but allows a different delimiter instead of a
|
||||
* db-quote option empty elements (evaluated after trim()) are ignored.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
function array_elm_to_str($arr, $elm, $delim = ',', $each = 'trim') {
|
||||
|
||||
$tmp = [];
|
||||
|
||||
if($arr && is_array($arr)) {
|
||||
foreach($arr as $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);
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -2501,7 +2554,7 @@ function website_portation_tools() {
|
||||
$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'),
|
||||
'$import_label' => t('Import website...'),
|
||||
'$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_hint' => t('Enter a path to a cloud files destination.'),
|
||||
'$cloud_export_select' => t('Specify folder'),
|
||||
));
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2578,8 +2631,9 @@ function extra_query_args() {
|
||||
* @param boolean $in_network default true
|
||||
* @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;
|
||||
$r = null;
|
||||
$match = array();
|
||||
@ -2635,21 +2689,20 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
|
||||
|
||||
$str_tags .= $newtag;
|
||||
}
|
||||
return [
|
||||
return [ [
|
||||
'replaced' => $replaced,
|
||||
'termtype' => $termtype,
|
||||
'term' => $basetag,
|
||||
'url' => $url,
|
||||
'contact' => []
|
||||
];
|
||||
|
||||
'contact' => [],
|
||||
'access_tag' => '',
|
||||
]];
|
||||
}
|
||||
|
||||
// END hashtags
|
||||
|
||||
// BEGIN mentions
|
||||
|
||||
|
||||
if ( in_array($termtype, [ TERM_MENTION, TERM_FORUM ] )) {
|
||||
|
||||
// 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);
|
||||
|
||||
//is it already replaced?
|
||||
if(strpos($tag,'[zrl=') || strpos($tag,'[url='))
|
||||
if(strpos($tag,"[zrl=") || strpos($tag,"[url="))
|
||||
return $replaced;
|
||||
|
||||
// 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($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)
|
||||
);
|
||||
@ -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
|
||||
|
||||
$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),
|
||||
intval($profile_uid)
|
||||
);
|
||||
@ -2710,7 +2763,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
|
||||
|
||||
if((! $r) && strpos($newname,'@')) {
|
||||
$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)
|
||||
);
|
||||
}
|
||||
@ -2719,7 +2772,7 @@ function handle_tag($a, &$body, &$access_tag, &$str_tags, $profile_uid, $tag, $i
|
||||
|
||||
if(! $r) {
|
||||
$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 . '@%')),
|
||||
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
|
||||
|
||||
$channel = App::get_channel();
|
||||
|
||||
if($r) {
|
||||
$profile = $r[0]['xchan_url'];
|
||||
$newname = $r[0]['xchan_name'];
|
||||
foreach($r as $xc) {
|
||||
$profile = $xc['xchan_url'];
|
||||
$newname = $xc['xchan_name'];
|
||||
// add the channel's xchan_hash to $access_tag if exclusive
|
||||
if($exclusive) {
|
||||
$access_tag .= 'cid:' . $r[0]['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);
|
||||
}
|
||||
}
|
||||
}
|
||||
$access_tag = 'cid:' . $xc['xchan_hash'];
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
$fn_results[] = [
|
||||
'replaced' => $replaced,
|
||||
'termtype' => $termtype,
|
||||
'term' => $newname,
|
||||
'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;
|
||||
$tagged = [];
|
||||
$results = [];
|
||||
|
||||
$tags = get_tags($body);
|
||||
|
||||
if(count($tags)) {
|
||||
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);
|
||||
if($success['replaced']) $tagged[] = $tag;
|
||||
foreach($success as $handled_tag) {
|
||||
$results[] = [ 'success' => $handled_tag ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3093,7 +3194,13 @@ function pdl_selector($uid, $current='') {
|
||||
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);
|
||||
|
||||
$entries = $arr['entries'];
|
||||
@ -3168,7 +3275,6 @@ function text_highlight($s, $lang) {
|
||||
'language' => $lang,
|
||||
'success' => false
|
||||
];
|
||||
|
||||
/**
|
||||
* @hooks text_highlight
|
||||
* * \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) {
|
||||
require_once('vendor/simplepie/simplepie/idn/idna_convert.class.php');
|
||||
$x = new idna_convert(['encoding' => 'utf8']);
|
||||
return $x->decode($s);
|
||||
|
||||
return $x->decode($s);
|
||||
}
|
||||
|
||||
|
||||
@ -3519,4 +3629,3 @@ function new_uuid() {
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
|
@ -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'
|
||||
* @param array $recipients
|
||||
* envelope information, array ( 'guid' => string, 'guid_sig' => string ); empty for public posts
|
||||
* @param string msg
|
||||
* @param string $msg
|
||||
* optional message
|
||||
* @param string $remote_key
|
||||
* 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
|
||||
|
||||
/// @FIXME $recipients is undefined here.
|
||||
$ret['message'] .= 'recipients ' . print_r($recipients,true) . EOL;
|
||||
|
||||
if ($data['recipients']) {
|
||||
|
@ -79,7 +79,7 @@ $HTTP["url"] =~ "\.(out|log|htaccess)$" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
|
||||
$HTTP["url"] =~ "(^|/)\.git|(^|/)store" {
|
||||
$HTTP["url"] =~ "(^|/)\.git|(^|/)store|(^|/)util" {
|
||||
url.access-deny = ("")
|
||||
}
|
||||
|
||||
|
@ -141,5 +141,10 @@ server {
|
||||
deny all;
|
||||
}
|
||||
|
||||
#deny access to util
|
||||
location ~ /util {
|
||||
deny all;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
147
tests/unit/Photo/PhotoGdTest.php
Normal file
147
tests/unit/Photo/PhotoGdTest.php
Normal 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());
|
||||
}
|
||||
|
||||
}
|
@ -106,10 +106,9 @@ class GetTagsTest extends PHPUnit_Framework_TestCase {
|
||||
|
||||
$tags=get_tags($text);
|
||||
|
||||
$inform='';
|
||||
$str_tags='';
|
||||
foreach($tags as $tag) {
|
||||
handle_tag($this->a, $text, $inform, $str_tags, 11, $tag);
|
||||
handle_tag($text, $str_tags, 11, $tag);
|
||||
}
|
||||
|
||||
//correct tags found?
|
||||
@ -117,7 +116,6 @@ class GetTagsTest extends PHPUnit_Framework_TestCase {
|
||||
$this->assertTrue(in_array("@Mike", $tags));
|
||||
|
||||
//correct output from handle_tag?
|
||||
$this->assertEquals("cid:15", $inform);
|
||||
$this->assertEquals("@[url=http://justatest.de]Mike Lastname[/url]", $str_tags);
|
||||
$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->assertTrue(in_array("@Mike.because", $tags));
|
||||
|
||||
$inform='';
|
||||
$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.
|
||||
// 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("hi @[url=http://justatest.de]Mike Lastname[/url].because", $text);
|
||||
|
||||
$this->assertEquals("", $inform);
|
||||
$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("#test_case", $tags));
|
||||
|
||||
$inform='';
|
||||
$str_tags='';
|
||||
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("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
|
||||
$this->assertTrue(in_array("@mike+15 id", $tags));
|
||||
|
||||
$inform='';
|
||||
$str_tags='';
|
||||
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("@[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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
39
tests/unit/includes/PhotodriverTest.php
Normal file
39
tests/unit/includes/PhotodriverTest.php
Normal 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.');
|
||||
}
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
INPUT = README.md index.php boot.php include/ install/ util/ view/ Zotlabs/
|
||||
RECURSIVE = YES
|
||||
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_PATTERNS = *smarty3* *strings.php *.out *test*
|
||||
OUTPUT_DIRECTORY = doc
|
||||
@ -33,5 +34,6 @@ INTERACTIVE_SVG = YES
|
||||
CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = NO
|
||||
# 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 = "php util/Doxygen_phpvarfilter.php"
|
||||
|
60
util/admins
Executable file
60
util/admins
Executable 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])
|
||||
);
|
||||
}
|
3333
util/hmessages.po
3333
util/hmessages.po
File diff suppressed because it is too large
Load Diff
7
vendor/composer/autoload_classmap.php
vendored
7
vendor/composer/autoload_classmap.php
vendored
@ -811,6 +811,7 @@ return array(
|
||||
'Zotlabs\\Access\\PermissionRoles' => $baseDir . '/Zotlabs/Access/PermissionRoles.php',
|
||||
'Zotlabs\\Access\\Permissions' => $baseDir . '/Zotlabs/Access/Permissions.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\\Cli_suggest' => $baseDir . '/Zotlabs/Daemon/Cli_suggest.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\\Techlevels' => $baseDir . '/Zotlabs/Lib/Techlevels.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\\Verify' => $baseDir . '/Zotlabs/Lib/Verify.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\\Site' => $baseDir . '/Zotlabs/Module/Admin/Site.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\\Appman' => $baseDir . '/Zotlabs/Module/Appman.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\\Email_resend' => $baseDir . '/Zotlabs/Module/Email_resend.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\\Events' => $baseDir . '/Zotlabs/Module/Events.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\\Zotfeed' => $baseDir . '/Zotlabs/Module/Zotfeed.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\\SimpleTemplate' => $baseDir . '/Zotlabs/Render/SimpleTemplate.php',
|
||||
'Zotlabs\\Render\\SmartyInterface' => $baseDir . '/Zotlabs/Render/SmartyInterface.php',
|
||||
|
7
vendor/composer/autoload_static.php
vendored
7
vendor/composer/autoload_static.php
vendored
@ -979,6 +979,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
|
||||
'Zotlabs\\Access\\PermissionRoles' => __DIR__ . '/../..' . '/Zotlabs/Access/PermissionRoles.php',
|
||||
'Zotlabs\\Access\\Permissions' => __DIR__ . '/../..' . '/Zotlabs/Access/Permissions.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\\Cli_suggest' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cli_suggest.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\\Techlevels' => __DIR__ . '/../..' . '/Zotlabs/Lib/Techlevels.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\\Verify' => __DIR__ . '/../..' . '/Zotlabs/Lib/Verify.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\\Site' => __DIR__ . '/../..' . '/Zotlabs/Module/Admin/Site.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\\Appman' => __DIR__ . '/../..' . '/Zotlabs/Module/Appman.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\\Email_resend' => __DIR__ . '/../..' . '/Zotlabs/Module/Email_resend.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\\Events' => __DIR__ . '/../..' . '/Zotlabs/Module/Events.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\\Zotfeed' => __DIR__ . '/../..' . '/Zotlabs/Module/Zotfeed.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\\SimpleTemplate' => __DIR__ . '/../..' . '/Zotlabs/Render/SimpleTemplate.php',
|
||||
'Zotlabs\\Render\\SmartyInterface' => __DIR__ . '/../..' . '/Zotlabs/Render/SmartyInterface.php',
|
||||
|
@ -400,12 +400,12 @@ function viewsrc(id) {
|
||||
function showHideComments(id) {
|
||||
if( $('#collapsed-comments-' + id).is(':visible')) {
|
||||
$('#collapsed-comments-' + id + ' .autotime').timeago('dispose');
|
||||
$('#collapsed-comments-' + id).slideUp();
|
||||
$('#collapsed-comments-' + id).hide();
|
||||
$('#hide-comments-' + id).html(aStr.showmore);
|
||||
$('#hide-comments-total-' + id).show();
|
||||
} else {
|
||||
$('#collapsed-comments-' + id + ' .autotime').timeago();
|
||||
$('#collapsed-comments-' + id).slideDown();
|
||||
$('#collapsed-comments-' + id).show();
|
||||
$('#hide-comments-' + id).html(aStr.showfewer);
|
||||
$('#hide-comments-total-' + id).hide();
|
||||
}
|
||||
@ -734,6 +734,7 @@ function updateConvItems(mode,data) {
|
||||
title.replace(/\s+$/, '');
|
||||
if (title) {
|
||||
savedTitle = title + " " + savedTitle;
|
||||
document.title = title;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -769,7 +770,7 @@ function updateConvItems(mode,data) {
|
||||
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;
|
||||
|
||||
if (bimgcount) {
|
||||
@ -845,10 +846,10 @@ function collapseHeight() {
|
||||
});
|
||||
|
||||
var collapsedContentHeight = Math.ceil($("#region_2").height());
|
||||
contentHeightDiff = origContentHeight - collapsedContentHeight;
|
||||
contentHeightDiff = liking ? 0 : origContentHeight - collapsedContentHeight;
|
||||
console.log('collapseHeight() - contentHeightDiff: ' + contentHeightDiff + 'px');
|
||||
|
||||
if(i){
|
||||
if(i && !liking){
|
||||
var sval = position - cDiff + ($(".divgrow-showmore").outerHeight() * i);
|
||||
console.log('collapsed above viewport count: ' + i);
|
||||
$(window).scrollTop(sval);
|
||||
@ -999,7 +1000,7 @@ function liveUpdate(notify_id) {
|
||||
$("#profile-jot-text-loading").hide();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
2353
view/ru/hmessages.po
2353
view/ru/hmessages.po
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,7 @@ App::$strings["Set the background color of items"] = "Цвет фона элем
|
||||
App::$strings["Set the background color of comments"] = "Цвет фона комментариев";
|
||||
App::$strings["Set font-size for the entire application"] = "Установить системный размер шрифта";
|
||||
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["Example: 4px"] = "Например: 4px";
|
||||
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["Please enter the required 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["your registration password"] = "ваш пароль регистрации";
|
||||
App::$strings["Registration details for %s"] = "Регистрационные данные для %s";
|
||||
@ -729,7 +729,6 @@ App::$strings["Directory Options"] = "Параметры каталога";
|
||||
App::$strings["Safe Mode"] = "Безопасный режим";
|
||||
App::$strings["Public Forums Only"] = "Только публичные форумы";
|
||||
App::$strings["This Website Only"] = "Только этот веб-сайт";
|
||||
App::$strings["view full size"] = "посмотреть в полный размер";
|
||||
App::$strings["Friendica"] = "";
|
||||
App::$strings["OStatus"] = "";
|
||||
App::$strings["GNU-Social"] = "";
|
||||
@ -999,7 +998,7 @@ App::$strings["Privacy group: "] = "Группа безопасности: ";
|
||||
App::$strings["Invalid channel."] = "Недействительный канал.";
|
||||
App::$strings["Token verification failed."] = "Не удалось выполнить проверку токена.";
|
||||
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["Validation token"] = "Проверочный токен";
|
||||
App::$strings["No channel."] = "Канала нет.";
|
||||
@ -1117,6 +1116,8 @@ App::$strings["Posts and comments"] = "Публикации и коммента
|
||||
App::$strings["Only posts"] = "Только публикации";
|
||||
App::$strings["vcard"] = "vCard";
|
||||
App::$strings["You must be logged in to see this page."] = "Вы должны авторизоваться, чтобы увидеть эту страницу.";
|
||||
App::$strings["🔁 Repeated %1\$s's %2\$s"] = "🔁 Повторил %1\$s %2\$s";
|
||||
App::$strings["Post repeated"] = "Публикация повторяется";
|
||||
App::$strings["No more system notifications."] = "Нет новых оповещений системы.";
|
||||
App::$strings["System Notifications"] = "Системные оповещения ";
|
||||
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["Default minimum affinity level"] = "Минимальный уровень сходства по умолчанию.";
|
||||
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["Addon Settings"] = "Настройки расширений";
|
||||
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["This is required for message delivery to work."] = "Это необходимо для функционирования доставки сообщений.";
|
||||
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["You can adjust these settings in the server php.ini file."] = "Вы можете изменить эти настройки в файле php.ini на сервере.";
|
||||
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 write to my wiki pages"] = "Может редактировать мои вики-страницы";
|
||||
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 like/dislike profiles and profile things"] = "Может комментировать или отмечать как нравится/ненравится мой профиль";
|
||||
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["Pin to navbar"] = "Добавить на панель навигации";
|
||||
App::$strings["Unpin from navbar"] = "Удалить с панели навигации";
|
||||
App::$strings["I will attend"] = "Я буду присутствовать";
|
||||
App::$strings["I will not attend"] = "Я не буду присутствовать";
|
||||
App::$strings["Privacy conflict. Discretion advised."] = "Конфиликт настроек конфиденциальности.";
|
||||
App::$strings["I will attend"] = "Я буду участвовать";
|
||||
App::$strings["I will not attend"] = "Я не буду участвовать";
|
||||
App::$strings["I might attend"] = "Я возможно буду присутствовать";
|
||||
App::$strings["I agree"] = "Я согласен";
|
||||
App::$strings["I disagree"] = "Я не согласен";
|
||||
App::$strings["I abstain"] = "Я воздержался";
|
||||
App::$strings["Add Tag"] = "Добавить тег";
|
||||
App::$strings["Repeat This"] = "Повторить это";
|
||||
App::$strings["repeat"] = "повторение";
|
||||
App::$strings["Share This"] = "Поделиться этим";
|
||||
App::$strings["share"] = "поделиться";
|
||||
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 file storage and photos"] = "Это настройка по умолчанию для тех, кто может просматривать ваше хранилище файлов и фотографий";
|
||||
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["1. Novice - not skilled but willing to learn"] = "1. Новичок - не опытный, но желающий учиться";
|
||||
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["Use markdown for editing posts"] = "Использовать язык разметки Markdown для редактирования публикаций";
|
||||
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 password"] = "Пароль 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 Creator and Tech Lead"] = "Создатель проекта и технический руководитель";
|
||||
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["Fuzzloc Settings"] = "Настройки Fuzzloc";
|
||||
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["Photos imported"] = "Фотографии импортированы";
|
||||
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["Maximum count to import"] = "Максимальное количество для импорта";
|
||||
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["Gallery"] = "Галерея";
|
||||
App::$strings["Photo Gallery"] = "Фотогалерея";
|
||||
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["Friendica API Path"] = "Путь к Friendica 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["Post to Insane Journal"] = "Опубликовать в 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 password"] = "Пароль 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["file"] = "файл";
|
||||
App::$strings["Post to Twitter"] = "Опубликовать в Twitter";
|
||||
App::$strings["Submit Settings"] = "Отправить настройки";
|
||||
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["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["Copy the PIN from Twitter here"] = "Скопируйте PIN-код из Twitter здесь";
|
||||
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["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["Maximum tweet length"] = "Максимальная длина твита";
|
||||
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["Clear OAuth configuration"] = "Очистить конфигурацию OAuth";
|
||||
App::$strings["Twitter Post Settings"] = "Настройки публикаций в Twitter";
|
||||
App::$strings["Submit Settings"] = "Отправить настройки";
|
||||
App::$strings["Twitter Crosspost Connector"] = "Пересылка публикаций Twitter";
|
||||
App::$strings["Edit your profile and change settings."] = "Отредактировать ваш профиль и изменить настройки.";
|
||||
App::$strings["Click here to see activity from your connections."] = "Нажмите сюда для отображения активности ваши контактов.";
|
||||
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["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["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["Share photos, links, location, etc."] = "Поделиться фотографией, ссылками, местоположение и т.п.";
|
||||
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["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["Superblock App"] = "Приложение Superblock";
|
||||
App::$strings["Block channels"] = "Заблокировать каналы";
|
||||
App::$strings["superblock settings updated"] = "Настройки Superblock обновлены.";
|
||||
App::$strings["Currently blocked"] = "В настоящее время заблокирован";
|
||||
@ -2997,8 +3017,9 @@ App::$strings["Include marker on map"] = "Включите маркер на к
|
||||
App::$strings["Include a marker on the map."] = "Включить маркер на карте";
|
||||
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 App"] = "Приложение Photo Cache";
|
||||
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["Activate addon"] = "Активировать расширение";
|
||||
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["Enable frontend JavaScript error tracking"] = "Включить отслеживание ошибок JavaScript на фронтенде.";
|
||||
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["return to the featured settings page"] = "Вернутся к странице настроек";
|
||||
App::$strings["Post to Pump.io"] = "Опубликовать в Pump.io";
|
||||
App::$strings["Pump.io Settings saved."] = "Настройки Pump.io сохранены.";
|
||||
App::$strings["Pump.io Crosspost Connector App"] = "Приложение \"Пересылка публикаций Pump.io\"";
|
||||
App::$strings["Relay public posts to pump.io"] = "Пересылает общедоступные публикации в Pump.io";
|
||||
App::$strings["Pump.io servername"] = "Имя сервера Pump.io";
|
||||
App::$strings["Without \"http://\" or \"https://\""] = "Без \"http://\" или \"https://\"";
|
||||
App::$strings["Pump.io username"] = "Имя пользователя Pump.io";
|
||||
App::$strings["Without the servername"] = "без имени сервера";
|
||||
App::$strings["You are not authenticated to pumpio"] = "Вы не аутентифицированы на 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["Should posts be public"] = "Публикации должны быть общедоступными";
|
||||
App::$strings["Mirror all public posts"] = "Отображать все общедоступные публикации";
|
||||
App::$strings["Pump.io Post Settings"] = "Настройки публикаций в Pump.io";
|
||||
App::$strings["PumpIO Settings saved."] = "Настройки публикаций в Pump.io сохранены.";
|
||||
App::$strings["Nsabait Settings updated."] = "Настройки Nsabait обновлены";
|
||||
App::$strings["Enable NSAbait Plugin"] = "Включить плагин NSAbait";
|
||||
App::$strings["NSAbait Settings"] = "Настройки Nsabait";
|
||||
App::$strings["Pump.io Crosspost Connector"] = "Пересылка публикаций Pump.io";
|
||||
App::$strings["You are now authenticated to pumpio."] = "Вы аутентифицированы в Pump.io";
|
||||
App::$strings["return to the featured settings page"] = "Вернутся к странице настроек";
|
||||
App::$strings["Post to Pump.io"] = "Опубликовать в Pump.io";
|
||||
App::$strings["NSA Bait App"] = "Приложение NSA Bait";
|
||||
App::$strings["Make yourself a political target"] = "Сделать себя политической мишенью";
|
||||
App::$strings["Send test email"] = "Отправить тестовый email";
|
||||
App::$strings["No recipients found."] = "Получателей не найдено.";
|
||||
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["Until modified date yyyy-mm-dd"] = "Заканчивая датой изменений yyyy-mm-dd";
|
||||
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 password"] = "Пароль 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["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["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["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";
|
||||
@ -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["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["GNU-Social Crosspost Connector"] = "";
|
||||
App::$strings["GNU-Social Crosspost Connector"] = "Подключение пересылки публикаций GNU Social";
|
||||
App::$strings["Post to GNU social"] = "Опубликовать в GNU Social";
|
||||
App::$strings["API URL"] = "";
|
||||
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["The error message was:"] = "Сообщение об ошибке было:";
|
||||
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["Examples: "apps", "network?f=&gid=37" (privacy collection), "channel" or "notifications/system" (leave blank for default network page (grid)."] = "Примеры: "apps", "network?f=&gid=37" (privacy collection), "channel" or "notifications/system" (оставьте пустым для для страницы сети по умолчанию).";
|
||||
App::$strings["Startpage"] = "Стартовая страница";
|
||||
@ -3250,9 +3274,10 @@ App::$strings["Invalid Payment Type. Please start again."] = "Недейств
|
||||
App::$strings["Order not found"] = "Заказ не найден";
|
||||
App::$strings["Federate"] = "";
|
||||
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["NoFed Settings"] = "Настройки NoFed";
|
||||
App::$strings["No Federation"] = "";
|
||||
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["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["Gravatar settings updated."] = "Настройки Gravatar обновлены.";
|
||||
App::$strings["WYSIWYG status editor"] = "WYSIWYG редактор статуса ";
|
||||
App::$strings["WYSIWYG Status App"] = "Приложение \"WYSIWYG статус\"";
|
||||
App::$strings["WYSIWYG Status"] = "WYSIWYG статус";
|
||||
App::$strings["Planets Settings updated."] = "Настройки Planets обновлены.";
|
||||
App::$strings["Enable Planets Plugin"] = "Включить плагин Planets";
|
||||
App::$strings["Planets Settings"] = "Настройки Planets";
|
||||
App::$strings["Random Planet App"] = "Приложение \"Случайная планета\"";
|
||||
App::$strings["Set a random planet from the Star Wars Empire as your location when posting"] = "Установить случайную планету из Империи Звездных Войн в качестве вашего местоположения при публикации";
|
||||
App::$strings["Add some colour to tag clouds"] = "Добавить немного цвета для облака тегов";
|
||||
App::$strings["Rainbow Tag App"] = "Приложение \"Радуга тегов\"";
|
||||
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 must be a local channel to create a game."] = "Ваш канал должен быть локальным чтобы создать игру.";
|
||||
App::$strings["You must select one opponent that is not yourself."] = "Вы должны выбрать противника который не является вами.";
|
||||
@ -3292,19 +3318,42 @@ App::$strings["Enable notifications"] = "Включить оповещения";
|
||||
App::$strings["Follow"] = "Отслеживать";
|
||||
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["GNU-Social Protocol App"] = "Приложение \"Протокол GNU-Social\"";
|
||||
App::$strings["GNU-Social Protocol"] = "Протокол GNU-Social";
|
||||
App::$strings["Message to display on every page on this server"] = "Отображаемое сообщение на каждой странице на этом сервере.";
|
||||
App::$strings["Pageheader Settings"] = "Настройки шапки страницы";
|
||||
App::$strings["TOTP Two-Step Verification"] = "Двухэтапная верификация TOTP";
|
||||
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["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["Send email to all members"] = "Отправить email всем участникам";
|
||||
App::$strings["%1\$d of %2\$d messages sent."] = "%1\$d из %2\$d сообщений отправлено.";
|
||||
App::$strings["Send email to all hub members."] = "Отправить email всем участникам узла.";
|
||||
App::$strings["Sender Email address"] = "Адрес электронной почты отправителя";
|
||||
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["Smileybutton Settings"] = "Настройки кнопки смайликов";
|
||||
App::$strings["Smileybutton Settings"] = "Настройки кнопки со смайликами";
|
||||
App::$strings["Friendica Photo Album Import"] = "Импортировать альбом фотографий Friendica";
|
||||
App::$strings["This will import all your Friendica photo albums to this Red channel."] = "Это позволит импортировать все ваши альбомы фотографий 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["Message sent to %s. New account registration: %s"] = "Сообщение отправлено в %s. Регистрация нового аккаунта: %s";
|
||||
App::$strings["Send your identity to all websites"] = "Отправить ваши данные на все веб-сайты";
|
||||
App::$strings["Sendzid App"] = "Приложение \"Отправить ZID\"";
|
||||
App::$strings["Send ZID"] = "Отправить ZID";
|
||||
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["drunk"] = "пьяный";
|
||||
App::$strings["horny"] = "возбуждённый";
|
||||
@ -3376,13 +3429,9 @@ App::$strings["victorious"] = "победивший";
|
||||
App::$strings["defeated"] = "проигравший";
|
||||
App::$strings["envious"] = "завидует";
|
||||
App::$strings["jealous"] = "ревнует";
|
||||
App::$strings["Who viewed my channel/profile"] = "Кто смотрел мой канал / профиль";
|
||||
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["Enable Visage Visitor Logging"] = "Включить журналирование посетителей Visage";
|
||||
App::$strings["Visage Settings"] = "Настройки Visage";
|
||||
App::$strings["Channels to auto connect"] = "Каналы для автоматического подключения";
|
||||
App::$strings["Comma separated list"] = "Список, разделённый запятыми";
|
||||
App::$strings["Popular Channels"] = "Популярные каналы";
|
||||
@ -3399,23 +3448,25 @@ App::$strings["Chord name: example: Em7"] = "Наименование аккор
|
||||
App::$strings["Show for left handed stringing"] = "Показывать струны для левшей";
|
||||
App::$strings["Quick Reference"] = "Быстрая ссылка";
|
||||
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 site URL"] = "URL сайта Libertree";
|
||||
App::$strings["Post to Libertree by default"] = "Публиковать в Libertree по умолчанию";
|
||||
App::$strings["Libertree Post Settings"] = "Настройки публикаций в Libertree";
|
||||
App::$strings["Libertree Settings saved."] = "Настройки Libertree сохранены.";
|
||||
App::$strings["Libertree Crosspost Connector"] = "Пересылка публикаций Libertree";
|
||||
App::$strings["Channel is required."] = "Необходим канал.";
|
||||
App::$strings["Hubzilla Crosspost Connector Settings saved."] = "";
|
||||
App::$strings["Relay public postings to another Hubzilla channel"] = "Ретранслировать общедоступные публикации в другой канал Hubzilla";
|
||||
App::$strings["Hubzilla Crosspost Connector Settings saved."] = "Настройки пересылки публикаций Hubzilla сохранены.";
|
||||
App::$strings["Relay public postings to another Hubzilla channel"] = "Пересылает общедоступные публикации в другой канал Hubzilla";
|
||||
App::$strings["Send public postings to Hubzilla channel by default"] = "Отправлять общедоступные публикации в канал Hubzilla по умолчанию";
|
||||
App::$strings["Hubzilla API Path"] = "Путь к Hubzilla API";
|
||||
App::$strings["Hubzilla login name"] = "Имя входа Hubzilla";
|
||||
App::$strings["Hubzilla channel name"] = "Название канала Hubzilla";
|
||||
App::$strings["Hubzilla Crosspost Connector"] = "";
|
||||
App::$strings["Post to Hubzilla"] = "";
|
||||
App::$strings["Hubzilla Crosspost Connector"] = "Пересылка публикаций Hubzilla";
|
||||
App::$strings["Post to Hubzilla"] = "Опубликовать в Hubzilla";
|
||||
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["Activitypub Protocol App"] = "Приложение \"Протокол 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["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["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["Diaspora Protocol App"] = "Приложение \"Протокол 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["Sign and forward posts and comments with no existing Diaspora signature"] = "Подписывать и отправлять публикации и комментарии с несуществующей подписью Diaspora";
|
||||
App::$strings["Followed hashtags (comma separated, do not include the #)"] = "Отслеживаемые теги (через запятую, исключая #)";
|
||||
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["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["WordPress username"] = "Имя пользователя WordPress";
|
||||
App::$strings["WordPress password"] = "Пароль WordPress";
|
||||
@ -3444,15 +3498,17 @@ App::$strings["Wordpress Post"] = "Публикация в WordPress";
|
||||
App::$strings["Possible adult content"] = "Возможно содержимое для взрослых";
|
||||
App::$strings["%s - view"] = "%s - просмотр";
|
||||
App::$strings["NSFW Settings saved."] = "Настройки NSFW сохранены.";
|
||||
App::$strings["NSFW App"] = "Приложение NSFW";
|
||||
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["Comma separated list of keywords to hide"] = "Список ключевых слов для скрытия, через запятую";
|
||||
App::$strings["Word, /regular-expression/, lang=xx, lang!=xx"] = "слово, /регулярное_выражение/, lang=xx, lang!=xx";
|
||||
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["A setting"] = "Настройка";
|
||||
App::$strings["Skeleton Settings"] = "Настройки скелета";
|
||||
App::$strings["__ctx:opensearch__ Search %1\$s (%2\$s)"] = "Искать %1\$s (%2\$s)";
|
||||
App::$strings["__ctx:opensearch__ \$Projectname"] = "";
|
||||
App::$strings["Search \$Projectname"] = "Поиск \$Projectname";
|
||||
App::$strings["Cover Photo"] = "";
|
||||
|
@ -146,6 +146,9 @@
|
||||
{{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>
|
||||
{{/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}}
|
||||
<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}}
|
||||
|
@ -139,6 +139,9 @@
|
||||
{{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>
|
||||
{{/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}}
|
||||
<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}}
|
||||
|
@ -13,6 +13,7 @@
|
||||
<input type='hidden' name='form_security_token' value='{{$form_security_token_edit}}'>
|
||||
{{include file="field_input.tpl" field=$gname}}
|
||||
{{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">
|
||||
{{$delete}}
|
||||
</a>
|
||||
|
@ -198,16 +198,24 @@ var activeCommentText = '';
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
window.location.href = 'rpost?f=&post_id='+id;
|
||||
}
|
||||
else {
|
||||
|
||||
if ($('#jot-popup').length != 0) $('#jot-popup').show();
|
||||
|
||||
$('#like-rotator-' + id).show();
|
||||
$.get('{{$baseurl}}/share/' + id, function(data) {
|
||||
$.get('{{$baseurl}}/embed/' + id, function(data) {
|
||||
if (!editor) $("#profile-jot-text").val("");
|
||||
initEditor(function(){
|
||||
addeditortext(data);
|
||||
|
@ -137,8 +137,11 @@
|
||||
<i id="profile-nocomment" class="fa fa-comments jot-icons"></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
{{if $custommoretoolsbuttons}}
|
||||
{{$custommoretoolsbuttons}}
|
||||
{{/if}}
|
||||
</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">
|
||||
<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>
|
||||
@ -176,6 +179,8 @@
|
||||
{{if $feature_nocomment}}
|
||||
<a class="dropdown-item" href="#" onclick="toggleNoComment(); return false;"><i id="profile-nocomment-sub" class="fa fa-comments"></i> {{$nocommenttitlesub}}</a>
|
||||
{{/if}}
|
||||
<hr />
|
||||
{{$custommoretoolsdropdown}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
@ -186,6 +191,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<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}}
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="preview_post();return false;" title="{{$preview}}">
|
||||
<i class="fa fa-eye jot-icons" ></i>
|
||||
|
@ -13,4 +13,3 @@
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
|
Reference in New Issue
Block a user