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:
commit
d489ba1510
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -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
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
36
Vagrantfile
vendored
36
Vagrantfile
vendored
|
@ -1,14 +1,16 @@
|
|||
|
||||
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
|
||||
|
@ -18,14 +20,36 @@ Vagrant.configure(2) do |config|
|
|||
# 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"
|
||||
trusty.vm.hostname = "friendica-trusty.dev"
|
||||
|
||||
# Create a static IP
|
||||
config.vm.network :private_network, ip: server_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|
|
||||
|
|
249
boot.php
249
boot.php
|
@ -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.
|
||||
|
@ -127,6 +127,10 @@ 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 );
|
||||
/* @}*/
|
||||
|
||||
/**
|
||||
|
@ -181,6 +185,28 @@ 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
|
||||
*
|
||||
|
@ -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 (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 ($ssl) {
|
||||
$scheme = 'https';
|
||||
else
|
||||
} else {
|
||||
$scheme = 'http';
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (get_config('config', 'hostname') != '') {
|
||||
$this->hostname = get_config('config', 'hostname');
|
||||
}
|
||||
|
||||
$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'))
|
||||
}
|
||||
if (x($parsed, 'path')) {
|
||||
$this->path = trim($parsed['path'], '\\/');
|
||||
|
||||
if (file_exists(".htpreconfig.php"))
|
||||
@include(".htpreconfig.php");
|
||||
|
||||
if (get_config('config','hostname') != "")
|
||||
$this->hostname = get_config('config','hostname');
|
||||
|
||||
if (!isset($this->hostname) OR ($this->hostname == ""))
|
||||
$this->hostname = $hostname;
|
||||
}
|
||||
|
||||
if (file_exists(".htpreconfig.php")) {
|
||||
@include(".htpreconfig.php");
|
||||
}
|
||||
|
||||
if (get_config('config', 'hostname') != '') {
|
||||
$this->hostname = get_config('config', 'hostname');
|
||||
}
|
||||
|
||||
if (!isset($this->hostname) OR ($this->hostname == '')) {
|
||||
$this->hostname = $hostname;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function get_hostname() {
|
||||
|
@ -975,21 +1043,29 @@ 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);
|
||||
$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
|
||||
|
@ -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,24 +1188,34 @@ 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove the active process from the "process" table
|
||||
|
@ -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.
|
||||
|
@ -1823,11 +1911,12 @@ 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 ""
|
||||
*
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
// 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($args as $arg) {
|
||||
foreach ($proc_args as $arg) {
|
||||
if (is_array($arg)) {
|
||||
foreach ($arg as $n) {
|
||||
$newargs[] = $n;
|
||||
$args[] = $n;
|
||||
}
|
||||
} else {
|
||||
$args[] = $arg;
|
||||
}
|
||||
} else
|
||||
$newargs[] = $arg;
|
||||
}
|
||||
|
||||
$args = $newargs;
|
||||
// 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;
|
||||
$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'");
|
||||
|
@ -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();
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
45
database.sql
45
database.sql
|
@ -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,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
Accesskeys in Friendica
|
||||
=======================
|
||||
|
||||
* [Home](help)
|
||||
|
||||
General
|
||||
-------
|
||||
* p: profile
|
||||
|
|
728
doc/BBCode.md
728
doc/BBCode.md
|
@ -1,187 +1,556 @@
|
|||
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"> </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>
|
||||
[tr]<br>
|
||||
[th]Header 1[/th]<br>
|
||||
[th]Header 2[/th]<br>
|
||||
[th]Header 2[/th]<br>
|
||||
[/tr]<br>
|
||||
[tr]<br>
|
||||
[td]Cell 1[/td]<br>
|
||||
[td]Cell 2[/td]<br>
|
||||
[td]Cell 3[/td]<br>
|
||||
[/tr]<br>
|
||||
[tr]<br>
|
||||
[td]Cell 4[/td]<br>
|
||||
[td]Cell 5[/td]<br>
|
||||
[td]Cell 6[/td]<br>
|
||||
[/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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Block
|
||||
-----
|
||||
|
||||
<pre>[code]code[/code]</pre>
|
||||
|
||||
<code>code</code>
|
||||
|
||||
<p style="clear:both;"> </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"> </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;"> </p>
|
||||
|
||||
<pre>[quote]quote[/quote]</pre>
|
||||
|
||||
<blockquote>quote</blockquote>
|
||||
|
||||
<p style="clear:both;"> </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;"> </p>
|
||||
|
||||
<pre>[center]centered text[/center]</pre>
|
||||
|
||||
<div style="text-align:center;">centered text</div>
|
||||
|
||||
<p style="clear:both;"> </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;"> </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;"> </p>
|
||||
|
||||
**List**
|
||||
|
||||
<pre>[list]
|
||||
[*] First list element
|
||||
[*] Second list element
|
||||
[/list]</pre>
|
||||
<table class="bbcodes">
|
||||
<tr>
|
||||
<th>BBCode</th>
|
||||
<th>Result</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[ul]<br>
|
||||
[li] First list element<br>
|
||||
[li] Second list element<br>
|
||||
[/ul]<br>
|
||||
[list]<br>
|
||||
[*] First list element<br>
|
||||
[*] Second list element<br>
|
||||
[/list]</td>
|
||||
<td>
|
||||
<ul class="listbullet" style="list-style-type: circle;">
|
||||
<li> First list element<br>
|
||||
</li>
|
||||
<li>First list element</li>
|
||||
<li>Second list element</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[ol]<br>
|
||||
[*] First list element<br>
|
||||
[*] Second list element<br>
|
||||
[/ol]<br>
|
||||
[list=1]<br>
|
||||
[*] First list element<br>
|
||||
[*] 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>
|
||||
[*] First list element<br>
|
||||
[*] 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>
|
||||
[*] First list element<br>
|
||||
[*] 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>
|
||||
[*] First list element<br>
|
||||
[*] 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>
|
||||
[*] First list element<br>
|
||||
[*] 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>
|
||||
[*] First list element<br>
|
||||
[*] 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>
|
||||
|
||||
[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>
|
||||
|
||||
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)
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
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 ..."
|
||||
<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]
|
||||
<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]
|
||||
pictures?[/abstract]<br>
|
||||
[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.
|
||||
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.
|
||||
|
||||
|
@ -189,20 +558,59 @@ 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><span class="custom">If the class exists,<br> this block will have the custom class<br> style applied.</span></pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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**
|
||||
|
|
|
@ -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
42
doc/Protocol.md
Normal 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)
|
63
doc/SSL.md
63
doc/SSL.md
|
@ -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
|
||||
---
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
35
doc/api.md
35
doc/api.md
|
@ -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)
|
||||
|
|
|
@ -2,7 +2,7 @@ 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 | |
|
||||
|
@ -10,13 +10,15 @@ Table notify
|
|||
| url | | varchar(255) | NO | | | |
|
||||
| photo | | varchar(255) | NO | | | |
|
||||
| date | | datetime | NO | | 0000-00-00 00:00:00 | |
|
||||
| msg | | mediumtext | NO | | NULL | |
|
||||
| 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 | | | |
|
||||
| iid | item.id | int(11) | NO | | 0 | |
|
||||
| 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)
|
||||
|
|
739
doc/de/BBCode.md
739
doc/de/BBCode.md
|
@ -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%;
|
||||
}
|
||||
|
||||
<pre>[b]fett[/b]</pre> : <strong>fett</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]kursiv[/i]</pre> : <em>kursiv</em>
|
||||
table.bbcodes > tr > th,
|
||||
table.bbcodes > * > tr > th {
|
||||
background-color: #f2f2f2;
|
||||
text-align: center;
|
||||
width: 50%
|
||||
}
|
||||
</style>
|
||||
|
||||
<pre>[u]unterstrichen[/u]</pre> : <u>unterstrichen</u>
|
||||
<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]überstrichen[/o]</td>
|
||||
<td><span class="overline">ü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ßer Text[/size]</td>
|
||||
<td><span style="font-size: xx-large;">großer Text</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[size=20]exakte Größe[/size] (die Größe kann beliebig in Pixeln gewält werden)</td>
|
||||
<td><span style="font-size: 20px;">exakte Größe</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[font=serif]Serife Schriftart[/font]</td>
|
||||
<td><span style="font-family: serif;">Serife Schriftart</span></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<pre>[s]durchgestrichen[/s]</pre> : <strike>durchgestrichen</strike>
|
||||
### Links
|
||||
|
||||
<pre>[color=red]rot[/color]</pre> : <span style="color: red;">rot</span>
|
||||
<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ähnung</td>
|
||||
<td>@<a href="javascript:void(0)">Erwä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>
|
||||
|
||||
<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>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>über<br>mehrere<br>Zeilen[/code]</td>
|
||||
<td><code>Programmcode
|
||||
ü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"> </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 öffnen/schließ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 öffnen/schließ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>
|
||||
|
||||
<pre>[size=xx-small]kleiner Text[/size]</pre> : <span style="font-size: xx-small;">kleiner Text</span>
|
||||
### Überschriften
|
||||
|
||||
<pre>[size=xx-large]groß Text[/size]</pre> : <span style="font-size: xx-large;">großer Text</span>
|
||||
<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>
|
||||
|
||||
<pre>[size=20]exakte Textgröße[/size] (Textgröße kann jede Zahl sein, in Pixeln)</pre> : <span style="font-size: 20px;">exakte Größe</span>
|
||||
### Tabellen
|
||||
|
||||
<table class="bbcodes">
|
||||
<tr>
|
||||
<th>BBCode</th>
|
||||
<th>Ergebnis</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[table]<br>
|
||||
[tr]<br>
|
||||
[th]Kopfzeile 1[/th]<br>
|
||||
[th]Kopfzeile 2[/th]<br>
|
||||
[th]Kopfzeile 2[/th]<br>
|
||||
[/tr]<br>
|
||||
[tr]<br>
|
||||
[td]Zelle 1[/td]<br>
|
||||
[td]Zelle 2[/td]<br>
|
||||
[td]Zelle 3[/td]<br>
|
||||
[/tr]<br>
|
||||
[tr]<br>
|
||||
[td]Zelle 4[/td]<br>
|
||||
[td]Zelle 5[/td]<br>
|
||||
[td]Zelle 6[/td]<br>
|
||||
[/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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Block Tags
|
||||
-----
|
||||
|
||||
<pre>[code]Code[/code]</pre>
|
||||
|
||||
<code>Code</code>
|
||||
|
||||
<p style="clear:both;"> </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"> </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;"> </p>
|
||||
|
||||
<pre>[quote]Zitat[/quote]</pre>
|
||||
|
||||
<blockquote>Zitat</blockquote>
|
||||
|
||||
<p style="clear:both;"> </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;"> </p>
|
||||
|
||||
<pre>[center]zentrierter Text[/center]</pre>
|
||||
|
||||
<div style="text-align:center;">zentrierter Text</div>
|
||||
|
||||
<p style="clear:both;"> </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;"> </p>
|
||||
|
||||
**Tabelle**
|
||||
<pre>[table border=1]
|
||||
[tr]
|
||||
[th]Tabellenzeile[/th]
|
||||
[/tr]
|
||||
[tr]
|
||||
[td]haben Überschriften[/td]
|
||||
[/tr]
|
||||
[/table]</pre>
|
||||
|
||||
<table border="1"><tbody><tr><th>Tabellenzeile</th></tr><tr><td>haben Überschriften</td></tr></tbody></table>
|
||||
|
||||
<p style="clear:both;"> </p>
|
||||
|
||||
**Listen**
|
||||
|
||||
<pre>[list]
|
||||
[*] Erstes Listenelement
|
||||
[*] Zweites Listenelement
|
||||
[/list]</pre>
|
||||
<table class="bbcodes">
|
||||
<tr>
|
||||
<th>BBCode</th>
|
||||
<th>Ergebnis</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[ul]<br>
|
||||
[li] Erstes Listenelement<br>
|
||||
[li] Zweites Listenelement<br>
|
||||
[/ul]<br>
|
||||
[list]<br>
|
||||
[*] Erstes Listenelement<br>
|
||||
[*] Zweites Listenelement<br>
|
||||
[/list]</td>
|
||||
<td>
|
||||
<ul class="listbullet" style="list-style-type: circle;">
|
||||
<li> Erstes Listenelement<br>
|
||||
</li>
|
||||
<li>Erstes Listenelement</li>
|
||||
<li>Zweites Listenelement</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[ol]<br>
|
||||
[*] Erstes Listenelement<br>
|
||||
[*] Zweites Listenelement<br>
|
||||
[/ol]<br>
|
||||
[list=1]<br>
|
||||
[*] Erstes Listenelement<br>
|
||||
[*] 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>
|
||||
[*] Erstes Listenelement<br>
|
||||
[*] 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>
|
||||
[*] Erstes Listenelement<br>
|
||||
[*] 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>
|
||||
[*] Erstes Listenelement<br>
|
||||
[*] 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>
|
||||
[*] Erstes Listenelement<br>
|
||||
[*] 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>
|
||||
[*] Erstes Listenelement<br>
|
||||
[*] 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>
|
||||
|
||||
[list] ist Equivalent zu [ul] (unsortierte Liste).
|
||||
## Einbetten
|
||||
|
||||
[ol] kann anstelle von [list] verwendet werden um eine sortierte Liste zu erzeugen:
|
||||
Du kannst Videos, Musikdateien und weitere Dinge in Beiträgen einbinden.
|
||||
|
||||
<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>
|
||||
<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ü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ützt, wird das Objekt eingebettet (z.B. Dokumente von scribd).
|
||||
Ansonsten wird der Titel der Seite mit der URL verlinkt.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Für weitere Optionen von sortierten Listen kann man den Stil der Numerierung der Liste definieren:
|
||||
<pre>[list=1]</pre> : dezimal
|
||||
## Karten
|
||||
|
||||
<pre>[list=i]</pre> : römisch, Kleinbuchstaben
|
||||
Das Einbetten von Karten benötigt das "openstreetmap" oder das "Google Maps" Addon.
|
||||
Wenn keines der Addons aktiv ist, werden stattdeßen die Kordinaten angezeigt-
|
||||
|
||||
<pre>[list=I]</pre> : römisch, Großbuchstaben
|
||||
<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>
|
||||
|
||||
<pre>[list=a]</pre> : alphabetisch, Kleinbuchstaben
|
||||
## Zusammenfassungen für lange Beiträge
|
||||
|
||||
<pre>[list=A] </pre> : alphabethisch, Großbuchstaben
|
||||
Wenn du deine Beiträge auf anderen Netzwerken von Drittanbietern verbreiten möchtest, z.B. Twitter, kö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öchte euch eine unglaublich langweilige Geschichte erzählen, die ihr sicherlich niemals hören wolltet.</td>
|
||||
<td>Auf Twitter würde folgender Text verlffentlicht werden <blockquote>Unglaublich interessant! Muss man gesehen haben! Unbedingt dem Link folgen!</blockquote>
|
||||
Wohingegen auf Friendica folgendes stehen würde <blockquote>Ich möchte euch eine unglaublich langweilige Geschichte erzählen, die ihr sicherlich niemals hören wolltet.</blockquote></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Wenn du magst, kannst du auch unterschiedliche Zusammenfassungen für die unterschiedlichen Netzwerke verwenden.
|
||||
|
||||
Einbettung von Inhalten
|
||||
------
|
||||
<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öne Bilder gemacht...</td>
|
||||
<td>Für Twitter und App.net wird Friendica in diesem Fall die speziell definierten Zusammenfassungen Verwenden. Für andere Netzwerke (wie z.B. bei der Verwendung des GNU Social Konnektors zum Veröffentlichen auf deinen GNU Social Account) würde die allgemeine Zusammenfassung verwenden.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Man kann viele Dinge, z.B. Video und Audio Dateine, in Nachrichten einbetten.
|
||||
Wenn du beispielsweise den "buffer"-Konnektor verwendest um Beiträge nach Facebook und Google+ zu senden, dort aber nicht den gesamten Blogbeitrag posten willst sondern nur einen Anreißer, kannst du dies mit dem [abstract]-Tag realisieren.
|
||||
|
||||
<pre>[video]url[/video]</pre>
|
||||
<pre>[audio]url[/audio]</pre>
|
||||
Bei Netzwerken wie Facebook oder Google+, die selbst kein Zeichenlimit haben wird das [abstract]-Element allerdings nicht grundsätzlich verwendet.
|
||||
Daher müssen diese Netzwerke explizit genannt werden.
|
||||
|
||||
Wobei die *url* von youtube, vimeo, soundcloud oder einer anderen Seite stammen kann die die oembed oder opengraph Spezifikationen unterstützt.
|
||||
Außerdem kann *url* die genaue url zu einer ogg Datei sein, die dann per HTML5 eingebunden wird.
|
||||
<table class="bbcodes">
|
||||
<tr>
|
||||
<th>BBCode</th>
|
||||
<th>Ergebnis</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
[abstract]Dieser Tage hatte ich eine ungewö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önes paßiert.[/abstract]<br>
|
||||
Als ich die Bilder im Wald aufgenommen habe, hatte ich eine wirklich ungewöhnliche Begegnung...</td>
|
||||
<td>Auf Google und Facebook würde nun die entsprechende Zusammenfassung verbreitet. Für andere Netzwerke würde die allgemeine Zusammenfassung verwendet werden.<br>
|
||||
<br>Auf Friendica wird weiterhin keine Zusammenfassung angezeigt.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<pre>[url]*url*[/url]</pre>
|
||||
Fü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ßt zu z.B. Friendica, Hubzilla, Diaspora oder GNU Social Kontakten; wird der ungekürzte Beitrag übertragen.
|
||||
Die Instanz des Kontakts kümmert sich um die Darstellung.
|
||||
|
||||
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.
|
||||
## Special
|
||||
|
||||
Um eine Karte in einen Beitrag einzubinden, muss das *openstreetmap* Addon aktiviert werden. Ist dies der Fall, kann mit
|
||||
<table class="bbcodes">
|
||||
<tr>
|
||||
<th>BBCode</th>
|
||||
<th>Ergebnis</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Wenn du verhindern möchtest, daß 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ü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 ändern-[/style]</td>
|
||||
<td><span style="text-shadow: 0 0 4px #cc0000;;">Du kannst alle CSS-Eigenschaften eines Blocks ä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><span class="custom">Wenn die<br>
|
||||
vergebene Klasse in den CSS Anweisungen<br>
|
||||
existiert,wird sie angewandt.</span></pre></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<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 über BBCode Tags in einer Nachricht schreiben möchtest, kannst Du [noparse], [nobb] oder [pre] verwenden um den BBCode Tags vor der Evaluierung zu schützen:
|
||||
|
||||
<pre>[noparse][b]fett[/b][/noparse]</pre> : [b]fett[/b]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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**
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
---
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
147
doc/htconfig.md
147
doc/htconfig.md
|
@ -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
BIN
doc/img/editor_frio.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -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
34
doc/upgrade.md
Normal 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.
|
|
@ -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');
|
||||
|
|
|
@ -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'])
|
||||
);
|
||||
|
||||
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 {
|
||||
} 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,43 +319,46 @@ 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) {
|
||||
|
||||
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;
|
||||
|
@ -332,40 +369,43 @@ function contact_photo_menu($contact, $uid = 0) {
|
|||
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')
|
||||
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";
|
||||
$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)))
|
||||
if (in_array($contact['network'], array(NETWORK_DFRN, NETWORK_DIASPORA))) {
|
||||
$pm_url = $a->get_baseurl() . '/message/new/' . $contact['id'];
|
||||
}
|
||||
|
||||
if ($contact["network"] == NETWORK_DFRN)
|
||||
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),
|
||||
'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,6 +589,17 @@ function get_contact($url, $uid = 0) {
|
|||
|
||||
update_contact_avatar($data["photo"],$uid,$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"]),
|
||||
|
@ -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')) {
|
||||
$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;
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -32,7 +32,7 @@ class Config {
|
|||
public static function load($family) {
|
||||
global $a;
|
||||
|
||||
$r = q("SELECT `v`, `k` FROM `config` WHERE `cat` = '%s'", dbesc($family));
|
||||
$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'];
|
||||
|
@ -74,7 +74,7 @@ class Config {
|
|||
|
||||
global $a;
|
||||
|
||||
if(! $instore) {
|
||||
if (!$refresh) {
|
||||
// Looking if the whole family isn't set
|
||||
if (isset($a->config[$family])) {
|
||||
if ($a->config[$family] === '!<unset>!') {
|
||||
|
@ -90,30 +90,7 @@ class Config {
|
|||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
);
|
||||
|
@ -122,22 +99,9 @@ class Config {
|
|||
$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;
|
||||
}
|
||||
|
@ -161,45 +125,35 @@ class Config {
|
|||
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);
|
||||
|
||||
if ($stored == $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$a->config[$family][$key] = $value;
|
||||
|
||||
// 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' ) ",
|
||||
|
||||
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)
|
||||
);
|
||||
if($ret)
|
||||
return $value;
|
||||
return $ret;
|
||||
}
|
||||
|
||||
} else {
|
||||
$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);*/
|
||||
|
||||
if($ret)
|
||||
}
|
||||
if ($ret) {
|
||||
return $value;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
|
@ -218,17 +172,13 @@ class Config {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ class PConfig {
|
|||
*/
|
||||
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)
|
||||
);
|
||||
|
@ -67,7 +67,7 @@ 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>!') {
|
||||
|
@ -83,30 +83,7 @@ class PConfig {
|
|||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
@ -116,22 +93,9 @@ class PConfig {
|
|||
$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;
|
||||
}
|
||||
|
@ -158,39 +122,37 @@ class PConfig {
|
|||
|
||||
global $a;
|
||||
|
||||
$stored = self::get($uid, $family, $key);
|
||||
|
||||
if ($stored == $value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// manage array value
|
||||
$dbvalue = (is_array($value) ? serialize($value):$value);
|
||||
|
||||
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' ) ",
|
||||
|
||||
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)
|
||||
);
|
||||
if($ret)
|
||||
return $value;
|
||||
return $ret;
|
||||
}
|
||||
} 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)
|
||||
);
|
||||
}
|
||||
|
||||
$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($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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,7 +225,6 @@ 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':
|
||||
|
@ -233,7 +233,6 @@ class NotificationsManager {
|
|||
$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_when = relative_date($it['created']);
|
||||
$default_tpl = $tpl_item_comments;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -244,7 +243,6 @@ class NotificationsManager {
|
|||
? 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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -254,7 +252,7 @@ class NotificationsManager {
|
|||
$notif = array(
|
||||
'label' => 'like',
|
||||
'link' => $this->a->get_baseurl(true).'/display/'.$it['pguid'],
|
||||
'$image' => proxy_url($it['author-avatar'], false, PROXY_SIZE_MICRO),
|
||||
'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']
|
||||
|
@ -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
552
include/ParseUrl.php
Normal 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);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* @file include/Photo.php
|
||||
* @brief This file contains the Photo class for image processing
|
||||
*/
|
||||
|
||||
require_once("include/photos.php");
|
||||
|
||||
if(! class_exists("Photo")) {
|
||||
class Photo {
|
||||
|
||||
private $image;
|
||||
|
||||
/**
|
||||
/*
|
||||
* Put back gd stuff, not everybody have Imagick
|
||||
*/
|
||||
private $imagick;
|
||||
|
@ -16,14 +21,13 @@ class Photo {
|
|||
private $types;
|
||||
|
||||
/**
|
||||
* supported mimetypes and corresponding file extensions
|
||||
* @brief supported mimetypes and corresponding file extensions
|
||||
*/
|
||||
static function supportedTypes() {
|
||||
if (class_exists('Imagick')) {
|
||||
/**
|
||||
* Imagick::queryFormats won't help us a lot there...
|
||||
* At least, not yet, other parts of friendica uses this array
|
||||
*/
|
||||
|
||||
// Imagick::queryFormats won't help us a lot there...
|
||||
// At least, not yet, other parts of friendica uses this array
|
||||
$t = array(
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/png' => 'png',
|
||||
|
@ -32,7 +36,9 @@ class Photo {
|
|||
} else {
|
||||
$t = array();
|
||||
$t['image/jpeg'] ='jpg';
|
||||
if (imagetypes() & IMG_PNG) $t['image/png'] = 'png';
|
||||
if (imagetypes() & IMG_PNG) {
|
||||
$t['image/png'] = 'png';
|
||||
}
|
||||
}
|
||||
|
||||
return $t;
|
||||
|
@ -71,7 +77,8 @@ class Photo {
|
|||
}
|
||||
|
||||
/**
|
||||
* Maps Mime types to Imagick formats
|
||||
* @brief Maps Mime types to Imagick formats
|
||||
* @return arr With with image formats (mime type as key)
|
||||
*/
|
||||
public function get_FormatsMap() {
|
||||
$m = array(
|
||||
|
@ -87,13 +94,12 @@ class Photo {
|
|||
$this->image = new Imagick();
|
||||
try {
|
||||
$this->image->readImageBlob($data);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
} catch (Exception $e) {
|
||||
// Imagick couldn't use the data
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
* Setup the image to the format it will be saved to
|
||||
*/
|
||||
$map = $this->get_FormatsMap();
|
||||
|
@ -103,15 +109,16 @@ class Photo {
|
|||
// Always coalesce, if it is not a multi-frame image it won't hurt anyway
|
||||
$this->image = $this->image->coalesceImages();
|
||||
|
||||
/**
|
||||
/*
|
||||
* setup the compression here, so we'll do it only once
|
||||
*/
|
||||
switch($this->getType()){
|
||||
case "image/png":
|
||||
$quality = get_config('system', 'png_quality');
|
||||
if((! $quality) || ($quality > 9))
|
||||
if ((! $quality) || ($quality > 9)) {
|
||||
$quality = PNG_QUALITY;
|
||||
/**
|
||||
}
|
||||
/*
|
||||
* From http://www.imagemagick.org/script/command-line-options.php#quality:
|
||||
*
|
||||
* 'For the MNG and PNG image formats, the quality value sets
|
||||
|
@ -124,8 +131,9 @@ class Photo {
|
|||
break;
|
||||
case "image/jpeg":
|
||||
$quality = get_config('system', 'jpeg_quality');
|
||||
if((! $quality) || ($quality > 100))
|
||||
if ((! $quality) || ($quality > 100)) {
|
||||
$quality = JPEG_QUALITY;
|
||||
}
|
||||
$this->image->setCompressionQuality($quality);
|
||||
}
|
||||
|
||||
|
@ -139,7 +147,7 @@ class Photo {
|
|||
|
||||
$this->valid = false;
|
||||
$this->image = @imagecreatefromstring($data);
|
||||
if($this->image !== FALSE) {
|
||||
if ($this->image !== false) {
|
||||
$this->width = imagesx($this->image);
|
||||
$this->height = imagesy($this->image);
|
||||
$this->valid = true;
|
||||
|
@ -153,32 +161,38 @@ class Photo {
|
|||
}
|
||||
|
||||
public function is_valid() {
|
||||
if($this->is_imagick())
|
||||
return ($this->image !== FALSE);
|
||||
if ($this->is_imagick()) {
|
||||
return ($this->image !== false);
|
||||
}
|
||||
return $this->valid;
|
||||
}
|
||||
|
||||
public function getWidth() {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->is_imagick())
|
||||
if ($this->is_imagick()) {
|
||||
return $this->image->getImageWidth();
|
||||
}
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
public function getHeight() {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->is_imagick())
|
||||
if ($this->is_imagick()) {
|
||||
return $this->image->getImageHeight();
|
||||
}
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
public function getImage() {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_imagick()) {
|
||||
/* Clean it */
|
||||
|
@ -189,30 +203,34 @@ class Photo {
|
|||
}
|
||||
|
||||
public function getType() {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getExt() {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->types[$this->getType()];
|
||||
}
|
||||
|
||||
public function scaleImage($max) {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$width = $this->getWidth();
|
||||
$height = $this->getHeight();
|
||||
|
||||
$dest_width = $dest_height = 0;
|
||||
|
||||
if((! $width)|| (! $height))
|
||||
return FALSE;
|
||||
if ((! $width)|| (! $height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($width > $max && $height > $max) {
|
||||
|
||||
|
@ -222,25 +240,19 @@ class Photo {
|
|||
if ((($height * 9) / 16) > $width) {
|
||||
$dest_width = $max;
|
||||
$dest_height = intval(($height * $max) / $width);
|
||||
}
|
||||
|
||||
} elseif ($width > $height) {
|
||||
// else constrain both dimensions
|
||||
|
||||
elseif($width > $height) {
|
||||
$dest_width = $max;
|
||||
$dest_height = intval(($height * $max) / $width);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$dest_width = intval(($width * $max) / $height);
|
||||
$dest_height = $max;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if ($width > $max) {
|
||||
$dest_width = $max;
|
||||
$dest_height = intval(($height * $max) / $width);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if ($height > $max) {
|
||||
|
||||
// very tall image (greater than 16:9)
|
||||
|
@ -249,13 +261,11 @@ class Photo {
|
|||
if ((($height * 9) / 16) > $width) {
|
||||
$dest_width = $width;
|
||||
$dest_height = $height;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$dest_width = intval(($width * $max) / $height);
|
||||
$dest_height = $max;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$dest_width = $width;
|
||||
$dest_height = $height;
|
||||
}
|
||||
|
@ -264,7 +274,7 @@ class Photo {
|
|||
|
||||
|
||||
if ($this->is_imagick()) {
|
||||
/**
|
||||
/*
|
||||
* If it is not animated, there will be only one iteration here,
|
||||
* so don't bother checking
|
||||
*/
|
||||
|
@ -289,18 +299,22 @@ class Photo {
|
|||
$dest = imagecreatetruecolor($dest_width, $dest_height);
|
||||
imagealphablending($dest, false);
|
||||
imagesavealpha($dest, true);
|
||||
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
if ($this->type=='image/png') {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
}
|
||||
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
|
||||
if($this->image)
|
||||
if ($this->image) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
$this->image = $dest;
|
||||
$this->width = imagesx($this->image);
|
||||
$this->height = imagesy($this->image);
|
||||
}
|
||||
|
||||
public function rotate($degrees) {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_imagick()) {
|
||||
$this->image->setFirstIterator();
|
||||
|
@ -316,14 +330,19 @@ class Photo {
|
|||
}
|
||||
|
||||
public function flip($horiz = true, $vert = false) {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_imagick()) {
|
||||
$this->image->setFirstIterator();
|
||||
do {
|
||||
if($horiz) $this->image->flipImage();
|
||||
if($vert) $this->image->flopImage();
|
||||
if ($horiz) {
|
||||
$this->image->flipImage();
|
||||
}
|
||||
if ($vert) {
|
||||
$this->image->flopImage();
|
||||
}
|
||||
} while ($this->image->nextImage());
|
||||
return;
|
||||
}
|
||||
|
@ -361,19 +380,22 @@ class Photo {
|
|||
}
|
||||
|
||||
$this->image->setImageOrientation(imagick::ORIENTATION_TOPLEFT);
|
||||
return TRUE;
|
||||
return true;
|
||||
}
|
||||
// based off comment on http://php.net/manual/en/function.imagerotate.php
|
||||
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if( (! function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg') )
|
||||
if ((!function_exists('exif_read_data')) || ($this->getType() !== 'image/jpeg')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$exif = @exif_read_data($filename,null,true);
|
||||
if(! $exif)
|
||||
if (!$exif) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ort = $exif['IFD0']['Orientation'];
|
||||
|
||||
|
@ -421,8 +443,9 @@ class Photo {
|
|||
|
||||
|
||||
public function scaleImageUp($min) {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$width = $this->getWidth();
|
||||
|
@ -430,46 +453,47 @@ class Photo {
|
|||
|
||||
$dest_width = $dest_height = 0;
|
||||
|
||||
if((! $width)|| (! $height))
|
||||
return FALSE;
|
||||
if ((!$width)|| (!$height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($width < $min && $height < $min) {
|
||||
if ($width > $height) {
|
||||
$dest_width = $min;
|
||||
$dest_height = intval(($height * $min) / $width);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$dest_width = intval(($width * $min) / $height);
|
||||
$dest_height = $min;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if ($width < $min) {
|
||||
$dest_width = $min;
|
||||
$dest_height = intval(($height * $min) / $width);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if ($height < $min) {
|
||||
$dest_width = intval(($width * $min) / $height);
|
||||
$dest_height = $min;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$dest_width = $width;
|
||||
$dest_height = $height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($this->is_imagick())
|
||||
if ($this->is_imagick()) {
|
||||
return $this->scaleImage($dest_width, $dest_height);
|
||||
}
|
||||
|
||||
$dest = imagecreatetruecolor($dest_width, $dest_height);
|
||||
imagealphablending($dest, false);
|
||||
imagesavealpha($dest, true);
|
||||
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
if ($this->type=='image/png') {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
}
|
||||
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dest_width, $dest_height, $width, $height);
|
||||
if($this->image)
|
||||
if ($this->image) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
$this->image = $dest;
|
||||
$this->width = imagesx($this->image);
|
||||
$this->height = imagesy($this->image);
|
||||
|
@ -478,8 +502,9 @@ class Photo {
|
|||
|
||||
|
||||
public function scaleImageSquare($dim) {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_imagick()) {
|
||||
$this->image->setFirstIterator();
|
||||
|
@ -492,10 +517,13 @@ class Photo {
|
|||
$dest = imagecreatetruecolor($dim, $dim);
|
||||
imagealphablending($dest, false);
|
||||
imagesavealpha($dest, true);
|
||||
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
if ($this->type=='image/png') {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
}
|
||||
imagecopyresampled($dest, $this->image, 0, 0, 0, 0, $dim, $dim, $this->width, $this->height);
|
||||
if($this->image)
|
||||
if ($this->image) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
$this->image = $dest;
|
||||
$this->width = imagesx($this->image);
|
||||
$this->height = imagesy($this->image);
|
||||
|
@ -503,14 +531,15 @@ class Photo {
|
|||
|
||||
|
||||
public function cropImage($max, $x, $y, $w, $h) {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_imagick()) {
|
||||
$this->image->setFirstIterator();
|
||||
do {
|
||||
$this->image->cropImage($w, $h, $x, $y);
|
||||
/**
|
||||
/*
|
||||
* We need to remove the canva,
|
||||
* or the image is not resized to the crop:
|
||||
* http://php.net/manual/en/imagick.cropimage.php#97232
|
||||
|
@ -523,18 +552,22 @@ class Photo {
|
|||
$dest = imagecreatetruecolor($max, $max);
|
||||
imagealphablending($dest, false);
|
||||
imagesavealpha($dest, true);
|
||||
if ($this->type=='image/png') imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
if ($this->type=='image/png') {
|
||||
imagefill($dest, 0, 0, imagecolorallocatealpha($dest, 0, 0, 0, 127)); // fill with alpha
|
||||
}
|
||||
imagecopyresampled($dest, $this->image, 0, 0, $x, $y, $max, $max, $w, $h);
|
||||
if($this->image)
|
||||
if ($this->image) {
|
||||
imagedestroy($this->image);
|
||||
}
|
||||
$this->image = $dest;
|
||||
$this->width = imagesx($this->image);
|
||||
$this->height = imagesy($this->image);
|
||||
}
|
||||
|
||||
public function saveImage($path) {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$string = $this->imageString();
|
||||
|
||||
|
@ -546,8 +579,9 @@ class Photo {
|
|||
}
|
||||
|
||||
public function imageString() {
|
||||
if(!$this->is_valid())
|
||||
return FALSE;
|
||||
if (!$this->is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->is_imagick()) {
|
||||
/* Clean it */
|
||||
|
@ -556,7 +590,7 @@ class Photo {
|
|||
return $string;
|
||||
}
|
||||
|
||||
$quality = FALSE;
|
||||
$quality = false;
|
||||
|
||||
ob_start();
|
||||
|
||||
|
@ -566,15 +600,17 @@ class Photo {
|
|||
switch($this->getType()){
|
||||
case "image/png":
|
||||
$quality = get_config('system', 'png_quality');
|
||||
if((! $quality) || ($quality > 9))
|
||||
if ((!$quality) || ($quality > 9)) {
|
||||
$quality = PNG_QUALITY;
|
||||
imagepng($this->image,NULL, $quality);
|
||||
}
|
||||
imagepng($this->image, null, $quality);
|
||||
break;
|
||||
case "image/jpeg":
|
||||
$quality = get_config('system', 'jpeg_quality');
|
||||
if((! $quality) || ($quality > 100))
|
||||
if ((!$quality) || ($quality > 100)) {
|
||||
$quality = JPEG_QUALITY;
|
||||
imagejpeg($this->image,NULL,$quality);
|
||||
}
|
||||
imagejpeg($this->image, null, $quality);
|
||||
}
|
||||
$string = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
@ -586,23 +622,24 @@ class Photo {
|
|||
|
||||
public function store($uid, $cid, $rid, $filename, $album, $scale, $profile = 0, $allow_cid = '', $allow_gid = '', $deny_cid = '', $deny_gid = '') {
|
||||
|
||||
$r = q("select `guid` from photo where `resource-id` = '%s' and `guid` != '' limit 1",
|
||||
$r = q("SELECT `guid` FROM `photo` WHERE `resource-id` = '%s' AND `guid` != '' LIMIT 1",
|
||||
dbesc($rid)
|
||||
);
|
||||
if(count($r))
|
||||
if (dbm::is_result($r)) {
|
||||
$guid = $r[0]['guid'];
|
||||
else
|
||||
} else {
|
||||
$guid = get_guid();
|
||||
}
|
||||
|
||||
$x = q("select id from photo where `resource-id` = '%s' and uid = %d and `contact-id` = %d and `scale` = %d limit 1",
|
||||
$x = q("SELECT `id` FROM `photo` WHERE `resource-id` = '%s' AND `uid` = %d AND `contact-id` = %d AND `scale` = %d LIMIT 1",
|
||||
dbesc($rid),
|
||||
intval($uid),
|
||||
intval($cid),
|
||||
intval($scale)
|
||||
);
|
||||
if(count($x)) {
|
||||
if (dbm::is_result($x)) {
|
||||
$r = q("UPDATE `photo`
|
||||
set `uid` = %d,
|
||||
SET `uid` = %d,
|
||||
`contact-id` = %d,
|
||||
`guid` = '%s',
|
||||
`resource-id` = '%s',
|
||||
|
@ -621,7 +658,7 @@ class Photo {
|
|||
`allow_gid` = '%s',
|
||||
`deny_cid` = '%s',
|
||||
`deny_gid` = '%s'
|
||||
where id = %d",
|
||||
WHERE `id` = %d",
|
||||
|
||||
intval($uid),
|
||||
intval($cid),
|
||||
|
@ -644,8 +681,7 @@ class Photo {
|
|||
dbesc($deny_gid),
|
||||
intval($x[0]['id'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$r = q("INSERT INTO `photo`
|
||||
(`uid`, `contact-id`, `guid`, `resource-id`, `created`, `edited`, `filename`, type, `album`, `height`, `width`, `datasize`, `data`, `scale`, `profile`, `allow_cid`, `allow_gid`, `deny_cid`, `deny_gid`)
|
||||
VALUES (%d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, %d, '%s', %d, %d, '%s', '%s', '%s', '%s')",
|
||||
|
@ -670,9 +706,10 @@ class Photo {
|
|||
dbesc($deny_gid)
|
||||
);
|
||||
}
|
||||
|
||||
return $r;
|
||||
}
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -711,7 +748,9 @@ function guess_image_type($filename, $fromcurl=false) {
|
|||
$types = Photo::supportedTypes();
|
||||
$type = "image/jpeg";
|
||||
foreach ($types as $m => $e){
|
||||
if ($ext==$e) $type = $m;
|
||||
if ($ext == $e) {
|
||||
$type = $m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -733,10 +772,11 @@ function guess_image_type($filename, $fromcurl=false) {
|
|||
function update_contact_avatar($avatar, $uid, $cid, $force = false) {
|
||||
|
||||
$r = q("SELECT `avatar`, `photo`, `thumb`, `micro` FROM `contact` WHERE `id` = %d LIMIT 1", intval($cid));
|
||||
if (!$r)
|
||||
if (!dbm::is_result($r)) {
|
||||
return false;
|
||||
else
|
||||
} else {
|
||||
$data = array($r[0]["photo"], $r[0]["thumb"], $r[0]["micro"]);
|
||||
}
|
||||
|
||||
if (($r[0]["avatar"] != $avatar) OR $force) {
|
||||
$photos = import_profile_photo($avatar, $uid, $cid, true);
|
||||
|
@ -756,11 +796,11 @@ function import_profile_photo($photo,$uid,$cid, $quit_on_error = false) {
|
|||
|
||||
$a = get_app();
|
||||
|
||||
$r = q("select `resource-id` from photo where `uid` = %d and `contact-id` = %d and `scale` = 4 and `album` = 'Contact Photos' limit 1",
|
||||
$r = q("SELECT `resource-id` FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `scale` = 4 AND `album` = 'Contact Photos' LIMIT 1",
|
||||
intval($uid),
|
||||
intval($cid)
|
||||
);
|
||||
if(count($r) && strlen($r[0]['resource-id'])) {
|
||||
if (dbm::is_result($r) && strlen($r[0]['resource-id'])) {
|
||||
$hash = $r[0]['resource-id'];
|
||||
} else {
|
||||
$hash = photo_new_resource();
|
||||
|
@ -771,8 +811,9 @@ function import_profile_photo($photo,$uid,$cid, $quit_on_error = false) {
|
|||
$filename = basename($photo);
|
||||
$img_str = fetch_url($photo, true);
|
||||
|
||||
if ($quit_on_error AND ($img_str == ""))
|
||||
if ($quit_on_error AND ($img_str == "")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$type = guess_image_type($photo, true);
|
||||
$img = new Photo($img_str, $type);
|
||||
|
@ -796,17 +837,20 @@ function import_profile_photo($photo,$uid,$cid, $quit_on_error = false) {
|
|||
|
||||
$r = $img->store($uid, $cid, $hash, $filename, 'Contact Photos', 6);
|
||||
|
||||
if($r === false)
|
||||
if ($r === false) {
|
||||
$photo_failure = true;
|
||||
}
|
||||
|
||||
$photo = $a->get_baseurl() . '/photo/' . $hash . '-4.' . $img->getExt();
|
||||
$thumb = $a->get_baseurl() . '/photo/' . $hash . '-5.' . $img->getExt();
|
||||
$micro = $a->get_baseurl() . '/photo/' . $hash . '-6.' . $img->getExt();
|
||||
} else
|
||||
} else {
|
||||
$photo_failure = true;
|
||||
}
|
||||
|
||||
if($photo_failure AND $quit_on_error)
|
||||
if ($photo_failure AND $quit_on_error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($photo_failure) {
|
||||
$photo = $a->get_baseurl() . '/images/person-175.jpg';
|
||||
|
@ -823,17 +867,13 @@ function get_photo_info($url) {
|
|||
|
||||
$data = Cache::get($url);
|
||||
|
||||
// Unserialise to be able to check in the next step if the cached data is alright.
|
||||
if (!is_null($data))
|
||||
$data = unserialize($data);
|
||||
|
||||
if (is_null($data) OR !$data) {
|
||||
if (is_null($data) OR !$data OR !is_array($data)) {
|
||||
$img_str = fetch_url($url, true, $redirects, 4);
|
||||
$filesize = strlen($img_str);
|
||||
|
||||
if (function_exists("getimagesizefromstring"))
|
||||
if (function_exists("getimagesizefromstring")) {
|
||||
$data = getimagesizefromstring($img_str);
|
||||
else {
|
||||
} else {
|
||||
$tempfile = tempnam(get_temppath(), "cache");
|
||||
|
||||
$a = get_app();
|
||||
|
@ -845,10 +885,11 @@ function get_photo_info($url) {
|
|||
unlink($tempfile);
|
||||
}
|
||||
|
||||
if ($data)
|
||||
if ($data) {
|
||||
$data["size"] = $filesize;
|
||||
}
|
||||
|
||||
Cache::set($url, serialize($data));
|
||||
Cache::set($url, $data);
|
||||
}
|
||||
|
||||
return $data;
|
||||
|
@ -858,8 +899,9 @@ function scale_image($width, $height, $max) {
|
|||
|
||||
$dest_width = $dest_height = 0;
|
||||
|
||||
if((!$width) || (!$height))
|
||||
return FALSE;
|
||||
if ((!$width) || (!$height)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($width > $max && $height > $max) {
|
||||
|
||||
|
@ -905,10 +947,10 @@ function scale_image($width, $height, $max) {
|
|||
|
||||
function store_photo($a, $uid, $imagedata = "", $url = "") {
|
||||
$r = q("SELECT `user`.`nickname`, `user`.`page-flags`, `contact`.`id` FROM `user` INNER JOIN `contact` on `user`.`uid` = `contact`.`uid`
|
||||
WHERE `user`.`uid` = %d AND `user`.`blocked` = 0 and `contact`.`self` = 1 LIMIT 1",
|
||||
WHERE `user`.`uid` = %d AND `user`.`blocked` = 0 AND `contact`.`self` = 1 LIMIT 1",
|
||||
intval($uid));
|
||||
|
||||
if(!count($r)) {
|
||||
if (!dbm::is_result($r)) {
|
||||
logger("Can't detect user data for uid ".$uid, LOGGER_DEBUG);
|
||||
return(array());
|
||||
}
|
||||
|
@ -976,10 +1018,12 @@ function store_photo($a, $uid, $imagedata = "", $url = "") {
|
|||
unlink($tempfile);
|
||||
|
||||
$max_length = get_config('system', 'max_image_length');
|
||||
if(! $max_length)
|
||||
if (! $max_length) {
|
||||
$max_length = MAX_IMAGE_LENGTH;
|
||||
if($max_length > 0)
|
||||
}
|
||||
if ($max_length > 0) {
|
||||
$ph->scaleImage($max_length);
|
||||
}
|
||||
|
||||
$width = $ph->getWidth();
|
||||
$height = $ph->getHeight();
|
||||
|
@ -1003,44 +1047,50 @@ function store_photo($a, $uid, $imagedata = "", $url = "") {
|
|||
$image = array("page" => $a->get_baseurl().'/photos/'.$page_owner_nick.'/image/'.$hash,
|
||||
"full" => $a->get_baseurl()."/photo/{$hash}-0.".$ph->getExt());
|
||||
|
||||
if($width > 800 || $height > 800)
|
||||
if ($width > 800 || $height > 800) {
|
||||
$image["large"] = $a->get_baseurl()."/photo/{$hash}-0.".$ph->getExt();
|
||||
}
|
||||
|
||||
if ($width > 640 || $height > 640) {
|
||||
$ph->scaleImage(640);
|
||||
$r = $ph->store($uid, $visitor, $hash, $tempfile, t('Wall Photos'), 1, 0, $defperm);
|
||||
if($r)
|
||||
if ($r) {
|
||||
$image["medium"] = $a->get_baseurl()."/photo/{$hash}-1.".$ph->getExt();
|
||||
}
|
||||
}
|
||||
|
||||
if ($width > 320 || $height > 320) {
|
||||
$ph->scaleImage(320);
|
||||
$r = $ph->store($uid, $visitor, $hash, $tempfile, t('Wall Photos'), 2, 0, $defperm);
|
||||
if($r)
|
||||
if ($r) {
|
||||
$image["small"] = $a->get_baseurl()."/photo/{$hash}-2.".$ph->getExt();
|
||||
}
|
||||
}
|
||||
|
||||
if ($width > 160 AND $height > 160) {
|
||||
$x = 0;
|
||||
$y = 0;
|
||||
|
||||
$min = $ph->getWidth();
|
||||
if ($min > 160)
|
||||
if ($min > 160) {
|
||||
$x = ($min - 160) / 2;
|
||||
}
|
||||
|
||||
if ($ph->getHeight() < $min) {
|
||||
$min = $ph->getHeight();
|
||||
if ($min > 160)
|
||||
if ($min > 160) {
|
||||
$y = ($min - 160) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
$min = 160;
|
||||
$ph->cropImage(160, $x, $y, $min, $min);
|
||||
|
||||
$r = $ph->store($uid, $visitor, $hash, $tempfile, t('Wall Photos'), 3, 0, $defperm);
|
||||
if($r)
|
||||
if ($r) {
|
||||
$image["thumb"] = $a->get_baseurl()."/photo/{$hash}-3.".$ph->getExt();
|
||||
}
|
||||
}
|
||||
|
||||
// Set the full image as preview image. This will be overwritten, if the picture is larger than 640.
|
||||
$image["preview"] = $image["full"];
|
||||
|
@ -1053,9 +1103,9 @@ function store_photo($a, $uid, $imagedata = "", $url = "") {
|
|||
//if (isset($image["small"]))
|
||||
// $image["preview"] = $image["small"];
|
||||
|
||||
if (isset($image["medium"]))
|
||||
if (isset($image["medium"])) {
|
||||
$image["preview"] = $image["medium"];
|
||||
}
|
||||
|
||||
return($image);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,9 +678,7 @@ class Probe {
|
|||
if (!is_object($vcards))
|
||||
return false;
|
||||
|
||||
if ($vcards->length == 0)
|
||||
return false;
|
||||
|
||||
if ($vcards->length > 0) {
|
||||
$vcard = $vcards->item(0);
|
||||
|
||||
// We have to discard the guid from the hcard in favour of the guid from lrdd
|
||||
|
@ -694,6 +709,7 @@ class Probe {
|
|||
$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); // */
|
||||
|
@ -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);
|
||||
|
|
456
include/api.php
456
include/api.php
|
@ -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,14 +714,8 @@
|
|||
*/
|
||||
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']));
|
||||
|
||||
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
|
||||
|
@ -1108,11 +1151,10 @@
|
|||
$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
|
||||
ORDER BY `item`.`id` DESC
|
||||
LIMIT 1",
|
||||
|
@ -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 ",
|
||||
|
@ -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())
|
||||
);
|
||||
|
||||
|
@ -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");
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -47,9 +47,8 @@ require_once("boot.php");
|
|||
|
||||
global $a, $db;
|
||||
|
||||
if(is_null($a)) {
|
||||
if (is_null($a))
|
||||
$a = new App;
|
||||
}
|
||||
|
||||
if (is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
|
@ -66,94 +65,177 @@ $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 {
|
||||
// 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"];
|
||||
if($iLength > 0) {
|
||||
// ovo znaci da smo nesto dobili
|
||||
|
||||
// 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":
|
||||
// provjeravamo je li korisnik dobar
|
||||
// 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));
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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));
|
||||
} else {
|
||||
// ovdje provjeri je li korisnik OK
|
||||
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);
|
||||
$sQuery = "SELECT `uid` FROM `user` WHERE `nickname`='". $db->escape($sUser) ."'";
|
||||
$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);
|
||||
if ($oResult = q($sQuery)){
|
||||
if ($oResult) {
|
||||
// korisnik OK
|
||||
$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 {
|
||||
// korisnik nije OK
|
||||
// The user isn't okay
|
||||
$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));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
break;
|
||||
case "auth":
|
||||
// provjeravamo autentifikaciju korisnika
|
||||
|
||||
/**
|
||||
* @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));
|
||||
} else {
|
||||
// ovdje provjeri prijavu
|
||||
return;
|
||||
}
|
||||
|
||||
// We now check if the password match
|
||||
$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] 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]));
|
||||
/*
|
||||
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;
|
||||
|
@ -164,6 +246,7 @@ class exAuth
|
|||
$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]);
|
||||
|
@ -173,55 +256,70 @@ class exAuth
|
|||
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));
|
||||
}
|
||||
}
|
||||
unset ($iHeader);
|
||||
unset ($aLength);
|
||||
unset ($iLength);
|
||||
unset($aCommand);
|
||||
} while (true);
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
// zatvori log file
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
||||
|
||||
|
||||
|
|
|
@ -17,13 +17,16 @@ function diaspora2bb($s) {
|
|||
|
||||
$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);
|
||||
|
||||
// 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\#])/', '#$1', $s);
|
||||
|
@ -41,7 +44,7 @@ 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);
|
||||
|
||||
|
@ -49,15 +52,16 @@ function diaspora2bb($s) {
|
|||
$s = str_replace('♲', html_entity_decode('♲', 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);
|
||||
|
|
|
@ -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('/\"\;/','"',$Text);
|
||||
|
||||
// fix any escaped ampersands that may have been converted into links
|
||||
$Text = preg_replace("/\<([^>]*?)(src|href)=(.*?)\&\;(.*?)\>/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)=(.*?)\&\;(.*?)\>/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.
|
||||
|
|
|
@ -1,72 +1,210 @@
|
|||
<?php
|
||||
/**
|
||||
* cache api
|
||||
* @file include/cache.php
|
||||
*
|
||||
* @brief Class for storing data for a short time
|
||||
*/
|
||||
|
||||
use \Friendica\Core\Config;
|
||||
use \Friendica\Core\PConfig;
|
||||
|
||||
class Cache {
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
if (!Config::get('system', 'memcache')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
|
||||
$memcache_port = Config::get('system', 'memcache_port', 11211);
|
||||
|
||||
$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) {
|
||||
|
||||
$r = q("SELECT `v` FROM `cache` WHERE `k`='%s' limit 1",
|
||||
dbesc($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);
|
||||
|
||||
if (count($r))
|
||||
return $r[0]['v'];
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Frequently clear cache
|
||||
self::clear($duration);
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
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($value),
|
||||
dbesc(serialize($value)),
|
||||
intval($duration),
|
||||
dbesc(datetime_convert()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* @brief Remove outdated data from the cache
|
||||
*
|
||||
* 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.
|
||||
* @param integer $maxlevel The maximum cache level that is to be cleared
|
||||
*/
|
||||
public static function clear($max_level = CACHE_MONTH) {
|
||||
|
||||
/*
|
||||
* $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(){
|
||||
// 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());
|
||||
}
|
||||
|
||||
if (($max_level <= CACHE_FIVE_MINUTES) AND (get_config("system", "cache_cleared_five_minute")) < time() - self::duration(CACHE_FIVE_MINUTES)) {
|
||||
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
|
||||
dbesc(datetime_convert('UTC','UTC',"now - 5 minutes")), intval(CACHE_FIVE_MINUTES));
|
||||
|
||||
set_config("system", "cache_cleared_five_minute", time());
|
||||
}
|
||||
|
||||
if (($max_level <= CACHE_MINUTE) AND (get_config("system", "cache_cleared_minute")) < time() - self::duration(CACHE_MINUTE)) {
|
||||
q("DELETE FROM `cache` WHERE `updated` < '%s' AND `expire_mode` = %d",
|
||||
dbesc(datetime_convert('UTC','UTC',"now - 1 minutes")), intval(CACHE_MINUTE));
|
||||
|
||||
set_config("system", "cache_cleared_minute", time());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -439,7 +439,7 @@ These Fields are not added below (yet). They are here to for bug search.
|
|||
function item_joins() {
|
||||
|
||||
return "STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id` AND
|
||||
NOT `contact`.`blocked` AND NOT `contact`.`pending`
|
||||
(NOT `contact`.`blocked` OR `contact`.`pending`)
|
||||
LEFT JOIN `contact` AS `author` ON `author`.`id`=`item`.`author-id`
|
||||
LEFT JOIN `contact` AS `owner` ON `owner`.`id`=`item`.`owner-id`";
|
||||
}
|
||||
|
@ -904,21 +904,22 @@ function best_link_url($item,&$sparkle,$ssl_state = false) {
|
|||
|
||||
|
||||
if (! function_exists('item_photo_menu')) {
|
||||
function item_photo_menu($item){
|
||||
|
||||
function item_photo_menu($item)
|
||||
{
|
||||
$ssl_state = false;
|
||||
|
||||
if(local_user())
|
||||
if(local_user()) {
|
||||
$ssl_state = true;
|
||||
}
|
||||
|
||||
$sub_link="";
|
||||
$poke_link="";
|
||||
$contact_url="";
|
||||
$pm_url="";
|
||||
$status_link="";
|
||||
$photos_link="";
|
||||
$posts_link="";
|
||||
$network = "";
|
||||
$sub_link = '';
|
||||
$poke_link = '';
|
||||
$contact_url = '';
|
||||
$pm_url = '';
|
||||
$status_link = '';
|
||||
$photos_link = '';
|
||||
$posts_link = '';
|
||||
$network = '';
|
||||
|
||||
if ((local_user()) && local_user() == $item['uid'] && $item['parent'] == $item['id'] && (! $item['self'])) {
|
||||
$sub_link = 'javascript:dosubthread(' . $item['id'] . '); return false;';
|
||||
|
@ -926,56 +927,62 @@ function item_photo_menu($item){
|
|||
|
||||
$sparkle = false;
|
||||
$profile_link = best_link_url($item, $sparkle, $ssl_state);
|
||||
if($profile_link === 'mailbox')
|
||||
if ($profile_link === 'mailbox') {
|
||||
$profile_link = '';
|
||||
}
|
||||
|
||||
$cid = 0;
|
||||
$network = "";
|
||||
$network = '';
|
||||
$rel = 0;
|
||||
$r = q("SELECT `id`, `network`, `rel` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' LIMIT 1",
|
||||
intval(local_user()), dbesc(normalise_link($item['author-link'])));
|
||||
if ($r) {
|
||||
$cid = $r[0]["id"];
|
||||
$network = $r[0]["network"];
|
||||
$rel = $r[0]["rel"];
|
||||
$cid = $r[0]['id'];
|
||||
$network = $r[0]['network'];
|
||||
$rel = $r[0]['rel'];
|
||||
}
|
||||
|
||||
if($sparkle) {
|
||||
$status_link = $profile_link."?url=status";
|
||||
$photos_link = $profile_link."?url=photos";
|
||||
$profile_link = $profile_link."?url=profile";
|
||||
$status_link = $profile_link . '?url=status';
|
||||
$photos_link = $profile_link . '?url=photos';
|
||||
$profile_link = $profile_link . '?url=profile';
|
||||
$zurl = '';
|
||||
} else
|
||||
} else {
|
||||
$profile_link = zrl($profile_link);
|
||||
}
|
||||
|
||||
if ($cid && !$item['self']) {
|
||||
$poke_link = 'poke/?f=&c=' . $cid;
|
||||
$contact_url = 'contacts/' . $cid;
|
||||
$posts_link = 'contacts/' . $cid . '/posts';
|
||||
|
||||
if (in_array($network, array(NETWORK_DFRN, NETWORK_DIASPORA)))
|
||||
if (in_array($network, array(NETWORK_DFRN, NETWORK_DIASPORA))) {
|
||||
$pm_url = 'message/new/' . $cid;
|
||||
}
|
||||
}
|
||||
|
||||
if (local_user()) {
|
||||
$menu = Array(
|
||||
t("Follow Thread") => $sub_link,
|
||||
t("View Status") => $status_link,
|
||||
t("View Profile") => $profile_link,
|
||||
t("View Photos") => $photos_link,
|
||||
t("Network Posts") => $posts_link,
|
||||
t("Edit Contact") => $contact_url,
|
||||
t("Send PM") => $pm_url
|
||||
t('Follow Thread') => $sub_link,
|
||||
t('View Status') => $status_link,
|
||||
t('View Profile') => $profile_link,
|
||||
t('View Photos') => $photos_link,
|
||||
t('Network Posts') => $posts_link,
|
||||
t('View Contact') => $contact_url,
|
||||
t('Send PM') => $pm_url
|
||||
);
|
||||
|
||||
if ($network == NETWORK_DFRN)
|
||||
if ($network == NETWORK_DFRN) {
|
||||
$menu[t("Poke")] = $poke_link;
|
||||
}
|
||||
|
||||
if ((($cid == 0) OR ($rel == CONTACT_IS_FOLLOWER)) AND
|
||||
in_array($item['network'], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA)))
|
||||
$menu[t("Connect/Follow")] = "follow?url=".urlencode($item['author-link']);
|
||||
} else
|
||||
$menu = array(t("View Profile") => $item['author-link']);
|
||||
in_array($item['network'], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA))) {
|
||||
$menu[t('Connect/Follow')] = 'follow?url=' . urlencode($item['author-link']);
|
||||
}
|
||||
} else {
|
||||
$menu = array(t('View Profile') => $item['author-link']);
|
||||
}
|
||||
|
||||
$args = array('item' => $item, 'menu' => $menu);
|
||||
|
||||
|
@ -983,13 +990,14 @@ function item_photo_menu($item){
|
|||
|
||||
$menu = $args['menu'];
|
||||
|
||||
$o = "";
|
||||
$o = '';
|
||||
foreach ($menu as $k => $v) {
|
||||
if (strpos($v, 'javascript:') === 0) {
|
||||
$v = substr($v, 11);
|
||||
$o .= "<li role=\"menuitem\"><a onclick=\"$v\">$k</a></li>\n";
|
||||
$o .= '<li role="menuitem"><a onclick="' . $v . '">' . $k . '</a></li>' . PHP_EOL;
|
||||
} elseif ($v!='') {
|
||||
$o .= '<li role="menuitem"><a href="' . $v . '">' . $k . '</a></li>' . PHP_EOL;
|
||||
}
|
||||
elseif ($v!="") $o .= "<li role=\"menuitem\"><a href=\"$v\">$k</a></li>\n";
|
||||
}
|
||||
return $o;
|
||||
}}
|
||||
|
@ -1139,7 +1147,7 @@ function format_like($cnt,$arr,$type,$id) {
|
|||
$explikers = sprintf( t('%s don\'t attend.'), $likers);
|
||||
break;
|
||||
case 'attendmaybe':
|
||||
$phrase = sprintf( t('<span %1$s>%2$d people</span> anttend maybe'), $spanatts, $cnt);
|
||||
$phrase = sprintf( t('<span %1$s>%2$d people</span> attend maybe'), $spanatts, $cnt);
|
||||
$explikers = sprintf( t('%s anttend maybe.'), $likers);
|
||||
break;
|
||||
}
|
||||
|
|
40
include/create_shadowentry.php
Normal file
40
include/create_shadowentry.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
/**
|
||||
* @file include/create_shadowentry.php
|
||||
* @brief This script creates posts with UID = 0 for a given public post.
|
||||
*
|
||||
* This script is started from mod/item.php to save some time when doing a post.
|
||||
*/
|
||||
require_once("boot.php");
|
||||
require_once("include/threads.php");
|
||||
|
||||
function create_shadowentry_run($argv, $argc) {
|
||||
global $a, $db;
|
||||
|
||||
if (is_null($a))
|
||||
$a = new App;
|
||||
|
||||
if (is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
}
|
||||
|
||||
load_config('config');
|
||||
load_config('system');
|
||||
|
||||
if ($argc != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
$message_id = intval($argv[1]);
|
||||
|
||||
add_shadow_entry($message_id);
|
||||
}
|
||||
|
||||
if (array_search(__file__,get_included_files())===0){
|
||||
create_shadowentry_run($_SERVER["argv"],$_SERVER["argc"]);
|
||||
killme();
|
||||
}
|
||||
?>
|
|
@ -11,6 +11,7 @@ if (!file_exists("boot.php") AND (sizeof($_SERVER["argv"]) != 0)) {
|
|||
}
|
||||
|
||||
require_once("boot.php");
|
||||
require_once("include/photos.php");
|
||||
|
||||
|
||||
function cron_run(&$argv, &$argc){
|
||||
|
@ -125,6 +126,10 @@ function cron_run(&$argv, &$argc){
|
|||
set_config('system','last_expire_day',$d2);
|
||||
|
||||
proc_run(PRIORITY_LOW, 'include/expire.php');
|
||||
|
||||
proc_run(PRIORITY_LOW, 'include/dbclean.php');
|
||||
|
||||
cron_update_photo_albums();
|
||||
}
|
||||
|
||||
// Clear cache entries
|
||||
|
@ -146,6 +151,20 @@ function cron_run(&$argv, &$argc){
|
|||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the cached values for the number of photo albums per user
|
||||
*/
|
||||
function cron_update_photo_albums() {
|
||||
$r = q("SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed`");
|
||||
if (!dbm::is_result($r)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($r AS $user) {
|
||||
photo_albums($user['uid'], true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Expire and remove user entries
|
||||
*/
|
||||
|
@ -306,7 +325,11 @@ function cron_poll_contacts($argc, $argv) {
|
|||
|
||||
logger("Polling ".$contact["network"]." ".$contact["id"]." ".$contact["nick"]." ".$contact["name"]);
|
||||
|
||||
if (($contact['network'] == NETWORK_FEED) AND ($contact['priority'] <= 3)) {
|
||||
proc_run(PRIORITY_MEDIUM, 'include/onepoll.php', $contact['id']);
|
||||
} else {
|
||||
proc_run(PRIORITY_LOW, 'include/onepoll.php', $contact['id']);
|
||||
}
|
||||
|
||||
if($interval)
|
||||
@time_sleep_until(microtime(true) + (float) $interval);
|
||||
|
@ -355,10 +378,10 @@ function cron_clear_cache(&$a) {
|
|||
}
|
||||
|
||||
// Delete the cached OEmbed entries that are older than one year
|
||||
q("DELETE FROM `oembed` WHERE `created` < NOW() - INTERVAL 1 YEAR");
|
||||
q("DELETE FROM `oembed` WHERE `created` < NOW() - INTERVAL 3 MONTH");
|
||||
|
||||
// Delete the cached "parse_url" entries that are older than one year
|
||||
q("DELETE FROM `parsed_url` WHERE `created` < NOW() - INTERVAL 1 YEAR");
|
||||
q("DELETE FROM `parsed_url` WHERE `created` < NOW() - INTERVAL 3 MONTH");
|
||||
|
||||
// Maximum table size in megabyte
|
||||
$max_tablesize = intval(get_config('system','optimize_max_tablesize')) * 1000000;
|
||||
|
|
|
@ -325,7 +325,7 @@ function datetimesel($format, $min, $max, $default, $label, $id = 'datetimepicke
|
|||
* Results relative to current timezone.
|
||||
* Limited to range of timestamps.
|
||||
*
|
||||
* @param string $posted_date
|
||||
* @param string $posted_date MySQL-formatted date string (YYYY-MM-DD HH:MM:SS)
|
||||
* @param string $format (optional) Parsed with sprintf()
|
||||
* <tt>%1$d %2$s ago</tt>, e.g. 22 hours ago, 1 minute ago
|
||||
*
|
||||
|
@ -333,7 +333,7 @@ function datetimesel($format, $min, $max, $default, $label, $id = 'datetimepicke
|
|||
*/
|
||||
function relative_date($posted_date, $format = null) {
|
||||
|
||||
$localtime = datetime_convert('UTC',date_default_timezone_get(),$posted_date);
|
||||
$localtime = $posted_date . ' UTC';
|
||||
|
||||
$abs = strtotime($localtime);
|
||||
|
||||
|
@ -347,13 +347,6 @@ function relative_date($posted_date,$format = null) {
|
|||
return t('less than a second ago');
|
||||
}
|
||||
|
||||
/*
|
||||
$time_append = '';
|
||||
if ($etime >= 86400) {
|
||||
$time_append = ' ('.$localtime.')';
|
||||
}
|
||||
*/
|
||||
|
||||
$a = array( 12 * 30 * 24 * 60 * 60 => array( t('year'), t('years')),
|
||||
30 * 24 * 60 * 60 => array( t('month'), t('months')),
|
||||
7 * 24 * 60 * 60 => array( t('week'), t('weeks')),
|
||||
|
@ -368,8 +361,9 @@ function relative_date($posted_date,$format = null) {
|
|||
if ($d >= 1) {
|
||||
$r = round($d);
|
||||
// translators - e.g. 22 hours ago, 1 minute ago
|
||||
if(! $format)
|
||||
if (!$format) {
|
||||
$format = t('%1$d %2$s ago');
|
||||
}
|
||||
|
||||
return sprintf($format, $r, (($r == 1) ? $str[0] : $str[1]));
|
||||
}
|
||||
|
|
185
include/dba.php
185
include/dba.php
|
@ -66,10 +66,10 @@ class dba {
|
|||
if (! mysqli_connect_errno()) {
|
||||
$this->connected = true;
|
||||
}
|
||||
if (isset($a->config["system"]["db_charset"]))
|
||||
if (isset($a->config["system"]["db_charset"])) {
|
||||
$this->db->set_charset($a->config["system"]["db_charset"]);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$this->mysqli = false;
|
||||
$this->db = mysql_connect($server,$user,$pass);
|
||||
if ($this->db && mysql_select_db($db,$this->db)) {
|
||||
|
@ -80,9 +80,10 @@ class dba {
|
|||
}
|
||||
if (!$this->connected) {
|
||||
$this->db = null;
|
||||
if(! $install)
|
||||
if (!$install) {
|
||||
system_unavailable();
|
||||
}
|
||||
}
|
||||
|
||||
$a->save_timestamp($stamp1, "network");
|
||||
}
|
||||
|
@ -91,37 +92,90 @@ class dba {
|
|||
return $this->db;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the MySQL server version string
|
||||
*
|
||||
* This function discriminate between the deprecated mysql API and the current
|
||||
* object-oriented mysqli API. Example of returned string: 5.5.46-0+deb8u1
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function server_info() {
|
||||
if ($this->mysqli) {
|
||||
$return = $this->db->server_info;
|
||||
} else {
|
||||
$return = mysql_get_server_info($this->db);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the selected database name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function database_name() {
|
||||
$r = $this->q("SELECT DATABASE() AS `db`");
|
||||
|
||||
return $r[0]['db'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of rows
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public function num_rows() {
|
||||
if (!$this->result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($this->mysqli) {
|
||||
$return = $this->result->num_rows;
|
||||
} else {
|
||||
$return = mysql_num_rows($this->result);
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function q($sql, $onlyquery = false) {
|
||||
global $a;
|
||||
|
||||
if((! $this->db) || (! $this->connected))
|
||||
if (!$this->db || !$this->connected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->error = '';
|
||||
|
||||
// Check the connection (This can reconnect the connection - if configured)
|
||||
if ($this->mysqli)
|
||||
if ($this->mysqli) {
|
||||
$connected = $this->db->ping();
|
||||
else
|
||||
} else {
|
||||
$connected = mysql_ping($this->db);
|
||||
|
||||
}
|
||||
$connstr = ($connected ? "Connected" : "Disonnected");
|
||||
|
||||
$stamp1 = microtime(true);
|
||||
|
||||
if($this->mysqli)
|
||||
$result = @$this->db->query($sql);
|
||||
else
|
||||
$result = @mysql_query($sql,$this->db);
|
||||
$orig_sql = $sql;
|
||||
|
||||
if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) {
|
||||
$sql = "/*".$a->callstack()." */ ".$sql;
|
||||
}
|
||||
|
||||
if ($this->mysqli) {
|
||||
$result = @$this->db->query($sql);
|
||||
} else {
|
||||
$result = @mysql_query($sql,$this->db);
|
||||
}
|
||||
$stamp2 = microtime(true);
|
||||
$duration = (float)($stamp2-$stamp1);
|
||||
|
||||
$a->save_timestamp($stamp1, "database");
|
||||
|
||||
if (strtolower(substr($sql, 0, 6)) != "select")
|
||||
if (strtolower(substr($orig_sql, 0, 6)) != "select") {
|
||||
$a->save_timestamp($stamp1, "database_write");
|
||||
|
||||
}
|
||||
if (x($a->config,'system') && x($a->config['system'],'db_log')) {
|
||||
if (($duration > $a->config["system"]["db_loglimit"])) {
|
||||
$duration = round($duration, 3);
|
||||
|
@ -151,16 +205,17 @@ class dba {
|
|||
|
||||
$mesg = '';
|
||||
|
||||
if($result === false)
|
||||
if ($result === false) {
|
||||
$mesg = 'false';
|
||||
elseif($result === true)
|
||||
} elseif ($result === true) {
|
||||
$mesg = 'true';
|
||||
else {
|
||||
if($this->mysqli)
|
||||
} else {
|
||||
if ($this->mysqli) {
|
||||
$mesg = $result->num_rows . ' results' . EOL;
|
||||
else
|
||||
} else {
|
||||
$mesg = mysql_num_rows($result) . ' results' . EOL;
|
||||
}
|
||||
}
|
||||
|
||||
$str = 'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg
|
||||
. (($this->error) ? ' error: ' . $this->error : '')
|
||||
|
@ -177,13 +232,14 @@ class dba {
|
|||
|
||||
if ($result === false) {
|
||||
logger('dba: ' . printable($sql) . ' returned false.' . "\n" . $this->error);
|
||||
if(file_exists('dbfail.out'))
|
||||
if (file_exists('dbfail.out')) {
|
||||
file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
if(($result === true) || ($result === false))
|
||||
if (($result === true) || ($result === false)) {
|
||||
return $result;
|
||||
|
||||
}
|
||||
if ($onlyquery) {
|
||||
$this->result = $result;
|
||||
return true;
|
||||
|
@ -196,8 +252,7 @@ class dba {
|
|||
$r[] = $x;
|
||||
$result->free_result();
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (mysql_num_rows($result)) {
|
||||
while($x = mysql_fetch_array($result, MYSQL_ASSOC))
|
||||
$r[] = $x;
|
||||
|
@ -207,15 +262,16 @@ class dba {
|
|||
|
||||
//$a->save_timestamp($stamp1, "database");
|
||||
|
||||
if($this->debug)
|
||||
if ($this->debug) {
|
||||
logger('dba: ' . printable(print_r($r, true)));
|
||||
}
|
||||
return($r);
|
||||
}
|
||||
|
||||
public function qfetch() {
|
||||
$x = false;
|
||||
|
||||
if ($this->result)
|
||||
if ($this->result) {
|
||||
if ($this->mysqli) {
|
||||
if ($this->result->num_rows)
|
||||
$x = $this->result->fetch_array(MYSQLI_ASSOC);
|
||||
|
@ -223,18 +279,19 @@ class dba {
|
|||
if (mysql_num_rows($this->result))
|
||||
$x = mysql_fetch_array($this->result, MYSQL_ASSOC);
|
||||
}
|
||||
|
||||
}
|
||||
return($x);
|
||||
}
|
||||
|
||||
public function qclose() {
|
||||
if ($this->result)
|
||||
if ($this->result) {
|
||||
if ($this->mysqli) {
|
||||
$this->result->free_result();
|
||||
} else {
|
||||
mysql_free_result($this->result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function dbg($dbg) {
|
||||
$this->debug = $dbg;
|
||||
|
@ -242,28 +299,41 @@ class dba {
|
|||
|
||||
public function escape($str) {
|
||||
if ($this->db && $this->connected) {
|
||||
if($this->mysqli)
|
||||
if ($this->mysqli) {
|
||||
return @$this->db->real_escape_string($str);
|
||||
else
|
||||
} else {
|
||||
return @mysql_real_escape_string($str,$this->db);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function connected() {
|
||||
if ($this->mysqli) {
|
||||
$connected = $this->db->ping();
|
||||
} else {
|
||||
$connected = mysql_ping($this->db);
|
||||
}
|
||||
return $connected;
|
||||
}
|
||||
|
||||
function __destruct() {
|
||||
if ($this->db)
|
||||
if($this->mysqli)
|
||||
if ($this->db) {
|
||||
if ($this->mysqli) {
|
||||
$this->db->close();
|
||||
else
|
||||
} else {
|
||||
mysql_close($this->db);
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
|
||||
if (! function_exists('printable')) {
|
||||
function printable($s) {
|
||||
$s = preg_replace("~([\x01-\x08\x0E-\x0F\x10-\x1F\x7F-\xFF])~",".", $s);
|
||||
$s = str_replace("\x00",'.',$s);
|
||||
if(x($_SERVER,'SERVER_NAME'))
|
||||
if (x($_SERVER,'SERVER_NAME')) {
|
||||
$s = escape_tags($s);
|
||||
}
|
||||
return $s;
|
||||
}}
|
||||
|
||||
|
@ -271,17 +341,19 @@ function printable($s) {
|
|||
if (! function_exists('dbg')) {
|
||||
function dbg($state) {
|
||||
global $db;
|
||||
if($db)
|
||||
if ($db) {
|
||||
$db->dbg($state);
|
||||
}
|
||||
}}
|
||||
|
||||
if (! function_exists('dbesc')) {
|
||||
function dbesc($str) {
|
||||
global $db;
|
||||
if($db && $db->connected)
|
||||
if ($db && $db->connected) {
|
||||
return($db->escape($str));
|
||||
else
|
||||
} else {
|
||||
return(str_replace("'","\\'",$str));
|
||||
}
|
||||
}}
|
||||
|
||||
|
||||
|
@ -317,6 +389,42 @@ function q($sql) {
|
|||
|
||||
}}
|
||||
|
||||
/**
|
||||
* @brief Performs a query with "dirty reads"
|
||||
*
|
||||
* By doing dirty reads (reading uncommitted data) no locks are performed
|
||||
* This function can be used to fetch data that doesn't need to be reliable.
|
||||
*
|
||||
* @param $args Query parameters (1 to N parameters of different types)
|
||||
* @return array Query array
|
||||
*/
|
||||
function qu($sql) {
|
||||
|
||||
global $db;
|
||||
$args = func_get_args();
|
||||
unset($args[0]);
|
||||
|
||||
if ($db && $db->connected) {
|
||||
$stmt = @vsprintf($sql,$args); // Disabled warnings
|
||||
if ($stmt === false)
|
||||
logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG);
|
||||
$db->q("SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
|
||||
$retval = $db->q($stmt);
|
||||
$db->q("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;");
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This will happen occasionally trying to store the
|
||||
* session data after abnormal program termination
|
||||
*
|
||||
*/
|
||||
logger('dba: no database: ' . print_r($args,true));
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Raw db query, no arguments
|
||||
|
@ -327,10 +435,11 @@ if(! function_exists('dbq')) {
|
|||
function dbq($sql) {
|
||||
|
||||
global $db;
|
||||
if($db && $db->connected)
|
||||
if ($db && $db->connected) {
|
||||
$ret = $db->q($sql);
|
||||
else
|
||||
} else {
|
||||
$ret = false;
|
||||
}
|
||||
return $ret;
|
||||
}}
|
||||
|
||||
|
|
157
include/dbclean.php
Normal file
157
include/dbclean.php
Normal file
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
/**
|
||||
* @file include/dbclean.php
|
||||
* @brief The script is called from time to time to clean the database entries and remove orphaned data.
|
||||
*/
|
||||
require_once("boot.php");
|
||||
|
||||
function dbclean_run(&$argv, &$argc) {
|
||||
global $a, $db;
|
||||
|
||||
if (is_null($a))
|
||||
$a = new App;
|
||||
|
||||
if (is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
}
|
||||
|
||||
load_config('config');
|
||||
load_config('system');
|
||||
|
||||
if ($argc == 2) {
|
||||
$stage = intval($argv[1]);
|
||||
} else {
|
||||
$stage = 0;
|
||||
}
|
||||
|
||||
if (get_config("system", "worker") AND ($stage == 0)) {
|
||||
proc_run(PRIORITY_LOW, 'include/dbclean.php', 1);
|
||||
proc_run(PRIORITY_LOW, 'include/dbclean.php', 2);
|
||||
proc_run(PRIORITY_LOW, 'include/dbclean.php', 3);
|
||||
proc_run(PRIORITY_LOW, 'include/dbclean.php', 4);
|
||||
proc_run(PRIORITY_LOW, 'include/dbclean.php', 5);
|
||||
proc_run(PRIORITY_LOW, 'include/dbclean.php', 6);
|
||||
proc_run(PRIORITY_LOW, 'include/dbclean.php', 7);
|
||||
} else {
|
||||
remove_orphans($stage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove orphaned database entries
|
||||
*/
|
||||
function remove_orphans($stage = 0) {
|
||||
global $db;
|
||||
|
||||
$count = 0;
|
||||
|
||||
if (($stage == 1) OR ($stage == 0)) {
|
||||
logger("Deleting old global item entries from item table without user copy");
|
||||
if ($db->q("SELECT `id` FROM `item` WHERE `uid` = 0
|
||||
AND NOT EXISTS (SELECT `guid` FROM `item` AS `i` WHERE `item`.`guid` = `i`.`guid` AND `i`.`uid` != 0)
|
||||
AND `received` < UTC_TIMESTAMP() - INTERVAL 90 DAY LIMIT 10000", true)) {
|
||||
$count = $db->num_rows();
|
||||
logger("found global item orphans: ".$count);
|
||||
while ($orphan = $db->qfetch()) {
|
||||
q("DELETE FROM `item` WHERE `id` = %d", intval($orphan["id"]));
|
||||
}
|
||||
}
|
||||
$db->qclose();
|
||||
logger("Done deleting old global item entries from item table without user copy");
|
||||
}
|
||||
|
||||
if (($stage == 2) OR ($stage == 0)) {
|
||||
logger("Deleting items without parents");
|
||||
if ($db->q("SELECT `id` FROM `item` WHERE NOT EXISTS (SELECT `id` FROM `item` AS `i` WHERE `item`.`parent` = `i`.`id`) LIMIT 10000", true)) {
|
||||
$count = $db->num_rows();
|
||||
logger("found item orphans without parents: ".$count);
|
||||
while ($orphan = $db->qfetch()) {
|
||||
q("DELETE FROM `item` WHERE `id` = %d", intval($orphan["id"]));
|
||||
}
|
||||
}
|
||||
$db->qclose();
|
||||
logger("Done deleting items without parents");
|
||||
}
|
||||
|
||||
if (($stage == 3) OR ($stage == 0)) {
|
||||
logger("Deleting orphaned data from thread table");
|
||||
if ($db->q("SELECT `iid` FROM `thread` WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`parent` = `thread`.`iid`)", true)) {
|
||||
$count = $db->num_rows();
|
||||
logger("found thread orphans: ".$count);
|
||||
while ($orphan = $db->qfetch()) {
|
||||
q("DELETE FROM `thread` WHERE `iid` = %d", intval($orphan["iid"]));
|
||||
}
|
||||
}
|
||||
$db->qclose();
|
||||
logger("Done deleting orphaned data from thread table");
|
||||
}
|
||||
|
||||
if (($stage == 4) OR ($stage == 0)) {
|
||||
logger("Deleting orphaned data from notify table");
|
||||
if ($db->q("SELECT `iid` FROM `notify` WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`id` = `notify`.`iid`)", true)) {
|
||||
$count = $db->num_rows();
|
||||
logger("found notify orphans: ".$count);
|
||||
while ($orphan = $db->qfetch()) {
|
||||
q("DELETE FROM `notify` WHERE `iid` = %d", intval($orphan["iid"]));
|
||||
}
|
||||
}
|
||||
$db->qclose();
|
||||
logger("Done deleting orphaned data from notify table");
|
||||
}
|
||||
|
||||
if (($stage == 5) OR ($stage == 0)) {
|
||||
logger("Deleting orphaned data from notify-threads table");
|
||||
if ($db->q("SELECT `id` FROM `notify-threads` WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`parent` = `notify-threads`.`master-parent-item`)", true)) {
|
||||
$count = $db->num_rows();
|
||||
logger("found notify-threads orphans: ".$count);
|
||||
while ($orphan = $db->qfetch()) {
|
||||
q("DELETE FROM `notify-threads` WHERE `id` = %d", intval($orphan["id"]));
|
||||
}
|
||||
}
|
||||
$db->qclose();
|
||||
logger("Done deleting orphaned data from notify-threads table");
|
||||
}
|
||||
|
||||
|
||||
if (($stage == 6) OR ($stage == 0)) {
|
||||
logger("Deleting orphaned data from sign table");
|
||||
if ($db->q("SELECT `iid` FROM `sign` WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`id` = `sign`.`iid`)", true)) {
|
||||
$count = $db->num_rows();
|
||||
logger("found sign orphans: ".$count);
|
||||
while ($orphan = $db->qfetch()) {
|
||||
q("DELETE FROM `sign` WHERE `iid` = %d", intval($orphan["iid"]));
|
||||
}
|
||||
}
|
||||
$db->qclose();
|
||||
logger("Done deleting orphaned data from sign table");
|
||||
}
|
||||
|
||||
|
||||
if (($stage == 7) OR ($stage == 0)) {
|
||||
logger("Deleting orphaned data from term table");
|
||||
if ($db->q("SELECT `oid` FROM `term` WHERE NOT EXISTS (SELECT `id` FROM `item` WHERE `item`.`id` = `term`.`oid`)", true)) {
|
||||
$count = $db->num_rows();
|
||||
logger("found term orphans: ".$count);
|
||||
while ($orphan = $db->qfetch()) {
|
||||
q("DELETE FROM `term` WHERE `oid` = %d", intval($orphan["oid"]));
|
||||
}
|
||||
}
|
||||
$db->qclose();
|
||||
logger("Done deleting orphaned data from term table");
|
||||
}
|
||||
|
||||
// Call it again if not all entries were purged
|
||||
if (($stage != 0) AND ($count > 0) AND get_config("system", "worker")) {
|
||||
proc_run(PRIORITY_LOW, 'include/dbclean.php');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (array_search(__file__,get_included_files())===0){
|
||||
dbclean_run($_SERVER["argv"],$_SERVER["argc"]);
|
||||
killme();
|
||||
}
|
||||
?>
|
|
@ -43,6 +43,10 @@ class dbm {
|
|||
* @return Whether $array is a filled array
|
||||
*/
|
||||
public static function is_result($array) {
|
||||
// It could be a return value from an update statement
|
||||
if (is_bool($array)) {
|
||||
return $array;
|
||||
}
|
||||
return (is_array($array) && count($array) > 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,8 +78,16 @@ function table_structure($table) {
|
|||
if ($index["Index_type"] == "FULLTEXT")
|
||||
continue;
|
||||
|
||||
if ($index['Key_name'] != 'PRIMARY' && $index['Non_unique'] == '0' && !isset($indexdata[$index["Key_name"]])) {
|
||||
$indexdata[$index["Key_name"]] = array('UNIQUE');
|
||||
}
|
||||
|
||||
$column = $index["Column_name"];
|
||||
if ($index["Sub_part"] != "")
|
||||
// On utf8mb4 a varchar index can only have a length of 191
|
||||
// To avoid the need to add this to every index definition we just ignore it here.
|
||||
// Exception are primary indexes
|
||||
// Since there are some combindex primary indexes we use the limit of 180 here.
|
||||
if (($index["Sub_part"] != "") AND (($index["Sub_part"] < 180) OR ($index["Key_name"] == "PRIMARY")))
|
||||
$column .= "(".$index["Sub_part"].")";
|
||||
|
||||
$indexdata[$index["Key_name"]][] = $column;
|
||||
|
@ -104,7 +112,7 @@ function table_structure($table) {
|
|||
return(array("fields"=>$fielddata, "indexes"=>$indexdata));
|
||||
}
|
||||
|
||||
function print_structure($database) {
|
||||
function print_structure($database, $charset) {
|
||||
echo "-- ------------------------------------------\n";
|
||||
echo "-- ".FRIENDICA_PLATFORM." ".FRIENDICA_VERSION." (".FRIENDICA_CODENAME,")\n";
|
||||
echo "-- DB_UPDATE_VERSION ".DB_UPDATE_VERSION."\n";
|
||||
|
@ -113,7 +121,7 @@ function print_structure($database) {
|
|||
echo "--\n";
|
||||
echo "-- TABLE $name\n";
|
||||
echo "--\n";
|
||||
db_create_table($name, $structure['fields'], true, false, $structure["indexes"]);
|
||||
db_create_table($name, $structure['fields'], $charset, true, false, $structure["indexes"]);
|
||||
|
||||
echo "\n";
|
||||
}
|
||||
|
@ -122,6 +130,14 @@ function print_structure($database) {
|
|||
function update_structure($verbose, $action, $tables=null, $definition=null) {
|
||||
global $a, $db;
|
||||
|
||||
if ($action)
|
||||
set_config('system', 'maintenance', 1);
|
||||
|
||||
if (isset($a->config["system"]["db_charset"]))
|
||||
$charset = $a->config["system"]["db_charset"];
|
||||
else
|
||||
$charset = "utf8";
|
||||
|
||||
$errors = false;
|
||||
|
||||
logger('updating structure', LOGGER_DEBUG);
|
||||
|
@ -140,15 +156,29 @@ function update_structure($verbose, $action, $tables=null, $definition=null) {
|
|||
|
||||
// Get the definition
|
||||
if (is_null($definition))
|
||||
$definition = db_definition();
|
||||
$definition = db_definition($charset);
|
||||
|
||||
// Ensure index conversion to unique removes duplicates
|
||||
$sql_config = "SET session old_alter_table=1;";
|
||||
if ($verbose)
|
||||
echo $sql_config."\n";
|
||||
if ($action)
|
||||
@$db->q($sql_config);
|
||||
|
||||
// MySQL >= 5.7.4 doesn't support the IGNORE keyword in ALTER TABLE statements
|
||||
if ((version_compare($db->server_info(), '5.7.4') >= 0) AND
|
||||
!(strpos($db->server_info(), 'MariaDB') !== false)) {
|
||||
$ignore = '';
|
||||
}else {
|
||||
$ignore = ' IGNORE';
|
||||
}
|
||||
|
||||
// Compare it
|
||||
foreach ($definition AS $name => $structure) {
|
||||
$is_new_table = False;
|
||||
$sql3="";
|
||||
if (!isset($database[$name])) {
|
||||
$r = db_create_table($name, $structure["fields"], $verbose, $action, $structure['indexes']);
|
||||
$r = db_create_table($name, $structure["fields"], $charset, $verbose, $action, $structure['indexes']);
|
||||
if(false === $r) {
|
||||
$errors .= t('Errors encountered creating database tables.').$name.EOL;
|
||||
}
|
||||
|
@ -167,7 +197,7 @@ function update_structure($verbose, $action, $tables=null, $definition=null) {
|
|||
if ($current_index_definition != $new_index_definition && substr($indexname, 0, 6) != 'local_') {
|
||||
$sql2=db_drop_index($indexname);
|
||||
if ($sql3 == "")
|
||||
$sql3 = "ALTER TABLE `".$name."` ".$sql2;
|
||||
$sql3 = "ALTER".$ignore." TABLE `".$name."` ".$sql2;
|
||||
else
|
||||
$sql3 .= ", ".$sql2;
|
||||
}
|
||||
|
@ -211,7 +241,7 @@ function update_structure($verbose, $action, $tables=null, $definition=null) {
|
|||
$sql2=db_create_index($indexname, $fieldnames);
|
||||
if ($sql2 != "") {
|
||||
if ($sql3 == "")
|
||||
$sql3 = "ALTER TABLE `".$name."` ".$sql2;
|
||||
$sql3 = "ALTER" . $ignore . " TABLE `".$name."` ".$sql2;
|
||||
else
|
||||
$sql3 .= ", ".$sql2;
|
||||
}
|
||||
|
@ -232,6 +262,9 @@ function update_structure($verbose, $action, $tables=null, $definition=null) {
|
|||
}
|
||||
}
|
||||
|
||||
if ($action)
|
||||
set_config('system', 'maintenance', 0);
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
|
@ -257,16 +290,9 @@ function db_field_command($parameters, $create = true) {
|
|||
return($fieldstruct);
|
||||
}
|
||||
|
||||
function db_create_table($name, $fields, $verbose, $action, $indexes=null) {
|
||||
function db_create_table($name, $fields, $charset, $verbose, $action, $indexes=null) {
|
||||
global $a, $db;
|
||||
|
||||
if (isset($a->config["system"]["db_charset"]))
|
||||
$charset = $a->config["system"]["db_charset"];
|
||||
elseif ($verbose)
|
||||
$charset = "utf8mb4";
|
||||
else
|
||||
$charset = "utf8";
|
||||
|
||||
$r = true;
|
||||
|
||||
$sql = "";
|
||||
|
@ -322,9 +348,9 @@ function db_create_index($indexname, $fieldnames, $method="ADD") {
|
|||
killme();
|
||||
}
|
||||
|
||||
|
||||
if ($indexname == "PRIMARY") {
|
||||
return sprintf("%s PRIMARY KEY(`%s`)", $method, implode("`,`", $fieldnames));
|
||||
if ($fieldnames[0] == "UNIQUE") {
|
||||
array_shift($fieldnames);
|
||||
$method .= ' UNIQUE';
|
||||
}
|
||||
|
||||
$names = "";
|
||||
|
@ -338,12 +364,26 @@ function db_create_index($indexname, $fieldnames, $method="ADD") {
|
|||
$names .= "`".dbesc($fieldname)."`";
|
||||
}
|
||||
|
||||
if ($indexname == "PRIMARY") {
|
||||
return sprintf("%s PRIMARY KEY(%s)", $method, $names);
|
||||
}
|
||||
|
||||
|
||||
$sql = sprintf("%s INDEX `%s` (%s)", $method, dbesc($indexname), $names);
|
||||
return($sql);
|
||||
}
|
||||
|
||||
function db_definition() {
|
||||
function db_index_suffix($charset, $reduce = 0) {
|
||||
if ($charset != "utf8mb4")
|
||||
return "";
|
||||
|
||||
// On utf8mb4 indexes can only have a length of 191
|
||||
$indexlength = 191 - $reduce;
|
||||
|
||||
return "(".$indexlength.")";
|
||||
}
|
||||
|
||||
function db_definition($charset) {
|
||||
|
||||
$database = array();
|
||||
|
||||
|
@ -401,8 +441,9 @@ function db_definition() {
|
|||
"updated" => array("type" => "datetime", "not null" => "1", "default" => "0000-00-00 00:00:00"),
|
||||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("k"),
|
||||
"PRIMARY" => array("k".db_index_suffix($charset)),
|
||||
"updated" => array("updated"),
|
||||
"expire_mode_updated" => array("expire_mode", "updated"),
|
||||
)
|
||||
);
|
||||
$database["challenge"] = array(
|
||||
|
@ -440,7 +481,7 @@ function db_definition() {
|
|||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("id"),
|
||||
"cat_k" => array("cat(30)","k(30)"),
|
||||
"cat_k" => array("UNIQUE", "cat(30)","k(30)"),
|
||||
)
|
||||
);
|
||||
$database["contact"] = array(
|
||||
|
@ -459,6 +500,7 @@ function db_definition() {
|
|||
"about" => array("type" => "text"),
|
||||
"keywords" => array("type" => "text"),
|
||||
"gender" => array("type" => "varchar(32)", "not null" => "1", "default" => ""),
|
||||
"xmpp" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"attag" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"avatar" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"photo" => array("type" => "text"),
|
||||
|
@ -498,6 +540,7 @@ function db_definition() {
|
|||
"writable" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"forum" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"prv" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"contact-type" => array("type" => "int(11) unsigned", "not null" => "1", "default" => "0"),
|
||||
"hidden" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"archive" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"pending" => array("type" => "tinyint(1)", "not null" => "1", "default" => "1"),
|
||||
|
@ -515,6 +558,7 @@ function db_definition() {
|
|||
"indexes" => array(
|
||||
"PRIMARY" => array("id"),
|
||||
"uid" => array("uid"),
|
||||
"addr_uid" => array("addr", "uid"),
|
||||
"nurl" => array("nurl"),
|
||||
)
|
||||
);
|
||||
|
@ -543,6 +587,7 @@ function db_definition() {
|
|||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("id"),
|
||||
"cmd_item_contact" => array("UNIQUE", "cmd", "item", "contact"),
|
||||
)
|
||||
);
|
||||
$database["event"] = array(
|
||||
|
@ -667,6 +712,7 @@ function db_definition() {
|
|||
"gender" => array("type" => "varchar(32)", "not null" => "1", "default" => ""),
|
||||
"birthday" => array("type" => "varchar(32)", "not null" => "1", "default" => "0000-00-00"),
|
||||
"community" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"contact-type" => array("type" => "tinyint(1)", "not null" => "1", "default" => "-1"),
|
||||
"hide" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"nsfw" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"network" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
|
@ -993,6 +1039,8 @@ function db_definition() {
|
|||
"seen" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"verb" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"otype" => array("type" => "varchar(16)", "not null" => "1", "default" => ""),
|
||||
"name_cache" => array("type" => "tinytext"),
|
||||
"msg_cache" => array("type" => "mediumtext")
|
||||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("id"),
|
||||
|
@ -1020,7 +1068,7 @@ function db_definition() {
|
|||
"created" => array("type" => "datetime", "not null" => "1", "default" => "0000-00-00 00:00:00"),
|
||||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("url"),
|
||||
"PRIMARY" => array("url".db_index_suffix($charset)),
|
||||
"created" => array("created"),
|
||||
)
|
||||
);
|
||||
|
@ -1033,7 +1081,7 @@ function db_definition() {
|
|||
"created" => array("type" => "datetime", "not null" => "1", "default" => "0000-00-00 00:00:00"),
|
||||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("url", "guessing", "oembed"),
|
||||
"PRIMARY" => array("url".db_index_suffix($charset), "guessing", "oembed"),
|
||||
"created" => array("created"),
|
||||
)
|
||||
);
|
||||
|
@ -1047,7 +1095,7 @@ function db_definition() {
|
|||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("id"),
|
||||
"uid_cat_k" => array("uid","cat(30)","k(30)"),
|
||||
"uid_cat_k" => array("UNIQUE", "uid","cat(30)","k(30)"),
|
||||
)
|
||||
);
|
||||
$database["photo"] = array(
|
||||
|
@ -1077,7 +1125,9 @@ function db_definition() {
|
|||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("id"),
|
||||
"uid" => array("uid"),
|
||||
"uid_contactid" => array("uid", "contact-id"),
|
||||
"uid_profile" => array("uid", "profile"),
|
||||
"uid_album_created" => array("uid", "album", "created"),
|
||||
"resource-id" => array("resource-id"),
|
||||
"guid" => array("guid"),
|
||||
)
|
||||
|
@ -1164,6 +1214,7 @@ function db_definition() {
|
|||
"education" => array("type" => "text"),
|
||||
"contact" => array("type" => "text"),
|
||||
"homepage" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"xmpp" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"photo" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"thumb" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"publish" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
|
@ -1229,6 +1280,7 @@ function db_definition() {
|
|||
"uid" => array("type" => "int(11) unsigned", "not null" => "1", "default" => "0"),
|
||||
"password" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"language" => array("type" => "varchar(16)", "not null" => "1", "default" => ""),
|
||||
"note" => array("type" => "text"),
|
||||
),
|
||||
"indexes" => array(
|
||||
"PRIMARY" => array("id"),
|
||||
|
@ -1311,6 +1363,7 @@ function db_definition() {
|
|||
"type_term" => array("type","term"),
|
||||
"uid_otype_type_term_global_created" => array("uid","otype","type","term","global","created"),
|
||||
"otype_type_term_tid" => array("otype","type","term","tid"),
|
||||
"uid_otype_type_url" => array("uid","otype","type","url"),
|
||||
"guid" => array("guid"),
|
||||
)
|
||||
);
|
||||
|
@ -1400,6 +1453,7 @@ function db_definition() {
|
|||
"cntunkmail" => array("type" => "int(11)", "not null" => "1", "default" => "10"),
|
||||
"notify-flags" => array("type" => "int(11) unsigned", "not null" => "1", "default" => "65535"),
|
||||
"page-flags" => array("type" => "int(11) unsigned", "not null" => "1", "default" => "0"),
|
||||
"account-type" => array("type" => "int(11) unsigned", "not null" => "1", "default" => "0"),
|
||||
"prvnets" => array("type" => "tinyint(1)", "not null" => "1", "default" => "0"),
|
||||
"pwdreset" => array("type" => "varchar(255)", "not null" => "1", "default" => ""),
|
||||
"maxreq" => array("type" => "int(11)", "not null" => "1", "default" => "10"),
|
||||
|
@ -1469,12 +1523,33 @@ function dbstructure_run(&$argv, &$argc) {
|
|||
|
||||
if ($argc==2) {
|
||||
switch ($argv[1]) {
|
||||
case "dryrun":
|
||||
update_structure(true, false);
|
||||
return;
|
||||
case "update":
|
||||
update_structure(true, true);
|
||||
|
||||
$build = get_config('system','build');
|
||||
if (!x($build)) {
|
||||
set_config('system','build',DB_UPDATE_VERSION);
|
||||
$build = DB_UPDATE_VERSION;
|
||||
}
|
||||
|
||||
$stored = intval($build);
|
||||
$current = intval(DB_UPDATE_VERSION);
|
||||
|
||||
// run any left update_nnnn functions in update.php
|
||||
for($x = $stored; $x < $current; $x ++) {
|
||||
$r = run_update_function($x);
|
||||
if (!$r) break;
|
||||
}
|
||||
|
||||
set_config('system','build',DB_UPDATE_VERSION);
|
||||
return;
|
||||
case "dumpsql":
|
||||
print_structure(db_definition());
|
||||
// For the dump that is used to create the database.sql we always assume utfmb4
|
||||
$charset = "utf8mb4";
|
||||
print_structure(db_definition($charset), $charset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1483,7 +1558,8 @@ function dbstructure_run(&$argv, &$argc) {
|
|||
// print help
|
||||
echo $argv[0]." <command>\n";
|
||||
echo "\n";
|
||||
echo "commands:\n";
|
||||
echo "Commands:\n";
|
||||
echo "dryrun show database update schema queries without running them\n";
|
||||
echo "update update database schema\n";
|
||||
echo "dumpsql dump database schema\n";
|
||||
return;
|
||||
|
|
|
@ -178,7 +178,7 @@ function delivery_run(&$argv, &$argc){
|
|||
|
||||
$r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`,
|
||||
`user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`,
|
||||
`user`.`page-flags`, `user`.`prvnets`
|
||||
`user`.`page-flags`, `user`.`account-type`, `user`.`prvnets`
|
||||
FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
|
||||
WHERE `contact`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1",
|
||||
intval($uid)
|
||||
|
@ -381,7 +381,14 @@ function delivery_run(&$argv, &$argc){
|
|||
if ($deliver_status == (-1)) {
|
||||
logger('notifier: delivery failed: queuing message');
|
||||
add_to_queue($contact['id'],NETWORK_DFRN,$atom);
|
||||
|
||||
// The message could not be delivered. We mark the contact as "dead"
|
||||
mark_for_death($contact);
|
||||
} else {
|
||||
// We successfully delivered a message, the contact is alive
|
||||
unmark_for_death($contact);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case NETWORK_OSTATUS:
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
* @file include/dfrn.php
|
||||
* @brief The implementation of the dfrn protocol
|
||||
*
|
||||
* https://github.com/friendica/friendica/wiki/Protocol
|
||||
* @see https://github.com/friendica/friendica/wiki/Protocol and
|
||||
* https://github.com/friendica/friendica/blob/master/spec/dfrn2.pdf
|
||||
*/
|
||||
|
||||
require_once("include/Contact.php");
|
||||
|
@ -98,9 +99,9 @@ class dfrn {
|
|||
|
||||
$sql_extra = " AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = '' ";
|
||||
|
||||
$r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`
|
||||
$r = q("SELECT `contact`.*, `user`.`nickname`, `user`.`timezone`, `user`.`page-flags`, `user`.`account-type`
|
||||
FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
|
||||
WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1",
|
||||
WHERE `contact`.`self` AND `user`.`nickname` = '%s' LIMIT 1",
|
||||
dbesc($owner_nick)
|
||||
);
|
||||
|
||||
|
@ -112,7 +113,6 @@ class dfrn {
|
|||
$owner_nick = $owner['nickname'];
|
||||
|
||||
$sql_post_table = "";
|
||||
$visibility = "";
|
||||
|
||||
if(! $public_feed) {
|
||||
|
||||
|
@ -135,7 +135,7 @@ class dfrn {
|
|||
break; // NOTREACHED
|
||||
}
|
||||
|
||||
$r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 AND `contact`.`uid` = %d $sql_extra LIMIT 1",
|
||||
$r = q("SELECT * FROM `contact` WHERE NOT `blocked` AND `contact`.`uid` = %d $sql_extra LIMIT 1",
|
||||
intval($owner_id)
|
||||
);
|
||||
|
||||
|
@ -171,9 +171,6 @@ class dfrn {
|
|||
else
|
||||
$sort = 'ASC';
|
||||
|
||||
$date_field = "`changed`";
|
||||
$sql_order = "`item`.`parent` ".$sort.", `item`.`created` ASC";
|
||||
|
||||
if(! strlen($last_update))
|
||||
$last_update = 'now -30 days';
|
||||
|
||||
|
@ -190,22 +187,19 @@ class dfrn {
|
|||
|
||||
$check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s');
|
||||
|
||||
// AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' )
|
||||
// dbesc($check_date),
|
||||
|
||||
$r = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id`,
|
||||
$r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
|
||||
`contact`.`name`, `contact`.`network`, `contact`.`photo`, `contact`.`url`,
|
||||
`contact`.`name-date`, `contact`.`uri-date`, `contact`.`avatar-date`,
|
||||
`contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
|
||||
`sign`.`signed_text`, `sign`.`signature`, `sign`.`signer`
|
||||
FROM `item` $sql_post_table
|
||||
INNER JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
|
||||
AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
|
||||
FROM `item` USE INDEX (`uid_wall_changed`, `uid_type_changed`) $sql_post_table
|
||||
STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
|
||||
AND (NOT `contact`.`blocked` OR `contact`.`pending`)
|
||||
LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id`
|
||||
WHERE `item`.`uid` = %d AND `item`.`visible` = 1 and `item`.`moderated` = 0 AND `item`.`parent` != 0
|
||||
AND ((`item`.`wall` = 1) $visibility) AND `item`.$date_field > '%s'
|
||||
WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`moderated` AND `item`.`parent` != 0
|
||||
AND `item`.`wall` AND `item`.`changed` > '%s'
|
||||
$sql_extra
|
||||
ORDER BY $sql_order LIMIT 0, 300",
|
||||
ORDER BY `item`.`parent` ".$sort.", `item`.`created` ASC LIMIT 0, 300",
|
||||
intval($owner_id),
|
||||
dbesc($check_date),
|
||||
dbesc($sort)
|
||||
|
@ -440,9 +434,13 @@ class dfrn {
|
|||
xml::add_element($doc, $root, "link", "", $attributes);
|
||||
}
|
||||
|
||||
// For backward compatibility we keep this element
|
||||
if ($owner['page-flags'] == PAGE_COMMUNITY)
|
||||
xml::add_element($doc, $root, "dfrn:community", 1);
|
||||
|
||||
// The former element is replaced by this one
|
||||
xml::add_element($doc, $root, "dfrn:account_type", $owner["account-type"]);
|
||||
|
||||
/// @todo We need a way to transmit the different page flags like "PAGE_PRVGROUP"
|
||||
|
||||
xml::add_element($doc, $root, "updated", datetime_convert("UTC", "UTC", "now", ATOM_TIME));
|
||||
|
@ -512,14 +510,16 @@ class dfrn {
|
|||
xml::add_element($doc, $author, "dfrn:birthday", $birthday);
|
||||
|
||||
// Only show contact details when we are allowed to
|
||||
$r = q("SELECT `profile`.`about`, `profile`.`name`, `profile`.`homepage`, `user`.`nickname`, `user`.`timezone`,
|
||||
`profile`.`locality`, `profile`.`region`, `profile`.`country-name`, `profile`.`pub_keywords`, `profile`.`dob`
|
||||
$r = q("SELECT `profile`.`about`, `profile`.`name`, `profile`.`homepage`, `user`.`nickname`,
|
||||
`user`.`timezone`, `profile`.`locality`, `profile`.`region`, `profile`.`country-name`,
|
||||
`profile`.`pub_keywords`, `profile`.`xmpp`, `profile`.`dob`
|
||||
FROM `profile`
|
||||
INNER JOIN `user` ON `user`.`uid` = `profile`.`uid`
|
||||
WHERE `profile`.`is-default` AND NOT `user`.`hidewall` AND `user`.`uid` = %d",
|
||||
intval($owner['uid']));
|
||||
if ($r) {
|
||||
$profile = $r[0];
|
||||
|
||||
xml::add_element($doc, $author, "poco:displayName", $profile["name"]);
|
||||
xml::add_element($doc, $author, "poco:updated", $namdate);
|
||||
|
||||
|
@ -550,12 +550,10 @@ class dfrn {
|
|||
|
||||
}
|
||||
|
||||
/// @todo When we are having the XMPP address in the profile we should propagate it here
|
||||
$xmpp = "";
|
||||
if (trim($xmpp) != "") {
|
||||
if (trim($profile["xmpp"]) != "") {
|
||||
$ims = $doc->createElement("poco:ims");
|
||||
xml::add_element($doc, $ims, "poco:type", "xmpp");
|
||||
xml::add_element($doc, $ims, "poco:value", $xmpp);
|
||||
xml::add_element($doc, $ims, "poco:value", $profile["xmpp"]);
|
||||
xml::add_element($doc, $ims, "poco:primary", "true");
|
||||
$author->appendChild($ims);
|
||||
}
|
||||
|
@ -1143,7 +1141,7 @@ class dfrn {
|
|||
$author["link"] = $xpath->evaluate($element."/atom:uri/text()", $context)->item(0)->nodeValue;
|
||||
|
||||
$r = q("SELECT `id`, `uid`, `url`, `network`, `avatar-date`, `name-date`, `uri-date`, `addr`,
|
||||
`name`, `nick`, `about`, `location`, `keywords`, `bdyear`, `bd`, `hidden`
|
||||
`name`, `nick`, `about`, `location`, `keywords`, `xmpp`, `bdyear`, `bd`, `hidden`, `contact-type`
|
||||
FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' AND `network` != '%s'",
|
||||
intval($importer["uid"]), dbesc(normalise_link($author["link"])), dbesc(NETWORK_STATUSNET));
|
||||
if ($r) {
|
||||
|
@ -1219,9 +1217,13 @@ class dfrn {
|
|||
if ($value != "")
|
||||
$poco["location"] = $value;
|
||||
|
||||
/// @todo Only search for elements with "poco:type" = "xmpp"
|
||||
$value = $xpath->evaluate($element."/poco:ims/poco:value/text()", $context)->item(0)->nodeValue;
|
||||
if ($value != "")
|
||||
$poco["xmpp"] = $value;
|
||||
|
||||
/// @todo Add support for the following fields that we don't support by now in the contact table:
|
||||
/// - poco:utcOffset
|
||||
/// - poco:ims
|
||||
/// - poco:urls
|
||||
/// - poco:locality
|
||||
/// - poco:region
|
||||
|
@ -1308,12 +1310,13 @@ class dfrn {
|
|||
|
||||
q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `about` = '%s', `location` = '%s',
|
||||
`addr` = '%s', `keywords` = '%s', `bdyear` = '%s', `bd` = '%s', `hidden` = %d,
|
||||
`name-date` = '%s', `uri-date` = '%s'
|
||||
`xmpp` = '%s', `name-date` = '%s', `uri-date` = '%s'
|
||||
WHERE `id` = %d AND `network` = '%s'",
|
||||
dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["about"]), dbesc($contact["location"]),
|
||||
dbesc($contact["addr"]), dbesc($contact["keywords"]), dbesc($contact["bdyear"]),
|
||||
dbesc($contact["bd"]), intval($contact["hidden"]), dbesc($contact["name-date"]),
|
||||
dbesc($contact["uri-date"]), intval($contact["id"]), dbesc($contact["network"]));
|
||||
dbesc($contact["bd"]), intval($contact["hidden"]), dbesc($contact["xmpp"]),
|
||||
dbesc($contact["name-date"]), dbesc($contact["uri-date"]),
|
||||
intval($contact["id"]), dbesc($contact["network"]));
|
||||
}
|
||||
|
||||
update_contact_avatar($author["avatar"], $importer["uid"], $contact["id"],
|
||||
|
@ -1327,6 +1330,7 @@ class dfrn {
|
|||
$poco["generation"] = 2;
|
||||
$poco["photo"] = $author["avatar"];
|
||||
$poco["hide"] = $hide;
|
||||
$poco["contact-type"] = $contact["contact-type"];
|
||||
update_gcontact($poco);
|
||||
}
|
||||
|
||||
|
@ -2483,7 +2487,19 @@ class dfrn {
|
|||
|
||||
logger("Import DFRN message for user ".$importer["uid"]." from contact ".$importer["id"], LOGGER_DEBUG);
|
||||
|
||||
// is it a public forum? Private forums aren't supported by now with this method
|
||||
// The account type is new since 3.5.1
|
||||
if ($xpath->query("/atom:feed/dfrn:account_type")->length > 0) {
|
||||
$accounttype = intval($xpath->evaluate("/atom:feed/dfrn:account_type/text()", $context)->item(0)->nodeValue);
|
||||
|
||||
if ($accounttype != $importer["contact-type"])
|
||||
q("UPDATE `contact` SET `contact-type` = %d WHERE `id` = %d",
|
||||
intval($accounttype),
|
||||
intval($importer["id"])
|
||||
);
|
||||
}
|
||||
|
||||
// is it a public forum? Private forums aren't supported with this method
|
||||
// This is deprecated since 3.5.1
|
||||
$forum = intval($xpath->evaluate("/atom:feed/dfrn:community/text()", $context)->item(0)->nodeValue);
|
||||
|
||||
if ($forum != $importer["forum"])
|
||||
|
|
|
@ -999,17 +999,21 @@ class diaspora {
|
|||
*/
|
||||
private function author_contact_by_url($contact, $person, $uid) {
|
||||
|
||||
$r = q("SELECT `id`, `network` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d LIMIT 1",
|
||||
$r = q("SELECT `id`, `network`, `url` FROM `contact` WHERE `nurl` = '%s' AND `uid` = %d LIMIT 1",
|
||||
dbesc(normalise_link($person["url"])), intval($uid));
|
||||
if ($r) {
|
||||
$cid = $r[0]["id"];
|
||||
$network = $r[0]["network"];
|
||||
|
||||
// We are receiving content from a user that is about to be terminated
|
||||
// This means the user is vital, so we remove a possible termination date.
|
||||
unmark_for_death($contact);
|
||||
} else {
|
||||
$cid = $contact["id"];
|
||||
$network = NETWORK_DIASPORA;
|
||||
}
|
||||
|
||||
return (array("cid" => $cid, "network" => $network));
|
||||
return array("cid" => $cid, "network" => $network);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2633,7 +2637,13 @@ class diaspora {
|
|||
} else {
|
||||
// queue message for redelivery
|
||||
add_to_queue($contact["id"], NETWORK_DIASPORA, $slap, $public_batch);
|
||||
|
||||
// The message could not be delivered. We mark the contact as "dead"
|
||||
mark_for_death($contact);
|
||||
}
|
||||
} elseif (($return_code >= 200) AND ($return_code <= 299)) {
|
||||
// We successfully delivered a message, the contact is alive
|
||||
unmark_for_death($contact);
|
||||
}
|
||||
|
||||
return(($return_code) ? $return_code : (-1));
|
||||
|
@ -2876,8 +2886,10 @@ class diaspora {
|
|||
"created_at" => $created,
|
||||
"provider_display_name" => $item["app"]);
|
||||
|
||||
if (count($location) == 0)
|
||||
// Diaspora rejects messages when they contain a location without "lat" or "lng"
|
||||
if (!isset($location["lat"]) OR !isset($location["lng"])) {
|
||||
unset($message["location"]);
|
||||
}
|
||||
|
||||
$type = "status_message";
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ function notification($params) {
|
|||
// with $params['show_in_notification_page'] == false, the notification isn't inserted into
|
||||
// the database, and an email is sent if applicable.
|
||||
// default, if not specified: true
|
||||
$show_in_notification_page = ((x($params, 'show_in_notification_page')) ? $params['show_in_notification_page']:True);
|
||||
$show_in_notification_page = ((x($params, 'show_in_notification_page')) ? $params['show_in_notification_page']:true);
|
||||
|
||||
$additional_mail_header = "";
|
||||
$additional_mail_header .= "Precedence: list\n";
|
||||
|
@ -418,6 +418,7 @@ function notification($params) {
|
|||
$datarray = array();
|
||||
$datarray['hash'] = $hash;
|
||||
$datarray['name'] = $params['source_name'];
|
||||
$datarray['name_cache'] = strip_tags(bbcode($params['source_name']));
|
||||
$datarray['url'] = $params['source_link'];
|
||||
$datarray['photo'] = $params['source_photo'];
|
||||
$datarray['date'] = datetime_convert();
|
||||
|
@ -439,8 +440,8 @@ function notification($params) {
|
|||
|
||||
// create notification entry in DB
|
||||
|
||||
$r = q("INSERT INTO `notify` (`hash`, `name`, `url`, `photo`, `date`, `uid`, `link`, `iid`, `parent`, `type`, `verb`, `otype`)
|
||||
values('%s', '%s', '%s', '%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s')",
|
||||
$r = q("INSERT INTO `notify` (`hash`, `name`, `url`, `photo`, `date`, `uid`, `link`, `iid`, `parent`, `type`, `verb`, `otype`, `name_cache`)
|
||||
values('%s', '%s', '%s', '%s', '%s', %d, '%s', %d, %d, %d, '%s', '%s', '%s')",
|
||||
dbesc($datarray['hash']),
|
||||
dbesc($datarray['name']),
|
||||
dbesc($datarray['url']),
|
||||
|
@ -452,7 +453,8 @@ function notification($params) {
|
|||
intval($datarray['parent']),
|
||||
intval($datarray['type']),
|
||||
dbesc($datarray['verb']),
|
||||
dbesc($datarray['otype'])
|
||||
dbesc($datarray['otype']),
|
||||
dbesc($datarray["name_cache"])
|
||||
);
|
||||
|
||||
$r = q("SELECT `id` FROM `notify` WHERE `hash` = '%s' AND `uid` = %d LIMIT 1",
|
||||
|
@ -494,8 +496,10 @@ function notification($params) {
|
|||
|
||||
$itemlink = $a->get_baseurl().'/notify/view/'.$notify_id;
|
||||
$msg = replace_macros($epreamble, array('$itemlink' => $itemlink));
|
||||
$r = q("UPDATE `notify` SET `msg` = '%s' WHERE `id` = %d AND `uid` = %d",
|
||||
$msg_cache = format_notification_message($datarray['name_cache'], strip_tags(bbcode($msg)));
|
||||
$r = q("UPDATE `notify` SET `msg` = '%s', `msg_cache` = '%s' WHERE `id` = %d AND `uid` = %d",
|
||||
dbesc($msg),
|
||||
dbesc($msg_cache),
|
||||
intval($notify_id),
|
||||
intval($params['uid'])
|
||||
);
|
||||
|
@ -778,4 +782,27 @@ function check_item_notification($itemid, $uid, $defaulttype = "") {
|
|||
if (isset($params["type"]))
|
||||
notification($params);
|
||||
}
|
||||
?>
|
||||
|
||||
/**
|
||||
* @brief Formats a notification message with the notification author
|
||||
*
|
||||
* Replace the name with {0} but ensure to make that only once. The {0} is used
|
||||
* later and prints the name in bold.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $message
|
||||
* @return string Formatted message
|
||||
*/
|
||||
function format_notification_message($name, $message) {
|
||||
if ($name != '') {
|
||||
$pos = strpos($message, $name);
|
||||
} else {
|
||||
$pos = false;
|
||||
}
|
||||
|
||||
if ($pos !== false) {
|
||||
$message = substr_replace($message, '{0}', $pos, strlen($name));
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
|
|
@ -480,6 +480,13 @@ function get_event_strings() {
|
|||
"month" => t("month"),
|
||||
"week" => t("week"),
|
||||
"day" => t("day"),
|
||||
"allday" => t("all-day"),
|
||||
|
||||
"noevent" => t("No events to display"),
|
||||
|
||||
"dtstart_label" => t("Starts:"),
|
||||
"dtend_label" => t("Finishes:"),
|
||||
"location_label" => t("Location:")
|
||||
);
|
||||
|
||||
return $i18n;
|
||||
|
@ -502,7 +509,7 @@ function event_by_id($owner_uid = 0, $event_params, $sql_extra = '') {
|
|||
// query for the event by event id
|
||||
$r = q("SELECT `event`.*, `item`.`id` AS `itemid`,`item`.`plink`,
|
||||
`item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event`
|
||||
LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid`
|
||||
STRAIGHT_JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid`
|
||||
WHERE `event`.`uid` = %d AND `event`.`id` = %d $sql_extra",
|
||||
intval($owner_uid),
|
||||
intval($event_params["event_id"])
|
||||
|
@ -535,7 +542,7 @@ function events_by_date($owner_uid = 0, $event_params, $sql_extra = '') {
|
|||
// query for the event by date
|
||||
$r = q("SELECT `event`.*, `item`.`id` AS `itemid`,`item`.`plink`,
|
||||
`item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event`
|
||||
LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid`
|
||||
STRAIGHT_JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid`
|
||||
WHERE `event`.`uid` = %d AND event.ignore = %d
|
||||
AND ((`adjust` = 0 AND (`finish` >= '%s' OR (nofinish AND start >= '%s')) AND `start` <= '%s')
|
||||
OR (`adjust` = 1 AND (`finish` >= '%s' OR (nofinish AND start >= '%s')) AND `start` <= '%s'))
|
||||
|
|
|
@ -12,15 +12,22 @@
|
|||
*/
|
||||
function feature_enabled($uid, $feature) {
|
||||
|
||||
if (($feature == 'richtext') AND !get_app()->theme_richtext_editor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$x = get_config('feature_lock', $feature);
|
||||
|
||||
if ($x === false) {
|
||||
$x = get_pconfig($uid, 'feature', $feature);
|
||||
if ($x === false) {
|
||||
$x = get_config('feature', $feature);
|
||||
if($x === false)
|
||||
if ($x === false) {
|
||||
$x = get_feature_default($feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$arr = array('uid' => $uid, 'feature' => $feature, 'enabled' => $x);
|
||||
call_hooks('feature_enabled',$arr);
|
||||
return($arr['enabled']);
|
||||
|
@ -72,7 +79,7 @@ function get_features($filtered = true) {
|
|||
t('Post Composition Features'),
|
||||
array('richtext', t('Richtext Editor'), t('Enable richtext editor'), false, get_config('feature_lock','richtext')),
|
||||
array('preview', t('Post Preview'), t('Allow previewing posts and comments before publishing them'), false, get_config('feature_lock','preview')),
|
||||
array('aclautomention', t('Auto-mention Forums'), t('Add/remove mention when a fourm page is selected/deselected in ACL window.'), false, get_config('feature_lock','aclautomention')),
|
||||
array('aclautomention', t('Auto-mention Forums'), t('Add/remove mention when a forum page is selected/deselected in ACL window.'), false, get_config('feature_lock','aclautomention')),
|
||||
),
|
||||
|
||||
// Network sidebar widgets
|
||||
|
@ -135,6 +142,11 @@ function get_features($filtered = true) {
|
|||
}
|
||||
}
|
||||
|
||||
// Remove the richtext editor setting if the theme doesn't support it
|
||||
if (!get_app()->theme_richtext_editor) {
|
||||
unset($arr['composition'][1]);
|
||||
}
|
||||
|
||||
call_hooks('get_features',$arr);
|
||||
return $arr;
|
||||
}
|
||||
|
|
|
@ -325,6 +325,14 @@ function feed_import($xml,$importer,&$contact, &$hub, $simulate = false) {
|
|||
logger("Stored feed: ".print_r($item, true), LOGGER_DEBUG);
|
||||
|
||||
$notify = item_is_remote_self($contact, $item);
|
||||
|
||||
// Distributed items should have a well formatted URI.
|
||||
// Additionally we have to avoid conflicts with identical URI between imported feeds and these items.
|
||||
if ($notify) {
|
||||
unset($item['uri']);
|
||||
unset($item['parent-uri']);
|
||||
}
|
||||
|
||||
$id = item_store($item, false, $notify);
|
||||
|
||||
logger("Feed for contact ".$contact["url"]." stored under id ".$id);
|
||||
|
|
|
@ -270,7 +270,7 @@ function new_contact($uid,$url,$interactive = false) {
|
|||
|
||||
// pull feed and consume it, which should subscribe to the hub.
|
||||
|
||||
proc_run(PRIORITY_MEDIUM, "include/onepoll.php", $contact_id, "force");
|
||||
proc_run(PRIORITY_HIGH, "include/onepoll.php", $contact_id, "force");
|
||||
|
||||
// create a follow slap
|
||||
|
||||
|
|
|
@ -39,14 +39,13 @@ function gprobe_run(&$argv, &$argc){
|
|||
|
||||
logger("gprobe start for ".normalise_link($url), LOGGER_DEBUG);
|
||||
|
||||
if (!count($r)) {
|
||||
if (!dbm::is_result($r)) {
|
||||
|
||||
// Is it a DDoS attempt?
|
||||
$urlparts = parse_url($url);
|
||||
|
||||
$result = Cache::get("gprobe:".$urlparts["host"]);
|
||||
if (!is_null($result)) {
|
||||
$result = unserialize($result);
|
||||
if (in_array($result["network"], array(NETWORK_FEED, NETWORK_PHANTOM))) {
|
||||
logger("DDoS attempt detected for ".$urlparts["host"]." by ".$_SERVER["REMOTE_ADDR"].". server data: ".print_r($_SERVER, true), LOGGER_DEBUG);
|
||||
return;
|
||||
|
@ -56,7 +55,7 @@ function gprobe_run(&$argv, &$argc){
|
|||
$arr = probe_url($url);
|
||||
|
||||
if (is_null($result))
|
||||
Cache::set("gprobe:".$urlparts["host"],serialize($arr));
|
||||
Cache::set("gprobe:".$urlparts["host"], $arr);
|
||||
|
||||
if (!in_array($arr["network"], array(NETWORK_FEED, NETWORK_PHANTOM)))
|
||||
update_gcontact($arr);
|
||||
|
@ -65,7 +64,7 @@ function gprobe_run(&$argv, &$argc){
|
|||
dbesc(normalise_link($url))
|
||||
);
|
||||
}
|
||||
if(count($r)) {
|
||||
if(dbm::is_result($r)) {
|
||||
// Check for accessibility and do a poco discovery
|
||||
if (poco_last_updated($r[0]['url'], true) AND ($r[0]["network"] == NETWORK_DFRN))
|
||||
poco_load(0,0,$r[0]['id'], str_replace('/profile/','/poco/',$r[0]['url']));
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
<?php
|
||||
/*
|
||||
html2bbcode.php
|
||||
Converter for HTML to BBCode
|
||||
Made by: ike@piratenpartei.de
|
||||
Originally made for the syncom project: http://wiki.piratenpartei.de/Syncom
|
||||
https://github.com/annando/Syncom
|
||||
/**
|
||||
* @file include/html2bbcode.php
|
||||
* @brief Converter for HTML to BBCode
|
||||
*
|
||||
* Made by: ike@piratenpartei.de
|
||||
* Originally made for the syncom project: http://wiki.piratenpartei.de/Syncom
|
||||
* https://github.com/annando/Syncom
|
||||
*/
|
||||
|
||||
require_once("include/xml.php");
|
||||
|
||||
function node2bbcode(&$doc, $oldnode, $attributes, $startbb, $endbb)
|
||||
{
|
||||
do {
|
||||
|
@ -76,15 +79,6 @@ function node2bbcodesub(&$doc, $oldnode, $attributes, $startbb, $endbb)
|
|||
return($replace);
|
||||
}
|
||||
|
||||
if(!function_exists('deletenode')) {
|
||||
function deletenode(&$doc, $node)
|
||||
{
|
||||
$xpath = new DomXPath($doc);
|
||||
$list = $xpath->query("//".$node);
|
||||
foreach ($list as $child)
|
||||
$child->parentNode->removeChild($child);
|
||||
}}
|
||||
|
||||
function _replace_code_cb($m){
|
||||
return "<code>".str_replace("\n","<br>\n",$m[1]). "</code>";
|
||||
}
|
||||
|
@ -117,12 +111,12 @@ function html2bbcode($message)
|
|||
|
||||
@$doc->loadHTML($message);
|
||||
|
||||
deletenode($doc, 'style');
|
||||
deletenode($doc, 'head');
|
||||
deletenode($doc, 'title');
|
||||
deletenode($doc, 'meta');
|
||||
deletenode($doc, 'xml');
|
||||
deletenode($doc, 'removeme');
|
||||
xml::deleteNode($doc, 'style');
|
||||
xml::deleteNode($doc, 'head');
|
||||
xml::deleteNode($doc, 'title');
|
||||
xml::deleteNode($doc, 'meta');
|
||||
xml::deleteNode($doc, 'xml');
|
||||
xml::deleteNode($doc, 'removeme');
|
||||
|
||||
$xpath = new DomXPath($doc);
|
||||
$list = $xpath->query("//pre");
|
||||
|
|
|
@ -149,17 +149,23 @@ function get_profiledata_by_nick($nickname, $uid = 0, $profile = 0) {
|
|||
|
||||
if($profile) {
|
||||
$profile_int = intval($profile);
|
||||
$r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `contact`.`avatar-date` AS picdate, `contact`.`addr`, `user`.* FROM `profile`
|
||||
INNER JOIN `contact` on `contact`.`uid` = `profile`.`uid` INNER JOIN `user` ON `profile`.`uid` = `user`.`uid`
|
||||
WHERE `user`.`nickname` = '%s' AND `profile`.`id` = %d AND `contact`.`self` = 1 LIMIT 1",
|
||||
$r = q("SELECT `contact`.`id` AS `contact_id`, `profile`.`uid` AS `profile_uid`, `profile`.*,
|
||||
`contact`.`avatar-date` AS picdate, `contact`.`addr`, `user`.*
|
||||
FROM `profile`
|
||||
INNER JOIN `contact` on `contact`.`uid` = `profile`.`uid` AND `contact`.`self`
|
||||
INNER JOIN `user` ON `profile`.`uid` = `user`.`uid`
|
||||
WHERE `user`.`nickname` = '%s' AND `profile`.`id` = %d LIMIT 1",
|
||||
dbesc($nickname),
|
||||
intval($profile_int)
|
||||
);
|
||||
}
|
||||
if((!$r) && (!count($r))) {
|
||||
$r = q("SELECT `profile`.`uid` AS `profile_uid`, `profile`.* , `contact`.`avatar-date` AS picdate, `contact`.`addr`, `user`.* FROM `profile`
|
||||
INNER JOIN `contact` ON `contact`.`uid` = `profile`.`uid` INNER JOIN `user` ON `profile`.`uid` = `user`.`uid`
|
||||
WHERE `user`.`nickname` = '%s' AND `profile`.`is-default` = 1 AND `contact`.`self` = 1 LIMIT 1",
|
||||
$r = q("SELECT `contact`.`id` AS `contact_id`, `profile`.`uid` AS `profile_uid`, `profile`.*,
|
||||
`contact`.`avatar-date` AS picdate, `contact`.`addr`, `user`.*
|
||||
FROM `profile`
|
||||
INNER JOIN `contact` ON `contact`.`uid` = `profile`.`uid` AND `contact`.`self`
|
||||
INNER JOIN `user` ON `profile`.`uid` = `user`.`uid`
|
||||
WHERE `user`.`nickname` = '%s' AND `profile`.`is-default` LIMIT 1",
|
||||
dbesc($nickname)
|
||||
);
|
||||
}
|
||||
|
@ -310,15 +316,8 @@ function profile_sidebar($profile, $block = 0) {
|
|||
);
|
||||
}
|
||||
|
||||
// check if profile is a forum
|
||||
if((intval($profile['page-flags']) == PAGE_COMMUNITY)
|
||||
|| (intval($profile['page-flags']) == PAGE_PRVGROUP)
|
||||
|| (isset($profile['forum']) && intval($profile['forum']))
|
||||
|| (isset($profile['prv']) && intval($profile['prv']))
|
||||
|| (isset($profile['community']) && intval($profile['community'])))
|
||||
$account_type = t('Forum');
|
||||
else
|
||||
$account_type = "";
|
||||
// Fetch the account type
|
||||
$account_type = account_type($profile);
|
||||
|
||||
if((x($profile,'address') == 1)
|
||||
|| (x($profile,'location') == 1)
|
||||
|
@ -337,6 +336,8 @@ function profile_sidebar($profile, $block = 0) {
|
|||
|
||||
$about = ((x($profile,'about') == 1) ? t('About:') : False);
|
||||
|
||||
$xmpp = ((x($profile,'xmpp') == 1) ? t('XMPP:') : False);
|
||||
|
||||
if(($profile['hidewall'] || $block) && (! local_user()) && (! remote_user())) {
|
||||
$location = $pdesc = $gender = $marital = $homepage = $about = False;
|
||||
}
|
||||
|
@ -370,7 +371,7 @@ function profile_sidebar($profile, $block = 0) {
|
|||
if(count($r))
|
||||
$updated = date("c", strtotime($r[0]['updated']));
|
||||
|
||||
$r = q("SELECT COUNT(*) AS `total` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0
|
||||
$r = q("SELECT COUNT(*) AS `total` FROM `contact` WHERE `uid` = %d AND NOT `self` AND NOT `blocked` AND NOT `hidden` AND NOT `archive`
|
||||
AND `network` IN ('%s', '%s', '%s', '')",
|
||||
intval($profile['uid']),
|
||||
dbesc(NETWORK_DFRN),
|
||||
|
@ -405,6 +406,7 @@ function profile_sidebar($profile, $block = 0) {
|
|||
$tpl = get_markup_template('profile_vcard.tpl');
|
||||
$o .= replace_macros($tpl, array(
|
||||
'$profile' => $p,
|
||||
'$xmpp' => $xmpp,
|
||||
'$connect' => $connect,
|
||||
'$remoteconnect' => $remoteconnect,
|
||||
'$subscribe_feed' => $subscribe_feed,
|
||||
|
@ -811,7 +813,6 @@ function zrl_init(&$a) {
|
|||
|
||||
$result = Cache::get("gprobe:".$urlparts["host"]);
|
||||
if (!is_null($result)) {
|
||||
$result = unserialize($result);
|
||||
if (in_array($result["network"], array(NETWORK_FEED, NETWORK_PHANTOM))) {
|
||||
logger("DDoS attempt detected for ".$urlparts["host"]." by ".$_SERVER["REMOTE_ADDR"].". server data: ".print_r($_SERVER, true), LOGGER_DEBUG);
|
||||
return;
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file include/items.php
|
||||
*/
|
||||
|
||||
use \Friendica\ParseUrl;
|
||||
|
||||
require_once('include/bbcode.php');
|
||||
require_once('include/oembed.php');
|
||||
require_once('include/salmon.php');
|
||||
|
@ -69,15 +75,13 @@ function limit_body_size($body) {
|
|||
$new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
|
||||
$textlen = $maxlen;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$new_body = $new_body . substr($orig_body, 0, $img_start);
|
||||
$textlen += $img_start;
|
||||
}
|
||||
|
||||
$new_body = $new_body . substr($orig_body, $img_start, $img_end - $img_start);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
|
||||
if ( ($textlen + $img_end) > $maxlen ) {
|
||||
if ($textlen < $maxlen) {
|
||||
|
@ -85,8 +89,7 @@ function limit_body_size($body) {
|
|||
$new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
|
||||
$textlen = $maxlen;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$new_body = $new_body . substr($orig_body, 0, $img_end);
|
||||
$textlen += $img_end;
|
||||
}
|
||||
|
@ -107,16 +110,14 @@ function limit_body_size($body) {
|
|||
$new_body = $new_body . substr($orig_body, 0, $maxlen - $textlen);
|
||||
$textlen = $maxlen;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
logger('limit_body_size: the text size with embedded images extracted did not violate the limit', LOGGER_DEBUG);
|
||||
$new_body = $new_body . $orig_body;
|
||||
$textlen += strlen($orig_body);
|
||||
}
|
||||
|
||||
return $new_body;
|
||||
}
|
||||
else
|
||||
} else
|
||||
return $body;
|
||||
}}
|
||||
|
||||
|
@ -152,19 +153,23 @@ function add_page_info_data($data) {
|
|||
// It maybe is a rich content, but if it does have everything that a link has,
|
||||
// then treat it that way
|
||||
if (($data["type"] == "rich") AND is_string($data["title"]) AND
|
||||
is_string($data["text"]) AND (sizeof($data["images"]) > 0))
|
||||
is_string($data["text"]) AND (sizeof($data["images"]) > 0)) {
|
||||
$data["type"] = "link";
|
||||
}
|
||||
|
||||
if ((($data["type"] != "link") AND ($data["type"] != "video") AND ($data["type"] != "photo")) OR ($data["title"] == $url))
|
||||
return("");
|
||||
if ((($data["type"] != "link") AND ($data["type"] != "video") AND ($data["type"] != "photo")) OR ($data["title"] == $data["url"])) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if ($no_photos AND ($data["type"] == "photo"))
|
||||
return("");
|
||||
if ($no_photos AND ($data["type"] == "photo")) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (sizeof($data["images"]) > 0)
|
||||
if (sizeof($data["images"]) > 0) {
|
||||
$preview = $data["images"][0];
|
||||
else
|
||||
} else {
|
||||
$preview = "";
|
||||
}
|
||||
|
||||
// Escape some bad characters
|
||||
$data["url"] = str_replace(array("[", "]"), array("[", "]"), htmlentities($data["url"], ENT_QUOTES, 'UTF-8', false));
|
||||
|
@ -172,19 +177,33 @@ function add_page_info_data($data) {
|
|||
|
||||
$text = "[attachment type='".$data["type"]."'";
|
||||
|
||||
if ($data["url"] != "")
|
||||
if ($data["text"] == "") {
|
||||
$data["text"] = $data["title"];
|
||||
}
|
||||
|
||||
if ($data["text"] == "") {
|
||||
$data["text"] = $data["url"];
|
||||
}
|
||||
|
||||
if ($data["url"] != "") {
|
||||
$text .= " url='".$data["url"]."'";
|
||||
if ($data["title"] != "")
|
||||
}
|
||||
|
||||
if ($data["title"] != "") {
|
||||
$text .= " title='".$data["title"]."'";
|
||||
}
|
||||
|
||||
if (sizeof($data["images"]) > 0) {
|
||||
$preview = str_replace(array("[", "]"), array("[", "]"), htmlentities($data["images"][0]["src"], ENT_QUOTES, 'UTF-8', false));
|
||||
// if the preview picture is larger than 500 pixels then show it in a larger mode
|
||||
// But only, if the picture isn't higher than large (To prevent huge posts)
|
||||
if (($data["images"][0]["width"] >= 500) AND ($data["images"][0]["width"] >= $data["images"][0]["height"]))
|
||||
if (($data["images"][0]["width"] >= 500) AND ($data["images"][0]["width"] >= $data["images"][0]["height"])) {
|
||||
$text .= " image='".$preview."'";
|
||||
else
|
||||
} else {
|
||||
$text .= " preview='".$preview."'";
|
||||
}
|
||||
}
|
||||
|
||||
$text .= "]".$data["text"]."[/attachment]";
|
||||
|
||||
$hashtags = "";
|
||||
|
@ -203,9 +222,8 @@ function add_page_info_data($data) {
|
|||
}
|
||||
|
||||
function query_page_info($url, $no_photos = false, $photo = "", $keywords = false, $keyword_blacklist = "") {
|
||||
require_once("mod/parse_url.php");
|
||||
|
||||
$data = parseurl_getsiteinfo_cached($url, true);
|
||||
$data = ParseUrl::getSiteinfoCached($url, true);
|
||||
|
||||
if ($photo != "")
|
||||
$data["images"][0]["src"] = $photo;
|
||||
|
@ -262,6 +280,10 @@ function add_page_info_to_body($body, $texturl = false, $no_photos = false) {
|
|||
|
||||
$URLSearchString = "^\[\]";
|
||||
|
||||
// Fix for Mastodon where the mentions are in a different format
|
||||
$body = preg_replace("/\[url\=([$URLSearchString]*)\]([#!@])(.*?)\[\/url\]/ism",
|
||||
'$2[url=$1]$3[/url]', $body);
|
||||
|
||||
// Adding these spaces is a quick hack due to my problems with regular expressions :)
|
||||
preg_match("/[^!#@]\[url\]([$URLSearchString]*)\[\/url\]/ism", " ".$body, $matches);
|
||||
|
||||
|
@ -319,9 +341,7 @@ function item_add_language_opt(&$arr) {
|
|||
return;
|
||||
}
|
||||
$postopts = $arr['postopts'];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
$postopts = "";
|
||||
}
|
||||
|
||||
|
@ -348,14 +368,20 @@ function item_add_language_opt(&$arr) {
|
|||
* @brief Creates an unique guid out of a given uri
|
||||
*
|
||||
* @param string $uri uri of an item entry
|
||||
* @param string $host (Optional) hostname for the GUID prefix
|
||||
* @return string unique guid
|
||||
*/
|
||||
function uri_to_guid($uri) {
|
||||
function uri_to_guid($uri, $host = "") {
|
||||
|
||||
// Our regular guid routine is using this kind of prefix as well
|
||||
// We have to avoid that different routines could accidentally create the same value
|
||||
$parsed = parse_url($uri);
|
||||
$guid_prefix = hash("crc32", $parsed["host"]);
|
||||
|
||||
if ($host == "") {
|
||||
$host = $parsed["host"];
|
||||
}
|
||||
|
||||
$guid_prefix = hash("crc32", $host);
|
||||
|
||||
// Remove the scheme to make sure that "https" and "http" doesn't make a difference
|
||||
unset($parsed["scheme"]);
|
||||
|
@ -370,6 +396,8 @@ function uri_to_guid($uri) {
|
|||
|
||||
function item_store($arr,$force_parent = false, $notify = false, $dontcache = false) {
|
||||
|
||||
$a = get_app();
|
||||
|
||||
// If it is a posting where users should get notifications, then define it as wall posting
|
||||
if ($notify) {
|
||||
$arr['wall'] = 1;
|
||||
|
@ -377,6 +405,15 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
$arr['origin'] = 1;
|
||||
$arr['last-child'] = 1;
|
||||
$arr['network'] = NETWORK_DFRN;
|
||||
|
||||
// We have to avoid duplicates. So we create the GUID in form of a hash of the plink or uri.
|
||||
// In difference to the call to "uri_to_guid" several lines below we add the hash of our own host.
|
||||
// This is done because our host is the original creator of the post.
|
||||
if (isset($arr['plink'])) {
|
||||
$arr['guid'] = uri_to_guid($arr['plink'], $a->get_hostname());
|
||||
} elseif (isset($arr['uri'])) {
|
||||
$arr['guid'] = uri_to_guid($arr['uri'], $a->get_hostname());
|
||||
}
|
||||
}
|
||||
|
||||
// If a Diaspora signature structure was passed in, pull it out of the
|
||||
|
@ -384,6 +421,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
|
||||
$dsprsig = null;
|
||||
if (x($arr,'dsprsig')) {
|
||||
$encoded_signature = $arr['dsprsig'];
|
||||
$dsprsig = json_decode(base64_decode($arr['dsprsig']));
|
||||
unset($arr['dsprsig']);
|
||||
}
|
||||
|
@ -463,7 +501,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
|
||||
$arr['wall'] = ((x($arr,'wall')) ? intval($arr['wall']) : 0);
|
||||
$arr['guid'] = ((x($arr,'guid')) ? notags(trim($arr['guid'])) : get_guid(32, $guid_prefix));
|
||||
$arr['uri'] = ((x($arr,'uri')) ? notags(trim($arr['uri'])) : $arr['guid']);
|
||||
$arr['uri'] = ((x($arr,'uri')) ? notags(trim($arr['uri'])) : item_new_uri($a->get_hostname(), $uid, $arr['guid']));
|
||||
$arr['extid'] = ((x($arr,'extid')) ? notags(trim($arr['extid'])) : '');
|
||||
$arr['author-name'] = ((x($arr,'author-name')) ? trim($arr['author-name']) : '');
|
||||
$arr['author-link'] = ((x($arr,'author-link')) ? notags(trim($arr['author-link'])) : '');
|
||||
|
@ -482,7 +520,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
$arr['last-child'] = ((x($arr,'last-child')) ? intval($arr['last-child']) : 0 );
|
||||
$arr['visible'] = ((x($arr,'visible') !== false) ? intval($arr['visible']) : 1 );
|
||||
$arr['deleted'] = 0;
|
||||
$arr['parent-uri'] = ((x($arr,'parent-uri')) ? notags(trim($arr['parent-uri'])) : '');
|
||||
$arr['parent-uri'] = ((x($arr,'parent-uri')) ? notags(trim($arr['parent-uri'])) : $arr['uri']);
|
||||
$arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : '');
|
||||
$arr['object-type'] = ((x($arr,'object-type')) ? notags(trim($arr['object-type'])) : '');
|
||||
$arr['object'] = ((x($arr,'object')) ? trim($arr['object']) : '');
|
||||
|
@ -614,8 +652,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
$deny_cid = $arr['deny_cid'];
|
||||
$deny_gid = $arr['deny_gid'];
|
||||
$notify_type = 'wall-new';
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
|
||||
// find the parent and snarf the item id and ACLs
|
||||
// and anything else we need to inherit
|
||||
|
@ -669,7 +706,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
|
||||
// If its a post from myself then tag the thread as "mention"
|
||||
logger("item_store: Checking if parent ".$parent_id." has to be tagged as mention for user ".$arr['uid'], LOGGER_DEBUG);
|
||||
$u = q("select * from user where uid = %d limit 1", intval($arr['uid']));
|
||||
$u = q("SELECT `nickname` FROM `user` WHERE `uid` = %d", intval($arr['uid']));
|
||||
if (count($u)) {
|
||||
$a = get_app();
|
||||
$self = normalise_link($a->get_baseurl() . '/profile/' . $u[0]['nickname']);
|
||||
|
@ -679,8 +716,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
logger("item_store: tagged thread ".$parent_id." as mention for user ".$self, LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
|
||||
// Allow one to see reply tweets from status.net even when
|
||||
// we don't have or can't see the original post.
|
||||
|
@ -690,8 +726,7 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
$parent_id = 0;
|
||||
$arr['parent-uri'] = $arr['uri'];
|
||||
$arr['gravity'] = 0;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
logger('item_store: item parent '.$arr['parent-uri'].' for '.$arr['uid'].' was not found - ignoring item');
|
||||
return 0;
|
||||
}
|
||||
|
@ -706,11 +741,22 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
dbesc(NETWORK_DFRN),
|
||||
intval($arr['uid'])
|
||||
);
|
||||
if($r && count($r)) {
|
||||
if (dbm::is_result($r)) {
|
||||
logger('duplicated item with the same uri found. '.print_r($arr,true));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// On Friendica and Diaspora the GUID is unique
|
||||
if (in_array($arr['network'], array(NETWORK_DFRN, NETWORK_DIASPORA))) {
|
||||
$r = q("SELECT `id` FROM `item` WHERE `guid` = '%s' AND `uid` = %d LIMIT 1",
|
||||
dbesc($arr['guid']),
|
||||
intval($arr['uid'])
|
||||
);
|
||||
if (dbm::is_result($r)) {
|
||||
logger('duplicated item with the same guid found. '.print_r($arr,true));
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
// Check for an existing post with the same content. There seems to be a problem with OStatus.
|
||||
$r = q("SELECT `id` FROM `item` WHERE `body` = '%s' AND `network` = '%s' AND `created` = '%s' AND `contact-id` = %d AND `uid` = %d LIMIT 1",
|
||||
dbesc($arr['body']),
|
||||
|
@ -719,15 +765,17 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
intval($arr['contact-id']),
|
||||
intval($arr['uid'])
|
||||
);
|
||||
if($r && count($r)) {
|
||||
if (dbm::is_result($r)) {
|
||||
logger('duplicated item with the same body found. '.print_r($arr,true));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Is this item available in the global items (with uid=0)?
|
||||
if ($arr["uid"] == 0) {
|
||||
$arr["global"] = true;
|
||||
|
||||
// Set the global flag on all items if this was a global item entry
|
||||
q("UPDATE `item` SET `global` = 1 WHERE `uri` = '%s'", dbesc($arr["uri"]));
|
||||
} else {
|
||||
$isglobal = q("SELECT `global` FROM `item` WHERE `uid` = 0 AND `uri` = '%s'", dbesc($arr["uri"]));
|
||||
|
@ -735,6 +783,19 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
$arr["global"] = (count($isglobal) > 0);
|
||||
}
|
||||
|
||||
// ACL settings
|
||||
if (strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid))
|
||||
$private = 1;
|
||||
else
|
||||
$private = $arr['private'];
|
||||
|
||||
$arr["allow_cid"] = $allow_cid;
|
||||
$arr["allow_gid"] = $allow_gid;
|
||||
$arr["deny_cid"] = $deny_cid;
|
||||
$arr["deny_gid"] = $deny_gid;
|
||||
$arr["private"] = $private;
|
||||
$arr["deleted"] = $parent_deleted;
|
||||
|
||||
// Fill the cache field
|
||||
put_item_in_cache($arr);
|
||||
|
||||
|
@ -748,6 +809,17 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Check for already added items.
|
||||
// There is a timing issue here that sometimes creates double postings.
|
||||
// An unique index would help - but the limitations of MySQL (maximum size of index values) prevent this.
|
||||
if ($arr["uid"] == 0) {
|
||||
$r = qu("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = 0 LIMIT 1", dbesc(trim($arr['uri'])));
|
||||
if (dbm::is_result($r)) {
|
||||
logger('Global item already stored. URI: '.$arr['uri'].' on network '.$arr['network'], LOGGER_DEBUG);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the unescaped version
|
||||
$unescaped = $arr;
|
||||
|
||||
|
@ -755,6 +827,9 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
|
||||
logger('item_store: ' . print_r($arr,true), LOGGER_DATA);
|
||||
|
||||
q("COMMIT");
|
||||
q("START TRANSACTION;");
|
||||
|
||||
$r = dbq("INSERT INTO `item` (`"
|
||||
. implode("`, `", array_keys($arr))
|
||||
. "`) VALUES ('"
|
||||
|
@ -764,84 +839,85 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
// And restore it
|
||||
$arr = $unescaped;
|
||||
|
||||
// find the item that we just created
|
||||
$r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `network` = '%s' ORDER BY `id` ASC",
|
||||
// When the item was successfully stored we fetch the ID of the item.
|
||||
if (dbm::is_result($r)) {
|
||||
$r = q("SELECT LAST_INSERT_ID() AS `item-id`");
|
||||
if (dbm::is_result($r)) {
|
||||
$current_post = $r[0]['item-id'];
|
||||
} else {
|
||||
// This shouldn't happen
|
||||
$current_post = 0;
|
||||
}
|
||||
} else {
|
||||
// This can happen - for example - if there are locking timeouts.
|
||||
q("ROLLBACK");
|
||||
|
||||
// Store the data into a spool file so that we can try again later.
|
||||
|
||||
// At first we restore the Diaspora signature that we removed above.
|
||||
if (isset($encoded_signature)) {
|
||||
$arr['dsprsig'] = $encoded_signature;
|
||||
}
|
||||
|
||||
// Now we store the data in the spool directory
|
||||
$file = 'item-'.round(microtime(true) * 10000).".msg";
|
||||
$spool = get_spoolpath().'/'.$file;
|
||||
file_put_contents($spool, json_encode($arr));
|
||||
logger("Item wasn't stored - Item was spooled into file ".$file, LOGGER_DEBUG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($current_post == 0) {
|
||||
// This is one of these error messages that never should occur.
|
||||
logger("couldn't find created item - we better quit now.");
|
||||
q("ROLLBACK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// How much entries have we created?
|
||||
// We wouldn't need this query when we could use an unique index - but MySQL has length problems with them.
|
||||
$r = q("SELECT COUNT(*) AS `entries` FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `network` = '%s'",
|
||||
dbesc($arr['uri']),
|
||||
intval($arr['uid']),
|
||||
dbesc($arr['network'])
|
||||
);
|
||||
|
||||
if(count($r) > 1) {
|
||||
// There are duplicates. Keep the oldest one, delete the others
|
||||
logger('item_store: duplicated post occurred. Removing newer duplicates. uri = '.$arr['uri'].' uid = '.$arr['uid']);
|
||||
q("DELETE FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `network` = '%s' AND `id` > %d",
|
||||
dbesc($arr['uri']),
|
||||
intval($arr['uid']),
|
||||
dbesc($arr['network']),
|
||||
intval($r[0]["id"])
|
||||
);
|
||||
if (!dbm::is_result($r)) {
|
||||
// This shouldn't happen, since COUNT always works when the database connection is there.
|
||||
logger("We couldn't count the stored entries. Very strange ...");
|
||||
q("ROLLBACK");
|
||||
return 0;
|
||||
} elseif(count($r)) {
|
||||
}
|
||||
|
||||
if ($r[0]["entries"] > 1) {
|
||||
// There are duplicates. We delete our just created entry.
|
||||
logger('Duplicated post occurred. uri = '.$arr['uri'].' uid = '.$arr['uid']);
|
||||
|
||||
// Yes, we could do a rollback here - but we are having many users with MyISAM.
|
||||
q("DELETE FROM `item` WHERE `id` = %d", intval($current_post));
|
||||
q("COMMIT");
|
||||
return 0;
|
||||
} elseif ($r[0]["entries"] == 0) {
|
||||
// This really should never happen since we quit earlier if there were problems.
|
||||
logger("Something is terribly wrong. We haven't found our created entry.");
|
||||
q("ROLLBACK");
|
||||
return 0;
|
||||
}
|
||||
|
||||
$current_post = $r[0]['id'];
|
||||
logger('item_store: created item '.$current_post);
|
||||
item_set_last_item($arr);
|
||||
|
||||
// Set "success_update" and "last-item" to the date of the last time we heard from this contact
|
||||
// This can be used to filter for inactive contacts.
|
||||
// Only do this for public postings to avoid privacy problems, since poco data is public.
|
||||
// Don't set this value if it isn't from the owner (could be an author that we don't know)
|
||||
|
||||
$update = (!$arr['private'] AND (($arr["author-link"] === $arr["owner-link"]) OR ($arr["parent-uri"] === $arr["uri"])));
|
||||
|
||||
// Is it a forum? Then we don't care about the rules from above
|
||||
if (!$update AND ($arr["network"] == NETWORK_DFRN) AND ($arr["parent-uri"] === $arr["uri"])) {
|
||||
$isforum = q("SELECT `forum` FROM `contact` WHERE `id` = %d AND `forum`",
|
||||
intval($arr['contact-id']));
|
||||
if ($isforum)
|
||||
$update = true;
|
||||
}
|
||||
|
||||
if ($update)
|
||||
q("UPDATE `contact` SET `success_update` = '%s', `last-item` = '%s' WHERE `id` = %d",
|
||||
dbesc($arr['received']),
|
||||
dbesc($arr['received']),
|
||||
intval($arr['contact-id'])
|
||||
);
|
||||
} else {
|
||||
logger('item_store: could not locate created item');
|
||||
return 0;
|
||||
}
|
||||
|
||||
if((! $parent_id) || ($arr['parent-uri'] === $arr['uri']))
|
||||
if (!$parent_id || ($arr['parent-uri'] === $arr['uri']))
|
||||
$parent_id = $current_post;
|
||||
|
||||
if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid))
|
||||
$private = 1;
|
||||
else
|
||||
$private = $arr['private'];
|
||||
|
||||
// Set parent id - and also make sure to inherit the parent's ACLs.
|
||||
|
||||
$r = q("UPDATE `item` SET `parent` = %d, `allow_cid` = '%s', `allow_gid` = '%s',
|
||||
`deny_cid` = '%s', `deny_gid` = '%s', `private` = %d, `deleted` = %d WHERE `id` = %d",
|
||||
// Set parent id
|
||||
$r = q("UPDATE `item` SET `parent` = %d WHERE `id` = %d",
|
||||
intval($parent_id),
|
||||
dbesc($allow_cid),
|
||||
dbesc($allow_gid),
|
||||
dbesc($deny_cid),
|
||||
dbesc($deny_gid),
|
||||
intval($private),
|
||||
intval($parent_deleted),
|
||||
intval($current_post)
|
||||
);
|
||||
|
||||
$arr['id'] = $current_post;
|
||||
$arr['parent'] = $parent_id;
|
||||
$arr['allow_cid'] = $allow_cid;
|
||||
$arr['allow_gid'] = $allow_gid;
|
||||
$arr['deny_cid'] = $deny_cid;
|
||||
$arr['deny_gid'] = $deny_gid;
|
||||
$arr['private'] = $private;
|
||||
$arr['deleted'] = $parent_deleted;
|
||||
|
||||
// update the commented timestamp on the parent
|
||||
// Only update "commented" if it is really a comment
|
||||
|
@ -874,19 +950,6 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If this is now the last-child, force all _other_ children of this parent to *not* be last-child
|
||||
*/
|
||||
|
||||
if($arr['last-child']) {
|
||||
$r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d",
|
||||
dbesc($arr['uri']),
|
||||
intval($arr['uid']),
|
||||
intval($current_post)
|
||||
);
|
||||
}
|
||||
|
||||
$deleted = tag_deliver($arr['uid'],$current_post);
|
||||
|
||||
// current post can be deleted if is for a community page and no mention are
|
||||
|
@ -903,15 +966,33 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
logger('item_store: new item not found in DB, id ' . $current_post);
|
||||
}
|
||||
|
||||
if ($arr['parent-uri'] === $arr['uri']) {
|
||||
add_thread($current_post);
|
||||
} else {
|
||||
update_thread($parent_id);
|
||||
}
|
||||
|
||||
q("COMMIT");
|
||||
|
||||
// Due to deadlock issues with the "term" table we are doing these steps after the commit.
|
||||
// This is not perfect - but a workable solution until we found the reason for the problem.
|
||||
create_tags_from_item($current_post);
|
||||
create_files_from_item($current_post);
|
||||
|
||||
// Only check for notifications on start posts
|
||||
if ($arr['parent-uri'] === $arr['uri'])
|
||||
add_thread($current_post);
|
||||
else {
|
||||
update_thread($parent_id);
|
||||
add_shadow_entry($arr);
|
||||
// If this is now the last-child, force all _other_ children of this parent to *not* be last-child
|
||||
// It is done after the transaction to avoid dead locks.
|
||||
if ($arr['last-child']) {
|
||||
$r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d",
|
||||
dbesc($arr['uri']),
|
||||
intval($arr['uid']),
|
||||
intval($current_post)
|
||||
);
|
||||
}
|
||||
|
||||
if ($arr['parent-uri'] === $arr['uri']) {
|
||||
add_shadow_thread($current_post);
|
||||
} else {
|
||||
add_shadow_entry($current_post);
|
||||
}
|
||||
|
||||
check_item_notification($current_post, $uid);
|
||||
|
@ -922,6 +1003,53 @@ function item_store($arr,$force_parent = false, $notify = false, $dontcache = fa
|
|||
return $current_post;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set "success_update" and "last-item" to the date of the last time we heard from this contact
|
||||
*
|
||||
* This can be used to filter for inactive contacts.
|
||||
* Only do this for public postings to avoid privacy problems, since poco data is public.
|
||||
* Don't set this value if it isn't from the owner (could be an author that we don't know)
|
||||
*
|
||||
* @param array $arr Contains the just posted item record
|
||||
*/
|
||||
function item_set_last_item($arr) {
|
||||
|
||||
$update = (!$arr['private'] AND (($arr["author-link"] === $arr["owner-link"]) OR ($arr["parent-uri"] === $arr["uri"])));
|
||||
|
||||
// Is it a forum? Then we don't care about the rules from above
|
||||
if (!$update AND ($arr["network"] == NETWORK_DFRN) AND ($arr["parent-uri"] === $arr["uri"])) {
|
||||
$isforum = q("SELECT `forum` FROM `contact` WHERE `id` = %d AND `forum`",
|
||||
intval($arr['contact-id']));
|
||||
if ($isforum) {
|
||||
$update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($update) {
|
||||
q("UPDATE `contact` SET `success_update` = '%s', `last-item` = '%s' WHERE `id` = %d",
|
||||
dbesc($arr['received']),
|
||||
dbesc($arr['received']),
|
||||
intval($arr['contact-id'])
|
||||
);
|
||||
}
|
||||
// Now do the same for the system wide contacts with uid=0
|
||||
if (!$arr['private']) {
|
||||
q("UPDATE `contact` SET `success_update` = '%s', `last-item` = '%s' WHERE `id` = %d",
|
||||
dbesc($arr['received']),
|
||||
dbesc($arr['received']),
|
||||
intval($arr['owner-id'])
|
||||
);
|
||||
|
||||
if ($arr['owner-id'] != $arr['author-id']) {
|
||||
q("UPDATE `contact` SET `success_update` = '%s', `last-item` = '%s' WHERE `id` = %d",
|
||||
dbesc($arr['received']),
|
||||
dbesc($arr['received']),
|
||||
intval($arr['author-id'])
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function item_body_set_hashtags(&$item) {
|
||||
|
||||
$tags = get_tags($item["body"]);
|
||||
|
@ -1469,8 +1597,7 @@ function lose_follower($importer,$contact,$datarray = array(),$item = "") {
|
|||
intval(CONTACT_IS_SHARING),
|
||||
intval($contact['id'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
contact_remove($contact['id']);
|
||||
}
|
||||
}
|
||||
|
@ -1482,8 +1609,7 @@ function lose_sharer($importer,$contact,$datarray = array(),$item = "") {
|
|||
intval(CONTACT_IS_FOLLOWER),
|
||||
intval($contact['id'])
|
||||
);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
contact_remove($contact['id']);
|
||||
}
|
||||
}
|
||||
|
@ -1585,8 +1711,7 @@ function fix_private_photos($s, $uid, $item = null, $cid = 0) {
|
|||
if (in_array($cid, $recips)) {
|
||||
$replace = true;
|
||||
}
|
||||
}
|
||||
elseif($item) {
|
||||
} elseif ($item) {
|
||||
if (compare_permissions($item,$r[0]))
|
||||
$replace = true;
|
||||
}
|
||||
|
@ -1810,21 +1935,21 @@ function drop_item($id,$interactive = true) {
|
|||
|
||||
$owner = $item['uid'];
|
||||
|
||||
$cid = 0;
|
||||
$contact_id = 0;
|
||||
|
||||
// check if logged in user is either the author or owner of this item
|
||||
|
||||
if (is_array($_SESSION['remote'])) {
|
||||
foreach($_SESSION['remote'] as $visitor) {
|
||||
if ($visitor['uid'] == $item['uid'] && $visitor['cid'] == $item['contact-id']) {
|
||||
$cid = $visitor['cid'];
|
||||
$contact_id = $visitor['cid'];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if((local_user() == $item['uid']) || ($cid) || (! $interactive)) {
|
||||
if ((local_user() == $item['uid']) || ($contact_id) || (! $interactive)) {
|
||||
|
||||
// Check if we should do HTML-based delete confirmation
|
||||
if ($_REQUEST['confirm']) {
|
||||
|
@ -1974,8 +2099,7 @@ function drop_item($id,$interactive = true) {
|
|||
create_files_from_itemuri($item['parent-uri'], $item['uid']);
|
||||
delete_thread_uri($item['parent-uri'], $item['uid']);
|
||||
// ignore the result
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// ensure that last-child is set in case the comment that had it just got wiped.
|
||||
q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ",
|
||||
dbesc(datetime_convert()),
|
||||
|
@ -2004,8 +2128,7 @@ function drop_item($id,$interactive = true) {
|
|||
return $owner;
|
||||
goaway($a->get_baseurl() . '/' . $_SESSION['return_url']);
|
||||
//NOTREACHED
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (! $interactive)
|
||||
return 0;
|
||||
notice( t('Permission denied.') . EOL);
|
||||
|
|
192
include/nav.php
192
include/nav.php
|
@ -43,181 +43,170 @@ function nav(&$a) {
|
|||
call_hooks('page_header', $a->page['nav']);
|
||||
}
|
||||
|
||||
|
||||
function nav_info(&$a) {
|
||||
|
||||
/**
|
||||
* @brief Prepares a list of navigation links
|
||||
*
|
||||
* @param App $a
|
||||
* @return array Navigation links
|
||||
* string 'sitelocation' => The webbie (username@site.com)
|
||||
* array 'nav' => Array of links used in the nav menu
|
||||
* string 'banner' => Formatted html link with banner image
|
||||
* array 'userinfo' => Array of user information (name, icon)
|
||||
*/
|
||||
function nav_info(App $a)
|
||||
{
|
||||
$ssl_state = ((local_user()) ? true : false);
|
||||
|
||||
/*
|
||||
*
|
||||
* Our network is distributed, and as you visit friends some of the
|
||||
* sites look exactly the same - it isn't always easy to know where you are.
|
||||
* Display the current site location as a navigation aid.
|
||||
*
|
||||
*/
|
||||
|
||||
$myident = ((is_array($a->user) && isset($a->user['nickname'])) ? $a->user['nickname'] . '@' : '');
|
||||
|
||||
$sitelocation = $myident . substr($a->get_baseurl($ssl_state), strpos($a->get_baseurl($ssl_state), '//') + 2 );
|
||||
|
||||
|
||||
// nav links: array of array('href', 'text', 'extra css classes', 'title')
|
||||
$nav = Array();
|
||||
|
||||
/*
|
||||
* Display login or logout
|
||||
*/
|
||||
$nav = array();
|
||||
|
||||
// Display login or logout
|
||||
$nav['usermenu'] = array();
|
||||
$userinfo = null;
|
||||
|
||||
if (local_user()) {
|
||||
$nav['logout'] = Array('logout',t('Logout'), "", t('End this session'));
|
||||
$nav['logout'] = array('logout', t('Logout'), '', t('End this session'));
|
||||
|
||||
// user menu
|
||||
$nav['usermenu'][] = Array('profile/' . $a->user['nickname'], t('Status'), "", t('Your posts and conversations'));
|
||||
$nav['usermenu'][] = Array('profile/' . $a->user['nickname']. '?tab=profile', t('Profile'), "", t('Your profile page'));
|
||||
$nav['usermenu'][] = Array('photos/' . $a->user['nickname'], t('Photos'), "", t('Your photos'));
|
||||
$nav['usermenu'][] = Array('videos/' . $a->user['nickname'], t('Videos'), "", t('Your videos'));
|
||||
$nav['usermenu'][] = Array('events/', t('Events'), "", t('Your events'));
|
||||
$nav['usermenu'][] = Array('notes/', t('Personal notes'), "", t('Your personal notes'));
|
||||
$nav['usermenu'][] = array('profile/' . $a->user['nickname'], t('Status'), '', t('Your posts and conversations'));
|
||||
$nav['usermenu'][] = array('profile/' . $a->user['nickname'] . '?tab=profile', t('Profile'), '', t('Your profile page'));
|
||||
$nav['usermenu'][] = array('photos/' . $a->user['nickname'], t('Photos'), '', t('Your photos'));
|
||||
$nav['usermenu'][] = array('videos/' . $a->user['nickname'], t('Videos'), '', t('Your videos'));
|
||||
$nav['usermenu'][] = array('events/', t('Events'), '', t('Your events'));
|
||||
$nav['usermenu'][] = array('notes/', t('Personal notes'), '', t('Your personal notes'));
|
||||
|
||||
// user info
|
||||
$r = q("SELECT micro FROM contact WHERE uid=%d AND self=1", intval($a->user['uid']));
|
||||
$r = q("SELECT `micro` FROM `contact` WHERE `uid` = %d AND `self` = 1", intval($a->user['uid']));
|
||||
$userinfo = array(
|
||||
'icon' => (count($r) ? $a->remove_baseurl($r[0]['micro']) : "images/person-48.jpg"),
|
||||
'icon' => (count($r) ? $a->remove_baseurl($r[0]['micro']) : 'images/person-48.jpg'),
|
||||
'name' => $a->user['username'],
|
||||
);
|
||||
|
||||
}
|
||||
else {
|
||||
$nav['login'] = Array('login',t('Login'), ($a->module == 'login'?'selected':''), t('Sign in'));
|
||||
} else {
|
||||
$nav['login'] = array('login', t('Login'), ($a->module == 'login' ? 'selected' : ''), t('Sign in'));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* "Home" should also take you home from an authenticated remote profile connection
|
||||
*/
|
||||
|
||||
// "Home" should also take you home from an authenticated remote profile connection
|
||||
$homelink = get_my_url();
|
||||
if(! $homelink)
|
||||
if (! $homelink) {
|
||||
$homelink = ((x($_SESSION,'visitor_home')) ? $_SESSION['visitor_home'] : '');
|
||||
}
|
||||
|
||||
if(($a->module != 'home') && (! (local_user())))
|
||||
$nav['home'] = array($homelink, t('Home'), "", t('Home Page'));
|
||||
if (($a->module != 'home') && (! (local_user()))) {
|
||||
$nav['home'] = array($homelink, t('Home'), '', t('Home Page'));
|
||||
}
|
||||
|
||||
|
||||
if(($a->config['register_policy'] == REGISTER_OPEN) && (! local_user()) && (! remote_user()))
|
||||
$nav['register'] = array('register',t('Register'), "", t('Create an account'));
|
||||
if (($a->config['register_policy'] == REGISTER_OPEN) && (! local_user()) && (! remote_user())) {
|
||||
$nav['register'] = array('register', t('Register'), '', t('Create an account'));
|
||||
}
|
||||
|
||||
$help_url = 'help';
|
||||
|
||||
if(! get_config('system','hide_help'))
|
||||
$nav['help'] = array($help_url, t('Help'), "", t('Help and documentation'));
|
||||
if (! get_config('system', 'hide_help')) {
|
||||
$nav['help'] = array($help_url, t('Help'), '', t('Help and documentation'));
|
||||
}
|
||||
|
||||
if(count($a->apps)>0)
|
||||
$nav['apps'] = array('apps', t('Apps'), "", t('Addon applications, utilities, games'));
|
||||
if (count($a->apps) > 0) {
|
||||
$nav['apps'] = array('apps', t('Apps'), '', t('Addon applications, utilities, games'));
|
||||
}
|
||||
|
||||
if (local_user() OR !get_config('system', 'local_search')) {
|
||||
$nav['search'] = array('search', t('Search'), "", t('Search site content'));
|
||||
$nav['search'] = array('search', t('Search'), '', t('Search site content'));
|
||||
|
||||
$nav['searchoption'] = array(
|
||||
t("Full Text"),
|
||||
t("Tags"),
|
||||
t("Contacts"));
|
||||
t('Full Text'),
|
||||
t('Tags'),
|
||||
t('Contacts'));
|
||||
|
||||
if (get_config('system','poco_local_search'))
|
||||
$nav['searchoption'][] = t("Forums");
|
||||
if (get_config('system', 'poco_local_search')) {
|
||||
$nav['searchoption'][] = t('Forums');
|
||||
}
|
||||
}
|
||||
|
||||
$gdirpath = 'directory';
|
||||
|
||||
if (strlen(get_config('system', 'singleuser'))) {
|
||||
$gdir = get_config('system', 'directory');
|
||||
if(strlen($gdir))
|
||||
$gdirpath = $gdir;
|
||||
if(strlen($gdir)) {
|
||||
$gdirpath = zrl($gdir, true);
|
||||
}
|
||||
} elseif (get_config('system', 'community_page_style') == CP_USERS_ON_SERVER) {
|
||||
$nav['community'] = array('community', t('Community'), '', t('Conversations on this site'));
|
||||
} elseif (get_config('system', 'community_page_style') == CP_GLOBAL_COMMUNITY) {
|
||||
$nav['community'] = array('community', t('Community'), '', t('Conversations on the network'));
|
||||
}
|
||||
elseif(get_config('system','community_page_style') == CP_USERS_ON_SERVER)
|
||||
$nav['community'] = array('community', t('Community'), "", t('Conversations on this site'));
|
||||
elseif(get_config('system','community_page_style') == CP_GLOBAL_COMMUNITY)
|
||||
$nav['community'] = array('community', t('Community'), "", t('Conversations on the network'));
|
||||
|
||||
if(local_user())
|
||||
$nav['events'] = Array('events', t('Events'), "", t('Events and Calendar'));
|
||||
|
||||
$nav['directory'] = array($gdirpath, t('Directory'), "", t('People directory'));
|
||||
|
||||
$nav['about'] = Array('friendica', t('Information'), "", t('Information about this friendica instance'));
|
||||
|
||||
/*
|
||||
*
|
||||
* The following nav links are only show to logged in users
|
||||
*
|
||||
*/
|
||||
|
||||
if (local_user()) {
|
||||
$nav['events'] = array('events', t('Events'), '', t('Events and Calendar'));
|
||||
}
|
||||
|
||||
$nav['network'] = array('network', t('Network'), "", t('Conversations from your friends'));
|
||||
$nav['net_reset'] = array('network/0?f=&order=comment&nets=all', t('Network Reset'), "", t('Load Network page with no filters'));
|
||||
$nav['directory'] = array($gdirpath, t('Directory'), '', t('People directory'));
|
||||
|
||||
$nav['home'] = array('profile/' . $a->user['nickname'], t('Home'), "", t('Your posts and conversations'));
|
||||
$nav['about'] = array('friendica', t('Information'), '', t('Information about this friendica instance'));
|
||||
|
||||
// The following nav links are only show to logged in users
|
||||
if (local_user()) {
|
||||
$nav['network'] = array('network', t('Network'), '', t('Conversations from your friends'));
|
||||
$nav['net_reset'] = array('network/0?f=&order=comment&nets=all', t('Network Reset'), '', t('Load Network page with no filters'));
|
||||
|
||||
$nav['home'] = array('profile/' . $a->user['nickname'], t('Home'), '', t('Your posts and conversations'));
|
||||
|
||||
if (in_array($_SESSION['page_flags'], array(PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE, PAGE_PRVGROUP))) {
|
||||
/* only show friend requests for normal pages. Other page types have automatic friendship. */
|
||||
if(in_array($_SESSION['page_flags'], array(PAGE_NORMAL, PAGE_SOAPBOX, PAGE_PRVGROUP)))
|
||||
$nav['introductions'] = array('notifications/intros', t('Introductions'), "", t('Friend Requests'));
|
||||
|
||||
// only show friend requests for normal pages. Other page types have automatic friendship.
|
||||
if (in_array($_SESSION['page_flags'], array(PAGE_NORMAL, PAGE_SOAPBOX, PAGE_PRVGROUP))) {
|
||||
$nav['introductions'] = array('notifications/intros', t('Introductions'), '', t('Friend Requests'));
|
||||
}
|
||||
if (in_array($_SESSION['page_flags'], array(PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE))) {
|
||||
$nav['notifications'] = array('notifications', t('Notifications'), "", t('Notifications'));
|
||||
$nav['notifications']['all']=array('notifications/system', t('See all notifications'), "", "");
|
||||
$nav['notifications'] = array('notifications', t('Notifications'), '', t('Notifications'));
|
||||
$nav['notifications']['all'] = array('notifications/system', t('See all notifications'), '', '');
|
||||
$nav['notifications']['mark'] = array('', t('Mark as seen'), '', t('Mark all system notifications seen'));
|
||||
}
|
||||
}
|
||||
|
||||
$nav['messages'] = array('message', t('Messages'), "", t('Private mail'));
|
||||
$nav['messages']['inbox'] = array('message', t('Inbox'), "", t('Inbox'));
|
||||
$nav['messages']['outbox']= array('message/sent', t('Outbox'), "", t('Outbox'));
|
||||
$nav['messages']['new'] = array('message/new', t('New Message'), "", t('New Message'));
|
||||
$nav['messages'] = array('message', t('Messages'), '', t('Private mail'));
|
||||
$nav['messages']['inbox'] = array('message', t('Inbox'), '', t('Inbox'));
|
||||
$nav['messages']['outbox'] = array('message/sent', t('Outbox'), '', t('Outbox'));
|
||||
$nav['messages']['new'] = array('message/new', t('New Message'), '', t('New Message'));
|
||||
|
||||
if (is_array($a->identities) && count($a->identities) > 1) {
|
||||
$nav['manage'] = array('manage', t('Manage'), "", t('Manage other pages'));
|
||||
$nav['manage'] = array('manage', t('Manage'), '', t('Manage other pages'));
|
||||
}
|
||||
|
||||
$nav['delegations'] = Array('delegate', t('Delegations'), "", t('Delegate Page Management'));
|
||||
$nav['delegations'] = array('delegate', t('Delegations'), '', t('Delegate Page Management'));
|
||||
|
||||
$nav['settings'] = array('settings', t('Settings'),"", t('Account settings'));
|
||||
$nav['settings'] = array('settings', t('Settings'), '', t('Account settings'));
|
||||
|
||||
if(feature_enabled(local_user(),'multi_profiles'))
|
||||
$nav['profiles'] = array('profiles', t('Profiles'),"", t('Manage/Edit Profiles'));
|
||||
|
||||
$nav['contacts'] = array('contacts', t('Contacts'),"", t('Manage/edit friends and contacts'));
|
||||
if (feature_enabled(local_user(), 'multi_profiles')) {
|
||||
$nav['profiles'] = array('profiles', t('Profiles'), '', t('Manage/Edit Profiles'));
|
||||
}
|
||||
|
||||
/*
|
||||
* Admin page
|
||||
*/
|
||||
$nav['contacts'] = array('contacts', t('Contacts'), '', t('Manage/edit friends and contacts'));
|
||||
}
|
||||
|
||||
// Show the link to the admin configuration page if user is admin
|
||||
if (is_site_admin()) {
|
||||
$nav['admin'] = array('admin/', t('Admin'), "", t('Site setup and configuration'));
|
||||
$nav['admin'] = array('admin/', t('Admin'), '', t('Site setup and configuration'));
|
||||
}
|
||||
|
||||
$nav['navigation'] = array('navigation/', t('Navigation'), '', t('Site map'));
|
||||
|
||||
$nav['navigation'] = array('navigation/', t('Navigation'), "", t('Site map'));
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* Provide a banner/logo/whatever
|
||||
*
|
||||
*/
|
||||
|
||||
// Provide a banner/logo/whatever
|
||||
$banner = get_config('system', 'banner');
|
||||
|
||||
if($banner === false)
|
||||
$banner .= '<a href="http://friendica.com"><img id="logo-img" src="images/friendica-32.png" alt="logo" /></a><span id="logo-text"><a href="http://friendica.com">Friendica</a></span>';
|
||||
if ($banner === false) {
|
||||
$banner = '<a href="http://friendica.com"><img id="logo-img" src="images/friendica-32.png" alt="logo" /></a><span id="logo-text"><a href="http://friendica.com">Friendica</a></span>';
|
||||
}
|
||||
|
||||
call_hooks('nav_info', $nav);
|
||||
|
||||
|
||||
return array(
|
||||
'sitelocation' => $sitelocation,
|
||||
'nav' => $nav,
|
||||
|
@ -226,7 +215,6 @@ function nav_info(&$a) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set a menu item in navbar as selected
|
||||
*
|
||||
|
|
|
@ -789,12 +789,12 @@ function short_link($url) {
|
|||
$yourls->set('password', $yourls_password);
|
||||
$yourls->set('ssl', $yourls_ssl);
|
||||
$yourls->set('yourls-url', $yourls_url);
|
||||
$slinky->set_cascade( array($yourls, new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL()));
|
||||
$slinky->set_cascade(array($yourls, new Slinky_Ur1ca(), new Slinky_TinyURL()));
|
||||
} else {
|
||||
// setup a cascade of shortening services
|
||||
// try to get a short link from these services
|
||||
// in the order ur1.ca, trim, id.gd, tinyurl
|
||||
$slinky->set_cascade(array(new Slinky_UR1ca(), new Slinky_Trim(), new Slinky_IsGd(), new Slinky_TinyURL()));
|
||||
// in the order ur1.ca, tinyurl
|
||||
$slinky->set_cascade(array(new Slinky_Ur1ca(), new Slinky_TinyURL()));
|
||||
}
|
||||
return $slinky->short();
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ function notifier_run(&$argv, &$argc){
|
|||
} elseif($cmd === 'removeme') {
|
||||
$r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`,
|
||||
`user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`,
|
||||
`user`.`page-flags`, `user`.`prvnets`, `user`.`guid`
|
||||
`user`.`page-flags`, `user`.`prvnets`, `user`.`account-type`, `user`.`guid`
|
||||
FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
|
||||
WHERE `contact`.`uid` = %d AND `contact`.`self` LIMIT 1",
|
||||
intval($item_id));
|
||||
|
@ -204,7 +204,7 @@ function notifier_run(&$argv, &$argc){
|
|||
|
||||
$r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`,
|
||||
`user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`,
|
||||
`user`.`page-flags`, `user`.`prvnets`
|
||||
`user`.`page-flags`, `user`.`prvnets`, `user`.`account-type`
|
||||
FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
|
||||
WHERE `contact`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1",
|
||||
intval($uid)
|
||||
|
@ -599,10 +599,10 @@ function notifier_run(&$argv, &$argc){
|
|||
|
||||
foreach($r as $rr) {
|
||||
if((! $mail) && (! $fsuggest) && (! $followup)) {
|
||||
q("insert into deliverq ( `cmd`,`item`,`contact` ) values ('%s', %d, %d )",
|
||||
dbesc($cmd),
|
||||
intval($item_id),
|
||||
intval($rr['id'])
|
||||
q("INSERT INTO `deliverq` (`cmd`,`item`,`contact`) VALUES ('%s', %d, %d)
|
||||
ON DUPLICATE KEY UPDATE `cmd` = '%s', `item` = %d, `contact` = %d",
|
||||
dbesc($cmd), intval($item_id), intval($rr['id']),
|
||||
dbesc($cmd), intval($item_id), intval($rr['id'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,29 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file include/oembed.php
|
||||
*/
|
||||
|
||||
use \Friendica\ParseUrl;
|
||||
use \Friendica\Core\Config;
|
||||
|
||||
function oembed_replacecb($matches){
|
||||
$embedurl=$matches[1];
|
||||
$j = oembed_fetch_url($embedurl);
|
||||
$s = oembed_format_object($j);
|
||||
|
||||
return $s;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get data from an URL to embed its content.
|
||||
*
|
||||
* @param string $embedurl The URL from which the data should be fetched.
|
||||
* @param bool $no_rich_type If set to true rich type content won't be fetched.
|
||||
*
|
||||
* @return bool|object Returns object with embed content or false if no embedable
|
||||
* content exists
|
||||
*/
|
||||
function oembed_fetch_url($embedurl, $no_rich_type = false){
|
||||
$embedurl = trim($embedurl, "'");
|
||||
$embedurl = trim($embedurl, '"');
|
||||
|
@ -16,11 +33,11 @@ function oembed_fetch_url($embedurl, $no_rich_type = false){
|
|||
$r = q("SELECT * FROM `oembed` WHERE `url` = '%s'",
|
||||
dbesc(normalise_link($embedurl)));
|
||||
|
||||
if ($r)
|
||||
if (dbm::is_result($r)) {
|
||||
$txt = $r[0]["content"];
|
||||
else
|
||||
} else {
|
||||
$txt = Cache::get($a->videowidth . $embedurl);
|
||||
|
||||
}
|
||||
// These media files should now be caught in bbcode.php
|
||||
// left here as a fallback in case this is called from another source
|
||||
|
||||
|
@ -34,7 +51,7 @@ function oembed_fetch_url($embedurl, $no_rich_type = false){
|
|||
if (!in_array($ext, $noexts)){
|
||||
// try oembed autodiscovery
|
||||
$redirects = 0;
|
||||
$html_text = fetch_url($embedurl, false, $redirects, 15, "text/*"); /**/
|
||||
$html_text = fetch_url($embedurl, false, $redirects, 15, "text/*");
|
||||
if ($html_text) {
|
||||
$dom = @DOMDocument::loadHTML($html_text);
|
||||
if ($dom) {
|
||||
|
@ -58,7 +75,7 @@ function oembed_fetch_url($embedurl, $no_rich_type = false){
|
|||
}
|
||||
|
||||
if ($txt==false || $txt=="") {
|
||||
$embedly = get_config("system", "embedly");
|
||||
$embedly = Config::get("system", "embedly");
|
||||
if ($embedly != "") {
|
||||
// try embedly service
|
||||
$ourl = "https://api.embed.ly/1/oembed?key=".$embedly."&url=".urlencode($embedurl);
|
||||
|
@ -70,16 +87,17 @@ function oembed_fetch_url($embedurl, $no_rich_type = false){
|
|||
|
||||
$txt=trim($txt);
|
||||
|
||||
if ($txt[0]!="{")
|
||||
if ($txt[0]!="{") {
|
||||
$txt='{"type":"error"}';
|
||||
else { //save in cache
|
||||
} else { //save in cache
|
||||
$j = json_decode($txt);
|
||||
if ($j->type != "error")
|
||||
if ($j->type != "error") {
|
||||
q("INSERT INTO `oembed` (`url`, `content`, `created`) VALUES ('%s', '%s', '%s')
|
||||
ON DUPLICATE KEY UPDATE `content` = '%s', `created` = '%s'",
|
||||
dbesc(normalise_link($embedurl)),
|
||||
dbesc($txt), dbesc(datetime_convert()),
|
||||
dbesc($txt), dbesc(datetime_convert()));
|
||||
}
|
||||
|
||||
Cache::set($a->videowidth.$embedurl, $txt, CACHE_DAY);
|
||||
}
|
||||
|
@ -87,20 +105,21 @@ function oembed_fetch_url($embedurl, $no_rich_type = false){
|
|||
|
||||
$j = json_decode($txt);
|
||||
|
||||
if (!is_object($j))
|
||||
if (!is_object($j)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Always embed the SSL version
|
||||
if (isset($j->html))
|
||||
if (isset($j->html)) {
|
||||
$j->html = str_replace(array("http://www.youtube.com/", "http://player.vimeo.com/"),
|
||||
array("https://www.youtube.com/", "https://player.vimeo.com/"), $j->html);
|
||||
}
|
||||
|
||||
$j->embedurl = $embedurl;
|
||||
|
||||
// If fetching information doesn't work, then improve via internal functions
|
||||
if (($j->type == "error") OR ($no_rich_type AND ($j->type == "rich"))) {
|
||||
require_once("mod/parse_url.php");
|
||||
$data = parseurl_getsiteinfo_cached($embedurl, true, false);
|
||||
$data = ParseUrl::getSiteinfoCached($embedurl, true, false);
|
||||
$j->type = $data["type"];
|
||||
|
||||
if ($j->type == "photo") {
|
||||
|
@ -109,11 +128,13 @@ function oembed_fetch_url($embedurl, $no_rich_type = false){
|
|||
//$j->height = $data["images"][0]["height"];
|
||||
}
|
||||
|
||||
if (isset($data["title"]))
|
||||
if (isset($data["title"])) {
|
||||
$j->title = $data["title"];
|
||||
}
|
||||
|
||||
if (isset($data["text"]))
|
||||
if (isset($data["text"])) {
|
||||
$j->description = $data["text"];
|
||||
}
|
||||
|
||||
if (is_array($data["images"])) {
|
||||
$j->thumbnail_url = $data["images"][0]["src"];
|
||||
|
@ -130,12 +151,11 @@ function oembed_fetch_url($embedurl, $no_rich_type = false){
|
|||
function oembed_format_object($j){
|
||||
require_once("mod/proxy.php");
|
||||
|
||||
$a = get_app();
|
||||
$embedurl = $j->embedurl;
|
||||
$jhtml = oembed_iframe($j->embedurl,(isset($j->width) ? $j->width : null), (isset($j->height) ? $j->height : null) );
|
||||
$ret="<span class='oembed ".$j->type."'>";
|
||||
switch ($j->type) {
|
||||
case "video": {
|
||||
case "video":
|
||||
if (isset($j->thumbnail_url)) {
|
||||
$tw = (isset($j->thumbnail_width) && intval($j->thumbnail_width)) ? $j->thumbnail_width:200;
|
||||
$th = (isset($j->thumbnail_height) && intval($j->thumbnail_height)) ? $j->thumbnail_height:180;
|
||||
|
@ -145,7 +165,7 @@ function oembed_format_object($j){
|
|||
$th=120; $tw = $th*$tr;
|
||||
$tpl=get_markup_template('oembed_video.tpl');
|
||||
$ret.=replace_macros($tpl, array(
|
||||
'$baseurl' => $a->get_baseurl(),
|
||||
'$baseurl' => App::get_baseurl(),
|
||||
'$embedurl'=>$embedurl,
|
||||
'$escapedhtml'=>base64_encode($jhtml),
|
||||
'$tw'=>$tw,
|
||||
|
@ -157,43 +177,49 @@ function oembed_format_object($j){
|
|||
$ret=$jhtml;
|
||||
}
|
||||
//$ret.="<br>";
|
||||
}; break;
|
||||
case "photo": {
|
||||
break;
|
||||
case "photo":
|
||||
$ret.= "<img width='".$j->width."' src='".proxy_url($j->url)."'>";
|
||||
}; break;
|
||||
case "link": {
|
||||
}; break;
|
||||
case "rich": {
|
||||
break;
|
||||
case "link":
|
||||
break;
|
||||
case "rich":
|
||||
// not so safe..
|
||||
if (!get_config("system","no_oembed_rich_content"))
|
||||
if (!Config::get("system","no_oembed_rich_content")) {
|
||||
$ret.= proxy_parse_html($jhtml);
|
||||
}; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// add link to source if not present in "rich" type
|
||||
if ($j->type!='rich' || !strpos($j->html,$embedurl) ){
|
||||
$ret .= "<h4>";
|
||||
if (isset($j->title)) {
|
||||
if (isset($j->provider_name))
|
||||
if (isset($j->provider_name)) {
|
||||
$ret .= $j->provider_name.": ";
|
||||
}
|
||||
|
||||
$embedlink = (isset($j->title))?$j->title:$embedurl;
|
||||
$ret .= "<a href='$embedurl' rel='oembed'>$embedlink</a>";
|
||||
if (isset($j->author_name))
|
||||
if (isset($j->author_name)) {
|
||||
$ret.=" (".$j->author_name.")";
|
||||
}
|
||||
} elseif (isset($j->provider_name) OR isset($j->author_name)) {
|
||||
$embedlink = "";
|
||||
if (isset($j->provider_name))
|
||||
if (isset($j->provider_name)) {
|
||||
$embedlink .= $j->provider_name;
|
||||
}
|
||||
|
||||
if (isset($j->author_name)) {
|
||||
if ($embedlink != "")
|
||||
if ($embedlink != "") {
|
||||
$embedlink .= ": ";
|
||||
}
|
||||
|
||||
$embedlink .= $j->author_name;
|
||||
}
|
||||
if (trim($embedlink) == "")
|
||||
if (trim($embedlink) == "") {
|
||||
$embedlink = $embedurl;
|
||||
}
|
||||
|
||||
$ret .= "<a href='$embedurl' rel='oembed'>$embedlink</a>";
|
||||
}
|
||||
|
@ -209,31 +235,39 @@ function oembed_format_object($j){
|
|||
return mb_convert_encoding($ret, 'HTML-ENTITIES', mb_detect_encoding($ret));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates the iframe HTML for an oembed attachment.
|
||||
*
|
||||
* Width and height are given by the remote, and are regularly too small for
|
||||
* the generated iframe.
|
||||
*
|
||||
* The width is entirely discarded for the actual width of the post, while fixed
|
||||
* height is used as a starting point before the inevitable resizing.
|
||||
*
|
||||
* Since the iframe is automatically resized on load, there are no need for ugly
|
||||
* and impractical scrollbars.
|
||||
*
|
||||
* @param string $src Original remote URL to embed
|
||||
* @param string $width
|
||||
* @param string $height
|
||||
* @return string formatted HTML
|
||||
*
|
||||
* @see oembed_format_object()
|
||||
*/
|
||||
function oembed_iframe($src, $width, $height) {
|
||||
|
||||
if(! $width || strstr($width,'%'))
|
||||
$width = '640';
|
||||
if (!$height || strstr($height,'%')) {
|
||||
$height = '300';
|
||||
$resize = 'onload="resizeIframe(this);"';
|
||||
} else
|
||||
$resize = '';
|
||||
|
||||
// try and leave some room for the description line.
|
||||
$height = intval($height) + 80;
|
||||
$width = intval($width) + 40;
|
||||
|
||||
$a = get_app();
|
||||
|
||||
$s = $a->get_baseurl()."/oembed/".base64url_encode($src);
|
||||
return '<iframe '.$resize.' class="embed_rich" height="'.$height.'" width="'.$width.'" src="'.$s.'" frameborder="no">'.t('Embedded content').'</iframe>';
|
||||
$height = '200';
|
||||
}
|
||||
$width = '100%';
|
||||
|
||||
$s = App::get_baseurl() . '/oembed/'.base64url_encode($src);
|
||||
return '<iframe onload="resizeIframe(this);" class="embed_rich" height="' . $height . '" width="' . $width . '" src="' . $s . '" allowfullscreen scrolling="no" frameborder="no">' . t('Embedded content') . '</iframe>';
|
||||
}
|
||||
|
||||
|
||||
|
||||
function oembed_bbcode2html($text){
|
||||
$stopoembed = get_config("system","no_oembed");
|
||||
$stopoembed = Config::get("system","no_oembed");
|
||||
if ($stopoembed == true){
|
||||
return preg_replace("/\[embed\](.+?)\[\/embed\]/is", "<!-- oembed $1 --><i>". t('Embedding disabled') ." : $1</i><!-- /oembed $1 -->" ,$text);
|
||||
}
|
||||
|
@ -268,8 +302,9 @@ function oembed_html2bbcode($text) {
|
|||
|
||||
// If it doesn't parse at all, just return the text.
|
||||
$dom = @DOMDocument::loadHTML($html_text);
|
||||
if(! $dom)
|
||||
if (! $dom) {
|
||||
return $text;
|
||||
}
|
||||
$xpath = new DOMXPath($dom);
|
||||
$attr = "oembed";
|
||||
|
||||
|
|
|
@ -443,7 +443,7 @@ function onepoll_run(&$argv, &$argc){
|
|||
$refs_arr[$x] = "'" . msgid2iri(str_replace(array('<','>',' '),array('','',''),dbesc($refs_arr[$x]))) . "'";
|
||||
}
|
||||
$qstr = implode(',',$refs_arr);
|
||||
$r = q("SELECT `uri` , `parent-uri` FROM `item` WHERE `uri` IN ( $qstr ) AND `uid` = %d LIMIT 1",
|
||||
$r = q("SELECT `uri` , `parent-uri` FROM `item` USE INDEX (`uid_uri`) WHERE `uri` IN ($qstr) AND `uid` = %d LIMIT 1",
|
||||
intval($importer_uid)
|
||||
);
|
||||
if(count($r))
|
||||
|
@ -475,9 +475,10 @@ function onepoll_run(&$argv, &$argc){
|
|||
|
||||
// If it seems to be a reply but a header couldn't be found take the last message with matching subject
|
||||
if(!x($datarray,'parent-uri') and $reply) {
|
||||
$r = q("SELECT `uri` , `parent-uri` FROM `item` WHERE `title` = \"%s\" AND `uid` = %d ORDER BY `created` DESC LIMIT 1",
|
||||
$r = q("SELECT `uri` , `parent-uri` FROM `item` WHERE `title` = \"%s\" AND `uid` = %d AND `network` = '%s' ORDER BY `created` DESC LIMIT 1",
|
||||
dbesc(protect_sprintf($datarray['title'])),
|
||||
intval($importer_uid));
|
||||
intval($importer_uid),
|
||||
dbesc(NETWORK_MAIL));
|
||||
if(count($r))
|
||||
$datarray['parent-uri'] = $r[0]['parent-uri'];
|
||||
}
|
||||
|
|
|
@ -806,11 +806,20 @@ class ostatus {
|
|||
}
|
||||
|
||||
// Get the parent
|
||||
$parents = q("SELECT `item`.`id`, `item`.`parent`, `item`.`uri`, `item`.`contact-id`, `item`.`type`,
|
||||
`item`.`verb`, `item`.`visible` FROM `term`
|
||||
STRAIGHT_JOIN `item` AS `thritem` ON `thritem`.`parent` = `term`.`oid`
|
||||
STRAIGHT_JOIN `item` ON `item`.`parent` = `thritem`.`parent`
|
||||
WHERE `term`.`uid` = %d AND `term`.`otype` = %d AND `term`.`type` = %d AND `term`.`url` = '%s'",
|
||||
intval($uid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), dbesc($conversation_url));
|
||||
|
||||
/* 2016-10-23: The old query will be kept until we are sure that the query above is a good and fast replacement
|
||||
|
||||
$parents = q("SELECT `id`, `parent`, `uri`, `contact-id`, `type`, `verb`, `visible` FROM `item` WHERE `id` IN
|
||||
(SELECT `parent` FROM `item` WHERE `id` IN
|
||||
(SELECT `oid` FROM `term` WHERE `uid` = %d AND `otype` = %d AND `type` = %d AND `url` = '%s'))",
|
||||
intval($uid), intval(TERM_OBJ_POST), intval(TERM_CONVERSATION), dbesc($conversation_url));
|
||||
|
||||
*/
|
||||
if ($parents)
|
||||
$parent = $parents[0];
|
||||
elseif (count($item) > 0) {
|
||||
|
@ -1961,9 +1970,23 @@ class ostatus {
|
|||
$last_update = 'now -30 days';
|
||||
|
||||
$check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s');
|
||||
$authorid = get_contact($owner["url"], 0);
|
||||
|
||||
$items = q("SELECT STRAIGHT_JOIN `item`.*, `item`.`id` AS `item_id` FROM `item`
|
||||
INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent`
|
||||
$items = q("SELECT `item`.*, `item`.`id` AS `item_id` FROM `item` USE INDEX (`uid_contactid_created`)
|
||||
STRAIGHT_JOIN `thread` ON `thread`.`iid` = `item`.`parent`
|
||||
WHERE `item`.`uid` = %d AND `item`.`contact-id` = %d AND
|
||||
`item`.`author-id` = %d AND `item`.`created` > '%s' AND
|
||||
NOT `item`.`deleted` AND NOT `item`.`private` AND
|
||||
`thread`.`network` IN ('%s', '%s')
|
||||
ORDER BY `item`.`created` DESC LIMIT 300",
|
||||
intval($owner["uid"]), intval($owner["id"]),
|
||||
intval($authorid), dbesc($check_date),
|
||||
dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DFRN));
|
||||
|
||||
/* 2016-10-23: The old query will be kept until we are sure that the query above is a good and fast replacement
|
||||
|
||||
$items = q("SELECT `item`.*, `item`.`id` AS `item_id` FROM `item`
|
||||
STRAIGHT_JOIN `thread` ON `thread`.`iid` = `item`.`parent`
|
||||
LEFT JOIN `item` AS `thritem` ON `thritem`.`uri`=`item`.`thr-parent` AND `thritem`.`uid`=`item`.`uid`
|
||||
WHERE `item`.`uid` = %d AND `item`.`received` > '%s' AND NOT `item`.`private` AND NOT `item`.`deleted`
|
||||
AND `item`.`allow_cid` = '' AND `item`.`allow_gid` = '' AND `item`.`deny_cid` = '' AND `item`.`deny_gid` = ''
|
||||
|
@ -1981,7 +2004,7 @@ class ostatus {
|
|||
dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"])),
|
||||
dbesc($owner["nurl"]), dbesc(str_replace("http://", "https://", $owner["nurl"]))
|
||||
);
|
||||
|
||||
*/
|
||||
$doc = new DOMDocument('1.0', 'utf-8');
|
||||
$doc->formatOutput = true;
|
||||
|
||||
|
|
|
@ -1,13 +1,7 @@
|
|||
<?php
|
||||
|
||||
require_once("include/dba.php");
|
||||
|
||||
/**
|
||||
* translation support
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief translation support
|
||||
*
|
||||
* Get the language setting directly from system variables, bypassing get_config()
|
||||
* as database may not yet be configured.
|
||||
|
@ -16,8 +10,12 @@ require_once("include/dba.php");
|
|||
*
|
||||
*/
|
||||
|
||||
require_once("include/dba.php");
|
||||
|
||||
if(! function_exists('get_browser_language')) {
|
||||
/**
|
||||
* @brief get the prefered language from the HTTP_ACCEPT_LANGUAGE header
|
||||
*/
|
||||
function get_browser_language() {
|
||||
|
||||
if (x($_SERVER,'HTTP_ACCEPT_LANGUAGE')) {
|
||||
|
@ -25,32 +23,34 @@ function get_browser_language() {
|
|||
preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i',
|
||||
$_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
|
||||
|
||||
$lang_list = [];
|
||||
if (count($lang_parse[1])) {
|
||||
// create a list like "en" => 0.8
|
||||
$langs = array_combine($lang_parse[1], $lang_parse[4]);
|
||||
|
||||
// set default to 1 for any without q factor
|
||||
foreach ($langs as $lang => $val) {
|
||||
if ($val === '') $langs[$lang] = 1;
|
||||
// go through the list of prefered languages and add a generic language
|
||||
// for sub-linguas (e.g. de-ch will add de) if not already in array
|
||||
for ($i=0; $i<count($lang_parse[1]); $i++) {
|
||||
$lang_list[] = strtolower($lang_parse[1][$i]);
|
||||
if ( strlen($lang_parse[1][$i])>3 ) {
|
||||
$dashpos = strpos($lang_parse[1][$i], '-');
|
||||
if (! in_array(substr($lang_parse[1][$i], 0, $dashpos), $lang_list ) ) {
|
||||
$lang_list[] = strtolower(substr($lang_parse[1][$i], 0, $dashpos));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort list based on value
|
||||
arsort($langs, SORT_NUMERIC);
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($langs) && count($langs)) {
|
||||
foreach ($langs as $lang => $v) {
|
||||
if(file_exists("view/$lang") && is_dir("view/$lang")) {
|
||||
// check if we have translations for the preferred languages and pick the 1st that has
|
||||
for ($i=0; $i<count($lang_list); $i++) {
|
||||
$lang = $lang_list[$i];
|
||||
if(file_exists("view/lang/$lang") && is_dir("view/lang/$lang")) {
|
||||
$preferred = $lang;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($preferred))
|
||||
return $preferred;
|
||||
|
||||
// in case none matches, get the system wide configured language, or fall back to English
|
||||
$a = get_app();
|
||||
return ((isset($a->config['system']['language'])) ? $a->config['system']['language'] : 'en');
|
||||
}}
|
||||
|
@ -112,8 +112,8 @@ function load_translation_table($lang) {
|
|||
}
|
||||
}
|
||||
|
||||
if(file_exists("view/$lang/strings.php")) {
|
||||
include("view/$lang/strings.php");
|
||||
if(file_exists("view/lang/$lang/strings.php")) {
|
||||
include("view/lang/$lang/strings.php");
|
||||
}
|
||||
|
||||
}}
|
||||
|
@ -162,25 +162,31 @@ function string_plural_select_default($n) {
|
|||
}}
|
||||
|
||||
|
||||
/**
|
||||
* Return installed languages as associative array
|
||||
* [
|
||||
* lang => lang,
|
||||
* ...
|
||||
* ]
|
||||
*/
|
||||
function get_avaiable_languages() {
|
||||
$lang_choices = array();
|
||||
$langs = glob('view/*/strings.php'); /**/
|
||||
|
||||
if(is_array($langs) && count($langs)) {
|
||||
if(! in_array('view/en/strings.php',$langs))
|
||||
$langs[] = 'view/en/';
|
||||
asort($langs);
|
||||
foreach($langs as $l) {
|
||||
$t = explode("/",$l);
|
||||
$lang_choices[$t[1]] = $t[1];
|
||||
/**
|
||||
* @brief Return installed languages codes as associative array
|
||||
*
|
||||
* Scans the view/lang directory for the existence of "strings.php" files, and
|
||||
* returns an alphabetical list of their folder names (@-char language codes).
|
||||
* Adds the english language if it's missing from the list.
|
||||
*
|
||||
* Ex: array('de' => 'de', 'en' => 'en', 'fr' => 'fr', ...)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function get_available_languages() {
|
||||
$langs = array();
|
||||
$strings_file_paths = glob('view/lang/*/strings.php');
|
||||
|
||||
if (is_array($strings_file_paths) && count($strings_file_paths)) {
|
||||
if (!in_array('view/lang/en/strings.php', $strings_file_paths)) {
|
||||
$strings_file_paths[] = 'view/lang/en/strings.php';
|
||||
}
|
||||
asort($strings_file_paths);
|
||||
foreach($strings_file_paths as $strings_file_path) {
|
||||
$path_array = explode('/', $strings_file_path);
|
||||
$langs[$path_array[2]] = $path_array[2];
|
||||
}
|
||||
}
|
||||
return $lang_choices;
|
||||
return $langs;
|
||||
}
|
||||
|
|
|
@ -25,3 +25,34 @@ function gps2Num($coordPart) {
|
|||
|
||||
return floatval($parts[0]) / floatval($parts[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fetch the photo albums that are available for a viewer
|
||||
*
|
||||
* The query in this function is cost intensive, so it is cached.
|
||||
*
|
||||
* @param int $uid User id of the photos
|
||||
* @param bool $update Update the cache
|
||||
*
|
||||
* @return array Returns array of the photo albums
|
||||
*/
|
||||
function photo_albums($uid, $update = false) {
|
||||
$sql_extra = permissions_sql($uid);
|
||||
|
||||
$key = "photo_albums:".$uid.":".local_user().":".remote_user();
|
||||
$albums = Cache::get($key);
|
||||
if (is_null($albums) OR $update) {
|
||||
/// @todo This query needs to be renewed. It is really slow
|
||||
// At this time we just store the data in the cache
|
||||
$albums = qu("SELECT COUNT(DISTINCT `resource-id`) AS `total`, `album`
|
||||
FROM `photo` USE INDEX (`uid_album_created`)
|
||||
WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' $sql_extra
|
||||
GROUP BY `album` ORDER BY `created` DESC",
|
||||
intval($uid),
|
||||
dbesc('Contact Photos'),
|
||||
dbesc(t('Contact Photos'))
|
||||
);
|
||||
Cache::set($key, $albums, CACHE_DAY);
|
||||
}
|
||||
return $albums;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,15 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file include/plaintext.php
|
||||
*/
|
||||
|
||||
use \Friendica\ParseUrl;
|
||||
|
||||
require_once("include/Photo.php");
|
||||
require_once("include/bbcode.php");
|
||||
require_once("include/html2plain.php");
|
||||
require_once("include/network.php");
|
||||
|
||||
/**
|
||||
* @brief Fetches attachment data that were generated the old way
|
||||
|
@ -181,20 +190,17 @@ function get_attached_data($body) {
|
|||
|
||||
// if nothing is found, it maybe having an image.
|
||||
if (!isset($post["type"])) {
|
||||
require_once("mod/parse_url.php");
|
||||
require_once("include/Photo.php");
|
||||
|
||||
$URLSearchString = "^\[\]";
|
||||
if (preg_match_all("(\[url=([$URLSearchString]*)\]\s*\[img\]([$URLSearchString]*)\[\/img\]\s*\[\/url\])ism", $body, $pictures, PREG_SET_ORDER)) {
|
||||
if (count($pictures) == 1) {
|
||||
// Checking, if the link goes to a picture
|
||||
$data = parseurl_getsiteinfo_cached($pictures[0][1], true);
|
||||
$data = ParseUrl::getSiteinfoCached($pictures[0][1], true);
|
||||
|
||||
// Workaround:
|
||||
// Sometimes photo posts to the own album are not detected at the start.
|
||||
// So we seem to cannot use the cache for these cases. That's strange.
|
||||
if (($data["type"] != "photo") AND strstr($pictures[0][1], "/photos/"))
|
||||
$data = parseurl_getsiteinfo($pictures[0][1], true);
|
||||
$data = ParseUrl::getSiteinfo($pictures[0][1], true);
|
||||
|
||||
if ($data["type"] == "photo") {
|
||||
$post["type"] = "photo";
|
||||
|
@ -246,8 +252,7 @@ function get_attached_data($body) {
|
|||
$post["text"] = trim($body);
|
||||
}
|
||||
} elseif (isset($post["url"]) AND ($post["type"] == "video")) {
|
||||
require_once("mod/parse_url.php");
|
||||
$data = parseurl_getsiteinfo_cached($post["url"], true);
|
||||
$data = ParseUrl::getSiteinfoCached($post["url"], true);
|
||||
|
||||
if (isset($data["images"][0]))
|
||||
$post["image"] = $data["images"][0]["src"];
|
||||
|
@ -288,9 +293,6 @@ function shortenmsg($msg, $limit, $twitter = false) {
|
|||
* @return string The converted message
|
||||
*/
|
||||
function plaintext($a, $b, $limit = 0, $includedlinks = false, $htmlmode = 2, $target_network = "") {
|
||||
require_once("include/bbcode.php");
|
||||
require_once("include/html2plain.php");
|
||||
require_once("include/network.php");
|
||||
|
||||
// Remove the hash tags
|
||||
$URLSearchString = "^\[\]";
|
||||
|
|
|
@ -15,7 +15,7 @@ use \Friendica\Core\PConfig;
|
|||
|
||||
require_once("boot.php");
|
||||
|
||||
function poller_run(&$argv, &$argc){
|
||||
function poller_run($argv, $argc){
|
||||
global $a, $db;
|
||||
|
||||
if(is_null($a)) {
|
||||
|
@ -29,18 +29,27 @@ function poller_run(&$argv, &$argc){
|
|||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
};
|
||||
|
||||
// Quit when in maintenance
|
||||
if (get_config('system', 'maintenance', true))
|
||||
return;
|
||||
|
||||
$a->start_process();
|
||||
|
||||
$mypid = getmypid();
|
||||
|
||||
if ($a->max_processes_reached())
|
||||
if (poller_max_connections_reached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (poller_max_connections_reached())
|
||||
if (App::maxload_reached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (App::maxload_reached())
|
||||
if(($argc <= 1) OR ($argv[1] != "no_cron")) {
|
||||
poller_run_cron();
|
||||
}
|
||||
|
||||
if ($a->max_processes_reached()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Checking the number of workers
|
||||
if (poller_too_much_workers()) {
|
||||
|
@ -48,60 +57,85 @@ function poller_run(&$argv, &$argc){
|
|||
return;
|
||||
}
|
||||
|
||||
if(($argc <= 1) OR ($argv[1] != "no_cron")) {
|
||||
// Run the cron job that calls all other jobs
|
||||
proc_run(PRIORITY_MEDIUM, "include/cron.php");
|
||||
$starttime = time();
|
||||
|
||||
// Run the cronhooks job separately from cron for being able to use a different timing
|
||||
proc_run(PRIORITY_MEDIUM, "include/cronhooks.php");
|
||||
while ($r = poller_worker_process()) {
|
||||
|
||||
// Cleaning dead processes
|
||||
poller_kill_stale_workers();
|
||||
} else
|
||||
// Sleep four seconds before checking for running processes again to avoid having too many workers
|
||||
sleep(4);
|
||||
|
||||
// Checking number of workers
|
||||
if (poller_too_much_workers())
|
||||
// Count active workers and compare them with a maximum value that depends on the load
|
||||
if (poller_too_much_workers()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!poller_execute($r[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Quit the poller once every hour
|
||||
if (time() > ($starttime + 3600))
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Execute a worker entry
|
||||
*
|
||||
* @param array $queue Workerqueue entry
|
||||
*
|
||||
* @return boolean "true" if further processing should be stopped
|
||||
*/
|
||||
function poller_execute($queue) {
|
||||
|
||||
$a = get_app();
|
||||
|
||||
$mypid = getmypid();
|
||||
|
||||
$cooldown = Config::get("system", "worker_cooldown", 0);
|
||||
|
||||
$starttime = time();
|
||||
|
||||
while ($r = q("SELECT * FROM `workerqueue` WHERE `executed` = '0000-00-00 00:00:00' ORDER BY `priority`, `created` LIMIT 1")) {
|
||||
|
||||
// Constantly check the number of parallel database processes
|
||||
if ($a->max_processes_reached())
|
||||
return;
|
||||
|
||||
// Constantly check the number of available database connections to let the frontend be accessible at any time
|
||||
if (poller_max_connections_reached())
|
||||
return;
|
||||
|
||||
// Count active workers and compare them with a maximum value that depends on the load
|
||||
if (poller_too_much_workers())
|
||||
return;
|
||||
|
||||
q("UPDATE `workerqueue` SET `executed` = '%s', `pid` = %d WHERE `id` = %d AND `executed` = '0000-00-00 00:00:00'",
|
||||
dbesc(datetime_convert()),
|
||||
intval($mypid),
|
||||
intval($r[0]["id"]));
|
||||
|
||||
// Assure that there are no tasks executed twice
|
||||
$id = q("SELECT `pid`, `executed` FROM `workerqueue` WHERE `id` = %d", intval($r[0]["id"]));
|
||||
if (!$id) {
|
||||
logger("Queue item ".$r[0]["id"]." vanished - skip this execution", LOGGER_DEBUG);
|
||||
continue;
|
||||
} elseif ((strtotime($id[0]["executed"]) <= 0) OR ($id[0]["pid"] == 0)) {
|
||||
logger("Entry for queue item ".$r[0]["id"]." wasn't stored - we better stop here", LOGGER_DEBUG);
|
||||
return;
|
||||
} elseif ($id[0]["pid"] != $mypid) {
|
||||
logger("Queue item ".$r[0]["id"]." is to be executed by process ".$id[0]["pid"]." and not by me (".$mypid.") - skip this execution", LOGGER_DEBUG);
|
||||
continue;
|
||||
// Quit when in maintenance
|
||||
if (get_config('system', 'maintenance', true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$argv = json_decode($r[0]["parameter"]);
|
||||
// Constantly check the number of parallel database processes
|
||||
if ($a->max_processes_reached()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Constantly check the number of available database connections to let the frontend be accessible at any time
|
||||
if (poller_max_connections_reached()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$upd = q("UPDATE `workerqueue` SET `executed` = '%s', `pid` = %d WHERE `id` = %d AND `pid` = 0",
|
||||
dbesc(datetime_convert()),
|
||||
intval($mypid),
|
||||
intval($queue["id"]));
|
||||
|
||||
if (!$upd) {
|
||||
logger("Couldn't update queue entry ".$queue["id"]." - skip this execution", LOGGER_DEBUG);
|
||||
q("COMMIT");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Assure that there are no tasks executed twice
|
||||
$id = q("SELECT `pid`, `executed` FROM `workerqueue` WHERE `id` = %d", intval($queue["id"]));
|
||||
if (!$id) {
|
||||
logger("Queue item ".$queue["id"]." vanished - skip this execution", LOGGER_DEBUG);
|
||||
q("COMMIT");
|
||||
return true;
|
||||
} elseif ((strtotime($id[0]["executed"]) <= 0) OR ($id[0]["pid"] == 0)) {
|
||||
logger("Entry for queue item ".$queue["id"]." wasn't stored - skip this execution", LOGGER_DEBUG);
|
||||
q("COMMIT");
|
||||
return true;
|
||||
} elseif ($id[0]["pid"] != $mypid) {
|
||||
logger("Queue item ".$queue["id"]." is to be executed by process ".$id[0]["pid"]." and not by me (".$mypid.") - skip this execution", LOGGER_DEBUG);
|
||||
q("COMMIT");
|
||||
return true;
|
||||
}
|
||||
q("COMMIT");
|
||||
|
||||
$argv = json_decode($queue["parameter"]);
|
||||
|
||||
$argc = count($argv);
|
||||
|
||||
|
@ -110,8 +144,8 @@ function poller_run(&$argv, &$argc){
|
|||
|
||||
if (!validate_include($include)) {
|
||||
logger("Include file ".$argv[0]." is not valid!");
|
||||
q("DELETE FROM `workerqueue` WHERE `id` = %d", intval($r[0]["id"]));
|
||||
continue;
|
||||
q("DELETE FROM `workerqueue` WHERE `id` = %d", intval($queue["id"]));
|
||||
return true;
|
||||
}
|
||||
|
||||
require_once($include);
|
||||
|
@ -119,25 +153,30 @@ function poller_run(&$argv, &$argc){
|
|||
$funcname = str_replace(".php", "", basename($argv[0]))."_run";
|
||||
|
||||
if (function_exists($funcname)) {
|
||||
logger("Process ".$mypid." - Prio ".$r[0]["priority"]." - ID ".$r[0]["id"].": ".$funcname." ".$r[0]["parameter"]);
|
||||
logger("Process ".$mypid." - Prio ".$queue["priority"]." - ID ".$queue["id"].": ".$funcname." ".$queue["parameter"]);
|
||||
|
||||
// For better logging create a new process id for every worker call
|
||||
// But preserve the old one for the worker
|
||||
$old_process_id = $a->process_id;
|
||||
$a->process_id = uniqid("wrk", true);
|
||||
|
||||
$funcname($argv, $argc);
|
||||
|
||||
$a->process_id = $old_process_id;
|
||||
|
||||
if ($cooldown > 0) {
|
||||
logger("Process ".$mypid." - Prio ".$r[0]["priority"]." - ID ".$r[0]["id"].": ".$funcname." - in cooldown for ".$cooldown." seconds");
|
||||
logger("Process ".$mypid." - Prio ".$queue["priority"]." - ID ".$queue["id"].": ".$funcname." - in cooldown for ".$cooldown." seconds");
|
||||
sleep($cooldown);
|
||||
}
|
||||
|
||||
logger("Process ".$mypid." - Prio ".$r[0]["priority"]." - ID ".$r[0]["id"].": ".$funcname." - done");
|
||||
logger("Process ".$mypid." - Prio ".$queue["priority"]." - ID ".$queue["id"].": ".$funcname." - done");
|
||||
|
||||
q("DELETE FROM `workerqueue` WHERE `id` = %d", intval($r[0]["id"]));
|
||||
} else
|
||||
q("DELETE FROM `workerqueue` WHERE `id` = %d", intval($queue["id"]));
|
||||
} else {
|
||||
logger("Function ".$funcname." does not exist");
|
||||
|
||||
// Quit the poller once every hour
|
||||
if (time() > ($starttime + 3600))
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,9 +190,7 @@ function poller_max_connections_reached() {
|
|||
$max = get_config("system", "max_connections");
|
||||
|
||||
// Fetch the percentage level where the poller will get active
|
||||
$maxlevel = get_config("system", "max_connections_level");
|
||||
if ($maxlevel == 0)
|
||||
$maxlevel = 75;
|
||||
$maxlevel = Config::get("system", "max_connections_level", 75);
|
||||
|
||||
if ($max == 0) {
|
||||
// the maximum number of possible user connections can be a system variable
|
||||
|
@ -269,13 +306,13 @@ function poller_kill_stale_workers() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the number of active workers exceeds the given limits
|
||||
*
|
||||
* @return bool Are there too much workers running?
|
||||
*/
|
||||
function poller_too_much_workers() {
|
||||
|
||||
|
||||
$queues = get_config("system", "worker_queues");
|
||||
|
||||
if ($queues == 0)
|
||||
$queues = 4;
|
||||
$queues = Config::get("system", "worker_queues", 4);
|
||||
|
||||
$maxqueues = $queues;
|
||||
|
||||
|
@ -284,9 +321,7 @@ function poller_too_much_workers() {
|
|||
// Decrease the number of workers at higher load
|
||||
$load = current_load();
|
||||
if($load) {
|
||||
$maxsysload = intval(get_config('system','maxloadavg'));
|
||||
if($maxsysload < 1)
|
||||
$maxsysload = 50;
|
||||
$maxsysload = intval(Config::get("system", "maxloadavg", 50));
|
||||
|
||||
$maxworkers = $queues;
|
||||
|
||||
|
@ -312,7 +347,28 @@ function poller_too_much_workers() {
|
|||
}
|
||||
}
|
||||
|
||||
logger("Current load: ".$load." - maximum: ".$maxsysload." - current queues: ".$active."/".$entries." - maximum: ".$queues."/".$maxqueues, LOGGER_DEBUG);
|
||||
// Create a list of queue entries grouped by their priority
|
||||
$running = array(PRIORITY_CRITICAL => 0,
|
||||
PRIORITY_HIGH => 0,
|
||||
PRIORITY_MEDIUM => 0,
|
||||
PRIORITY_LOW => 0,
|
||||
PRIORITY_NEGLIGIBLE => 0);
|
||||
|
||||
$r = q("SELECT COUNT(*) AS `running`, `priority` FROM `process` INNER JOIN `workerqueue` ON `workerqueue`.`pid` = `process`.`pid` GROUP BY `priority`");
|
||||
if (dbm::is_result($r))
|
||||
foreach ($r AS $process)
|
||||
$running[$process["priority"]] = $process["running"];
|
||||
|
||||
$processlist = "";
|
||||
$r = q("SELECT COUNT(*) AS `entries`, `priority` FROM `workerqueue` GROUP BY `priority`");
|
||||
if (dbm::is_result($r))
|
||||
foreach ($r as $entry) {
|
||||
if ($processlist != "")
|
||||
$processlist .= ", ";
|
||||
$processlist .= $entry["priority"].":".$running[$entry["priority"]]."/".$entry["entries"];
|
||||
}
|
||||
|
||||
logger("Load: ".$load."/".$maxsysload." - processes: ".$active."/".$entries." (".$processlist.") - maximum: ".$queues."/".$maxqueues, LOGGER_DEBUG);
|
||||
|
||||
// Are there fewer workers running as possible? Then fork a new one.
|
||||
if (!get_config("system", "worker_dont_fork") AND ($queues > ($active + 1)) AND ($entries > 1)) {
|
||||
|
@ -326,12 +382,193 @@ function poller_too_much_workers() {
|
|||
return($active >= $queues);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of active poller processes
|
||||
*
|
||||
* @return integer Number of active poller processes
|
||||
*/
|
||||
function poller_active_workers() {
|
||||
$workers = q("SELECT COUNT(*) AS `processes` FROM `process` WHERE `command` = 'poller.php'");
|
||||
|
||||
return($workers[0]["processes"]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if we should pass some slow processes
|
||||
*
|
||||
* When the active processes of the highest priority are using more than 2/3
|
||||
* of all processes, we let pass slower processes.
|
||||
*
|
||||
* @param string $highest_priority Returns the currently highest priority
|
||||
* @return bool We let pass a slower process than $highest_priority
|
||||
*/
|
||||
function poller_passing_slow(&$highest_priority) {
|
||||
|
||||
$highest_priority = 0;
|
||||
|
||||
$r = q("SELECT `priority`
|
||||
FROM `process`
|
||||
INNER JOIN `workerqueue` ON `workerqueue`.`pid` = `process`.`pid`");
|
||||
|
||||
// No active processes at all? Fine
|
||||
if (!dbm::is_result($r))
|
||||
return(false);
|
||||
|
||||
$priorities = array();
|
||||
foreach ($r AS $line)
|
||||
$priorities[] = $line["priority"];
|
||||
|
||||
// Should not happen
|
||||
if (count($priorities) == 0)
|
||||
return(false);
|
||||
|
||||
$highest_priority = min($priorities);
|
||||
|
||||
// The highest process is already the slowest one?
|
||||
// Then we quit
|
||||
if ($highest_priority == PRIORITY_NEGLIGIBLE)
|
||||
return(false);
|
||||
|
||||
$high = 0;
|
||||
foreach ($priorities AS $priority)
|
||||
if ($priority == $highest_priority)
|
||||
++$high;
|
||||
|
||||
logger("Highest priority: ".$highest_priority." Total processes: ".count($priorities)." Count high priority processes: ".$high, LOGGER_DEBUG);
|
||||
$passing_slow = (($high/count($priorities)) > (2/3));
|
||||
|
||||
if ($passing_slow)
|
||||
logger("Passing slower processes than priority ".$highest_priority, LOGGER_DEBUG);
|
||||
|
||||
return($passing_slow);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the next worker process
|
||||
*
|
||||
* @return string SQL statement
|
||||
*/
|
||||
function poller_worker_process() {
|
||||
|
||||
q("START TRANSACTION;");
|
||||
|
||||
// Check if we should pass some low priority process
|
||||
$highest_priority = 0;
|
||||
|
||||
if (poller_passing_slow($highest_priority)) {
|
||||
// Are there waiting processes with a higher priority than the currently highest?
|
||||
$r = q("SELECT * FROM `workerqueue`
|
||||
WHERE `executed` = '0000-00-00 00:00:00' AND `priority` < %d
|
||||
ORDER BY `priority`, `created` LIMIT 1", dbesc($highest_priority));
|
||||
if (dbm::is_result($r))
|
||||
return $r;
|
||||
|
||||
// Give slower processes some processing time
|
||||
$r = q("SELECT * FROM `workerqueue`
|
||||
WHERE `executed` = '0000-00-00 00:00:00' AND `priority` > %d
|
||||
ORDER BY `priority`, `created` LIMIT 1", dbesc($highest_priority));
|
||||
}
|
||||
|
||||
// If there is no result (or we shouldn't pass lower processes) we check without priority limit
|
||||
if (($highest_priority == 0) OR !dbm::is_result($r))
|
||||
$r = q("SELECT * FROM `workerqueue` WHERE `executed` = '0000-00-00 00:00:00' ORDER BY `priority`, `created` LIMIT 1");
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Call the front end worker
|
||||
*/
|
||||
function call_worker() {
|
||||
if (!Config::get("system", "frontend_worker") OR !Config::get("system", "worker")) {
|
||||
return;
|
||||
}
|
||||
|
||||
$url = get_app()->get_baseurl()."/worker";
|
||||
fetch_url($url, false, $redirects, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Call the front end worker if there aren't any active
|
||||
*/
|
||||
function call_worker_if_idle() {
|
||||
if (!Config::get("system", "frontend_worker") OR !Config::get("system", "worker")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do we have "proc_open"? Then we can fork the poller
|
||||
if (function_exists("proc_open")) {
|
||||
// When was the last time that we called the worker?
|
||||
// Less than one minute? Then we quit
|
||||
if ((time() - get_config("system", "worker_started")) < 60) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_config("system", "worker_started", time());
|
||||
|
||||
// Do we have enough running workers? Then we quit here.
|
||||
if (poller_too_much_workers()) {
|
||||
// Cleaning dead processes
|
||||
poller_kill_stale_workers();
|
||||
get_app()->remove_inactive_processes();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
poller_run_cron();
|
||||
|
||||
logger('Call poller', LOGGER_DEBUG);
|
||||
|
||||
$args = array("php", "include/poller.php", "no_cron");
|
||||
$a = get_app();
|
||||
$a->proc_run($args);
|
||||
return;
|
||||
}
|
||||
|
||||
// We cannot execute background processes.
|
||||
// We now run the processes from the frontend.
|
||||
// This won't work with long running processes.
|
||||
poller_run_cron();
|
||||
|
||||
clear_worker_processes();
|
||||
|
||||
$workers = q("SELECT COUNT(*) AS `processes` FROM `process` WHERE `command` = 'worker.php'");
|
||||
|
||||
if ($workers[0]["processes"] == 0) {
|
||||
call_worker();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes long running worker processes
|
||||
*/
|
||||
function clear_worker_processes() {
|
||||
$timeout = Config::get("system", "frontend_worker_timeout", 10);
|
||||
|
||||
/// @todo We should clean up the corresponding workerqueue entries as well
|
||||
q("DELETE FROM `process` WHERE `created` < '%s' AND `command` = 'worker.php'",
|
||||
dbesc(datetime_convert('UTC','UTC',"now - ".$timeout." minutes")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Runs the cron processes
|
||||
*/
|
||||
function poller_run_cron() {
|
||||
logger('Add cron entries', LOGGER_DEBUG);
|
||||
|
||||
// Check for spooled items
|
||||
proc_run(PRIORITY_HIGH, "include/spool_post.php");
|
||||
|
||||
// Run the cron job that calls all other jobs
|
||||
proc_run(PRIORITY_MEDIUM, "include/cron.php");
|
||||
|
||||
// Run the cronhooks job separately from cron for being able to use a different timing
|
||||
proc_run(PRIORITY_MEDIUM, "include/cronhooks.php");
|
||||
|
||||
// Cleaning dead processes
|
||||
poller_kill_stale_workers();
|
||||
}
|
||||
|
||||
if (array_search(__file__,get_included_files())===0){
|
||||
poller_run($_SERVER["argv"],$_SERVER["argc"]);
|
||||
|
||||
|
|
|
@ -8,15 +8,19 @@
|
|||
*/
|
||||
function post_update() {
|
||||
|
||||
if (!post_update_1192())
|
||||
if (!post_update_1192()) {
|
||||
return;
|
||||
|
||||
if (!post_update_1194())
|
||||
}
|
||||
if (!post_update_1194()) {
|
||||
return;
|
||||
|
||||
if (!post_update_1198())
|
||||
}
|
||||
if (!post_update_1198()) {
|
||||
return;
|
||||
}
|
||||
if (!post_update_1206()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief set the gcontact-id in all item entries
|
||||
|
@ -174,13 +178,18 @@ function post_update_1198() {
|
|||
}
|
||||
|
||||
// Update the thread table from the item table
|
||||
q("UPDATE `thread` INNER JOIN `item` ON `item`.`id`=`thread`.`iid`
|
||||
$r = q("UPDATE `thread` INNER JOIN `item` ON `item`.`id`=`thread`.`iid`
|
||||
SET `thread`.`author-id` = `item`.`author-id`,
|
||||
`thread`.`owner-id` = `item`.`owner-id`
|
||||
WHERE `thread`.`author-id` = 0 AND `thread`.`owner-id` = 0 AND
|
||||
(`thread`.`uid` IN (SELECT `uid` from `user`) OR `thread`.`uid` = 0)");
|
||||
|
||||
logger("Updated threads", LOGGER_DEBUG);
|
||||
if (dbm::is_result($r)) {
|
||||
set_config("system", "post_update_version", 1198);
|
||||
logger("Done", LOGGER_DEBUG);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -215,4 +224,39 @@ function post_update_1198() {
|
|||
logger("Updated items", LOGGER_DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief update the "last-item" field in the "self" contact
|
||||
*
|
||||
* This field avoids cost intensive calls in the admin panel and in "nodeinfo"
|
||||
*
|
||||
* @return bool "true" when the job is done
|
||||
*/
|
||||
function post_update_1206() {
|
||||
// Was the script completed?
|
||||
if (get_config("system", "post_update_version") >= 1206)
|
||||
return true;
|
||||
|
||||
logger("Start", LOGGER_DEBUG);
|
||||
$r = q("SELECT `contact`.`id`, `contact`.`last-item`,
|
||||
(SELECT MAX(`changed`) FROM `item` FORCE INDEX (`uid_wall_changed`) WHERE `wall` AND `uid` = `user`.`uid`) AS `lastitem_date`
|
||||
FROM `user`
|
||||
INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self`");
|
||||
|
||||
if (!dbm::is_result($r)) {
|
||||
return false;
|
||||
}
|
||||
foreach ($r AS $user) {
|
||||
if (!empty($user["lastitem_date"]) AND ($user["lastitem_date"] > $user["last-item"])) {
|
||||
q("UPDATE `contact` SET `last-item` = '%s' WHERE `id` = %d",
|
||||
dbesc($user["lastitem_date"]),
|
||||
intval($user["id"]));
|
||||
}
|
||||
}
|
||||
|
||||
set_config("system", "post_update_version", 1206);
|
||||
logger("Done", LOGGER_DEBUG);
|
||||
return true;
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -15,22 +15,35 @@ function remove_queue_item($id) {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the communication with a given contact had problems recently
|
||||
*
|
||||
* @param int $cid Contact id
|
||||
*
|
||||
* @return bool The communication with this contact has currently problems
|
||||
*/
|
||||
function was_recently_delayed($cid) {
|
||||
|
||||
$was_delayed = false;
|
||||
|
||||
// Are there queue entries that were recently added?
|
||||
$r = q("SELECT `id` FROM `queue` WHERE `cid` = %d
|
||||
and last > UTC_TIMESTAMP() - interval 15 minute limit 1",
|
||||
AND `last` > UTC_TIMESTAMP() - interval 15 minute LIMIT 1",
|
||||
intval($cid)
|
||||
);
|
||||
if(count($r))
|
||||
return true;
|
||||
|
||||
$r = q("select `term-date` from contact where id = %d and `term-date` != '' and `term-date` != '0000-00-00 00:00:00' limit 1",
|
||||
$was_delayed = dbm::is_result($r);
|
||||
|
||||
// We set "term-date" to a current date if the communication has problems.
|
||||
// If the communication works again we reset this value.
|
||||
if ($was_delayed) {
|
||||
$r = q("SELECT `term-date` FROM `contact` WHERE `id` = %d AND `term-date` <= '1000-01-01' LIMIT 1",
|
||||
intval($cid)
|
||||
);
|
||||
if(count($r))
|
||||
return true;
|
||||
$was_delayed = !dbm::is_result($r);
|
||||
}
|
||||
|
||||
return false;
|
||||
return $was_delayed;
|
||||
}
|
||||
|
||||
|
||||
|
|
52
include/remove_contact.php
Normal file
52
include/remove_contact.php
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
/**
|
||||
* @file include/remove_contact.php
|
||||
* @brief Removes orphaned data from deleted contacts
|
||||
*/
|
||||
require_once("boot.php");
|
||||
|
||||
function remove_contact_run($argv, $argc) {
|
||||
global $a, $db;
|
||||
|
||||
if (is_null($a)) {
|
||||
$a = new App;
|
||||
}
|
||||
|
||||
if (is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
}
|
||||
|
||||
load_config('config');
|
||||
load_config('system');
|
||||
|
||||
if ($argc != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = intval($argv[1]);
|
||||
|
||||
// Only delete if the contact doesn't exist (anymore)
|
||||
$r = q("SELECT `id` FROM `contact` WHERE `id` = %d", intval($id));
|
||||
if (dbm::is_result($r)) {
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
if (array_search(__file__, get_included_files()) === 0) {
|
||||
remove_contact_run($_SERVER["argv"], $_SERVER["argc"]);
|
||||
killme();
|
||||
}
|
||||
?>
|
|
@ -31,8 +31,7 @@ function get_salmon_key($uri,$keyhash) {
|
|||
$ret[$x] = substr($ret[$x],strpos($ret[$x],',')+1);
|
||||
else
|
||||
$ret[$x] = substr($ret[$x],5);
|
||||
}
|
||||
else
|
||||
} elseif (normalise_link($ret[$x]) == 'http://')
|
||||
$ret[$x] = fetch_url($ret[$x]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,11 +79,9 @@ function authenticate_success($user_record, $login_initial = false, $interactive
|
|||
header('X-Account-Management-Status: active; name="' . $a->user['username'] . '"; id="' . $a->user['nickname'] .'"');
|
||||
|
||||
if($login_initial || $login_refresh) {
|
||||
$l = get_browser_language();
|
||||
|
||||
q("UPDATE `user` SET `login_date` = '%s', `language` = '%s' WHERE `uid` = %d",
|
||||
q("UPDATE `user` SET `login_date` = '%s' WHERE `uid` = %d",
|
||||
dbesc(datetime_convert()),
|
||||
dbesc($l),
|
||||
intval($_SESSION['uid'])
|
||||
);
|
||||
|
||||
|
|
|
@ -1,32 +1,58 @@
|
|||
<?php
|
||||
|
||||
// Session management functions. These provide database storage of PHP
|
||||
// session info.
|
||||
|
||||
require_once('include/cache.php');
|
||||
|
||||
$session_exists = 0;
|
||||
$session_expire = 180000;
|
||||
|
||||
if(! function_exists('ref_session_open')) {
|
||||
function ref_session_open($s, $n) {
|
||||
return true;
|
||||
}}
|
||||
}
|
||||
|
||||
if(! function_exists('ref_session_read')) {
|
||||
function ref_session_read($id) {
|
||||
global $session_exists;
|
||||
if(x($id))
|
||||
|
||||
if (!x($id)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$memcache = cache::memcache();
|
||||
if (is_object($memcache)) {
|
||||
$data = $memcache->get(get_app()->get_hostname().":session:".$id);
|
||||
if (!is_bool($data)) {
|
||||
return $data;
|
||||
}
|
||||
logger("no data for session $id", LOGGER_TRACE);
|
||||
return '';
|
||||
}
|
||||
|
||||
$r = q("SELECT `data` FROM `session` WHERE `sid`= '%s'", dbesc($id));
|
||||
|
||||
if(count($r)) {
|
||||
if (dbm::is_result($r)) {
|
||||
$session_exists = true;
|
||||
return $r[0]['data'];
|
||||
} else {
|
||||
logger("no data for session $id", LOGGER_TRACE);
|
||||
}
|
||||
return '';
|
||||
}}
|
||||
|
||||
if(! function_exists('ref_session_write')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Standard PHP session write callback
|
||||
*
|
||||
* This callback updates the DB-stored session data and/or the expiration depending
|
||||
* on the case. Uses the $session_expire global for existing session, 5 minutes
|
||||
* for newly created session.
|
||||
*
|
||||
* @global bool $session_exists Whether a session with the given id already exists
|
||||
* @global int $session_expire Session expiration delay in seconds
|
||||
* @param string $id Session ID with format: [a-z0-9]{26}
|
||||
* @param string $data Serialized session data
|
||||
* @return boolean Returns false if parameters are missing, true otherwise
|
||||
*/
|
||||
function ref_session_write($id, $data) {
|
||||
global $session_exists, $session_expire;
|
||||
|
||||
|
@ -37,40 +63,49 @@ function ref_session_write ($id,$data) {
|
|||
$expire = time() + $session_expire;
|
||||
$default_expire = time() + 300;
|
||||
|
||||
$memcache = cache::memcache();
|
||||
if (is_object($memcache)) {
|
||||
$memcache->set(get_app()->get_hostname().":session:".$id, $data, MEMCACHE_COMPRESSED, $expire);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($session_exists) {
|
||||
$r = q("UPDATE `session`
|
||||
SET `data` = '%s'
|
||||
WHERE `data` != '%s' AND `sid` = '%s'",
|
||||
dbesc($data), dbesc($data), dbesc($id));
|
||||
|
||||
$r = q("UPDATE `session`
|
||||
SET `expire` = '%s'
|
||||
WHERE `expire` != '%s' AND `sid` = '%s'",
|
||||
dbesc($expire), dbesc($expire), dbesc($id));
|
||||
} else
|
||||
SET `data` = '%s', `expire` = '%s'
|
||||
WHERE `sid` = '%s'
|
||||
AND (`data` != '%s' OR `expire` != '%s')",
|
||||
dbesc($data), dbesc($expire), dbesc($id), dbesc($data), dbesc($expire));
|
||||
} else {
|
||||
$r = q("INSERT INTO `session`
|
||||
SET `sid` = '%s', `expire` = '%s', `data` = '%s'",
|
||||
dbesc($id), dbesc($default_expire), dbesc($data));
|
||||
}
|
||||
|
||||
return true;
|
||||
}}
|
||||
}
|
||||
|
||||
if(! function_exists('ref_session_close')) {
|
||||
function ref_session_close() {
|
||||
return true;
|
||||
}}
|
||||
}
|
||||
|
||||
if(! function_exists('ref_session_destroy')) {
|
||||
function ref_session_destroy($id) {
|
||||
q("DELETE FROM `session` WHERE `sid` = '%s'", dbesc($id));
|
||||
return true;
|
||||
}}
|
||||
$memcache = cache::memcache();
|
||||
|
||||
if (is_object($memcache)) {
|
||||
$memcache->delete(get_app()->get_hostname().":session:".$id);
|
||||
return true;
|
||||
}
|
||||
|
||||
q("DELETE FROM `session` WHERE `sid` = '%s'", dbesc($id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if(! function_exists('ref_session_gc')) {
|
||||
function ref_session_gc($expire) {
|
||||
q("DELETE FROM `session` WHERE `expire` < %d", dbesc(time()));
|
||||
|
||||
return true;
|
||||
}}
|
||||
}
|
||||
|
||||
$gc_probability = 50;
|
||||
|
||||
|
@ -78,7 +113,8 @@ ini_set('session.gc_probability', $gc_probability);
|
|||
ini_set('session.use_only_cookies', 1);
|
||||
ini_set('session.cookie_httponly', 1);
|
||||
|
||||
if (!get_config('system', 'disable_database_session'))
|
||||
if (!get_config('system', 'disable_database_session')) {
|
||||
session_set_save_handler('ref_session_open', 'ref_session_close',
|
||||
'ref_session_read', 'ref_session_write',
|
||||
'ref_session_destroy', 'ref_session_gc');
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
|
|||
if(! $url)
|
||||
return;
|
||||
|
||||
$url = $url . (($uid) ? '/@me/@all?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation' : '?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation') ;
|
||||
$url = $url . (($uid) ? '/@me/@all?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation' : '?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation') ;
|
||||
|
||||
logger('poco_load: ' . $url, LOGGER_DEBUG);
|
||||
|
||||
|
@ -86,6 +86,7 @@ function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
|
|||
$about = '';
|
||||
$keywords = '';
|
||||
$gender = '';
|
||||
$contact_type = -1;
|
||||
$generation = 0;
|
||||
|
||||
$name = $entry->displayName;
|
||||
|
@ -133,6 +134,9 @@ function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
|
|||
foreach($entry->tags as $tag)
|
||||
$keywords = implode(", ", $tag);
|
||||
|
||||
if(isset($entry->contactType) AND ($entry->contactType >= 0))
|
||||
$contact_type = $entry->contactType;
|
||||
|
||||
// If you query a Friendica server for its profiles, the network has to be Friendica
|
||||
/// TODO It could also be a Redmatrix server
|
||||
//if ($uid == 0)
|
||||
|
@ -140,6 +144,9 @@ function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
|
|||
|
||||
poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
|
||||
|
||||
$gcontact = array("url" => $profile_url, "contact-type" => $contact_type, "generation" => $generation);
|
||||
update_gcontact($gcontact);
|
||||
|
||||
// Update the Friendica contacts. Diaspora is doing it via a message. (See include/diaspora.php)
|
||||
// Deactivated because we now update Friendica contacts in dfrn.php
|
||||
//if (($location != "") OR ($about != "") OR ($keywords != "") OR ($gender != ""))
|
||||
|
@ -384,6 +391,15 @@ function poco_detect_server($profile) {
|
|||
}
|
||||
}
|
||||
|
||||
// Mastodon
|
||||
if ($server_url == "") {
|
||||
$red = preg_replace("=(https?://)(.*)/users/(.*)=ism", "$1$2", $profile);
|
||||
if ($red != $profile) {
|
||||
$server_url = $red;
|
||||
$network = NETWORK_OSTATUS;
|
||||
}
|
||||
}
|
||||
|
||||
return $server_url;
|
||||
}
|
||||
|
||||
|
@ -748,6 +764,13 @@ function poco_check_server($server_url, $network = "", $force = false) {
|
|||
$versionparts = explode("-", $version);
|
||||
$version = $versionparts[0];
|
||||
}
|
||||
|
||||
if(stristr($line,'Server: Mastodon')) {
|
||||
$platform = "Mastodon";
|
||||
$network = NETWORK_OSTATUS;
|
||||
// Mastodon doesn't reveal version numbers
|
||||
$version = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1068,8 +1091,16 @@ function all_friends($uid,$cid,$start = 0, $limit = 80) {
|
|||
|
||||
function suggestion_query($uid, $start = 0, $limit = 80) {
|
||||
|
||||
if(! $uid)
|
||||
if (!$uid) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Uncommented because the result of the queries are to big to store it in the cache.
|
||||
// We need to decide if we want to change the db column type or if we want to delete it.
|
||||
// $list = Cache::get("suggestion_query:".$uid.":".$start.":".$limit);
|
||||
// if (!is_null($list)) {
|
||||
// return $list;
|
||||
// }
|
||||
|
||||
$network = array(NETWORK_DFRN);
|
||||
|
||||
|
@ -1080,9 +1111,10 @@ function suggestion_query($uid, $start = 0, $limit = 80) {
|
|||
$network[] = NETWORK_OSTATUS;
|
||||
|
||||
$sql_network = implode("', '", $network);
|
||||
//$sql_network = "'".$sql_network."', ''";
|
||||
$sql_network = "'".$sql_network."'";
|
||||
|
||||
/// @todo This query is really slow
|
||||
// By now we cache the data for five minutes
|
||||
$r = q("SELECT count(glink.gcid) as `total`, gcontact.* from gcontact
|
||||
INNER JOIN `glink` ON `glink`.`gcid` = `gcontact`.`id`
|
||||
where uid = %d and not gcontact.nurl in ( select nurl from contact where uid = %d )
|
||||
|
@ -1101,8 +1133,13 @@ function suggestion_query($uid, $start = 0, $limit = 80) {
|
|||
intval($limit)
|
||||
);
|
||||
|
||||
if(count($r) && count($r) >= ($limit -1))
|
||||
if (count($r) && count($r) >= ($limit -1)) {
|
||||
// Uncommented because the result of the queries are to big to store it in the cache.
|
||||
// We need to decide if we want to change the db column type or if we want to delete it.
|
||||
// Cache::set("suggestion_query:".$uid.":".$start.":".$limit, $r, CACHE_FIVE_MINUTES);
|
||||
|
||||
return $r;
|
||||
}
|
||||
|
||||
$r2 = q("SELECT gcontact.* FROM gcontact
|
||||
INNER JOIN `glink` ON `glink`.`gcid` = `gcontact`.`id`
|
||||
|
@ -1131,6 +1168,9 @@ function suggestion_query($uid, $start = 0, $limit = 80) {
|
|||
while (sizeof($list) > ($limit))
|
||||
array_pop($list);
|
||||
|
||||
// Uncommented because the result of the queries are to big to store it in the cache.
|
||||
// We need to decide if we want to change the db column type or if we want to delete it.
|
||||
// Cache::set("suggestion_query:".$uid.":".$start.":".$limit, $list, CACHE_FIVE_MINUTES);
|
||||
return $list;
|
||||
}
|
||||
|
||||
|
@ -1236,7 +1276,7 @@ function poco_discover($complete = false) {
|
|||
}
|
||||
|
||||
// Fetch all users from the other server
|
||||
$url = $server["poco"]."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation";
|
||||
$url = $server["poco"]."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation";
|
||||
|
||||
logger("Fetch all users from the server ".$server["nurl"], LOGGER_DEBUG);
|
||||
|
||||
|
@ -1255,7 +1295,7 @@ function poco_discover($complete = false) {
|
|||
$updatedSince = date("Y-m-d H:i:s", time() - $timeframe * 86400);
|
||||
|
||||
// Fetch all global contacts from the other server (Not working with Redmatrix and Friendica versions before 3.3)
|
||||
$url = $server["poco"]."/@global?updatedSince=".$updatedSince."&fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation";
|
||||
$url = $server["poco"]."/@global?updatedSince=".$updatedSince."&fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation";
|
||||
|
||||
$success = false;
|
||||
|
||||
|
@ -1303,7 +1343,7 @@ function poco_discover_server_users($data, $server) {
|
|||
logger("Fetch contacts for the user ".$username." from the server ".$server["nurl"], LOGGER_DEBUG);
|
||||
|
||||
// Fetch all contacts from a given user from the other server
|
||||
$url = $server["poco"]."/".$username."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,generation";
|
||||
$url = $server["poco"]."/".$username."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation";
|
||||
|
||||
$retdata = z_fetch_url($url);
|
||||
if ($retdata["success"])
|
||||
|
@ -1330,6 +1370,7 @@ function poco_discover_server($data, $default_generation = 0) {
|
|||
$about = '';
|
||||
$keywords = '';
|
||||
$gender = '';
|
||||
$contact_type = -1;
|
||||
$generation = $default_generation;
|
||||
|
||||
$name = $entry->displayName;
|
||||
|
@ -1374,6 +1415,9 @@ function poco_discover_server($data, $default_generation = 0) {
|
|||
if(isset($entry->generation) AND ($entry->generation > 0))
|
||||
$generation = ++$entry->generation;
|
||||
|
||||
if(isset($entry->contactType) AND ($entry->contactType >= 0))
|
||||
$contact_type = $entry->contactType;
|
||||
|
||||
if(isset($entry->tags))
|
||||
foreach($entry->tags as $tag)
|
||||
$keywords = implode(", ", $tag);
|
||||
|
@ -1383,6 +1427,10 @@ function poco_discover_server($data, $default_generation = 0) {
|
|||
|
||||
logger("Store profile ".$profile_url, LOGGER_DEBUG);
|
||||
poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, 0, 0, 0);
|
||||
|
||||
$gcontact = array("url" => $profile_url, "contact-type" => $contact_type, "generation" => $generation);
|
||||
update_gcontact($gcontact);
|
||||
|
||||
logger("Done for profile ".$profile_url, LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
|
@ -1534,7 +1582,7 @@ function update_gcontact($contact) {
|
|||
return false;
|
||||
|
||||
$r = q("SELECT `name`, `nick`, `photo`, `location`, `about`, `addr`, `generation`, `birthday`, `gender`, `keywords`,
|
||||
`hide`, `nsfw`, `network`, `alias`, `notify`, `server_url`, `connect`, `updated`, `url`
|
||||
`contact-type`, `hide`, `nsfw`, `network`, `alias`, `notify`, `server_url`, `connect`, `updated`, `url`
|
||||
FROM `gcontact` WHERE `id` = %d LIMIT 1",
|
||||
intval($gcontact_id));
|
||||
|
||||
|
@ -1614,20 +1662,20 @@ function update_gcontact($contact) {
|
|||
}
|
||||
|
||||
if ($update) {
|
||||
logger("Update gcontact for ".$contact["url"]." Callstack: ".App::callstack(), LOGGER_DEBUG);
|
||||
logger("Update gcontact for ".$contact["url"], LOGGER_DEBUG);
|
||||
|
||||
q("UPDATE `gcontact` SET `photo` = '%s', `name` = '%s', `nick` = '%s', `addr` = '%s', `network` = '%s',
|
||||
`birthday` = '%s', `gender` = '%s', `keywords` = '%s', `hide` = %d, `nsfw` = %d,
|
||||
`alias` = '%s', `notify` = '%s', `url` = '%s',
|
||||
`contact-type` = %d, `alias` = '%s', `notify` = '%s', `url` = '%s',
|
||||
`location` = '%s', `about` = '%s', `generation` = %d, `updated` = '%s',
|
||||
`server_url` = '%s', `connect` = '%s'
|
||||
WHERE `nurl` = '%s' AND (`generation` = 0 OR `generation` >= %d)",
|
||||
dbesc($contact["photo"]), dbesc($contact["name"]), dbesc($contact["nick"]),
|
||||
dbesc($contact["addr"]), dbesc($contact["network"]), dbesc($contact["birthday"]),
|
||||
dbesc($contact["gender"]), dbesc($contact["keywords"]), intval($contact["hide"]),
|
||||
intval($contact["nsfw"]), dbesc($contact["alias"]), dbesc($contact["notify"]),
|
||||
dbesc($contact["url"]), dbesc($contact["location"]), dbesc($contact["about"]),
|
||||
intval($contact["generation"]), dbesc($contact["updated"]),
|
||||
intval($contact["nsfw"]), intval($contact["contact-type"]), dbesc($contact["alias"]),
|
||||
dbesc($contact["notify"]), dbesc($contact["url"]), dbesc($contact["location"]),
|
||||
dbesc($contact["about"]), intval($contact["generation"]), dbesc($contact["updated"]),
|
||||
dbesc($contact["server_url"]), dbesc($contact["connect"]),
|
||||
dbesc(normalise_link($contact["url"])), intval($contact["generation"]));
|
||||
|
||||
|
@ -1644,13 +1692,14 @@ function update_gcontact($contact) {
|
|||
|
||||
q("UPDATE `contact` SET `name` = '%s', `nick` = '%s', `addr` = '%s',
|
||||
`network` = '%s', `bd` = '%s', `gender` = '%s',
|
||||
`keywords` = '%s', `alias` = '%s', `url` = '%s',
|
||||
`location` = '%s', `about` = '%s'
|
||||
`keywords` = '%s', `alias` = '%s', `contact-type` = %d,
|
||||
`url` = '%s', `location` = '%s', `about` = '%s'
|
||||
WHERE `id` = %d",
|
||||
dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["addr"]),
|
||||
dbesc($contact["network"]), dbesc($contact["birthday"]), dbesc($contact["gender"]),
|
||||
dbesc($contact["keywords"]), dbesc($contact["alias"]), dbesc($contact["url"]),
|
||||
dbesc($contact["location"]), dbesc($contact["about"]), intval($r[0]["id"]));
|
||||
dbesc($contact["keywords"]), dbesc($contact["alias"]), intval($contact["contact-type"]),
|
||||
dbesc($contact["url"]), dbesc($contact["location"]), dbesc($contact["about"]),
|
||||
intval($r[0]["id"]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
49
include/spool_post.php
Normal file
49
include/spool_post.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* @file include/spool_post.php
|
||||
* @brief Posts items that wer spooled because they couldn't be posted.
|
||||
*/
|
||||
require_once("boot.php");
|
||||
require_once("include/items.php");
|
||||
|
||||
function spool_post_run($argv, $argc) {
|
||||
global $a, $db;
|
||||
|
||||
if (is_null($a)) {
|
||||
$a = new App;
|
||||
}
|
||||
|
||||
if (is_null($db)) {
|
||||
@include(".htconfig.php");
|
||||
require_once("include/dba.php");
|
||||
$db = new dba($db_host, $db_user, $db_pass, $db_data);
|
||||
unset($db_host, $db_user, $db_pass, $db_data);
|
||||
}
|
||||
|
||||
load_config('config');
|
||||
load_config('system');
|
||||
|
||||
$path = get_spoolpath();
|
||||
|
||||
if (is_writable($path)){
|
||||
if ($dh = opendir($path)) {
|
||||
while (($file = readdir($dh)) !== false) {
|
||||
$fullfile = $path."/".$file;
|
||||
if (filetype($fullfile) != "file") {
|
||||
continue;
|
||||
}
|
||||
$arr = json_decode(file_get_contents($fullfile), true);
|
||||
$result = item_store($arr);
|
||||
logger("Spool file ".$file." stored: ".$result, LOGGER_DEBUG);
|
||||
unlink($fullfile);
|
||||
}
|
||||
closedir($dh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (array_search(__file__, get_included_files()) === 0) {
|
||||
spool_post_run($_SERVER["argv"], $_SERVER["argc"]);
|
||||
killme();
|
||||
}
|
||||
?>
|
130
include/text.php
130
include/text.php
|
@ -581,14 +581,14 @@ function get_intltext_template($s) {
|
|||
if(! isset($lang))
|
||||
$lang = 'en';
|
||||
|
||||
if(file_exists("view/$lang$engine/$s")) {
|
||||
if(file_exists("view/lang/$lang$engine/$s")) {
|
||||
$stamp1 = microtime(true);
|
||||
$content = file_get_contents("view/$lang$engine/$s");
|
||||
$content = file_get_contents("view/lang/$lang$engine/$s");
|
||||
$a->save_timestamp($stamp1, "file");
|
||||
return $content;
|
||||
} elseif(file_exists("view/en$engine/$s")) {
|
||||
} elseif(file_exists("view/lang/en$engine/$s")) {
|
||||
$stamp1 = microtime(true);
|
||||
$content = file_get_contents("view/en$engine/$s");
|
||||
$content = file_get_contents("view/lang/en$engine/$s");
|
||||
$a->save_timestamp($stamp1, "file");
|
||||
return $content;
|
||||
} else {
|
||||
|
@ -683,6 +683,8 @@ if(! function_exists('logger')) {
|
|||
$LOGGER_LEVELS = array();
|
||||
|
||||
/**
|
||||
* @brief Logs the given message at the given log level
|
||||
*
|
||||
* log levels:
|
||||
* LOGGER_NORMAL (default)
|
||||
* LOGGER_TRACE
|
||||
|
@ -692,35 +694,48 @@ $LOGGER_LEVELS = array();
|
|||
*
|
||||
* @global App $a
|
||||
* @global dba $db
|
||||
* @global array $LOGGER_LEVELS
|
||||
* @param string $msg
|
||||
* @param int $level
|
||||
*/
|
||||
function logger($msg, $level = 0) {
|
||||
// turn off logger in install mode
|
||||
global $a;
|
||||
global $db;
|
||||
global $LOGGER_LEVELS;
|
||||
|
||||
if(($a->module == 'install') || (! ($db && $db->connected))) return;
|
||||
|
||||
if (count($LOGGER_LEVELS)==0){
|
||||
foreach (get_defined_constants() as $k=>$v){
|
||||
if (substr($k,0,7)=="LOGGER_")
|
||||
$LOGGER_LEVELS[$v] = substr($k,7,7);
|
||||
}
|
||||
// turn off logger in install mode
|
||||
if (
|
||||
$a->module == 'install'
|
||||
|| ! ($db && $db->connected)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
$debugging = get_config('system','debugging');
|
||||
$loglevel = intval(get_config('system','loglevel'));
|
||||
$logfile = get_config('system','logfile');
|
||||
$loglevel = intval(get_config('system','loglevel'));
|
||||
|
||||
if((! $debugging) || (! $logfile) || ($level > $loglevel))
|
||||
if (
|
||||
! $debugging
|
||||
|| ! $logfile
|
||||
|| $level > $loglevel
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($LOGGER_LEVELS) == 0) {
|
||||
foreach (get_defined_constants() as $k => $v) {
|
||||
if (substr($k, 0, 7) == "LOGGER_") {
|
||||
$LOGGER_LEVELS[$v] = substr($k, 7, 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$process_id = session_id();
|
||||
|
||||
if ($process_id == "")
|
||||
if ($process_id == '') {
|
||||
$process_id = get_app()->process_id;
|
||||
}
|
||||
|
||||
$callers = debug_backtrace();
|
||||
$logline = sprintf("%s@%s\t[%s]:%s:%s:%s\t%s\n",
|
||||
|
@ -736,7 +751,6 @@ function logger($msg,$level = 0) {
|
|||
$stamp1 = microtime(true);
|
||||
@file_put_contents($logfile, $logline, FILE_APPEND);
|
||||
$a->save_timestamp($stamp1, "file");
|
||||
return;
|
||||
}}
|
||||
|
||||
|
||||
|
@ -755,71 +769,75 @@ function activity_match($haystack,$needle) {
|
|||
}}
|
||||
|
||||
|
||||
if(! function_exists('get_tags')) {
|
||||
/**
|
||||
* Pull out all #hashtags and @person tags from $s;
|
||||
* @brief Pull out all #hashtags and @person tags from $string.
|
||||
*
|
||||
* We also get @person@domain.com - which would make
|
||||
* the regex quite complicated as tags can also
|
||||
* end a sentence. So we'll run through our results
|
||||
* and strip the period from any tags which end with one.
|
||||
* Returns array of tags found, or empty array.
|
||||
*
|
||||
* @param string $s
|
||||
* @return array
|
||||
* @param string $string Post content
|
||||
* @return array List of tag and person names
|
||||
*/
|
||||
function get_tags($s) {
|
||||
function get_tags($string) {
|
||||
$ret = array();
|
||||
|
||||
// Convert hashtag links to hashtags
|
||||
$s = preg_replace("/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism", "#$2", $s);
|
||||
$string = preg_replace('/#\[url\=([^\[\]]*)\](.*?)\[\/url\]/ism', '#$2', $string);
|
||||
|
||||
// ignore anything in a code block
|
||||
$s = preg_replace('/\[code\](.*?)\[\/code\]/sm','',$s);
|
||||
$string = preg_replace('/\[code\](.*?)\[\/code\]/sm', '', $string);
|
||||
|
||||
// Force line feeds at bbtags
|
||||
$s = str_replace(array("[", "]"), array("\n[", "]\n"), $s);
|
||||
$string = str_replace(array('[', ']'), array("\n[", "]\n"), $string);
|
||||
|
||||
// ignore anything in a bbtag
|
||||
$s = preg_replace('/\[(.*?)\]/sm','',$s);
|
||||
$string = preg_replace('/\[(.*?)\]/sm', '', $string);
|
||||
|
||||
// Match full names against @tags including the space between first and last
|
||||
// We will look these up afterward to see if they are full names or not recognisable.
|
||||
|
||||
if(preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/',$s,$match)) {
|
||||
foreach($match[1] as $mtch) {
|
||||
if(strstr($mtch,"]")) {
|
||||
if (preg_match_all('/(@[^ \x0D\x0A,:?]+ [^ \x0D\x0A@,:?]+)([ \x0D\x0A@,:?]|$)/', $string, $matches)) {
|
||||
foreach ($matches[1] as $match) {
|
||||
if (strstr($match, ']')) {
|
||||
// we might be inside a bbcode color tag - leave it alone
|
||||
continue;
|
||||
}
|
||||
if(substr($mtch,-1,1) === '.')
|
||||
$ret[] = substr($mtch,0,-1);
|
||||
else
|
||||
$ret[] = $mtch;
|
||||
if (substr($match, -1, 1) === '.') {
|
||||
$ret[] = substr($match, 0, -1);
|
||||
} else {
|
||||
$ret[] = $match;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise pull out single word tags. These can be @nickname, @first_last
|
||||
// and #hash tags.
|
||||
|
||||
if(preg_match_all('/([!#@][^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/',$s,$match)) {
|
||||
foreach($match[1] as $mtch) {
|
||||
if(strstr($mtch,"]")) {
|
||||
if (preg_match_all('/([!#@][^\^ \x0D\x0A,;:?]+)([ \x0D\x0A,;:?]|$)/', $string, $matches)) {
|
||||
foreach($matches[1] as $match) {
|
||||
if (strstr($match, ']')) {
|
||||
// we might be inside a bbcode color tag - leave it alone
|
||||
continue;
|
||||
}
|
||||
if(substr($mtch,-1,1) === '.')
|
||||
$mtch = substr($mtch,0,-1);
|
||||
if (substr($match, -1, 1) === '.') {
|
||||
$match = substr($match,0,-1);
|
||||
}
|
||||
// ignore strictly numeric tags like #1
|
||||
if((strpos($mtch,'#') === 0) && ctype_digit(substr($mtch,1)))
|
||||
if ((strpos($match, '#') === 0) && ctype_digit(substr($match, 1))) {
|
||||
continue;
|
||||
}
|
||||
// try not to catch url fragments
|
||||
if(strpos($s,$mtch) && preg_match('/[a-zA-z0-9\/]/',substr($s,strpos($s,$mtch)-1,1)))
|
||||
if (strpos($string, $match) && preg_match('/[a-zA-z0-9\/]/', substr($string, strpos($string, $match) - 1, 1))) {
|
||||
continue;
|
||||
$ret[] = $mtch;
|
||||
}
|
||||
$ret[] = $match;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
|
@ -856,8 +874,8 @@ function contact_block() {
|
|||
if((! is_array($a->profile)) || ($a->profile['hide-friends']))
|
||||
return $o;
|
||||
$r = q("SELECT COUNT(*) AS `total` FROM `contact`
|
||||
WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0
|
||||
AND `hidden` = 0 AND `archive` = 0
|
||||
WHERE `uid` = %d AND NOT `self` AND NOT `blocked`
|
||||
AND NOT `hidden` AND NOT `archive`
|
||||
AND `network` IN ('%s', '%s', '%s')",
|
||||
intval($a->profile['uid']),
|
||||
dbesc(NETWORK_DFRN),
|
||||
|
@ -874,7 +892,7 @@ function contact_block() {
|
|||
} else {
|
||||
// Splitting the query in two parts makes it much faster
|
||||
$r = q("SELECT `id` FROM `contact`
|
||||
WHERE `uid` = %d AND NOT `self` AND NOT `blocked` AND NOT `pending`
|
||||
WHERE `uid` = %d AND NOT `self` AND NOT `blocked`
|
||||
AND NOT `hidden` AND NOT `archive`
|
||||
AND `network` IN ('%s', '%s', '%s') ORDER BY RAND() LIMIT %d",
|
||||
intval($a->profile['uid']),
|
||||
|
@ -988,7 +1006,7 @@ function search($s,$id='search-box',$url='search',$save = false, $aside = true)
|
|||
$a = get_app();
|
||||
|
||||
$values = array(
|
||||
'$s' => $s,
|
||||
'$s' => htmlspecialchars($s),
|
||||
'$id' => $id,
|
||||
'$action_url' => $url,
|
||||
'$search_label' => t('Search'),
|
||||
|
@ -1152,33 +1170,29 @@ function link_compare($a,$b) {
|
|||
return false;
|
||||
}}
|
||||
|
||||
|
||||
if(! function_exists('redir_private_images')) {
|
||||
/**
|
||||
* Find any non-embedded images in private items and add redir links to them
|
||||
* @brief Find any non-embedded images in private items and add redir links to them
|
||||
*
|
||||
* @param App $a
|
||||
* @param array $item
|
||||
* @param array &$item The field array of an item row
|
||||
*/
|
||||
function redir_private_images($a, &$item) {
|
||||
|
||||
function redir_private_images($a, &$item)
|
||||
{
|
||||
$matches = false;
|
||||
$cnt = preg_match_all('|\[img\](http[^\[]*?/photo/[a-fA-F0-9]+?(-[0-9]\.[\w]+?)?)\[\/img\]|', $item['body'], $matches, PREG_SET_ORDER);
|
||||
if ($cnt) {
|
||||
//logger("redir_private_images: matches = " . print_r($matches, true));
|
||||
foreach ($matches as $mtch) {
|
||||
if(strpos($mtch[1], '/redir') !== false)
|
||||
if (strpos($mtch[1], '/redir') !== false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((local_user() == $item['uid']) && ($item['private'] != 0) && ($item['contact-id'] != $a->contact['id']) && ($item['network'] == NETWORK_DFRN)) {
|
||||
//logger("redir_private_images: redir");
|
||||
$img_url = 'redir?f=1&quiet=1&url=' . $mtch[1] . '&conurl=' . $item['author-link'];
|
||||
$item['body'] = str_replace($mtch[0], "[img]".$img_url."[/img]", $item['body']);
|
||||
$img_url = 'redir?f=1&quiet=1&url=' . urlencode($mtch[1]) . '&conurl=' . urlencode($item['author-link']);
|
||||
$item['body'] = str_replace($mtch[0], '[img]' . $img_url . '[/img]', $item['body']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
||||
|
||||
function put_item_in_cache(&$item, $update = false) {
|
||||
|
||||
|
|
|
@ -18,42 +18,70 @@ function add_thread($itemid, $onlyshadow = false) {
|
|||
.implode("', '", array_values($item))
|
||||
."')");
|
||||
|
||||
logger("add_thread: Add thread for item ".$itemid." - ".print_r($result, true), LOGGER_DEBUG);
|
||||
logger("Add thread for item ".$itemid." - ".print_r($result, true), LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
|
||||
// is it already a copy?
|
||||
if (($itemid == 0) OR ($item['uid'] == 0))
|
||||
/**
|
||||
* @brief Add a shadow entry for a given item id that is a thread starter
|
||||
*
|
||||
* We store every public item entry additionally with the user id "0".
|
||||
* This is used for the community page and for the search.
|
||||
* It is planned that in the future we will store public item entries only once.
|
||||
*
|
||||
* @param integer $itemid Item ID that should be added
|
||||
*/
|
||||
function add_shadow_thread($itemid) {
|
||||
$items = q("SELECT `uid`, `wall`, `private`, `moderated`, `visible`, `contact-id`, `deleted`, `network`
|
||||
FROM `item` WHERE `id` = %d AND (`parent` = %d OR `parent` = 0) LIMIT 1", intval($itemid), intval($itemid));
|
||||
|
||||
if (!dbm::is_result($items)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$item = $items[0];
|
||||
|
||||
// is it already a copy?
|
||||
if (($itemid == 0) OR ($item['uid'] == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Is it a visible public post?
|
||||
if (!$item["visible"] OR $item["deleted"] OR $item["moderated"] OR $item["private"])
|
||||
if (!$item["visible"] OR $item["deleted"] OR $item["moderated"] OR $item["private"]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// is it an entry from a connector? Only add an entry for natively connected networks
|
||||
if (!in_array($item["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, "")))
|
||||
if (!in_array($item["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, ""))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only do these checks if the post isn't a wall post
|
||||
if (!$item["wall"]) {
|
||||
// Check, if hide-friends is activated - then don't do a shadow entry
|
||||
$r = q("SELECT `hide-friends` FROM `profile` WHERE `is-default` AND `uid` = %d AND NOT `hide-friends`",
|
||||
$item['uid']);
|
||||
if (!count($r))
|
||||
|
||||
if (!dbm::is_result($r)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the contact is hidden or blocked
|
||||
$r = q("SELECT `id` FROM `contact` WHERE NOT `hidden` AND NOT `blocked` AND `id` = %d",
|
||||
$item['contact-id']);
|
||||
if (!count($r))
|
||||
|
||||
if (!dbm::is_result($r)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Only add a shadow, if the profile isn't hidden
|
||||
$r = q("SELECT `uid` FROM `user` where `uid` = %d AND NOT `hidewall`", $item['uid']);
|
||||
if (!count($r))
|
||||
if (!dbm::is_result($r)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$item = q("SELECT * FROM `item` WHERE `id` = %d",
|
||||
intval($itemid));
|
||||
$item = q("SELECT * FROM `item` WHERE `id` = %d", intval($itemid));
|
||||
|
||||
if (count($item) AND ($item[0]["allow_cid"] == '') AND ($item[0]["allow_gid"] == '') AND
|
||||
($item[0]["deny_cid"] == '') AND ($item[0]["deny_gid"] == '')) {
|
||||
|
@ -61,7 +89,7 @@ function add_thread($itemid, $onlyshadow = false) {
|
|||
$r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = 0 LIMIT 1",
|
||||
dbesc($item['uri']));
|
||||
|
||||
if (!$r) {
|
||||
if (!dbm::is_result($r)) {
|
||||
// Preparing public shadow (removing user specific data)
|
||||
require_once("include/items.php");
|
||||
require_once("include/Contact.php");
|
||||
|
@ -69,15 +97,44 @@ function add_thread($itemid, $onlyshadow = false) {
|
|||
unset($item[0]['id']);
|
||||
$item[0]['uid'] = 0;
|
||||
$item[0]['origin'] = 0;
|
||||
$item[0]['wall'] = 0;
|
||||
$item[0]['contact-id'] = get_contact($item[0]['author-link'], 0);
|
||||
|
||||
if (in_array($item[0]['type'], array("net-comment", "wall-comment"))) {
|
||||
$item[0]['type'] = 'remote-comment';
|
||||
} elseif ($item[0]['type'] == 'wall') {
|
||||
$item[0]['type'] = 'remote';
|
||||
}
|
||||
|
||||
$public_shadow = item_store($item[0], false, false, true);
|
||||
|
||||
logger("add_thread: Stored public shadow for post ".$itemid." under id ".$public_shadow, LOGGER_DEBUG);
|
||||
logger("Stored public shadow for thread ".$itemid." under id ".$public_shadow, LOGGER_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function add_shadow_entry($item) {
|
||||
/**
|
||||
* @brief Add a shadow entry for a given item id that is a comment
|
||||
*
|
||||
* This function does the same like the function above - but for comments
|
||||
*
|
||||
* @param integer $itemid Item ID that should be added
|
||||
*/
|
||||
function add_shadow_entry($itemid) {
|
||||
|
||||
$items = q("SELECT * FROM `item` WHERE `id` = %d", intval($itemid));
|
||||
|
||||
if (!dbm::is_result($items)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$item = $items[0];
|
||||
|
||||
// Is it a toplevel post?
|
||||
if ($item['id'] == $item['parent']) {
|
||||
add_shadow_thread($itemid);
|
||||
return;
|
||||
}
|
||||
|
||||
// Is this a shadow entry?
|
||||
if ($item['uid'] == 0)
|
||||
|
@ -99,7 +156,16 @@ function add_shadow_entry($item) {
|
|||
|
||||
unset($item['id']);
|
||||
$item['uid'] = 0;
|
||||
$item['origin'] = 0;
|
||||
$item['wall'] = 0;
|
||||
$item['contact-id'] = get_contact($item['author-link'], 0);
|
||||
|
||||
if (in_array($item['type'], array("net-comment", "wall-comment"))) {
|
||||
$item['type'] = 'remote-comment';
|
||||
} elseif ($item['type'] == 'wall') {
|
||||
$item['type'] = 'remote';
|
||||
}
|
||||
|
||||
$public_shadow = item_store($item, false, false, true);
|
||||
|
||||
logger("Stored public shadow for comment ".$item['uri']." under id ".$public_shadow, LOGGER_DEBUG);
|
||||
|
@ -193,8 +259,10 @@ function update_threads() {
|
|||
|
||||
logger("update_threads: fetched messages: ".count($messages));
|
||||
|
||||
while ($message = $db->qfetch())
|
||||
while ($message = $db->qfetch()) {
|
||||
add_thread($message["id"]);
|
||||
add_shadow_thread($message["id"]);
|
||||
}
|
||||
$db->qclose();
|
||||
}
|
||||
|
||||
|
@ -227,7 +295,7 @@ function update_shadow_copy() {
|
|||
|
||||
logger("fetched messages: ".count($messages));
|
||||
while ($message = $db->qfetch())
|
||||
add_thread($message["iid"], true);
|
||||
add_shadow_thread($message["iid"]);
|
||||
|
||||
$db->qclose();
|
||||
}
|
||||
|
|
|
@ -378,6 +378,29 @@ function create_user($arr) {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief send registration confiŕmation with the intormation that reg is pending
|
||||
*
|
||||
* @param string $email
|
||||
* @param string $sitename
|
||||
* @param string $username
|
||||
* @return NULL|boolean from notification() and email() inherited
|
||||
*/
|
||||
function send_register_pending_eml($email, $sitename, $username) {
|
||||
$body = deindent(t('
|
||||
Dear %1$s,
|
||||
Thank you for registering at %2$s. Your account is pending for approval by the administrator.
|
||||
'));
|
||||
|
||||
$body = sprintf($body, $username, $sitename);
|
||||
|
||||
return notification(array(
|
||||
'type' => "SYSTEM_EMAIL",
|
||||
'to_email' => $email,
|
||||
'subject'=> sprintf( t('Registration at %s'), $sitename),
|
||||
'body' => $body));
|
||||
}
|
||||
|
||||
/*
|
||||
* send registration confirmation.
|
||||
* It's here as a function because the mail is sent
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file include/xml.php
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @brief This class contain functions to work with XML data
|
||||
* @brief This class contain methods to work with XML data
|
||||
*
|
||||
*/
|
||||
class xml {
|
||||
|
@ -24,14 +25,16 @@ class xml {
|
|||
|
||||
if ($root) {
|
||||
foreach ($array as $key => $value) {
|
||||
foreach ($namespaces AS $nskey => $nsvalue)
|
||||
foreach ($namespaces AS $nskey => $nsvalue) {
|
||||
$key .= " xmlns".($nskey == "" ? "":":").$nskey.'="'.$nsvalue.'"';
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$root = new SimpleXMLElement("<".$key."/>");
|
||||
self::from_array($value, $root, $remove_header, $namespaces, false);
|
||||
} else
|
||||
} else {
|
||||
$root = new SimpleXMLElement("<".$key.">".xmlify($value)."</".$key.">");
|
||||
}
|
||||
|
||||
$dom = dom_import_simplexml($root)->ownerDocument;
|
||||
$dom->formatOutput = true;
|
||||
|
@ -39,16 +42,18 @@ class xml {
|
|||
|
||||
$xml_text = $dom->saveXML();
|
||||
|
||||
if ($remove_header)
|
||||
if ($remove_header) {
|
||||
$xml_text = trim(substr($xml_text, 21));
|
||||
}
|
||||
|
||||
return $xml_text;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($array as $key => $value) {
|
||||
if (!isset($element) AND isset($xml))
|
||||
if (!isset($element) AND isset($xml)) {
|
||||
$element = $xml;
|
||||
}
|
||||
|
||||
if (is_integer($key)) {
|
||||
if (isset($element)) {
|
||||
|
@ -62,27 +67,31 @@ class xml {
|
|||
}
|
||||
|
||||
$element_parts = explode(":", $key);
|
||||
if ((count($element_parts) > 1) AND isset($namespaces[$element_parts[0]]))
|
||||
if ((count($element_parts) > 1) AND isset($namespaces[$element_parts[0]])) {
|
||||
$namespace = $namespaces[$element_parts[0]];
|
||||
elseif (isset($namespaces[""])) {
|
||||
} elseif (isset($namespaces[""])) {
|
||||
$namespace = $namespaces[""];
|
||||
} else
|
||||
} else {
|
||||
$namespace = NULL;
|
||||
}
|
||||
|
||||
// Remove undefined namespaces from the key
|
||||
if ((count($element_parts) > 1) AND is_null($namespace))
|
||||
if ((count($element_parts) > 1) AND is_null($namespace)) {
|
||||
$key = $element_parts[1];
|
||||
}
|
||||
|
||||
if (substr($key, 0, 11) == "@attributes") {
|
||||
if (!isset($element) OR !is_array($value))
|
||||
if (!isset($element) OR !is_array($value)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($value as $attr_key => $attr_value) {
|
||||
$element_parts = explode(":", $attr_key);
|
||||
if ((count($element_parts) > 1) AND isset($namespaces[$element_parts[0]]))
|
||||
if ((count($element_parts) > 1) AND isset($namespaces[$element_parts[0]])) {
|
||||
$namespace = $namespaces[$element_parts[0]];
|
||||
else
|
||||
} else {
|
||||
$namespace = NULL;
|
||||
}
|
||||
|
||||
$element->addAttribute($attr_key, $attr_value, $namespace);
|
||||
}
|
||||
|
@ -90,9 +99,9 @@ class xml {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!is_array($value))
|
||||
if (!is_array($value)) {
|
||||
$element = $xml->addChild($key, xmlify($value), $namespace);
|
||||
elseif (is_array($value)) {
|
||||
} elseif (is_array($value)) {
|
||||
$element = $xml->addChild($key, NULL, $namespace);
|
||||
self::from_array($value, $element, $remove_header, $namespaces, false);
|
||||
}
|
||||
|
@ -111,10 +120,11 @@ class xml {
|
|||
$target->addChild($elementname, xmlify($source));
|
||||
else {
|
||||
$child = $target->addChild($elementname);
|
||||
foreach ($source->children() AS $childfield => $childentry)
|
||||
foreach ($source->children() AS $childfield => $childentry) {
|
||||
self::copy($childentry, $child, $childfield);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create an XML element
|
||||
|
@ -168,9 +178,9 @@ class xml {
|
|||
return(null);
|
||||
}
|
||||
|
||||
if (!is_string($xml_element) &&
|
||||
!is_array($xml_element) &&
|
||||
(get_class($xml_element) == 'SimpleXMLElement')) {
|
||||
if (!is_string($xml_element)
|
||||
&& !is_array($xml_element)
|
||||
&& (get_class($xml_element) == 'SimpleXMLElement')) {
|
||||
$xml_element_copy = $xml_element;
|
||||
$xml_element = get_object_vars($xml_element);
|
||||
}
|
||||
|
@ -224,7 +234,9 @@ class xml {
|
|||
* @return array The parsed XML in an array form. Use print_r() to see the resulting array structure.
|
||||
*/
|
||||
public static function to_array($contents, $namespaces = true, $get_attributes = 1, $priority = 'attribute') {
|
||||
if(!$contents) return array();
|
||||
if (!$contents) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if (!function_exists('xml_parser_create')) {
|
||||
logger('xml::to_array: parser function missing');
|
||||
|
@ -235,10 +247,11 @@ class xml {
|
|||
libxml_use_internal_errors(true);
|
||||
libxml_clear_errors();
|
||||
|
||||
if($namespaces)
|
||||
if ($namespaces) {
|
||||
$parser = @xml_parser_create_ns("UTF-8",':');
|
||||
else
|
||||
} else {
|
||||
$parser = @xml_parser_create();
|
||||
}
|
||||
|
||||
if (! $parser) {
|
||||
logger('xml::to_array: xml_parser_create: no resource');
|
||||
|
@ -254,8 +267,9 @@ class xml {
|
|||
|
||||
if (! $xml_values) {
|
||||
logger('xml::to_array: libxml: parse error: ' . $contents, LOGGER_DATA);
|
||||
foreach(libxml_get_errors() as $err)
|
||||
foreach (libxml_get_errors() as $err) {
|
||||
logger('libxml: parse: ' . $err->code . " at " . $err->line . ":" . $err->column . " : " . $err->message, LOGGER_DATA);
|
||||
}
|
||||
libxml_clear_errors();
|
||||
return;
|
||||
}
|
||||
|
@ -281,15 +295,21 @@ class xml {
|
|||
$attributes_data = array();
|
||||
|
||||
if (isset($value)) {
|
||||
if($priority == 'tag') $result = $value;
|
||||
else $result['value'] = $value; // Put the value in a assoc array if we are in the 'Attribute' mode
|
||||
if ($priority == 'tag') {
|
||||
$result = $value;
|
||||
} else {
|
||||
$result['value'] = $value; // Put the value in a assoc array if we are in the 'Attribute' mode
|
||||
}
|
||||
}
|
||||
|
||||
//Set the attributes too.
|
||||
if (isset($attributes) and $get_attributes) {
|
||||
foreach ($attributes as $attr => $val) {
|
||||
if($priority == 'tag') $attributes_data[$attr] = $val;
|
||||
else $result['@attributes'][$attr] = $val; // Set all the attributes in a array called 'attr'
|
||||
if($priority == 'tag') {
|
||||
$attributes_data[$attr] = $val;
|
||||
} else {
|
||||
$result['@attributes'][$attr] = $val; // Set all the attributes in a array called 'attr'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,7 +325,9 @@ class xml {
|
|||
$parent[$level-1] = &$current;
|
||||
if (!is_array($current) or (!in_array($tag, array_keys($current)))) { // Insert New tag
|
||||
$current[$tag] = $result;
|
||||
if($attributes_data) $current[$tag. '_attr'] = $attributes_data;
|
||||
if ($attributes_data) {
|
||||
$current[$tag. '_attr'] = $attributes_data;
|
||||
}
|
||||
$repeated_tag_index[$tag.'_'.$level] = 1;
|
||||
|
||||
$current = &$current[$tag];
|
||||
|
@ -334,7 +356,9 @@ class xml {
|
|||
if (!isset($current[$tag])) { //New Key
|
||||
$current[$tag] = $result;
|
||||
$repeated_tag_index[$tag.'_'.$level] = 1;
|
||||
if($priority == 'tag' and $attributes_data) $current[$tag. '_attr'] = $attributes_data;
|
||||
if ($priority == 'tag' and $attributes_data) {
|
||||
$current[$tag. '_attr'] = $attributes_data;
|
||||
}
|
||||
|
||||
} else { // If taken, put all things inside a list(array)
|
||||
if (isset($current[$tag][0]) and is_array($current[$tag])) { // If it is already an array...
|
||||
|
@ -372,5 +396,18 @@ class xml {
|
|||
|
||||
return($xml_array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Delete a node in a XML object
|
||||
*
|
||||
* @param object $doc XML document
|
||||
* @param string $node Node name
|
||||
*/
|
||||
public static function deleteNode(&$doc, $node) {
|
||||
$xpath = new DomXPath($doc);
|
||||
$list = $xpath->query("//".$node);
|
||||
foreach ($list as $child) {
|
||||
$child->parentNode->removeChild($child);
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
|
|
@ -99,6 +99,10 @@ if (!$a->is_backend()) {
|
|||
$stamp1 = microtime(true);
|
||||
session_start();
|
||||
$a->save_timestamp($stamp1, "parser");
|
||||
} else {
|
||||
require_once "include/poller.php";
|
||||
|
||||
call_worker_if_idle();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -296,7 +296,7 @@ function string2bb(element) {
|
|||
$.fn.bbco_autocomplete = function(type) {
|
||||
|
||||
if(type=='bbcode') {
|
||||
var open_close_elements = ['bold', 'italic', 'underline', 'overline', 'strike', 'quote', 'code', 'spoiler', 'map', 'img', 'url', 'audio', 'video', 'youtube', 'vimeo', 'list', 'ul', 'ol', 'li', 'table', 'tr', 'th', 'td', 'center', 'color', 'font', 'size', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'nobb', 'noparse', 'pre', 'abstract'];
|
||||
var open_close_elements = ['bold', 'italic', 'underline', 'overline', 'strike', 'quote', 'code', 'spoiler', 'map', 'img', 'url', 'audio', 'video', 'embed', 'youtube', 'vimeo', 'list', 'ul', 'ol', 'li', 'table', 'tr', 'th', 'td', 'center', 'color', 'font', 'size', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'nobb', 'noparse', 'pre', 'abstract'];
|
||||
var open_elements = ['*', 'hr'];
|
||||
|
||||
var elements = open_close_elements.concat(open_elements);
|
||||
|
|
|
@ -44,7 +44,7 @@ aStates[20]="|Brestskaya (Brest)|Homyel'skaya (Homyel')|Horad Minsk|Hrodzyenskay
|
|||
aStates[21]="|Antwerpen|Brabant Wallon|Brussels Capitol Region|Hainaut|Liege|Limburg|Luxembourg|Namur|Oost-Vlaanderen|Vlaams Brabant|West-Vlaanderen";
|
||||
aStates[22]="|Belize|Cayo|Corozal|Orange Walk|Stann Creek|Toledo";
|
||||
aStates[23]="|Alibori|Atakora|Atlantique|Borgou|Collines|Couffo|Donga|Littoral|Mono|Oueme|Plateau|Zou";
|
||||
aStates[24]="|Devonshire|Hamilton|Hamilton|Paget|Pembroke|Saint George|Saint Georges|Sandys|Smiths|Southampton|Warwick";
|
||||
aStates[24]="|Devonshire|Hamilton (City)|Hamilton|Paget|Pembroke|Saint George|Saint Georges|Sandys|Smiths|Southampton|Warwick";
|
||||
aStates[25]="|Bumthang|Chhukha|Chirang|Daga|Geylegphug|Ha|Lhuntshi|Mongar|Paro|Pemagatsel|Punakha|Samchi|Samdrup Jongkhar|Shemgang|Tashigang|Thimphu|Tongsa|Wangdi Phodrang";
|
||||
aStates[26]="|Beni|Chuquisaca|Cochabamba|La Paz|Oruro|Pando|Potosi|Santa Cruz|Tarija";
|
||||
aStates[27]="|Federation of Bosnia and Herzegovina|Republika Srpska";
|
||||
|
@ -125,7 +125,7 @@ aStates[99]="|Holy See (Vatican City)"
|
|||
aStates[100]="|Atlantida|Choluteca|Colon|Comayagua|Copan|Cortes|El Paraiso|Francisco Morazan|Gracias a Dios|Intibuca|Islas de la Bahia|La Paz|Lempira|Ocotepeque|Olancho|Santa Barbara|Valle|Yoro";
|
||||
aStates[101]="|Hong Kong";
|
||||
aStates[102]="|Howland Island";
|
||||
aStates[103]="|Bacs-Kiskun|Baranya|Bekes|Bekescsaba|Borsod-Abauj-Zemplen|Budapest|Csongrad|Debrecen|Dunaujvaros|Eger|Fejer|Gyor|Gyor-Moson-Sopron|Hajdu-Bihar|Heves|Hodmezovasarhely|Jasz-Nagykun-Szolnok|Kaposvar|Kecskemet|Komarom-Esztergom|Miskolc|Nagykanizsa|Nograd|Nyiregyhaza|Pecs|Pest|Somogy|Sopron|Szabolcs-Szatmar-Bereg|Szeged|Szekesfehervar|Szolnok|Szombathely|Tatabanya|Tolna|Vas|Veszprem|Veszprem|Zala|Zalaegerszeg";
|
||||
aStates[103]="|Bacs-Kiskun|Baranya|Bekes|Bekescsaba|Borsod-Abauj-Zemplen|Budapest|Csongrad|Debrecen|Dunaujvaros|Eger|Fejer|Gyor|Gyor-Moson-Sopron|Hajdu-Bihar|Heves|Hodmezovasarhely|Jasz-Nagykun-Szolnok|Kaposvar|Kecskemet|Komarom-Esztergom|Miskolc|Nagykanizsa|Nograd|Nyiregyhaza|Pecs|Pest|Somogy|Sopron|Szabolcs-Szatmar-Bereg|Szeged|Szekesfehervar|Szolnok|Szombathely|Tatabanya|Tolna|Vas|Veszprem|Veszprem (City)|Zala|Zalaegerszeg";
|
||||
aStates[104]="|Akranes|Akureyri|Arnessysla|Austur-Bardhastrandarsysla|Austur-Hunavatnssysla|Austur-Skaftafellssysla|Borgarfjardharsysla|Dalasysla|Eyjafjardharsysla|Gullbringusysla|Hafnarfjordhur|Husavik|Isafjordhur|Keflavik|Kjosarsysla|Kopavogur|Myrasysla|Neskaupstadhur|Nordhur-Isafjardharsysla|Nordhur-Mulasys-la|Nordhur-Thingeyjarsysla|Olafsfjordhur|Rangarvallasysla|Reykjavik|Saudharkrokur|Seydhisfjordhur|Siglufjordhur|Skagafjardharsysla|Snaefellsnes-og Hnappadalssysla|Strandasysla|Sudhur-Mulasysla|Sudhur-Thingeyjarsysla|Vesttmannaeyjar|Vestur-Bardhastrandarsysla|Vestur-Hunavatnssysla|Vestur-Isafjardharsysla|Vestur-Skaftafellssysla";
|
||||
aStates[105]="|Andaman and Nicobar Islands|Andhra Pradesh|Arunachal Pradesh|Assam|Bihar|Chandigarh|Chhattisgarh|Dadra and Nagar Haveli|Daman and Diu|Delhi|Goa|Gujarat|Haryana|Himachal Pradesh|Jammu and Kashmir|Jharkhand|Karnataka|Kerala|Lakshadweep|Madhya Pradesh|Maharashtra|Manipur|Meghalaya|Mizoram|Nagaland|Orissa|Pondicherry|Punjab|Rajasthan|Sikkim|Tamil Nadu|Tripura|Uttar Pradesh|Uttaranchal|West Bengal";
|
||||
aStates[106]="|Aceh|Bali|Banten|Bengkulu|East Timor|Gorontalo|Irian Jaya|Jakarta Raya|Jambi|Jawa Barat|Jawa Tengah|Jawa Timur|Kalimantan Barat|Kalimantan Selatan|Kalimantan Tengah|Kalimantan Timur|Kepulauan Bangka Belitung|Lampung|Maluku|Maluku Utara|Nusa Tenggara Barat|Nusa Tenggara Timur|Riau|Sulawesi Selatan|Sulawesi Tengah|Sulawesi Tenggara|Sulawesi Utara|Sumatera Barat|Sumatera Selatan|Sumatera Utara|Yogyakarta";
|
||||
|
@ -145,12 +145,12 @@ aStates[119]="|'Amman|Ajlun|Al 'Aqabah|Al Balqa'|Al Karak|Al Mafraq|At Tafilah|A
|
|||
aStates[120]="|Juan de Nova Island";
|
||||
aStates[121]="|Almaty|Aqmola|Aqtobe|Astana|Atyrau|Batys Qazaqstan|Bayqongyr|Mangghystau|Ongtustik Qazaqstan|Pavlodar|Qaraghandy|Qostanay|Qyzylorda|Shyghys Qazaqstan|Soltustik Qazaqstan|Zhambyl";
|
||||
aStates[122]="|Central|Coast|Eastern|Nairobi Area|North Eastern|Nyanza|Rift Valley|Western";
|
||||
aStates[123]="|Abaiang|Abemama|Aranuka|Arorae|Banaba|Banaba|Beru|Butaritari|Central Gilberts|Gilbert Islands|Kanton|Kiritimati|Kuria|Line Islands|Line Islands|Maiana|Makin|Marakei|Nikunau|Nonouti|Northern Gilberts|Onotoa|Phoenix Islands|Southern Gilberts|Tabiteuea|Tabuaeran|Tamana|Tarawa|Tarawa|Teraina";
|
||||
aStates[123]="|Abaiang|Abemama|Aranuka|Arorae|Banaba (District)|Banaba|Beru|Butaritari|Central Gilberts (District)|Gilbert Islands (Unit)|Kanton|Kiritimati|Kuria|Line Islands (District)|Line Islands (Unit)|Maiana|Makin|Marakei|Nikunau|Nonouti|Northern Gilberts (District)|Onotoa|Phoenix Islands (Unit)|Southern Gilberts (District)|Tabiteuea|Tabuaeran|Tamana|Tarawa (District)|Tarawa|Teraina";
|
||||
aStates[124]="|Chagang-do (Chagang Province)|Hamgyong-bukto (North Hamgyong Province)|Hamgyong-namdo (South Hamgyong Province)|Hwanghae-bukto (North Hwanghae Province)|Hwanghae-namdo (South Hwanghae Province)|Kaesong-si (Kaesong City)|Kangwon-do (Kangwon Province)|Namp'o-si (Namp'o City)|P'yongan-bukto (North P'yongan Province)|P'yongan-namdo (South P'yongan Province)|P'yongyang-si (P'yongyang City)|Yanggang-do (Yanggang Province)"
|
||||
aStates[125]="|Ch'ungch'ong-bukto|Ch'ungch'ong-namdo|Cheju-do|Cholla-bukto|Cholla-namdo|Inch'on-gwangyoksi|Kangwon-do|Kwangju-gwangyoksi|Kyonggi-do|Kyongsang-bukto|Kyongsang-namdo|Pusan-gwangyoksi|Soul-t'ukpyolsi|Taegu-gwangyoksi|Taejon-gwangyoksi|Ulsan-gwangyoksi";
|
||||
aStates[126]="|Al 'Asimah|Al Ahmadi|Al Farwaniyah|Al Jahra'|Hawalli";
|
||||
aStates[127]="|Batken Oblasty|Bishkek Shaary|Chuy Oblasty (Bishkek)|Jalal-Abad Oblasty|Naryn Oblasty|Osh Oblasty|Talas Oblasty|Ysyk-Kol Oblasty (Karakol)"
|
||||
aStates[128]="|Attapu|Bokeo|Bolikhamxai|Champasak|Houaphan|Khammouan|Louangnamtha|Louangphabang|Oudomxai|Phongsali|Salavan|Savannakhet|Viangchan|Viangchan|Xaignabouli|Xaisomboun|Xekong|Xiangkhoang";
|
||||
aStates[128]="|Attapu|Bokeo|Bolikhamxai|Champasak|Houaphan|Khammouan|Louangnamtha|Louangphabang|Oudomxai|Phongsali|Salavan|Savannakhet|Viangchan City|Viangchan|Xaignabouli|Xaisomboun|Xekong|Xiangkhoang";
|
||||
aStates[129]="|Aizkraukles Rajons|Aluksnes Rajons|Balvu Rajons|Bauskas Rajons|Cesu Rajons|Daugavpils|Daugavpils Rajons|Dobeles Rajons|Gulbenes Rajons|Jekabpils Rajons|Jelgava|Jelgavas Rajons|Jurmala|Kraslavas Rajons|Kuldigas Rajons|Leipaja|Liepajas Rajons|Limbazu Rajons|Ludzas Rajons|Madonas Rajons|Ogres Rajons|Preilu Rajons|Rezekne|Rezeknes Rajons|Riga|Rigas Rajons|Saldus Rajons|Talsu Rajons|Tukuma Rajons|Valkas Rajons|Valmieras Rajons|Ventspils|Ventspils Rajons";
|
||||
aStates[130]="|Beyrouth|Ech Chimal|Ej Jnoub|El Bekaa|Jabal Loubnane";
|
||||
aStates[131]="|Berea|Butha-Buthe|Leribe|Mafeteng|Maseru|Mohales Hoek|Mokhotlong|Qacha's Nek|Quthing|Thaba-Tseka";
|
||||
|
@ -176,7 +176,7 @@ aStates[150]="|Mayotte";
|
|||
aStates[151]="|Aguascalientes|Baja California|Baja California Sur|Campeche|Chiapas|Chihuahua|Coahuila de Zaragoza|Colima|Distrito Federal|Durango|Guanajuato|Guerrero|Hidalgo|Jalisco|Mexico|Michoacan de Ocampo|Morelos|Nayarit|Nuevo Leon|Oaxaca|Puebla|Queretaro de Arteaga|Quintana Roo|San Luis Potosi|Sinaloa|Sonora|Tabasco|Tamaulipas|Tlaxcala|Veracruz-Llave|Yucatan|Zacatecas";
|
||||
aStates[152]="|Chuuk (Truk)|Kosrae|Pohnpei|Yap";
|
||||
aStates[153]="|Midway Islands";
|
||||
aStates[154]="|Balti|Cahul|Chisinau|Chisinau|Dubasari|Edinet|Gagauzia|Lapusna|Orhei|Soroca|Tighina|Ungheni";
|
||||
aStates[154]="|Balti|Cahul|Chisinau (City)|Chisinau|Dubasari|Edinet|Gagauzia|Lapusna|Orhei|Soroca|Tighina|Ungheni";
|
||||
aStates[155]="|Fontvieille|La Condamine|Monaco-Ville|Monte-Carlo";
|
||||
aStates[156]="|Arhangay|Bayan-Olgiy|Bayanhongor|Bulgan|Darhan|Dornod|Dornogovi|Dundgovi|Dzavhan|Erdenet|Govi-Altay|Hentiy|Hovd|Hovsgol|Omnogovi|Ovorhangay|Selenge|Suhbaatar|Tov|Ulaanbaatar|Uvs";
|
||||
aStates[157]="|Saint Anthony|Saint Georges|Saint Peter's";
|
||||
|
@ -243,7 +243,7 @@ aStates[217]="|Hhohho|Lubombo|Manzini|Shiselweni";
|
|||
aStates[218]="|Blekinge|Dalarnas|Gavleborgs|Gotlands|Hallands|Jamtlands|Jonkopings|Kalmar|Kronobergs|Norrbottens|Orebro|Ostergotlands|Skane|Sodermanlands|Stockholms|Uppsala|Varmlands|Vasterbottens|Vasternorrlands|Vastmanlands|Vastra Gotalands";
|
||||
aStates[219]="|Aargau|Ausser-Rhoden|Basel-Landschaft|Basel-Stadt|Bern|Fribourg|Geneve|Glarus|Graubunden|Inner-Rhoden|Jura|Luzern|Neuchatel|Nidwalden|Obwalden|Sankt Gallen|Schaffhausen|Schwyz|Solothurn|Thurgau|Ticino|Uri|Valais|Vaud|Zug|Zurich";
|
||||
aStates[220]="|Al Hasakah|Al Ladhiqiyah|Al Qunaytirah|Ar Raqqah|As Suwayda'|Dar'a|Dayr az Zawr|Dimashq|Halab|Hamah|Hims|Idlib|Rif Dimashq|Tartus";
|
||||
aStates[221]="|Chang-hua|Chi-lung|Chia-i|Chia-i|Chung-hsing-hsin-ts'un|Hsin-chu|Hsin-chu|Hua-lien|I-lan|Kao-hsiung|Kao-hsiung|Miao-li|Nan-t'ou|P'eng-hu|P'ing-tung|T'ai-chung|T'ai-chung|T'ai-nan|T'ai-nan|T'ai-pei|T'ai-pei|T'ai-tung|T'ao-yuan|Yun-lin";
|
||||
aStates[221]="|Chang-hua|Chi-lung|Chia-i (City)|Chia-i|Chung-hsing-hsin-ts'un|Hsin-chu (City)|Hsin-chu|Hua-lien|I-lan|Kao-hsiung (City)|Kao-hsiung|Miao-li|Nan-t'ou|P'eng-hu|P'ing-tung|T'ai-chung (City)|T'ai-chung|T'ai-nan (City)|T'ai-nan|T'ai-pei (City)|T'ai-pei|T'ai-tung|T'ao-yuan|Yun-lin";
|
||||
aStates[222]="|Viloyati Khatlon|Viloyati Leninobod|Viloyati Mukhtori Kuhistoni Badakhshon";
|
||||
aStates[223]="|Arusha|Dar es Salaam|Dodoma|Iringa|Kagera|Kigoma|Kilimanjaro|Lindi|Mara|Mbeya|Morogoro|Mtwara|Mwanza|Pemba North|Pemba South|Pwani|Rukwa|Ruvuma|Shinyanga|Singida|Tabora|Tanga|Zanzibar Central/South|Zanzibar North|Zanzibar Urban/West";
|
||||
aStates[224]="|Amnat Charoen|Ang Thong|Buriram|Chachoengsao|Chai Nat|Chaiyaphum|Chanthaburi|Chiang Mai|Chiang Rai|Chon Buri|Chumphon|Kalasin|Kamphaeng Phet|Kanchanaburi|Khon Kaen|Krabi|Krung Thep Mahanakhon (Bangkok)|Lampang|Lamphun|Loei|Lop Buri|Mae Hong Son|Maha Sarakham|Mukdahan|Nakhon Nayok|Nakhon Pathom|Nakhon Phanom|Nakhon Ratchasima|Nakhon Sawan|Nakhon Si Thammarat|Nan|Narathiwat|Nong Bua Lamphu|Nong Khai|Nonthaburi|Pathum Thani|Pattani|Phangnga|Phatthalung|Phayao|Phetchabun|Phetchaburi|Phichit|Phitsanulok|Phra Nakhon Si Ayutthaya|Phrae|Phuket|Prachin Buri|Prachuap Khiri Khan|Ranong|Ratchaburi|Rayong|Roi Et|Sa Kaeo|Sakon Nakhon|Samut Prakan|Samut Sakhon|Samut Songkhram|Sara Buri|Satun|Sing Buri|Sisaket|Songkhla|Sukhothai|Suphan Buri|Surat Thani|Surin|Tak|Trang|Trat|Ubon Ratchathani|Udon Thani|Uthai Thani|Uttaradit|Yala|Yasothon";
|
||||
|
|
184
js/main.js
184
js/main.js
|
@ -5,17 +5,14 @@
|
|||
|
||||
function _resizeIframe(obj, desth) {
|
||||
var h = obj.style.height;
|
||||
var ch = obj.contentWindow.document.body.scrollHeight + 'px';
|
||||
if (h==ch) {
|
||||
var ch = obj.contentWindow.document.body.scrollHeight;
|
||||
if (h == (ch + 'px')) {
|
||||
return;
|
||||
}
|
||||
//console.log("_resizeIframe", obj, desth, ch);
|
||||
if (desth!=ch) {
|
||||
setTimeout(_resizeIframe, 500, obj, ch);
|
||||
} else {
|
||||
if (ch>0) obj.style.height = ch;
|
||||
setTimeout(_resizeIframe, 1000, obj, ch);
|
||||
if (desth == ch && ch>0) {
|
||||
obj.style.height = ch + 'px';
|
||||
}
|
||||
setTimeout(_resizeIframe, 100, obj, ch);
|
||||
}
|
||||
|
||||
function openClose(theID) {
|
||||
|
@ -35,6 +32,11 @@
|
|||
document.getElementById(theID).style.display = "none"
|
||||
}
|
||||
|
||||
function decodeHtml(html) {
|
||||
var txt = document.createElement("textarea");
|
||||
txt.innerHTML = html;
|
||||
return txt.value;
|
||||
}
|
||||
|
||||
|
||||
var src = null;
|
||||
|
@ -90,7 +92,6 @@
|
|||
/* event from comment textarea button popups */
|
||||
/* insert returned bbcode at cursor position or replace selected text */
|
||||
$("body").on("fbrowser.image.comment", function(e, filename, bbcode, id) {
|
||||
console.log("on", id);
|
||||
$.colorbox.close();
|
||||
var textarea = document.getElementById("comment-edit-text-" +id);
|
||||
var start = textarea.selectionStart;
|
||||
|
@ -115,7 +116,6 @@
|
|||
$("#"+id+"_onoff ."+ (val==0?"on":"off")).addClass("hidden");
|
||||
$("#"+id+"_onoff ."+ (val==1?"on":"off")).removeClass("hidden");
|
||||
input.val(val);
|
||||
//console.log(id);
|
||||
});
|
||||
|
||||
/* setup field_richtext */
|
||||
|
@ -125,6 +125,7 @@
|
|||
function close_last_popup_menu() {
|
||||
if(last_popup_menu) {
|
||||
last_popup_menu.hide();
|
||||
last_popup_menu.off('click', function(e) {e.stopPropagation()});
|
||||
last_popup_button.removeClass("selected");
|
||||
last_popup_menu = null;
|
||||
last_popup_button = null;
|
||||
|
@ -147,6 +148,7 @@
|
|||
last_popup_button = null;
|
||||
} else {
|
||||
last_popup_menu = menu;
|
||||
last_popup_menu.on('click', function(e) {e.stopPropagation()});
|
||||
last_popup_button = parent;
|
||||
$('#nav-notifications-menu').perfectScrollbar('update');
|
||||
}
|
||||
|
@ -176,109 +178,76 @@
|
|||
|
||||
/* nav update event */
|
||||
$('nav').bind('nav-update', function(e, data){
|
||||
var invalid = $(data).find('invalid').text();
|
||||
var invalid = data.invalid || 0;
|
||||
if(invalid == 1) { window.location.href=window.location.href }
|
||||
|
||||
var net = $(data).find('net').text();
|
||||
if(net == 0) { net = ''; $('#net-update').removeClass('show') } else { $('#net-update').addClass('show') }
|
||||
$('#net-update').html(net);
|
||||
['net', 'home', 'intro', 'mail', 'events', 'birthdays', 'notify'].forEach(function(type) {
|
||||
var number = data[type];
|
||||
if (number == 0) {
|
||||
number = '';
|
||||
$('#' + type + '-update').removeClass('show');
|
||||
} else {
|
||||
$('#' + type + '-update').addClass('show');
|
||||
}
|
||||
$('#' + type + '-update').text(number);
|
||||
});
|
||||
|
||||
var home = $(data).find('home').text();
|
||||
if(home == 0) { home = ''; $('#home-update').removeClass('show') } else { $('#home-update').addClass('show') }
|
||||
$('#home-update').html(home);
|
||||
|
||||
var intro = $(data).find('intro').text();
|
||||
if(intro == 0) { intro = ''; $('#intro-update').removeClass('show') } else { $('#intro-update').addClass('show') }
|
||||
$('#intro-update').html(intro);
|
||||
|
||||
var mail = $(data).find('mail').text();
|
||||
if(mail == 0) { mail = ''; $('#mail-update').removeClass('show') } else { $('#mail-update').addClass('show') }
|
||||
$('#mail-update').html(mail);
|
||||
|
||||
var intro = $(data).find('intro').text();
|
||||
var intro = data['intro'];
|
||||
if(intro == 0) { intro = ''; $('#intro-update-li').removeClass('show') } else { $('#intro-update-li').addClass('show') }
|
||||
$('#intro-update-li').html(intro);
|
||||
|
||||
var mail = $(data).find('mail').text();
|
||||
var mail = data['mail'];
|
||||
if(mail == 0) { mail = ''; $('#mail-update-li').removeClass('show') } else { $('#mail-update-li').addClass('show') }
|
||||
$('#mail-update-li').html(mail);
|
||||
|
||||
|
||||
var allevents = $(data).find('all-events').text();
|
||||
if(allevents == 0) { allevents = ''; $('#allevents-update').removeClass('show') } else { $('#allevents-update').addClass('show') }
|
||||
$('#allevents-update').html(allevents);
|
||||
|
||||
var alleventstoday = $(data).find('all-events-today').text();
|
||||
if(alleventstoday == 0) { $('#allevents-update').removeClass('notif-allevents-today') } else { $('#allevents-update').addClass('notif-allevents-today') }
|
||||
|
||||
var events = $(data).find('events').text();
|
||||
if(events == 0) { events = ''; $('#events-update').removeClass('show') } else { $('#events-update').addClass('show') }
|
||||
$('#events-update').html(events);
|
||||
|
||||
var eventstoday = $(data).find('events-today').text();
|
||||
if(eventstoday == 0) { $('#events-update').removeClass('notif-events-today') } else { $('#events-update').addClass('notif-events-today') }
|
||||
|
||||
var birthdays = $(data).find('birthdays').text();
|
||||
if(birthdays == 0) {birthdays = ''; $('#birthdays-update').removeClass('show') } else { $('#birthdays-update').addClass('show') }
|
||||
$('#birthdays-update').html(birthdays);
|
||||
|
||||
var birthdaystoday = $(data).find('birthdays-today').text();
|
||||
if(birthdaystoday == 0) { $('#birthdays-update').removeClass('notif-birthdays-today') } else { $('#birthdays-update').addClass('notif-birthdays-today') }
|
||||
|
||||
$(".sidebar-group-li .notify").removeClass("show");
|
||||
$(data).find("group").each(function() {
|
||||
var gid = this.id;
|
||||
var gcount = this.innerHTML;
|
||||
$(data.groups).each(function(key, group) {
|
||||
var gid = group.id;
|
||||
var gcount = group.count;
|
||||
$(".group-"+gid+" .notify").addClass("show").text(gcount);
|
||||
});
|
||||
|
||||
$(".forum-widget-entry .notify").removeClass("show");
|
||||
$(data).find("forum").each(function() {
|
||||
var fid = this.id;
|
||||
var fcount = this.innerHTML;
|
||||
$(data.forums).each(function(key, forum) {
|
||||
var fid = forum.id;
|
||||
var fcount = forum.count;
|
||||
$(".forum-"+fid+" .notify").addClass("show").text(fcount);
|
||||
});
|
||||
|
||||
|
||||
var eNotif = $(data).find('notif')
|
||||
|
||||
if (eNotif.children("note").length==0){
|
||||
if (data.notifications.length == 0) {
|
||||
$("#nav-notifications-menu").html(notifications_empty);
|
||||
} else {
|
||||
nnm = $("#nav-notifications-menu");
|
||||
var nnm = $("#nav-notifications-menu");
|
||||
nnm.html(notifications_all + notifications_mark);
|
||||
//nnm.attr('popup','true');
|
||||
|
||||
var notification_lastitem = parseInt(localStorage.getItem("notification-lastitem"));
|
||||
var notification_id = 0;
|
||||
eNotif.children("note").each(function(){
|
||||
e = $(this);
|
||||
var text = e.text().format("<span class='contactname'>"+e.attr('name')+"</span>");
|
||||
var contact = ("<a href="+e.attr('url')+"><span class='contactname'>"+e.attr('name')+"</span></a>");
|
||||
var seenclass = (e.attr('seen')==1)?"notify-seen":"notify-unseen";
|
||||
$(data.notifications).each(function(key, notif){
|
||||
var text = notif.message.format('<span class="contactname">' + notif.name + '</span>');
|
||||
var contact = ('<a href="' + notif.url + '"><span class="contactname">' + notif.name + '</span></a>');
|
||||
var seenclass = (notif.seen == 1) ? "notify-seen" : "notify-unseen";
|
||||
var html = notifications_tpl.format(
|
||||
e.attr('href'), // {0} // link to the source
|
||||
e.attr('photo'), // {1} // photo of the contact
|
||||
text, // {2} // preformatet text (autor + text)
|
||||
e.attr('date'), // {3} // date of notification (time ago)
|
||||
seenclass, // {4} // vistiting status of the notification
|
||||
new Date(e.attr('timestamp')*1000), // {5} //date of notification
|
||||
e.attr('url'), // {6} // profile url of the contact
|
||||
e.text().format(""), // {7} // clean status text
|
||||
contact // {8} //preformatat author (name + profile url)
|
||||
notif.href, // {0} // link to the source
|
||||
notif.photo, // {1} // photo of the contact
|
||||
text, // {2} // preformatted text (autor + text)
|
||||
notif.date, // {3} // date of notification (time ago)
|
||||
seenclass, // {4} // visited status of the notification
|
||||
new Date(notif.timestamp*1000), // {5} // date of notification
|
||||
notif.url, // {6} // profile url of the contact
|
||||
notif.message.format(contact), // {7} // preformatted html (text including author profile url)
|
||||
'' // {8} // Deprecated
|
||||
);
|
||||
nnm.append(html);
|
||||
});
|
||||
$(eNotif.children("note").get().reverse()).each(function(){
|
||||
e = $(this);
|
||||
notification_id = parseInt(e.attr('timestamp'));
|
||||
$(data.notifications.reverse()).each(function(key, e){
|
||||
notification_id = parseInt(e.timestamp);
|
||||
if (notification_lastitem !== null && notification_id > notification_lastitem) {
|
||||
if (getNotificationPermission() === "granted") {
|
||||
var notification = new Notification(document.title, {
|
||||
body: e.text().replace('→ ','').format(e.attr('name')),
|
||||
icon: e.attr('photo'),
|
||||
body: decodeHtml(e.message.replace('→ ', '').format(e.name)),
|
||||
icon: e.photo,
|
||||
});
|
||||
notification['url'] = e.attr('href');
|
||||
notification['url'] = e.href;
|
||||
notification.addEventListener("click", function(ev){
|
||||
window.location = ev.target.url;
|
||||
});
|
||||
|
@ -299,23 +268,18 @@
|
|||
});
|
||||
}
|
||||
|
||||
notif = eNotif.attr('count');
|
||||
var notif = data['notify'];
|
||||
if (notif > 0){
|
||||
$("#nav-notifications-linkmenu").addClass("on");
|
||||
} else {
|
||||
$("#nav-notifications-linkmenu").removeClass("on");
|
||||
}
|
||||
if(notif == 0) { notif = ''; $('#notify-update').removeClass('show') } else { $('#notify-update').addClass('show') }
|
||||
$('#notify-update').html(notif);
|
||||
|
||||
var eSysmsg = $(data).find('sysmsgs');
|
||||
eSysmsg.children("notice").each(function(){
|
||||
text = $(this).text();
|
||||
$.jGrowl(text, { sticky: true, theme: 'notice' });
|
||||
$(data.sysmsgs.notice).each(function(key, message){
|
||||
$.jGrowl(message, {sticky: true, theme: 'notice'});
|
||||
});
|
||||
eSysmsg.children("info").each(function(){
|
||||
text = $(this).text();
|
||||
$.jGrowl(text, { sticky: false, theme: 'info', life: 5000 });
|
||||
$(data.sysmsgs.info).each(function(key, message){
|
||||
$.jGrowl(message, {sticky: false, theme: 'info', life: 5000});
|
||||
});
|
||||
|
||||
/* update the js scrollbars */
|
||||
|
@ -371,49 +335,37 @@
|
|||
function NavUpdate() {
|
||||
|
||||
if (!stopped) {
|
||||
var pingCmd = 'ping' + ((localUser != 0) ? '?f=&uid=' + localUser : '');
|
||||
var pingCmd = 'ping?format=json' + ((localUser != 0) ? '&f=&uid=' + localUser : '');
|
||||
$.get(pingCmd, function(data) {
|
||||
$(data).find('result').each(function() {
|
||||
if (data.result) {
|
||||
// send nav-update event
|
||||
$('nav').trigger('nav-update', this);
|
||||
|
||||
$('nav').trigger('nav-update', data.result);
|
||||
|
||||
// start live update
|
||||
|
||||
if($('#live-network').length) { src = 'network'; liveUpdate(); }
|
||||
if($('#live-profile').length) { src = 'profile'; liveUpdate(); }
|
||||
if($('#live-community').length) { src = 'community'; liveUpdate(); }
|
||||
if($('#live-notes').length) { src = 'notes'; liveUpdate(); }
|
||||
if($('#live-display').length) { src = 'display'; liveUpdate(); }
|
||||
/* if($('#live-display').length) {
|
||||
if(liking) {
|
||||
liking = 0;
|
||||
window.location.href=window.location.href
|
||||
['network', 'profile', 'community', 'notes', 'display'].forEach(function (src) {
|
||||
if ($('#live-' + src).length) {
|
||||
liveUpdate(src);
|
||||
}
|
||||
}*/
|
||||
});
|
||||
if ($('#live-photos').length) {
|
||||
if (liking) {
|
||||
liking = 0;
|
||||
window.location.href=window.location.href
|
||||
window.location.href = window.location.href;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
}) ;
|
||||
}
|
||||
timer = setTimeout(NavUpdate, updateInterval);
|
||||
}
|
||||
|
||||
function liveUpdate() {
|
||||
function liveUpdate(src) {
|
||||
if((src == null) || (stopped) || (! profile_uid)) { $('.like-rotator').hide(); return; }
|
||||
if(($('.comment-edit-text-full').length) || (in_progress)) {
|
||||
if(livetime) {
|
||||
clearTimeout(livetime);
|
||||
}
|
||||
livetime = setTimeout(liveUpdate, 5000);
|
||||
livetime = setTimeout(function() {liveUpdate(src)}, 5000);
|
||||
return;
|
||||
}
|
||||
if(livetime != null)
|
||||
|
@ -736,8 +688,6 @@
|
|||
// page number
|
||||
infinite_scroll.pageno+=1;
|
||||
|
||||
console.log('Loading page ' + infinite_scroll.pageno);
|
||||
|
||||
// get the raw content from the next page and insert this content
|
||||
// right before "#conversation-end"
|
||||
$.get('network?mode=raw' + infinite_scroll.reload_uri + '&page=' + infinite_scroll.pageno, function(data) {
|
||||
|
|
1063
library/fullcalendar/CHANGELOG.txt
Normal file
1063
library/fullcalendar/CHANGELOG.txt
Normal file
File diff suppressed because it is too large
Load diff
127
library/fullcalendar/CONTRIBUTING.txt
Normal file
127
library/fullcalendar/CONTRIBUTING.txt
Normal file
|
@ -0,0 +1,127 @@
|
|||
|
||||
## Reporting Bugs
|
||||
|
||||
Each bug report MUST have a [JSFiddle/JSBin] recreation before any work can begin. [further instructions »](http://fullcalendar.io/wiki/Reporting-Bugs/)
|
||||
|
||||
|
||||
## Requesting Features
|
||||
|
||||
Please search the [Issue Tracker] to see if your feature has already been requested, and if so, subscribe to it. Otherwise, read these [further instructions »](http://fullcalendar.io/wiki/Requesting-Features/)
|
||||
|
||||
|
||||
## Contributing Features
|
||||
|
||||
The FullCalendar project welcomes [Pull Requests][Using Pull Requests] for new features, but because there are so many feature requests (over 100), and because every new feature requires refinement and maintenance, each PR will be prioritized against the project's other demands and might take a while to make it to an official release.
|
||||
|
||||
Furthermore, each new feature should be designed as robustly as possible and be useful beyond the immediate usecase it was initially designed for. Feel free to start a ticket discussing the feature's specs before coding.
|
||||
|
||||
|
||||
## Contributing Bugfixes
|
||||
|
||||
In the description of your [Pull Request][Using Pull Requests], please include recreation steps for the bug as well as a [JSFiddle/JSBin] demo. Communicating the buggy behavior is a requirement before a merge can happen.
|
||||
|
||||
|
||||
## Contributing Locales
|
||||
|
||||
Please edit the original files in the `locale/` directory. DO NOT edit anything in the `dist/` directory. The build system will responsible for merging FullCalendar's `locale/` data with the [MomentJS locale data].
|
||||
|
||||
|
||||
## Other Ways to Contribute
|
||||
|
||||
[Read about other ways to contribute »](http://fullcalendar.io/wiki/Contributing/)
|
||||
|
||||
|
||||
## Getting Set Up
|
||||
|
||||
You will need [Git][git], [Node][node], and NPM installed. For clarification, please view the [jQuery readme][jq-readme], which requires a similar setup.
|
||||
|
||||
Also, you will need the [gulp-cli][gulp-cli] package installed globally (`-g`) on your system:
|
||||
|
||||
npm install -g gulp-cli
|
||||
|
||||
Then, clone FullCalendar's git repo:
|
||||
|
||||
git clone git://github.com/fullcalendar/fullcalendar.git
|
||||
|
||||
Enter the directory and install FullCalendar's dependencies:
|
||||
|
||||
cd fullcalendar
|
||||
npm install
|
||||
|
||||
|
||||
## What to edit
|
||||
|
||||
When modifying files, please do not edit the generated or minified files in the `dist/` directory. Please edit the original `src/` files.
|
||||
|
||||
|
||||
## Development Workflow
|
||||
|
||||
After you make code changes, you'll want to compile the JS/CSS so that it can be previewed from the tests and demos. You can either manually rebuild each time you make a change:
|
||||
|
||||
gulp dev
|
||||
|
||||
Or, you can run a script that automatically rebuilds whenever you save a source file:
|
||||
|
||||
gulp watch
|
||||
|
||||
When you are finished, run the following command to write the distributable files into the `./dist/` directory:
|
||||
|
||||
gulp dist
|
||||
|
||||
If you want to clean up the generated files, run:
|
||||
|
||||
gulp clean
|
||||
|
||||
|
||||
## Style Guide
|
||||
|
||||
Please follow the [Google JavaScript Style Guide] as closely as possible. With the following exceptions:
|
||||
|
||||
```js
|
||||
if (true) {
|
||||
}
|
||||
else { // please put else, else if, and catch on a separate line
|
||||
}
|
||||
|
||||
// please write one-line array literals with a one-space padding inside
|
||||
var a = [ 1, 2, 3 ];
|
||||
|
||||
// please write one-line object literals with a one-space padding inside
|
||||
var o = { a: 1, b: 2, c: 3 };
|
||||
```
|
||||
|
||||
Other exceptions:
|
||||
|
||||
- please ignore anything about Google Closure Compiler or the `goog` library
|
||||
- please do not write JSDoc comments
|
||||
|
||||
Notes about whitespace:
|
||||
|
||||
- **use *tabs* instead of spaces**
|
||||
- separate functions with *2* blank lines
|
||||
- separate logical blocks within functions with *1* blank line
|
||||
|
||||
Run the command line tool to automatically check your style:
|
||||
|
||||
gulp lint
|
||||
|
||||
|
||||
## Before Submitting your Code
|
||||
|
||||
If you have edited code (including **tests** and **translations**) and would like to submit a pull request, please make sure you have done the following:
|
||||
|
||||
1. Conformed to the style guide (successfully run `gulp lint`)
|
||||
|
||||
2. Written automated tests. View the [Automated Test Readme]
|
||||
|
||||
|
||||
[JSFiddle/JSBin]: http://fullcalendar.io/wiki/Reporting-Bugs/
|
||||
[Issue Tracker]: https://github.com/fullcalendar/fullcalendar/issues
|
||||
[Using Pull Requests]: https://help.github.com/articles/using-pull-requests/
|
||||
[MomentJS locale data]: https://github.com/moment/moment/tree/develop/locale
|
||||
[git]: http://git-scm.com/
|
||||
[node]: http://nodejs.org/
|
||||
[gulp-cli]: https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md
|
||||
[jq-readme]: https://github.com/jquery/jquery/blob/master/README.md#what-you-need-to-build-your-own-jquery
|
||||
[Google JavaScript Style Guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
|
||||
[Automated Test Readme]: https://github.com/fullcalendar/fullcalendar/wiki/Automated-Tests
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2013 Adam Shaw
|
||||
Copyright (c) 2015 Adam Shaw
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
|
@ -1,382 +0,0 @@
|
|||
|
||||
version 1.6.4 (9/1/13)
|
||||
- better algorithm for positioning timed agenda events (issue 1115)
|
||||
- `slotEventOverlap` option to tweak timed agenda event overlapping (issue 218)
|
||||
- selection bug when slot height is customized (issue 1035)
|
||||
- supply view argument in `loading` callback (issue 1018)
|
||||
- fixed week number not displaying in agenda views (issue 1951)
|
||||
- fixed fullCalendar not initializing with no options (issue 1356)
|
||||
- NPM's package.json, no more warnings or errors (issue 1762)
|
||||
- building the bower component should output bower.json instead of component.json (PR 125)
|
||||
- use bower internally for fetching new versions of jQuery and jQuery UI
|
||||
|
||||
version 1.6.3 (8/10/13)
|
||||
- viewRender callback (PR 15)
|
||||
- viewDestroy callback (PR 15)
|
||||
- eventDestroy callback (PR 111)
|
||||
- handleWindowResize option (PR 54)
|
||||
- eventStartEditable/startEditable options (PR 49)
|
||||
- eventDurationEditable/durationEditable options (PR 49)
|
||||
- specify function for $.ajax `data` parameter for JSON event sources (PR 59)
|
||||
- fixed bug with agenda event dropping in wrong column (PR 55)
|
||||
- easier event element z-index customization (PR 58)
|
||||
- classNames on past/future days (PR 88)
|
||||
- allow null/undefined event titles (PR 84)
|
||||
- small optimize for agenda event rendering (PR 56)
|
||||
- deprecated:
|
||||
- viewDisplay
|
||||
- disableDragging
|
||||
- disableResizing
|
||||
- bundled with latest jQuery (1.10.2) and jQuery UI (1.10.3)
|
||||
|
||||
version 1.6.2 (7/18/13)
|
||||
- hiddenDays option (issue 686)
|
||||
- bugfix: when eventRender returns false, incorrect stacking of events (issue 762)
|
||||
- bugfix: couldn't change event.backgroundImage when calling updateEvent (thx stephenharris)
|
||||
|
||||
version 1.6.1 (4/14/13)
|
||||
- fixed event inner content overflow bug (issue 1783)
|
||||
- fixed table header className bug (1772)
|
||||
- removed text-shadow on events (better for general use, thx tkrotoff)
|
||||
|
||||
version 1.6.0 (3/18/13)
|
||||
- visual facelift, with bootstrap-inspired buttons and colors
|
||||
- simplified HTML/CSS for events and buttons
|
||||
- dayRender, for modifying a day cell (issue 191, thx althaus)
|
||||
- week numbers on side of calendar (issue 295)
|
||||
- weekNumber
|
||||
- weekNumberCalculation
|
||||
- weekNumberTitle
|
||||
- "W" formatting variable
|
||||
- finer snapping granularity for agenda view events (issue 495, thx ms-doodle-com)
|
||||
- eventAfterAllRender (issue 753, thx pdrakeweb)
|
||||
- eventDataTransform (thx joeyspo)
|
||||
- data-date attributes on cells (thx Jae)
|
||||
- expose $.fullCalendar.dateFormatters
|
||||
- when clicking fast on buttons, prevent text selection
|
||||
- bundled with latest jQuery (1.9.1) and jQuery UI (1.10.2)
|
||||
- Grunt/Lumbar build system for internal development
|
||||
- build for Bower package manager
|
||||
- build for jQuery plugin site
|
||||
|
||||
version 1.5.4 (9/5/12)
|
||||
- made compatible with jQuery 1.8.* (thx archaeron)
|
||||
- bundled with jQuery 1.8.1 and jQuery UI 1.8.23
|
||||
|
||||
version 1.5.3 (2/6/12)
|
||||
- fixed dragging issue with jQuery UI 1.8.16 (issue 1168)
|
||||
- bundled with jQuery 1.7.1 and jQuery UI 1.8.17
|
||||
|
||||
version 1.5.2 (8/21/11)
|
||||
- correctly process UTC "Z" ISO8601 date strings (issue 750)
|
||||
|
||||
version 1.5.1 (4/9/11)
|
||||
- more flexible ISO8601 date parsing (issue 814)
|
||||
- more flexible parsing of UNIX timestamps (issue 826)
|
||||
- FullCalendar now buildable from source on a Mac (issue 795)
|
||||
- FullCalendar QA'd in FF4 (issue 883)
|
||||
- upgraded to jQuery 1.5.2 (which supports IE9) and jQuery UI 1.8.11
|
||||
|
||||
version 1.5 (3/19/11)
|
||||
- slicker default styling for buttons
|
||||
- reworked a lot of the calendar's HTML and accompanying CSS
|
||||
(solves issues 327 and 395)
|
||||
- more printer-friendly (fullcalendar-print.css)
|
||||
- fullcalendar now inherits styles from jquery-ui themes differently.
|
||||
styles for buttons are distinct from styles for calendar cells.
|
||||
(solves issue 299)
|
||||
- can now color events through FullCalendar options and Event-Object properties (issue 117)
|
||||
THIS IS NOW THE PREFERRED METHOD OF COLORING EVENTS (as opposed to using className and CSS)
|
||||
- FullCalendar options:
|
||||
- eventColor (changes both background and border)
|
||||
- eventBackgroundColor
|
||||
- eventBorderColor
|
||||
- eventTextColor
|
||||
- Event-Object options:
|
||||
- color (changes both background and border)
|
||||
- backgroundColor
|
||||
- borderColor
|
||||
- textColor
|
||||
- can now specify an event source as an *object* with a `url` property (json feed) or
|
||||
an `events` property (function or array) with additional properties that will
|
||||
be applied to the entire event source:
|
||||
- color (changes both background and border)
|
||||
- backgroudColor
|
||||
- borderColor
|
||||
- textColor
|
||||
- className
|
||||
- editable
|
||||
- allDayDefault
|
||||
- ignoreTimezone
|
||||
- startParam (for a feed)
|
||||
- endParam (for a feed)
|
||||
- ANY OF THE JQUERY $.ajax OPTIONS
|
||||
allows for easily changing from GET to POST and sending additional parameters (issue 386)
|
||||
allows for easily attaching ajax handlers such as `error` (issue 754)
|
||||
allows for turning caching on (issue 355)
|
||||
- Google Calendar feeds are now specified differently:
|
||||
- specify a simple string of your feed's URL
|
||||
- specify an *object* with a `url` property of your feed's URL.
|
||||
you can include any of the new Event-Source options in this object.
|
||||
- the old `$.fullCalendar.gcalFeed` method still works
|
||||
- no more IE7 SSL popup (issue 504)
|
||||
- remove `cacheParam` - use json event source `cache` option instead
|
||||
- latest jquery/jquery-ui
|
||||
|
||||
version 1.4.11 (2/22/11)
|
||||
- fixed rerenderEvents bug (issue 790)
|
||||
- fixed bug with faulty dragging of events from all-day slot in agenda views
|
||||
- bundled with jquery 1.5 and jquery-ui 1.8.9
|
||||
|
||||
version 1.4.10 (1/2/11)
|
||||
- fixed bug with resizing event to different week in 5-day month view (issue 740)
|
||||
- fixed bug with events not sticking after a removeEvents call (issue 757)
|
||||
- fixed bug with underlying parseTime method, and other uses of parseInt (issue 688)
|
||||
|
||||
version 1.4.9 (11/16/10)
|
||||
- new algorithm for vertically stacking events (issue 111)
|
||||
- resizing an event to a different week (issue 306)
|
||||
- bug: some events not rendered with consecutive calls to addEventSource (issue 679)
|
||||
|
||||
version 1.4.8 (10/16/10)
|
||||
- ignoreTimezone option (set to `false` to process UTC offsets in ISO8601 dates)
|
||||
- bugfixes
|
||||
- event refetching not being called under certain conditions (issues 417, 554)
|
||||
- event refetching being called multiple times under certain conditions (issues 586, 616)
|
||||
- selection cannot be triggered by right mouse button (issue 558)
|
||||
- agenda view left axis sized incorrectly (issue 465)
|
||||
- IE js error when calendar is too narrow (issue 517)
|
||||
- agenda view looks strange when no scrollbars (issue 235)
|
||||
- improved parsing of ISO8601 dates with UTC offsets
|
||||
- $.fullCalendar.version
|
||||
- an internal refactor of the code, for easier future development and modularity
|
||||
|
||||
version 1.4.7 (7/5/10)
|
||||
- "dropping" external objects onto the calendar
|
||||
- droppable (boolean, to turn on/off)
|
||||
- dropAccept (to filter which events the calendar will accept)
|
||||
- drop (trigger)
|
||||
- selectable options can now be specified with a View Option Hash
|
||||
- bugfixes
|
||||
- dragged & reverted events having wrong time text (issue 406)
|
||||
- bug rendering events that have an endtime with seconds, but no hours/minutes (issue 477)
|
||||
- gotoDate date overflow bug (issue 429)
|
||||
- wrong date reported when clicking on edge of last column in agenda views (412)
|
||||
- support newlines in event titles
|
||||
- select/unselect callbacks now passes native js event
|
||||
|
||||
version 1.4.6 (5/31/10)
|
||||
- "selecting" days or timeslots
|
||||
- options: selectable, selectHelper, unselectAuto, unselectCancel
|
||||
- callbacks: select, unselect
|
||||
- methods: select, unselect
|
||||
- when dragging an event, the highlighting reflects the duration of the event
|
||||
- code compressing by Google Closure Compiler
|
||||
- bundled with jQuery 1.4.2 and jQuery UI 1.8.1
|
||||
|
||||
version 1.4.5 (2/21/10)
|
||||
- lazyFetching option, which can force the calendar to fetch events on every view/date change
|
||||
- scroll state of agenda views are preserved when switching back to view
|
||||
- bugfixes
|
||||
- calling methods on an uninitialized fullcalendar throws error
|
||||
- IE6/7 bug where an entire view becomes invisible (issue 320)
|
||||
- error when rendering a hidden calendar (in jquery ui tabs for example) in IE (issue 340)
|
||||
- interconnected bugs related to calendar resizing and scrollbars
|
||||
- when switching views or clicking prev/next, calendar would "blink" (issue 333)
|
||||
- liquid-width calendar's events shifted (depending on initial height of browser) (issue 341)
|
||||
- more robust underlying algorithm for calendar resizing
|
||||
|
||||
version 1.4.4 (2/3/10)
|
||||
- optimized event rendering in all views (events render in 1/10 the time)
|
||||
- gotoDate() does not force the calendar to unnecessarily rerender
|
||||
- render() method now correctly readjusts height
|
||||
|
||||
version 1.4.3 (12/22/09)
|
||||
- added destroy method
|
||||
- Google Calendar event pages respect currentTimezone
|
||||
- caching now handled by jQuery's ajax
|
||||
- protection from setting aspectRatio to zero
|
||||
- bugfixes
|
||||
- parseISO8601 and DST caused certain events to display day before
|
||||
- button positioning problem in IE6
|
||||
- ajax event source removed after recently being added, events still displayed
|
||||
- event not displayed when end is an empty string
|
||||
- dynamically setting calendar height when no events have been fetched, throws error
|
||||
|
||||
version 1.4.2 (12/02/09)
|
||||
- eventAfterRender trigger
|
||||
- getDate & getView methods
|
||||
- height & contentHeight options (explicitly sets the pixel height)
|
||||
- minTime & maxTime options (restricts shown hours in agenda view)
|
||||
- getters [for all options] and setters [for height, contentHeight, and aspectRatio ONLY! stay tuned..]
|
||||
- render method now readjusts calendar's size
|
||||
- bugfixes
|
||||
- lightbox scripts that use iframes (like fancybox)
|
||||
- day-of-week classNames were off when firstDay=1
|
||||
- guaranteed space on right side of agenda events (even when stacked)
|
||||
- accepts ISO8601 dates with a space (instead of 'T')
|
||||
|
||||
version 1.4.1 (10/31/09)
|
||||
- can exclude weekends with new 'weekends' option
|
||||
- gcal feed 'currentTimezone' option
|
||||
- bugfixes
|
||||
- year/month/date option sometimes wouldn't set correctly (depending on current date)
|
||||
- daylight savings issue caused agenda views to start at 1am (for BST users)
|
||||
- cleanup of gcal.js code
|
||||
|
||||
version 1.4 (10/19/09)
|
||||
- agendaWeek and agendaDay views
|
||||
- added some options for agenda views:
|
||||
- allDaySlot
|
||||
- allDayText
|
||||
- firstHour
|
||||
- slotMinutes
|
||||
- defaultEventMinutes
|
||||
- axisFormat
|
||||
- modified some existing options/triggers to work with agenda views:
|
||||
- dragOpacity and timeFormat can now accept a "View Hash" (a new concept)
|
||||
- dayClick now has an allDay parameter
|
||||
- eventDrop now has an an allDay parameter
|
||||
(this will affect those who use revertFunc, adjust parameter list)
|
||||
- added 'prevYear' and 'nextYear' for buttons in header
|
||||
- minor change for theme users, ui-state-hover not applied to active/inactive buttons
|
||||
- added event-color-changing example in docs
|
||||
- better defaults for right-to-left themed button icons
|
||||
|
||||
version 1.3.2 (10/13/09)
|
||||
- Bugfixes (please upgrade from 1.3.1!)
|
||||
- squashed potential infinite loop when addMonths and addDays
|
||||
is called with an invalid date
|
||||
- $.fullCalendar.parseDate() now correctly parses IETF format
|
||||
- when switching views, the 'today' button sticks inactive, fixed
|
||||
- gotoDate now can accept a single Date argument
|
||||
- documentation for changes in 1.3.1 and 1.3.2 now on website
|
||||
|
||||
version 1.3.1 (9/30/09)
|
||||
- Important Bugfixes (please upgrade from 1.3!)
|
||||
- When current date was late in the month, for long months, and prev/next buttons
|
||||
were clicked in month-view, some months would be skipped/repeated
|
||||
- In certain time zones, daylight savings time would cause certain days
|
||||
to be misnumbered in month-view
|
||||
- Subtle change in way week interval is chosen when switching from month to basicWeek/basicDay view
|
||||
- Added 'allDayDefault' option
|
||||
- Added 'changeView' and 'render' methods
|
||||
|
||||
version 1.3 (9/21/09)
|
||||
- different 'views': month/basicWeek/basicDay
|
||||
- more flexible 'header' system for buttons
|
||||
- themable by jQuery UI themes
|
||||
- resizable events (require jQuery UI resizable plugin)
|
||||
- rescoped & rewritten CSS, enhanced default look
|
||||
- cleaner css & rendering techniques for right-to-left
|
||||
- reworked options & API to support multiple views / be consistent with jQuery UI
|
||||
- refactoring of entire codebase
|
||||
- broken into different JS & CSS files, assembled w/ build scripts
|
||||
- new test suite for new features, uses firebug-lite
|
||||
- refactored docs
|
||||
- Options
|
||||
+ date
|
||||
+ defaultView
|
||||
+ aspectRatio
|
||||
+ disableResizing
|
||||
+ monthNames (use instead of $.fullCalendar.monthNames)
|
||||
+ monthNamesShort (use instead of $.fullCalendar.monthAbbrevs)
|
||||
+ dayNames (use instead of $.fullCalendar.dayNames)
|
||||
+ dayNamesShort (use instead of $.fullCalendar.dayAbbrevs)
|
||||
+ theme
|
||||
+ buttonText
|
||||
+ buttonIcons
|
||||
x draggable -> editable/disableDragging
|
||||
x fixedWeeks -> weekMode
|
||||
x abbrevDayHeadings -> columnFormat
|
||||
x buttons/title -> header
|
||||
x eventDragOpacity -> dragOpacity
|
||||
x eventRevertDuration -> dragRevertDuration
|
||||
x weekStart -> firstDay
|
||||
x rightToLeft -> isRTL
|
||||
x showTime (use 'allDay' CalEvent property instead)
|
||||
- Triggered Actions
|
||||
+ eventResizeStart
|
||||
+ eventResizeStop
|
||||
+ eventResize
|
||||
x monthDisplay -> viewDisplay
|
||||
x resize -> windowResize
|
||||
'eventDrop' params changed, can revert if ajax cuts out
|
||||
- CalEvent Properties
|
||||
x showTime -> allDay
|
||||
x draggable -> editable
|
||||
'end' is now INCLUSIVE when allDay=true
|
||||
'url' now produces a real <a> tag, more native clicking/tab behavior
|
||||
- Methods:
|
||||
+ renderEvent
|
||||
x prevMonth -> prev
|
||||
x nextMonth -> next
|
||||
x prevYear/nextYear -> moveDate
|
||||
x refresh -> rerenderEvents/refetchEvents
|
||||
x removeEvent -> removeEvents
|
||||
x getEventsByID -> clientEvents
|
||||
- Utilities:
|
||||
'formatDate' format string completely changed (inspired by jQuery UI datepicker + datejs)
|
||||
'formatDates' added to support date-ranges
|
||||
- Google Calendar Options:
|
||||
x draggable -> editable
|
||||
- Bugfixes
|
||||
- gcal extension fetched 25 results max, now fetches all
|
||||
|
||||
version 1.2.1 (6/29/09)
|
||||
- bugfixes
|
||||
- allows and corrects invalid end dates for events
|
||||
- doesn't throw an error in IE while rendering when display:none
|
||||
- fixed 'loading' callback when used w/ multiple addEventSource calls
|
||||
- gcal className can now be an array
|
||||
|
||||
version 1.2 (5/31/09)
|
||||
- expanded API
|
||||
- 'className' CalEvent attribute
|
||||
- 'source' CalEvent attribute
|
||||
- dynamically get/add/remove/update events of current month
|
||||
- locale improvements: change month/day name text
|
||||
- better date formatting ($.fullCalendar.formatDate)
|
||||
- multiple 'event sources' allowed
|
||||
- dynamically add/remove event sources
|
||||
- options for prevYear and nextYear buttons
|
||||
- docs have been reworked (include addition of Google Calendar docs)
|
||||
- changed behavior of parseDate for number strings
|
||||
(now interpets as unix timestamp, not MS times)
|
||||
- bugfixes
|
||||
- rightToLeft month start bug
|
||||
- off-by-one errors with month formatting commands
|
||||
- events from previous months sticking when clicking prev/next quickly
|
||||
- Google Calendar API changed to work w/ multiple event sources
|
||||
- can also provide 'className' and 'draggable' options
|
||||
- date utilties moved from $ to $.fullCalendar
|
||||
- more documentation in source code
|
||||
- minified version of fullcalendar.js
|
||||
- test suit (available from svn)
|
||||
- top buttons now use <button> w/ an inner <span> for better css cusomization
|
||||
- thus CSS has changed. IF UPGRADING FROM PREVIOUS VERSIONS,
|
||||
UPGRADE YOUR FULLCALENDAR.CSS FILE!!!
|
||||
|
||||
version 1.1 (5/10/09)
|
||||
- Added the following options:
|
||||
- weekStart
|
||||
- rightToLeft
|
||||
- titleFormat
|
||||
- timeFormat
|
||||
- cacheParam
|
||||
- resize
|
||||
- Fixed rendering bugs
|
||||
- Opera 9.25 (events placement & window resizing)
|
||||
- IE6 (window resizing)
|
||||
- Optimized window resizing for ALL browsers
|
||||
- Events on same day now sorted by start time (but first by timespan)
|
||||
- Correct z-index when dragging
|
||||
- Dragging contained in overflow DIV for IE6
|
||||
- Modified fullcalendar.css
|
||||
- for right-to-left support
|
||||
- for variable start-of-week
|
||||
- for IE6 resizing bug
|
||||
- for THEAD and TBODY (in 1.0, just used TBODY, restructured in 1.1)
|
||||
- IF UPGRADING FROM FULLCALENDAR 1.0, YOU MUST UPGRADE FULLCALENDAR.CSS
|
||||
!!!!!!!!!!!
|
||||
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
5
library/fullcalendar/fullcalendar.min.css
vendored
Normal file
5
library/fullcalendar/fullcalendar.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
12
library/fullcalendar/fullcalendar.min.js
vendored
12
library/fullcalendar/fullcalendar.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
|||
/*!
|
||||
* FullCalendar v1.6.4 Print Stylesheet
|
||||
* Docs & License: http://arshaw.com/fullcalendar/
|
||||
* (c) 2013 Adam Shaw
|
||||
* FullCalendar v3.0.1 Print Stylesheet
|
||||
* Docs & License: http://fullcalendar.io/
|
||||
* (c) 2016 Adam Shaw
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -10,23 +10,199 @@
|
|||
* Make sure to include this stylesheet IN ADDITION to the regular fullcalendar.css.
|
||||
*/
|
||||
|
||||
.fc {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
/* Events
|
||||
-----------------------------------------------------*/
|
||||
|
||||
/* Global Event Restyling
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-event {
|
||||
background: #fff !important;
|
||||
color: #000 !important;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
/* for vertical events */
|
||||
.fc-event .fc-resizer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.fc-event-bg {
|
||||
|
||||
/* Table & Day-Row Restyling
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc th,
|
||||
.fc td,
|
||||
.fc hr,
|
||||
.fc thead,
|
||||
.fc tbody,
|
||||
.fc-row {
|
||||
border-color: #ccc !important;
|
||||
background: #fff !important;
|
||||
}
|
||||
|
||||
/* kill the overlaid, absolutely-positioned components */
|
||||
/* common... */
|
||||
.fc-bg,
|
||||
.fc-bgevent-skeleton,
|
||||
.fc-highlight-skeleton,
|
||||
.fc-helper-skeleton,
|
||||
/* for timegrid. within cells within table skeletons... */
|
||||
.fc-bgevent-container,
|
||||
.fc-business-container,
|
||||
.fc-highlight-container,
|
||||
.fc-helper-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* don't force a min-height on rows (for DayGrid) */
|
||||
.fc tbody .fc-row {
|
||||
height: auto !important; /* undo height that JS set in distributeHeight */
|
||||
min-height: 0 !important; /* undo the min-height from each view's specific stylesheet */
|
||||
}
|
||||
|
||||
.fc tbody .fc-row .fc-content-skeleton {
|
||||
position: static; /* undo .fc-rigid */
|
||||
padding-bottom: 0 !important; /* use a more border-friendly method for this... */
|
||||
}
|
||||
|
||||
.fc tbody .fc-row .fc-content-skeleton tbody tr:last-child td { /* only works in newer browsers */
|
||||
padding-bottom: 1em; /* ...gives space within the skeleton. also ensures min height in a way */
|
||||
}
|
||||
|
||||
.fc tbody .fc-row .fc-content-skeleton table {
|
||||
/* provides a min-height for the row, but only effective for IE, which exaggerates this value,
|
||||
making it look more like 3em. for other browers, it will already be this tall */
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
|
||||
/* Undo month-view event limiting. Display all events and hide the "more" links
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-more-cell,
|
||||
.fc-more {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.fc-event .ui-resizable-handle {
|
||||
display: none !important;
|
||||
.fc tr.fc-limited {
|
||||
display: table-row !important;
|
||||
}
|
||||
|
||||
.fc td.fc-limited {
|
||||
display: table-cell !important;
|
||||
}
|
||||
|
||||
.fc-popover {
|
||||
display: none; /* never display the "more.." popover in print mode */
|
||||
}
|
||||
|
||||
|
||||
/* TimeGrid Restyling
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* undo the min-height 100% trick used to fill the container's height */
|
||||
.fc-time-grid {
|
||||
min-height: 0 !important;
|
||||
}
|
||||
|
||||
/* don't display the side axis at all ("all-day" and time cells) */
|
||||
.fc-agenda-view .fc-axis {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* don't display the horizontal lines */
|
||||
.fc-slats,
|
||||
.fc-time-grid hr { /* this hr is used when height is underused and needs to be filled */
|
||||
display: none !important; /* important overrides inline declaration */
|
||||
}
|
||||
|
||||
/* let the container that holds the events be naturally positioned and create real height */
|
||||
.fc-time-grid .fc-content-skeleton {
|
||||
position: static;
|
||||
}
|
||||
|
||||
/* in case there are no events, we still want some height */
|
||||
.fc-time-grid .fc-content-skeleton table {
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
/* kill the horizontal spacing made by the event container. event margins will be done below */
|
||||
.fc-time-grid .fc-event-container {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
/* TimeGrid *Event* Restyling
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* naturally position events, vertically stacking them */
|
||||
.fc-time-grid .fc-event {
|
||||
position: static !important;
|
||||
margin: 3px 2px !important;
|
||||
}
|
||||
|
||||
/* for events that continue to a future day, give the bottom border back */
|
||||
.fc-time-grid .fc-event.fc-not-end {
|
||||
border-bottom-width: 1px !important;
|
||||
}
|
||||
|
||||
/* indicate the event continues via "..." text */
|
||||
.fc-time-grid .fc-event.fc-not-end:after {
|
||||
content: "...";
|
||||
}
|
||||
|
||||
/* for events that are continuations from previous days, give the top border back */
|
||||
.fc-time-grid .fc-event.fc-not-start {
|
||||
border-top-width: 1px !important;
|
||||
}
|
||||
|
||||
/* indicate the event is a continuation via "..." text */
|
||||
.fc-time-grid .fc-event.fc-not-start:before {
|
||||
content: "...";
|
||||
}
|
||||
|
||||
/* time */
|
||||
|
||||
/* undo a previous declaration and let the time text span to a second line */
|
||||
.fc-time-grid .fc-event .fc-time {
|
||||
white-space: normal !important;
|
||||
}
|
||||
|
||||
/* hide the the time that is normally displayed... */
|
||||
.fc-time-grid .fc-event .fc-time span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ...replace it with a more verbose version (includes AM/PM) stored in an html attribute */
|
||||
.fc-time-grid .fc-event .fc-time:after {
|
||||
content: attr(data-full);
|
||||
}
|
||||
|
||||
|
||||
/* Vertical Scroller & Containers
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* kill the scrollbars and allow natural height */
|
||||
.fc-scroller,
|
||||
.fc-day-grid-container, /* these divs might be assigned height, which we need to cleared */
|
||||
.fc-time-grid-container { /* */
|
||||
overflow: visible !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* kill the horizontal border/padding used to compensate for scrollbars */
|
||||
.fc-row {
|
||||
border: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
/* Button Controls
|
||||
--------------------------------------------------------------------------------------------------*/
|
||||
|
||||
.fc-button-group,
|
||||
.fc button {
|
||||
display: none; /* don't display any button-related controls */
|
||||
}
|
||||
|
|
|
@ -1,107 +1,180 @@
|
|||
/*!
|
||||
* FullCalendar v1.6.4 Google Calendar Plugin
|
||||
* Docs & License: http://arshaw.com/fullcalendar/
|
||||
* (c) 2013 Adam Shaw
|
||||
* FullCalendar v3.0.1 Google Calendar Plugin
|
||||
* Docs & License: http://fullcalendar.io/
|
||||
* (c) 2016 Adam Shaw
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
(function(factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define([ 'jquery' ], factory);
|
||||
}
|
||||
else if (typeof exports === 'object') { // Node/CommonJS
|
||||
module.exports = factory(require('jquery'));
|
||||
}
|
||||
else {
|
||||
factory(jQuery);
|
||||
}
|
||||
})(function($) {
|
||||
|
||||
|
||||
var fc = $.fullCalendar;
|
||||
var formatDate = fc.formatDate;
|
||||
var parseISO8601 = fc.parseISO8601;
|
||||
var addDays = fc.addDays;
|
||||
var applyAll = fc.applyAll;
|
||||
var API_BASE = 'https://www.googleapis.com/calendar/v3/calendars';
|
||||
var FC = $.fullCalendar;
|
||||
var applyAll = FC.applyAll;
|
||||
|
||||
|
||||
fc.sourceNormalizers.push(function(sourceOptions) {
|
||||
if (sourceOptions.dataType == 'gcal' ||
|
||||
sourceOptions.dataType === undefined &&
|
||||
(sourceOptions.url || '').match(/^(http|https):\/\/www.google.com\/calendar\/feeds\//)) {
|
||||
sourceOptions.dataType = 'gcal';
|
||||
if (sourceOptions.editable === undefined) {
|
||||
FC.sourceNormalizers.push(function(sourceOptions) {
|
||||
var googleCalendarId = sourceOptions.googleCalendarId;
|
||||
var url = sourceOptions.url;
|
||||
var match;
|
||||
|
||||
// if the Google Calendar ID hasn't been explicitly defined
|
||||
if (!googleCalendarId && url) {
|
||||
|
||||
// detect if the ID was specified as a single string.
|
||||
// will match calendars like "asdf1234@calendar.google.com" in addition to person email calendars.
|
||||
if (/^[^\/]+@([^\/\.]+\.)*(google|googlemail|gmail)\.com$/.test(url)) {
|
||||
googleCalendarId = url;
|
||||
}
|
||||
// try to scrape it out of a V1 or V3 API feed URL
|
||||
else if (
|
||||
(match = /^https:\/\/www.googleapis.com\/calendar\/v3\/calendars\/([^\/]*)/.exec(url)) ||
|
||||
(match = /^https?:\/\/www.google.com\/calendar\/feeds\/([^\/]*)/.exec(url))
|
||||
) {
|
||||
googleCalendarId = decodeURIComponent(match[1]);
|
||||
}
|
||||
|
||||
if (googleCalendarId) {
|
||||
sourceOptions.googleCalendarId = googleCalendarId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (googleCalendarId) { // is this a Google Calendar?
|
||||
|
||||
// make each Google Calendar source uneditable by default
|
||||
if (sourceOptions.editable == null) {
|
||||
sourceOptions.editable = false;
|
||||
}
|
||||
|
||||
// We want removeEventSource to work, but it won't know about the googleCalendarId primitive.
|
||||
// Shoehorn it into the url, which will function as the unique primitive. Won't cause side effects.
|
||||
// This hack is obsolete since 2.2.3, but keep it so this plugin file is compatible with old versions.
|
||||
sourceOptions.url = googleCalendarId;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
fc.sourceFetchers.push(function(sourceOptions, start, end) {
|
||||
if (sourceOptions.dataType == 'gcal') {
|
||||
return transformOptions(sourceOptions, start, end);
|
||||
FC.sourceFetchers.push(function(sourceOptions, start, end, timezone) {
|
||||
if (sourceOptions.googleCalendarId) {
|
||||
return transformOptions(sourceOptions, start, end, timezone, this); // `this` is the calendar
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function transformOptions(sourceOptions, start, end) {
|
||||
|
||||
function transformOptions(sourceOptions, start, end, timezone, calendar) {
|
||||
var url = API_BASE + '/' + encodeURIComponent(sourceOptions.googleCalendarId) + '/events?callback=?'; // jsonp
|
||||
var apiKey = sourceOptions.googleCalendarApiKey || calendar.options.googleCalendarApiKey;
|
||||
var success = sourceOptions.success;
|
||||
var data = $.extend({}, sourceOptions.data || {}, {
|
||||
'start-min': formatDate(start, 'u'),
|
||||
'start-max': formatDate(end, 'u'),
|
||||
'singleevents': true,
|
||||
'max-results': 9999
|
||||
});
|
||||
var data;
|
||||
var timezoneArg; // populated when a specific timezone. escaped to Google's liking
|
||||
|
||||
var ctz = sourceOptions.currentTimezone;
|
||||
if (ctz) {
|
||||
data.ctz = ctz = ctz.replace(' ', '_');
|
||||
function reportError(message, apiErrorObjs) {
|
||||
var errorObjs = apiErrorObjs || [ { message: message } ]; // to be passed into error handlers
|
||||
|
||||
// call error handlers
|
||||
(sourceOptions.googleCalendarError || $.noop).apply(calendar, errorObjs);
|
||||
(calendar.options.googleCalendarError || $.noop).apply(calendar, errorObjs);
|
||||
|
||||
// print error to debug console
|
||||
FC.warn.apply(null, [ message ].concat(apiErrorObjs || []));
|
||||
}
|
||||
|
||||
if (!apiKey) {
|
||||
reportError("Specify a googleCalendarApiKey. See http://fullcalendar.io/docs/google_calendar/");
|
||||
return {}; // an empty source to use instead. won't fetch anything.
|
||||
}
|
||||
|
||||
// The API expects an ISO8601 datetime with a time and timezone part.
|
||||
// Since the calendar's timezone offset isn't always known, request the date in UTC and pad it by a day on each
|
||||
// side, guaranteeing we will receive all events in the desired range, albeit a superset.
|
||||
// .utc() will set a zone and give it a 00:00:00 time.
|
||||
if (!start.hasZone()) {
|
||||
start = start.clone().utc().add(-1, 'day');
|
||||
}
|
||||
if (!end.hasZone()) {
|
||||
end = end.clone().utc().add(1, 'day');
|
||||
}
|
||||
|
||||
// when sending timezone names to Google, only accepts underscores, not spaces
|
||||
if (timezone && timezone != 'local') {
|
||||
timezoneArg = timezone.replace(' ', '_');
|
||||
}
|
||||
|
||||
data = $.extend({}, sourceOptions.data || {}, {
|
||||
key: apiKey,
|
||||
timeMin: start.format(),
|
||||
timeMax: end.format(),
|
||||
timeZone: timezoneArg,
|
||||
singleEvents: true,
|
||||
maxResults: 9999
|
||||
});
|
||||
|
||||
return $.extend({}, sourceOptions, {
|
||||
url: sourceOptions.url.replace(/\/basic$/, '/full') + '?alt=json-in-script&callback=?',
|
||||
dataType: 'jsonp',
|
||||
googleCalendarId: null, // prevents source-normalizing from happening again
|
||||
url: url,
|
||||
data: data,
|
||||
startParam: false,
|
||||
endParam: false,
|
||||
startParam: false, // `false` omits this parameter. we already included it above
|
||||
endParam: false, // same
|
||||
timezoneParam: false, // same
|
||||
success: function(data) {
|
||||
var events = [];
|
||||
if (data.feed.entry) {
|
||||
$.each(data.feed.entry, function(i, entry) {
|
||||
var startStr = entry['gd$when'][0]['startTime'];
|
||||
var start = parseISO8601(startStr, true);
|
||||
var end = parseISO8601(entry['gd$when'][0]['endTime'], true);
|
||||
var allDay = startStr.indexOf('T') == -1;
|
||||
var url;
|
||||
$.each(entry.link, function(i, link) {
|
||||
if (link.type == 'text/html') {
|
||||
url = link.href;
|
||||
if (ctz) {
|
||||
url += (url.indexOf('?') == -1 ? '?' : '&') + 'ctz=' + ctz;
|
||||
var successArgs;
|
||||
var successRes;
|
||||
|
||||
if (data.error) {
|
||||
reportError('Google Calendar API: ' + data.error.message, data.error.errors);
|
||||
}
|
||||
else if (data.items) {
|
||||
$.each(data.items, function(i, entry) {
|
||||
var url = entry.htmlLink || null;
|
||||
|
||||
// make the URLs for each event show times in the correct timezone
|
||||
if (timezoneArg && url !== null) {
|
||||
url = injectQsComponent(url, 'ctz=' + timezoneArg);
|
||||
}
|
||||
});
|
||||
if (allDay) {
|
||||
addDays(end, -1); // make inclusive
|
||||
}
|
||||
|
||||
events.push({
|
||||
id: entry['gCal$uid']['value'],
|
||||
title: entry['title']['$t'],
|
||||
id: entry.id,
|
||||
title: entry.summary,
|
||||
start: entry.start.dateTime || entry.start.date, // try timed. will fall back to all-day
|
||||
end: entry.end.dateTime || entry.end.date, // same
|
||||
url: url,
|
||||
start: start,
|
||||
end: end,
|
||||
allDay: allDay,
|
||||
location: entry['gd$where'][0]['valueString'],
|
||||
description: entry['content']['$t']
|
||||
location: entry.location,
|
||||
description: entry.description
|
||||
});
|
||||
});
|
||||
|
||||
// call the success handler(s) and allow it to return a new events array
|
||||
successArgs = [ events ].concat(Array.prototype.slice.call(arguments, 1)); // forward other jq args
|
||||
successRes = applyAll(success, this, successArgs);
|
||||
if ($.isArray(successRes)) {
|
||||
return successRes;
|
||||
}
|
||||
var args = [events].concat(Array.prototype.slice.call(arguments, 1));
|
||||
var res = applyAll(success, this, args);
|
||||
if ($.isArray(res)) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return events;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
// legacy
|
||||
fc.gcalFeed = function(url, sourceOptions) {
|
||||
return $.extend({}, sourceOptions, { url: url, dataType: 'gcal' });
|
||||
};
|
||||
// Injects a string like "arg=value" into the querystring of a URL
|
||||
function injectQsComponent(url, component) {
|
||||
// inject it after the querystring but before the fragment
|
||||
return url.replace(/(\?.*?)?(#|$)/, function(whole, qs, hash) {
|
||||
return (qs ? qs + '&' : '?') + component + hash;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
})(jQuery);
|
||||
});
|
||||
|
|
5
library/fullcalendar/locale-all.js
Normal file
5
library/fullcalendar/locale-all.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -396,7 +396,7 @@ class Text_LanguageDetect
|
|||
* Returns the list of detectable languages
|
||||
*
|
||||
* @access public
|
||||
* @return array the names of the languages known to this object<<<<<<<
|
||||
* @return array the names of the languages known to this object
|
||||
* @throws Text_LanguageDetect_Exception
|
||||
*/
|
||||
function getLanguages()
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue