define ( 'FRIENDICA_CODENAME', 'Asparagus');
define ( 'FRIENDICA_VERSION', '3.5.1-dev' );
define ( 'DFRN_PROTOCOL_VERSION', '2.23' );
-define ( 'DB_UPDATE_VERSION', 1204 );
+define ( 'DB_UPDATE_VERSION', 1206 );
/**
* @brief Constant with a HTML line break.
/**
* @brief Image storage quality.
- *
+ *
* Lower numbers save space at cost of image detail.
* For ease of upgrade, please do not change here. Change jpeg quality with
* $a->config['system']['jpeg_quality'] = n;
/**
* @name SSL Policy
- *
+ *
* SSL redirection policies
* @{
*/
/**
* @name Logger
- *
+ *
* log levels
* @{
*/
/**
* @name Cache
- *
+ *
* Cache levels
* @{
*/
/**
* @name Register
- *
+ *
* Registration policies
* @{
*/
/**
* @name Contact_is
- *
+ *
* Relationship types
* @{
*/
/**
* @name Update
- *
+ *
* DB update return values
* @{
*/
/**
* @name CP
- *
+ *
* Type of the community page
* @{
*/
/**
* @name Network
- *
+ *
* Network and protocol family types
* @{
*/
/**
* @name Notify
- *
+ *
* Email notification options
* @{
*/
/**
* @name Term
- *
+ *
* Tag/term types
* @{
*/
/**
* @name Namespaces
- *
+ *
* Various namespaces we may need to parse
* @{
*/
/**
* @name Activity
- *
+ *
* Activity stream defines
* @{
*/
/**
* @name Gravity
- *
+ *
* Item weight for query ordering
* @{
*/
/**
*
* class: App
- *
+ *
* @brief Our main application structure for the life of this page.
- *
+ *
* Primarily deals with the URL that got us here
* and tries to make some sense of it, and
* stores our page contents and config storage
/**
* @brief Register template engine class
- *
+ *
* If $name is "", is used class static property $class::$name
- *
+ *
* @param string $class
* @param string $name
*/
/**
* @brief Return template engine instance.
- *
+ *
* If $name is not defined, return engine defined by theme,
* or default
*
/**
* @brief Retrieve the App structure
- *
+ *
* Useful in functions which require it but don't get it passed to them
*/
function get_app() {
* and mark it uninstalled in the database (for now we'll remove it).
* Then go through the config list and if we have a plugin that isn't installed,
* call the install procedure and add it to the database.
- *
+ *
* @param App $a
*
*/
}
}
-/**
+/**
* @brief Wrapper for adding a login box.
- *
+ *
* @param bool $register
* If $register == true provide a registration link.
* This will most always depend on the value of $a->config['register_policy'].
* @param bool $hiddens
- *
+ *
* @return string
* Returns the complete html for inserting into the page
- *
+ *
* @hooks 'login_hook'
* string $o
*/
/**
* @brief Returns the user id of locally logged in user or false.
- *
+ *
* @return int|bool user id or false
*/
function local_user() {
/**
* @brief Returns contact id of authenticated site visitor or false
- *
+ *
* @return int|bool visitor_id or false
*/
function remote_user() {
* so plugins can take part in process :)
*
* @param (string|integer) $cmd program to run or priority
- *
+ *
* 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);
*
* @note $cmd and string args are surrounded with ""
- *
+ *
* @hooks 'proc_run'
* array $arr
*/
/**
* @brief Return full URL to theme which is currently in effect.
- *
+ *
* Provide a sane default if nothing is chosen or the specified theme does not exist.
- *
+ *
* @return string
*/
function current_theme_url() {
/**
* @brief get c-style args
- *
+ *
* @return int
*/
function argc() {
/**
* @brief Returns the value of a argv key
- *
+ *
* @param int $x argv key
* @return string Value of the argv key
*/
/**
* @brief Get the data which is needed for infinite scroll
- *
+ *
* For invinite scroll we need the page number of the actual page
* and the the URI where the content of the next page comes from.
* This data is needed for the js part in main.js.
* Note: infinite scroll does only work for the network page (module)
- *
+ *
* @param string $module The name of the module (e.g. "network")
* @return array Of infinite scroll data
* 'pageno' => $pageno The number of the actual page
-- ------------------------------------------
-- Friendica 3.5.1-dev (Asparagus)
--- DB_UPDATE_VERSION 1204
+-- DB_UPDATE_VERSION 1205
-- ------------------------------------------
`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;
--
`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;
--
--- /dev/null
+# 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.
\ No newline at end of file
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'];
}
}
- $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)
);
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]);
+ $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' ) ",
- dbesc($family),
- dbesc($key),
- dbesc($dbvalue)
- );
- if($ret)
- return $value;
- return $ret;
- }
- $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s'",
- dbesc($dbvalue),
+ $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' )
+ON DUPLICATE KEY UPDATE `v` = '%s'",
dbesc($family),
- dbesc($key)
+ dbesc($key),
+ dbesc($dbvalue),
+ dbesc($dbvalue)
);
-
- $a->config[$family][$key] = $value;
-
if($ret)
return $value;
return $ret;
*/
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)
);
}
}
- $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)
// 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' ) ",
- intval($uid),
- dbesc($family),
- dbesc($key),
- dbesc($dbvalue)
- );
- if($ret)
- return $value;
- return $ret;
- }
- $ret = q("UPDATE `pconfig` SET `v` = '%s' WHERE `uid` = %d AND `cat` = '%s' AND `k` = '%s'",
- dbesc($dbvalue),
+ $a->config[$uid][$family][$key] = $value;
+
+ $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($key),
+ dbesc($dbvalue),
+ dbesc($dbvalue)
);
-
- $a->config[$uid][$family][$key] = $value;
-
if($ret)
return $value;
return $ret;
*/
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'];
$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"]))
return array("network" => NETWORK_TWITTER);
$lrdd = self::xrd($host);
+
if (!$lrdd)
return self::mail($uri, $uid);
$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);
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
$pubkey = substr($pubkey, strpos($pubkey, ',') + 1);
else
$pubkey = substr($pubkey, 5);
- } else
+ } elseif (normalise_link($pubkey) == 'http://')
$pubkey = fetch_url($pubkey);
$key = explode(".", $pubkey);
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;
+ }
+
public function q($sql, $onlyquery = false) {
global $a;
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"];
// 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.
if (is_null($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) {
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;
}
$sql2=db_create_index($indexname, $fieldnames);
if ($sql2 != "") {
if ($sql3 == "")
- $sql3 = "ALTER TABLE `".$name."` ".$sql2;
+ $sql3 = "ALTER" . $ignore . " TABLE `".$name."` ".$sql2;
else
$sql3 .= ", ".$sql2;
}
killme();
}
+ if ($fieldnames[0] == "UNIQUE") {
+ array_shift($fieldnames);
+ $method .= ' UNIQUE';
+ }
+
$names = "";
foreach ($fieldnames AS $fieldname) {
if ($names != "")
),
"indexes" => array(
"PRIMARY" => array("id"),
- "cat_k" => array("cat(30)","k(30)"),
+ "cat_k" => array("UNIQUE", "cat(30)","k(30)"),
)
);
$database["contact"] = array(
),
"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(
if ($argc==2) {
switch ($argv[1]) {
+ case "dryrun":
+ update_structure(true, false);
+ return;
case "update":
update_structure(true, true);
// 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;
// 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']);
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.
$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);
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'])
+ );
+ }
} 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
$starttime = time();
- while ($r = q("SELECT * FROM `workerqueue` WHERE `executed` = '0000-00-00 00:00:00' ORDER BY `priority`, `created` LIMIT 1")) {
+ while ($r = poller_worker_process()) {
// Quit when in maintenance
if (get_config('system', 'maintenance', true))
if (function_exists($funcname)) {
logger("Process ".$mypid." - Prio ".$r[0]["priority"]." - ID ".$r[0]["id"].": ".$funcname." ".$r[0]["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");
sleep($cooldown);
}
}
- 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)) {
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`
+ WHERE `process`.`command` = 'poller.php'");
+
+ // 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() {
+
+ // 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;
+}
+
if (array_search(__file__,get_included_files())===0){
poller_run($_SERVER["argv"],$_SERVER["argc"]);
$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]);
}
}
/* ordering */
$valid_orders = array(
- 'contact.name',
+ 'contact.name',
'user.email',
'user.register_date',
'user.login_date',
- 'lastitem.lastitem_date',
+ 'lastitem_date',
'user.page-flags'
);
-
+
$order = "contact.name";
$order_direction = "+";
if (x($_GET,'o')){
$order_direction = "-";
$new_order = substr($new_order,1);
}
-
+
if (in_array($new_order, $valid_orders)){
$order = $new_order;
}
if (x($_GET,'d')){
$new_direction = $_GET['d'];
-
}
}
$sql_order = "`".str_replace('.','`.`',$order)."`";
$sql_order_direction = ($order_direction==="+")?"ASC":"DESC";
-
- $users = q("SELECT `user`.* , `contact`.`name` , `contact`.`url` , `contact`.`micro`, `lastitem`.`lastitem_date`, `user`.`account_expired`
- FROM
- (SELECT MAX(`item`.`changed`) as `lastitem_date`, `item`.`uid`
- FROM `item`
- WHERE `item`.`type` = 'wall'
- GROUP BY `item`.`uid`) AS `lastitem`
- RIGHT OUTER JOIN `user` ON `user`.`uid` = `lastitem`.`uid`,
- `contact`
- WHERE
- `user`.`uid` = `contact`.`uid`
- AND `user`.`verified` =1
- AND `contact`.`self` =1
- ORDER BY $sql_order $sql_order_direction LIMIT %d, %d
- ",
+
+ $users = q("SELECT `user`.*, `contact`.`name`, `contact`.`url`, `contact`.`micro`, `user`.`account_expired`,
+ (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`
+ WHERE `user`.`verified`
+ ORDER BY $sql_order $sql_order_direction LIMIT %d, %d",
intval($a->pager['start']),
intval($a->pager['itemspage'])
);
-
+
//echo "<pre>$users"; killme();
-
+
$adminlist = explode(",", str_replace(" ", "", $a->config['admin_email']));
$_setup_users = function ($e) use ($adminlist){
$accounts = array(
// If this is a comment, set the permissions from the parent.
if($parent_item) {
- $private = 0;
// for non native networks use the network of the original post as network of the item
if (($parent_item['network'] != NETWORK_DIASPORA)
AND ($network == ""))
$network = $parent_item['network'];
- if(($parent_item['private'])
- || strlen($parent_item['allow_cid'])
- || strlen($parent_item['allow_gid'])
- || strlen($parent_item['deny_cid'])
- || strlen($parent_item['deny_gid'])) {
- $private = (($parent_item['private']) ? $parent_item['private'] : 1);
- }
-
$str_contact_allow = $parent_item['allow_cid'];
$str_group_allow = $parent_item['allow_gid'];
$str_contact_deny = $parent_item['deny_cid'];
$str_group_deny = $parent_item['deny_gid'];
+ $private = $parent_item['private'];
}
+
$pubmail_enable = ((x($_REQUEST,'pubmail_enable') && intval($_REQUEST['pubmail_enable']) && (! $private)) ? 1 : 0);
// if using the API, we won't see pubmail_enable - figure out if it should be set
if(! count($r))
continue;
-
$r = q("UPDATE `photo` SET `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s'
WHERE `resource-id` = '%s' AND `uid` = %d AND `album` = '%s' ",
dbesc($str_contact_allow),
intval($profile_uid),
dbesc( t('Wall Photos'))
);
-
}
}
}
$datarray['self'] = $self;
// $datarray['prvnets'] = $user['prvnets'];
+ $datarray['parent-uri'] = ($parent == 0) ? $uri : $parent_item['uri'];
+ $datarray['plink'] = $a->get_baseurl().'/display/'.urlencode($datarray['guid']);
+ $datarray['last-child'] = 1;
+ $datarray['visible'] = 1;
+
if($orig_post)
$datarray['edit'] = true;
goaway($a->get_baseurl() . "/" . $return_path );
}
killme();
- }
- else
+ } else
$post_id = 0;
-
$r = q("INSERT INTO `item` (`guid`, `extid`, `uid`,`type`,`wall`,`gravity`, `network`, `contact-id`,
`owner-name`,`owner-link`,`owner-avatar`, `owner-id`,
`author-name`, `author-link`, `author-avatar`, `author-id`,
`tag`, `inform`, `verb`, `object-type`, `postopts`,
`allow_cid`, `allow_gid`, `deny_cid`, `deny_gid`, `private`,
`pubmail`, `attach`, `bookmark`,`origin`, `moderated`, `file`,
- `rendered-html`, `rendered-hash`)
+ `rendered-html`, `rendered-hash`,
+ `parent`, `parent-uri`, `plink`, `last-child`, `visible`)
VALUES('%s', '%s', %d, '%s', %d, %d, '%s', %d,
'%s', '%s', '%s', %d,
'%s', '%s', '%s', %d,
'%s', '%s', '%s', '%s', '%s',
'%s', '%s', '%s', '%s', %d,
%d, '%s', %d, %d, %d, '%s',
- '%s', '%s')",
+ '%s', '%s',
+ %d, '%s', '%s', %d, %d)",
dbesc($datarray['guid']),
dbesc($datarray['extid']),
intval($datarray['uid']),
intval($datarray['moderated']),
dbesc($datarray['file']),
dbesc($datarray['rendered-html']),
- dbesc($datarray['rendered-hash'])
+ dbesc($datarray['rendered-hash']),
+ intval($datarray['parent']),
+ dbesc($datarray['parent-uri']),
+ dbesc($datarray['plink']),
+ intval($datarray['last-child']),
+ intval($datarray['visible'])
);
$r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1",
logger('mod_item: saved item ' . $post_id);
$datarray["id"] = $post_id;
- $datarray["plink"] = $a->get_baseurl().'/display/'.urlencode($datarray["guid"]);
// update filetags in pconfig
file_tag_update_pconfig($uid,$categories_old,$categories_new,'category');
if($parent) {
// This item is the last leaf and gets the comment box, clear any ancestors
- $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent` = %d ",
+ $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent` = %d AND `last-child` AND `id` != %d",
dbesc(datetime_convert()),
- intval($parent)
+ intval($parent),
+ intval($post_id)
);
- update_thread($parent, true);
-
- // Inherit ACLs from the parent item.
- $r = q("UPDATE `item` SET `allow_cid` = '%s', `allow_gid` = '%s', `deny_cid` = '%s', `deny_gid` = '%s', `private` = %d
- WHERE `id` = %d",
- dbesc($parent_item['allow_cid']),
- dbesc($parent_item['allow_gid']),
- dbesc($parent_item['deny_cid']),
- dbesc($parent_item['deny_gid']),
- intval($parent_item['private']),
- intval($post_id)
+ // update the commented timestamp on the parent
+ q("UPDATE `item` SET `visible` = 1, `commented` = '%s', `changed` = '%s' WHERE `id` = %d",
+ dbesc(datetime_convert()),
+ dbesc(datetime_convert()),
+ intval($parent)
);
if($contact_record != $author) {
} else {
$parent = $post_id;
+ $r = q("UPDATE `item` SET `parent` = %d WHERE `id` = %d",
+ intval($parent),
+ intval($post_id));
+
if($contact_record != $author) {
notification(array(
'type' => NOTIFY_WALL,
}
}
- // fallback so that parent always gets set to non-zero.
-
- if(! $parent)
- $parent = $post_id;
-
- $r = q("UPDATE `item` SET `parent` = %d, `parent-uri` = '%s', `plink` = '%s', `changed` = '%s', `last-child` = 1, `visible` = 1
- WHERE `id` = %d",
- intval($parent),
- dbesc(($parent == $post_id) ? $uri : $parent_item['uri']),
- dbesc($a->get_baseurl().'/display/'.urlencode($datarray['guid'])),
- dbesc(datetime_convert()),
- intval($post_id)
- );
-
- // photo comments turn the corresponding item visible to the profile wall
- // This way we don't see every picture in your new photo album posted to your wall at once.
- // They will show up as people comment on them.
-
- if(! $parent_item['visible']) {
- $r = q("UPDATE `item` SET `visible` = 1 WHERE `id` = %d",
- intval($parent_item['id'])
- );
- update_thread($parent_item['id']);
- }
-
- // update the commented timestamp on the parent
-
- q("UPDATE `item` set `commented` = '%s', `changed` = '%s' WHERE `id` = %d",
- dbesc(datetime_convert()),
- dbesc(datetime_convert()),
- intval($parent)
- );
- if ($post_id != $parent)
- update_thread($parent);
-
call_hooks('post_local_end', $datarray);
if(strlen($emailcc) && $profile_uid == local_user()) {
if ($post_id == $parent)
add_thread($post_id);
+ else {
+ update_thread($parent, true);
+
+ // Insert an item entry for UID=0 for global entries
+ // We have to remove or change some data before that,
+ // so that the post appear like a regular received post.
+ unset($datarray['self']);
+ unset($datarray['wall']);
+ unset($datarray['origin']);
+
+ if (in_array($datarray['type'], array("net-comment", "wall-comment")))
+ $datarray['type'] = 'remote-comment';
+ elseif ($datarray['type'] == 'wall')
+ $datarray['type'] = 'remote';
+
+ add_shadow_entry($datarray);
+ }
// This is a real juggling act on shared hosting services which kill your processes
// e.g. dreamhost. We used to start delivery to our native delivery agents in the background
}
logger("cron_start");
- $users = q("SELECT profile.*, `user`.`login_date`, `lastitem`.`lastitem_date`
- FROM (SELECT MAX(`item`.`changed`) as `lastitem_date`, `item`.`uid`
- FROM `item`
- WHERE `item`.`type` = 'wall'
- GROUP BY `item`.`uid`) AS `lastitem`
- RIGHT OUTER JOIN `user` ON `user`.`uid` = `lastitem`.`uid`, `contact`, `profile`
- WHERE
- `user`.`uid` = `contact`.`uid` AND `profile`.`uid` = `user`.`uid`
- AND `profile`.`is-default` AND (`profile`.`publish` OR `profile`.`net-publish`)
- AND `user`.`verified` AND `contact`.`self`
- AND NOT `user`.`blocked`
- AND NOT `user`.`account_removed`
- AND NOT `user`.`account_expired`");
-
+ $users = q("SELECT `user`.`uid`, `user`.`login_date`,
+ (SELECT MAX(`changed`) FROM `item` FORCE INDEX (`uid_wall_changed`) WHERE `wall` AND `uid` = `user`.`uid`) AS `lastitem_date`
+ FROM `user`
+ INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid` AND `profile`.`is-default`
+ WHERE (`profile`.`publish` OR `profile`.`net-publish`) AND `user`.`verified`
+ AND NOT `user`.`blocked` AND NOT `user`.`account_removed`
+ AND NOT `user`.`account_expired`");
if (is_array($users)) {
$total_users = count($users);
$active_users_halfyear = 0;
<?php
-define('UPDATE_VERSION' , 1204);
+define('UPDATE_VERSION' , 1206);
/**
*
$idx = array_search($plugin, $plugins_arr);
if ($idx !== false){
unset($plugins_arr[$idx]);
- //delete forumlist manually from addon and hook table
+ //delete forumlist manually from addon and hook table
// since uninstall_plugin() don't work here
q("DELETE FROM `addon` WHERE `name` = 'forumlist' ");
q("DELETE FROM `hook` WHERE `file` = 'addon/forumlist/forumlist.php' ");
.hidden {
display: none !important;
}
+code {
+ white-space: pre;
+}
/*
* standard page elements