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

Also removed <<<<< as this interfers (a bit) with searching for merge conflicts
with a more simplier editor.

Signed-off-by: Roland Häder <roland@mxchange.org>

Conflicts:
	mod/ping.php
	view/lang/fr/messages.po
	view/lang/fr/strings.php
This commit is contained in:
Roland Häder 2016-12-13 09:59:43 +01:00
commit d489ba1510
1261 changed files with 66596 additions and 191430 deletions

6
.gitignore vendored
View file

@ -42,3 +42,9 @@ nbproject
#ignore local folder
/local/
#ignore config files from Visual Studio
/.vs/
/php_friendica.phpproj
/php_friendica.sln
/php_friendica.phpproj.user

View file

@ -1,5 +1,5 @@
Friendica Communications Server
Copyright (c) 2010-2013 the Friendica Project
Copyright (c) 2010-2016 the Friendica Project
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by

View file

@ -24,12 +24,12 @@ If you want to get your work into the source tree yourself, feel free to do so a
The process is simple and friendica ships with all the tools necessary.
The location of the translated files in the source tree is
/view/LNG-CODE/
/view/lang/LNG-CODE/
where LNG-CODE is the language code used, e.g. de for German or fr for French.
The translated strings come as a "message.po" file from transifex which needs to be translated into the PHP file friendica uses.
To do so, place the file in the directory mentioned above and use the "po2php" utility from the util directory of your friendica installation.
Assuming you want to convert the German localization which is placed in view/de/message.po you would do the following.
Assuming you want to convert the German localization which is placed in view/lang/de/message.po you would do the following.
1. Navigate at the command prompt to the base directory of your
friendica installation
@ -37,9 +37,9 @@ Assuming you want to convert the German localization which is placed in view/de/
2. Execute the po2php script, which will place the translation
in the strings.php file that is used by friendica.
$> php util/po2php.php view/de/messages.po
$> php util/po2php.php view/lang/de/messages.po
The output of the script will be placed at view/de/strings.php where
The output of the script will be placed at view/lang/de/strings.php where
friendica is expecting it, so you can test your translation immediately.
3. Visit your friendica page to check if it still works in the language you
@ -50,7 +50,7 @@ Assuming you want to convert the German localization which is placed in view/de/
not give any output if the file is ok but might give a hint for
searching the bug in the file.
$> php view/de/strings.php
$> php view/lang/de/strings.php
4. commit the two files with a meaningful commit message to your git
repository, push it to your fork of the friendica repository at github and

52
Vagrantfile vendored
View file

@ -1,31 +1,55 @@
server_ip = "192.168.22.10"
server_memory = "384" # MB
server_ip_trusty = "192.168.22.10"
server_ip_xenial = "192.168.22.11"
server_memory = "1024" # MB
server_timezone = "UTC"
public_folder = "/vagrant"
Vagrant.configure(2) do |config|
######################################################################
# Set server to Ubuntu 14.04
config.vm.box = "ubuntu/trusty64"
config.vm.define "trusty" do |trusty|
trusty.vm.box = "ubuntu/trusty64"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a hostname, don't forget to put it to the `hosts` file
# This will point to the server's default virtual host
# TO DO: Make this work with virtualhost along-side xip.io URL
config.vm.hostname = "friendica.dev"
# Create a hostname, don't forget to put it to the `hosts` file
# This will point to the server's default virtual host
# TO DO: Make this work with virtualhost along-side xip.io URL
trusty.vm.hostname = "friendica-trusty.dev"
# Create a static IP
config.vm.network :private_network, ip: server_ip
# Create a static IP
trusty.vm.network :private_network, ip: server_ip_trusty
end
######################################################################
# Set server to Ubuntu 16.04
config.vm.define "xenial" do |xenial|
xenial.vm.box = "boxcutter/ubuntu1604"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a hostname, don't forget to put it to the `hosts` file
# This will point to the server's default virtual host
# TO DO: Make this work with virtualhost along-side xip.io URL
xenial.vm.hostname = "friendica-xenial.dev"
# Create a static IP
xenial.vm.network :private_network, ip: server_ip_xenial
end
######################################################################
# Share a folder between host and guest
config.vm.synced_folder "./", "/vagrant/", owner: "www-data", group: "vagrant"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
config.vm.provider "virtualbox" do |vb|

327
boot.php
View file

@ -36,9 +36,9 @@ require_once('include/dbstructure.php');
define ( 'FRIENDICA_PLATFORM', 'Friendica');
define ( 'FRIENDICA_CODENAME', 'Asparagus');
define ( 'FRIENDICA_VERSION', '3.5' );
define ( 'FRIENDICA_VERSION', '3.5.1-dev' );
define ( 'DFRN_PROTOCOL_VERSION', '2.23' );
define ( 'DB_UPDATE_VERSION', 1202 );
define ( 'DB_UPDATE_VERSION', 1209 );
/**
* @brief Constant with a HTML line break.
@ -53,7 +53,7 @@ define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' );
/**
* @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;
@ -95,7 +95,7 @@ define ( 'DEFAULT_DB_ENGINE', 'MyISAM' );
/**
* @name SSL Policy
*
*
* SSL redirection policies
* @{
*/
@ -106,7 +106,7 @@ define ( 'SSL_POLICY_SELFSIGN', 2 );
/**
* @name Logger
*
*
* log levels
* @{
*/
@ -119,7 +119,7 @@ define ( 'LOGGER_ALL', 4 );
/**
* @name Cache
*
*
* Cache levels
* @{
*/
@ -127,11 +127,15 @@ define ( 'CACHE_MONTH', 0 );
define ( 'CACHE_WEEK', 1 );
define ( 'CACHE_DAY', 2 );
define ( 'CACHE_HOUR', 3 );
define ( 'CACHE_HALF_HOUR', 4 );
define ( 'CACHE_QUARTER_HOUR', 5 );
define ( 'CACHE_FIVE_MINUTES', 6 );
define ( 'CACHE_MINUTE', 7 );
/* @}*/
/**
* @name Register
*
*
* Registration policies
* @{
*/
@ -142,7 +146,7 @@ define ( 'REGISTER_OPEN', 2 );
/**
* @name Contact_is
*
*
* Relationship types
* @{
*/
@ -153,7 +157,7 @@ define ( 'CONTACT_IS_FRIEND', 3);
/**
* @name Update
*
*
* DB update return values
* @{
*/
@ -181,9 +185,31 @@ 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
* @{
*/
define ( 'ACCOUNT_TYPE_PERSON', 0 );
define ( 'ACCOUNT_TYPE_ORGANISATION',1 );
define ( 'ACCOUNT_TYPE_NEWS', 2 );
define ( 'ACCOUNT_TYPE_COMMUNITY', 3 );
/** @}*/
/**
* @name CP
*
*
* Type of the community page
* @{
*/
@ -194,7 +220,7 @@ define ( 'CP_GLOBAL_COMMUNITY', 1 );
/**
* @name Network
*
*
* Network and protocol family types
* @{
*/
@ -266,7 +292,7 @@ define ( 'ZCURL_TIMEOUT' , (-1));
/**
* @name Notify
*
*
* Email notification options
* @{
*/
@ -288,7 +314,7 @@ define ( 'NOTIFY_SYSTEM', 0x8000 );
/**
* @name Term
*
*
* Tag/term types
* @{
*/
@ -308,7 +334,7 @@ define ( 'TERM_OBJ_PHOTO', 2 );
/**
* @name Namespaces
*
*
* Various namespaces we may need to parse
* @{
*/
@ -331,7 +357,7 @@ define ( 'NAMESPACE_ATOM1', 'http://www.w3.org/2005/Atom' );
/**
* @name Activity
*
*
* Activity stream defines
* @{
*/
@ -377,7 +403,7 @@ define ( 'ACTIVITY_OBJ_QUESTION', 'http://activityschema.org/object/question' );
/**
* @name Gravity
*
*
* Item weight for query ordering
* @{
*/
@ -444,9 +470,9 @@ function startup() {
/**
*
* class: App
*
*
* @brief Our main application structure for the life of this page.
*
*
* Primarily deals with the URL that got us here
* and tries to make some sense of it, and
* stores our page contents and config storage
@ -504,6 +530,7 @@ class App {
public $videoheight = 350;
public $force_max_items = 0;
public $theme_thread_allow = true;
public $theme_richtext_editor = true;
public $theme_events_in_profile = true;
/**
@ -583,6 +610,7 @@ class App {
$this->performance["markstart"] = microtime(true);
$this->callstack["database"] = array();
$this->callstack["database_write"] = array();
$this->callstack["network"] = array();
$this->callstack["file"] = array();
$this->callstack["rendering"] = array();
@ -759,60 +787,100 @@ class App {
return($this->scheme);
}
/**
* @brief Retrieves the Friendica instance base URL
*
* This function assembles the base URL from multiple parts:
* - Protocol is determined either by the request or a combination of
* system.ssl_policy and the $ssl parameter.
* - Host name is determined either by system.hostname or inferred from request
* - Path is inferred from SCRIPT_NAME
*
* Caches the result (depending on $ssl value) for performance.
*
* Note: $ssl parameter value doesn't directly correlate with the resulting protocol
*
* @param bool $ssl Whether to append http or https under SSL_POLICY_SELFSIGN
* @return string Friendica server base URL
*/
function get_baseurl($ssl = false) {
// Is the function called statically?
if (!is_object($this))
return(self::$a->get_baseurl($ssl));
if (!is_object($this)) {
return self::$a->get_baseurl($ssl);
}
// Arbitrary values, the resulting url protocol can be different
$cache_index = $ssl ? 'https' : 'http';
// Cached value found, nothing to process
if (isset($this->baseurl[$cache_index])) {
return $this->baseurl[$cache_index];
}
$scheme = $this->scheme;
if((x($this->config,'system')) && (x($this->config['system'],'ssl_policy'))) {
if(intval($this->config['system']['ssl_policy']) === intval(SSL_POLICY_FULL))
if ((x($this->config, 'system')) && (x($this->config['system'], 'ssl_policy'))) {
if (intval($this->config['system']['ssl_policy']) === SSL_POLICY_FULL) {
$scheme = 'https';
}
// Basically, we have $ssl = true on any links which can only be seen by a logged in user
// (and also the login link). Anything seen by an outsider will have it turned off.
if($this->config['system']['ssl_policy'] == SSL_POLICY_SELFSIGN) {
if($ssl)
if ($this->config['system']['ssl_policy'] == SSL_POLICY_SELFSIGN) {
if ($ssl) {
$scheme = 'https';
else
} else {
$scheme = 'http';
}
}
}
if (get_config('config','hostname') != "")
$this->hostname = get_config('config','hostname');
if (get_config('config', 'hostname') != '') {
$this->hostname = get_config('config', 'hostname');
}
$this->baseurl = $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' );
return $this->baseurl;
$this->baseurl[$cache_index] = $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' );
return $this->baseurl[$cache_index];
}
/**
* @brief Initializes the baseurl components
*
* Clears the baseurl cache to prevent inconstistencies
*
* @param string $url
*/
function set_baseurl($url) {
$parsed = @parse_url($url);
$this->baseurl = $url;
$this->baseurl = [];
if($parsed) {
$this->scheme = $parsed['scheme'];
$hostname = $parsed['host'];
if(x($parsed,'port'))
if (x($parsed, 'port')) {
$hostname .= ':' . $parsed['port'];
if(x($parsed,'path'))
$this->path = trim($parsed['path'],'\\/');
}
if (x($parsed, 'path')) {
$this->path = trim($parsed['path'], '\\/');
}
if (file_exists(".htpreconfig.php"))
if (file_exists(".htpreconfig.php")) {
@include(".htpreconfig.php");
}
if (get_config('config','hostname') != "")
$this->hostname = get_config('config','hostname');
if (get_config('config', 'hostname') != '') {
$this->hostname = get_config('config', 'hostname');
}
if (!isset($this->hostname) OR ($this->hostname == ""))
if (!isset($this->hostname) OR ($this->hostname == '')) {
$this->hostname = $hostname;
}
}
}
function get_hostname() {
@ -975,27 +1043,35 @@ class App {
/**
* @brief Removes the baseurl from an url. This avoids some mixed content problems.
*
* @param string $url
* @param string $orig_url
*
* @return string The cleaned url
*/
function remove_baseurl($url){
function remove_baseurl($orig_url){
// Is the function called statically?
if (!is_object($this))
return(self::$a->remove_baseurl($url));
if (!is_object($this)) {
return(self::$a->remove_baseurl($orig_url));
}
$url = normalise_link($url);
// Remove the hostname from the url if it is an internal link
$nurl = normalise_link($orig_url);
$base = normalise_link($this->get_baseurl());
$url = str_replace($base."/", "", $url);
return $url;
$url = str_replace($base."/", "", $nurl);
// if it is an external link return the orignal value
if ($url == normalise_link($orig_url)) {
return $orig_url;
} else {
return $url;
}
}
/**
* @brief Register template engine class
*
*
* If $name is "", is used class static property $class::$name
*
*
* @param string $class
* @param string $name
*/
@ -1013,7 +1089,7 @@ class App {
/**
* @brief Return template engine instance.
*
*
* If $name is not defined, return engine defined by theme,
* or default
*
@ -1078,6 +1154,9 @@ class App {
}
function save_timestamp($stamp, $value) {
if (!isset($this->config['system']['profiler']) || !$this->config['system']['profiler'])
return;
$duration = (float)(microtime(true)-$stamp);
if (!isset($this->performance[$value])) {
@ -1109,23 +1188,33 @@ class App {
$this->remove_inactive_processes();
q("START TRANSACTION");
$r = q("SELECT `pid` FROM `process` WHERE `pid` = %d", intval(getmypid()));
if(!dbm::is_result($r))
if(!dbm::is_result($r)) {
q("INSERT INTO `process` (`pid`,`command`,`created`) VALUES (%d, '%s', '%s')",
intval(getmypid()),
dbesc($command),
dbesc(datetime_convert()));
}
q("COMMIT");
}
/**
* @brief Remove inactive processes
*/
function remove_inactive_processes() {
q("START TRANSACTION");
$r = q("SELECT `pid` FROM `process`");
if(dbm::is_result($r))
foreach ($r AS $process)
if (!posix_kill($process["pid"], 0))
if(dbm::is_result($r)) {
foreach ($r AS $process) {
if (!posix_kill($process["pid"], 0)) {
q("DELETE FROM `process` WHERE `pid` = %d", intval($process["pid"]));
}
}
}
q("COMMIT");
}
/**
@ -1154,11 +1243,6 @@ class App {
return implode(", ", $callstack);
}
function mark_timestamp($mark) {
//$this->performance["markstart"] -= microtime(true) - $this->performance["marktime"];
$this->performance["markstart"] = microtime(true) - $this->performance["markstart"] - $this->performance["marktime"];
}
function get_useragent() {
return(FRIENDICA_PLATFORM." '".FRIENDICA_CODENAME."' ".FRIENDICA_VERSION."-".DB_UPDATE_VERSION."; ".$this->get_baseurl());
}
@ -1302,8 +1386,12 @@ class App {
function proc_run($args) {
if (!function_exists("proc_open")) {
return;
}
// Add the php path if it is a php call
if (count($args) && ($args[0] === 'php' OR is_int($args[0]))) {
if (count($args) && ($args[0] === 'php' OR !is_string($args[0]))) {
// If the last worker fork was less than 10 seconds before then don't fork another one.
// This should prevent the forking of masses of workers.
@ -1336,7 +1424,7 @@ class App {
/**
* @brief Retrieve the App structure
*
*
* Useful in functions which require it but don't get it passed to them
*/
function get_app() {
@ -1590,7 +1678,7 @@ function run_update_function($x) {
* and mark it uninstalled in the database (for now we'll remove it).
* Then go through the config list and if we have a plugin that isn't installed,
* call the install procedure and add it to the database.
*
*
* @param App $a
*
*/
@ -1656,17 +1744,17 @@ function get_guid($size=16, $prefix = "") {
}
}
/**
/**
* @brief Wrapper for adding a login box.
*
*
* @param bool $register
* If $register == true provide a registration link.
* This will most always depend on the value of $a->config['register_policy'].
* @param bool $hiddens
*
*
* @return string
* Returns the complete html for inserting into the page
*
*
* @hooks 'login_hook'
* string $o
*/
@ -1756,7 +1844,7 @@ function goaway($s) {
/**
* @brief Returns the user id of locally logged in user or false.
*
*
* @return int|bool user id or false
*/
function local_user() {
@ -1767,7 +1855,7 @@ function local_user() {
/**
* @brief Returns contact id of authenticated site visitor or false
*
*
* @return int|bool visitor_id or false
*/
function remote_user() {
@ -1823,14 +1911,15 @@ function get_max_import_size() {
* @brief Wrap calls to proc_close(proc_open()) and call hook
* so plugins can take part in process :)
*
* @param (string|integer) $cmd program to run or priority
*
* @param (string|integer|array) $cmd program to run, priority or parameter array
*
* next args are passed as $cmd command line
* e.g.: proc_run("ls","-la","/tmp");
* or: proc_run(PRIORITY_HIGH, "include/notifier.php", "drop", $drop_id);
* or: proc_run(array('priority' => PRIORITY_HIGH, 'dont_fork' => true), "include/create_shadowentry.php", $post_id);
*
* @note $cmd and string args are surrounded with ""
*
*
* @hooks 'proc_run'
* array $arr
*/
@ -1838,24 +1927,31 @@ function proc_run($cmd){
$a = get_app();
$args = func_get_args();
$proc_args = func_get_args();
$newargs = array();
if (!count($args))
$args = array();
if (!count($proc_args)) {
return;
// expand any arrays
foreach($args as $arg) {
if(is_array($arg)) {
foreach($arg as $n) {
$newargs[] = $n;
}
} else
$newargs[] = $arg;
}
$args = $newargs;
// Preserve the first parameter
// It could contain a command, the priority or an parameter array
// If we use the parameter array we have to protect it from the following function
$run_parameter = array_shift($proc_args);
// expand any arrays
foreach ($proc_args as $arg) {
if (is_array($arg)) {
foreach ($arg as $n) {
$args[] = $n;
}
} else {
$args[] = $arg;
}
}
// Now we add the run parameters back to the array
array_unshift($args, $run_parameter);
$arr = array('args' => $args, 'run_cmd' => true);
@ -1863,16 +1959,24 @@ function proc_run($cmd){
if (!$arr['run_cmd'] OR !count($args))
return;
if (!get_config("system", "worker") OR
(($args[0] != 'php') AND !is_int($args[0]))) {
if (!get_config("system", "worker") OR (is_string($run_parameter) AND ($run_parameter != 'php'))) {
$a->proc_run($args);
return;
}
if (is_int($args[0]))
$priority = $args[0];
else
$priority = PRIORITY_MEDIUM;
$priority = PRIORITY_MEDIUM;
$dont_fork = get_config("system", "worker_dont_fork");
if (is_int($run_parameter)) {
$priority = $run_parameter;
} elseif (is_array($run_parameter)) {
if (isset($run_parameter['priority'])) {
$priority = $run_parameter['priority'];
}
if (isset($run_parameter['dont_fork'])) {
$dont_fork = $run_parameter['dont_fork'];
}
}
$argv = $args;
array_shift($argv);
@ -1889,8 +1993,9 @@ function proc_run($cmd){
intval($priority));
// Should we quit and wait for the poller to be called as a cronjob?
if (get_config("system", "worker_dont_fork"))
if ($dont_fork) {
return;
}
// Checking number of workers
$workers = q("SELECT COUNT(*) AS `workers` FROM `workerqueue` WHERE `executed` != '0000-00-00 00:00:00'");
@ -1989,9 +2094,9 @@ function current_theme(){
/**
* @brief Return full URL to theme which is currently in effect.
*
*
* Provide a sane default if nothing is chosen or the specified theme does not exist.
*
*
* @return string
*/
function current_theme_url() {
@ -2260,6 +2365,36 @@ function get_lockpath() {
return "";
}
/**
* @brief Returns the path where spool files are stored
*
* @return string Spool path
*/
function get_spoolpath() {
$spoolpath = get_config('system','spoolpath');
if (($spoolpath != "") AND is_dir($spoolpath) AND is_writable($spoolpath)) {
return($spoolpath);
}
$temppath = get_temppath();
if ($temppath != "") {
$spoolpath = $temppath."/spool";
if (!is_dir($spoolpath)) {
mkdir($spoolpath);
} elseif (!is_writable($spoolpath)) {
$spoolpath = $temppath;
}
if (is_dir($spoolpath) AND is_writable($spoolpath)) {
set_config("system", "spoolpath", $spoolpath);
return($spoolpath);
}
}
return "";
}
function get_temppath() {
$a = get_app();
@ -2338,7 +2473,7 @@ function current_load() {
/**
* @brief get c-style args
*
*
* @return int
*/
function argc() {
@ -2347,7 +2482,7 @@ function argc() {
/**
* @brief Returns the value of a argv key
*
*
* @param int $x argv key
* @return string Value of the argv key
*/
@ -2360,12 +2495,12 @@ function argv($x) {
/**
* @brief Get the data which is needed for infinite scroll
*
*
* For invinite scroll we need the page number of the actual page
* and the the URI where the content of the next page comes from.
* This data is needed for the js part in main.js.
* Note: infinite scroll does only work for the network page (module)
*
*
* @param string $module The name of the module (e.g. "network")
* @return array Of infinite scroll data
* 'pageno' => $pageno The number of the actual page

View file

@ -1,19 +0,0 @@
ALTER TABLE `profile` DROP INDEX `pub_keywords` ;
ALTER TABLE `profile` DROP INDEX `prv_keywords` ;
ALTER TABLE `item` DROP INDEX `title` ;
ALTER TABLE `item` DROP INDEX `body` ;
ALTER TABLE `item` DROP INDEX `allow_cid` ;
ALTER TABLE `item` DROP INDEX `allow_gid` ;
ALTER TABLE `item` DROP INDEX `deny_cid` ;
ALTER TABLE `item` DROP INDEX `deny_gid` ;
ALTER TABLE `item` DROP INDEX `tag` ;
ALTER TABLE `item` DROP INDEX `file` ;
SELECT CONCAT('ALTER TABLE ',table_schema,'.',table_name,' engine=InnoDB;')
FROM information_schema.tables
WHERE engine = 'MyISAM';

View file

@ -1,6 +1,6 @@
-- ------------------------------------------
-- Friendica 3.5-dev (Asparagus)
-- DB_UPDATE_VERSION 1200
-- Friendica 3.5.1-dev (Asparagus)
-- DB_UPDATE_VERSION 1208
-- ------------------------------------------
@ -58,8 +58,9 @@ CREATE TABLE IF NOT EXISTS `cache` (
`v` text,
`expire_mode` int(11) NOT NULL DEFAULT 0,
`updated` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY(`k`),
INDEX `updated` (`updated`)
PRIMARY KEY(`k`(191)),
INDEX `updated` (`updated`),
INDEX `expire_mode_updated` (`expire_mode`,`updated`)
) DEFAULT CHARSET=utf8mb4;
--
@ -97,7 +98,7 @@ CREATE TABLE IF NOT EXISTS `config` (
`k` varchar(255) NOT NULL DEFAULT '',
`v` text,
PRIMARY KEY(`id`),
INDEX `cat_k` (`cat`(30),`k`(30))
UNIQUE INDEX `cat_k` (`cat`(30),`k`(30))
) DEFAULT CHARSET=utf8mb4;
--
@ -118,6 +119,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
`about` text,
`keywords` text,
`gender` varchar(32) NOT NULL DEFAULT '',
`xmpp` varchar(255) NOT NULL DEFAULT '',
`attag` varchar(255) NOT NULL DEFAULT '',
`avatar` varchar(255) NOT NULL DEFAULT '',
`photo` text,
@ -157,6 +159,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
`writable` tinyint(1) NOT NULL DEFAULT 0,
`forum` tinyint(1) NOT NULL DEFAULT 0,
`prv` tinyint(1) NOT NULL DEFAULT 0,
`contact-type` int(11) unsigned NOT NULL DEFAULT 0,
`hidden` tinyint(1) NOT NULL DEFAULT 0,
`archive` tinyint(1) NOT NULL DEFAULT 0,
`pending` tinyint(1) NOT NULL DEFAULT 1,
@ -172,6 +175,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
`ffi_keyword_blacklist` mediumtext,
PRIMARY KEY(`id`),
INDEX `uid` (`uid`),
INDEX `addr_uid` (`addr`,`uid`),
INDEX `nurl` (`nurl`)
) DEFAULT CHARSET=utf8mb4;
@ -199,7 +203,8 @@ CREATE TABLE IF NOT EXISTS `deliverq` (
`cmd` varchar(32) NOT NULL DEFAULT '',
`item` int(11) NOT NULL DEFAULT 0,
`contact` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY(`id`)
PRIMARY KEY(`id`),
UNIQUE INDEX `cmd_item_contact` (`cmd`,`item`,`contact`)
) DEFAULT CHARSET=utf8mb4;
--
@ -326,6 +331,7 @@ CREATE TABLE IF NOT EXISTS `gcontact` (
`gender` varchar(32) NOT NULL DEFAULT '',
`birthday` varchar(32) NOT NULL DEFAULT '0000-00-00',
`community` tinyint(1) NOT NULL DEFAULT 0,
`contact-type` tinyint(1) NOT NULL DEFAULT -1,
`hide` tinyint(1) NOT NULL DEFAULT 0,
`nsfw` tinyint(1) NOT NULL DEFAULT 0,
`network` varchar(255) NOT NULL DEFAULT '',
@ -652,6 +658,8 @@ CREATE TABLE IF NOT EXISTS `notify` (
`seen` tinyint(1) NOT NULL DEFAULT 0,
`verb` varchar(255) NOT NULL DEFAULT '',
`otype` varchar(16) NOT NULL DEFAULT '',
`name_cache` tinytext,
`msg_cache` mediumtext,
PRIMARY KEY(`id`),
INDEX `uid` (`uid`)
) DEFAULT CHARSET=utf8mb4;
@ -677,7 +685,7 @@ CREATE TABLE IF NOT EXISTS `oembed` (
`url` varchar(255) NOT NULL,
`content` text,
`created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY(`url`),
PRIMARY KEY(`url`(191)),
INDEX `created` (`created`)
) DEFAULT CHARSET=utf8mb4;
@ -690,7 +698,7 @@ CREATE TABLE IF NOT EXISTS `parsed_url` (
`oembed` tinyint(1) NOT NULL DEFAULT 0,
`content` text,
`created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY(`url`,`guessing`,`oembed`),
PRIMARY KEY(`url`(191),`guessing`,`oembed`),
INDEX `created` (`created`)
) DEFAULT CHARSET=utf8mb4;
@ -704,7 +712,7 @@ CREATE TABLE IF NOT EXISTS `pconfig` (
`k` varchar(255) NOT NULL DEFAULT '',
`v` mediumtext,
PRIMARY KEY(`id`),
INDEX `uid_cat_k` (`uid`,`cat`(30),`k`(30))
UNIQUE INDEX `uid_cat_k` (`uid`,`cat`(30),`k`(30))
) DEFAULT CHARSET=utf8mb4;
--
@ -734,7 +742,9 @@ CREATE TABLE IF NOT EXISTS `photo` (
`deny_cid` mediumtext,
`deny_gid` mediumtext,
PRIMARY KEY(`id`),
INDEX `uid` (`uid`),
INDEX `uid_contactid` (`uid`,`contact-id`),
INDEX `uid_profile` (`uid`,`profile`),
INDEX `uid_album_created` (`uid`,`album`,`created`),
INDEX `resource-id` (`resource-id`),
INDEX `guid` (`guid`)
) DEFAULT CHARSET=utf8mb4;
@ -771,6 +781,17 @@ CREATE TABLE IF NOT EXISTS `poll_result` (
INDEX `choice` (`choice`)
) DEFAULT CHARSET=utf8mb4;
--
-- TABLE process
--
CREATE TABLE IF NOT EXISTS `process` (
`pid` int(10) unsigned NOT NULL,
`command` varchar(32) NOT NULL DEFAULT '',
`created` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY(`pid`),
INDEX `command` (`command`)
) DEFAULT CHARSET=utf8mb4;
--
-- TABLE profile
--
@ -812,6 +833,7 @@ CREATE TABLE IF NOT EXISTS `profile` (
`education` text,
`contact` text,
`homepage` varchar(255) NOT NULL DEFAULT '',
`xmpp` varchar(255) NOT NULL DEFAULT '',
`photo` varchar(255) NOT NULL DEFAULT '',
`thumb` varchar(255) NOT NULL DEFAULT '',
`publish` tinyint(1) NOT NULL DEFAULT 0,
@ -877,6 +899,7 @@ CREATE TABLE IF NOT EXISTS `register` (
`uid` int(11) unsigned NOT NULL DEFAULT 0,
`password` varchar(255) NOT NULL DEFAULT '',
`language` varchar(16) NOT NULL DEFAULT '',
`note` text,
PRIMARY KEY(`id`)
) DEFAULT CHARSET=utf8mb4;
@ -957,6 +980,7 @@ CREATE TABLE IF NOT EXISTS `term` (
INDEX `type_term` (`type`,`term`),
INDEX `uid_otype_type_term_global_created` (`uid`,`otype`,`type`,`term`,`global`,`created`),
INDEX `otype_type_term_tid` (`otype`,`type`,`term`,`tid`),
INDEX `uid_otype_type_url` (`uid`,`otype`,`type`,`url`),
INDEX `guid` (`guid`)
) DEFAULT CHARSET=utf8mb4;
@ -1048,6 +1072,7 @@ CREATE TABLE IF NOT EXISTS `user` (
`cntunkmail` int(11) NOT NULL DEFAULT 10,
`notify-flags` int(11) unsigned NOT NULL DEFAULT 65535,
`page-flags` int(11) unsigned NOT NULL DEFAULT 0,
`account-type` int(11) unsigned NOT NULL DEFAULT 0,
`prvnets` tinyint(1) NOT NULL DEFAULT 0,
`pwdreset` varchar(255) NOT NULL DEFAULT '',
`maxreq` int(11) NOT NULL DEFAULT 10,

View file

@ -1,6 +1,8 @@
Accesskeys in Friendica
=======================
* [Home](help)
General
-------
* p: profile

View file

@ -1,208 +1,616 @@
Friendica BBCode tags reference
========================
* [Home](help)
* [Creating posts](help/Text_editor)
Inline
-----
## Inline
<style>
table.bbcodes {
margin: 1em 0;
background-color: #f9f9f9;
border: 1px solid #aaa;
border-collapse: collapse;
color: #000;
width: 100%;
}
<pre>[b]bold[/b]</pre> : <strong>bold</strong>
table.bbcodes > tr > th,
table.bbcodes > tr > td,
table.bbcodes > * > tr > th,
table.bbcodes > * > tr > td {
border: 1px solid #aaa;
padding: 0.2em 0.4em
}
<pre>[i]italic[/i]</pre> : <em>italic</em>
table.bbcodes > tr > th,
table.bbcodes > * > tr > th {
background-color: #f2f2f2;
text-align: center;
width: 50%
}
</style>
<pre>[u]underlined[/u]</pre> : <u>underlined</u>
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>[b]bold[/b]</td>
<td><strong>bold</strong></td>
</tr>
<tr>
<td>[i]italic[/i]</td>
<td><em>italic</em></td>
</tr>
<tr>
<td>[u]underlined[/u]</td>
<td><u>underlined</u></td>
</tr>
<tr>
<td>[s]strike[/s]</td>
<td><strike>strike</strike></td>
</tr>
<tr>
<td>[o]overline[/o]</td>
<td><span class="overline">overline</span></td>
</tr>
<tr>
<td>[color=red]red[/color]</td>
<td><span style="color: red;">red</span></td>
</tr>
<tr>
<td>[url=http://www.friendica.com]Friendica[/url]</td>
<td><a href="http://www.friendica.com" target="external-link">Friendica</a></td>
</tr>
<tr>
<td>[img]http://friendica.com/sites/default/files/friendika-32.png[/img]</td>
<td><img src="http://friendica.com/sites/default/files/friendika-32.png" alt="Immagine/foto"></td>
</tr>
<tr>
<td>[img=64x32]http://friendica.com/sites/default/files/friendika-32.png[/img]<br>
<br>Note: provided height is simply discarded.</td>
<td><img src="http://friendica.com/sites/default/files/friendika-32.png" style="width: 64px;"></td>
</tr>
<tr>
<td>[size=xx-small]small text[/size]</td>
<td><span style="font-size: xx-small;">small text</span></td>
</tr>
<tr>
<td>[size=xx-large]big text[/size]</td>
<td><span style="font-size: xx-large;">big text</span></td>
</tr>
<tr>
<td>[size=20]exact size[/size] (size can be any number, in pixel)</td>
<td><span style="font-size: 20px;">exact size</span></td>
</tr>
<tr>
<td>[font=serif]Serif font[/font]</td>
<td><span style="font-family: serif;">Serif font</span></td>
</tr>
</table>
<pre>[s]strike[/s]</pre> : <strike>strike</strike>
### Links
<pre>[color=red]red[/color]</pre> : <span style="color: red;">red</span>
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>[url]http://friendica.com[/url]</td>
<td><a href="http://friendica.com">http://friendica.com</a></td>
</tr>
<tr>
<td>[url=http://friendica.com]Friendica[/url]</td>
<td><a href="http://friendica.com">Friendica</a></td>
</tr>
<tr>
<td>[bookmark]http://friendica.com[/bookmark]<br><br>
#^[url]http://friendica.com[/url]</td>
<td><span class="oembed link"><h4>Friendica: <a href="http://friendica.com" rel="oembed"></a><a href="http://friendica.com" target="_blank">http://friendica.com</a></h4></span></td>
</tr>
<tr>
<td>[bookmark=http://friendica.com]Bookmark[/bookmark]<br><br>
#^[url=http://friendica.com]Bookmark[/url]<br><br>
#[url=http://friendica.com]^[/url][url=http://friendica.com]Bookmark[/url]</td>
<td><span class="oembed link"><h4>Friendica: <a href="http://friendica.com" rel="oembed"></a><a href="http://friendica.com" target="_blank">Bookmark</a></h4></span></td>
</tr>
<tr>
<td>[url=/posts/f16d77b0630f0134740c0cc47a0ea02a]Diaspora post with GUID[/url]</td>
<td><a href="/display/f16d77b0630f0134740c0cc47a0ea02a" target="_blank">Diaspora post with GUID</a></td>
</tr>
<tr>
<td>#Friendica</td>
<td>#<a href="/search?tag=Friendica">Friendica</a></td>
</tr>
<tr>
<td>@Mention</td>
<td>@<a href="javascript:void(0)">Mention</a></td>
</tr>
<tr>
<td>acct:account@friendica.host.com (WebFinger)</td>
<td><a href="/acctlink?addr=account@friendica.host.com" target="extlink">acct:account@friendica.host.com</a></td>
</tr>
<tr>
<td>[mail]user@mail.example.com[/mail]</td>
<td><a href="mailto:user@mail.example.com">user@mail.example.com</a></td>
</tr>
<tr>
<td>[mail=user@mail.example.com]Send an email to User[/mail]</td>
<td><a href="mailto:user@mail.example.com">Send an email to User</a></td>
</tr>
</table>
<pre>[url=http://www.friendica.com]Friendica[/url]</pre> : <a href="http://www.friendica.com" target="external-link">Friendica</a>
## Blocks
<pre>[img]http://friendica.com/sites/default/files/friendika-32.png[/img]</pre> : <img src="http://friendica.com/sites/default/files/friendika-32.png" alt="Immagine/foto">
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>[p]A paragraph of text[/p]</td>
<td><p>A paragraph of text</p></td>
</tr>
<tr>
<td>Inline [code]code[/code] in a paragraph</td>
<td>Inline <key>code</key> in a paragraph</td>
</tr>
<tr>
<td>[code]Multi<br>line<br>code[/code]</td>
<td><code>Multi
line
code</code></td>
</tr>
<tr>
<td>[code=php]function text_highlight($s,$lang)[/code]</td>
<td><code><div class="hl-main"><ol class="hl-main"><li><span class="hl-code">&nbsp;</span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">text_highlight</span><span class="hl-brackets">(</span><span class="hl-var">$s</span><span class="hl-code">,</span><span class="hl-var">$lang</span><span class="hl-brackets">)</span></li></ol></div></code></td>
</tr>
<tr>
<td>[quote]quote[/quote]</td>
<td><blockquote>quote</blockquote></td>
</tr>
<tr>
<td>[quote=Author]Author? Me? No, no, no...[/quote]</td>
<td><strong class="author">Author wrote:</strong><blockquote>Author? Me? No, no, no...</blockquote></td>
</tr>
<tr>
<td>[center]Centered text[/center]</td>
<td><div style="text-align:center;">Centered text</div></td>
</tr>
<tr>
<td>You should not read any further if you want to be surprised.[spoiler]There is a happy end.[/spoiler]</td>
<td>
<div class="wall-item-container">
You should not read any further if you want to be surprised.<br>
<span id="spoiler-wrap-0716e642" class="spoiler-wrap fakelink" onclick="openClose('spoiler-0716e642');">Click to open/close</span>
<blockquote class="spoiler" id="spoiler-0716e642" style="display: none;">There is a happy end.</blockquote>
<div class="body-attach"><div class="clear"></div></div>
</div>
</td>
</tr>
<tr>
<td>[spoiler=Author]Spoiler quote[/spoiler]</td>
<td>
<div class="wall-item-container">
<strong class="spoiler">Author wrote:</strong><br>
<span id="spoiler-wrap-a893765a" class="spoiler-wrap fakelink" onclick="openClose('spoiler-a893765a');">Click to open/close</span>
<blockquote class="spoiler" id="spoiler-a893765a" style="display: none;">Spoiler quote</blockquote>
<div class="body-attach"><div class="clear"></div></div>
</div>
</td>
</tr>
<tr>
<td>[hr] (horizontal line)</td>
<td><hr></td>
</tr>
</table>
<pre>[size=xx-small]small text[/size]</pre> : <span style="font-size: xx-small;">small text</span>
### Titles
<pre>[size=xx-large]big text[/size]</pre> : <span style="font-size: xx-large;">big text</span>
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>[h1]Title 1[/h1]</td>
<td><h1>Title 1</h1></td>
</tr>
<tr>
<td>[h2]Title 2[/h2]</td>
<td><h2>Title 2</h2></td>
</tr>
<tr>
<td>[h3]Title 3[/h3]</td>
<td><h3>Title 3</h3></td>
</tr>
<tr>
<td>[h4]Title 4[/h4]</td>
<td><h4>Title 4</h4></td>
</tr>
<tr>
<td>[h5]Title 5[/h5]</td>
<td><h5>Title 5</h5></td>
</tr>
<tr>
<td>[h6]Title 6[/h6]</td>
<td><h6>Title 6</h6></td>
</tr>
</table>
<pre>[size=20]exact size[/size] (size can be any number, in pixel)</pre> : <span style="font-size: 20px;">exact size</span>
### Tables
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>[table]<br>
&nbsp;&nbsp;[tr]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[th]Header 1[/th]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[th]Header 2[/th]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[th]Header 2[/th]<br>
&nbsp;&nbsp;[/tr]<br>
&nbsp;&nbsp;[tr]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Cell 1[/td]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Cell 2[/td]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Cell 3[/td]<br>
&nbsp;&nbsp;[/tr]<br>
&nbsp;&nbsp;[tr]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Cell 4[/td]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Cell 5[/td]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Cell 6[/td]<br>
&nbsp;&nbsp;[/tr]<br>
[/table]</td>
<td>
<table>
<tbody>
<tr>
<th>Header 1</th>
<th>Header 2</th>
<th>Header 3</th>
</tr>
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
<td>Cell 3</td>
</tr>
<tr>
<td>Cell 4</td>
<td>Cell 5</td>
<td>Cell 6</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>[table border=0]</td>
<td>
<table border="0">
<tbody>
<tr>
<th>Header 1</th>
<th>Header 2</th>
<th>Header 3</th>
</tr>
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
<td>Cell 3</td>
</tr>
<tr>
<td>Cell 4</td>
<td>Cell 5</td>
<td>Cell 6</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>[table border=1]</td>
<td>
<table border="1">
<tbody>
<tr>
<th>Header 1</th>
<th>Header 2</th>
<th>Header 3</th>
</tr>
<tr>
<td>Cell 1</td>
<td>Cell 2</td>
<td>Cell 3</td>
</tr>
<tr>
<td>Cell 4</td>
<td>Cell 5</td>
<td>Cell 6</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
### Lists
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>[ul]<br>
&nbsp;&nbsp;[li] First list element<br>
&nbsp;&nbsp;[li] Second list element<br>
[/ul]<br>
[list]<br>
&nbsp;&nbsp;[*] First list element<br>
&nbsp;&nbsp;[*] Second list element<br>
[/list]</td>
<td>
<ul class="listbullet" style="list-style-type: circle;">
<li>First list element</li>
<li>Second list element</li>
</ul>
</td>
</tr>
<tr>
<td>[ol]<br>
&nbsp;&nbsp;[*] First list element<br>
&nbsp;&nbsp;[*] Second list element<br>
[/ol]<br>
[list=1]<br>
&nbsp;&nbsp;[*] First list element<br>
&nbsp;&nbsp;[*] Second list element<br>
[/list]</td>
<td>
<ul class="listdecimal" style="list-style-type: decimal;">
<li> First list element</li>
<li> Second list element</li>
</ul>
</td>
</tr>
<tr>
<td>[list=]<br>
&nbsp;&nbsp;[*] First list element<br>
&nbsp;&nbsp;[*] Second list element<br>
[/list]</td>
<td>
<ul class="listnone" style="list-style-type: none;">
<li> First list element</li>
<li> Second list element</li>
</ul>
</td>
</tr>
<tr>
<td>[list=i]<br>
&nbsp;&nbsp;[*] First list element<br>
&nbsp;&nbsp;[*] Second list element<br>
[/list]</td>
<td>
<ul class="listlowerroman" style="list-style-type: lower-roman;">
<li> First list element</li>
<li> Second list element</li>
</ul>
</td>
</tr>
<tr>
<td>[list=I]<br>
&nbsp;&nbsp;[*] First list element<br>
&nbsp;&nbsp;[*] Second list element<br>
[/list]</td>
<td>
<ul class="listupperroman" style="list-style-type: upper-roman;">
<li> First list element</li>
<li> Second list element</li>
</ul>
</td>
</tr>
<tr>
<td>[list=a]<br>
&nbsp;&nbsp;[*] First list element<br>
&nbsp;&nbsp;[*] Second list element<br>
[/list]</td>
<td>
<ul class="listloweralpha" style="list-style-type: lower-alpha;">
<li> First list element</li>
<li> Second list element</li>
</ul>
</td>
</tr>
<tr>
<td>[list=A]<br>
&nbsp;&nbsp;[*] First list element<br>
&nbsp;&nbsp;[*] Second list element<br>
[/list]</td>
<td>
<ul class="listupperalpha" style="list-style-type: upper-alpha;">
<li> First list element</li>
<li> Second list element</li>
</ul>
</td>
</tr>
</table>
Block
-----
<pre>[code]code[/code]</pre>
<code>code</code>
<p style="clear:both;">&nbsp;</p>
<pre>[code=php]function text_highlight($s,$lang)[/code]</pre>
<code><div class="hl-main"><ol class="hl-main"><li><span class="hl-code">&nbsp;</span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">text_highlight</span><span class="hl-brackets">(</span><span class="hl-var">$s</span><span class="hl-code">,</span><span class="hl-var">$lang</span><span class="hl-brackets">)</span></li></ol></div></code>
<p style="clear:both;">&nbsp;</p>
<pre>[quote]quote[/quote]</pre>
<blockquote>quote</blockquote>
<p style="clear:both;">&nbsp;</p>
<pre>[quote=Author]Author? Me? No, no, no...[/quote]</pre>
<strong class="author">Author wrote:</strong><blockquote>Author? Me? No, no, no...</blockquote>
<p style="clear:both;">&nbsp;</p>
<pre>[center]centered text[/center]</pre>
<div style="text-align:center;">centered text</div>
<p style="clear:both;">&nbsp;</p>
<pre>You should not read any further if you want to be surprised.[spoiler]There is a happy end.[/spoiler]</pre>
You should not read any further if you want to be surprised.<br />*click to open/close*
(The text between thhe opening and the closing of the spoiler tag will be visible once the link is clicked. So *"There is a happy end."* wont be visible until the spoiler is uncovered.)
<p style="clear:both;">&nbsp;</p>
**Table**
<pre>[table border=1]
[tr]
[th]Tables now[/th]
[/tr]
[tr]
[td]Have headers[/td]
[/tr]
[/table]</pre>
<table border="1"><tbody><tr><th>Tables now</th></tr><tr><td>Have headers</td></tr></tbody></table>
<p style="clear:both;">&nbsp;</p>
**List**
<pre>[list]
[*] First list element
[*] Second list element
[/list]</pre>
<ul class="listbullet" style="list-style-type: circle;">
<li> First list element<br>
</li>
<li> Second list element</li>
</ul>
[list] is equivalent to [ul] (unordered list).
[ol] can be used instead of [list] to show an ordered list:
<pre>[ol]
[*] First list element
[*] Second list element
[/ol]</pre>
<ul class="listdecimal" style="list-style-type: decimal;"><li> First list element<br></li><li> Second list element</li></ul>
For more options on ordered lists, you can define the style of numeration on [list] argument:
<pre>[list=1]</pre> : decimal
<pre>[list=i]</pre> : lover case roman
<pre>[list=I]</pre> : upper case roman
<pre>[list=a]</pre> : lover case alphabetic
<pre>[list=A] </pre> : upper case alphabetic
Embed
------
## Embed
You can embed video, audio and more in a message.
<pre>[video]url[/video]</pre>
<pre>[audio]url[/audio]</pre>
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>[video]url[/video]</td>
<td>Where *url* can be an url to youtube, vimeo, soundcloud, or other sites wich supports oembed or opengraph specifications.</td>
</tr>
<tr>
<td>[video]Video file url[/video]
[audio]Audio file url[/audio]</td>
<td>Full URL to an ogg/ogv/oga/ogm/webm/mp4/mp3 file. An HTML5 player will be used to show it.</td>
</tr>
<tr>
<td>[youtube]Youtube URL[/youtube]</td>
<td>Youtube video OEmbed display. May not embed an actual player.</td>
</tr>
<tr>
<td>[youtube]Youtube video ID[/youtube]</td>
<td>Youtube player iframe embed.</td>
</tr>
<tr>
<td>[vimeo]Vimeo URL[/vimeo]</td>
<td>Vimeo video OEmbed display. May not embed an actual player.</td>
</tr>
<tr>
<td>[vimeo]Vimeo video ID[/vimeo]</td>
<td>Vimeo player iframe embed.</td>
</tr>
<tr>
<td>[embed]URL[/embed]</td>
<td>Embed OEmbed rich content.</td>
</tr>
<tr>
<td>[iframe]URL[/iframe]</td>
<td>General embed, iframe size is limited by the theme size for video players.</td>
</tr>
<tr>
<td>[url]*url*[/url]</td>
<td>If *url* supports oembed or opengraph specifications the embedded object will be shown (eg, documents from scribd).
Page title with a link to *url* will be shown.</td>
</tr>
</table>
Where *url* can be an url to youtube, vimeo, soundcloud, or other sites wich supports oembed or opengraph specifications.
*url* can be also full url to an ogg file. HTML5 tag will be used to show it.
## Map
<pre>[url]*url*[/url]</pre>
This require "openstreetmap" or "Google Maps" addon version 1.3 or newer.
If the addon isn't activated, the raw coordinates are shown instead.
If *url* supports oembed or opengraph specifications the embedded object will be shown (eg, documents from scribd).
Page title with a link to *url* will be shown.
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>[map]address[/map]</td>
<td>Embeds a map centered on this address.</td>
</tr>
<tr>
<td>[map=lat,long]</td>
<td>Embeds a map centered on those coordinates.</td>
</tr>
<tr>
<td>[map]</td>
<td>Embeds a map centered on the post's location.</td>
</tr>
</table>
Map
---
## Abstract for longer posts
<pre>[map]address[/map]</pre>
<pre>[map=lat,long]</pre>
If you want to spread your post to several third party networks you can have the problem that these networks have a length limitation like on Twitter.
You can embed maps from coordinates or addresses.
This require "openstreetmap" addon version 1.3 or newer.
-----------------------------------------------------------
Abstract for longer posts
-------------------------
If you want to spread your post to several third party networks you can have the problem that these networks have (for example) a length limitation.
(Like on Twitter)
Friendica is using a semi intelligent mechanism to generate a fitting abstract.
But it can be interesting to define an own abstract that will only be displayed on the external network.
This is done with the [abstract]-element.
Example:
<pre>[abstract]Totally interesting! A must-see! Please click the link![/abstract]
I want to tell you a really boring story that you really never wanted
to hear.</pre>
Twitter would display the text "Totally interesting! A must-see! Please click the link!".
On Friendica you would only see the text after "I want to tell you a really ..."
Friendica is using a semi intelligent mechanism to generate a fitting abstract.
But it can be interesting to define a custom abstract that will only be displayed on the external network.
This is done with the [abstract]-element.
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>[abstract]Totally interesting! A must-see! Please click the link![/abstract]<br>
I want to tell you a really boring story that you really never wanted to hear.</td>
<td>Twitter would display the text <blockquote>Totally interesting! A must-see! Please click the link!</blockquote>
On Friendica you would only see the text after <blockquote>I want to tell you a really ...</blockquote></td>
</tr>
</table>
It is even possible to define abstracts for separate networks:
<pre>
[abstract]Hi friends Here are my newest pictures![abstract]
[abstract=twit]Hi my dear Twitter followers. Do you want to see my new
pictures?[abstract]
[abstract=apdn]Helly my dear followers on ADN. I made sone new pictures
that I wanted to share with you.[abstract]
Today I was in the woods and took some real cool pictures ...
</pre>
For Twitter and App.net the system will use the defined abstracts.
For other networks (e.g. when you are using the "statusnet" connector that is used to post to GNU Social) the general abstract element will be used.
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>
[abstract]Hi friends Here are my newest pictures![/abstract]<br>
[abstract=twit]Hi my dear Twitter followers. Do you want to see my new
pictures?[/abstract]<br>
[abstract=apdn]Helly my dear followers on ADN. I made sone new pictures
that I wanted to share with you.[/abstract]<br>
Today I was in the woods and took some real cool pictures ...</td>
<td>For Twitter and App.net the system will use the defined abstracts.<br>
For other networks (e.g. when you are using the "statusnet" connector that is used to post to your GNU Social account) the general abstract element will be used.</td>
</tr>
</table>
If you use (for example) the "buffer" connector to post to Facebook or Google+ you can use this element to define an abstract for a longer blogpost that you don't want to post completely to these networks.
Networks like Facebook or Google+ aren't length limited.
For this reason the [abstract] element isn't used.
Networks like Facebook or Google+ aren't length limited.
For this reason the [abstract] element isn't used.
Instead you have to name the explicit network:
<pre>
[abstract]These days I had a strange encounter ...[abstract]
[abstract=goog]Helly my dear Google+ followers. You have to read my
newest blog post![abstract]
[abstract=face]Hello my Facebook friends. These days happened something
really cool.[abstract]
While taking pictures in the woods I had a really strange encounter ... </pre>
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>
[abstract]These days I had a strange encounter...[/abstract]<br>
[abstract=goog]Helly my dear Google+ followers. You have to read my newest blog post![/abstract]<br>
[abstract=face]Hello my Facebook friends. These days happened something really cool.[/abstract]<br>
While taking pictures in the woods I had a really strange encounter...</td>
<td>Google and Facebook will show the respective abstracts while the other networks will show the default one.<br>
<br>Meanwhile, Friendica won't show any of the abstracts.</td>
</tr>
</table>
The [abstract] element isn't working with the native OStatus connection or with connectors where we post the HTML.
(Like Tumblr, Wordpress or Pump.io)
The [abstract] element isn't working with connectors where we post the HTML like Tumblr, Wordpress or Pump.io.
For the native connections--that is to e.g. Friendica, Hubzilla, Diaspora or GNU Social--the full posting is used and the contacts instance will display the posting as desired.
Special
-------
## Special
If you need to put literal bbcode in a message, [noparse], [nobb] or [pre] are used to escape bbcode:
<pre>[noparse][b]bold[/b][/noparse]</pre> : [b]bold[/b]
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Result</th>
</tr>
<tr>
<td>If you need to put literal bbcode in a message, [noparse], [nobb] or [pre] are used to escape bbcode:
<ul>
<li>[noparse][b]bold[/b][/noparse]</li>
<li>[nobb][b]bold[/b][/nobb]</li>
<li>[pre][b]bold[/b][/pre]</li>
</ul>
</td>
<td>[b]bold[/b]</td>
</tr>
<tr>
<td>[nosmile] is used to disable smilies on a post by post basis<br>
<br>
[nosmile] ;-) :-O
</td>
<td>;-) :-O</td>
</tr>
<tr>
<td>Custom inline styles<br>
<br>
[style=text-shadow: 0 0 4px #CC0000;]You can change all the CSS properties of this block.[/style]</td>
<td><span style="text-shadow: 0 0 4px #cc0000;;">You can change all the CSS properties of this block.</span></td>
</tr>
<tr>
<td>Custom class block<br>
<br>
[class=custom]If the class exists, this block will have the custom class style applied.[/class]</td>
<td><pre>&lt;span class="custom"&gt;If the class exists,<br> this block will have the custom class<br> style applied.&lt;/span&gt;</pre></td>
</tr>
</table>

View file

@ -9,7 +9,7 @@ This helps us get new features faster.
You can also contact the [friendica support forum](https://helpers.pyxis.uberspace.de/profile/helpers) and report your problem there.
Maybe someone from another node encountered the problem as well and can help you.
If you're a technical user, or your site doesn't have a support page, you'll need to use the [Bug Tracker](http://bugs.friendica.com/).
If you're a technical user, or your site doesn't have a support page, you'll need to use the [Bug Tracker](https://github.com/friendica/friendica/issues).
Please perform a search to see if there's already an open bug that matches yours before submitting anything.
Try to provide as much information as you can about the bug, including the **full** text of any error messages or notices, and any steps required to replicate the problem in as much detail as possible.

View file

@ -7,11 +7,11 @@ Friendica Documentation and Resources
* [Account Basics](help/Account-Basics)
* [New User Quick Start](help/Quick-Start-guide)
* [Creating posts](help/Text_editor)
* [BBCode tag reference](help/BBCode)
* [BBCode tag reference](help/BBCode)
* [Comment, sort and delete posts](help/Text_comment)
* [Profiles](help/Profiles)
* [Accesskey reference](help/Accesskeys)
* [Events](help/events)
* [Events](help/events)
* You and other users
* [Connectors](help/Connectors)
* [Making Friends](help/Making-Friends)
@ -20,7 +20,6 @@ Friendica Documentation and Resources
* [Community Forums](help/Forums)
* [Chats](help/Chats)
* Further information
* [Improve Performance](help/Improve-Performance)
* [Move your account](help/Move-Account)
* [Delete your account](help/Remove-Account)
* [Frequently asked questions (FAQ)](help/FAQ)
@ -31,10 +30,9 @@ Friendica Documentation and Resources
* [Settings & Admin Panel](help/Settings)
* [Installing Connectors (Twitter/GNU Social)](help/Installing-Connectors)
* [Install an ejabberd server (XMPP chat) with synchronized credentials](help/install-ejabberd)
* [Message Flow](help/Message-Flow)
* [Using SSL with Friendica](help/SSL)
* [Twitter/GNU Social API Functions](help/api)
* [Config values that can only be set in .htconfig.php](help/htconfig)
* [Improve Performance](help/Improve-Performance)
**Developer Manual**
@ -46,9 +44,11 @@ Friendica Documentation and Resources
* [Plugin Development](help/Plugins)
* [Theme Development](help/themes)
* [Smarty 3 Templates](help/smarty3-templates)
* [Protocol Documentation](help/Protocol)
* [Database schema documantation](help/database)
* [Class Autoloading](help/autoloader)
* [Code - Reference(Doxygen generated - sets cookies)](doc/html/)
* [Twitter/GNU Social API Functions](help/api)
**External Resources**

View file

@ -4,7 +4,7 @@ Friendica Message Flow
This page documents some of the details of how messages get from one person to another in the Friendica network.
There are multiple paths, using multiple protocols and message formats.
Those attempting to understand these message flows should become familiar with (at the minimum) the [DFRN protocol document](http://dfrn.org/dfrn.pdf) and the message passing elements of the OStatus stack (salmon and Pubsubhubbub).
Those attempting to understand these message flows should become familiar with (at the minimum) the [DFRN protocol document](https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf) and the message passing elements of the OStatus stack (salmon and Pubsubhubbub).
Most message passing involves the file include/items.php, which has functions for several feed-related import/export activities.
@ -21,8 +21,8 @@ Push (pubsubhubbub) feeds arrive via mod/pubsub.php
DFRN-poll feed imports arrive via include/poller.php as a scheduled task, this implements the local side of the DFRN-poll protocol.
Scenario #1. Bob posts a public status message
---
### Scenario #1. Bob posts a public status message
This is a public message with no conversation members so no private transport is used.
There are two paths it can take - as a bbcode path to DFRN clients, and converted to HTML with the server's PuSH (pubsubhubbub) hubs notified.
When a PuSH hub is operational, dfrn-poll clients prefer to receive their information through the PuSH channel.
@ -30,31 +30,31 @@ They will fall back on a daily poll in case the hub has delivery issues (this is
If there is no specified hub or hubs, DFRN clients will poll at a configurable (per-contact) rate at up to 5-minute intervals.
Feeds retrieved via dfrn-poll are bbcode and may also contain private conversations which the poller has permissions to see.
Scenario #2. Jack replies to Bob's public message. Jack is on the Friendica/DFRN network.
---
### Scenario #2. Jack replies to Bob's public message. Jack is on the Friendica/DFRN network.
Jack uses dfrn-notify to send a direct reply to Bob.
Bob then creates a feed of the conversation and sends it to everybody involved in the conversation using dfrn-notify.
PuSH hubs are notified that new content is available.
The hub or hubs will then retrieve the latest feed and transmit it to all hub subscribers (which may be on different networks).
Scenario #3. Mary replies to Bob's public message. Mary is on the Friendica/DFRN network.
---
### Scenario #3. Mary replies to Bob's public message. Mary is on the Friendica/DFRN network.
Mary uses dfrn-notify to send a direct reply to Bob.
Bob then creates a feed of the conversation and sends it to everybody involved in the conversation (excluding himself, the conversation is now sent to both Jack and Mary).
Messages are sent using dfrn-notify.
Push hubs are also notified that new content is available.
The hub or hubs will then retrieve the latest feed and transmit it to all hub subscribers (which may be on different networks).
Scenario #4. William replies to Bob's public message. William is on the OStatus network.
---
### Scenario #4. William replies to Bob's public message. William is on the OStatus network.
William uses salmon to notify Bob of the reply.
Content is html embedded in salmon magic envelope.
Bob then creates a feed of the conversation and sends it to all Friendica participants involved in the conversation using dfrn-notify (excluding himself, the conversation is sent to both Jack and Mary).
Push hubs are notified that new content is available.
The hub or hubs will then retrieve the latest feed and transmit it to all hub subscribers (which may be on different networks).
Scenario #5. Bob posts a private message to Mary and Jack.
---
### Scenario #5. Bob posts a private message to Mary and Jack.
Message is delivered immediately to Mary and Jack using dfrn_notify.
Public hubs are not notified.
Requeueing is attempted in case of timeout.

42
doc/Protocol.md Normal file
View file

@ -0,0 +1,42 @@
Used Protocols
===============
* [Home](help)
Friendicas DFRN Protocol
---
* [Document with the DFRN specification](spec/dfrn2.pdf)
* [Schema of the contact request process](spec/dfrn2_contact_request.png)
* [Schema of the contact request confirmation](spec/dfrn2_contact_confirmation.png)
* [Description of the message flow](help/Message-Flow)
ActivityStreams
---
Friendica is using ActivityStreams in version 1.0 for its activities and object types.
Additional types are used for non standard activities.
* [Link to the specification](http://activitystrea.ms/head/activity-schema.html)
* [List of used ActivityStreams verbs and object types.](https://github.com/friendica/friendica/wiki/ActivityStreams)
Salmon
---
Salmon is used as a message exchange protocol for replies and mentions.
* [Link to the protocol summary](http://www.salmon-protocol.org/salmon-protocol-summary)
Portable Contacts
---
Portable Contacts is used for friends lists.
* [Link to the specification](https://web.archive.org/web/20160426223008/http://portablecontacts.net/draft-spec.html) (Link to archive.org)
pubsubhubbub
---
pubsubhubbub is used for OStatus.
* [Link to the specification](https://pubsubhubbub.github.io/PubSubHubbub/pubsubhubbub-core-0.4.html)

View file

@ -5,7 +5,7 @@ Using SSL with Friendica
Disclaimer
---
**This document has been updated in November 2015.
**This document has been updated in November 2016.
SSL encryption is relevant for security.
This means that recommended settings change fast.
Keep your setup up to date and do not rely on this document being updated as fast as technologies change!**
@ -40,65 +40,26 @@ If your Friendica instance is running on a shared hosting platform, you should f
They have instructions for you on how to do it there.
You can always order a paid certificate with your provider.
They will either install it for you or provide an easy way to upload the certificate and the key via a web interface.
It might be worth asking if your provider would install a certificate you provide yourself, to save money.
If so, read on.
Getting a free StartSSL certificate
---
StartSSL is a certificate authority that issues certificates for free.
They are valid for a year and are sufficient for our purposes.
### Step 1: Create a client certificate
When you initially sign up with StartSSL, you receive a certificate that is installed in your browser.
You need it for the login on startssl.com, also when coming back to the site later.
It has nothing to do with the SSL certificate for your server.
### Step 2: Validate your email address and your domain
To continue you have to prove that you own the email address you specified and the domain that you want a certificate for.
Specify your email address, request a validation link via email from the "validations wizard".
Same procedure for the domain validation.
### Step 3: Request the certificate
Go to the "certificates wizard".
Choose the target web server.
When you are first prompted for a domain to certify, you need to enter your main domain, e.g. example.com.
In the next step, you will be able to specify a subdomain for Friendica, if needed.
Example: If you have friendica.example.com, you first enter example.com, then specify the subdomain friendica later.
If you know how to generate an openssl key and a certificate signing request (csr) yourself, do so.
Paste the csr into your browser to get it signed by StartSSL.
If you do not know how to generate a key and a csr, accept StartSSL's offer to generate it for you.
This means: StartSSL has the key to your encryption but it is better than no certificate at all.
Download your certificate from the website.
(Or in the second case: Download your certificate and your key.)
To install your certificate on a server, you need one or two extra files: sub.class1.server.ca.pem and ca.pem, delivered by startssl.com
Go to the "Tool box" section and download "Class 1 Intermediate Server CA" and "StartCom Root CA (PEM encoded)".
If you want to send your certificate to your hosting provider, they need the certificate, the key and probably at least the intermediate server CA.
To be sure, send those three and the ca.pem file.
With some providers, you have to send them your certificate.
They need the certificate, the key and the CA's intermediate certificate.
To be sure, send those three files.
**You should send them to your provider via an encrypted channel!**
If you run your own server, upload the files and check out the Mozilla wiki link below.
Let's encrypt
Own server
---
If you run your own server, the "Let's encrypt" initiative might become an interesting alternative.
Their offer is in public beta right now.
Check out [their website](https://letsencrypt.org/) for status updates.
If you run your own server, we recommend to check out the ["Let's Encrypt" initiative](https://letsencrypt.org/).
Not only do they offer free SSL certificates, but also a way to automate their renewal.
You need to install a client software on your server to use it.
Instructions for the official client are [here](https://certbot.eff.org/).
Depending on your needs, you might want to look at the [list of alternative letsencrypt clients](https://letsencrypt.org/docs/client-options/).
Web server settings
---
Visit the [Mozilla's wiki](https://wiki.mozilla.org/Security/Server_Side_TLS) for instructions on how to configure a secure webserver.
They provide recommendations for [different web servers](https://wiki.mozilla.org/Security/Server_Side_TLS#Recommended_Server_Configurations).
They provide recommendations for [different web servers](https://mozilla.github.io/server-side-tls/ssl-config-generator/).
Test your SSL settings
---

View file

@ -1,5 +1,7 @@
# Settings
* [Home](help)
If you are the admin of a Friendica node, you have access to the so called **Admin Panel** where you can configure your Friendica node.
On the front page of the admin panel you will see a summary of information about your node.
@ -9,7 +11,8 @@ This number should decrease quickly.
The second is the messages which could for various reasons not being delivered.
They will be resend later.
You can have a quick glance into that second queus in the "Inspect Queue" section of the admin panel.
If you have activated the background workers, there might be a third number representing the count of jobs queued for the workers.
If you have activated the background workers, there is a third number representing the count of jobs queued for the workers.
These worker tasks are prioritised and are done accordingly.
Then you get an overview of the accounts on your node, which can be moderated in the "Users" section of the panel.
As well as an overview of the currently active addons

View file

@ -8,7 +8,11 @@ Getting started
[Vagrant](https://www.vagrantup.com/) is a virtualization solution for developers.
No need to setup up a webserver, database etc. before actually starting.
Vagrant creates a virtual machine (an Ubuntu 14.04) for you that you can just run inside VirtualBox and start to work directly on Friendica.
Vagrant creates a virtual machine for you that you can just run inside VirtualBox and start to work directly on Friendica.
You can choose between two different Ubuntu Linux versions:
1. Ubuntu Trusty (14.04) with PHP 5.5.9 and MySQL 5.5.53
2. Ubuntu Xenial (16.04) with PHP 7.0 and MySQL 5.7.16
What you need to do:
@ -16,21 +20,27 @@ What you need to do:
Please use an up-to-date vagrant version from https://www.vagrantup.com/downloads.html.
2. Git clone your Friendica repository.
Inside, you'll find a "Vagrantfile" and some scripts in the utils folder.
3. Run "vagrant up" from inside the friendica clone.
3. Choose the Ubuntu version you'll need und run "vagrant up <ubuntu-version>" from inside the friendica clone:
$> vagrant up trusty
$> vagrant up xenial
Be patient: When it runs for the first time, it downloads an Ubuntu Server image.
4. Run "vagrant ssh" to log into the virtual machine to log in to the VM.
5. Open 192.168.22.10 in a browser.
4. Run "vagrant ssh <ubuntu-version>" to log into the virtual machine to log in to the VM:
$> vagrant ssh trusty
$> vagrant ssh xenial
5. Open you test installation in a browser.
If you selected an Ubuntu Trusty go to 192.168.22.10.
If you started a Xenial machine go to 192.168.22.11.
The mysql database is called "friendica", the mysql user and password both are "root".
6. Work on Friendica's code in your git clone on your machine (not in the VM).
Your local working directory is set up as a shared directory with the VM (/vagrant).
7. Check the changes in your browser in the VM.
Debug via the "vagrant ssh" login.
Debug via the "vagrant ssh <ubuntu-version>" login.
Find the Friendica log file /vagrant/logfile.out.
8. Commit and push your changes directly back to Github.
If you want to stop vagrant after finishing your work, run the following command
$> vagrant halt
$> vagrant halt <ubuntu-version>
in the development directory.
@ -44,10 +54,3 @@ You will then have the following accounts to login:
* 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/).
**Important notice:**
If you already had an Ubuntu 12.04 Vagrant VM, please run
$> vagrant destroy
before starting the new 14.04 machine.

View file

@ -1,5 +1,8 @@
Friendica API
===
* [Home](help)
The Friendica API aims to be compatible to the [GNU Social API](http://wiki.gnusocial.de/gnusocial:api) and the [Twitter API](https://dev.twitter.com/rest/public).
Please refer to the linked documentation for further information.
@ -744,6 +747,38 @@ On success:
On error:
* different JSON returns {"result":"error","message":"searchstring not specified"}
---
### friendica/profile/show (GET; AUTH)
show data of all profiles or a single profile of the authenticated user
#### Parameters
* profile_id: id of the profile to be returned (optional, if omitted all profiles are returned by default)
#### Return values
On success: Array of:
* multi_profiles: true if user has activated multi_profiles
* global_dir: URL of the global directory set in server settings
* friendica_owner: user data of the authenticated user
* profiles: array of the profile data
On error:
HTTP 403 Forbidden: when no authentication provided
HTTP 400 Bad Request: if given profile_id is not in db or not assigned to authenticated user
General description of profile data in API returns:
* profile_id
* profile_name
* is_default: true if this is the public profile
* hide_friends: true if friends are hidden
* profile_photo
* profile_thumb
* publish: true if published on the server's local directory
* net_publish: true if published to global_dir
* description ... homepage: different data fields from 'profile' table in database
* users: array with the users allowed to view this profile (empty if is_default=true)
---
## Not Implemented API calls
The following API calls are implemented in GNU Social but not in Friendica: (incomplete)

View file

@ -1,22 +1,24 @@
Table notify
============
| Field | Description | Type | Null | Key | Default | Extra |
| ------ | --------------------------------- | ------------ | ---- | --- | ------------------- | --------------- |
| id | sequential ID | int(11) | NO | PRI | NULL | auto_increment |
| hash | | varchar(64) | NO | | | |
| type | | int(11) | NO | | 0 | |
| name | | varchar(255) | NO | | | |
| url | | varchar(255) | NO | | | |
| photo | | varchar(255) | NO | | | |
| date | | datetime | NO | | 0000-00-00 00:00:00 | |
| msg | | mediumtext | NO | | NULL | |
| uid | user.id of the owner of this data | int(11) | NO | MUL | 0 | |
| link | | varchar(255) | NO | | | |
| parent | | int(11) | NO | | 0 | |
| seen | | tinyint(1) | NO | | 0 | |
| verb | | varchar(255) | NO | | | |
| otype | | varchar(16) | NO | | | |
| iid | item.id | int(11) | NO | | 0 | |
| Field | Description | Type | Null | Key | Default | Extra |
| ---------- | --------------------------------- | ------------ | ---- | --- | ------------------- | --------------- |
| id | sequential ID | int(11) | NO | PRI | NULL | auto_increment |
| hash | | varchar(64) | NO | | | |
| type | | int(11) | NO | | 0 | |
| name | | varchar(255) | NO | | | |
| url | | varchar(255) | NO | | | |
| photo | | varchar(255) | NO | | | |
| date | | datetime | NO | | 0000-00-00 00:00:00 | |
| msg | | mediumtext | YES | | NULL | |
| uid | user.id of the owner of this data | int(11) | NO | MUL | 0 | |
| link | | varchar(255) | NO | | | |
| iid | item.id | int(11) | NO | | 0 | |
| parent | | int(11) | NO | | 0 | |
| seen | | tinyint(1) | NO | | 0 | |
| verb | | varchar(255) | NO | | | |
| otype | | varchar(16) | NO | | | |
| name_cache | Cached bbcode parsing of name | tinytext | YES | | NULL | |
| msg_cache | Cached bbcode parsing of msg | mediumtext | YES | | NULL | |
Return to [database documentation](help/database)

View file

@ -3,201 +3,616 @@ Referenz der Friendica BBCode Tags
* [Zur Startseite der Hilfe](help)
Inline Tags
-----
## Inline
<style>
table.bbcodes {
margin: 1em 0;
background-color: #f9f9f9;
border: 1px solid #aaa;
border-collapse: collapse;
color: #000;
width: 100%;
}
table.bbcodes > tr > th,
table.bbcodes > tr > td,
table.bbcodes > * > tr > th,
table.bbcodes > * > tr > td {
border: 1px solid #aaa;
padding: 0.2em 0.4em
}
table.bbcodes > tr > th,
table.bbcodes > * > tr > th {
background-color: #f2f2f2;
text-align: center;
width: 50%
}
</style>
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>[b]fett[/b]</td>
<td><strong>fett</strong></td>
</tr>
<tr>
<td>[i]kursiv[/i]</td>
<td><em>kursiv</em></td>
</tr>
<tr>
<td>[u]unterstrichen[/u]</td>
<td><u>unterstrichen</u></td>
</tr>
<tr>
<td>[s]durchgestrichen[/s]</td>
<td><strike>durchgestrichen</strike></td>
</tr>
<tr>
<td>[o]&uuml;berstrichen[/o]</td>
<td><span class="overline">&uuml;berstrichen</span></td>
</tr>
<tr>
<td>[color=red]rot[/color]</td>
<td><span style="color: red;">rot</span></td>
</tr>
<tr>
<td>[url=http://www.friendica.com]Friendica[/url]</td>
<td><a href="http://www.friendica.com" target="external-link">Friendica</a></td>
</tr>
<tr>
<td>[img]http://friendica.com/sites/default/files/friendika-32.png[/img]</td>
<td><img src="http://friendica.com/sites/default/files/friendika-32.png" alt="Immagine/foto"></td>
</tr>
<tr>
<td>[img=64x32]http://friendica.com/sites/default/files/friendika-32.png[/img]<br>
<br>Note: provided height is simply discarded.</td>
<td><img src="http://friendica.com/sites/default/files/friendika-32.png" style="width: 64px;"></td>
</tr>
<tr>
<td>[size=xx-small]kleiner Text[/size]</td>
<td><span style="font-size: xx-small;">kleiner Text</span></td>
</tr>
<tr>
<td>[size=xx-large]gro&szlig;er Text[/size]</td>
<td><span style="font-size: xx-large;">gro&szlig;er Text</span></td>
</tr>
<tr>
<td>[size=20]exakte Gr&ouml;&szlig;e[/size] (die Gr&ouml;&szlig;e kann beliebig in Pixeln gew&auml;lt werden)</td>
<td><span style="font-size: 20px;">exakte Gr&ouml;&szlig;e</span></td>
</tr>
<tr>
<td>[font=serif]Serife Schriftart[/font]</td>
<td><span style="font-family: serif;">Serife Schriftart</span></td>
</tr>
</table>
### Links
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>[url]http://friendica.com[/url]</td>
<td><a href="http://friendica.com">http://friendica.com</a></td>
</tr>
<tr>
<td>[url=http://friendica.com]Friendica[/url]</td>
<td><a href="http://friendica.com">Friendica</a></td>
</tr>
<tr>
<td>[bookmark]http://friendica.com[/bookmark]<br><br>
#^[url]http://friendica.com[/url]</td>
<td><span class="oembed link"><h4>Friendica: <a href="http://friendica.com" rel="oembed"></a><a href="http://friendica.com" target="_blank">http://friendica.com</a></h4></span></td>
</tr>
<tr>
<td>[bookmark=http://friendica.com]Lesezeichen[/bookmark]<br><br>
#^[url=http://friendica.com]Lesezeichen[/url]<br><br>
#[url=http://friendica.com]^[/url][url=http://friendica.com]Lesezeichen[/url]</td>
<td><span class="oembed link"><h4>Friendica: <a href="http://friendica.com" rel="oembed"></a><a href="http://friendica.com" target="_blank">Lesezeichen</a></h4></span></td>
</tr>
<tr>
<td>[url=/posts/f16d77b0630f0134740c0cc47a0ea02a]Diaspora Beitrag mit GUID[/url]</td>
<td><a href="/display/f16d77b0630f0134740c0cc47a0ea02a" target="_blank">Diaspora Beitrag mit GUID</a></td>
</tr>
<tr>
<td>#Friendica</td>
<td>#<a href="/search?tag=Friendica">Friendica</a></td>
</tr>
<tr>
<td>@Erw&auml;hnung</td>
<td>@<a href="javascript:void(0)">Erw&auml;hnung</a></td>
</tr>
<tr>
<td>acct:account@friendica.host.com (WebFinger)</td>
<td><a href="/acctlink?addr=account@friendica.host.com" target="extlink">acct:account@friendica.host.com</a></td>
</tr>
<tr>
<td>[mail]user@mail.example.com[/mail]</td>
<td><a href="mailto:user@mail.example.com">user@mail.example.com</a></td>
</tr>
<tr>
<td>[mail=user@mail.example.com]Eine E-Mail senden[/mail]</td>
<td><a href="mailto:user@mail.example.com">Eine E-Mail senden</a></td>
</tr>
</table>
## Blocks
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>[p]Ein Absatz mit Text[/p]</td>
<td><p>Ein Absatz mit Text</p></td>
</tr>
<tr>
<td>Eingebetteter [code]Programmcode[/code] im Text</td>
<td>Eingebetteter <key>Programmcode</key> im Text</td>
</tr>
<tr>
<td>[code]Programmcode<br>&uuml;ber<br>mehrere<br>Zeilen[/code]</td>
<td><code>Programmcode
&uuml;ber
mehrere
Zeilen</code></td>
</tr>
<tr>
<td>[code=php]function text_highlight($s,$lang)[/code]</td>
<td><code><div class="hl-main"><ol class="hl-main"><li><span class="hl-code">&nbsp;</span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">text_highlight</span><span class="hl-brackets">(</span><span class="hl-var">$s</span><span class="hl-code">,</span><span class="hl-var">$lang</span><span class="hl-brackets">)</span></li></ol></div></code></td>
</tr>
<tr>
<td>[quote]Zitat[/quote]</td>
<td><blockquote>Zitat</blockquote></td>
</tr>
<tr>
<td>[quote=Autor]Autor? Ich? Nein, niemals...[/quote]</td>
<td><strong class="Autor">Autor hat geschrieben:</strong><blockquote>Autor? Ich? Nein, niemals...</blockquote></td>
</tr>
<tr>
<td>[center]zentrierter Text[/center]</td>
<td><div style="text-align:center;">zentrierter Text</div></td>
</tr>
<tr>
<td>Du solltest nicht weiter lesen, wenn du das Ende des Films nicht vorher erfahren willst. [spoiler]Es gibt ein Happy End.[/spoiler]</td>
<td>
<div class="wall-item-container">
Du solltest nicht weiter lesen, wenn du das Ende des Films nicht vorher erfahren willst. <br>
<span id="spoiler-wrap-0716e642" class="spoiler-wrap fakelink" onclick="openClose('spoiler-0716e642');">Zum &ouml;ffnen/schlie&szlig;en klicken</span>
<blockquote class="spoiler" id="spoiler-0716e642" style="display: none;">Es gibt ein Happy End.</blockquote>
<div class="body-attach"><div class="clear"></div></div>
</div>
</td>
</tr>
<tr>
<td>[spoiler=Autor]Spoiler Alarm[/spoiler]</td>
<td>
<div class="wall-item-container">
<strong class="spoiler">Autor hat geschrieben</strong><br>
<span id="spoiler-wrap-a893765a" class="spoiler-wrap fakelink" onclick="openClose('spoiler-a893765a');">Zum &ouml;ffnen/schlie&szlig;en klicken</span>
<blockquote class="spoiler" id="spoiler-a893765a" style="display: none;">Spoiler Alarm</blockquote>
<div class="body-attach"><div class="clear"></div></div>
</div>
</td>
</tr>
<tr>
<td>[hr] (horizontale Linie)</td>
<td><hr></td>
</tr>
</table>
### &Uuml;berschriften
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>[h1]Titel 1[/h1]</td>
<td><h1>Titel 1</h1></td>
</tr>
<tr>
<td>[h2]Titel 2[/h2]</td>
<td><h2>Titel 2</h2></td>
</tr>
<tr>
<td>[h3]Titel 3[/h3]</td>
<td><h3>Titel 3</h3></td>
</tr>
<tr>
<td>[h4]Titel 4[/h4]</td>
<td><h4>Titel 4</h4></td>
</tr>
<tr>
<td>[h5]Titel 5[/h5]</td>
<td><h5>Titel 5</h5></td>
</tr>
<tr>
<td>[h6]Titel 6[/h6]</td>
<td><h6>Titel 6</h6></td>
</tr>
</table>
### Tabellen
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>[table]<br>
&nbsp;&nbsp;[tr]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[th]Kopfzeile 1[/th]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[th]Kopfzeile 2[/th]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[th]Kopfzeile 2[/th]<br>
&nbsp;&nbsp;[/tr]<br>
&nbsp;&nbsp;[tr]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Zelle 1[/td]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Zelle 2[/td]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Zelle 3[/td]<br>
&nbsp;&nbsp;[/tr]<br>
&nbsp;&nbsp;[tr]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Zelle 4[/td]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Zelle 5[/td]<br>
&nbsp;&nbsp;&nbsp;&nbsp;[td]Zelle 6[/td]<br>
&nbsp;&nbsp;[/tr]<br>
[/table]</td>
<td>
<table>
<tbody>
<tr>
<th>Kopfzeile 1</th>
<th>Kopfzeile 2</th>
<th>Kopfzeile 3</th>
</tr>
<tr>
<td>Zelle 1</td>
<td>Zelle 2</td>
<td>Zelle 3</td>
</tr>
<tr>
<td>Zelle 4</td>
<td>Zelle 5</td>
<td>Zelle 6</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>[table border=0]</td>
<td>
<table border="0">
<tbody>
<tr>
<th>Kopfzeile 1</th>
<th>Kopfzeile 2</th>
<th>Kopfzeile 3</th>
</tr>
<tr>
<td>Zelle 1</td>
<td>Zelle 2</td>
<td>Zelle 3</td>
</tr>
<tr>
<td>Zelle 4</td>
<td>Zelle 5</td>
<td>Zelle 6</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td>[table border=1]</td>
<td>
<table border="1">
<tbody>
<tr>
<th>Kopfzeile 1</th>
<th>Kopfzeile 2</th>
<th>Kopfzeile 3</th>
</tr>
<tr>
<td>Zelle 1</td>
<td>Zelle 2</td>
<td>Zelle 3</td>
</tr>
<tr>
<td>Zelle 4</td>
<td>Zelle 5</td>
<td>Zelle 6</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
### Listen
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>[ul]<br>
&nbsp;&nbsp;[li] Erstes Listenelement<br>
&nbsp;&nbsp;[li] Zweites Listenelement<br>
[/ul]<br>
[list]<br>
&nbsp;&nbsp;[*] Erstes Listenelement<br>
&nbsp;&nbsp;[*] Zweites Listenelement<br>
[/list]</td>
<td>
<ul class="listbullet" style="list-style-type: circle;">
<li>Erstes Listenelement</li>
<li>Zweites Listenelement</li>
</ul>
</td>
</tr>
<tr>
<td>[ol]<br>
&nbsp;&nbsp;[*] Erstes Listenelement<br>
&nbsp;&nbsp;[*] Zweites Listenelement<br>
[/ol]<br>
[list=1]<br>
&nbsp;&nbsp;[*] Erstes Listenelement<br>
&nbsp;&nbsp;[*] Zweites Listenelement<br>
[/list]</td>
<td>
<ul class="listdecimal" style="list-style-type: decimal;">
<li> Erstes Listenelement</li>
<li> Zweites Listenelement</li>
</ul>
</td>
</tr>
<tr>
<td>[list=]<br>
&nbsp;&nbsp;[*] Erstes Listenelement<br>
&nbsp;&nbsp;[*] Zweites Listenelement<br>
[/list]</td>
<td>
<ul class="listnone" style="list-style-type: none;">
<li> Erstes Listenelement</li>
<li> Zweites Listenelement</li>
</ul>
</td>
</tr>
<tr>
<td>[list=i]<br>
&nbsp;&nbsp;[*] Erstes Listenelement<br>
&nbsp;&nbsp;[*] Zweites Listenelement<br>
[/list]</td>
<td>
<ul class="listlowerroman" style="list-style-type: lower-roman;">
<li> Erstes Listenelement</li>
<li> Zweites Listenelement</li>
</ul>
</td>
</tr>
<tr>
<td>[list=I]<br>
&nbsp;&nbsp;[*] Erstes Listenelement<br>
&nbsp;&nbsp;[*] Zweites Listenelement<br>
[/list]</td>
<td>
<ul class="listupperroman" style="list-style-type: upper-roman;">
<li> Erstes Listenelement</li>
<li> Zweites Listenelement</li>
</ul>
</td>
</tr>
<tr>
<td>[list=a]<br>
&nbsp;&nbsp;[*] Erstes Listenelement<br>
&nbsp;&nbsp;[*] Zweites Listenelement<br>
[/list]</td>
<td>
<ul class="listloweralpha" style="list-style-type: lower-alpha;">
<li> Erstes Listenelement</li>
<li> Zweites Listenelement</li>
</ul>
</td>
</tr>
<tr>
<td>[list=A]<br>
&nbsp;&nbsp;[*] Erstes Listenelement<br>
&nbsp;&nbsp;[*] Zweites Listenelement<br>
[/list]</td>
<td>
<ul class="listupperalpha" style="list-style-type: upper-alpha;">
<li> Erstes Listenelement</li>
<li> Zweites Listenelement</li>
</ul>
</td>
</tr>
</table>
## Einbetten
Du kannst Videos, Musikdateien und weitere Dinge in Beitr&auml;gen einbinden.
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>[video]url[/video]</td>
<td>Wobei die *url* eine URL von youtube, vimeo, soundcloud oder einer anderen Plattform sein kann, die die opengraph Spezifikationen unterst&uuml;tzt.</td>
</tr>
<tr>
<td>[video]URL der Videodatei[/video]
[audio]URL der Musikdatei[/audio]</td>
<td>Die komplette URL einer ogg/ogv/oga/ogm/webm/mp4/mp3 Datei angeben, diese wird dann mit einem HTML5-Player angezeigt.</td>
</tr>
<tr>
<td>[youtube]Youtube URL[/youtube]</td>
<td>Youtube Video mittels OEmbed anzeigen. Kann u.U, den Player nicht einbetten.</td>
</tr>
<tr>
<td>[youtube]Youtube video ID[/youtube]</td>
<td>Youtube-Player im iframe einbinden.</td>
</tr>
<tr>
<td>[vimeo]Vimeo URL[/vimeo]</td>
<td>Vimeo Video mittels OEmbed anzeigen. Kann u.U, den Player nicht einbetten.</td>
</tr>
<tr>
<td>[vimeo]Vimeo video ID[/vimeo]</td>
<td>Vimeo-Player im iframe einbinden.</td>
</tr>
<tr>
<td>[embed]URL[/embed]</td>
<td>OEmbed rich content einbetten.</td>
</tr>
<tr>
<td>[iframe]URL[/iframe]</td>
<td>General embed, iframe size is limited by the theme size for video players.</td>
</tr>
<tr>
<td>[url]*url*[/url]</td>
<td>Wenn *url* die OEmbed- oder Opengraph-Spezifikationen unterst&uuml;tzt, wird das Objekt eingebettet (z.B. Dokumente von scribd).
Ansonsten wird der Titel der Seite mit der URL verlinkt.</td>
</tr>
</table>
## Karten
Das Einbetten von Karten ben&ouml;tigt das "openstreetmap" oder das "Google Maps" Addon.
Wenn keines der Addons aktiv ist, werden stattde&szlig;en die Kordinaten angezeigt-
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>[map]Adresse[/map]</td>
<td>Bindet eine Karte ein, auf der die angegebene Adresse zentriert ist.</td>
</tr>
<tr>
<td>[map=lat,long]</td>
<td>Bindet eine Karte ein, die auf die angegebenen Koordinaten zentriert ist.</td>
</tr>
<tr>
<td>[map]</td>
<td>Bindet eine Karte ein, die auf die Position des Beitrags zentriert ist.</td>
</tr>
</table>
## Zusammenfassungen f&uuml;r lange Beitr&auml;ge
Wenn du deine Beitr&auml;ge auf anderen Netzwerken von Drittanbietern verbreiten m&ouml;chtest, z.B. Twitter, k&ouml;nntest du Probleme mit deren Zeichenbegrenzung haben.
Friendica verwendet einen semi-inelligenten Mechanismus um passende Zusammenfassungen zu erstellen.
Du kannst allerdings auch selbst die Zusammenfassungen erstellen, die auf den unterschiedlichen Netzwerken angezeigt werden.
Um dies zu tun, verwendest du den [abstract]-Tag.
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>[abstract]Unglaublich interessant! Muss man gesehen haben! Unbedingt dem Link folgen![/abstract]<br>
Ich m&ouml;chte euch eine unglaublich langweilige Geschichte erz&auml;hlen, die ihr sicherlich niemals h&ouml;ren wolltet.</td>
<td>Auf Twitter w&uuml;rde folgender Text verlffentlicht werden <blockquote>Unglaublich interessant! Muss man gesehen haben! Unbedingt dem Link folgen!</blockquote>
Wohingegen auf Friendica folgendes stehen w&uuml;rde <blockquote>Ich m&ouml;chte euch eine unglaublich langweilige Geschichte erz&auml;hlen, die ihr sicherlich niemals h&ouml;ren wolltet.</blockquote></td>
</tr>
</table>
Wenn du magst, kannst du auch unterschiedliche Zusammenfassungen f&uuml;r die unterschiedlichen Netzwerke verwenden.
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>
[abstract]Hey Leute, hier sind meines neuesten Bilder![/abstract]<br>
[abstract=twit]Hallo liebe Twitter Follower. Wollt ihr meine neuesten Bilder sehen?[/abstract]<br>
[abstract=apdn]Moin liebe Follower auf ADN. Ich habe einige neue Bilder gemacht, die ich euch gerne zeigen will.[/abstract]<br>
Heute war ich im Wald unterwegs und habe einige wirklich sch&ouml;ne Bilder gemacht...</td>
<td>F&uuml;r Twitter und App.net wird Friendica in diesem Fall die speziell definierten Zusammenfassungen Verwenden. F&uuml;r andere Netzwerke (wie z.B. bei der Verwendung des GNU Social Konnektors zum Ver&ouml;ffentlichen auf deinen GNU Social Account) w&uuml;rde die allgemeine Zusammenfassung verwenden.</td>
</tr>
</table>
Wenn du beispielsweise den "buffer"-Konnektor verwendest um Beitr&auml;ge nach Facebook und Google+ zu senden, dort aber nicht den gesamten Blogbeitrag posten willst sondern nur einen Anrei&szlig;er, kannst du dies mit dem [abstract]-Tag realisieren.
Bei Netzwerken wie Facebook oder Google+, die selbst kein Zeichenlimit haben wird das [abstract]-Element allerdings nicht grunds&auml;tzlich verwendet.
Daher m&uuml;ssen diese Netzwerke explizit genannt werden.
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>
[abstract]Dieser Tage hatte ich eine ungew&ouml;hnliche Begegnung...[/abstract]<br>
[abstract=goog]Hey liebe Google+ Follower. Habt ich schon meinen neuesten Blog-Beitrag gelesen?[/abstract]<br>
[abstract=face]Hallo liebe Facebook Freunde. Letztens ist mir etwas wirklich sch&ouml;nes pa&szlig;iert.[/abstract]<br>
Als ich die Bilder im Wald aufgenommen habe, hatte ich eine wirklich ungew&ouml;hnliche Begegnung...</td>
<td>Auf Google und Facebook w&uuml;rde nun die entsprechende Zusammenfassung verbreitet. F&uuml;r andere Netzwerke w&uuml;rde die allgemeine Zusammenfassung verwendet werden.<br>
<br>Auf Friendica wird weiterhin keine Zusammenfassung angezeigt.</td>
</tr>
</table>
F&uuml;r Verbindungen zu Netzwerken, zu denen Friendica den HTML Code postet, wie Tumblr, Wordpress oder Pump.io wird das [abstract] Element nicht verwendet.
Bei nativen Verbindungen; das hei&szlig;t zu z.B. Friendica, Hubzilla, Diaspora oder GNU Social Kontakten; wird der ungek&uuml;rzte Beitrag &uuml;bertragen.
Die Instanz des Kontakts k&uuml;mmert sich um die Darstellung.
## Special
<table class="bbcodes">
<tr>
<th>BBCode</th>
<th>Ergebnis</th>
</tr>
<tr>
<td>Wenn du verhindern m&ouml;chtest, da&szlig; der BBCode in einer Nachricht interpretiert wird, kannst du die [noparse], [nobb] oder [pre] Tag verwenden:<br>
<ul>
<li>[noparse][b]fett[/b][/noparse]</li>
<li>[nobb][b]fett[/b][/nobb]</li>
<li>[pre][b]fett[/b][/pre]</li>
</ul>
</td>
<td>[b]fett[/b]</td>
</tr>
<tr>
<td>[nosmile] kann verwendet werden um f&uuml;r einen Beitrag das umsetzen von Smilies zu verhindern.<br>
<br>
[nosmile] ;-) :-O
</td>
<td>;-) :-O</td>
</tr>
<tr>
<td>Benutzerdefinierte Inline-Styles<br>
<br>
[style=text-shadow: 0 0 4px #CC0000;]Du kannst alle CSS-Eigenschaften eines Blocks &auml;ndern-[/style]</td>
<td><span style="text-shadow: 0 0 4px #cc0000;;">Du kannst alle CSS-Eigenschaften eines Blocks &auml;ndern-</span></td>
</tr>
<tr>
<td>Benutzerdefinierte CSS Klassen<br>
<br>
[class=custom]Wenn die vergebene Klasse in den CSS Anweisungen existiert, wird sie angewandt.[/class]</td>
<td><pre>&lt;span class="custom"&gt;Wenn die<br>
vergebene Klasse in den CSS Anweisungen<br>
existiert,wird sie angewandt.&lt;/span&gt;</pre></td>
</tr>
</table>
<pre>[b]fett[/b]</pre> : <strong>fett</strong>
<pre>[i]kursiv[/i]</pre> : <em>kursiv</em>
<pre>[u]unterstrichen[/u]</pre> : <u>unterstrichen</u>
<pre>[s]durchgestrichen[/s]</pre> : <strike>durchgestrichen</strike>
<pre>[color=red]rot[/color]</pre> : <span style="color: red;">rot</span>
<pre>[url=http://www.friendica.com]Friendica[/url]</pre> : <a href="http://www.friendica.com" target="external-link">Friendica</a>
<pre>[img]http://friendica.com/sites/default/files/friendika-32.png[/img]</pre> : <img src="http://friendica.com/sites/default/files/friendika-32.png" alt="Immagine/foto">
<pre>[size=xx-small]kleiner Text[/size]</pre> : <span style="font-size: xx-small;">kleiner Text</span>
<pre>[size=xx-large]gro&szlig; Text[/size]</pre> : <span style="font-size: xx-large;">gro&szlig;er Text</span>
<pre>[size=20]exakte Textgr&ouml;&szlig;e[/size] (Textgr&ouml;&szlig;e kann jede Zahl sein, in Pixeln)</pre> : <span style="font-size: 20px;">exakte Gr&ouml;&szlig;e</span>
Block Tags
-----
<pre>[code]Code[/code]</pre>
<code>Code</code>
<p style="clear:both;">&nbsp;</p>
<pre>[code=php]function text_highlight($s,$lang)[/code]</pre>
<code><div class="hl-main"><ol class="hl-main"><li><span class="hl-code">&nbsp;</span><span class="hl-reserved">function</span><span class="hl-code"> </span><span class="hl-identifier">text_highlight</span><span class="hl-brackets">(</span><span class="hl-var">$s</span><span class="hl-code">,</span><span class="hl-var">$lang</span><span class="hl-brackets">)</span></li></ol></div></code>
<p style="clear:both;">&nbsp;</p>
<pre>[quote]Zitat[/quote]</pre>
<blockquote>Zitat</blockquote>
<p style="clear:both;">&nbsp;</p>
<pre>[quote=Autor]Der Autor? Ich? Nein, nein, nein...[/quote]</pre>
<strong class="author">Autor hat geschrieben:</strong><blockquote>Der Autor? Ich? Nein, nein, nein...</blockquote>
<p style="clear:both;">&nbsp;</p>
<pre>[center]zentrierter Text[/center]</pre>
<div style="text-align:center;">zentrierter Text</div>
<p style="clear:both;">&nbsp;</p>
<pre>Wer überrascht werden möchte sollte nicht weiter lesen.[spoiler]Es gibt ein Happy End.[/spoiler]</pre>
Wer überrascht werden möchte sollte nicht weiter lesen.<br />*klicken zum öffnen/schließen*
(Der Text zweischen dem öffnenden und dem schließenden Teil des spoiler Tags wird nicht angezeigt, bis der Link angeklickt wurde. In dem Fall wird *"Es gibt ein Happy End."* also erst angezeigt, wenn der Spoiler verraten wird.)
<p style="clear:both;">&nbsp;</p>
**Tabelle**
<pre>[table border=1]
[tr]
[th]Tabellenzeile[/th]
[/tr]
[tr]
[td]haben &Uuml;berschriften[/td]
[/tr]
[/table]</pre>
<table border="1"><tbody><tr><th>Tabellenzeile</th></tr><tr><td>haben &Uuml;berschriften</td></tr></tbody></table>
<p style="clear:both;">&nbsp;</p>
**Listen**
<pre>[list]
[*] Erstes Listenelement
[*] Zweites Listenelement
[/list]</pre>
<ul class="listbullet" style="list-style-type: circle;">
<li> Erstes Listenelement<br>
</li>
<li> Zweites Listenelement</li>
</ul>
[list] ist Equivalent zu [ul] (unsortierte Liste).
[ol] kann anstelle von [list] verwendet werden um eine sortierte Liste zu erzeugen:
<pre>[ol]
[*] Erstes Listenelement
[*] Zweites Listenelement
[/ol]</pre>
<ul class="listdecimal" style="list-style-type: decimal;"><li>Erstes Listenelement<br></li><li> Zweites Listenelement</li></ul>
F&uuml;r weitere Optionen von sortierten Listen kann man den Stil der Numerierung der Liste definieren:
<pre>[list=1]</pre> : dezimal
<pre>[list=i]</pre> : r&ouml;misch, Kleinbuchstaben
<pre>[list=I]</pre> : r&ouml;misch, Gro&szlig;buchstaben
<pre>[list=a]</pre> : alphabetisch, Kleinbuchstaben
<pre>[list=A] </pre> : alphabethisch, Gro&szlig;buchstaben
Einbettung von Inhalten
------
Man kann viele Dinge, z.B. Video und Audio Dateine, in Nachrichten einbetten.
<pre>[video]url[/video]</pre>
<pre>[audio]url[/audio]</pre>
Wobei die *url* von youtube, vimeo, soundcloud oder einer anderen Seite stammen kann die die oembed oder opengraph Spezifikationen unterst&uuml;tzt.
Au&szlig;erdem kann *url* die genaue url zu einer ogg Datei sein, die dann per HTML5 eingebunden wird.
<pre>[url]*url*[/url]</pre>
Wenn *url* entweder oembed oder opengraph unterstützt wird das eingebettete Objekt (z.B. ein Dokument von scribd) eingebunden.
Der Titel der Seite mit einem Link zur *url* wird ebenfalls angezeigt.
Um eine Karte in einen Beitrag einzubinden, muss das *openstreetmap* Addon aktiviert werden. Ist dies der Fall, kann mit
<pre>[map]Broadway 26, New York[/map]</pre>
eine Karte von [OpenStreetmap](http://openstreetmap.org) eingebettet werden. Zur Identifikation des Ortes können entweder seine Koordinaten in der Form
<pre>[map=lat,long]</pre>
oder eine Adresse in obiger Form verwendet werden.
Zusammenfassung für längere Beiträge
------------------------------------
Wenn man seine Beiträge über mehrere Netzwerke verbreiten möchte, hat man häufig das Problem, dass diese Netzwerke z.B. eine Längenbeschränkung haben.
(Z.B. Twitter).
Friendica benutzt zum Erzeugen eines Anreißtextes eine halbwegs intelligente Logik.
Es kann aber dennoch von Interesse sein, eine eigene Zusammenfassung zu erstellen, die nur auf dem Fremdnetzwerk dargestellt wird.
Dies geschieht mit dem [abstract]-Element.
Beispiel:
<pre>[abstract]Total spannend! Unbedingt diesen Link anklicken![/abstract]
Hier erzähle ich euch eine total langweilige Geschichte, die ihr noch
nie hören wolltet.</pre>
Auf Twitter würde das "Total spannend! Unbedingt diesen Link anklicken!" stehen, auf Friendica würde nur der Text nach "Hier erzähle ..." erscheinen.
Es ist sogar möglich, für einzelne Netzwerke eigene Zusammenfassungen zu erstellen:
<pre>
[abstract]Hallo Leute, hier meine neuesten Bilder![abstract]
[abstract=twit]Hallo Twitter-User, hier meine neuesten Bilder![abstract]
[abstract=apdn]Hallo App.net-User, hier meine neuesten Bilder![abstract]
Ich war heute wieder im Wald unterwegs und habe tolle Bilder geschossen ...
</pre>
Für Twitter und App.net nimmt das System die entsprechenden Texte.
Bei anderen Netzwerken, bei denen der Inhalt gekürzt wird (z.B. beim "statusnet"-Connector, der für das Posten nach GNU Social verwendet wird) wird dann die Zusammenfassung unter [abstract] verwendet.
Wenn man z.B. den "buffer"-Connector verwendet, um nach Facebook oder Google+ zu posten, kann man dieses Element ebenfalls verwenden, wenn man z.B. einen längeren Blogbeitrag erstellt hat, aber ihn nicht komplett in diese Netzwerke posten möchte.
Netzwerke wie Facebook oder Google+ sind nicht in der Postinglänge beschränkt.
Aus diesem Grund greift nicht die [abstract]-Zusammenfassung. Stattdessen muss man das Netzwerk explizit angeben:
<pre>
[abstract]Ich habe neulich wieder etwas erlebt, was ich euch mitteilen möchte.[abstract]
[abstract=goog]Hallo meine Google+-Kreislinge. Ich habe neulich wieder
etwas erlebt, was ich euch mitteilen möchte.[abstract]
[abstract=face]Hallo Facebook-Freunde! Ich habe neulich wieder etwas
erlebt, was ich euch mitteilen möchte.[abstract]
Beim Bildermachen im Wald habe ich neulich eine interessante Person
getroffen ... </pre>
Das [abstract]-Element greift nicht bei der nativen OStatus-Verbindung oder bei Connectoren, die den HTML-Text posten wie z.B. die Connectoren zu Tumblr, Wordpress oder Pump.io.
Spezielle Tags
-------
Wenn Du &uuml;ber BBCode Tags in einer Nachricht schreiben m&ouml;chtest, kannst Du [noparse], [nobb] oder [pre] verwenden um den BBCode Tags vor der Evaluierung zu sch&uuml;tzen:
<pre>[noparse][b]fett[/b][/noparse]</pre> : [b]fett[/b]

View file

@ -6,7 +6,7 @@ Bugs und Probleme
Du solltest jeden Bug und jedes Problem, den/das Du findest, zunächst dem Administrator (oder gegebenenfalls der Support-Seite) Deines Servers melden, statt auf der allgemeinen Bug-Seite.
Das erleichtert den Entwicklern ihre Arbeit (z. B. neue Features zu entwickeln), da sie sich nicht mit Fehlern beschäftigen müssen, mit denen sie nichts zu tun haben.
Wenn Du technisch versiert bist oder Dein Knoten keine Support-Seite hat, dann kannst Du den <a href="http://bugs.friendica.com/">Bug Tracker</a> nutzen.
Wenn Du technisch versiert bist oder Dein Knoten keine Support-Seite hat, dann kannst Du den <a href="https://github.com/friendica/friendica/issues">Bug Tracker</a> nutzen.
Bitte durchsuche zunächst die Seite, ob es bereits einen offenen Bug gibt, der Deiner Anfrage entspricht.
Liefere so viele Informationen wie möglich zu dem Bug.

View file

@ -20,38 +20,37 @@ Friendica - Dokumentation und Ressourcen
* [Community-Foren](help/Forums)
* [Chats](help/Chats)
* Weiterführende Informationen
* [Performance verbessern](help/Improve-Performance)
* [Account umziehen](help/Move-Account)
* [Account löschen](help/Remove-Account)
* [Bugs und Probleme](help/Bugs-and-Issues)
* [Häufig gestellte Fragen (FAQ)](help/FAQ)
**Technische Dokumentation**
**Dokumentation für Administratoren**
* [Installation](help/Install)
* [Konfigurationen & Admin-Panel](help/Settings)
* [Plugins](help/Plugins)
* [Konnektoren (Connectors) installieren (Twitter/GNU Social)](help/Installing-Connectors)
* [Installation eines ejabberd Servers (XMPP-Chat) mit synchronisierten Anmeldedaten](help/install-ejabberd) (EN)
* [Nachrichtenfluss](help/Message-Flow)
* [Betreibe deine Seite mit einem SSL-Zertifikat](help/SSL)
* [Entwickler](help/Developers)
* [Twitter/GNU Social API Functions](help/api) (EN)
* [Translation of Friendica](help/translations) (EN)
* [Konfigurationswerte, die nur in der .htconfig.php gesetzt werden können](help/htconfig) (EN)
* [Performance verbessern](help/Improve-Performance)
**Entwickler Dokumentation**
**Dokumentation für Entwickler**
* [Where to get started?](help/Developers-Intro)
* [Entwickler](help/Developers)
* [Where to get started?](help/Developers-Intro) (EN)
* [Help on Github](help/Github)
* [Help on Vagrant](help/Vagrant)
* [How to translate Friendica](help/translations)
* [How to translate Friendica](help/translations) (EN)
* [Bugs and Issues](help/Bugs-and-Issues)
* [Plugin Development](help/Plugins)
* [Theme Development](help/themes)
* [Smarty 3 Templates](help/smarty3-templates)
* [Protokoll Dokumentation](help/Protocol) (EN)
* [Datenbank-Schema](help/database)
* [Code-Referenz (mit doxygen generiert - setzt Cookies)](doc/html/)
* [Twitter/GNU Social API Functions](help/api) (EN)
**Externe Ressourcen**

View file

@ -6,7 +6,7 @@ Friendica Nachrichtenfluss
Diese Seite soll einige Infos darüber dokumentieren, wie Nachrichten innerhalb von Friendica von einer Person zur anderen übertragen werden.
Es gibt verschiedene Pfade, die verschiedene Protokolle und Nachrichtenformate nutzen.
Diejenigen, die den Nachrichtenfluss genauer verstehen wollen, sollten sich mindestens mit dem DFRN-Protokoll (http://dfrn.org/dfrn.pdf) und den Elementen zur Nachrichtenverarbeitung des OStatus Stack informieren (salmon und Pubsubhubbub).
Diejenigen, die den Nachrichtenfluss genauer verstehen wollen, sollten sich mindestens mit dem DFRN-Protokoll ([Dokument mit den DFRN Spezifikationen](https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf)) und den Elementen zur Nachrichtenverarbeitung des OStatus Stack informieren (salmon und Pubsubhubbub).
Der Großteil der Nachrichtenverarbeitung nutzt die Datei include/items.php, welche Funktionen für verschiedene Feed-bezogene Import-/Exportaktivitäten liefert.
@ -24,7 +24,7 @@ PuSh-Feeds (pubsubhubbub) kommen via mod/pubsub.php an.
DFRN-poll Feed-Imports kommen via include/poller.php als geplanter Task an, das implementiert die lokale Bearbeitung (local side) des DFRN-Protokolls.
Szenario #1. Bob schreibt eine öffentliche Statusnachricht
### Szenario #1. Bob schreibt eine öffentliche Statusnachricht
Dies ist eine öffentliche Nachricht ohne begrenzte Nutzerfreigabe, so dass keine private Übertragung notwendig ist.
Es gibt zwei Wege, die genutzt werden können - als bbcode an DFRN-Clients oder als durch den Server konvertierten HTML-Code (mit PuSH; pubsubhubbub).
@ -33,13 +33,13 @@ Sie fallen zurück auf eine tägliche Abfrage, wenn der Hub Übertragungsschwier
Wenn kein spezifizierter Hub oder Hubs ausgewählt sind, werden DFRN-Clients in einer pro Kontakt konfigurierbaren Rate mit bis zu 5-Minuten-Intervallen abfragen.
Feeds, die via DFRN-Poll abgerufen werden, sind bbcode und können auch private Unterhaltungen enthalten, die vom Poller auf ihre Zugriffsrechte hin geprüft werden.
Szenario #2. Jack antwortet auf Bobs öffentliche Nachricht. Jack ist im Friendica/DFRN-Netzwerk.
### Szenario #2. Jack antwortet auf Bobs öffentliche Nachricht. Jack ist im Friendica/DFRN-Netzwerk.
Jack nutzt dfrn-notify, um eine direkte Antwort an Bob zu schicken.
Bob erstellt dann einen Feed der Unterhaltung und sendet diesen an jeden, der an der Unterhaltung beteiligt ist und dfrn-notify nutzt.
Die PuSH-Hubs werden darüber informiert, dass neuer Inhalt verfügbar ist. Der/die Hub/s erhalten dann die neuesten Feeds und übertragen diese an alle Hub-Teilnehmer (die auch zu verschiedenen Netzwerken gehören können).
Szenario #3. Mary antwortet auf Bobs öffentliche Nachricht. Mary ist im Friendica/DFRN-Netzwerk.
### Szenario #3. Mary antwortet auf Bobs öffentliche Nachricht. Mary ist im Friendica/DFRN-Netzwerk.
Mary nutzt dfrn-notify, um eine direkte Antwort an Bob zu schicken.
Bob erstellt dann einen Feed der Unterhaltung und sendet diesen an jeden, der an der Unterhaltung beteiligt ist (mit Ausnahme von Bob selbst; die Unterhaltung wird nun an Jack und Mary geschickt).
@ -47,14 +47,14 @@ Die Nachrichten werden mit dfrn-notify übertragen.
PuSH-Hubs werden darüber informiert, dass neuer Inhalt verfügbar ist.
Der/die Hub/s erhalten dann die neuesten Feeds und übertragen sie an alle Hub-Teilnehmer (die auch zu verschiedenen Netzwerken gehören können).
Szenario #4. William antwortet auf Bobs öffentliche Nachricht. William ist in einem OStatus-Netzwerk.
### Szenario #4. William antwortet auf Bobs öffentliche Nachricht. William ist in einem OStatus-Netzwerk.
William nutzt salmon, um Bob über seine Antwort zu benachrichtigen.
Der Inhalt ist HTML-Code, der in das Salmon Magic Envelope eingebettet ist.
Bob erstellt dann einen Feed der Unterhaltung und sendet es an alle Friendica-Nutzer, die an der Unterhaltung beteiligt sind und dfrn-notify nutzen (mit Ausnahme von William selbst; die Unterhaltung wird an Jack und Mary weitergeleitet).
PuSH-Hubs werden darüber informiert, dass neuer Inhalt verfügbar ist. Der/die Hub/s erhalten dann die neuesten Feeds und übertragen sie an alle Hub-Teilnehmer (die auch zu verschiedenen Netzwerken gehören können).
Szenario #5. Bob schreibt eine private Nachricht an Mary und Jack.
### Szenario #5. Bob schreibt eine private Nachricht an Mary und Jack.
Die Nachricht wird sofort an Mary und Jack mit Hilfe von dfrn_notify geschickt.
Öffentliche Hubs werden nicht benachrichtigt.

View file

@ -5,7 +5,7 @@ Friendica mit SSL nutzen
Disclaimer
---
**Dieses Dokument wurde im November 2015 aktualisiert.
**Dieses Dokument wurde im November 2016 aktualisiert.
SSL-Verschlüsselung ist sicherheitskritisch.
Das bedeutet, dass sich die empfohlenen Einstellungen schnell verändern.
Halte deine Installation auf dem aktuellen Stand und verlasse dich nicht darauf, dass dieses Dokument genau so schnell aktualisiert wird, wie sich Technologien verändern!**
@ -45,55 +45,15 @@ Sie installieren es für dich oder haben in der Weboberfläche eine einfache Upl
Um Geld zu sparen, kann es sich lohnen, dort auch nachzufragen, ob sie ein anderes Zertifikat, das du selbst beschaffst, für dich installieren würden.
Wenn ja, dann lies weiter.
Ein kostenloses StartSSL-Zertifikat besorgen
---
StartSSL ist eine Zertifizierungsstelle, die kostenlose Zertifikate ausstellt.
Sie sind für ein Jahr gültig und genügen für unsere Zwecke.
### Schritt 1: Client-Zertifikat erstellen
Wenn du dich erstmalig bei StartSSL anmeldest, erhältst du ein Zertifikat, das in deinem Browser installiert wird.
Du brauchst es, um dich bei StartSSL einzuloggen, auch wenn du später wiederkommst.
Dieses Client-Zertifikat hat nichts mit dem SSL-Zertifikat für deinen Server zu tun.
### Schritt 2: Email-Adresse und Domain validieren
Um fortzufahren musst du beweisen, dass du die Email-Adresse, die du angegeben hast, und die Domain, für die du das Zertifikat möchtest, besitzt.
Gehe in den "Validation wizard" und fordere einen Bestätigungslink per Mail an.
Dasselbe machst du auch für die Validierung der Domain.
### Schritt 3: Das Zertifikat bestellen
Gehe in den "Certificate wizard".
Wähle das Target Webserver.
Bei der ersten Abfrage der Domain gibst du deine Hauptdomain an.
Im nächsten Schritt kannst du eine Subdomain hinzufügen.
Ein Beispiel: Wenn die Adresse der Friendica-Instanz friendica.beispiel.net lautet, gibst du zuerst beispiel.net an und danach friendica.
Wenn du weißt, wie man einen openssl-Schlüssel und einen Certificate Signing Request (CSR) erstellt, tu das.
Kopiere den CSR in den Browser, um ihn von StartSSL signiert zu bekommen.
Wenn du nicht weißt, wie man Schlüssel und CSR erzeugt, nimm das Angebot von StartSSL an, beides für dich zu generieren.
Das bedeutet: StartSSL hat den Schlüssel zu deiner SSL-Verschlüsselung, aber das ist immer noch besser als gar kein Zertifikat.
Lade dein Zertifikat von der Website herunter.
(Oder im zweiten Fall: Lade Zertifikat und Schlüssel herunter.)
Um dein Zertifikat auf einem Webserver zu installieren, brauchst du noch ein oder zwei andere Dateien: sub.class1.server.ca.pem und ca.pem, auch von StartSSL.
Gehe in die Rubrik "Tool box" und lade "Class 1 Intermediate Server CA" und "StartCom Root CA (PEM encoded)" herunter.
Wenn du dein Zertifikat zu deinem Hosting-Provider schicken möchtest, brauchen Sie mindestens Zertifikat und Schlüssel.
Schick zur Sicherheit alle vier Dateien hin.
**Du solltest sie auf einem verschlüsselten Weg hinschicken!**
Wenn du deinen eigenen Server betreibst, lade die Dateien hoch und besuche das Mozilla-Wiki (Link unten).
Let's encrypt
---
Wenn du einen eigenen Server betreibst und den Nameserver kontrollierst, könnte auch die Initiative "Let's encrypt" interessant für dich werden.
Momentan ist deren Angebot noch nicht fertig.
Auf der [Website](https://letsencrypt.org/) kannst du dich über den Stand informieren.
Sie bietet nicht nur freie SSL Zertifikate sondern auch einen automatisierten Prozess zum Erneuern der Zertifikate.
Um letsencrypt Zertifikate verwenden zu können, musst du dir einen Client auf deinem Server installieren.
Eine Anleitung zum offiziellen Client findet du [hier](https://certbot.eff.org/).
Falls du dir andere Clients anschauen willst, kannst du einen Blick in diese [Liste von alternativen letsencrypt Clients](https://letsencrypt.org/docs/client-options/).
Webserver-Einstellungen
---

View file

@ -1,5 +1,7 @@
# Settings
* [Zur Startseite der Hilfe](help)
Wenn du der Administrator einer Friendica Instanz bist, hast du Zugriff auf das so genannte **Admin Panel** in dem du die Friendica Instanz konfigurieren kannst,
Auf der Startseite des Admin Panels werden die Informationen zu der Instanz zusammengefasst.
@ -9,8 +11,9 @@ Diese Zahl sollte sich relativ schnell sinken.
Die zweite 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.
Solltest du für die Hintergrundprozesse die Worker aktiviert haben, könntest du eine dritte Zahl angezeigt bekommen.
Solltest du für die Hintergrundprozesse die Worker aktiviert haben, wird eine dritte Zahl angezeigt.
Diese repräsentiert die Anzahl der Aufgaben, die die Worker noch vor sich haben.
Die Aufgaben der Worker sind priorisiert und werden anhand dieser Prioritäten abgearbeitet.
Des weiteren findest du eine Übersicht über die Accounts auf dem Friendica Knoten, die unter dem Punkt "Nutzer" moderiert werden können.
Sowie eine Liste der derzeit aktivierten Addons.

View file

@ -52,6 +52,8 @@ Cleanzero <img src="doc/img/editor_zero.png" alt="cleanzero.png" style="padding
Darkbubble <img src="doc/img/editor_darkbubble.png" alt="darkbubble.png" style="padding-left: 14px; vertical-align:middle;"> <i>(inkl. smoothly, testbubble)</i>
Frio <img src="doc/img/editor_frio.png" alt="frio.png" style="padding-left: 44px; vertical-align:middle;">
Frost <img src="doc/img/editor_frost.png" alt="frost.png" style="padding-left: 42px; vertical-align:middle;">
Vier <img src="doc/img/editor_vier.png" alt="vier.png" style="padding-left: 44px; vertical-align:middle;"> <i>(inkl. dispy)</i>

View file

@ -1,102 +1,105 @@
Config values that can only be set in .htconfig.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.
* [Home](help)
**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.
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.
The header of the section describes the category, the value is the parameter. Example: To set the directory value please add this
line to your .htconfig.php:
**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 directory value please add this line to your .htconfig.php:
$a->config['system']['directory'] = 'http://dir.friendi.ca';
## jabber ##
* **debug** (Boolean) - Enable debug level for the jabber account synchronisation.
* **logfile** - Logfile for the jabber account synchronisation.
## system ##
## Jabber ##
* debug (Boolean) - Enable debug level for the jabber account synchronisation.
* logfile - Logfile for the jabber account synchronisation.
## System ##
* birthday_input_format - Default value is "ymd".
* block_local_dir (Boolean) - Blocks the access to the directory of the local users.
* default_service_class -
* delivery_batch_count - Number of deliveries per process. Default value is 1. (Disabled when using the worker)
* diaspora_test (Boolean) - For development only. Disables the message transfer.
* directory - The path to global directory. If not set then "http://dir.friendi.ca" is used.
* 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.
* event_input_format - Default value is "ymd".
* ignore_cache (Boolean) - For development only. Disables the item cache.
* 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 the search for not logged in users to prevent crawlers from blocking your system.
* max_connections - The poller process isn't started when the maximum level of the possible database connections are used. 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 poller 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.
* no_oembed (Boolean) - Don't use OEmbed to fetch more information about a link.
* no_oembed_rich_content (Boolean) - Don't show the rich content (e.g. embedded PDF).
* 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.
* 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.
* relay_server - Experimental Diaspora feature. Address of the relay server where public posts should be send to. For example https://podrelay.net
* relay_subscribe (Boolean) - Enables the receiving of public posts from the relay. They will be included in the search and on the community page when it is set up to show all public items.
* relay_scope - Can be "all" or "tags". "all" means that every public post should be received. "tags" means that only posts with selected tags should be received.
* relay_server_tags - Comma separated list of tags for the "tags" subscription (see "relay_scrope")
* relay_user_tags (Boolean) - If enabled, the tags from the saved searches will used for the "tags" subscription in addition to the "relay_server_tags".
* 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.
* 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.
* **allowed_link_protocols** (Array) - Allowed protocols in links URLs, add at your own risk. http is always allowed.
* **birthday_input_format** - Default value is "ymd".
* **block_local_dir** (Boolean) - Blocks the access to the directory of the local users.
* **default_service_class** -
* **delivery_batch_count** - Number of deliveries per process. Default value is 1. (Disabled when using the worker)
* **diaspora_test** (Boolean) - For development only. Disables the message transfer.
* **directory** - The path to global directory. If not set then "http://dir.friendi.ca" is used.
* **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.
* **event_input_format** - Default value is "ymd".
* **frontend_worker_timeout** - Value in minutes after we think that a frontend task was killed by the webserver. Default value is 10.
* **ignore_cache** (Boolean) - For development only. Disables the item cache.
* **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 the search for not logged in users to prevent crawlers from blocking your system.
* **max_connections** - The poller process isn't started when the maximum level of the possible database connections are used. 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 poller 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.
* **memcache** (Boolean) - Use memcache. To use memcache the PECL extension "memcache" has to be installed and activated.
* **memcache_host** - Hostname of the memcache daemon. Default is '127.0.0.1'.
* **memcache_port** - Portnumberof the memcache daemon. Default is 11211.
* **no_oembed** (Boolean) - Don't use OEmbed to fetch more information about a link.
* **no_oembed_rich_content** (Boolean) - Don't show the rich content (e.g. embedded PDF).
* **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.
* **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.
* **relay_server** - Experimental Diaspora feature. Address of the relay server where public posts should be send to. For example https://podrelay.net
* **relay_subscribe** (Boolean) - Enables the receiving of public posts from the relay. They will be included in the search and on the community page when it is set up to show all public items.
* **relay_scope** - Can be "all" or "tags". "all" means that every public post should be received. "tags" means that only posts with selected tags should be received.
* **relay_server_tags** - Comma separated list of tags for the "tags" subscription (see "relay_scrope")
* **relay_user_tags** (Boolean) - If enabled, the tags from the saved searches will used for the "tags" subscription in addition to the "relay_server_tags".
* **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.
* **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.
## service_class ##
* upgrade_link -
* **upgrade_link** -
## experimentals ##
* exp_themes (Boolean) - Show experimental themes as well.
* **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
* **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
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.
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.
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";

BIN
doc/img/editor_frio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -1,6 +1,8 @@
Friendica translations
======================
* [Home](help)
Translation Process
-------------------
@ -24,12 +26,12 @@ If you want to get your work into the source tree yourself, feel free to do so a
The process is simple and friendica ships with all the tools necessary.
The location of the translated files in the source tree is
/view/LNG-CODE/
/view/lang/LNG-CODE/
where LNG-CODE is the language code used, e.g. de for German or fr for French.
The translated strings come as a "message.po" file from transifex which needs to be translated into the PHP file friendica uses.
To do so, place the file in the directory mentioned above and use the "po2php" utility from the util directory of your friendica installation.
Assuming you want to convert the German localization which is placed in view/de/message.po you would do the following.
Assuming you want to convert the German localization which is placed in view/lang/de/message.po you would do the following.
1. Navigate at the command prompt to the base directory of your
friendica installation
@ -37,9 +39,9 @@ Assuming you want to convert the German localization which is placed in view/de/
2. Execute the po2php script, which will place the translation
in the strings.php file that is used by friendica.
$> php util/po2php.php view/de/messages.po
$> php util/po2php.php view/lang/de/messages.po
The output of the script will be placed at view/de/strings.php where
The output of the script will be placed at view/lang/de/strings.php where
friendica is expecting it, so you can test your translation immediately.
3. Visit your friendica page to check if it still works in the language you
@ -50,7 +52,7 @@ Assuming you want to convert the German localization which is placed in view/de/
not give any output if the file is ok but might give a hint for
searching the bug in the file.
$> php view/de/strings.php
$> php view/lang/de/strings.php
4. commit the two files with a meaningful commit message to your git
repository, push it to your fork of the friendica repository at github and

34
doc/upgrade.md Normal file
View file

@ -0,0 +1,34 @@
# Considerations before upgrading Friendica
* [Home](help)
## MySQL >= 5.7.4
Starting from MySQL version 5.7.4, the IGNORE keyword in ALTER TABLE statements is ignored.
This prevents automatic table deduplication if a UNIQUE index is added to a Friendica table's structure.
If a DB update fails for you while creating a UNIQUE index, make sure to manually deduplicate the table before trying the update again.
### Manual deduplication
There are two main ways of doing it, either by manually removing the duplicates or by recreating the table.
Manually removing the duplicates is usually faster if they're not too numerous.
To manually remove the duplicates, you need to know the UNIQUE index columns available in `database.sql`.
```SQL
SELECT GROUP_CONCAT(id), <index columns>, count(*) as count FROM users
GROUP BY <index columns> HAVING count >= 2;
/* delete or merge duplicate from above query */;
```
If there are too many rows to handle manually, you can create a new table with the same structure as the table with duplicates and insert the existing content with INSERT IGNORE.
To recreate the table you need to know the table structure available in `database.sql`.
```SQL
CREATE TABLE <table_name>_new <rest of the CREATE TABLE>;
INSERT IGNORE INTO <table_name>_new SELECT * FROM <table_name>;
DROP TABLE <table_name>;
RENAME TABLE <table_name>_new TO <table_name>;
```
This method is slower overall, but it is better suited for large numbers of duplicates.

View file

@ -66,7 +66,7 @@ $a->config['system']['allowed_themes'] = 'quattro,vier,duepuntozero';
// default system theme
$a->config['system']['theme'] = 'duepuntozero';
$a->config['system']['theme'] = 'vier';
// By default allow pseudonyms
@ -78,3 +78,6 @@ $a->config['system']['no_regfullname'] = true;
// Location of the global directory
$a->config['system']['directory'] = 'http://dir.friendi.ca';
// Allowed protocols in link URLs; HTTP protocols always are accepted
$a->config['system']['allowed_link_protocols'] = array('ftp', 'ftps', 'mailto', 'cid', 'gopher');

View file

@ -22,6 +22,7 @@ function user_remove($uid) {
$r[0]['nickname']
);
/// @todo Should be done in a background job since this likely will run into a time out
// don't delete yet, will be done later when contacts have deleted my stuff
// q("DELETE FROM `contact` WHERE `uid` = %d", intval($uid));
q("DELETE FROM `gcign` WHERE `uid` = %d", intval($uid));
@ -74,25 +75,10 @@ function contact_remove($id) {
return;
}
q("DELETE FROM `contact` WHERE `id` = %d",
intval($id)
);
q("DELETE FROM `item` WHERE `contact-id` = %d ",
intval($id)
);
q("DELETE FROM `photo` WHERE `contact-id` = %d ",
intval($id)
);
q("DELETE FROM `mail` WHERE `contact-id` = %d ",
intval($id)
);
q("DELETE FROM `event` WHERE `cid` = %d ",
intval($id)
);
q("DELETE FROM `queue` WHERE `cid` = %d ",
intval($id)
);
q("DELETE FROM `contact` WHERE `id` = %d", intval($id));
// Delete the rest in the background
proc_run(PRIORITY_LOW, 'include/remove_contact.php', $id);
}
@ -145,7 +131,6 @@ function terminate_friendship($user,$self,$contact) {
// This provides for the possibility that their database is temporarily messed
// up or some other transient event and that there's a possibility we could recover from it.
if(! function_exists('mark_for_death')) {
function mark_for_death($contact) {
if($contact['archive'])
@ -156,14 +141,24 @@ function mark_for_death($contact) {
dbesc(datetime_convert()),
intval($contact['id'])
);
}
else {
/// @todo
if ($contact['url'] != '') {
q("UPDATE `contact` SET `term-date` = '%s'
WHERE `nurl` = '%s' AND `term-date` <= '1000-00-00'",
dbesc(datetime_convert()),
dbesc(normalise_link($contact['url']))
);
}
} else {
/// @todo
/// We really should send a notification to the owner after 2-3 weeks
/// so they won't be surprised when the contact vanishes and can take
/// remedial action if this was a serious mistake or glitch
/// @todo
/// Check for contact vitality via probing
$expiry = $contact['term-date'] . ' + 32 days ';
if(datetime_convert() > datetime_convert('UTC','UTC',$expiry)) {
@ -171,31 +166,51 @@ function mark_for_death($contact) {
// archive them rather than delete
// though if the owner tries to unarchive them we'll start the whole process over again
q("update contact set `archive` = 1 where id = %d",
q("UPDATE `contact` SET `archive` = 1 WHERE `id` = %d",
intval($contact['id'])
);
q("UPDATE `item` SET `private` = 2 WHERE `contact-id` = %d AND `uid` = %d", intval($contact['id']), intval($contact['uid']));
//contact_remove($contact['id']);
if ($contact['url'] != '') {
q("UPDATE `contact` SET `archive` = 1 WHERE `nurl` = '%s'",
dbesc(normalise_link($contact['url']))
);
}
}
}
}}
}
if(! function_exists('unmark_for_death')) {
function unmark_for_death($contact) {
$r = q("SELECT `term-date` FROM `contact` WHERE `id` = %d AND `term-date` > '%s'",
intval($contact['id']),
dbesc('1000-00-00 00:00:00')
);
// We don't need to update, we never marked this contact as dead
if (!dbm::is_result($r)) {
return;
}
// It's a miracle. Our dead contact has inexplicably come back to life.
q("UPDATE `contact` SET `term-date` = '%s' WHERE `id` = %d",
dbesc('0000-00-00 00:00:00'),
intval($contact['id'])
);
}}
if ($contact['url'] != '') {
q("UPDATE `contact` SET `term-date` = '%s' WHERE `nurl` = '%s'",
dbesc('0000-00-00 00:00:00'),
dbesc(normalise_link($contact['url']))
);
}
}
/**
* @brief Get contact data for a given profile link
*
* The function looks at several places (contact table and gcontact table) for the contact
* It caches its result for the same script execution to prevent duplicate calls
*
* @param string $url The profile link
* @param int $uid User id
@ -204,35 +219,45 @@ function unmark_for_death($contact) {
* @return array Contact data
*/
function get_contact_details_by_url($url, $uid = -1, $default = array()) {
if ($uid == -1)
static $cache = array();
if ($uid == -1) {
$uid = local_user();
}
if (isset($cache[$url][$uid])) {
return $cache[$url][$uid];
}
// Fetch contact data from the contact table for the given user
$r = q("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`,
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `bd` AS `birthday`, `self`
$r = q("SELECT `id`, `id` AS `cid`, 0 AS `gid`, 0 AS `zid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, `self`
FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d",
dbesc(normalise_link($url)), intval($uid));
// Fetch the data from the contact table with "uid=0" (which is filled automatically)
if (!$r)
$r = q("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`,
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `bd` AS `birthday`, 0 AS `self`
$r = q("SELECT `id`, 0 AS `cid`, `id` AS `zid`, 0 AS `gid`, `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, `xmpp`,
`keywords`, `gender`, `photo`, `thumb`, `micro`, `forum`, `prv`, (`forum` | `prv`) AS `community`, `contact-type`, `bd` AS `birthday`, 0 AS `self`
FROM `contact` WHERE `nurl` = '%s' AND `uid` = 0",
dbesc(normalise_link($url)));
// Fetch the data from the gcontact table
if (!$r)
$r = q("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`,
`keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `birthday`, 0 AS `self`
$r = q("SELECT 0 AS `id`, 0 AS `cid`, `id` AS `gid`, 0 AS `zid`, 0 AS `uid`, `url`, `nurl`, `alias`, `network`, `name`, `nick`, `addr`, `location`, `about`, '' AS `xmpp`,
`keywords`, `gender`, `photo`, `photo` AS `thumb`, `photo` AS `micro`, `community` AS `forum`, 0 AS `prv`, `community`, `contact-type`, `birthday`, 0 AS `self`
FROM `gcontact` WHERE `nurl` = '%s'",
dbesc(normalise_link($url)));
if ($r) {
// If there is more than one entry we filter out the connector networks
if (count($r) > 1)
foreach ($r AS $id => $result)
if ($result["network"] == NETWORK_STATUSNET)
if (count($r) > 1) {
foreach ($r AS $id => $result) {
if ($result["network"] == NETWORK_STATUSNET) {
unset($r[$id]);
}
}
}
$profile = array_shift($r);
@ -251,31 +276,40 @@ function get_contact_details_by_url($url, $uid = -1, $default = array()) {
$profile["bd"] = $current_year."-".$month."-".$day;
$current = $current_year."-".$current_month."-".$current_day;
if ($profile["bd"] < $current)
if ($profile["bd"] < $current) {
$profile["bd"] = (++$current_year)."-".$month."-".$day;
} else
}
} else {
$profile["bd"] = "0000-00-00";
} else
}
} else {
$profile = $default;
}
if (($profile["photo"] == "") AND isset($default["photo"]))
if (($profile["photo"] == "") AND isset($default["photo"])) {
$profile["photo"] = $default["photo"];
}
if (($profile["name"] == "") AND isset($default["name"]))
if (($profile["name"] == "") AND isset($default["name"])) {
$profile["name"] = $default["name"];
}
if (($profile["network"] == "") AND isset($default["network"]))
if (($profile["network"] == "") AND isset($default["network"])) {
$profile["network"] = $default["network"];
}
if (($profile["thumb"] == "") AND isset($profile["photo"]))
if (($profile["thumb"] == "") AND isset($profile["photo"])) {
$profile["thumb"] = $profile["photo"];
}
if (($profile["micro"] == "") AND isset($profile["thumb"]))
if (($profile["micro"] == "") AND isset($profile["thumb"])) {
$profile["micro"] = $profile["thumb"];
}
if ((($profile["addr"] == "") OR ($profile["name"] == "")) AND ($profile["gid"] != 0) AND
in_array($profile["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS)))
in_array($profile["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS))) {
proc_run(PRIORITY_LOW, "include/update_gcontact.php", $profile["gid"]);
}
// Show contact details of Diaspora contacts only if connected
if (($profile["cid"] == 0) AND ($profile["network"] == NETWORK_DIASPORA)) {
@ -285,87 +319,93 @@ function get_contact_details_by_url($url, $uid = -1, $default = array()) {
$profile["birthday"] = "0000-00-00";
}
return($profile);
$cache[$url][$uid] = $profile;
return $profile;
}
if(! function_exists('contact_photo_menu')){
function contact_photo_menu($contact, $uid = 0) {
if (! function_exists('contact_photo_menu')) {
function contact_photo_menu($contact, $uid = 0)
{
$a = get_app();
$contact_url="";
$pm_url="";
$status_link="";
$photos_link="";
$posts_link="";
$contact_drop_link = "";
$poke_link="";
$contact_url = '';
$pm_url = '';
$status_link = '';
$photos_link = '';
$posts_link = '';
$contact_drop_link = '';
$poke_link = '';
if ($uid == 0)
if ($uid == 0) {
$uid = local_user();
}
if ($contact["uid"] != $uid) {
if ($contact['uid'] != $uid) {
if ($uid == 0) {
$profile_link = zrl($contact['url']);
$menu = Array('profile' => array(t("View Profile"), $profile_link, true));
$menu = Array('profile' => array(t('View Profile'), $profile_link, true));
return $menu;
}
$r = q("SELECT * FROM `contact` WHERE `nurl` = '%s' AND `network` = '%s' AND `uid` = %d",
dbesc($contact["nurl"]), dbesc($contact["network"]), intval($uid));
if ($r)
dbesc($contact['nurl']), dbesc($contact['network']), intval($uid));
if ($r) {
return contact_photo_menu($r[0], $uid);
else {
} else {
$profile_link = zrl($contact['url']);
$connlnk = 'follow/?url='.$contact['url'];
$menu = Array(
'profile' => array(t("View Profile"), $profile_link, true),
'follow' => array(t("Connect/Follow"), $connlnk, true)
);
$menu = array(
'profile' => array(t('View Profile'), $profile_link, true),
'follow' => array(t('Connect/Follow'), $connlnk, true)
);
return $menu;
}
}
$sparkle = false;
if($contact['network'] === NETWORK_DFRN) {
if ($contact['network'] === NETWORK_DFRN) {
$sparkle = true;
$profile_link = $a->get_baseurl() . '/redir/' . $contact['id'];
}
else
} else {
$profile_link = $contact['url'];
if($profile_link === 'mailbox')
$profile_link = '';
if($sparkle) {
$status_link = $profile_link . "?url=status";
$photos_link = $profile_link . "?url=photos";
$profile_link = $profile_link . "?url=profile";
}
if (in_array($contact["network"], array(NETWORK_DFRN, NETWORK_DIASPORA)))
$pm_url = $a->get_baseurl() . '/message/new/' . $contact['id'];
if ($profile_link === 'mailbox') {
$profile_link = '';
}
if ($contact["network"] == NETWORK_DFRN)
if ($sparkle) {
$status_link = $profile_link . '?url=status';
$photos_link = $profile_link . '?url=photos';
$profile_link = $profile_link . '?url=profile';
}
if (in_array($contact['network'], array(NETWORK_DFRN, NETWORK_DIASPORA))) {
$pm_url = $a->get_baseurl() . '/message/new/' . $contact['id'];
}
if ($contact['network'] == NETWORK_DFRN) {
$poke_link = $a->get_baseurl() . '/poke/?f=&c=' . $contact['id'];
}
$contact_url = $a->get_baseurl() . '/contacts/' . $contact['id'];
$posts_link = $a->get_baseurl() . "/contacts/" . $contact['id'] . '/posts';
$contact_drop_link = $a->get_baseurl() . "/contacts/" . $contact['id'] . '/drop?confirm=1';
$posts_link = $a->get_baseurl() . '/contacts/' . $contact['id'] . '/posts';
$contact_drop_link = $a->get_baseurl() . '/contacts/' . $contact['id'] . '/drop?confirm=1';
/**
* menu array:
* "name" => [ "Label", "link", (bool)Should the link opened in a new tab? ]
*/
$menu = Array(
$menu = array(
'status' => array(t("View Status"), $status_link, true),
'profile' => array(t("View Profile"), $profile_link, true),
'photos' => array(t("View Photos"), $photos_link,true),
'network' => array(t("Network Posts"), $posts_link,false),
'edit' => array(t("Edit Contact"), $contact_url, false),
'photos' => array(t("View Photos"), $photos_link, true),
'network' => array(t("Network Posts"), $posts_link, false),
'edit' => array(t("View Contact"), $contact_url, false),
'drop' => array(t("Drop Contact"), $contact_drop_link, false),
'pm' => array(t("Send PM"), $pm_url, false),
'poke' => array(t("Poke"), $poke_link, false),
@ -378,9 +418,11 @@ function contact_photo_menu($contact, $uid = 0) {
$menucondensed = array();
foreach ($menu AS $menuname=>$menuitem)
if ($menuitem[1] != "")
foreach ($menu AS $menuname => $menuitem) {
if ($menuitem[1] != '') {
$menucondensed[$menuname] = $menuitem;
}
}
return $menucondensed;
}}
@ -422,9 +464,20 @@ function contacts_not_grouped($uid,$start = 0,$count = 0) {
return $r;
}
function get_contact($url, $uid = 0) {
/**
* @brief Fetch the contact id for a given url and user
*
* @param string $url Contact URL
* @param integer $uid The user id for the contact
* @param boolean $no_update Don't update the contact
*
* @return integer Contact ID
*/
function get_contact($url, $uid = 0, $no_update = false) {
require_once("include/Scrape.php");
logger("Get contact data for url ".$url." and user ".$uid." - ".App::callstack(), LOGGER_DEBUG);;
$data = array();
$contactid = 0;
@ -454,8 +507,9 @@ function get_contact($url, $uid = 0) {
$update_photo = ($contact[0]['avatar-date'] < datetime_convert('','','now -7 days'));
//$update_photo = ($contact[0]['avatar-date'] < datetime_convert('','','now -12 hours'));
if (!$update_photo)
if (!$update_photo OR $no_update) {
return($contactid);
}
} elseif ($uid != 0)
return 0;
@ -481,9 +535,9 @@ function get_contact($url, $uid = 0) {
if ($contactid == 0) {
q("INSERT INTO `contact` (`uid`, `created`, `url`, `nurl`, `addr`, `alias`, `notify`, `poll`,
`name`, `nick`, `photo`, `network`, `pubkey`, `rel`, `priority`,
`batch`, `request`, `confirm`, `poco`,
`batch`, `request`, `confirm`, `poco`, `name-date`, `uri-date`,
`writable`, `blocked`, `readonly`, `pending`)
VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', 1, 0, 0, 0)",
VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', 1, 0, 0, 0)",
intval($uid),
dbesc(datetime_convert()),
dbesc($data["url"]),
@ -502,7 +556,9 @@ function get_contact($url, $uid = 0) {
dbesc($data["batch"]),
dbesc($data["request"]),
dbesc($data["confirm"]),
dbesc($data["poco"])
dbesc($data["poco"]),
dbesc(datetime_convert()),
dbesc(datetime_convert())
);
$contact = q("SELECT `id` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d ORDER BY `id` LIMIT 2",
@ -533,16 +589,27 @@ function get_contact($url, $uid = 0) {
update_contact_avatar($data["photo"],$uid,$contactid);
q("UPDATE `contact` SET `addr` = '%s', `alias` = '%s', `name` = '%s', `nick` = '%s',
`name-date` = '%s', `uri-date` = '%s' WHERE `id` = %d",
dbesc($data["addr"]),
dbesc($data["alias"]),
dbesc($data["name"]),
dbesc($data["nick"]),
dbesc(datetime_convert()),
dbesc(datetime_convert()),
intval($contactid)
);
$r = q("SELECT `addr`, `alias`, `name`, `nick` FROM `contact` WHERE `id` = %d", intval($contactid));
// This condition should always be true
if (!dbm::is_result($r))
return $contactid;
// Only update if there had something been changed
if (($data["addr"] != $r[0]["addr"]) OR
($data["alias"] != $r[0]["alias"]) OR
($data["name"] != $r[0]["name"]) OR
($data["nick"] != $r[0]["nick"]))
q("UPDATE `contact` SET `addr` = '%s', `alias` = '%s', `name` = '%s', `nick` = '%s',
`name-date` = '%s', `uri-date` = '%s' WHERE `id` = %d",
dbesc($data["addr"]),
dbesc($data["alias"]),
dbesc($data["name"]),
dbesc($data["nick"]),
dbesc(datetime_convert()),
dbesc(datetime_convert()),
intval($contactid)
);
return $contactid;
}
@ -599,57 +666,55 @@ function posts_from_gcontact($a, $gcontact_id) {
return $o;
}
/**
* @brief Returns posts from a given contact
* @brief Returns posts from a given contact url
*
* @param App $a argv application class
* @param int $contact_id contact
* @param string $contact_url Contact URL
*
* @return string posts in HTML
*/
function posts_from_contact($a, $contact_id) {
function posts_from_contact_url($a, $contact_url) {
require_once('include/conversation.php');
$r = q("SELECT `url` FROM `contact` WHERE `id` = %d", intval($contact_id));
if (!$r)
return false;
// There are no posts with "uid = 0" with connector networks
// This speeds up the query a lot
$r = q("SELECT `network`, `id` AS `author-id` FROM `contact`
WHERE `contact`.`nurl` = '%s' AND `contact`.`uid` = 0",
dbesc(normalise_link($contact_url)));
if (in_array($r[0]["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, ""))) {
$sql = "(`item`.`uid` = 0 OR (`item`.`uid` = %d AND `item`.`private`))";
} else {
$sql = "`item`.`uid` = %d";
}
$contact = $r[0];
$author_id = intval($r[0]["author-id"]);
if(get_config('system', 'old_pager')) {
if (get_config('system', 'old_pager')) {
$r = q("SELECT COUNT(*) AS `total` FROM `item`
WHERE `item`.`uid` = %d AND `author-link` IN ('%s', '%s')",
intval(local_user()),
dbesc(str_replace("https://", "http://", $contact["url"])),
dbesc(str_replace("http://", "https://", $contact["url"])));
WHERE `author-id` = %d and $sql",
intval($author_id),
intval(local_user()));
$a->set_pager_total($r[0]['total']);
}
$r = q("SELECT `item`.`uri`, `item`.*, `item`.`id` AS `item_id`,
`author-name` AS `name`, `owner-avatar` AS `photo`,
`owner-link` AS `url`, `owner-avatar` AS `thumb`
FROM `item` FORCE INDEX (`uid_contactid_id`)
WHERE `item`.`uid` = %d AND `contact-id` = %d
AND `author-link` IN ('%s', '%s')
AND NOT `deleted` AND NOT `moderated` AND `visible`
ORDER BY `item`.`id` DESC LIMIT %d, %d",
$r = q(item_query()." AND `item`.`author-id` = %d AND ".$sql.
" ORDER BY `item`.`created` DESC LIMIT %d, %d",
intval($author_id),
intval(local_user()),
intval($contact_id),
dbesc(str_replace("https://", "http://", $contact["url"])),
dbesc(str_replace("http://", "https://", $contact["url"])),
intval($a->pager['start']),
intval($a->pager['itemspage'])
);
$o .= conversation($a,$r,'community',false);
$o = conversation($a,$r,'community',false);
if(!get_config('system', 'old_pager'))
if (!get_config('system', 'old_pager')) {
$o .= alt_pager($a,count($r));
else
} else {
$o .= paginate($a);
}
return $o;
}
@ -683,4 +748,50 @@ function formatted_location($profile) {
return $location;
}
/**
* @brief Returns the account type name
*
* The function can be called with either the user or the contact array
*
* @param array $contact contact or user array
*/
function account_type($contact) {
// There are several fields that indicate that the contact or user is a forum
// "page-flags" is a field in the user table,
// "forum" and "prv" are used in the contact table. They stand for PAGE_COMMUNITY and PAGE_PRVGROUP.
// "community" is used in the gcontact table and is true if the contact is PAGE_COMMUNITY or PAGE_PRVGROUP.
if((isset($contact['page-flags']) && (intval($contact['page-flags']) == PAGE_COMMUNITY))
|| (isset($contact['page-flags']) && (intval($contact['page-flags']) == PAGE_PRVGROUP))
|| (isset($contact['forum']) && intval($contact['forum']))
|| (isset($contact['prv']) && intval($contact['prv']))
|| (isset($contact['community']) && intval($contact['community'])))
$type = ACCOUNT_TYPE_COMMUNITY;
else
$type = ACCOUNT_TYPE_PERSON;
// The "contact-type" (contact table) and "account-type" (user table) are more general then the chaos from above.
if (isset($contact["contact-type"]))
$type = $contact["contact-type"];
if (isset($contact["account-type"]))
$type = $contact["account-type"];
switch($type) {
case ACCOUNT_TYPE_ORGANISATION:
$account_type = t("Organisation");
break;
case ACCOUNT_TYPE_NEWS:
$account_type = t('News');
break;
case ACCOUNT_TYPE_COMMUNITY:
$account_type = t("Forum");
break;
default:
$account_type = "";
break;
}
return $account_type;
}
?>

View file

@ -2,7 +2,7 @@
namespace Friendica\Core;
/**
* @file include/Core/Config.php
*
*
* @brief Contains the class with methods for system configuration
*/
@ -32,9 +32,9 @@ class Config {
public static function load($family) {
global $a;
$r = q("SELECT `v`, `k` FROM `config` WHERE `cat` = '%s'", dbesc($family));
if(count($r)) {
foreach($r as $rr) {
$r = q("SELECT `v`, `k` FROM `config` WHERE `cat` = '%s' ORDER BY `cat`, `k`, `id`", dbesc($family));
if (count($r)) {
foreach ($r as $rr) {
$k = $rr['k'];
if ($family === 'config') {
$a->config[$k] = $rr['v'];
@ -70,74 +70,38 @@ class Config {
* If true the config is loaded from the db and not from the cache (default: false)
* @return mixed Stored value or null if it does not exist
*/
public static function get($family, $key, $default_value=null, $refresh = false) {
public static function get($family, $key, $default_value = null, $refresh = false) {
global $a;
if(! $instore) {
if (!$refresh) {
// Looking if the whole family isn't set
if(isset($a->config[$family])) {
if($a->config[$family] === '!<unset>!') {
if (isset($a->config[$family])) {
if ($a->config[$family] === '!<unset>!') {
return $default_value;
}
}
if(isset($a->config[$family][$key])) {
if($a->config[$family][$key] === '!<unset>!') {
if (isset($a->config[$family][$key])) {
if ($a->config[$family][$key] === '!<unset>!') {
return $default_value;
}
return $a->config[$family][$key];
}
}
// If APC is enabled then fetch the data from there, else try XCache
/*if (function_exists("apc_fetch") AND function_exists("apc_exists"))
if (apc_exists($family."|".$key)) {
$val = apc_fetch($family."|".$key);
$a->config[$family][$key] = $val;
if ($val === '!<unset>!')
return false;
else
return $val;
}
elseif (function_exists("xcache_fetch") AND function_exists("xcache_isset"))
if (xcache_isset($family."|".$key)) {
$val = xcache_fetch($family."|".$key);
$a->config[$family][$key] = $val;
if ($val === '!<unset>!')
return false;
else
return $val;
}
*/
$ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
$ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' ORDER BY `id` DESC LIMIT 1",
dbesc($family),
dbesc($key)
);
if(count($ret)) {
if (count($ret)) {
// manage array value
$val = (preg_match("|^a:[0-9]+:{.*}$|s", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']);
$a->config[$family][$key] = $val;
// If APC is enabled then store the data there, else try XCache
/*if (function_exists("apc_store"))
apc_store($family."|".$key, $val, 600);
elseif (function_exists("xcache_set"))
xcache_set($family."|".$key, $val, 600);*/
return $val;
}
else {
} else {
$a->config[$family][$key] = '!<unset>!';
// If APC is enabled then store the data there, else try XCache
/*if (function_exists("apc_store"))
apc_store($family."|".$key, '!<unset>!', 600);
elseif (function_exists("xcache_set"))
xcache_set($family."|".$key, '!<unset>!', 600);*/
}
return $default_value;
}
@ -158,48 +122,38 @@ class Config {
* The value to store
* @return mixed Stored $value or false if the database update failed
*/
public static function set($family,$key,$value) {
public static function set($family, $key, $value) {
global $a;
// If $a->config[$family] has been previously set to '!<unset>!', then
// $a->config[$family][$key] will evaluate to $a->config[$family][0], and
// $a->config[$family][$key] = $value will be equivalent to
// $a->config[$family][0] = $value[0] (this causes infuriating bugs),
// so unset the family before assigning a value to a family's key
if($a->config[$family] === '!<unset>!')
unset($a->config[$family]);
$stored = self::get($family, $key);
// manage array value
$dbvalue = (is_array($value)?serialize($value):$value);
$dbvalue = (is_bool($dbvalue) ? intval($dbvalue) : $dbvalue);
if(is_null(self::get($family,$key,null,true))) {
$a->config[$family][$key] = $value;
$ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ",
dbesc($family),
dbesc($key),
dbesc($dbvalue)
);
if($ret)
return $value;
return $ret;
if ($stored == $value) {
return true;
}
$ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s'",
dbesc($dbvalue),
dbesc($family),
dbesc($key)
);
$a->config[$family][$key] = $value;
// If APC is enabled then store the data there, else try XCache
/*if (function_exists("apc_store"))
apc_store($family."|".$key, $value, 600);
elseif (function_exists("xcache_set"))
xcache_set($family."|".$key, $value, 600);*/
// manage array value
$dbvalue = (is_array($value) ? serialize($value) : $value);
$dbvalue = (is_bool($dbvalue) ? intval($dbvalue) : $dbvalue);
if($ret)
if (is_null($stored)) {
$ret = q("INSERT INTO `config` (`cat`, `k`, `v`) VALUES ('%s', '%s', '%s') ON DUPLICATE KEY UPDATE `v` = '%s'",
dbesc($family),
dbesc($key),
dbesc($dbvalue),
dbesc($dbvalue)
);
} else {
$ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s'",
dbesc($dbvalue),
dbesc($family),
dbesc($key)
);
}
if ($ret) {
return $value;
}
return $ret;
}
@ -215,20 +169,16 @@ class Config {
* The configuration key to delete
* @return mixed
*/
public static function delete($family,$key) {
public static function delete($family, $key) {
global $a;
if(x($a->config[$family],$key))
if (x($a->config[$family],$key)) {
unset($a->config[$family][$key]);
}
$ret = q("DELETE FROM `config` WHERE `cat` = '%s' AND `k` = '%s'",
dbesc($family),
dbesc($key)
);
// If APC is enabled then delete the data from there, else try XCache
/*if (function_exists("apc_delete"))
apc_delete($family."|".$key);
elseif (function_exists("xcache_unset"))
xcache_unset($family."|".$key);*/
return $ret;
}

View file

@ -27,14 +27,14 @@ class PConfig {
* The category of the configuration value
* @return void
*/
public static function load($uid,$family) {
public static function load($uid, $family) {
global $a;
$r = q("SELECT `v`,`k` FROM `pconfig` WHERE `cat` = '%s' AND `uid` = %d",
$r = q("SELECT `v`,`k` FROM `pconfig` WHERE `cat` = '%s' AND `uid` = %d ORDER BY `cat`, `k`, `id`",
dbesc($family),
intval($uid)
);
if(count($r)) {
foreach($r as $rr) {
if (count($r)) {
foreach ($r as $rr) {
$k = $rr['k'];
$a->config[$uid][$family][$k] = $rr['v'];
}
@ -67,71 +67,35 @@ class PConfig {
global $a;
if(! $instore) {
if (!$refresh) {
// Looking if the whole family isn't set
if(isset($a->config[$uid][$family])) {
if($a->config[$uid][$family] === '!<unset>!') {
if (isset($a->config[$uid][$family])) {
if ($a->config[$uid][$family] === '!<unset>!') {
return $default_value;
}
}
if(isset($a->config[$uid][$family][$key])) {
if($a->config[$uid][$family][$key] === '!<unset>!') {
if (isset($a->config[$uid][$family][$key])) {
if ($a->config[$uid][$family][$key] === '!<unset>!') {
return $default_value;
}
return $a->config[$uid][$family][$key];
}
}
// If APC is enabled then fetch the data from there, else try XCache
/*if (function_exists("apc_fetch") AND function_exists("apc_exists"))
if (apc_exists($uid."|".$family."|".$key)) {
$val = apc_fetch($uid."|".$family."|".$key);
$a->config[$uid][$family][$key] = $val;
if ($val === '!<unset>!')
return false;
else
return $val;
}
elseif (function_exists("xcache_get") AND function_exists("xcache_isset"))
if (xcache_isset($uid."|".$family."|".$key)) {
$val = xcache_get($uid."|".$family."|".$key);
$a->config[$uid][$family][$key] = $val;
if ($val === '!<unset>!')
return false;
else
return $val;
}*/
$ret = q("SELECT `v` FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' LIMIT 1",
$ret = q("SELECT `v` FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s' ORDER BY `id` DESC LIMIT 1",
intval($uid),
dbesc($family),
dbesc($key)
);
if(count($ret)) {
if (count($ret)) {
$val = (preg_match("|^a:[0-9]+:{.*}$|s", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']);
$a->config[$uid][$family][$key] = $val;
// If APC is enabled then store the data there, else try XCache
/*if (function_exists("apc_store"))
apc_store($uid."|".$family."|".$key, $val, 600);
elseif (function_exists("xcache_set"))
xcache_set($uid."|".$family."|".$key, $val, 600);*/
return $val;
}
else {
} else {
$a->config[$uid][$family][$key] = '!<unset>!';
// If APC is enabled then store the data there, else try XCache
/*if (function_exists("apc_store"))
apc_store($uid."|".$family."|".$key, '!<unset>!', 600);
elseif (function_exists("xcache_set"))
xcache_set($uid."|".$family."|".$key, '!<unset>!', 600);*/
}
return $default_value;
}
@ -154,43 +118,41 @@ class PConfig {
* The value to store
* @return mixed Stored $value or false
*/
public static function set($uid,$family,$key,$value) {
public static function set($uid, $family, $key, $value) {
global $a;
// manage array value
$dbvalue = (is_array($value)?serialize($value):$value);
$stored = self::get($uid, $family, $key);
if(is_null(self::get($uid,$family,$key,null, true))) {
$a->config[$uid][$family][$key] = $value;
$ret = q("INSERT INTO `pconfig` ( `uid`, `cat`, `k`, `v` ) VALUES ( %d, '%s', '%s', '%s' ) ",
intval($uid),
dbesc($family),
dbesc($key),
dbesc($dbvalue)
);
if($ret)
return $value;
return $ret;
if ($stored == $value) {
return true;
}
$ret = q("UPDATE `pconfig` SET `v` = '%s' WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s'",
dbesc($dbvalue),
intval($uid),
dbesc($family),
dbesc($key)
);
// manage array value
$dbvalue = (is_array($value) ? serialize($value):$value);
$a->config[$uid][$family][$key] = $value;
// If APC is enabled then store the data there, else try XCache
/*if (function_exists("apc_store"))
apc_store($uid."|".$family."|".$key, $value, 600);
elseif (function_exists("xcache_set"))
xcache_set($uid."|".$family."|".$key, $value, 600);*/
if (is_null($stored)) {
$ret = q("INSERT INTO `pconfig` (`uid`, `cat`, `k`, `v`) VALUES (%d, '%s', '%s', '%s') ON DUPLICATE KEY UPDATE `v` = '%s'",
intval($uid),
dbesc($family),
dbesc($key),
dbesc($dbvalue),
dbesc($dbvalue)
);
} else {
$ret = q("UPDATE `pconfig` SET `v` = '%s' WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s'",
dbesc($dbvalue),
intval($uid),
dbesc($family),
dbesc($key)
);
}
if($ret)
if ($ret) {
return $value;
}
return $ret;
}
@ -210,13 +172,17 @@ class PConfig {
public static function delete($uid,$family,$key) {
global $a;
if(x($a->config[$uid][$family],$key))
if (x($a->config[$uid][$family], $key)) {
unset($a->config[$uid][$family][$key]);
}
$ret = q("DELETE FROM `pconfig` WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s'",
intval($uid),
dbesc($family),
dbesc($key)
);
return $ret;
}
}

View file

@ -213,8 +213,9 @@ class NotificationsManager {
// Because we use different db tables for the notification query
// we have sometimes $it['unseen'] and sometimes $it['seen].
// So we will have to transform $it['unseen']
if($it['unseen'])
if (array_key_exists('unseen', $it)) {
$it['seen'] = ($it['unseen'] > 0 ? false : true);
}
// Depending on the identifier of the notification we need to use different defaults
switch ($ident) {
@ -224,16 +225,14 @@ class NotificationsManager {
$default_item_image = proxy_url($it['photo'], false, PROXY_SIZE_MICRO);
$default_item_text = strip_tags(bbcode($it['msg']));
$default_item_when = relative_date($it['date']);
$default_tpl = $tpl_notify;
break;
case 'home':
$default_item_label = 'comment';
$default_item_link = $this->a->get_baseurl(true).'/display/'.$it['pguid'];
$default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
$default_item_text = sprintf( t("%s commented on %s's post"), $it['author-name'], $it['pname']);
$default_item_text = sprintf(t("%s commented on %s's post"), $it['author-name'], $it['pname']);
$default_item_when = relative_date($it['created']);
$default_tpl = $tpl_item_comments;
break;
default:
@ -241,21 +240,20 @@ class NotificationsManager {
$default_item_link = $this->a->get_baseurl(true).'/display/'.$it['pguid'];
$default_item_image = proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO);
$default_item_text = (($it['id'] == $it['parent'])
? sprintf( t("%s created a new post"), $it['author-name'])
: sprintf( t("%s commented on %s's post"), $it['author-name'], $it['pname']));
? sprintf(t("%s created a new post"), $it['author-name'])
: sprintf(t("%s commented on %s's post"), $it['author-name'], $it['pname']));
$default_item_when = relative_date($it['created']);
$default_tpl = (($it['id'] == $it['parent']) ? $tpl_item_posts : $tpl_item_comments);
}
// Transform the different types of notification in an usable array
switch($it['verb']){
switch ($it['verb']){
case ACTIVITY_LIKE:
$notif = array(
'label' => 'like',
'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
'$image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'text' => sprintf( t("%s liked %s's post"), $it['author-name'], $it['pname']),
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'text' => sprintf(t("%s liked %s's post"), $it['author-name'], $it['pname']),
'when' => relative_date($it['created']),
'seen' => $it['seen']
);
@ -266,7 +264,7 @@ class NotificationsManager {
'label' => 'dislike',
'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'text' => sprintf( t("%s disliked %s's post"), $it['author-name'], $it['pname']),
'text' => sprintf(t("%s disliked %s's post"), $it['author-name'], $it['pname']),
'when' => relative_date($it['created']),
'seen' => $it['seen']
);
@ -277,7 +275,7 @@ class NotificationsManager {
'label' => 'attend',
'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'text' => sprintf( t("%s is attending %s's event"), $it['author-name'], $it['pname']),
'text' => sprintf(t("%s is attending %s's event"), $it['author-name'], $it['pname']),
'when' => relative_date($it['created']),
'seen' => $it['seen']
);
@ -299,7 +297,7 @@ class NotificationsManager {
'label' => 'attendmaybe',
'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'text' => sprintf( t("%s may attend %s's event"), $it['author-name'], $it['pname']),
'text' => sprintf(t("%s may attend %s's event"), $it['author-name'], $it['pname']),
'when' => relative_date($it['created']),
'seen' => $it['seen']
);
@ -314,7 +312,7 @@ class NotificationsManager {
'label' => 'friend',
'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
'image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
'text' => sprintf( t("%s is now friends with %s"), $it['author-name'], $it['fname']),
'text' => sprintf(t("%s is now friends with %s"), $it['author-name'], $it['fname']),
'when' => relative_date($it['created']),
'seen' => $it['seen']
);
@ -554,7 +552,7 @@ class NotificationsManager {
$r = q("SELECT `item`.`id`,`item`.`parent`, `item`.`verb`, `item`.`author-name`, `item`.`unseen`,
`item`.`author-link`, `item`.`author-avatar`, `item`.`created`, `item`.`object` AS `object`,
`pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`,
`pitem`.`author-name` AS `pname`, `pitem`.`author-link` AS `plink`, `pitem`.`guid` AS `pguid`
FROM `item` INNER JOIN `item` AS `pitem` ON `pitem`.`id`=`item`.`parent`
WHERE `item`.`visible` = 1
$sql_extra

552
include/ParseUrl.php Normal file
View file

@ -0,0 +1,552 @@
<?php
/**
* @file include/ParseUrl.php
* @brief Get informations about a given URL
*/
namespace Friendica;
use \Friendica\Core\Config;
require_once("include/network.php");
require_once("include/Photo.php");
require_once("include/oembed.php");
require_once("include/xml.php");
/**
* @brief Class with methods for extracting certain content from an url
*/
class ParseUrl {
/**
* @brief Search for chached embeddable data of an url otherwise fetch it
*
* @param type $url The url of the page which should be scraped
* @param type $no_guessing If true the parse doens't search for
* preview pictures
* @param type $do_oembed The false option is used by the function fetch_oembed()
* to avoid endless loops
*
* @return array which contains needed data for embedding
* string 'url' => The url of the parsed page
* string 'type' => Content type
* string 'title' => The title of the content
* string 'text' => The description for the content
* string 'image' => A preview image of the content (only available
* if $no_geuessing = false
* array'images' = Array of preview pictures
* string 'keywords' => The tags which belong to the content
*
* @see ParseUrl::getSiteinfo() for more information about scraping
* embeddable content
*/
public static function getSiteinfoCached($url, $no_guessing = false, $do_oembed = true) {
if ($url == "") {
return false;
}
$r = q("SELECT * FROM `parsed_url` WHERE `url` = '%s' AND `guessing` = %d AND `oembed` = %d",
dbesc(normalise_link($url)), intval(!$no_guessing), intval($do_oembed));
if ($r) {
$data = $r[0]["content"];
}
if (!is_null($data)) {
$data = unserialize($data);
return $data;
}
$data = self::getSiteinfo($url, $no_guessing, $do_oembed);
q("INSERT INTO `parsed_url` (`url`, `guessing`, `oembed`, `content`, `created`) VALUES ('%s', %d, %d, '%s', '%s')
ON DUPLICATE KEY UPDATE `content` = '%s', `created` = '%s'",
dbesc(normalise_link($url)), intval(!$no_guessing), intval($do_oembed),
dbesc(serialize($data)), dbesc(datetime_convert()),
dbesc(serialize($data)), dbesc(datetime_convert()));
return $data;
}
/**
* @brief Parse a page for embeddable content information
*
* This method parses to url for meta data which can be used to embed
* the content. If available it prioritizes Open Graph meta tags.
* If this is not available it uses the twitter cards meta tags.
* As fallback it uses standard html elements with meta informations
* like \<title\>Awesome Title\</title\> or
* \<meta name="description" content="An awesome description"\>
*
* @param type $url The url of the page which should be scraped
* @param type $no_guessing If true the parse doens't search for
* preview pictures
* @param type $do_oembed The false option is used by the function fetch_oembed()
* to avoid endless loops
* @param type $count Internal counter to avoid endless loops
*
* @return array which contains needed data for embedding
* string 'url' => The url of the parsed page
* string 'type' => Content type
* string 'title' => The title of the content
* string 'text' => The description for the content
* string 'image' => A preview image of the content (only available
* if $no_geuessing = false
* array'images' = Array of preview pictures
* string 'keywords' => The tags which belong to the content
*
* @todo https://developers.google.com/+/plugins/snippet/
* @verbatim
* <meta itemprop="name" content="Awesome title">
* <meta itemprop="description" content="An awesome description">
* <meta itemprop="image" content="http://maple.libertreeproject.org/images/tree-icon.png">
*
* <body itemscope itemtype="http://schema.org/Product">
* <h1 itemprop="name">Shiny Trinket</h1>
* <img itemprop="image" src="{image-url}" />
* <p itemprop="description">Shiny trinkets are shiny.</p>
* </body>
* @endverbatim
*/
public static function getSiteinfo($url, $no_guessing = false, $do_oembed = true, $count = 1) {
$a = get_app();
$siteinfo = array();
// Check if the URL does contain a scheme
$scheme = parse_url($url, PHP_URL_SCHEME);
if ($scheme == "") {
$url = "http://".trim($url, "/");
}
if ($count > 10) {
logger("parseurl_getsiteinfo: Endless loop detected for ".$url, LOGGER_DEBUG);
return($siteinfo);
}
$url = trim($url, "'");
$url = trim($url, '"');
$url = original_url($url);
$siteinfo["url"] = $url;
$siteinfo["type"] = "link";
$check_cert = Config::get("system", "verifyssl");
$stamp1 = microtime(true);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_NOBODY, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, $a->get_useragent());
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, (($check_cert) ? 2 : false));
$header = curl_exec($ch);
$curl_info = @curl_getinfo($ch);
$http_code = $curl_info["http_code"];
curl_close($ch);
$a->save_timestamp($stamp1, "network");
if ((($curl_info["http_code"] == "301") || ($curl_info["http_code"] == "302") || ($curl_info["http_code"] == "303") || ($curl_info["http_code"] == "307"))
&& (($curl_info["redirect_url"] != "") || ($curl_info["location"] != ""))) {
if ($curl_info["redirect_url"] != "") {
$siteinfo = self::getSiteinfo($curl_info["redirect_url"], $no_guessing, $do_oembed, ++$count);
} else {
$siteinfo = self::getSiteinfo($curl_info["location"], $no_guessing, $do_oembed, ++$count);
}
return($siteinfo);
}
// If the file is too large then exit
if ($curl_info["download_content_length"] > 1000000) {
return($siteinfo);
}
// If it isn't a HTML file then exit
if (($curl_info["content_type"] != "") && !strstr(strtolower($curl_info["content_type"]), "html")) {
return($siteinfo);
}
if ($do_oembed) {
$oembed_data = oembed_fetch_url($url);
if (!in_array($oembed_data->type, array("error", "rich"))) {
$siteinfo["type"] = $oembed_data->type;
}
if (($oembed_data->type == "link") && ($siteinfo["type"] != "photo")) {
if (isset($oembed_data->title)) {
$siteinfo["title"] = $oembed_data->title;
}
if (isset($oembed_data->description)) {
$siteinfo["text"] = trim($oembed_data->description);
}
if (isset($oembed_data->thumbnail_url)) {
$siteinfo["image"] = $oembed_data->thumbnail_url;
}
}
}
$stamp1 = microtime(true);
// Now fetch the body as well
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_NOBODY, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, $a->get_useragent());
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, (($check_cert) ? 2 : false));
$header = curl_exec($ch);
$curl_info = @curl_getinfo($ch);
$http_code = $curl_info["http_code"];
curl_close($ch);
$a->save_timestamp($stamp1, "network");
// Fetch the first mentioned charset. Can be in body or header
$charset = "";
if (preg_match('/charset=(.*?)['."'".'"\s\n]/', $header, $matches)) {
$charset = trim(trim(trim(array_pop($matches)), ';,'));
}
if ($charset == "") {
$charset = "utf-8";
}
$pos = strpos($header, "\r\n\r\n");
if ($pos) {
$body = trim(substr($header, $pos));
} else {
$body = $header;
}
if (($charset != "") && (strtoupper($charset) != "UTF-8")) {
logger("parseurl_getsiteinfo: detected charset ".$charset, LOGGER_DEBUG);
//$body = mb_convert_encoding($body, "UTF-8", $charset);
$body = iconv($charset, "UTF-8//TRANSLIT", $body);
}
$body = mb_convert_encoding($body, 'HTML-ENTITIES', "UTF-8");
$doc = new \DOMDocument();
@$doc->loadHTML($body);
\xml::deleteNode($doc, "style");
\xml::deleteNode($doc, "script");
\xml::deleteNode($doc, "option");
\xml::deleteNode($doc, "h1");
\xml::deleteNode($doc, "h2");
\xml::deleteNode($doc, "h3");
\xml::deleteNode($doc, "h4");
\xml::deleteNode($doc, "h5");
\xml::deleteNode($doc, "h6");
\xml::deleteNode($doc, "ol");
\xml::deleteNode($doc, "ul");
$xpath = new \DomXPath($doc);
$list = $xpath->query("//meta[@content]");
foreach ($list as $node) {
$attr = array();
if ($node->attributes->length) {
foreach ($node->attributes as $attribute) {
$attr[$attribute->name] = $attribute->value;
}
}
if (@$attr["http-equiv"] == "refresh") {
$path = $attr["content"];
$pathinfo = explode(";", $path);
$content = "";
foreach ($pathinfo as $value) {
if (substr(strtolower($value), 0, 4) == "url=") {
$content = substr($value, 4);
}
}
if ($content != "") {
$siteinfo = self::getSiteinfo($content, $no_guessing, $do_oembed, ++$count);
return($siteinfo);
}
}
}
$list = $xpath->query("//title");
if ($list->length > 0) {
$siteinfo["title"] = $list->item(0)->nodeValue;
}
//$list = $xpath->query("head/meta[@name]");
$list = $xpath->query("//meta[@name]");
foreach ($list as $node) {
$attr = array();
if ($node->attributes->length) {
foreach ($node->attributes as $attribute) {
$attr[$attribute->name] = $attribute->value;
}
}
$attr["content"] = trim(html_entity_decode($attr["content"], ENT_QUOTES, "UTF-8"));
if ($attr["content"] != "") {
switch (strtolower($attr["name"])) {
case "fulltitle":
$siteinfo["title"] = $attr["content"];
break;
case "description":
$siteinfo["text"] = $attr["content"];
break;
case "thumbnail":
$siteinfo["image"] = $attr["content"];
break;
case "twitter:image":
$siteinfo["image"] = $attr["content"];
break;
case "twitter:image:src":
$siteinfo["image"] = $attr["content"];
break;
case "twitter:card":
if (($siteinfo["type"] == "") || ($attr["content"] == "photo")) {
$siteinfo["type"] = $attr["content"];
}
break;
case "twitter:description":
$siteinfo["text"] = $attr["content"];
break;
case "twitter:title":
$siteinfo["title"] = $attr["content"];
break;
case "dc.title":
$siteinfo["title"] = $attr["content"];
break;
case "dc.description":
$siteinfo["text"] = $attr["content"];
break;
case "keywords":
$keywords = explode(",", $attr["content"]);
break;
case "news_keywords":
$keywords = explode(",", $attr["content"]);
break;
}
}
if ($siteinfo["type"] == "summary") {
$siteinfo["type"] = "link";
}
}
if (isset($keywords)) {
$siteinfo["keywords"] = array();
foreach ($keywords as $keyword) {
if (!in_array(trim($keyword), $siteinfo["keywords"])) {
$siteinfo["keywords"][] = trim($keyword);
}
}
}
//$list = $xpath->query("head/meta[@property]");
$list = $xpath->query("//meta[@property]");
foreach ($list as $node) {
$attr = array();
if ($node->attributes->length) {
foreach ($node->attributes as $attribute) {
$attr[$attribute->name] = $attribute->value;
}
}
$attr["content"] = trim(html_entity_decode($attr["content"], ENT_QUOTES, "UTF-8"));
if ($attr["content"] != "") {
switch (strtolower($attr["property"])) {
case "og:image":
$siteinfo["image"] = $attr["content"];
break;
case "og:title":
$siteinfo["title"] = $attr["content"];
break;
case "og:description":
$siteinfo["text"] = $attr["content"];
break;
}
}
}
if ((@$siteinfo["image"] == "") && !$no_guessing) {
$list = $xpath->query("//img[@src]");
foreach ($list as $node) {
$attr = array();
if ($node->attributes->length) {
foreach ($node->attributes as $attribute) {
$attr[$attribute->name] = $attribute->value;
}
}
$src = self::completeUrl($attr["src"], $url);
$photodata = get_photo_info($src);
if (($photodata) && ($photodata[0] > 150) && ($photodata[1] > 150)) {
if ($photodata[0] > 300) {
$photodata[1] = round($photodata[1] * (300 / $photodata[0]));
$photodata[0] = 300;
}
if ($photodata[1] > 300) {
$photodata[0] = round($photodata[0] * (300 / $photodata[1]));
$photodata[1] = 300;
}
$siteinfo["images"][] = array("src" => $src,
"width" => $photodata[0],
"height" => $photodata[1]);
}
}
} elseif ($siteinfo["image"] != "") {
$src = self::completeUrl($siteinfo["image"], $url);
unset($siteinfo["image"]);
$photodata = get_photo_info($src);
if (($photodata) && ($photodata[0] > 10) && ($photodata[1] > 10)) {
$siteinfo["images"][] = array("src" => $src,
"width" => $photodata[0],
"height" => $photodata[1]);
}
}
if ((@$siteinfo["text"] == "") && (@$siteinfo["title"] != "") && !$no_guessing) {
$text = "";
$list = $xpath->query("//div[@class='article']");
foreach ($list as $node) {
if (strlen($node->nodeValue) > 40) {
$text .= " ".trim($node->nodeValue);
}
}
if ($text == "") {
$list = $xpath->query("//div[@class='content']");
foreach ($list as $node) {
if (strlen($node->nodeValue) > 40) {
$text .= " ".trim($node->nodeValue);
}
}
}
// If none text was found then take the paragraph content
if ($text == "") {
$list = $xpath->query("//p");
foreach ($list as $node) {
if (strlen($node->nodeValue) > 40) {
$text .= " ".trim($node->nodeValue);
}
}
}
if ($text != "") {
$text = trim(str_replace(array("\n", "\r"), array(" ", " "), $text));
while (strpos($text, " ")) {
$text = trim(str_replace(" ", " ", $text));
}
$siteinfo["text"] = trim(html_entity_decode(substr($text, 0, 350), ENT_QUOTES, "UTF-8").'...');
}
}
logger("parseurl_getsiteinfo: Siteinfo for ".$url." ".print_r($siteinfo, true), LOGGER_DEBUG);
call_hooks("getsiteinfo", $siteinfo);
return($siteinfo);
}
/**
* @brief Convert tags from CSV to an array
*
* @param string $string Tags
* @return array with formatted Hashtags
*/
public static function convertTagsToArray($string) {
$arr_tags = str_getcsv($string);
if (count($arr_tags)) {
// add the # sign to every tag
array_walk($arr_tags, array("self", "arrAddHashes"));
return $arr_tags;
}
}
/**
* @brief Add a hasht sign to a string
*
* This method is used as callback function
*
* @param string $tag The pure tag name
* @param int $k Counter for internal use
*/
private static function arrAddHashes(&$tag, $k) {
$tag = "#" . $tag;
}
/**
* @brief Add a scheme to an url
*
* The src attribute of some html elements (e.g. images)
* can miss the scheme so we need to add the correct
* scheme
*
* @param string $url The url which possibly does have
* a missing scheme (a link to an image)
* @param string $scheme The url with a correct scheme
* (e.g. the url from the webpage which does contain the image)
*
* @return string The url with a scheme
*/
private static function completeUrl($url, $scheme) {
$urlarr = parse_url($url);
// If the url does allready have an scheme
// we can stop the process here
if (isset($urlarr["scheme"])) {
return($url);
}
$schemearr = parse_url($scheme);
$complete = $schemearr["scheme"]."://".$schemearr["host"];
if (@$schemearr["port"] != "") {
$complete .= ":".$schemearr["port"];
}
if (strpos($urlarr["path"],"/") !== 0) {
$complete .= "/";
}
$complete .= $urlarr["path"];
if (@$urlarr["query"] != "") {
$complete .= "?".$urlarr["query"];
}
if (@$urlarr["fragment"] != "") {
$complete .= "#".$urlarr["fragment"];
}
return($complete);
}
}

File diff suppressed because it is too large Load diff

View file

@ -118,18 +118,16 @@ class Probe {
*/
public static function webfinger_dfrn($webbie, &$hcard) {
if (!strstr($webbie, '@'))
return $webbie;
$profile_link = '';
$links = self::webfinger($webbie);
$links = self::lrdd($webbie);
logger('webfinger_dfrn: '.$webbie.':'.print_r($links,true), LOGGER_DATA);
if (count($links)) {
foreach ($links as $link) {
if ($link['@attributes']['rel'] === NAMESPACE_DFRN)
$profile_link = $link['@attributes']['href'];
if ($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB)
if (($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB) AND ($profile_link == ""))
$profile_link = 'stat:'.$link['@attributes']['template'];
if ($link['@attributes']['rel'] === 'http://microformats.org/profile/hcard')
$hcard = $link['@attributes']['href'];
@ -180,6 +178,11 @@ class Probe {
$path = str_replace('{uri}', urlencode($uri), $link);
$webfinger = self::webfinger($path);
if (!$webfinger AND (strstr($uri, "@"))) {
$path = str_replace('{uri}', urlencode("acct:".$uri), $link);
$webfinger = self::webfinger($path);
}
}
if (!is_array($webfinger["links"]))
@ -214,7 +217,6 @@ class Probe {
if ($cache) {
$result = Cache::get("probe_url:".$network.":".$uri);
if (!is_null($result)) {
$result = unserialize($result);
return $result;
}
}
@ -254,7 +256,7 @@ class Probe {
// Only store into the cache if the value seems to be valid
if (!in_array($data['network'], array(NETWORK_PHANTOM, NETWORK_MAIL))) {
Cache::set("probe_url:".$network.":".$uri,serialize($data), CACHE_DAY);
Cache::set("probe_url:".$network.":".$uri, $data, CACHE_DAY);
/// @todo temporary fix - we need a real contact update function that updates only changing fields
/// The biggest problem is the avatar picture that could have a reduced image size.
@ -310,6 +312,7 @@ class Probe {
return array("network" => NETWORK_TWITTER);
$lrdd = self::xrd($host);
if (!$lrdd)
return self::mail($uri, $uid);
@ -356,6 +359,12 @@ class Probe {
$path = str_replace('{uri}', urlencode($addr), $link);
$webfinger = self::webfinger($path);
// Mastodon needs to have it with "acct:"
if (!$webfinger) {
$path = str_replace('{uri}', urlencode("acct:".$addr), $link);
$webfinger = self::webfinger($path);
}
// If webfinger wasn't successful then try it with the URL - possibly in the format https://...
if (!$webfinger AND ($uri != $addr)) {
$path = str_replace('{uri}', urlencode($uri), $link);
@ -560,6 +569,8 @@ class Probe {
$data = array();
logger("Check profile ".$profile, LOGGER_DEBUG);
// Fetch data via noscrape - this is faster
$noscrape = str_replace(array("/hcard/", "/profile/"), "/noscrape/", $profile);
$data = self::poll_noscrape($noscrape, $data);
@ -582,6 +593,8 @@ class Probe {
$prof_data["fn"] = $data["name"];
$prof_data["key"] = $data["pubkey"];
logger("Result for profile ".$profile.": ".print_r($prof_data, true), LOGGER_DEBUG);
return $prof_data;
}
@ -651,8 +664,12 @@ class Probe {
*/
private function poll_hcard($hcard, $data, $dfrn = false) {
$content = fetch_url($hcard);
if (!$content)
return false;
$doc = new DOMDocument();
if (!@$doc->loadHTMLFile($hcard))
if (!@$doc->loadHTML($content))
return false;
$xpath = new DomXPath($doc);
@ -661,40 +678,39 @@ class Probe {
if (!is_object($vcards))
return false;
if ($vcards->length == 0)
return false;
if ($vcards->length > 0) {
$vcard = $vcards->item(0);
$vcard = $vcards->item(0);
// We have to discard the guid from the hcard in favour of the guid from lrdd
// Reason: Hubzilla doesn't use the value "uid" in the hcard like Diaspora does.
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' uid ')]", $vcard); // */
if (($search->length > 0) AND ($data["guid"] == ""))
$data["guid"] = $search->item(0)->nodeValue;
// We have to discard the guid from the hcard in favour of the guid from lrdd
// Reason: Hubzilla doesn't use the value "uid" in the hcard like Diaspora does.
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' uid ')]", $vcard); // */
if (($search->length > 0) AND ($data["guid"] == ""))
$data["guid"] = $search->item(0)->nodeValue;
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' nickname ')]", $vcard); // */
if ($search->length > 0)
$data["nick"] = $search->item(0)->nodeValue;
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' nickname ')]", $vcard); // */
if ($search->length > 0)
$data["nick"] = $search->item(0)->nodeValue;
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' fn ')]", $vcard); // */
if ($search->length > 0)
$data["name"] = $search->item(0)->nodeValue;
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' fn ')]", $vcard); // */
if ($search->length > 0)
$data["name"] = $search->item(0)->nodeValue;
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' searchable ')]", $vcard); // */
if ($search->length > 0)
$data["searchable"] = $search->item(0)->nodeValue;
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' searchable ')]", $vcard); // */
if ($search->length > 0)
$data["searchable"] = $search->item(0)->nodeValue;
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' key ')]", $vcard); // */
if ($search->length > 0) {
$data["pubkey"] = $search->item(0)->nodeValue;
if (strstr($data["pubkey"], 'RSA '))
$data["pubkey"] = rsatopem($data["pubkey"]);
}
$search = $xpath->query("//*[contains(concat(' ', @class, ' '), ' key ')]", $vcard); // */
if ($search->length > 0) {
$data["pubkey"] = $search->item(0)->nodeValue;
if (strstr($data["pubkey"], 'RSA '))
$data["pubkey"] = rsatopem($data["pubkey"]);
$search = $xpath->query("//*[@id='pod_location']", $vcard); // */
if ($search->length > 0)
$data["baseurl"] = trim($search->item(0)->nodeValue, "/");
}
$search = $xpath->query("//*[@id='pod_location']", $vcard); // */
if ($search->length > 0)
$data["baseurl"] = trim($search->item(0)->nodeValue, "/");
$avatar = array();
$photos = $xpath->query("//*[contains(concat(' ', @class, ' '), ' photo ') or contains(concat(' ', @class, ' '), ' avatar ')]", $vcard); // */
foreach ($photos AS $photo) {
@ -815,6 +831,9 @@ class Probe {
if (strstr($alias, "@"))
$data["addr"] = str_replace('acct:', '', $alias);
if (is_string($webfinger["subject"]) AND strstr($webfinger["subject"], "@"))
$data["addr"] = str_replace('acct:', '', $webfinger["subject"]);
$pubkey = "";
foreach ($webfinger["links"] AS $link) {
if (($link["rel"] == "http://webfinger.net/rel/profile-page") AND
@ -832,7 +851,7 @@ class Probe {
$pubkey = substr($pubkey, strpos($pubkey, ',') + 1);
else
$pubkey = substr($pubkey, 5);
} else
} elseif (normalise_link($pubkey) == 'http://')
$pubkey = fetch_url($pubkey);
$key = explode(".", $pubkey);

View file

@ -280,6 +280,44 @@
$duration = (float)(microtime(true)-$stamp);
logger("API call duration: ".round($duration, 2)."\t".$a->query_string, LOGGER_DEBUG);
if (get_config("system", "profiler")) {
$duration = microtime(true)-$a->performance["start"];
logger(parse_url($a->query_string, PHP_URL_PATH).": ".sprintf("Database: %s/%s, Network: %s, I/O: %s, Other: %s, Total: %s",
round($a->performance["database"] - $a->performance["database_write"], 3),
round($a->performance["database_write"], 3),
round($a->performance["network"], 2),
round($a->performance["file"], 2),
round($duration - ($a->performance["database"] + $a->performance["network"]
+ $a->performance["file"]), 2),
round($duration, 2)),
LOGGER_DEBUG);
if (get_config("rendertime", "callstack")) {
$o = "Database Read:\n";
foreach ($a->callstack["database"] AS $func => $time) {
$time = round($time, 3);
if ($time > 0)
$o .= $func.": ".$time."\n";
}
$o .= "\nDatabase Write:\n";
foreach ($a->callstack["database_write"] AS $func => $time) {
$time = round($time, 3);
if ($time > 0)
$o .= $func.": ".$time."\n";
}
$o .= "\nNetwork:\n";
foreach ($a->callstack["network"] AS $func => $time) {
$time = round($time, 3);
if ($time > 0)
$o .= $func.": ".$time."\n";
}
logger($o, LOGGER_DEBUG);
}
}
if ($r===false) {
// api function returned false withour throw an
// exception. This should not happend, throw a 500
@ -391,7 +429,7 @@
* Contact url or False if contact id is unknown
*/
function api_unique_id_to_url($id){
$r = q("SELECT `url` FROM `gcontact` WHERE `id`=%d LIMIT 1",
$r = q("SELECT `url` FROM `contact` WHERE `uid` = 0 AND `id` = %d LIMIT 1",
intval($id));
if ($r)
return ($r[0]["url"]);
@ -423,7 +461,7 @@
if (api_user()!==false) $extra_query .= "AND `contact`.`uid`=".intval(api_user());
}
// Searching for unique contact id
// Searching for contact id with uid = 0
if(!is_null($contact_id) AND (intval($contact_id) != 0)){
$user = dbesc(api_unique_id_to_url($contact_id));
@ -496,14 +534,16 @@
// Selecting the id by priority, friendica first
api_best_nickname($uinfo);
// if the contact wasn't found, fetch it from the unique contacts
// if the contact wasn't found, fetch it from the contacts with uid = 0
if (count($uinfo)==0) {
$r = array();
if ($url != "")
$r = q("SELECT * FROM `gcontact` WHERE `nurl`='%s' LIMIT 1", dbesc(normalise_link($url)));
$r = q("SELECT * FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s' LIMIT 1", dbesc(normalise_link($url)));
if ($r) {
$network_name = network_to_name($r[0]['network'], $r[0]['url']);
// If no nick where given, extract it from the address
if (($r[0]['nick'] == "") OR ($r[0]['name'] == $r[0]['nick']))
$r[0]['nick'] = api_get_nick($r[0]["url"]);
@ -513,8 +553,10 @@
'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"],
'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"],
'url' => $r[0]["url"],
'protected' => false,
'followers_count' => 0,
@ -531,16 +573,13 @@
'contributors_enabled' => false,
'is_translator' => false,
'is_translation_enabled' => false,
'profile_image_url' => $r[0]["photo"],
'profile_image_url_https' => $r[0]["photo"],
'following' => false,
'follow_request_sent' => false,
'notifications' => false,
'statusnet_blocking' => false,
'notifications' => false,
'statusnet_profile_url' => $r[0]["url"],
'uid' => 0,
'cid' => get_contact($r[0]["url"], api_user()),
'cid' => get_contact($r[0]["url"], api_user(), true),
'self' => 0,
'network' => $r[0]["network"],
);
@ -563,28 +602,28 @@
intval(api_user())
);
//AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''",
// 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 `type`='wall'",
intval($uinfo[0]['uid'])
);
$countitms = $r[0]['count'];
//$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;
}
else {
//AND `allow_cid`='' AND `allow_gid`='' AND `deny_cid`='' AND `deny_gid`=''",
$r = q("SELECT count(*) as `count` FROM `item`
WHERE `contact-id` = %d",
intval($uinfo[0]['id'])
);
$countitms = $r[0]['count'];
}
/*
// 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 `blocked`=0 AND `pending`=0 AND `hidden`=0",
AND `self`=0 AND NOT `blocked` AND `hidden`=0",
intval($uinfo[0]['uid']),
intval(CONTACT_IS_SHARING),
intval(CONTACT_IS_FRIEND)
@ -593,7 +632,7 @@
$r = q("SELECT count(*) as `count` FROM `contact`
WHERE `uid` = %d AND `rel` IN ( %d, %d )
AND `self`=0 AND `blocked`=0 AND `pending`=0 AND `hidden`=0",
AND `self`=0 AND NOT `blocked` AND `hidden`=0",
intval($uinfo[0]['uid']),
intval(CONTACT_IS_FOLLOWER),
intval(CONTACT_IS_FRIEND)
@ -611,6 +650,10 @@
$countfollowers = 0;
$starred = 0;
}
*/
$countfriends = 0;
$countfollowers = 0;
$starred = 0;
// Add a nick if it isn't present there
if (($uinfo[0]['nick'] == "") OR ($uinfo[0]['name'] == $uinfo[0]['nick'])) {
@ -619,12 +662,11 @@
$network_name = network_to_name($uinfo[0]['network'], $uinfo[0]['url']);
$gcontact_id = get_gcontact_id(array("url" => $uinfo[0]['url'], "network" => $uinfo[0]['network'],
"photo" => $uinfo[0]['micro'], "name" => $uinfo[0]['name']));
$pcontact_id = get_contact($uinfo[0]['url'], 0, true);
$ret = Array(
'id' => intval($gcontact_id),
'id_str' => (string) intval($gcontact_id),
'id' => intval($pcontact_id),
'id_str' => (string) intval($pcontact_id),
'name' => (($uinfo[0]['name']) ? $uinfo[0]['name'] : $uinfo[0]['nick']),
'screen_name' => (($uinfo[0]['nick']) ? $uinfo[0]['nick'] : $uinfo[0]['name']),
'location' => ($usr) ? $usr[0]['default-location'] : $network_name,
@ -635,13 +677,20 @@
'protected' => false,
'followers_count' => intval($countfollowers),
'friends_count' => intval($countfriends),
'listed_count' => 0,
'created_at' => api_date($uinfo[0]['created']),
'favourites_count' => intval($starred),
'utc_offset' => "0",
'time_zone' => 'UTC',
'statuses_count' => intval($countitms),
'following' => (($uinfo[0]['rel'] == CONTACT_IS_FOLLOWER) OR ($uinfo[0]['rel'] == CONTACT_IS_FRIEND)),
'geo_enabled' => false,
'verified' => true,
'statuses_count' => intval($countitms),
'lang' => '',
'contributors_enabled' => false,
'is_translator' => false,
'is_translation_enabled' => false,
'following' => (($uinfo[0]['rel'] == CONTACT_IS_FOLLOWER) OR ($uinfo[0]['rel'] == CONTACT_IS_FRIEND)),
'follow_request_sent' => false,
'statusnet_blocking' => false,
'notifications' => false,
//'statusnet_profile_url' => App::get_baseurl()."/contacts/".$uinfo[0]['cid'],
@ -665,21 +714,15 @@
*/
function api_item_get_user(&$a, $item) {
// Make sure that there is an entry in the global contacts for author and owner
get_gcontact_id(array("url" => $item['author-link'], "network" => $item['network'],
"photo" => $item['author-avatar'], "name" => $item['author-name']));
$status_user = api_get_user($a, $item["author-link"]);
get_gcontact_id(array("url" => $item['owner-link'], "network" => $item['network'],
"photo" => $item['owner-avatar'], "name" => $item['owner-name']));
$status_user = api_get_user($a,$item["author-link"]);
$status_user["protected"] = (($item["allow_cid"] != "") OR
($item["allow_gid"] != "") OR
($item["deny_cid"] != "") OR
($item["deny_gid"] != "") OR
$item["private"]);
$owner_user = api_get_user($a,$item["owner-link"]);
$owner_user = api_get_user($a, $item["owner-link"]);
return (array($status_user, $owner_user));
}
@ -1108,12 +1151,11 @@
$privacy_sql = "";
// get last public wall message
$lastwall = q("SELECT `item`.*, `i`.`contact-id` as `reply_uid`, `i`.`author-link` AS `item-author`
FROM `item`, `item` as `i`
$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 `i`.`id` = `item`.`parent`
AND `item`.`type`!='activity' $privacy_sql
AND `item`.`type` != 'activity' $privacy_sql
ORDER BY `item`.`id` DESC
LIMIT 1",
intval($user_info['cid']),
@ -1127,37 +1169,7 @@
if (count($lastwall)>0){
$lastwall = $lastwall[0];
$in_reply_to_status_id = NULL;
$in_reply_to_user_id = NULL;
$in_reply_to_status_id_str = NULL;
$in_reply_to_user_id_str = NULL;
$in_reply_to_screen_name = NULL;
if (intval($lastwall['parent']) != intval($lastwall['id'])) {
$in_reply_to_status_id= intval($lastwall['parent']);
$in_reply_to_status_id_str = (string) intval($lastwall['parent']);
$r = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($lastwall['item-author'])));
if ($r) {
if ($r[0]['nick'] == "")
$r[0]['nick'] = api_get_nick($r[0]["url"]);
$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']);
}
}
// There seems to be situation, where both fields are identical:
// https://github.com/friendica/friendica/issues/1010
// This is a bugfix for that.
if (intval($in_reply_to_status_id) == intval($lastwall['id'])) {
logger('api_status_show: this message should never appear: id: '.$lastwall['id'].' similar to reply-to: '.$in_reply_to_status_id, LOGGER_DEBUG);
$in_reply_to_status_id = NULL;
$in_reply_to_user_id = NULL;
$in_reply_to_status_id_str = NULL;
$in_reply_to_user_id_str = NULL;
$in_reply_to_screen_name = NULL;
}
$in_reply_to = api_in_reply_to($lastwall);
$converted = api_convert_item($lastwall);
@ -1173,11 +1185,11 @@
'text' => $converted["text"],
'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'),
'truncated' => false,
'in_reply_to_status_id' => $in_reply_to_status_id,
'in_reply_to_status_id_str' => $in_reply_to_status_id_str,
'in_reply_to_user_id' => $in_reply_to_user_id,
'in_reply_to_user_id_str' => $in_reply_to_user_id_str,
'in_reply_to_screen_name' => $in_reply_to_screen_name,
'in_reply_to_status_id' => $in_reply_to['status_id'],
'in_reply_to_status_id_str' => $in_reply_to['status_id_str'],
'in_reply_to_user_id' => $in_reply_to['user_id'],
'in_reply_to_user_id_str' => $in_reply_to['user_id_str'],
'in_reply_to_screen_name' => $in_reply_to['screen_name'],
'user' => $user_info,
$geo => NULL,
'coordinates' => "",
@ -1254,29 +1266,7 @@
if (count($lastwall)>0){
$lastwall = $lastwall[0];
$in_reply_to_status_id = NULL;
$in_reply_to_user_id = NULL;
$in_reply_to_status_id_str = NULL;
$in_reply_to_user_id_str = NULL;
$in_reply_to_screen_name = NULL;
if ($lastwall['parent']!=$lastwall['id']) {
$reply = q("SELECT `item`.`id`, `item`.`contact-id` as `reply_uid`, `contact`.`nick` as `reply_author`, `item`.`author-link` AS `item-author`
FROM `item`,`contact` WHERE `contact`.`id`=`item`.`contact-id` AND `item`.`id` = %d", intval($lastwall['parent']));
if (count($reply)>0) {
$in_reply_to_status_id = intval($lastwall['parent']);
$in_reply_to_status_id_str = (string) intval($lastwall['parent']);
$r = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($reply[0]['item-author'])));
if ($r) {
if ($r[0]['nick'] == "")
$r[0]['nick'] = api_get_nick($r[0]["url"]);
$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 = api_in_reply_to($lastwall);
$converted = api_convert_item($lastwall);
@ -1289,14 +1279,14 @@
'text' => $converted["text"],
'truncated' => false,
'created_at' => api_date($lastwall['created']),
'in_reply_to_status_id' => $in_reply_to_status_id,
'in_reply_to_status_id_str' => $in_reply_to_status_id_str,
'in_reply_to_status_id' => $in_reply_to['status_id'],
'in_reply_to_status_id_str' => $in_reply_to['status_id_str'],
'source' => (($lastwall['app']) ? $lastwall['app'] : 'web'),
'id' => intval($lastwall['contact-id']),
'id_str' => (string) $lastwall['contact-id'],
'in_reply_to_user_id' => $in_reply_to_user_id,
'in_reply_to_user_id_str' => $in_reply_to_user_id_str,
'in_reply_to_screen_name' => $in_reply_to_screen_name,
'in_reply_to_user_id' => $in_reply_to['user_id'],
'in_reply_to_user_id_str' => $in_reply_to['user_id_str'],
'in_reply_to_screen_name' => $in_reply_to['screen_name'],
$geo => NULL,
'favorited' => $lastwall['starred'] ? true : false,
'statusnet_html' => $converted["html"],
@ -1335,9 +1325,9 @@
$userlist = array();
if (isset($_GET["q"])) {
$r = q("SELECT id FROM `gcontact` WHERE `name`='%s'", dbesc($_GET["q"]));
$r = q("SELECT id FROM `contact` WHERE `uid` = 0 AND `name` = '%s'", dbesc($_GET["q"]));
if (!count($r))
$r = q("SELECT `id` FROM `gcontact` WHERE `nick`='%s'", dbesc($_GET["q"]));
$r = q("SELECT `id` FROM `contact` WHERE `uid` = 0 AND `nick` = '%s'", dbesc($_GET["q"]));
if (count($r)) {
$k = 0;
@ -1383,7 +1373,6 @@
$user_info = api_get_user($a);
// get last newtork messages
// params
$count = (x($_REQUEST,'count')?$_REQUEST['count']:20);
$page = (x($_REQUEST,'page')?$_REQUEST['page']-1:0);
@ -1410,7 +1399,7 @@
`contact`.`id` AS `cid`
FROM `item`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid`
AND NOT `contact`.`blocked` AND NOT `contact`.`pending`
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
@ -1487,7 +1476,7 @@
`user`.`nickname`, `user`.`hidewall`
FROM `item`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid`
AND NOT `contact`.`blocked` AND NOT `contact`.`pending`
AND (NOT `contact`.`blocked` OR `contact`.`pending`)
STRAIGHT_JOIN `user` ON `user`.`uid` = `item`.`uid`
AND NOT `user`.`hidewall`
WHERE `verb` = '%s' AND `item`.`visible` AND NOT `item`.`deleted` AND NOT `item`.`moderated`
@ -1554,7 +1543,7 @@
`contact`.`id` AS `cid`
FROM `item`
INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid`
AND NOT `contact`.`blocked` AND NOT `contact`.`pending`
AND (NOT `contact`.`blocked` OR `contact`.`pending`)
WHERE `item`.`visible` AND NOT `item`.`moderated` AND NOT `item`.`deleted`
AND `item`.`uid` = %d AND `item`.`verb` = '%s'
$sql_extra",
@ -1630,7 +1619,7 @@
`contact`.`id` AS `cid`
FROM `item`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid`
AND NOT `contact`.`blocked` AND NOT `contact`.`pending`
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'
@ -1684,7 +1673,7 @@
`contact`.`id` AS `cid`
FROM `item`
INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND `contact`.`uid` = `item`.`uid`
AND NOT `contact`.`blocked` AND NOT `contact`.`pending`
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` = ''
@ -1803,7 +1792,7 @@
`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` AND NOT `contact`.`pending`
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`
@ -1877,7 +1866,7 @@
`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` AND NOT `contact`.`pending`
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`
@ -2013,7 +2002,7 @@
AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`deleted` = 0
AND `item`.`starred` = 1
AND `contact`.`id` = `item`.`contact-id`
AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
AND (NOT `contact`.`blocked` OR `contact`.`pending`)
$sql_extra
AND `item`.`id`>%d
ORDER BY `item`.`id` DESC LIMIT %d ,%d ",
@ -2373,7 +2362,7 @@
//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']);
$user = api_get_user($a, $i['author-link']);
switch($i['verb']) {
case ACTIVITY_LIKE:
$activities['like'][] = $user;
@ -2412,6 +2401,59 @@
}
/**
* @brief return data from profiles
*
* @param array $profile array containing data from db table 'profile'
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return array
*/
function api_format_items_profiles(&$profile = null, $type = "json") {
if ($profile != null) {
$profile = array('profile_id' => $profile['id'],
'profile_name' => $profile['profile-name'],
'is_default' => $profile['is-default'] ? true : false,
'hide_friends'=> $profile['hide-friends'] ? true : false,
'profile_photo' => $profile['photo'],
'profile_thumb' => $profile['thumb'],
'publish' => $profile['publish'] ? true : false,
'net_publish' => $profile['net-publish'] ? true : false,
'description' => $profile['pdesc'],
'date_of_birth' => $profile['dob'],
'address' => $profile['address'],
'city' => $profile['locality'],
'region' => $profile['region'],
'postal_code' => $profile['postal-code'],
'country' => $profile['country-name'],
'hometown' => $profile['hometown'],
'gender' => $profile['gender'],
'marital' => $profile['marital'],
'marital_with' => $profile['with'],
'marital_since' => $profile['howlong'],
'sexual' => $profile['sexual'],
'politic' => $profile['politic'],
'religion' => $profile['religion'],
'public_keywords' => $profile['pub_keywords'],
'private_keywords' => $profile['prv_keywords'],
'likes' => bbcode(api_clean_plain_items($profile['likes']), false, false, 2, false),
'dislikes' => bbcode(api_clean_plain_items($profile['dislikes']), false, false, 2, false),
'about' => bbcode(api_clean_plain_items($profile['about']), false, false, 2, false),
'music' => bbcode(api_clean_plain_items($profile['music']), false, false, 2, false),
'book' => bbcode(api_clean_plain_items($profile['book']), false, false, 2, false),
'tv' => bbcode(api_clean_plain_items($profile['tv']), false, false, 2, false),
'film' => bbcode(api_clean_plain_items($profile['film']), false, false, 2, false),
'interest' => bbcode(api_clean_plain_items($profile['interest']), false, false, 2, false),
'romance' => bbcode(api_clean_plain_items($profile['romance']), false, false, 2, false),
'work' => bbcode(api_clean_plain_items($profile['work']), false, false, 2, false),
'education' => bbcode(api_clean_plain_items($profile['education']), false, false, 2, false),
'social_networks' => bbcode(api_clean_plain_items($profile['contact']), false, false, 2, false),
'homepage' => $profile['homepage'],
'users' => null);
return $profile;
}
}
/**
* @brief format items to be returned by api
*
@ -2434,43 +2476,7 @@
if ($filter_user AND ($status_user["id"] != $user_info["id"]))
continue;
if ($item['thr-parent'] != $item['uri']) {
$r = q("SELECT id FROM item WHERE uid=%d AND uri='%s' LIMIT 1",
intval(api_user()),
dbesc($item['thr-parent']));
if ($r)
$in_reply_to_status_id = intval($r[0]['id']);
else
$in_reply_to_status_id = intval($item['parent']);
$in_reply_to_status_id_str = (string) intval($item['parent']);
$in_reply_to_screen_name = NULL;
$in_reply_to_user_id = NULL;
$in_reply_to_user_id_str = NULL;
$r = q("SELECT `author-link` FROM item WHERE uid=%d AND id=%d LIMIT 1",
intval(api_user()),
intval($in_reply_to_status_id));
if ($r) {
$r = q("SELECT * FROM `gcontact` WHERE `url` = '%s'", dbesc(normalise_link($r[0]['author-link'])));
if ($r) {
if ($r[0]['nick'] == "")
$r[0]['nick'] = api_get_nick($r[0]["url"]);
$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']);
}
}
} else {
$in_reply_to_screen_name = NULL;
$in_reply_to_user_id = NULL;
$in_reply_to_status_id = NULL;
$in_reply_to_user_id_str = NULL;
$in_reply_to_status_id_str = NULL;
}
$in_reply_to = api_in_reply_to($item);
$converted = api_convert_item($item);
@ -2483,14 +2489,14 @@
'text' => $converted["text"],
'truncated' => False,
'created_at'=> api_date($item['created']),
'in_reply_to_status_id' => $in_reply_to_status_id,
'in_reply_to_status_id_str' => $in_reply_to_status_id_str,
'in_reply_to_status_id' => $in_reply_to['status_id'],
'in_reply_to_status_id_str' => $in_reply_to['status_id_str'],
'source' => (($item['app']) ? $item['app'] : 'web'),
'id' => intval($item['id']),
'id_str' => (string) intval($item['id']),
'in_reply_to_user_id' => $in_reply_to_user_id,
'in_reply_to_user_id_str' => $in_reply_to_user_id_str,
'in_reply_to_screen_name' => $in_reply_to_screen_name,
'in_reply_to_user_id' => $in_reply_to['user_id'],
'in_reply_to_user_id_str' => $in_reply_to['user_id_str'],
'in_reply_to_screen_name' => $in_reply_to['screen_name'],
$geo => NULL,
'favorited' => $item['starred'] ? true : false,
'user' => $status_user ,
@ -2642,7 +2648,7 @@
if ($user_info['self'] == 0)
$sql_extra = " AND false ";
$r = q("SELECT `nurl` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 $sql_extra",
$r = q("SELECT `nurl` FROM `contact` WHERE `uid` = %d AND NOT `self` AND (NOT `blocked` OR `pending`) $sql_extra",
intval(api_user())
);
@ -2743,7 +2749,9 @@
$stringify_ids = (x($_REQUEST,'stringify_ids')?$_REQUEST['stringify_ids']:false);
$r = q("SELECT `gcontact`.`id` FROM `contact`, `gcontact` WHERE `contact`.`nurl` = `gcontact`.`nurl` AND `uid` = %d AND NOT `self` AND NOT `blocked` AND NOT `pending` $sql_extra",
$r = q("SELECT `pcontact`.`id` FROM `contact`
INNER JOIN `contact` AS `pcontact` ON `contact`.`nurl` = `pcontact`.`nurl` AND `pcontact`.`uid` = 0
WHERE `contact`.`uid` = %s AND NOT `contact`.`self`",
intval(api_user())
);
@ -2838,7 +2846,7 @@
* @brief delete a direct_message from mail table through api
*
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string
* @return string
*/
function api_direct_messages_destroy($type){
$a = get_app();
@ -3215,10 +3223,10 @@
}
$attributes = preg_replace("/\[share(.*?)\]\s?(.*?)\s?\[\/share\]\s?/ism","$1",$body);
// Skip if there is no shared message in there
// we already checked this in diaspora::is_reshare()
// but better one more than one less...
if ($body == $attributes)
// Skip if there is no shared message in there
// we already checked this in diaspora::is_reshare()
// but better one more than one less...
if ($body == $attributes)
return false;
@ -3297,7 +3305,7 @@
$nick = "";
$r = q("SELECT `nick` FROM `gcontact` WHERE `nurl` = '%s'",
$r = q("SELECT `nick` FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s'",
dbesc(normalise_link($profile)));
if ($r)
$nick = $r[0]["nick"];
@ -3356,6 +3364,60 @@
return(false);
}
function api_in_reply_to($item) {
$in_reply_to = array();
$in_reply_to['status_id'] = NULL;
$in_reply_to['user_id'] = NULL;
$in_reply_to['status_id_str'] = NULL;
$in_reply_to['user_id_str'] = NULL;
$in_reply_to['screen_name'] = NULL;
if (($item['thr-parent'] != $item['uri']) AND (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']);
} 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'])
);
if (dbm::is_result($r)) {
if ($r[0]['nick'] == "") {
$r[0]['nick'] = api_get_nick($r[0]["url"]);
}
$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']);
}
// There seems to be situation, where both fields are identical:
// https://github.com/friendica/friendica/issues/1010
// This is a bugfix for that.
if (intval($in_reply_to['status_id']) == intval($item['id'])) {
logger('this message should never appear: id: '.$item['id'].' similar to reply-to: '.$in_reply_to['status_id'], LOGGER_DEBUG);
$in_reply_to['status_id'] = NULL;
$in_reply_to['user_id'] = NULL;
$in_reply_to['status_id_str'] = NULL;
$in_reply_to['user_id_str'] = NULL;
$in_reply_to['screen_name'] = NULL;
}
}
return $in_reply_to;
}
function api_clean_plain_items($Text) {
$include_entities = strtolower(x($_REQUEST,'include_entities')?$_REQUEST['include_entities']:"false");
@ -3842,7 +3904,7 @@
$user_info = api_get_user($a);
$searchstring = (x($_REQUEST,'searchstring') ? $_REQUEST['searchstring'] : "");
$uid = $user_info['uid'];
// error if no searchstring specified
if ($searchstring == "") {
$answer = array('result' => 'error', 'message' => 'searchstring not specified');
@ -3880,6 +3942,71 @@
api_register_func('api/friendica/direct_messages_search', 'api_friendica_direct_messages_search', true);
/**
* @brief return data of all the profiles a user has to the client
*
* @param string $type Known types are 'atom', 'rss', 'xml' and 'json'
* @return string
*/
function api_friendica_profile_show($type){
$a = get_app();
if (api_user()===false) throw new ForbiddenException();
// input params
$profileid = (x($_REQUEST,'profile_id') ? $_REQUEST['profile_id'] : 0);
// retrieve general information about profiles for user
$multi_profiles = feature_enabled(api_user(),'multi_profiles');
$directory = get_config('system', 'directory');
// get data of the specified profile id or all profiles of the user if not specified
if ($profileid != 0) {
$r = q("SELECT * FROM `profile` WHERE `uid` = %d AND `id` = %d",
intval(api_user()),
intval($profileid));
// error message if specified gid is not in database
if (count($r) == 0)
throw new BadRequestException("profile_id not available");
}
else
$r = q("SELECT * FROM `profile` WHERE `uid` = %d",
intval(api_user()));
// loop through all returned profiles and retrieve data and users
$k = 0;
foreach ($r as $rr) {
$profile = api_format_items_profiles($rr, $type);
// select all users from contact table, loop and prepare standard return for user data
$users = array();
$r = q("SELECT `id`, `nurl` FROM `contact` WHERE `uid`= %d AND `profile-id` = %d",
intval(api_user()),
intval($rr['profile_id']));
foreach ($r as $rr) {
$user = api_get_user($a, $rr['nurl']);
($type == "xml") ? $users[$k++.":user"] = $user : $users[] = $user;
}
$profile['users'] = $users;
// add prepared profile data to array for final return
if ($type == "xml") {
$profiles[$k++.":profile"] = $profile;
} else {
$profiles[] = $profile;
}
}
// return settings, authenticated user and profiles data
$result = array('multi_profiles' => $multi_profiles ? true : false,
'global_dir' => $directory,
'friendica_owner' => api_get_user($a, intval(api_user())),
'profiles' => $profiles);
return api_format_data("friendica_profiles", $type, array('$result' => $result));
}
api_register_func('api/friendica/profile/show', 'api_friendica_profile_show', true, API_METHOD_GET);
/*
To.Do:
[pagename] => api/1.1/statuses/lookup.json
@ -3905,6 +4032,9 @@ account/update_profile_background_image
account/update_profile_image
blocks/create
blocks/destroy
friendica/profile/update
friendica/profile/create
friendica/profile/delete
Not implemented in status.net:
statuses/retweeted_to_me

View file

@ -47,11 +47,10 @@ require_once("boot.php");
global $a, $db;
if(is_null($a)) {
if (is_null($a))
$a = new App;
}
if(is_null($db)) {
if (is_null($db)) {
@include(".htconfig.php");
require_once("include/dba.php");
$db = new dba($db_host, $db_user, $db_pass, $db_data);
@ -66,162 +65,261 @@ $bDebug = get_config('jabber','debug');
$oAuth = new exAuth($sLogFile, $bDebug);
class exAuth
{
class exAuth {
private $sLogFile;
private $bDebug;
private $rLogFile;
public function __construct($sLogFile, $bDebug)
{
/**
* @brief Create the class and do the authentification studd
*
* @param string $sLogFile The logfile name
* @param boolean $bDebug Debug mode
*/
public function __construct($sLogFile, $bDebug) {
global $db;
// setter
$this->sLogFile = $sLogFile;
$this->bDebug = $bDebug;
// ovo ne provjeravamo jer ako ne mozes kreirati log file, onda si u kvascu :)
// Open the logfile if the logfile name is defined
if ($this->sLogFile != '')
$this->rLogFile = fopen($this->sLogFile, "a") or die("Error opening log file: ". $this->sLogFile);
$this->writeLog("[exAuth] start");
// ovdje bi trebali biti spojeni na MySQL, imati otvoren log i zavrtit cekalicu
// We are connected to the SQL server and are having a log file.
do {
$iHeader = fgets(STDIN, 3);
$aLength = unpack("n", $iHeader);
$iLength = $aLength["1"];
if($iLength > 0) {
// ovo znaci da smo nesto dobili
$sData = fgets(STDIN, $iLength + 1);
$this->writeDebugLog("[debug] received data: ". $sData);
$aCommand = explode(":", $sData);
if (is_array($aCommand)){
switch ($aCommand[0]){
case "isuser":
// provjeravamo je li korisnik dobar
if (!isset($aCommand[1])){
$this->writeLog("[exAuth] invalid isuser command, no username given");
fwrite(STDOUT, pack("nn", 2, 0));
} else {
// ovdje provjeri je li korisnik OK
$sUser = str_replace(array("%20", "(a)"), array(" ", "@"), $aCommand[1]);
$this->writeDebugLog("[debug] checking isuser for ". $sUser);
$sQuery = "SELECT `uid` FROM `user` WHERE `nickname`='". $db->escape($sUser) ."'";
$this->writeDebugLog("[debug] using query ". $sQuery);
if ($oResult = q($sQuery)){
if ($oResult) {
// korisnik OK
$this->writeLog("[exAuth] valid user: ". $sUser);
fwrite(STDOUT, pack("nn", 2, 1));
} else {
// korisnik nije OK
$this->writeLog("[exAuth] invalid user: ". $sUser);
fwrite(STDOUT, pack("nn", 2, 0));
}
//$oResult->close();
} else {
$this->writeLog("[MySQL] invalid query: ". $sQuery);
fwrite(STDOUT, pack("nn", 2, 0));
}
}
break;
case "auth":
// provjeravamo autentifikaciju korisnika
if (sizeof($aCommand) != 4){
$this->writeLog("[exAuth] invalid auth command, data missing");
fwrite(STDOUT, pack("nn", 2, 0));
} else {
// ovdje provjeri prijavu
$sUser = str_replace(array("%20", "(a)"), array(" ", "@"), $aCommand[1]);
$this->writeDebugLog("[debug] doing auth for ". $sUser);
//$sQuery = "SELECT `uid`, `password` FROM `user` WHERE `password`='".hash('whirlpool',$aCommand[3])."' AND `nickname`='". $db->escape($sUser) ."'";
$sQuery = "SELECT `uid`, `password` FROM `user` WHERE `nickname`='". $db->escape($sUser) ."'";
$this->writeDebugLog("[debug] using query ". $sQuery);
if ($oResult = q($sQuery)){
$uid = $oResult[0]["uid"];
$Error = ($oResult[0]["password"] != hash('whirlpool',$aCommand[3]));
/*
if ($oResult[0]["password"] == hash('whirlpool',$aCommand[3])) {
// korisnik OK
$this->writeLog("[exAuth] authentificated user ". $sUser ."@". $aCommand[2]);
fwrite(STDOUT, pack("nn", 2, 1));
} else {
// korisnik nije OK
$this->writeLog("[exAuth] authentification failed for user ". $sUser ."@". $aCommand[2]);
fwrite(STDOUT, pack("nn", 2, 0));
}
$oResult->close();
*/
} else {
$this->writeLog("[MySQL] invalid query: ". $sQuery);
$Error = true;
$uid = -1;
}
if ($Error) {
$oConfig = q("SELECT `v` FROM `pconfig` WHERE `uid`=%d AND `cat` = 'xmpp' AND `k`='password' LIMIT 1;", intval($uid));
$this->writeLog("[exAuth] got password ".$oConfig[0]["v"]);
$Error = ($aCommand[3] != $oConfig[0]["v"]);
}
if ($Error) {
$this->writeLog("[exAuth] authentification failed for user ". $sUser ."@". $aCommand[2]);
fwrite(STDOUT, pack("nn", 2, 0));
} else {
$this->writeLog("[exAuth] authentificated user ". $sUser ."@". $aCommand[2]);
fwrite(STDOUT, pack("nn", 2, 1));
}
}
break;
case "setpass":
// postavljanje zaporke, onemoguceno
$this->writeLog("[exAuth] setpass command disabled");
fwrite(STDOUT, pack("nn", 2, 0));
break;
default:
// ako je uhvaceno ista drugo
$this->writeLog("[exAuth] unknown command ". $aCommand[0]);
fwrite(STDOUT, pack("nn", 2, 0));
break;
}
} else {
$this->writeDebugLog("[debug] invalid command string");
fwrite(STDOUT, pack("nn", 2, 0));
}
// Quit if the database connection went down
if (!$db->connected()) {
$this->writeDebugLog("[debug] the database connection went down");
return;
}
$iHeader = fgets(STDIN, 3);
$aLength = unpack("n", $iHeader);
$iLength = $aLength["1"];
// No data? Then quit
if ($iLength == 0) {
$this->writeDebugLog("[debug] we got no data");
return;
}
// Fetching the data
$sData = fgets(STDIN, $iLength + 1);
$this->writeDebugLog("[debug] received data: ". $sData);
$aCommand = explode(":", $sData);
if (is_array($aCommand)) {
switch ($aCommand[0]) {
case "isuser":
// Check the existance of a given username
$this->isuser($aCommand);
break;
case "auth":
// Check if the givven password is correct
$this->auth($aCommand);
break;
case "setpass":
// We don't accept the setting of passwords here
$this->writeLog("[exAuth] setpass command disabled");
fwrite(STDOUT, pack("nn", 2, 0));
break;
default:
// We don't know the given command
$this->writeLog("[exAuth] unknown command ". $aCommand[0]);
fwrite(STDOUT, pack("nn", 2, 0));
break;
}
} else {
$this->writeDebugLog("[debug] invalid command string");
fwrite(STDOUT, pack("nn", 2, 0));
}
unset ($iHeader);
unset ($aLength);
unset ($iLength);
unset($aCommand);
} while (true);
}
public function __destruct()
{
// zatvori log file
/**
* @brief Check if the given username exists
*
* @param array $aCommand The command array
*/
private function isuser($aCommand) {
global $a;
// Check if there is a username
if (!isset($aCommand[1])) {
$this->writeLog("[exAuth] invalid isuser command, no username given");
fwrite(STDOUT, pack("nn", 2, 0));
return;
}
// Now we check if the given user is valid
$sUser = str_replace(array("%20", "(a)"), array(" ", "@"), $aCommand[1]);
$this->writeDebugLog("[debug] checking isuser for ". $sUser."@".$aCommand[2]);
// If the hostnames doesn't match, we try to check remotely
if ($a->get_hostname() != $aCommand[2])
$found = $this->check_user($aCommand[2], $aCommand[1], true);
else {
$sQuery = "SELECT `uid` FROM `user` WHERE `nickname`='".dbesc($sUser)."'";
$this->writeDebugLog("[debug] using query ". $sQuery);
$r = q($sQuery);
$found = dbm::is_result($r);
}
if ($found) {
// The user is okay
$this->writeLog("[exAuth] valid user: ". $sUser);
fwrite(STDOUT, pack("nn", 2, 1));
} else {
// The user isn't okay
$this->writeLog("[exAuth] invalid user: ". $sUser);
fwrite(STDOUT, pack("nn", 2, 0));
}
}
/**
* @brief Check remote user existance via HTTP(S)
*
* @param string $host The hostname
* @param string $user Username
* @param boolean $ssl Should the check be done via SSL?
*
* @return boolean Was the user found?
*/
private function check_user($host, $user, $ssl) {
$url = ($ssl ? "https":"http")."://".$host."/noscrape/".$user;
$data = z_fetch_url($url);
if (!is_array($data))
return(false);
if ($data["return_code"] != "200")
return(false);
$json = @json_decode($data["body"]);
if (!is_object($json))
return(false);
return($json->nick == $user);
}
/**
* @brief Authenticate the givven user and password
*
* @param array $aCommand The command array
*/
private function auth($aCommand) {
global $a;
// check user authentication
if (sizeof($aCommand) != 4) {
$this->writeLog("[exAuth] invalid auth command, data missing");
fwrite(STDOUT, pack("nn", 2, 0));
return;
}
// We now check if the password match
$sUser = str_replace(array("%20", "(a)"), array(" ", "@"), $aCommand[1]);
$this->writeDebugLog("[debug] doing auth for ".$sUser."@".$aCommand[2]);
// If the hostnames doesn't match, we try to authenticate remotely
if ($a->get_hostname() != $aCommand[2])
$Error = !$this->check_credentials($aCommand[2], $aCommand[1], $aCommand[3], true);
else {
$sQuery = "SELECT `uid`, `password` FROM `user` WHERE `nickname`='".dbesc($sUser)."'";
$this->writeDebugLog("[debug] using query ". $sQuery);
if ($oResult = q($sQuery)) {
$uid = $oResult[0]["uid"];
$Error = ($oResult[0]["password"] != hash('whirlpool',$aCommand[3]));
} else {
$this->writeLog("[MySQL] invalid query: ". $sQuery);
$Error = true;
$uid = -1;
}
if ($Error) {
$oConfig = q("SELECT `v` FROM `pconfig` WHERE `uid` = %d AND `cat` = 'xmpp' AND `k`='password' LIMIT 1;", intval($uid));
$this->writeLog("[exAuth] got password ".$oConfig[0]["v"]);
$Error = ($aCommand[3] != $oConfig[0]["v"]);
}
}
if ($Error) {
$this->writeLog("[exAuth] authentification failed for user ".$sUser."@". $aCommand[2]);
fwrite(STDOUT, pack("nn", 2, 0));
} else {
$this->writeLog("[exAuth] authentificated user ".$sUser."@".$aCommand[2]);
fwrite(STDOUT, pack("nn", 2, 1));
}
}
/**
* @brief Check remote credentials via HTTP(S)
*
* @param string $host The hostname
* @param string $user Username
* @param string $password Password
* @param boolean $ssl Should the check be done via SSL?
*
* @return boolean Are the credentials okay?
*/
private function check_credentials($host, $user, $password, $ssl) {
$this->writeDebugLog("[debug] check credentials for user ".$user." on ".$host);
$url = ($ssl ? "https":"http")."://".$host."/api/account/verify_credentials.json";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, $user.':'.$password);
$header = curl_exec($ch);
$curl_info = @curl_getinfo($ch);
$http_code = $curl_info["http_code"];
curl_close($ch);
$this->writeDebugLog("[debug] got HTTP code ".$http_code);
return ($http_code == 200);
}
/**
* @brief write data to the logfile
*
* @param string $sMessage The logfile message
*/
private function writeLog($sMessage) {
if (is_resource($this->rLogFile))
fwrite($this->rLogFile, date("r")." ".$sMessage."\n");
}
/**
* @brief write debug data to the logfile
*
* @param string $sMessage The logfile message
*/
private function writeDebugLog($sMessage) {
if ($this->bDebug)
$this->writeLog($sMessage);
}
/**
* @brief destroy the class
*/
public function __destruct() {
// close the log file
$this->writeLog("[exAuth] stop");
if (is_resource($this->rLogFile)){
if (is_resource($this->rLogFile))
fclose($this->rLogFile);
}
}
private function writeLog($sMessage)
{
if (is_resource($this->rLogFile)) {
fwrite($this->rLogFile, date("r") ." ". $sMessage ."\n");
}
}
private function writeDebugLog($sMessage)
{
if ($this->bDebug){
$this->writeLog($sMessage);
}
}
}
?>

View file

@ -15,24 +15,27 @@ require_once("library/html-to-markdown/HTML_To_Markdown.php");
function diaspora2bb($s) {
$s = html_entity_decode($s,ENT_COMPAT,'UTF-8');
$s = html_entity_decode($s, ENT_COMPAT, 'UTF-8');
// Remove CR to avoid problems with following code
$s = str_replace("\r","",$s);
// Handles single newlines
$s = str_replace("\r", '<br>', $s);
$s = str_replace("\n"," \n",$s);
$s = str_replace("\n", " \n", $s);
// Replace lonely stars in lines not starting with it with literal stars
$s = preg_replace('/^([^\*]+)\*([^\*]*)$/im', '$1\*$2', $s);
// The parser cannot handle paragraphs correctly
$s = str_replace(array("</p>", "<p>", '<p dir="ltr">'),array("<br>", "<br>", "<br>"),$s);
$s = str_replace(array('</p>', '<p>', '<p dir="ltr">'), array('<br>', '<br>', '<br>'), $s);
// Escaping the hash tags
$s = preg_replace('/\#([^\s\#])/','&#35;$1',$s);
$s = preg_replace('/\#([^\s\#])/', '&#35;$1', $s);
$s = Markdown($s);
$s = preg_replace('/\@\{(.+?)\; (.+?)\@(.+?)\}/','@[url=https://$3/u/$2]$1[/url]',$s);
$s = preg_replace('/\@\{(.+?)\; (.+?)\@(.+?)\}/', '@[url=https://$3/u/$2]$1[/url]', $s);
$s = str_replace('&#35;','#',$s);
$s = str_replace('&#35;', '#', $s);
$search = array(" \n", "\n ");
$replace = array("\n", "\n");
@ -41,23 +44,24 @@ function diaspora2bb($s) {
$s = str_replace($search, $replace, $s);
} while ($oldtext != $s);
$s = str_replace("\n\n", "<br>", $s);
$s = str_replace("\n\n", '<br>', $s);
$s = html2bbcode($s);
// protect the recycle symbol from turning into a tag, but without unescaping angles and naked ampersands
$s = str_replace('&#x2672;',html_entity_decode('&#x2672;',ENT_QUOTES,'UTF-8'),$s);
$s = str_replace('&#x2672;', html_entity_decode('&#x2672;', ENT_QUOTES, 'UTF-8'), $s);
// Convert everything that looks like a link to a link
$s = preg_replace("/([^\]\=]|^)(https?\:\/\/)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url=$2$3]$2$3[/url]',$s);
$s = preg_replace('/([^\]=]|^)(https?\:\/\/)([a-zA-Z0-9:\/\-?&;.=_~#%$!+,@]+(?<!,))/ism', '$1[url=$2$3]$2$3[/url]', $s);
//$s = preg_replace("/([^\]\=]|^)(https?\:\/\/)(vimeo|youtu|www\.youtube|soundcloud)([a-zA-Z0-9\:\/\-\?\&\;\.\=\_\~\#\%\$\!\+\,]+)/ism", '$1[url=$2$3$4]$2$3$4[/url]',$s);
$s = bb_tag_preg_replace("/\[url\=?(.*?)\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/url\]/ism",'[youtube]$2[/youtube]','url',$s);
$s = bb_tag_preg_replace("/\[url\=https?:\/\/www.youtube.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism",'[youtube]$1[/youtube]','url',$s);
$s = bb_tag_preg_replace("/\[url\=?(.*?)\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/url\]/ism",'[vimeo]$2[/vimeo]','url',$s);
$s = bb_tag_preg_replace("/\[url\=https?:\/\/vimeo.com\/([0-9]+)\](.*?)\[\/url\]/ism",'[vimeo]$1[/vimeo]','url',$s);
$s = bb_tag_preg_replace('/\[url\=?(.*?)\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/url\]/ism', '[youtube]$2[/youtube]', 'url', $s);
$s = bb_tag_preg_replace('/\[url\=https?:\/\/www.youtube.com\/watch\?v\=(.*?)\].*?\[\/url\]/ism' , '[youtube]$1[/youtube]', 'url', $s);
$s = bb_tag_preg_replace('/\[url\=?(.*?)\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/url\]/ism' , '[vimeo]$2[/vimeo]' , 'url', $s);
$s = bb_tag_preg_replace('/\[url\=https?:\/\/vimeo.com\/([0-9]+)\](.*?)\[\/url\]/ism' , '[vimeo]$1[/vimeo]' , 'url', $s);
// remove duplicate adjacent code tags
$s = preg_replace("/(\[code\])+(.*?)(\[\/code\])+/ism","[code]$2[/code]", $s);
$s = preg_replace('/(\[code\])+(.*?)(\[\/code\])+/ism', '[code]$2[/code]', $s);
// Don't show link to full picture (until it is fixed)
$s = scale_external_images($s, false);

View file

@ -1,4 +1,6 @@
<?php
use \Friendica\Core\Config;
require_once("include/oembed.php");
require_once('include/event.php');
require_once('include/map.php');
@ -146,7 +148,7 @@ function cleancss($input) {
if (($char >= "a") and ($char <= "z"))
$cleaned .= $char;
if (!(strpos(" #;:0123456789-_", $char) === false))
if (!(strpos(" #;:0123456789-_.%", $char) === false))
$cleaned .= $char;
}
@ -892,8 +894,7 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal
// we may need to restrict this further if it picks up too many strays
// link acct:user@host to a webfinger profile redirector
$Text = preg_replace('/acct:(.*?)@(.*?)([ ,])/', '<a href="' . $a->get_baseurl() . '/acctlink?addr=' . "$1@$2"
. '" target="extlink" >acct:' . "$1@$2$3" . '</a>',$Text);
$Text = preg_replace('/acct:([^@]+)@((?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63})/', '<a href="' . $a->get_baseurl() . '/acctlink?addr=$1@$2" target="extlink">acct:$1@$2</a>',$Text);
// Perform MAIL Search
$Text = preg_replace("/\[mail\]([$MAILSearchString]*)\[\/mail\]/", '<a href="mailto:$1">$1</a>', $Text);
@ -1162,11 +1163,24 @@ function bbcode($Text,$preserve_nl = false, $tryoembed = true, $simplehtml = fal
$Text = preg_replace('/\&quot\;/','"',$Text);
// fix any escaped ampersands that may have been converted into links
$Text = preg_replace("/\<([^>]*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism",'<$1$2=$3&$4>',$Text);
$Text = preg_replace("/\<([^>]*?)(src|href)=\"(?!http|ftp|mailto|gopher|cid)(.*?)\>/ism",'<$1$2="">',$Text);
$Text = preg_replace('/\<([^>]*?)(src|href)=(.*?)\&amp\;(.*?)\>/ism', '<$1$2=$3&$4>', $Text);
if($saved_image)
// sanitizes src attributes (only relative redir URIs or http URLs)
$Text = preg_replace('#<([^>]*?)(src)="(?!http|redir)(.*?)"(.*?)>#ism', '<$1$2=""$4 class="invalid-src" title="' . t('Invalid source protocol') . '">', $Text);
// sanitize href attributes (only whitelisted protocols URLs)
// default value for backward compatibility
$allowed_link_protocols = Config::get('system', 'allowed_link_protocols', array('ftp', 'mailto', 'gopher', 'cid'));
// Always allowed protocol even if config isn't set or not including it
$allowed_link_protocols[] = 'http';
$regex = '#<([^>]*?)(href)="(?!' . implode('|', $allowed_link_protocols) . ')(.*?)"(.*?)>#ism';
$Text = preg_replace($regex, '<$1$2="javascript:void(0)"$4 class="invalid-href" title="' . t('Invalid link protocol') . '">', $Text);
if($saved_image) {
$Text = bb_replace_images($Text, $saved_image);
}
// Clean up the HTML by loading and saving the HTML with the DOM.
// Bad structured html can break a whole page.

View file

@ -1,72 +1,210 @@
<?php
/**
* @file include/cache.php
*
* @brief Class for storing data for a short time
*/
use \Friendica\Core\Config;
use \Friendica\Core\PConfig;
class Cache {
/**
* cache api
* @brief Check for memcache and open a connection if configured
*
* @return object|boolean The memcache object - or "false" if not successful
*/
public static function memcache() {
if (!function_exists('memcache_connect')) {
return false;
}
class Cache {
public static function get($key) {
if (!Config::get('system', 'memcache')) {
return false;
}
$r = q("SELECT `v` FROM `cache` WHERE `k`='%s' limit 1",
dbesc($key)
);
$memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
$memcache_port = Config::get('system', 'memcache_port', 11211);
if (count($r))
return $r[0]['v'];
$memcache = new Memcache;
if (!$memcache->connect($memcache_host, $memcache_port)) {
return false;
}
return $memcache;
}
/**
* @brief Return the duration for a given cache level
*
* @param integer $level Cache level
*
* @return integer The cache duration in seconds
*/
private function duration($level) {
switch($level) {
case CACHE_MONTH;
$seconds = 2592000;
break;
case CACHE_WEEK;
$seconds = 604800;
break;
case CACHE_DAY;
$seconds = 86400;
break;
case CACHE_HOUR;
$seconds = 3600;
break;
case CACHE_HALF_HOUR;
$seconds = 1800;
break;
case CACHE_QUARTER_HOUR;
$seconds = 900;
break;
case CACHE_FIVE_MINUTES;
$seconds = 300;
break;
case CACHE_MINUTE;
$seconds = 60;
break;
}
return $seconds;
}
/**
* @brief Fetch cached data according to the key
*
* @param string $key The key to the cached data
*
* @return mixed Cached $value or "null" if not found
*/
public static function get($key) {
$memcache = self::memcache();
if (is_object($memcache)) {
// We fetch with the hostname as key to avoid problems with other applications
$cached = $memcache->get(get_app()->get_hostname().":".$key);
$value = @unserialize($cached);
// Only return a value if the serialized value is valid.
// We also check if the db entry is a serialized
// boolean 'false' value (which we want to return).
if ($cached === serialize(false) || $value !== false) {
return $value;
}
return null;
}
public static function set($key,$value, $duration = CACHE_MONTH) {
// Frequently clear cache
self::clear($duration);
q("REPLACE INTO `cache` (`k`,`v`,`expire_mode`,`updated`) VALUES ('%s','%s',%d,'%s')",
dbesc($key),
dbesc($value),
intval($duration),
dbesc(datetime_convert()));
$r = q("SELECT `v` FROM `cache` WHERE `k`='%s' LIMIT 1",
dbesc($key)
);
if (dbm::is_result($r)) {
$cached = $r[0]['v'];
$value = @unserialize($cached);
// Only return a value if the serialized value is valid.
// We also check if the db entry is a serialized
// boolean 'false' value (which we want to return).
if ($cached === serialize(false) || $value !== false) {
return $value;
}
}
/*
*
* Leaving this legacy code temporaily to see how REPLACE fares
* as opposed to non-atomic checks when faced with fast moving key duplication.
* As a MySQL extension it isn't portable, but we're not yet very portable.
*/
/*
* $r = q("SELECT * FROM `cache` WHERE `k`='%s' limit 1",
* dbesc($key)
* );
* if(count($r)) {
* q("UPDATE `cache` SET `v` = '%s', `updated = '%s' WHERE `k` = '%s'",
* dbesc($value),
* dbesc(datetime_convert()),
* dbesc($key));
* }
* else {
* q("INSERT INTO `cache` (`k`,`v`,`updated`) VALUES ('%s','%s','%s')",
* dbesc($key),
* dbesc($value),
* dbesc(datetime_convert()));
* }
* }
*/
public static function clear(){
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 30 days")), intval(CACHE_MONTH));
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 7 days")), intval(CACHE_WEEK));
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 1 days")), intval(CACHE_DAY));
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 1 hours")), intval(CACHE_HOUR));
}
return null;
}
/**
* @brief Put data in the cache according to the key
*
* The input $value can have multiple formats.
*
* @param string $key The key to the cached data
* @param mixed $valie The value that is about to be stored
* @param integer $duration The cache lifespan
*/
public static function set($key, $value, $duration = CACHE_MONTH) {
// Do we have an installed memcache? Use it instead.
$memcache = self::memcache();
if (is_object($memcache)) {
// We store with the hostname as key to avoid problems with other applications
$memcache->set(get_app()->get_hostname().":".$key, serialize($value), MEMCACHE_COMPRESSED, self::duration($duration));
return;
}
/// @todo store the cache data in the same way like the config data
q("REPLACE INTO `cache` (`k`,`v`,`expire_mode`,`updated`) VALUES ('%s','%s',%d,'%s')",
dbesc($key),
dbesc(serialize($value)),
intval($duration),
dbesc(datetime_convert()));
}
/**
* @brief Remove outdated data from the cache
*
* @param integer $maxlevel The maximum cache level that is to be cleared
*/
public static function clear($max_level = CACHE_MONTH) {
// Clear long lasting cache entries only once a day
if (get_config("system", "cache_cleared_day") < time() - self::duration(CACHE_DAY)) {
if ($max_level == CACHE_MONTH) {
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 30 days")), intval(CACHE_MONTH));
}
if ($max_level <= CACHE_WEEK) {
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 7 days")), intval(CACHE_WEEK));
}
if ($max_level <= CACHE_DAY) {
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 1 days")), intval(CACHE_DAY));
}
set_config("system", "cache_cleared_day", time());
}
if (($max_level <= CACHE_HOUR) AND (get_config("system", "cache_cleared_hour")) < time() - self::duration(CACHE_HOUR)) {
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 1 hours")), intval(CACHE_HOUR));
set_config("system", "cache_cleared_hour", time());
}
if (($max_level <= CACHE_HALF_HOUR) AND (get_config("system", "cache_cleared_half_hour")) < time() - self::duration(CACHE_HALF_HOUR)) {
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 30 minutes")), intval(CACHE_HALF_HOUR));
set_config("system", "cache_cleared_half_hour", time());
}
if (($max_level <= CACHE_QUARTER_HOUR) AND (get_config("system", "cache_cleared_hour")) < time() - self::duration(CACHE_QUARTER_HOUR)) {
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
dbesc(datetime_convert('UTC','UTC',"now - 15 minutes")), intval(CACHE_QUARTER_HOUR));
set_config("system", "cache_cleared_quarter_hour", time());