streams/include/channel.php

2857 lines
83 KiB
PHP
Raw Normal View History

<?php
/**
* @file include/channel.php
* @brief Channel related functions.
*/
2012-07-30 05:43:51 +00:00
2018-06-01 02:42:13 +00:00
use Zotlabs\Lib\Libzot;
2018-06-05 01:40:11 +00:00
use Zotlabs\Lib\Libsync;
use Zotlabs\Lib\AccessList;
use Zotlabs\Lib\Crypto;
use Zotlabs\Lib\Connect;
2018-08-24 05:44:22 +00:00
use Zotlabs\Access\PermissionRoles;
use Zotlabs\Access\PermissionLimits;
use Zotlabs\Access\Permissions;
use Zotlabs\Daemon\Master;
use Zotlabs\Lib\System;
use Zotlabs\Render\Comanche;
2018-06-01 02:42:13 +00:00
2015-09-09 00:51:48 +00:00
require_once('include/menu.php');
require_once('include/photo/photo_driver.php');
2012-07-30 05:43:51 +00:00
2013-12-04 01:33:48 +00:00
/**
* @brief Called when creating a new channel.
*
* Checks the account's service class and number of current channels to determine
* whether creating a new channel is within the current service class constraints.
2013-12-04 01:33:48 +00:00
*
* @param int $account_id
* Account_id used for this request
*
* @returns associative array with:
* * \e boolean \b success boolean true if creating a new channel is allowed for this account
* * \e string \b message (optional) if success is false, optional error text
* * \e int \b total_identities
2013-12-04 01:33:48 +00:00
*/
2012-08-27 06:05:00 +00:00
function identity_check_service_class($account_id) {
$ret = array('success' => false, 'message' => '');
2015-06-16 00:28:52 +00:00
$r = q("select count(channel_id) as total from channel where channel_account_id = %d and channel_removed = 0 ",
intval($account_id)
2012-08-27 06:05:00 +00:00
);
if(! ($r && count($r))) {
$ret['total_identities'] = 0;
2012-08-27 06:05:00 +00:00
$ret['message'] = t('Unable to obtain identity information from database');
return $ret;
}
2012-08-27 06:05:00 +00:00
$ret['total_identities'] = intval($r[0]['total']);
if (! account_service_class_allows($account_id, 'total_identities', $r[0]['total'])) {
$ret['message'] .= upgrade_message();
return $ret;
2012-08-27 06:05:00 +00:00
}
$ret['success'] = true;
2012-08-27 06:05:00 +00:00
return $ret;
}
2013-12-04 01:33:48 +00:00
/**
* @brief Determine if the channel name is allowed when creating a new channel.
*
2013-12-04 01:33:48 +00:00
* This action is pluggable.
* We're currently only checking for an empty name or one that exceeds our
* storage limit (191 chars). 191 chars is probably going to create a mess on
* some pages.
* Plugins can set additional policies such as full name requirements, character
* sets, multi-byte length, etc.
2013-12-04 01:33:48 +00:00
*
* @param string $name
* @return string describing the error state, or nil return if name is valid
2013-12-04 01:33:48 +00:00
*/
function validate_channelname($name) {
if (! $name)
return t('Empty name');
if (mb_strlen($name) > 191)
return t('Name too long');
$arr = ['name' => $name];
/**
* @hooks validate_channelname
* Used to validate the names used by a channel.
* * \e string \b name
* * \e string \b message - return error message
*/
call_hooks('validate_channelname', $arr);
if (x($arr, 'message'))
return $arr['message'];
}
2012-08-27 06:05:00 +00:00
2013-12-04 01:33:48 +00:00
/**
* @brief Create a system channel - which has no account attached.
2013-12-04 01:33:48 +00:00
*
*/
function create_sys_channel() {
if (get_sys_channel())
return;
// Ensure that there is a host keypair.
if ((! get_config('system', 'pubkey')) && (! get_config('system', 'prvkey'))) {
$hostkey = Crypto::new_keypair(4096);
set_config('system', 'pubkey', $hostkey['pubkey']);
set_config('system', 'prvkey', $hostkey['prvkey']);
}
create_identity([
'account_id' => 'xxx', // Typecast trickery: account_id is required. This will create an identity with an (integer) account_id of 0
'nickname' => 'sys',
'name' => 'System',
'pageflags' => 0,
'publish' => 0,
'system' => 1
]);
}
/**
* @brief Returns the sys channel.
*
* @return array|boolean
*/
function get_sys_channel() {
2015-06-16 00:28:52 +00:00
$r = q("select * from channel left join xchan on channel_hash = xchan_hash where channel_system = 1 limit 1");
if ($r)
return $r[0];
return false;
}
/**
* @brief Checks if $channel_id is sys channel.
*
* @param int $channel_id
* @return boolean
*/
function is_sys_channel($channel_id) {
2015-06-16 00:28:52 +00:00
$r = q("select channel_system from channel where channel_id = %d and channel_system = 1 limit 1",
intval($channel_id)
);
2015-06-16 00:28:52 +00:00
if($r)
return true;
return false;
}
2013-12-04 01:33:48 +00:00
/**
* @brief Return the total number of channels on this site.
2013-12-04 01:33:48 +00:00
*
* No filtering is performed except to check PAGE_REMOVED.
2013-12-04 01:33:48 +00:00
*
* @returns int|booleean
* on error returns boolean false
2013-12-04 01:33:48 +00:00
*/
2013-07-25 01:17:00 +00:00
function channel_total() {
2015-06-16 00:28:52 +00:00
$r = q("select channel_id from channel where channel_removed = 0");
if (is_array($r))
2013-07-25 01:17:00 +00:00
return count($r);
2013-07-25 01:17:00 +00:00
return false;
}
2013-12-04 01:33:48 +00:00
/**
* @brief Create a new channel.
2013-12-04 01:33:48 +00:00
*
* Also creates the related xchan, hubloc, profile, and "self" abook records,
* and an empty "Friends" group/collection for the new channel.
2013-12-04 01:33:48 +00:00
*
* @param array $arr associative array with:
* * \e string \b name full name of channel
* * \e string \b nickname "email/url-compliant" nickname
* * \e int \b account_id to attach with this channel
* * [other identity fields as desired]
2013-12-04 01:33:48 +00:00
*
* @returns array
* 'success' => boolean true or false
* 'message' => optional error text if success is false
* 'channel' => if successful the created channel array
*/
2012-08-24 03:00:10 +00:00
function create_identity($arr) {
2012-08-27 06:05:00 +00:00
$ret = array('success' => false);
if(! $arr['account_id']) {
2015-12-29 19:16:13 +00:00
$ret['message'] = t('No account identifier');
2012-08-27 06:05:00 +00:00
return $ret;
}
$ret = identity_check_service_class($arr['account_id']);
if (!$ret['success']) {
return $ret;
}
// save this for auto_friending
$total_identities = $ret['total_identities'];
2013-08-28 00:19:52 +00:00
$nick = mb_strtolower(trim($arr['nickname']));
if(! $nick) {
$ret['message'] = t('Nickname is required.');
return $ret;
}
2012-08-27 06:05:00 +00:00
$name = escape_tags($arr['name']);
$pageflags = ((x($arr,'pageflags')) ? intval($arr['pageflags']) : PAGE_NORMAL);
$system = ((x($arr,'system')) ? intval($arr['system']) : 0);
$name_error = validate_channelname($arr['name']);
if($name_error) {
$ret['message'] = $name_error;
return $ret;
}
2015-06-16 00:28:52 +00:00
if($nick === 'sys' && (! $system)) {
$ret['message'] = t('Reserved nickname. Please choose another.');
return $ret;
}
2012-08-24 08:57:36 +00:00
if(check_webbie(array($nick)) !== $nick) {
2012-08-24 08:14:42 +00:00
$ret['message'] = t('Nickname has unsupported characters or is already being used on this site.');
return $ret;
}
2018-06-01 02:42:13 +00:00
$guid = Libzot::new_uid($nick);
$key = Crypto::new_keypair(4096);
2012-08-24 03:00:10 +00:00
2018-06-01 02:42:13 +00:00
$sig = Libzot::sign($guid,$key['prvkey']);
$hash = Libzot::make_xchan_hash($guid,$key['pubkey']);
2013-02-12 10:02:35 +00:00
// Force a few things on the short term until we can provide a theme or app with choice
$publish = 1;
if(array_key_exists('publish', $arr))
$publish = intval($arr['publish']);
$role_permissions = null;
if(array_key_exists('permissions_role',$arr) && $arr['permissions_role']) {
2018-08-24 05:44:22 +00:00
$role_permissions = PermissionRoles::role_perms($arr['permissions_role']);
2013-08-19 03:20:03 +00:00
}
2013-02-15 01:39:16 +00:00
2016-07-19 08:26:28 +00:00
if($role_permissions && array_key_exists('directory_publish',$role_permissions))
$publish = intval($role_permissions['directory_publish']);
$primary = true;
2016-07-19 08:26:28 +00:00
if(array_key_exists('primary', $arr))
$primary = intval($arr['primary']);
$expire = 0;
2014-09-18 04:16:15 +00:00
$r = channel_store_lowlevel(
2017-03-29 13:16:41 +00:00
[
'channel_account_id' => intval($arr['account_id']),
'channel_primary' => intval($primary),
'channel_name' => $name,
'channel_address' => $nick,
'channel_guid' => $guid,
'channel_guid_sig' => $sig,
'channel_hash' => $hash,
'channel_prvkey' => $key['prvkey'],
'channel_pubkey' => $key['pubkey'],
'channel_pageflags' => intval($pageflags),
'channel_system' => intval($system),
'channel_expire_days' => intval($expire),
'channel_timezone' => App::$timezone
]
2012-08-24 08:14:42 +00:00
);
2013-08-19 03:20:03 +00:00
$r = q("select * from channel where channel_account_id = %d
and channel_guid = '%s' limit 1",
2012-08-27 06:05:00 +00:00
intval($arr['account_id']),
2012-08-24 08:14:42 +00:00
dbesc($guid)
);
2012-08-27 06:05:00 +00:00
2013-02-12 10:02:35 +00:00
if(! $r) {
2012-08-24 08:14:42 +00:00
$ret['message'] = t('Unable to retrieve created identity');
return $ret;
}
$a = q("select * from account where account_id = %d",
intval($arr['account_id'])
);
2017-09-20 07:00:26 +00:00
$photo_type = null;
$z = [
'account' => $a[0],
'channel' => $r[0],
'photo_url' => ''
];
/**
* @hooks create_channel_photo
* * \e array \b account
* * \e array \b channel
* * \e string \b photo_url - Return value
*/
call_hooks('create_channel_photo', $z);
if($z['photo_url']) {
2017-09-20 07:00:26 +00:00
$photo_type = import_channel_photo_from_url($z['photo_url'],$arr['account_id'],$r[0]['channel_id']);
}
2016-07-19 08:26:28 +00:00
if($role_permissions && array_key_exists('limits',$role_permissions))
$perm_limits = $role_permissions['limits'];
else
$perm_limits = site_default_perms();
foreach($perm_limits as $p => $v)
2018-08-24 05:44:22 +00:00
PermissionLimits::Set($r[0]['channel_id'],$p,$v);
2016-07-19 08:26:28 +00:00
if($role_permissions && array_key_exists('perms_auto',$role_permissions))
set_pconfig($r[0]['channel_id'],'system','autoperms',intval($role_permissions['perms_auto']));
$ret['channel'] = $r[0];
2012-08-27 06:05:00 +00:00
if(intval($arr['account_id']))
set_default_login_identity($arr['account_id'],$ret['channel']['channel_id'],false);
2012-10-02 02:04:21 +00:00
2012-08-27 06:05:00 +00:00
// Create a verified hub location pointing to this site.
2017-01-30 23:01:22 +00:00
$r = hubloc_store_lowlevel(
[
'hubloc_guid' => $guid,
'hubloc_guid_sig' => $sig,
2018-05-24 04:48:19 +00:00
'hubloc_id_url' => channel_url($ret['channel']),
2017-01-30 23:01:22 +00:00
'hubloc_hash' => $hash,
'hubloc_addr' => channel_reddress($ret['channel']),
2017-12-11 03:43:54 +00:00
'hubloc_primary' => intval($primary),
2017-01-30 23:01:22 +00:00
'hubloc_url' => z_root(),
2018-06-01 02:42:13 +00:00
'hubloc_url_sig' => Libzot::sign(z_root(),$ret['channel']['channel_prvkey']),
'hubloc_site_id' => Libzot::make_xchan_hash(z_root(),get_config('system','pubkey')),
2017-01-30 23:01:22 +00:00
'hubloc_host' => App::get_hostname(),
2018-05-30 04:08:52 +00:00
'hubloc_callback' => z_root() . '/zot',
2017-01-30 23:01:22 +00:00
'hubloc_sitekey' => get_config('system','pubkey'),
2018-04-23 06:48:49 +00:00
'hubloc_network' => 'zot6',
2017-01-30 23:01:22 +00:00
'hubloc_updated' => datetime_convert()
]
2012-08-27 06:05:00 +00:00
);
if(! $r)
logger('Unable to store hub location');
2012-08-28 08:55:18 +00:00
$newuid = $ret['channel']['channel_id'];
2012-08-28 08:55:18 +00:00
$r = xchan_store_lowlevel(
[
'xchan_hash' => $hash,
'xchan_guid' => $guid,
'xchan_guid_sig' => $sig,
'xchan_pubkey' => $key['pubkey'],
2017-09-20 07:00:26 +00:00
'xchan_photo_mimetype' => (($photo_type) ? $photo_type : 'image/png'),
'xchan_photo_l' => z_root() . "/photo/profile/l/{$newuid}",
'xchan_photo_m' => z_root() . "/photo/profile/m/{$newuid}",
'xchan_photo_s' => z_root() . "/photo/profile/s/{$newuid}",
'xchan_addr' => channel_reddress($ret['channel']),
2018-05-24 04:48:19 +00:00
'xchan_url' => channel_url($ret['channel']),
'xchan_follow' => z_root() . '/follow?f=&url=%s',
'xchan_connurl' => z_root() . '/poco/' . $ret['channel']['channel_address'],
'xchan_name' => $ret['channel']['channel_name'],
'xchan_network' => 'zot6',
'xchan_photo_date' => datetime_convert(),
'xchan_name_date' => datetime_convert(),
'xchan_system' => $system
]
2012-10-02 02:04:21 +00:00
);
// Not checking return value.
2012-10-02 02:04:21 +00:00
// It's ok for this to fail if it's an imported channel, and therefore the hash is a duplicate
$r = profile_store_lowlevel(
[
'aid' => intval($ret['channel']['channel_account_id']),
'uid' => intval($newuid),
'profile_guid' => new_uuid(),
'profile_name' => t('Default Profile'),
'is_default' => 1,
'publish' => $publish,
'fullname' => $ret['channel']['channel_name'],
'photo' => z_root() . "/photo/profile/l/{$newuid}",
'thumb' => z_root() . "/photo/profile/m/{$newuid}"
]
2012-08-28 08:55:18 +00:00
);
if($role_permissions) {
2016-07-19 08:26:28 +00:00
$myperms = ((array_key_exists('perms_connect',$role_permissions)) ? $role_permissions['perms_connect'] : array());
}
else {
2018-08-24 05:44:22 +00:00
$x = PermissionRoles::role_perms('social');
2016-07-19 08:26:28 +00:00
$myperms = $x['perms_connect'];
}
$r = abook_store_lowlevel(
[
'abook_account' => intval($ret['channel']['channel_account_id']),
'abook_channel' => intval($newuid),
'abook_xchan' => $hash,
'abook_closeness' => 0,
'abook_created' => datetime_convert(),
'abook_updated' => datetime_convert(),
'abook_self' => 1
]
);
2018-08-27 06:24:27 +00:00
$x = Permissions::serialise(Permissions::FilledPerms($myperms));
2018-06-08 03:17:30 +00:00
set_abconfig($newuid,$hash,'system','my_perms',$x);
2016-07-19 08:26:28 +00:00
if(intval($ret['channel']['channel_account_id'])) {
2012-08-28 08:55:18 +00:00
// Save our permissions role so we can perhaps call it up and modify it later.
2014-09-18 04:52:30 +00:00
if($role_permissions) {
set_pconfig($newuid,'system','permissions_role',$arr['permissions_role']);
2014-09-18 04:52:30 +00:00
if(array_key_exists('online',$role_permissions))
2014-09-29 02:30:47 +00:00
set_pconfig($newuid,'system','hide_presence',1-intval($role_permissions['online']));
2016-07-19 04:37:34 +00:00
if(array_key_exists('perms_auto',$role_permissions)) {
$autoperms = intval($role_permissions['perms_auto']);
set_pconfig($newuid,'system','autoperms',$autoperms);
}
2014-09-18 04:52:30 +00:00
}
// Create a group with yourself as a member. This allows somebody to use it
// right away as a default group for new contacts.
2012-08-28 08:55:18 +00:00
AccessList::add($newuid, t('Friends'));
AccessList::member_add($newuid,t('Friends'),$ret['channel']['channel_hash']);
2013-02-12 10:02:35 +00:00
// if our role_permissions indicate that we're using a default collection ACL, add it.
if(is_array($role_permissions) && $role_permissions['default_collection']) {
2018-09-26 00:47:43 +00:00
$r = q("select hash from pgrp where uid = %d and gname = '%s' limit 1",
intval($newuid),
dbesc( t('Friends') )
);
if($r) {
PostgreSQL support initial commit There were 11 main types of changes: - UPDATE's and DELETE's sometimes had LIMIT 1 at the end of them. This is not only non-compliant but it would certainly not do what whoever wrote it thought it would. It is likely this mistake was just copied from Friendica. All of these instances, the LIMIT 1 was simply removed. - Bitwise operations (and even some non-zero int checks) erroneously rely on MySQL implicit integer-boolean conversion in the WHERE clauses. This is non-compliant (and bad programming practice to boot). Proper explicit boolean conversions were added. New queries should use proper conventions. - MySQL has a different operator for bitwise XOR than postgres. Rather than add yet another dba_ func, I converted them to "& ~" ("AND NOT") when turning off, and "|" ("OR") when turning on. There were no true toggles (XOR). New queries should refrain from using XOR when not necessary. - There are several fields which the schema has marked as NOT NULL, but the inserts don't specify them. The reason this works is because mysql totally ignores the constraint and adds an empty text default automatically. Again, non-compliant, obviously. In these cases a default of empty text was added. - Several statements rely on a non-standard MySQL feature (http://dev.mysql.com/doc/refman/5.5/en/group-by-handling.html). These queries can all be rewritten to be standards compliant. Interestingly enough, the newly rewritten standards compliant queries run a zillion times faster, even on MySQL. - A couple of function/operator name translations were needed (RAND/RANDOM, GROUP_CONCAT/STRING_AGG, UTC_NOW, REGEXP/~, ^/#) -- assist functions added in the dba_ - INTERVALs: postgres requires quotes around the value, mysql requires that there are not quotes around the value -- assist functions added in the dba_ - NULL_DATE's -- Postgres does not allow the invalid date '0000-00-00 00:00:00' (there is no such thing as year 0 or month 0 or day 0). We use '0001-01-01 00:00:00' for postgres. Conversions are handled in Zot/item packets automagically by quoting all dates with dbescdate(). - char(##) specifications in the schema creates fields with blank spaces that aren't trimmed in the code. MySQL apparently treats char(##) as varchar(##), again, non-compliant. Since postgres works better with text fields anyway, this ball of bugs was simply side-stepped by using 'text' datatype for all text fields in the postgres schema. varchar was used in a couple of places where it actually seemed appropriate (size constraint), but without rigorously vetting that all of the PHP code actually validates data, new bugs might come out from under the rug. - postgres doesn't store nul bytes and a few other non-printables in text fields, even when quoted. bytea fields were used when storing binary data (photo.data, attach.data). A new dbescbin() function was added to handle this transparently. - postgres does not support LIMIT #,# syntax. All databases support LIMIT # OFFSET # syntax. Statements were updated to be standard. These changes require corresponding changes in the coding standards. Please review those before adding any code going forward. Still on my TODO list: - remove quotes from non-reserved identifiers and make reserved identifiers use dba func for quoting - Rewrite search queries for better results (both MySQL and Postgres)
2014-11-13 20:21:58 +00:00
q("update channel set channel_default_group = '%s', channel_allow_gid = '%s' where channel_id = %d",
2014-10-21 23:33:35 +00:00
dbesc($r[0]['hash']),
dbesc('<' . $r[0]['hash'] . '>'),
intval($newuid)
);
}
}
if(! $system) {
2018-08-01 02:09:42 +00:00
set_pconfig($ret['channel']['channel_id'],'system','photo_path', '%Y/%Y-%m');
set_pconfig($ret['channel']['channel_id'],'system','attach_path','%Y/%Y-%m');
}
// auto-follow any of the hub's pre-configured channel choices.
// Only do this if it's the first channel for this account;
// otherwise it could get annoying. Don't make this list too big
// or it will impact registration time.
$accts = get_config('system','auto_follow');
if(($accts) && (! $total_identities)) {
if(! is_array($accts))
$accts = array($accts);
foreach($accts as $acct) {
if(trim($acct)) {
$f = Connect::connect($ret['channel'],trim($acct));
if($f['success']) {
$clone = [];
foreach($f['abook'] as $k => $v) {
if(strpos($k,'abook_') === 0) {
$clone[$k] = $v;
}
}
unset($clone['abook_id']);
unset($clone['abook_account']);
unset($clone['abook_channel']);
$abconfig = load_abconfig($ret['channel']['channel_id'],$clone['abook_xchan']);
if($abconfig) {
$clone['abconfig'] = $abconfig;
}
2019-01-23 03:11:53 +00:00
Libsync::build_sync_packet($ret['channel']['channel_id'], [ 'abook' => [ $clone ] ], true);
$can_view_stream = their_perms_contains($ret['channel']['channel_id'],$clone['abook_xchan'],'view_stream');
// If we can view their stream, pull in some posts
if(($can_view_stream) || ($f['abook']['xchan_network'] === 'rss')) {
Master::Summon([ 'Onepoll',$f['abook']['abook_id'] ]);
}
}
}
}
}
/**
* @hooks create_identity
* Called when creating a channel.
* * \e int - The UID of the created identity
*/
call_hooks('create_identity', $newuid);
2018-08-24 05:44:22 +00:00
Master::Summon(array('Directory', $ret['channel']['channel_id']));
}
2013-02-12 10:02:35 +00:00
2012-08-24 08:14:42 +00:00
$ret['success'] = true;
return $ret;
2012-08-24 03:00:10 +00:00
}
function change_channel_keys($channel) {
$ret = array('success' => false);
$stored = [];
$key = Crypto::new_keypair(4096);
2018-06-01 02:42:13 +00:00
$sig = Libzot::sign($channel['channel_guid'],$key['prvkey']);
$hash = Libzot::make_xchan_hash($channel['channel_guid'],$channel['channel_pubkey']);
$stored['old_guid'] = $channel['channel_guid'];
$stored['old_guid_sig'] = $channel['channel_guid_sig'];
$stored['old_key'] = $channel['channel_pubkey'];
$stored['old_hash'] = $channel['channel_hash'];
$stored['new_key'] = $key['pubkey'];
2018-06-01 02:42:13 +00:00
$stored['new_sig'] = Libzot::sign($key['pubkey'],$channel['channel_prvkey']);
// Save this info for the notifier to collect
set_pconfig($channel['channel_id'],'system','keychange',$stored);
$r = q("update channel set channel_prvkey = '%s', channel_pubkey = '%s', channel_guid_sig = '%s', channel_hash = '%s' where channel_id = %d",
dbesc($key['prvkey']),
dbesc($key['pubkey']),
dbesc($sig),
dbesc($hash),
intval($channel['channel_id'])
);
if(! $r) {
return $ret;
}
2017-07-26 00:58:46 +00:00
$r = q("select * from channel where channel_id = %d",
intval($channel['channel_id'])
);
if(! $r) {
$ret['message'] = t('Unable to retrieve modified identity');
return $ret;
}
$modified = $r[0];
$h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ",
dbesc($stored['old_hash']),
dbesc(z_root())
);
if($h) {
foreach($h as $hv) {
$hv['hubloc_guid_sig'] = $sig;
$hv['hubloc_hash'] = $hash;
2018-06-01 02:42:13 +00:00
$hv['hubloc_url_sig'] = Libzot::sign(z_root(),$modifed['channel_prvkey']);
hubloc_store_lowlevel($hv);
}
}
$x = q("select * from xchan where xchan_hash = '%s' ",
dbesc($stored['old_hash'])
);
$check = q("select * from xchan where xchan_hash = '%s'",
dbesc($hash)
);
if(($x) && (! $check)) {
$oldxchan = $x[0];
foreach($x as $xv) {
$xv['xchan_guid_sig'] = $sig;
$xv['xchan_hash'] = $hash;
$xv['xchan_pubkey'] = $key['pubkey'];
xchan_store_lowlevel($xv);
$newxchan = $xv;
}
}
2018-06-05 01:40:11 +00:00
Libsync::build_sync_packet($channel['channel_id'], [ 'keychange' => $stored ]);
2017-07-26 00:58:46 +00:00
$a = q("select * from abook where abook_xchan = '%s' and abook_self = 1",
dbesc($stored['old_hash'])
);
if($a) {
q("update abook set abook_xchan = '%s' where abook_id = %d",
dbesc($hash),
intval($a[0]['abook_id'])
);
}
xchan_change_key($oldxchan,$newxchan,$stored);
2018-08-24 05:44:22 +00:00
Master::Summon([ 'Notifier', 'keychange', $channel['channel_id'] ]);
$ret['success'] = true;
return $ret;
}
2017-08-05 02:02:41 +00:00
function channel_change_address($channel,$new_address) {
$ret = array('success' => false);
$old_address = $channel['channel_address'];
if($new_address === 'sys') {
$ret['message'] = t('Reserved nickname. Please choose another.');
return $ret;
}
2017-08-05 02:02:41 +00:00
if(check_webbie(array($new_address)) !== $new_address) {
$ret['message'] = t('Nickname has unsupported characters or is already being used on this site.');
return $ret;
}
2017-08-05 02:02:41 +00:00
$r = q("update channel set channel_address = '%s' where channel_id = %d",
dbesc($new_address),
intval($channel['channel_id'])
);
if(! $r) {
return $ret;
}
$r = q("select * from channel where channel_id = %d",
intval($channel['channel_id'])
);
if(! $r) {
$ret['message'] = t('Unable to retrieve modified identity');
return $ret;
}
$r = q("update xchan set xchan_addr = '%s' where xchan_hash = '%s'",
dbesc($new_address . '@' . App::get_hostname()),
dbesc($channel['channel_hash'])
);
$h = q("select * from hubloc where hubloc_hash = '%s' and hubloc_url = '%s' ",
dbesc($channel['channel_hash']),
dbesc(z_root())
);
if($h) {
foreach($h as $hv) {
if($hv['hubloc_primary']) {
q("update hubloc set hubloc_primary = 0 where hubloc_id = %d",
intval($hv['hubloc_id'])
);
}
q("update hubloc set hubloc_deleted = 1 where hubloc_id = %d",
intval($hv['hubloc_id'])
);
unset($hv['hubloc_id']);
$hv['hubloc_addr'] = $new_address . '@' . App::get_hostname();
hubloc_store_lowlevel($hv);
}
}
// fix apps which were stored with the actual name rather than a macro
$r = q("select * from app where app_channel = %d and app_system = 1",
intval($channel['channel_id'])
);
if($r) {
foreach($r as $rv) {
$replace = preg_replace('/([\=\/])(' . $old_address . ')($|[\%\/])/ism','$1' . $new_address . '$3',$rv['app_url']);
if($replace != $rv['app_url']) {
q("update app set app_url = '%s' where id = %d",
dbesc($replace),
intval($rv['id'])
);
}
}
}
2017-08-05 02:02:41 +00:00
2018-08-24 05:44:22 +00:00
Master::Summon(array('Notifier', 'refresh_all', $channel['channel_id']));
2017-08-05 02:02:41 +00:00
$ret['success'] = true;
return $ret;
}
2013-12-04 01:33:48 +00:00
/**
* @brief Set default channel to be used on login.
2013-12-04 01:33:48 +00:00
*
* @param int $account_id
* login account
* @param int $channel_id
* channel id to set as default for this account
* @param boolean $force (optional) default true
* - if true, set this default unconditionally
* - if $force is false only do this if there is no existing default
2013-12-04 01:33:48 +00:00
*/
function set_default_login_identity($account_id, $channel_id, $force = true) {
$r = q("select account_default_channel from account where account_id = %d limit 1",
2012-08-27 06:05:00 +00:00
intval($account_id)
);
if ($r) {
if ((intval($r[0]['account_default_channel']) == 0) || ($force)) {
PostgreSQL support initial commit There were 11 main types of changes: - UPDATE's and DELETE's sometimes had LIMIT 1 at the end of them. This is not only non-compliant but it would certainly not do what whoever wrote it thought it would. It is likely this mistake was just copied from Friendica. All of these instances, the LIMIT 1 was simply removed. - Bitwise operations (and even some non-zero int checks) erroneously rely on MySQL implicit integer-boolean conversion in the WHERE clauses. This is non-compliant (and bad programming practice to boot). Proper explicit boolean conversions were added. New queries should use proper conventions. - MySQL has a different operator for bitwise XOR than postgres. Rather than add yet another dba_ func, I converted them to "& ~" ("AND NOT") when turning off, and "|" ("OR") when turning on. There were no true toggles (XOR). New queries should refrain from using XOR when not necessary. - There are several fields which the schema has marked as NOT NULL, but the inserts don't specify them. The reason this works is because mysql totally ignores the constraint and adds an empty text default automatically. Again, non-compliant, obviously. In these cases a default of empty text was added. - Several statements rely on a non-standard MySQL feature (http://dev.mysql.com/doc/refman/5.5/en/group-by-handling.html). These queries can all be rewritten to be standards compliant. Interestingly enough, the newly rewritten standards compliant queries run a zillion times faster, even on MySQL. - A couple of function/operator name translations were needed (RAND/RANDOM, GROUP_CONCAT/STRING_AGG, UTC_NOW, REGEXP/~, ^/#) -- assist functions added in the dba_ - INTERVALs: postgres requires quotes around the value, mysql requires that there are not quotes around the value -- assist functions added in the dba_ - NULL_DATE's -- Postgres does not allow the invalid date '0000-00-00 00:00:00' (there is no such thing as year 0 or month 0 or day 0). We use '0001-01-01 00:00:00' for postgres. Conversions are handled in Zot/item packets automagically by quoting all dates with dbescdate(). - char(##) specifications in the schema creates fields with blank spaces that aren't trimmed in the code. MySQL apparently treats char(##) as varchar(##), again, non-compliant. Since postgres works better with text fields anyway, this ball of bugs was simply side-stepped by using 'text' datatype for all text fields in the postgres schema. varchar was used in a couple of places where it actually seemed appropriate (size constraint), but without rigorously vetting that all of the PHP code actually validates data, new bugs might come out from under the rug. - postgres doesn't store nul bytes and a few other non-printables in text fields, even when quoted. bytea fields were used when storing binary data (photo.data, attach.data). A new dbescbin() function was added to handle this transparently. - postgres does not support LIMIT #,# syntax. All databases support LIMIT # OFFSET # syntax. Statements were updated to be standard. These changes require corresponding changes in the coding standards. Please review those before adding any code going forward. Still on my TODO list: - remove quotes from non-reserved identifiers and make reserved identifiers use dba func for quoting - Rewrite search queries for better results (both MySQL and Postgres)
2014-11-13 20:21:58 +00:00
$r = q("update account set account_default_channel = %d where account_id = %d",
intval($channel_id),
intval($account_id)
);
}
2012-07-30 05:43:51 +00:00
}
2012-08-27 06:05:00 +00:00
}
/**
* @brief Return an array with default list of sections to export.
*
* @return array with default section names to export
*/
function get_default_export_sections() {
$sections = [
'channel',
'connections',
'config',
'apps',
'chatrooms',
'events',
'webpages',
'mail',
'wikis'
];
$cb = [ 'sections' => $sections ];
/**
* @hooks get_default_export_sections
* Called to get the default list of functional data groups to export in identity_basic_export().
* * \e array \b sections - return value
*/
call_hooks('get_default_export_sections', $cb);
return $cb['sections'];
}
2013-12-04 01:33:48 +00:00
/**
* @brief Create an array representing the important channel information
2013-12-04 01:33:48 +00:00
* which would be necessary to create a nomadic identity clone. This includes
* most channel resources and connection information with the exception of content.
*
* @param int $channel_id
* Channel_id to export
* @param array $sections (optional)
* Which sections to include in the export, default see get_default_export_sections()
* @return array
2013-12-04 01:33:48 +00:00
* See function for details
*/
function identity_basic_export($channel_id, $sections = null) {
/*
* basic channel export
*/
if(! $sections) {
$sections = get_default_export_sections();
}
$ret = [];
// use constants here as otherwise we will have no idea if we can import from a site
2016-01-18 00:29:32 +00:00
// with a non-standard platform and version.
$ret['compatibility'] = [
2018-10-04 20:45:20 +00:00
'project' => PLATFORM_NAME,
'codebase' => ((defined('NOMADIC')) ? 'zap' : 'osada'),
'version' => STD_VERSION,
'database' => DB_UPDATE_VERSION,
2018-08-24 05:44:22 +00:00
'server_role' => System::get_server_role()
];
/*
* Process channel information regardless of it is one of the sections desired
* because we need the channel relocation information in all export files/streams.
*/
$r = q("select * from channel where channel_id = %d limit 1",
intval($channel_id)
);
if($r) {
$ret['relocate'] = [ 'channel_address' => $r[0]['channel_address'], 'url' => z_root()];
if(in_array('channel',$sections)) {
$ret['channel'] = $r[0];
unset($ret['channel']['channel_password']);
unset($ret['channel']['channel_salt']);
}
}
if(in_array('channel',$sections) || in_array('profile',$sections)) {
$r = q("select * from profile where uid = %d",
intval($channel_id)
);
if($r)
$ret['profile'] = $r;
$r = q("select mimetype, content, os_storage from photo
where imgscale = 4 and photo_usage = %d and uid = %d limit 1",
intval(PHOTO_PROFILE),
intval($channel_id)
);
if($r) {
$ret['photo'] = [
'type' => $r[0]['mimetype'],
'data' => (($r[0]['os_storage'])
? base64url_encode(file_get_contents($r[0]['content'])) : base64url_encode($r[0]['content']))
];
}
}
if(in_array('connections',$sections)) {
$xchans = array();
$r = q("select * from abook where abook_channel = %d ",
intval($channel_id)
);
if($r) {
$ret['abook'] = $r;
for($x = 0; $x < count($ret['abook']); $x ++) {
$xchans[] = $ret['abook'][$x]['abook_xchan'];
$abconfig = load_abconfig($channel_id,$ret['abook'][$x]['abook_xchan']);
if($abconfig)
$ret['abook'][$x]['abconfig'] = $abconfig;
}
stringify_array_elms($xchans);
}
if($xchans) {
$r = q("select * from xchan where xchan_hash in ( " . implode(',',$xchans) . " ) ");
if($r)
$ret['xchan'] = $r;
$r = q("select * from hubloc where hubloc_hash in ( " . implode(',',$xchans) . " ) ");
if($r)
$ret['hubloc'] = $r;
}
2018-09-26 00:47:43 +00:00
$r = q("select * from pgrp where uid = %d ",
intval($channel_id)
);
if($r)
$ret['group'] = $r;
2018-09-26 00:47:43 +00:00
$r = q("select * from pgrp_member where uid = %d ",
intval($channel_id)
);
if($r)
$ret['group_member'] = $r;
}
if(in_array('config',$sections)) {
$r = q("select * from pconfig where uid = %d",
intval($channel_id)
);
if($r)
$ret['config'] = $r;
// All other term types will be included in items, if requested.
$r = q("select * from term where ttype in (%d,%d) and uid = %d",
intval(TERM_SAVEDSEARCH),
intval(TERM_THING),
intval($channel_id)
);
if($r)
$ret['term'] = $r;
// add psuedo-column obj_baseurl to aid in relocations
$r = q("select obj.*, '%s' as obj_baseurl from obj where obj_channel = %d",
dbesc(z_root()),
intval($channel_id)
);
2015-09-02 05:26:21 +00:00
if($r)
$ret['obj'] = $r;
2015-09-04 01:44:40 +00:00
$r = q("select * from likes where channel_id = %d",
intval($channel_id)
);
if($r)
$ret['likes'] = $r;
2015-09-08 05:54:43 +00:00
}
if(in_array('apps',$sections)) {
$r = q("select * from app where app_channel = %d and app_system = 0",
intval($channel_id)
);
if($r) {
for($x = 0; $x < count($r); $x ++) {
$r[$x]['term'] = q("select * from term where otype = %d and oid = %d",
intval(TERM_OBJ_APP),
intval($r[$x]['id'])
);
}
$ret['app'] = $r;
2015-09-09 00:51:48 +00:00
}
}
2015-09-08 05:54:43 +00:00
if(in_array('chatrooms',$sections)) {
$r = q("select * from chatroom where cr_uid = %d",
intval($channel_id)
);
if($r)
$ret['chatroom'] = $r;
}
if(in_array('events',$sections)) {
$r = q("select * from event where uid = %d",
intval($channel_id)
);
if($r)
$ret['event'] = $r;
$r = q("select * from item where resource_type = 'event' and uid = %d",
intval($channel_id)
);
if($r) {
$ret['event_item'] = array();
xchan_query($r);
$r = fetch_post_tags($r,true);
foreach($r as $rr)
$ret['event_item'][] = encode_item($rr,true);
}
2015-10-15 00:49:35 +00:00
}
2015-10-14 20:11:24 +00:00
if(in_array('webpages',$sections)) {
$x = menu_list($channel_id);
if($x) {
$ret['menu'] = array();
for($y = 0; $y < count($x); $y ++) {
$m = menu_fetch($x[$y]['menu_name'],$channel_id,$ret['channel']['channel_hash']);
if($m)
$ret['menu'][] = menu_element($ret['channel'],$m);
}
2015-10-14 20:11:24 +00:00
}
$r = q("select * from item where item_type in ( "
. ITEM_TYPE_BLOCK . "," . ITEM_TYPE_PDL . "," . ITEM_TYPE_WEBPAGE . " ) and uid = %d",
intval($channel_id)
);
if($r) {
$ret['webpages'] = array();
xchan_query($r);
$r = fetch_post_tags($r,true);
foreach($r as $rr)
$ret['webpages'][] = encode_item($rr,true);
}
}
if(in_array('mail',$sections)) {
$r = q("select * from conv where uid = %d",
intval($channel_id)
);
if($r) {
for($x = 0; $x < count($r); $x ++) {
$r[$x]['subject'] = base64url_decode(str_rot47($r[$x]['subject']));
}
$ret['conv'] = $r;
}
2014-09-16 04:31:32 +00:00
2017-06-01 03:59:36 +00:00
$r = q("select * from mail where channel_id = %d",
intval($channel_id)
);
if($r) {
$m = array();
foreach($r as $rr) {
xchan_mail_query($rr);
2017-06-01 03:59:36 +00:00
$m[] = encode_mail($rr,true);
}
$ret['mail'] = $m;
}
}
if(in_array('wikis',$sections)) {
$r = q("select * from item where resource_type like 'nwiki%%' and uid = %d order by created",
intval($channel_id)
);
if($r) {
$ret['wiki'] = array();
xchan_query($r);
$r = fetch_post_tags($r,true);
foreach($r as $rv) {
$ret['wiki'][] = encode_item($rv,true);
}
}
}
if(in_array('items', $sections)) {
/** @warning this may run into memory limits on smaller systems */
/** export three months of posts. If you want to export and import all posts you have to start with
* the first year and export/import them in ascending order.
*
* Don't export linked resource items. we'll have to pull those out separately.
*/
$r = q("select * from item where item_wall = 1 and item_deleted = 0 and uid = %d
and created > %s - INTERVAL %s and resource_type = '' order by created",
intval($channel_id),
db_utcnow(),
db_quoteinterval('3 MONTH')
);
if($r) {
$ret['item'] = array();
xchan_query($r);
$r = fetch_post_tags($r,true);
foreach($r as $rr)
$ret['item'][] = encode_item($rr,true);
}
2014-09-16 04:31:32 +00:00
}
$addon = [
'channel_id' => $channel_id,
'sections' => $sections,
'data' => $ret
];
/**
* @hooks identity_basic_export
* Called when exporting a channel's basic information for backup or transfer.
* * \e number \b channel_id - The channel ID
* * \e array \b sections
* * \e array \b data - The data will get returned
*/
call_hooks('identity_basic_export', $addon);
$ret = $addon['data'];
return $ret;
}
/**
* @brief Export items for a year, or a month of a year.
*
* @param int $channel_id The channel ID
* @param number $year YYYY
* @param number $month (optional) 0-12, default 0 complete year
* @return array An associative array
* * \e array \b relocate - (optional)
* * \e array \b item - array with items encoded_item()
*/
function identity_export_year($channel_id, $year, $month = 0) {
if(! $year)
return array();
if($month && $month <= 12) {
$target_month = sprintf('%02d', $month);
$target_month_plus = sprintf('%02d', $month+1);
}
else
$target_month = '01';
$mindate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month . '-01 00:00:00');
if($month && $month < 12)
$maxdate = datetime_convert('UTC', 'UTC', $year . '-' . $target_month_plus . '-01 00:00:00');
else
$maxdate = datetime_convert('UTC', 'UTC', $year+1 . '-01-01 00:00:00');
return channel_export_items_date($channel_id,$mindate,$maxdate);
}
/**
* @brief Export items within an arbitrary date range.
*
* Date/time is in UTC.
*
* @param int $channel_id The channel ID
* @param string $start
* @param string $finish
* @return array
*/
2019-02-22 04:02:38 +00:00
function channel_export_items_date($channel_id, $start, $finish) {
if(! $start)
return array();
else
$start = datetime_convert('UTC', 'UTC', $start);
$finish = datetime_convert('UTC', 'UTC', (($finish) ? $finish : 'now'));
if($finish < $start)
return array();
$ret = array();
$ch = channelx_by_n($channel_id);
if($ch) {
$ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()];
}
2017-02-01 01:32:52 +00:00
$r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and created >= '%s' and created <= '%s' and resource_type = '' order by created",
intval(ITEM_TYPE_POST),
intval($channel_id),
dbesc($start),
dbesc($finish)
);
if($r) {
$ret['item'] = array();
xchan_query($r);
$r = fetch_post_tags($r, true);
foreach($r as $rr)
$ret['item'][] = encode_item($rr, true);
}
return $ret;
}
/**
* @brief Export items with pagination
*
*
* @param int $channel_id The channel ID
* @param int $page
* @param int $limit (default 50)
* @return array
*/
function channel_export_items_page($channel_id, $start, $finish, $page = 0, $limit = 50) {
if(intval($page) < 1) {
$page = 0;
}
if(intval($limit) < 1) {
$limit = 1;
}
if(intval($limit) > 5000) {
$limit = 5000;
}
if(! $start)
$start = NULL_DATE;
else
$start = datetime_convert('UTC', 'UTC', $start);
$finish = datetime_convert('UTC', 'UTC', (($finish) ? $finish : 'now'));
if($finish < $start)
return [];
$offset = intval($limit) * intval($page);
$ret = [];
$ch = channelx_by_n($channel_id);
if($ch) {
$ret['relocate'] = [ 'channel_address' => $ch['channel_address'], 'url' => z_root()];
}
$r = q("select * from item where ( item_wall = 1 or item_type != %d ) and item_deleted = 0 and uid = %d and resource_type = '' and created >= '%s' and created <= '%s' order by created limit %d offset %d",
intval(ITEM_TYPE_POST),
intval($channel_id),
intval($start),
intval($finish),
intval($limit),
intval($offset)
);
if($r) {
$ret['item'] = array();
xchan_query($r);
$r = fetch_post_tags($r, true);
foreach($r as $rr)
$ret['item'][] = encode_item($rr, true);
}
return $ret;
}
/**
* @brief Loads a profile into the App structure.
*
* The function requires the nickname of a valid channel.
*
2013-12-04 01:33:48 +00:00
* Permissions of the current observer are checked. If a restricted profile is available
* to the current observer, that will be loaded instead of the channel default profile.
*
2013-12-04 01:33:48 +00:00
* The channel owner can set $profile to a valid profile_guid to preview that profile.
*
2013-12-04 01:33:48 +00:00
* The channel default theme is also selected for use, unless over-riden elsewhere.
*
* @param string $nickname
* @param string $profile
*/
function profile_load($nickname, $profile = '') {
// logger('profile_load: ' . $nickname . (($profile) ? ' profile: ' . $profile : ''));
2015-06-16 00:28:52 +00:00
$user = q("select channel_id from channel where channel_address = '%s' and channel_removed = 0 limit 1",
dbesc($nickname)
);
if(! $user) {
2016-03-31 23:06:03 +00:00
logger('profile error: ' . App::$query_string, LOGGER_DEBUG);
notice( t('Requested channel is not available.') . EOL );
2016-03-31 23:06:03 +00:00
App::$error = 404;
return;
}
// get the current observer
2016-03-31 23:06:03 +00:00
$observer = App::get_observer();
2014-02-24 22:19:21 +00:00
$can_view_profile = true;
// Can the observer see our profile?
require_once('include/permissions.php');
if(! perm_is_allowed($user[0]['channel_id'],$observer['xchan_hash'],'view_profile')) {
2014-02-24 22:19:21 +00:00
$can_view_profile = false;
}
if(! $profile) {
$r = q("SELECT abook_profile FROM abook WHERE abook_xchan = '%s' and abook_channel = '%d' limit 1",
dbesc($observer['xchan_hash']),
intval($user[0]['channel_id'])
);
if($r)
$profile = $r[0]['abook_profile'];
}
2014-02-24 22:19:21 +00:00
$p = null;
if($profile) {
2014-02-24 22:19:21 +00:00
$p = q("SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile
LEFT JOIN channel ON profile.uid = channel.channel_id
WHERE channel.channel_address = '%s' AND profile.profile_guid = '%s' LIMIT 1",
dbesc($nickname),
dbesc($profile)
);
}
2014-02-24 22:19:21 +00:00
if(! $p) {
2014-02-24 23:07:00 +00:00
$p = q("SELECT profile.uid AS profile_uid, profile.*, channel.* FROM profile
LEFT JOIN channel ON profile.uid = channel.channel_id
2015-06-16 00:28:52 +00:00
WHERE channel.channel_address = '%s' and channel_removed = 0
AND profile.is_default = 1 LIMIT 1",
2015-06-16 00:28:52 +00:00
dbesc($nickname)
);
}
2014-02-24 22:19:21 +00:00
if(! $p) {
2016-03-31 23:06:03 +00:00
logger('profile error: ' . App::$query_string, LOGGER_DEBUG);
notice( t('Requested profile is not available.') . EOL );
2016-03-31 23:06:03 +00:00
App::$error = 404;
return;
}
2014-08-14 05:22:52 +00:00
$q = q("select * from profext where hash = '%s' and channel_id = %d",
dbesc($p[0]['profile_guid']),
intval($p[0]['profile_uid'])
);
if($q) {
$extra_fields = array();
require_once('include/channel.php');
2014-08-14 05:22:52 +00:00
$profile_fields_basic = get_profile_fields_basic();
$profile_fields_advanced = get_profile_fields_advanced();
2015-01-29 04:56:04 +00:00
$advanced = ((feature_enabled(local_channel(),'advanced_profiles')) ? true : false);
2014-08-14 05:22:52 +00:00
if($advanced)
$fields = $profile_fields_advanced;
else
$fields = $profile_fields_basic;
foreach($q as $qq) {
foreach($fields as $k => $f) {
if($k == $qq['k']) {
$p[0][$k] = $qq['v'];
$extra_fields[] = $k;
break;
}
}
}
}
$p[0]['extra_fields'] = $extra_fields;
$z = q("select xchan_photo_date, xchan_addr from xchan where xchan_hash = '%s' limit 1",
dbesc($p[0]['channel_hash'])
);
if($z) {
$p[0]['picdate'] = $z[0]['xchan_photo_date'];
$p[0]['reddress'] = str_replace('@','&#x40;',unpunify($z[0]['xchan_addr']));
}
// fetch user tags if this isn't the default profile
2014-02-24 22:19:21 +00:00
if(! $p[0]['is_default']) {
$x = q("select keywords from profile where uid = %d and is_default = 1 limit 1",
2016-04-14 05:58:37 +00:00
intval($p[0]['profile_uid'])
);
2014-02-24 22:19:21 +00:00
if($x && $can_view_profile)
$p[0]['keywords'] = $x[0]['keywords'];
}
2014-02-24 22:19:21 +00:00
if($p[0]['keywords']) {
$keywords = str_replace(array('#',',',' ',',,'),array('',' ',',',','),$p[0]['keywords']);
if(strlen($keywords) && $can_view_profile)
2016-03-31 23:06:03 +00:00
App::$page['htmlhead'] .= '<meta name="keywords" content="' . htmlentities($keywords,ENT_COMPAT,'UTF-8') . '" />' . "\r\n" ;
}
2016-03-31 23:06:03 +00:00
App::$profile = $p[0];
App::$profile_uid = $p[0]['profile_uid'];
App::$page['title'] = App::$profile['channel_name'] . " - " . unpunify(channel_reddress(App::$profile));
2016-03-31 23:06:03 +00:00
App::$profile['permission_to_view'] = $can_view_profile;
2014-02-24 22:19:21 +00:00
if($can_view_profile) {
$online = get_online_status($nickname);
2016-03-31 23:06:03 +00:00
App::$profile['online_status'] = $online['result'];
2014-02-24 22:19:21 +00:00
}
2015-01-29 04:56:04 +00:00
if(local_channel()) {
2016-03-31 23:06:03 +00:00
App::$profile['channel_mobile_theme'] = get_pconfig(local_channel(),'system', 'mobile_theme');
$_SESSION['mobile_theme'] = App::$profile['channel_mobile_theme'];
2014-02-24 22:19:21 +00:00
}
/*
* load/reload current theme info
*/
// $_SESSION['theme'] = $p[0]['channel_theme'];
2014-02-24 22:19:21 +00:00
}
function profile_edit_menu($uid) {
$ret = array();
$is_owner = (($uid == local_channel()) ? true : false);
// show edit profile to profile owner
if($is_owner) {
$ret['menu'] = array(
'chg_photo' => t('Change profile photo'),
'entries' => array(),
);
$multi_profiles = feature_enabled(local_channel(), 'multi_profiles');
if($multi_profiles) {
$ret['multi'] = 1;
2016-03-31 05:13:24 +00:00
$ret['edit'] = array(z_root(). '/profiles', t('Edit Profiles'), '', t('Edit'));
$ret['menu']['cr_new'] = t('Create New Profile');
}
else {
2016-03-31 05:13:24 +00:00
$ret['edit'] = array(z_root() . '/profiles/' . $uid, t('Edit Profile'), '', t('Edit'));
}
$r = q("SELECT * FROM profile WHERE uid = %d",
local_channel()
);
if($r) {
foreach($r as $rr) {
if(!($multi_profiles || $rr['is_default']))
continue;
$ret['menu']['entries'][] = array(
'photo' => $rr['thumb'],
'id' => $rr['id'],
'alt' => t('Profile Image'),
'profile_name' => $rr['profile_name'],
'isdefault' => $rr['is_default'],
'visible_to_everybody' => t('Visible to everybody'),
'edit_visibility' => t('Edit visibility'),
);
}
}
}
return $ret;
}
/**
* @brief Formats a profile for display in the sidebar.
*
* It is very difficult to templatise the HTML completely
* because of all the conditional logic.
*
* @param array $profile
* @param int $block
* @param boolean $show_connect (optional) default true
* @param mixed $zcard (optional) default false
*
* @return HTML string suitable for sidebar inclusion
* Exceptions: Returns empty string if passed $profile is wrong type or not populated
*/
2016-02-09 02:16:42 +00:00
function profile_sidebar($profile, $block = 0, $show_connect = true, $zcard = false) {
2016-03-31 23:06:03 +00:00
$observer = App::get_observer();
$o = '';
$location = false;
$pdesc = true;
$reddress = true;
if(! perm_is_allowed($profile['uid'],((is_array($observer)) ? $observer['xchan_hash'] : ''),'view_profile')) {
$block = true;
}
if((! is_array($profile)) && (! count($profile)))
return $o;
head_set_icon($profile['thumb']);
if(is_sys_channel($profile['uid']))
$show_connect = false;
$profile['picdate'] = urlencode($profile['picdate']);
/**
* @hooks profile_sidebar_enter
* Called before generating the 'channel sidebar' or mini-profile.
*/
call_hooks('profile_sidebar_enter', $profile);
if($show_connect) {
// This will return an empty string if we're already connected.
$connect_url = rconnect_url($profile['uid'],get_observer_hash());
$connect = (($connect_url) ? t('Connect') : '');
if($connect_url)
$connect_url = sprintf($connect_url,urlencode(channel_reddress($profile)));
// premium channel - over-ride
if($profile['channel_pageflags'] & PAGE_PREMIUM)
$connect_url = z_root() . '/connect/' . $profile['channel_address'];
}
if((x($profile,'address') == 1)
|| (x($profile,'locality') == 1)
|| (x($profile,'region') == 1)
|| (x($profile,'postal_code') == 1)
|| (x($profile,'country_name') == 1))
$location = t('Location:');
$profile['homepage'] = linkify($profile['homepage'],true);
$gender = ((x($profile,'gender') == 1) ? t('Gender:') : False);
$marital = ((x($profile,'marital') == 1) ? t('Status:') : False);
$homepage = ((x($profile,'homepage') == 1) ? t('Homepage:') : False);
// zap/osada do not have a realtime chat system at this time so don't show online state
// $profile['online'] = (($profile['online_status'] === 'online') ? t('Online Now') : False);
// logger('online: ' . $profile['online']);
$profile['online'] = false;
2015-01-29 04:58:59 +00:00
if(($profile['hidewall'] && (! local_channel()) && (! remote_channel())) || $block ) {
$location = $reddress = $pdesc = $gender = $marital = $homepage = False;
}
if($profile['gender']) {
$profile['gender_icon'] = gender_icon($profile['gender']);
}
$firstname = ((strpos($profile['channel_name'],' '))
? trim(substr($profile['channel_name'],0,strpos($profile['channel_name'],' '))) : $profile['channel_name']);
$lastname = (($firstname === $profile['channel_name']) ? '' : trim(substr($profile['channel_name'],strlen($firstname))));
$contact_block = contact_block();
$channel_menu = false;
$menu = get_pconfig($profile['uid'],'system','channel_menu');
if($menu && ! $block) {
require_once('include/menu.php');
$m = menu_fetch($menu,$profile['uid'],$observer['xchan_hash']);
if($m)
$channel_menu = menu_render($m);
}
$menublock = get_pconfig($profile['uid'],'system','channel_menublock');
if ($menublock && (! $block)) {
2018-08-24 05:44:22 +00:00
$comanche = new Comanche();
$channel_menu .= $comanche->block($menublock);
}
if($zcard)
$tpl = get_markup_template('profile_vcard_short.tpl');
else
$tpl = get_markup_template('profile_vcard.tpl');
$o .= replace_macros($tpl, array(
2016-02-09 02:16:42 +00:00
'$zcard' => $zcard,
'$profile' => $profile,
'$connect' => $connect,
'$connect_url' => $connect_url,
'$location' => $location,
'$gender' => $gender,
'$pdesc' => $pdesc,
'$marital' => $marital,
'$homepage' => $homepage,
'$chanmenu' => $channel_menu,
'$reddress' => $reddress,
2017-03-17 01:36:58 +00:00
'$rating' => '',
'$contact_block' => $contact_block,
'$change_photo' => t('Change your profile photo'),
'$editmenu' => profile_edit_menu($profile['uid'])
));
$arr = [
'profile' => $profile,
'entry' => $o
];
/**
* @hooks profile_sidebar
* Called when generating the 'channel sidebar' or mini-profile.
* * \e array \b profile
* * \e string \b entry - The parsed HTML template
*/
call_hooks('profile_sidebar', $arr);
return $arr['entry'];
}
function gender_icon($gender) {
2017-06-28 05:05:06 +00:00
// logger('gender: ' . $gender);
// This can easily get throw off if the observer language is different
// than the channel owner language.
if(strpos(strtolower($gender),strtolower(t('Female'))) !== false)
return 'venus';
if(strpos(strtolower($gender),strtolower(t('Male'))) !== false)
return 'mars';
if(strpos(strtolower($gender),strtolower(t('Trans'))) !== false)
return 'transgender';
if(strpos(strtolower($gender),strtolower(t('Neuter'))) !== false)
return 'neuter';
if(strpos(strtolower($gender),strtolower(t('Non-specific'))) !== false)
return 'genderless';
return '';
}
function advanced_profile() {
require_once('include/text.php');
2016-03-31 23:06:03 +00:00
if(! perm_is_allowed(App::$profile['profile_uid'],get_observer_hash(),'view_profile'))
return '';
2016-06-01 04:45:33 +00:00
if(App::$profile['fullname']) {
$profile_fields_basic = get_profile_fields_basic();
$profile_fields_advanced = get_profile_fields_advanced();
2016-03-31 23:06:03 +00:00
$advanced = ((feature_enabled(App::$profile['profile_uid'],'advanced_profiles')) ? true : false);
if($advanced)
$fields = $profile_fields_advanced;
else
$fields = $profile_fields_basic;
$clean_fields = array();
if($fields) {
foreach($fields as $k => $v) {
$clean_fields[] = trim($k);
}
}
$tpl = get_markup_template('profile_advanced.tpl');
$profile = array();
2016-06-01 04:45:33 +00:00
$profile['fullname'] = array( t('Full Name:'), App::$profile['fullname'] ) ;
2016-03-31 23:06:03 +00:00
if(App::$profile['gender']) $profile['gender'] = array( t('Gender:'), App::$profile['gender'] );
2014-06-23 05:16:26 +00:00
$ob_hash = get_observer_hash();
2016-03-31 23:06:03 +00:00
if($ob_hash && perm_is_allowed(App::$profile['profile_uid'],$ob_hash,'post_like')) {
2014-06-23 05:16:26 +00:00
$profile['canlike'] = true;
$profile['likethis'] = t('Like this channel');
2016-03-31 23:06:03 +00:00
$profile['profile_guid'] = App::$profile['profile_guid'];
}
2014-06-23 05:16:26 +00:00
$likers = q("select liker, xchan.* from likes left join xchan on liker = xchan_hash where channel_id = %d and target_type = '%s' and verb = '%s'",
2016-03-31 23:06:03 +00:00
intval(App::$profile['profile_uid']),
2014-06-23 05:16:26 +00:00
dbesc(ACTIVITY_OBJ_PROFILE),
dbesc(ACTIVITY_LIKE)
);
$profile['likers'] = array();
$profile['like_count'] = count($likers);
$profile['like_button_label'] = tt('Like','Likes',$profile['like_count'],'noun');
if($likers) {
foreach($likers as $l)
$profile['likers'][] = array('name' => $l['xchan_name'],'photo' => zid($l['xchan_photo_s']), 'url' => zid($l['xchan_url']));
2014-06-23 05:16:26 +00:00
}
2016-03-31 23:06:03 +00:00
if((App::$profile['dob']) && (App::$profile['dob'] != '0000-00-00')) {
$val = '';
2016-03-31 23:06:03 +00:00
if((substr(App::$profile['dob'],5,2) === '00') || (substr(App::$profile['dob'],8,2) === '00'))
$val = substr(App::$profile['dob'],0,4);
$year_bd_format = t('j F, Y');
$short_bd_format = t('j F');
if(! $val) {
$val = ((intval(App::$profile['dob']))
2016-03-31 23:06:03 +00:00
? day_translate(datetime_convert('UTC','UTC',App::$profile['dob'] . ' 00:00 +00:00',$year_bd_format))
: day_translate(datetime_convert('UTC','UTC','2001-' . substr(App::$profile['dob'],5) . ' 00:00 +00:00',$short_bd_format)));
}
$profile['birthday'] = array( t('Birthday:'), $val);
}
2016-03-31 23:06:03 +00:00
if($age = age(App::$profile['dob'],App::$profile['timezone'],''))
$profile['age'] = array( t('Age:'), $age );
2016-03-31 23:06:03 +00:00
if(App::$profile['marital'])
$profile['marital'] = array( t('Status:'), App::$profile['marital']);
2016-06-01 04:45:33 +00:00
if(App::$profile['partner'])
$profile['marital']['partner'] = zidify_links(bbcode(App::$profile['partner']));
2016-09-26 00:06:13 +00:00
if(strlen(App::$profile['howlong']) && App::$profile['howlong'] > NULL_DATE) {
2016-03-31 23:06:03 +00:00
$profile['howlong'] = relative_date(App::$profile['howlong'], t('for %1$d %2$s'));
}
2018-03-29 22:32:35 +00:00
if(App::$profile['keywords']) {
$keywords = str_replace(',',' ', App::$profile['keywords']);
$keywords = str_replace(' ',' ', $keywords);
$karr = explode(' ', $keywords);
if($karr) {
for($cnt = 0; $cnt < count($karr); $cnt ++) {
$karr[$cnt] = '<a href="' . z_root() . '/directory/f=&keywords=' . trim($karr[$cnt]) . '">' . $karr[$cnt] . '</a>';
}
}
$profile['keywords'] = array( t('Tags:'), implode(' ', $karr));
}
2016-03-31 23:06:03 +00:00
if(App::$profile['sexual']) $profile['sexual'] = array( t('Sexual Preference:'), App::$profile['sexual'] );
2016-03-31 23:06:03 +00:00
if(App::$profile['homepage']) $profile['homepage'] = array( t('Homepage:'), linkify(App::$profile['homepage']) );
2016-03-31 23:06:03 +00:00
if(App::$profile['hometown']) $profile['hometown'] = array( t('Hometown:'), linkify(App::$profile['hometown']) );
2016-03-31 23:06:03 +00:00
if(App::$profile['politic']) $profile['politic'] = array( t('Political Views:'), App::$profile['politic']);
2016-03-31 23:06:03 +00:00
if(App::$profile['religion']) $profile['religion'] = array( t('Religion:'), App::$profile['religion']);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['about'])) $profile['about'] = array( t('About:'), $txt );
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['interest'])) $profile['interest'] = array( t('Hobbies/Interests:'), $txt);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['likes'])) $profile['likes'] = array( t('Likes:'), $txt);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['dislikes'])) $profile['dislikes'] = array( t('Dislikes:'), $txt);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['contact'])) $profile['contact'] = array( t('Contact information and Social Networks:'), $txt);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['channels'])) $profile['channels'] = array( t('My other channels:'), $txt);
2014-01-17 23:30:33 +00:00
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['music'])) $profile['music'] = array( t('Musical interests:'), $txt);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['book'])) $profile['book'] = array( t('Books, literature:'), $txt);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['tv'])) $profile['tv'] = array( t('Television:'), $txt);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['film'])) $profile['film'] = array( t('Film/dance/culture/entertainment:'), $txt);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['romance'])) $profile['romance'] = array( t('Love/Romance:'), $txt);
2016-06-01 04:45:33 +00:00
if($txt = prepare_text(App::$profile['employment'])) $profile['employment'] = array( t('Work/employment:'), $txt);
2016-03-31 23:06:03 +00:00
if($txt = prepare_text(App::$profile['education'])) $profile['education'] = array( t('School/education:'), $txt );
2016-03-31 23:06:03 +00:00
if(App::$profile['extra_fields']) {
foreach(App::$profile['extra_fields'] as $f) {
2014-08-14 05:22:52 +00:00
$x = q("select * from profdef where field_name = '%s' limit 1",
dbesc($f)
);
2016-03-31 23:06:03 +00:00
if($x && $txt = prepare_text(App::$profile[$f]))
2014-08-14 05:22:52 +00:00
$profile[$f] = array( $x[0]['field_desc'] . ':',$txt);
}
2016-03-31 23:06:03 +00:00
$profile['extra_fields'] = App::$profile['extra_fields'];
2014-08-14 05:22:52 +00:00
}
2016-03-31 23:06:03 +00:00
$things = get_things(App::$profile['profile_guid'],App::$profile['profile_uid']);
// logger('mod_profile: things: ' . print_r($things,true), LOGGER_DATA);
// $exportlink = ((App::$profile['profile_vcard']) ? zid(z_root() . '/profile/' . App::$profile['channel_address'] . '/vcard') : '');
2017-01-16 05:23:43 +00:00
return replace_macros($tpl, array(
'$title' => t('Profile'),
2014-06-23 12:19:28 +00:00
'$canlike' => (($profile['canlike'])? true : false),
'$likethis' => t('Like this thing'),
2017-01-16 05:23:43 +00:00
'$export' => t('Export'),
'$exportlink' => '', // $exportlink,
'$profile' => $profile,
'$fields' => $clean_fields,
2016-03-31 23:06:03 +00:00
'$editmenu' => profile_edit_menu(App::$profile['profile_uid']),
2013-12-29 01:04:23 +00:00
'$things' => $things
));
}
return '';
}
function get_my_url() {
if(x($_SESSION, 'zrl_override'))
return $_SESSION['zrl_override'];
if(x($_SESSION, 'my_url'))
2014-03-04 23:29:27 +00:00
return $_SESSION['my_url'];
return false;
}
function get_my_address() {
if(x($_SESSION, 'zid_override'))
return $_SESSION['zid_override'];
if(x($_SESSION, 'my_address'))
return $_SESSION['my_address'];
return false;
}
/**
* @brief Add visitor's zid to our xchan and attempt authentication.
*
* If somebody arrives at our site using a zid, add their xchan to our DB if we
* don't have it already.
* And if they aren't already authenticated here, attempt reverse magic auth.
*/
2016-07-15 02:43:47 +00:00
function zid_init() {
$tmp_str = get_my_address();
if(validate_email($tmp_str)) {
$arr = [
'zid' => $tmp_str,
'url' => App::$cmd
];
/**
* @hooks zid_init
* * \e string \b zid - their zid
* * \e string \b url - the destination url
*/
call_hooks('zid_init', $arr);
2015-01-29 04:56:04 +00:00
if(! local_channel()) {
$r = q("select * from hubloc where hubloc_addr = '%s' order by hubloc_connected desc limit 1",
dbesc($tmp_str)
);
if(! $r) {
2018-08-24 05:44:22 +00:00
Master::Summon(array('Gprobe',bin2hex($tmp_str)));
}
2015-01-29 04:58:59 +00:00
if($r && remote_channel() && remote_channel() === $r[0]['hubloc_hash'])
return;
logger('Not authenticated. Invoking reverse magic-auth for ' . $tmp_str);
// try to avoid recursion - but send them home to do a proper magic auth
2016-03-31 23:06:03 +00:00
$query = App::$query_string;
$query = str_replace(array('?zid=','&zid='),array('?rzid=','&rzid='),$query);
2018-07-19 00:48:23 +00:00
$dest = '/' . $query;
if($r && ($r[0]['hubloc_url'] != z_root()) && (! strstr($dest,'/magic')) && (! strstr($dest,'/rmagic'))) {
2018-07-19 00:05:38 +00:00
goaway($r[0]['hubloc_url'] . '/magic' . '?f=&rev=1&owa=1&bdest=' . bin2hex(z_root() . $dest));
}
else
logger('No hubloc found.');
}
}
}
2016-07-15 02:43:47 +00:00
/**
* @brief If somebody arrives at our site using a zat, authenticate them.
2016-07-15 02:43:47 +00:00
*
*/
function zat_init() {
if(local_channel() || remote_channel())
return;
$r = q("select * from atoken where atoken_token = '%s' limit 1",
dbesc($_REQUEST['zat'])
);
if($r) {
$xchan = atoken_xchan($r[0]);
atoken_login($xchan);
2016-07-15 02:43:47 +00:00
}
}
/**
* @brief Used from within PCSS themes to set theme parameters.
*
* If there's a puid request variable, that is the "page owner" and normally
* their theme settings take precedence; unless a local user sets the "always_my_theme"
* system pconfig, which means they don't want to see anybody else's theme
* settings except their own while on this site.
*
* @return int
*/
function get_theme_uid() {
$uid = (($_REQUEST['puid']) ? intval($_REQUEST['puid']) : 0);
2015-01-29 04:56:04 +00:00
if(local_channel()) {
if((get_pconfig(local_channel(),'system','always_my_theme')) || (! $uid))
return local_channel();
}
2017-11-15 08:40:14 +00:00
if(! $uid) {
$x = get_sys_channel();
if($x)
return $x['channel_id'];
}
return $uid;
}
/**
* @brief Retrieves the path of the default_profile_photo for this system
* with the specified size.
*
* @param int $size (optional) default 300
* one of (300, 80, 48)
* @return string with path to profile photo
*/
function get_default_profile_photo($size = 300) {
$scheme = get_config('system','default_profile_photo');
if(! $scheme)
$scheme = 'rainbow_man';
if(! is_dir('images/default_profile_photos/' . $scheme)) {
$x = [ 'scheme' => $scheme, 'size' => $size, 'url' => '' ];
call_hooks('default_profile_photo',$x);
if($x['url']) {
return $x['url'];
}
else {
$scheme = 'rainbow_man';
}
}
return 'images/default_profile_photos/' . $scheme . '/' . $size . '.png';
}
/**
2015-05-05 10:59:51 +00:00
* @brief Test whether a given identity is NOT a member of the Hubzilla.
*
* @param string $s
* xchan_hash of the identity in question
* @return boolean true or false
*/
function is_foreigner($s) {
return((strpbrk($s, '.:@')) ? true : false);
}
/**
2015-05-05 10:59:51 +00:00
* @brief Test whether a given identity is a member of the Hubzilla.
*
* @param string $s
* xchan_hash of the identity in question
* @return boolean true or false
*/
function is_member($s) {
return((is_foreigner($s)) ? false : true);
}
2014-01-29 01:07:25 +00:00
/**
* @brief Get chatpresence status for nick.
*
* @param string $nick
* @return array An associative array with
* * \e bool \b result
*/
2014-01-29 01:07:25 +00:00
function get_online_status($nick) {
$ret = array('result' => false);
if(observer_prohibited())
2014-02-12 23:12:42 +00:00
return $ret;
2014-01-29 01:07:25 +00:00
$r = q("select channel_id, channel_hash from channel where channel_address = '%s' limit 1",
dbesc(argv(1))
);
if($r) {
$hide = get_pconfig($r[0]['channel_id'],'system','hide_online_status');
if($hide)
return $ret;
2014-01-29 01:07:25 +00:00
$x = q("select cp_status from chatpresence where cp_xchan = '%s' and cp_room = 0 limit 1",
dbesc($r[0]['channel_hash'])
);
if($x)
$ret['result'] = $x[0]['cp_status'];
}
return $ret;
}
/**
* @brief
*
* @param string $webbie
* @return array|boolean|string
*/
function remote_online_status($webbie) {
$result = false;
$r = q("select * from hubloc where hubloc_addr = '%s' limit 1",
dbesc($webbie)
);
if(! $r)
return $result;
$url = $r[0]['hubloc_url'] . '/online/' . substr($webbie,0,strpos($webbie,'@'));
$x = z_fetch_url($url);
if($x['success']) {
$j = json_decode($x['body'],true);
if($j)
$result = (($j['result']) ? $j['result'] : false);
}
return $result;
}
2014-02-21 01:07:43 +00:00
/**
* @brief Return the parsed identity selector HTML template.
*
* @return string with parsed HTML channel_id_selet template
*/
function identity_selector() {
if(local_channel()) {
2015-06-16 00:28:52 +00:00
$r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel.channel_account_id = %d and channel_removed = 0 order by channel_name ",
intval(get_account_id())
);
if($r && count($r) > 1) {
2016-03-31 23:06:03 +00:00
//$account = App::get_account();
$o = replace_macros(get_markup_template('channel_id_select.tpl'), array(
'$channels' => $r,
2015-01-29 04:56:04 +00:00
'$selected' => local_channel()
));
return $o;
}
}
return '';
2014-04-11 13:54:07 +00:00
}
2014-07-04 00:26:42 +00:00
function is_public_profile() {
2015-01-29 04:56:04 +00:00
if(! local_channel())
2014-07-04 00:26:42 +00:00
return false;
2014-07-04 00:26:42 +00:00
if(intval(get_config('system','block_public')))
return false;
2016-03-31 23:06:03 +00:00
$channel = App::get_channel();
2016-07-20 05:32:22 +00:00
if($channel) {
2018-08-24 05:44:22 +00:00
$perm = PermissionLimits::Get($channel['channel_id'],'view_profile');
2016-07-20 05:32:22 +00:00
if($perm == PERMS_PUBLIC)
return true;
}
2014-07-04 00:26:42 +00:00
return false;
}
function get_profile_fields_basic($filter = 0) {
$profile_fields_basic = (($filter == 0) ? get_config('system','profile_fields_basic') : null);
if(! $profile_fields_basic)
2018-08-01 00:27:50 +00:00
$profile_fields_basic = array('fullname','pdesc','chandesc','gender','dob','dob_tz','region','country_name','marital','sexual','homepage','hometown','keywords','about','contact');
$x = array();
if($profile_fields_basic)
foreach($profile_fields_basic as $f)
$x[$f] = 1;
return $x;
}
function get_profile_fields_advanced($filter = 0) {
$basic = get_profile_fields_basic($filter);
$profile_fields_advanced = (($filter == 0) ? get_config('system','profile_fields_advanced') : null);
if(! $profile_fields_advanced)
2018-08-01 00:27:50 +00:00
$profile_fields_advanced = array('comms', 'address','locality','postal_code','partner','howlong','politic','religion','likes','dislikes','interest','channels','music','book','film','tv','romance','employment','education');
$x = array();
if($basic)
foreach($basic as $f => $v)
$x[$f] = $v;
if($profile_fields_advanced)
foreach($profile_fields_advanced as $f)
$x[$f] = 1;
return $x;
}
/**
* @brief Clear notifyflags for a channel.
*
* Most likely during bulk import of content or other activity that is likely
* to generate huge amounts of undesired notifications.
*
* @param int $channel_id
* The channel to disable notifications for
* @return int
* Current notification flag value. Send this to notifications_on() to restore the channel settings when finished
* with the activity requiring notifications_off();
*/
function notifications_off($channel_id) {
$r = q("select channel_notifyflags from channel where channel_id = %d limit 1",
intval($channel_id)
);
q("update channel set channel_notifyflags = 0 where channel_id = %d",
intval($channel_id)
);
return intval($r[0]['channel_notifyflags']);
}
function notifications_on($channel_id, $value) {
PostgreSQL support initial commit There were 11 main types of changes: - UPDATE's and DELETE's sometimes had LIMIT 1 at the end of them. This is not only non-compliant but it would certainly not do what whoever wrote it thought it would. It is likely this mistake was just copied from Friendica. All of these instances, the LIMIT 1 was simply removed. - Bitwise operations (and even some non-zero int checks) erroneously rely on MySQL implicit integer-boolean conversion in the WHERE clauses. This is non-compliant (and bad programming practice to boot). Proper explicit boolean conversions were added. New queries should use proper conventions. - MySQL has a different operator for bitwise XOR than postgres. Rather than add yet another dba_ func, I converted them to "& ~" ("AND NOT") when turning off, and "|" ("OR") when turning on. There were no true toggles (XOR). New queries should refrain from using XOR when not necessary. - There are several fields which the schema has marked as NOT NULL, but the inserts don't specify them. The reason this works is because mysql totally ignores the constraint and adds an empty text default automatically. Again, non-compliant, obviously. In these cases a default of empty text was added. - Several statements rely on a non-standard MySQL feature (http://dev.mysql.com/doc/refman/5.5/en/group-by-handling.html). These queries can all be rewritten to be standards compliant. Interestingly enough, the newly rewritten standards compliant queries run a zillion times faster, even on MySQL. - A couple of function/operator name translations were needed (RAND/RANDOM, GROUP_CONCAT/STRING_AGG, UTC_NOW, REGEXP/~, ^/#) -- assist functions added in the dba_ - INTERVALs: postgres requires quotes around the value, mysql requires that there are not quotes around the value -- assist functions added in the dba_ - NULL_DATE's -- Postgres does not allow the invalid date '0000-00-00 00:00:00' (there is no such thing as year 0 or month 0 or day 0). We use '0001-01-01 00:00:00' for postgres. Conversions are handled in Zot/item packets automagically by quoting all dates with dbescdate(). - char(##) specifications in the schema creates fields with blank spaces that aren't trimmed in the code. MySQL apparently treats char(##) as varchar(##), again, non-compliant. Since postgres works better with text fields anyway, this ball of bugs was simply side-stepped by using 'text' datatype for all text fields in the postgres schema. varchar was used in a couple of places where it actually seemed appropriate (size constraint), but without rigorously vetting that all of the PHP code actually validates data, new bugs might come out from under the rug. - postgres doesn't store nul bytes and a few other non-printables in text fields, even when quoted. bytea fields were used when storing binary data (photo.data, attach.data). A new dbescbin() function was added to handle this transparently. - postgres does not support LIMIT #,# syntax. All databases support LIMIT # OFFSET # syntax. Statements were updated to be standard. These changes require corresponding changes in the coding standards. Please review those before adding any code going forward. Still on my TODO list: - remove quotes from non-reserved identifiers and make reserved identifiers use dba func for quoting - Rewrite search queries for better results (both MySQL and Postgres)
2014-11-13 20:21:58 +00:00
$x = q("update channel set channel_notifyflags = %d where channel_id = %d",
intval($value),
intval($channel_id)
);
return $x;
}
function get_channel_default_perms($uid) {
2016-09-06 23:15:45 +00:00
$ret = [];
2016-07-14 06:05:19 +00:00
$r = q("select abook_xchan from abook where abook_channel = %d and abook_self = 1 limit 1",
2015-06-15 04:08:00 +00:00
intval($uid)
);
2016-09-06 23:15:45 +00:00
if($r) {
2018-06-08 03:17:30 +00:00
$ret = \Zotlabs\Access\Permissions::FilledPerms(get_abconfig($uid,$r[0]['abook_xchan'],'system','my_perms',EMPTY_STR));
2016-09-06 23:15:45 +00:00
}
2016-09-06 23:15:45 +00:00
return $ret;
}
function profiles_build_sync($channel_id,$send = true) {
$r = q("select * from profile where uid = %d",
intval($channel_id)
);
if($r) {
if($send) {
2018-06-05 01:40:11 +00:00
Libsync::build_sync_packet($channel_id,array('profile' => $r));
}
else {
return $r;
}
}
}
function auto_channel_create($account_id) {
if(! $account_id)
return false;
$arr = array();
$arr['account_id'] = $account_id;
$arr['name'] = get_aconfig($account_id,'register','channel_name');
$arr['nickname'] = legal_webbie(get_aconfig($account_id,'register','channel_address'));
$arr['permissions_role'] = get_aconfig($account_id,'register','permissions_role');
del_aconfig($account_id,'register','channel_name');
del_aconfig($account_id,'register','channel_address');
del_aconfig($account_id,'register','permissions_role');
if((! $arr['name']) || (! $arr['nickname'])) {
$x = q("select * from account where account_id = %d limit 1",
intval($account_id)
);
if($x) {
if(! $arr['name'])
$arr['name'] = substr($x[0]['account_email'],0,strpos($x[0]['account_email'],'@'));
if(! $arr['nickname'])
$arr['nickname'] = legal_webbie(substr($x[0]['account_email'],0,strpos($x[0]['account_email'],'@')));
}
}
if(! $arr['permissions_role'])
$arr['permissions_role'] = 'social';
if(validate_channelname($arr['name']))
return false;
if($arr['nickname'] === 'sys')
$arr['nickname'] = $arr['nickname'] . mt_rand(1000,9999);
$arr['nickname'] = check_webbie(array($arr['nickname'], $arr['nickname'] . mt_rand(1000,9999)));
return create_identity($arr);
}
2016-01-15 01:25:27 +00:00
function get_cover_photo($channel_id,$format = 'bbcode', $res = PHOTO_RES_COVER_1200) {
$r = q("select height, width, resource_id, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d",
2016-01-15 01:25:27 +00:00
intval($channel_id),
intval($res),
intval(PHOTO_COVER)
);
if(! $r)
return false;
$output = false;
$url = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $res ;
switch($format) {
case 'bbcode':
$output = '[zrl=' . $r[0]['width'] . 'x' . $r[0]['height'] . ']' . $url . '[/zrl]';
break;
case 'html':
$output = '<img class="zrl" width="' . $r[0]['width'] . '" height="' . $r[0]['height'] . '" src="' . $url . '" alt="' . t('cover photo') . '" />';
break;
case 'array':
default:
$output = array(
'width' => $r[0]['width'],
'height' => $r[0]['height'],
'type' => $r[0]['mimetype'],
2016-01-15 01:25:27 +00:00
'url' => $url
);
break;
}
return $output;
}
/**
* @brief Return parsed HTML zcard template.
*
* @param array $channel
* @param string $observer_hash (optional)
* @param array $args (optional)
* @return string parsed HTML from \e zcard template
*/
function get_zcard($channel, $observer_hash = '', $args = array()) {
logger('get_zcard');
$maxwidth = (($args['width']) ? intval($args['width']) : 0);
$maxheight = (($args['height']) ? intval($args['height']) : 0);
2017-01-13 02:52:29 +00:00
if(($maxwidth > 1200) || ($maxwidth < 1)) {
$maxwidth = 1200;
2017-01-13 02:52:29 +00:00
$cover_width = 1200;
}
if($maxwidth <= 425) {
$width = 425;
2017-01-13 02:52:29 +00:00
$cover_width = 425;
$size = 'hz_small';
$cover_size = PHOTO_RES_COVER_425;
$pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']);
} elseif($maxwidth <= 900) {
2016-02-09 02:16:42 +00:00
$width = 900;
2017-01-13 02:52:29 +00:00
$cover_width = 850;
$size = 'hz_medium';
$cover_size = PHOTO_RES_COVER_850;
$pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']);
} elseif($maxwidth <= 1200) {
$width = 1200;
2017-01-13 02:52:29 +00:00
$cover_width = 1200;
$size = 'hz_large';
$cover_size = PHOTO_RES_COVER_1200;
$pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']);
}
// $scale = (float) $maxwidth / $width;
// $translate = intval(($scale / 1.0) * 100);
$channel['channel_addr'] = channel_reddress($channel);
$zcard = array('chan' => $channel);
$r = q("select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d",
intval($channel['channel_id']),
intval($cover_size),
intval(PHOTO_COVER)
);
if($r) {
$cover = $r[0];
$cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale'];
} else {
2017-01-13 02:52:29 +00:00
$default_cover = get_config('system','default_cover_photo','pexels-94622');
$cover = [ 'href' => z_root() . '/images/default_cover_photos/' . $default_cover . '/' . $cover_width . '.jpg' ];
2016-02-03 03:39:47 +00:00
}
$o .= replace_macros(get_markup_template('zcard.tpl'), array(
2016-02-09 02:16:42 +00:00
'$maxwidth' => $maxwidth,
'$scale' => $scale,
'$translate' => $translate,
'$size' => $size,
'$cover' => $cover,
'$pphoto' => $pphoto,
'$zcard' => $zcard
));
return $o;
2016-02-19 21:28:21 +00:00
}
/**
* @brief Return parsed HTML embed zcard template.
*
* @param array $channel
* @param string $observer_hash (optional)
* @param array $args (optional)
* @return string parsed HTML from \e zcard_embed template
*/
function get_zcard_embed($channel, $observer_hash = '', $args = array()) {
logger('get_zcard_embed');
$maxwidth = (($args['width']) ? intval($args['width']) : 0);
$maxheight = (($args['height']) ? intval($args['height']) : 0);
2017-01-13 02:52:29 +00:00
if(($maxwidth > 1200) || ($maxwidth < 1)) {
$maxwidth = 1200;
2017-01-13 02:52:29 +00:00
$cover_width = 1200;
}
if($maxwidth <= 425) {
$width = 425;
2017-01-13 02:52:29 +00:00
$cover_width = 425;
$size = 'hz_small';
$cover_size = PHOTO_RES_COVER_425;
$pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 80 , 'height' => 80, 'href' => $channel['xchan_photo_m']);
}
elseif($maxwidth <= 900) {
$width = 900;
2017-01-13 02:52:29 +00:00
$cover_width = 850;
$size = 'hz_medium';
$cover_size = PHOTO_RES_COVER_850;
$pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 160 , 'height' => 160, 'href' => $channel['xchan_photo_l']);
}
elseif($maxwidth <= 1200) {
$width = 1200;
2017-01-13 02:52:29 +00:00
$cover_width = 1200;
$size = 'hz_large';
$cover_size = PHOTO_RES_COVER_1200;
$pphoto = array('mimetype' => $channel['xchan_photo_mimetype'], 'width' => 300 , 'height' => 300, 'href' => $channel['xchan_photo_l']);
}
$channel['channel_addr'] = channel_reddress($channel);
$zcard = array('chan' => $channel);
$r = q("select height, width, resource_id, imgscale, mimetype from photo where uid = %d and imgscale = %d and photo_usage = %d",
intval($channel['channel_id']),
intval($cover_size),
intval(PHOTO_COVER)
);
if($r) {
$cover = $r[0];
$cover['href'] = z_root() . '/photo/' . $r[0]['resource_id'] . '-' . $r[0]['imgscale'];
2017-01-13 02:52:29 +00:00
}
else {
$default_cover = get_config('system','default_cover_photo','pexels-94622');
$cover = [ 'href' => z_root() . '/images/default_cover_photos/' . $default_cover . '/' . $cover_width . '.jpg' ];
}
$o .= replace_macros(get_markup_template('zcard_embed.tpl'),array(
'$maxwidth' => $maxwidth,
'$scale' => $scale,
'$translate' => $translate,
'$size' => $size,
'$cover' => $cover,
'$pphoto' => $pphoto,
'$zcard' => $zcard
));
return $o;
}
/**
* @brief Get a channel array from a channel nickname.
*
* @param string $nick - A channel_address nickname
* @return array|boolean
* - array with channel entry
* - false if no channel with $nick was found
*/
function channelx_by_nick($nick) {
// If we are provided a Unicode nickname convert to IDN
$nick = punify($nick);
$r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_address = '%s' and channel_removed = 0 LIMIT 1",
dbesc($nick)
);
return(($r) ? $r[0] : false);
}
/**
* @brief Get a channel array by a channel_hash.
*
* @param string $hash
* @return array|boolean false if channel ID not found, otherwise the channel array
*/
function channelx_by_hash($hash) {
$r = q("SELECT * FROM channel left join xchan on channel_hash = xchan_hash WHERE channel_hash = '%s' and channel_removed = 0 LIMIT 1",
dbesc($hash)
);
return(($r) ? $r[0] : false);
}
/**
* @brief Get a channel array by a channel ID.
*
* @param int $id A channel ID
* @return array|boolean false if channel ID not found, otherwise the channel array
*/
function channelx_by_n($id) {
$r = q("SELECT * FROM channel LEFT JOIN xchan ON channel_hash = xchan_hash WHERE channel_id = %d AND channel_removed = 0 LIMIT 1",
dbesc($id)
);
return(($r) ? $r[0] : false);
}
/**
* @brief
*
* @param array $channel
* @return string
*/
function channel_reddress($channel) {
if(! ($channel && array_key_exists('channel_address', $channel)))
return '';
return strtolower($channel['channel_address'] . '@' . App::get_hostname());
}
/**
* @brief Get manual channel conversation update config.
*
* Check the channel config \e manual_conversation_update, if not set fall back
* to global system config, defaults to 1 if nothing set.
*
* @param int $channel_id
* @return int
*/
function channel_manual_conv_update($channel_id) {
$x = get_pconfig($channel_id, 'system', 'manual_conversation_update');
if($x === false)
$x = get_config('system', 'manual_conversation_update', 1);
return intval($x);
}
/**
* @brief Return parsed HTML remote_login template.
*
* @return string with parsed HTML from \e remote_login template
*/
function remote_login() {
$o = replace_macros(get_markup_template('remote_login.tpl'),array(
'$title' => t('Remote Authentication'),
'$desc' => t('Enter your channel address (e.g. channel@example.com)'),
'$submit' => t('Authenticate')
));
return $o;
}
function channel_store_lowlevel($arr) {
$store = [
'channel_account_id' => ((array_key_exists('channel_account_id',$arr)) ? $arr['channel_account_id'] : '0'),
'channel_primary' => ((array_key_exists('channel_primary',$arr)) ? $arr['channel_primary'] : '0'),
'channel_name' => ((array_key_exists('channel_name',$arr)) ? $arr['channel_name'] : ''),
'channel_address' => ((array_key_exists('channel_address',$arr)) ? $arr['channel_address'] : ''),
'channel_guid' => ((array_key_exists('channel_guid',$arr)) ? $arr['channel_guid'] : ''),
'channel_guid_sig' => ((array_key_exists('channel_guid_sig',$arr)) ? $arr['channel_guid_sig'] : ''),
'channel_hash' => ((array_key_exists('channel_hash',$arr)) ? $arr['channel_hash'] : ''),
'channel_timezone' => ((array_key_exists('channel_timezone',$arr)) ? $arr['channel_timezone'] : 'UTC'),
'channel_location' => ((array_key_exists('channel_location',$arr)) ? $arr['channel_location'] : ''),
'channel_theme' => ((array_key_exists('channel_theme',$arr)) ? $arr['channel_theme'] : ''),
'channel_startpage' => ((array_key_exists('channel_startpage',$arr)) ? $arr['channel_startpage'] : ''),
'channel_pubkey' => ((array_key_exists('channel_pubkey',$arr)) ? $arr['channel_pubkey'] : ''),
'channel_prvkey' => ((array_key_exists('channel_prvkey',$arr)) ? $arr['channel_prvkey'] : ''),
'channel_notifyflags' => ((array_key_exists('channel_notifyflags',$arr)) ? $arr['channel_notifyflags'] : '65535'),
'channel_pageflags' => ((array_key_exists('channel_pageflags',$arr)) ? $arr['channel_pageflags'] : '0'),
'channel_dirdate' => ((array_key_exists('channel_dirdate',$arr)) ? $arr['channel_dirdate'] : NULL_DATE),
'channel_lastpost' => ((array_key_exists('channel_lastpost',$arr)) ? $arr['channel_lastpost'] : NULL_DATE),
'channel_deleted' => ((array_key_exists('channel_deleted',$arr)) ? $arr['channel_deleted'] : NULL_DATE),
'channel_active' => ((array_key_exists('channel_active',$arr)) ? $arr['channel_active'] : NULL_DATE),
'channel_max_anon_mail' => ((array_key_exists('channel_max_anon_mail',$arr)) ? $arr['channel_max_anon_mail'] : '10'),
'channel_max_friend_req' => ((array_key_exists('channel_max_friend_req',$arr)) ? $arr['channel_max_friend_req'] : '10'),
'channel_expire_days' => ((array_key_exists('channel_expire_days',$arr)) ? $arr['channel_expire_days'] : '0'),
'channel_passwd_reset' => ((array_key_exists('channel_passwd_reset',$arr)) ? $arr['channel_passwd_reset'] : ''),
'channel_default_group' => ((array_key_exists('channel_default_group',$arr)) ? $arr['channel_default_group'] : ''),
'channel_allow_cid' => ((array_key_exists('channel_allow_cid',$arr)) ? $arr['channel_allow_cid'] : ''),
'channel_allow_gid' => ((array_key_exists('channel_allow_gid',$arr)) ? $arr['channel_allow_gid'] : ''),
'channel_deny_cid' => ((array_key_exists('channel_deny_cid',$arr)) ? $arr['channel_deny_cid'] : ''),
'channel_deny_gid' => ((array_key_exists('channel_deny_gid',$arr)) ? $arr['channel_deny_gid'] : ''),
'channel_removed' => ((array_key_exists('channel_removed',$arr)) ? $arr['channel_removed'] : '0'),
'channel_system' => ((array_key_exists('channel_system',$arr)) ? $arr['channel_system'] : '0'),
'channel_moved' => ((array_key_exists('channel_moved',$arr)) ? $arr['channel_moved'] : ''),
'channel_password' => ((array_key_exists('channel_password',$arr)) ? $arr['channel_password'] : ''),
'channel_salt' => ((array_key_exists('channel_salt',$arr)) ? $arr['channel_salt'] : '')
];
return create_table_from_array('channel', $store);
}
function profile_store_lowlevel($arr) {
$store = [
'profile_guid' => ((array_key_exists('profile_guid',$arr)) ? $arr['profile_guid'] : ''),
'aid' => ((array_key_exists('aid',$arr)) ? $arr['aid'] : 0),
'uid' => ((array_key_exists('uid',$arr)) ? $arr['uid'] : 0),
'profile_name' => ((array_key_exists('profile_name',$arr)) ? $arr['profile_name'] : ''),
'is_default' => ((array_key_exists('is_default',$arr)) ? $arr['is_default'] : 0),
'hide_friends' => ((array_key_exists('hide_friends',$arr)) ? $arr['hide_friends'] : 0),
'fullname' => ((array_key_exists('fullname',$arr)) ? $arr['fullname'] : ''),
'pdesc' => ((array_key_exists('pdesc',$arr)) ? $arr['pdesc'] : ''),
'chandesc' => ((array_key_exists('chandesc',$arr)) ? $arr['chandesc'] : ''),
'dob' => ((array_key_exists('dob',$arr)) ? $arr['dob'] : ''),
'dob_tz' => ((array_key_exists('dob_tz',$arr)) ? $arr['dob_tz'] : ''),
'address' => ((array_key_exists('address',$arr)) ? $arr['address'] : ''),
'locality' => ((array_key_exists('locality',$arr)) ? $arr['locality'] : ''),
'region' => ((array_key_exists('region',$arr)) ? $arr['region'] : ''),
'postal_code' => ((array_key_exists('postal_code',$arr)) ? $arr['postal_code'] : ''),
'country_name' => ((array_key_exists('country_name',$arr)) ? $arr['country_name'] : ''),
'hometown' => ((array_key_exists('hometown',$arr)) ? $arr['hometown'] : ''),
'gender' => ((array_key_exists('gender',$arr)) ? $arr['gender'] : ''),
'marital' => ((array_key_exists('marital',$arr)) ? $arr['marital'] : ''),
'partner' => ((array_key_exists('partner',$arr)) ? $arr['partner'] : ''),
'howlong' => ((array_key_exists('howlong',$arr)) ? $arr['howlong'] : NULL_DATE),
'sexual' => ((array_key_exists('sexual',$arr)) ? $arr['sexual'] : ''),
'politic' => ((array_key_exists('politic',$arr)) ? $arr['politic'] : ''),
'religion' => ((array_key_exists('religion',$arr)) ? $arr['religion'] : ''),
'keywords' => ((array_key_exists('keywords',$arr)) ? $arr['keywords'] : ''),
'likes' => ((array_key_exists('likes',$arr)) ? $arr['likes'] : ''),
'dislikes' => ((array_key_exists('dislikes',$arr)) ? $arr['dislikes'] : ''),
'about' => ((array_key_exists('about',$arr)) ? $arr['about'] : ''),
'summary' => ((array_key_exists('summary',$arr)) ? $arr['summary'] : ''),
'music' => ((array_key_exists('music',$arr)) ? $arr['music'] : ''),
'book' => ((array_key_exists('book',$arr)) ? $arr['book'] : ''),
'tv' => ((array_key_exists('tv',$arr)) ? $arr['tv'] : ''),
'film' => ((array_key_exists('film',$arr)) ? $arr['film'] : ''),
'interest' => ((array_key_exists('interest',$arr)) ? $arr['interest'] : ''),
'romance' => ((array_key_exists('romance',$arr)) ? $arr['romance'] : ''),
'employment' => ((array_key_exists('employment',$arr)) ? $arr['employment'] : ''),
'education' => ((array_key_exists('education',$arr)) ? $arr['education'] : ''),
'contact' => ((array_key_exists('contact',$arr)) ? $arr['contact'] : ''),
'channels' => ((array_key_exists('channels',$arr)) ? $arr['channels'] : ''),
'homepage' => ((array_key_exists('homepage',$arr)) ? $arr['homepage'] : ''),
'photo' => ((array_key_exists('photo',$arr)) ? $arr['photo'] : ''),
'thumb' => ((array_key_exists('thumb',$arr)) ? $arr['thumb'] : ''),
'publish' => ((array_key_exists('publish',$arr)) ? $arr['publish'] : 0),
'profile_vcard' => ((array_key_exists('profile_vcard',$arr)) ? $arr['profile_vcard'] : '')
];
return create_table_from_array('profile',$store);
}
/**
* Included here for completeness, but this is a very dangerous operation.
* It is the caller's responsibility to confirm the requestor's intent and
* authorisation to do this.
*
* @param int $account_id
* @param boolean $local (optional) default true
* @param boolean $unset_session (optional) default true
* @return boolean|array
*/
function account_remove($account_id, $local = true, $unset_session = true) {
logger('account_remove: ' . $account_id);
if(! intval($account_id)) {
logger('No account.');
return false;
}
// Don't let anybody nuke the only admin account.
$r = q("select account_id from account where (account_roles & %d) > 0",
intval(ACCOUNT_ROLE_ADMIN)
);
if($r !== false && count($r) == 1 && $r[0]['account_id'] == $account_id) {
logger("Unable to remove the only remaining admin account");
return false;
}
$r = q("select * from account where account_id = %d limit 1",
intval($account_id)
);
$account_email=$r[0]['account_email'];
if(! $r) {
logger('No account with id: ' . $account_id);
return false;
}
$x = q("select channel_id from channel where channel_account_id = %d",
intval($account_id)
);
if($x) {
foreach($x as $xx) {
channel_remove($xx['channel_id'],$local,false);
}
}
$r = q("delete from account where account_id = %d",
intval($account_id)
);
if ($unset_session) {
App::$session->nuke();
notice( sprintf(t('Account \'%s\' deleted'),$account_email) . EOL);
goaway(z_root());
}
return $r;
}
/**
* @brief Removes a channel.
*
* @param int $channel_id
* @param boolean $local default true
* @param boolean $unset_session default false
*/
function channel_remove($channel_id, $local = true, $unset_session = false) {
if(! $channel_id)
return;
logger('Removing channel: ' . $channel_id);
logger('local only: ' . intval($local));
$r = q("select * from channel where channel_id = %d limit 1", intval($channel_id));
if(! $r) {
logger('channel not found: ' . $channel_id);
return;
}
$channel = $r[0];
/**
* @hooks channel_remove
* Called when removing a channel.
* * \e array with entry from channel tabel for $channel_id
*/
call_hooks('channel_remove', $r[0]);
if(! $local) {
2018-10-04 02:10:52 +00:00
if(intval($r[0]['channel_removed'])) {
// already removed. do not propagate deletion of a channel which
// may have been removed locally at some previous time.
return;
}
$r = q("update channel set channel_deleted = '%s', channel_removed = 1 where channel_id = %d",
dbesc(datetime_convert()),
intval($channel_id)
);
q("delete from pconfig where uid = %d",
intval($channel_id)
);
logger('deleting hublocs',LOGGER_DEBUG);
$r = q("update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s'",
dbesc($channel['channel_hash'])
);
$r = q("update xchan set xchan_deleted = 1 where xchan_hash = '%s'",
dbesc($channel['channel_hash'])
);
2018-08-24 05:44:22 +00:00
Master::Summon(array('Notifier','purge_all',$channel_id));
}
2018-08-12 22:09:02 +00:00
$r = q("select iid from iconfig left join item on item.id = iconfig.iid
where item.uid = %d",
intval($channel_id)
);
if($r) {
foreach($r as $rr) {
q("delete from iconfig where iid = %d",
intval($rr['iid'])
);
}
}
q("DELETE FROM app WHERE app_channel = %d", intval($channel_id));
q("DELETE FROM atoken WHERE atoken_uid = %d", intval($channel_id));
q("DELETE FROM chatroom WHERE cr_uid = %d", intval($channel_id));
q("DELETE FROM conv WHERE uid = %d", intval($channel_id));
2018-09-26 00:47:43 +00:00
q("DELETE FROM pgrp WHERE uid = %d", intval($channel_id));
q("DELETE FROM pgrp_member WHERE uid = %d", intval($channel_id));
q("DELETE FROM event WHERE uid = %d", intval($channel_id));
q("DELETE FROM mail WHERE channel_id = %d", intval($channel_id));
q("DELETE FROM menu WHERE menu_channel_id = %d", intval($channel_id));
q("DELETE FROM menu_item WHERE mitem_channel_id = %d", intval($channel_id));
q("DELETE FROM notify WHERE uid = %d", intval($channel_id));
q("DELETE FROM obj WHERE obj_channel = %d", intval($channel_id));
q("DELETE FROM photo WHERE uid = %d", intval($channel_id));
q("DELETE FROM attach WHERE uid = %d", intval($channel_id));
q("DELETE FROM profile WHERE uid = %d", intval($channel_id));
q("DELETE FROM source WHERE src_channel_id = %d", intval($channel_id));
$r = q("select hash FROM attach WHERE uid = %d", intval($channel_id));
if($r) {
foreach($r as $rv) {
attach_delete($channel_id,$rv['hash']);
}
}
$r = q("select id from item where uid = %d", intval($channel_id));
if($r) {
foreach($r as $rv) {
drop_item($rv['id'],false);
}
}
q("delete from abook where abook_xchan = '%s' and abook_self = 1 ",
dbesc($channel['channel_hash'])
);
$r = q("update channel set channel_deleted = '%s', channel_removed = 1 where channel_id = %d",
dbesc(datetime_convert()),
intval($channel_id)
);
// if this was the default channel, set another one as default
if(App::$account['account_default_channel'] == $channel_id) {
$r = q("select channel_id from channel where channel_account_id = %d and channel_removed = 0 limit 1",
intval(App::$account['account_id']),
intval(PAGE_REMOVED));
if ($r) {
$rr = q("update account set account_default_channel = %d where account_id = %d",
intval($r[0]['channel_id']),
intval(App::$account['account_id']));
logger("Default channel deleted, changing default to channel_id " . $r[0]['channel_id']);
}
else {
$rr = q("update account set account_default_channel = 0 where account_id = %d",
intval(App::$account['account_id'])
);
}
}
logger('deleting hublocs',LOGGER_DEBUG);
$r = q("update hubloc set hubloc_deleted = 1 where hubloc_hash = '%s' and hubloc_url = '%s' ",
dbesc($channel['channel_hash']),
dbesc(z_root())
);
// Do we have any valid hublocs remaining?
$hublocs = 0;
$r = q("select hubloc_id from hubloc where hubloc_hash = '%s' and hubloc_deleted = 0",
dbesc($channel['channel_hash'])
);
if($r)
$hublocs = count($r);
if(! $hublocs) {
$r = q("update xchan set xchan_deleted = 1 where xchan_hash = '%s' ",
dbesc($channel['channel_hash'])
);
}
//remove from file system
$f = 'store/' . $channel['channel_address'];
if(is_dir($f)) {
@rrmdir($f);
}
2018-08-24 05:44:22 +00:00
Master::Summon([ 'Directory', $channel_id ]);
if($channel_id == local_channel() && $unset_session) {
App::$session->nuke();
goaway(z_root());
}
}
// execute this at least a week after removing a channel
function channel_remove_final($channel_id) {
q("delete from abook where abook_channel = %d", intval($channel_id));
q("delete from abconfig where chan = %d", intval($channel_id));
q("delete from pconfig where uid = %d", intval($channel_id));
}
/**
* @brief This checks if a channel is allowed to publish executable code.
*
* It is up to the caller to determine if the observer or local_channel
* is in fact the resource owner whose channel_id is being checked.
*
* @param int $channel_id
* @return boolean
*/
function channel_codeallowed($channel_id) {
if(! intval($channel_id))
return false;
$x = channelx_by_n($channel_id);
if(($x) && ($x['channel_pageflags'] & PAGE_ALLOWCODE))
return true;
return false;
2017-03-29 13:16:41 +00:00
}
function anon_identity_init($reqvars) {
$x = [
'request_vars' => $reqvars,
'xchan' => null,
'success' => 'unset'
];
/**
* @hooks anon_identity_init
* * \e array \b request_vars
* * \e string \b xchan - return value
* * \e string|int \b success - Must be a number, so xchan return value gets used
*/
call_hooks('anon_identity_init', $x);
if($x['success'] !== 'unset' && intval($x['success']) && $x['xchan'])
return $x['xchan'];
// allow a captcha handler to over-ride
if($x['success'] !== 'unset' && (intval($x['success']) === 0))
return false;
$anon_name = strip_tags(trim($reqvars['anonname']));
$anon_email = strip_tags(trim($reqvars['anonmail']));
$anon_url = strip_tags(trim($reqvars['anonurl']));
if(! ($anon_name && $anon_email)) {
logger('anonymous commenter did not complete form');
return false;
}
if(! validate_email($anon_email)) {
logger('enonymous email not valid');
return false;
}
if(! $anon_url)
$anon_url = z_root();
$hash = hash('md5',$anon_email);
$x = q("select * from xchan where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'anon' limit 1",
dbesc($anon_email),
dbesc($hash)
);
if(! $x) {
xchan_store_lowlevel([
'xchan_guid' => $anon_email,
'xchan_hash' => $hash,
'xchan_name' => $anon_name,
'xchan_url' => $anon_url,
'xchan_network' => 'anon',
'xchan_name_date' => datetime_convert()
]);
$x = q("select * from xchan where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'anon' limit 1",
dbesc($anon_email),
dbesc($hash)
);
$photo = z_root() . '/' . get_default_profile_photo(300);
$photos = import_xchan_photo($photo,$hash);
2018-04-09 12:28:41 +00:00
$r = q("update xchan set xchan_photo_date = '%s', xchan_photo_l = '%s', xchan_photo_m = '%s', xchan_photo_s = '%s', xchan_photo_mimetype = '%s' where xchan_guid = '%s' and xchan_hash = '%s' and xchan_network = 'anon' ",
dbesc(datetime_convert()),
dbesc($photos[0]),
dbesc($photos[1]),
dbesc($photos[2]),
dbesc($photos[3]),
dbesc($anon_email),
dbesc($hash)
);
}
return $x[0];
}
2018-05-24 04:48:19 +00:00
function channel_url($channel) {
2018-09-11 10:36:31 +00:00
if(! is_array($channel))
btlogger('not a channel array');
2018-05-24 04:48:19 +00:00
return (($channel) ? z_root() . '/channel/' . $channel['channel_address'] : z_root());
2018-08-15 01:19:34 +00:00
}