diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 000000000..4592266d8 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,36 @@ +codecov: + branch: develop + +comment: off + +coverage: + status: + patch: + default: off + source: + target: 80% + flags: source + backend: + target: 80% + flags: backend + project: + default: off + source: + flags: source + backend: + flags: backend + +flags: + source: + paths: + - src/ + backend: + paths: + - mod/ + - include/ + binary: + paths: + - bin/ + tests: + paths: + - tests/ diff --git a/.gitattributes b/.gitattributes index 4be1c9185..18ba9e075 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -# Disable LF normalization for all files -* -text \ No newline at end of file +# Disable LF normalization for all files +* -text diff --git a/.gitignore b/.gitignore index 9e6504184..db1c3c130 100644 --- a/.gitignore +++ b/.gitignore @@ -1,61 +1,71 @@ -favicon.* -.htconfig.php -.htpreconfig.php -\#* -include/jquery-1.4.2.min.js -*.log -*.out -*.version* -favicon.* -home.html -addon -*.orig -*~ -robots.txt - -#ignore documentation, it should be newly built -doc/html - -#ignore reports, should be generted with every build -report/ - -#ignore config files from eclipse, we don't want IDE files in our repository -.project -.buildpath -.externalToolBuilders -.settings -#ignore OSX .DS_Store files -.DS_Store - -/nbproject/private/ - -#ignore smarty cache -/view/smarty3/compiled/ - -#ignore cache folders -/privacy_image_cache/ -/photo/ -/proxy/ -nbproject - -#ignore vagrant dir -.vagrant/ - -#ignore local folder -/local/ - -#ignore config files from Visual Studio -/.vs/ -/php_friendica.phpproj -/php_friendica.sln -/php_friendica.phpproj.user - -#ignore things from transifex-client -venv/ - -#ignore Composer dependencies -/vendor -/view/asset - -#ignore config files from JetBrains -/.idea +favicon.* +.htconfig.php +.htpreconfig.php +\#* +*.log +*.out +*.version* +home.html + +*~ +robots.txt + +#ignore local config +/config/local.ini.php +/config/addon.ini.php + +#ignore documentation, it should be newly built +/doc/html + +#ignore reports, should be generated with every build +report/ + +#ignore config files from eclipse, we don't want IDE files in our repository +.project +.buildpath +.externalToolBuilders +.settings + +#ignore OSX .DS_Store files +.DS_Store + +#ignore NetBeans IDE's private files (at least) +/nbproject/private/ + +#ignore smarty cache +/view/smarty3/compiled/ + +#ignore cache folders +/privacy_image_cache/ +/photo/ +/proxy/ +nbproject + +#ignore vagrant dir +.vagrant/ + +#ignore local folder +/local/ + +#ignore config files from Visual Studio +/.vs/ +/php_friendica.phpproj +/php_friendica.sln +/php_friendica.phpproj.user + +#ignore things from transifex-client +venv/ + +#ignore Composer dependencies +/vendor +/view/asset + +#ignore config files from JetBrains +/.idea + +#ignore addons directory +/addons +/addon + +#ignore .htaccess +.htaccess diff --git a/.htaccess b/.htaccess-dist similarity index 96% rename from .htaccess rename to .htaccess-dist index 2348cdc38..a671cc680 100644 --- a/.htaccess +++ b/.htaccess-dist @@ -4,7 +4,7 @@ AddType audio/ogg .oga #AddHandler php53-cgi .php - + #Apache 2.4 Require all denied @@ -38,4 +38,3 @@ AddType audio/ogg .oga RewriteRule ^(.*)$ index.php?pagename=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA] - diff --git a/.travis.yml b/.travis.yml index d68b7727e..bb8b11311 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,25 @@ --- language: php -## Friendica supports PHP version >= 5.6 +## Friendica supports PHP version >= 5.6.1 php: - 5.6 - 7.0 - 7.1 - 7.2 -install: composer install +services: + - mysql + - redis-server + - memcached +env: + - MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USERNAME=travis MYSQL_PASSWORD= MYSQL_DATABASE=test + +install: + - composer install +before_script: + - cp config/local-sample.ini.php config/local.ini.php + - mysql -e 'CREATE DATABASE IF NOT EXISTS test;' + - mysql -utravis test < database.sql + - echo "extension=redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini +after_success: bash <(curl -s https://codecov.io/bash) diff --git a/CHANGELOG b/CHANGELOG index 6385d22bb..2f675fb6d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,134 @@ +Version 2018.09 (2018-09-23) + Friendica Core: + Update to the translation (CS, DE, EN-US, FI, IT, NL, PL, ZH-CN) [translation teams] + Update to the documentation [Aditoo17, annando, astifter, rebeka-catalina, fabrixxm, M-arcus, microgroove, nupplaphil, tobiasd] + Enhancements to the database structure, handling and documentation [abanink, Angristan, annando, miqrogroove, tobiasd] + Enhancements of unit testing [abanink, nupplaphil, rudloff] + Enhancements to labelling of UI elements [andyhee, tobiasd] + Enhancements to the background workers [annando, miqrogroove, rabuzarus] + Enhancements to the PHP7.2 compatibility [annando, miqrogroove, MrPetovan] + Enhancements to the content filter [MrPetovan] + Enhancements to the hooks provided for addons [abanink] + Enhancements to the interaction with public postings [annando] + Enhancements to the config storage [frabrixxm] + Enhancements to the themes (frio, quattro, smoothly, vier) [annando, astifter, hoergen, MrPetovan, rabuzarus, tobiasd] + Enhancements to the handling of locks [nupplaphil] + Enhancements to the redis integration [nupplaphil] + Enhancements to the admin panel [JeroenED, tobiasd] + Enhancements to the user import process [annando] + Enhancements to the display of invitation information [JeroenED] + Enhancements to the automatic installation process [nupplaphil] + Enhancements to the contact group UI [annando, astifter] + Enhancements to the call of JS [hypolite] + Enhancements to the storage of items in the database [annando] + Enhancements to the process of changing relationships [annando] + Enhancements to the OEmbed of data [MrPetovan] + Fixed various PHP notice occurrences [annando, MrPetovan] + Fixed a bug that could lead to the display of posts from deleted accounts on the community page for a short period [annando] + Fixed a bug that prevented email notification to be send out [annando] + Fixed a bug in database optimisation [annando] + Fixed a bug during removing contacts [annando] + Fixed a bug in the tag-cloud widget [annando] + Fixed a bug in the daemon mode of the background worker [annando] + Fixed a bug in the frio theme that contact filtering [rabuzarus] + Fixed a bug that mangled the display of some additional smileys [abanink] + Fixed a bug in generating registration mails [MrPetovan] + Fixed a bug that caused blank re-share bodies [MrPetovon] + Fixed a bug in the API handling of private mails [fabrixxm] + Fixed a bug when calling the mail() function [miqrogroove] + Fixed a bug that caused deleted accounts being displayed in the local directory [miqrogroove] + Fixed a bug when checking the domain of an email address [VVelox] + Fixed a bug that prevented re-shares from Twitter to be shown as this [annando] + Fixed a bug that caused broken profile links [miqrogroove] + Fixed a bug that caused content from unknown accounts appearing in the timeline [annando] + Fixed a bug with the ignoring and blocking of contacts [annando] + Fixed a bug with showing hidden contacts in some places [annando] + Fixed a bug that prevented the deletion of events by contacts [annando] + Fixed a bug that prevented email contacts from being added [annando] + Fixed a bug in the notification/seen API call [fabrixxm] + Fixed a bug that prevented a refresh after un-/ignoring a conversation [annando] + Fixed a bug in the handling of some language translations [anndno] + Fixed a bug in the hook handling [annando] + Fixed the handling of too long tags [annando] + Fixed a bug that prevented the unliking of dis-/likes [annando] + Fixed bugs with the handling of private nodes [annando] + Fixed a bug in the session initialisation [annando] + Fixed bugs in the execution of the background processes [annando, Quix0r] + Fixed a problem with the notification page [MrPetovan] + Fixed a bug with wrong dates in importing some Atom feeds [annando] + Fixed forum exclusive distribution of postings using the !notation [annando] + Fixed a bug that lead to empty notifications [MrPetovan] + Fixed a problem that could sometimes prevent the execution of the relocation [annando] + Fixed a bug with the handling of images in postings over the connectors [annando] + Added conversation cleanup configuration [miqrogroove] + Added support of the usage of internal diaspora links to accounts [annando] + Added the possibility for admins to block certain nicknames (e.g. role names) [tobiasd] + Added the generation of system guid [nupplaphil] + Added the possibility for admins to mark a node for explicit content [tobiasd] + Added filter by account type to the community page [annando] + Added private flag to API results [fabrixxm] + Added post update checks to the console utility [annando] + Added codecov analysis [nupplaphil] + Added access-keys to the frio theme [tobiasd] + Added the profile settings to the user settings [tobiasd] + General code refactoring and beautification work [annando, MrPetovan, Quix0r, tobiasd] + Fixation of the position on the network page when new posts arrive [rabuzarus] + Ported OpenWebAuth from Hubzilla [annando, rabuzarus] + Removed hard coded syntax highlighting from code blocks [MrPetovan] + Removed (temporarily) the possibility to add pictures to private messages [annando] + New INI style config file format in /config [MrPetovan, tobiasd] + The .htaccess file is not part of the git repository anymore [annando, Quix0r] + + Friendica Addons: + Update to the translations (CS, DE, EN-US, NL, PL, ZH-CN) [translation teams] + General update to adopt changes in core [annando, MrPetovan, Quix0r, tobiasd] + advancedcontentfilter: + Enhancement to the error handling [MrPetovan] + Honour the CSP settings [MrPetovan] + Fixed translation problems [annando] + blockem: + Enhancement of the settings [AlfredSK] + buffer: + support for app.net removed [annando] + js_upload: + Enhancement of the album name handling [rabuzarus] + Enhancement to the wording of the labels [astifter] + langfilter: + Fixed a problem with default values of the filtered languages [tobiasd] + libravatar: + The service wont shutdown, so we can keep the addon [tobiasd] + pumpio: + Fixed a problem that prevented new connections [annando] + superblock: + Fixed a bug that prevented the addon to block accounts [annando] + Enhancements of the settings [AlfredSK] + twitter: + Use rich text for quote tweets [MrPetovan] + Prevent empty quotes from being created [annando] + Fixed a problem with re-shares from remote_self contacts [annando] + Changed URL display after link expansion [MrPetovan] + Fixed a problem with EXIF handling [MrPetovan] + added addons: + mastodoncustomemojis [MrPetovan] + deprecated addons: + notimeline, retriver, remote_permissions, widgets + + Directory: + Enhancements of the health summary [andyhee] + Enhancements of the PHP7 compatibility [MrPetovan] + + Closed Issues: + 901, 1034, 1074, 1303, 1308, 1391, 1490, 1470, 1559, 2093, 2337, + 2340, 2381, 2396, 2675, 3291, 3299, 3493, 3501, 3535, 3643, 3840, + 4148, 4419, 4475, 4507, 4655, 4659, 4710, 4726, 4739, 4753, 4814, + 4830, 4868, 4889, 4923, 4971, 4950, 4985, 5066, 5099, 5137, 5148, + 5158, 5168, 5188, 5202, 5211, 5222, 5233, 5243, 5247, 5252, 5257, + 5260, 5262, 5268, 5274, 5275, 5276, 5278, 5298, 5318, 5319, 5320, + 5321, 5322, 5330, 5333, 5341, 5365, 5405, 5407, 5411, 5423, 5432, + 5434, 5436, 5443, 5455, 5464, 5467, 5469, 5486, 5496, 5497, 5514, + 5539, 5524, 5541, 5544, 5550, 5564, 5566, 5605, 5630, 5638, 5651, + 5653, 5660, 5670, 5691, 5733, 5745, 5768 + Version 2018.05 (2018-06-01) Friendica Core: Update to the translations (DE, EN-GB, EN-US, FI, IS, IT, NL, PL, RU, ZN CH) [translation teams] diff --git a/INSTALL.txt b/INSTALL.txt index 9340927f4..705eb8fed 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -32,7 +32,7 @@ link if your cert is self-signed). - Apache with mod-rewrite enabled and "Options All" so you can use a local .htaccess file - - PHP 5.6+ (PHP 7 recommended for performance). + - PHP 5.6.1+ (PHP 7 recommended for performance). - PHP *command line* access with register_argc_argv set to true in the php.ini file [or see 'poormancron' in section 8] @@ -66,10 +66,39 @@ OR 2b. Clone the friendica/friendica GitHub repository and import dependencies - git clone https://github.com/friendica/friendica [web server folder] + git clone https://github.com/friendica/friendica -b master [web server folder] cd [web server folder] php bin/composer.phar install +Make sure the folder view/smarty3 exists and is writable by the webserver +user, in this case `www-data` + + mkdir view/smarty3 + chown www-data:www-data view/smarty3 + chmod 775 view/smarty3 + +Get the addons by going into your website folder. + + cd mywebsite + +Clone the addon repository (separately): + + git clone https://github.com/friendica/friendica-addons.git -b master addon + +If you copy the directory tree to your webserver, make sure that you also +copy .htaccess - as "dot" files are often hidden and aren't normally copied. + +If you want to use the development version of Friendica you can switch to +the devel branch in the repository by running + + git checkout develop + bin/composer.phar install + cd addon + git checkout develop + +please be aware that the develop branch may break your Friendica node at any +time. If you encounter a bug, please let us know. + 3. Create an empty database and note the access details (hostname, username, password, database name). @@ -77,8 +106,8 @@ password, database name). - Please check the additional notes if running on MySQ 5.7.17 or newer 4. If you know in advance that it will be impossible for the web server to -write or create files in your web directory, create an empty file called -.htconfig.php and make it writable by the web server. +write or create files in the config/ subfolder, create an empty file called +local.ini.php and make it writable by the web server. 5. Visit your website with a web browser and follow the instructions. Please note any error messages and correct these before continuing. @@ -92,9 +121,9 @@ so in the host name setting for the database. 6. *If* the automated installation fails for any reason, check the following: - - ".htconfig.php" exists - If not, edit htconfig.php and change system settings. Rename -to .htconfig.php + - "config/local.ini.php" exists + If not, edit local-sample.ini.php and change system settings. Rename +to local.ini.php - Database is populated. If not, import the contents of "database.sql" with phpmyadmin or mysql command line @@ -103,7 +132,7 @@ or mysql command line Registration errors should all be recoverable automatically. If you get any *critical* failure at this point, it generally indicates the database was not installed correctly. You might wish to move/rename -.htconfig.php to another name and empty (called 'dropping') the database +local.ini.php to another name and empty (called 'dropping') the database tables, so that you can start fresh. **************************************************************************** @@ -148,7 +177,7 @@ Bad things will happen. Let there be a hardware failure, a corrupted database or whatever you can think of. So once the installation of your Friendica node is done, you should make yoursef a backup plan. -The most important file is the `.htconfig.php` file in the base directory. +The most important file is the `config/local.ini.php` file in the base directory. As it stores all your data, you should also have a recent dump of your Friendica database at hand, should you have to recover your node. @@ -245,21 +274,21 @@ Windows). ##################################################################### -- If you are unable to write the file .htconfig.php during installation +- If you are unable to write the file config/local.ini.php during installation due to permissions issues: ##################################################################### create an empty file with that name and give it world-write permission. For Linux: -% touch .htconfig.php -% chmod 777 .htconfig.php +% touch config/local.ini.php +% chmod 664 config/local.ini.php Retry the installation. As soon as the database has been created, ******* this is important ********* -% chmod 755 .htconfig.php +% chmod 644 config/local.ini.php ##################################################################### - Some configurations with "suhosin" security are configured without @@ -298,11 +327,11 @@ After a while I noticed, that bin/worker.php calls further php script via proc_open. These scripts themselves also use proc_open and fail, because they are NOT called with -d suhosin.executor.func.blacklist=none. -So the simple solution is to put the correct parameters into .htconfig.php: - // Location of PHP command line processor - $a->config['php_path'] = '/usr/bin/php -d suhosin.executor.func.blacklist=none --d suhosin.executor.eval.blacklist=none'; +So the simple solution is to put the correct parameters into config/local.ini.php: +[config] +; Location of PHP command line processor +php_path = "/usr/bin/php -d suhosin.executor.func.blacklist=none -d suhosin.executor.eval.blacklist=none" This is obvious as soon as you notice that the friendica-cron uses proc_open to execute php-scripts that also use proc_open, but it took me quite some time to diff --git a/VERSION b/VERSION index 41df8e6ae..40284ee69 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2018.05 +2018.08-rc diff --git a/bin/auth_ejabberd.php b/bin/auth_ejabberd.php index 06d8488df..7ad28c96f 100755 --- a/bin/auth_ejabberd.php +++ b/bin/auth_ejabberd.php @@ -33,8 +33,6 @@ */ use Friendica\App; -use Friendica\BaseObject; -use Friendica\Core\Config; use Friendica\Util\ExAuth; if (sizeof($_SERVER["argv"]) == 0) { @@ -55,12 +53,8 @@ require_once "boot.php"; require_once "include/dba.php"; $a = new App(dirname(__DIR__)); -BaseObject::setApp($a); -@include ".htconfig.php"; -dba::connect($db_host, $db_user, $db_pass, $db_data); -unset($db_host, $db_user, $db_pass, $db_data); - -$oAuth = new ExAuth(); - -$oAuth->readStdin(); +if ($a->mode === App::MODE_NORMAL) { + $oAuth = new ExAuth(); + $oAuth->readStdin(); +} \ No newline at end of file diff --git a/bin/daemon.php b/bin/daemon.php index 6b0e377a3..159b20e15 100755 --- a/bin/daemon.php +++ b/bin/daemon.php @@ -6,10 +6,48 @@ * * This script was taken from http://php.net/manual/en/function.pcntl-fork.php */ -function shutdown() { - posix_kill(posix_getpid(), SIGHUP); + +use Friendica\App; +use Friendica\Core\Config; +use Friendica\Core\Worker; +use Friendica\Database\DBA; + +// Get options +$shortopts = 'f'; +$longopts = ['foreground']; +$options = getopt($shortopts, $longopts); + +// Ensure that daemon.php is executed from the base path of the installation +if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) { + $directory = dirname($_SERVER["argv"][0]); + + if (substr($directory, 0, 1) != "/") { + $directory = $_SERVER["PWD"] . "/" . $directory; + } + $directory = realpath($directory . "/.."); + + chdir($directory); } +require_once "boot.php"; +require_once "include/dba.php"; + +$a = new App(dirname(__DIR__)); + +if ($a->isInstallMode()) { + die("Friendica isn't properly installed yet.\n"); +} + +Config::load(); + +if (empty(Config::get('system', 'pidfile'))) { + die('Please set system.pidfile in config/local.ini.php. For example:'."\n". + '[system]'."\n". + 'pidfile = /path/to/daemon.pid'."\n"); +} + +$pidfile = Config::get('system', 'pidfile'); + if (in_array("start", $_SERVER["argv"])) { $mode = "start"; } @@ -22,6 +60,8 @@ if (in_array("status", $_SERVER["argv"])) { $mode = "status"; } +$foreground = array_key_exists('f', $options) || array_key_exists('foreground', $options); + if (!isset($mode)) { die("Please use either 'start', 'stop' or 'status'.\n"); } @@ -30,27 +70,15 @@ if (empty($_SERVER["argv"][0])) { die("Unexpected script behaviour. This message should never occur.\n"); } -// Fetch the base directory -$directory = dirname($_SERVER["argv"][0]); +$pid = null; -if (substr($directory, 0, 1) != "/") { - $directory = $_SERVER["PWD"]."/".$directory; -} -$directory = realpath($directory."/.."); - -@include($directory."/.htconfig.php"); - -if (!isset($pidfile)) { - die('Please specify a pid file in the variable $pidfile in the .htconfig.php. For example:'."\n". - '$pidfile = "/path/to/daemon.pid";'."\n"); +if (is_readable($pidfile)) { + $pid = intval(file_get_contents($pidfile)); } -if (in_array($mode, array("stop", "status"))) { - $pid = @file_get_contents($pidfile); - - if (!$pid) { - die("Pidfile wasn't found. Is the daemon running?\n"); - } +if (empty($pid) && in_array($mode, ["stop", "status"])) { + Config::set('system', 'worker_daemon_mode', false); + die("Pidfile wasn't found. Is the daemon running?\n"); } if ($mode == "status") { @@ -60,6 +88,7 @@ if ($mode == "status") { unlink($pidfile); + Config::set('system', 'worker_daemon_mode', false); die("Daemon process $pid isn't running.\n"); } @@ -68,59 +97,102 @@ if ($mode == "stop") { unlink($pidfile); + logger("Worker daemon process $pid was killed.", LOGGER_DEBUG); + + Config::set('system', 'worker_daemon_mode', false); die("Worker daemon process $pid was killed.\n"); } -echo "Starting worker daemon.\n"; - -if (isset($a->config['php_path'])) { - $php = $a->config['php_path']; -} else { - $php = "php"; +if (!empty($pid) && posix_kill($pid, 0)) { + die("Daemon process $pid is already running.\n"); } -// Switch over to daemon mode. -if ($pid = pcntl_fork()) - return; // Parent +logger('Starting worker daemon.', LOGGER_DEBUG); -fclose(STDIN); // Close all of the standard -fclose(STDOUT); // file descriptors as we -fclose(STDERR); // are running as a daemon. +if (!$foreground) { + echo "Starting worker daemon.\n"; -register_shutdown_function('shutdown'); + // Switch over to daemon mode. + if ($pid = pcntl_fork()) { + return; // Parent + } -if (posix_setsid() < 0) - return; + fclose(STDIN); // Close all of the standard -if ($pid = pcntl_fork()) - return; // Parent + // Enabling this seem to block a running php process with 100% CPU usage when there is an outpout + // fclose(STDOUT); // file descriptors as we + // fclose(STDERR); // are running as a daemon. -$pid = getmypid(); -file_put_contents($pidfile, $pid); + DBA::disconnect(); + + register_shutdown_function('shutdown'); + + if (posix_setsid() < 0) { + return; + } + + if ($pid = pcntl_fork()) { + return; // Parent + } + + $pid = getmypid(); + file_put_contents($pidfile, $pid); + + // We lose the database connection upon forking + $a->loadDatabase(); +} + +Config::set('system', 'worker_daemon_mode', true); + +// Just to be sure that this script really runs endlessly +set_time_limit(0); + +$wait_interval = intval(Config::get('system', 'cron_interval', 5)) * 60; + +$do_cron = true; +$last_cron = 0; // Now running as a daemon. while (true) { - // Just to be sure that this script really runs endlessly - set_time_limit(0); - - // Call the worker - $cmdline = $php.' bin/worker.php'; - - $executed = false; - - if (function_exists('proc_open')) { - $resource = proc_open($cmdline . ' &', array(), $foo, $directory); - - if (is_resource($resource)) { - $executed = true; - proc_close($resource); - } + if (!$do_cron && ($last_cron + $wait_interval) < time()) { + logger('Forcing cron worker call.', LOGGER_DEBUG); + $do_cron = true; } - if (!$executed) { - exec($cmdline.' spawn'); + Worker::spawnWorker($do_cron); + + if ($do_cron) { + // We force a reconnect of the database connection. + // This is done to ensure that the connection don't get lost over time. + DBA::reconnect(); + + $last_cron = time(); } - // Now sleep for 5 minutes - sleep(300); + logger("Sleeping", LOGGER_DEBUG); + $start = time(); + do { + $seconds = (time() - $start); + + // logarithmic wait time calculation. + // Background: After jobs had been started, they often fork many workers. + // To not waste too much time, the sleep period increases. + $arg = (($seconds + 1) / ($wait_interval / 9)) + 1; + $sleep = round(log10($arg) * 1000000, 0); + usleep($sleep); + + $timeout = ($seconds >= $wait_interval); + } while (!$timeout && !Worker::IPCJobsExists()); + + if ($timeout) { + $do_cron = true; + logger("Woke up after $wait_interval seconds.", LOGGER_DEBUG); + } else { + $do_cron = false; + logger("Worker jobs are calling to be forked.", LOGGER_DEBUG); + } +} + +function shutdown() { + posix_kill(posix_getpid(), SIGHUP); } diff --git a/bin/dev/friendica-to-smarty-tpl.py b/bin/dev/friendica-to-smarty-tpl.py index 8149051ca..017b8b77a 100755 --- a/bin/dev/friendica-to-smarty-tpl.py +++ b/bin/dev/friendica-to-smarty-tpl.py @@ -83,7 +83,7 @@ def fix_element(element): element += ldelim + parts[first+1].rstrip('}') + rdelim else: # This takes care of elements where the filename is a path, e.g. {{ inc file.tpl }} - element += parts[first+1].rstrip('}') + element += parts[first+1].rstrip('}') element += '"' @@ -205,7 +205,7 @@ try: except getopt.GetoptError: help(sys.argv[0]) sys.exit(2) - + if path == '': path = raw_input('Path to template folder to convert: ') @@ -220,7 +220,7 @@ if not os.path.exists(outpath): files = os.listdir(path) for a_file in files: - if a_file == 'htconfig.tpl': + if a_file == 'local.ini.tpl': php_tpl = True else: php_tpl = False diff --git a/bin/dev/vagrant_provision.sh b/bin/dev/vagrant_provision.sh index fc3e266f2..0599efa0c 100755 --- a/bin/dev/vagrant_provision.sh +++ b/bin/dev/vagrant_provision.sh @@ -86,7 +86,8 @@ cd /var/www php bin/composer.phar install # initial config file for friendica in vagrant -cp /vagrant/util/htconfig.vagrant.php /vagrant/.htconfig.php +#cp /vagrant/util/htconfig.vagrant.php /vagrant/.htconfig.php +cp /vagrant/util/local.ini.vagrant.php /vagrant/config/local.ini.php # create the friendica database echo "create database friendica DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci" | $MYSQL -u root -proot diff --git a/bin/worker.php b/bin/worker.php index b4b265283..ceab479ce 100755 --- a/bin/worker.php +++ b/bin/worker.php @@ -6,10 +6,15 @@ */ use Friendica\App; -use Friendica\BaseObject; use Friendica\Core\Addon; use Friendica\Core\Config; use Friendica\Core\Worker; +use Friendica\Core\L10n; + +// Get options +$shortopts = 'sn'; +$longopts = ['spawn', 'no_cron']; +$options = getopt($shortopts, $longopts); // Ensure that worker.php is executed from the base path of the installation if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) { @@ -24,17 +29,14 @@ if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) { } require_once "boot.php"; -require_once "include/dba.php"; $a = new App(dirname(__DIR__)); -BaseObject::setApp($a); - -require_once ".htconfig.php"; -dba::connect($db_host, $db_user, $db_pass, $db_data); -unset($db_host, $db_user, $db_pass, $db_data); Config::load(); +$lang = L10n::getBrowserLanguage(); +L10n::loadTranslationTable($lang); + // Check the database structure and possibly fixes it check_db(true); @@ -47,14 +49,14 @@ $a->set_baseurl(Config::get('system', 'url')); Addon::loadHooks(); -$spawn = (($_SERVER["argc"] == 2) && ($_SERVER["argv"][1] == "spawn")); +$spawn = array_key_exists('s', $options) || array_key_exists('spawn', $options); if ($spawn) { Worker::spawnWorker(); killme(); } -$run_cron = (($_SERVER["argc"] <= 1) || ($_SERVER["argv"][1] != "no_cron")); +$run_cron = !array_key_exists('n', $options) && !array_key_exists('no_cron', $options); Worker::processQueue($run_cron); @@ -63,4 +65,3 @@ Worker::unclaimProcess(); Worker::endProcess(); killme(); - diff --git a/boot.php b/boot.php index 8f077b440..2236a4d5d 100644 --- a/boot.php +++ b/boot.php @@ -29,7 +29,7 @@ use Friendica\Core\PConfig; use Friendica\Core\Protocol; use Friendica\Core\System; use Friendica\Core\Worker; -use Friendica\Database\DBM; +use Friendica\Database\DBA; use Friendica\Database\DBStructure; use Friendica\Model\Contact; use Friendica\Model\Conversation; @@ -39,9 +39,9 @@ require_once 'include/text.php'; define('FRIENDICA_PLATFORM', 'Friendica'); define('FRIENDICA_CODENAME', 'The Tazmans Flax-lily'); -define('FRIENDICA_VERSION', '2018.05'); +define('FRIENDICA_VERSION', '2018.08-rc'); define('DFRN_PROTOCOL_VERSION', '2.23'); -define('DB_UPDATE_VERSION', 1266); +define('DB_UPDATE_VERSION', 1283); define('NEW_UPDATE_ROUTINE_VERSION', 1170); /** @@ -64,15 +64,13 @@ define('EOL', "
\r\n"); * @brief Image storage quality. * * Lower numbers save space at cost of image detail. - * For ease of upgrade, please do not change here. Change jpeg quality with - * $a->config['system']['jpeg_quality'] = n; - * in .htconfig.php, where n is netween 1 and 100, and with very poor results - * below about 50 + * For ease of upgrade, please do not change here. Set [system] jpegquality = n in config/local.ini.php, + * where n is between 1 and 100, and with very poor results below about 50 */ define('JPEG_QUALITY', 100); /** - * $a->config['system']['png_quality'] from 0 (uncompressed) to 9 + * [system] png_quality = n where is between 0 (uncompressed) to 9 */ define('PNG_QUALITY', 8); @@ -83,9 +81,10 @@ define('PNG_QUALITY', 8); * this length (on the longest side, the other side will be scaled appropriately). * Modify this value using * - * $a->config['system']['max_image_length'] = n; + * [system] + * max_image_length = n; * - * in .htconfig.php + * in config/local.ini.php * * If you don't want to set a maximum length, set to -1. The default value is * defined by 'MAX_IMAGE_LENGTH' below. @@ -114,11 +113,12 @@ define('SSL_POLICY_SELFSIGN', 2); * log levels * @{ */ -define('LOGGER_NORMAL', 0); -define('LOGGER_TRACE', 1); -define('LOGGER_DEBUG', 2); -define('LOGGER_DATA', 3); -define('LOGGER_ALL', 4); +define('LOGGER_WARNING', 0); +define('LOGGER_INFO', 1); +define('LOGGER_TRACE', 2); +define('LOGGER_DEBUG', 3); +define('LOGGER_DATA', 4); +define('LOGGER_ALL', 5); /* @}*/ /** @@ -152,19 +152,6 @@ define('REGISTER_OPEN', 2); * @} */ -/** - * @name Contact_is - * - * Relationship types - * @{ - */ -define('CONTACT_IS_FOLLOWER', 1); -define('CONTACT_IS_SHARING', 2); -define('CONTACT_IS_FRIEND', 3); -/** - * @} - */ - /** * @name Update * @@ -177,55 +164,6 @@ define('UPDATE_FAILED', 1); * @} */ -/** - * @name page/profile types - * - * PAGE_NORMAL is a typical personal profile account - * PAGE_SOAPBOX automatically approves all friend requests as CONTACT_IS_SHARING, (readonly) - * PAGE_COMMUNITY automatically approves all friend requests as CONTACT_IS_SHARING, but with - * write access to wall and comments (no email and not included in page owner's ACL lists) - * PAGE_FREELOVE automatically approves all friend requests as full friends (CONTACT_IS_FRIEND). - * - * @{ - */ -define('PAGE_NORMAL', 0); -define('PAGE_SOAPBOX', 1); -define('PAGE_COMMUNITY', 2); -define('PAGE_FREELOVE', 3); -define('PAGE_BLOG', 4); -define('PAGE_PRVGROUP', 5); -/** - * @} - */ - -/** - * @name account types - * - * ACCOUNT_TYPE_PERSON - the account belongs to a person - * Associated page types: PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE - * - * ACCOUNT_TYPE_ORGANISATION - the account belongs to an organisation - * Associated page type: PAGE_SOAPBOX - * - * ACCOUNT_TYPE_NEWS - the account is a news reflector - * Associated page type: PAGE_SOAPBOX - * - * ACCOUNT_TYPE_COMMUNITY - the account is community forum - * Associated page types: PAGE_COMMUNITY, PAGE_PRVGROUP - * - * ACCOUNT_TYPE_RELAY - the account is a relay - * This will only be assigned to contacts, not to user accounts - * @{ - */ -define('ACCOUNT_TYPE_PERSON', 0); -define('ACCOUNT_TYPE_ORGANISATION', 1); -define('ACCOUNT_TYPE_NEWS', 2); -define('ACCOUNT_TYPE_COMMUNITY', 3); -define('ACCOUNT_TYPE_RELAY', 4); -/** - * @} - */ - /** * @name CP * @@ -241,84 +179,32 @@ define('CP_USERS_AND_GLOBAL', 2); * @} */ -/** - * @name Protocols - * @deprecated since version 3.6 - * @see Conversation - * - * Different protocols that we are storing - * @{ - */ -define('PROTOCOL_UNKNOWN' , Conversation::PROTOCOL_UNKNOWN); -define('PROTOCOL_DFRN' , Conversation::PROTOCOL_DFRN); -define('PROTOCOL_DIASPORA' , Conversation::PROTOCOL_DIASPORA); -define('PROTOCOL_OSTATUS_SALMON' , Conversation::PROTOCOL_OSTATUS_SALMON); -define('PROTOCOL_OSTATUS_FEED' , Conversation::PROTOCOL_OSTATUS_FEED); // Deprecated -define('PROTOCOL_GS_CONVERSATION', Conversation::PROTOCOL_GS_CONVERSATION); // Deprecated -define('PROTOCOL_SPLITTED_CONV' , Conversation::PROTOCOL_SPLITTED_CONV); -/** - * @} - */ - -/** - * @name Network constants - * @deprecated since version 3.6 - * @see Protocol - * - * Network and protocol family types - * @{ - */ -define('NETWORK_DFRN' , Protocol::DFRN); // Friendica, Mistpark, other DFRN implementations -define('NETWORK_ZOT' , Protocol::ZOT); // Zot! - Currently unsupported -define('NETWORK_OSTATUS' , Protocol::OSTATUS); // GNU-social, Pleroma, Mastodon, other OStatus implementations -define('NETWORK_FEED' , Protocol::FEED); // RSS/Atom feeds with no known "post/notify" protocol -define('NETWORK_DIASPORA' , Protocol::DIASPORA); // Diaspora -define('NETWORK_MAIL' , Protocol::MAIL); // IMAP/POP -define('NETWORK_FACEBOOK' , Protocol::FACEBOOK); // Facebook API -define('NETWORK_LINKEDIN' , Protocol::LINKEDIN); // LinkedIn -define('NETWORK_XMPP' , Protocol::XMPP); // XMPP - Currently unsupported -define('NETWORK_MYSPACE' , Protocol::MYSPACE); // MySpace - Currently unsupported -define('NETWORK_GPLUS' , Protocol::GPLUS); // Google+ -define('NETWORK_PUMPIO' , Protocol::PUMPIO); // pump.io -define('NETWORK_TWITTER' , Protocol::TWITTER); // Twitter -define('NETWORK_DIASPORA2', Protocol::DIASPORA2); // Diaspora connector -define('NETWORK_STATUSNET', Protocol::STATUSNET); // Statusnet connector -define('NETWORK_APPNET' , Protocol::APPNET); // app.net - Dead protocol -define('NETWORK_NEWS' , Protocol::NEWS); // Network News Transfer Protocol - Currently unsupported -define('NETWORK_ICALENDAR', Protocol::ICALENDAR); // iCalendar - Currently unsupported -define('NETWORK_PNUT' , Protocol::PNUT); // pnut.io - Currently unsupported -define('NETWORK_PHANTOM' , Protocol::PHANTOM); // Place holder -/** - * @} - */ - /** * These numbers are used in stored permissions * and existing allocations MUST NEVER BE CHANGED * OR RE-ASSIGNED! You may only add to them. */ $netgroup_ids = [ - NETWORK_DFRN => (-1), - NETWORK_ZOT => (-2), - NETWORK_OSTATUS => (-3), - NETWORK_FEED => (-4), - NETWORK_DIASPORA => (-5), - NETWORK_MAIL => (-6), - NETWORK_FACEBOOK => (-8), - NETWORK_LINKEDIN => (-9), - NETWORK_XMPP => (-10), - NETWORK_MYSPACE => (-11), - NETWORK_GPLUS => (-12), - NETWORK_PUMPIO => (-13), - NETWORK_TWITTER => (-14), - NETWORK_DIASPORA2 => (-15), - NETWORK_STATUSNET => (-16), - NETWORK_APPNET => (-17), - NETWORK_NEWS => (-18), - NETWORK_ICALENDAR => (-19), - NETWORK_PNUT => (-20), + Protocol::DFRN => (-1), + Protocol::ZOT => (-2), + Protocol::OSTATUS => (-3), + Protocol::FEED => (-4), + Protocol::DIASPORA => (-5), + Protocol::MAIL => (-6), + Protocol::FACEBOOK => (-8), + Protocol::LINKEDIN => (-9), + Protocol::XMPP => (-10), + Protocol::MYSPACE => (-11), + Protocol::GPLUS => (-12), + Protocol::PUMPIO => (-13), + Protocol::TWITTER => (-14), + Protocol::DIASPORA2 => (-15), + Protocol::STATUSNET => (-16), + Protocol::NEWS => (-18), + Protocol::ICALENDAR => (-19), + Protocol::PNUT => (-20), - NETWORK_PHANTOM => (-127), + Protocol::PHANTOM => (-127), ]; /** @@ -451,8 +337,9 @@ define('ACTIVITY_OBJ_QUESTION', 'http://activityschema.org/object/question'); * @{ */ define('GRAVITY_PARENT', 0); -define('GRAVITY_LIKE', 3); +define('GRAVITY_ACTIVITY', 3); define('GRAVITY_COMMENT', 6); +define('GRAVITY_UNKNOWN', 9); /* @}*/ /** @@ -498,36 +385,6 @@ if (!defined("SIGTERM")) { if (!defined('CURLE_OPERATION_TIMEDOUT')) { define('CURLE_OPERATION_TIMEDOUT', CURLE_OPERATION_TIMEOUTED); } -/** - * Reverse the effect of magic_quotes_gpc if it is enabled. - * Please disable magic_quotes_gpc so we don't have to do this. - * See http://php.net/manual/en/security.magicquotes.disabling.php - */ -function startup() -{ - error_reporting(E_ERROR | E_WARNING | E_PARSE); - - set_time_limit(0); - - // This has to be quite large to deal with embedded private photos - ini_set('pcre.backtrack_limit', 500000); - - if (get_magic_quotes_gpc()) { - $process = [&$_GET, &$_POST, &$_COOKIE, &$_REQUEST]; - while (list($key, $val) = each($process)) { - foreach ($val as $k => $v) { - unset($process[$key][$k]); - if (is_array($v)) { - $process[$key][stripslashes($k)] = $v; - $process[] = &$process[$key][stripslashes($k)]; - } else { - $process[$key][stripslashes($k)] = stripslashes($v); - } - } - } - unset($process); - } -} /** * @brief Retrieve the App structure @@ -538,14 +395,7 @@ function startup() */ function get_app() { - global $a; - - if (empty($a)) { - $a = new App(dirname(__DIR__)); - BaseObject::setApp($a); - } - - return $a; + return BaseObject::getApp(); } /** @@ -706,7 +556,7 @@ function check_url(App $a) // and www.example.com vs example.com. // We will only change the url to an ip address if there is no existing setting - if (empty($url) || (!link_compare($url, System::baseUrl())) && (!preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $a->get_hostname))) { + if (empty($url) || (!link_compare($url, System::baseUrl())) && (!preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $a->get_hostname()))) { Config::set('system', 'url', System::baseUrl()); } @@ -740,10 +590,17 @@ function update_db() return; } + // run the pre_update_nnnn functions in update.php + for ($x = $stored + 1; $x <= $current; $x++) { + $r = run_update_function($x, 'pre_update'); + if (!$r) { + break; + } + } + Config::set('database', 'dbupdate_' . DB_UPDATE_VERSION, time()); - // run update routine - // it update the structure in one call + // update the structure in one call $retval = DBStructure::update(false, true); if ($retval) { DBStructure::updateFail( @@ -755,9 +612,9 @@ function update_db() Config::set('database', 'dbupdate_' . DB_UPDATE_VERSION, 'success'); } - // run any left update_nnnn functions in update.php + // run the update_nnnn functions in update.php for ($x = $stored + 1; $x <= $current; $x++) { - $r = run_update_function($x); + $r = run_update_function($x, 'update'); if (!$r) { break; } @@ -768,9 +625,11 @@ function update_db() return; } -function run_update_function($x) +function run_update_function($x, $prefix) { - if (function_exists('update_' . $x)) { + $funcname = $prefix . '_' . $x; + + if (function_exists($funcname)) { // There could be a lot of processes running or about to run. // We want exactly one process to run the update command. // So store the fact that we're taking responsibility @@ -778,16 +637,14 @@ function run_update_function($x) // If the update fails or times-out completely you may need to // delete the config entry to try again. - $t = Config::get('database', 'update_' . $x); + $t = Config::get('database', $funcname); if (!is_null($t)) { return false; } - Config::set('database', 'update_' . $x, time()); + Config::set('database', $funcname, time()); // call the specific update - - $func = 'update_' . $x; - $retval = $func(); + $retval = $funcname(); if ($retval) { //send the administrator an e-mail @@ -797,13 +654,21 @@ function run_update_function($x) ); return false; } else { - Config::set('database', 'update_' . $x, 'success'); - Config::set('system', 'build', $x); + Config::set('database', $funcname, 'success'); + + if ($prefix == 'update') { + Config::set('system', 'build', $x); + } + return true; } } else { - Config::set('database', 'update_' . $x, 'success'); - Config::set('system', 'build', $x); + Config::set('database', $funcname, 'success'); + + if ($prefix == 'update') { + Config::set('system', 'build', $x); + } + return true; } } @@ -811,7 +676,7 @@ function run_update_function($x) /** * @brief Synchronise addons: * - * $a->config['system']['addon'] contains a comma-separated list of names + * system.addon contains a comma-separated list of names * of addons which are used on this system. * Go through the database list of already installed addons, and if we have * an entry, but it isn't in the config list, call the uninstall procedure @@ -824,7 +689,7 @@ function run_update_function($x) function check_addons(App $a) { $r = q("SELECT * FROM `addon` WHERE `installed` = 1"); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $installed = $r; } else { $installed = []; @@ -864,28 +729,6 @@ function check_addons(App $a) return; } -function get_guid($size = 16, $prefix = '') -{ - if (is_bool($prefix) && !$prefix) { - $prefix = ''; - } elseif ($prefix == '') { - $a = get_app(); - $prefix = hash('crc32', $a->get_hostname()); - } - - while (strlen($prefix) < ($size - 13)) { - $prefix .= mt_rand(); - } - - if ($size >= 24) { - $prefix = substr($prefix, 0, $size - 22); - return str_replace('.', '', uniqid($prefix, true)); - } else { - $prefix = substr($prefix, 0, max($size - 13, 0)); - return uniqid($prefix); - } -} - /** * @brief Used to end the current process, after saving session state. * @deprecated @@ -917,7 +760,7 @@ function goaway($path) */ function local_user() { - if (x($_SESSION, 'authenticated') && x($_SESSION, 'uid')) { + if (!empty($_SESSION['authenticated']) && !empty($_SESSION['uid'])) { return intval($_SESSION['uid']); } return false; @@ -960,6 +803,11 @@ function remote_user() // if (local_user()) { // return false; // } + + if (empty($_SESSION)) { + return false; + } + if (x($_SESSION, 'authenticated') && x($_SESSION, 'visitor_id')) { return intval($_SESSION['visitor_id']); } @@ -975,6 +823,10 @@ function remote_user() */ function notice($s) { + if (empty($_SESSION)) { + return; + } + $a = get_app(); if (!x($_SESSION, 'sysmsg')) { $_SESSION['sysmsg'] = []; @@ -1007,17 +859,6 @@ function info($s) } } -/** - * @brief Wrapper around config to limit the text length of an incoming message - * - * @return int - */ -function get_max_import_size() -{ - $a = get_app(); - return (x($a->config, 'max_import_size') ? $a->config['max_import_size'] : 0); -} - function feed_birthday($uid, $tz) { /** @@ -1042,13 +883,9 @@ function feed_birthday($uid, $tz) $tz = 'UTC'; } - $p = q( - "SELECT `dob` FROM `profile` WHERE `is-default` = 1 AND `uid` = %d LIMIT 1", - intval($uid) - ); - - if (DBM::is_result($p)) { - $tmp_dob = substr($p[0]['dob'], 5); + $profile = DBA::selectFirst('profile', ['dob'], ['is-default' => true, 'uid' => $uid]); + if (DBA::isResult($profile)) { + $tmp_dob = substr($profile['dob'], 5); if (intval($tmp_dob)) { $y = DateTimeFormat::timezoneNow($tz, 'Y'); $bd = $y . '-' . $tmp_dob . ' 00:00'; @@ -1073,13 +910,11 @@ function is_site_admin() { $a = get_app(); - $adminlist = explode(",", str_replace(" ", "", $a->config['admin_email'])); + $admin_email = Config::get('config', 'admin_email'); - //if(local_user() && x($a->user,'email') && x($a->config,'admin_email') && ($a->user['email'] === $a->config['admin_email'])) - if (local_user() && x($a->user, 'email') && x($a->config, 'admin_email') && in_array($a->user['email'], $adminlist)) { - return true; - } - return false; + $adminlist = explode(',', str_replace(' ', '', $admin_email)); + + return local_user() && $admin_email && in_array(defaults($a->user, 'email', ''), $adminlist); } /** @@ -1155,7 +990,7 @@ function explode_querystring($query) function curPageURL() { $pageURL = 'http'; - if ($_SERVER["HTTPS"] == "on") { + if (!empty($_SERVER["HTTPS"]) && ($_SERVER["HTTPS"] == "on")) { $pageURL .= "s"; } @@ -1173,7 +1008,7 @@ function random_digits($digits) { $rn = ''; for ($i = 0; $i < $digits; $i++) { - /// @TODO rand() is different to mt_rand() and maybe lesser "random" + /// @TODO Avoid rand/mt_rand, when it comes to cryptography, they are generating predictable (seedable) numbers. $rn .= rand(0, 9); } return $rn; @@ -1187,7 +1022,7 @@ function get_server() $server = "https://dir.friendica.social"; } - return($server); + return $server; } function get_temppath() @@ -1236,7 +1071,7 @@ function get_cachefile($file, $writemode = true) $cache = get_itemcachepath(); if ((!$cache) || (!is_dir($cache))) { - return(""); + return ""; } $subfolder = $cache . "/" . substr($file, 0, 2); @@ -1250,7 +1085,6 @@ function get_cachefile($file, $writemode = true) } } - /// @TODO no need to put braces here return $cachepath; } @@ -1357,7 +1191,6 @@ function get_spoolpath() return ""; } - if (!function_exists('exif_imagetype')) { function exif_imagetype($file) { @@ -1395,7 +1228,7 @@ function validate_include(&$file) } // Simply return flag - return ($valid); + return $valid; } function current_load() diff --git a/composer.json b/composer.json index b7cd645bd..c71777550 100644 --- a/composer.json +++ b/composer.json @@ -13,18 +13,17 @@ "issues": "https://github.com/friendica/friendica/issues" }, "require": { - "php": ">5.6", + "php": ">=5.6.1", "ext-xml": "*", "asika/simple-console": "^1.0", "divineomega/password_exposed": "^2.4", "ezyang/htmlpurifier": "~4.7.0", - "league/html-to-markdown": "~4.4.1", + "league/html-to-markdown": "~4.8.0", "lightopenid/lightopenid": "dev-master", "michelf/php-markdown": "^1.7", "mobiledetect/mobiledetectlib": "2.8.*", "paragonie/random_compat": "^2.0", "pear/Text_LanguageDetect": "1.*", - "pear/Text_Highlighter": "dev-master", "seld/cli-prompt": "^1.0", "smarty/smarty": "^3.1", "fxp/composer-asset-plugin": "~1.3", @@ -37,7 +36,8 @@ "npm-asset/jquery-datetimepicker": "^2.4.0", "npm-asset/jgrowl": "^1.4", "npm-asset/fullcalendar": "^3.0.1", - "npm-asset/cropperjs": "1.2.2" + "npm-asset/cropperjs": "1.2.2", + "npm-asset/imagesloaded": "4.1.4" }, "repositories": [ { @@ -47,7 +47,8 @@ ], "autoload": { "psr-4": { - "Friendica\\": "src/" + "Friendica\\": "src/", + "Friendica\\Test\\": "tests/" }, "psr-0": { "": "library/" @@ -68,5 +69,14 @@ "exclude": [ "log", "cache", "/photo", "/proxy" ] + }, + "require-dev": { + "phpunit/dbunit": "^2.0", + "phpdocumentor/reflection-docblock": "^3.0.2", + "phpunit/php-token-stream": "^1.4.2", + "mikey179/vfsStream": "^1.6" + }, + "scripts": { + "test": "phpunit" } } diff --git a/composer.lock b/composer.lock index f294c16ef..b0ff745c6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "f97245142e60a521f048a667bec4e436", + "content-hash": "5f6a43237dc52758484cd21cd76e8ce6", "packages": [ { "name": "asika/simple-console", @@ -483,16 +483,16 @@ }, { "name": "league/html-to-markdown", - "version": "4.4.1", + "version": "4.8.0", "source": { "type": "git", "url": "https://github.com/thephpleague/html-to-markdown.git", - "reference": "82ea375b5b2b1da1da222644c0565c695bf88186" + "reference": "f9a879a068c68ff47b722de63f58bec79e448f9d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/82ea375b5b2b1da1da222644c0565c695bf88186", - "reference": "82ea375b5b2b1da1da222644c0565c695bf88186", + "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/f9a879a068c68ff47b722de63f58bec79e448f9d", + "reference": "f9a879a068c68ff47b722de63f58bec79e448f9d", "shasum": "" }, "require": { @@ -511,7 +511,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.5-dev" + "dev-master": "4.9-dev" } }, "autoload": { @@ -524,17 +524,17 @@ "MIT" ], "authors": [ - { - "name": "Colin O'Dell", - "email": "colinodell@gmail.com", - "homepage": "http://www.colinodell.com", - "role": "Lead Developer" - }, { "name": "Nick Cernis", "email": "nick@cern.is", "homepage": "http://modernnerd.net", "role": "Original Author" + }, + { + "name": "Colin O'Dell", + "email": "colinodell@gmail.com", + "homepage": "https://www.colinodell.com", + "role": "Lead Developer" } ], "description": "An HTML-to-markdown conversion helper for PHP", @@ -543,7 +543,7 @@ "html", "markdown" ], - "time": "2017-03-16T00:45:59+00:00" + "time": "2018-09-18T12:18:08+00:00" }, { "name": "lightopenid/lightopenid", @@ -770,6 +770,49 @@ ], "time": "2018-01-03T13:39:39+00:00" }, + { + "name": "npm-asset/ev-emitter", + "version": "1.1.1", + "dist": { + "type": "tar", + "url": "https://registry.npmjs.org/ev-emitter/-/ev-emitter-1.1.1.tgz", + "reference": null, + "shasum": "8f18b0ce5c76a5d18017f71c0a795c65b9138f2a" + }, + "type": "npm-asset-library", + "extra": { + "npm-asset-bugs": { + "url": "https://github.com/metafizzy/ev-emitter/issues" + }, + "npm-asset-main": "ev-emitter.js", + "npm-asset-directories": { + "test": "test" + }, + "npm-asset-repository": { + "type": "git", + "url": "git+https://github.com/metafizzy/ev-emitter.git" + }, + "npm-asset-scripts": { + "test": "mocha test/test" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David DeSandro" + } + ], + "description": "lil' event emitter", + "homepage": "https://github.com/metafizzy/ev-emitter#readme", + "keywords": [ + "emitter", + "event", + "pubsub" + ], + "time": "2017-07-06T13:46:38+00:00" + }, { "name": "npm-asset/fullcalendar", "version": "3.8.2", @@ -882,6 +925,70 @@ ], "time": "2018-01-30T23:49:01+00:00" }, + { + "name": "npm-asset/imagesloaded", + "version": "4.1.4", + "dist": { + "type": "tar", + "url": "https://registry.npmjs.org/imagesloaded/-/imagesloaded-4.1.4.tgz", + "reference": null, + "shasum": "1376efcd162bb768c34c3727ac89cc04051f3cc7" + }, + "require": { + "npm-asset/ev-emitter": ">=1.0.0,<2.0.0" + }, + "require-dev": { + "npm-asset/chalk": ">=1.1.1,<2.0.0", + "npm-asset/cheerio": ">=0.19.0,<0.20.0", + "npm-asset/gulp": ">=3.9.0,<4.0.0", + "npm-asset/gulp-jshint": ">=1.11.2,<2.0.0", + "npm-asset/gulp-json-lint": ">=0.1.0,<0.2.0", + "npm-asset/gulp-rename": ">=1.2.2,<2.0.0", + "npm-asset/gulp-replace": ">=0.5.4,<0.6.0", + "npm-asset/gulp-requirejs-optimize": "dev-github:metafizzy/gulp-requirejs-optimize", + "npm-asset/gulp-uglify": ">=1.4.2,<2.0.0", + "npm-asset/gulp-util": ">=3.0.7,<4.0.0", + "npm-asset/highlight.js": ">=8.9.1,<9.0.0", + "npm-asset/marked": ">=0.3.5,<0.4.0", + "npm-asset/minimist": ">=1.2.0,<2.0.0", + "npm-asset/transfob": ">=1.0.0,<2.0.0" + }, + "type": "npm-asset-library", + "extra": { + "npm-asset-bugs": { + "url": "https://github.com/desandro/imagesloaded/issues" + }, + "npm-asset-main": "imagesloaded.js", + "npm-asset-directories": { + "test": "test" + }, + "npm-asset-repository": { + "type": "git", + "url": "git://github.com/desandro/imagesloaded.git" + }, + "npm-asset-scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "David DeSandro" + } + ], + "description": "JavaScript is all like _You images done yet or what?_", + "homepage": "https://github.com/desandro/imagesloaded", + "keywords": [ + "dom", + "images", + "jquery-plugin", + "loaded", + "ui" + ], + "time": "2018-01-02T16:56:03+00:00" + }, { "name": "npm-asset/jgrowl", "version": "1.4.6", @@ -1562,204 +1669,6 @@ ], "time": "2018-02-15T05:50:20+00:00" }, - { - "name": "pear/console_getopt", - "version": "v1.4.1", - "source": { - "type": "git", - "url": "https://github.com/pear/Console_Getopt.git", - "reference": "82f05cd1aa3edf34e19aa7c8ca312ce13a6a577f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pear/Console_Getopt/zipball/82f05cd1aa3edf34e19aa7c8ca312ce13a6a577f", - "reference": "82f05cd1aa3edf34e19aa7c8ca312ce13a6a577f", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-0": { - "Console": "./" - } - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "./" - ], - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Greg Beaver", - "email": "cellog@php.net", - "role": "Helper" - }, - { - "name": "Andrei Zmievski", - "email": "andrei@php.net", - "role": "Lead" - }, - { - "name": "Stig Bakken", - "email": "stig@php.net", - "role": "Developer" - } - ], - "description": "More info available on: http://pear.php.net/package/Console_Getopt", - "time": "2015-07-20T20:28:12+00:00" - }, - { - "name": "pear/pear-core-minimal", - "version": "v1.10.3", - "source": { - "type": "git", - "url": "https://github.com/pear/pear-core-minimal.git", - "reference": "070f0b600b2caca2501e2c9b7e553016e4b0d115" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pear/pear-core-minimal/zipball/070f0b600b2caca2501e2c9b7e553016e4b0d115", - "reference": "070f0b600b2caca2501e2c9b7e553016e4b0d115", - "shasum": "" - }, - "require": { - "pear/console_getopt": "~1.4", - "pear/pear_exception": "~1.0" - }, - "replace": { - "rsky/pear-core-min": "self.version" - }, - "type": "library", - "autoload": { - "psr-0": { - "": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "src/" - ], - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Christian Weiske", - "email": "cweiske@php.net", - "role": "Lead" - } - ], - "description": "Minimal set of PEAR core files to be used as composer dependency", - "time": "2017-02-28T16:46:11+00:00" - }, - { - "name": "pear/pear_exception", - "version": "v1.0.0", - "source": { - "type": "git", - "url": "https://github.com/pear/PEAR_Exception.git", - "reference": "8c18719fdae000b690e3912be401c76e406dd13b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pear/PEAR_Exception/zipball/8c18719fdae000b690e3912be401c76e406dd13b", - "reference": "8c18719fdae000b690e3912be401c76e406dd13b", - "shasum": "" - }, - "require": { - "php": ">=4.4.0" - }, - "require-dev": { - "phpunit/phpunit": "*" - }, - "type": "class", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-0": { - "PEAR": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "." - ], - "license": [ - "BSD-2-Clause" - ], - "authors": [ - { - "name": "Helgi Thormar", - "email": "dufuz@php.net" - }, - { - "name": "Greg Beaver", - "email": "cellog@php.net" - } - ], - "description": "The PEAR Exception base class.", - "homepage": "https://github.com/pear/PEAR_Exception", - "keywords": [ - "exception" - ], - "time": "2015-02-10T20:07:52+00:00" - }, - { - "name": "pear/text_highlighter", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/pear/Text_Highlighter.git", - "reference": "2ccac2d9eaf55dc08bbbdb7136c93fb399d0f855" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pear/Text_Highlighter/zipball/2ccac2d9eaf55dc08bbbdb7136c93fb399d0f855", - "reference": "2ccac2d9eaf55dc08bbbdb7136c93fb399d0f855", - "shasum": "" - }, - "require": { - "pear/pear-core-minimal": "~1.10.0", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "@stable" - }, - "type": "library", - "autoload": { - "psr-0": { - "Text": "./" - } - }, - "include-path": [ - "./" - ], - "license": [ - "PHP-3.01" - ], - "authors": [ - { - "email": "ssttoo@gmail.com", - "name": "Stoyan Stefanov", - "role": "Lead" - }, - { - "email": "demenev@gmail.com", - "name": "Andrey Demenev", - "role": "Lead" - } - ], - "description": "More info available on: http://pear.php.net/package/Text_Highlighter", - "support": { - "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Text_Highlighter", - "source": "https://github.com/pear/Text_Highlighter" - }, - "time": "2018-01-27T08:24:15+00:00" - }, { "name": "pear/text_languagedetect", "version": "v1.0.0", @@ -2090,17 +1999,1484 @@ "time": "2016-12-14T21:57:25+00:00" } ], - "packages-dev": [], + "packages-dev": [ + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14T21:17:01+00:00" + }, + { + "name": "mikey179/vfsStream", + "version": "v1.6.5", + "source": { + "type": "git", + "url": "https://github.com/mikey179/vfsStream.git", + "reference": "d5fec95f541d4d71c4823bb5e30cf9b9e5b96145" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mikey179/vfsStream/zipball/d5fec95f541d4d71c4823bb5e30cf9b9e5b96145", + "reference": "d5fec95f541d4d71c4823bb5e30cf9b9e5b96145", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "org\\bovigo\\vfs\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Frank Kleine", + "homepage": "http://frankkleine.de/", + "role": "Developer" + } + ], + "description": "Virtual file system to mock the real file system in unit tests.", + "homepage": "http://vfs.bovigo.org/", + "time": "2017-08-01T08:02:14+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.7.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^4.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "time": "2017-10-19T19:58:43+00:00" + }, + { + "name": "phar-io/version", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df", + "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "time": "2017-03-05T17:38:23+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "phpunit/phpunit": "^4.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "time": "2017-09-11T18:02:19+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", + "webmozart/assert": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^4.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "time": "2017-11-10T14:09:06+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "0.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "shasum": "" + }, + "require": { + "php": "^5.5 || ^7.0", + "phpdocumentor/reflection-common": "^1.0" + }, + "require-dev": { + "mockery/mockery": "^0.9.4", + "phpunit/phpunit": "^5.2||^4.8.24" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "time": "2017-07-14T14:27:02+00:00" + }, + { + "name": "phpspec/prophecy", + "version": "1.7.6", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.5|^3.2", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.7.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2018-04-18T13:57:24+00:00" + }, + { + "name": "phpunit/dbunit", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/dbunit.git", + "reference": "5c35d74549c21ba55d0ea74ba89d191a51f8cf25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/dbunit/zipball/5c35d74549c21ba55d0ea74ba89d191a51f8cf25", + "reference": "5c35d74549c21ba55d0ea74ba89d191a51f8cf25", + "shasum": "" + }, + "require": { + "ext-pdo": "*", + "ext-simplexml": "*", + "php": "^5.4 || ^7.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0", + "symfony/yaml": "^2.1 || ^3.0" + }, + "bin": [ + "dbunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "DbUnit port for PHP/PHPUnit to support database interaction testing.", + "homepage": "https://github.com/sebastianbergmann/dbunit/", + "keywords": [ + "database", + "testing", + "xunit" + ], + "time": "2016-12-02T14:39:14+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "4.0.8", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "reference": "ef7b2f56815df854e66ceaee8ebe9393ae36a40d", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", + "php": "^5.6 || ^7.0", + "phpunit/php-file-iterator": "^1.3", + "phpunit/php-text-template": "^1.2", + "phpunit/php-token-stream": "^1.4.2 || ^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0", + "sebastian/environment": "^1.3.2 || ^2.0", + "sebastian/version": "^1.0 || ^2.0" + }, + "require-dev": { + "ext-xdebug": "^2.1.4", + "phpunit/phpunit": "^5.7" + }, + "suggest": { + "ext-xdebug": "^2.5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2017-04-02T07:44:40+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.4.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2017-11-27T13:52:08+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2015-06-21T13:50:34+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2017-02-26T11:10:40+00:00" + }, + { + "name": "phpunit/php-token-stream", + "version": "1.4.12", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2017-12-04T08:55:13+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "5.7.27", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "myclabs/deep-copy": "~1.3", + "php": "^5.6 || ^7.0", + "phpspec/prophecy": "^1.6.2", + "phpunit/php-code-coverage": "^4.0.4", + "phpunit/php-file-iterator": "~1.4", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "^1.0.6", + "phpunit/phpunit-mock-objects": "^3.2", + "sebastian/comparator": "^1.2.4", + "sebastian/diff": "^1.4.3", + "sebastian/environment": "^1.3.4 || ^2.0", + "sebastian/exporter": "~2.0", + "sebastian/global-state": "^1.1", + "sebastian/object-enumerator": "~2.0", + "sebastian/resource-operations": "~1.0", + "sebastian/version": "^1.0.6|^2.0.1", + "symfony/yaml": "~2.1|~3.0|~4.0" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "3.0.2" + }, + "require-dev": { + "ext-pdo": "*" + }, + "suggest": { + "ext-xdebug": "*", + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.7.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2018-02-01T05:50:59+00:00" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "3.4.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", + "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.6 || ^7.0", + "phpunit/php-text-template": "^1.2", + "sebastian/exporter": "^1.2 || ^2.0" + }, + "conflict": { + "phpunit/phpunit": "<5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.4" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2017-06-30T09:13:00+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "time": "2017-03-04T06:30:41+00:00" + }, + { + "name": "sebastian/comparator", + "version": "1.2.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "reference": "2b7424b55f5047b47ac6e5ccb20b2aea4011d9be", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.2", + "sebastian/exporter": "~1.2 || ~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2017-01-29T09:50:25+00:00" + }, + { + "name": "sebastian/diff", + "version": "1.4.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "reference": "7f066a26a962dbe58ddea9f72a4e82874a3975a4", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2017-05-22T07:24:03+00:00" + }, + { + "name": "sebastian/environment", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "reference": "5795ffe5dc5b02460c3e34222fee8cbe245d8fac", + "shasum": "" + }, + "require": { + "php": "^5.6 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2016-11-26T07:53:53+00:00" + }, + { + "name": "sebastian/exporter", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/recursion-context": "~2.0" + }, + "require-dev": { + "ext-mbstring": "*", + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2016-11-19T08:54:04+00:00" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12T03:26:01+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", + "shasum": "" + }, + "require": { + "php": ">=5.6", + "sebastian/recursion-context": "~2.0" + }, + "require-dev": { + "phpunit/phpunit": "~5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "time": "2017-02-18T15:18:39+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "http://www.github.com/sebastianbergmann/recursion-context", + "time": "2016-11-19T07:33:16+00:00" + }, + { + "name": "sebastian/resource-operations", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/resource-operations.git", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52", + "shasum": "" + }, + "require": { + "php": ">=5.6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides a list of PHP built-in functions that operate on resources", + "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "time": "2015-07-28T20:34:47+00:00" + }, + { + "name": "sebastian/version", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019", + "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2016-10-03T07:35:21+00:00" + }, + { + "name": "symfony/yaml", + "version": "v3.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a42f9da85c7c38d59f5e53f076fe81a091f894d0", + "reference": "a42f9da85c7c38d59f5e53f076fe81a091f894d0", + "shasum": "" + }, + "require": { + "php": "^5.5.9|>=7.0.8" + }, + "conflict": { + "symfony/console": "<3.4" + }, + "require-dev": { + "symfony/console": "~3.4|~4.0" + }, + "suggest": { + "symfony/console": "For validating YAML files using the lint command" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2018-04-03T05:14:20+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/webmozart/assert.git", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "shasum": "" + }, + "require": { + "php": "^5.3.3 || ^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.6", + "sebastian/version": "^1.0.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "time": "2018-01-29T19:49:41+00:00" + } + ], "aliases": [], "minimum-stability": "stable", "stability-flags": { - "lightopenid/lightopenid": 20, - "pear/text_highlighter": 20 + "lightopenid/lightopenid": 20 }, "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">5.6", + "php": ">=5.6.1", "ext-xml": "*" }, "platform-dev": [] diff --git a/config/addon-sample.ini.php b/config/addon-sample.ini.php new file mode 100644 index 000000000..2665412a5 --- /dev/null +++ b/config/addon-sample.ini.php @@ -0,0 +1,10 @@ +<78>"}, + "allow_gid": {"type": "mediumtext", "comment": "Access Control - list of allowed groups"}, + "deny_cid": {"type": "mediumtext", "comment": "Access Control - list of denied contact.id"}, + "deny_gid": {"type": "mediumtext", "comment": "Access Control - list of denied groups"} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "auth_codes": { + "comment": "OAuth usage", + "fields": { + "id": {"type": "varchar(40)", "not null": "1", "primary": "1", "comment": ""}, + "client_id": {"type": "varchar(20)", "not null": "1", "default": "", "relation": {"clients": "client_id"}, "comment": ""}, + "redirect_uri": {"type": "varchar(200)", "not null": "1", "default": "", "comment": ""}, + "expires": {"type": "int", "not null": "1", "default": "0", "comment": ""}, + "scope": {"type": "varchar(250)", "not null": "1", "default": "", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "cache": { + "comment": "Stores temporary data", + "fields": { + "k": {"type": "varbinary(255)", "not null": "1", "primary": "1", "comment": "cache key"}, + "v": {"type": "mediumtext", "comment": "cached serialized value"}, + "expires": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "datetime of cache expiration"}, + "updated": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "datetime of cache insertion"} + }, + "indexes": { + "PRIMARY": ["k"], + "k_expires": ["k", "expires"] + } + }, + "challenge": { + "comment": "", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "challenge": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "dfrn-id": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "expire": {"type": "int unsigned", "not null": "1", "default": "0", "comment": ""}, + "type": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "last_update": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "clients": { + "comment": "OAuth usage", + "fields": { + "client_id": {"type": "varchar(20)", "not null": "1", "primary": "1", "comment": ""}, + "pw": {"type": "varchar(20)", "not null": "1", "default": "", "comment": ""}, + "redirect_uri": {"type": "varchar(200)", "not null": "1", "default": "", "comment": ""}, + "name": {"type": "text", "comment": ""}, + "icon": {"type": "text", "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"} + }, + "indexes": { + "PRIMARY": ["client_id"] + } + }, + "config": { + "comment": "main configuration storage", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": ""}, + "cat": {"type": "varbinary(50)", "not null": "1", "default": "", "comment": ""}, + "k": {"type": "varbinary(50)", "not null": "1", "default": "", "comment": ""}, + "v": {"type": "mediumtext", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "cat_k": ["UNIQUE", "cat", "k"] + } + }, + "contact": { + "comment": "contact table", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner User id"}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "self": {"type": "boolean", "not null": "1", "default": "0", "comment": "1 if the contact is the user him/her self"}, + "remote_self": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "rel": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": "The kind of the relation between the user and the contact"}, + "duplex": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "network": {"type": "char(4)", "not null": "1", "default": "", "comment": "Network protocol of the contact"}, + "name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Name that this contact is known by"}, + "nick": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Nick- and user name of the contact"}, + "location": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "about": {"type": "text", "comment": ""}, + "keywords": {"type": "text", "comment": "public keywords (interests) of the contact"}, + "gender": {"type": "varchar(32)", "not null": "1", "default": "", "comment": ""}, + "xmpp": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "attag": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "avatar": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "photo": {"type": "varchar(255)", "default": "", "comment": "Link to the profile photo of the contact"}, + "thumb": {"type": "varchar(255)", "default": "", "comment": "Link to the profile photo (thumb size)"}, + "micro": {"type": "varchar(255)", "default": "", "comment": "Link to the profile photo (micro size)"}, + "site-pubkey": {"type": "text", "comment": ""}, + "issued-id": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "dfrn-id": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "nurl": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "addr": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "alias": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "pubkey": {"type": "text", "comment": "RSA public key 4096 bit"}, + "prvkey": {"type": "text", "comment": "RSA private key 4096 bit"}, + "batch": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "request": {"type": "varchar(255)", "comment": ""}, + "notify": {"type": "varchar(255)", "comment": ""}, + "poll": {"type": "varchar(255)", "comment": ""}, + "confirm": {"type": "varchar(255)", "comment": ""}, + "poco": {"type": "varchar(255)", "comment": ""}, + "aes_allow": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "ret-aes": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "usehub": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "subhub": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "hub-verify": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "last-update": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date of the last try to update the contact info"}, + "success_update": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date of the last successful contact update"}, + "failure_update": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date of the last failed update"}, + "name-date": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "uri-date": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "avatar-date": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "term-date": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "last-item": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "date of the last post"}, + "priority": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "blocked": {"type": "boolean", "not null": "1", "default": "1", "comment": ""}, + "readonly": {"type": "boolean", "not null": "1", "default": "0", "comment": "posts of the contact are readonly"}, + "writable": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "forum": {"type": "boolean", "not null": "1", "default": "0", "comment": "contact is a forum"}, + "prv": {"type": "boolean", "not null": "1", "default": "0", "comment": "contact is a private group"}, + "contact-type": {"type": "tinyint", "not null": "1", "default": "0", "comment": ""}, + "hidden": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "archive": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "pending": {"type": "boolean", "not null": "1", "default": "1", "comment": ""}, + "rating": {"type": "tinyint", "not null": "1", "default": "0", "comment": ""}, + "reason": {"type": "text", "comment": ""}, + "closeness": {"type": "tinyint unsigned", "not null": "1", "default": "99", "comment": ""}, + "info": {"type": "mediumtext", "comment": ""}, + "profile-id": {"type": "int unsigned", "not null": "1", "default": "0", "comment": ""}, + "bdyear": {"type": "varchar(4)", "not null": "1", "default": "", "comment": ""}, + "bd": {"type": "date", "not null": "1", "default": "0001-01-01", "comment": ""}, + "notify_new_posts": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "fetch_further_information": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "ffi_keyword_blacklist": {"type": "text", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "uid_name": ["uid", "name(190)"], + "self_uid": ["self", "uid"], + "alias_uid": ["alias(32)", "uid"], + "pending_uid": ["pending", "uid"], + "blocked_uid": ["blocked", "uid"], + "uid_rel_network_poll": ["uid", "rel", "network", "poll(64)", "archive"], + "uid_network_batch": ["uid", "network", "batch(64)"], + "addr_uid": ["addr(32)", "uid"], + "nurl_uid": ["nurl(32)", "uid"], + "nick_uid": ["nick(32)", "uid"], + "dfrn-id": ["dfrn-id(64)"], + "issued-id": ["issued-id(64)"] + } + }, + "conv": { + "comment": "private messages", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "guid": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "A unique identifier for this conversation"}, + "recips": {"type": "text", "comment": "sender_handle;recipient_handle"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner User id"}, + "creator": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "handle of creator"}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "creation timestamp"}, + "updated": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "edited timestamp"}, + "subject": {"type": "text", "comment": "subject of initial message"} + }, + "indexes": { + "PRIMARY": ["id"], + "uid": ["uid"] + } + }, + "conversation": { + "comment": "Raw data and structure information for messages", + "fields": { + "item-uri": {"type": "varbinary(255)", "not null": "1", "primary": "1", "comment": "Original URI of the item - unrelated to the table with the same name"}, + "reply-to-uri": {"type": "varbinary(255)", "not null": "1", "default": "", "comment": "URI to which this item is a reply"}, + "conversation-uri": {"type": "varbinary(255)", "not null": "1", "default": "", "comment": "GNU Social conversation URI"}, + "conversation-href": {"type": "varbinary(255)", "not null": "1", "default": "", "comment": "GNU Social conversation link"}, + "protocol": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": "The protocol of the item"}, + "source": {"type": "mediumtext", "comment": "Original source"}, + "received": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Receiving date"} + }, + "indexes": { + "PRIMARY": ["item-uri"], + "conversation-uri": ["conversation-uri"], + "received": ["received"] + } + }, + "event": { + "comment": "Events", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "guid": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner User id"}, + "cid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "contact_id (ID of the contact in contact table)"}, + "uri": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "creation time"}, + "edited": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "last edit time"}, + "start": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "event start time"}, + "finish": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "event end time"}, + "summary": {"type": "text", "comment": "short description or title of the event"}, + "desc": {"type": "text", "comment": "event description"}, + "location": {"type": "text", "comment": "event location"}, + "type": {"type": "varchar(20)", "not null": "1", "default": "", "comment": "event or birthday"}, + "nofinish": {"type": "boolean", "not null": "1", "default": "0", "comment": "if event does have no end this is 1"}, + "adjust": {"type": "boolean", "not null": "1", "default": "1", "comment": "adjust to timezone of the recipient (0 or 1)"}, + "ignore": {"type": "boolean", "not null": "1", "default": "0", "comment": "0 or 1"}, + "allow_cid": {"type": "mediumtext", "comment": "Access Control - list of allowed contact.id '<19><78>'"}, + "allow_gid": {"type": "mediumtext", "comment": "Access Control - list of allowed groups"}, + "deny_cid": {"type": "mediumtext", "comment": "Access Control - list of denied contact.id"}, + "deny_gid": {"type": "mediumtext", "comment": "Access Control - list of denied groups"} + }, + "indexes": { + "PRIMARY": ["id"], + "uid_start": ["uid", "start"] + } + }, + "fcontact": { + "comment": "Diaspora compatible contacts - used in the Diaspora implementation", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "guid": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "unique id"}, + "url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "photo": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "request": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "nick": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "addr": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "batch": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "notify": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "poll": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "confirm": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "priority": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "network": {"type": "char(4)", "not null": "1", "default": "", "comment": ""}, + "alias": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "pubkey": {"type": "text", "comment": ""}, + "updated": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "addr": ["addr(32)"], + "url": ["UNIQUE", "url(190)"] + } + }, + "fsuggest": { + "comment": "friend suggestion stuff", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "cid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": ""}, + "name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "request": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "photo": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "note": {"type": "text", "comment": ""}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "gcign": { + "comment": "contacts ignored by friend suggestions", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Local User id"}, + "gcid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"gcontact": "id"}, "comment": "gcontact.id of ignored contact"} + }, + "indexes": { + "PRIMARY": ["id"], + "uid": ["uid"], + "gcid": ["gcid"] + } + }, + "gcontact": { + "comment": "global contacts", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Name that this contact is known by"}, + "nick": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Nick- and user name of the contact"}, + "url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Link to the contacts profile page"}, + "nurl": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "photo": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Link to the profile photo"}, + "connect": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "updated": {"type": "datetime", "default": "0001-01-01 00:00:00", "comment": ""}, + "last_contact": {"type": "datetime", "default": "0001-01-01 00:00:00", "comment": ""}, + "last_failure": {"type": "datetime", "default": "0001-01-01 00:00:00", "comment": ""}, + "location": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "about": {"type": "text", "comment": ""}, + "keywords": {"type": "text", "comment": "puplic keywords (interests)"}, + "gender": {"type": "varchar(32)", "not null": "1", "default": "", "comment": ""}, + "birthday": {"type": "varchar(32)", "not null": "1", "default": "0001-01-01", "comment": ""}, + "community": {"type": "boolean", "not null": "1", "default": "0", "comment": "1 if contact is forum account"}, + "contact-type": {"type": "tinyint", "not null": "1", "default": "-1", "comment": ""}, + "hide": {"type": "boolean", "not null": "1", "default": "0", "comment": "1 = should be hidden from search"}, + "nsfw": {"type": "boolean", "not null": "1", "default": "0", "comment": "1 = contact posts nsfw content"}, + "network": {"type": "char(4)", "not null": "1", "default": "", "comment": "social network protocol"}, + "addr": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "notify": {"type": "varchar(255)", "comment": ""}, + "alias": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "generation": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "server_url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "baseurl of the contacts server"} + }, + "indexes": { + "PRIMARY": ["id"], + "nurl": ["UNIQUE", "nurl(190)"], + "name": ["name(64)"], + "nick": ["nick(32)"], + "addr": ["addr(64)"], + "hide_network_updated": ["hide", "network", "updated"], + "updated": ["updated"] + } + }, + "glink": { + "comment": "'friends of friends' linkages derived from poco", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "cid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "gcid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"gcontact": "id"}, "comment": ""}, + "zcid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"gcontact": "id"}, "comment": ""}, + "updated": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "cid_uid_gcid_zcid": ["UNIQUE", "cid", "uid", "gcid", "zcid"], + "gcid": ["gcid"] + } + }, + "group": { + "comment": "privacy groups, group info", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner User id"}, + "visible": {"type": "boolean", "not null": "1", "default": "0", "comment": "1 indicates the member list is not private"}, + "deleted": {"type": "boolean", "not null": "1", "default": "0", "comment": "1 indicates the group has been deleted"}, + "name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "human readable name of group"} + }, + "indexes": { + "PRIMARY": ["id"], + "uid": ["uid"] + } + }, + "group_member": { + "comment": "privacy groups, member info", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "gid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"group": "id"}, "comment": "groups.id of the associated group"}, + "contact-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "contact.id of the member assigned to the associated group"} + }, + "indexes": { + "PRIMARY": ["id"], + "contactid": ["contact-id"], + "gid_contactid": ["UNIQUE", "gid", "contact-id"] + } + }, + "gserver": { + "comment": "Global servers", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "nurl": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "version": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "site_name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "info": {"type": "text", "comment": ""}, + "register_policy": {"type": "tinyint", "not null": "1", "default": "0", "comment": ""}, + "registered-users": {"type": "int unsigned", "not null": "1", "default": "0", "comment": "Number of registered users"}, + "poco": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "noscrape": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "network": {"type": "char(4)", "not null": "1", "default": "", "comment": ""}, + "platform": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "relay-subscribe": {"type": "boolean", "not null": "1", "default": "0", "comment": "Has the server subscribed to the relay system"}, + "relay-scope": {"type": "varchar(10)", "not null": "1", "default": "", "comment": "The scope of messages that the server wants to get"}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "last_poco_query": {"type": "datetime", "default": "0001-01-01 00:00:00", "comment": ""}, + "last_contact": {"type": "datetime", "default": "0001-01-01 00:00:00", "comment": ""}, + "last_failure": {"type": "datetime", "default": "0001-01-01 00:00:00", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "nurl": ["UNIQUE", "nurl(190)"] + } + }, + "gserver-tag": { + "comment": "Tags that the server has subscribed", + "fields": { + "gserver-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"gserver": "id"}, "primary": "1", "comment": "The id of the gserver"}, + "tag": {"type": "varchar(100)", "not null": "1", "default": "", "primary": "1", "comment": "Tag that the server has subscribed"} + }, + "indexes": { + "PRIMARY": ["gserver-id", "tag"], + "tag": ["tag"] + } + }, + "hook": { + "comment": "addon hook registry", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "hook": {"type": "varbinary(100)", "not null": "1", "default": "", "comment": "name of hook"}, + "file": {"type": "varbinary(200)", "not null": "1", "default": "", "comment": "relative filename of hook handler"}, + "function": {"type": "varbinary(200)", "not null": "1", "default": "", "comment": "function name of hook handler"}, + "priority": {"type": "smallint unsigned", "not null": "1", "default": "0", "comment": "not yet implemented - can be used to sort conflicts in hook handling by calling handlers in priority order"} + }, + "indexes": { + "PRIMARY": ["id"], + "hook_file_function": ["UNIQUE", "hook", "file", "function"] + } + }, + "intro": { + "comment": "", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "fid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"fcontact": "id"}, "comment": ""}, + "contact-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": ""}, + "knowyou": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "duplex": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "note": {"type": "text", "comment": ""}, + "hash": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "datetime": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "blocked": {"type": "boolean", "not null": "1", "default": "1", "comment": ""}, + "ignore": {"type": "boolean", "not null": "1", "default": "0", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "item": { + "comment": "Structure for all posts", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "relation": {"thread": "iid"}}, + "guid": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "A unique identifier for this item"}, + "uri": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "uri-id": {"type": "int unsigned", "relation": {"item-uri": "id"}, "comment": "Id of the item-uri table entry that contains the item uri"}, + "uri-hash": {"type": "varchar(80)", "not null": "1", "default": "", "comment": "RIPEMD-128 hash from uri"}, + "parent": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"item": "id"}, "comment": "item.id of the parent to this item if it is a reply of some form; otherwise this must be set to the id of this item"}, + "parent-uri": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "uri of the parent to this item"}, + "parent-uri-id": {"type": "int unsigned", "relation": {"item-uri": "id"}, "comment": "Id of the item-uri table that contains the parent uri"}, + "thr-parent": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "If the parent of this item is not the top-level item in the conversation, the uri of the immediate parent; otherwise set to parent-uri"}, + "thr-parent-id": {"type": "int unsigned", "relation": {"item-uri": "id"}, "comment": "Id of the item-uri table that contains the thread parent uri"}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Creation timestamp."}, + "edited": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date of last edit (default is created)"}, + "commented": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date of last comment/reply to this item"}, + "received": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "datetime"}, + "changed": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date that something in the conversation changed, indicating clients should fetch the conversation again"}, + "gravity": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "network": {"type": "char(4)", "not null": "1", "default": "", "comment": "Network from where the item comes from"}, + "owner-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "Link to the contact table with uid=0 of the owner of this item"}, + "author-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "Link to the contact table with uid=0 of the author of this item"}, + "icid": {"type": "int unsigned", "relation": {"item-content": "id"}, "comment": "Id of the item-content table entry that contains the whole item content"}, + "iaid": {"type": "int unsigned", "relation": {"item-activity": "id"}, "comment": "Id of the item-activity table entry that contains the activity data"}, + "extid": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "post-type": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": "Post type (personal note, bookmark, ...)"}, + "global": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "private": {"type": "boolean", "not null": "1", "default": "0", "comment": "distribution is restricted"}, + "visible": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "moderated": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "deleted": {"type": "boolean", "not null": "1", "default": "0", "comment": "item has been deleted"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner id which owns this copy of the item"}, + "contact-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "contact.id"}, + "wall": {"type": "boolean", "not null": "1", "default": "0", "comment": "This item was posted to the wall of uid"}, + "origin": {"type": "boolean", "not null": "1", "default": "0", "comment": "item originated at this site"}, + "pubmail": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "starred": {"type": "boolean", "not null": "1", "default": "0", "comment": "item has been favourited"}, + "unseen": {"type": "boolean", "not null": "1", "default": "1", "comment": "item has not been seen"}, + "mention": {"type": "boolean", "not null": "1", "default": "0", "comment": "The owner of this item was mentioned in it"}, + "forum_mode": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "psid": {"type": "int unsigned", "relation": {"permissionset": "id"}, "comment": "ID of the permission set of this post"}, + "resource-id": {"type": "varchar(32)", "not null": "1", "default": "", "comment": "Used to link other tables to items, it identifies the linked resource (e.g. photo) and if set must also set resource_type"}, + "event-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"event": "id"}, "comment": "Used to link to the event.id"}, + "attach": {"type": "mediumtext", "comment": "JSON structure representing attachments to this item"}, + "allow_cid": {"type": "mediumtext", "comment": "Deprecated"}, + "allow_gid": {"type": "mediumtext", "comment": "Deprecated"}, + "deny_cid": {"type": "mediumtext", "comment": "Deprecated"}, + "deny_gid": {"type": "mediumtext", "comment": "Deprecated"}, + "postopts": {"type": "text", "comment": "Deprecated"}, + "inform": {"type": "mediumtext", "comment": "Deprecated"}, + "type": {"type": "varchar(20)", "comment": "Deprecated"}, + "bookmark": {"type": "boolean", "comment": "Deprecated"}, + "file": {"type": "mediumtext", "comment": "Deprecated"}, + "location": {"type": "varchar(255)", "comment": "Deprecated"}, + "coord": {"type": "varchar(255)", "comment": "Deprecated"}, + "tag": {"type": "mediumtext", "comment": "Deprecated"}, + "plink": {"type": "varchar(255)", "comment": "Deprecated"}, + "title": {"type": "varchar(255)", "comment": "Deprecated"}, + "content-warning": {"type": "varchar(255)", "comment": "Deprecated"}, + "body": {"type": "mediumtext", "comment": "Deprecated"}, + "app": {"type": "varchar(255)", "comment": "Deprecated"}, + "verb": {"type": "varchar(100)", "comment": "Deprecated"}, + "object-type": {"type": "varchar(100)", "comment": "Deprecated"}, + "object": {"type": "text", "comment": "Deprecated"}, + "target-type": {"type": "varchar(100)", "comment": "Deprecated"}, + "target": {"type": "text", "comment": "Deprecated"}, + "author-name": {"type": "varchar(255)", "comment": "Deprecated"}, + "author-link": {"type": "varchar(255)", "comment": "Deprecated"}, + "author-avatar": {"type": "varchar(255)", "comment": "Deprecated"}, + "owner-name": {"type": "varchar(255)", "comment": "Deprecated"}, + "owner-link": {"type": "varchar(255)", "comment": "Deprecated"}, + "owner-avatar": {"type": "varchar(255)", "comment": "Deprecated"}, + "rendered-hash": {"type": "varchar(32)", "comment": "Deprecated"}, + "rendered-html": {"type": "mediumtext", "comment": "Deprecated"} + }, + "indexes": { + "PRIMARY": ["id"], + "guid": ["guid(191)"], + "uri": ["uri(191)"], + "parent": ["parent"], + "parent-uri": ["parent-uri(191)"], + "extid": ["extid(191)"], + "uid_id": ["uid", "id"], + "uid_contactid_id": ["uid", "contact-id", "id"], + "uid_created": ["uid", "created"], + "uid_commented": ["uid", "commented"], + "uid_unseen_contactid": ["uid", "unseen", "contact-id"], + "uid_network_received": ["uid", "network", "received"], + "uid_network_commented": ["uid", "network", "commented"], + "uid_thrparent": ["uid", "thr-parent(190)"], + "uid_parenturi": ["uid", "parent-uri(190)"], + "uid_contactid_created": ["uid", "contact-id", "created"], + "authorid_created": ["author-id", "created"], + "ownerid": ["owner-id"], + "uid_uri": ["uid", "uri(190)"], + "resource-id": ["resource-id"], + "deleted_changed": ["deleted", "changed"], + "uid_wall_changed": ["uid", "wall", "changed"], + "uid_eventid": ["uid", "event-id"], + "icid": ["icid"], + "iaid": ["iaid"], + "psid_wall": ["psid", "wall"] + } + }, + "item-activity": { + "comment": "Activities for items", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "relation": {"thread": "iid"}}, + "uri": {"type": "varchar(255)", "comment": ""}, + "uri-id": {"type": "int unsigned", "relation": {"item-uri": "id"}, "comment": "Id of the item-uri table entry that contains the item uri"}, + "uri-hash": {"type": "varchar(80)", "not null": "1", "default": "", "comment": "RIPEMD-128 hash from uri"}, + "activity": {"type": "smallint unsigned", "not null": "1", "default": "0", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "uri-hash": ["UNIQUE", "uri-hash"], + "uri": ["uri(191)"] + } + }, + "item-content": { + "comment": "Content for all posts", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "relation": {"thread": "iid"}}, + "uri": {"type": "varchar(255)", "comment": ""}, + "uri-id": {"type": "int unsigned", "relation": {"item-uri": "id"}, "comment": "Id of the item-uri table entry that contains the item uri"}, + "uri-plink-hash": {"type": "varchar(80)", "not null": "1", "default": "", "comment": "RIPEMD-128 hash from uri"}, + "title": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "item title"}, + "content-warning": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "body": {"type": "mediumtext", "comment": "item body content"}, + "location": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "text location where this item originated"}, + "coord": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "longitude/latitude pair representing location where this item originated"}, + "language": {"type": "text", "comment": "Language information about this post"}, + "app": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "application which generated this item"}, + "rendered-hash": {"type": "varchar(32)", "not null": "1", "default": "", "comment": ""}, + "rendered-html": {"type": "mediumtext", "comment": "item.body converted to html"}, + "object-type": {"type": "varchar(100)", "not null": "1", "default": "", "comment": "ActivityStreams object type"}, + "object": {"type": "text", "comment": "JSON encoded object structure unless it is an implied object (normal post)"}, + "target-type": {"type": "varchar(100)", "not null": "1", "default": "", "comment": "ActivityStreams target type if applicable (URI)"}, + "target": {"type": "text", "comment": "JSON encoded target structure if used"}, + "plink": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "permalink or URL to a displayable copy of the message at its source"}, + "verb": {"type": "varchar(100)", "not null": "1", "default": "", "comment": "ActivityStreams verb"} + }, + "indexes": { + "PRIMARY": ["id"], + "uri-plink-hash": ["UNIQUE", "uri-plink-hash"], + "uri": ["uri(191)"] + } + }, + "item-delivery-data": { + "comment": "Delivery data for items", + "fields": { + "iid": {"type": "int unsigned", "not null": "1", "primary": "1", "relation": {"item": "id"}, "comment": "Item id"}, + "postopts": {"type": "text", "comment": "External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery"}, + "inform": {"type": "mediumtext", "comment": "Additional receivers of the linked item"} + }, + "indexes": { + "PRIMARY": ["iid"] + } + }, + "item-uri": { + "comment": "URI and GUID for items", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1"}, + "uri": {"type": "varbinary(255)", "not null": "1", "comment": "URI of an item"}, + "guid": {"type": "varbinary(255)", "comment": "A unique identifier for an item"} + }, + "indexes": { + "PRIMARY": ["id"], + "uri": ["UNIQUE", "uri"], + "guid": ["guid"] + } + }, + "locks": { + "comment": "", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "name": {"type": "varchar(128)", "not null": "1", "default": "", "comment": ""}, + "locked": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "pid": {"type": "int unsigned", "not null": "1", "default": "0", "comment": "Process ID"}, + "expires": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "datetime of cache expiration"} + }, + "indexes": { + "PRIMARY": ["id"], + "name_expires": ["name", "expires"] + } + }, + "mail": { + "comment": "private messages", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner User id"}, + "guid": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "A unique identifier for this private message"}, + "from-name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "name of the sender"}, + "from-photo": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "contact photo link of the sender"}, + "from-url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "profile linke of the sender"}, + "contact-id": {"type": "varchar(255)", "not null": "1", "default": "", "relation": {"contact": "id"}, "comment": "contact.id"}, + "convid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"conv": "id"}, "comment": "conv.id"}, + "title": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "body": {"type": "mediumtext", "comment": ""}, + "seen": {"type": "boolean", "not null": "1", "default": "0", "comment": "if message visited it is 1"}, + "reply": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "replied": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "unknown": {"type": "boolean", "not null": "1", "default": "0", "comment": "if sender not in the contact table this is 1"}, + "uri": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "parent-uri": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "creation time of the private message"} + }, + "indexes": { + "PRIMARY": ["id"], + "uid_seen": ["uid", "seen"], + "convid": ["convid"], + "uri": ["uri(64)"], + "parent-uri": ["parent-uri(64)"], + "contactid": ["contact-id(32)"] + } + }, + "mailacct": { + "comment": "Mail account data for fetching mails", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "server": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "port": {"type": "smallint unsigned", "not null": "1", "default": "0", "comment": ""}, + "ssltype": {"type": "varchar(16)", "not null": "1", "default": "", "comment": ""}, + "mailbox": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "user": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "pass": {"type": "text", "comment": ""}, + "reply_to": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "action": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "movetofolder": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "pubmail": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "last_check": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "manage": { + "comment": "table of accounts that can manage each other", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "mid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"} + }, + "indexes": { + "PRIMARY": ["id"], + "uid_mid": ["UNIQUE", "uid", "mid"] + } + }, + "notify": { + "comment": "notifications", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "hash": {"type": "varchar(64)", "not null": "1", "default": "", "comment": ""}, + "type": {"type": "smallint unsigned", "not null": "1", "default": "0", "comment": ""}, + "name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "photo": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "date": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "msg": {"type": "mediumtext", "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner User id"}, + "link": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "iid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"item": "id"}, "comment": "item.id"}, + "parent": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"item": "id"}, "comment": ""}, + "seen": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "verb": {"type": "varchar(100)", "not null": "1", "default": "", "comment": ""}, + "otype": {"type": "varchar(10)", "not null": "1", "default": "", "comment": ""}, + "name_cache": {"type": "tinytext", "comment": "Cached bbcode parsing of name"}, + "msg_cache": {"type": "mediumtext", "comment": "Cached bbcode parsing of msg"} + }, + "indexes": { + "PRIMARY": ["id"], + "hash_uid": ["hash", "uid"], + "seen_uid_date": ["seen", "uid", "date"], + "uid_date": ["uid", "date"], + "uid_type_link": ["uid", "type", "link(190)"] + } + }, + "notify-threads": { + "comment": "", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "notify-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"notify": "id"}, "comment": ""}, + "master-parent-item": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"item": "id"}, "comment": ""}, + "parent-item": {"type": "int unsigned", "not null": "1", "default": "0", "comment": ""}, + "receiver-uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "oembed": { + "comment": "cache for OEmbed queries", + "fields": { + "url": {"type": "varbinary(255)", "not null": "1", "primary": "1", "comment": "page url"}, + "maxwidth": {"type": "mediumint unsigned", "not null": "1", "primary": "1", "comment": "Maximum width passed to Oembed"}, + "content": {"type": "mediumtext", "comment": "OEmbed data of the page"}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "datetime of creation"} + }, + "indexes": { + "PRIMARY": ["url", "maxwidth"], + "created": ["created"] + } + }, + "openwebauth-token": { + "comment": "Store OpenWebAuth token to verify contacts", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "type": {"type": "varchar(32)", "not null": "1", "default": "", "comment": "Verify type"}, + "token": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "A generated token"}, + "meta": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "datetime of creation"} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "parsed_url": { + "comment": "cache for 'parse_url' queries", + "fields": { + "url": {"type": "varbinary(255)", "not null": "1", "primary": "1", "comment": "page url"}, + "guessing": {"type": "boolean", "not null": "1", "default": "0", "primary": "1", "comment": "is the 'guessing' mode active?"}, + "oembed": {"type": "boolean", "not null": "1", "default": "0", "primary": "1", "comment": "is the data the result of oembed?"}, + "content": {"type": "mediumtext", "comment": "page data"}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "datetime of creation"} + }, + "indexes": { + "PRIMARY": ["url", "guessing", "oembed"], + "created": ["created"] + } + }, + "participation": { + "comment": "Storage for participation messages from Diaspora", + "fields": { + "iid": {"type": "int unsigned", "not null": "1", "primary": "1", "relation": {"item": "id"}, "comment": ""}, + "server": {"type": "varchar(60)", "not null": "1", "primary": "1", "comment": ""}, + "cid": {"type": "int unsigned", "not null": "1", "relation": {"contact": "id"}, "comment": ""}, + "fid": {"type": "int unsigned", "not null": "1", "relation": {"fcontact": "id"}, "comment": ""} + }, + "indexes": { + "PRIMARY": ["iid", "server"] + } + }, + "pconfig": { + "comment": "personal (per user) configuration storage", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "cat": {"type": "varbinary(50)", "not null": "1", "default": "", "comment": ""}, + "k": {"type": "varbinary(100)", "not null": "1", "default": "", "comment": ""}, + "v": {"type": "mediumtext", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "uid_cat_k": ["UNIQUE", "uid", "cat", "k"] + } + }, + "permissionset": { + "comment": "", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner id of this permission set"}, + "allow_cid": {"type": "mediumtext", "comment": "Access Control - list of allowed contact.id '<19><78>'"}, + "allow_gid": {"type": "mediumtext", "comment": "Access Control - list of allowed groups"}, + "deny_cid": {"type": "mediumtext", "comment": "Access Control - list of denied contact.id"}, + "deny_gid": {"type": "mediumtext", "comment": "Access Control - list of denied groups"} + }, + "indexes": { + "PRIMARY": ["id"], + "uid_allow_cid_allow_gid_deny_cid_deny_gid": ["allow_cid(50)", "allow_gid(30)", "deny_cid(50)", "deny_gid(30)"] + } + }, + "photo": { + "comment": "photo storage", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner User id"}, + "contact-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "contact.id"}, + "guid": {"type": "char(16)", "not null": "1", "default": "", "comment": "A unique identifier for this photo"}, + "resource-id": {"type": "char(32)", "not null": "1", "default": "", "comment": ""}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "creation date"}, + "edited": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "last edited date"}, + "title": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "desc": {"type": "text", "comment": ""}, + "album": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "The name of the album to which the photo belongs"}, + "filename": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "type": {"type": "varchar(30)", "not null": "1", "default": "image/jpeg"}, + "height": {"type": "smallint unsigned", "not null": "1", "default": "0", "comment": ""}, + "width": {"type": "smallint unsigned", "not null": "1", "default": "0", "comment": ""}, + "datasize": {"type": "int unsigned", "not null": "1", "default": "0", "comment": ""}, + "data": {"type": "mediumblob", "not null": "1", "comment": ""}, + "scale": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "profile": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "allow_cid": {"type": "mediumtext", "comment": "Access Control - list of allowed contact.id '<19><78>'"}, + "allow_gid": {"type": "mediumtext", "comment": "Access Control - list of allowed groups"}, + "deny_cid": {"type": "mediumtext", "comment": "Access Control - list of denied contact.id"}, + "deny_gid": {"type": "mediumtext", "comment": "Access Control - list of denied groups"} + }, + "indexes": { + "PRIMARY": ["id"], + "contactid": ["contact-id"], + "uid_contactid": ["uid", "contact-id"], + "uid_profile": ["uid", "profile"], + "uid_album_scale_created": ["uid", "album(32)", "scale", "created"], + "uid_album_resource-id_created": ["uid", "album(32)", "resource-id", "created"], + "resource-id": ["resource-id"] + } + }, + "poll": { + "comment": "Currently unused table for storing poll results", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "q0": {"type": "text", "comment": ""}, + "q1": {"type": "text", "comment": ""}, + "q2": {"type": "text", "comment": ""}, + "q3": {"type": "text", "comment": ""}, + "q4": {"type": "text", "comment": ""}, + "q5": {"type": "text", "comment": ""}, + "q6": {"type": "text", "comment": ""}, + "q7": {"type": "text", "comment": ""}, + "q8": {"type": "text", "comment": ""}, + "q9": {"type": "text", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "uid": ["uid"] + } + }, + "poll_result": { + "comment": "data for polls - currently unused", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "poll_id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"poll": "id"}}, + "choice": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "poll_id": ["poll_id"] + } + }, + "process": { + "comment": "Currently running system processes", + "fields": { + "pid": {"type": "int unsigned", "not null": "1", "primary": "1", "comment": ""}, + "command": {"type": "varbinary(32)", "not null": "1", "default": "", "comment": ""}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""} + }, + "indexes": { + "PRIMARY": ["pid"], + "command": ["command"] + } + }, + "profile": { + "comment": "user profiles data", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "Owner User id"}, + "profile-name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Name of the profile"}, + "is-default": {"type": "boolean", "not null": "1", "default": "0", "comment": "Mark this profile as default profile"}, + "hide-friends": {"type": "boolean", "not null": "1", "default": "0", "comment": "Hide friend list from viewers of this profile"}, + "name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "pdesc": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Title or description"}, + "dob": {"type": "varchar(32)", "not null": "1", "default": "0000-00-00", "comment": "Day of birth"}, + "address": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "locality": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "region": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "postal-code": {"type": "varchar(32)", "not null": "1", "default": "", "comment": ""}, + "country-name": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "hometown": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "gender": {"type": "varchar(32)", "not null": "1", "default": "", "comment": ""}, + "marital": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "with": {"type": "text", "comment": ""}, + "howlong": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "sexual": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "politic": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "religion": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "pub_keywords": {"type": "text", "comment": ""}, + "prv_keywords": {"type": "text", "comment": ""}, + "likes": {"type": "text", "comment": ""}, + "dislikes": {"type": "text", "comment": ""}, + "about": {"type": "text", "comment": ""}, + "summary": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "music": {"type": "text", "comment": ""}, + "book": {"type": "text", "comment": ""}, + "tv": {"type": "text", "comment": ""}, + "film": {"type": "text", "comment": ""}, + "interest": {"type": "text", "comment": ""}, + "romance": {"type": "text", "comment": ""}, + "work": {"type": "text", "comment": ""}, + "education": {"type": "text", "comment": ""}, + "contact": {"type": "text", "comment": ""}, + "homepage": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "xmpp": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "photo": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "thumb": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "publish": {"type": "boolean", "not null": "1", "default": "0", "comment": "publish default profile in local directory"}, + "net-publish": {"type": "boolean", "not null": "1", "default": "0", "comment": "publish profile in global directory"} + }, + "indexes": { + "PRIMARY": ["id"], + "uid_is-default": ["uid", "is-default"] + } + }, + "profile_check": { + "comment": "DFRN remote auth use", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "cid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "contact.id"}, + "dfrn_id": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "sec": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "expire": {"type": "int unsigned", "not null": "1", "default": "0", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "push_subscriber": { + "comment": "Used for OStatus: Contains feed subscribers", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "callback_url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "topic": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "nickname": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "push": {"type": "tinyint", "not null": "1", "default": "0", "comment": "Retrial counter"}, + "last_update": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date of last successful trial"}, + "next_try": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Next retrial date"}, + "renewed": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date of last subscription renewal"}, + "secret": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "next_try": ["next_try"] + } + }, + "queue": { + "comment": "Queue for messages that couldn't be delivered", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "cid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "Message receiver"}, + "network": {"type": "char(4)", "not null": "1", "default": "", "comment": "Receiver's network"}, + "guid": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Unique GUID of the message"}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date, when the message was created"}, + "last": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Date of last trial"}, + "next": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Next retrial date"}, + "retrial": {"type": "tinyint", "not null": "1", "default": "0", "comment": "Retrial counter"}, + "content": {"type": "mediumtext", "comment": ""}, + "batch": {"type": "boolean", "not null": "1", "default": "0", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "last": ["last"], + "next": ["next"] + } + }, + "register": { + "comment": "registrations requiring admin approval", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "hash": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "password": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "language": {"type": "varchar(16)", "not null": "1", "default": "", "comment": ""}, + "note": {"type": "text", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "search": { + "comment": "", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "term": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "uid": ["uid"] + } + }, + "session": { + "comment": "web session storage", + "fields": { + "id": {"type": "bigint unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "sid": {"type": "varbinary(255)", "not null": "1", "default": "", "comment": ""}, + "data": {"type": "text", "comment": ""}, + "expire": {"type": "int unsigned", "not null": "1", "default": "0", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "sid": ["sid(64)"], + "expire": ["expire"] + } + }, + "sign": { + "comment": "Diaspora signatures", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "iid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"item": "id"}, "comment": "item.id"}, + "signed_text": {"type": "mediumtext", "comment": ""}, + "signature": {"type": "text", "comment": ""}, + "signer": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "iid": ["UNIQUE", "iid"] + } + }, + "term": { + "comment": "item taxonomy (categories, tags, etc.) table", + "fields": { + "tid": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": ""}, + "oid": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"item": "id"}, "comment": ""}, + "otype": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "type": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "term": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "url": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "guid": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "received": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "global": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"} + }, + "indexes": { + "PRIMARY": ["tid"], + "oid_otype_type_term": ["oid", "otype", "type", "term(32)"], + "uid_otype_type_term_global_created": ["uid", "otype", "type", "term(32)", "global", "created"], + "uid_otype_type_url": ["uid", "otype", "type", "url(64)"], + "guid": ["guid(64)"] + } + }, + "thread": { + "comment": "Thread related data", + "fields": { + "iid": {"type": "int unsigned", "not null": "1", "default": "0", "primary": "1", "relation": {"item": "id"}, "comment": "sequential ID"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"}, + "contact-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": ""}, + "owner-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "Item owner"}, + "author-id": {"type": "int unsigned", "not null": "1", "default": "0", "relation": {"contact": "id"}, "comment": "Item author"}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "edited": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "commented": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "received": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "changed": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": ""}, + "wall": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "private": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "pubmail": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "moderated": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "visible": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "starred": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "ignored": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "post-type": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": "Post type (personal note, bookmark, ...)"}, + "unseen": {"type": "boolean", "not null": "1", "default": "1", "comment": ""}, + "deleted": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "origin": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "forum_mode": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "mention": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "network": {"type": "char(4)", "not null": "1", "default": "", "comment": ""}, + "bookmark": {"type": "boolean", "comment": ""} + }, + "indexes": { + "PRIMARY": ["iid"], + "uid_network_commented": ["uid", "network", "commented"], + "uid_network_created": ["uid", "network", "created"], + "uid_contactid_commented": ["uid", "contact-id", "commented"], + "uid_contactid_created": ["uid", "contact-id", "created"], + "contactid": ["contact-id"], + "ownerid": ["owner-id"], + "authorid": ["author-id"], + "uid_created": ["uid", "created"], + "uid_commented": ["uid", "commented"], + "uid_wall_created": ["uid", "wall", "created"], + "private_wall_origin_commented": ["private", "wall", "origin", "commented"] + } + }, + "tokens": { + "comment": "OAuth usage", + "fields": { + "id": {"type": "varchar(40)", "not null": "1", "primary": "1", "comment": ""}, + "secret": {"type": "text", "comment": ""}, + "client_id": {"type": "varchar(20)", "not null": "1", "default": "", "relation": {"clients": "client_id"}}, + "expires": {"type": "int", "not null": "1", "default": "0", "comment": ""}, + "scope": {"type": "varchar(200)", "not null": "1", "default": "", "comment": ""}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "User id"} + }, + "indexes": { + "PRIMARY": ["id"] + } + }, + "user": { + "comment": "The local users", + "fields": { + "uid": {"type": "mediumint unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "parent-uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "relation": {"user": "uid"}, "comment": "The parent user that has full control about this user"}, + "guid": {"type": "varchar(64)", "not null": "1", "default": "", "comment": "A unique identifier for this user"}, + "username": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Name that this user is known by"}, + "password": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "encrypted password"}, + "legacy_password": {"type": "boolean", "not null": "1", "default": "0", "comment": "Is the password hash double-hashed?"}, + "nickname": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "nick- and user name"}, + "email": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "the users email address"}, + "openid": {"type": "varchar(255)", "not null": "1", "default": "", "comment": ""}, + "timezone": {"type": "varchar(128)", "not null": "1", "default": "", "comment": "PHP-legal timezone"}, + "language": {"type": "varchar(32)", "not null": "1", "default": "en", "comment": "default language"}, + "register_date": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "timestamp of registration"}, + "login_date": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "timestamp of last login"}, + "default-location": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "Default for item.location"}, + "allow_location": {"type": "boolean", "not null": "1", "default": "0", "comment": "1 allows to display the location"}, + "theme": {"type": "varchar(255)", "not null": "1", "default": "", "comment": "user theme preference"}, + "pubkey": {"type": "text", "comment": "RSA public key 4096 bit"}, + "prvkey": {"type": "text", "comment": "RSA private key 4096 bit"}, + "spubkey": {"type": "text", "comment": ""}, + "sprvkey": {"type": "text", "comment": ""}, + "verified": {"type": "boolean", "not null": "1", "default": "0", "comment": "user is verified through email"}, + "blocked": {"type": "boolean", "not null": "1", "default": "0", "comment": "1 for user is blocked"}, + "blockwall": {"type": "boolean", "not null": "1", "default": "0", "comment": "Prohibit contacts to post to the profile page of the user"}, + "hidewall": {"type": "boolean", "not null": "1", "default": "0", "comment": "Hide profile details from unkown viewers"}, + "blocktags": {"type": "boolean", "not null": "1", "default": "0", "comment": "Prohibit contacts to tag the post of this user"}, + "unkmail": {"type": "boolean", "not null": "1", "default": "0", "comment": "Permit unknown people to send private mails to this user"}, + "cntunkmail": {"type": "int unsigned", "not null": "1", "default": "10", "comment": ""}, + "notify-flags": {"type": "smallint unsigned", "not null": "1", "default": "65535", "comment": "email notification options"}, + "page-flags": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": "page/profile type"}, + "account-type": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": ""}, + "prvnets": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "pwdreset": {"type": "varchar(255)", "comment": "Password reset request token"}, + "pwdreset_time": {"type": "datetime", "comment": "Timestamp of the last password reset request"}, + "maxreq": {"type": "int unsigned", "not null": "1", "default": "10", "comment": ""}, + "expire": {"type": "int unsigned", "not null": "1", "default": "0", "comment": ""}, + "account_removed": {"type": "boolean", "not null": "1", "default": "0", "comment": "if 1 the account is removed"}, + "account_expired": {"type": "boolean", "not null": "1", "default": "0", "comment": ""}, + "account_expires_on": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "timestamp when account expires and will be deleted"}, + "expire_notification_sent": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "timestamp of last warning of account expiration"}, + "def_gid": {"type": "int unsigned", "not null": "1", "default": "0", "comment": ""}, + "allow_cid": {"type": "mediumtext", "comment": "default permission for this user"}, + "allow_gid": {"type": "mediumtext", "comment": "default permission for this user"}, + "deny_cid": {"type": "mediumtext", "comment": "default permission for this user"}, + "deny_gid": {"type": "mediumtext", "comment": "default permission for this user"}, + "openidserver": {"type": "text", "comment": ""} + }, + "indexes": { + "PRIMARY": ["uid"], + "nickname": ["nickname(32)"] + } + }, + "userd": { + "comment": "Deleted usernames", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "sequential ID"}, + "username": {"type": "varchar(255)", "not null": "1", "comment": ""} + }, + "indexes": { + "PRIMARY": ["id"], + "username": ["username(32)"] + } + }, + "user-contact": { + "comment": "User specific public contact data", + "fields": { + "cid": {"type": "int unsigned", "not null": "1", "default": "0", "primary": "1", "relation": {"contact": "id"}, "comment": "Contact id of the linked public contact"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "primary": "1", "relation": {"user": "uid"}, "comment": "User id"}, + "blocked": {"type": "boolean", "comment": "Contact is completely blocked for this user"}, + "ignored": {"type": "boolean", "comment": "Posts from this contact are ignored"}, + "collapsed": {"type": "boolean", "comment": "Posts from this contact are collapsed"} + }, + "indexes": { + "PRIMARY": ["uid", "cid"] + } + }, + "user-item": { + "comment": "User specific item data", + "fields": { + "iid": {"type": "int unsigned", "not null": "1", "default": "0", "primary": "1", "relation": {"item": "id"}, "comment": "Item id"}, + "uid": {"type": "mediumint unsigned", "not null": "1", "default": "0", "primary": "1", "relation": {"user": "uid"}, "comment": "User id"}, + "hidden": {"type": "boolean", "not null": "1", "default": "0", "comment": "Marker to hide an item from the user"}, + "ignored": {"type": "boolean", "comment": "Ignore this thread if set"} + }, + "indexes": { + "PRIMARY": ["uid", "iid"] + } + }, + "worker-ipc": { + "comment": "Inter process communication between the frontend and the worker", + "fields": { + "key": {"type": "int", "not null": "1", "primary": "1", "comment": ""}, + "jobs": {"type": "boolean", "comment": "Flag for outstanding jobs"} + }, + "indexes": { + "PRIMARY": ["key"] + }, + "engine": "MEMORY" + }, + "workerqueue": { + "comment": "Background tasks queue entries", + "fields": { + "id": {"type": "int unsigned", "not null": "1", "extra": "auto_increment", "primary": "1", "comment": "Auto incremented worker task id"}, + "parameter": {"type": "mediumblob", "comment": "Task command"}, + "priority": {"type": "tinyint unsigned", "not null": "1", "default": "0", "comment": "Task priority"}, + "created": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Creation date"}, + "pid": {"type": "int unsigned", "not null": "1", "default": "0", "comment": "Process id of the worker"}, + "executed": {"type": "datetime", "not null": "1", "default": "0001-01-01 00:00:00", "comment": "Execution date"}, + "done": {"type": "boolean", "not null": "1", "default": "0", "comment": "Marked 1 when the task was done - will be deleted later"} + }, + "indexes": { + "PRIMARY": ["id"], + "pid": ["pid"], + "parameter": ["parameter(64)"], + "priority_created": ["priority", "created"], + "done_executed": ["done", "executed"] + } + } +} diff --git a/config/local-sample.ini.php b/config/local-sample.ini.php new file mode 100644 index 000000000..359f3ee68 --- /dev/null +++ b/config/local-sample.ini.php @@ -0,0 +1,41 @@ +<78>', + `allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups', + `deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id', + `deny_gid` mediumtext COMMENT 'Access Control - list of denied groups', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='file attachments'; -- -- TABLE auth_codes @@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS `auth_codes` ( `expires` int NOT NULL DEFAULT 0 COMMENT '', `scope` varchar(250) NOT NULL DEFAULT '' COMMENT '', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage'; -- -- TABLE cache @@ -61,20 +61,20 @@ CREATE TABLE IF NOT EXISTS `cache` ( `updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of cache insertion', PRIMARY KEY(`k`), INDEX `k_expires` (`k`,`expires`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Stores temporary data'; -- -- TABLE challenge -- CREATE TABLE IF NOT EXISTS `challenge` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `challenge` varchar(255) NOT NULL DEFAULT '' COMMENT '', `dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT '', `expire` int unsigned NOT NULL DEFAULT 0 COMMENT '', `type` varchar(255) NOT NULL DEFAULT '' COMMENT '', `last_update` varchar(255) NOT NULL DEFAULT '' COMMENT '', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; -- -- TABLE clients @@ -87,7 +87,7 @@ CREATE TABLE IF NOT EXISTS `clients` ( `icon` text COMMENT '', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', PRIMARY KEY(`client_id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage'; -- -- TABLE config @@ -99,32 +99,32 @@ CREATE TABLE IF NOT EXISTS `config` ( `v` mediumtext COMMENT '', PRIMARY KEY(`id`), UNIQUE INDEX `cat_k` (`cat`,`k`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='main configuration storage'; -- -- TABLE contact -- CREATE TABLE IF NOT EXISTS `contact` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `self` boolean NOT NULL DEFAULT '0' COMMENT '', + `self` boolean NOT NULL DEFAULT '0' COMMENT '1 if the contact is the user him/her self', `remote_self` boolean NOT NULL DEFAULT '0' COMMENT '', - `rel` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', + `rel` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'The kind of the relation between the user and the contact', `duplex` boolean NOT NULL DEFAULT '0' COMMENT '', - `network` char(4) NOT NULL DEFAULT '' COMMENT '', - `name` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `nick` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `network` char(4) NOT NULL DEFAULT '' COMMENT 'Network protocol of the contact', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this contact is known by', + `nick` varchar(255) NOT NULL DEFAULT '' COMMENT 'Nick- and user name of the contact', `location` varchar(255) NOT NULL DEFAULT '' COMMENT '', `about` text COMMENT '', - `keywords` text COMMENT '', + `keywords` text COMMENT 'public keywords (interests) of the contact', `gender` varchar(32) NOT NULL DEFAULT '' COMMENT '', `xmpp` varchar(255) NOT NULL DEFAULT '' COMMENT '', `attag` varchar(255) NOT NULL DEFAULT '' COMMENT '', `avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `photo` varchar(255) DEFAULT '' COMMENT '', - `thumb` varchar(255) DEFAULT '' COMMENT '', - `micro` varchar(255) DEFAULT '' COMMENT '', + `photo` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo of the contact', + `thumb` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (thumb size)', + `micro` varchar(255) DEFAULT '' COMMENT 'Link to the profile photo (micro size)', `site-pubkey` text COMMENT '', `issued-id` varchar(255) NOT NULL DEFAULT '' COMMENT '', `dfrn-id` varchar(255) NOT NULL DEFAULT '' COMMENT '', @@ -132,8 +132,8 @@ CREATE TABLE IF NOT EXISTS `contact` ( `nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '', `addr` varchar(255) NOT NULL DEFAULT '' COMMENT '', `alias` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `pubkey` text COMMENT '', - `prvkey` text COMMENT '', + `pubkey` text COMMENT 'RSA public key 4096 bit', + `prvkey` text COMMENT 'RSA private key 4096 bit', `batch` varchar(255) NOT NULL DEFAULT '' COMMENT '', `request` varchar(255) COMMENT '', `notify` varchar(255) COMMENT '', @@ -145,20 +145,20 @@ CREATE TABLE IF NOT EXISTS `contact` ( `usehub` boolean NOT NULL DEFAULT '0' COMMENT '', `subhub` boolean NOT NULL DEFAULT '0' COMMENT '', `hub-verify` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `last-update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `success_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `failure_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `last-update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last try to update the contact info', + `success_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last successful contact update', + `failure_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last failed update', `name-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `uri-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `avatar-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `term-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `last-item` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `last-item` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last post', `priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `blocked` boolean NOT NULL DEFAULT '1' COMMENT '', - `readonly` boolean NOT NULL DEFAULT '0' COMMENT '', + `readonly` boolean NOT NULL DEFAULT '0' COMMENT 'posts of the contact are readonly', `writable` boolean NOT NULL DEFAULT '0' COMMENT '', - `forum` boolean NOT NULL DEFAULT '0' COMMENT '', - `prv` boolean NOT NULL DEFAULT '0' COMMENT '', + `forum` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a forum', + `prv` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a private group', `contact-type` tinyint NOT NULL DEFAULT 0 COMMENT '', `hidden` boolean NOT NULL DEFAULT '0' COMMENT '', `archive` boolean NOT NULL DEFAULT '0' COMMENT '', @@ -186,74 +186,74 @@ CREATE TABLE IF NOT EXISTS `contact` ( INDEX `nick_uid` (`nick`(32),`uid`), INDEX `dfrn-id` (`dfrn-id`(64)), INDEX `issued-id` (`issued-id`(64)) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contact table'; -- -- TABLE conv -- CREATE TABLE IF NOT EXISTS `conv` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `guid` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `recips` text COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `creator` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `subject` text COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `guid` varchar(255) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this conversation', + `recips` text COMMENT 'sender_handle;recipient_handle', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', + `creator` varchar(255) NOT NULL DEFAULT '' COMMENT 'handle of creator', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation timestamp', + `updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'edited timestamp', + `subject` text COMMENT 'subject of initial message', PRIMARY KEY(`id`), INDEX `uid` (`uid`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='private messages'; -- -- TABLE conversation -- CREATE TABLE IF NOT EXISTS `conversation` ( - `item-uri` varbinary(255) NOT NULL COMMENT '', - `reply-to-uri` varbinary(255) NOT NULL DEFAULT '' COMMENT '', - `conversation-uri` varbinary(255) NOT NULL DEFAULT '' COMMENT '', - `conversation-href` varbinary(255) NOT NULL DEFAULT '' COMMENT '', - `protocol` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', - `source` mediumtext COMMENT '', - `received` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `item-uri` varbinary(255) NOT NULL COMMENT 'Original URI of the item - unrelated to the table with the same name', + `reply-to-uri` varbinary(255) NOT NULL DEFAULT '' COMMENT 'URI to which this item is a reply', + `conversation-uri` varbinary(255) NOT NULL DEFAULT '' COMMENT 'GNU Social conversation URI', + `conversation-href` varbinary(255) NOT NULL DEFAULT '' COMMENT 'GNU Social conversation link', + `protocol` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'The protocol of the item', + `source` mediumtext COMMENT 'Original source', + `received` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Receiving date', PRIMARY KEY(`item-uri`), INDEX `conversation-uri` (`conversation-uri`), INDEX `received` (`received`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Raw data and structure information for messages'; -- -- TABLE event -- CREATE TABLE IF NOT EXISTS `event` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `guid` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `cid` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', + `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact_id (ID of the contact in contact table)', `uri` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `start` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `finish` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `summary` text COMMENT '', - `desc` text COMMENT '', - `location` text COMMENT '', - `type` varchar(20) NOT NULL DEFAULT '' COMMENT '', - `nofinish` boolean NOT NULL DEFAULT '0' COMMENT '', - `adjust` boolean NOT NULL DEFAULT '1' COMMENT '', - `ignore` boolean NOT NULL DEFAULT '0' COMMENT '', - `allow_cid` mediumtext COMMENT '', - `allow_gid` mediumtext COMMENT '', - `deny_cid` mediumtext COMMENT '', - `deny_gid` mediumtext COMMENT '', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation time', + `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'last edit time', + `start` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'event start time', + `finish` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'event end time', + `summary` text COMMENT 'short description or title of the event', + `desc` text COMMENT 'event description', + `location` text COMMENT 'event location', + `type` varchar(20) NOT NULL DEFAULT '' COMMENT 'event or birthday', + `nofinish` boolean NOT NULL DEFAULT '0' COMMENT 'if event does have no end this is 1', + `adjust` boolean NOT NULL DEFAULT '1' COMMENT 'adjust to timezone of the recipient (0 or 1)', + `ignore` boolean NOT NULL DEFAULT '0' COMMENT '0 or 1', + `allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>\'', + `allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups', + `deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id', + `deny_gid` mediumtext COMMENT 'Access Control - list of denied groups', PRIMARY KEY(`id`), INDEX `uid_start` (`uid`,`start`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events'; -- -- TABLE fcontact -- CREATE TABLE IF NOT EXISTS `fcontact` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `guid` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `guid` varchar(255) NOT NULL DEFAULT '' COMMENT 'unique id', `url` varchar(255) NOT NULL DEFAULT '' COMMENT '', `name` varchar(255) NOT NULL DEFAULT '' COMMENT '', `photo` varchar(255) NOT NULL DEFAULT '' COMMENT '', @@ -272,7 +272,7 @@ CREATE TABLE IF NOT EXISTS `fcontact` ( PRIMARY KEY(`id`), INDEX `addr` (`addr`(32)), UNIQUE INDEX `url` (`url`(190)) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Diaspora compatible contacts - used in the Diaspora implementation'; -- -- TABLE fsuggest @@ -288,30 +288,30 @@ CREATE TABLE IF NOT EXISTS `fsuggest` ( `note` text COMMENT '', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='friend suggestion stuff'; -- -- TABLE gcign -- CREATE TABLE IF NOT EXISTS `gcign` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `gcid` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Local User id', + `gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'gcontact.id of ignored contact', PRIMARY KEY(`id`), INDEX `uid` (`uid`), INDEX `gcid` (`gcid`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contacts ignored by friend suggestions'; -- -- TABLE gcontact -- CREATE TABLE IF NOT EXISTS `gcontact` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `name` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `nick` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `url` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this contact is known by', + `nick` varchar(255) NOT NULL DEFAULT '' COMMENT 'Nick- and user name of the contact', + `url` varchar(255) NOT NULL DEFAULT '' COMMENT 'Link to the contacts profile page', `nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `photo` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `photo` varchar(255) NOT NULL DEFAULT '' COMMENT 'Link to the profile photo', `connect` varchar(255) NOT NULL DEFAULT '' COMMENT '', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `updated` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', @@ -319,19 +319,19 @@ CREATE TABLE IF NOT EXISTS `gcontact` ( `last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', `location` varchar(255) NOT NULL DEFAULT '' COMMENT '', `about` text COMMENT '', - `keywords` text COMMENT '', + `keywords` text COMMENT 'puplic keywords (interests)', `gender` varchar(32) NOT NULL DEFAULT '' COMMENT '', `birthday` varchar(32) NOT NULL DEFAULT '0001-01-01' COMMENT '', - `community` boolean NOT NULL DEFAULT '0' COMMENT '', + `community` boolean NOT NULL DEFAULT '0' COMMENT '1 if contact is forum account', `contact-type` tinyint NOT NULL DEFAULT -1 COMMENT '', - `hide` boolean NOT NULL DEFAULT '0' COMMENT '', - `nsfw` boolean NOT NULL DEFAULT '0' COMMENT '', - `network` char(4) NOT NULL DEFAULT '' COMMENT '', + `hide` boolean NOT NULL DEFAULT '0' COMMENT '1 = should be hidden from search', + `nsfw` boolean NOT NULL DEFAULT '0' COMMENT '1 = contact posts nsfw content', + `network` char(4) NOT NULL DEFAULT '' COMMENT 'social network protocol', `addr` varchar(255) NOT NULL DEFAULT '' COMMENT '', `notify` varchar(255) COMMENT '', `alias` varchar(255) NOT NULL DEFAULT '' COMMENT '', `generation` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', - `server_url` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `server_url` varchar(255) NOT NULL DEFAULT '' COMMENT 'baseurl of the contacts server', PRIMARY KEY(`id`), UNIQUE INDEX `nurl` (`nurl`(190)), INDEX `name` (`name`(64)), @@ -339,13 +339,13 @@ CREATE TABLE IF NOT EXISTS `gcontact` ( INDEX `addr` (`addr`(64)), INDEX `hide_network_updated` (`hide`,`network`,`updated`), INDEX `updated` (`updated`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='global contacts'; -- -- TABLE glink -- CREATE TABLE IF NOT EXISTS `glink` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `cid` int unsigned NOT NULL DEFAULT 0 COMMENT '', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `gcid` int unsigned NOT NULL DEFAULT 0 COMMENT '', @@ -354,45 +354,45 @@ CREATE TABLE IF NOT EXISTS `glink` ( PRIMARY KEY(`id`), UNIQUE INDEX `cid_uid_gcid_zcid` (`cid`,`uid`,`gcid`,`zcid`), INDEX `gcid` (`gcid`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='\'friends of friends\' linkages derived from poco'; -- -- TABLE group -- CREATE TABLE IF NOT EXISTS `group` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `visible` boolean NOT NULL DEFAULT '0' COMMENT '', - `deleted` boolean NOT NULL DEFAULT '0' COMMENT '', - `name` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', + `visible` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the member list is not private', + `deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the group has been deleted', + `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'human readable name of group', PRIMARY KEY(`id`), INDEX `uid` (`uid`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, group info'; -- -- TABLE group_member -- CREATE TABLE IF NOT EXISTS `group_member` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `gid` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `gid` int unsigned NOT NULL DEFAULT 0 COMMENT 'groups.id of the associated group', + `contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id of the member assigned to the associated group', PRIMARY KEY(`id`), INDEX `contactid` (`contact-id`), UNIQUE INDEX `gid_contactid` (`gid`,`contact-id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, member info'; -- -- TABLE gserver -- CREATE TABLE IF NOT EXISTS `gserver` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `url` varchar(255) NOT NULL DEFAULT '' COMMENT '', `nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '', `version` varchar(255) NOT NULL DEFAULT '' COMMENT '', `site_name` varchar(255) NOT NULL DEFAULT '' COMMENT '', `info` text COMMENT '', `register_policy` tinyint NOT NULL DEFAULT 0 COMMENT '', - `registered-users` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `registered-users` int unsigned NOT NULL DEFAULT 0 COMMENT 'Number of registered users', `poco` varchar(255) NOT NULL DEFAULT '' COMMENT '', `noscrape` varchar(255) NOT NULL DEFAULT '' COMMENT '', `network` char(4) NOT NULL DEFAULT '' COMMENT '', @@ -405,7 +405,7 @@ CREATE TABLE IF NOT EXISTS `gserver` ( `last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', PRIMARY KEY(`id`), UNIQUE INDEX `nurl` (`nurl`(190)) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Global servers'; -- -- TABLE gserver-tag @@ -415,26 +415,26 @@ CREATE TABLE IF NOT EXISTS `gserver-tag` ( `tag` varchar(100) NOT NULL DEFAULT '' COMMENT 'Tag that the server has subscribed', PRIMARY KEY(`gserver-id`,`tag`), INDEX `tag` (`tag`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Tags that the server has subscribed'; -- -- TABLE hook -- CREATE TABLE IF NOT EXISTS `hook` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `hook` varbinary(100) NOT NULL DEFAULT '' COMMENT '', - `file` varbinary(200) NOT NULL DEFAULT '' COMMENT '', - `function` varbinary(200) NOT NULL DEFAULT '' COMMENT '', - `priority` smallint unsigned NOT NULL DEFAULT 0 COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `hook` varbinary(100) NOT NULL DEFAULT '' COMMENT 'name of hook', + `file` varbinary(200) NOT NULL DEFAULT '' COMMENT 'relative filename of hook handler', + `function` varbinary(200) NOT NULL DEFAULT '' COMMENT 'function name of hook handler', + `priority` smallint unsigned NOT NULL DEFAULT 0 COMMENT 'not yet implemented - can be used to sort conflicts in hook handling by calling handlers in priority order', PRIMARY KEY(`id`), UNIQUE INDEX `hook_file_function` (`hook`,`file`,`function`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='addon hook registry'; -- -- TABLE intro -- CREATE TABLE IF NOT EXISTS `intro` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `fid` int unsigned NOT NULL DEFAULT 0 COMMENT '', `contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', @@ -446,76 +446,83 @@ CREATE TABLE IF NOT EXISTS `intro` ( `blocked` boolean NOT NULL DEFAULT '1' COMMENT '', `ignore` boolean NOT NULL DEFAULT '0' COMMENT '', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; -- -- TABLE item -- CREATE TABLE IF NOT EXISTS `item` ( `id` int unsigned NOT NULL auto_increment, - `guid` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `guid` varchar(255) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this item', `uri` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `type` varchar(20) NOT NULL DEFAULT '' COMMENT '', - `wall` boolean NOT NULL DEFAULT '0' COMMENT '', + `uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the item uri', + `uri-hash` varchar(80) NOT NULL DEFAULT '' COMMENT 'RIPEMD-128 hash from uri', + `parent` int unsigned NOT NULL DEFAULT 0 COMMENT 'item.id of the parent to this item if it is a reply of some form; otherwise this must be set to the id of this item', + `parent-uri` varchar(255) NOT NULL DEFAULT '' COMMENT 'uri of the parent to this item', + `parent-uri-id` int unsigned COMMENT 'Id of the item-uri table that contains the parent uri', + `thr-parent` varchar(255) NOT NULL DEFAULT '' COMMENT 'If the parent of this item is not the top-level item in the conversation, the uri of the immediate parent; otherwise set to parent-uri', + `thr-parent-id` int unsigned COMMENT 'Id of the item-uri table that contains the thread parent uri', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation timestamp.', + `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of last edit (default is created)', + `commented` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of last comment/reply to this item', + `received` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime', + `changed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date that something in the conversation changed, indicating clients should fetch the conversation again', `gravity` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', - `parent` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `parent-uri` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `network` char(4) NOT NULL DEFAULT '' COMMENT 'Network from where the item comes from', + `owner-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Link to the contact table with uid=0 of the owner of this item', + `author-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Link to the contact table with uid=0 of the author of this item', + `icid` int unsigned COMMENT 'Id of the item-content table entry that contains the whole item content', + `iaid` int unsigned COMMENT 'Id of the item-activity table entry that contains the activity data', `extid` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `thr-parent` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `commented` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `received` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `changed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `owner-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `owner-name` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `owner-link` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `owner-avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `author-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `author-name` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `author-link` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `author-avatar` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `title` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `content-warning` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `body` mediumtext COMMENT '', - `app` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `verb` varchar(100) NOT NULL DEFAULT '' COMMENT '', - `object-type` varchar(100) NOT NULL DEFAULT '' COMMENT '', - `object` text COMMENT '', - `target-type` varchar(100) NOT NULL DEFAULT '' COMMENT '', - `target` text COMMENT '', - `postopts` text COMMENT '', - `plink` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `resource-id` varchar(32) NOT NULL DEFAULT '' COMMENT '', - `event-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `tag` mediumtext COMMENT '', - `attach` mediumtext COMMENT '', - `inform` mediumtext COMMENT '', - `file` mediumtext COMMENT '', - `location` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `coord` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `allow_cid` mediumtext COMMENT '', - `allow_gid` mediumtext COMMENT '', - `deny_cid` mediumtext COMMENT '', - `deny_gid` mediumtext COMMENT '', - `private` boolean NOT NULL DEFAULT '0' COMMENT '', - `pubmail` boolean NOT NULL DEFAULT '0' COMMENT '', - `moderated` boolean NOT NULL DEFAULT '0' COMMENT '', - `visible` boolean NOT NULL DEFAULT '0' COMMENT '', - `spam` boolean NOT NULL DEFAULT '0' COMMENT '', - `starred` boolean NOT NULL DEFAULT '0' COMMENT '', - `bookmark` boolean NOT NULL DEFAULT '0' COMMENT '', - `unseen` boolean NOT NULL DEFAULT '1' COMMENT '', - `deleted` boolean NOT NULL DEFAULT '0' COMMENT '', - `origin` boolean NOT NULL DEFAULT '0' COMMENT '', - `forum_mode` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', - `mention` boolean NOT NULL DEFAULT '0' COMMENT '', - `network` char(4) NOT NULL DEFAULT '' COMMENT '', - `rendered-hash` varchar(32) NOT NULL DEFAULT '' COMMENT '', - `rendered-html` mediumtext COMMENT '', + `post-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Post type (personal note, bookmark, ...)', `global` boolean NOT NULL DEFAULT '0' COMMENT '', + `private` boolean NOT NULL DEFAULT '0' COMMENT 'distribution is restricted', + `visible` boolean NOT NULL DEFAULT '0' COMMENT '', + `moderated` boolean NOT NULL DEFAULT '0' COMMENT '', + `deleted` boolean NOT NULL DEFAULT '0' COMMENT 'item has been deleted', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner id which owns this copy of the item', + `contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id', + `wall` boolean NOT NULL DEFAULT '0' COMMENT 'This item was posted to the wall of uid', + `origin` boolean NOT NULL DEFAULT '0' COMMENT 'item originated at this site', + `pubmail` boolean NOT NULL DEFAULT '0' COMMENT '', + `starred` boolean NOT NULL DEFAULT '0' COMMENT 'item has been favourited', + `unseen` boolean NOT NULL DEFAULT '1' COMMENT 'item has not been seen', + `mention` boolean NOT NULL DEFAULT '0' COMMENT 'The owner of this item was mentioned in it', + `forum_mode` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', + `psid` int unsigned COMMENT 'ID of the permission set of this post', + `resource-id` varchar(32) NOT NULL DEFAULT '' COMMENT 'Used to link other tables to items, it identifies the linked resource (e.g. photo) and if set must also set resource_type', + `event-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Used to link to the event.id', + `attach` mediumtext COMMENT 'JSON structure representing attachments to this item', + `allow_cid` mediumtext COMMENT 'Deprecated', + `allow_gid` mediumtext COMMENT 'Deprecated', + `deny_cid` mediumtext COMMENT 'Deprecated', + `deny_gid` mediumtext COMMENT 'Deprecated', + `postopts` text COMMENT 'Deprecated', + `inform` mediumtext COMMENT 'Deprecated', + `type` varchar(20) COMMENT 'Deprecated', + `bookmark` boolean COMMENT 'Deprecated', + `file` mediumtext COMMENT 'Deprecated', + `location` varchar(255) COMMENT 'Deprecated', + `coord` varchar(255) COMMENT 'Deprecated', + `tag` mediumtext COMMENT 'Deprecated', + `plink` varchar(255) COMMENT 'Deprecated', + `title` varchar(255) COMMENT 'Deprecated', + `content-warning` varchar(255) COMMENT 'Deprecated', + `body` mediumtext COMMENT 'Deprecated', + `app` varchar(255) COMMENT 'Deprecated', + `verb` varchar(100) COMMENT 'Deprecated', + `object-type` varchar(100) COMMENT 'Deprecated', + `object` text COMMENT 'Deprecated', + `target-type` varchar(100) COMMENT 'Deprecated', + `target` text COMMENT 'Deprecated', + `author-name` varchar(255) COMMENT 'Deprecated', + `author-link` varchar(255) COMMENT 'Deprecated', + `author-avatar` varchar(255) COMMENT 'Deprecated', + `owner-name` varchar(255) COMMENT 'Deprecated', + `owner-link` varchar(255) COMMENT 'Deprecated', + `owner-avatar` varchar(255) COMMENT 'Deprecated', + `rendered-hash` varchar(32) COMMENT 'Deprecated', + `rendered-html` mediumtext COMMENT 'Deprecated', PRIMARY KEY(`id`), INDEX `guid` (`guid`(191)), INDEX `uri` (`uri`(191)), @@ -536,61 +543,125 @@ CREATE TABLE IF NOT EXISTS `item` ( INDEX `ownerid` (`owner-id`), INDEX `uid_uri` (`uid`,`uri`(190)), INDEX `resource-id` (`resource-id`), - INDEX `contactid_allowcid_allowpid_denycid_denygid` (`contact-id`,`allow_cid`(10),`allow_gid`(10),`deny_cid`(10),`deny_gid`(10)), - INDEX `uid_type_changed` (`uid`,`type`,`changed`), - INDEX `contactid_verb` (`contact-id`,`verb`), INDEX `deleted_changed` (`deleted`,`changed`), INDEX `uid_wall_changed` (`uid`,`wall`,`changed`), INDEX `uid_eventid` (`uid`,`event-id`), - INDEX `uid_authorlink` (`uid`,`author-link`(190)), - INDEX `uid_ownerlink` (`uid`,`owner-link`(190)) -) DEFAULT COLLATE utf8mb4_general_ci; + INDEX `icid` (`icid`), + INDEX `iaid` (`iaid`), + INDEX `psid_wall` (`psid`,`wall`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Structure for all posts'; + +-- +-- TABLE item-activity +-- +CREATE TABLE IF NOT EXISTS `item-activity` ( + `id` int unsigned NOT NULL auto_increment, + `uri` varchar(255) COMMENT '', + `uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the item uri', + `uri-hash` varchar(80) NOT NULL DEFAULT '' COMMENT 'RIPEMD-128 hash from uri', + `activity` smallint unsigned NOT NULL DEFAULT 0 COMMENT '', + PRIMARY KEY(`id`), + UNIQUE INDEX `uri-hash` (`uri-hash`), + INDEX `uri` (`uri`(191)) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Activities for items'; + +-- +-- TABLE item-content +-- +CREATE TABLE IF NOT EXISTS `item-content` ( + `id` int unsigned NOT NULL auto_increment, + `uri` varchar(255) COMMENT '', + `uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the item uri', + `uri-plink-hash` varchar(80) NOT NULL DEFAULT '' COMMENT 'RIPEMD-128 hash from uri', + `title` varchar(255) NOT NULL DEFAULT '' COMMENT 'item title', + `content-warning` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `body` mediumtext COMMENT 'item body content', + `location` varchar(255) NOT NULL DEFAULT '' COMMENT 'text location where this item originated', + `coord` varchar(255) NOT NULL DEFAULT '' COMMENT 'longitude/latitude pair representing location where this item originated', + `language` text COMMENT 'Language information about this post', + `app` varchar(255) NOT NULL DEFAULT '' COMMENT 'application which generated this item', + `rendered-hash` varchar(32) NOT NULL DEFAULT '' COMMENT '', + `rendered-html` mediumtext COMMENT 'item.body converted to html', + `object-type` varchar(100) NOT NULL DEFAULT '' COMMENT 'ActivityStreams object type', + `object` text COMMENT 'JSON encoded object structure unless it is an implied object (normal post)', + `target-type` varchar(100) NOT NULL DEFAULT '' COMMENT 'ActivityStreams target type if applicable (URI)', + `target` text COMMENT 'JSON encoded target structure if used', + `plink` varchar(255) NOT NULL DEFAULT '' COMMENT 'permalink or URL to a displayable copy of the message at its source', + `verb` varchar(100) NOT NULL DEFAULT '' COMMENT 'ActivityStreams verb', + PRIMARY KEY(`id`), + UNIQUE INDEX `uri-plink-hash` (`uri-plink-hash`), + INDEX `uri` (`uri`(191)) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Content for all posts'; + +-- +-- TABLE item-delivery-data +-- +CREATE TABLE IF NOT EXISTS `item-delivery-data` ( + `iid` int unsigned NOT NULL COMMENT 'Item id', + `postopts` text COMMENT 'External post connectors add their network name to this comma-separated string to identify that they should be delivered to these networks during delivery', + `inform` mediumtext COMMENT 'Additional receivers of the linked item', + PRIMARY KEY(`iid`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Delivery data for items'; + +-- +-- TABLE item-uri +-- +CREATE TABLE IF NOT EXISTS `item-uri` ( + `id` int unsigned NOT NULL auto_increment, + `uri` varbinary(255) NOT NULL COMMENT 'URI of an item', + `guid` varbinary(255) COMMENT 'A unique identifier for an item', + PRIMARY KEY(`id`), + UNIQUE INDEX `uri` (`uri`), + INDEX `guid` (`guid`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='URI and GUID for items'; -- -- TABLE locks -- CREATE TABLE IF NOT EXISTS `locks` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `name` varchar(128) NOT NULL DEFAULT '' COMMENT '', `locked` boolean NOT NULL DEFAULT '0' COMMENT '', - `pid` int unsigned NOT NULL DEFAULT 0 COMMENT '', - PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; + `pid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Process ID', + `expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of cache expiration', + PRIMARY KEY(`id`), + INDEX `name_expires` (`name`,`expires`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; -- -- TABLE mail -- CREATE TABLE IF NOT EXISTS `mail` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `guid` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `from-name` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `from-photo` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `from-url` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `contact-id` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `convid` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', + `guid` varchar(255) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this private message', + `from-name` varchar(255) NOT NULL DEFAULT '' COMMENT 'name of the sender', + `from-photo` varchar(255) NOT NULL DEFAULT '' COMMENT 'contact photo link of the sender', + `from-url` varchar(255) NOT NULL DEFAULT '' COMMENT 'profile linke of the sender', + `contact-id` varchar(255) NOT NULL DEFAULT '' COMMENT 'contact.id', + `convid` int unsigned NOT NULL DEFAULT 0 COMMENT 'conv.id', `title` varchar(255) NOT NULL DEFAULT '' COMMENT '', `body` mediumtext COMMENT '', - `seen` boolean NOT NULL DEFAULT '0' COMMENT '', + `seen` boolean NOT NULL DEFAULT '0' COMMENT 'if message visited it is 1', `reply` boolean NOT NULL DEFAULT '0' COMMENT '', `replied` boolean NOT NULL DEFAULT '0' COMMENT '', - `unknown` boolean NOT NULL DEFAULT '0' COMMENT '', + `unknown` boolean NOT NULL DEFAULT '0' COMMENT 'if sender not in the contact table this is 1', `uri` varchar(255) NOT NULL DEFAULT '' COMMENT '', `parent-uri` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation time of the private message', PRIMARY KEY(`id`), INDEX `uid_seen` (`uid`,`seen`), INDEX `convid` (`convid`), INDEX `uri` (`uri`(64)), INDEX `parent-uri` (`parent-uri`(64)), INDEX `contactid` (`contact-id`(32)) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='private messages'; -- -- TABLE mailacct -- CREATE TABLE IF NOT EXISTS `mailacct` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `server` varchar(255) NOT NULL DEFAULT '' COMMENT '', `port` smallint unsigned NOT NULL DEFAULT 0 COMMENT '', @@ -604,24 +675,24 @@ CREATE TABLE IF NOT EXISTS `mailacct` ( `pubmail` boolean NOT NULL DEFAULT '0' COMMENT '', `last_check` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Mail account data for fetching mails'; -- -- TABLE manage -- CREATE TABLE IF NOT EXISTS `manage` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `mid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', PRIMARY KEY(`id`), UNIQUE INDEX `uid_mid` (`uid`,`mid`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='table of accounts that can manage each other'; -- -- TABLE notify -- CREATE TABLE IF NOT EXISTS `notify` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `hash` varchar(64) NOT NULL DEFAULT '' COMMENT '', `type` smallint unsigned NOT NULL DEFAULT 0 COMMENT '', `name` varchar(255) NOT NULL DEFAULT '' COMMENT '', @@ -629,58 +700,71 @@ CREATE TABLE IF NOT EXISTS `notify` ( `photo` varchar(255) NOT NULL DEFAULT '' COMMENT '', `date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `msg` mediumtext COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', `link` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `iid` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `iid` int unsigned NOT NULL DEFAULT 0 COMMENT 'item.id', `parent` int unsigned NOT NULL DEFAULT 0 COMMENT '', `seen` boolean NOT NULL DEFAULT '0' COMMENT '', `verb` varchar(100) NOT NULL DEFAULT '' COMMENT '', `otype` varchar(10) NOT NULL DEFAULT '' COMMENT '', - `name_cache` tinytext COMMENT '', - `msg_cache` mediumtext COMMENT '', + `name_cache` tinytext COMMENT 'Cached bbcode parsing of name', + `msg_cache` mediumtext COMMENT 'Cached bbcode parsing of msg', PRIMARY KEY(`id`), INDEX `hash_uid` (`hash`,`uid`), INDEX `seen_uid_date` (`seen`,`uid`,`date`), INDEX `uid_date` (`uid`,`date`), INDEX `uid_type_link` (`uid`,`type`,`link`(190)) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='notifications'; -- -- TABLE notify-threads -- CREATE TABLE IF NOT EXISTS `notify-threads` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `notify-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', `master-parent-item` int unsigned NOT NULL DEFAULT 0 COMMENT '', `parent-item` int unsigned NOT NULL DEFAULT 0 COMMENT '', `receiver-uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; -- -- TABLE oembed -- CREATE TABLE IF NOT EXISTS `oembed` ( - `url` varbinary(255) NOT NULL COMMENT '', - `maxwidth` mediumint unsigned NOT NULL COMMENT '', - `content` mediumtext COMMENT '', - `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `url` varbinary(255) NOT NULL COMMENT 'page url', + `maxwidth` mediumint unsigned NOT NULL COMMENT 'Maximum width passed to Oembed', + `content` mediumtext COMMENT 'OEmbed data of the page', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of creation', PRIMARY KEY(`url`,`maxwidth`), INDEX `created` (`created`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='cache for OEmbed queries'; + +-- +-- TABLE openwebauth-token +-- +CREATE TABLE IF NOT EXISTS `openwebauth-token` ( + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', + `type` varchar(32) NOT NULL DEFAULT '' COMMENT 'Verify type', + `token` varchar(255) NOT NULL DEFAULT '' COMMENT 'A generated token', + `meta` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of creation', + PRIMARY KEY(`id`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Store OpenWebAuth token to verify contacts'; -- -- TABLE parsed_url -- CREATE TABLE IF NOT EXISTS `parsed_url` ( - `url` varbinary(255) NOT NULL COMMENT '', - `guessing` boolean NOT NULL DEFAULT '0' COMMENT '', - `oembed` boolean NOT NULL DEFAULT '0' COMMENT '', - `content` mediumtext COMMENT '', - `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `url` varbinary(255) NOT NULL COMMENT 'page url', + `guessing` boolean NOT NULL DEFAULT '0' COMMENT 'is the \'guessing\' mode active?', + `oembed` boolean NOT NULL DEFAULT '0' COMMENT 'is the data the result of oembed?', + `content` mediumtext COMMENT 'page data', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of creation', PRIMARY KEY(`url`,`guessing`,`oembed`), INDEX `created` (`created`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='cache for \'parse_url\' queries'; -- -- TABLE participation @@ -691,7 +775,7 @@ CREATE TABLE IF NOT EXISTS `participation` ( `cid` int unsigned NOT NULL COMMENT '', `fid` int unsigned NOT NULL COMMENT '', PRIMARY KEY(`iid`,`server`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Storage for participation messages from Diaspora'; -- -- TABLE pconfig @@ -704,22 +788,36 @@ CREATE TABLE IF NOT EXISTS `pconfig` ( `v` mediumtext COMMENT '', PRIMARY KEY(`id`), UNIQUE INDEX `uid_cat_k` (`uid`,`cat`,`k`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='personal (per user) configuration storage'; + +-- +-- TABLE permissionset +-- +CREATE TABLE IF NOT EXISTS `permissionset` ( + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner id of this permission set', + `allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>\'', + `allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups', + `deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id', + `deny_gid` mediumtext COMMENT 'Access Control - list of denied groups', + PRIMARY KEY(`id`), + INDEX `uid_allow_cid_allow_gid_deny_cid_deny_gid` (`allow_cid`(50),`allow_gid`(30),`deny_cid`(50),`deny_gid`(30)) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; -- -- TABLE photo -- CREATE TABLE IF NOT EXISTS `photo` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `guid` char(16) NOT NULL DEFAULT '' COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', + `contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id', + `guid` char(16) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this photo', `resource-id` char(32) NOT NULL DEFAULT '' COMMENT '', - `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation date', + `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'last edited date', `title` varchar(255) NOT NULL DEFAULT '' COMMENT '', `desc` text COMMENT '', - `album` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `album` varchar(255) NOT NULL DEFAULT '' COMMENT 'The name of the album to which the photo belongs', `filename` varchar(255) NOT NULL DEFAULT '' COMMENT '', `type` varchar(30) NOT NULL DEFAULT 'image/jpeg', `height` smallint unsigned NOT NULL DEFAULT 0 COMMENT '', @@ -728,10 +826,10 @@ CREATE TABLE IF NOT EXISTS `photo` ( `data` mediumblob NOT NULL COMMENT '', `scale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `profile` boolean NOT NULL DEFAULT '0' COMMENT '', - `allow_cid` mediumtext COMMENT '', - `allow_gid` mediumtext COMMENT '', - `deny_cid` mediumtext COMMENT '', - `deny_gid` mediumtext COMMENT '', + `allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>\'', + `allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups', + `deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id', + `deny_gid` mediumtext COMMENT 'Access Control - list of denied groups', PRIMARY KEY(`id`), INDEX `contactid` (`contact-id`), INDEX `uid_contactid` (`uid`,`contact-id`), @@ -739,7 +837,7 @@ CREATE TABLE IF NOT EXISTS `photo` ( INDEX `uid_album_scale_created` (`uid`,`album`(32),`scale`,`created`), INDEX `uid_album_resource-id_created` (`uid`,`album`(32),`resource-id`,`created`), INDEX `resource-id` (`resource-id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='photo storage'; -- -- TABLE poll @@ -759,18 +857,18 @@ CREATE TABLE IF NOT EXISTS `poll` ( `q9` text COMMENT '', PRIMARY KEY(`id`), INDEX `uid` (`uid`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Currently unused table for storing poll results'; -- -- TABLE poll_result -- CREATE TABLE IF NOT EXISTS `poll_result` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `poll_id` int unsigned NOT NULL DEFAULT 0, `choice` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', PRIMARY KEY(`id`), INDEX `poll_id` (`poll_id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='data for polls - currently unused'; -- -- TABLE process @@ -781,20 +879,20 @@ CREATE TABLE IF NOT EXISTS `process` ( `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', PRIMARY KEY(`pid`), INDEX `command` (`command`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Currently running system processes'; -- -- TABLE profile -- CREATE TABLE IF NOT EXISTS `profile` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `profile-name` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `is-default` boolean NOT NULL DEFAULT '0' COMMENT '', - `hide-friends` boolean NOT NULL DEFAULT '0' COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id', + `profile-name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name of the profile', + `is-default` boolean NOT NULL DEFAULT '0' COMMENT 'Mark this profile as default profile', + `hide-friends` boolean NOT NULL DEFAULT '0' COMMENT 'Hide friend list from viewers of this profile', `name` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `pdesc` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `dob` varchar(32) NOT NULL DEFAULT '0000-00-00' COMMENT '', + `pdesc` varchar(255) NOT NULL DEFAULT '' COMMENT 'Title or description', + `dob` varchar(32) NOT NULL DEFAULT '0000-00-00' COMMENT 'Day of birth', `address` varchar(255) NOT NULL DEFAULT '' COMMENT '', `locality` varchar(255) NOT NULL DEFAULT '' COMMENT '', `region` varchar(255) NOT NULL DEFAULT '' COMMENT '', @@ -827,30 +925,30 @@ CREATE TABLE IF NOT EXISTS `profile` ( `xmpp` varchar(255) NOT NULL DEFAULT '' COMMENT '', `photo` varchar(255) NOT NULL DEFAULT '' COMMENT '', `thumb` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `publish` boolean NOT NULL DEFAULT '0' COMMENT '', - `net-publish` boolean NOT NULL DEFAULT '0' COMMENT '', + `publish` boolean NOT NULL DEFAULT '0' COMMENT 'publish default profile in local directory', + `net-publish` boolean NOT NULL DEFAULT '0' COMMENT 'publish profile in global directory', PRIMARY KEY(`id`), INDEX `uid_is-default` (`uid`,`is-default`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='user profiles data'; -- -- TABLE profile_check -- CREATE TABLE IF NOT EXISTS `profile_check` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `cid` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id', `dfrn_id` varchar(255) NOT NULL DEFAULT '' COMMENT '', `sec` varchar(255) NOT NULL DEFAULT '' COMMENT '', `expire` int unsigned NOT NULL DEFAULT 0 COMMENT '', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='DFRN remote auth use'; -- -- TABLE push_subscriber -- CREATE TABLE IF NOT EXISTS `push_subscriber` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `callback_url` varchar(255) NOT NULL DEFAULT '' COMMENT '', `topic` varchar(255) NOT NULL DEFAULT '' COMMENT '', @@ -862,13 +960,13 @@ CREATE TABLE IF NOT EXISTS `push_subscriber` ( `secret` varchar(255) NOT NULL DEFAULT '' COMMENT '', PRIMARY KEY(`id`), INDEX `next_try` (`next_try`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Used for OStatus: Contains feed subscribers'; -- -- TABLE queue -- CREATE TABLE IF NOT EXISTS `queue` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Message receiver', `network` char(4) NOT NULL DEFAULT '' COMMENT 'Receiver\'s network', `guid` varchar(255) NOT NULL DEFAULT '' COMMENT 'Unique GUID of the message', @@ -881,13 +979,13 @@ CREATE TABLE IF NOT EXISTS `queue` ( PRIMARY KEY(`id`), INDEX `last` (`last`), INDEX `next` (`next`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Queue for messages that couldn\'t be delivered'; -- -- TABLE register -- CREATE TABLE IF NOT EXISTS `register` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `hash` varchar(255) NOT NULL DEFAULT '' COMMENT '', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', @@ -895,44 +993,44 @@ CREATE TABLE IF NOT EXISTS `register` ( `language` varchar(16) NOT NULL DEFAULT '' COMMENT '', `note` text COMMENT '', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='registrations requiring admin approval'; -- -- TABLE search -- CREATE TABLE IF NOT EXISTS `search` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `term` varchar(255) NOT NULL DEFAULT '' COMMENT '', PRIMARY KEY(`id`), INDEX `uid` (`uid`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; -- -- TABLE session -- CREATE TABLE IF NOT EXISTS `session` ( - `id` bigint unsigned NOT NULL auto_increment COMMENT '', + `id` bigint unsigned NOT NULL auto_increment COMMENT 'sequential ID', `sid` varbinary(255) NOT NULL DEFAULT '' COMMENT '', `data` text COMMENT '', `expire` int unsigned NOT NULL DEFAULT 0 COMMENT '', PRIMARY KEY(`id`), INDEX `sid` (`sid`(64)), INDEX `expire` (`expire`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='web session storage'; -- -- TABLE sign -- CREATE TABLE IF NOT EXISTS `sign` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', - `iid` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', + `iid` int unsigned NOT NULL DEFAULT 0 COMMENT 'item.id', `signed_text` mediumtext COMMENT '', `signature` text COMMENT '', `signer` varchar(255) NOT NULL DEFAULT '' COMMENT '', PRIMARY KEY(`id`), UNIQUE INDEX `iid` (`iid`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Diaspora signatures'; -- -- TABLE term @@ -948,24 +1046,23 @@ CREATE TABLE IF NOT EXISTS `term` ( `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `received` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `global` boolean NOT NULL DEFAULT '0' COMMENT '', - `aid` int unsigned NOT NULL DEFAULT 0 COMMENT '', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', PRIMARY KEY(`tid`), INDEX `oid_otype_type_term` (`oid`,`otype`,`type`,`term`(32)), INDEX `uid_otype_type_term_global_created` (`uid`,`otype`,`type`,`term`(32),`global`,`created`), INDEX `uid_otype_type_url` (`uid`,`otype`,`type`,`url`(64)), INDEX `guid` (`guid`(64)) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='item taxonomy (categories, tags, etc.) table'; -- -- TABLE thread -- CREATE TABLE IF NOT EXISTS `thread` ( - `iid` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `iid` int unsigned NOT NULL DEFAULT 0 COMMENT 'sequential ID', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `owner-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `author-id` int unsigned NOT NULL DEFAULT 0 COMMENT '', + `owner-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Item owner', + `author-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'Item author', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `commented` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', @@ -976,16 +1073,16 @@ CREATE TABLE IF NOT EXISTS `thread` ( `pubmail` boolean NOT NULL DEFAULT '0' COMMENT '', `moderated` boolean NOT NULL DEFAULT '0' COMMENT '', `visible` boolean NOT NULL DEFAULT '0' COMMENT '', - `spam` boolean NOT NULL DEFAULT '0' COMMENT '', `starred` boolean NOT NULL DEFAULT '0' COMMENT '', `ignored` boolean NOT NULL DEFAULT '0' COMMENT '', - `bookmark` boolean NOT NULL DEFAULT '0' COMMENT '', + `post-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'Post type (personal note, bookmark, ...)', `unseen` boolean NOT NULL DEFAULT '1' COMMENT '', `deleted` boolean NOT NULL DEFAULT '0' COMMENT '', `origin` boolean NOT NULL DEFAULT '0' COMMENT '', `forum_mode` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `mention` boolean NOT NULL DEFAULT '0' COMMENT '', `network` char(4) NOT NULL DEFAULT '' COMMENT '', + `bookmark` boolean COMMENT '', PRIMARY KEY(`iid`), INDEX `uid_network_commented` (`uid`,`network`,`commented`), INDEX `uid_network_created` (`uid`,`network`,`created`), @@ -998,7 +1095,7 @@ CREATE TABLE IF NOT EXISTS `thread` ( INDEX `uid_commented` (`uid`,`commented`), INDEX `uid_wall_created` (`uid`,`wall`,`created`), INDEX `private_wall_origin_commented` (`private`,`wall`,`origin`,`commented`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Thread related data'; -- -- TABLE tokens @@ -1011,70 +1108,82 @@ CREATE TABLE IF NOT EXISTS `tokens` ( `scope` varchar(200) NOT NULL DEFAULT '' COMMENT '', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', PRIMARY KEY(`id`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage'; -- -- TABLE user -- CREATE TABLE IF NOT EXISTS `user` ( - `uid` mediumint unsigned NOT NULL auto_increment COMMENT '', + `uid` mediumint unsigned NOT NULL auto_increment COMMENT 'sequential ID', `parent-uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'The parent user that has full control about this user', - `guid` varchar(64) NOT NULL DEFAULT '' COMMENT '', - `username` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `password` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `guid` varchar(64) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this user', + `username` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this user is known by', + `password` varchar(255) NOT NULL DEFAULT '' COMMENT 'encrypted password', `legacy_password` boolean NOT NULL DEFAULT '0' COMMENT 'Is the password hash double-hashed?', - `nickname` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `email` varchar(255) NOT NULL DEFAULT '' COMMENT '', + `nickname` varchar(255) NOT NULL DEFAULT '' COMMENT 'nick- and user name', + `email` varchar(255) NOT NULL DEFAULT '' COMMENT 'the users email address', `openid` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `timezone` varchar(128) NOT NULL DEFAULT '' COMMENT '', - `language` varchar(32) NOT NULL DEFAULT 'en' COMMENT '', - `register_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `login_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `default-location` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `allow_location` boolean NOT NULL DEFAULT '0' COMMENT '', - `theme` varchar(255) NOT NULL DEFAULT '' COMMENT '', - `pubkey` text COMMENT '', - `prvkey` text COMMENT '', + `timezone` varchar(128) NOT NULL DEFAULT '' COMMENT 'PHP-legal timezone', + `language` varchar(32) NOT NULL DEFAULT 'en' COMMENT 'default language', + `register_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of registration', + `login_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last login', + `default-location` varchar(255) NOT NULL DEFAULT '' COMMENT 'Default for item.location', + `allow_location` boolean NOT NULL DEFAULT '0' COMMENT '1 allows to display the location', + `theme` varchar(255) NOT NULL DEFAULT '' COMMENT 'user theme preference', + `pubkey` text COMMENT 'RSA public key 4096 bit', + `prvkey` text COMMENT 'RSA private key 4096 bit', `spubkey` text COMMENT '', `sprvkey` text COMMENT '', - `verified` boolean NOT NULL DEFAULT '0' COMMENT '', - `blocked` boolean NOT NULL DEFAULT '0' COMMENT '', - `blockwall` boolean NOT NULL DEFAULT '0' COMMENT '', - `hidewall` boolean NOT NULL DEFAULT '0' COMMENT '', - `blocktags` boolean NOT NULL DEFAULT '0' COMMENT '', - `unkmail` boolean NOT NULL DEFAULT '0' COMMENT '', + `verified` boolean NOT NULL DEFAULT '0' COMMENT 'user is verified through email', + `blocked` boolean NOT NULL DEFAULT '0' COMMENT '1 for user is blocked', + `blockwall` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to post to the profile page of the user', + `hidewall` boolean NOT NULL DEFAULT '0' COMMENT 'Hide profile details from unkown viewers', + `blocktags` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to tag the post of this user', + `unkmail` boolean NOT NULL DEFAULT '0' COMMENT 'Permit unknown people to send private mails to this user', `cntunkmail` int unsigned NOT NULL DEFAULT 10 COMMENT '', - `notify-flags` smallint unsigned NOT NULL DEFAULT 65535 COMMENT '', - `page-flags` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', + `notify-flags` smallint unsigned NOT NULL DEFAULT 65535 COMMENT 'email notification options', + `page-flags` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'page/profile type', `account-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `prvnets` boolean NOT NULL DEFAULT '0' COMMENT '', `pwdreset` varchar(255) COMMENT 'Password reset request token', `pwdreset_time` datetime COMMENT 'Timestamp of the last password reset request', `maxreq` int unsigned NOT NULL DEFAULT 10 COMMENT '', `expire` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `account_removed` boolean NOT NULL DEFAULT '0' COMMENT '', + `account_removed` boolean NOT NULL DEFAULT '0' COMMENT 'if 1 the account is removed', `account_expired` boolean NOT NULL DEFAULT '0' COMMENT '', - `account_expires_on` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', - `expire_notification_sent` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', + `account_expires_on` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp when account expires and will be deleted', + `expire_notification_sent` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last warning of account expiration', `def_gid` int unsigned NOT NULL DEFAULT 0 COMMENT '', - `allow_cid` mediumtext COMMENT '', - `allow_gid` mediumtext COMMENT '', - `deny_cid` mediumtext COMMENT '', - `deny_gid` mediumtext COMMENT '', + `allow_cid` mediumtext COMMENT 'default permission for this user', + `allow_gid` mediumtext COMMENT 'default permission for this user', + `deny_cid` mediumtext COMMENT 'default permission for this user', + `deny_gid` mediumtext COMMENT 'default permission for this user', `openidserver` text COMMENT '', PRIMARY KEY(`uid`), INDEX `nickname` (`nickname`(32)) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='The local users'; -- -- TABLE userd -- CREATE TABLE IF NOT EXISTS `userd` ( - `id` int unsigned NOT NULL auto_increment COMMENT '', + `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `username` varchar(255) NOT NULL COMMENT '', PRIMARY KEY(`id`), INDEX `username` (`username`(32)) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Deleted usernames'; + +-- +-- TABLE user-contact +-- +CREATE TABLE IF NOT EXISTS `user-contact` ( + `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Contact id of the linked public contact', + `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', + `blocked` boolean COMMENT 'Contact is completely blocked for this user', + `ignored` boolean COMMENT 'Posts from this contact are ignored', + `collapsed` boolean COMMENT 'Posts from this contact are collapsed', + PRIMARY KEY(`uid`,`cid`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific public contact data'; -- -- TABLE user-item @@ -1082,9 +1191,19 @@ CREATE TABLE IF NOT EXISTS `userd` ( CREATE TABLE IF NOT EXISTS `user-item` ( `iid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Item id', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', - `hidden` boolean NOT NULL DEFAULT '0' COMMENT 'Hidden marker', + `hidden` boolean NOT NULL DEFAULT '0' COMMENT 'Marker to hide an item from the user', + `ignored` boolean COMMENT 'Ignore this thread if set', PRIMARY KEY(`uid`,`iid`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific item data'; + +-- +-- TABLE worker-ipc +-- +CREATE TABLE IF NOT EXISTS `worker-ipc` ( + `key` int NOT NULL COMMENT '', + `jobs` boolean COMMENT 'Flag for outstanding jobs', + PRIMARY KEY(`key`) +) ENGINE=MEMORY DEFAULT COLLATE utf8mb4_general_ci COMMENT='Inter process communication between the frontend and the worker'; -- -- TABLE workerqueue @@ -1096,12 +1215,12 @@ CREATE TABLE IF NOT EXISTS `workerqueue` ( `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Creation date', `pid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Process id of the worker', `executed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Execution date', - `done` boolean NOT NULL DEFAULT '0' COMMENT 'Marked when the task was done, will be deleted later', + `done` boolean NOT NULL DEFAULT '0' COMMENT 'Marked 1 when the task was done - will be deleted later', PRIMARY KEY(`id`), INDEX `pid` (`pid`), INDEX `parameter` (`parameter`(64)), INDEX `priority_created` (`priority`,`created`), INDEX `done_executed` (`done`,`executed`) -) DEFAULT COLLATE utf8mb4_general_ci; +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Background tasks queue entries'; diff --git a/doc/Accesskeys.md b/doc/Accesskeys.md index 7434fc049..8e889fa99 100644 --- a/doc/Accesskeys.md +++ b/doc/Accesskeys.md @@ -79,6 +79,7 @@ General ../settings --------- * o - Account +* p - Profiles * t - Additional features * w - Social Networks * l - Addons diff --git a/doc/Addons.md b/doc/Addons.md index f7d93aac7..2465db730 100644 --- a/doc/Addons.md +++ b/doc/Addons.md @@ -25,22 +25,23 @@ Addons should contain a comment block with the four following parameters: * Author: John Q. Public */ +Please also add a README or README.md file to the addon directory. +It will be displayed in the admin panel and should include some further information in addition to the header information. + +## PHP addon hooks + Register your addon hooks during installation. Addon::registerHook($hookname, $file, $function); -$hookname is a string and corresponds to a known Friendica hook. +$hookname is a string and corresponds to a known Friendica PHP hook. $file is a pathname relative to the top-level Friendica directory. -This *should* be 'addon/addon_name/addon_name.php' in most cases. +This *should* be 'addon/*addon_name*/*addon_name*.php' in most cases. $function is a string and is the name of the function which will be executed when the hook is called. -Please also add a README or README.md file to the addon directory. -It will be displayed in the admin panel and should include some further information in addition to the header information. - -Arguments ---- +### Arguments Your hook callback functions will be called with at least one and possibly two arguments function myhook_function(App $a, &$b) { @@ -48,10 +49,10 @@ Your hook callback functions will be called with at least one and possibly two a } -If you wish to make changes to the calling data, you must declare them as reference variables (with '&') during function declaration. +If you wish to make changes to the calling data, you must declare them as reference variables (with `&`) during function declaration. #### $a -$a is the Friendica 'App' class. +$a is the Friendica `App` class. It contains a wealth of information about the current state of Friendica: * which module has been called, @@ -59,15 +60,64 @@ It contains a wealth of information about the current state of Friendica: * the page contents at the point the hook was invoked, * profile and user information, etc. -It is recommeded you call this '$a' to match its usage elsewhere. +It is recommeded you call this `$a` to match its usage elsewhere. #### $b $b can be called anything you like. This is information specific to the hook currently being processed, and generally contains information that is being immediately processed or acted on that you can use, display, or alter. -Remember to declare it with '&' if you wish to alter it. +Remember to declare it with `&` if you wish to alter it. -Modules ---- +## JavaScript addon hooks + +### PHP part + +Make sure your JavaScript addon file (addon/*addon_name*/*addon_name*.js) is listed in the document response. + +In your addon install function, add: + +```php +Addon::registerHook('template_vars', __FILE__, '_template_vars'); +``` + +In your addon uninstall function, add: + +```php +Addon::unregisterHook('template_vars', __FILE__, '_template_vars'); +``` + +Then, add your addon name to the *addon_hooks* template variable array: + +```php +function _template_vars($a, &$arr) +{ + if (!array_key_exists('addon_hooks', $arr['vars'])) + { + $arr['vars']['addon_hooks'] = array(); + } + $arr['vars']['addon_hooks'][] = ""; +} +``` + +### JavaScript part + +Register your addon hooks in file `addon/*addon_name*/*addon_name*.js`. + +```js +Addon_registerHook(type, hookfnstr); +``` + +*type* is the name of the hook and corresponds to a known Friendica JavaScript hook. +*hookfnstr* is the name of your JavaScript function to execute. + +No arguments are provided to your JavaScript callback function. Example: + +```javascript +function myhook_function() { + +} +``` + +## Modules Addons may also act as "modules" and intercept all page requests for a given URL path. In order for a addon to act as a module it needs to define a function "addon_name_module()" which takes no arguments and needs not do anything. @@ -77,15 +127,16 @@ These are parsed into an array $a->argv, with a corresponding $a->argc indicatin So http://my.web.site/addon/arg1/arg2 would look for a module named "addon" and pass its module functions the $a App structure (which is available to many components). This will include: - $a->argc = 3 - $a->argv = array(0 => 'addon', 1 => 'arg1', 2 => 'arg2'); +```php +$a->argc = 3 +$a->argv = array(0 => 'addon', 1 => 'arg1', 2 => 'arg2'); +``` Your module functions will often contain the function addon_name_content(App $a), which defines and returns the page body content. They may also contain addon_name_post(App $a) which is called before the _content function and typically handles the results of POST forms. You may also have addon_name_init(App $a) which is called very early on and often does module initialisation. -Templates ---- +## Templates If your addon needs some template, you can use the Friendica template system. Friendica uses [smarty3](http://www.smarty.net/) as a template engine. @@ -94,230 +145,256 @@ Put your tpl files in the *templates/* subfolder of your addon. In your code, like in the function addon_name_content(), load the template file and execute it passing needed values: - # load template file. first argument is the template name, - # second is the addon path relative to friendica top folder - $tpl = get_markup_template('mytemplate.tpl', 'addon/addon_name/'); +```php +# load template file. first argument is the template name, +# second is the addon path relative to friendica top folder +$tpl = get_markup_template('mytemplate.tpl', 'addon/addon_name/'); - # apply template. first argument is the loaded template, - # second an array of 'name'=>'values' to pass to template - $output = replace_macros($tpl,array( - 'title' => 'My beautiful addon', - )); +# apply template. first argument is the loaded template, +# second an array of 'name' => 'values' to pass to template +$output = replace_macros($tpl, array( + 'title' => 'My beautiful addon', +)); +``` See also the wiki page [Quick Template Guide](https://github.com/friendica/friendica/wiki/Quick-Template-Guide). -Current hooks -------------- +## Current PHP hooks -### 'authenticate' -'authenticate' is called when a user attempts to login. -$b is an array containing: +### authenticate +Called when a user attempts to login. +`$b` is an array containing: - 'username' => the supplied username - 'password' => the supplied password - 'authenticated' => set this to non-zero to authenticate the user. - 'user_record' => successful authentication must also return a valid user record from the database +- **username**: the supplied username +- **password**: the supplied password +- **authenticated**: set this to non-zero to authenticate the user. +- **user_record**: successful authentication must also return a valid user record from the database -### 'logged_in' -'logged_in' is called after a user has successfully logged in. -$b contains the $a->user array. +### logged_in +Called after a user has successfully logged in. +`$b` contains the `$a->user` array. -### 'display_item' -'display_item' is called when formatting a post for display. +### display_item +Called when formatting a post for display. $b is an array: - 'item' => The item (array) details pulled from the database - 'output' => the (string) HTML representation of this item prior to adding it to the page +- **item**: The item (array) details pulled from the database +- **output**: the (string) HTML representation of this item prior to adding it to the page -### 'post_local' -* called when a status post or comment is entered on the local system -* $b is the item array of the information to be stored in the database -* Please note: body contents are bbcode - not HTML +### post_local +Called when a status post or comment is entered on the local system. +`$b` is the item array of the information to be stored in the database. +Please note: body contents are bbcode - not HTML. -### 'post_local_end' -* called when a local status post or comment has been stored on the local system -* $b is the item array of the information which has just been stored in the database -* Please note: body contents are bbcode - not HTML +### post_local_end +Called when a local status post or comment has been stored on the local system. +`$b` is the item array of the information which has just been stored in the database. +Please note: body contents are bbcode - not HTML -### 'post_remote' -* called when receiving a post from another source. This may also be used to post local activity or system generated messages. -* $b is the item array of information to be stored in the database and the item body is bbcode. +### post_remote +Called when receiving a post from another source. This may also be used to post local activity or system generated messages. +`$b` is the item array of information to be stored in the database and the item body is bbcode. -### 'settings_form' -* called when generating the HTML for the user Settings page -* $b is the (string) HTML of the settings page before the final '' tag. +### settings_form +Called when generating the HTML for the user Settings page. +`$b` is the HTML string of the settings page before the final `` tag. -### 'settings_post' -* called when the Settings pages are submitted -* $b is the $_POST array +### settings_post +Called when the Settings pages are submitted. +`$b` is the $_POST array. -### 'addon_settings' -* called when generating the HTML for the addon settings page -* $b is the (string) HTML of the addon settings page before the final '' tag. +### addon_settings +Called when generating the HTML for the addon settings page. +`$b` is the (string) HTML of the addon settings page before the final `` tag. -### 'addon_settings_post' -* called when the Addon Settings pages are submitted -* $b is the $_POST array +### addon_settings_post +Called when the Addon Settings pages are submitted. +`$b` is the $_POST array. -### 'profile_post' -* called when posting a profile page -* $b is the $_POST array +### profile_post +Called when posting a profile page. +`$b` is the $_POST array. -### 'profile_edit' -'profile_edit' is called prior to output of profile edit page. -$b is an array containing: +### profile_edit +Called prior to output of profile edit page. +`$b` is an array containing: - 'profile' => profile (array) record from the database - 'entry' => the (string) HTML of the generated entry +- **profile**: profile (array) record from the database +- **entry**: the (string) HTML of the generated entry -### 'profile_advanced' -* called when the HTML is generated for the 'Advanced profile', corresponding to the 'Profile' tab within a person's profile page -* $b is the (string) HTML representation of the generated profile -* The profile array details are in $a->profile. +### profile_advanced +Called when the HTML is generated for the Advanced profile, corresponding to the Profile tab within a person's profile page. +`$b` is the HTML string representation of the generated profile. +The profile array details are in `$a->profile`. -### 'directory_item' -'directory_item' is called from the Directory page when formatting an item for display. +### directory_item +Called from the Directory page when formatting an item for display. +`$b` is an array: + +- **contact**: contact record array for the person from the database +- **entry**: the HTML string of the generated entry + +### profile_sidebar_enter +Called prior to generating the sidebar "short" profile for a page. +`$b` is the person's profile array + +### profile_sidebar +Called when generating the sidebar "short" profile for a page. +`$b` is an array: + +- **profile**: profile record array for the person from the database +- **entry**: the HTML string of the generated entry + +### contact_block_end +Called when formatting the block of contacts/friends on a profile sidebar has completed. +`$b` is an array: + +- **contacts**: array of contacts +- **output**: the generated HTML string of the contact block + +### bbcode +Called after conversion of bbcode to HTML. +`$b` is an HTML string converted text. + +### html2bbcode +Called after tag conversion of HTML to bbcode (e.g. remote message posting) +`$b` is a string converted text + +### page_header +Called after building the page navigation section. +`$b` is a string HTML of nav region. + +### personal_xrd +Called prior to output of personal XRD file. +`$b` is an array: + +- **user**: the user record array for the person +- **xml**: the complete XML string to be output + +### home_content +Called prior to output home page content, shown to unlogged users. +`$b` is the HTML sring of section region. + +### contact_edit +Called when editing contact details on an individual from the Contacts page. $b is an array: - 'contact' => contact (array) record for the person from the database - 'entry' => the (string) HTML of the generated entry +- **contact**: contact record (array) of target contact +- **output**: the (string) generated HTML of the contact edit page -### 'profile_sidebar_enter' -* called prior to generating the sidebar "short" profile for a page -* $b is the person's profile array +### contact_edit_post +Called when posting the contact edit page. +`$b` is the `$_POST` array -### 'profile_sidebar' -'profile_sidebar is called when generating the sidebar "short" profile for a page. -$b is an array: +### init_1 +Called just after DB has been opened and before session start. +No hook data. - 'profile' => profile (array) record for the person from the database - 'entry' => the (string) HTML of the generated entry +### page_end +Called after HTML content functions have completed. +`$b` is (string) HTML of content div. -### 'contact_block_end' -is called when formatting the block of contacts/friends on a profile sidebar has completed. -$b is an array: +### avatar_lookup +Called when looking up the avatar. `$b` is an array: - 'contacts' => array of contacts - 'output' => the (string) generated HTML of the contact block +- **size**: the size of the avatar that will be looked up +- **email**: email to look up the avatar for +- **url**: the (string) generated URL of the avatar -### 'bbcode' -* called during conversion of bbcode to html -* $b is a string converted text +### emailer_send_prepare +Called from `Emailer::send()` before building the mime message. +`$b` is an array of params to `Emailer::send()`. -### 'html2bbcode' -* called during conversion of html to bbcode (e.g. remote message posting) -* $b is a string converted text +- **fromName**: name of the sender +- **fromEmail**: email fo the sender +- **replyTo**: replyTo address to direct responses +- **toEmail**: destination email address +- **messageSubject**: subject of the message +- **htmlVersion**: html version of the message +- **textVersion**: text only version of the message +- **additionalMailHeader**: additions to the smtp mail header -### 'page_header' -* called after building the page navigation section -* $b is a string HTML of nav region +### emailer_send +Called before calling PHP's `mail()`. +`$b` is an array of params to `mail()`. -### 'personal_xrd' -'personal_xrd' is called prior to output of personal XRD file. -$b is an array: +- **to** +- **subject** +- **body** +- **headers** - 'user' => the user record for the person - 'xml' => the complete XML to be output +### load_config +Called during `App` initialization to allow addons to load their own configuration file(s) with `App::loadConfigFile()`. -### 'home_content' -* called prior to output home page content, shown to unlogged users -* $b is (string) HTML of section region +### nav_info +Called after the navigational menu is build in `include/nav.php`. +`$b` is an array containing `$nav` from `include/nav.php`. -### 'contact_edit' -is called when editing contact details on an individual from the Contacts page. -$b is an array: - - 'contact' => contact record (array) of target contact - 'output' => the (string) generated HTML of the contact edit page - -### 'contact_edit_post' -* called when posting the contact edit page. -* $b is the $_POST array - -### 'init_1' -* called just after DB has been opened and before session start -* $b is not used or passed - -### 'page_end' -* called after HTML content functions have completed -* $b is (string) HTML of content div - -### 'avatar_lookup' -'avatar_lookup' is called when looking up the avatar. -$b is an array: - - 'size' => the size of the avatar that will be looked up - 'email' => email to look up the avatar for - 'url' => the (string) generated URL of the avatar - -### 'emailer_send_prepare' -'emailer_send_prepare' called from Emailer::send() before building the mime message. -$b is an array, params to Emailer::send() - - 'fromName' => name of the sender - 'fromEmail' => email fo the sender - 'replyTo' => replyTo address to direct responses - 'toEmail' => destination email address - 'messageSubject' => subject of the message - 'htmlVersion' => html version of the message - 'textVersion' => text only version of the message - 'additionalMailHeader' => additions to the smtp mail header - -### 'emailer_send' -is called before calling PHP's mail(). -$b is an array, params to mail() - - 'to' - 'subject' - 'body' - 'headers' - -### 'nav_info' -is called after the navigational menu is build in include/nav.php. -$b is an array containing $nav from nav.php. - -### 'template_vars' -is called before vars are passed to the template engine to render the page. +### template_vars +Called before vars are passed to the template engine to render the page. The registered function can add,change or remove variables passed to template. -$b is an array with: +`$b` is an array with: - 'template' => filename of template - 'vars' => array of vars passed to template +- **template**: filename of template +- **vars**: array of vars passed to the template -### 'acl_lookup_end' -is called after the other queries have passed. -The registered function can add, change or remove the acl_lookup() variables. +### acl_lookup_end +Called after the other queries have passed. +The registered function can add, change or remove the `acl_lookup()` variables. - 'results' => array of the acl_lookup() vars +- **results**: array of the acl_lookup() vars -### 'prepare_body_init' +### prepare_body_init Called at the start of prepare_body Hook data: - 'item' => item array (input/output) -### 'prepare_body_content_filter' +- **item** (input/output): item array + +### prepare_body_content_filter Called before the HTML conversion in prepare_body. If the item matches a content filter rule set by an addon, it should just add the reason to the filter_reasons element of the hook data. Hook data: - 'item' => item array (input) - 'filter_reasons' => reasons array (input/output) -### 'prepare_body' -Called after the HTML conversion in prepare_body. +- **item**: item array (input) +- **filter_reasons** (input/output): reasons array + +### prepare_body +Called after the HTML conversion in `prepare_body()`. Hook data: - 'item' => item array (input) - 'html' => converted item body (input/output) - 'is_preview' => post preview flag (input) - 'filter_reasons' => reasons array (input) -### 'prepare_body_final' -Called at the end of prepare_body. +- **item** (input): item array +- **html** (input/output): converted item body +- **is_preview** (input): post preview flag +- **filter_reasons** (input): reasons array + +### prepare_body_final +Called at the end of `prepare_body()`. Hook data: - 'item' => item array (input) - 'html' => converted item body (input/output) -Complete list of hook callbacks ---- +- **item**: item array (input) +- **html**: converted item body (input/output) + +### put_item_in_cache +Called after `prepare_text()` in `put_item_in_cache()`. +Hook data: + +- **item** (input): item array +- **rendered-html** (input/output): final item body HTML +- **rendered-hash** (input/output): original item body hash + +### magic_auth_success +Called when a magic-auth was successful. +Hook data: + + visitor => array with the contact record of the visitor + url => the query string + +## Current JavaScript hooks + +### postprocess_liveupdate +Called at the end of the live update process (XmlHttpRequest) + +## Complete list of hook callbacks Here is a complete list of all hook callbacks with file locations (as of 01-Apr-2018). Please see the source for details of any hooks not documented above. @@ -333,7 +410,7 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks($a->module.'_mod_content', $arr); Addon::callHooks($a->module.'_mod_aftercontent', $arr); Addon::callHooks('page_end', $a->page['content']); - + ### include/api.php Addon::callHooks('logged_in', $a->user); @@ -341,12 +418,12 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks('logged_in', $a->user); ### include/enotify.php - + Addon::callHooks('enotify', $h); Addon::callHooks('enotify_store', $datarray); Addon::callHooks('enotify_mail', $datarray); Addon::callHooks('check_item_notification', $notification_data); - + ### include/conversation.php Addon::callHooks('conversation_start', $cb); @@ -364,6 +441,7 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks('contact_block_end', $arr); Addon::callHooks('poke_verbs', $arr); + Addon::callHooks('put_item_in_cache', $hook_data); Addon::callHooks('prepare_body_init', $item); Addon::callHooks('prepare_body_content_filter', $hook_data); Addon::callHooks('prepare_body', $hook_data); @@ -405,7 +483,7 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- ### mod/friendica.php Addon::callHooks('about_hook', $o); - + ### mod/subthread.php Addon::callHooks('post_local_end', $arr); @@ -478,7 +556,7 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks('post_local', $datarray); Addon::callHooks('post_local_end', $datarray); -### mod/editpost.php +### mod/editpost.php Addon::callHooks('jot_tool', $jotplugins); @@ -490,6 +568,10 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks("template_vars", $arr); +### src/App.php + + Addon::callHooks('load_config'); + ### src/Model/Item.php Addon::callHooks('post_local', $item); @@ -510,6 +592,7 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks('profile_sidebar', $arr); Addon::callHooks('profile_tabs', $arr); Addon::callHooks('zrl_init', $arr); + Addon::callHooks('magic_auth_success', $arr); ### src/Model/Event.php @@ -546,11 +629,11 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks('sexpref_selector', $select); Addon::callHooks('marital_selector', $select); -### src/Content/OEmbed.php +### src/Content/OEmbed.php Addon::callHooks('oembed_fetch_url', $embedurl, $j); -### src/Content/Nav.php +### src/Content/Nav.php Addon::callHooks('page_header', $a->page['nav']); Addon::callHooks('nav_info', $nav); @@ -563,7 +646,7 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks('notifier_end', $target_item); -### src/Worker/Queue.php +### src/Worker/Queue.php Addon::callHooks('queue_predeliver', $r); Addon::callHooks('queue_deliver', $params); @@ -573,7 +656,7 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks('authenticate', $addon_auth); Addon::callHooks('login_hook', $o); -### src/Module/Logout.php +### src/Module/Logout.php Addon::callHooks("logging_out"); @@ -618,3 +701,7 @@ Here is a complete list of all hook callbacks with file locations (as of 01-Apr- Addon::callHooks('atom_feed_end', $atom); Addon::callHooks('atom_feed_end', $atom); + +### view/js/main.js + + callAddonHooks("postprocess_liveupdate"); diff --git a/doc/Config.md b/doc/Config.md new file mode 100644 index 000000000..788c1cd0b --- /dev/null +++ b/doc/Config.md @@ -0,0 +1,231 @@ +Config values that can only be set in config/local.ini.php +========================================================== + +* [Home](help) + +Friendica's configuration is done in two places: in INI configuration files and in the `config` database table. +Database config values overwrite the same file config values. + +## File configuration + +WARNING: some characters `?{}|&~![()^"` should not be used in the keys or values. If one of those character is required put the value between double quotes (eg. password = "let&me&in") +The configuration format for file configuration is an INI string returned from a PHP file. +This prevents your webserver from displaying your private configuration it interprets the configuration files and displays nothing. + +A typical configuration file looks like this: + +```php + +table.config { + margin: 1em 0; + background-color: #f9f9f9; + border: 1px solid #aaa; + border-collapse: collapse; + color: #000; + width: 100%; +} + +table.config > tr > th, +table.config > tr > td, +table.config > * > tr > th, +table.config > * > tr > td { + border: 1px solid #aaa; + padding: 0.2em 0.4em +} + +table.config > tr > th, +table.config > * > tr > th { + background-color: #f2f2f2; + text-align: center; + width: 50% +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
.htconfig.phpconfig/local.ini.php
+$db_host = 'localhost';
+$db_user = 'mysqlusername';
+$db_pass = 'mysqlpassword';
+$db_data = 'mysqldatabasename';
+$a->config["system"]["db_charset"] = 'utf8mb4';
+
+[database]
+hostname = localhost
+username = mysqlusername
+password = mysqlpassword
+database = mysqldatabasename
+charset = utf8mb4
+
+$a->config["section"]["key"] = "value";
+
+[section]
+key = value
+
+$a->config["section"]["key"] = array(
+	"value1",
+	"value2",
+	"value3"
+);
+
+[section]
+key[] = value1
+key[] = value2
+key[] = value3
+
+$a->config["key"] = "value";
+
+[config]
+key = value
+
+$a->path = "value";
+
+[system]
+urlpath = value
+
+$default_timezone = "value";
+
+[system]
+default_timezone = value
+
+$pidfile = "value";
+
+[system]
+pidfile = value
+
+$lang = "value";
+
+[system]
+language = value
+
+ + +### Database Settings + +The configuration variables database.hostname, database.username, database.password, database.database and database.charset are holding your credentials for the database connection. +If you need to specify a port to access the database, you can do so by appending ":portnumber" to the database.hostname variable. + + [database] + hostname = your.mysqlhost.com:123456 + +If all of the following environment variables are set, Friendica will use them instead of the previously configured variables for the db: + + MYSQL_HOST + MYSQL_PORT + MYSQL_USERNAME + MYSQL_PASSWORD + MYSQL_DATABASE + +## Config values that can only be set in config/local.ini.php + +There are some config values that haven't found their way into the administration page. +This has several reasons. +Maybe they are part of a current development that isn't considered stable and will be added later in the administration page when it is considered safe. +Or it triggers something that isn't expected to be of public interest. +Or it is for testing purposes only. + +**Attention:** Please be warned that you shouldn't use one of these values without the knowledge what it could trigger. +Especially don't do that with undocumented values. + +These configurations keys and their default value are listed in `config/config.ini.php` and should be ovewritten in `config/local.ini.php`. + +## Administrator Options + +Enabling the admin panel for an account, and thus making the account holder admin of the node, is done by setting the variable + + [config] + admin_email = someone@example.com + +Where you have to match the email address used for the account with the one you enter to the config/local.ini.php file. +If more then one account should be able to access the admin panel, separate the email addresses with a comma. + + [config] + admin_email = someone@example.com,someoneelse@example.com + +If you want to have a more personalized closing line for the notification emails you can set a variable for the admin_name. + + [config] + admin_name = Marvin diff --git a/doc/Developers-Intro.md b/doc/Developers-Intro.md index 1e077902d..5deaf98ff 100644 --- a/doc/Developers-Intro.md +++ b/doc/Developers-Intro.md @@ -33,7 +33,7 @@ If you don't want to translate the UI, or it is already done to your satisfactio Are you good at designing things? If you have seen Friendica you probably have ideas to improve it, haven't you? -* If you would like to work with us on enhancing the user interface, please join the [UX Watchdogs forum](https://fc.oscp.info/profile/ux-watchdogs) +* If you would like to work with us on enhancing the user interface, please join the [forum for Friendica development](https://forum.friendi.ca/profile/developers). * Make plans for a better Friendica interface design and share them with us. * Tell us if you are able to realize your ideas or what kind of help you need. We can't promise we have the right skills in the group but we'll try. diff --git a/doc/FAQ.md b/doc/FAQ.md index 31d980a9a..1b84b5cd8 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -197,14 +197,14 @@ If you are searching for new themes, you can find them at [Friendica-Themes.com] ### I've changed my email address now the admin panel is gone? -Have a look into your .htconfig.php and fix your email address there. +Have a look into your config/local.ini.php and fix your email address there. ### Can there be more then one admin for a node? Yes. You just have to list more then one email address in the -.htconfig.php file. +config/local.ini.php file. The listed emails need to be separated by a comma. diff --git a/doc/Home.md b/doc/Home.md index 1d72a2600..a353bab0d 100644 --- a/doc/Home.md +++ b/doc/Home.md @@ -32,7 +32,7 @@ Friendica Documentation and Resources * [Installing Connectors (Twitter/GNU Social)](help/Installing-Connectors) * [Install an ejabberd server (XMPP chat) with synchronized credentials](help/install-ejabberd) * [Using SSL with Friendica](help/SSL) -* [Config values that can only be set in .htconfig.php](help/htconfig) +* [Config values that can only be set in config/local.ini.php](help/Config) * [Improve Performance](help/Improve-Performance) * [Administration Tools](help/tools) @@ -51,6 +51,7 @@ Friendica Documentation and Resources * [Translate Friendica](help/translations) * [Use Composer](help/Composer) * [Move classes to `src`](help/Developer-How-To-Move-Classes-to-src) + * [Run tests](help/Tests) * Reference * [Twitter/GNU Social API Functions](help/api) * [Code (Doxygen generated - sets cookies)](doc/html/) @@ -72,4 +73,3 @@ Friendica Documentation and Resources * [Site/Version Info](friendica) * [Friendica Credits](credits) - diff --git a/doc/Install.md b/doc/Install.md index 2a80cf72a..3854c32bc 100644 --- a/doc/Install.md +++ b/doc/Install.md @@ -27,7 +27,7 @@ Requirements --- * Apache with mod-rewrite enabled and "Options All" so you can use a local .htaccess file -* PHP 5.6+ (PHP 7 is recommended for performance) +* PHP 5.6.1+ (PHP 7 is recommended for performance) * PHP *command line* access with register_argc_argv set to true in the php.ini file * Curl, GD, PDO, MySQLi, hash, xml, zip and OpenSSL extensions * The POSIX module of PHP needs to be activated (e.g. [RHEL, CentOS](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) have disabled it) @@ -47,14 +47,15 @@ If you are able to do so, we recommend using git to clone the source repository This makes the software much easier to update. The Linux commands to clone the repository into a directory "mywebsite" would be - git clone https://github.com/friendica/friendica.git mywebsite + git clone https://github.com/friendica/friendica.git -b master mywebsite cd mywebsite bin/composer.phar install -Make sure the folder *view/smarty3* exists and is writable by the webserver user +Make sure the folder *view/smarty3* exists and is writable by the webserver user, in this case `www-data` mkdir view/smarty3 - chmod 777 view/smarty3 + chown www-data:www-data view/smarty3 + chmod 775 view/smarty3 Get the addons by going into your website folder. @@ -62,10 +63,20 @@ Get the addons by going into your website folder. Clone the addon repository (separately): - git clone https://github.com/friendica/friendica-addons.git addon + git clone https://github.com/friendica/friendica-addons.git -b master addon If you copy the directory tree to your webserver, make sure that you also copy .htaccess - as "dot" files are often hidden and aren't normally copied. +If you want to use the development version of Friendica you can switch to the devel branch in the repository by running + + git checkout develop + bin/composer.phar install + cd addon + git checkout develop + +please be aware that the develop branch may break your Friendica node at any time. +If you encounter a bug, please let us know. + ### Create a database Create an empty database and note the access details (hostname, username, password, database name). @@ -89,40 +100,124 @@ If you need to specify a port for the connection to the database, you can do so *If* the manual installation fails for any reason, check the following: -* Does ".htconfig.php" exist? If not, edit htconfig.php and change the system settings. Rename to .htconfig.php -* Is the database is populated? If not, import the contents of "database.sql" with phpmyadmin or the mysql command line. +* Does "config/local.ini.php" exist? If not, edit config/local-sample.ini.php and change the system settings. +* Rename to `config/local.ini.php`. +* Is the database is populated? If not, import the contents of `database.sql` with phpmyadmin or the mysql command line. At this point visit your website again, and register your personal account. Registration errors should all be recoverable automatically. If you get any *critical* failure at this point, it generally indicates the database was not installed correctly. -You might wish to move/rename .htconfig.php to another name and empty (called 'dropping') the database tables, so that you can start fresh. +You might wish to move/rename `config/local.ini.php` to another name and empty (called 'dropping') the database tables, so that you can start fresh. ### Option B: Run the automatic install script -Open the file htconfig.php in the main Friendica directory with a text editor. -Remove the `die('...');` line and edit the lines to suit your installation (MySQL, language, theme etc.). -Then save the file (do not rename it). +You have the following options to automatically install Friendica: +- creating a prepared config file (f.e. `prepared.ini.php`) +- using environment variables (f.e. `MYSQL_HOST`) +- using options (f.e. `--dbhost `) -Navigate to the main Friendica directory and execute the following command: - - bin/console autoinstall - -Or if you wish to include all optional checks, execute this statement instead: - - bin/console autoinstall -a - -At this point visit your website again, and register your personal account. - -*If* the automatic installation fails for any reason, check the following: - -* Does ".htconfig.php" already exist? If yes, the automatic installation won't start -* Are the settings inside "htconfig.php" correct? If not, edit the file again. -* Is the empty MySQL-database created? If not, create it. +You can combine environment variables and options, but be aware that options are prioritized over environment variables. For more information during the installation, you can use this command line option bin/console autoinstall -v +If you wish to include all optional checks, use `-a` like this statement: + + bin/console autoinstall -a + +*If* the automatic installation fails for any reason, check the following: + +* Does `config/local.ini.php` already exist? If yes, the automatic installation won't start +* Are the options in the `config/local.ini.php` correct? If not, edit them directly. +* Is the empty MySQL-database created? If not, create it. + +#### B.1: Config file + +You can use a prepared config file like [local-sample.ini.php](config/local-sample.ini.php). + +Navigate to the main Friendica directory and execute the following command: + + bin/console autoinstall -f + +#### B.2: Environment variables + +There are two types of environment variables. +- those you can use in normal mode too (Currently just **database credentials**) +- those you can only use during installation (because Friendica will normally ignore it) + +You can use the options during installation too and skip some of the environment variables. + +**Database credentials** + +if you don't use the option `--savedb` during installation, the DB credentials will **not** be saved in the `config/local.ini.php`. + +- `MYSQL_HOST` The host of the mysql/mariadb database +- `MYSQL_PORT` The port of the mysql/mariadb database +- `MYSQL_USERNAME` The username of the mysql database login (used for mysql) +- `MYSQL_USER` The username of the mysql database login (used for mariadb) +- `MYSQL_PASSWORD` The password of the mysql/mariadb database login +- `MYSQL_DATABASE` The name of the mysql/mariadb database + +**Friendica settings** + +This variables wont be used at normal Friendica runtime. +Instead, they get saved into `config/local.ini.php`. + +- `FRIENDICA_PHP_PATH` The path of the PHP binary +- `FRIENDICA_ADMIN_MAIL` The admin email address of Friendica (this email will be used for admin access) +- `FRIENDICA_TZ` The timezone of Friendica +- `FRIENDICA_LANG` The langauge of Friendica + +Navigate to the main Friendica directory and execute the following command: + + bin/console autoinstall [--savedb] + +#### B.3: Execution options + +All options will be saved in the `config/local.ini.php` and are overruling the associated environment variables. + +- `-H|--dbhost ` The host of the mysql/mariadb database (env `MYSQL_HOST`) +- `-p|--dbport ` The port of the mysql/mariadb database (env `MYSQL_PORT`) +- `-U|--dbuser ` The username of the mysql/mariadb database login (env `MYSQL_USER` or `MYSQL_USERNAME`) +- `-P|--dbpass ` The password of the mysql/mariadb database login (env `MYSQL_PASSWORD`) +- `-d|--dbdata ` The name of the mysql/mariadb database (env `MYSQL_DATABASE`) +- `-b|--phppath ` The path of the PHP binary (env `FRIENDICA_PHP_PATH`) +- `-A|--admin ` The admin email address of Friendica (env `FRIENDICA_ADMIN_MAIL`) +- `-T|--tz ` The timezone of Friendica (env `FRIENDICA_TZ`) +- `-L|--land ` The language of Friendica (env `FRIENDICA_LANG`) + +Navigate to the main Friendica directory and execute the following command: + + bin/console autoinstall [options] + +### Prepare .htaccess file + +Copy .htaccess-dist to .htaccess (be careful under Windows) to have working mod-rewrite again. If you have installed Friendica into a sub directory, like /friendica/ set this path in RewriteBase accordingly. + +Example: + + cp .htacces-dist .htaccess + +*Note*: Do **not** rename the .htaccess-dist file as it is tracked by GIT and renaming will cause a dirty working directory. + +### Verify the "host-meta" page is working + +Friendica should respond automatically to important addresses under the /.well-known/ rewrite path. +One critical URL would look like, for example, https://example.com/.well-known/host-meta +It must be visible to the public and must respond with an XML file that is automatically customized to your site. + +If that URL is not working, it is possible that some other software is using the /.well-known/ path. +Other symptoms may include an error message in the Admin settings that says "host-meta is not reachable on your system. +This is a severe configuration issue that prevents server to server communication." +Another common error related to host-meta is the "Invalid profile URL." + +Check for a .well-known directory that did not come with Friendica. +The preferred configuration is to remove the directory, however this is not always possible. +If there is any /.well-known/.htaccess file, it could interfere with this Friendica core requirement. +You should remove any RewriteRules from that file, or remove that whole file if appropriate. +It may be necessary to chmod the /.well-known/.htaccess file if you were not given write permissions by default. + ### Set up the worker Set up a cron job or scheduled task to run the worker once every 5-10 minutes in order to perform background processing. @@ -151,5 +246,5 @@ Bad things will happen. Let there be a hardware failure, a corrupted database or whatever you can think of. So once the installation of your Friendica node is done, you should make yourself a backup plan. -The most important file is the `.htconfig.php` file in the base directory. +The most important file is the `config/local.ini.php` file. As it stores all your data, you should also have a recent dump of your Friendica database at hand, should you have to recover your node. diff --git a/doc/Installing-Connectors.md b/doc/Installing-Connectors.md index 5ea34d473..f1eba5a6a 100644 --- a/doc/Installing-Connectors.md +++ b/doc/Installing-Connectors.md @@ -4,7 +4,7 @@ Installing Connectors (Twitter/GNU Social) * [Home](help) -Friendica uses addons to provide connectivity to some networks, such as Twitter or App.net. +Friendica uses addons to provide connectivity to some networks, such as Twitter. There is also a addon to post through to an existing account on a GNU Social service. You only need this to post to an already existing GNU Social account, but not to communicate with GNU Social members in general. @@ -19,7 +19,7 @@ Addons must be installed by the site administrator before they can be used. This is accomplished through the site administration panel. Each of the connectors also requires an "API key" from the service you wish to connect with. -Some addons allow you to enter this information in the site administration pages, while others may require you to edit your configuration file (.htconfig.php). +Some addons allow you to enter this information in the site administration pages, while others may require you to edit your configuration file (config/local.ini.php). The ways to obtain these keys vary between the services, but they all require an existing account on the target service. Once installed, these API keys can usually be shared by all site members. @@ -39,10 +39,11 @@ You can get it from [Twitter](https://twitter.com/apps). Register your Friendica site as "Client" application with "Read & Write" access. We do not need "Twitter as login". When you've registered the app you get a key pair with an OAuth Consumer key and a secret key for your application/site. -Add this key pair to your global .htconfig.php: +Add this key pair to your config/local.ini.php: - $a->config['twitter']['consumerkey'] = 'your consumer_key here'; - $a->config['twitter']['consumersecret'] = 'your consumer_secret here'; + [twitter] + consumerkey = your consumer_key here + consumersecret = your consumer_secret here After this, your users can configure their Twitter account settings from "Settings -> Connector Settings". @@ -67,8 +68,8 @@ When the addon is activated the user has to acquire the following in order to co To get the OAuth Consumer key pair the user has to -1 ask her Friendica admin if a pair already exists or -2 has to register the Friendica server as a client application on the GNU Social server. +1 ask her Friendica admin if a pair already exists or +2 has to register the Friendica server as a client application on the GNU Social server. This can be done from the account settings under "Settings -> Connections -> Register an OAuth client application -> Register a new application" on the GNU Social server. @@ -83,6 +84,6 @@ During the registration of the OAuth client remember the following: After the required credentials for the application are stored in the configuration you have to actually connect your Friendica account with GNU Social. This is done from the Settings -> Connector Settings page. Follow the Sign in with GNU Social button, allow access and then copy the security code into the box provided. -Friendica will then try to acquire the final OAuth credentials from the API. +Friendica will then try to acquire the final OAuth credentials from the API. If successful, the addon settings will allow you to select to post your public messages to your GNU Social account (have a look behind the little lock symbol beneath the status "editor" on your Home or Network pages). diff --git a/doc/Move-Account.md b/doc/Move-Account.md index c34a93e94..2193c42c4 100644 --- a/doc/Move-Account.md +++ b/doc/Move-Account.md @@ -1,31 +1,31 @@ -How to move your account between servers -============ - -* [Home](help) - - -! **This is an experimental feature** - -* Go to "Settings" -> "[Export personal data](uexport)" -* Click on "Export account" to save your account data. -* **Save the file in a secure place!** It contains your details, your contacts, groups, and personal settings. It also contains your secret keys to authenticate yourself to your contacts. -* Go to your new server, and open *http://newserver.com/uimport* (there is not a direct link to this page at the moment). -* Do NOT create a new account prior to importing your old settings - uimport should be used *instead* of register. -* Load your saved account file and click "Import". -* After the move, the account on the old server will not work reliably anymore, and should be not used. - - -Friendica contacts ---- -Friendica will recreate your account on the new server, with your contacts and groups. -A message is sent to Friendica contacts, to inform them about your move: -If your contacts are runnning on an updated server, your details on their side will be automatically updated. - -GNU Social contacts ---- -Contacts on GNU Social will be archived, as we can't inform them about your move. -You should ask them to remove your contact from their lists and re-add you, and you should do the same with their contact. - -Diaspora contacts ---- -Newer Diaspora servers are able to process "account migration" messages. +How to move your account between servers +============ + +* [Home](help) + + +! **This is an experimental feature** + +* Go to "Settings" -> "[Export personal data](uexport)" +* Click on "Export account" to save your account data. +* **Save the file in a secure place!** It contains your details, your contacts, groups, and personal settings. It also contains your secret keys to authenticate yourself to your contacts. +* Go to your new server, and open *http://newserver.com/uimport* (there is not a direct link to this page at the moment). Please consider that this is only possible on servers with open registration. On other systems only the administrator can add accounts with an uploaded file. +* Do NOT create a new account prior to importing your old settings - uimport should be used *instead* of register. +* Load your saved account file and click "Import". +* After the move, the account on the old server will not work reliably anymore, and should be not used. + + +Friendica contacts +--- +Friendica will recreate your account on the new server, with your contacts and groups. +A message is sent to Friendica contacts, to inform them about your move: +If your contacts are runnning on an updated server, your details on their side will be automatically updated. + +GNU Social contacts +--- +Contacts on GNU Social will be archived, as we can't inform them about your move. +You should ask them to remove your contact from their lists and re-add you, and you should do the same with their contact. + +Diaspora contacts +--- +Newer Diaspora servers are able to process "account migration" messages. diff --git a/doc/Quick-Start-groupsandpages.md b/doc/Quick-Start-groupsandpages.md index 652407d64..d4680f65f 100644 --- a/doc/Quick-Start-groupsandpages.md +++ b/doc/Quick-Start-groupsandpages.md @@ -15,6 +15,6 @@ Remember the link at the top of this page will bring you back here. Once you've added some groups, move on to the next section. - + diff --git a/doc/Settings.md b/doc/Settings.md index dbf57395f..dda2a9a58 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -69,7 +69,7 @@ You can chose between the following modes: ##### Invitation based registry Additionally to the setting in the admin panel, you can devide if registrations are only possible using an invitation code or not. -To enable invitation based registration, you have to set the `invitation_only` setting in the [.htconfig.php](/help/htconfig) file. +To enable invitation based registration, you have to set the `invitation_only` setting in the [config/local.ini.php](/help/Config) file. If you want to use this method, the registration policy has to be set to either *open* or *requires approval*. #### Check Full Names @@ -162,6 +162,14 @@ It is disabled by default, as it causes additional load on the server and may be As admin of the node you can also set this flag directly in the database. Before doing so, you should be sure you know what you do and have a backup of the database. +#### Explicit Content + +If you are running a node with explicit content, you can announce this with this option. +When checked an information flag will be set in the published information about your node. +(Should *Publish Server Information* be enabled.) + +Additionally a note will be displayed on the registration page for new users. + ### Advanced #### Proxy Configuration Settings @@ -317,7 +325,7 @@ You should set up some kind of [log rotation](https://en.wikipedia.org/wiki/Log_ **Known Issues**: The filename ``friendica.log`` can cause problems depending on your server configuration (see [issue 2209](https://github.com/friendica/friendica/issues/2209)). By default PHP warnings and error messages are supressed. -If you want to enable those, you have to activate them in the ``.htconfig.php`` file. +If you want to enable those, you have to activate them in the ``config/local.ini.php`` file. Use the following settings to redirect PHP errors to a file. Config: @@ -365,24 +373,27 @@ By default this will be the one account you create during the installation proce But you can expand the list of email addresses by any used email address you want. Registration of new accounts with a listed email address is not possible. - $a->config['admin_email'] = 'you@example.com, buddy@example.com'; + [config] + admin_email = you@example.com, buddy@example.com ## PHP Path Some of Friendicas processes are running in the background. For this you need to specify the path to the PHP binary to be used. - $a->config['php_path'] = '{{$phpath}}'; + [config] + php_path = {{$phpath}} ## Subdirectory configuration It is possible to install Friendica into a subdirectory of your webserver. -We strongly discurage you from doing so, as this will break federation to other networks (e.g. Diaspora, GNU Socia, Hubzilla) +We strongly discourage you from doing so, as this will break federation to other networks (e.g. Diaspora, GNU Socia, Hubzilla) Say you have a subdirectory for tests and put Friendica into a further subdirectory, the config would be: - $a->path = 'tests/friendica'; + [system] + urlpath = tests/friendica ## Other exceptions -Furthermore there are some experimental settings, you can read-up in the [Config values that can only be set in .htconfig.php](help/htconfig) section of the documentation. +Furthermore there are some experimental settings, you can read-up in the [Config values that can only be set in config/local.ini.php](help/Config) section of the documentation. diff --git a/doc/Tags-and-Mentions.md b/doc/Tags-and-Mentions.md index 5b046228f..020214457 100644 --- a/doc/Tags-and-Mentions.md +++ b/doc/Tags-and-Mentions.md @@ -18,7 +18,7 @@ You can tag **persons who are in your social circle** by adding the "@"-sign in * @mike+151 - this form is used by the drop-down tag completion tool. It indicates the contact whose nickname is mike and whose contact identifier number is 151. The drop-down tool may be used to resolve people with duplicate nicknames. You can tag a person on a different network or one that is **not in your social circle** by using the following notation: - + * @mike@macgirvin.com - This is called a "remote mention" and can only be an email-style locator, not a web URL. Unless their system blocks unsolicited "mentions", the person tagged will likely receive a "Mention" post/activity or become a direct participant in the conversation in the case of public posts. @@ -27,7 +27,7 @@ The exception is an ongoing conversation started from a contact of both you and This is a spam prevention measure. Remote mentions are delivered using the OStatus protocol. -This protocol is used by Friendica and GNU Social and several other systems like Mastodon, but is not currently implemented in Diaspora. +This protocol is used by Friendica and GNU Social and several other systems like Mastodon, but is not currently implemented in Diaspora. As the OStatus protocol allows this Friendica user can be @-mentioned by users from platforms using this protocol in conversations if the "Enable OStatus support" is activated on the Friendica node. These @-mentions wont be blocked, even if there is no relationship between the sender and the receiver of the message. @@ -52,5 +52,5 @@ The same rules apply as with names that spaces within tags are represented by th It is therefore not possible to create a tag whose target contains an underscore. Topical tags are also not linked if they are purely numeric, e.g. #1. -If you wish to use a numerica hashtag, please add some descriptive text such as #2012-elections. +If you wish to use a numerica hashtag, please add some descriptive text such as #2012-elections. diff --git a/doc/Tests.md b/doc/Tests.md new file mode 100644 index 000000000..6acb4e783 --- /dev/null +++ b/doc/Tests.md @@ -0,0 +1,18 @@ +# Themes + +* [Home](help) + +You can run unit tests with [PHPUnit](https://phpunit.de/): + +```bash +phpunit +``` + +Some tests require access to a MySQL database. +You can specify the database credentials in environment variables: + +```bash +USER=database_user PASS=database_password DB=database_name phpunit +``` + +**Warning**: This will empty all the tables! Never use this on a production database. diff --git a/doc/Update.md b/doc/Update.md index 9e9324da9..835dd3375 100644 --- a/doc/Update.md +++ b/doc/Update.md @@ -7,7 +7,7 @@ Updating Friendica If you installed Friendica in the ``path/to/friendica`` folder: 1. Unpack the new Friendica archive in ``path/to/friendica_new``. -2. Copy ``.htconfig.php``, ``photo/`` and ``proxy/`` from ``path/to/friendica`` to ``path/to/friendica_new``. +2. Copy ``config/local.ini.php``, ``photo/`` and ``proxy/`` from ``path/to/friendica`` to ``path/to/friendica_new``. 3. Rename the ``path/to/friendica`` folder to ``path/to/friendica_old``. 4. Rename the ``path/to/friendica_new`` folder to ``path/to/friendica``. 5. Check your site. Note: it may go into maintenance mode to update the database schema. diff --git a/doc/Vagrant.md b/doc/Vagrant.md index a224ebafc..6fd74240d 100644 --- a/doc/Vagrant.md +++ b/doc/Vagrant.md @@ -42,7 +42,7 @@ This will not delete the virtual machine. 9. To ultimately delete the virtual machine run $> vagrant destroy - $> rm /vagrant/.htconfig.php + $> rm /vagrant/config/local.ini.php to make sure that you can start from scratch with another "vagrant up". @@ -53,6 +53,6 @@ You will then have the following accounts to login: * friendica1, password friendica1 * friendica2, password friendica2 and so on until friendica5 * friendica1 is connected to all others. friendica1 has two groups: group1 with friendica2 and friendica4, group2 with friendica3 and friendica5. - * friendica2 and friendica3 are conntected. friendica4 and friendica5 are connected. + * friendica2 and friendica3 are conntected. friendica4 and friendica5 are connected. For further documentation of vagrant, please see [the vagrant*docs*](https://docs.vagrantup.com/v2/). diff --git a/doc/api.md b/doc/api.md index 08fca74fa..07813b6a7 100644 --- a/doc/api.md +++ b/doc/api.md @@ -634,6 +634,73 @@ Friendica doesn't allow showing the friends of other users. * trim_user * contributor_details +--- + +### Return values for statuses/* api calls + +Returned status object is conform to GNU Social/Twitter api. + +Friendica adds some addictional fields: + +- owner: a user object, it's the owner of the item. +- private: boolean, true if the item is marked as private +- activities: map with activities related to the item. Every activity is a list of user objects. + +This properties are prefixed with "friendica_" in JSON responses and namespaced under "http://friendi.ca/schema/api/1/" in XML responses + +JSON: + +```json +[ + { + // ... + 'friendica_owner' : { + // user object + }, + 'friendica_private' : true, + 'friendica_activities': { + 'like': [ + { + // user object + }, + // ... + ], + 'dislike': [], + 'attendyes': [], + 'attendno': [], + 'attendmaybe': [] + } + }, + // ... +] +``` + +XML: + +```xml + + + + + true + + + + + + + + + + + + + + + +``` + + --- ### statusnet/config (*) diff --git a/doc/de/Addons.md b/doc/de/Addons.md index 02be19e30..ce9af3778 100644 --- a/doc/de/Addons.md +++ b/doc/de/Addons.md @@ -203,7 +203,7 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Addon::callHooks($a->module.'_mod_content', $arr); Addon::callHooks($a->module.'_mod_aftercontent', $arr); Addon::callHooks('page_end', $a->page['content']); - + ### include/api.php Addon::callHooks('logged_in', $a->user); @@ -216,7 +216,7 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Addon::callHooks('enotify_store', $datarray); Addon::callHooks('enotify_mail', $datarray); Addon::callHooks('check_item_notification', $notification_data); - + ### include/conversation.php Addon::callHooks('conversation_start', $cb); @@ -234,6 +234,7 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap Addon::callHooks('contact_block_end', $arr); Addon::callHooks('poke_verbs', $arr); + Addon::callHooks('put_item_in_cache', $hook_data); Addon::callHooks('prepare_body_init', $item); Addon::callHooks('prepare_body_content_filter', $hook_data); Addon::callHooks('prepare_body', $hook_data); @@ -275,7 +276,7 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap ### mod/friendica.php Addon::callHooks('about_hook', $o); - + ### mod/subthread.php Addon::callHooks('post_local_end', $arr); @@ -322,7 +323,7 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap ### mod/contacts.php - Addon::callHooks('contact_edit_post', $_POST); + Addon::callHooks('contact_edit_post', $_POST); Addon::callHooks('contact_edit', $arr); ### mod/tagger.php diff --git a/doc/de/FAQ.md b/doc/de/FAQ.md index b52aa3d39..fe8a9d128 100644 --- a/doc/de/FAQ.md +++ b/doc/de/FAQ.md @@ -85,9 +85,9 @@ Zum Konvertieren von Videos in das lizenfreie Videoformat WebM gibt es unter Win ### Ist es möglich, bei mehreren Profilen verschiedene Avatare (Nutzerbilder) zu haben? Ja. -Auf Deiner ["Profile verwalten/editieren"-Seite](../profiles) wählst Du zunächst das gewünschte Profil aus. -Anschließend siehst Du eine Seite mit allen Infos zu diesem Profil. -Klicke nun oben auf den Link "Profilbild ändern" und lade im nächsten Fenster ein Bild von Deinem PC hoch. +Auf Deiner ["Profile verwalten/editieren"-Seite](../profiles) wählst Du zunächst das gewünschte Profil aus. +Anschließend siehst Du eine Seite mit allen Infos zu diesem Profil. +Klicke nun oben auf den Link "Profilbild ändern" und lade im nächsten Fenster ein Bild von Deinem PC hoch. Um Deine privaten Daten zu schützen, wird in Beiträgen nur das Bild aus Deinem öffentlichen Profil angezeigt. @@ -180,7 +180,7 @@ Hier ist eine Liste von Clients bei denen dies möglich ist, bzw. die speziell f ### Wo finde ich Hilfe? -Wenn Du Probleme mit Deiner Friendica-Seite hast, dann kannst Du die Community in der [Friendica-Support-Gruppe](https://forum.friendi.ca/profile/helpers) oder im [deutschen Friendica-Support-Forum](http://toktan.org/profile/wiki) fragen oder Dir das [deutsche Wiki](http://wiki.toktan.org/doku.php) anschauen. +Wenn Du Probleme mit Deiner Friendica-Seite hast, dann kannst Du die Community in der [Friendica-Support-Gruppe](https://forum.friendi.ca/profile/helpers) fragen oder Dir das [deutsche Wiki](https://friendica-wiki.de/) anschauen. Wenn Du Deinen Account nicht nutzen kannst, kannst Du entweder einen [Testaccount](https://tryfriendica.de) bzw. einen Account auf einer öffentlichen Seite ([Liste](https://dir.friendica.social/servers)) nutzen. Wenn du dir keinen weiteren Friendica Account einrichten willst, kannst du auch gerne über einen der folgenden alternativen Kanäle Hilfe suchen: @@ -199,7 +199,7 @@ Admin Ja, das ist möglich. Es ist allerdings nicht möglich, eine Datenbank durch zwei Domains zu nutzen. -Solange Du Deine .htconfig.php allerdings so einrichtest, dass das System nicht versucht, eine Installation durchzuführen, kannst Du die richtige Config-Datei in include/$hostname/.htconfig.php hinterlegen. +Solange Du Deine config/local.ini.php allerdings so einrichtest, dass das System nicht versucht, eine Installation durchzuführen, kannst Du die richtige Config-Datei in include/$hostname/config/local.ini.php hinterlegen. Alle Cache-Aspekte und der Zugriffsschutz können pro Instanz konfiguriert werden. @@ -216,13 +216,13 @@ Wenn Du neue Themen suchst, findest Du sie auf [Friendica-Themes.com](http://fri ### Ich habe meine E-Mail Adresse geändern und jetzt ist das Admin Panel verschwunden? -Bitte aktualisiere deine E-Mail Adresse in der .htconfig.php Datei. +Bitte aktualisiere deine E-Mail Adresse in der config/local.ini.php Datei. ### Kann es mehr als einen Admin auf einer Friendica Instanz geben? Ja. -Du kannst in der .htconfig.php Datei mehrere E-Mail Adressen auflisten. +Du kannst in der config/local.ini.php Datei mehrere E-Mail Adressen auflisten. Die aufgelisteten Adressen werden mit Kommata von einander getrennt. diff --git a/doc/de/Home.md b/doc/de/Home.md index 8e4be6bfc..6c95476ed 100644 --- a/doc/de/Home.md +++ b/doc/de/Home.md @@ -34,7 +34,7 @@ Friendica - Dokumentation und Ressourcen * [Konnektoren (Connectors) installieren (Twitter/GNU Social)](help/Installing-Connectors) * [Installation eines ejabberd Servers (XMPP-Chat) mit synchronisierten Anmeldedaten](help/install-ejabberd) (EN) * [Betreibe deine Seite mit einem SSL-Zertifikat](help/SSL) -* [Konfigurationswerte, die nur in der .htconfig.php gesetzt werden können](help/htconfig) (EN) +* [Konfigurationswerte, die nur in der config/local.ini.php gesetzt werden können](help/Config) (EN) * [Performance verbessern](help/Improve-Performance) * [Administration Werkzeuge](help/tools) (EN) @@ -56,11 +56,12 @@ Friendica - Dokumentation und Ressourcen * [Code-Referenz (mit doxygen generiert - setzt Cookies)](doc/html/) * [Twitter/GNU Social API Functions](help/api) (EN) * [Translation of Friendica](help/translations) (EN) +* [Run tests](help/Tests) (EN) **Externe Ressourcen** * [Haupt-Webseite](https://friendi.ca) -* [Deutsches Friendica-Wiki](http://wiki.toktan.org/doku.php) +* [Deutsches Friendica-Wiki](https://friendica-wiki.de) * Support Kanäle * [Friendica Support Forum](https://forum.friendi.ca/~helpers) * [Mailing Listen Archiv](http://mailman.friendi.ca/mailman/listinfo/support-friendi.ca) zum Abonnieren der Liste eine E-Mail an ``support-request(at)friendi.ca?subject=subscribe`` senden @@ -72,4 +73,3 @@ Friendica - Dokumentation und Ressourcen * [Seite/Friendica-Version](friendica) * [Mitwirkenden bei Friendica](credits) - diff --git a/doc/de/Install.md b/doc/de/Install.md index ff077351b..05a4e1e46 100644 --- a/doc/de/Install.md +++ b/doc/de/Install.md @@ -28,7 +28,7 @@ Requirements --- * Apache mit einer aktiverten mod-rewrite-Funktion und dem Eintrag "Options All", so dass du die lokale .htaccess-Datei nutzen kannst -* PHP 5.6+ (PHP 7 ist aufgrund der Performance empfohlen) +* PHP 5.6.1+ (PHP 7 ist aufgrund der Performance empfohlen) * PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei * Curl, GD, PDO, MySQLi, xml, zip und OpenSSL-Erweiterung * Das POSIX Modul muss aktiviert sein ([CentOS, RHEL](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) haben dies z.B. deaktiviert) @@ -47,22 +47,22 @@ Wenn du die Möglichkeit hierzu hast, empfehlen wir dir "git" zu nutzen, um die Das macht die Aktualisierung wesentlich einfacher. Der Linux-Code, mit dem man die Dateien direkt in ein Verzeichnis wie "meinewebseite" kopiert, ist - git clone https://github.com/friendica/friendica.git mywebsite + git clone https://github.com/friendica/friendica.git -b master mywebsite cd mywebsite bin/composer.phar install Stelle sicher, dass der Ordner *view/smarty3* existiert and von dem Webserver-Benutzer beschreibbar ist mkdir view/smarty3 - chmod 777 view/smarty3 + chmod 775 view/smarty3 -Falls Addons installiert werden sollen: Gehe in den Friendica-Ordner +Falls Addons installiert werden sollen: Gehe in den Friendica-Ordner cd mywebsite Und die Addon Repository klonst: - git clone https://github.com/friendica/friendica-addons.git addon + git clone https://github.com/friendica/friendica-addons.git -b master addon Um das Addon-Verzeichnis aktuell zu halten, solltest du in diesem Pfad ein "git pull"-Befehl eintragen @@ -71,6 +71,17 @@ Um das Addon-Verzeichnis aktuell zu halten, solltest du in diesem Pfad ein "git Wenn du den Verzeichnispfad auf deinen Webserver kopierst, dann stelle sicher, dass du auch die .htaccess kopierst, da "Punkt"-Dateien oft versteckt sind und normalerweise nicht direkt kopiert werden. +Wenn du die Entwickler Version von Friendica verwenden möchtest kannst du auf den develop Branch im git Repository wechseln. +Dies tust du mit den folgenden Befehlen + + git checkout develop + bin/composer.phar install + cd addon + git checkout develop + +Die Entwickler Version kann nach einem fehlerhaften Commit vorübergehend Probleme haben oder gar nicht mehr funktionieren. +Sollte dir so etwas passieren, lass es uns bitte wissen, damit der Fehler behoben werden kann. + ### Erselle eine Datenbank Erstelle eine leere Datenbank und notiere alle Zugangsdaten (Adresse der Datenbank, Nutzername, Passwort, Datenbankname). @@ -87,46 +98,105 @@ Starte MySQL dann neu und es sollte klappen. ### Option A: Der manuelle Installer -Besuche deine Webseite mit deinem Browser und befolge die Anleitung. +Besuche deine Webseite mit deinem Browser und befolge die Anleitung. Bitte beachte jeden Fehler und korrigiere diese, bevor du fortfährst. Falls du einen Port für die Datenbankverbindung angeben musst, kannst du diesen in der Host-Eingabe Zeile angeben. *Wenn* die manuelle Installation aus irgendeinem Grund fehlschlägt, dann prüfe das Folgende: -* ".htconfig.php" existiert ... wenn nicht, bearbeite die „htconfig.php“ und ändere die Systemeinstellungen. Benenne sie um in „.htconfig.php". +* "config/local.ini.php" existiert ... wenn nicht, bearbeite die „config/local-sample.ini.php“ und ändere die Systemeinstellungen. Benenne sie um in „config/local.ini.php". * die Datenbank beinhaltet Daten. ... wenn nicht, importiere den Inhalt der Datei "database.sql" mit phpmyadmin oder per mysql-Kommandozeile. Besuche deine Seite an diesem Punkt wieder und registriere deinen persönlichen Account. Alle Registrierungsprobleme sollten automatisch behebbar sein. Wenn du irgendwelche **kritischen** Fehler zu diesen Zeitpunkt erhalten solltest, deutet das darauf hin, dass die Datenbank nicht korrekt installiert wurde. -Du kannst bei Bedarf die Datei .htconfig.php verschieben/umbenennen und die Datenbank leeren (als „Dropping“ bezeichnet), so dass du mit einem sauberen System neu starten kannst. +Du kannst bei Bedarf die Datei config/local.ini.php verschieben/umbenennen und die Datenbank leeren (als „Dropping“ bezeichnet), so dass du mit einem sauberen System neu starten kannst. -### Option B: Starte das manuelle Installationsscript +### Option B: Starte das automatische Installationsscript -Öffne die Datei htconfig.php im Friendica-Hauptordner mit einem Text-Editor. -Entferne die `die('...');` Zeile und bearbeite die Einstellungen so, das sie zu deinem System passen (MySQL, Sprache, Theme etc.). -Dann speichere die Datei (jedoch nicht umbenennen). +Es existieren folgende Varianten zur automatischen Installation von Friendica: +- Eine vorgefertigte Konfigurationsdatei erstellen (z.B. `prepared.ini.php`) +- Verwendung von Umgebungsvariablen (z.B. `MYSQL_HOST`) +- Verwendung von Optionen (z.B. `--dbhost `) -Gehe in den Friendica-Hauptordner und führe den Kommandozeilen Befehl aus: - - bin/console autoinstall - -Oder falls du alle optionalen Checks ausfürehn lassen möchtest, benutze diese Option: - - bin/console autoinstall -a - -*Wenn* die automatisierte Installation aus irgendeinem Grund fehlschlägt, dann prüfe das Folgende: -* Existiert die `.htconfig.php`? Falls ja, wird die automatisierte Installation nicht gestartet. -* Sind Einstellungen in der `.htconfig.php` korrekt? Falls nicht, bitte bearbeite diese Datei erneut. -* Ist die leere MySQL-Datenbank erstellt? Falls nicht, erstelle diese. +Umgebungsvariablen und Optionen können auch kombiniert werden. +Dabei ist jedoch darauf zu achten, dass etwaige Optionen immer die zugehörigen Umgebungsvariablen überschreiben. Für mehr Informationen kannst du diese Option verwenden: bin/console autoinstall -v +Falls du alle optionalen Checks ausfürehn lassen möchtest, benutze diese Option: + + bin/console autoinstall -a + +*Wenn* die automatisierte Installation aus irgendeinem Grund fehlschlägt, dann prüfe das Folgende: +* Existiert die `config/local.ini.php`? Falls ja, wird die automatisierte Installation nicht gestartet. +* Sind Einstellungen in der `config/local.ini.php` korrekt? Falls nicht, bitte bearbeite diese Datei erneut. +* Ist die leere MySQL-Datenbank erstellt? Falls nicht, erstelle diese. + +#### B.1: Konfigurationsdatei + +Für diese Variante muss ein Konfigurationsdatei bereits vor der Installation fertig definiert sein (z.B. [local-sample.ini.php](config/local-sample.ini.php). + +Gehe im Anschluss in den Friendica-Hauptordner und führe den Kommandozeilen Befehl aus: + + bin/console autoinstall -f + +#### B.2: Umgebungsvariablen + +Es existieren Zwei Arten von Umgebungsvariablen in Friendica: +- Jene, die auch im normalen Betrieb verwendet werden können (derzeit ausschließlich **Datenbank Einstellungen**) +- Jene, die nur während der Installation verwedent werden können (im normalen Betrieb werden sie ignoriert) + +Umgebungsvariablen können auch durch adäquate Optionen (z.B. `--dbhost `)übersteuert werden. + +**Datenbank Einstellungen** + +Nur wenn die Option `--savedb` gesetzt ist, werden diese Umgebungsvariablen auch in `config/local.ini.php` gespeichert! + +- `MYSQL_HOST` Der Host der MySQL/MariaDB Datenbank +- `MYSQL_PORT` Der Port der MySQL/MariaDB Datenbank +- `MYSQL_USERNAME` Der Benutzername des MySQL Datenbanklogins (MySql - Variante) +- `MYSQL_USER` Der Benutzername des MariaDB Datenbanklogins (MariaDB-Variante) +- `MYSQL_PASSWORD` Das Passwort der MySQL/MariaDB Datenbanklogins +- `MYSQL_DATABASE` Der Name der MySQL/MariaDB Datenbank + +**Friendica Einstellungen** + +Diese Umgebungsvariablen können nicht während des normalen Friendica Betriebs verwendet werden. +Sie werden stattdessen direkt in `config/local.ini.php` gespeichert. + +- `FRIENDICA_PHP_PATH` Der Pfad zur PHP-Datei +- `FRIENDICA_ADMIN_MAIL` Die Admin E-Mail Adresse dieses Friendica Knotens (wird auch für den Admin-Zugang benötigt) +- `FRIENDICA_TZ` Die Zeitzone von Friendica +- `FRIENDICA_LANG` Die Sprache von Friendica + +Gehe im Anschluss in den Friendica-Hauptordner und führe den Kommandozeilen Befehl aus: + + bin/console autoinstall [--savedb] + +#### B.3: Optionen + +Alle Optionen werden in `config/local.ini.php` gespeichert und überschreiben etwaige, zugehörige Umgebungsvariablen. + +- `-H|--dbhost ` Der Host der MySQL/MariaDB Datenbank (env `MYSQL_HOST`) +- `-p|--dbport ` Der Port der MySQL/MariaDB Datenbank (env `MYSQL_PORT`) +- `-U|--dbuser ` Der Benutzername des MySQL/MariaDB Datenbanklogins (env `MYSQL_USER` or `MYSQL_USERNAME`) +- `-P|--dbpass ` Das Passwort der MySQL/MariaDB Datenbanklogins (env `MYSQL_PASSWORD`) +- `-d|--dbdata ` Der Name der MySQL/MariaDB Datenbank (env `MYSQL_DATABASE`) +- `-b|--phppath ` Der Pfad zur PHP-Datei (env `FRIENDICA_PHP_PATH`) +- `-A|--admin ` Die Admin E-Mail Adresse dieses Friendica Knotens (env `FRIENDICA_ADMIN_MAIL`) +- `-T|--tz ` Die Zeitzone von Friendica (env `FRIENDICA_TZ`) +- `-L|--land ` Die Sprache von Friendica (env `FRIENDICA_LANG`) + +Gehe in den Friendica-Hauptordner und führe den Kommandozeilen Befehl aus: + + bin/console autoinstall [options] + ### Einen Worker einrichten -Erstelle einen Cron job oder einen regelmäßigen Task, um den Poller alle 5-10 Minuten im Hintergrund ablaufen zu lassen. +Erstelle einen Cron job oder einen regelmäßigen Task, um den Poller alle 5-10 Minuten im Hintergrund ablaufen zu lassen. Beispiel: cd /base/directory; /path/to/php bin/worker.php @@ -149,5 +219,5 @@ Es werden schlimme Dinge geschehen. Sei es nun ein Hardwareversagen oder eine kaputte Datenbank. Deshalb solltest du dir, nachdem die Installation deines Friendica Knotens abgeschlossen ist, einen Backup Plan erstellen. -Die wichtigste Datei ist die `.htconfig.php` im Stammverzeichnis deiner Friendica Installation. +Die wichtigste Datei ist die `config/local.ini.php` im Stammverzeichnis deiner Friendica Installation. Und da alle Daten in der Datenbank gespeichert werden, solltest du einen nicht all zu alten Dump der Friendica Datenbank zur Hand haben, solltest du deinen Knoten wieder herstellen müssen. diff --git a/doc/de/Installing-Connectors.md b/doc/de/Installing-Connectors.md index f9fe5b0f7..3fa46ab5a 100644 --- a/doc/de/Installing-Connectors.md +++ b/doc/de/Installing-Connectors.md @@ -1,25 +1,25 @@ -Konnektoren installieren (Twitter/GNU Social) +Konnektoren installieren (Twitter/GNU Social) ================================================== * [Zur Startseite der Hilfe](help) Friendica nutzt Erweiterung, um die Verbindung zu anderen Netzwerken wie Twitter oder App.net zu gewährleisten. -Es gibt außerdem ein Erweiterung, um über einen bestehenden GNU Social-Account diesen Service zu nutzen. -Du brauchst dieses Erweiterung aber nicht, um mit GNU Social-Mitgliedern von Friendica aus zu kommunizieren - es sei denn, du wünschst es, über einen existierenden Account einen Beitrag zu schreiben. +Es gibt außerdem ein Erweiterung, um über einen bestehenden GNU Social-Account diesen Service zu nutzen. +Du brauchst dieses Erweiterung aber nicht, um mit GNU Social-Mitgliedern von Friendica aus zu kommunizieren - es sei denn, du wünschst es, über einen existierenden Account einen Beitrag zu schreiben. -Alle drei Erweiterung benötigen einen Account im gewünschten Netzwerk. +Alle drei Erweiterung benötigen einen Account im gewünschten Netzwerk. Zusätzlich musst du (bzw. der Administrator der Seite) einen API-Schlüssel holen, um einen authentifizierten Zugriff zu deinem Friendica-Server herstellen zu lassen. **Seitenkonfiguration** -Erweiterung müssen vom Administrator installiert werden, bevor sie genutzt werden können. +Erweiterung müssen vom Administrator installiert werden, bevor sie genutzt werden können. Dieses kann über das Administrationsmenü erstellt werden. -Jeder der Konnektoren benötigt zudem einen API-Schlüssel vom Service, der verbunden werden soll. -Einige Erweiterung erlaube es, diese Informationen auf den Administrationsseiten einzustellen, wohingegen andere eine direkte Bearbeitung der Konfigurationsdatei ".htconfig.php" erfordern. -Der Weg, um diese Schlüssel zu erhalten, variiert stark, jedoch brauchen fast alle einen bestehenden Account im gewünschten Service. +Jeder der Konnektoren benötigt zudem einen API-Schlüssel vom Service, der verbunden werden soll. +Einige Erweiterung erlaube es, diese Informationen auf den Administrationsseiten einzustellen, wohingegen andere eine direkte Bearbeitung der Konfigurationsdatei "config/local.ini.php" erfordern. +Der Weg, um diese Schlüssel zu erhalten, variiert stark, jedoch brauchen fast alle einen bestehenden Account im gewünschten Service. Einmal installiert, können diese Schlüssel von allen Seitennutzern genutzt werden. Im Folgenden findest du die Einstellungen für die verschiedenen Services (viele dieser Informationen kommen direkt aus den Quelldateien der Erweiterung): @@ -37,11 +37,12 @@ Um dieses Erweiterung zu nutzen, benötigst du einen OAuth Consumer-Schlüsselpa Registriere deine Friendica-Seite als "Client"-Anwendung mit "Read&Write"-Zugriff. Wir benötigen "Twitter als Login" nicht. Sobald du deine Anwendung installiert hast, erhältst du das Schlüsselpaar für deine Seite. -Trage dieses Schlüsselpaar in deine globale ".htconfig.php"-Datei ein. +Trage dieses Schlüsselpaar in deine globale "config/local.ini.php"-Datei ein. ``` -$a->config['twitter']['consumerkey'] = 'your consumer_key here'; -$a->config['twitter']['consumersecret'] = 'your consumer_secret here'; +[twitter] +consumerkey = your consumer_key here +consumersecret = your consumer_secret here ``` Anschließend kann der Nutzer deiner Seite die Twitter-Einstellungen selbst eintragen: "Einstellungen -> Connector Einstellungen". @@ -63,10 +64,10 @@ Wenn das Addon aktiv ist, muss der Nutzer die folgenden Einstellungen vornehmen, Um das OAuth-Schlüsselpaar zu erhalten, muss der Nutzer -(a) seinen Friendica-Admin fragen, ob bereits ein Schlüsselpaar existiert oder +(a) seinen Friendica-Admin fragen, ob bereits ein Schlüsselpaar existiert oder (b) einen Friendica-Server als Anwendung auf dem GNU Social-Server anmelden. -Dies kann über Einstellungen --> Connections --> "Register an OAuth client application" -> "Register a new application" auf dem GNU Social-Server durchgeführt werden. +Dies kann über Einstellungen --> Connections --> "Register an OAuth client application" -> "Register a new application" auf dem GNU Social-Server durchgeführt werden. Während der Registrierung des OAuth-Clients ist Folgendes zu beachten: @@ -76,9 +77,9 @@ Während der Registrierung des OAuth-Clients ist Folgendes zu beachten: * stelle Lese- und Schreibrechte ein * die Quell-URL sollte die URL deines Friendica-Servers sein -Sobald die benötigten Daten gespeichert sind, musst du deinen Friendica-Account mit GNU Social verbinden. -Das kannst du über Einstellungen --> Connector-Einstellungen durchführen. -Folge dem "Einloggen mit GNU Social"-Button, erlaube den Zugriff und kopiere den Sicherheitscode in die entsprechende Box. +Sobald die benötigten Daten gespeichert sind, musst du deinen Friendica-Account mit GNU Social verbinden. +Das kannst du über Einstellungen --> Connector-Einstellungen durchführen. +Folge dem "Einloggen mit GNU Social"-Button, erlaube den Zugriff und kopiere den Sicherheitscode in die entsprechende Box. Friendica wird dann versuchen, die abschließende OAuth-Einstellungen über die API zu beziehen. Wenn es geklappt hat, kannst du in den Einstellungen festlegen, ob deine öffentlichen Nachrichten automatisch in deinem GNU Social-Account erscheinen soll (achte hierbei auf das kleine Schloss-Symbol im Status-Editor) diff --git a/doc/de/Move-Account.md b/doc/de/Move-Account.md index 1b0c7eb8f..79e7eae91 100644 --- a/doc/de/Move-Account.md +++ b/doc/de/Move-Account.md @@ -1,34 +1,38 @@ -Accounts Umziehen -================= - -* [Zur Startseite der Hilfe](help) - - -! **Dies ist ein experimentelles Feature** - -**Wie man einen Account von einem Server zu einem anderen umzieht.** - -Unter "Einstellungen" -> "[Persönliche Daten exportieren](uexport)" aufrufen. -"Account exportieren" anklicken und die Daten speichern. -Diese Datei enthält Details über dich, deine Kontakte, Gruppen und persönliche Einstellungen. -Außerdem enthält sie deinen geheimen Schlüssel mit dem du dich deinen Kontakten gegenüber ausweist. - -**Speichere diese Datei an einem sicheren Ort**! - -Rufe nun dem neuen Server die Seite *http://newserver.com/uimport* auf (es gibt derzeit keinen direkten Link auf diese Seite). - -Lege auf dem neuen Server auf keinen Fall einen gleichnamigen Account an! -uimport muss anstelle des Registrierens verwendet werden. - -Wähle die gesicherte Account Datei aus und klicke "Importieren". - -Friendica wird nun deinen Account auf dem neuen Server wiederherstellen, mit all deinen Friendica Kontakten und Gruppen. -An deine Friendica Kontakte wird außerdem eine Nachricht gesendet um sie über deine neue Adresse zu informieren. -Wenn deine Kontakte ihren Account auf einem aktuellen Server haben werden deine Kontaktdetails automatisch aktualisiert. - -Neuere Diaspora Server unterstützen ebenfalls eine Umzugsbenachrichtigung. - -Kontakte auf GNU Social werden archiviert, da wir ihnen keine Information über deinen Umzug zukommen lassen können. -Du solltest sie persönlich anschreiben deinen Eintrag aus ihren Kontaktlisten zu entfernen und dich neu hinzuzufügen, anschließend solltest du da gleiche mit ihren Accounts tun. - -Nach dem Umzug wird dein Account auf dem alten Server nicht mehr zuverlässig funktionieren und sollte deshalb gelöscht werden. +Accounts Umziehen +================= + +* [Zur Startseite der Hilfe](help) + + +! **Dies ist ein experimentelles Feature** + +**Wie man einen Account von einem Server zu einem anderen umzieht.** + +Unter "Einstellungen" -> "[Persönliche Daten exportieren](uexport)" aufrufen. +"Account exportieren" anklicken und die Daten speichern. +Diese Datei enthält Details über dich, deine Kontakte, Gruppen und persönliche Einstellungen. +Außerdem enthält sie deinen geheimen Schlüssel mit dem du dich deinen Kontakten gegenüber ausweist. + +**Speichere diese Datei an einem sicheren Ort**! + +Rufe nun dem neuen Server die Seite *http://newserver.com/uimport* auf (es gibt derzeit keinen direkten Link auf diese Seite). + +Bitte beachte, dass dies nur auf Servern möglich ist, an denen man sich offen anmelden kann. +Bei Servern, bei denen der Administrator Accounts freigeben muss, ist das Hochladen nicht möglich. +Hier kann dies nur der Administrator selber durchführen. + +Lege auf dem neuen Server auf keinen Fall einen gleichnamigen Account an! +uimport muss anstelle des Registrierens verwendet werden. + +Wähle die gesicherte Account Datei aus und klicke "Importieren". + +Friendica wird nun deinen Account auf dem neuen Server wiederherstellen, mit all deinen Friendica Kontakten und Gruppen. +An deine Friendica Kontakte wird außerdem eine Nachricht gesendet um sie über deine neue Adresse zu informieren. +Wenn deine Kontakte ihren Account auf einem aktuellen Server haben werden deine Kontaktdetails automatisch aktualisiert. + +Neuere Diaspora Server unterstützen ebenfalls eine Umzugsbenachrichtigung. + +Kontakte auf GNU Social werden archiviert, da wir ihnen keine Information über deinen Umzug zukommen lassen können. +Du solltest sie persönlich anschreiben deinen Eintrag aus ihren Kontaktlisten zu entfernen und dich neu hinzuzufügen, anschließend solltest du da gleiche mit ihren Accounts tun. + +Nach dem Umzug wird dein Account auf dem alten Server nicht mehr zuverlässig funktionieren und sollte deshalb gelöscht werden. diff --git a/doc/de/Settings.md b/doc/de/Settings.md index 8a654e32c..4d84f873f 100644 --- a/doc/de/Settings.md +++ b/doc/de/Settings.md @@ -8,8 +8,8 @@ Auf der Startseite des Admin Panels werden die Informationen zu der Instanz zusa Die erste Zahl gibt die Anzahl von Nachrichten an, die nicht zugestellt werden konnten. Die Zustellung wird zu einem späteren Zeitpunkt noch einmal versucht. Unter dem Punkt "Warteschlange Inspizieren" kannst du einen schnellen Blick auf die zweite Warteschlange werfen. -Die zweite Zahl steht für die Anzahl der Aufgaben, die die Worker noch vor sich haben. -Die Worker arbeiten Hintergrundprozesse ab. +Die zweite Zahl steht für die Anzahl der Aufgaben, die die Worker noch vor sich haben. +Die Worker arbeiten Hintergrundprozesse ab. Die Aufgaben der Worker sind priorisiert und werden anhand dieser Prioritäten abgearbeitet. Desweiteren findest du eine Übersicht über die Accounts auf dem Friendica Knoten, die unter dem Punkt "Nutzer" moderiert werden können. @@ -31,7 +31,7 @@ Da die meisten Konfigurationsoptionen einen Hilfstext im Admin Panel haben, kann #### Banner/Logo -Hiermit legst du das Banner der Seite fest. Standardmäßig ist das Friendica-Logo und der Name festgelegt. +Hiermit legst du das Banner der Seite fest. Standardmäßig ist das Friendica-Logo und der Name festgelegt. Du kannst hierfür HTML/CSS nutzen, um den Inhalt zu gestalten und/oder die Position zu ändern, wenn es nicht bereits voreingestellt ist. #### Systensprache @@ -63,33 +63,33 @@ Dabei kannst du zwischen den folgenden Optionen wählen: * **Bedarf der Zustimmung**: Jeder kann ein Nutzerkonto anlegen. Dieses muss allerdings durch den Admin freigeschaltet werden, bevor es verwendet werden kann. * **Geschlossen**: Es können keine weiteren Nutzerkonten angelegt werden. -##### Einladungen +##### Einladungen Zusätzlich zu den oben genannten Möglichkeiten, kann die Registrierung eines neuen Nutzerkontos an eine Einladung durch einen bestehenden Nutzer gekoppelt werden. -Hierzu muss in der [.htconfig.php](/help/htconfig) Datei die Option `invitation_only` aktiviert und als Registrierungsmethode entweder *Offen* oder *Bedarf der Zustimmung* gewählt werden. +Hierzu muss in der [config/local.ini.php](/help/Config) Datei die Option `invitation_only` aktiviert und als Registrierungsmethode entweder *Offen* oder *Bedarf der Zustimmung* gewählt werden. #### Namen auf Vollständigkeit überprüfen -Es kann vorkommen, dass viele Spammer versuchen, sich auf deiner Seite zu registrieren. -In Testphasen haben wir festgestellt, dass diese automatischen Registrierungen das Feld "Vollständiger Name" oft nur mit Namen ausfüllen, die kein Leerzeichen beinhalten. -Wenn du Leuten erlauben willst, sich nur mit einem Namen anzumelden, dann setze die Einstellung auf "true". +Es kann vorkommen, dass viele Spammer versuchen, sich auf deiner Seite zu registrieren. +In Testphasen haben wir festgestellt, dass diese automatischen Registrierungen das Feld "Vollständiger Name" oft nur mit Namen ausfüllen, die kein Leerzeichen beinhalten. +Wenn du Leuten erlauben willst, sich nur mit einem Namen anzumelden, dann setze die Einstellung auf "true". Die Standardeinstellung ist auf "false" gesetzt. - + #### OpenID Unterstützung -Standardmäßig wird OpenID für die Registrierung und für Logins genutzt. +Standardmäßig wird OpenID für die Registrierung und für Logins genutzt. Wenn du nicht willst, dass OpenID-Strukturen für dein System übernommen werden, dann setze "no_openid" auf "true". Standardmäßig ist hier "false" gesetzt. #### Unterbinde Mehrfachregistrierung -Um mehrfache Seiten zu erstellen, muss sich eine Person mehrfach registrieren können. -Deine Seiteneinstellung kann Registrierungen komplett blockieren oder an Bedingungen knüpfen. -Standardmäßig können eingeloggte Nutzer weitere Accounts für die Seitenerstellung registrieren. -Hier ist weiterhin eine Bestätigung notwendig, wenn "REGISTER_APPROVE" ausgewählt ist. -Wenn du die Erstellung weiterer Accounts blockieren willst, dann setze die Einstellung "block_extended_register" auf "true". +Um mehrfache Seiten zu erstellen, muss sich eine Person mehrfach registrieren können. +Deine Seiteneinstellung kann Registrierungen komplett blockieren oder an Bedingungen knüpfen. +Standardmäßig können eingeloggte Nutzer weitere Accounts für die Seitenerstellung registrieren. +Hier ist weiterhin eine Bestätigung notwendig, wenn "REGISTER_APPROVE" ausgewählt ist. +Wenn du die Erstellung weiterer Accounts blockieren willst, dann setze die Einstellung "block_extended_register" auf "true". Standardmäßig ist hier "false" gesetzt. - + ### Datei hochladen #### Maximale Bildgröße @@ -100,26 +100,26 @@ Maximale Bild-Dateigröße in Byte. Standardmäßig ist 0 gesetzt, was bedeutet, #### URL des weltweiten Verzeichnisses -Mit diesem Befehl wird die URL eingestellt, die zum Update des globalen Verzeichnisses genutzt wird. -Dieser Befehl ist in der Standardkonfiguration enthalten. -Der nicht dokumentierte Teil dieser Einstellung ist, dass das globale Verzeichnis gar nicht verfügbar ist, wenn diese Einstellung nicht gesetzt wird. +Mit diesem Befehl wird die URL eingestellt, die zum Update des globalen Verzeichnisses genutzt wird. +Dieser Befehl ist in der Standardkonfiguration enthalten. +Der nicht dokumentierte Teil dieser Einstellung ist, dass das globale Verzeichnis gar nicht verfügbar ist, wenn diese Einstellung nicht gesetzt wird. Dies erlaubt eine private Kommunikation, die komplett vom globalen Verzeichnis isoliert ist. #### Erzwinge Veröffentlichung -Standardmäßig können Nutzer selbst auswählen, ob ihr Profil im Seitenverzeichnis erscheint. -Diese Einstellung zwingt alle Nutzer dazu, im Verzeichnis zu erscheinen. +Standardmäßig können Nutzer selbst auswählen, ob ihr Profil im Seitenverzeichnis erscheint. +Diese Einstellung zwingt alle Nutzer dazu, im Verzeichnis zu erscheinen. Diese Einstellung kann vom Nutzer nicht deaktiviert werden. Die Standardeinstellung steht auf "false". #### Öffentlichen Zugriff blockieren -Aktiviere diese Einstellung um den öffentlichen Zugriff auf alle Seiten zu sperren, solange man nicht eingeloggt ist. -Das blockiert die Ansicht von Profilen, Freunden, Fotos, vom Verzeichnis und den Suchseiten. -Ein Nebeneffekt ist, dass Einträge dieser Seite nicht im globalen Verzeichnis erscheinen. -Wir empfehlen, speziell diese Einstellung auszuschalten (die Einstellung ist an anderer Stelle auf dieser Seite erklärt). -Beachte: das ist speziell für Seiten, die beabsichtigen, von anderen Friendica-Netzwerken abgeschottet zu sein. -Unautorisierte Personen haben ebenfalls nicht die Möglichkeit, Freundschaftsanfragen von Seitennutzern zu beantworten. -Die Standardeinstellung ist deaktiviert. +Aktiviere diese Einstellung um den öffentlichen Zugriff auf alle Seiten zu sperren, solange man nicht eingeloggt ist. +Das blockiert die Ansicht von Profilen, Freunden, Fotos, vom Verzeichnis und den Suchseiten. +Ein Nebeneffekt ist, dass Einträge dieser Seite nicht im globalen Verzeichnis erscheinen. +Wir empfehlen, speziell diese Einstellung auszuschalten (die Einstellung ist an anderer Stelle auf dieser Seite erklärt). +Beachte: das ist speziell für Seiten, die beabsichtigen, von anderen Friendica-Netzwerken abgeschottet zu sein. +Unautorisierte Personen haben ebenfalls nicht die Möglichkeit, Freundschaftsanfragen von Seitennutzern zu beantworten. +Die Standardeinstellung ist deaktiviert. Verfügbar in Version 2.2 und höher. #### Für Besucher verfügbare Gemeinschaftsseiten @@ -133,15 +133,15 @@ Angemeldete Nutzer des Knotens können grundsätzlich beide Seiten verwenden. #### Erlaubte Domains für Kontakte -Kommagetrennte Liste von Domains, welche eine Freundschaft mit dieser Seite eingehen dürfen. +Kommagetrennte Liste von Domains, welche eine Freundschaft mit dieser Seite eingehen dürfen. Wildcards werden akzeptiert (Wildcard-Unterstützung unter Windows benötigt PHP5.3) Standardmäßig sind alle gültigen Domains erlaubt. Mit dieser Option kann man einfach geschlossene Netzwerke, z.B. im schulischen Bereich aufbauen, aus denen nicht mit dem Rest des Netzwerks kommuniziert werden soll. #### Erlaubte Domains für E-Mails -Kommagetrennte Liste von Domains, welche bei der Registrierung als Part der Email-Adresse erlaubt sind. -Das grenzt Leute aus, die nicht Teil der Gruppe oder Organisation sind. +Kommagetrennte Liste von Domains, welche bei der Registrierung als Part der Email-Adresse erlaubt sind. +Das grenzt Leute aus, die nicht Teil der Gruppe oder Organisation sind. Wildcards werden akzeptiert (Wildcard-Unterstützung unter Windows benötigt PHP5.3) Standardmäßig sind alle gültigen Email-Adressen erlaubt. #### Nutzern erlauben das remote_self Flag zu setzen @@ -156,6 +156,14 @@ Außerdem könnte es durch Nutzer als Spam Verteiler missbraucht werden. Als Administrator der Friendica-Instanz kannst du diese Einstellungen ansonsten nur direkt in der Datenbank vornehmen. Bevor du das tust solltest du sicherstellen, dass du ein Backup der Datenbank hast und genau weißt was die Änderungen an der Datenbank bewirken, die du vornehmen willst. +#### Explizite Inhalte + +Wenn Sie einen Knoten mit explizitem, nicht jugendfreien Inhalt betreiben, können Sie dies mit dieser Option ankündigen. +Ist diese Option aktiviert, wird ein Informationsflag in den veröffentlichten Informationen zu Ihrem Knoten gesetzt. +(Sollte *Server Informationen veröffentlichen* aktiviert sein.) + +Zusätzlich wird auf der Registrierungsseite für neue Benutzer ein Hinweis angezeigt. + ### Erweitert #### Proxy Einstellungen @@ -164,23 +172,23 @@ Wenn deine Seite eine Proxy-Einstellung nutzt, musst du diese Einstellungen vorn #### Netzwerk Wartezeit -Legt fest, wie lange das Netzwerk warten soll, bevor ein Timeout eintritt. +Legt fest, wie lange das Netzwerk warten soll, bevor ein Timeout eintritt. Der Wert wird in Sekunden angegeben. Standardmäßig ist 60 eingestellt; 0 steht für "unbegrenzt" (nicht empfohlen). #### UTF-8 Reguläre Ausdrücke -Während der Registrierung werden die Namen daraufhin geprüft, ob sie reguläre UTF-8-Ausdrücke nutzen. -Hierfür wird PHP benötigt, um mit einer speziellen Einstellung kompiliert zu werden, die UTF-8-Ausdrücke benutzt. +Während der Registrierung werden die Namen daraufhin geprüft, ob sie reguläre UTF-8-Ausdrücke nutzen. +Hierfür wird PHP benötigt, um mit einer speziellen Einstellung kompiliert zu werden, die UTF-8-Ausdrücke benutzt. Wenn du absolut keine Möglichkeit hast, Accounts zu registrieren, setze diesen Wert auf ja. #### SSL Überprüfen -Standardmäßig erlaubt Friendica SSL-Kommunikation von Seiten, die "selbst unterzeichnete" SSL-Zertifikate nutzen. -Um eine weitreichende Kompatibilität mit anderen Netzwerken und Browsern zu gewährleisten, empfehlen wir, selbst unterzeichnete Zertifikate **nicht** zu nutzen. -Aber wir halten dich nicht davon ab, solche zu nutzen. SSL verschlüsselt alle Daten zwischen den Webseiten (und für deinen Browser), was dir eine komplett verschlüsselte Kommunikation erlaubt. -Auch schützt es deine Login-Daten vor Datendiebstahl. Selbst unterzeichnete Zertifikate können kostenlos erstellt werden. -Diese Zertifikate können allerdings Opfer eines sogenannten ["man-in-the-middle"-Angriffs](http://de.wikipedia.org/wiki/Man-in-the-middle-Angriff) werden, und sind daher weniger bevorzugt. -Wenn du es wünscht, kannst du eine strikte Zertifikatabfrage einstellen. +Standardmäßig erlaubt Friendica SSL-Kommunikation von Seiten, die "selbst unterzeichnete" SSL-Zertifikate nutzen. +Um eine weitreichende Kompatibilität mit anderen Netzwerken und Browsern zu gewährleisten, empfehlen wir, selbst unterzeichnete Zertifikate **nicht** zu nutzen. +Aber wir halten dich nicht davon ab, solche zu nutzen. SSL verschlüsselt alle Daten zwischen den Webseiten (und für deinen Browser), was dir eine komplett verschlüsselte Kommunikation erlaubt. +Auch schützt es deine Login-Daten vor Datendiebstahl. Selbst unterzeichnete Zertifikate können kostenlos erstellt werden. +Diese Zertifikate können allerdings Opfer eines sogenannten ["man-in-the-middle"-Angriffs](http://de.wikipedia.org/wiki/Man-in-the-middle-Angriff) werden, und sind daher weniger bevorzugt. +Wenn du es wünscht, kannst du eine strikte Zertifikatabfrage einstellen. Das führt dazu, dass du keinerlei Verbindung zu einer selbst unterzeichneten SSL-Seite erstellen kannst ### Automatisch ein Kontaktverzeichnis erstellen @@ -305,7 +313,7 @@ Du solltest deshalb einen Dienst zur [log rotation](https://en.wikipedia.org/wik **Bekannte Probleme**: Der Dateiname `friendica.log` kann bei speziellen Server Konfigurationen zu Problemen führen (siehe [issue 2209](https://github.com/friendica/friendica/issues/2209)). Normalerweise werden Fehler- und Warnmeldungen von PHP unterdrückt. -Wenn du sie aktivieren willst, musst du folgendes in der `.htconfig.php` Datei eintragen um die Meldungen in die Datei `php.out` zu speichern +Wenn du sie aktivieren willst, musst du folgendes in der `config/local.ini.php` Datei eintragen um die Meldungen in die Datei `php.out` zu speichern error_reporting(E_ERROR | E_WARNING | E_PARSE ); ini_set('error_log','php.out'); @@ -344,6 +352,14 @@ Mit den folgenden Einstellungen kannst du die Zugriffsdaten für den Datenbank S $db_pass = 'db_password'; $db_data = 'database_name'; +Sollten alle der folgenden Environment-Variablen gesetzt sein, wird Friendica diese anstatt der vorher konfigurierten Werte nutzen. + + MYSQL_HOST + MYSQL_PORT + MYSQL_USERNAME + MYSQL_PASSWORD + MYSQL_DATABASE + ## Administratoren Du kannst einen, oder mehrere Accounts, zu Administratoren machen. @@ -351,14 +367,16 @@ Normalerweise trifft dies auf den ersten Account zu, der nach der Installation a Die Liste der E-Mail Adressen kann aber einfach erweitert werden. Mit keiner der angegebenen E-Mail Adressen können weitere Accounts registriert werden. - $a->config['admin_email'] = 'you@example.com, buddy@example.com'; + [config] + admin_email = you@example.com, buddy@example.com ## PHP Pfad Einige Prozesse von Friendica laufen im Hintergrund. Für diese Prozesse muss der Pfad zu der PHP Version gesetzt sein, die verwendet werden soll. - $a->config['php_path'] = '/pfad/zur/php-version'; + [config] + php_path = {{$phpath}} ## Unterverzeichnis Konfiguration @@ -366,9 +384,10 @@ Man kann Friendica in ein Unterverzeichnis des Webservers installieren. Wir raten allerdings dringen davon ab, da es die Interoperabilität mit anderen Netzwerken (z.B. Diaspora, GNU Social, Hubzilla) verhindert. Mal angenommen, du hast ein Unterverzeichnis tests und willst Friendica in ein weiteres Unterverzeichnis installieren, dann lautet die Konfiguration hierfür: - $a->path = 'tests/friendica'; + [system] + urlpath = tests/friendica ## Weitere Ausnahmen -Es gibt noch einige experimentelle Einstellungen, die nur in der ``.htconfig.php`` Datei konfiguriert werden können. -Im [Konfigurationswerte, die nur in der .htconfig.php gesetzt werden können (EN)](help/htconfig) Artikel kannst du mehr darüber erfahren. +Es gibt noch einige experimentelle Einstellungen, die nur in der ``config/local.ini.php`` Datei konfiguriert werden können. +Im [Konfigurationswerte, die nur in der config/local.ini.php gesetzt werden können (EN)](help/Config) Artikel kannst du mehr darüber erfahren. diff --git a/doc/htconfig.md b/doc/htconfig.md deleted file mode 100644 index 6598fa142..000000000 --- a/doc/htconfig.md +++ /dev/null @@ -1,126 +0,0 @@ -Config values that can only be set in .htconfig.php -=================================================== - -* [Home](help) - -There are some config values that haven't found their way into the administration page. -This has several reasons. -Maybe they are part of a current development that isn't considered stable and will be added later in the administration page when it is considered safe. -Or it triggers something that isn't expected to be of public interest. -Or it is for testing purposes only. - -**Attention:** Please be warned that you shouldn't use one of these values without the knowledge what it could trigger. -Especially don't do that with undocumented values. - -The header of the section describes the category, the value is the parameter. -Example: To set the automatic database cleanup process add this line to your .htconfig.php: - - $a->config['system']['always_show_preview'] = true; - -## jabber ## -* **debug** (Boolean) - Enable debug level for the jabber account synchronisation. -* **lockpath** - Must be writable by the ejabberd process. if set then it will prevent the running of multiple processes. - -## system ## - -* **allowed_link_protocols** (Array) - Allowed protocols in links URLs, add at your own risk. http is always allowed. -* **always_show_preview** (Boolean) - Only show small preview picures. Default value is false. -* **archival_days** (Integer) - Number of days that we try to deliver content before we archive a contact. Defaults to 32. -* **auth_cookie_lifetime** (Integer) - Number of days that should pass without any activity before a user who chose "Remember me" when logging in is considered logged out. Defaults to 7. -* **block_local_dir** (Boolean) - Blocks the access to the directory of the local users. -* **config_adapter** (jit|preload) - Allow to switch the configuration adapter to improve performances at the cost of memory consumption. Default value is "jit" -* **curl_range_bytes** - Maximum number of bytes that should be fetched. Default is 0, which mean "no limit". -* **db_log** - Name of a logfile to log slow database queries -* **db_loglimit** - If a database call lasts longer than this value it is logged -* **db_log_index** - Name of a logfile to log queries with bad indexes -* **db_log_index_watch** - Watchlist of indexes to watch -* **db_loglimit_index** - Number of index rows needed to be logged for indexes on the watchlist -* **db_loglimit_index_high** - Number of index rows to be logged anyway (for any index) -* **db_log_index_blacklist** - Blacklist of indexes that shouldn't be watched -* **diaspora_test** (Boolean) - For development only. Disables the message transfer. -* **disable_email_validation** (Boolean) - Disables the check if a mail address is in a valid format and can be resolved via DNS. -* **disable_url_validation** (Boolean) - Disables the DNS lookup of an URL. -* **disable_password_exposed** (Boolean) - Disable the exposition check against the remote haveibeenpwned API on password change. Default value is false. -* **dlogfile - location of the developer log file -* **dlogip - restricts develop log writes to requests originating from this IP address -* **frontend_worker_timeout** - Value in minutes after we think that a frontend task was killed by the webserver. Default value is 10. -* **hsts** (Boolean) - Enables the sending of HTTP Strict Transport Security headers -* **ignore_cache** (Boolean) - For development only. Disables the item cache. -* **instances_social_key** - Key to the API of https://instances.social which retrieves data about mastodon servers. See https://instances.social/api/token to get an API key. -* **ipv4_resolve** (Boolean) - Resolve IPV4 addresses only. Don't resolve to IPV6. Default value is false. -* **invitation_only** (Boolean) - If set true registration is only possible after a current member of the node has send an invitation. Default is false. -* **like_no_comment** (Boolean) - Don't update the "commented" value of an item when it is liked. -* **local_block** (Boolean) - Used in conjunction with "block_public". -* **local_search** (Boolean) - Blocks search for users who are not logged in to prevent crawlers from blocking your system. -* **local_tags** (Boolean) - If activated, all hashtags will point to the local server. -* **max_connections** - The maximum number of database connections which can be in use before the worker process is deferred to it's next interval. When the system can't detect the maximum numbers of connection then this value can be used. -* **max_connections_level** - The maximum level of connections that are allowed to let the worker start. It is a percentage value. Default value is 75. -* **max_contact_queue** - Default value is 500. -* **max_batch_queue** - Default value is 1000. -* **max_processes_backend** - Maximum number of concurrent database processes for background tasks. Default value is 5. -* **max_processes_frontend** - Maximum number of concurrent database processes for foreground tasks. Default value is 20. -* **min_poll_interval** - minimal distance in minutes between two polls for a contact. Default is 1. Reasonable values are between 1 and 59. -* **session_handler** (database|cache|native) - Whether to use Cache to store session data or to use PHP native session storage. Default value is `database`. -* **cache_driver** (database|memcache|memcached) - Whether to use Memcache or Memcached to store temporary cache. Default value is `database`. -* **memcache_host** - Host name of the memcache daemon. Default is '127.0.0.1'. -* **memcache_port** - Port number of the memcache daemon. Default is 11211. -* **memcached_hosts** - Array of Memcached servers info `[host, port(, weight)]`. Default value is `[['127.0.0.1', 11211]]`. -* **no_count** (Boolean) - Don't do count calculations (currently only when showing albums) -* **no_oembed** (Boolean) - Don't use OEmbed to fetch more information about a link. -* **no_smilies** (Boolean) - Don't show smilies. -* **no_view_full_size** (Boolean) - Don't add the link "View full size" under a resized image. -* **optimize_items** (Boolean) - Triggers an SQL command to optimize the item table before expiring items. -* **ostatus_poll_timeframe** - Defines how old an item can be to try to complete the conversation with it. -* **paranoia** (Boolean) - Log out users if their IP address changed. -* **permit_crawling** (Boolean) - Restricts the search for not logged in users to one search per minute. -* **queue_no_dead_check** (Boolean) - Ignore if the target contact or server seems to be dead during queue delivery. -* **worker_debug** (Boolean) - If enabled, it prints out the number of running processes split by priority. -* **worker_fetch_limit** - Number of worker tasks that are fetched in a single query. Default is 1. -* **profiler** (Boolean) - Enable internal timings to help optimize code. Needed for "rendertime" addon. Default is false. -* **free_crawls** - Number of "free" searches when "permit_crawling" is activated (Default value is 10) -* **crawl_permit_period** - Period in seconds between allowed searches when the number of free searches is reached and "permit_crawling" is activated (Default value is 60) -* **png_quality** - Default value is 8. -* **proc_windows** (Boolean) - Should be enabled if Friendica is running under Windows. -* **proxy_cache_time** - Time after which the cache is cleared. Default value is one day. -* **pushpoll_frequency** - -* **qsearch_limit** - Default value is 100. -* **remove_multiplicated_lines** (Boolean) - If enabled, multiple linefeeds in items are stripped to a single one. -* **show_unsupported_addons** (Boolean) - Show all addons including the unsupported ones. -* **show_unsupported_themes** (Boolean) - Show all themes including the unsupported ones. -* **show_global_community_hint** (Boolean) - When the global community page is enabled, use this option to display a hint above the stream, that this is a collection of all public top-level postings that arrive on your node. -* **throttle_limit_day** - Maximum number of posts that a user can send per day with the API. -* **throttle_limit_week** - Maximum number of posts that a user can send per week with the API. -* **throttle_limit_month** - Maximum number of posts that a user can send per month with the API. -* **wall-to-wall_share** (Boolean) - Displays forwarded posts like "wall-to-wall" posts. -* **worker_cooldown** - Cooldown time after each worker function call. Default value is 0 seconds. -* **xrd_timeout** - Timeout for fetching the XRD links. Default value is 20 seconds. - -## experimental ## - -* **exp_themes** (Boolean) - Show experimental themes as well. - -## theme ## - -* **hide_eventlist** (Boolean) - Don't show the birthdays and events on the profile and network page - -# Administrator Options # - -Enabling the admin panel for an account, and thus making the account holder admin of the node, is done by setting the variable - - $a->config['admin_email'] = "someone@example.com"; - -Where you have to match the email address used for the account with the one you enter to the .htconfig file. -If more then one account should be able to access the admin panel, seperate the email addresses with a comma. - - $a->config['admin_email'] = "someone@example.com,someonelese@example.com"; - -If you want to have a more personalized closing line for the notification emails you can set a variable for the admin_name. - - $a->config['admin_name'] = "Marvin"; - -## Database Settings - -The configuration variables db_host, db_user, db_pass and db_data are holding your credentials for the database connection. -If you need to specify a port to access the database, you can do so by appending ":portnumber" to the db_host variable. - - $db_host = 'your.mysqlhost.com:123456'; diff --git a/doc/img/acl_win.png b/doc/img/acl_win.png index 00ad76699..559493d44 100644 Binary files a/doc/img/acl_win.png and b/doc/img/acl_win.png differ diff --git a/doc/img/camera.png b/doc/img/camera.png index 1f8a92c34..89acdbde7 100644 Binary files a/doc/img/camera.png and b/doc/img/camera.png differ diff --git a/doc/img/darkbubble.png b/doc/img/darkbubble.png index 1ca0ba5fb..08c7a93ee 100644 Binary files a/doc/img/darkbubble.png and b/doc/img/darkbubble.png differ diff --git a/doc/img/darkzero.png b/doc/img/darkzero.png index 7c6aa177a..00dc3ee4f 100644 Binary files a/doc/img/darkzero.png and b/doc/img/darkzero.png differ diff --git a/doc/img/diabook.png b/doc/img/diabook.png index 5c55baaaf..1a1f8d915 100644 Binary files a/doc/img/diabook.png and b/doc/img/diabook.png differ diff --git a/doc/img/dispy.png b/doc/img/dispy.png index 57387a781..476fa3341 100644 Binary files a/doc/img/dispy.png and b/doc/img/dispy.png differ diff --git a/doc/img/editor_darkbubble.png b/doc/img/editor_darkbubble.png index 6bad6ef50..d76664866 100644 Binary files a/doc/img/editor_darkbubble.png and b/doc/img/editor_darkbubble.png differ diff --git a/doc/img/editor_frio.png b/doc/img/editor_frio.png index 8428b3438..cb5ad0bcc 100644 Binary files a/doc/img/editor_frio.png and b/doc/img/editor_frio.png differ diff --git a/doc/img/editor_frost.png b/doc/img/editor_frost.png index 39bf11e3e..d0ac5a412 100644 Binary files a/doc/img/editor_frost.png and b/doc/img/editor_frost.png differ diff --git a/doc/img/editor_vier.png b/doc/img/editor_vier.png index 67df8aed8..217f67ac6 100644 Binary files a/doc/img/editor_vier.png and b/doc/img/editor_vier.png differ diff --git a/doc/img/editor_zero.png b/doc/img/editor_zero.png index c1607396c..a1ee37b2d 100644 Binary files a/doc/img/editor_zero.png and b/doc/img/editor_zero.png differ diff --git a/doc/img/friendica_editor.png b/doc/img/friendica_editor.png index aebcf111b..79f0cb35d 100644 Binary files a/doc/img/friendica_editor.png and b/doc/img/friendica_editor.png differ diff --git a/doc/img/friendica_rich_editor.png b/doc/img/friendica_rich_editor.png index 4cd8beac9..170b8ec3b 100644 Binary files a/doc/img/friendica_rich_editor.png and b/doc/img/friendica_rich_editor.png differ diff --git a/doc/img/frost.png b/doc/img/frost.png index 011598b13..1b619a6ac 100644 Binary files a/doc/img/frost.png and b/doc/img/frost.png differ diff --git a/doc/img/globe.png b/doc/img/globe.png index ec1b7ff4a..7a563abcf 100644 Binary files a/doc/img/globe.png and b/doc/img/globe.png differ diff --git a/doc/img/paper_clip.png b/doc/img/paper_clip.png index 51c7cdf70..97aea40a4 100644 Binary files a/doc/img/paper_clip.png and b/doc/img/paper_clip.png differ diff --git a/doc/img/post_categorize.png b/doc/img/post_categorize.png index 54a311870..86aee535e 100644 Binary files a/doc/img/post_categorize.png and b/doc/img/post_categorize.png differ diff --git a/doc/img/post_link.png b/doc/img/post_link.png index b28fca3b9..cd7df78d2 100644 Binary files a/doc/img/post_link.png and b/doc/img/post_link.png differ diff --git a/doc/img/post_mark.png b/doc/img/post_mark.png index 6bee82f18..7781b8ca0 100644 Binary files a/doc/img/post_mark.png and b/doc/img/post_mark.png differ diff --git a/doc/img/post_share.png b/doc/img/post_share.png index f5821692e..a75ce2ad2 100644 Binary files a/doc/img/post_share.png and b/doc/img/post_share.png differ diff --git a/doc/img/post_tag.png b/doc/img/post_tag.png index dc9826c23..fa9fca679 100644 Binary files a/doc/img/post_tag.png and b/doc/img/post_tag.png differ diff --git a/doc/img/post_thumbs_down.png b/doc/img/post_thumbs_down.png index 818dcf504..01177796e 100644 Binary files a/doc/img/post_thumbs_down.png and b/doc/img/post_thumbs_down.png differ diff --git a/doc/img/post_thumbs_up.png b/doc/img/post_thumbs_up.png index a3562c386..aea54abc4 100644 Binary files a/doc/img/post_thumbs_up.png and b/doc/img/post_thumbs_up.png differ diff --git a/doc/img/posts_define.png b/doc/img/posts_define.png index 376a161c5..1d2cb087e 100644 Binary files a/doc/img/posts_define.png and b/doc/img/posts_define.png differ diff --git a/doc/img/video.png b/doc/img/video.png index 00561f1b3..3ee2d1257 100644 Binary files a/doc/img/video.png and b/doc/img/video.png differ diff --git a/htconfig.php b/htconfig.php deleted file mode 100644 index 0e838bd90..000000000 --- a/htconfig.php +++ /dev/null @@ -1,112 +0,0 @@ -config['system']['db_charset'] = "utf8mb4"; - -// Choose a legal default timezone. If you are unsure, use "America/Los_Angeles". -// It can be changed later and only applies to timestamps for anonymous viewers. - -$default_timezone = 'America/Los_Angeles'; - -// Default system language - -$a->config['system']['language'] = 'en'; - -// What is your site name? - -$a->config['sitename'] = "Friendica Social Network"; - -// Your choices are REGISTER_OPEN, REGISTER_APPROVE, or REGISTER_CLOSED. -// Be certain to create your own personal account before setting -// REGISTER_CLOSED. 'register_text' (if set) will be displayed prominently on -// the registration page. REGISTER_APPROVE requires you set 'admin_email' -// to the email address of an already registered person who can authorise -// and/or approve/deny the request. - -// In order to perform system administration via the admin panel, admin_email -// must precisely match the email address of the person logged in. - -$a->config['register_policy'] = REGISTER_OPEN; -$a->config['register_text'] = ''; -$a->config['admin_email'] = ''; - -// Maximum size of an imported message, 0 is unlimited - -$a->config['max_import_size'] = 200000; - -// maximum size of uploaded photos - -$a->config['system']['maximagesize'] = 800000; - -// Location of PHP command line processor - -$a->config['php_path'] = 'php'; - -// Server-to-server private message encryption (RINO) is allowed by default. -// set to 0 to disable, 1 to enable - -$a->config['system']['rino_encrypt'] = 1; - -// allowed themes (change this from admin panel after installation) - -$a->config['system']['allowed_themes'] = 'quattro,vier,duepuntozero,smoothly'; - -// default system theme - -$a->config['system']['theme'] = 'vier'; - - -// By default allow pseudonyms - -$a->config['system']['no_regfullname'] = true; - -//Deny public access to the local directory -//$a->config['system']['block_local_dir'] = false; - -// Location of the global directory -$a->config['system']['directory'] = 'https://dir.friendica.social'; - -// Allowed protocols in link URLs; HTTP protocols always are accepted -$a->config['system']['allowed_link_protocols'] = ['ftp', 'ftps', 'mailto', 'cid', 'gopher']; - -// Authentication cookie lifetime, in days -$a->config['system']['auth_cookie_lifetime'] = 7; diff --git a/images/appnet.png b/images/appnet.png index c51dbf8c4..d92199704 100644 Binary files a/images/appnet.png and b/images/appnet.png differ diff --git a/images/blank.png b/images/blank.png index 67d391966..e42e9f918 100644 Binary files a/images/blank.png and b/images/blank.png differ diff --git a/images/blogger.png b/images/blogger.png index 9b0b4e47b..070da9408 100644 Binary files a/images/blogger.png and b/images/blogger.png differ diff --git a/images/content-types.png b/images/content-types.png index e46eba610..c63e2007d 100644 Binary files a/images/content-types.png and b/images/content-types.png differ diff --git a/images/default-profile-mm.jpg b/images/default-profile-mm.jpg index 79c1a8530..4878c016d 100644 Binary files a/images/default-profile-mm.jpg and b/images/default-profile-mm.jpg differ diff --git a/images/default-profile-sm.jpg b/images/default-profile-sm.jpg index 348957fb4..ea0030ede 100644 Binary files a/images/default-profile-sm.jpg and b/images/default-profile-sm.jpg differ diff --git a/images/default-profile.jpg b/images/default-profile.jpg index 85fbca8cd..2e3696fb3 100644 Binary files a/images/default-profile.jpg and b/images/default-profile.jpg differ diff --git a/images/diaspora-logo.png b/images/diaspora-logo.png index b385d4bda..6bfe8559a 100644 Binary files a/images/diaspora-logo.png and b/images/diaspora-logo.png differ diff --git a/images/dreamwidth.png b/images/dreamwidth.png index fe39b962e..48aafc71e 100644 Binary files a/images/dreamwidth.png and b/images/dreamwidth.png differ diff --git a/images/facebook.png b/images/facebook.png index d8e598189..4a3248edc 100644 Binary files a/images/facebook.png and b/images/facebook.png differ diff --git a/images/friendica-128.jpg b/images/friendica-128.jpg index f7d86ae50..dc210584a 100644 Binary files a/images/friendica-128.jpg and b/images/friendica-128.jpg differ diff --git a/images/friendica-256.jpg b/images/friendica-256.jpg index 182810d62..cca2facf7 100644 Binary files a/images/friendica-256.jpg and b/images/friendica-256.jpg differ diff --git a/images/gnusocial.png b/images/gnusocial.png index be23e621b..e4b4e0458 100644 Binary files a/images/gnusocial.png and b/images/gnusocial.png differ diff --git a/images/googleplus.png b/images/googleplus.png index e51c3dd70..90ce8f352 100644 Binary files a/images/googleplus.png and b/images/googleplus.png differ diff --git a/images/icons.png b/images/icons.png index e255316e6..6a407a37a 100644 Binary files a/images/icons.png and b/images/icons.png differ diff --git a/images/icons/10/add.png b/images/icons/10/add.png index 296e8e500..d70b353bd 100644 Binary files a/images/icons/10/add.png and b/images/icons/10/add.png differ diff --git a/images/icons/10/audio.png b/images/icons/10/audio.png index cfc2fdcbd..77878583d 100644 Binary files a/images/icons/10/audio.png and b/images/icons/10/audio.png differ diff --git a/images/icons/10/delete.png b/images/icons/10/delete.png index e4bd85933..725c5db1c 100644 Binary files a/images/icons/10/delete.png and b/images/icons/10/delete.png differ diff --git a/images/icons/10/edit.png b/images/icons/10/edit.png index d939342a1..203da1807 100644 Binary files a/images/icons/10/edit.png and b/images/icons/10/edit.png differ diff --git a/images/icons/10/gear.png b/images/icons/10/gear.png index b1f2a8f52..796117ceb 100644 Binary files a/images/icons/10/gear.png and b/images/icons/10/gear.png differ diff --git a/images/icons/10/menu.png b/images/icons/10/menu.png index 7dbb779d0..f9dce5e27 100644 Binary files a/images/icons/10/menu.png and b/images/icons/10/menu.png differ diff --git a/images/icons/10/play.png b/images/icons/10/play.png index a9f99e4d1..6f949c180 100644 Binary files a/images/icons/10/play.png and b/images/icons/10/play.png differ diff --git a/images/icons/10/plugin.png b/images/icons/10/plugin.png index 6cfc40786..44b68e0b3 100644 Binary files a/images/icons/10/plugin.png and b/images/icons/10/plugin.png differ diff --git a/images/icons/10/star.png b/images/icons/10/star.png index cd8518a18..ba8a5dfc4 100644 Binary files a/images/icons/10/star.png and b/images/icons/10/star.png differ diff --git a/images/icons/16/add.png b/images/icons/16/add.png index 1ff064bb3..5e353d1a3 100644 Binary files a/images/icons/16/add.png and b/images/icons/16/add.png differ diff --git a/images/icons/16/delete.png b/images/icons/16/delete.png index 532346860..cc87e1afc 100644 Binary files a/images/icons/16/delete.png and b/images/icons/16/delete.png differ diff --git a/images/icons/16/edit.png b/images/icons/16/edit.png index bd845a767..d66b68f73 100644 Binary files a/images/icons/16/edit.png and b/images/icons/16/edit.png differ diff --git a/images/icons/16/gear.png b/images/icons/16/gear.png index 86cbe1eb8..d7f4c7f01 100644 Binary files a/images/icons/16/gear.png and b/images/icons/16/gear.png differ diff --git a/images/icons/16/image.png b/images/icons/16/image.png index c52b9730d..d8028872d 100644 Binary files a/images/icons/16/image.png and b/images/icons/16/image.png differ diff --git a/images/icons/16/menu.png b/images/icons/16/menu.png index a6b1cac47..585f98b2a 100644 Binary files a/images/icons/16/menu.png and b/images/icons/16/menu.png differ diff --git a/images/icons/16/play.png b/images/icons/16/play.png index 19ad05bf2..08d2c6b79 100644 Binary files a/images/icons/16/play.png and b/images/icons/16/play.png differ diff --git a/images/icons/16/star.png b/images/icons/16/star.png index ac620c43c..33dfc11c6 100644 Binary files a/images/icons/16/star.png and b/images/icons/16/star.png differ diff --git a/images/icons/22/add.png b/images/icons/22/add.png index 2079e725d..bd068c0ee 100644 Binary files a/images/icons/22/add.png and b/images/icons/22/add.png differ diff --git a/images/icons/22/delete.png b/images/icons/22/delete.png index d0f2acf13..b0e9b1a5a 100644 Binary files a/images/icons/22/delete.png and b/images/icons/22/delete.png differ diff --git a/images/icons/22/edit.png b/images/icons/22/edit.png index ad251b8ed..fc2a6aabf 100644 Binary files a/images/icons/22/edit.png and b/images/icons/22/edit.png differ diff --git a/images/icons/22/gear.png b/images/icons/22/gear.png index 9ccf5280b..751cb737c 100644 Binary files a/images/icons/22/gear.png and b/images/icons/22/gear.png differ diff --git a/images/icons/22/image.png b/images/icons/22/image.png index f00e3a290..f6a0982dc 100644 Binary files a/images/icons/22/image.png and b/images/icons/22/image.png differ diff --git a/images/icons/22/menu.png b/images/icons/22/menu.png index e3461da8b..a6d610a29 100644 Binary files a/images/icons/22/menu.png and b/images/icons/22/menu.png differ diff --git a/images/icons/22/notify_on.png b/images/icons/22/notify_on.png index 18002e15c..d4c93bf01 100644 Binary files a/images/icons/22/notify_on.png and b/images/icons/22/notify_on.png differ diff --git a/images/icons/22/star.png b/images/icons/22/star.png index 484e24794..9a7ea4184 100644 Binary files a/images/icons/22/star.png and b/images/icons/22/star.png differ diff --git a/images/icons/48/add.png b/images/icons/48/add.png index 753ac4b68..31e70ceab 100644 Binary files a/images/icons/48/add.png and b/images/icons/48/add.png differ diff --git a/images/icons/48/audio.png b/images/icons/48/audio.png index 23f49b54d..e1dcec7c8 100644 Binary files a/images/icons/48/audio.png and b/images/icons/48/audio.png differ diff --git a/images/icons/48/delete.png b/images/icons/48/delete.png index 2835638a9..e93b6155c 100644 Binary files a/images/icons/48/delete.png and b/images/icons/48/delete.png differ diff --git a/images/icons/48/edit.png b/images/icons/48/edit.png index 709fbb357..8623c6461 100644 Binary files a/images/icons/48/edit.png and b/images/icons/48/edit.png differ diff --git a/images/icons/48/feed.png b/images/icons/48/feed.png index 13d88f707..21f62b99e 100644 Binary files a/images/icons/48/feed.png and b/images/icons/48/feed.png differ diff --git a/images/icons/48/gear.png b/images/icons/48/gear.png index 8bb12e96e..08d66ea89 100644 Binary files a/images/icons/48/gear.png and b/images/icons/48/gear.png differ diff --git a/images/icons/48/group.png b/images/icons/48/group.png index 26238ac6a..7e4c38b06 100644 Binary files a/images/icons/48/group.png and b/images/icons/48/group.png differ diff --git a/images/icons/48/image.png b/images/icons/48/image.png index cc0c86872..885eb3001 100644 Binary files a/images/icons/48/image.png and b/images/icons/48/image.png differ diff --git a/images/icons/48/lock.png b/images/icons/48/lock.png index b9db282ac..dfc4b0ae6 100644 Binary files a/images/icons/48/lock.png and b/images/icons/48/lock.png differ diff --git a/images/icons/48/menu.png b/images/icons/48/menu.png index 376e60cab..50fe7d3bc 100644 Binary files a/images/icons/48/menu.png and b/images/icons/48/menu.png differ diff --git a/images/icons/48/notify_on.png b/images/icons/48/notify_on.png index 3275898c5..ac10a2a0e 100644 Binary files a/images/icons/48/notify_on.png and b/images/icons/48/notify_on.png differ diff --git a/images/icons/48/play.png b/images/icons/48/play.png index fce589370..479b61b1c 100644 Binary files a/images/icons/48/play.png and b/images/icons/48/play.png differ diff --git a/images/icons/48/plugin.png b/images/icons/48/plugin.png index c74c6bf46..6a6682e2f 100644 Binary files a/images/icons/48/plugin.png and b/images/icons/48/plugin.png differ diff --git a/images/icons/48/star.png b/images/icons/48/star.png index f53914efa..26130caa5 100644 Binary files a/images/icons/48/star.png and b/images/icons/48/star.png differ diff --git a/images/icons/48/text.png b/images/icons/48/text.png index 502394547..d59c1f244 100644 Binary files a/images/icons/48/text.png and b/images/icons/48/text.png differ diff --git a/images/icons/48/user.png b/images/icons/48/user.png index c42410cd1..1c9f1df8b 100644 Binary files a/images/icons/48/user.png and b/images/icons/48/user.png differ diff --git a/images/icons/48/video.png b/images/icons/48/video.png index dde02f600..ab7c83aec 100644 Binary files a/images/icons/48/video.png and b/images/icons/48/video.png differ diff --git a/images/icons/48/zip.png b/images/icons/48/zip.png index 08a9330c4..f8183e10f 100644 Binary files a/images/icons/48/zip.png and b/images/icons/48/zip.png differ diff --git a/images/icons/add.png b/images/icons/add.png index 78497fbc9..c1157ba7c 100644 Binary files a/images/icons/add.png and b/images/icons/add.png differ diff --git a/images/icons/audio.png b/images/icons/audio.png index 1e1561266..a8892f5f6 100644 Binary files a/images/icons/audio.png and b/images/icons/audio.png differ diff --git a/images/icons/delete.png b/images/icons/delete.png index f0cae5154..679f233eb 100644 Binary files a/images/icons/delete.png and b/images/icons/delete.png differ diff --git a/images/icons/edit.png b/images/icons/edit.png index aeaf835fe..102b08159 100644 Binary files a/images/icons/edit.png and b/images/icons/edit.png differ diff --git a/images/icons/feed.png b/images/icons/feed.png index 6894257e9..15af32e80 100644 Binary files a/images/icons/feed.png and b/images/icons/feed.png differ diff --git a/images/icons/gear.png b/images/icons/gear.png index 02847ef9d..20b9b4117 100644 Binary files a/images/icons/gear.png and b/images/icons/gear.png differ diff --git a/images/icons/group.png b/images/icons/group.png index de0dc7901..0b77349ca 100644 Binary files a/images/icons/group.png and b/images/icons/group.png differ diff --git a/images/icons/image.png b/images/icons/image.png index cec105bfd..e9bcb932d 100644 Binary files a/images/icons/image.png and b/images/icons/image.png differ diff --git a/images/icons/info.png b/images/icons/info.png index ea2b0ffa4..7c6d99e46 100644 Binary files a/images/icons/info.png and b/images/icons/info.png differ diff --git a/images/icons/link.png b/images/icons/link.png index 743bdf0f9..0a69a41fb 100644 Binary files a/images/icons/link.png and b/images/icons/link.png differ diff --git a/images/icons/lock.png b/images/icons/lock.png index 91a8f3ef2..3a840e7e0 100644 Binary files a/images/icons/lock.png and b/images/icons/lock.png differ diff --git a/images/icons/menu.png b/images/icons/menu.png index 56afc8541..7e39afb55 100644 Binary files a/images/icons/menu.png and b/images/icons/menu.png differ diff --git a/images/icons/notice.png b/images/icons/notice.png index b6017c8de..609994d9e 100644 Binary files a/images/icons/notice.png and b/images/icons/notice.png differ diff --git a/images/icons/notify_off.png b/images/icons/notify_off.png index e6eac16b8..a7f317aaf 100644 Binary files a/images/icons/notify_off.png and b/images/icons/notify_off.png differ diff --git a/images/icons/notify_on.png b/images/icons/notify_on.png index b9e07d24e..747f18a37 100644 Binary files a/images/icons/notify_on.png and b/images/icons/notify_on.png differ diff --git a/images/icons/play.png b/images/icons/play.png index 7c942072e..7049534f0 100644 Binary files a/images/icons/play.png and b/images/icons/play.png differ diff --git a/images/icons/plugin.png b/images/icons/plugin.png index 943be0d93..b35e64305 100644 Binary files a/images/icons/plugin.png and b/images/icons/plugin.png differ diff --git a/images/icons/star.png b/images/icons/star.png index 4a2236c9b..16238461a 100644 Binary files a/images/icons/star.png and b/images/icons/star.png differ diff --git a/images/icons/text.png b/images/icons/text.png index c20190ce2..1351b333a 100644 Binary files a/images/icons/text.png and b/images/icons/text.png differ diff --git a/images/icons/unlock.png b/images/icons/unlock.png index 4bf7e7eae..a83945b05 100644 Binary files a/images/icons/unlock.png and b/images/icons/unlock.png differ diff --git a/images/icons/user.png b/images/icons/user.png index f1132b1ae..9d87b367e 100644 Binary files a/images/icons/user.png and b/images/icons/user.png differ diff --git a/images/icons/video.png b/images/icons/video.png index 467a2123e..58d22d2b7 100644 Binary files a/images/icons/video.png and b/images/icons/video.png differ diff --git a/images/icons/zip.png b/images/icons/zip.png index df961dd49..c9a87e889 100644 Binary files a/images/icons/zip.png and b/images/icons/zip.png differ diff --git a/images/libertree.png b/images/libertree.png index ff6bcddf4..db3833cc3 100644 Binary files a/images/libertree.png and b/images/libertree.png differ diff --git a/images/mail.png b/images/mail.png index 2e47fd844..08d297898 100644 Binary files a/images/mail.png and b/images/mail.png differ diff --git a/images/onoff.jpg b/images/onoff.jpg index 7884912a7..caa268150 100644 Binary files a/images/onoff.jpg and b/images/onoff.jpg differ diff --git a/images/person-175.jpg b/images/person-175.jpg index 527a9d78b..b1deaf4d1 100644 Binary files a/images/person-175.jpg and b/images/person-175.jpg differ diff --git a/images/person-48.jpg b/images/person-48.jpg index 8984bb295..6d919cdd0 100644 Binary files a/images/person-48.jpg and b/images/person-48.jpg differ diff --git a/images/person-80.jpg b/images/person-80.jpg index 4e70a4dde..5733602f3 100644 Binary files a/images/person-80.jpg and b/images/person-80.jpg differ diff --git a/images/plugin.png b/images/plugin.png index 08b09e060..575057962 100644 Binary files a/images/plugin.png and b/images/plugin.png differ diff --git a/images/screenshots/friendica-frio-brown-profile-1.png b/images/screenshots/friendica-frio-brown-profile-1.png index edf001ea7..6c3806246 100644 Binary files a/images/screenshots/friendica-frio-brown-profile-1.png and b/images/screenshots/friendica-frio-brown-profile-1.png differ diff --git a/images/screenshots/friendica-frio-brown-profile-2.png b/images/screenshots/friendica-frio-brown-profile-2.png index 11faaa5a2..1141a4b76 100644 Binary files a/images/screenshots/friendica-frio-brown-profile-2.png and b/images/screenshots/friendica-frio-brown-profile-2.png differ diff --git a/images/screenshots/friendica-frio-default-profile-1.png b/images/screenshots/friendica-frio-default-profile-1.png index 89577647e..abe32ef9d 100644 Binary files a/images/screenshots/friendica-frio-default-profile-1.png and b/images/screenshots/friendica-frio-default-profile-1.png differ diff --git a/images/screenshots/friendica-frio-green-profle-1.png b/images/screenshots/friendica-frio-green-profle-1.png index 4c882639b..71b6e2993 100644 Binary files a/images/screenshots/friendica-frio-green-profle-1.png and b/images/screenshots/friendica-frio-green-profle-1.png differ diff --git a/images/screenshots/friendica-frio-green-profle-2.png b/images/screenshots/friendica-frio-green-profle-2.png index 13d958297..db8a2e9fd 100644 Binary files a/images/screenshots/friendica-frio-green-profle-2.png and b/images/screenshots/friendica-frio-green-profle-2.png differ diff --git a/images/screenshots/friendica-frio-mobile-profle-1.png b/images/screenshots/friendica-frio-mobile-profle-1.png index b596ca3a4..e51edccc8 100644 Binary files a/images/screenshots/friendica-frio-mobile-profle-1.png and b/images/screenshots/friendica-frio-mobile-profle-1.png differ diff --git a/images/screenshots/friendica-frio-mobile-profle-2.png b/images/screenshots/friendica-frio-mobile-profle-2.png index 0976472fd..f6eec0919 100644 Binary files a/images/screenshots/friendica-frio-mobile-profle-2.png and b/images/screenshots/friendica-frio-mobile-profle-2.png differ diff --git a/images/screenshots/friendica-frio-red-profle-1.png b/images/screenshots/friendica-frio-red-profle-1.png index c64188369..5a4961c77 100644 Binary files a/images/screenshots/friendica-frio-red-profle-1.png and b/images/screenshots/friendica-frio-red-profle-1.png differ diff --git a/images/screenshots/friendica-frio-red-profle-2.png b/images/screenshots/friendica-frio-red-profle-2.png index f4d24a4ee..324301737 100644 Binary files a/images/screenshots/friendica-frio-red-profle-2.png and b/images/screenshots/friendica-frio-red-profle-2.png differ diff --git a/images/screenshots/friendica-frio-red-profle-3.png b/images/screenshots/friendica-frio-red-profle-3.png index a0cb75051..2d4604c84 100644 Binary files a/images/screenshots/friendica-frio-red-profle-3.png and b/images/screenshots/friendica-frio-red-profle-3.png differ diff --git a/images/search_18.png b/images/search_18.png index 539739670..9fb2db7d5 100644 Binary files a/images/search_18.png and b/images/search_18.png differ diff --git a/images/show_all_off.png b/images/show_all_off.png index cc96d28f0..8ebccad2f 100644 Binary files a/images/show_all_off.png and b/images/show_all_off.png differ diff --git a/images/tumblr.png b/images/tumblr.png index 5c4e2e79d..651d14ee6 100644 Binary files a/images/tumblr.png and b/images/tumblr.png differ diff --git a/images/twitter.png b/images/twitter.png index 513c59899..c53d2a232 100644 Binary files a/images/twitter.png and b/images/twitter.png differ diff --git a/images/wordpress.png b/images/wordpress.png index da57a9a50..23a620b61 100644 Binary files a/images/wordpress.png and b/images/wordpress.png differ diff --git a/include/api.php b/include/api.php index a70c18f3c..5510eddb4 100644 --- a/include/api.php +++ b/include/api.php @@ -16,9 +16,10 @@ use Friendica\Core\Config; use Friendica\Core\L10n; use Friendica\Core\NotificationsManager; use Friendica\Core\PConfig; +use Friendica\Core\Protocol; use Friendica\Core\System; use Friendica\Core\Worker; -use Friendica\Database\DBM; +use Friendica\Database\DBA; use Friendica\Model\Contact; use Friendica\Model\Group; use Friendica\Model\Item; @@ -39,6 +40,7 @@ use Friendica\Object\Image; use Friendica\Protocol\Diaspora; use Friendica\Util\DateTimeFormat; use Friendica\Util\Network; +use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\XML; require_once 'include/conversation.php'; @@ -46,7 +48,6 @@ require_once 'mod/share.php'; require_once 'mod/item.php'; require_once 'include/security.php'; require_once 'mod/wall_upload.php'; -require_once 'mod/proxy.php'; define('API_METHOD_ANY', '*'); define('API_METHOD_GET', 'GET'); @@ -54,7 +55,7 @@ define('API_METHOD_POST', 'POST,PUT'); define('API_METHOD_DELETE', 'POST,DELETE'); $API = []; -$called_api = null; +$called_api = []; /** * It is not sufficient to use local_user() to check whether someone is allowed to use the API, @@ -90,11 +91,15 @@ function api_source() } // Support for known clients that doesn't send a source name - if (strpos($_SERVER['HTTP_USER_AGENT'], "Twidere") !== false) { - return "Twidere"; - } + if (!empty($_SERVER['HTTP_USER_AGENT'])) { + if(strpos($_SERVER['HTTP_USER_AGENT'], "Twidere") !== false) { + return "Twidere"; + } - logger("Unrecognized user-agent ".$_SERVER['HTTP_USER_AGENT'], LOGGER_DEBUG); + logger("Unrecognized user-agent ".$_SERVER['HTTP_USER_AGENT'], LOGGER_DEBUG); + } else { + logger("Empty user-agent", LOGGER_DEBUG); + } return "api"; } @@ -164,7 +169,8 @@ function api_login(App $a) $oauth1 = new FKOAuth1(); // login with oauth try { - list($consumer, $token) = $oauth1->verify_request(OAuthRequest::from_request()); + $request = OAuthRequest::from_request(); + list($consumer, $token) = $oauth1->verify_request($request); if (!is_null($token)) { $oauth1->loginUser($token->uid); Addon::callHooks('logged_in', $a->user); @@ -193,8 +199,8 @@ function api_login(App $a) throw new UnauthorizedException("This API requires login"); } - $user = $_SERVER['PHP_AUTH_USER']; - $password = $_SERVER['PHP_AUTH_PW']; + $user = defaults($_SERVER, 'PHP_AUTH_USER', ''); + $password = defaults($_SERVER, 'PHP_AUTH_PW', ''); // allow "user@server" login (but ignore 'server' part) $at = strstr($user, "@", true); @@ -224,11 +230,11 @@ function api_login(App $a) } else { $user_id = User::authenticate(trim($user), trim($password)); if ($user_id !== false) { - $record = dba::selectFirst('user', [], ['uid' => $user_id]); + $record = DBA::selectFirst('user', [], ['uid' => $user_id]); } } - if (!DBM::is_result($record)) { + if (!DBA::isResult($record)) { logger('API_login failure: ' . print_r($_SERVER, true), LOGGER_DEBUG); header('WWW-Authenticate: Basic realm="Friendica"'); //header('HTTP/1.0 401 Unauthorized'); @@ -258,7 +264,7 @@ function api_check_method($method) if ($method == "*") { return true; } - return (strpos($method, $_SERVER['REQUEST_METHOD']) !== false); + return (stripos($method, defaults($_SERVER, 'REQUEST_METHOD', 'GET')) !== false); } /** @@ -298,7 +304,7 @@ function api_call(App $a) //unset($_SERVER['PHP_AUTH_USER']); /// @TODO should be "true ==[=] $info['auth']", if you miss only one = character, you assign a variable (only with ==). Let's make all this even. - if ($info['auth'] === true && api_user() === false) { + if (!empty($info['auth']) && api_user() === false) { api_login($a); } @@ -475,7 +481,7 @@ function api_rss_extra(App $a, $arr, $user_info) 'base' => System::baseUrl(), 'updated' => api_date(null), 'atom_updated' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), - 'language' => $user_info['language'], + 'language' => $user_info['lang'], 'logo' => System::baseUrl() . "/images/friendica-32.png", ]; @@ -492,9 +498,9 @@ function api_rss_extra(App $a, $arr, $user_info) */ function api_unique_id_to_nurl($id) { - $r = dba::selectFirst('contact', ['nurl'], ['uid' => 0, 'id' => $id]); + $r = DBA::selectFirst('contact', ['nurl'], ['id' => $id]); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { return $r["nurl"]; } else { return false; @@ -519,7 +525,7 @@ function api_get_user(App $a, $contact_id = null) // Searching for contact URL if (!is_null($contact_id) && (intval($contact_id) == 0)) { - $user = dbesc(normalise_link($contact_id)); + $user = DBA::escape(normalise_link($contact_id)); $url = $user; $extra_query = "AND `contact`.`nurl` = '%s' "; if (api_user() !== false) { @@ -529,10 +535,10 @@ function api_get_user(App $a, $contact_id = null) // Searching for contact id with uid = 0 if (!is_null($contact_id) && (intval($contact_id) != 0)) { - $user = dbesc(api_unique_id_to_nurl(intval($contact_id))); + $user = DBA::escape(api_unique_id_to_nurl(intval($contact_id))); if ($user == "") { - throw new BadRequestException("User not found."); + throw new BadRequestException("User ID ".$contact_id." not found."); } $url = $user; @@ -543,10 +549,10 @@ function api_get_user(App $a, $contact_id = null) } if (is_null($user) && x($_GET, 'user_id')) { - $user = dbesc(api_unique_id_to_nurl($_GET['user_id'])); + $user = DBA::escape(api_unique_id_to_nurl($_GET['user_id'])); if ($user == "") { - throw new BadRequestException("User not found."); + throw new BadRequestException("User ID ".$_GET['user_id']." not found."); } $url = $user; @@ -556,7 +562,7 @@ function api_get_user(App $a, $contact_id = null) } } if (is_null($user) && x($_GET, 'screen_name')) { - $user = dbesc($_GET['screen_name']); + $user = DBA::escape($_GET['screen_name']); $extra_query = "AND `contact`.`nick` = '%s' "; if (api_user() !== false) { $extra_query .= "AND `contact`.`uid`=".intval(api_user()); @@ -564,18 +570,24 @@ function api_get_user(App $a, $contact_id = null) } if (is_null($user) && x($_GET, 'profileurl')) { - $user = dbesc(normalise_link($_GET['profileurl'])); + $user = DBA::escape(normalise_link($_GET['profileurl'])); $extra_query = "AND `contact`.`nurl` = '%s' "; if (api_user() !== false) { $extra_query .= "AND `contact`.`uid`=".intval(api_user()); } } + // $called_api is the API path exploded on / and is expected to have at least 2 elements if (is_null($user) && ($a->argc > (count($called_api) - 1)) && (count($called_api) > 0)) { $argid = count($called_api); - list($user, $null) = explode(".", $a->argv[$argid]); + if (!empty($a->argv[$argid])) { + $data = explode(".", $a->argv[$argid]); + if (count($data) > 1) { + list($user, $null) = $data; + } + } if (is_numeric($user)) { - $user = dbesc(api_unique_id_to_nurl(intval($user))); + $user = DBA::escape(api_unique_id_to_nurl(intval($user))); if ($user != "") { $url = $user; @@ -585,7 +597,7 @@ function api_get_user(App $a, $contact_id = null) } } } else { - $user = dbesc($user); + $user = DBA::escape($user); $extra_query = "AND `contact`.`nick` = '%s' "; if (api_user() !== false) { $extra_query .= "AND `contact`.`uid`=" . intval(api_user()); @@ -621,38 +633,38 @@ function api_get_user(App $a, $contact_id = null) } // if the contact wasn't found, fetch it from the contacts with uid = 0 - if (!DBM::is_result($uinfo)) { - $r = []; - - if ($url != "") { - $r = q("SELECT * FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s' LIMIT 1", dbesc(normalise_link($url))); + if (!DBA::isResult($uinfo)) { + if ($url == "") { + throw new BadRequestException("User not found."); } - if (DBM::is_result($r)) { - $network_name = ContactSelector::networkToName($r[0]['network'], $r[0]['url']); + $contact = DBA::selectFirst('contact', [], ['uid' => 0, 'nurl' => normalise_link($url)]); + + if (DBA::isResult($contact)) { + $network_name = ContactSelector::networkToName($contact['network'], $contact['url']); // If no nick where given, extract it from the address - if (($r[0]['nick'] == "") || ($r[0]['name'] == $r[0]['nick'])) { - $r[0]['nick'] = api_get_nick($r[0]["url"]); + if (($contact['nick'] == "") || ($contact['name'] == $contact['nick'])) { + $contact['nick'] = api_get_nick($contact["url"]); } $ret = [ - 'id' => $r[0]["id"], - 'id_str' => (string) $r[0]["id"], - 'name' => $r[0]["name"], - 'screen_name' => (($r[0]['nick']) ? $r[0]['nick'] : $r[0]['name']), - 'location' => ($r[0]["location"] != "") ? $r[0]["location"] : $network_name, - 'description' => $r[0]["about"], - 'profile_image_url' => $r[0]["micro"], - 'profile_image_url_https' => $r[0]["micro"], - 'profile_image_url_profile_size' => $r[0]["thumb"], - 'profile_image_url_large' => $r[0]["photo"], - 'url' => $r[0]["url"], + 'id' => $contact["id"], + 'id_str' => (string) $contact["id"], + 'name' => $contact["name"], + 'screen_name' => (($contact['nick']) ? $contact['nick'] : $contact['name']), + 'location' => ($contact["location"] != "") ? $contact["location"] : $network_name, + 'description' => $contact["about"], + 'profile_image_url' => $contact["micro"], + 'profile_image_url_https' => $contact["micro"], + 'profile_image_url_profile_size' => $contact["thumb"], + 'profile_image_url_large' => $contact["photo"], + 'url' => $contact["url"], 'protected' => false, 'followers_count' => 0, 'friends_count' => 0, 'listed_count' => 0, - 'created_at' => api_date($r[0]["created"]), + 'created_at' => api_date($contact["created"]), 'favourites_count' => 0, 'utc_offset' => 0, 'time_zone' => 'UTC', @@ -667,85 +679,29 @@ function api_get_user(App $a, $contact_id = null) 'follow_request_sent' => false, 'statusnet_blocking' => false, 'notifications' => false, - 'statusnet_profile_url' => $r[0]["url"], + 'statusnet_profile_url' => $contact["url"], 'uid' => 0, - 'cid' => Contact::getIdForURL($r[0]["url"], api_user(), true), + 'cid' => Contact::getIdForURL($contact["url"], api_user(), true), + 'pid' => Contact::getIdForURL($contact["url"], 0, true), 'self' => 0, - 'network' => $r[0]["network"], + 'network' => $contact["network"], ]; return $ret; } else { - throw new BadRequestException("User not found."); + throw new BadRequestException("User ".$url." not found."); } } if ($uinfo[0]['self']) { if ($uinfo[0]['network'] == "") { - $uinfo[0]['network'] = NETWORK_DFRN; + $uinfo[0]['network'] = Protocol::DFRN; } - $usr = q( - "SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", - intval(api_user()) - ); - $profile = q( - "SELECT * FROM `profile` WHERE `uid` = %d AND `is-default` = 1 LIMIT 1", - intval(api_user()) - ); - - /// @TODO old-lost code? (twice) - // Counting is deactivated by now, due to performance issues - // count public wall messages - //$r = q("SELECT COUNT(*) as `count` FROM `item` WHERE `uid` = %d AND `wall`", - // intval($uinfo[0]['uid']) - //); - //$countitms = $r[0]['count']; - $countitms = 0; - } else { - // Counting is deactivated by now, due to performance issues - //$r = q("SELECT count(*) as `count` FROM `item` - // WHERE `contact-id` = %d", - // intval($uinfo[0]['id']) - //); - //$countitms = $r[0]['count']; - $countitms = 0; + $usr = DBA::selectFirst('user', ['default-location'], ['uid' => api_user()]); + $profile = DBA::selectFirst('profile', ['about'], ['uid' => api_user(), 'is-default' => true]); } - - /// @TODO old-lost code? (twice) - /* - // Counting is deactivated by now, due to performance issues - // count friends - $r = q("SELECT count(*) as `count` FROM `contact` - WHERE `uid` = %d AND `rel` IN ( %d, %d ) - AND `self`=0 AND NOT `blocked` AND NOT `pending` AND `hidden`=0", - intval($uinfo[0]['uid']), - intval(CONTACT_IS_SHARING), - intval(CONTACT_IS_FRIEND) - ); - $countfriends = $r[0]['count']; - - $r = q("SELECT count(*) as `count` FROM `contact` - WHERE `uid` = %d AND `rel` IN ( %d, %d ) - AND `self`=0 AND NOT `blocked` AND NOT `pending` AND `hidden`=0", - intval($uinfo[0]['uid']), - intval(CONTACT_IS_FOLLOWER), - intval(CONTACT_IS_FRIEND) - ); - $countfollowers = $r[0]['count']; - - $r = q("SELECT count(*) as `count` FROM item where starred = 1 and uid = %d and deleted = 0", - intval($uinfo[0]['uid']) - ); - $starred = $r[0]['count']; - - - if (! $uinfo[0]['self']) { - $countfriends = 0; - $countfollowers = 0; - $starred = 0; - } - */ + $countitems = 0; $countfriends = 0; $countfollowers = 0; $starred = 0; @@ -759,14 +715,14 @@ function api_get_user(App $a, $contact_id = null) $pcontact_id = Contact::getIdForURL($uinfo[0]['url'], 0, true); - if (!empty($profile[0]['about'])) { - $description = $profile[0]['about']; + if (!empty($profile['about'])) { + $description = $profile['about']; } else { $description = $uinfo[0]["about"]; } - if (!empty($usr[0]['default-location'])) { - $location = $usr[0]['default-location']; + if (!empty($usr['default-location'])) { + $location = $usr['default-location']; } elseif (!empty($uinfo[0]["location"])) { $location = $uinfo[0]["location"]; } else { @@ -795,12 +751,12 @@ function api_get_user(App $a, $contact_id = null) 'time_zone' => 'UTC', 'geo_enabled' => false, 'verified' => true, - 'statuses_count' => intval($countitms), + 'statuses_count' => intval($countitems), 'lang' => '', 'contributors_enabled' => false, 'is_translator' => false, 'is_translation_enabled' => false, - 'following' => (($uinfo[0]['rel'] == CONTACT_IS_FOLLOWER) || ($uinfo[0]['rel'] == CONTACT_IS_FRIEND)), + 'following' => (($uinfo[0]['rel'] == Contact::FOLLOWER) || ($uinfo[0]['rel'] == Contact::FRIEND)), 'follow_request_sent' => false, 'statusnet_blocking' => false, 'notifications' => false, @@ -809,15 +765,17 @@ function api_get_user(App $a, $contact_id = null) 'statusnet_profile_url' => $uinfo[0]['url'], 'uid' => intval($uinfo[0]['uid']), 'cid' => intval($uinfo[0]['cid']), + 'pid' => Contact::getIdForURL($uinfo[0]["url"], 0, true), 'self' => $uinfo[0]['self'], 'network' => $uinfo[0]['network'], ]; // If this is a local user and it uses Frio, we can get its color preferences. if ($ret['self']) { - $theme_info = dba::selectFirst('user', ['theme'], ['uid' => $ret['uid']]); + $theme_info = DBA::selectFirst('user', ['theme'], ['uid' => $ret['uid']]); if ($theme_info['theme'] === 'frio') { $schema = PConfig::get($ret['uid'], 'frio', 'schema'); + if ($schema && ($schema != '---')) { if (file_exists('view/theme/frio/schema/'.$schema.'.php')) { $schemefile = 'view/theme/frio/schema/'.$schema.'.php'; @@ -828,13 +786,13 @@ function api_get_user(App $a, $contact_id = null) $link_color = PConfig::get($ret['uid'], 'frio', 'link_color'); $bgcolor = PConfig::get($ret['uid'], 'frio', 'background_color'); } - if (!$nav_bg) { + if (empty($nav_bg)) { $nav_bg = "#708fa0"; } - if (!$link_color) { + if (empty($link_color)) { $link_color = "#6fdbe8"; } - if (!$bgcolor) { + if (empty($bgcolor)) { $bgcolor = "#ededed"; } @@ -856,16 +814,12 @@ function api_get_user(App $a, $contact_id = null) */ function api_item_get_user(App $a, $item) { - $status_user = api_get_user($a, $item["author-link"]); + $status_user = api_get_user($a, defaults($item, 'author-id', null)); - $status_user["protected"] = (($item["allow_cid"] != "") || - ($item["allow_gid"] != "") || - ($item["deny_cid"] != "") || - ($item["deny_gid"] != "") || - $item["private"]); + $status_user["protected"] = defaults($item, 'private', 0); - if ($item['thr-parent'] == $item['uri']) { - $owner_user = api_get_user($a, $item["owner-link"]); + if (defaults($item, 'thr-parent', '') == defaults($item, 'uri', '')) { + $owner_user = api_get_user($a, defaults($item, 'author-id', null)); } else { $owner_user = $status_user; } @@ -935,11 +889,10 @@ function api_reformat_xml(&$item, &$key) * * @return string The XML data */ -function api_create_xml($data, $root_element) +function api_create_xml(array $data, $root_element) { $childname = key($data); $data2 = array_pop($data); - $key = key($data2); $namespaces = ["" => "http://api.twitter.com", "statusnet" => "http://status.net/schema/api/1/", @@ -952,18 +905,19 @@ function api_create_xml($data, $root_element) } if (is_array($data2)) { + $key = key($data2); api_walk_recursive($data2, "api_reformat_xml"); - } - if ($key == "0") { - $data4 = []; - $i = 1; + if ($key == "0") { + $data4 = []; + $i = 1; - foreach ($data2 as $item) { - $data4[$i++.":".$childname] = $item; + foreach ($data2 as $item) { + $data4[$i++ . ":" . $childname] = $item; + } + + $data2 = $data4; } - - $data2 = $data4; } $data3 = [$root_element => $data2]; @@ -1068,7 +1022,7 @@ function requestdata($k) } /** - * Waitman Gobble Mod + * Deprecated function to upload media. * * @param string $type Return type (atom, rss, xml, json) * @@ -1084,7 +1038,6 @@ function api_statuses_mediap($type) } $user_info = api_get_user($a); - $_REQUEST['type'] = 'wall'; $_REQUEST['profile_uid'] = api_user(); $_REQUEST['api_source'] = true; $txt = requestdata('status'); @@ -1100,18 +1053,16 @@ function api_statuses_mediap($type) } $txt = HTML::toBBCode($txt); - $a->argv[1]=$user_info['screen_name']; //should be set to username? + $a->argv[1] = $user_info['screen_name']; //should be set to username? - // tell wall_upload function to return img info instead of echo - $_REQUEST['hush'] = 'yeah'; - $bebop = wall_upload_post($a); + $picture = wall_upload_post($a, false); // now that we have the img url in bbcode we can add it to the status and insert the wall item. - $_REQUEST['body'] = $txt . "\n\n" . $bebop; - item_post($a); + $_REQUEST['body'] = $txt . "\n\n" . '[url=' . $picture["albumpage"] . '][img]' . $picture["preview"] . "[/img][/url]"; + $item_id = item_post($a); - // this should output the last post (the one we just posted). - return api_status_show($type); + // output the post that we just posted. + return api_status_show($type, $item_id); } /// @TODO move this to top of file or somewhere better! @@ -1127,7 +1078,6 @@ api_register_func('api/statuses/mediap', 'api_statuses_mediap', true, API_METHOD */ function api_statuses_update($type) { - $a = get_app(); if (api_user() === false) { @@ -1175,26 +1125,14 @@ function api_statuses_update($type) } $_REQUEST['profile_uid'] = api_user(); - if ($parent) { - $_REQUEST['type'] = 'net-comment'; - } else { + if (!$parent) { // Check for throttling (maximum posts per day, week and month) $throttle_day = Config::get('system', 'throttle_limit_day'); if ($throttle_day > 0) { $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60); - $r = q( - "SELECT COUNT(*) AS `posts_day` FROM `item` WHERE `uid`=%d AND `wall` - AND `created` > '%s' AND `id` = `parent`", - intval(api_user()), - dbesc($datefrom) - ); - - if (DBM::is_result($r)) { - $posts_day = $r[0]["posts_day"]; - } else { - $posts_day = 0; - } + $condition = ["`uid` = ? AND `wall` AND `created` > ?", api_user(), $datefrom]; + $posts_day = DBA::count('thread', $condition); if ($posts_day > $throttle_day) { logger('Daily posting limit reached for user '.api_user(), LOGGER_DEBUG); @@ -1207,18 +1145,8 @@ function api_statuses_update($type) if ($throttle_week > 0) { $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*7); - $r = q( - "SELECT COUNT(*) AS `posts_week` FROM `item` WHERE `uid`=%d AND `wall` - AND `created` > '%s' AND `id` = `parent`", - intval(api_user()), - dbesc($datefrom) - ); - - if (DBM::is_result($r)) { - $posts_week = $r[0]["posts_week"]; - } else { - $posts_week = 0; - } + $condition = ["`uid` = ? AND `wall` AND `created` > ?", api_user(), $datefrom]; + $posts_week = DBA::count('thread', $condition); if ($posts_week > $throttle_week) { logger('Weekly posting limit reached for user '.api_user(), LOGGER_DEBUG); @@ -1231,18 +1159,8 @@ function api_statuses_update($type) if ($throttle_month > 0) { $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*30); - $r = q( - "SELECT COUNT(*) AS `posts_month` FROM `item` WHERE `uid`=%d AND `wall` - AND `created` > '%s' AND `id` = `parent`", - intval(api_user()), - dbesc($datefrom) - ); - - if (DBM::is_result($r)) { - $posts_month = $r[0]["posts_month"]; - } else { - $posts_month = 0; - } + $condition = ["`uid` = ? AND `wall` AND `created` > ?", api_user(), $datefrom]; + $posts_month = DBA::count('thread', $condition); if ($posts_month > $throttle_month) { logger('Monthly posting limit reached for user '.api_user(), LOGGER_DEBUG); @@ -1250,16 +1168,13 @@ function api_statuses_update($type) throw new TooManyRequestsException(L10n::t("Monthly posting limit of %d post reached. The post was rejected.", "Monthly posting limit of %d posts reached. The post was rejected.", $throttle_month)); } } - - $_REQUEST['type'] = 'wall'; } if (x($_FILES, 'media')) { // upload the image if we have one - $_REQUEST['hush'] = 'yeah'; //tell wall_upload function to return img info instead of echo - $media = wall_upload_post($a); - if (strlen($media) > 0) { - $_REQUEST['body'] .= "\n\n" . $media; + $picture = wall_upload_post($a, false); + if (is_array($picture)) { + $_REQUEST['body'] .= "\n\n" . '[url=' . $picture["albumpage"] . '][img]' . $picture["preview"] . "[/img][/url]"; } } @@ -1270,7 +1185,7 @@ function api_statuses_update($type) intval(requestdata('media_ids')), api_user() ); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $phototypes = Image::supportedTypes(); $ext = $phototypes[$r[0]['type']]; $_REQUEST['body'] .= "\n\n" . '[url=' . System::baseUrl() . '/photos/' . $r[0]['nickname'] . '/image/' . $r[0]['resource-id'] . ']'; @@ -1287,10 +1202,10 @@ function api_statuses_update($type) } // call out normal post function - item_post($a); + $item_id = item_post($a); - // this should output the last post (the one we just posted). - return api_status_show($type); + // output the post that we just posted. + return api_status_show($type, $item_id); } /// @TODO move to top of file or somewhere better @@ -1347,7 +1262,7 @@ api_register_func('api/media/upload', 'api_media_upload', true, API_METHOD_POST) * * @return array|string */ -function api_status_show($type) +function api_status_show($type, $item_id = 0) { $a = get_app(); @@ -1356,31 +1271,22 @@ function api_status_show($type) logger('api_status_show: user_info: '.print_r($user_info, true), LOGGER_DEBUG); if ($type == "raw") { - $privacy_sql = "AND `item`.`allow_cid`='' AND `item`.`allow_gid`='' AND `item`.`deny_cid`='' AND `item`.`deny_gid`=''"; + $privacy_sql = "AND NOT `private`"; } else { $privacy_sql = ""; } - // get last public wall message - $lastwall = q( - "SELECT `item`.* - FROM `item` - WHERE `item`.`contact-id` = %d AND `item`.`uid` = %d - AND ((`item`.`author-link` IN ('%s', '%s')) OR (`item`.`owner-link` IN ('%s', '%s'))) - AND `item`.`type` != 'activity' $privacy_sql - ORDER BY `item`.`id` DESC - LIMIT 1", - intval($user_info['cid']), - intval(api_user()), - dbesc($user_info['url']), - dbesc(normalise_link($user_info['url'])), - dbesc($user_info['url']), - dbesc(normalise_link($user_info['url'])) - ); - - if (DBM::is_result($lastwall)) { - $lastwall = $lastwall[0]; + if (!empty($item_id)) { + // Get the item with the given id + $condition = ['id' => $item_id]; + } else { + // get last public wall message + $condition = ['owner-id' => $user_info['pid'], 'uid' => api_user(), + 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]]; + } + $lastwall = Item::selectFirst(Item::ITEM_FIELDLIST, $condition, ['order' => ['id' => true]]); + if (DBA::isResult($lastwall)) { $in_reply_to = api_in_reply_to($lastwall); $converted = api_convert_item($lastwall); @@ -1405,19 +1311,19 @@ function api_status_show($type) 'in_reply_to_screen_name' => $in_reply_to['screen_name'], 'user' => $user_info, $geo => null, - 'coordinates' => "", - 'place' => "", - 'contributors' => "", + 'coordinates' => '', + 'place' => '', + 'contributors' => '', 'is_quote_status' => false, 'retweet_count' => 0, 'favorite_count' => 0, 'favorited' => $lastwall['starred'] ? true : false, 'retweeted' => false, 'possibly_sensitive' => false, - 'lang' => "", + 'lang' => '', 'statusnet_html' => $converted["html"], 'statusnet_conversation_id' => $lastwall['parent'], - 'external_url' => System::baseUrl() . "/display/" . $lastwall['guid'], + 'external_url' => System::baseUrl() . '/display/' . $lastwall['guid'], ]; if (count($converted["attachments"]) > 0) { @@ -1428,10 +1334,10 @@ function api_status_show($type) $status_info["entities"] = $converted["entities"]; } - if (($lastwall['item_network'] != "") && ($status_info["source"] == 'web')) { - $status_info["source"] = ContactSelector::networkToName($lastwall['item_network'], $user_info['url']); - } elseif (($lastwall['item_network'] != "") && (ContactSelector::networkToName($lastwall['item_network'], $user_info['url']) != $status_info["source"])) { - $status_info["source"] = trim($status_info["source"].' ('.ContactSelector::networkToName($lastwall['item_network'], $user_info['url']).')'); + if ($status_info["source"] == 'web') { + $status_info["source"] = ContactSelector::networkToName($lastwall['network'], $user_info['url']); + } elseif (ContactSelector::networkToName($lastwall['network'], $user_info['url']) != $status_info["source"]) { + $status_info["source"] = trim($status_info["source"].' ('.ContactSelector::networkToName($lastwall['network'], $user_info['url']).')'); } // "uid" and "self" are only needed for some internal stuff, so remove it from here @@ -1460,28 +1366,12 @@ function api_users_show($type) $a = get_app(); $user_info = api_get_user($a); - $lastwall = q( - "SELECT `item`.* - FROM `item` - INNER JOIN `contact` ON `contact`.`id`=`item`.`contact-id` AND `contact`.`uid` = `item`.`uid` - WHERE `item`.`uid` = %d AND `verb` = '%s' AND `item`.`contact-id` = %d - AND ((`item`.`author-link` IN ('%s', '%s')) OR (`item`.`owner-link` IN ('%s', '%s'))) - AND `type`!='activity' - AND `item`.`allow_cid`='' AND `item`.`allow_gid`='' AND `item`.`deny_cid`='' AND `item`.`deny_gid`='' - ORDER BY `id` DESC - LIMIT 1", - intval(api_user()), - dbesc(ACTIVITY_POST), - intval($user_info['cid']), - dbesc($user_info['url']), - dbesc(normalise_link($user_info['url'])), - dbesc($user_info['url']), - dbesc(normalise_link($user_info['url'])) - ); - if (DBM::is_result($lastwall)) { - $lastwall = $lastwall[0]; + $condition = ['owner-id' => $user_info['pid'], 'uid' => api_user(), + 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'private' => false]; + $lastwall = Item::selectFirst(Item::ITEM_FIELDLIST, $condition, ['order' => ['id' => true]]); + if (DBA::isResult($lastwall)) { $in_reply_to = api_in_reply_to($lastwall); $converted = api_convert_item($lastwall); @@ -1519,12 +1409,12 @@ function api_users_show($type) $user_info["status"]["entities"] = $converted["entities"]; } - if (($lastwall['item_network'] != "") && ($user_info["status"]["source"] == 'web')) { - $user_info["status"]["source"] = ContactSelector::networkToName($lastwall['item_network'], $user_info['url']); + if ($user_info["status"]["source"] == 'web') { + $user_info["status"]["source"] = ContactSelector::networkToName($lastwall['network'], $user_info['url']); } - if (($lastwall['item_network'] != "") && (ContactSelector::networkToName($lastwall['item_network'], $user_info['url']) != $user_info["status"]["source"])) { - $user_info["status"]["source"] = trim($user_info["status"]["source"] . ' (' . ContactSelector::networkToName($lastwall['item_network'], $user_info['url']) . ')'); + if (ContactSelector::networkToName($lastwall['network'], $user_info['url']) != $user_info["status"]["source"]) { + $user_info["status"]["source"] = trim($user_info["status"]["source"] . ' (' . ContactSelector::networkToName($lastwall['network'], $user_info['url']) . ')'); } } @@ -1554,13 +1444,13 @@ function api_users_search($type) $userlist = []; if (x($_GET, 'q')) { - $r = q("SELECT id FROM `contact` WHERE `uid` = 0 AND `name` = '%s'", dbesc($_GET["q"])); + $r = q("SELECT id FROM `contact` WHERE `uid` = 0 AND `name` = '%s'", DBA::escape($_GET["q"])); - if (!DBM::is_result($r)) { - $r = q("SELECT `id` FROM `contact` WHERE `uid` = 0 AND `nick` = '%s'", dbesc($_GET["q"])); + if (!DBA::isResult($r)) { + $r = q("SELECT `id` FROM `contact` WHERE `uid` = 0 AND `nick` = '%s'", DBA::escape($_GET["q"])); } - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $k = 0; foreach ($r as $user) { $user_info = api_get_user($a, $user["id"]); @@ -1573,10 +1463,10 @@ function api_users_search($type) } $userlist = ["users" => $userlist]; } else { - throw new BadRequestException("User not found."); + throw new BadRequestException("User ".$_GET["q"]." not found."); } } else { - throw new BadRequestException("User not found."); + throw new BadRequestException("No user specified."); } return api_format_data("users", $type, $userlist); @@ -1599,7 +1489,7 @@ function api_users_lookup($type) { $users = []; - if (x($_REQUEST['user_id'])) { + if (!empty($_REQUEST['user_id'])) { foreach (explode(',', $_REQUEST['user_id']) as $id) { if (!empty($id)) { $users[] = api_get_user(get_app(), $id); @@ -1637,7 +1527,6 @@ function api_search($type) } $data = []; - $sql_extra = ''; if (!x($_REQUEST, 'q')) { throw new BadRequestException("q parameter is required."); @@ -1657,24 +1546,20 @@ function api_search($type) $start = $page * $count; + $condition = ["`gravity` IN (?, ?) AND `item`.`id` > ? + AND (`item`.`uid` = 0 OR (`item`.`uid` = ? AND NOT `item`.`global`)) + AND `item`.`body` LIKE CONCAT('%',?,'%')", + GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, api_user(), $_REQUEST['q']]; + if ($max_id > 0) { - $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); + $condition[0] .= " AND `item`.`id` <= ?"; + $condition[] = $max_id; } - $r = dba::p( - "SELECT ".item_fieldlists()." - FROM `item` ".item_joins(api_user())." - WHERE ".item_condition()." AND (`item`.`uid` = 0 OR (`item`.`uid` = ? AND NOT `item`.`global`)) - AND `item`.`body` LIKE CONCAT('%',?,'%') - $sql_extra - AND `item`.`id`>? - ORDER BY `item`.`id` DESC LIMIT ".intval($start)." ,".intval($count)." ", - api_user(), - $_REQUEST['q'], - $since_id - ); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Item::selectForUser(api_user(), [], $condition, $params); - $data['status'] = api_format_items(dba::inArray($r), $user_info); + $data['status'] = api_format_items(Item::inArray($statuses), $user_info); return api_format_data("statuses", $type, $data); } @@ -1724,47 +1609,36 @@ function api_statuses_home_timeline($type) $start = $page * $count; - $sql_extra = ''; + $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `item`.`id` > ?", + api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id]; + if ($max_id > 0) { - $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); + $condition[0] .= " AND `item`.`id` <= ?"; + $condition[] = $max_id; } if ($exclude_replies > 0) { - $sql_extra .= ' AND `item`.`parent` = `item`.`id`'; + $condition[0] .= ' AND `item`.`parent` = `item`.`id`'; } if ($conversation_id > 0) { - $sql_extra .= ' AND `item`.`parent` = ' . intval($conversation_id); + $condition[0] .= " AND `item`.`parent` = ?"; + $condition[] = $conversation_id; } - $r = q( - "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, - `contact`.`id` AS `cid` - FROM `item` - STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` - AND (NOT `contact`.`blocked` OR `contact`.`pending`) - WHERE `item`.`uid` = %d AND `verb` = '%s' - AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` - $sql_extra - AND `item`.`id`>%d - ORDER BY `item`.`id` DESC LIMIT %d ,%d ", - intval(api_user()), - dbesc(ACTIVITY_POST), - intval($since_id), - intval($start), - intval($count) - ); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Item::selectForUser(api_user(), [], $condition, $params); - $ret = api_format_items($r, $user_info, false, $type); + $items = Item::inArray($statuses); + + $ret = api_format_items($items, $user_info, false, $type); // Set all posts from the query above to seen $idarray = []; - foreach ($r as $item) { + foreach ($items as $item) { $idarray[] = intval($item["id"]); } if (!empty($idarray)) { - $unseen = dba::exists('item', ['unseen' => true, 'id' => $idarray]); + $unseen = Item::exists(['unseen' => true, 'id' => $idarray]); if ($unseen) { Item::update(['unseen' => false], ['unseen' => true, 'id' => $idarray]); } @@ -1819,61 +1693,35 @@ function api_statuses_public_timeline($type) $sql_extra = ''; if ($exclude_replies && !$conversation_id) { + $condition = ["`gravity` IN (?, ?) AND `iid` > ? AND NOT `private` AND `wall` AND NOT `user`.`hidewall`", + GRAVITY_PARENT, GRAVITY_COMMENT, $since_id]; + if ($max_id > 0) { - $sql_extra = 'AND `thread`.`iid` <= ' . intval($max_id); + $condition[0] .= " AND `thread`.`iid` <= ?"; + $condition[] = $max_id; } - $r = dba::p( - "SELECT " . item_fieldlists() . " - FROM `thread` - STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` - " . item_joins(api_user()) . " - STRAIGHT_JOIN `user` ON `user`.`uid` = `thread`.`uid` - AND NOT `user`.`hidewall` - AND `verb` = ? - AND NOT `thread`.`private` - AND `thread`.`wall` - AND `thread`.`visible` - AND NOT `thread`.`deleted` - AND NOT `thread`.`moderated` - AND `thread`.`iid` > ? - $sql_extra - ORDER BY `thread`.`iid` DESC - LIMIT " . intval($start) . ", " . intval($count), - ACTIVITY_POST, - $since_id - ); + $params = ['order' => ['iid' => true], 'limit' => [$start, $count]]; + $statuses = Item::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); - $r = dba::inArray($r); + $r = Item::inArray($statuses); } else { + $condition = ["`gravity` IN (?, ?) AND `id` > ? AND NOT `private` AND `wall` AND NOT `user`.`hidewall` AND `item`.`origin`", + GRAVITY_PARENT, GRAVITY_COMMENT, $since_id]; + if ($max_id > 0) { - $sql_extra = 'AND `item`.`id` <= ' . intval($max_id); + $condition[0] .= " AND `item`.`id` <= ?"; + $condition[] = $max_id; } if ($conversation_id > 0) { - $sql_extra .= ' AND `item`.`parent` = ' . intval($conversation_id); + $condition[0] .= " AND `item`.`parent` = ?"; + $condition[] = $conversation_id; } - $r = dba::p( - "SELECT " . item_fieldlists() . " - FROM `item` - " . item_joins(api_user()) . " - STRAIGHT_JOIN `user` ON `user`.`uid` = `item`.`uid` - AND NOT `user`.`hidewall` - AND `verb` = ? - AND NOT `item`.`private` - AND `item`.`wall` - AND `item`.`visible` - AND NOT `item`.`deleted` - AND NOT `item`.`moderated` - AND `item`.`id` > ? - $sql_extra - ORDER BY `item`.`id` DESC - LIMIT " . intval($start) . ", " . intval($count), - ACTIVITY_POST, - $since_id - ); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Item::selectForUser(api_user(), [], $condition, $params); - $r = dba::inArray($r); + $r = Item::inArray($statuses); } $ret = api_format_items($r, $user_info, false, $type); @@ -1921,33 +1769,18 @@ function api_statuses_networkpublic_timeline($type) } $start = ($page - 1) * $count; - $sql_extra = ''; + $condition = ["`uid` = 0 AND `gravity` IN (?, ?) AND `thread`.`iid` > ? AND NOT `private`", + GRAVITY_PARENT, GRAVITY_COMMENT, $since_id]; + if ($max_id > 0) { - $sql_extra = 'AND `thread`.`iid` <= ' . intval($max_id); + $condition[0] .= " AND `thread`.`iid` <= ?"; + $condition[] = $max_id; } - $r = dba::p( - "SELECT " . item_fieldlists() . " - FROM `thread` - STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid` - " . item_joins(api_user()) . " - WHERE `thread`.`uid` = 0 - AND `verb` = ? - AND NOT `thread`.`private` - AND `thread`.`visible` - AND NOT `thread`.`deleted` - AND NOT `thread`.`moderated` - AND `thread`.`iid` > ? - $sql_extra - ORDER BY `thread`.`iid` DESC - LIMIT " . intval($start) . ", " . intval($count), - ACTIVITY_POST, - $since_id - ); + $params = ['order' => ['iid' => true], 'limit' => [$start, $count]]; + $statuses = Item::selectThreadForUser(api_user(), Item::DISPLAY_FIELDLIST, $condition, $params); - $r = dba::inArray($r); - - $ret = api_format_items($r, $user_info, false, $type); + $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); $data = ['status' => $ret]; switch ($type) { @@ -1980,63 +1813,50 @@ function api_statuses_show($type) } // params - $id = intval($a->argv[3]); + $id = intval(defaults($a->argv, 3, 0)); if ($id == 0) { - $id = intval($_REQUEST["id"]); + $id = intval(defaults($_REQUEST, 'id', 0)); } // Hotot workaround if ($id == 0) { - $id = intval($a->argv[4]); + $id = intval(defaults($a->argv, 4, 0)); } logger('API: api_statuses_show: ' . $id); - $conversation = (x($_REQUEST, 'conversation') ? 1 : 0); - - $sql_extra = ''; - if ($conversation) { - $sql_extra .= " AND `item`.`parent` = %d ORDER BY `id` ASC "; - } else { - $sql_extra .= " AND `item`.`id` = %d"; - } + $conversation = !empty($_REQUEST['conversation']); // try to fetch the item for the local user - or the public item, if there is no local one - $uri_item = dba::selectFirst('item', ['uri'], ['id' => $id]); - if (!DBM::is_result($uri_item)) { + $uri_item = Item::selectFirst(['uri'], ['id' => $id]); + if (!DBA::isResult($uri_item)) { throw new BadRequestException("There is no status with this id."); } - $item = dba::selectFirst('item', ['id'], ['uri' => $uri_item['uri'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); - if (!DBM::is_result($item)) { + $item = Item::selectFirst(['id'], ['uri' => $uri_item['uri'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); + if (!DBA::isResult($item)) { throw new BadRequestException("There is no status with this id."); } $id = $item['id']; - $r = q( - "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, - `contact`.`id` AS `cid` - FROM `item` - INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` - AND (NOT `contact`.`blocked` OR `contact`.`pending`) - WHERE `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` - AND `item`.`uid` IN (0, %d) AND `item`.`verb` = '%s' - $sql_extra", - intval(api_user()), - dbesc(ACTIVITY_POST), - intval($id) - ); + if ($conversation) { + $condition = ['parent' => $id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]]; + $params = ['order' => ['id' => true]]; + } else { + $condition = ['id' => $id, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]]; + $params = []; + } + + $statuses = Item::selectForUser(api_user(), [], $condition, $params); /// @TODO How about copying this to above methods which don't check $r ? - if (!DBM::is_result($r)) { + if (!DBA::isResult($statuses)) { throw new BadRequestException("There is no status with this id."); } - $ret = api_format_items($r, $user_info, false, $type); + $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); if ($conversation) { $data = ['status' => $ret]; @@ -2066,73 +1886,57 @@ function api_conversation_show($type) } // params - $id = intval($a->argv[3]); - $count = (x($_REQUEST, 'count') ? $_REQUEST['count'] : 20); - $page = (x($_REQUEST, 'page') ? $_REQUEST['page'] - 1 : 0); + $id = intval(defaults($a->argv , 3 , 0)); + $since_id = intval(defaults($_REQUEST, 'since_id', 0)); + $max_id = intval(defaults($_REQUEST, 'max_id' , 0)); + $count = intval(defaults($_REQUEST, 'count' , 20)); + $page = intval(defaults($_REQUEST, 'page' , 1)) - 1; if ($page < 0) { $page = 0; } - $since_id = (x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0); - $max_id = (x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0); - $start = $page*$count; + $start = $page * $count; if ($id == 0) { - $id = intval($_REQUEST["id"]); + $id = intval(defaults($_REQUEST, 'id', 0)); } // Hotot workaround if ($id == 0) { - $id = intval($a->argv[4]); + $id = intval(defaults($a->argv, 4, 0)); } logger('API: api_conversation_show: '.$id); // try to fetch the item for the local user - or the public item, if there is no local one - $item = dba::selectFirst('item', ['parent-uri'], ['id' => $id]); - if (!DBM::is_result($item)) { + $item = Item::selectFirst(['parent-uri'], ['id' => $id]); + if (!DBA::isResult($item)) { throw new BadRequestException("There is no status with this id."); } - $parent = dba::selectFirst('item', ['id'], ['uri' => $item['parent-uri'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); - if (!DBM::is_result($parent)) { + $parent = Item::selectFirst(['id'], ['uri' => $item['parent-uri'], 'uid' => [0, api_user()]], ['order' => ['uid' => true]]); + if (!DBA::isResult($parent)) { throw new BadRequestException("There is no status with this id."); } $id = $parent['id']; - $sql_extra = ''; + $condition = ["`parent` = ? AND `uid` IN (0, ?) AND `gravity` IN (?, ?) AND `item`.`id` > ?", + $id, api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id]; if ($max_id > 0) { - $sql_extra = ' AND `item`.`id` <= ' . intval($max_id); + $condition[0] .= " AND `item`.`id` <= ?"; + $condition[] = $max_id; } - $r = q( - "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, - `contact`.`id` AS `cid` - FROM `item` - STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` - AND (NOT `contact`.`blocked` OR `contact`.`pending`) - WHERE `item`.`parent` = %d AND `item`.`visible` - AND NOT `item`.`moderated` AND NOT `item`.`deleted` - AND `item`.`uid` IN (0, %d) AND `item`.`verb` = '%s' - AND `item`.`id`>%d $sql_extra - ORDER BY `item`.`id` DESC LIMIT %d ,%d", - intval($id), - intval(api_user()), - dbesc(ACTIVITY_POST), - intval($since_id), - intval($start), - intval($count) - ); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Item::selectForUser(api_user(), [], $condition, $params); - if (!DBM::is_result($r)) { - throw new BadRequestException("There is no status with this id."); + if (!DBA::isResult($statuses)) { + throw new BadRequestException("There is no status with id $id."); } - $ret = api_format_items($r, $user_info, false, $type); + $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); $data = ['status' => $ret]; return api_format_data("statuses", $type, $data); @@ -2162,62 +1966,48 @@ function api_statuses_repeat($type) api_get_user($a); // params - $id = intval($a->argv[3]); + $id = intval(defaults($a->argv, 3, 0)); if ($id == 0) { - $id = intval($_REQUEST["id"]); + $id = intval(defaults($_REQUEST, 'id', 0)); } // Hotot workaround if ($id == 0) { - $id = intval($a->argv[4]); + $id = intval(defaults($a->argv, 4, 0)); } logger('API: api_statuses_repeat: '.$id); - $r = q( - "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, `contact`.`nick` as `reply_author`, - `contact`.`name`, `contact`.`photo` as `reply_photo`, `contact`.`url` as `reply_url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, - `contact`.`id` AS `cid` - FROM `item` - INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` - AND (NOT `contact`.`blocked` OR `contact`.`pending`) - WHERE `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` - AND NOT `item`.`private` AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' - AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' - AND `item`.`id`=%d", - intval($id) - ); + $fields = ['body', 'author-name', 'author-link', 'author-avatar', 'guid', 'created', 'plink']; + $item = Item::selectFirst($fields, ['id' => $id, 'private' => false]); - /// @TODO other style than above functions! - if (DBM::is_result($r) && $r[0]['body'] != "") { - if (strpos($r[0]['body'], "[/share]") !== false) { - $pos = strpos($r[0]['body'], "[share"); - $post = substr($r[0]['body'], $pos); + if (DBA::isResult($item) && $item['body'] != "") { + if (strpos($item['body'], "[/share]") !== false) { + $pos = strpos($item['body'], "[share"); + $post = substr($item['body'], $pos); } else { - $post = share_header($r[0]['author-name'], $r[0]['author-link'], $r[0]['author-avatar'], $r[0]['guid'], $r[0]['created'], $r[0]['plink']); + $post = share_header($item['author-name'], $item['author-link'], $item['author-avatar'], $item['guid'], $item['created'], $item['plink']); - $post .= $r[0]['body']; + $post .= $item['body']; $post .= "[/share]"; } $_REQUEST['body'] = $post; $_REQUEST['profile_uid'] = api_user(); - $_REQUEST['type'] = 'wall'; $_REQUEST['api_source'] = true; if (!x($_REQUEST, "source")) { $_REQUEST["source"] = api_source(); } - item_post($a); + $item_id = item_post($a); } else { throw new ForbiddenException(); } - // this should output the last post (the one we just posted). - $called_api = null; - return api_status_show($type); + // output the post that we just posted. + $called_api = []; + return api_status_show($type, $item_id); } /// @TODO move to top of file or somewhere better @@ -2241,15 +2031,15 @@ function api_statuses_destroy($type) api_get_user($a); // params - $id = intval($a->argv[3]); + $id = intval(defaults($a->argv, 3, 0)); if ($id == 0) { - $id = intval($_REQUEST["id"]); + $id = intval(defaults($_REQUEST, 'id', 0)); } // Hotot workaround if ($id == 0) { - $id = intval($a->argv[4]); + $id = intval(defaults($a->argv, 4, 0)); } logger('API: api_statuses_destroy: '.$id); @@ -2299,43 +2089,19 @@ function api_statuses_mentions($type) $start = ($page - 1) * $count; - // Ugly code - should be changed - $myurl = System::baseUrl() . '/profile/'. $a->user['nickname']; - $myurl = substr($myurl, strpos($myurl, '://') + 3); - $myurl = str_replace('www.', '', $myurl); - - $sql_extra = ''; + $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `item`.`id` > ? AND `author-id` != ? + AND `item`.`parent` IN (SELECT `iid` FROM `thread` WHERE `thread`.`uid` = ? AND `thread`.`mention` AND NOT `thread`.`ignored`)", + api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, $user_info['pid'], api_user()]; if ($max_id > 0) { - $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); + $condition[0] .= " AND `item`.`id` <= ?"; + $condition[] = $max_id; } - $r = q( - "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, - `contact`.`id` AS `cid` - FROM `item` FORCE INDEX (`uid_id`) - STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` - AND (NOT `contact`.`blocked` OR `contact`.`pending`) - WHERE `item`.`uid` = %d AND `verb` = '%s' - AND NOT (`item`.`author-link` IN ('https://%s', 'http://%s')) - AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` - AND `item`.`parent` IN (SELECT `iid` FROM `thread` WHERE `uid` = %d AND `mention` AND !`ignored`) - $sql_extra - AND `item`.`id`>%d - ORDER BY `item`.`id` DESC LIMIT %d ,%d ", - intval(api_user()), - dbesc(ACTIVITY_POST), - dbesc(protect_sprintf($myurl)), - dbesc(protect_sprintf($myurl)), - intval(api_user()), - intval($since_id), - intval($start), - intval($count) - ); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Item::selectForUser(api_user(), [], $condition, $params); - $ret = api_format_items($r, $user_info, false, $type); + $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); $data = ['status' => $ret]; switch ($type) { @@ -2391,46 +2157,31 @@ function api_statuses_user_timeline($type) } $start = ($page - 1) * $count; - $sql_extra = ''; + $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `item`.`id` > ? AND `item`.`contact-id` = ?", + api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, $user_info['cid']]; + if ($user_info['self'] == 1) { - $sql_extra .= " AND `item`.`wall` = 1 "; + $condition[0] .= ' AND `item`.`wall` '; } if ($exclude_replies > 0) { - $sql_extra .= ' AND `item`.`parent` = `item`.`id`'; + $condition[0] .= ' AND `item`.`parent` = `item`.`id`'; } if ($conversation_id > 0) { - $sql_extra .= ' AND `item`.`parent` = ' . intval($conversation_id); + $condition[0] .= " AND `item`.`parent` = ?"; + $condition[] = $conversation_id; } if ($max_id > 0) { - $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); + $condition[0] .= " AND `item`.`id` <= ?"; + $condition[] = $max_id; } - $r = q( - "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, - `contact`.`id` AS `cid` - FROM `item` FORCE INDEX (`uid_contactid_id`) - STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` - AND (NOT `contact`.`blocked` OR `contact`.`pending`) - WHERE `item`.`uid` = %d AND `verb` = '%s' - AND `item`.`contact-id` = %d - AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` - $sql_extra - AND `item`.`id` > %d - ORDER BY `item`.`id` DESC LIMIT %d ,%d ", - intval(api_user()), - dbesc(ACTIVITY_POST), - intval($user_info['cid']), - intval($since_id), - intval($start), - intval($count) - ); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Item::selectForUser(api_user(), [], $condition, $params); - $ret = api_format_items($r, $user_info, true, $type); + $ret = api_format_items(Item::inArray($statuses), $user_info, true, $type); $data = ['status' => $ret]; switch ($type) { @@ -2465,7 +2216,7 @@ function api_favorites_create_destroy($type) // for versioned api. /// @TODO We need a better global soluton $action_argv_id = 2; - if ($a->argv[1] == "1.1") { + if (count($a->argv) > 1 && $a->argv[1] == "1.1") { $action_argv_id = 3; } @@ -2474,30 +2225,29 @@ function api_favorites_create_destroy($type) } $action = str_replace("." . $type, "", $a->argv[$action_argv_id]); if ($a->argc == $action_argv_id + 2) { - $itemid = intval($a->argv[$action_argv_id + 1]); + $itemid = intval(defaults($a->argv, $action_argv_id + 1, 0)); } else { - /// @TODO use x() to check if _REQUEST contains 'id' - $itemid = intval($_REQUEST['id']); + $itemid = intval(defaults($_REQUEST, 'id', 0)); } - $item = q("SELECT * FROM `item` WHERE `id`=%d AND `uid`=%d LIMIT 1", $itemid, api_user()); + $item = Item::selectFirstForUser(api_user(), [], ['id' => $itemid, 'uid' => api_user()]); - if (!DBM::is_result($item) || count($item) == 0) { + if (!DBA::isResult($item)) { throw new BadRequestException("Invalid item."); } switch ($action) { case "create": - $item[0]['starred'] = 1; + $item['starred'] = 1; break; case "destroy": - $item[0]['starred'] = 0; + $item['starred'] = 0; break; default: throw new BadRequestException("Invalid action ".$action); } - $r = Item::update(['starred' => $item[0]['starred']], ['id' => $itemid]); + $r = Item::update(['starred' => $item['starred']], ['id' => $itemid]); if ($r === false) { throw new InternalServerErrorException("DB error"); @@ -2505,7 +2255,7 @@ function api_favorites_create_destroy($type) $user_info = api_get_user($a); - $rets = api_format_items($item, $user_info, false, $type); + $rets = api_format_items([$item], $user_info, false, $type); $ret = $rets[0]; $data = ['status' => $ret]; @@ -2549,8 +2299,6 @@ function api_favorites($type) if ($user_info['self'] == 0) { $ret = []; } else { - $sql_extra = ""; - // params $since_id = (x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0); $max_id = (x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0); @@ -2562,31 +2310,19 @@ function api_favorites($type) $start = $page*$count; + $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `id` > ? AND `starred`", + api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id]; + + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + if ($max_id > 0) { - $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); + $condition[0] .= " AND `item`.`id` <= ?"; + $condition[] = $max_id; } - $r = q( - "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, - `contact`.`id` AS `cid` - FROM `item`, `contact` - WHERE `item`.`uid` = %d - AND `item`.`visible` = 1 AND `item`.`moderated` = 0 AND `item`.`deleted` = 0 - AND `item`.`starred` = 1 - AND `contact`.`id` = `item`.`contact-id` - AND (NOT `contact`.`blocked` OR `contact`.`pending`) - $sql_extra - AND `item`.`id`>%d - ORDER BY `item`.`id` DESC LIMIT %d ,%d ", - intval(api_user()), - intval($since_id), - intval($start), - intval($count) - ); + $statuses = Item::selectForUser(api_user(), [], $condition, $params); - $ret = api_format_items($r, $user_info, false, $type); + $ret = api_format_items(Item::inArray($statuses), $user_info, false, $type); } $data = ['status' => $ret]; @@ -2614,25 +2350,33 @@ function api_format_messages($item, $recipient, $sender) { // standard meta information $ret = [ - 'id' => $item['id'], - 'sender_id' => $sender['id'] , - 'text' => "", - 'recipient_id' => $recipient['id'], - 'created_at' => api_date($item['created']), - 'sender_screen_name' => $sender['screen_name'], - 'recipient_screen_name' => $recipient['screen_name'], - 'sender' => $sender, - 'recipient' => $recipient, - 'title' => "", - 'friendica_seen' => $item['seen'], - 'friendica_parent_uri' => $item['parent-uri'], + 'id' => $item['id'], + 'sender_id' => $sender['id'] , + 'text' => "", + 'recipient_id' => $recipient['id'], + 'created_at' => api_date(defaults($item, 'created', DateTimeFormat::utcNow())), + 'sender_screen_name' => $sender['screen_name'], + 'recipient_screen_name' => $recipient['screen_name'], + 'sender' => $sender, + 'recipient' => $recipient, + 'title' => "", + 'friendica_seen' => defaults($item, 'seen', 0), + 'friendica_parent_uri' => defaults($item, 'parent-uri', ''), ]; // "uid" and "self" are only needed for some internal stuff, so remove it from here - unset($ret["sender"]["uid"]); - unset($ret["sender"]["self"]); - unset($ret["recipient"]["uid"]); - unset($ret["recipient"]["self"]); + if (isset($ret['sender']['uid'])) { + unset($ret['sender']['uid']); + } + if (isset($ret['sender']['self'])) { + unset($ret['sender']['self']); + } + if (isset($ret['recipient']['uid'])) { + unset($ret['recipient']['uid']); + } + if (isset($ret['recipient']['self'])) { + unset($ret['recipient']['self']); + } //don't send title to regular StatusNET requests to avoid confusing these apps if (x($_GET, 'getText')) { @@ -2679,8 +2423,8 @@ function api_convert_item($item) $statustext = trim($statustitle."\n\n".$statusbody); } - if (($item["network"] == NETWORK_FEED) && (strlen($statustext)> 1000)) { - $statustext = substr($statustext, 0, 1000)."... \n".$item["plink"]; + if ((defaults($item, 'network', Protocol::PHANTOM) == Protocol::FEED) && (strlen($statustext)> 1000)) { + $statustext = substr($statustext, 0, 1000) . "... \n" . defaults($item, 'plink', ''); } $statushtml = BBCode::convert(api_clean_attachments($body), false); @@ -2714,7 +2458,7 @@ function api_convert_item($item) } // feeds without body should contain the link - if (($item['network'] == NETWORK_FEED) && (strlen($item['body']) == 0)) { + if ((defaults($item, 'network', Protocol::PHANTOM) == Protocol::FEED) && (strlen($item['body']) == 0)) { $statushtml .= BBCode::convert($item['plink']); } @@ -2732,7 +2476,7 @@ function api_convert_item($item) * * @param string $body * - * @return array|false + * @return array */ function api_get_attachments(&$body) { @@ -2743,7 +2487,7 @@ function api_get_attachments(&$body) $ret = preg_match_all("/\[img\]([$URLSearchString]*)\[\/img\]/ism", $text, $images); if (!$ret) { - return false; + return []; } $attachments = []; @@ -2756,7 +2500,7 @@ function api_get_attachments(&$body) } } - if (strstr($_SERVER['HTTP_USER_AGENT'], "AndStatus")) { + if (strstr(defaults($_SERVER, 'HTTP_USER_AGENT', ''), "AndStatus")) { foreach ($images[0] as $orig) { $body = str_replace($orig, "", $body); } @@ -2781,7 +2525,7 @@ function api_get_entitities(&$text, $bbcode) preg_match_all("/\[img](.*?)\[\/img\]/ism", $bbcode, $images); foreach ($images[1] as $image) { - $replace = proxy_url($image); + $replace = ProxyUtils::proxifyUrl($image); $text = str_replace($image, $replace, $text); } return []; @@ -2890,7 +2634,7 @@ function api_get_entitities(&$text, $bbcode) // If image cache is activated, then use the following sizes: // thumb (150), small (340), medium (600) and large (1024) if (!Config::get("system", "proxy_disabled")) { - $media_url = proxy_url($url); + $media_url = ProxyUtils::proxifyUrl($url); $sizes = []; $scale = Image::getScalingDimensions($image[0], $image[1], 150); @@ -3000,20 +2744,16 @@ function api_format_items_activities(&$item, $type = "json") 'attendmaybe' => [], ]; - $items = q( - 'SELECT * FROM item - WHERE uid=%d AND `thr-parent`="%s" AND visible AND NOT deleted', - intval($item['uid']), - dbesc($item['uri']) - ); + $condition = ['uid' => $item['uid'], 'thr-parent' => $item['uri']]; + $ret = Item::selectForUser($item['uid'], ['author-id', 'verb'], $condition); - foreach ($items as $i) { + while ($item = Item::fetch($ret)) { // not used as result should be structured like other user data //builtin_activity_puller($i, $activities); // get user data and add it to the array of the activity - $user = api_get_user($a, $i['author-link']); - switch ($i['verb']) { + $user = api_get_user($a, $item['author-id']); + switch ($item['verb']) { case ACTIVITY_LIKE: $activities['like'][] = $user; break; @@ -3034,6 +2774,8 @@ function api_format_items_activities(&$item, $type = "json") } } + DBA::close($ret); + if ($type == "xml") { $xml_activities = []; foreach ($activities as $k => $v) { @@ -3153,6 +2895,7 @@ function api_format_items($r, $user_info, $filter_user = false, $type = "json") 'favorited' => $item['starred'] ? true : false, 'user' => $status_user , 'friendica_owner' => $owner_user, + 'friendica_private' => $item['private'] == 1, //'entities' => NULL, 'statusnet_html' => $converted["html"], 'statusnet_conversation_id' => $item['parent'], @@ -3168,26 +2911,18 @@ function api_format_items($r, $user_info, $filter_user = false, $type = "json") $status["entities"] = $converted["entities"]; } - if (($item['item_network'] != "") && ($status["source"] == 'web')) { - $status["source"] = ContactSelector::networkToName($item['item_network'], $user_info['url']); - } elseif (($item['item_network'] != "") && (ContactSelector::networkToName($item['item_network'], $user_info['url']) != $status["source"])) { - $status["source"] = trim($status["source"].' ('.ContactSelector::networkToName($item['item_network'], $user_info['url']).')'); + if ($status["source"] == 'web') { + $status["source"] = ContactSelector::networkToName($item['network'], $user_info['url']); + } elseif (ContactSelector::networkToName($item['network'], $user_info['url']) != $status["source"]) { + $status["source"] = trim($status["source"].' ('.ContactSelector::networkToName($item['network'], $user_info['url']).')'); } - - // Retweets are only valid for top postings - // It doesn't work reliable with the link if its a feed - //$IsRetweet = ($item['owner-link'] != $item['author-link']); - //if ($IsRetweet) - // $IsRetweet = (($item['owner-name'] != $item['author-name']) || ($item['owner-avatar'] != $item['author-avatar'])); - - if ($item["id"] == $item["parent"]) { $retweeted_item = api_share_as_retweet($item); if ($retweeted_item !== false) { $retweeted_status = $status; try { - $retweeted_status["user"] = api_get_user($a, $retweeted_item["author-link"]); + $retweeted_status["user"] = api_get_user($a, $retweeted_item["author-id"]); } catch (BadRequestException $e) { // user not found. should be found? /// @todo check if the user should be always found @@ -3320,7 +3055,7 @@ function api_lists_ownerships($type) $user_info = api_get_user($a); $uid = $user_info['uid']; - $groups = dba::select('group', [], ['deleted' => 0, 'uid' => $uid]); + $groups = DBA::select('group', [], ['deleted' => 0, 'uid' => $uid]); // loop through all groups $lists = []; @@ -3384,39 +3119,25 @@ function api_lists_statuses($type) $start = $page * $count; - $sql_extra = ''; + $condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `id` > ? AND `group_member`.`gid` = ?", + api_user(), GRAVITY_PARENT, GRAVITY_COMMENT, $since_id, $_REQUEST['list_id']]; + if ($max_id > 0) { - $sql_extra .= ' AND `item`.`id` <= ' . intval($max_id); + $condition[0] .= " AND `item`.`id` <= ?"; + $condition[] = $max_id; } if ($exclude_replies > 0) { - $sql_extra .= ' AND `item`.`parent` = `item`.`id`'; + $condition[0] .= ' AND `item`.`parent` = `item`.`id`'; } if ($conversation_id > 0) { - $sql_extra .= ' AND `item`.`parent` = ' . intval($conversation_id); + $condition[0] .= " AND `item`.`parent` = ?"; + $condition[] = $conversation_id; } - $statuses = dba::p( - "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, - `contact`.`id` AS `cid`, `group_member`.`gid` - FROM `item` - STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` - AND (NOT `contact`.`blocked` OR `contact`.`pending`) - STRAIGHT_JOIN `group_member` ON `group_member`.`contact-id` = `item`.`contact-id` - WHERE `item`.`uid` = ? AND `verb` = ? - AND `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted` - $sql_extra - AND `item`.`id`>? - AND `group_member`.`gid` = ? - ORDER BY `item`.`id` DESC LIMIT ".intval($start)." ,".intval($count), - api_user(), - ACTIVITY_POST, - $since_id, - $_REQUEST['list_id'] - ); + $params = ['order' => ['id' => true], 'limit' => [$start, $count]]; + $statuses = Item::selectForUser(api_user(), [], $condition, $params); - $items = api_format_items(dba::inArray($statuses), $user_info, false, $type); + $items = api_format_items(Item::inArray($statuses), $user_info, false, $type); $data = ['status' => $items]; switch ($type) { @@ -3473,9 +3194,9 @@ function api_statuses_f($qtype) $sql_extra = ''; if ($qtype == 'friends') { - $sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(CONTACT_IS_SHARING), intval(CONTACT_IS_FRIEND)); + $sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(Contact::SHARING), intval(Contact::FRIEND)); } elseif ($qtype == 'followers') { - $sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(CONTACT_IS_FOLLOWER), intval(CONTACT_IS_FRIEND)); + $sql_extra = sprintf(" AND ( `rel` = %d OR `rel` = %d ) ", intval(Contact::FOLLOWER), intval(Contact::FRIEND)); } // friends and followers only for self @@ -3622,18 +3343,15 @@ function api_statusnet_config($type) { $a = get_app(); - $name = $a->config['sitename']; - $server = $a->get_hostname(); - $logo = System::baseUrl() . '/images/friendica-64.png'; - $email = $a->config['admin_email']; - $closed = (($a->config['register_policy'] == REGISTER_CLOSED) ? 'true' : 'false'); - $private = ((Config::get('system', 'block_public')) ? 'true' : 'false'); - $textlimit = (string) (($a->config['max_import_size']) ? $a->config['max_import_size'] : 200000); - if ($a->config['api_import_size']) { - $textlimit = (string) $a->config['api_import_size']; - } - $ssl = ((Config::get('system', 'have_ssl')) ? 'true' : 'false'); - $sslserver = (($ssl === 'true') ? str_replace('http:', 'https:', System::baseUrl()) : ''); + $name = Config::get('config', 'sitename'); + $server = $a->get_hostname(); + $logo = System::baseUrl() . '/images/friendica-64.png'; + $email = Config::get('config', 'admin_email'); + $closed = intval(Config::get('config', 'register_policy')) === REGISTER_CLOSED ? 'true' : 'false'; + $private = Config::get('system', 'block_public') ? 'true' : 'false'; + $textlimit = (string) Config::get('config', 'api_import_size', Config::get('config', 'max_import_size', 200000)); + $ssl = Config::get('system', 'have_ssl') ? 'true' : 'false'; + $sslserver = Config::get('system', 'have_ssl') ? str_replace('http:', 'https:', System::baseUrl()) : ''; $config = [ 'site' => ['name' => $name,'server' => $server, 'theme' => 'default', 'path' => '', @@ -3699,7 +3417,7 @@ function api_ff_ids($type) WHERE `contact`.`uid` = %s AND NOT `contact`.`self`", intval(api_user()) ); - if (!DBM::is_result($r)) { + if (!DBA::isResult($r)) { return; } @@ -3755,34 +3473,40 @@ api_register_func('api/followers/ids', 'api_followers_ids', true); */ function api_direct_messages_new($type) { - $a = get_app(); if (api_user() === false) { throw new ForbiddenException(); } - if (!x($_POST, "text") || (!x($_POST, "screen_name") && !x($_POST, "user_id"))) { + if (empty($_POST["text"]) || empty($_POST["screen_name"]) && empty($_POST["user_id"])) { return; } $sender = api_get_user($a); - if ($_POST['screen_name']) { + $recipient = null; + if (!empty($_POST['screen_name'])) { $r = q( "SELECT `id`, `nurl`, `network` FROM `contact` WHERE `uid`=%d AND `nick`='%s'", intval(api_user()), - dbesc($_POST['screen_name']) + DBA::escape($_POST['screen_name']) ); - // Selecting the id by priority, friendica first - api_best_nickname($r); + if (DBA::isResult($r)) { + // Selecting the id by priority, friendica first + api_best_nickname($r); - $recipient = api_get_user($a, $r[0]['nurl']); + $recipient = api_get_user($a, $r[0]['nurl']); + } } else { $recipient = api_get_user($a, $_POST['user_id']); } + if (empty($recipient)) { + throw new NotFoundException('Recipient not found'); + } + $replyto = ''; $sub = ''; if (x($_REQUEST, 'replyto')) { @@ -3863,7 +3587,7 @@ function api_direct_messages_destroy($type) } // add parent-uri to sql command if specified by calling app - $sql_extra = ($parenturi != "" ? " AND `parent-uri` = '" . dbesc($parenturi) . "'" : ""); + $sql_extra = ($parenturi != "" ? " AND `parent-uri` = '" . DBA::escape($parenturi) . "'" : ""); // get data of the specified message id $r = q( @@ -3873,7 +3597,7 @@ function api_direct_messages_destroy($type) ); // error message if specified id is not in database - if (!DBM::is_result($r)) { + if (!DBA::isResult($r)) { if ($verbose == "true") { $answer = ['result' => 'error', 'message' => 'message id not in database']; return api_format_data("direct_messages_delete", $type, ['$result' => $answer]); @@ -3916,24 +3640,21 @@ api_register_func('api/direct_messages/destroy', 'api_direct_messages_destroy', function api_direct_messages_box($type, $box, $verbose) { $a = get_app(); - $user_info = api_get_user($a); - - if (api_user() === false || $user_info === false) { + if (api_user() === false) { throw new ForbiddenException(); } - // params - $count = (x($_GET, 'count') ? $_GET['count'] : 20); - $page = (x($_REQUEST, 'page') ? $_REQUEST['page'] -1 : 0); + $count = defaults($_GET, 'count', 20); + $page = defaults($_REQUEST, 'page', 1) - 1; if ($page < 0) { $page = 0; } - $since_id = (x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0); - $max_id = (x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0); + $since_id = defaults($_REQUEST, 'since_id', 0); + $max_id = defaults($_REQUEST, 'max_id', 0); - $user_id = (x($_REQUEST, 'user_id') ? $_REQUEST['user_id'] : ""); - $screen_name = (x($_REQUEST, 'screen_name') ? $_REQUEST['screen_name'] : ""); + $user_id = defaults($_REQUEST, 'user_id', ''); + $screen_name = defaults($_REQUEST, 'screen_name', ''); // caller user info unset($_REQUEST["user_id"]); @@ -3942,6 +3663,10 @@ function api_direct_messages_box($type, $box, $verbose) unset($_REQUEST["screen_name"]); unset($_GET["screen_name"]); + $user_info = api_get_user($a); + if ($user_info === false) { + throw new ForbiddenException(); + } $profile_url = $user_info["url"]; // pagination @@ -3951,13 +3676,13 @@ function api_direct_messages_box($type, $box, $verbose) // filters if ($box=="sentbox") { - $sql_extra = "`mail`.`from-url`='" . dbesc($profile_url) . "'"; + $sql_extra = "`mail`.`from-url`='" . DBA::escape($profile_url) . "'"; } elseif ($box == "conversation") { - $sql_extra = "`mail`.`parent-uri`='" . dbesc($_GET["uri"]) . "'"; + $sql_extra = "`mail`.`parent-uri`='" . DBA::escape(defaults($_GET, 'uri', '')) . "'"; } elseif ($box == "all") { $sql_extra = "true"; } elseif ($box == "inbox") { - $sql_extra = "`mail`.`from-url`!='" . dbesc($profile_url) . "'"; + $sql_extra = "`mail`.`from-url`!='" . DBA::escape($profile_url) . "'"; } if ($max_id > 0) { @@ -3967,7 +3692,7 @@ function api_direct_messages_box($type, $box, $verbose) if ($user_id != "") { $sql_extra .= ' AND `mail`.`contact-id` = ' . intval($user_id); } elseif ($screen_name !="") { - $sql_extra .= " AND `contact`.`nick` = '" . dbesc($screen_name). "'"; + $sql_extra .= " AND `contact`.`nick` = '" . DBA::escape($screen_name). "'"; } $r = q( @@ -3977,7 +3702,7 @@ function api_direct_messages_box($type, $box, $verbose) intval($start), intval($count) ); - if ($verbose == "true" && !DBM::is_result($r)) { + if ($verbose == "true" && !DBA::isResult($r)) { $answer = ['result' => 'error', 'message' => 'no mails available']; return api_format_data("direct_messages_all", $type, ['$result' => $answer]); } @@ -4130,29 +3855,26 @@ function api_fr_photoalbum_delete($type) $r = q( "SELECT DISTINCT `resource-id` FROM `photo` WHERE `uid` = %d AND `album` = '%s'", intval(api_user()), - dbesc($album) + DBA::escape($album) ); - if (!DBM::is_result($r)) { + if (!DBA::isResult($r)) { throw new BadRequestException("album not available"); } // function for setting the items to "deleted = 1" which ensures that comments, likes etc. are not shown anymore // to the user and the contacts of the users (drop_items() performs the federation of the deletion to other networks foreach ($r as $rr) { - $photo_item = q( - "SELECT `id` FROM `item` WHERE `uid` = %d AND `resource-id` = '%s' AND `type` = 'photo'", - intval(local_user()), - dbesc($rr['resource-id']) - ); + $condition = ['uid' => local_user(), 'resource-id' => $rr['resource-id'], 'type' => 'photo']; + $photo_item = Item::selectFirstForUser(local_user(), ['id'], $condition); - if (!DBM::is_result($photo_item)) { + if (!DBA::isResult($photo_item)) { throw new InternalServerErrorException("problem with deleting items occured"); } - Item::deleteForUser(['id' => $photo_item[0]['id']], api_user()); + Item::deleteForUser(['id' => $photo_item['id']], api_user()); } // now let's delete all photos from the album - $result = dba::delete('photo', ['uid' => api_user(), 'album' => $album]); + $result = DBA::delete('photo', ['uid' => api_user(), 'album' => $album]); // return success of deletion or error message if ($result) { @@ -4186,11 +3908,11 @@ function api_fr_photoalbum_update($type) throw new BadRequestException("no new albumname specified"); } // check if album is existing - if (!dba::exists('photo', ['uid' => api_user(), 'album' => $album])) { + if (!DBA::exists('photo', ['uid' => api_user(), 'album' => $album])) { throw new BadRequestException("album not available"); } // now let's update all photos to the albumname - $result = dba::update('photo', ['album' => $album_new], ['uid' => api_user(), 'album' => $album]); + $result = DBA::update('photo', ['album' => $album_new], ['uid' => api_user(), 'album' => $album]); // return success of updating or error message if ($result) { @@ -4225,7 +3947,7 @@ function api_fr_photos_list($type) 'image/gif' => 'gif' ]; $data = ['photo'=>[]]; - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { foreach ($r as $rr) { $photo = []; $photo['id'] = $rr['resource-id']; @@ -4294,10 +4016,10 @@ function api_fr_photo_create_update($type) $r = q( "SELECT `id` FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' AND `album` = '%s'", intval(api_user()), - dbesc($photo_id), - dbesc($album) + DBA::escape($photo_id), + DBA::escape($album) ); - if (!DBM::is_result($r)) { + if (!DBA::isResult($r)) { throw new BadRequestException("photo not available"); } } @@ -4364,8 +4086,8 @@ function api_fr_photo_create_update($type) $sql_extra, DateTimeFormat::utcNow(), // update edited timestamp intval(api_user()), - dbesc($photo_id), - dbesc($album) + DBA::escape($photo_id), + DBA::escape($album) ); } else { $nothingtodo = true; @@ -4395,7 +4117,6 @@ function api_fr_photo_create_update($type) throw new InternalServerErrorException("unknown error - this error on uploading or updating a photo should never happen"); } - /** * @brief delete a single photo from the database through api * @@ -4419,29 +4140,26 @@ function api_fr_photo_delete($type) $r = q( "SELECT `id` FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s'", intval(api_user()), - dbesc($photo_id) + DBA::escape($photo_id) ); - if (!DBM::is_result($r)) { + if (!DBA::isResult($r)) { throw new BadRequestException("photo not available"); } // now we can perform on the deletion of the photo - $result = dba::delete('photo', ['uid' => api_user(), 'resource-id' => $photo_id]); + $result = DBA::delete('photo', ['uid' => api_user(), 'resource-id' => $photo_id]); // return success of deletion or error message if ($result) { // retrieve the id of the parent element (the photo element) - $photo_item = q( - "SELECT `id` FROM `item` WHERE `uid` = %d AND `resource-id` = '%s' AND `type` = 'photo'", - intval(local_user()), - dbesc($photo_id) - ); + $condition = ['uid' => local_user(), 'resource-id' => $photo_id, 'type' => 'photo']; + $photo_item = Item::selectFirstForUser(local_user(), ['id'], $condition); - if (!DBM::is_result($photo_item)) { + if (!DBA::isResult($photo_item)) { throw new InternalServerErrorException("problem with deleting items occured"); } // function for setting the items to "deleted = 1" which ensures that comments, likes etc. are not shown anymore // to the user and the contacts of the users (drop_items() do all the necessary magic to avoid orphans in database and federate deletion) - Item::deleteForUser(['id' => $photo_item[0]['id']], api_user()); + Item::deleteForUser(['id' => $photo_item['id']], api_user()); $answer = ['result' => 'deleted', 'message' => 'photo with id `' . $photo_id . '` has been deleted from server.']; return api_format_data("photo_delete", $type, ['$result' => $answer]); @@ -4501,9 +4219,9 @@ function api_account_update_profile_image($type) // check if specified profile id is valid if ($profile_id != 0) { - $profile = dba::selectFirst('profile', ['is-default'], ['uid' => api_user(), 'id' => $profile_id]); + $profile = DBA::selectFirst('profile', ['is-default'], ['uid' => api_user(), 'id' => $profile_id]); // error message if specified profile id is not in database - if (!DBM::is_result($profile)) { + if (!DBA::isResult($profile)) { throw new BadRequestException("profile_id not available"); } $is_default_profile = $profile['is-default']; @@ -4534,20 +4252,20 @@ function api_account_update_profile_image($type) } else { throw new InternalServerErrorException('Unsupported filetype'); } + // change specified profile or all profiles to the new resource-id if ($is_default_profile) { $condition = ["`profile` AND `resource-id` != ? AND `uid` = ?", $data['photo']['id'], api_user()]; - dba::update('photo', ['profile' => false], $condition); + DBA::update('photo', ['profile' => false], $condition); } else { $fields = ['photo' => System::baseUrl() . '/photo/' . $data['photo']['id'] . '-4.' . $filetype, 'thumb' => System::baseUrl() . '/photo/' . $data['photo']['id'] . '-5.' . $filetype]; - dba::update('profile', $fields, ['id' => $_REQUEST['profile'], 'uid' => api_user()]); + DBA::update('profile', $fields, ['id' => $_REQUEST['profile'], 'uid' => api_user()]); } Contact::updateSelfFromUserID(api_user(), true); // Update global directory in background - //$user = api_get_user(get_app()); $url = System::baseUrl() . '/profile/' . get_app()->user['nickname']; if ($url && strlen(Config::get('system', 'directory'))) { Worker::add(PRIORITY_LOW, "Directory", $url); @@ -4587,16 +4305,16 @@ function api_account_update_profile($type) $api_user = api_get_user(get_app()); if (!empty($_POST['name'])) { - dba::update('profile', ['name' => $_POST['name']], ['uid' => $local_user]); - dba::update('user', ['username' => $_POST['name']], ['uid' => $local_user]); - dba::update('contact', ['name' => $_POST['name']], ['uid' => $local_user, 'self' => 1]); - dba::update('contact', ['name' => $_POST['name']], ['id' => $api_user['id']]); + DBA::update('profile', ['name' => $_POST['name']], ['uid' => $local_user]); + DBA::update('user', ['username' => $_POST['name']], ['uid' => $local_user]); + DBA::update('contact', ['name' => $_POST['name']], ['uid' => $local_user, 'self' => 1]); + DBA::update('contact', ['name' => $_POST['name']], ['id' => $api_user['id']]); } if (isset($_POST['description'])) { - dba::update('profile', ['about' => $_POST['description']], ['uid' => $local_user]); - dba::update('contact', ['about' => $_POST['description']], ['uid' => $local_user, 'self' => 1]); - dba::update('contact', ['about' => $_POST['description']], ['id' => $api_user['id']]); + DBA::update('profile', ['about' => $_POST['description']], ['uid' => $local_user]); + DBA::update('contact', ['about' => $_POST['description']], ['uid' => $local_user, 'self' => 1]); + DBA::update('contact', ['about' => $_POST['description']], ['id' => $api_user['id']]); } Worker::add(PRIORITY_LOW, 'ProfileUpdate', $local_user); @@ -4630,12 +4348,8 @@ function check_acl_input($acl_string) foreach ($cid_array as $cid) { $cid = str_replace("<", "", $cid); $cid = str_replace(">", "", $cid); - $contact = q( - "SELECT * FROM `contact` WHERE `id` = %d AND `uid` = %d", - intval($cid), - intval(api_user()) - ); - $contact_not_found |= !DBM::is_result($contact); + $condition = ['id' => $cid, 'uid' => api_user()]; + $contact_not_found |= !DBA::exists('contact', $condition); } return $contact_not_found; } @@ -4689,7 +4403,7 @@ function save_media_to_database($mediatype, $media, $type, $album, $allow_cid, $ if ($filetype == "") { $filetype=Image::guessType($filename); } - $imagedata = getimagesize($src); + $imagedata = @getimagesize($src); if ($imagedata) { $filetype = $imagedata['mime']; } @@ -4816,24 +4530,24 @@ function save_media_to_database($mediatype, $media, $type, $album, $allow_cid, $ function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $filetype, $visibility = false) { // get data about the api authenticated user - $uri = item_new_uri(get_app()->get_hostname(), intval(api_user())); - $owner_record = q("SELECT * FROM `contact` WHERE `uid`= %d AND `self` LIMIT 1", intval(api_user())); + $uri = Item::newURI(intval(api_user())); + $owner_record = DBA::selectFirst('contact', [], ['uid' => api_user(), 'self' => true]); $arr = []; - $arr['guid'] = get_guid(32); + $arr['guid'] = System::createGUID(32); $arr['uid'] = intval(api_user()); $arr['uri'] = $uri; $arr['parent-uri'] = $uri; $arr['type'] = 'photo'; $arr['wall'] = 1; $arr['resource-id'] = $hash; - $arr['contact-id'] = $owner_record[0]['id']; - $arr['owner-name'] = $owner_record[0]['name']; - $arr['owner-link'] = $owner_record[0]['url']; - $arr['owner-avatar'] = $owner_record[0]['thumb']; - $arr['author-name'] = $owner_record[0]['name']; - $arr['author-link'] = $owner_record[0]['url']; - $arr['author-avatar'] = $owner_record[0]['thumb']; + $arr['contact-id'] = $owner_record['id']; + $arr['owner-name'] = $owner_record['name']; + $arr['owner-link'] = $owner_record['url']; + $arr['owner-avatar'] = $owner_record['thumb']; + $arr['author-name'] = $owner_record['name']; + $arr['author-link'] = $owner_record['url']; + $arr['author-avatar'] = $owner_record['thumb']; $arr['title'] = ""; $arr['allow_cid'] = $allow_cid; $arr['allow_gid'] = $allow_gid; @@ -4849,7 +4563,7 @@ function post_photo_item($hash, $allow_cid, $deny_cid, $allow_gid, $deny_gid, $f ]; // adds link to the thumbnail scale photo - $arr['body'] = '[url=' . System::baseUrl() . '/photos/' . $owner_record[0]['nick'] . '/image/' . $hash . ']' + $arr['body'] = '[url=' . System::baseUrl() . '/photos/' . $owner_record['nick'] . '/image/' . $hash . ']' . '[img]' . System::baseUrl() . '/photo/' . $hash . '-' . "2" . '.'. $typetoext[$filetype] . '[/img]' . '[/url]'; @@ -4886,7 +4600,7 @@ function prepare_photo_data($type, $scale, $photo_id) FROM `photo` WHERE `uid` = %d AND `resource-id` = '%s' %s GROUP BY `resource-id`", $data_sql, intval(local_user()), - dbesc($photo_id), + DBA::escape($photo_id), $scale_sql ); @@ -4897,7 +4611,7 @@ function prepare_photo_data($type, $scale, $photo_id) ]; // prepare output data for photo - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $data = ['photo' => $r[0]]; $data['photo']['id'] = $data['photo']['resource-id']; if ($scale !== false) { @@ -4929,32 +4643,19 @@ function prepare_photo_data($type, $scale, $photo_id) } // retrieve item element for getting activities (like, dislike etc.) related to photo - $item = q( - "SELECT * FROM `item` WHERE `uid` = %d AND `resource-id` = '%s' AND `type` = 'photo'", - intval(local_user()), - dbesc($photo_id) - ); - $data['photo']['friendica_activities'] = api_format_items_activities($item[0], $type); + $condition = ['uid' => local_user(), 'resource-id' => $photo_id, 'type' => 'photo']; + $item = Item::selectFirstForUser(local_user(), ['id'], $condition); + + $data['photo']['friendica_activities'] = api_format_items_activities($item, $type); // retrieve comments on photo - $r = q( - "SELECT `item`.*, `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, - `contact`.`name`, `contact`.`photo`, `contact`.`url`, `contact`.`rel`, - `contact`.`network`, `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`, - `contact`.`id` AS `cid` - FROM `item` - STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid` - AND (NOT `contact`.`blocked` OR `contact`.`pending`) - WHERE `item`.`parent` = %d AND `item`.`visible` - AND NOT `item`.`moderated` AND NOT `item`.`deleted` - AND `item`.`uid` = %d AND (`item`.`verb`='%s' OR `type`='photo')", - intval($item[0]['parent']), - intval(api_user()), - dbesc(ACTIVITY_POST) - ); + $condition = ["`parent` = ? AND `uid` = ? AND (`gravity` IN (?, ?) OR `type`='photo')", + $item[0]['parent'], api_user(), GRAVITY_PARENT, GRAVITY_COMMENT]; + + $statuses = Item::selectForUser(api_user(), [], $condition); // prepare output of comments - $commentData = api_format_items($r, $user_info, false, $type); + $commentData = api_format_items(Item::inArray($statuses), $user_info, false, $type); $comments = []; if ($type == "xml") { $k = 0; @@ -5003,9 +4704,9 @@ function api_friendica_remoteauth() // traditional DFRN - $contact = dba::selectFirst('contact', [], ['uid' => api_user(), 'nurl' => $c_url]); + $contact = DBA::selectFirst('contact', [], ['uid' => api_user(), 'nurl' => $c_url]); - if (!DBM::is_result($contact) || ($contact['network'] !== NETWORK_DFRN)) { + if (!DBA::isResult($contact) || ($contact['network'] !== Protocol::DFRN)) { throw new BadRequestException("Unknown contact"); } @@ -5026,7 +4727,7 @@ function api_friendica_remoteauth() $fields = ['uid' => api_user(), 'cid' => $cid, 'dfrn_id' => $dfrn_id, 'sec' => $sec, 'expire' => time() + 45]; - dba::insert('profile_check', $fields); + DBA::insert('profile_check', $fields); logger($contact['name'] . ' ' . $sec, LOGGER_DEBUG); $dest = ($url ? '&destination_url=' . $url : ''); @@ -5153,20 +4854,20 @@ function api_get_nick($profile) $r = q( "SELECT `nick` FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s'", - dbesc(normalise_link($profile)) + DBA::escape(normalise_link($profile)) ); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $nick = $r[0]["nick"]; } if (!$nick == "") { $r = q( "SELECT `nick` FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s'", - dbesc(normalise_link($profile)) + DBA::escape(normalise_link($profile)) ); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $nick = $r[0]["nick"]; } } @@ -5240,35 +4941,26 @@ function api_in_reply_to($item) $in_reply_to['screen_name'] = null; if (($item['thr-parent'] != $item['uri']) && (intval($item['parent']) != intval($item['id']))) { - $r = q( - "SELECT `id` FROM `item` WHERE `uid` = %d AND `uri` = '%s' LIMIT 1", - intval($item['uid']), - dbesc($item['thr-parent']) - ); - - if (DBM::is_result($r)) { - $in_reply_to['status_id'] = intval($r[0]['id']); + $parent = Item::selectFirst(['id'], ['uid' => $item['uid'], 'uri' => $item['thr-parent']]); + if (DBA::isResult($parent)) { + $in_reply_to['status_id'] = intval($parent['id']); } else { $in_reply_to['status_id'] = intval($item['parent']); } $in_reply_to['status_id_str'] = (string) intval($in_reply_to['status_id']); - $r = q( - "SELECT `contact`.`nick`, `contact`.`name`, `contact`.`id`, `contact`.`url` FROM item - STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`author-id` - WHERE `item`.`id` = %d LIMIT 1", - intval($in_reply_to['status_id']) - ); + $fields = ['author-nick', 'author-name', 'author-id', 'author-link']; + $parent = Item::selectFirst($fields, ['id' => $in_reply_to['status_id']]); - if (DBM::is_result($r)) { - if ($r[0]['nick'] == "") { - $r[0]['nick'] = api_get_nick($r[0]["url"]); + if (DBA::isResult($parent)) { + if ($parent['author-nick'] == "") { + $parent['author-nick'] = api_get_nick($parent['author-link']); } - $in_reply_to['screen_name'] = (($r[0]['nick']) ? $r[0]['nick'] : $r[0]['name']); - $in_reply_to['user_id'] = intval($r[0]['id']); - $in_reply_to['user_id_str'] = (string) intval($r[0]['id']); + $in_reply_to['screen_name'] = (($parent['author-nick']) ? $parent['author-nick'] : $parent['author-name']); + $in_reply_to['user_id'] = intval($parent['author-id']); + $in_reply_to['user_id_str'] = (string) intval($parent['author-id']); } // There seems to be situation, where both fields are identical: @@ -5289,27 +4981,27 @@ function api_in_reply_to($item) /** * - * @param string $Text + * @param string $text * * @return string */ -function api_clean_plain_items($Text) +function api_clean_plain_items($text) { $include_entities = strtolower(x($_REQUEST, 'include_entities') ? $_REQUEST['include_entities'] : "false"); - $Text = BBCode::cleanPictureLinks($Text); + $text = BBCode::cleanPictureLinks($text); $URLSearchString = "^\[\]"; - $Text = preg_replace("/([!#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$1$3', $Text); + $text = preg_replace("/([!#@])\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '$1$3', $text); if ($include_entities == "true") { - $Text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '[url=$1]$1[/url]', $Text); + $text = preg_replace("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism", '[url=$1]$1[/url]', $text); } // Simplify "attachment" element - $Text = api_clean_attachments($Text); + $text = api_clean_attachments($text); - return($Text); + return $text; } /** @@ -5438,7 +5130,7 @@ function api_friendica_group_show($type) intval($gid) ); // error message if specified gid is not in database - if (!DBM::is_result($r)) { + if (!DBA::isResult($r)) { throw new BadRequestException("gid not available"); } } else { @@ -5508,7 +5200,7 @@ function api_friendica_group_delete($type) intval($gid) ); // error message if specified gid is not in database - if (!DBM::is_result($r)) { + if (!DBA::isResult($r)) { throw new BadRequestException('gid not available'); } @@ -5517,10 +5209,10 @@ function api_friendica_group_delete($type) "SELECT * FROM `group` WHERE `uid` = %d AND `id` = %d AND `name` = '%s'", intval($uid), intval($gid), - dbesc($name) + DBA::escape($name) ); // error message if specified gid is not in database - if (!DBM::is_result($rname)) { + if (!DBA::isResult($rname)) { throw new BadRequestException('wrong group name'); } @@ -5563,7 +5255,7 @@ function api_lists_destroy($type) } // get data of the specified group id - $group = dba::selectFirst('group', [], ['uid' => $uid, 'id' => $gid]); + $group = DBA::selectFirst('group', [], ['uid' => $uid, 'id' => $gid]); // error message if specified gid is not in database if (!$group) { throw new BadRequestException('gid not available'); @@ -5602,10 +5294,10 @@ function group_create($name, $uid, $users = []) $rname = q( "SELECT * FROM `group` WHERE `uid` = %d AND `name` = '%s' AND `deleted` = 0", intval($uid), - dbesc($name) + DBA::escape($name) ); // error message if specified group name already exists - if (DBM::is_result($rname)) { + if (DBA::isResult($rname)) { throw new BadRequestException('group name already exists'); } @@ -5613,10 +5305,10 @@ function group_create($name, $uid, $users = []) $rname = q( "SELECT * FROM `group` WHERE `uid` = %d AND `name` = '%s' AND `deleted` = 1", intval($uid), - dbesc($name) + DBA::escape($name) ); // error message if specified group name already exists - if (DBM::is_result($rname)) { + if (DBA::isResult($rname)) { $reactivate_group = true; } @@ -5817,7 +5509,7 @@ function api_lists_update($type) } // get data of the specified group id - $group = dba::selectFirst('group', [], ['uid' => $uid, 'id' => $gid]); + $group = DBA::selectFirst('group', [], ['uid' => $uid, 'id' => $gid]); // error message if specified gid is not in database if (!$group) { throw new BadRequestException('gid not available'); @@ -5903,8 +5595,10 @@ function api_friendica_notification($type) if ($type == "xml") { $xmlnotes = []; - foreach ($notes as $note) { - $xmlnotes[] = ["@attributes" => $note]; + if (!empty($notes)) { + foreach ($notes as $note) { + $xmlnotes[] = ["@attributes" => $note]; + } } $notes = $xmlnotes; @@ -5944,14 +5638,10 @@ function api_friendica_notification_seen($type) $nm->setSeen($note); if ($note['otype']=='item') { // would be really better with an ItemsManager and $im->getByID() :-P - $r = q( - "SELECT * FROM `item` WHERE `id`=%d AND `uid`=%d", - intval($note['iid']), - intval(local_user()) - ); - if ($r!==false) { + $item = Item::selectFirstForUser(api_user(), [], ['id' => $note['iid'], 'uid' => api_user()]); + if (DBA::isResult($item)) { // we found the item, return it to the user - $ret = api_format_items($r, $user_info, false, $type); + $ret = api_format_items([$item], $user_info, false, $type); $data = ['status' => $ret]; return api_format_data("status", $type, $data); } @@ -5989,13 +5679,13 @@ function api_friendica_direct_messages_setseen($type) } // error message if specified id is not in database - if (!dba::exists('mail', ['id' => $id, 'uid' => $uid])) { + if (!DBA::exists('mail', ['id' => $id, 'uid' => $uid])) { $answer = ['result' => 'error', 'message' => 'message id not in database']; return api_format_data("direct_messages_setseen", $type, ['$result' => $answer]); } // update seen indicator - $result = dba::update('mail', ['seen' => true], ['id' => $id]); + $result = DBA::update('mail', ['seen' => true], ['id' => $id]); if ($result) { // return success @@ -6042,13 +5732,13 @@ function api_friendica_direct_messages_search($type, $box = "") $r = q( "SELECT `mail`.*, `contact`.`nurl` AS `contact-url` FROM `mail`,`contact` WHERE `mail`.`contact-id` = `contact`.`id` AND `mail`.`uid`=%d AND `body` LIKE '%s' ORDER BY `mail`.`id` DESC", intval($uid), - dbesc('%'.$searchstring.'%') + DBA::escape('%'.$searchstring.'%') ); $profile_url = $user_info["url"]; // message if nothing was found - if (!DBM::is_result($r)) { + if (!DBA::isResult($r)) { $success = ['success' => false, 'search_results' => 'problem with query']; } elseif (count($r) == 0) { $success = ['success' => false, 'search_results' => 'nothing found']; @@ -6106,7 +5796,7 @@ function api_friendica_profile_show($type) ); // error message if specified gid is not in database - if (!DBM::is_result($r)) { + if (!DBA::isResult($r)) { throw new BadRequestException("profile_id not available"); } } else { @@ -6126,7 +5816,7 @@ function api_friendica_profile_show($type) $nurls = q( "SELECT `id`, `nurl` FROM `contact` WHERE `uid`= %d AND `profile-id` = %d", intval(api_user()), - intval($rr['profile_id']) + intval($rr['id']) ); foreach ($nurls as $nurl) { @@ -6144,11 +5834,11 @@ function api_friendica_profile_show($type) } // return settings, authenticated user and profiles data - $self = q("SELECT `nurl` FROM `contact` WHERE `uid`= %d AND `self` LIMIT 1", intval(api_user())); + $self = DBA::selectFirst('contact', ['nurl'], ['uid' => api_user(), 'self' => true]); $result = ['multi_profiles' => $multi_profiles ? true : false, 'global_dir' => $directory, - 'friendica_owner' => api_get_user($a, $self[0]['nurl']), + 'friendica_owner' => api_get_user($a, $self['nurl']), 'profiles' => $profiles]; return api_format_data("friendica_profiles", $type, ['$result' => $result]); } @@ -6165,7 +5855,7 @@ api_register_func('api/friendica/profile/show', 'api_friendica_profile_show', tr */ function api_saved_searches_list($type) { - $terms = dba::select('search', ['id', 'term'], ['uid' => local_user()]); + $terms = DBA::select('search', ['id', 'term'], ['uid' => local_user()]); $result = []; while ($term = $terms->fetch()) { @@ -6179,7 +5869,7 @@ function api_saved_searches_list($type) ]; } - dba::close($terms); + DBA::close($terms); return api_format_data("terms", $type, ['terms' => $result]); } diff --git a/include/conversation.php b/include/conversation.php index 290d68bf3..e41b697b5 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -11,13 +11,17 @@ use Friendica\Core\Addon; use Friendica\Core\Config; use Friendica\Core\L10n; use Friendica\Core\PConfig; +use Friendica\Core\Protocol; use Friendica\Core\System; -use Friendica\Database\DBM; +use Friendica\Database\DBA; use Friendica\Model\Contact; +use Friendica\Model\Item; use Friendica\Model\Profile; +use Friendica\Model\Term; use Friendica\Object\Post; use Friendica\Object\Thread; use Friendica\Util\DateTimeFormat; +use Friendica\Util\Proxy as ProxyUtils; use Friendica\Util\Temporal; use Friendica\Util\XML; @@ -109,14 +113,21 @@ function item_redir_and_replace_images($body, $images, $cid) { /** * Render actions localized */ -function localize_item(&$item) { - +function localize_item(&$item) +{ $extracted = item_extract_images($item['body']); if ($extracted['images']) { $item['body'] = item_redir_and_replace_images($extracted['body'], $extracted['images'], $item['contact-id']); } - /// @Separted ??? + /* + heluecht 2018-06-19: from my point of view this whole code part is useless. + It just renders the body message of technical posts (Like, dislike, ...). + But: The body isn't visible at all. So we do this stuff just because we can. + Even if these messages were visible, this would only mean that something went wrong. + During the further steps of the database restructuring I would like to address this issue. + */ + $xmlhead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">"; if (activity_match($item['verb'], ACTIVITY_LIKE) || activity_match($item['verb'], ACTIVITY_DISLIKE) @@ -124,15 +135,11 @@ function localize_item(&$item) { || activity_match($item['verb'], ACTIVITY_ATTENDNO) || activity_match($item['verb'], ACTIVITY_ATTENDMAYBE)) { - /// @TODO may hurt performance - $r = q("SELECT * FROM `item`, `contact` - WHERE `item`.`contact-id`=`contact`.`id` - AND `item`.`uri`='%s'", - dbesc($item['parent-uri'])); - if (!DBM::is_result($r)) { + $fields = ['author-link', 'author-name', 'verb', 'object-type', 'resource-id', 'body', 'plink']; + $obj = Item::selectFirst($fields, ['uri' => $item['parent-uri']]); + if (!DBA::isResult($obj)) { return; } - $obj = $r[0]; $author = '[url=' . $item['author-link'] . ']' . $item['author-name'] . '[/url]'; $objauthor = '[url=' . $obj['author-link'] . ']' . $obj['author-name'] . '[/url]'; @@ -162,22 +169,19 @@ function localize_item(&$item) { if (activity_match($item['verb'], ACTIVITY_LIKE)) { $bodyverb = L10n::t('%1$s likes %2$s\'s %3$s'); - } - elseif (activity_match($item['verb'], ACTIVITY_DISLIKE)) { + } elseif (activity_match($item['verb'], ACTIVITY_DISLIKE)) { $bodyverb = L10n::t('%1$s doesn\'t like %2$s\'s %3$s'); - } - elseif (activity_match($item['verb'], ACTIVITY_ATTEND)) { + } elseif (activity_match($item['verb'], ACTIVITY_ATTEND)) { $bodyverb = L10n::t('%1$s attends %2$s\'s %3$s'); - } - elseif (activity_match($item['verb'], ACTIVITY_ATTENDNO)) { + } elseif (activity_match($item['verb'], ACTIVITY_ATTENDNO)) { $bodyverb = L10n::t('%1$s doesn\'t attend %2$s\'s %3$s'); - } - elseif (activity_match($item['verb'], ACTIVITY_ATTENDMAYBE)) { + } elseif (activity_match($item['verb'], ACTIVITY_ATTENDMAYBE)) { $bodyverb = L10n::t('%1$s attends maybe %2$s\'s %3$s'); } - $item['body'] = sprintf($bodyverb, $author, $objauthor, $plink); + $item['body'] = sprintf($bodyverb, $author, $objauthor, $plink); } + if (activity_match($item['verb'], ACTIVITY_FRIEND)) { if ($item['object-type']=="" || $item['object-type']!== ACTIVITY_OBJ_PERSON) return; @@ -191,7 +195,8 @@ function localize_item(&$item) { $links = XML::parseString($xmlhead."".unxmlify($obj->link).""); $Bname = $obj->title; - $Blink = ""; $Bphoto = ""; + $Blink = ""; + $Bphoto = ""; foreach ($links->link as $l) { $atts = $l->attributes(); switch ($atts['rel']) { @@ -200,10 +205,10 @@ function localize_item(&$item) { } } - $A = '[url=' . Profile::zrl($Alink) . ']' . $Aname . '[/url]'; - $B = '[url=' . Profile::zrl($Blink) . ']' . $Bname . '[/url]'; + $A = '[url=' . Contact::magicLink($Alink) . ']' . $Aname . '[/url]'; + $B = '[url=' . Contact::magicLink($Blink) . ']' . $Bname . '[/url]'; if ($Bphoto != "") { - $Bphoto = '[url=' . Profile::zrl($Blink) . '][img]' . $Bphoto . '[/img][/url]'; + $Bphoto = '[url=' . Contact::magicLink($Blink) . '][img]' . $Bphoto . '[/img][/url]'; } $item['body'] = L10n::t('%1$s is now friends with %2$s', $A, $B)."\n\n\n".$Bphoto; @@ -224,12 +229,12 @@ function localize_item(&$item) { $xmlhead = "<" . "?xml version='1.0' encoding='UTF-8' ?" . ">"; $obj = XML::parseString($xmlhead.$item['object']); - $links = XML::parseString($xmlhead."".unxmlify($obj->link).""); $Bname = $obj->title; - $Blink = ""; + $Blink = $obj->id; $Bphoto = ""; - foreach ($links->link as $l) { + + foreach ($obj->link as $l) { $atts = $l->attributes(); switch ($atts['rel']) { case "alternate": $Blink = $atts['href']; @@ -237,10 +242,10 @@ function localize_item(&$item) { } } - $A = '[url=' . Profile::zrl($Alink) . ']' . $Aname . '[/url]'; - $B = '[url=' . Profile::zrl($Blink) . ']' . $Bname . '[/url]'; + $A = '[url=' . Contact::magicLink($Alink) . ']' . $Aname . '[/url]'; + $B = '[url=' . Contact::magicLink($Blink) . ']' . $Bname . '[/url]'; if ($Bphoto != "") { - $Bphoto = '[url=' . Profile::zrl($Blink) . '][img=80x80]' . $Bphoto . '[/img][/url]'; + $Bphoto = '[url=' . Contact::magicLink($Blink) . '][img=80x80]' . $Bphoto . '[/img][/url]'; } /* @@ -251,7 +256,7 @@ function localize_item(&$item) { // now translate the verb $poked_t = trim(sprintf($txt, "", "")); - $txt = str_replace( $poked_t, L10n::t($verb), $txt); + $txt = str_replace($poked_t, L10n::t($verb), $txt); // then do the sprintf on the translation string @@ -260,20 +265,20 @@ function localize_item(&$item) { } if (activity_match($item['verb'], ACTIVITY_TAG)) { - /// @TODO may hurt performance "joining" two tables + asterisk - $r = q("SELECT * FROM `item`, `contact` - WHERE `item`.`contact-id`=`contact`.`id` - AND `item`.`uri`='%s'", - dbesc($item['parent-uri'])); - - if (!DBM::is_result($r)) { + $fields = ['author-id', 'author-link', 'author-name', 'author-network', + 'verb', 'object-type', 'resource-id', 'body', 'plink']; + $obj = Item::selectFirst($fields, ['uri' => $item['parent-uri']]); + if (!DBA::isResult($obj)) { return; } - $obj = $r[0]; + $author_arr = ['uid' => 0, 'id' => $item['author-id'], + 'network' => $item['author-network'], 'url' => $item['author-link']]; + $author = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $item['author-name'] . '[/url]'; - $author = '[url=' . Profile::zrl($item['author-link']) . ']' . $item['author-name'] . '[/url]'; - $objauthor = '[url=' . Profile::zrl($obj['author-link']) . ']' . $obj['author-name'] . '[/url]'; + $author_arr = ['uid' => 0, 'id' => $obj['author-id'], + 'network' => $obj['author-network'], 'url' => $obj['author-link']]; + $objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]'; switch ($obj['verb']) { case ACTIVITY_POST: @@ -301,11 +306,10 @@ function localize_item(&$item) { $parsedobj = XML::parseString($xmlhead.$item['object']); $tag = sprintf('#[url=%s]%s[/url]', $parsedobj->id, $parsedobj->content); - $item['body'] = L10n::t('%1$s tagged %2$s\'s %3$s with %4$s', $author, $objauthor, $plink, $tag ); - + $item['body'] = L10n::t('%1$s tagged %2$s\'s %3$s with %4$s', $author, $objauthor, $plink, $tag); } - if (activity_match($item['verb'], ACTIVITY_FAVORITE)) { + if (activity_match($item['verb'], ACTIVITY_FAVORITE)) { if ($item['object-type'] == "") { return; } @@ -317,17 +321,13 @@ function localize_item(&$item) { $obj = XML::parseString($xmlhead.$item['object']); if (strlen($obj->id)) { - $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - dbesc($obj->id), - intval($item['uid']) - ); - - if (DBM::is_result($r) && $r[0]['plink']) { - $target = $r[0]; + $fields = ['author-link', 'author-name', 'plink']; + $target = Item::selectFirst($fields, ['uri' => $obj->id, 'uid' => $item['uid']]); + if (DBA::isResult($target) && $target['plink']) { $Bname = $target['author-name']; $Blink = $target['author-link']; - $A = '[url=' . Profile::zrl($Alink) . ']' . $Aname . '[/url]'; - $B = '[url=' . Profile::zrl($Blink) . ']' . $Bname . '[/url]'; + $A = '[url=' . Contact::magicLink($Alink) . ']' . $Aname . '[/url]'; + $B = '[url=' . Contact::magicLink($Blink) . ']' . $Bname . '[/url]'; $P = '[url=' . $target['plink'] . ']' . L10n::t('post/item') . '[/url]'; $item['body'] = L10n::t('%1$s marked %2$s\'s %3$s as favorite', $A, $B, $P)."\n"; } @@ -337,7 +337,7 @@ function localize_item(&$item) { if (preg_match_all('/@\[url=(.*?)\]/is', $item['body'], $matches, PREG_SET_ORDER)) { foreach ($matches as $mtch) { if (!strpos($mtch[1], 'zrl=')) { - $item['body'] = str_replace($mtch[0], '@[url=' . Profile::zrl($mtch[1]) . ']', $item['body']); + $item['body'] = str_replace($mtch[0], '@[url=' . Contact::magicLink($mtch[1]) . ']', $item['body']); } } } @@ -350,15 +350,11 @@ function localize_item(&$item) { } // add sparkle links to appropriate permalinks + $author = ['uid' => 0, 'id' => $item['author-id'], + 'network' => $item['author-network'], 'url' => $item['author-link']]; - $x = stristr($item['plink'],'/display/'); - if ($x) { - $sparkle = false; - $y = best_link_url($item, $sparkle); - - if (strstr($y, '/redir/')) { - $item['plink'] = $y . '?f=&url=' . $item['plink']; - } + if (!empty($item['plink'])) { + $item['plink'] = Contact::magicLinkbyContact($author, $item['plink']); } } @@ -394,102 +390,36 @@ function visible_activity($item) { } } - if (activity_match($item['verb'], ACTIVITY_FOLLOW) && $item['object-type'] === ACTIVITY_OBJ_NOTE) { - if (!($item['self'] && ($item['uid'] == local_user()))) { - return false; - } + // @TODO below if() block can be rewritten to a single line: $isVisible = allConditionsHere; + if (activity_match($item['verb'], ACTIVITY_FOLLOW) && $item['object-type'] === ACTIVITY_OBJ_NOTE && empty($item['self']) && $item['uid'] == local_user()) { + return false; } return true; } -/** - * @brief SQL query for items - * - * @param int $uid user id - */ -function item_query($uid = 0) { - return "SELECT " . item_fieldlists() . " FROM `item` " . - item_joins($uid) . " WHERE " . item_condition(); -} +function conv_get_blocklist() +{ + if (!local_user()) { + return []; + } -/** - * @brief List of all data fields that are needed for displaying items - */ -function item_fieldlists() { + $str_blocked = PConfig::get(local_user(), 'system', 'blocked'); + if (empty($str_blocked)) { + return []; + } -/* -These Fields are not added below (yet). They are here to for bug search. -`item`.`type`, -`item`.`extid`, -`item`.`changed`, -`item`.`moderated`, -`item`.`target-type`, -`item`.`target`, -`item`.`resource-id`, -`item`.`tag`, -`item`.`inform`, -`item`.`pubmail`, -`item`.`visible`, -`item`.`spam`, -`item`.`bookmark`, -`item`.`unseen`, -`item`.`deleted`, -`item`.`forum_mode`, -`item`.`mention`, -`item`.`global`, -`item`.`shadow`, -*/ + $blocklist = []; - return "`item`.`author-id`, `item`.`author-link`, `item`.`author-name`, `item`.`author-avatar`, - `item`.`owner-id`, `item`.`owner-link`, `item`.`owner-name`, `item`.`owner-avatar`, - `item`.`contact-id`, `item`.`uid`, `item`.`id`, `item`.`parent`, - `item`.`uri`, `item`.`thr-parent`, `item`.`parent-uri`, `item`.`content-warning`, - `item`.`commented`, `item`.`created`, `item`.`edited`, `item`.`received`, - `item`.`verb`, `item`.`object-type`, `item`.`postopts`, `item`.`plink`, - `item`.`guid`, `item`.`wall`, `item`.`private`, `item`.`starred`, `item`.`origin`, - `item`.`title`, `item`.`body`, `item`.`file`, `item`.`event-id`, - `item`.`location`, `item`.`coord`, `item`.`app`, `item`.`attach`, - `item`.`rendered-hash`, `item`.`rendered-html`, `item`.`object`, - `item`.`allow_cid`, `item`.`allow_gid`, `item`.`deny_cid`, `item`.`deny_gid`, - `item`.`id` AS `item_id`, `item`.`network` AS `item_network`, + foreach (explode(',', $str_blocked) as $entry) { + // The 4th parameter guarantees that there always will be a public contact entry + $cid = Contact::getIdForURL(trim($entry), 0, true, ['url' => trim($entry)]); + if (!empty($cid)) { + $blocklist[] = $cid; + } + } - `author`.`thumb` AS `author-thumb`, `owner`.`thumb` AS `owner-thumb`, - - `contact`.`network`, `contact`.`url`, `contact`.`name`, `contact`.`writable`, - `contact`.`self`, `contact`.`id` AS `cid`, `contact`.`alias`, - - `event`.`created` AS `event-created`, `event`.`edited` AS `event-edited`, - `event`.`start` AS `event-start`,`event`.`finish` AS `event-finish`, - `event`.`summary` AS `event-summary`,`event`.`desc` AS `event-desc`, - `event`.`location` AS `event-location`, `event`.`type` AS `event-type`, - `event`.`nofinish` AS `event-nofinish`,`event`.`adjust` AS `event-adjust`, - `event`.`ignore` AS `event-ignore`, `event`.`id` AS `event-id`"; -} - -/** - * @brief SQL join for contacts that are needed for displaying items - * - * @param int $uid user id - */ -function item_joins($uid = 0) { - return sprintf("STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - AND NOT `contact`.`blocked` - AND ((NOT `contact`.`readonly` AND NOT `contact`.`pending` AND (`contact`.`rel` IN (%s, %s))) - OR `contact`.`self` OR (`item`.`id` != `item`.`parent`) OR `contact`.`uid` = 0) - INNER JOIN `contact` AS `author` ON `author`.`id`=`item`.`author-id` AND NOT `author`.`blocked` - INNER JOIN `contact` AS `owner` ON `owner`.`id`=`item`.`owner-id` AND NOT `owner`.`blocked` - LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = %d - LEFT JOIN `event` ON `event-id` = `event`.`id`", - CONTACT_IS_SHARING, CONTACT_IS_FRIEND, intval($uid) - ); -} - -/** - * @brief SQL condition for items that are needed for displaying items - */ -function item_condition() { - return "`item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated` AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`) "; + return $blocklist; } /** @@ -502,26 +432,14 @@ function item_condition() { * that are based on unique features of the calling module. * */ -function conversation(App $a, $items, $mode, $update, $preview = false, $order = 'commented', $uid = 0) { - require_once 'mod/proxy.php'; +function conversation(App $a, array $items, $mode, $update, $preview = false, $order = 'commented', $uid = 0) { - $ssl_state = ((local_user()) ? true : false); + $ssl_state = (local_user() ? true : false); $profile_owner = 0; $live_update_div = ''; - $arr_blocked = null; - - if (local_user()) { - $str_blocked = PConfig::get(local_user(), 'system', 'blocked'); - if ($str_blocked) { - $arr_blocked = explode(',', $str_blocked); - for ($x = 0; $x < count($arr_blocked); $x ++) { - $arr_blocked[$x] = trim($arr_blocked[$x]); - } - } - - } + $blocklist = conv_get_blocklist(); $previewing = (($preview) ? ' preview ' : ''); @@ -545,7 +463,6 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = . ((x($_GET, 'bmark')) ? '&bmark=' . $_GET['bmark'] : '') . ((x($_GET, 'liked')) ? '&liked=' . $_GET['liked'] : '') . ((x($_GET, 'conv')) ? '&conv=' . $_GET['conv'] : '') - . ((x($_GET, 'spam')) ? '&spam=' . $_GET['spam'] : '') . ((x($_GET, 'nets')) ? '&nets=' . $_GET['nets'] : '') . ((x($_GET, 'cmin')) ? '&cmin=' . $_GET['cmin'] : '') . ((x($_GET, 'cmax')) ? '&cmax=' . $_GET['cmax'] : '') @@ -554,6 +471,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = . "'; var profile_page = " . $a->pager['page'] . "; \r\n"; } } elseif ($mode === 'profile') { + $items = conversation_add_children($items, false, $order, $uid); $profile_owner = $a->profile['profile_uid']; if (!$update) { @@ -573,27 +491,41 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = } } } elseif ($mode === 'notes') { + $items = conversation_add_children($items, false, $order, $uid); $profile_owner = local_user(); + if (!$update) { $live_update_div = '
' . "\r\n" . "\r\n"; } } elseif ($mode === 'display') { + $items = conversation_add_children($items, false, $order, $uid); $profile_owner = $a->profile['uid']; + if (!$update) { $live_update_div = '
' . "\r\n" - . ""; } } elseif ($mode === 'community') { $items = conversation_add_children($items, true, $order, $uid); $profile_owner = 0; + if (!$update) { $live_update_div = '
' . "\r\n" . "\r\n"; } + } elseif ($mode === 'contacts') { + $items = conversation_add_children($items, true, $order, $uid); + $profile_owner = 0; + + if (!$update) { + $live_update_div = '
' . "\r\n" + . "\r\n"; + } } elseif ($mode === 'search') { $live_update_div = '' . "\r\n"; } @@ -620,11 +552,11 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = $page_template = get_markup_template("conversation.tpl"); - if ($items && count($items)) { - if ($mode === 'community') { + if (!empty($items)) { + if (in_array($mode, ['community', 'contacts'])) { $writable = true; } else { - $writable = ($items[0]['uid'] == 0) && in_array($items[0]['network'], [NETWORK_OSTATUS, NETWORK_DIASPORA, NETWORK_DFRN]); + $writable = ($items[0]['uid'] == 0) && in_array($items[0]['network'], [Protocol::OSTATUS, Protocol::DIASPORA, Protocol::DFRN]); } if (!local_user()) { @@ -646,20 +578,10 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = continue; } - if ($arr_blocked) { - $blocked = false; - foreach ($arr_blocked as $b) { - if ($b && link_compare($item['author-link'], $b)) { - $blocked = true; - break; - } - } - if ($blocked) { - continue; - } + if (in_array($item['author-id'], $blocklist)) { + continue; } - $threadsid++; $owner_url = ''; @@ -667,45 +589,23 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = $sparkle = ''; // prevent private email from leaking. - if ($item['network'] === NETWORK_MAIL && local_user() != $item['uid']) { + if ($item['network'] === Protocol::MAIL && local_user() != $item['uid']) { continue; } - $profile_name = (strlen($item['author-name']) ? $item['author-name'] : $item['name']); - if ($item['author-link'] && !$item['author-name']) { + $profile_name = $item['author-name']; + if (!empty($item['author-link']) && empty($item['author-name'])) { $profile_name = $item['author-link']; } - $tags = \Friendica\Model\Term::populateTagsFromItem($item); + $tags = Term::populateTagsFromItem($item); - $sp = false; - $profile_link = best_link_url($item, $sp); - if ($profile_link === 'mailbox') { - $profile_link = ''; - } + $author = ['uid' => 0, 'id' => $item['author-id'], + 'network' => $item['author-network'], 'url' => $item['author-link']]; + $profile_link = Contact::magicLinkbyContact($author); - if ($sp) { + if (strpos($profile_link, 'redir/') === 0) { $sparkle = ' sparkle'; - } else { - $profile_link = Profile::zrl($profile_link); - } - - if (!x($item, 'author-thumb') || ($item['author-thumb'] == "")) { - $author_contact = Contact::getDetailsByURL($item['author-link'], $profile_owner); - if ($author_contact["thumb"]) { - $item['author-thumb'] = $author_contact["thumb"]; - } else { - $item['author-thumb'] = $item['author-avatar']; - } - } - - if (!isset($item['owner-thumb']) || ($item['owner-thumb'] == "")) { - $owner_contact = Contact::getDetailsByURL($item['owner-link'], $profile_owner); - if ($owner_contact["thumb"]) { - $item['owner-thumb'] = $owner_contact["thumb"]; - } else { - $item['owner-thumb'] = $item['owner-avatar']; - } } $locate = ['location' => $item['location'], 'coord' => $item['coord'], 'html' => '']; @@ -752,23 +652,19 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = $location_e = $location; $owner_name_e = $owner_name; - if ($item['item_network'] == "") { - $item['item_network'] = $item['network']; - } - $tmp_item = [ 'template' => $tpl, - 'id' => (($preview) ? 'P0' : $item['item_id']), - 'guid' => (($preview) ? 'Q0' : $item['guid']), - 'network' => $item['item_network'], - 'network_name' => ContactSelector::networkToName($item['item_network'], $profile_link), - 'linktitle' => L10n::t('View %s\'s profile @ %s', $profile_name, ((strlen($item['author-link'])) ? $item['author-link'] : $item['url'])), + 'id' => ($preview ? 'P0' : $item['id']), + 'guid' => ($preview ? 'Q0' : $item['guid']), + 'network' => $item['network'], + 'network_name' => ContactSelector::networkToName($item['network'], $profile_link), + 'linktitle' => L10n::t('View %s\'s profile @ %s', $profile_name, $item['author-link']), 'profile_url' => $profile_link, 'item_photo_menu' => item_photo_menu($item), 'name' => $profile_name_e, 'sparkle' => $sparkle, 'lock' => $lock, - 'thumb' => System::removedBaseUrl(proxy_url($item['author-thumb'], false, PROXY_SIZE_THUMB)), + 'thumb' => System::removedBaseUrl(ProxyUtils::proxifyUrl($item['author-avatar'], false, ProxyUtils::SIZE_THUMB)), 'title' => $title_e, 'body' => $body_e, 'tags' => $tags_e, @@ -787,7 +683,7 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = 'indent' => '', 'owner_name' => $owner_name_e, 'owner_url' => $owner_url, - 'owner_photo' => System::removedBaseUrl(proxy_url($item['owner-thumb'], false, PROXY_SIZE_THUMB)), + 'owner_photo' => System::removedBaseUrl(ProxyUtils::proxifyUrl($item['owner-avatar'], false, ProxyUtils::SIZE_THUMB)), 'plink' => get_plink($item), 'edpost' => false, 'isstarred' => $isstarred, @@ -806,8 +702,8 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = $arr = ['item' => $item, 'output' => $tmp_item]; Addon::callHooks('display_item', $arr); - $threads[$threadsid]['id'] = $item['item_id']; - $threads[$threadsid]['network'] = $item['item_network']; + $threads[$threadsid]['id'] = $item['id']; + $threads[$threadsid]['network'] = $item['network']; $threads[$threadsid]['items'] = [$arr['output']]; } @@ -823,24 +719,15 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = * But for now, this array respects the old style, just in case */ foreach ($items as $item) { - if ($arr_blocked) { - $blocked = false; - foreach ($arr_blocked as $b) { - if ($b && link_compare($item['author-link'], $b)) { - $blocked = true; - break; - } - } - if ($blocked) { - continue; - } + if (in_array($item['author-id'], $blocklist)) { + continue; } // Can we put this after the visibility check? builtin_activity_puller($item, $conv_responses); // Only add what is visible - if ($item['network'] === NETWORK_MAIL && local_user() != $item['uid']) { + if ($item['network'] === Protocol::MAIL && local_user() != $item['uid']) { continue; } @@ -848,6 +735,8 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = continue; } + /// @todo Check if this call is needed or not + $arr = ['item' => $item]; Addon::callHooks('display_item', $arr); $item['pagedrop'] = $page_dropping; @@ -890,26 +779,26 @@ function conversation(App $a, $items, $mode, $update, $preview = false, $order = * * @return array items with parents and comments */ -function conversation_add_children($parents, $block_authors, $order, $uid) { +function conversation_add_children(array $parents, $block_authors, $order, $uid) { $max_comments = Config::get('system', 'max_comments', 100); + $params = ['order' => ['uid', 'commented' => true]]; + if ($max_comments > 0) { - $limit = ' LIMIT '.intval($max_comments + 1); - } else { - $limit = ''; + $params['limit'] = $max_comments; } $items = []; - $block_sql = $block_authors ? "AND NOT `author`.`hidden` AND NOT `author`.`blocked`" : ""; - foreach ($parents AS $parent) { - $thread_items = dba::p(item_query(local_user())."AND `item`.`parent-uri` = ? - AND `item`.`uid` IN (0, ?) $block_sql - ORDER BY `item`.`uid` ASC, `item`.`commented` DESC" . $limit, - $parent['uri'], local_user()); + $condition = ["`item`.`parent-uri` = ? AND `item`.`uid` IN (0, ?) ", + $parent['uri'], local_user()]; + if ($block_authors) { + $condition[0] .= "AND NOT `author`.`hidden`"; + } + $thread_items = Item::selectForUser(local_user(), [], $condition, $params); - $comments = dba::inArray($thread_items); + $comments = Item::inArray($thread_items); if (count($comments) != 0) { $items = array_merge($items, $comments); @@ -918,7 +807,7 @@ function conversation_add_children($parents, $block_authors, $order, $uid) { foreach ($items as $index => $item) { if ($item['uid'] == 0) { - $items[$index]['writable'] = in_array($item['network'], [NETWORK_OSTATUS, NETWORK_DIASPORA, NETWORK_DFRN]); + $items[$index]['writable'] = in_array($item['network'], [Protocol::OSTATUS, Protocol::DIASPORA, Protocol::DFRN]); } } @@ -927,48 +816,6 @@ function conversation_add_children($parents, $block_authors, $order, $uid) { return $items; } -function best_link_url($item, &$sparkle, $url = '') { - - $best_url = ''; - $sparkle = false; - - $clean_url = normalise_link($item['author-link']); - - if (local_user()) { - $condition = [ - 'network' => NETWORK_DFRN, - 'uid' => local_user(), - 'nurl' => normalise_link($clean_url), - 'pending' => false - ]; - $contact = dba::selectFirst('contact', ['id'], $condition); - if (DBM::is_result($contact)) { - $best_url = 'redir/' . $contact['id']; - $sparkle = true; - if ($url != '') { - $hostname = get_app()->get_hostname(); - if (!strstr($url, $hostname)) { - $best_url .= "?url=".$url; - } else { - $best_url = $url; - } - } - } - } - if (!$best_url) { - if ($url != '') { - $best_url = $url; - } elseif (strlen($item['author-link'])) { - $best_url = $item['author-link']; - } else { - $best_url = $item['url']; - } - } - - return $best_url; -} - - function item_photo_menu($item) { $sub_link = ''; $poke_link = ''; @@ -982,18 +829,17 @@ function item_photo_menu($item) { $sub_link = 'javascript:dosubthread(' . $item['id'] . '); return false;'; } - $sparkle = false; - $profile_link = best_link_url($item, $sparkle); - if ($profile_link === 'mailbox') { - $profile_link = ''; - } + $author = ['uid' => 0, 'id' => $item['author-id'], + 'network' => $item['author-network'], 'url' => $item['author-link']]; + $profile_link = Contact::magicLinkbyContact($author); + $sparkle = (strpos($profile_link, 'redir/') === 0); $cid = 0; $network = ''; $rel = 0; $condition = ['uid' => local_user(), 'nurl' => normalise_link($item['author-link'])]; - $contact = dba::selectFirst('contact', ['id', 'network', 'rel'], $condition); - if (DBM::is_result($contact)) { + $contact = DBA::selectFirst('contact', ['id', 'network', 'rel'], $condition); + if (DBA::isResult($contact)) { $cid = $contact['id']; $network = $contact['network']; $rel = $contact['rel']; @@ -1003,8 +849,6 @@ function item_photo_menu($item) { $status_link = $profile_link . '?url=status'; $photos_link = $profile_link . '?url=photos'; $profile_link = $profile_link . '?url=profile'; - } else { - $profile_link = Profile::zrl($profile_link); } if ($cid && !$item['self']) { @@ -1012,7 +856,7 @@ function item_photo_menu($item) { $contact_url = 'contacts/' . $cid; $posts_link = 'contacts/' . $cid . '/posts'; - if (in_array($network, [NETWORK_DFRN, NETWORK_DIASPORA])) { + if (in_array($network, [Protocol::DFRN, Protocol::DIASPORA])) { $pm_url = 'message/new/' . $cid; } } @@ -1028,12 +872,12 @@ function item_photo_menu($item) { L10n::t('Send PM') => $pm_url ]; - if ($network == NETWORK_DFRN) { + if ($network == Protocol::DFRN) { $menu[L10n::t("Poke")] = $poke_link; } - if ((($cid == 0) || ($rel == CONTACT_IS_FOLLOWER)) && - in_array($item['network'], [NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA])) { + if ((($cid == 0) || ($rel == Contact::FOLLOWER)) && + in_array($item['network'], [Protocol::DFRN, Protocol::OSTATUS, Protocol::DIASPORA])) { $menu[L10n::t('Connect/Follow')] = 'follow?url=' . urlencode($item['author-link']); } } else { @@ -1092,17 +936,16 @@ function builtin_activity_puller($item, &$conv_responses) { } if (activity_match($item['verb'], $verb) && ($item['id'] != $item['parent'])) { - $url = $item['author-link']; - if (local_user() && (local_user() == $item['uid']) && ($item['network'] === NETWORK_DFRN) && !$item['self'] && link_compare($item['author-link'], $item['url'])) { - $url = 'redir/' . $item['contact-id']; + $author = ['uid' => 0, 'id' => $item['author-id'], + 'network' => $item['author-network'], 'url' => $item['author-link']]; + $url = Contact::magicLinkbyContact($author); + if (strpos($url, 'redir/') === 0) { $sparkle = ' class="sparkle" '; - } else { - $url = Profile::zrl($url); } $url = '
' . htmlentities($item['author-name']) . ''; - if (!$item['thr-parent']) { + if (!x($item, 'thr-parent')) { $item['thr-parent'] = $item['parent-uri']; } @@ -1182,7 +1025,7 @@ function format_like($cnt, array $arr, $type, $id) { } if ($total >= MAX_LIKERS) { $str = implode(', ', $arr); - $str .= L10n::t('and %d other people', $total - MAX_LIKERS ); + $str .= L10n::t('and %d other people', $total - MAX_LIKERS); } $likers = $str; @@ -1317,7 +1160,8 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false) '$wait' => L10n::t('Please wait'), '$permset' => L10n::t('Permission settings'), '$shortpermset' => L10n::t('permissions'), - '$ptyp' => $notes_cid ? 'note' : 'wall', + '$wall' => $notes_cid ? 0 : 1, + '$posttype' => $notes_cid ? Item::PT_PERSONAL_NOTE : Item::PT_ARTICLE, '$content' => defaults($x, 'content', ''), '$post_id' => defaults($x, 'post_id', ''), '$baseurl' => System::baseUrl(true), @@ -1503,10 +1347,16 @@ function conv_sort(array $item_list, $order) return $parents; } + $blocklist = conv_get_blocklist(); + $item_array = []; // Dedupes the item list on the uri to prevent infinite loops foreach ($item_list as $item) { + if (in_array($item['author-id'], $blocklist)) { + continue; + } + $item_array[$item['uri']] = $item; } @@ -1590,23 +1440,21 @@ function sort_thr_commented(array $a, array $b) return strcmp($b['commented'], $a['commented']); } -/// @TODO Add type-hint -function render_location_dummy($item) { - if ($item['location'] != "") { +function render_location_dummy(array $item) { + if (x($item, 'location') && !empty($item['location'])) { return $item['location']; } - if ($item['coord'] != "") { + if (x($item, 'coord') && !empty($item['coord'])) { return $item['coord']; } } -/// @TODO Add type-hint -function get_responses($conv_responses, $response_verbs, $ob, $item) { +function get_responses(array $conv_responses, array $response_verbs, $ob, array $item) { $ret = []; foreach ($response_verbs as $v) { $ret[$v] = []; - $ret[$v]['count'] = defaults($conv_responses[$v], $item['uri'], ''); + $ret[$v]['count'] = defaults($conv_responses[$v], $item['uri'], 0); $ret[$v]['list'] = defaults($conv_responses[$v], $item['uri'] . '-l', []); $ret[$v]['self'] = defaults($conv_responses[$v], $item['uri'] . '-self', '0'); if (count($ret[$v]['list']) > MAX_LIKERS) { diff --git a/include/dba.php b/include/dba.php index e64575538..9e168eac7 100644 --- a/include/dba.php +++ b/include/dba.php @@ -1,1365 +1,40 @@ 1) { - $port = trim($serverdata[1]); - } - - $server = trim($server); - $user = trim($user); - $pass = trim($pass); - $db = trim($db); - - if (!(strlen($server) && strlen($user))) { - return false; - } - - if (class_exists('\PDO') && in_array('mysql', PDO::getAvailableDrivers())) { - self::$driver = 'pdo'; - $connect = "mysql:host=".$server.";dbname=".$db; - - if (isset($port)) { - $connect .= ";port=".$port; - } - - if (isset($a->config["system"]["db_charset"])) { - $connect .= ";charset=".$a->config["system"]["db_charset"]; - } - try { - self::$db = @new PDO($connect, $user, $pass); - self::$connected = true; - } catch (PDOException $e) { - } - } - - if (!self::$connected && class_exists('mysqli')) { - self::$driver = 'mysqli'; - self::$db = @new mysqli($server, $user, $pass, $db, $port); - if (!mysqli_connect_errno()) { - self::$connected = true; - - if (isset($a->config["system"]["db_charset"])) { - self::$db->set_charset($a->config["system"]["db_charset"]); - } - } - } - - // No suitable SQL driver was found. - if (!self::$connected) { - self::$driver = null; - self::$db = null; - } - $a->save_timestamp($stamp1, "network"); - - return self::$connected; - } - - /** - * @brief Returns the MySQL server version string - * - * This function discriminate between the deprecated mysql API and the current - * object-oriented mysqli API. Example of returned string: 5.5.46-0+deb8u1 - * - * @return string - */ - public static function server_info() { - if (self::$_server_info == '') { - switch (self::$driver) { - case 'pdo': - self::$_server_info = self::$db->getAttribute(PDO::ATTR_SERVER_VERSION); - break; - case 'mysqli': - self::$_server_info = self::$db->server_info; - break; - } - } - return self::$_server_info; - } - - /** - * @brief Returns the selected database name - * - * @return string - */ - public static function database_name() { - $ret = self::p("SELECT DATABASE() AS `db`"); - $data = self::inArray($ret); - return $data[0]['db']; - } - - /** - * @brief Analyze a database query and log this if some conditions are met. - * - * @param string $query The database query that will be analyzed - */ - private static function logIndex($query) { - $a = get_app(); - - if (empty($a->config["system"]["db_log_index"])) { - return; - } - - // Don't explain an explain statement - if (strtolower(substr($query, 0, 7)) == "explain") { - return; - } - - // Only do the explain on "select", "update" and "delete" - if (!in_array(strtolower(substr($query, 0, 6)), ["select", "update", "delete"])) { - return; - } - - $r = self::p("EXPLAIN ".$query); - if (!DBM::is_result($r)) { - return; - } - - $watchlist = explode(',', $a->config["system"]["db_log_index_watch"]); - $blacklist = explode(',', $a->config["system"]["db_log_index_blacklist"]); - - while ($row = dba::fetch($r)) { - if ((intval($a->config["system"]["db_loglimit_index"]) > 0)) { - $log = (in_array($row['key'], $watchlist) && - ($row['rows'] >= intval($a->config["system"]["db_loglimit_index"]))); - } else { - $log = false; - } - - if ((intval($a->config["system"]["db_loglimit_index_high"]) > 0) && ($row['rows'] >= intval($a->config["system"]["db_loglimit_index_high"]))) { - $log = true; - } - - if (in_array($row['key'], $blacklist) || ($row['key'] == "")) { - $log = false; - } - - if ($log) { - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - @file_put_contents($a->config["system"]["db_log_index"], DateTimeFormat::utcNow()."\t". - $row['key']."\t".$row['rows']."\t".$row['Extra']."\t". - basename($backtrace[1]["file"])."\t". - $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t". - substr($query, 0, 2000)."\n", FILE_APPEND); - } - } - } - - public static function escape($str) { - switch (self::$driver) { - case 'pdo': - return substr(@self::$db->quote($str, PDO::PARAM_STR), 1, -1); - case 'mysqli': - return @self::$db->real_escape_string($str); - } - } - - public static function connected() { - $connected = false; - - switch (self::$driver) { - case 'pdo': - $r = dba::p("SELECT 1"); - if (DBM::is_result($r)) { - $row = dba::inArray($r); - $connected = ($row[0]['1'] == '1'); - } - break; - case 'mysqli': - $connected = self::$db->ping(); - break; - } - return $connected; - } - - /** - * @brief Replaces ANY_VALUE() function by MIN() function, - * if the database server does not support ANY_VALUE(). - * - * Considerations for Standard SQL, or MySQL with ONLY_FULL_GROUP_BY (default since 5.7.5). - * ANY_VALUE() is available from MySQL 5.7.5 https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html - * A standard fall-back is to use MIN(). - * - * @param string $sql An SQL string without the values - * @return string The input SQL string modified if necessary. - */ - public static function any_value_fallback($sql) { - $server_info = self::server_info(); - if (version_compare($server_info, '5.7.5', '<') || - (stripos($server_info, 'MariaDB') !== false)) { - $sql = str_ireplace('ANY_VALUE(', 'MIN(', $sql); - } - return $sql; - } - - /** - * @brief beautifies the query - useful for "SHOW PROCESSLIST" - * - * This is safe when we bind the parameters later. - * The parameter values aren't part of the SQL. - * - * @param string $sql An SQL string without the values - * @return string The input SQL string modified if necessary. - */ - public static function clean_query($sql) { - $search = ["\t", "\n", "\r", " "]; - $replace = [' ', ' ', ' ', ' ']; - do { - $oldsql = $sql; - $sql = str_replace($search, $replace, $sql); - } while ($oldsql != $sql); - - return $sql; - } - - - /** - * @brief Replaces the ? placeholders with the parameters in the $args array - * - * @param string $sql SQL query - * @param array $args The parameters that are to replace the ? placeholders - * @return string The replaced SQL query - */ - private static function replaceParameters($sql, $args) { - $offset = 0; - foreach ($args AS $param => $value) { - if (is_int($args[$param]) || is_float($args[$param])) { - $replace = intval($args[$param]); - } else { - $replace = "'".self::escape($args[$param])."'"; - } - - $pos = strpos($sql, '?', $offset); - if ($pos !== false) { - $sql = substr_replace($sql, $replace, $pos, 1); - } - $offset = $pos + strlen($replace); - } - return $sql; - } - - /** - * @brief Convert parameter array to an universal form - * @param array $args Parameter array - * @return array universalized parameter array - */ - private static function getParam($args) { - unset($args[0]); - - // When the second function parameter is an array then use this as the parameter array - if ((count($args) > 0) && (is_array($args[1]))) { - return $args[1]; - } else { - return $args; - } - } - - /** - * @brief Executes a prepared statement that returns data - * @usage Example: $r = p("SELECT * FROM `item` WHERE `guid` = ?", $guid); - * - * Please only use it with complicated queries. - * For all regular queries please use dba::select or dba::exists - * - * @param string $sql SQL statement - * @return bool|object statement object - */ - public static function p($sql) { - $a = get_app(); - - $stamp1 = microtime(true); - - $params = self::getParam(func_get_args()); - - // Renumber the array keys to be sure that they fit - $i = 0; - $args = []; - foreach ($params AS $param) { - // Avoid problems with some MySQL servers and boolean values. See issue #3645 - if (is_bool($param)) { - $param = (int)$param; - } - $args[++$i] = $param; - } - - if (!self::$connected) { - return false; - } - - if ((substr_count($sql, '?') != count($args)) && (count($args) > 0)) { - // Question: Should we continue or stop the query here? - logger('Parameter mismatch. Query "'.$sql.'" - Parameters '.print_r($args, true), LOGGER_DEBUG); - } - - $sql = self::clean_query($sql); - $sql = self::any_value_fallback($sql); - - $orig_sql = $sql; - - if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) { - $sql = "/*".System::callstack()." */ ".$sql; - } - - self::$error = ''; - self::$errorno = 0; - self::$affected_rows = 0; - - // We have to make some things different if this function is called from "e" - $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2); - - if (isset($trace[1])) { - $called_from = $trace[1]; - } else { - // We use just something that is defined to avoid warnings - $called_from = $trace[0]; - } - // We are having an own error logging in the function "e" - $called_from_e = ($called_from['function'] == 'e'); - - switch (self::$driver) { - case 'pdo': - // If there are no arguments we use "query" - if (count($args) == 0) { - if (!$retval = self::$db->query($sql)) { - $errorInfo = self::$db->errorInfo(); - self::$error = $errorInfo[2]; - self::$errorno = $errorInfo[1]; - $retval = false; - break; - } - self::$affected_rows = $retval->rowCount(); - break; - } - - if (!$stmt = self::$db->prepare($sql)) { - $errorInfo = self::$db->errorInfo(); - self::$error = $errorInfo[2]; - self::$errorno = $errorInfo[1]; - $retval = false; - break; - } - - foreach ($args AS $param => $value) { - $stmt->bindParam($param, $args[$param]); - } - - if (!$stmt->execute()) { - $errorInfo = $stmt->errorInfo(); - self::$error = $errorInfo[2]; - self::$errorno = $errorInfo[1]; - $retval = false; - } else { - $retval = $stmt; - self::$affected_rows = $retval->rowCount(); - } - break; - case 'mysqli': - // There are SQL statements that cannot be executed with a prepared statement - $parts = explode(' ', $orig_sql); - $command = strtolower($parts[0]); - $can_be_prepared = in_array($command, ['select', 'update', 'insert', 'delete']); - - // The fallback routine is called as well when there are no arguments - if (!$can_be_prepared || (count($args) == 0)) { - $retval = self::$db->query(self::replaceParameters($sql, $args)); - if (self::$db->errno) { - self::$error = self::$db->error; - self::$errorno = self::$db->errno; - $retval = false; - } else { - if (isset($retval->num_rows)) { - self::$affected_rows = $retval->num_rows; - } else { - self::$affected_rows = self::$db->affected_rows; - } - } - break; - } - - $stmt = self::$db->stmt_init(); - - if (!$stmt->prepare($sql)) { - self::$error = $stmt->error; - self::$errorno = $stmt->errno; - $retval = false; - break; - } - - $params = ''; - $values = []; - foreach ($args AS $param => $value) { - if (is_int($args[$param])) { - $params .= 'i'; - } elseif (is_float($args[$param])) { - $params .= 'd'; - } elseif (is_string($args[$param])) { - $params .= 's'; - } else { - $params .= 'b'; - } - $values[] = &$args[$param]; - } - - if (count($values) > 0) { - array_unshift($values, $params); - call_user_func_array([$stmt, 'bind_param'], $values); - } - - if (!$stmt->execute()) { - self::$error = self::$db->error; - self::$errorno = self::$db->errno; - $retval = false; - } else { - $stmt->store_result(); - $retval = $stmt; - self::$affected_rows = $retval->affected_rows; - } - break; - } - - // We are having an own error logging in the function "e" - if ((self::$errorno != 0) && !$called_from_e) { - // We have to preserve the error code, somewhere in the logging it get lost - $error = self::$error; - $errorno = self::$errorno; - - logger('DB Error '.self::$errorno.': '.self::$error."\n". - System::callstack(8)."\n".self::replaceParameters($sql, $params)); - - self::$error = $error; - self::$errorno = $errorno; - } - - $a->save_timestamp($stamp1, 'database'); - - if (x($a->config,'system') && x($a->config['system'], 'db_log')) { - - $stamp2 = microtime(true); - $duration = (float)($stamp2 - $stamp1); - - if (($duration > $a->config["system"]["db_loglimit"])) { - $duration = round($duration, 3); - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - - @file_put_contents($a->config["system"]["db_log"], DateTimeFormat::utcNow()."\t".$duration."\t". - basename($backtrace[1]["file"])."\t". - $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t". - substr(self::replaceParameters($sql, $args), 0, 2000)."\n", FILE_APPEND); - } - } - return $retval; - } - - /** - * @brief Executes a prepared statement like UPDATE or INSERT that doesn't return data - * - * Please use dba::delete, dba::insert, dba::update, ... instead - * - * @param string $sql SQL statement - * @return boolean Was the query successfull? False is returned only if an error occurred - */ - public static function e($sql) { - $a = get_app(); - - $stamp = microtime(true); - - $params = self::getParam(func_get_args()); - - // In a case of a deadlock we are repeating the query 20 times - $timeout = 20; - - do { - $stmt = self::p($sql, $params); - - if (is_bool($stmt)) { - $retval = $stmt; - } elseif (is_object($stmt)) { - $retval = true; - } else { - $retval = false; - } - - self::close($stmt); - - } while ((self::$errorno == 1213) && (--$timeout > 0)); - - if (self::$errorno != 0) { - // We have to preserve the error code, somewhere in the logging it get lost - $error = self::$error; - $errorno = self::$errorno; - - logger('DB Error '.self::$errorno.': '.self::$error."\n". - System::callstack(8)."\n".self::replaceParameters($sql, $params)); - - self::$error = $error; - self::$errorno = $errorno; - } - - $a->save_timestamp($stamp, "database_write"); - - return $retval; - } - - /** - * @brief Check if data exists - * - * @param string $table Table name - * @param array $condition array of fields for condition - * - * @return boolean Are there rows for that condition? - */ - public static function exists($table, $condition) { - if (empty($table)) { - return false; - } - - $fields = []; - - reset($condition); - $first_key = key($condition); - if (!is_int($first_key)) { - $fields = [$first_key]; - } - - $stmt = self::select($table, $fields, $condition, ['limit' => 1]); - - if (is_bool($stmt)) { - $retval = $stmt; - } else { - $retval = (self::num_rows($stmt) > 0); - } - - self::close($stmt); - - return $retval; - } - - /** - * Fetches the first row - * - * Please use dba::selectFirst or dba::exists whenever this is possible. - * - * @brief Fetches the first row - * @param string $sql SQL statement - * @return array first row of query - */ - public static function fetch_first($sql) { - $params = self::getParam(func_get_args()); - - $stmt = self::p($sql, $params); - - if (is_bool($stmt)) { - $retval = $stmt; - } else { - $retval = self::fetch($stmt); - } - - self::close($stmt); - - return $retval; - } - - /** - * @brief Returns the number of affected rows of the last statement - * - * @return int Number of rows - */ - public static function affected_rows() { - return self::$affected_rows; - } - - /** - * @brief Returns the number of columns of a statement - * - * @param object Statement object - * @return int Number of columns - */ - public static function columnCount($stmt) { - if (!is_object($stmt)) { - return 0; - } - switch (self::$driver) { - case 'pdo': - return $stmt->columnCount(); - case 'mysqli': - return $stmt->field_count; - } - return 0; - } - /** - * @brief Returns the number of rows of a statement - * - * @param PDOStatement|mysqli_result|mysqli_stmt Statement object - * @return int Number of rows - */ - public static function num_rows($stmt) { - if (!is_object($stmt)) { - return 0; - } - switch (self::$driver) { - case 'pdo': - return $stmt->rowCount(); - case 'mysqli': - return $stmt->num_rows; - } - return 0; - } - - /** - * @brief Fetch a single row - * - * @param mixed $stmt statement object - * @return array current row - */ - public static function fetch($stmt) { - $a = get_app(); - - $stamp1 = microtime(true); - - $columns = []; - - if (!is_object($stmt)) { - return false; - } - - switch (self::$driver) { - case 'pdo': - $columns = $stmt->fetch(PDO::FETCH_ASSOC); - break; - case 'mysqli': - if (get_class($stmt) == 'mysqli_result') { - $columns = $stmt->fetch_assoc(); - break; - } - - // This code works, but is slow - - // Bind the result to a result array - $cols = []; - - $cols_num = []; - for ($x = 0; $x < $stmt->field_count; $x++) { - $cols[] = &$cols_num[$x]; - } - - call_user_func_array([$stmt, 'bind_result'], $cols); - - if (!$stmt->fetch()) { - return false; - } - - // The slow part: - // We need to get the field names for the array keys - // It seems that there is no better way to do this. - $result = $stmt->result_metadata(); - $fields = $result->fetch_fields(); - - foreach ($cols_num AS $param => $col) { - $columns[$fields[$param]->name] = $col; - } - } - - $a->save_timestamp($stamp1, 'database'); - - return $columns; - } - - /** - * @brief Insert a row into a table - * - * @param string $table Table name - * @param array $param parameter array - * @param bool $on_duplicate_update Do an update on a duplicate entry - * - * @return boolean was the insert successfull? - */ - public static function insert($table, $param, $on_duplicate_update = false) { - - if (empty($table) || empty($param)) { - logger('Table and fields have to be set'); - return false; - } - - $sql = "INSERT INTO `".self::escape($table)."` (`".implode("`, `", array_keys($param))."`) VALUES (". - substr(str_repeat("?, ", count($param)), 0, -2).")"; - - if ($on_duplicate_update) { - $sql .= " ON DUPLICATE KEY UPDATE `".implode("` = ?, `", array_keys($param))."` = ?"; - - $values = array_values($param); - $param = array_merge_recursive($values, $values); - } - - return self::e($sql, $param); - } - - /** - * @brief Fetch the id of the last insert command - * - * @return integer Last inserted id - */ - public static function lastInsertId() { - switch (self::$driver) { - case 'pdo': - $id = self::$db->lastInsertId(); - break; - case 'mysqli': - $id = self::$db->insert_id; - break; - } - return $id; - } - - /** - * @brief Locks a table for exclusive write access - * - * This function can be extended in the future to accept a table array as well. - * - * @param string $table Table name - * - * @return boolean was the lock successful? - */ - public static function lock($table) { - // See here: https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html - self::e("SET autocommit=0"); - $success = self::e("LOCK TABLES `".self::escape($table)."` WRITE"); - if (!$success) { - self::e("SET autocommit=1"); - } else { - self::$in_transaction = true; - } - return $success; - } - - /** - * @brief Unlocks all locked tables - * - * @return boolean was the unlock successful? - */ - public static function unlock() { - // See here: https://dev.mysql.com/doc/refman/5.7/en/lock-tables-and-transactions.html - self::e("COMMIT"); - $success = self::e("UNLOCK TABLES"); - self::e("SET autocommit=1"); - self::$in_transaction = false; - return $success; - } - - /** - * @brief Starts a transaction - * - * @return boolean Was the command executed successfully? - */ - public static function transaction() { - if (!self::e('COMMIT')) { - return false; - } - if (!self::e('START TRANSACTION')) { - return false; - } - self::$in_transaction = true; - return true; - } - - /** - * @brief Does a commit - * - * @return boolean Was the command executed successfully? - */ - public static function commit() { - if (!self::e('COMMIT')) { - return false; - } - self::$in_transaction = false; - return true; - } - - /** - * @brief Does a rollback - * - * @return boolean Was the command executed successfully? - */ - public static function rollback() { - if (!self::e('ROLLBACK')) { - return false; - } - self::$in_transaction = false; - return true; - } - - /** - * @brief Build the array with the table relations - * - * The array is build from the database definitions in DBStructure.php - * - * This process must only be started once, since the value is cached. - */ - private static function buildRelationData() { - $definition = DBStructure::definition(); - - foreach ($definition AS $table => $structure) { - foreach ($structure['fields'] AS $field => $field_struct) { - if (isset($field_struct['relation'])) { - foreach ($field_struct['relation'] AS $rel_table => $rel_field) { - self::$relation[$rel_table][$rel_field][$table][] = $field; - } - } - } - } - } - - /** - * @brief Delete a row from a table - * - * @param string $table Table name - * @param array $conditions Field condition(s) - * @param array $options - * - cascade: If true we delete records in other tables that depend on the one we're deleting through - * relations (default: true) - * @param boolean $in_process Internal use: Only do a commit after the last delete - * @param array $callstack Internal use: prevent endless loops - * - * @return boolean|array was the delete successful? When $in_process is set: deletion data - */ - public static function delete($table, array $conditions, array $options = [], $in_process = false, array &$callstack = []) - { - if (empty($table) || empty($conditions)) { - logger('Table and conditions have to be set'); - return false; - } - - $commands = []; - - // Create a key for the loop prevention - $key = $table . ':' . implode(':', array_keys($conditions)) . ':' . implode(':', $conditions); - - // We quit when this key already exists in the callstack. - if (isset($callstack[$key])) { - return $commands; - } - - $callstack[$key] = true; - - $table = self::escape($table); - - $commands[$key] = ['table' => $table, 'conditions' => $conditions]; - - $cascade = defaults($options, 'cascade', true); - - // To speed up the whole process we cache the table relations - if ($cascade && count(self::$relation) == 0) { - self::buildRelationData(); - } - - // Is there a relation entry for the table? - if ($cascade && isset(self::$relation[$table])) { - // We only allow a simple "one field" relation. - $field = array_keys(self::$relation[$table])[0]; - $rel_def = array_values(self::$relation[$table])[0]; - - // Create a key for preventing double queries - $qkey = $field . '-' . $table . ':' . implode(':', array_keys($conditions)) . ':' . implode(':', $conditions); - - // When the search field is the relation field, we don't need to fetch the rows - // This is useful when the leading record is already deleted in the frontend but the rest is done in the backend - if ((count($conditions) == 1) && ($field == array_keys($conditions)[0])) { - foreach ($rel_def AS $rel_table => $rel_fields) { - foreach ($rel_fields AS $rel_field) { - $retval = self::delete($rel_table, [$rel_field => array_values($conditions)[0]], $options, true, $callstack); - $commands = array_merge($commands, $retval); - } - } - // We quit when this key already exists in the callstack. - } elseif (!isset($callstack[$qkey])) { - - $callstack[$qkey] = true; - - // Fetch all rows that are to be deleted - $data = self::select($table, [$field], $conditions); - - while ($row = self::fetch($data)) { - // Now we accumulate the delete commands - $retval = self::delete($table, [$field => $row[$field]], $options, true, $callstack); - $commands = array_merge($commands, $retval); - } - - self::close($data); - - // Since we had split the delete command we don't need the original command anymore - unset($commands[$key]); - } - } - - if (!$in_process) { - // Now we finalize the process - $do_transaction = !self::$in_transaction; - - if ($do_transaction) { - self::transaction(); - } - - $compacted = []; - $counter = []; - - foreach ($commands AS $command) { - $conditions = $command['conditions']; - reset($conditions); - $first_key = key($conditions); - - $condition_string = self::buildCondition($conditions); - - if ((count($command['conditions']) > 1) || is_int($first_key)) { - $sql = "DELETE FROM `" . $command['table'] . "`" . $condition_string; - logger(self::replaceParameters($sql, $conditions), LOGGER_DATA); - - if (!self::e($sql, $conditions)) { - if ($do_transaction) { - self::rollback(); - } - return false; - } - } else { - $key_table = $command['table']; - $key_condition = array_keys($command['conditions'])[0]; - $value = array_values($command['conditions'])[0]; - - // Split the SQL queries in chunks of 100 values - // We do the $i stuff here to make the code better readable - $i = $counter[$key_table][$key_condition]; - if (isset($compacted[$key_table][$key_condition][$i]) && count($compacted[$key_table][$key_condition][$i]) > 100) { - ++$i; - } - - $compacted[$key_table][$key_condition][$i][$value] = $value; - $counter[$key_table][$key_condition] = $i; - } - } - foreach ($compacted AS $table => $values) { - foreach ($values AS $field => $field_value_list) { - foreach ($field_value_list AS $field_values) { - $sql = "DELETE FROM `" . $table . "` WHERE `" . $field . "` IN (" . - substr(str_repeat("?, ", count($field_values)), 0, -2) . ");"; - - logger(self::replaceParameters($sql, $field_values), LOGGER_DATA); - - if (!self::e($sql, $field_values)) { - if ($do_transaction) { - self::rollback(); - } - return false; - } - } - } - } - if ($do_transaction) { - self::commit(); - } - return true; - } - - return $commands; - } - - /** - * @brief Updates rows - * - * Updates rows in the database. When $old_fields is set to an array, - * the system will only do an update if the fields in that array changed. - * - * Attention: - * Only the values in $old_fields are compared. - * This is an intentional behaviour. - * - * Example: - * We include the timestamp field in $fields but not in $old_fields. - * Then the row will only get the new timestamp when the other fields had changed. - * - * When $old_fields is set to a boolean value the system will do this compare itself. - * When $old_fields is set to "true" the system will do an insert if the row doesn't exists. - * - * Attention: - * Only set $old_fields to a boolean value when you are sure that you will update a single row. - * When you set $old_fields to "true" then $fields must contain all relevant fields! - * - * @param string $table Table name - * @param array $fields contains the fields that are updated - * @param array $condition condition array with the key values - * @param array|boolean $old_fields array with the old field values that are about to be replaced (true = update on duplicate) - * - * @return boolean was the update successfull? - */ - public static function update($table, $fields, $condition, $old_fields = []) { - - if (empty($table) || empty($fields) || empty($condition)) { - logger('Table, fields and condition have to be set'); - return false; - } - - $table = self::escape($table); - - $condition_string = self::buildCondition($condition); - - if (is_bool($old_fields)) { - $do_insert = $old_fields; - - $old_fields = self::selectFirst($table, [], $condition); - - if (is_bool($old_fields)) { - if ($do_insert) { - $values = array_merge($condition, $fields); - return self::insert($table, $values, $do_insert); - } - $old_fields = []; - } - } - - $do_update = (count($old_fields) == 0); - - foreach ($old_fields AS $fieldname => $content) { - if (isset($fields[$fieldname])) { - if ($fields[$fieldname] == $content) { - unset($fields[$fieldname]); - } else { - $do_update = true; - } - } - } - - if (!$do_update || (count($fields) == 0)) { - return true; - } - - $sql = "UPDATE `".$table."` SET `". - implode("` = ?, `", array_keys($fields))."` = ?".$condition_string; - - $params1 = array_values($fields); - $params2 = array_values($condition); - $params = array_merge_recursive($params1, $params2); - - return self::e($sql, $params); - } - - /** - * Retrieve a single record from a table and returns it in an associative array - * - * @brief Retrieve a single record from a table - * @param string $table - * @param array $fields - * @param array $condition - * @param array $params - * @return bool|array - * @see dba::select - */ - public static function selectFirst($table, array $fields = [], array $condition = [], $params = []) - { - $params['limit'] = 1; - $result = self::select($table, $fields, $condition, $params); - - if (is_bool($result)) { - return $result; - } else { - $row = self::fetch($result); - self::close($result); - return $row; - } - } - - /** - * @brief Select rows from a table - * - * @param string $table Table name - * @param array $fields Array of selected fields, empty for all - * @param array $condition Array of fields for condition - * @param array $params Array of several parameters - * - * @return boolean|object - * - * Example: - * $table = "item"; - * $fields = array("id", "uri", "uid", "network"); - * - * $condition = array("uid" => 1, "network" => 'dspr'); - * or: - * $condition = array("`uid` = ? AND `network` IN (?, ?)", 1, 'dfrn', 'dspr'); - * - * $params = array("order" => array("id", "received" => true), "limit" => 10); - * - * $data = dba::select($table, $fields, $condition, $params); - */ - public static function select($table, array $fields = [], array $condition = [], array $params = []) - { - if ($table == '') { - return false; - } - - $table = self::escape($table); - - if (count($fields) > 0) { - $select_fields = "`" . implode("`, `", array_values($fields)) . "`"; - } else { - $select_fields = "*"; - } - - $condition_string = self::buildCondition($condition); - - $order_string = ''; - if (isset($params['order'])) { - $order_string = " ORDER BY "; - foreach ($params['order'] AS $fields => $order) { - if (!is_int($fields)) { - $order_string .= "`" . $fields . "` " . ($order ? "DESC" : "ASC") . ", "; - } else { - $order_string .= "`" . $order . "`, "; - } - } - $order_string = substr($order_string, 0, -2); - } - - $limit_string = ''; - if (isset($params['limit']) && is_int($params['limit'])) { - $limit_string = " LIMIT " . $params['limit']; - } - - if (isset($params['limit']) && is_array($params['limit'])) { - $limit_string = " LIMIT " . intval($params['limit'][0]) . ", " . intval($params['limit'][1]); - } - - $sql = "SELECT " . $select_fields . " FROM `" . $table . "`" . $condition_string . $order_string . $limit_string; - - $result = self::p($sql, $condition); - - return $result; - } - - /** - * @brief Counts the rows from a table satisfying the provided condition - * - * @param string $table Table name - * @param array $condition array of fields for condition - * - * @return int - * - * Example: - * $table = "item"; - * - * $condition = ["uid" => 1, "network" => 'dspr']; - * or: - * $condition = ["`uid` = ? AND `network` IN (?, ?)", 1, 'dfrn', 'dspr']; - * - * $count = dba::count($table, $condition); - */ - public static function count($table, array $condition = []) - { - if ($table == '') { - return false; - } - - $condition_string = self::buildCondition($condition); - - $sql = "SELECT COUNT(*) AS `count` FROM `".$table."`".$condition_string; - - $row = self::fetch_first($sql, $condition); - - return $row['count']; - } - - /** - * @brief Returns the SQL condition string built from the provided condition array - * - * This function operates with two modes. - * - Supplied with a filed/value associative array, it builds simple strict - * equality conditions linked by AND. - * - Supplied with a flat list, the first element is the condition string and - * the following arguments are the values to be interpolated - * - * $condition = ["uid" => 1, "network" => 'dspr']; - * or: - * $condition = ["`uid` = ? AND `network` IN (?, ?)", 1, 'dfrn', 'dspr']; - * - * In either case, the provided array is left with the parameters only - * - * @param array $condition - * @return string - */ - private static function buildCondition(array &$condition = []) - { - $condition_string = ''; - if (count($condition) > 0) { - reset($condition); - $first_key = key($condition); - if (is_int($first_key)) { - $condition_string = " WHERE ".array_shift($condition); - } else { - $new_values = []; - $condition_string = ""; - foreach ($condition as $field => $value) { - if ($condition_string != "") { - $condition_string .= " AND "; - } - if (is_array($value)) { - $new_values = array_merge($new_values, array_values($value)); - $placeholders = substr(str_repeat("?, ", count($value)), 0, -2); - $condition_string .= "`" . $field . "` IN (" . $placeholders . ")"; - } else { - $new_values[$field] = $value; - $condition_string .= "`" . $field . "` = ?"; - } - } - $condition_string = " WHERE " . $condition_string; - $condition = $new_values; - } - } - - return $condition_string; - } - - /** - * @brief Fills an array with data from a query - * - * @param object $stmt statement object - * @return array Data array - */ - public static function inArray($stmt, $do_close = true) { - if (is_bool($stmt)) { - return $stmt; - } - - $data = []; - while ($row = self::fetch($stmt)) { - $data[] = $row; - } - if ($do_close) { - self::close($stmt); - } - return $data; - } - - /** - * @brief Returns the error number of the last query - * - * @return string Error number (0 if no error) - */ - public static function errorNo() { - return self::$errorno; - } - - /** - * @brief Returns the error message of the last query - * - * @return string Error message ('' if no error) - */ - public static function errorMessage() { - return self::$error; - } - - /** - * @brief Closes the current statement - * - * @param object $stmt statement object - * @return boolean was the close successful? - */ - public static function close($stmt) { - $a = get_app(); - - $stamp1 = microtime(true); - - if (!is_object($stmt)) { - return false; - } - - switch (self::$driver) { - case 'pdo': - $ret = $stmt->closeCursor(); - break; - case 'mysqli': - $stmt->free_result(); - $ret = $stmt->close(); - break; - } - - $a->save_timestamp($stamp1, 'database'); - - return $ret; - } -} - -function dbesc($str) { - if (dba::$connected) { - return(dba::escape($str)); - } else { - return(str_replace("'","\\'",$str)); - } -} +use Friendica\Database\DBA; /** * @brief execute SQL query with printf style args - deprecated * - * Please use the dba:: functions instead: - * dba::select, dba::exists, dba::insert - * dba::delete, dba::update, dba::p, dba::e + * Please use the DBA:: functions instead: + * DBA::select, DBA::exists, DBA::insert + * DBA::delete, DBA::update, DBA::p, DBA::e * * @param $args Query parameters (1 to N parameters of different types) * @return array|bool Query array + * @deprecated */ function q($sql) { $args = func_get_args(); unset($args[0]); - if (!dba::$connected) { + if (!DBA::$connected) { return false; } - $sql = dba::clean_query($sql); - $sql = dba::any_value_fallback($sql); + $sql = DBA::cleanQuery($sql); + $sql = DBA::anyValueFallback($sql); $stmt = @vsprintf($sql, $args); - $ret = dba::p($stmt); + $ret = DBA::p($stmt); if (is_bool($ret)) { return $ret; } - $columns = dba::columnCount($ret); + $columns = DBA::columnCount($ret); - $data = dba::inArray($ret); + $data = DBA::toArray($ret); if ((count($data) == 0) && ($columns == 0)) { return true; @@ -1367,7 +42,3 @@ function q($sql) { return $data; } - -function dba_timer() { - return microtime(true); -} diff --git a/include/enotify.php b/include/enotify.php index 39c74fdc6..70abce584 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -8,7 +8,9 @@ use Friendica\Core\Addon; use Friendica\Core\Config; use Friendica\Core\L10n; use Friendica\Core\System; -use Friendica\Database\DBM; +use Friendica\Database\DBA; +use Friendica\Model\Contact; +use Friendica\Model\Item; use Friendica\Util\DateTimeFormat; use Friendica\Util\Emailer; @@ -25,6 +27,25 @@ function notification($params) { $a = get_app(); + // Temporary logging for finding the origin + if (!isset($params['language']) || !isset($params['uid'])) { + logger('Missing parameters.' . System::callstack()); + } + + // Ensure that the important fields are set at any time + $fields = ['notify-flags', 'language', 'username', 'email']; + $user = DBA::selectFirst('user', $fields, ['uid' => $params['uid']]); + + if (!DBA::isResult($user)) { + logger('Unknown user ' . $params['uid']); + return; + } + + $params['notify_flags'] = defaults($params, 'notify_flags', $user['notify-flags']); + $params['language'] = defaults($params, 'language', $user['language']); + $params['to_name'] = defaults($params, 'to_name', $user['username']); + $params['to_email'] = defaults($params, 'to_email', $user['email']); + // from here on everything is in the recipients language L10n::pushLang($params['language']); @@ -32,11 +53,11 @@ function notification($params) $product = FRIENDICA_PLATFORM; $siteurl = System::baseUrl(true); $thanks = L10n::t('Thank You,'); - $sitename = $a->config['sitename']; - if (!x($a->config['admin_name'])) { - $site_admin = L10n::t('%s Administrator', $sitename); + $sitename = Config::get('config', 'sitename'); + if (Config::get('config', 'admin_name')) { + $site_admin = L10n::t('%1$s, %2$s Administrator', Config::get('config', 'admin_name'), $sitename); } else { - $site_admin = L10n::t('%1$s, %2$s Administrator', $a->config['admin_name'], $sitename); + $site_admin = L10n::t('%s Administrator', $sitename); } $sender_name = $sitename; @@ -48,15 +69,17 @@ function notification($params) $sender_email = $a->getSenderEmailAddress(); if ($params['type'] != SYSTEM_EMAIL) { - $user = dba::selectFirst('user', ['nickname', 'page-flags'], + $user = DBA::selectFirst('user', ['nickname', 'page-flags'], ['uid' => $params['uid']]); // There is no need to create notifications for forum accounts - if (!DBM::is_result($user) || in_array($user["page-flags"], [PAGE_COMMUNITY, PAGE_PRVGROUP])) { + if (!DBA::isResult($user) || in_array($user["page-flags"], [Contact::PAGE_COMMUNITY, Contact::PAGE_PRVGROUP])) { return; } + $nickname = $user["nickname"]; + } else { + $nickname = ''; } - $nickname = $user["nickname"]; // with $params['show_in_notification_page'] == false, the notification isn't inserted into // the database, and an email is sent if applicable. @@ -91,7 +114,12 @@ function notification($params) $parent_id = 0; } + $epreamble = ''; + if ($params['type'] == NOTIFY_MAIL) { + $itemlink = $siteurl.'/message/'.$params['item']['id']; + $params["link"] = $itemlink; + $subject = L10n::t('[Friendica:Notify] New mail received at %s', $sitename); $preamble = L10n::t('%1$s sent you a new private message at %2$s.', $params['source_name'], $sitename); @@ -100,26 +128,20 @@ function notification($params) $sitelink = L10n::t('Please visit %s to view and/or reply to your private messages.'); $tsitelink = sprintf($sitelink, $siteurl.'/message/'.$params['item']['id']); $hsitelink = sprintf($sitelink, ''.$sitename.''); - $itemlink = $siteurl.'/message/'.$params['item']['id']; } if ($params['type'] == NOTIFY_COMMENT) { - $thread = dba::selectFirst('thread', ['ignored'], ['iid' => $parent_id]); - if (DBM::is_result($thread) && $thread["ignored"]) { + $thread = Item::selectFirstThreadForUser($params['uid'] ,['ignored'], ['iid' => $parent_id]); + if (DBA::isResult($thread) && $thread["ignored"]) { logger("Thread ".$parent_id." will be ignored", LOGGER_DEBUG); return; } // Check to see if there was already a tag notify or comment notify for this post. // If so don't create a second notification - $p = q("SELECT `id` FROM `notify` WHERE `type` IN (%d, %d, %d) AND `link` = '%s' AND `uid` = %d LIMIT 1", - intval(NOTIFY_TAGSELF), - intval(NOTIFY_COMMENT), - intval(NOTIFY_SHARE), - dbesc($params['link']), - intval($params['uid']) - ); - if ($p && count($p)) { + $condition = ['type' => [NOTIFY_TAGSELF, NOTIFY_COMMENT, NOTIFY_SHARE], + 'link' => $params['link'], 'uid' => $params['uid']]; + if (DBA::exists('notify', $condition)) { L10n::popLang(); return; } @@ -129,10 +151,11 @@ function notification($params) $item = null; if ($params['otype'] === 'item' && $parent_id) { - $item = dba::selectFirst('item', [], ['id' => $parent_id]); + $item = Item::selectFirstForUser($params['uid'], Item::ITEM_FIELDLIST, ['id' => $parent_id]); } $item_post_type = item_post_type($item); + $itemlink = $item['plink']; // "a post" $dest_str = L10n::t('%1$s commented on [url=%2$s]a %3$s[/url]', @@ -152,7 +175,7 @@ function notification($params) } // "your post" - if (DBM::is_result($item) && $item['owner-name'] == $item['author-name'] && $item['wall']) { + if (DBA::isResult($item) && $item['owner-id'] == $item['author-id'] && $item['wall']) { $dest_str = L10n::t('%1$s commented on [url=%2$s]your %3$s[/url]', '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', $itemlink, @@ -241,6 +264,7 @@ function notification($params) } if ($params['type'] == NOTIFY_TAGSHARE) { + $itemlink = $params['link']; $subject = L10n::t('[Friendica:Notify] %s tagged your post', $params['source_name']); $preamble = L10n::t('%1$s tagged your post at %2$s', $params['source_name'], $sitename); @@ -252,10 +276,10 @@ function notification($params) $sitelink = L10n::t('Please visit %s to view and/or reply to the conversation.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, ''.$sitename.''); - $itemlink = $params['link']; } if ($params['type'] == NOTIFY_INTRO) { + $itemlink = $params['link']; $subject = L10n::t('[Friendica:Notify] Introduction received'); $preamble = L10n::t('You\'ve received an introduction from \'%1$s\' at %2$s', $params['source_name'], $sitename); @@ -269,7 +293,6 @@ function notification($params) $sitelink = L10n::t('Please visit %s to approve or reject the introduction.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, ''.$sitename.''); - $itemlink = $params['link']; switch ($params['verb']) { case ACTIVITY_FRIEND: @@ -299,6 +322,7 @@ function notification($params) } if ($params['type'] == NOTIFY_SUGGEST) { + $itemlink = $params['link']; $subject = L10n::t('[Friendica:Notify] Friend suggestion received'); $preamble = L10n::t('You\'ve received a friend suggestion from \'%1$s\' at %2$s', $params['source_name'], $sitename); @@ -315,11 +339,11 @@ function notification($params) $sitelink = L10n::t('Please visit %s to approve or reject the suggestion.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, ''.$sitename.''); - $itemlink = $params['link']; } if ($params['type'] == NOTIFY_CONFIRM) { if ($params['verb'] == ACTIVITY_FRIEND) { // mutual connection + $itemlink = $params['link']; $subject = L10n::t('[Friendica:Notify] Connection accepted'); $preamble = L10n::t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename); @@ -333,8 +357,8 @@ function notification($params) $sitelink = L10n::t('Please visit %s if you wish to make any changes to this relationship.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, ''.$sitename.''); - $itemlink = $params['link']; } else { // ACTIVITY_FOLLOW + $itemlink = $params['link']; $subject = L10n::t('[Friendica:Notify] Connection accepted'); $preamble = L10n::t('\'%1$s\' has accepted your connection request at %2$s', $params['source_name'], $sitename); @@ -350,13 +374,13 @@ function notification($params) $sitelink = L10n::t('Please visit %s if you wish to make any changes to this relationship.'); $tsitelink = sprintf($sitelink, $siteurl); $hsitelink = sprintf($sitelink, ''.$sitename.''); - $itemlink = $params['link']; } } if ($params['type'] == NOTIFY_SYSTEM) { switch($params['event']) { case "SYSTEM_REGISTER_REQUEST": + $itemlink = $params['link']; $subject = L10n::t('[Friendica System Notify]') . ' ' . L10n::t('registration request'); $preamble = L10n::t('You\'ve received a registration request from \'%1$s\' at %2$s', $params['source_name'], $sitename); @@ -365,7 +389,7 @@ function notification($params) '[url='.$params['source_link'].']'.$params['source_name'].'[/url]' ); - $body = L10n::t('Full Name: %1$s\nSite Location: %2$s\nLogin Name: %3$s ' . "\x28" . '%4$s' . "\x29", + $body = L10n::t("Full Name: %s\nSite Location: %s\nLogin Name: %s (%s)", $params['source_name'], $siteurl, $params['source_mail'], $params['source_nick'] @@ -374,7 +398,6 @@ function notification($params) $sitelink = L10n::t('Please visit %s to approve or reject the request.'); $tsitelink = sprintf($sitelink, $params['link']); $hsitelink = sprintf($sitelink, ''.$sitename.'

'); - $itemlink = $params['link']; break; case "SYSTEM_DB_UPDATE_FAIL": break; @@ -432,9 +455,7 @@ function notification($params) do { $dups = false; $hash = random_string(); - $r = q("SELECT `id` FROM `notify` WHERE `hash` = '%s' LIMIT 1", - dbesc($hash)); - if (DBM::is_result($r)) { + if (DBA::exists('notify', ['hash' => $hash])) { $dups = true; } } while ($dups == true); @@ -464,45 +485,26 @@ function notification($params) } // create notification entry in DB - q("INSERT INTO `notify` (`hash`, `name`, `url`, `photo`, `date`, `uid`, `link`, `iid`, `parent`, `type`, `verb`, `otype`, `name_cache`) - values('%s', '%s', '%s', '%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s')", - dbesc($datarray['hash']), - dbesc($datarray['name']), - dbesc($datarray['url']), - dbesc($datarray['photo']), - dbesc($datarray['date']), - intval($datarray['uid']), - dbesc($datarray['link']), - intval($datarray['iid']), - intval($datarray['parent']), - intval($datarray['type']), - dbesc($datarray['verb']), - dbesc($datarray['otype']), - dbesc($datarray["name_cache"]) - ); + $fields = ['hash' => $datarray['hash'], 'name' => $datarray['name'], 'url' => $datarray['url'], + 'photo' => $datarray['photo'], 'date' => $datarray['date'], 'uid' => $datarray['uid'], + 'link' => $datarray['link'], 'iid' => $datarray['iid'], 'parent' => $datarray['parent'], + 'type' => $datarray['type'], 'verb' => $datarray['verb'], 'otype' => $datarray['otype'], + 'name_cache' => $datarray["name_cache"]]; + DBA::insert('notify', $fields); - $r = q("SELECT `id` FROM `notify` WHERE `hash` = '%s' AND `uid` = %d LIMIT 1", - dbesc($hash), - intval($params['uid']) - ); - if ($r) { - $notify_id = $r[0]['id']; - } else { - L10n::popLang(); - return False; - } + $notify_id = DBA::lastInsertId(); // we seem to have a lot of duplicate comment notifications due to race conditions, mostly from forums // After we've stored everything, look again to see if there are any duplicates and if so remove them $p = q("SELECT `id` FROM `notify` WHERE `type` IN (%d, %d) AND `link` = '%s' AND `uid` = %d ORDER BY `id`", intval(NOTIFY_TAGSELF), intval(NOTIFY_COMMENT), - dbesc($params['link']), + DBA::escape($params['link']), intval($params['uid']) ); if ($p && (count($p) > 1)) { for ($d = 1; $d < count($p); $d ++) { - dba::delete('notify', ['id' => $p[$d]['id']]); + DBA::delete('notify', ['id' => $p[$d]['id']]); } // only continue on if we stored the first one @@ -515,12 +517,10 @@ function notification($params) $itemlink = System::baseUrl().'/notify/view/'.$notify_id; $msg = replace_macros($epreamble, ['$itemlink' => $itemlink]); $msg_cache = format_notification_message($datarray['name_cache'], strip_tags(BBCode::convert($msg))); - q("UPDATE `notify` SET `msg` = '%s', `msg_cache` = '%s' WHERE `id` = %d AND `uid` = %d", - dbesc($msg), - dbesc($msg_cache), - intval($notify_id), - intval($params['uid']) - ); + + $fields = ['msg' => $msg, 'msg_cache' => $msg_cache]; + $condition = ['id' => $notify_id, 'uid' => $params['uid']]; + DBA::update('notify', $fields, $condition); } // send email notification if notification preferences permit @@ -534,21 +534,12 @@ function notification($params) $id_for_parent = $params['parent']."@".$hostname; // Is this the first email notification for this parent item and user? - - $r = q("SELECT `id` FROM `notify-threads` WHERE `master-parent-item` = %d AND `receiver-uid` = %d LIMIT 1", - intval($params['parent']), - intval($params['uid'])); - - // If so, create the record of it and use a message-id smtp header. - - if (!$r) { + if (!DBA::exists('notify-threads', ['master-parent-item' => $params['parent'], 'receiver-uid' => $params['uid']])) { logger("notify_id:".intval($notify_id).", parent: ".intval($params['parent'])."uid: ".intval($params['uid']), LOGGER_DEBUG); - q("INSERT INTO `notify-threads` (`notify-id`, `master-parent-item`, `receiver-uid`, `parent-item`) - values(%d, %d, %d, %d)", - intval($notify_id), - intval($params['parent']), - intval($params['uid']), - 0); + + $fields = ['notify-id' => $notify_id, 'master-parent-item' => $params['parent'], + 'receiver-uid' => $params['uid'], 'parent-item' => 0]; + DBA::insert('notify-threads', $fields); $additional_mail_header .= "Message-ID: <${id_for_parent}>\n"; $log_msg = "include/enotify: No previous notification found for this parent:\n". @@ -557,15 +548,12 @@ function notification($params) } else { // If not, just "follow" the thread. $additional_mail_header .= "References: <${id_for_parent}>\nIn-Reply-To: <${id_for_parent}>\n"; - logger("There's already a notification for this parent:\n".print_r($r, true), LOGGER_DEBUG); + logger("There's already a notification for this parent.", LOGGER_DEBUG); } } - // textversion keeps linebreaks - $textversion = strip_tags(str_replace("
", "\n", html_entity_decode(BBCode::convert(stripslashes(str_replace(["\\r\\n", "\\r", "\\n"], "\n", - $body))),ENT_QUOTES, 'UTF-8'))); - $htmlversion = html_entity_decode(BBCode::convert(stripslashes(str_replace(["\\r\\n", "\\r", "\\n\\n", "\\n"], - "
\n", $body))), ENT_QUOTES, 'UTF-8'); + $textversion = BBCode::toPlaintext($body); + $htmlversion = BBCode::convert($body); $datarray = []; $datarray['banner'] = $banner; @@ -574,12 +562,12 @@ function notification($params) $datarray['sitename'] = $sitename; $datarray['siteurl'] = $siteurl; $datarray['type'] = $params['type']; - $datarray['parent'] = $params['parent']; - $datarray['source_name'] = $params['source_name']; - $datarray['source_link'] = $params['source_link']; - $datarray['source_photo'] = $params['source_photo']; + $datarray['parent'] = $parent_id; + $datarray['source_name'] = defaults($params, 'source_name', ''); + $datarray['source_link'] = defaults($params, 'source_link', ''); + $datarray['source_photo'] = defaults($params, 'source_photo', ''); $datarray['uid'] = $params['uid']; - $datarray['username'] = $params['to_name']; + $datarray['username'] = defaults($params, 'to_name', ''); $datarray['hsitelink'] = $hsitelink; $datarray['tsitelink'] = $tsitelink; $datarray['hitemlink'] = ''.$itemlink.''; @@ -609,7 +597,7 @@ function notification($params) '$source_name' => $datarray['source_name'], '$source_link' => $datarray['source_link'], '$source_photo' => $datarray['source_photo'], - '$username' => $datarray['to_name'], + '$username' => $datarray['username'], '$hsitelink' => $datarray['hsitelink'], '$hitemlink' => $datarray['hitemlink'], '$thanks' => $datarray['thanks'], @@ -630,7 +618,7 @@ function notification($params) '$source_name' => $datarray['source_name'], '$source_link' => $datarray['source_link'], '$source_photo' => $datarray['source_photo'], - '$username' => $datarray['to_name'], + '$username' => $datarray['username'], '$tsitelink' => $datarray['tsitelink'], '$titemlink' => $datarray['titemlink'], '$thanks' => $datarray['thanks'], @@ -665,13 +653,13 @@ function notification($params) */ function check_user_notification($itemid) { // fetch all users in the thread - $users = dba::p("SELECT DISTINCT(`contact`.`uid`) FROM `item` + $users = DBA::p("SELECT DISTINCT(`contact`.`uid`) FROM `item` INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` != 0 WHERE `parent` IN (SELECT `parent` FROM `item` WHERE `id`=?)", $itemid); - while ($user = dba::fetch($users)) { + while ($user = DBA::fetch($users)) { check_item_notification($itemid, $user['uid']); } - dba::close($users); + DBA::close($users); } /** @@ -679,7 +667,7 @@ function check_user_notification($itemid) { * * @param int $itemid ID of the item for which the check should be done * @param int $uid User ID - * @param str $defaulttype (Optional) Forces a notification with this type. + * @param string $defaulttype (Optional) Forces a notification with this type. */ function check_item_notification($itemid, $uid, $defaulttype = "") { $notification_data = ["uid" => $uid, "profiles" => []]; @@ -687,14 +675,14 @@ function check_item_notification($itemid, $uid, $defaulttype = "") { $profiles = $notification_data["profiles"]; - $fields = ['notify-flags', 'language', 'username', 'email', 'nickname']; - $user = dba::selectFirst('user', $fields, ['uid' => $uid]); - if (!DBM::is_result($user)) { + $fields = ['nickname']; + $user = DBA::selectFirst('user', $fields, ['uid' => $uid]); + if (!DBA::isResult($user)) { return false; } - $owner = dba::selectFirst('contact', ['url'], ['self' => true, 'uid' => $uid]); - if (!DBM::is_result($owner)) { + $owner = DBA::selectFirst('contact', ['url'], ['self' => true, 'uid' => $uid]); + if (!DBA::isResult($owner)) { return false; } @@ -726,54 +714,50 @@ function check_item_notification($itemid, $uid, $defaulttype = "") { $profiles = $profiles2; - $profile_list = ""; + $ret = DBA::select('contact', ['id'], ['uid' => 0, 'nurl' => $profiles]); - foreach ($profiles AS $profile) { - if ($profile_list != "") - $profile_list .= "', '"; + $contacts = []; - $profile_list .= dbesc($profile); + while ($contact = DBA::fetch($ret)) { + $contacts[] = $contact['id']; } - $profile_list = "'".$profile_list."'"; + DBA::close($ret); // Only act if it is a "real" post // We need the additional check for the "local_profile" because of mixed situations on connector networks - $item = q("SELECT `id`, `mention`, `tag`,`parent`, `title`, `body`, `author-name`, `author-link`, `author-avatar`, `guid`, - `parent-uri`, `uri`, `contact-id` - FROM `item` WHERE `id` = %d AND `verb` IN ('%s', '') AND `type` != 'activity' AND - NOT (`author-link` IN ($profile_list)) LIMIT 1", - intval($itemid), dbesc(ACTIVITY_POST)); - if (!$item) - return false; + $fields = ['id', 'mention', 'tag', 'parent', 'title', 'body', + 'author-link', 'author-name', 'author-avatar', 'author-id', + 'guid', 'parent-uri', 'uri', 'contact-id', 'network']; + $condition = ['id' => $itemid, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT]]; + $item = Item::selectFirst($fields, $condition); + if (!DBA::isResult($item) || in_array($item['author-id'], $contacts)) { + return; + } // Generate the notification array $params = []; $params["uid"] = $uid; - $params["notify_flags"] = $user["notify-flags"]; - $params["language"] = $user["language"]; - $params["to_name"] = $user["username"]; - $params["to_email"] = $user["email"]; - $params["item"] = $item[0]; - $params["parent"] = $item[0]["parent"]; - $params["link"] = System::baseUrl().'/display/'.urlencode($item[0]["guid"]); + $params["item"] = $item; + $params["parent"] = $item["parent"]; + $params["link"] = System::baseUrl().'/display/'.urlencode($item["guid"]); $params["otype"] = 'item'; - $params["source_name"] = $item[0]["author-name"]; - $params["source_link"] = $item[0]["author-link"]; - $params["source_photo"] = $item[0]["author-avatar"]; + $params["source_name"] = $item["author-name"]; + $params["source_link"] = $item["author-link"]; + $params["source_photo"] = $item["author-avatar"]; - if ($item[0]["parent-uri"] === $item[0]["uri"]) { + if ($item["parent-uri"] === $item["uri"]) { // Send a notification for every new post? - $send_notification = dba::exists('contact', ['id' => $item[0]['contact-id'], 'notify_new_posts' => true]); + $send_notification = DBA::exists('contact', ['id' => $item['contact-id'], 'notify_new_posts' => true]); if (!$send_notification) { $tags = q("SELECT `url` FROM `term` WHERE `otype` = %d AND `oid` = %d AND `type` = %d AND `uid` = %d", intval(TERM_OBJ_POST), intval($itemid), intval(TERM_MENTION), intval($uid)); - if (DBM::is_result($tags)) { + if (DBA::isResult($tags)) { foreach ($tags AS $tag) { $condition = ['nurl' => normalise_link($tag["url"]), 'uid' => $uid, 'notify_new_posts' => true]; - $r = dba::exists('contact', $condition); + $r = DBA::exists('contact', $condition); if ($r) { $send_notification = true; } @@ -791,29 +775,35 @@ function check_item_notification($itemid, $uid, $defaulttype = "") { $tagged = false; foreach ($profiles AS $profile) { - if (strpos($item[0]["tag"], "=".$profile."]") || strpos($item[0]["body"], "=".$profile."]")) + if (strpos($item["tag"], "=".$profile."]") || strpos($item["body"], "=".$profile."]")) $tagged = true; } - if ($item[0]["mention"] || $tagged || ($defaulttype == NOTIFY_TAGSELF)) { + if ($item["mention"] || $tagged || ($defaulttype == NOTIFY_TAGSELF)) { $params["type"] = NOTIFY_TAGSELF; $params["verb"] = ACTIVITY_TAG; } - // Is it a post that the user had started or where he interacted? - $parent = q("SELECT `thread`.`iid` FROM `thread` INNER JOIN `item` ON `item`.`parent` = `thread`.`iid` - WHERE `thread`.`iid` = %d AND NOT `thread`.`ignored` AND - (`thread`.`mention` OR `item`.`author-link` IN ($profile_list)) - LIMIT 1", - intval($item[0]["parent"])); + // Is it a post that the user had started? + $fields = ['ignored', 'mention']; + $thread = Item::selectFirstThreadForUser($params['uid'], $fields, ['iid' => $item["parent"]]); - if ($parent && !isset($params["type"])) { + if ($thread['mention'] && !$thread['ignored'] && !isset($params["type"])) { $params["type"] = NOTIFY_COMMENT; $params["verb"] = ACTIVITY_POST; } - if (isset($params["type"])) + // And now we check for participation of one of our contacts in the thread + $condition = ['parent' => $item["parent"], 'author-id' => $contacts]; + + if (!$thread['ignored'] && !isset($params["type"]) && Item::exists($condition)) { + $params["type"] = NOTIFY_COMMENT; + $params["verb"] = ACTIVITY_POST; + } + + if (isset($params["type"])) { notification($params); + } } /** diff --git a/include/items.php b/include/items.php index 4533b6077..9fd557a77 100644 --- a/include/items.php +++ b/include/items.php @@ -3,13 +3,15 @@ * @file include/items.php */ +use Friendica\BaseObject; use Friendica\Content\Feature; use Friendica\Core\Addon; use Friendica\Core\Config; use Friendica\Core\L10n; use Friendica\Core\PConfig; +use Friendica\Core\Protocol; use Friendica\Core\System; -use Friendica\Database\DBM; +use Friendica\Database\DBA; use Friendica\Model\Item; use Friendica\Protocol\DFRN; use Friendica\Protocol\Feed; @@ -23,9 +25,14 @@ require_once 'include/text.php'; require_once 'mod/share.php'; require_once 'include/enotify.php'; -function add_page_info_data($data, $no_photos = false) { +function add_page_info_data(array $data, $no_photos = false) +{ Addon::callHooks('page_info_data', $data); + if (empty($data['type'])) { + return ''; + } + // It maybe is a rich content, but if it does have everything that a link has, // then treat it that way if (($data["type"] == "rich") && is_string($data["title"]) && @@ -33,6 +40,8 @@ function add_page_info_data($data, $no_photos = false) { $data["type"] = "link"; } + $data["title"] = defaults($data, "title", ""); + if ((($data["type"] != "link") && ($data["type"] != "video") && ($data["type"] != "photo")) || ($data["title"] == $data["url"])) { return ""; } @@ -47,23 +56,24 @@ function add_page_info_data($data, $no_photos = false) { $text = "[attachment type='".$data["type"]."'"; - if ($data["text"] == "") { + if (empty($data["text"])) { $data["text"] = $data["title"]; } - if ($data["text"] == "") { + if (empty($data["text"])) { $data["text"] = $data["url"]; } - if ($data["url"] != "") { + if (!empty($data["url"])) { $text .= " url='".$data["url"]."'"; } - if ($data["title"] != "") { + if (!empty($data["title"])) { $text .= " title='".$data["title"]."'"; } - if (!empty($data["images"])) { + // Only embedd a picture link when it seems to be a valid picture ("width" is set) + if (!empty($data["images"]) && !empty($data["images"][0]["width"])) { $preview = str_replace(["[", "]"], ["[", "]"], htmlentities($data["images"][0]["src"], ENT_QUOTES, 'UTF-8', false)); // if the preview picture is larger than 500 pixels then show it in a larger mode // But only, if the picture isn't higher than large (To prevent huge posts) @@ -80,7 +90,7 @@ function add_page_info_data($data, $no_photos = false) { $hashtags = ""; if (isset($data["keywords"]) && count($data["keywords"])) { $hashtags = "\n"; - foreach ($data["keywords"] AS $keyword) { + foreach ($data["keywords"] as $keyword) { /// @TODO make a positive list of allowed characters $hashtag = str_replace([" ", "+", "/", ".", "#", "'", "’", "`", "(", ")", "„", "“"], ["", "", "", "", "", "", "", "", "", "", "", ""], $keyword); @@ -91,8 +101,8 @@ function add_page_info_data($data, $no_photos = false) { return "\n".$text.$hashtags; } -function query_page_info($url, $photo = "", $keywords = false, $keyword_blacklist = "") { - +function query_page_info($url, $photo = "", $keywords = false, $keyword_blacklist = "") +{ $data = ParseUrl::getSiteinfoCached($url, true); if ($photo != "") { @@ -107,8 +117,10 @@ function query_page_info($url, $photo = "", $keywords = false, $keyword_blacklis if (($keyword_blacklist != "") && isset($data["keywords"])) { $list = explode(", ", $keyword_blacklist); - foreach ($list AS $keyword) { + + foreach ($list as $keyword) { $keyword = trim($keyword); + $index = array_search($keyword, $data["keywords"]); if ($index !== false) { unset($data["keywords"][$index]); @@ -119,12 +131,13 @@ function query_page_info($url, $photo = "", $keywords = false, $keyword_blacklis return $data; } -function add_page_keywords($url, $photo = "", $keywords = false, $keyword_blacklist = "") { +function add_page_keywords($url, $photo = "", $keywords = false, $keyword_blacklist = "") +{ $data = query_page_info($url, $photo, $keywords, $keyword_blacklist); $tags = ""; if (isset($data["keywords"]) && count($data["keywords"])) { - foreach ($data["keywords"] AS $keyword) { + foreach ($data["keywords"] as $keyword) { $hashtag = str_replace([" ", "+", "/", ".", "#", "'"], ["", "", "", "", "", ""], $keyword); @@ -139,16 +152,21 @@ function add_page_keywords($url, $photo = "", $keywords = false, $keyword_blackl return $tags; } -function add_page_info($url, $no_photos = false, $photo = "", $keywords = false, $keyword_blacklist = "") { +function add_page_info($url, $no_photos = false, $photo = "", $keywords = false, $keyword_blacklist = "") +{ $data = query_page_info($url, $photo, $keywords, $keyword_blacklist); - $text = add_page_info_data($data, $no_photos); + $text = ''; + + if (is_array($data)) { + $text = add_page_info_data($data, $no_photos); + } return $text; } -function add_page_info_to_body($body, $texturl = false, $no_photos = false) { - +function add_page_info_to_body($body, $texturl = false, $no_photos = false) +{ logger('add_page_info_to_body: fetch page info for body ' . $body, LOGGER_DEBUG); $URLSearchString = "^\[\]"; @@ -225,11 +243,10 @@ function add_page_info_to_body($body, $texturl = false, $no_photos = false) { * model where comments can have sub-threads. That would require some massive sorting * to get all the feed items into a mostly linear ordering, and might still require * recursion. - * - * @TODO find proper type-hints */ -function consume_feed($xml, $importer, $contact, &$hub, $datedir = 0, $pass = 0) { - if ($contact['network'] === NETWORK_OSTATUS) { +function consume_feed($xml, array $importer, array $contact, &$hub, $datedir = 0, $pass = 0) +{ + if ($contact['network'] === Protocol::OSTATUS) { if ($pass < 2) { // Test - remove before flight //$tempfile = tempnam(get_temppath(), "ostatus2"); @@ -237,60 +254,56 @@ function consume_feed($xml, $importer, $contact, &$hub, $datedir = 0, $pass = 0) logger("Consume OStatus messages ", LOGGER_DEBUG); OStatus::import($xml, $importer, $contact, $hub); } + return; } - if ($contact['network'] === NETWORK_FEED) { + if ($contact['network'] === Protocol::FEED) { if ($pass < 2) { logger("Consume feeds", LOGGER_DEBUG); Feed::import($xml, $importer, $contact, $hub); } + return; } - if ($contact['network'] === NETWORK_DFRN) { + if ($contact['network'] === Protocol::DFRN) { logger("Consume DFRN messages", LOGGER_DEBUG); - - $r = q("SELECT `contact`.*, `contact`.`uid` AS `importer_uid`, - `contact`.`pubkey` AS `cpubkey`, - `contact`.`prvkey` AS `cprvkey`, - `contact`.`thumb` AS `thumb`, - `contact`.`url` as `url`, - `contact`.`name` as `senderName`, - `user`.* - FROM `contact` - LEFT JOIN `user` ON `contact`.`uid` = `user`.`uid` - WHERE `contact`.`id` = %d AND `user`.`uid` = %d", - dbesc($contact["id"]), dbesc($importer["uid"]) - ); - if (DBM::is_result($r)) { + $dfrn_importer = DFRN::getImporter($contact["id"], $importer["uid"]); + if (!empty($dfrn_importer)) { logger("Now import the DFRN feed"); - DFRN::import($xml, $r[0], true); + DFRN::import($xml, $dfrn_importer, true); return; } } } -function subscribe_to_hub($url, $importer, $contact, $hubmode = 'subscribe') { - - $a = get_app(); - - if (is_array($importer)) { - $r = q("SELECT `nickname` FROM `user` WHERE `uid` = %d LIMIT 1", - intval($importer['uid']) - ); - } - +function subscribe_to_hub($url, array $importer, array $contact, $hubmode = 'subscribe') +{ /* * Diaspora has different message-ids in feeds than they do * through the direct Diaspora protocol. If we try and use * the feed, we'll get duplicates. So don't. */ - if ((!DBM::is_result($r)) || $contact['network'] === NETWORK_DIASPORA) { + if ($contact['network'] === Protocol::DIASPORA) { return; } - $push_url = System::baseUrl() . '/pubsub/' . $r[0]['nickname'] . '/' . $contact['id']; + // Without an importer we don't have a user id - so we quit + if (empty($importer)) { + return; + } + + $a = BaseObject::getApp(); + + $user = DBA::selectFirst('user', ['nickname'], ['uid' => $importer['uid']]); + + // No user, no nickname, we quit + if (!DBA::isResult($user)) { + return; + } + + $push_url = System::baseUrl() . '/pubsub/' . $user['nickname'] . '/' . $contact['id']; // Use a single verify token, even if multiple hubs $verify_token = ((strlen($contact['hub-verify'])) ? $contact['hub-verify'] : random_string()); @@ -300,7 +313,7 @@ function subscribe_to_hub($url, $importer, $contact, $hubmode = 'subscribe') { logger('subscribe_to_hub: ' . $hubmode . ' ' . $contact['name'] . ' to hub ' . $url . ' endpoint: ' . $push_url . ' with verifier ' . $verify_token); if (!strlen($contact['hub-verify']) || ($contact['hub-verify'] != $verify_token)) { - dba::update('contact', ['hub-verify' => $verify_token], ['id' => $contact['id']]); + DBA::update('contact', ['hub-verify' => $verify_token], ['id' => $contact['id']]); } Network::post($url, $params); @@ -311,40 +324,39 @@ function subscribe_to_hub($url, $importer, $contact, $hubmode = 'subscribe') { } -/// @TODO type-hint is array -function drop_items($items) { +function drop_items(array $items) +{ $uid = 0; if (!local_user() && !remote_user()) { return; } - if (count($items)) { + if (!empty($items)) { foreach ($items as $item) { $owner = Item::deleteForUser(['id' => $item], local_user()); - if ($owner && !$uid) + + if ($owner && !$uid) { $uid = $owner; + } } } } -function drop_item($id) { - - $a = get_app(); +function drop_item($id) +{ + $a = BaseObject::getApp(); // locate item to be deleted - $r = q("SELECT * FROM `item` WHERE `id` = %d LIMIT 1", - intval($id) - ); + $fields = ['id', 'uid', 'contact-id', 'deleted']; + $item = Item::selectFirstForUser(local_user(), $fields, ['id' => $id]); - if (!DBM::is_result($r)) { + if (!DBA::isResult($item)) { notice(L10n::t('Item not found.') . EOL); goaway(System::baseUrl() . '/' . $_SESSION['return_url']); } - $item = $r[0]; - if ($item['deleted']) { return 0; } @@ -353,7 +365,7 @@ function drop_item($id) { // check if logged in user is either the author or owner of this item - if (is_array($_SESSION['remote'])) { + if (!empty($_SESSION['remote'])) { foreach ($_SESSION['remote'] as $visitor) { if ($visitor['uid'] == $item['uid'] && $visitor['cid'] == $item['contact-id']) { $contact_id = $visitor['cid']; @@ -363,13 +375,13 @@ function drop_item($id) { } if ((local_user() == $item['uid']) || $contact_id) { - // Check if we should do HTML-based delete confirmation - if ($_REQUEST['confirm']) { + if (!empty($_REQUEST['confirm'])) { //
can't take arguments in its "action" parameter // so add any arguments as hidden inputs $query = explode_querystring($a->query_string); $inputs = []; + foreach ($query['args'] as $arg) { if (strpos($arg, 'confirm=') === false) { $arg_parts = explode('=', $arg); @@ -388,7 +400,7 @@ function drop_item($id) { ]); } // Now check how the user responded to the confirmation query - if ($_REQUEST['canceled']) { + if (!empty($_REQUEST['canceled'])) { goaway(System::baseUrl() . '/' . $_SESSION['return_url']); } @@ -405,7 +417,8 @@ function drop_item($id) { } /* arrange the list in years */ -function list_post_dates($uid, $wall) { +function list_post_dates($uid, $wall) +{ $dnow = DateTimeFormat::localNow('Y-m-d'); $dthen = Item::firstPostDate($uid, $wall); @@ -430,16 +443,19 @@ function list_post_dates($uid, $wall) { $start_month = DateTimeFormat::utc($dstart, 'Y-m-d'); $end_month = DateTimeFormat::utc($dend, 'Y-m-d'); $str = day_translate(DateTimeFormat::utc($dnow, 'F')); - if (!$ret[$dyear]) { + + if (empty($ret[$dyear])) { $ret[$dyear] = []; } + $ret[$dyear][] = [$str, $end_month, $start_month]; $dnow = DateTimeFormat::utc($dnow . ' -1 month', 'Y-m-d'); } return $ret; } -function posted_date_widget($url, $uid, $wall) { +function posted_date_widget($url, $uid, $wall) +{ $o = ''; if (!Feature::isEnabled($uid, 'archives')) { @@ -453,14 +469,11 @@ function posted_date_widget($url, $uid, $wall) { return $o; */ - $visible_years = PConfig::get($uid,'system','archive_visible_years'); - if (!$visible_years) { - $visible_years = 5; - } + $visible_years = PConfig::get($uid, 'system', 'archive_visible_years', 5); $ret = list_post_dates($uid, $wall); - if (!DBM::is_result($ret)) { + if (!DBA::isResult($ret)) { return $o; } diff --git a/include/security.php b/include/security.php index b13a507cf..2063bdd16 100644 --- a/include/security.php +++ b/include/security.php @@ -8,9 +8,11 @@ use Friendica\Core\Config; use Friendica\Core\L10n; use Friendica\Core\PConfig; use Friendica\Core\System; -use Friendica\Database\DBM; +use Friendica\Database\DBA; +use Friendica\Model\Contact; use Friendica\Model\Group; use Friendica\Util\DateTimeFormat; +use Friendica\Model\PermissionSet; /** * @brief Calculate the hash that is needed for the "Friendica" cookie @@ -41,7 +43,7 @@ function new_cookie($time, $user = []) if ($user) { $value = json_encode(["uid" => $user["uid"], "hash" => cookie_hash($user), - "ip" => $_SERVER['REMOTE_ADDR']]); + "ip" => defaults($_SERVER, 'REMOTE_ADDR', '0.0.0.0')]); } else { $value = ""; } @@ -70,7 +72,7 @@ function authenticate_success($user_record, $login_initial = false, $interactive $_SESSION['page_flags'] = $user_record['page-flags']; $_SESSION['my_url'] = System::baseUrl() . '/profile/' . $user_record['nickname']; $_SESSION['my_address'] = $user_record['nickname'] . '@' . substr(System::baseUrl(), strpos(System::baseUrl(), '://') + 3); - $_SESSION['addr'] = $_SERVER['REMOTE_ADDR']; + $_SESSION['addr'] = defaults($_SERVER, 'REMOTE_ADDR', '0.0.0.0'); $a->user = $user_record; @@ -99,11 +101,9 @@ function authenticate_success($user_record, $login_initial = false, $interactive $master_record = $a->user; if ((x($_SESSION, 'submanage')) && intval($_SESSION['submanage'])) { - $r = dba::fetch_first("SELECT * FROM `user` WHERE `uid` = ? LIMIT 1", - intval($_SESSION['submanage']) - ); - if (DBM::is_result($r)) { - $master_record = $r; + $user = DBA::selectFirst('user', [], ['uid' => $_SESSION['submanage']]); + if (DBA::isResult($user)) { + $master_record = $user; } } @@ -114,38 +114,38 @@ function authenticate_success($user_record, $login_initial = false, $interactive 'nickname' => $master_record['nickname']]]; // Then add all the children - $r = dba::select('user', ['uid', 'username', 'nickname'], + $r = DBA::select('user', ['uid', 'username', 'nickname'], ['parent-uid' => $master_record['uid'], 'account_removed' => false]); - if (DBM::is_result($r)) { - $a->identities = array_merge($a->identities, dba::inArray($r)); + if (DBA::isResult($r)) { + $a->identities = array_merge($a->identities, DBA::toArray($r)); } } else { // Just ensure that the array is always defined $a->identities = []; // First entry is our parent - $r = dba::select('user', ['uid', 'username', 'nickname'], + $r = DBA::select('user', ['uid', 'username', 'nickname'], ['uid' => $master_record['parent-uid'], 'account_removed' => false]); - if (DBM::is_result($r)) { - $a->identities = dba::inArray($r); + if (DBA::isResult($r)) { + $a->identities = DBA::toArray($r); } // Then add all siblings - $r = dba::select('user', ['uid', 'username', 'nickname'], + $r = DBA::select('user', ['uid', 'username', 'nickname'], ['parent-uid' => $master_record['parent-uid'], 'account_removed' => false]); - if (DBM::is_result($r)) { - $a->identities = array_merge($a->identities, dba::inArray($r)); + if (DBA::isResult($r)) { + $a->identities = array_merge($a->identities, DBA::toArray($r)); } } - $r = dba::p("SELECT `user`.`uid`, `user`.`username`, `user`.`nickname` + $r = DBA::p("SELECT `user`.`uid`, `user`.`username`, `user`.`nickname` FROM `manage` INNER JOIN `user` ON `manage`.`mid` = `user`.`uid` WHERE `user`.`account_removed` = 0 AND `manage`.`uid` = ?", $master_record['uid'] ); - if (DBM::is_result($r)) { - $a->identities = array_merge($a->identities, dba::inArray($r)); + if (DBA::isResult($r)) { + $a->identities = array_merge($a->identities, DBA::toArray($r)); } if ($login_initial) { @@ -155,30 +155,32 @@ function authenticate_success($user_record, $login_initial = false, $interactive logger('auth_identities refresh: ' . print_r($a->identities, true), LOGGER_DEBUG); } - $r = dba::fetch_first("SELECT * FROM `contact` WHERE `uid` = ? AND `self` LIMIT 1", $_SESSION['uid']); - if (DBM::is_result($r)) { - $a->contact = $r; - $a->cid = $r['id']; + $contact = DBA::selectFirst('contact', [], ['uid' => $_SESSION['uid'], 'self' => true]); + if (DBA::isResult($contact)) { + $a->contact = $contact; + $a->cid = $contact['id']; $_SESSION['cid'] = $a->cid; } header('X-Account-Management-Status: active; name="' . $a->user['username'] . '"; id="' . $a->user['nickname'] . '"'); if ($login_initial || $login_refresh) { - dba::update('user', ['login_date' => DateTimeFormat::utcNow()], ['uid' => $_SESSION['uid']]); + DBA::update('user', ['login_date' => DateTimeFormat::utcNow()], ['uid' => $_SESSION['uid']]); // Set the login date for all identities of the user - dba::update('user', ['login_date' => DateTimeFormat::utcNow()], + DBA::update('user', ['login_date' => DateTimeFormat::utcNow()], ['parent-uid' => $master_record['uid'], 'account_removed' => false]); } if ($login_initial) { - // If the user specified to remember the authentication, then set a cookie - // that expires after one week (the default is when the browser is closed). - // The cookie will be renewed automatically. - // The week ensures that sessions will expire after some inactivity. - if ($_SESSION['remember']) { - logger('Injecting cookie for remembered user ' . $_SESSION['remember_user']['nickname']); + /* + * If the user specified to remember the authentication, then set a cookie + * that expires after one week (the default is when the browser is closed). + * The cookie will be renewed automatically. + * The week ensures that sessions will expire after some inactivity. + */ + if (!empty($_SESSION['remember'])) { + logger('Injecting cookie for remembered user ' . $a->user['nickname']); new_cookie(604800, $user_record); unset($_SESSION['remember']); } @@ -206,6 +208,10 @@ function can_write_wall($owner) return true; } + if (local_user() && ($owner == 0)) { + return true; + } + if (remote_user()) { // use remembered decision and avoid a DB lookup for each and every display item // DO NOT use this function if there are going to be multiple owners @@ -219,7 +225,7 @@ function can_write_wall($owner) } else { $cid = 0; - if (is_array($_SESSION['remote'])) { + if (!empty($_SESSION['remote'])) { foreach ($_SESSION['remote'] as $visitor) { if ($visitor['uid'] == $owner) { $cid = $visitor['cid']; @@ -237,12 +243,12 @@ function can_write_wall($owner) AND `user`.`blockwall` = 0 AND `readonly` = 0 AND ( `contact`.`rel` IN ( %d , %d ) OR `user`.`page-flags` = %d ) LIMIT 1", intval($owner), intval($cid), - intval(CONTACT_IS_SHARING), - intval(CONTACT_IS_FRIEND), - intval(PAGE_COMMUNITY) + intval(Contact::SHARING), + intval(Contact::FRIEND), + intval(Contact::PAGE_COMMUNITY) ); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $verified = 2; return true; } else { @@ -254,6 +260,7 @@ function can_write_wall($owner) return false; } +/// @TODO $groups should be array function permissions_sql($owner_id, $remote_verified = false, $groups = null) { $local_user = local_user(); @@ -275,6 +282,13 @@ function permissions_sql($owner_id, $remote_verified = false, $groups = null) */ if ($local_user && $local_user == $owner_id) { $sql = ''; + /** + * Authenticated visitor. Unless pre-verified, + * check that the contact belongs to this $owner_id + * and load the groups the visitor belongs to. + * If pre-verified, the caller is expected to have already + * done this and passed the groups into this function. + */ } elseif ($remote_user) { /* * Authenticated visitor. Unless pre-verified, @@ -285,11 +299,7 @@ function permissions_sql($owner_id, $remote_verified = false, $groups = null) */ if (!$remote_verified) { - $r = q("SELECT id FROM contact WHERE id = %d AND uid = %d AND blocked = 0 LIMIT 1", - intval($remote_user), - intval($owner_id) - ); - if (DBM::is_result($r)) { + if (DBA::exists('contact', ['id' => $remote_user, 'uid' => $owner_id, 'blocked' => false])) { $remote_verified = true; $groups = Group::getIdsByContactId($remote_user); } @@ -298,9 +308,10 @@ function permissions_sql($owner_id, $remote_verified = false, $groups = null) if ($remote_verified) { $gs = '<<>>'; // should be impossible to match - if (is_array($groups) && count($groups)) { - foreach ($groups as $g) + if (is_array($groups)) { + foreach ($groups as $g) { $gs .= '|<' . intval($g) . '>'; + } } $sql = sprintf( @@ -309,9 +320,9 @@ function permissions_sql($owner_id, $remote_verified = false, $groups = null) ) ", intval($remote_user), - dbesc($gs), + DBA::escape($gs), intval($remote_user), - dbesc($gs) + DBA::escape($gs) ); } } @@ -328,12 +339,7 @@ function item_permissions_sql($owner_id, $remote_verified = false, $groups = nul * * default permissions - anonymous user */ - $sql = " AND `item`.allow_cid = '' - AND `item`.allow_gid = '' - AND `item`.deny_cid = '' - AND `item`.deny_gid = '' - AND `item`.private = 0 - "; + $sql = " AND NOT `item`.`private`"; // Profile owner - everything is visible if ($local_user && ($local_user == $owner_id)) { @@ -346,37 +352,15 @@ function item_permissions_sql($owner_id, $remote_verified = false, $groups = nul * If pre-verified, the caller is expected to have already * done this and passed the groups into this function. */ - if (!$remote_verified) { - $r = q("SELECT id FROM contact WHERE id = %d AND uid = %d AND blocked = 0 LIMIT 1", - intval($remote_user), - intval($owner_id) - ); - if (DBM::is_result($r)) { - $remote_verified = true; - $groups = Group::getIdsByContactId($remote_user); - } + $set = PermissionSet::get($owner_id, $remote_user, $groups); + + if (!empty($set)) { + $sql_set = " OR (`item`.`private` IN (1,2) AND `item`.`wall` AND `item`.`psid` IN (" . implode(',', $set) . "))"; + } else { + $sql_set = ''; } - if ($remote_verified) { - $gs = '<<>>'; // should be impossible to match - - if (is_array($groups) && count($groups)) { - foreach ($groups as $g) { - $gs .= '|<' . intval($g) . '>'; - } - } - - $sql = sprintf( - " AND ( `item`.private = 0 OR ( `item`.private in (1,2) AND `item`.`wall` = 1 - AND ( NOT (`item`.deny_cid REGEXP '<%d>' OR `item`.deny_gid REGEXP '%s') - AND ( `item`.allow_cid REGEXP '<%d>' OR `item`.allow_gid REGEXP '%s' OR ( `item`.allow_cid = '' AND `item`.allow_gid = ''))))) - ", - intval($remote_user), - dbesc($gs), - intval($remote_user), - dbesc($gs) - ); - } + $sql = " AND (NOT `item`.`private`" . $sql_set . ")"; } return $sql; diff --git a/include/text.php b/include/text.php index 6830f91e0..97baee7f6 100644 --- a/include/text.php +++ b/include/text.php @@ -12,17 +12,17 @@ use Friendica\Core\Addon; use Friendica\Core\Config; use Friendica\Core\L10n; use Friendica\Core\PConfig; +use Friendica\Core\Protocol; use Friendica\Core\System; -use Friendica\Database\DBM; +use Friendica\Database\DBA; use Friendica\Model\Contact; use Friendica\Model\Event; use Friendica\Model\Item; -use Friendica\Model\Profile; use Friendica\Render\FriendicaSmarty; use Friendica\Util\DateTimeFormat; use Friendica\Util\Map; +use Friendica\Util\Proxy as ProxyUtils; -require_once "mod/proxy.php"; require_once "include/conversation.php"; /** @@ -152,7 +152,7 @@ function autoname($len) { 'nd','ng','nk','nt','rn','rp','rt']; $noend = ['bl', 'br', 'cl','cr','dr','fl','fr','gl','gr', - 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh']; + 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh','q']; $start = mt_rand(0,2); if ($start == 0) { @@ -178,14 +178,13 @@ function autoname($len) { $word = substr($word,0,$len); foreach ($noend as $noe) { - if ((strlen($word) > 2) && (substr($word, -2) == $noe)) { - $word = substr($word, 0, -1); + $noelen = strlen($noe); + if ((strlen($word) > $noelen) && (substr($word, -$noelen) == $noe)) { + $word = autoname($len); break; } } - if (substr($word, -1) == 'q') { - $word = substr($word, 0, -1); - } + return $word; } @@ -453,7 +452,7 @@ function perms2str($p) { if (is_array($p)) { $tmp = $p; } else { - $tmp = explode(',',$p); + $tmp = explode(',', $p); } if (is_array($tmp)) { @@ -463,113 +462,6 @@ function perms2str($p) { return $ret; } - -/** - * generate a guaranteed unique (for this domain) item ID for ATOM - * safe from birthday paradox - * - * @param string $hostname - * @param int $uid - * @return string - */ -function item_new_uri($hostname, $uid, $guid = "") { - - do { - if ($guid == "") { - $hash = get_guid(32); - } else { - $hash = $guid; - $guid = ""; - } - - $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash; - - $dups = dba::exists('item', ['uri' => $uri]); - } while ($dups == true); - - return $uri; -} - -/** - * @deprecated - * wrapper to load a view template, checking for alternate - * languages before falling back to the default - * - * @global string $lang - * @global App $a - * @param string $s view name - * @return string - */ -function load_view_file($s) { - global $lang, $a; - if (! isset($lang)) { - $lang = 'en'; - } - $b = basename($s); - $d = dirname($s); - if (file_exists("$d/$lang/$b")) { - $stamp1 = microtime(true); - $content = file_get_contents("$d/$lang/$b"); - $a->save_timestamp($stamp1, "file"); - return $content; - } - - $theme = $a->getCurrentTheme(); - - if (file_exists("$d/theme/$theme/$b")) { - $stamp1 = microtime(true); - $content = file_get_contents("$d/theme/$theme/$b"); - $a->save_timestamp($stamp1, "file"); - return $content; - } - - $stamp1 = microtime(true); - $content = file_get_contents($s); - $a->save_timestamp($stamp1, "file"); - return $content; -} - - -/** - * load a view template, checking for alternate - * languages before falling back to the default - * - * @global string $lang - * @param string $s view path - * @return string - */ -function get_intltext_template($s) { - global $lang; - - $a = get_app(); - $engine = ''; - if ($a->theme['template_engine'] === 'smarty3') { - $engine = "/smarty3"; - } - - if (! isset($lang)) { - $lang = 'en'; - } - - if (file_exists("view/lang/$lang$engine/$s")) { - $stamp1 = microtime(true); - $content = file_get_contents("view/lang/$lang$engine/$s"); - $a->save_timestamp($stamp1, "file"); - return $content; - } elseif (file_exists("view/lang/en$engine/$s")) { - $stamp1 = microtime(true); - $content = file_get_contents("view/lang/en$engine/$s"); - $a->save_timestamp($stamp1, "file"); - return $content; - } else { - $stamp1 = microtime(true); - $content = file_get_contents("view$engine/$s"); - $a->save_timestamp($stamp1, "file"); - return $content; - } -} - - /** * load template $s * @@ -621,36 +513,28 @@ $LOGGER_LEVELS = []; * @brief Logs the given message at the given log level * * log levels: - * LOGGER_NORMAL (default) + * LOGGER_WARNING + * LOGGER_INFO (default) * LOGGER_TRACE * LOGGER_DEBUG * LOGGER_DATA * LOGGER_ALL * - * @global App $a * @global array $LOGGER_LEVELS * @param string $msg * @param int $level */ -function logger($msg, $level = 0) { +function logger($msg, $level = LOGGER_INFO) { $a = get_app(); global $LOGGER_LEVELS; - // turn off logger in install mode - if ( - $a->mode == App::MODE_INSTALL - || !dba::$connected - ) { - return; - } - - $debugging = Config::get('system','debugging'); - $logfile = Config::get('system','logfile'); - $loglevel = intval(Config::get('system','loglevel')); + $debugging = Config::get('system', 'debugging'); + $logfile = Config::get('system', 'logfile'); + $loglevel = intval(Config::get('system', 'loglevel')); if ( - ! $debugging - || ! $logfile + !$debugging + || !$logfile || $level > $loglevel ) { return; @@ -671,13 +555,20 @@ function logger($msg, $level = 0) { } $callers = debug_backtrace(); + + if (count($callers) > 1) { + $function = $callers[1]['function']; + } else { + $function = ''; + } + $logline = sprintf("%s@%s\t[%s]:%s:%s:%s\t%s\n", DateTimeFormat::utcNow(DateTimeFormat::ATOM), $process_id, $LOGGER_LEVELS[$level], basename($callers[0]['file']), $callers[0]['line'], - $callers[1]['function'], + $function, $msg ); @@ -693,31 +584,22 @@ function logger($msg, $level = 0) { * personally without background noise * * log levels: - * LOGGER_NORMAL (default) + * LOGGER_WARNING + * LOGGER_INFO (default) * LOGGER_TRACE * LOGGER_DEBUG * LOGGER_DATA * LOGGER_ALL * - * @global App $a * @global array $LOGGER_LEVELS * @param string $msg * @param int $level */ - -function dlogger($msg, $level = 0) { +function dlogger($msg, $level = LOGGER_INFO) { $a = get_app(); - // turn off logger in install mode - if ( - $a->mode == App::MODE_INSTALL - || !dba::$connected - ) { - return; - } - $logfile = Config::get('system', 'dlogfile'); - if (! $logfile) { + if (!$logfile) { return; } @@ -737,7 +619,7 @@ function dlogger($msg, $level = 0) { $process_id = session_id(); if ($process_id == '') { - $process_id = get_app()->process_id; + $process_id = $a->process_id; } $callers = debug_backtrace(); @@ -874,11 +756,11 @@ function contact_block() { AND NOT `pending` AND NOT `hidden` AND NOT `archive` AND `network` IN ('%s', '%s', '%s')", intval($a->profile['uid']), - dbesc(NETWORK_DFRN), - dbesc(NETWORK_OSTATUS), - dbesc(NETWORK_DIASPORA) + DBA::escape(Protocol::DFRN), + DBA::escape(Protocol::OSTATUS), + DBA::escape(Protocol::DIASPORA) ); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $total = intval($r[0]['total']); } if (!$total) { @@ -892,20 +774,20 @@ function contact_block() { AND `network` IN ('%s', '%s', '%s') ORDER BY RAND() LIMIT %d", intval($a->profile['uid']), - dbesc(NETWORK_DFRN), - dbesc(NETWORK_OSTATUS), - dbesc(NETWORK_DIASPORA), + DBA::escape(Protocol::DFRN), + DBA::escape(Protocol::OSTATUS), + DBA::escape(Protocol::DIASPORA), intval($shown) ); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $contacts = []; foreach ($r AS $contact) { $contacts[] = $contact["id"]; } $r = q("SELECT `id`, `uid`, `addr`, `url`, `name`, `thumb`, `network` FROM `contact` WHERE `id` IN (%s)", - dbesc(implode(",", $contacts))); + DBA::escape(implode(",", $contacts))); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $contacts = L10n::tt('%d Contact', '%d Contacts', $total); $micropro = []; foreach ($r as $rr) { @@ -962,13 +844,9 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) { $redir = false; if ($redirect) { - $redirect_url = 'redir/' . $contact['id']; - if (local_user() && ($contact['uid'] == local_user()) && ($contact['network'] === NETWORK_DFRN)) { - $redir = true; - $url = $redirect_url; + $url = Contact::magicLink($contact['url']); + if (strpos($url, 'redir/') === 0) { $sparkle = ' sparkle'; - } else { - $url = Profile::zrl($url); } } @@ -981,7 +859,7 @@ function micropro($contact, $redirect = false, $class = '', $textmode = false) { '$click' => defaults($contact, 'click', ''), '$class' => $class, '$url' => $url, - '$photo' => proxy_url($contact['thumb'], false, PROXY_SIZE_THUMB), + '$photo' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB), '$name' => $contact['name'], 'title' => $contact['name'] . ' [' . $contact['addr'] . ']', '$parkle' => $sparkle, @@ -1016,7 +894,7 @@ function search($s, $id = 'search-box', $url = 'search', $save = false, $aside = '$action_url' => $url, '$search_label' => L10n::t('Search'), '$save_label' => $save_label, - '$savedsearch' => Feature::isEnabled(local_user(),'savedsearch'), + '$savedsearch' => local_user() && Feature::isEnabled(local_user(),'savedsearch'), '$search_hint' => L10n::t('@name, !forum, #tags, content'), '$mode' => $mode ]; @@ -1162,7 +1040,7 @@ function redir_private_images($a, &$item) continue; } - if ((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) { + if ((local_user() == $item['uid']) && ($item['private'] == 1) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == Protocol::DFRN)) { $img_url = 'redir?f=1&quiet=1&url=' . urlencode($mtch[1]) . '&conurl=' . urlencode($item['author-link']); $item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']); } @@ -1188,7 +1066,7 @@ function put_item_in_cache(&$item, $update = false) $rendered_html = defaults($item, 'rendered-html', ''); if ($rendered_hash == '' - || $item["rendered-html"] == "" + || $rendered_html == "" || $rendered_hash != hash("md5", $item["body"]) || Config::get("system", "ignore_cache") ) { @@ -1198,6 +1076,12 @@ function put_item_in_cache(&$item, $update = false) $item["rendered-html"] = prepare_text($item["body"]); $item["rendered-hash"] = hash("md5", $item["body"]); + $hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']]; + Addon::callHooks('put_item_in_cache', $hook_data); + $item['rendered-html'] = $hook_data['rendered-html']; + $item['rendered-hash'] = $hook_data['rendered-hash']; + unset($hook_data); + // Force an update if the generated values differ from the existing ones if ($rendered_hash != $item["rendered-hash"]) { $update = true; @@ -1208,9 +1092,9 @@ function put_item_in_cache(&$item, $update = false) $update = true; } - if ($update && ($item["id"] > 0)) { - dba::update('item', ['rendered-html' => $item["rendered-html"], 'rendered-hash' => $item["rendered-hash"]], - ['id' => $item["id"]], false); + if ($update && !empty($item["id"])) { + Item::update(['rendered-html' => $item["rendered-html"], 'rendered-hash' => $item["rendered-hash"]], + ['id' => $item["id"]]); } } @@ -1285,9 +1169,7 @@ function prepare_body(array &$item, $attach = false, $is_preview = false) $s = $hook_data['html']; unset($hook_data); - $s = apply_content_filter($s, $filter_reasons); - - if (! $attach) { + if (!$attach) { // Replace the blockquotes with quotes that are used in mails. $mailquote = '
'; $s = str_replace(['
', '
', '
'], [$mailquote, $mailquote, $mailquote], $s); @@ -1301,11 +1183,7 @@ function prepare_body(array &$item, $attach = false, $is_preview = false) foreach ($matches as $mtch) { $mime = $mtch[3]; - if ((local_user() == $item['uid']) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) { - $the_url = 'redir/' . $item['contact-id'] . '?f=1&url=' . $mtch[1]; - } else { - $the_url = $mtch[1]; - } + $the_url = Contact::magicLinkById($item['author-id'], $mtch[1]); if (strpos($mime, 'video') !== false) { if (!$vhead) { @@ -1318,7 +1196,8 @@ function prepare_body(array &$item, $attach = false, $is_preview = false) ]); } - $id = end(explode('/', $the_url)); + $url_parts = explode('/', $the_url); + $id = end($url_parts); $as .= replace_macros(get_markup_template('video_top.tpl'), [ '$video' => [ 'id' => $id, @@ -1394,6 +1273,8 @@ function prepare_body(array &$item, $attach = false, $is_preview = false) $s = preg_replace('|(]+src="[^"]+/photo/[0-9a-f]+)-[0-9]|', "$1-" . $ps, $s); } + $s = apply_content_filter($s, $filter_reasons); + $hook_data = ['item' => $item, 'html' => $s]; Addon::callHooks('prepare_body_final', $hook_data); @@ -1578,26 +1459,6 @@ function return_bytes($size_str) { } } - -/** - * @return string - */ -function generate_user_guid() { - $found = true; - do { - $guid = get_guid(32); - $x = q("SELECT `uid` FROM `user` WHERE `guid` = '%s' LIMIT 1", - dbesc($guid) - ); - if (! DBM::is_result($x)) { - $found = false; - } - } while ($found == true); - - return $guid; -} - - /** * @param string $s * @param boolean $strip_padding @@ -1631,7 +1492,7 @@ function base64url_decode($s) { * // Uncomment if you find you need it. * * $l = strlen($s); - * if (! strpos($s,'=')) { + * if (!strpos($s,'=')) { * $m = $l % 4; * if ($m == 2) * $s .= '=='; @@ -1661,10 +1522,11 @@ function bb_translate_video($s) { $r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER); if ($r) { foreach ($matches as $mtch) { - if ((stristr($mtch[1],'youtube')) || (stristr($mtch[1],'youtu.be'))) - $s = str_replace($mtch[0],'[youtube]' . $mtch[1] . '[/youtube]',$s); - elseif (stristr($mtch[1],'vimeo')) - $s = str_replace($mtch[0],'[vimeo]' . $mtch[1] . '[/vimeo]',$s); + if ((stristr($mtch[1], 'youtube')) || (stristr($mtch[1], 'youtu.be'))) { + $s = str_replace($mtch[0], '[youtube]' . $mtch[1] . '[/youtube]', $s); + } elseif (stristr($mtch[1], 'vimeo')) { + $s = str_replace($mtch[0], '[vimeo]' . $mtch[1] . '[/vimeo]', $s); + } } } return $s; @@ -1745,11 +1607,11 @@ function reltoabs($text, $base) { * @return string */ function item_post_type($item) { - if (intval($item['event-id'])) { + if (!empty($item['event-id'])) { return L10n::t('event'); - } elseif (strlen($item['resource-id'])) { + } elseif (!empty($item['resource-id'])) { return L10n::t('photo'); - } elseif (strlen($item['verb']) && $item['verb'] !== ACTIVITY_POST) { + } elseif (!empty($item['verb']) && $item['verb'] !== ACTIVITY_POST) { return L10n::t('activity'); } elseif ($item['id'] != $item['parent']) { return L10n::t('comment'); @@ -1778,11 +1640,11 @@ function file_tag_file_query($table,$s,$type = 'file') { } else { $str = preg_quote('<' . str_replace('%', '%%', file_tag_encode($s)) . '>'); } - return " AND " . (($table) ? dbesc($table) . '.' : '') . "file regexp '" . dbesc($str) . "' "; + return " AND " . (($table) ? DBA::escape($table) . '.' : '') . "file regexp '" . DBA::escape($str) . "' "; } // ex. given music,video return