You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

7588 lines
272 KiB

<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
use Tiki\File\DiagramHelper;
use Tiki\TikiInit;
require_once(__DIR__ . '/../lib/debug/Tracer.php');
// this script may only be included - so its better to die if called directly.
if (strpos($_SERVER["SCRIPT_NAME"], basename(__FILE__)) !== false) {
header("location: index.php");
exit;
}
// This class is included by all the Tiki php scripts, so it's important
// to keep the class as small as possible to improve performance.
// What goes in this class:
// * generic functions that MANY scripts must use
// * shared functions (marked as /*shared*/) are functions that are
// called from Tiki modules.
/**
*
*/
class TikiLib extends TikiDb_Bridge
{
public $buffer;
public $flag;
public $usergroups_cache = [];
public $num_queries = 0;
public $now;
public $cache_page_info = [];
public $sessionId = null;
/**
* Collection of Tiki libraries.
* Populated by TikiLib::lib()
* @var array
*/
protected static $libraries = [];
protected static $isExternalContext = false;
/** Gets a library reference
*
* @param $name string The name of the library as specified in the id attribute in db/config/tiki.xml
* @return object|\AccountingLib|\ActivityLib|\AdminLib|\AreasLib|\ArtLib|\AttributeLib|\AutoSaveLib|\BannerLib|\BigBlueButtonLib|\blacklistLib|\ocrLib|\BlogLib|\CacheLib|\CalendarLib|\Captcha|\CartLib|\CategLib|\Comments|\ContactLib|\ContributionLib|\CreditsLib|\CryptLib|\cssLib|\Tiki\CustomRoute\CustomRouteLib|\DCSLib|\EditLib|\ErrorManagerLib|\FaqLib|\FederatedSearchLib|\FileGalBatchLib|\FileGalLib|\FlaggedRevisionLib|\FreetagLib|\GeoLib|\GoalEventLib|\GoalLib|\GoalRewardLib|\GroupAlertLib|\H5PLib|\HeaderLib|\HistLib|\IconsetLib|\ImageGalsLib|\KalturaLib|\KalturaLib|\Language|\LanguageTranslations|\LdapLib|\LoginLib|\Tiki\Lib\Logs\LogsLib|\Tiki\Lib\Logs\LogsQueryLib|\MailinLib|\Memcachelib|\MenuLib|\Messu|\MimeLib|\ModLib|\MonitorLib|\MonitorMailLib|\MultilingualLib|\NotificationLib|\OAuthLib|\ObjectLib|OpenIdConnectLib|\PageContentLib|\ParserLib|\PaymentLib|\PdfImagesLib\PerspectiveLib|\PollLib|\PreferencesLib|\QuantifyLib|\QueueLib|\QuizLib|\RatingConfigLib|\RatingLib|\ReferencesLib|\RegistrationLib|\RelationLib|\RSSLib|\SchedulersLib|\ScoreLib|\ScormLib|\SearchStatsLib|\SemanticLib|\ServiceLib|\SheetLib|\Smarty_Tiki|\SocialLib|\StatsLib|\StoredSearchLib|\StructLib|\TemplatesLib|\ThemeControlLib|\ThemeLib|\Tiki_Connect_Client|\Tiki_Connect_Server|\Tiki_Event_Manager|\Tiki_Profile_SymbolLoader|\Tiki\ObjectSelector\Selector|\Tiki\Recommendation\BatchProcessor|\Tiki\Wiki\SlugManager|\TikiAccessLib|\TikiCalendarLib|\TikiDate|\TodoLib|\Tracker\Tabular\Manager|\TrackerLib|\TWVersion|\UnifiedSearchLib|\UserMailinLib|\UserModulesLib|\UserPrefsLib|\UsersLib|\Validators|\VimeoLib|\VueJsLib|\WikiLib|\WizardLib|\WYSIWYGLib|\XMPPLib|\ZoteroLib
* @throws Exception
*/
public static function lib($name)
{
if (isset(self::$libraries[$name])) {
return self::$libraries[$name];
}
$container = TikiInit::getContainer();
//if no period in the lib name, default to tiki.lib prefix.
if (strpos($name, ".") !== false) {
$service = $name;
} else {
$service = "tiki.lib.$name";
}
if ($lib = $container->get($service, \Symfony\Component\DependencyInjection\ContainerInterface::NULL_ON_INVALID_REFERENCE)) {
return $lib;
}
// One-time inits of the libraries provided
switch ($name) {
case 'tiki':
global $tikilib;
return self::$libraries[$name] = $tikilib;
}
if (file_exists(__DIR__ . '/../temp/cache/container.php')) {
unlink(__DIR__ . '/../temp/cache/container.php'); // Remove the container cache to help transition
}
throw new Exception(tr("%0 library not found. This may be due to a typo or caused by a recent update.", $name));
}
/**
* @return Tiki_Event_Manager
* @throws Exception
*/
public static function events()
{
return self::lib('events');
}
/**
* @return Tiki_Profile_SymbolLoader
* @throws Exception
*/
public static function symbols()
{
return self::lib('symbols');
}
/**
* @return mixed
*/
public function get_site_hash()
{
global $prefs;
if (! isset($prefs['internal_site_hash'])) {
$hash = $this->generate_unique_sequence();
$this->set_preference('internal_site_hash', $hash);
}
return $prefs['internal_site_hash'];
}
/**
* Generates cryptographically secure pseudo-random sequence of bytes encoded into the base 64 character set
*
* @param int $entropy Number of bytes to return
* @param bool $urlSafe If true, substitutes '-' and '_', for '+' and '_', and strips the '=' padding
* character for url safe sequence.
* @return string
*/
public function generate_unique_sequence($entropy = 100, $urlSafe = false)
{
$random_value = \phpseclib\Crypt\Random::string($entropy);
$encoded_value = base64_encode($random_value);
return $urlSafe ? strtr(str_replace('=', '', $encoded_value), '+/', '-_')
: $encoded_value;
}
// DB param left for interface compatibility, although not considered
/**
* @param null $db
*/
public function __construct($db = null)
{
$this->now = time();
}
public function allocate_extra($type, $callback)
{
global $prefs;
$memory_name = 'allocate_memory_' . $type;
$time_name = 'allocate_time_' . $type;
if (! empty($prefs[$memory_name])) {
$memory_limit = new Tiki_MemoryLimit($prefs[$memory_name]);
}
if (! empty($prefs[$time_name])) {
$time_limit = new Tiki_TimeLimit($prefs[$time_name]);
}
return call_user_func($callback);
}
/**
* @param bool $url
* @param array $options
* @return mixed|Laminas\Http\Client
*/
public function get_http_client($url = false, $options = null, $user = null)
{
global $prefs;
$config = [
'timeout' => 10,
'keepalive' => true,
];
if ($prefs['use_proxy'] == 'y') {
$config['adapter'] = 'Laminas\Http\Client\Adapter\Proxy';
$config["proxy_host"] = $prefs['proxy_host'];
$config["proxy_port"] = $prefs['proxy_port'];
if ($prefs['proxy_user'] || $prefs['proxy_pass']) {
$config["proxy_user"] = $prefs['proxy_user'];
$config["proxy_pass"] = $prefs['proxy_pass'];
}
} elseif (function_exists('curl_init') && $prefs['zend_http_use_curl'] === 'y') {
// Laminas\Http\Client defaults to sockets, which aren't allowed in all environments so use curl when available if selected
$config['adapter'] = 'Laminas\Http\Client\Adapter\Curl';
}
if ($prefs['zend_http_sslverifypeer'] == 'y') {
$config['sslverifypeer'] = true;
} else {
$config['sslverifypeer'] = false;
}
if (is_array($options)) {
foreach ($options as $key => $value) {
$config[$key] = $value;
}
}
$client = new Laminas\Http\Client(null, $config);
$client->setArgSeparator('&');
if ($url) {
$client = $this->prepare_http_client($client, $url, $user);
$client->setUri($this->urlencode_accent($url)); // Laminas\Http\Client seems to fail with accents in urls (jb june 2011)
}
return $client;
}
/**
* @param $client
* @param $url
* @return mixed
*/
private function prepare_http_client($client, $url, $user = null)
{
$info = parse_url($url);
// Obtain all methods matching the scheme and domain
$table = $this->table('tiki_source_auth');
$authentications = $table->fetchAll(
['path', 'method', 'arguments'],
['scheme' => $info['scheme'], 'domain' => $info['host'], 'user' => $user]
);
if (! $authentications && $user) {
// try system-wide authentications not constrainted to a specific user
$authentications = $table->fetchAll(
['path', 'method', 'arguments'],
['scheme' => $info['scheme'], 'domain' => $info['host'], 'user' => null]
);
}
// Obtain the method with the longest path matching
$max = -1;
$method = false;
$arguments = false;
foreach ($authentications as $auth) {
if (0 === strpos($info['path'], $auth['path'])) {
$len = strlen($auth['path']);
if ($len > $max) {
$max = $len;
$method = $auth['method'];
$arguments = $auth['arguments'];
}
}
}
if ($method) {
$functionName = 'prepare_http_auth_' . $method;
if (method_exists($this, $functionName)) {
$arguments = json_decode($arguments, true);
return $this->$functionName($client, $arguments);
}
} else {
// Nothing special to do
return $client;
}
}
/**
* @param $client
* @param $arguments
* @return mixed
*/
private function prepare_http_auth_basic($client, $arguments)
{
$client->setAuth($arguments['username'], $arguments['password'], Laminas\Http\Client::AUTH_BASIC);
return $client;
}
/**
* @param $client
* @param $arguments
* @return mixed
*/
private function prepare_http_auth_get($client, $arguments)
{
$url = $arguments['url'];
$client->setUri($this->urlencode_accent($url)); // Laminas\Http\Client seems to fail with accents in urls
$client->setMethod(Laminas\Http\Request::METHOD_GET);
$response = $client->send();
$client->resetParameters();
return $client;
}
/**
* @param $client
* @param $arguments
* @return mixed
*/
private function prepare_http_auth_post($client, $arguments)
{
$url = $arguments['post_url'];
unset($arguments['post_url']);
$client->setUri($this->urlencode_accent($url)); // Laminas\Http\Client seems to fail with accents in urls
$client->setMethod(Laminas\Http\Request::METHOD_GET);
$response = $client->send();
$client->resetParameters();
$client->setUri($this->urlencode_accent($url)); // Laminas\Http\Client seems to fail with accents in urls
$client->setParameterPost($arguments);
$client->setMethod(Laminas\Http\Request::METHOD_POST);
$response = $client->send();
$client->resetParameters();
// check for oauth2 password post returning a Authorization: Bearer token
if (! empty($arguments['grant_type']) && $arguments['grant_type'] === 'password') { // TODO other grant_types may need this too
$body = json_decode($response->getBody());
if ($body && $body->access_token) {
$headers = $client->getRequest()->getHeaders();
// add the Bearer token to the request headers
$headers->addHeader(new Laminas\Http\Header\Authorization('Bearer ' . $body->access_token));
$client->setHeaders($headers);
}
}
return $client;
}
/**
* Authorization header method
*
* @param $client \Laminas\Http\Client
* @param $arguments array
* @return \Laminas\Http\Client
*/
private function prepare_http_auth_header($client, $arguments)
{
$url = $arguments['url'];
$client->setUri($this->urlencode_accent($url)); // Laminas\Http\Client seems to fail with accents in urls
$client->setMethod(Laminas\Http\Request::METHOD_GET);
$headers = $client->getRequest()->getHeaders();
if (empty($arguments['header_name'])) {
$headers->addHeader(new Laminas\Http\Header\Authorization($arguments['header']));
} else {
$headers->addHeaderLine($arguments['header_name'], $arguments['header']);
}
$client->setHeaders($headers);
return $client;
}
/**
* Request body parameters method
*
* @param $client \Laminas\Http\Client
* @param $arguments array
* @return \Laminas\Http\Client
*/
private function prepare_http_auth_body($client, $arguments)
{
$client->setParameterGet($arguments);
$client->setParameterPost($arguments);
return $client;
}
/**
* @param $client
* @return mixed
*/
public function http_perform_request($client)
{
global $prefs;
$response = $client->send();
$attempts = 0;
while ($response->isRedirect() && $attempts < 10) { // prevent redirect loop
$client->setUri($client->getUri());
$response = $client->send();
$attempts++;
}
if ($prefs['http_skip_frameset'] == 'y') {
if ($outcome = $this->http_perform_request_skip_frameset($client, $response)) {
return $outcome;
}
}
return $response;
}
/**
* @param $client
* @param $response
* @return mixed
*/
private function http_perform_request_skip_frameset($client, $response)
{
// Only attempt if document is declared as HTML
if (0 === strpos($response->getHeaders()->get('Content-Type'), 'text/html')) {
$use_int_errors = libxml_use_internal_errors(true); // suppress errors and warnings due to bad HTML
$dom = new DOMDocument();
if ($response->getBody() && $dom->loadHTML($response->getBody())) {
$frames = $dom->getElementsByTagName('frame');
if (count($frames)) {
// Frames were found
foreach ($frames as $f) {
// Request with the first frame where scrolling is not disabled (likely to be a menu or some other web 2.0 helper)
if ($f->getAttribute('scrolling') != 'no') {
$client->setUri($this->http_get_uri($client->getUri(), $this->urlencode_accent($f->getAttribute('src'))));
libxml_clear_errors();
libxml_use_internal_errors($use_int_errors);
return $client->send();
}
}
}
}
libxml_clear_errors();
libxml_use_internal_errors($use_int_errors);
}
}
/**
* @param Laminas\Uri\Http $uri
* @param $relative
* @return Laminas\Uri\Http
*/
public function http_get_uri(Laminas\Uri\Http $uri, $relative)
{
if (strpos($relative, 'http://') === 0 || strpos($relative, 'https://') === 0) {
$uri = new Laminas\Uri\Http($relative);
} else {
$uri = clone $uri;
$uri->setQuery([]);
$parts = explode('?', $relative, 2);
$relative = $parts[0];
if ($relative[0] === '/') {
$uri->setPath($relative);
} else {
$path = dirname($uri->getPath());
if ($path === '/') {
$path = '';
}
$uri->setPath("$path/$relative");
}
if (isset($parts[1])) {
$uri->setQuery($parts[1]);
}
}
return $uri;
}
/**
* @param $url
* @param string $reqmethod
* @return bool
*/
public function httprequest($url, $reqmethod = "GET")
{
// test url :
// rewrite url if sloppy # added a case for https urls
if (
(substr($url, 0, 7) <> "http://") and
(substr($url, 0, 8) <> "https://")
) {
$url = "http://" . $url;
}
try {
$client = $this->get_http_client($url);
/* @var $response Laminas\Http\Response */
$response = $this->http_perform_request($client);
if (! $response->isSuccess()) {
return false;
}
return $response->getBody();
} catch (Laminas\Http\Exception\ExceptionInterface $e) {
return false;
}
}
/*shared*/
/**
* @param $name
* @return bool
*/
public function get_dsn_by_name($name)
{
if ($name == 'local') {
return true;
}
return $this->table('tiki_dsn')->fetchOne('dsn', ['name' => $name]);
}
/**
* @param $name
* @return array
*/
public function get_dsn_info($name)
{
$info = [];
$dsnsqlplugin = $this->get_dsn_by_name($name);
$parsedsn = $dsnsqlplugin;
$info['driver'] = strtok($parsedsn, ":");
$parsedsn = substr($parsedsn, strlen($info['driver']) + 3);
$info['user'] = strtok($parsedsn, ":");
$parsedsn = substr($parsedsn, strlen($info['user']) + 1);
$info['password'] = strtok($parsedsn, "@");
$parsedsn = substr($parsedsn, strlen($info['password']) + 1);
$info['host'] = strtok($parsedsn, "/");
$parsedsn = substr($parsedsn, strlen($info['host']) + 1);
$info['database'] = $parsedsn;
return $info;
}
/**
* @param $name
* @return mixed
*/
public function get_db_by_name($name)
{
include_once('tiki-setup.php');
if ($name == 'local' || empty($name)) {
return TikiDb::get();
}
try {
static $connectionMap = [];
if (! isset($connectionMap[$name])) {
$connectionMap[$name] = false;
$info = $this->get_dsn_info($name);
$dbdriver = $info['driver'];
$dbuserid = $info['user'];
$dbpassword = $info['password'];
$dbhost = $info['host'];
$database = $info['database'];
$api_tiki = null;
require 'db/local.php';
if (isset($api_tiki) && $api_tiki == 'adodb') {
// Force autoloading
if (! class_exists('ADOConnection')) {
return null;
}
$dbsqlplugin = ADONewConnection($dbdriver);
if ($dbsqlplugin->NConnect($dbhost, $dbuserid, $dbpassword, $database)) {
$connectionMap[$name] = new TikiDb_AdoDb($dbsqlplugin);
}
} else {
$dbsqlplugin = new PDO("$dbdriver:host=$dbhost;dbname=$database", $dbuserid, $dbpassword);
$connectionMap[$name] = new TikiDb_Pdo($dbsqlplugin);
}
}
return $connectionMap[$name];
} catch (Exception $e) {
Feedback::error($e->getMessage());
}
}
/*shared*/
// Returns IP address or IP address forwarded by the proxy if feature load balancer is set
/**
* @param $firewall true to detect ip behind a firewall
* @return null|string
*/
public function get_ip_address($firewall = 0)
{
global $prefs;
if ($firewall || (isset($prefs['feature_loadbalancer']) && $prefs['feature_loadbalancer'] === "y")) {
$header_checks = [
'HTTP_CF_CONNECTING_IP',
'HTTP_CLIENT_IP',
'HTTP_PRAGMA',
'HTTP_XONNECTION',
'HTTP_CACHE_INFO',
'HTTP_XPROXY',
'HTTP_PROXY',
'HTTP_PROXY_RENAMED',
'HTTP_PROXY_CONNECTION',
'HTTP_VIA',
'HTTP_X_COMING_FROM',
'HTTP_COMING_FROM',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED',
'HTTP_CACHE_CONTROL',
'HTTP_X_REAL_IP',
'REMOTE_ADDR'];
foreach ($header_checks as $key) {
if (array_key_exists($key, $_SERVER) === true) {
foreach (explode(',', $_SERVER[$key]) as $ip) {
$ip = trim($ip);
//filter the ip with filter functions
if (filter_var($ip, FILTER_VALIDATE_IP) !== false) {
return $ip;
}
}
}
}
}
if (isset($_SERVER['REMOTE_ADDR']) && filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP)) {
return $_SERVER['REMOTE_ADDR'];
} else {
return '0.0.0.0';
}
}
/*shared*/
/**
* @param $user
* @param $section
* @return bool
*/
public function check_rules($user, $section)
{
global $prefs;
// Admin is never banned
if ($user == 'admin') {
return false;
}
$fullip = $this->get_ip_address();
$ips = explode(".", $fullip);
$query = "select tb.`message`,tb.`user`,tb.`ip1`,tb.`ip2`,tb.`ip3`,tb.`ip4`,tb.`mode` from `tiki_banning` tb, `tiki_banning_sections` tbs where tbs.`banId`=tb.`banId` and tbs.`section`=? and ( (tb.`use_dates` = ?) or (tb.`date_from` <= FROM_UNIXTIME(?) and tb.`date_to` >= FROM_UNIXTIME(?)))";
$result = $this->fetchAll($query, [$section,'n',(int)$this->now,(int)$this->now]);
foreach ($result as $res) {
if (! $res['message']) {
$res['message'] = tra('You are banned from') . ': ' . $section;
}
if ($user && $res['mode'] == 'user') {
// check user
$pattern = '/' . $res['user'] . '/';
if (preg_match($pattern, $user)) {
return $res['message'];
}
if ($prefs['feature_banning_email'] === 'y') {
$info = TikiLib::lib('user')->get_user_info($user);
if (preg_match($pattern, $info['email'])) {
return $res['message'];
}
}
} else {
// check ip
if (count($ips) == 4) {
if (
($ips[0] == $res['ip1'] || $res['ip1'] == '*') && ($ips[1] == $res['ip2'] || $res['ip2'] == '*')
&& ($ips[2] == $res['ip3'] || $res['ip3'] == '*') && ($ips[3] == $res['ip4'] || $res['ip4'] == '*')
) {
return $res['message'];
}
}
}
}
return false;
}
// $noteId 0 means create a new note
/**
* @param $user
* @param $noteId
* @param $name
* @param $data
* @param null $parse_mode
* @return mixed
*/
public function replace_note($user, $noteId, $name, $data, $parse_mode = null)
{
$data = $this->convertAbsoluteLinksToRelative($data);
$size = strlen($data);
$queryData = [
'user' => $user,
'name' => $name,
'data' => $data,
'created' => $this->now,
'lastModif' => $this->now,
'size' => (int) $size,
'parse_mode' => $parse_mode,
];
$userNotes = $this->table('tiki_user_notes');
if ($noteId) {
$userNotes->update($queryData, ['noteId' => (int) $noteId,]);
} else {
$noteId = $userNotes->insert($queryData);
}
return $noteId;
}
/**
* @param $offset
* @param $maxRecords
* @param $sort_mode
* @param $find
* @return array
*/
public function list_watches($offset, $maxRecords, $sort_mode, $find)
{
$mid = '';
$mid2 = '';
$bindvars1 = $bindvars2 = [];
if ($find) {
$mid = ' where `event` like ? or `email` like ? or `user` like ? or `object` like ? or `type` like ?';
$mid2 = ' where `event` like ? or `group` like ? or `object` like ? or `type` like ?';
$bindvars1 = ["%$find%", "%$find%", "%$find%", "%$find%", "%$find%"];
$bindvars2 = ["%$find%", "%$find%", "%$find%", "%$find%"];
}
$query = "select 'user' as watchtype, `watchId`, `user`, `event`, `object`, `title`, `type`, `url`, `email` from `tiki_user_watches` $mid
UNION ALL
select 'group' as watchtype, `watchId`, `group`, `event`, `object`, `title`, `type`, `url`, '' as `email`
from `tiki_group_watches` $mid2
order by " . $this->convertSortMode($sort_mode);
$query_cant = 'select count(*) from `tiki_user_watches` ' . $mid;
$query_cant2 = 'select count(*) from `tiki_group_watches` ' . $mid2;
$ret = $this->fetchAll($query, array_merge($bindvars1, $bindvars2), $maxRecords, $offset);
$cant = $this->getOne($query_cant, $bindvars1) + $this->getOne($query_cant2, $bindvars2);
$retval = [];
$retval["data"] = $ret;
$retval["cant"] = $cant;
return $retval;
}
/*shared*/
/**
* @param $user
* @param $event
* @param $object
* @param null $type
* @param null $title
* @param null $url
* @param null $email
*
* @return int
* @throws Exception
*/
public function add_user_watch($user, $event, $object, $type = null, $title = null, $url = null, $email = null)
{
// Allow a warning when the watch won't be effective
if (empty($email)) {
$userlib = TikiLib::lib('user');
$email = $userlib->get_user_email($user);
if (empty($email)) {
return false;
}
}
if ($event != 'auth_token_called') {
$this->remove_user_watch($user, $event, $object, $type, $email);
}
$userWatches = $this->table('tiki_user_watches');
return $userWatches->insert(
[
'user' => $user,
'event' => $event,
'object' => $object,
'email' => $email,
'type' => $type,
'title' => $title,
'url' => $url,
]
);
}
/**
* @param $group
* @param $event
* @param $object
* @param null $type
* @param null $title
* @param null $url
* @return bool
*/
public function add_group_watch($group, $event, $object, $type = null, $title = null, $url = null)
{
if ($type == 'Category' && $object == 0) {
return false;
} else {
$this->remove_group_watch($group, $event, $object, $type);
$groupWatches = $this->table('tiki_group_watches');
$groupWatches->insert(
[
'group' => $group,
'event' => $event,
'object' => $object,
'type' => $type,
'title' => $title,
'url' => $url,
]
);
return true;
}
}
/**
* get_user_notification: returns the owner (user) related to a watchId
*
* @param mixed $id watchId
* @access public
* @return the user login related to the watchId
*/
public function get_user_notification($id)
{
return $this->table('tiki_user_watches')->fetchOne('user', ['watchId' => $id]);
}
/*shared*/
/**
* @param $id
*
* @return bool|TikiDb_Adodb_Result|TikiDb_Pdo_Result
*/
public function remove_user_watch_by_id($id)
{
global $tiki_p_admin_notifications, $user;
if ($tiki_p_admin_notifications === 'y' or $user === $this->get_user_notification($id)) {
return $this->table('tiki_user_watches')->delete(['watchId' => (int) $id]);
}
return false;
}
/**
* @param $id
*
* @return TikiDb_Adodb_Result|TikiDb_Pdo_Result
*/
public function remove_group_watch_by_id($id)
{
return $this->table('tiki_group_watches')->delete(['watchId' => (int) $id,]);
}
/*shared*/
/**
* @param string $user
* @param string $event
* @param string $object
* @param string $type = 'wiki page'
* @param string $email = ''
*
* @return TikiDb_Adodb_Result|TikiDb_Pdo_Result
*/
public function remove_user_watch($user, $event, $object, $type = 'wiki page', $email = '')
{
$conditions = [
'user' => $user,
'event' => $event,
'object' => $object,
'type' => $type,
];
if ($email) {
$conditions['email'] = $email;
}
return $this->table('tiki_user_watches')->deleteMultiple($conditions);
}
/*token notification*/
/**
* @param $event
* @param $object
* @param string $type
*/
public function remove_user_watch_object($event, $object, $type = 'wiki page')
{
$query = "delete from `tiki_user_watches` where `event`=? and `object`=? and `type` = ?";
$this->query($query, [$event,$object,$type]);
}
public function remove_stale_comment_watches()
{
$query = "DELETE FROM `tiki_user_watches` WHERE `event` = 'thread_comment_replied' AND `object` NOT IN (SELECT `threadId` FROM `tiki_comments`)";
$this->query($query);
}
/**
* @param $group
* @param $event
* @param $object
* @param string $type
*/
public function remove_group_watch($group, $event, $object, $type = 'wiki page')
{
$conditions = [
'group' => $group,
'event' => $event,
'object' => $object,
];
if (isset($type)) {
$conditions['type'] = $type;
}
$this->table('tiki_group_watches')->deleteMultiple($conditions);
}
/*shared*/
/**
* @param $user
* @param string $event
* @return mixed
*/
public function get_user_watches($user, $event = '')
{
$userWatches = $this->table('tiki_user_watches');
$conditions = [
'user' => $userWatches->exactly($user),
];
if ($event) {
$conditions['event'] = $event;
}
return $userWatches->fetchAll($userWatches->all(), $conditions);
}
/*shared*/
/**
* @return array
*/
public function get_watches_events()
{
$query = "select distinct `event` from `tiki_user_watches`";
$result = $this->fetchAll($query, []);
$ret = [];
foreach ($result as $res) {
$ret[] = $res['event'];
}
return $ret;
}
/*shared*/
/**
* @param $user
* @param $event
* @param $object
* @param null $type
* @return bool
*/
public function user_watches($user, $event, $object, $type = null)
{
$userWatches = $this->table('tiki_user_watches');
$conditions = [
'user' => $user,
'object' => $object,
];
if ($type) {
$conditions['type'] = $type;
}
if (is_array($event)) {
$conditions['event'] = $userWatches->in($event);
$ret = $userWatches->fetchColumn('event', $conditions);
return empty($ret) ? false : $ret;
} else {
return $userWatches->fetchCount($conditions);
}
}
/**
* @param $object
* @param $event
* @param null $type
* @return mixed
*/
public function get_groups_watching($object, $event, $type = null)
{
$groupWatches = $this->table('tiki_group_watches');
$conditions = [
'object' => $object,
'event' => $event,
];
if ($type) {
$conditions['type'] = $type;
}
return $groupWatches->fetchColumn('group', $conditions);
}
/*shared*/
/**
* @param $user
* @param $event
* @param $object
* @return mixed
*/
public function get_user_event_watches($user, $event, $object)
{
$userWatches = $this->table('tiki_user_watches');
return $userWatches->fetchAll(
$userWatches->all(),
[
'user' => $user,
'event' => $event,
'object' => is_array($object) ? $userWatches->in($object) : $object,
]
);
}
/*shared*/
/**
* @param $event
* @param $object
* @param null $info
* @return array
*/
public function get_event_watches($event, $object, $info = null)
{
global $prefs;
$ret = [];
$mid = '';
if ($prefs['feature_user_watches_translations'] == 'y' && $event == 'wiki_page_changed') {
// If $prefs['feature_user_watches_translations'] is turned on, also look for
// pages in a translation group.
$mid = "`event`=?";
$bindvars[] = $event;
$multilinguallib = TikiLib::lib('multilingual');
$page_info = $this->get_page_info($object);
$pages = $multilinguallib->getTranslations('wiki page', $page_info['page_id'], $object, '');
foreach ($pages as $page) {
$mids[] = "`object`=?";
$bindvars[] = $page['objName'];
}
$mid .= ' and (' . implode(' or ', $mids) . ')';
} elseif (
$prefs['feature_user_watches_translations'] == 'y'
&& $event == 'wiki_page_created'
) {
$page_info = $this->get_page_info($object);
$mid = "`event`='wiki_page_in_lang_created' and `object`=? and `type`='lang'";
$bindvars[] = $page_info['lang'];
} elseif ($prefs['feature_user_watches_languages'] == 'y' && $event == 'category_changed') {
$mid = "`object`=? and ((`event`='category_changed_in_lang' and `type`=? ) or (`event`='category_changed'))";
$bindvars[] = $object;
$bindvars[] = $info['lang'];
} elseif ($event == 'forum_post_topic') {
$mid = "(`event`=? or `event`=?) and `object`=?";
$bindvars[] = $event;
$bindvars[] = 'forum_post_topic_and_thread';
$bindvars[] = $object;
} elseif ($event == 'forum_post_thread') {
$mid = "(`event`=? and `object`=?) or ( `event`=? and `object`=?)";
$bindvars[] = $event;
$bindvars[] = $object;
$bindvars[] = 'forum_post_topic_and_thread';
$forumId = $info['forumId'];
$bindvars[] = $forumId;
} else {
$extraEvents = "";
if (substr_count($event, 'article_')) {
$extraEvents = " or `event`='article_*'";
} elseif ($event == 'wiki_comment_changes') {
$extraEvents = " or `event`='wiki_page_changed'";
// Blog comment mail
} elseif ($event == 'blog_comment_changes') {
$extraEvents = " or `event`='blog_page_changed'";
}
$mid = "(`event`=?$extraEvents) and (`object`=? or `object`='*')";
$bindvars[] = $event;
$bindvars[] = $object;
}
// Obtain the list of watches on event/object for user watches
// Union obtains all users member of groups being watched
// Distinct union insures there are no duplicates
$query = "select tuw.`watchId`, tuw.`user`, tuw.`event`, tuw.`object`, tuw.`title`, tuw.`type`, tuw.`url`, tuw.`email`,
tup1.`value` as language, tup2.`value` as mailCharset
from
`tiki_user_watches` tuw
left join `tiki_user_preferences` tup1 on (tup1.`user`=tuw.`user` and tup1.`prefName`='language')
left join `tiki_user_preferences` tup2 on (tup2.`user`=tuw.`user` and tup2.`prefName`='mailCharset')
where $mid
UNION DISTINCT
select tgw.`watchId`, uu.`login`, tgw.`event`, tgw.`object`, tgw.`title`, tgw.`type`, tgw.`url`, uu.`email`,
tup1.`value` as language, tup2.`value` as mailCharset
from
`tiki_group_watches` tgw
inner join `users_usergroups` ug on tgw.`group` = ug.`groupName`
inner join `users_users` uu on ug.`userId` = uu.`userId` and uu.`email` is not null and uu.`email` <> ''
left join `tiki_user_preferences` tup1 on (tup1.`user`=uu.`login` and tup1.`prefName`='language')
left join `tiki_user_preferences` tup2 on (tup2.`user`=uu.`login` and tup2.`prefName`='mailCharset')
where $mid
";
$result = $this->fetchAll($query, array_merge($bindvars, $bindvars));
if (count($result) > 0) {
foreach ($result as $res) {
if (empty($res['language'])) {
$res['language'] = $this->get_preference('site_language');
}
switch ($event) {
case 'wiki_page_changed':
case 'wiki_page_created':
$res['perm'] = ($this->user_has_perm_on_object($res['user'], $object, 'wiki page', 'tiki_p_view') ||
$this->user_has_perm_on_object($res['user'], $object, 'wiki page', 'tiki_p_admin_wiki'));
break;
case 'tracker_modified':
$res['perm'] = $this->user_has_perm_on_object($res['user'], $object, 'tracker', 'tiki_p_view_trackers');
break;
case 'tracker_item_modified':
$res['perm'] = $this->user_has_perm_on_object($res['user'], $object, 'trackeritem', 'tiki_p_view_trackers');
break;
case 'blog_post':
$res['perm'] = ($this->user_has_perm_on_object($res['user'], $object, 'blog', 'tiki_p_read_blog') ||
$this->user_has_perm_on_object($res['user'], $object, 'blog', 'tiki_p_admin_blog'));
break;
// Blog comment mail
case 'blog_comment_changes':
$res['perm'] = ($this->user_has_perm_on_object($res['user'], $object, 'blog', 'tiki_p_read_blog') ||
$this->user_has_perm_on_object($res['user'], $object, 'comments', 'tiki_p_read_comments'));
break;
case 'forum_post_topic':
$res['perm'] = ($this->user_has_perm_on_object($res['user'], $object, 'forum', 'tiki_p_forum_read') ||
$this->user_has_perm_on_object($res['user'], $object, 'forum', 'tiki_p_admin_forum'));
break;
case 'forum_post_thread':
$res['perm'] = ($this->user_has_perm_on_object($res['user'], $object, 'thread', 'tiki_p_forum_read') ||
$this->user_has_perm_on_object($res['user'], $object, 'forum', 'tiki_p_admin_forum'));
break;
case 'file_gallery_changed':
$res['perm'] = ($this->user_has_perm_on_object($res['user'], $object, 'file gallery', 'tiki_p_view_file_gallery') ||
$this->user_has_perm_on_object($res['user'], $object, 'file gallery', 'tiki_p_download_files'));
break;
case 'article_submitted':
case 'article_edited':
case 'article_deleted':
$userlib = TikiLib::lib('user');
$res['perm'] = (empty($object) && $userlib->user_has_permission($res['user'], 'tiki_p_read_article'))
|| $this->user_has_perm_on_object($res['user'], $object, 'article', 'tiki_p_read_article');
break;
case 'topic_article_created':
case 'topic_article_edited':
case 'topic_article_deleted':
$userlib = TikiLib::lib('user');
$res['perm'] = (empty($object) && $userlib->user_has_permission($res['user'], 'tiki_p_read_article'))
|| $this->user_has_perm_on_object($res['user'], $object, 'topic', 'tiki_p_read_article');
break;
case 'calendar_changed':
$res['perm'] = $this->user_has_perm_on_object($res['user'], $object, 'calendar', 'tiki_p_view_calendar');
break;
case 'category_changed':
$categlib = TikiLib::lib('categ');
$res['perm'] = $categlib->has_view_permission($res['user'], $object);
break;
case 'fgal_quota_exceeded':
global $tiki_p_admin_file_galleries;
$res['perm'] = ($tiki_p_admin_file_galleries == 'y');
break;
case 'article_commented':
case 'wiki_comment_changes':
$res['perm'] = $this->user_has_perm_on_object($res['user'], $object, 'comments', 'tiki_p_read_comments');
break;
case 'user_registers':
$userlib = TikiLib::lib('user');
$res['perm'] = $userlib->user_has_permission($res['user'], 'tiki_p_admin');
break;
case 'auth_token_called':
$res['perm'] = true;
break;
case 'user_joins_group':
$res['perm'] = $this->user_has_perm_on_object($res['user'], $object, 'group', 'tiki_p_group_view_members');
break;
case 'thread_comment_replied':
$res['perm'] = true;
break;
default:
// for security we deny all others.
$res['perm'] = false;
break;
}
if ($res['perm'] || empty($res['user']) && ! empty($res['email'])) {
// Allow admin created email (non-user) watches
$ret[] = $res;
}
}
}
// Also include users that are watching a category to which this object belongs to.
if ($event != 'category_changed') {
if ($prefs['feature_categories'] == 'y') {
$categlib = TikiLib::lib('categ');
$objectType = "";
switch ($event) {
case 'wiki_page_changed':
$objectType = "wiki page";
break;
case 'wiki_page_created':
$objectType = "wiki page";
break;
case 'blog_post':
$objectType = "blog";
break;
// Blog comment mail
case 'blog_page_changed':
$objectType = "blog page";
break;
case 'map_changed':
$objectType = "map_changed";
break;
case 'forum_post_topic':
$objectType = "forum";
break;
case 'forum_post_thread':
$objectType = "forum";
break;
case 'file_gallery_changed':
$objectType = "file gallery";
break;
case 'article_submitted':
$objectType = "topic";
break;
case 'tracker_modified':
$objectType = "tracker";
break;
case 'tracker_item_modified':
$objectType = "tracker";
break;
case 'calendar_changed':
$objectType = "calendar";
break;
}
if ($objectType != "") {
// If a forum post was changed, check the categories of the forum.
if ($event == "forum_post_thread") {
$commentslib = TikiLib::lib('comments');
$object = $commentslib->get_comment_forum_id($object);
}
// If a tracker item was changed, check the categories of the tracker.
if ($event == "tracker_item_modified") {
$trklib = TikiLib::lib('trk');
$object = $trklib->get_tracker_for_item($object);
}
$categs = $categlib->get_object_categories($objectType, $object);
foreach ($categs as $category) {
$watching_users = $this->get_event_watches('category_changed', $category, $info);
// Add all users that are not already included
foreach ($watching_users as $wu) {
$included = false;
foreach ($ret as $item) {
if ($item['user'] == $wu['user']) {
$included = true;
}
}
if (! $included) {
$ret[] = $wu;
}
}
}
}
}
}
return $ret;
}
/*shared*/
/**
* @return array
*/
public function dir_stats()
{
$sites = $this->table('tiki_directory_sites');
$categories = $this->table('tiki_directory_categories');
$search = $this->table('tiki_directory_search');
$aux = [];
$aux["valid"] = $sites->fetchCount(['isValid' => 'y']);
$aux["invalid"] = $sites->fetchCount(['isValid' => 'n']);
$aux["categs"] = $categories->fetchCount([]);
$aux["searches"] = $search->fetchOne($search->sum('hits'), []);
$aux["visits"] = $search->fetchOne($sites->sum('hits'), []);
return $aux;
}
/*shared*/
/**
* @param $offset
* @param $maxRecords
* @param $sort_mode
* @param $find
* @return array
*/
public function dir_list_all_valid_sites2($offset, $maxRecords, $sort_mode, $find)
{
$sites = $this->table('tiki_directory_sites');
$conditions = [
'isValid' => 'y',
];
if ($find) {
$conditions['search'] = $sites->expr('(`name` like ? or `description` like ?)', ["%$find%", "%$find%"]);
}
return [
'data' => $sites->fetchAll($sites->all(), $conditions, $maxRecords, $offset, $sites->expr($this->convertSortMode($sort_mode))),
'cant' => $sites->fetchCount($conditions),
];
}
/*shared*/
/**
* @param $categId
* @return mixed
*/
public function get_directory($categId)
{
return $this->table('tiki_directory_categories')->fetchFullRow(['categId' => $categId]);
}
/*shared*/
/**
* @param $user
* @return mixed
*/
public function user_unread_messages($user)
{
$messages = $this->table('messu_messages');
return $messages->fetchCount(
[
'user' => $user,
'isRead' => 'n',
]
);
}
/*shared*/
/**
* @return array
*/
public function get_online_users()
{
if (! isset($this->online_users_cache)) {
$this->update_session();
$this->online_users_cache = [];
$query = "select s.`user`, p.`value` as `realName`, `timestamp`, `tikihost` from `tiki_sessions` s left join `tiki_user_preferences` p on s.`user`<>? and s.`user` = p.`user` and p.`prefName` = 'realName' where s.`user` is not null;";
$result = $this->fetchAll($query, ['']);
foreach ($result as $res) {
$res['user_information'] = $this->get_user_preference($res['user'], 'user_information', 'public');
$res['allowMsgs'] = $this->get_user_preference($res['user'], 'allowMsgs', 'y');
$this->online_users_cache[$res['user']] = $res;
}
}
return $this->online_users_cache;
}
/*shared*/
/**
* @param $whichuser
* @return bool
*/
public function is_user_online($whichuser)
{
if (! isset($this->online_users_cache)) {
$this->get_online_users();
}
return(isset($this->online_users_cache[$whichuser]));
}
/*
* Score methods begin
*/
// All information about an event type
// shared
/**
* @param $event
* @return mixed
*/
public function get_event($event)
{
return $this->table('tiki_score')->fetchFullRow(['event' => $event]);
}
// List users by best scoring
// shared
/**
* @param int $limit
* @param int $start
* @return mixed
*/
public function rank_users($limit = 10, $start = 0)
{
global $prefs;
$score_expiry_days = $prefs['feature_score_expday'];
if (! $start) {
$start = "0";
}
if (empty($score_expiry_days)) {
// score does not expire
$query = "select `recipientObjectId` as `login`,
`pointsBalance` as `score`
from `tiki_object_scores` tos
where `recipientObjectType`='user'
and tos.`id` = (select max(id) from `tiki_object_scores` where `recipientObjectId` = tos.`recipientObjectId` and `recipientObjectType`='user' group by `recipientObjectId`)
group by `recipientObjectId`, `pointsBalance` order by `score` desc";
$result = $this->fetchAll($query, null, $limit, $start);
} else {
// score expires
$query = "select `recipientObjectId` as `login`,
`pointsBalance` - ifnull((select `pointsBalance` from `tiki_object_scores`
where `recipientObjectId`=tos.`recipientObjectId`
and `recipientObjectType`='user'
and `date` < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL ? DAY))
order by id desc limit 1), 0) as `score`
from `tiki_object_scores` tos
where `recipientObjectType`='user'
and tos.`id` = (select max(id) from `tiki_object_scores` where `recipientObjectId` = tos.`recipientObjectId` and `recipientObjectType`='user' group by `recipientObjectId`)
group by `recipientObjectId`, `pointsBalance` order by `score` desc";
$result = $this->fetchAll($query, $score_expiry_days, $limit, $start);
}
foreach ($result as & $res) {
$res['position'] = ++$start;
}
return $result;
}
// Returns html <img> tag to star corresponding to user's score
// shared
/**
* @param $score
* @return string
*/
public function get_star($score)
{
global $prefs;
$star = '';
$star_colors = [0 => 'grey',
100 => 'blue',
500 => 'green',
1000 => 'yellow',
2500 => 'orange',
5000 => 'red',
10000 => 'purple'];
foreach ($star_colors as $boundary => $color) {
if ($score >= $boundary) {
$star = 'star_' . $color . '.gif';
}
}
if (! empty($star)) {
$alt = sprintf(tra("%d points"), $score);
if ($prefs['theme_iconset'] === 'legacy') {
$star = "<img src='img/icons/$star' height='11' width='11' alt='$alt' />&nbsp;";
} else {
$smarty = TikiLib::lib('smarty');
$smarty->loadPlugin('smarty_function_icon');
$star = smarty_function_icon(['name' => 'star', 'istyle' => 'color:' . $color, 'iclass' => 'tips',
'ititle' => ':' . $alt], $smarty->getEmptyInternalTemplate()) . "&nbsp;";
}
}
return $star;
}
/*
* Score methods end
*/
//shared
// \todo remove all hardcoded html in get_user_avatar()
/**
* @param $user
* @param string $float
* @return string
*/
public function get_user_avatar($user, $float = '')
{
global $prefs;
if (empty($user)) {
return '';
}
if (is_array($user)) {
$res = $user;
$user = $user['login'];
} else {
$res = $this->table('users_users')->fetchRow(['login', 'avatarType', 'avatarLibName', 'email'], ['login' => $user]);
}
if (! $res) {
return '';
}
if ($prefs['user_use_gravatar'] == 'y' && $res['email']) {
$https_mode = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on';
$hash = md5(strtolower(trim($res['email'])));
if ($https_mode) {
$url = "https://secure.gravatar.com/avatar/$hash?s=45";
} else {
$url = "http://www.gravatar.com/avatar/$hash?s=45";
}
$type = 'g';
} else {
$type = $res["avatarType"] ? $res["avatarType"] : 'u';
$libname = $res["avatarLibName"];
$ret = '';
}
$style = '';
if (strcasecmp($float, "left") == 0) {
$style = "style='float:left;margin-right:5px;'";
} elseif (strcasecmp($float, "right") == 0) {
$style = "style='float:right;margin-left:5px;'";
}
$username = htmlspecialchars(
TikiLib::lib('user')->clean_user($user),
ENT_COMPAT
);
switch ($type) {
case 'l':
if ($libname) {
$ret = '<img class="user-profile-picture rounded" width="45" height="45" src="' . $libname . '" ' . $style . ' alt="' . $username . '">';
}
break;
case 'u':
$userprefslib = TikiLib::lib('userprefs');
$path = $userprefslib->get_public_avatar_path($user);
if ($path) {
$url = $this->tikiUrlOpt($path);
$ret = '<img class="user-profile-picture rounded" src="' . htmlspecialchars($url, ENT_NOQUOTES) . '" ' . $style . ' alt="' . $username . '">';
}
break;
case 'g':
$ret = '<img class="user-profile-picture rounded" src="' . htmlspecialchars($url, ENT_NOQUOTES) . '" ' . $style . ' alt="' . $username . '">';
break;
case 'n':
default:
$ret = '';
break;
}
return $ret;
}
/**
* Return user avatar as Base64 encoded inline image.
*/
public function get_user_avatar_inline($user)
{
global $prefs;
if (empty($user)) {
return '';
}
if (is_array($user)) {
$res = $user;
$user = $user['login'];
} else {
$res = $this->table('users_users')->fetchRow(['login', 'avatarType', 'avatarFileType', 'avatarData', 'avatarLibName', 'email'], ['login' => $user]);
}
if (! $res) {
return '';
}
if ($prefs['user_use_gravatar'] == 'y' && $res['email']) {
$hash = md5(strtolower(trim($res['email'])));
$url = "https://secure.gravatar.com/avatar/$hash.jpg?s=45";
$data = file_get_contents($url);
$mime = 'image/jpeg';
} elseif ($res['avatarType'] == 'l') {
$url = $this->tikiUrlOpt($res['avatarLibName']);
$data = file_get_contents($url);
if (class_exists('finfo')) {
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->buffer($data);
} else {
$mime = 'image/jpeg';
}
} else {
$data = $res['avatarData'];
$mime = $res['avatarFileType'];
}
if ($data && $mime) {
return "data:$mime;base64," . base64_encode($data);
} else {
return '';
}
}
/*shared*/
/**
* @return array
*/
public function get_forum_sections()
{
$query = "select distinct `section` from `tiki_forums` where `section`<>?";
$result = $this->fetchAll($query, ['']);
$ret = [];
foreach ($result as $res) {
$ret[] = $res["section"];
}
return $ret;
}
/* Referer stats */
/*shared*/
/**
* @param $referer
* @param $fullurl
*/
public function register_referer($referer, $fullurl)
{
$refererStats = $this->table('tiki_referer_stats');
$cant = $refererStats->fetchCount(['referer' => $referer]);
if ($cant) {
$refererStats->update(
[
'hits' => $refererStats->increment(1),
'last' => $this->now,
'lasturl' => $fullurl,
],
['referer' => $referer]
);
} else {
$refererStats->insert(
[
'last' => $this->now,
'referer' => $referer,
'hits' => 1,
'lasturl' => $fullurl,
]
);
}
}
// File attachments functions for the wiki ////
/*shared*/
/**
* @param $id
* @return bool
*/
public function add_wiki_attachment_hit($id)
{
global $prefs, $user;
if (StatsLib::is_stats_hit()) {
$wikiAttachments = $this->table('tiki_wiki_attachments');
$wikiAttachments->update(
['hits' => $wikiAttachments->increment(1)],
['attId' => (int) $id]
);
}
return true;
}
/*shared*/
/**
* @param $attId
* @return mixed
*/
public function get_wiki_attachment($attId)
{
return $this->table('tiki_wiki_attachments')->fetchFullRow(['attId' => (int) $attId]);
}
// Last visit module ////
/*shared*/
/**
* @param $user
* @return array|bool
*/
public function get_news_from_last_visit($user)
{
if (! $user) {
return false;
}
$last = $this->table('users_users')->fetchOne('lastLogin', ['login' => $user]);
$ret = [];
if (! $last) {
$last = time();
}
$ret["lastVisit"] = $last;
$ret["pages"] = $this->getOne("select count(*) from `tiki_pages` where `lastModif`>?", [(int)$last]);
$ret["files"] = $this->getOne("select count(*) from `tiki_files` where `created`>?", [(int)$last]);
$ret["comments"] = $this->getOne("select count(*) from `tiki_comments` where `commentDate`>?", [(int)$last]);
$ret["users"] = $this->getOne("select count(*) from `users_users` where `registrationDate`>? and `provpass`=?", [(int)$last, '']);
$ret["trackers"] = $this->getOne("select count(*) from `tiki_tracker_items` where `lastModif`>?", [(int)$last]);
$ret["calendar"] = $this->getOne("select count(*) from `tiki_calendar_items` where `lastmodif`>?", [(int)$last]);
return $ret;
}
/**
* @return mixed|string
*/
public function pick_cookie()
{
$cant = $this->getOne("select count(*) from `tiki_cookies`", []);
if (! $cant) {
return '';
}
$bid = rand(0, $cant - 1);
//$cookie = $this->getOne("select `cookie` from `tiki_cookies` limit $bid,1"); getOne seems not to work with limit
$result = $this->query("select `cookie` from `tiki_cookies`", [], 1, $bid);
if ($res = $result->fetchRow()) {
$cookie = str_replace("\n", "", $res['cookie']);
return preg_replace('/^(.+?)(\s*--.+)?$/', '<em>"$1"</em>$2', $cookie);
} else {
return "";
}
}
public function get_usage_chart_data()
{
TikiLib::lib('quiz')->compute_quiz_stats();
$data['xdata'][] = tra('wiki');
$data['ydata'][] = $this->getOne('select sum(`hits`) from `tiki_pages`', []);
$data['xdata'][] = tra('file-g');
$data['ydata'][] = $this->getOne('select sum(`hits`) from `tiki_file_galleries`', []);
$data['xdata'][] = tra('FAQs');
$data['ydata'][] = $this->getOne('select sum(`hits`) from `tiki_faqs`', []);
$data['xdata'][] = tra('quizzes');
$data['ydata'][] = $this->getOne('select sum(`timesTaken`) from `tiki_quiz_stats_sum`', []);
$data['xdata'][] = tra('arts');
$data['ydata'][] = $this->getOne('select sum(`nbreads`) from `tiki_articles`', []);
$data['xdata'][] = tra('blogs');
$data['ydata'][] = $this->getOne('select sum(`hits`) from `tiki_blogs`', []);
$data['xdata'][] = tra('forums');
$data['ydata'][] = $this->getOne('select sum(`hits`) from `tiki_forums`', []);
return $data;
}
// User assigned modules ////
/*shared*/
/**
* @param $id
* @return mixed
*/
public function get_user_login($id)
{
return $this->table('users_users')->fetchOne('login', ['userId' => (int) $id]);
}
/**
* @param $u
* @return int
*/
public function get_user_id($u)
{
// Anonymous is not in db
if ($u == '') {
return -1;
}
// If we ask for the current user id and if we already know it in session
$current = ( isset($_SESSION['u_info']) && $u == $_SESSION['u_info']['login'] );
if (isset($_SESSION['u_info']['id']) && $current) {
return $_SESSION['u_info']['id'];
}
// In other cases, we look in db
$id = $this->table('users_users')->fetchOne('userId', ['login' => $u]);
$id = ($id === false) ? -1 : $id;
if ($current) {
$_SESSION['u_info']['id'] = $id;
}
return $id;
}
/*shared*/
/**
* @param $group
* @return array
*/
public function get_groups_all($group)
{
$result = $this->table('tiki_group_inclusion')->fetchColumn('groupName', ['includeGroup' => $group]);
$ret = $result;
foreach ($result as $res) {
$ret = array_merge($ret, $this->get_groups_all($res));
}
return array_unique($ret);
}
/*shared*/
/**
* @param $group
* @return array
*/
public function get_included_groups($group)
{
$result = $this->table('tiki_group_inclusion')->fetchColumn('includeGroup', ['groupName' => $group]);
$ret = $result;
foreach ($result as $res) {
$ret = array_merge($ret, $this->get_included_groups($res));
}
return array_unique($ret);
}
/*shared*/
/**
* @param string $user username
* @param bool $included_groups include inherited/included groups
*
* @return array
*/
public function get_user_groups($user, $included_groups = true)
{
global $prefs;
$userlib = TikiLib::lib('user');
if (empty($user) || $user === 'Anonymous') {
$ret = [];
$ret[] = "Anonymous";
return $ret;
}
if ($prefs['feature_intertiki'] == 'y' and empty($prefs['feature_intertiki_mymaster']) and strstr($user, '@')) {
$realm = substr($user, strpos($user, '@') + 1);
if (isset($prefs['interlist'][$realm])) {
$user = substr($user, 0, strpos($user, '@'));
$groups = $prefs['interlist'][$realm]['groups'] . ',Anonymous';
return explode(',', $groups);
}
}
$cachekey = $user . ($included_groups ? '' : '_direct');
if (! isset($this->usergroups_cache[$cachekey])) {
$userid = $this->get_user_id($user);
$result = $this->table('users_usergroups')->fetchColumn('groupName', ['userId' => $userid]);
$ret = $result;
if ($included_groups) {
foreach ($result as $res) {
$ret = array_merge($ret, $userlib->get_included_groups($res));
}
}
$ret[] = "Registered";
if (isset($_SESSION["groups_are_emulated"]) && $_SESSION["groups_are_emulated"] == "y") {
if (in_array('Admins', $ret)) {
// Members of group 'Admins' can emulate being in any list of groups
$ret = unserialize($_SESSION['groups_emulated']);
} else {
// For security purposes, user can only emulate a subset of user's list of groups
// This prevents privilege escalation
$ret = array_intersect($ret, unserialize($_SESSION['groups_emulated']));
}
}
$ret = array_values(array_unique($ret));
$this->usergroups_cache[$cachekey] = $ret;
return $ret;
} else {
return $this->usergroups_cache[$cachekey];
}
}
/**
* @param $user
*/
public function invalidate_usergroups_cache($user)
{
unset($this->usergroups_cache[$user]);
unset($this->usergroups_cache[$user . '_direct']);
}
/**
* @param $user
* @return string
*/
public function get_user_cache_id($user)
{
$groups = $this->get_user_groups($user);
sort($groups, SORT_STRING);
$cacheId = implode(":", $groups);
if ($user == 'admin') {
// in this case user get permissions from no group
$cacheId = 'ADMIN:' . $cacheId;
}
return $cacheId;
}
/*shared*/
/**
* @return string
* @see UsersLib::genPass(), which generates passwords easier to remember
* TODO: Merge with above
*/
public static function genPass()
{
global $prefs;
$length = max($prefs['min_pass_length'], 8);
$list = ['aeiou', 'AEIOU', 'bcdfghjklmnpqrstvwxyz', 'BCDFGHJKLMNPQRSTVWXYZ', '0123456789'];
$list[] = $prefs['pass_chr_special'] == 'y' ? '_*&+!*-=$@' : '_';
shuffle($list);
$r = '';
for ($i = 0; $i < $length; $i++) {
$ch = $list[$i % count($list)];
$r .= $ch[rand(0, strlen($ch) - 1)];
}
return $r;
}
// generate a random string (for unsubscription code etc.)
/**
* @param string $base
* @return string
*/
public function genRandomString($base = "")
{
if ($base == "") {
$base = $this->genPass();
}
$base .= microtime();
return md5($base);
}
// This function calculates the pageRanks for the tiki_pages
// it can be used to compute the most relevant pages
// according to the number of links they have
// this can be a very interesting ranking for the Wiki
// More about this on version 1.3 when we add the pageRank
// column to tiki_pages
/**
* @param int $loops
* @return array
*/
public function pageRank($loops = 16)
{
$pagesTable = $this->table('tiki_pages');
$ret = $pagesTable->fetchColumn('pageName', []);
// Now calculate the loop
$pages = [];
foreach ($ret as $page) {
$val = 1 / count($ret);
$pages[$page] = $val;
$pagesTable->update(['pageRank' => (int) $val], ['pageName' => $page]);
}
for ($i = 0; $i < $loops; $i++) {
foreach ($pages as $pagename => $rank) {
// Get all the pages linking to this one
// Fixed query. -rlpowell
$query = "select `fromPage` from `tiki_links` where `toPage` = ? and `fromPage` not like 'objectlink:%'";
// page rank does not count links from non-page objects TODO: full feature allowing this with options
$result = $this->fetchAll($query, [$pagename]);
$sum = 0;
foreach ($result as $res) {
$linking = $res["fromPage"];
if (isset($pages[$linking])) {
// Fixed query. -rlpowell
$q2 = "select count(*) from `tiki_links` where `fromPage`= ? and `fromPage` not like 'objectlink:%'";
// page rank does not count links from non-page objects TODO: full feature allowing this with options
$cant = $this->getOne($q2, [$linking]);
if ($cant == 0) {
$cant = 1;
}
$sum += $pages[$linking] / $cant;
}
}
$val = (1 - 0.85) + 0.85 * $sum;
$pages[$pagename] = $val;
$pagesTable->update(['pageRank' => (int) $val], ['pageName' => $pagename]);
}
}
arsort($pages);
return $pages;
}
/**
* @param $maxRecords
* @return array
*/
public function list_recent_forum_topics($maxRecords)
{
$bindvars = ['forum', 0];
$query = 'select `threadId`, `forumId` from `tiki_comments`,`tiki_forums`'
. " where `object`=`forumId` and `objectType`=? and `tiki_comments`.`parentId`=? order by " . $this->convertSortMode('commentDate_desc');
$result = $this->fetchAll($query, $bindvars, $maxRecords * 3, 0); // Load a little more, for permission filters
$res = $ret = $retids = [];
$n = 0;
foreach ($result as $res) {
$objperm = $this->get_perm_object($res['threadId'], 'thread', '', false);
if ($objperm['tiki_p_forum_read'] == 'y') {
$retids[] = $res['threadId'];
$n++;
if ($n >= $maxRecords) {
break;
}
}
}
if ($n > 0) {
$query = 'select * from `tiki_comments`'
. ' where `threadId` in (' . implode(',', $retids) . ') order by ' . $this->convertSortMode('commentDate_desc');
$ret = $this->fetchAll($query);
}
$retval = [];
$retval['data'] = $ret;
$retval['cant'] = $n;
return $retval;
}
/*shared*/
/**
* @param $forumId
* @param $offset
* @param $maxRecords
* @param $sort_mode
* @param $find
* @return array
*/
public function list_forum_topics($forumId, $offset, $maxRecords, $sort_mode, $find)
{
$bindvars = [$forumId,$forumId,'forum',0];
if ($find) {
$findesc = '%' . $find . '%';
$mid = " and (`title` like ? or `data` like ?)";
$bindvars[] = $findesc;
$bindvars[] = $findesc;
} else {
$mid = "";
}
$query = "select * from `tiki_comments`,`tiki_forums` where ";
$query .= " `forumId`=? and `object`=? and `objectType`=? and `parentId`=? $mid order by " . $this->convertSortMode($sort_mode);
$query_cant = "select count(*) from `tiki_comments`,`tiki_forums` where ";
$query_cant .= " `forumId`=? and `object`=? and `objectType`=? and `parentId`=? $mid";
$ret = $this->fetchAll($query, $bindvars, $maxRecords, $offset);
$cant = $this->getOne($query_cant, $bindvars);
$retval = [];
$retval["data"] = $ret;
$retval["cant"] = $cant;
return $retval;
}
/*shared*/
/**
* @param $type
* @param $id
* @return bool
*/
public function remove_object($type, $id)
{
global $prefs;
$categlib = TikiLib::lib('categ');
$objectlib = TikiLib::lib('object');
$categlib->uncategorize_object($type, $id);
// Now remove comments
$threads = $this->table('tiki_comments')->fetchColumn('threadId', ['object' => $id, 'objectType' => $type]);
if (! empty($threads)) {
$commentslib = TikiLib::lib('comments');
foreach ($threads as $threadId) {
$commentslib->remove_comment($threadId);
}
}
// Remove individual permissions for this object if they exist
$object = $type . $id;
$this->table('users_objectpermissions')->deleteMultiple(['objectId' => md5($object), 'objectType' => $type]);
// remove links from this object to pages
$linkhandle = "objectlink:$type:$id";
$this->table('tiki_links')->deleteMultiple(['fromPage' => $linkhandle]);
// remove fgal backlinks
if ($prefs['feature_file_galleries'] == 'y') {
$filegallib = TikiLib::lib('filegal');
$filegallib->deleteBacklinks(['type' => $type, 'object' => $id]);
}
// remove object
$objectlib->delete_object($type, $id);
$objectAttributes = $this->table('tiki_object_attributes');
$objectAttributes->deleteMultiple(['type' => $type,'itemId' => $id]);
$objectRelations = $this->table('tiki_object_relations');
$objectRelations->deleteMultiple(['source_type' => $type, 'source_itemId' => $id]);
$objectRelations->deleteMultiple(['target_type' => $type, 'target_itemId' => $id]);
return true;
}
/*shared*/
/**
* @param $offset
* @param $maxRecords
* @param $sort_mode
* @param string $find
* @param string $type
* @param string $structureName
* @return array
*/
public function list_received_pages($offset, $maxRecords, $sort_mode, $find = '', $type = '', $structureName = '')
{
$bindvars = [];
if ($type == 's') {
$mid = ' `trp`.`structureName` is not null ';
}
if (! $sort_mode) {
$sort_mode = '`structureName_asc';
} elseif ($type == 'p') {
$mid = ' `trp`.`structureName` is null ';
}
if (! $sort_mode) {
$sort_mode = '`pageName_asc';
} else {
$mid = '';
}
if ($find) {
$findesc = '%' . $find . '%';
if ($mid) {
$mid .= ' and ';
}
$mid .= '(`trp`.`pageName` like ? or `trp`.`structureName` like ? or `trp`.`data` like ?)';
$bindvars[] = $findesc;
$bindvars[] = $findesc;
$bindvars[] = $findesc;
}
if ($structureName) {
if ($mid) {
$mid .= ' and ';
}
$mid .= ' `trp`.`structureName`=? ';
$bindvars[] = $structureName;
}
if ($mid) {
$mid = "where $mid";
}
$query = "select trp.*, tp.`pageName` as pageExists from `tiki_received_pages` trp left join `tiki_pages` tp on (tp.`pageName`=trp.`pageName`) $mid order by `structureName` asc, `pos` asc," . $this->convertSortMode($sort_mode);
$query_cant = "select count(*) from `tiki_received_pages` trp $mid";
$ret = $this->fetchAll($query, $bindvars, $maxRecords, $offset);
$cant = $this->getOne($query_cant, $bindvars);
$retval = [];
$retval["data"] = $ret;
$retval["cant"] = $cant;
return $retval;
}
// User voting system ////
// Used to vote everything (polls,comments,files,submissions,etc) ////
// Checks if a user has voted
/*shared*/
/**
* @param $user
* @param $id
* @return bool
*/
public function user_has_voted($user, $id)
{
global $prefs;
$ret = false;
if (isset($_SESSION['votes'])) {
$votes = $_SESSION['votes'];
if (is_array($votes) && in_array($id, $votes)) { // has already voted in the session (logged or not)
return true;
}
}
if (! $user) {
if ($prefs['ip_can_be_checked'] != 'y' && ! isset($_COOKIE[ session_name() ])) {// cookie has not been activated too bad for him
$ret = true;
} elseif (isset($_COOKIE[md5("tiki_wiki_poll_$id")])) {
$ret = true;
}
// we have no idea if cookie was deleted or if really he has not voted
} else {
$query = "select count(*) from `tiki_user_votings` where `user`=? and `id`=?";
if ($this->getOne($query, [$user,(string) $id]) > 0) {
$ret = true;
}
}
if ($prefs['ip_can_be_checked'] == 'y') {
$query = 'select count(*) from `tiki_user_votings` where `ip`=? and `id`=?';
if ($this->getOne($query, [$this->get_ip_address(), $id]) > 0) {
return true; // IP has already voted logged or not
}
}
return $ret;
}
// Registers a user vote
/*shared*/
/**
* @param $user
* @param $id
* @param bool $optionId
* @param array $valid_options
* @param bool $allow_revote
* @return bool
*/
public function register_user_vote($user, $id, $optionId = false, array $valid_options = [], $allow_revote = false)
{
global $prefs;
// If an option is specified and the valid options are specified, skip the vote entirely if not valid
if (false !== $optionId && count($valid_options) > 0 && ! in_array($optionId, $valid_options)) {
return false;
}
if ($user && ! $allow_revote && $this->user_has_voted($user, $id)) {
return false;
}
$userVotings = $this->table('tiki_user_votings');
$ip = $this->get_ip_address();
$_SESSION['votes'][] = $id;
setcookie(md5("tiki_wiki_poll_$id"), $ip, time() + 60 * 60 * 24 * 300);
if (! $user) {
if ($prefs['ip_can_be_checked'] == 'y') {
$userVotings->delete(['ip' => $ip, 'id' => $id]);
if ($optionId !== false && $optionId != 'NULL') {
$userVotings->insert(
[
'user' => '',
'ip' => $ip,
'id' => (string) $id,
'optionId' => (int) $optionId,
'time' => $this->now,
]
);
}
} elseif (isset($_COOKIE[md5("tiki_wiki_poll_$id")])) {
Feedback::error(tr('You need to enable ip_can_be_checked feature before to change vote as anonymous. If you can\'t, please contact the administrator'));
return false;
} elseif ($optionId !== false && $optionId != 'NULL') {
$userVotings->insert(
[
'user' => '',
'ip' => $ip,
'id' => (string) $id,
'optionId' => (int) $optionId,
'time' => $this->now,
]
);
}
} else {
if ($prefs['ip_can_be_checked'] == 'y') {
$userVotings->delete(['user' => $user,'id' => $id]);
$userVotings->delete(['ip' => $ip,'id' => $id]);
} else {
$userVotings->delete(['user' => $user,'id' => $id]);
}
if ($optionId !== false && $optionId !== 'NULL') {
$userVotings->insert(
[
'user' => $user,
'ip' => $ip,
'id' => (string) $id,
'optionId' => (int) $optionId,
'time' => $this->now,
]
);
}
}
return true;
}
/**
* @param $id
* @param $user
* @return null
*/
public function get_user_vote($id, $user)
{
global $prefs;
$vote = null;
if ($user) {
$vote = $this->getOne("select `optionId` from `tiki_user_votings` where `user` = ? and `id` = ? order by `time` desc", [ $user, $id]);
}
if ($vote == null && $prefs['ip_can_be_checked'] == 'y') {
$ip = $this->get_ip_address();
$vote = $this->getOne("select `optionId` from `tiki_user_votings` where `ip` = ? and `id` = ? order by `time` desc", [ $ip, $id]);
}
return $vote;
}
// end of user voting methods
/**
* @param int $offset
* @param $maxRecords
* @param string $sort_mode
* @param string $find
* @param bool $include_prefs
* @return array
*/
public function list_users($offset = 0, $maxRecords = -1, $sort_mode = 'pref:realName', $find = '', $include_prefs = false)
{
global $user, $prefs;
$userprefslib = TikiLib::lib('userprefs');
$bindvars = [];
if ($find) {
$findesc = '%' . $find . '%';
$mid = 'where (`login` like ? or p1.`value` like ?)';
$mid_cant = $mid;
$bindvars[] = $findesc;
$bindvars[] = $findesc;
$bindvars2 = [$findesc, $findesc];
$find_join = " left join `tiki_user_preferences` p1 on (u.`login` = p1.`user` and p1.`prefName` = 'realName')";
$find_join_cant = $find_join;
} else {
$mid = '';
$bindvars2 = [];
$find_join = '';
$find_join_cant = '';
$mid_cant = '';
}
// This allows to use a sort_mode by prefs
// In this case, sort_mode must have this syntax :
// pref:PREFERENCE_NAME[_asc|_desc]
// e.g. to sort on country :
// pref:country OR pref:country_asc OR pref:country_desc
if ($ppos = strpos($sort_mode, ':')) {
$sort_value = substr($sort_mode, $ppos + 1);
$sort_way = 'asc';
if (preg_match('/^(.+)_(asc|desc)$/i', $sort_value, $regs)) {
$sort_value = $regs[1];
$sort_way = $regs[2];
unset($regs);
}
if ($find_join != '' && $sort_value == 'realName') {
// Avoid two joins if we can do only one
$find_join = '';
$mid = 'where (`login` like ? or p.`value` like ?)';
}
$sort_mode = "p.`value` $sort_way";
$pref_where = ( ( $mid == '' ) ? 'where' : $mid . ' and' ) . " p.`prefName` = '$sort_value'";
$pref_join = 'left join `tiki_user_preferences` p on (u.`login` = p.`user`)';
$pref_field = ', p.`value` as sf';
} else {
$sort_mode = $this->convertSortMode($sort_mode);
$pref_where = $mid;
$pref_join = '';
$pref_field = '';
}
if ($sort_mode != '') {
$sort_mode = 'order by ' . $sort_mode;
}
$query = "select u.* $pref_field from `users_users` u $pref_join $find_join $pref_where $sort_mode";
$query_cant = "select count(distinct u.`login`) from `users_users` u $find_join_cant $mid_cant";
$result = $this->fetchAll($query, $bindvars, $maxRecords, $offset);
$cant = $this->getOne($query_cant, $bindvars2);
$ret = [];
foreach ($result as $res) {
if ($include_prefs) {
$res['preferences'] = $userprefslib->get_userprefs($res['login']);
}
$ret[] = $res;
}
return ['data' => $ret, 'cant' => $cant];
}
// CMS functions -ARTICLES- & -SUBMISSIONS- ////
/*shared*/
/**
* @param int $max
* @return mixed
*/
public function get_featured_links($max = 10)
{
$query = "select * from `tiki_featured_links` where `position` > ? order by " . $this->convertSortMode("position_asc");
return $this->fetchAll($query, [0], (int)$max, 0);
}
/**
* @param $sessionId
*/
public function setSessionId($sessionId)
{
$this->sessionId = $sessionId;
}
/**
* @return null
*/
public function getSessionId()
{
return $this->sessionId;
}
/**
* @return bool
*/
public function update_session()
{
static $uptodate = false;
if ($uptodate === true || $this->sessionId === null) {
return true;
}
global $user, $prefs;
$logslib = TikiLib::lib('logs');
if ($user === false) {
$user = '';
}
// If pref login_multiple_forbidden is set, length of tiki_sessions must match real session length to be up to date so we can detect concurrent logins of same user
if ($prefs['login_multiple_forbidden'] == 'y') {
$delay = ini_get('session.gc_maxlifetime');
} else { // Low value so as to guess who actually is in front of the computer
$delay = 5 * 60; // 5 minutes
}
$oldy = $this->now - $delay;
if ($user != '') { // was the user timeout?
$query = "select count(*) from `tiki_sessions` where `sessionId`=?";
$cant = $this->getOne($query, [$this->sessionId]);
if ($cant == 0) {
if ($prefs['login_multiple_forbidden'] != 'y' || $user == 'admin') {
// Recover after timeout
$logslib->add_log("login", "back", $user, '', '', $this->now);
} else {
// Prevent multiple sessions for same user
// Must check any user session, not only timed out ones
$query = "SELECT count(*) FROM `tiki_sessions` WHERE user = ?";
$cant = $this->getOne($query, [$user]);
if ($cant == 0) {
// Recover after timeout (no other session)
$logslib->add_log("login", "back", $user, '', '', $this->now);
} else {
// User has an active session on another browser
$userlib = TikiLib::lib('user');
$userlib->user_logout($user, false, '');
}
}
}
}
$query = "select * from `tiki_sessions` where `timestamp`<?";
$result = $this->fetchAll($query, [$oldy]);
foreach ($result as $res) {
if ($res['user'] && $res['user'] != $user) {
$logslib->add_log('login', 'timeout', $res['user'], ' ', ' ', $res['timestamp'] + $delay);
}
}
$sessions = $this->table('tiki_sessions');
$sessions->delete(['sessionId' => $this->sessionId]);
$sessions->deleteMultiple(['timestamp' => $sessions->lesserThan($oldy)]);
if ($user) {
$sessions->delete(['user' => $user]);
}
$sessions->insert(
[
'sessionId' => $this->sessionId,
'timestamp' => $this->now,
'user' => $user,
'tikihost' => isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost',
]
);
if ($prefs['session_storage'] == 'db') {
// clean up adodb sessions as well in case adodb session garbage collection not working
$sessions = $this->table('sessions');
$sessions->deleteMultiple(['expiry' => $sessions->lesserThan($oldy)]);
}
$uptodate = true;
return true;
}
// Returns the number of registered users which logged in or were active in the last 5 minutes.
/**
* @return mixed
*/
public function count_sessions()
{
$this->update_session();
return $this->table('tiki_sessions')->fetchCount([]);
}
// Returns a string-indexed array with all the hosts/servers active in the last 5 minutes. Keys are hostnames. Values represent the number of registered users which logged in or were active in the last 5 minutes on the host.
/**
* @return array
*/
public function count_cluster_sessions()
{
$this->update_session();
$query = "select `tikihost`, count(`tikihost`) as cant from `tiki_sessions` group by `tikihost`";
return $this->fetchMap($query, []);
}
/**
* @param $links
* @return bool
*/
public function cache_links($links)
{
global $prefs;
if ($prefs['cachepages'] != 'y') {
return false;
}
foreach ($links as $link) {
if (! $this->is_cached($link)) {
$this->cache_url($link);
}
}
}
/**
* @param $data
* @return array
*/
public function get_links($data)
{
$links = [];
/// Prevent the substitution of link [] inside a <tag> ex: <input name="tracker[9]" ... >
$data = preg_replace("/<[^>]*>/", "", $data);
/// Match things like [...], but ignore things like [[foo].
// -Robin
if (preg_match_all("/(?<!\[)\[([^\[\|\]]+)(?:\|?[^\[\|\]]*){0,2}\]/", $data, $r1)) {
$res = $r1[1];
$links = array_unique($res);
}
return $links;
}
/**
* Convert internal links from absolute to relative
*
* @param string $data
* @return string
*/
public function convertAbsoluteLinksToRelative($data)
{
global $prefs, $tikilib;
preg_match_all('/\[(([^|\]]+)(\|([^|\]]+))?)\]/', $data, $matches);
$counter = count($matches[0]);
for ($i = 0; $i < $counter; $i++) {
$label = ! empty($matches[3][$i]) ? ltrim($matches[3][$i], '|') : '';
if (! empty($label) && $matches[2][$i] == $label) {
$data = str_replace($matches[0][$i], '[' . $matches[2][$i] . ']', $data);
}
// Check if link part is valid url
if (filter_var($matches[2][$i], FILTER_VALIDATE_URL) === false) {
continue;
}
// Check if url matches tiki instance links
if ($url = $this->getMatchBaseUrlSchema($matches[2][$i]) && $matches[2][$i] == $matches[4][$i]) {
$newLink = '[' . $matches[2][$i] . ']';
$data = str_replace($matches[0][$i], $newLink, $data);
}
}
preg_match_all('/\(\((([^|)]+)(\|([^|)]+))?)\)\)/', $data, $matches);
$counter = count($matches[0]);
for ($i = 0; $i < $counter; $i++) {
if ($matches[0][$i]) {
$linkArray = explode('|', trim($matches[0][$i], '(())'));
if (count($linkArray) == 2 && $linkArray[0] == $linkArray[1]) {
$newLink = '((' . $linkArray[0] . '))';
$data = str_replace($matches[0][$i], $newLink, $data);
}
}
}
if ($prefs['feature_absolute_to_relative_links'] != 'y') {
return $data;
}
$notification = false;
$from = 0;
$to = strlen($data);
$replace = [];
foreach ($this->getWikiMarkers() as $marker) {
while (false !== $open = $this->findText($data, $marker[0], $from, $to)) {
// Wiki marker -+ begin should be proceeded by space or a newline
if ($marker[0] == '-+' && $open != 0 && ! preg_match('/\s/', $data[$open - 1])) {
$from = $open + 1;
continue;
}
if (false !== $close = $this->findText($data, $marker[1], $open, $to)) {
$from = $close;
$size = ($close - $open) + strlen($marker[1]);
$markerBody = substr($data, $open, $size);
$key = "§" . md5($tikilib->genPass()) . "§" ;
$replace[$key] = $markerBody;
$data = str_replace($markerBody, $key, $data);
} else {
break;
}
}
}
// convert absolute to relative links
$pluginMatches = WikiParser_PluginMatcher::match($data);
foreach ($pluginMatches as $pluginMatch) {
$pluginBody = $pluginMatch->getBody();
if (empty($pluginBody)) {
$pluginBody = $pluginMatch->getArguments();
}
$key = "§" . md5($tikilib->genPass()) . "§" ;
$replace[$key] = $pluginBody;
$data = str_replace($pluginBody, $key, $data);
}
// Detect tiki internal links
preg_match_all('/\(\((([^|)]+)(\|([^|)]+))?)\)\)/', $data, $matches);
$counter = count($matches[0]);
for ($i = 0; $i < $counter; $i++) {
$linkArray = explode('|', trim($matches[0][$i], '(())'));
if (count($linkArray) == 2 && $linkArray[0] == $linkArray[1]) {
$newLink = '((' . $linkArray[0] . '))';
$data = str_replace($matches[0][$i], $newLink, $data);
$notification = true;
}
// Check if link part is valid url
if (filter_var($matches[2][$i], FILTER_VALIDATE_URL) === false) {
continue;
}
// Check if url matches tiki instance links
if ($url = $this->getMatchBaseUrlSchema($matches[2][$i])) {
$newPath = str_replace($url, '', $matches[2][$i]);
// In case of a tikibase instance point link to Homepage
if (empty($newPath) || $newPath == '/') {
$newPath = 'Homepage';
}
$newLink = '((' . $newPath . $matches[3][$i] . '))';
$data = str_replace($matches[0][$i], $newLink, $data);
$notification = true;
}
}
// Detect external links
preg_match_all('/\[(([^|\]]+)(\|([^|\]]+))?)\]/', $data, $matches);
$counter = count($matches[0]);
for ($i = 0; $i < $counter; $i++) {
// Check if link part is valid url
if (filter_var($matches[2][$i], FILTER_VALIDATE_URL) === false) {
continue;
}
// Check if url matches tiki instance links
if ($url = $this->getMatchBaseUrlSchema($matches[2][$i])) {
$newPath = str_replace($url, '', $matches[2][$i]);
if (! empty($newPath)) {
$newLink = '[' . $newPath . $matches[3][$i] . ']';
$newLinkArray = explode('|', trim($newLink, '[]'));
if (count($newLinkArray) === 2 && $newLinkArray[0] == str_replace($url, '', $newLinkArray[1])) {
$newLink = '[' . $newLinkArray[0] . ']';
}
$data = str_replace($matches[0][$i], $newLink, $data);
$notification = true;
}
}
}
// Detect links outside wikiplugin or wiki markers
preg_match_all('/(?<!==)(http|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?/', $data, $matches);
$counter = count($matches[0]);
for ($i = 0; $i < $counter; $i++) {
// Check if link part is valid url
if (filter_var($matches[0][$i], FILTER_VALIDATE_URL) === false) {
continue;
}
// Check if url matches tiki instance links
if ($url = $this->getMatchBaseUrlSchema($matches[0][$i])) {
$newPath = str_replace($url, '', $matches[0][$i]);
$objectLink = $this->getObjectRelativeLink($newPath);
if (! empty($newPath) && ! empty($objectLink)) {
$objStartPos = strpos($data, $matches[0][$i]);
$objLength = strlen($matches[0][$i]);
$data = substr_replace($data, $objectLink, $objStartPos, $objLength);
$notification = true;
}
}
}
foreach ($replace as $key => $body) {
$data = str_replace($key, $body, $data);
}
if ($notification) {
Feedback::note(tr('Tiki links converted to relative links'));
}
return $data;
}
/**
* Return the base url in the matched link protocol (http or https)
*
* @param string $link The link to check
*
* @return string The tiki base url with the matched schema (http or https)
*/
public function getMatchBaseUrlSchema($link)
{
global $base_url_http, $base_url_https;
if (strpos($link, $base_url_http) !== false) {
return $base_url_http;
} elseif (strpos($link, rtrim($base_url_http, '/')) !== false) {
return rtrim($base_url_http, '/');
} elseif (strpos($link, $base_url_https) !== false) {
return $base_url_https;
} elseif (strpos($link, rtrim($base_url_https, '/')) !== false) {
return rtrim($base_url_https, '/');
} else {
return null;
}
}
/**
* Returns the object internal link
*
* @param string $uri
* @return string
*/
public function getObjectRelativeLink($uri)
{
global $prefs;
$objectLink = '';
if (! empty($prefs['feature_sefurl']) && $prefs['feature_sefurl'] === 'y') {
$slug = explode('-', $uri);
$slug = $slug[0];
switch ($slug) {
case (substr($slug, 0, 7) === 'article' || substr($slug, 0, 3) === 'art'):
$articleId = substr($slug, 0, 7) === 'article' ? substr($slug, 7) : substr($slug, 3);
$artlib = TikiLib::lib('art');
$article = $artlib->get_article($articleId);
$objectLink = ! empty($article['title']) ? '[' . $uri . '|' . $article['title'] . ']' : '';
break;
case substr($slug, 0, 8) === 'blogpost':
$blogPostId = substr($slug, 8);
$bloglib = TikiLib::lib('blog');
$blogPost = $bloglib->get_post($blogPostId);
$objectLink = ! empty($blogPost['title']) ? '[' . $uri . '|' . $blogPost['title'] . ']' : '';
break;
case substr($slug, 0, 4) === 'blog':
$blogId = substr($slug, 4);
$bloglib = TikiLib::lib('blog');
$blog = $bloglib->get_blog($blogId);
$objectLink = ! empty($blog['title']) ? '[' . $uri . '|' . $blog['title'] . ']' : '';
break;
case substr($slug, 0, 8) === 'calevent':
$eventId = substr($slug, 8);
$calendarlib = TikiLib::lib('calendar');
$event = $calendarlib->get_item($eventId);
$objectLink = ! empty($event['name']) ? '[' . $uri . '|' . $event['name'] . ']' : '';
break;
case substr($slug, 0, 3) === 'cal':
$calendarId = substr($slug, 3);
$calendarlib = TikiLib::lib('calendar');
$calendar = $calendarlib->get_calendar($calendarId);
$objectLink = ! empty($calendar['name']) ? '[' . $uri . '|' . $calendar['name'] . ']' : '';
break;
case substr($slug, 0, 3) === 'cat':
$catId = substr($slug, 3);
$categlib = TikiLib::lib('categ');
$cat = $categlib->get_category($catId);
$objectLink = ! empty($cat['name']) ? '[' . $uri . '|' . $cat['name'] . ']' : '';
break;
case substr($slug, 0, 9) === 'directory':
$directoryCatId = substr($slug, 9);
if ($directoryCatId == 0) {
$objectLink = '[' . $uri . '|Top]';
} else {
global $dirlib;
include_once('lib/directory/dirlib.php');
$directoryCat = $dirlib->dir_get_category($directoryCatId);
$objectLink = ! empty($directoryCat['name']) ? '[' . $uri . '|' . $directoryCat['name'] . ']' : '';
}
break;
case substr($slug, 0, 7) === 'dirlink':
$siteId = substr($slug, 7);
global $dirlib;
include_once('lib/directory/dirlib.php');
$site = $dirlib->dir_get_site($siteId);
$objectLink = ! empty($site['name']) ? '[' . $uri . '|' . $site['name'] . ']' : '';
break;
case substr($slug, 0, 5) === 'event':
$eventId = substr($slug, 5);
$calendarlib = TikiLib::lib('calendar');
$event = $calendarlib->get_item($eventId);
$objectLink = ! empty($event['name']) ? '[' . $uri . '|' . $event['name'] . ']' : '';
break;
case substr($slug, 0, 3) === 'faq':
$faqId = substr($slug, 3);
$faqlib = TikiLib::lib('faq');
$faq = $faqlib->get_faq($faqId);
$objectLink = ! empty($faq['title']) ? '[' . $uri . '|' . $faq['title'] . ']' : '';
break;
case substr($slug, 0, 4) === 'file':
$fileGalleryId = substr($slug, 4);
$filegallib = TikiLib::lib('filegal');
$gallery = $filegallib->get_file_gallery($fileGalleryId);
$objectLink = ! empty($gallery['name']) ? '[' . $uri . '|' . $gallery['name'] . ']' : '';
break;
case substr($slug, 0, 7) === 'gallery':
$galleryId = substr($slug, 7);
$filegallib = TikiLib::lib('filegal');
$gallery = $filegallib->get_file_gallery($galleryId);
$objectLink = ! empty($gallery['name']) ? '[' . $uri . '|' . $gallery['name'] . ']' : '';
break;
case (substr($slug, 0, 2) === 'dl' || substr($slug, 0, 9) === 'thumbnail' || substr($slug, 0, 7) === 'display' || substr($slug, 0, 7) === 'preview'):
if (substr($slug, 0, 2) === 'dl') {
$fileId = substr($slug, 2);
} elseif (substr($slug, 0, 9) === 'thumbnail') {
$fileId = substr($slug, 9);
} else {
$fileId = substr($slug, 7);
}
$filegallib = TikiLib::lib('filegal');
$file = $filegallib->get_file($fileId);
$objectLink = ! empty($file['name']) ? '[' . $uri . '|' . $file['name'] . ']' : '';
break;
case substr($slug, 0, 11) === 'forumthread':
$forumCommentId = substr($slug, 11);
$commentslib = TikiLib::lib('comments');
$forumComment = $commentslib->get_comment($forumCommentId);
$objectLink = ! empty($forumComment['title']) ? '[' . $uri . '|' . $forumComment['title'] . ']' : '';
break;
case substr($slug, 0, 5) === 'forum':
$forumId = substr($slug, 5);
$commentslib = TikiLib::lib('comments');
$forum = $commentslib->get_forum($forumId);
$objectLink = ! empty($forum['name']) ? '[' . $uri . '|' . $forum['name'] . ']' : '';
break;
case substr($slug, 0, 4) === 'item':
$itemId = substr($slug, 4);
$trklib = TikiLib::lib('trk');
$trackerItem = $trklib->get_tracker_item($itemId);
$objectLink = ! empty($trackerItem) ? '[' . $uri . '|' . $slug . ']' : '';
break;
case substr($slug, 0, 3) === 'int':
$repID = substr($slug, 3);
$integrator = new TikiIntegrator($dbTiki);
$rep = $integrator->get_repository($repID);
$objectLink = ! empty($rep['name']) ? '[' . $uri . '|' . $rep['name'] . ']' : '';
break;
case (substr($slug, 0, 10) === 'newsletter' || substr($slug, 0, 2) === 'nl'):
$newsletterId = substr($slug, 0, 10) === 'newsletter' ? substr($slug, 10) : substr($slug, 2);
global $nllib;
include_once('lib/newsletters/nllib.php');
$newsletter = $nllib->get_newsletter($newsletterId);
$objectLink = ! empty($newsletter['name']) ? '[' . $uri . '|' . $newsletter['name'] . ']' : '';
break;
case substr($slug, 0, 4) === 'poll':
$pollId = substr($slug, 4);
$polllib = TikiLib::lib('poll');
$poll = $polllib->get_poll($pollId);
$objectLink = ! empty($poll['title']) ? '[' . $uri . '|' . $poll['title'] . ']' : '';
break;
case substr($slug, 0, 4) === 'quiz':
$quizId = substr($slug, 4);
$quizlib = TikiLib::lib('quiz');
$quiz = $quizlib->get_quiz($quizId);
$objectLink = ! empty($quiz['name']) ? '[' . $uri . '|' . $quiz['name'] . ']' : '';
break;
case substr($slug, 0, 7) === 'tracker':
$trackerId = substr($slug, 7);
$trklib = TikiLib::lib('trk');
$tracker = $trklib->get_tracker($trackerId);
$objectLink = ! empty($tracker['name']) ? '[' . $uri . '|' . $tracker['name'] . ']' : '';
break;
case substr($slug, 0, 5) === 'sheet':
$sheetId = substr($slug, 5);
$sheetlib = TikiLib::lib("sheet");
$sheet = $sheetlib->get_sheet_info($sheetId);
$objectLink = ! empty($sheet['title']) ? '[' . $uri . '|' . $sheet['title'] . ']' : '';
break;
case substr($slug, 0, 6) === 'survey':
include_once('lib/surveys/surveylib.php');
$surveyId = substr($slug, 6);
$survey = $srvlib->get_survey($surveyId);
$objectLink = ! empty($survey['name']) ? '[' . $uri . '|' . $survey['name'] . ']' : '';
break;
case substr($slug, 0, 4) === 'user':
$userId = substr($slug, 4);
$user = $this->get_user_login($userId);
$objectLink = ! empty($user) ? '[' . $uri . '|' . $user . ']' : '';
break;
default:
$pageName = $this->getPageBySlug($uri);
$objectLink = ! empty($pageName) ? '((' . $pageName . '))' : '';
}
}
$uriParams = explode('?', $uri);
$param = ! empty($uriParams[1]) ? $uriParams[1] : '';
$clearParam = ! empty($param) ? explode('&', $param) : '';
$param = ! empty($clearParam[0]) ? $clearParam[0] : '';
if (! empty($param)) {
switch ($param) {
case substr($param, 0, 9) === 'articleId':
$articleId = substr($param, 10);
$artlib = TikiLib::lib('art');
$article = $artlib->get_article($articleId);
$objectLink = ! empty($article['title']) ? '[' . $uri . '|' . $article['title'] . ']' : '';
break;
case substr($param, 0, 6) === 'blogId':
$blogId = substr($param, 7);
$bloglib = TikiLib::lib('blog');
$blog = $bloglib->get_blog($blogId);
$objectLink = ! empty($blog['title']) ? '[' . $uri . '|' . $blog['title'] . ']' : '';
break;
case substr($param, 0, 6) === 'postId':
$blogPostId = substr($param, 7);
$bloglib = TikiLib::lib('blog');
$blogPost = $bloglib->get_post($blogPostId);
$objectLink = ! empty($blogPost['title']) ? '[' . $uri . '|' . $blogPost['title'] . ']' : '';
break;
case substr($param, 0, 10) === 'calendarId':
$calendarId = substr($param, 11);
$calendarlib = TikiLib::lib('calendar');
$calendar = $calendarlib->get_calendar($calendarId);
$objectLink = ! empty($calendar['name']) ? '[' . $uri . '|' . $calendar['name'] . ']' : '';
break;
case substr($param, 0, 17) === 'comments_parentId':
$forumCommentId = substr($param, 18);
$commentslib = TikiLib::lib('comments');
$forumComment = $commentslib->get_comment($forumCommentId);
$objectLink = ! empty($forumComment['title']) ? '[' . $uri . '|' . $forumComment['title'] . ']' : '';
break;
case substr($param, 0, 6) === 'parent':
$directoryCatId = substr($param, 7);
if ($directoryCatId == 0) {
$objectLink = '[' . $uri . '|Top]';
} else {
global $dirlib;
include_once('lib/directory/dirlib.php');
$directoryCat = $dirlib->dir_get_category($directoryCatId);
$objectLink = ! empty($directoryCat['name']) ? '[' . $uri . '|' . $directoryCat['name'] . ']' : '';
}
break;
case substr($param, 0, 9) === 'galleryId':
$fileGalleryId = substr($param, 10);
$filegallib = TikiLib::lib('filegal');
$gallery = $filegallib->get_file_gallery($fileGalleryId);
$objectLink = ! empty($gallery['name']) ? '[' . $uri . '|' . $gallery['name'] . ']' : '';
break;
case substr($param, 0, 5) === 'faqId':
$faqId = substr($param, 6);
$faqlib = TikiLib::lib('faq');
$faq = $faqlib->get_faq($faqId);
$objectLink = ! empty($faq['title']) ? '[' . $uri . '|' . $faq['title'] . ']' : '';
break;
case substr($param, 0, 6) === 'fileId':
$fileId = substr($param, 7);
$filegallib = TikiLib::lib('filegal');
$file = $filegallib->get_file($fileId);
$objectLink = ! empty($file['name']) ? '[' . $uri . '|' . $file['name'] . ']' : '';
break;
case substr($param, 0, 7) === 'forumId':
$forumId = substr($param, 8);
$commentslib = TikiLib::lib('comments');
$forum = $commentslib->get_forum($forumId);
$objectLink = ! empty($forum['name']) ? '[' . $uri . '|' . $forum['name'] . ']' : '';
break;
case substr($param, 0, 4) === 'nlId':
$newsletterId = substr($param, 5);
global $nllib;
include_once('lib/newsletters/nllib.php');
$newsletter = $nllib->get_newsletter($newsletterId);
$objectLink = ! empty($newsletter['name']) ? '[' . $uri . '|' . $newsletter['name'] . ']' : '';
break;
case substr($param, 0, 4) === 'page':
$pageSlug = substr($param, 5);
if ($uriParams[0] == 'tiki-index.php') {
$pageName = $this->getPageBySlug($pageSlug);
$objectLink = ! empty($pageName) ? '((' . $pageName . '))' : '';
} else {
$objectLink = '[' . $uri . '|' . $uri . ']';
}
break;
case substr($param, 0, 8) === 'parentId':
$catId = substr($param, 9);
$categlib = TikiLib::lib('categ');
$cat = $categlib->get_category($catId);
$objectLink = ! empty($cat['name']) ? '[' . $uri . '|' . $cat['name'] . ']' : '';
break;
case substr($param, 0, 6) === 'pollId':
$pollId = substr($param, 7);
$polllib = TikiLib::lib('poll');
$poll = $polllib->get_poll($pollId);
$objectLink = ! empty($poll['title']) ? '[' . $uri . '|' . $poll['title'] . ']' : '';
break;
case substr($param, 0, 6) === 'quizId':
$quizId = substr($param, 7);
$quizlib = TikiLib::lib('quiz');
$quiz = $quizlib->get_quiz($quizId);
$objectLink = ! empty($quiz['name']) ? '[' . $uri . '|' . $quiz['name'] . ']' : '';
break;
case substr($param, 0, 5) === 'repID':
$repID = substr($param, 6);
$integrator = new TikiIntegrator($dbTiki);
$rep = $integrator->get_repository($repID);
$objectLink = ! empty($rep['name']) ? '[' . $uri . '|' . $rep['name'] . ']' : '';
break;
case substr($param, 0, 6) === 'siteId':
$siteId = substr($param, 7);
global $dirlib;
include_once('lib/directory/dirlib.php');
$site = $dirlib->dir_get_site($siteId);
$objectLink = ! empty($site['name']) ? '[' . $uri . '|' . $site['name'] . ']' : '';
break;
case substr($param, 0, 7) === 'sheetId':
$sheetId = substr($param, 8);
$sheetlib = TikiLib::lib("sheet");
$sheet = $sheetlib->get_sheet_info($sheetId);
$objectLink = ! empty($sheet['title']) ? '[' . $uri . '|' . $sheet['title'] . ']' : '';
break;
case substr($param, 0, 8) === 'surveyId':
include_once('lib/surveys/surveylib.php');
$surveyId = substr($param, 9);
$survey = $srvlib->get_survey($surveyId);
$objectLink = ! empty($survey['name']) ? '[' . $uri . '|' . $survey['name'] . ']' : '';
break;
case substr($param, 0, 9) === 'trackerId':
$trackerId = substr($param, 10);
$trklib = TikiLib::lib('trk');
$tracker = $trklib->get_tracker($trackerId);
$objectLink = ! empty($tracker['name']) ? '[' . $uri . '|' . $tracker['name'] . ']' : '';
break;
case substr($param, 0, 6) === 'userId':
$userId = substr($param, 7);
$user = $this->get_user_login($userId);
$objectLink = ! empty($user) ? '[' . $uri . '|' . $user . ']' : '';
break;
case substr($param, 0, 13) === 'viewcalitemId':
$eventId = substr($param, 14);
$calendarlib = TikiLib::lib('calendar');
$event = $calendarlib->get_item($eventId);
$objectLink = ! empty($event['name']) ? '[' . $uri . '|' . $event['name'] . ']' : '';
break;
}
}
if (empty($objectLink)) {
$pageName = $this->getPageBySlug($uri);
if (in_array($uri, ['index.php', 'tiki-index.php'])) {
$objectLink = '[' . $uri . '|' . $uri . ']';
} else {
$objectLink = ! empty($pageName) ? '((' . $pageName . '))' : '[' . $uri . '|' . $uri . ']';
}
}
return $objectLink;
}
/**
* Return wiki pages
*
* @param $slug
* @return string
*/
public function getPageBySlug($slug)
{
global $prefs;
$pages = TikiDb::get()->table('tiki_pages');
$found = $pages->fetchOne('pageName', ['pageSlug' => $slug]);
return ! empty($found) ? $found : '';
}
/**
* @param $data
* @return array
*/
public function get_links_nocache($data)
{
$links = [];
if (preg_match_all("/\[([^\]]+)/", $data, $r1)) {
$res = [];
foreach ($r1[1] as $alink) {
$parts = explode('|', $alink);
if (isset($parts[1]) && $parts[1] == 'nocache') {
$res[] = $parts[0];
} elseif (isset($parts[2]) && $parts[2] == 'nocache') {
$res[] = $parts[0];
} else {
if (isset($parts[3]) && $parts[3] == 'nocache') {
$res[] = $parts[0];
}
}
/// avoid caching URLs with common binary file extensions
$extension = substr($parts[0], -4);
$binary = [
'.arj',
'.asf',
'.avi',
'.bz2',
'.com',
'.dat',
'.doc',
'.exe',
'.hqx',
'.mid',
'.mov',
'.mp3',
'.mpg',
'.ogg',
'.pdf',
'.ram',
'.rar',
'.rpm',
'.rtf',
'.sea',
'.sit',
'.tar',
'.tgz',
'.wav',
'.wmv',
'.xls',
'.zip',
'ar.Z', // .tar.Z
'r.gz' // .tar.gz
];
if (in_array($extension, $binary)) {
$res[] = $parts[0];
}
}
$links = array_unique($res);
}
return $links;
}
/**
* @param $url
* @return bool
*/
public function is_cacheable($url)
{
// simple implementation: future versions should analyse
// if this is a link to the local machine
if (strstr($url, 'tiki-')) {
return false;
}
if (strstr($url, 'messu-')) {
return false;
}
return true;
}
/**
* @param $url
* @return mixed
*/
public function is_cached($url)
{
return $this->table('tiki_link_cache')->fetchCount(['url' => $url]);
}
/**
* @param $offset
* @param $maxRecords
* @param $sort_mode
* @param $find
* @return array
*/
public function list_cache($offset, $maxRecords, $sort_mode, $find)
{
if ($find) {
$findesc = '%' . $find . '%';
$mid = " where (`url` like ?) ";
$bindvars = [$findesc];
} else {
$mid = "";
$bindvars = [];
}
$query = "select `cacheId` ,`url`,`refresh` from `tiki_link_cache` $mid order by " . $this->convertSortMode($sort_mode);
$query_cant = "select count(*) from `tiki_link_cache` $mid";
$ret = $this->fetchAll($query, $bindvars, $maxRecords, $offset);
$cant = $this->getOne($query_cant, $bindvars);
$retval = [];
$retval["data"] = $ret;
$retval["cant"] = $cant;
return $retval;
}
/**
* @param $cacheId
* @return bool
*/
public function refresh_cache($cacheId)
{
$linkCache = $this->table('tiki_link_cache');
$url = $linkCache->fetchOne('url', ['cacheId' => $cacheId]);
$data = $this->httprequest($url);
$linkCache->update(['data' => $data, 'refresh' => $this->now], ['cacheId' => $cacheId]);
return true;
}
/**
* @param $cacheId
* @return bool
*/
public function remove_cache($cacheId)
{
$linkCache = $this->table('tiki_link_cache');
$linkCache->delete(['cacheId' => $cacheId]);
return true;
}
/**
* @param $cacheId
* @return mixed
*/
public function get_cache($cacheId)
{
return $this->table('tiki_link_cache')->fetchFullRow(['cacheId' => $cacheId]);
}
/**
* @param $url
* @return bool
*/
public function get_cache_id($url)
{
$id = $this->table('tiki_link_cache')->fetchOne('cacheId', ['url' => $url]);
return $id ? $id : false;
}
/* cachetime = 0 => no cache, otherwise duration cache is valid */
/**
* @param $url
* @param $isFresh
* @param int $cachetime
* @return mixed
*/
public function get_cached_url($url, &$isFresh, $cachetime = 0)
{
$linkCache = $this->table('tiki_link_cache');
$res = $linkCache->fetchFullRow(['url' => $url]);
$now = $this->now;
if (empty($res) || ($now - $res['refresh']) > $cachetime) { // no cache or need to refresh
$res['data'] = $this->httprequest($url);
$isFresh = true;
//echo '<br />Not cached:'.$url.'/'.strlen($res['data']);
$res['refresh'] = $now;
if ($cachetime > 0) {
if (empty($res['cacheId'])) {
$linkCache->insert(['url' => $url, 'data' => $res['data'], 'refresh' => $res['refresh']]);
$res = $linkCache->fetchFullRow(['url' => $url]);
} else {
$linkCache->update(['data' => $res['data'], 'refresh' => $res['refresh']], ['cacheId' => $res['cacheId']]);
}
}
} else {
//echo '<br />Cached:'.$url;
$isFresh = false;
}
return $res;
}
// This funcion return the $limit most accessed pages
// it returns pageName and hits for each page
/**
* @param $limit
* @return array
*/
public function get_top_pages($limit)
{
$query = "select `pageName` , `hits`
from `tiki_pages`
order by `hits` desc";
$result = $this->fetchAll($query, [], $limit);
$ret = [];
foreach ($result as $res) {
$aux["pageName"] = $res["pageName"];
$aux["hits"] = $res["hits"];
$ret[] = $aux;
}
return $ret;
}
// Returns the name of all pages
/**
* Get all tiki pages
* @param array $columns
* @return mixed
*/
public function get_all_pages($columns = [])
{
return $this->table('tiki_pages')->fetchAll($columns, []);
}
/**
* \brief Cache given url
* If \c $data present (passed) it is just associated \c $url and \c $data.
* Else it will request data for given URL and store it in DB.
* Actualy (currently) data may be proviced by TIkiIntegrator only.
*/
public function cache_url($url, $data = '')
{
// Avoid caching internal references... (only if $data not present)
// (cdx) And avoid other protocols than http...
// 03-Nov-2003, by zaufi
// preg_match("_^(mailto:|ftp:|gopher:|file:|smb:|news:|telnet:|javascript:|nntp:|nfs:)_",$url)
// was removed (replaced to explicit http[s]:// detection) bcouse
// I now (and actualy use in my production Tiki) another bunch of protocols
// available in my konqueror... (like ldap://, ldaps://, nfs://, fish://...)
// ... seems like it is better to enum that allowed explicitly than all
// noncacheable protocols.
if (
((strstr($url, 'tiki-') || strstr($url, 'messu-')) && $data == '')
|| (substr($url, 0, 7) != 'http://' && substr($url, 0, 8) != 'https://')
) {
return false;
}
// Request data for URL if nothing given in parameters
// (reuse $data var)
if ($data == '') {
$data = $this->httprequest($url);
}
// If stuff inside [] is *really* malformatted, $data
// will be empty. -rlpowell
if ($data) {
$linkCache = $this->table('tiki_link_cache');
$linkCache->insert(['url' => $url, 'data' => $data, 'refresh' => $this->now]);
return true;
} else {
return false;
}
}
// Removes all the versions of a page and the page itself
/*shared*/
/**
* @param $page
* @param string $comment
* @return bool
*/
public function remove_all_versions($page, $comment = '')
{
$page_info = $this->get_page_info($page);
if (! $page_info) {
return false;
}
global $user, $prefs;
if ($prefs['feature_actionlog'] == 'y' && isset($page_info['data'])) {
$params = 'del=' . strlen($page_info['data']);
} else {
$params = '';
}
// Deal with mail notifications.
include_once(__DIR__ . '/notifications/notificationemaillib.php');
$foo = parse_url($_SERVER["REQUEST_URI"]);
$machine = self::httpPrefix(true) . dirname($foo["path"]);
sendWikiEmailNotification('wiki_page_deleted', $page, $user, $comment, 1, $page_info['data'], $machine);
//Remove the bibliography references for this page
$this->removePageReference($page);
$wikilib = TikiLib::lib('wiki');
$multilinguallib = TikiLib::lib('multilingual');
$multilinguallib->detachTranslation('wiki page', $multilinguallib->get_page_id_from_name($page));
$this->invalidate_cache($page);
//Delete structure references before we delete the page
$query = "select `page_ref_id` ";
$query .= "from `tiki_structures` ts, `tiki_pages` tp ";
$query .= "where ts.`page_id`=tp.`page_id` and `pageName`=?";
$result = $this->fetchAll($query, [$page]);
foreach ($result as $res) {
$this->remove_from_structure($res["page_ref_id"]);
}
$this->table('tiki_pages')->delete(['pageName' => $page]);
if ($prefs['feature_contribution'] == 'y') {
$contributionlib = TikiLib::lib('contribution');
$contributionlib->remove_page($page);
}
$this->table('tiki_history')->deleteMultiple(['pageName' => $page]);
$this->table('tiki_links')->deleteMultiple(['fromPage' => $page]);
$logslib = TikiLib::lib('logs');
$logslib->add_action('Removed', $page, 'wiki page', $params);
//get_strings tra("Removed");
$this->table('users_groups')->updateMultiple(['groupHome' => null], ['groupHome' => $page]);
$this->table('tiki_theme_control_objects')->deleteMultiple(['name' => $page,'type' => 'wiki page']);
$this->table('tiki_copyrights')->deleteMultiple(['page' => $page]);
$this->remove_object('wiki page', $page);
$this->table('tiki_user_watches')->deleteMultiple(['event' => 'wiki_page_changed', 'object' => $page]);
$this->table('tiki_group_watches')->deleteMultiple(['event' => 'wiki_page_changed', 'object' => $page]);
$atts = $wikilib->list_wiki_attachments($page, 0, -1, 'created_desc', '');
foreach ($atts["data"] as $at) {
$wikilib->remove_wiki_attachment($at["attId"]);
}
$wikilib->remove_footnote('', $page);
$this->refresh_index('wiki page', $page);
return true;
}
/*shared*/
/**
* @param $page_ref_id
* @return bool
*/
public function remove_from_structure($page_ref_id)
{
// Now recursively remove
$query = "select `page_ref_id` ";
$query .= "from `tiki_structures` as ts, `tiki_pages` as tp ";
$query .= "where ts.`page_id`=tp.`page_id` and `parent_id`=?";
$result = $this->fetchAll($query, [$page_ref_id]);
foreach ($result as $res) {
$this->remove_from_structure($res["page_ref_id"]);
}
$structlib = TikiLib::lib('struct');
$page_info = $structlib->s_get_page_info($page_ref_id);
$structures = $this->table('tiki_structures');
$structures->updateMultiple(
['pos' => $structures->decrement(1)],
['pos' => $structures->greaterThan((int) $page_info['pos']), 'parent_id' => (int) $page_info['parent_id'],]
);
$structures->delete(['page_ref_id' => $page_ref_id]);
return true;
}
// Deprecated in favor of list_pages
/**
* @param $maxRecords
* @param string $categories
* @return array
*/
public function last_pages($maxRecords = -1, $categories = '')
{
if (is_array($categories)) {
$filter = ["categId" => $categories];
} else {
$filter = [];
}
return $this->list_pages(0, $maxRecords, "lastModif_desc", '', '', true, true, false, false, $filter);
}
// Broken. Equivalent to last_pages($maxRecords)
/**
* @param $maxRecords
* @return array
*/
public function last_major_pages($maxRecords = -1)
{
return $this->list_pages(0, $maxRecords, "lastModif_desc");
}
// use this function to speed up when pagename is only needed (the 3 getOne can killed tikiwith more that 3000 pages)
/**
* @param int $offset
* @param $maxRecords
* @param string $sort_mode
* @param string $find
* @return array
*/
public function list_pageNames($offset = 0, $maxRecords = -1, $sort_mode = 'pageName_asc', $find = '')
{
return $this->list_pages($offset, $maxRecords, $sort_mode, $find, '', true, true);
}
/**
* @param int $offset
* @param $maxRecords
* @param string $sort_mode
* @param string $find
* @param string $initial
* @param bool $exact_match
* @param bool $onlyName
* @param bool $forListPages
* @param bool $only_orphan_pages
* @param string $filter
* @param bool $onlyCant
* @param string $ref
* @return array
*/
public function list_pages($offset = 0, $maxRecords = -1, $sort_mode = 'pageName_desc', $find = '', $initial = '', $exact_match = true, $onlyName = false, $forListPages = false, $only_orphan_pages = false, $filter = '', $onlyCant = false, $ref = '', $exclude_pages = '')
{
global $prefs, $tiki_p_wiki_view_ratings;
$loadCategories = (isset($prefs['wiki_list_categories']) && $prefs['wiki_list_categories'] == 'y') || (isset($prefs['wiki_list_categories_path']) && $prefs['wiki_list_categories_path'] == 'y');
$loadCategories = $loadCategories && $forListPages;
$join_tables = '';
$join_bindvars = [];
$old_sort_mode = '';
if ($sort_mode == 'size_desc') {
$sort_mode = 'page_size_desc';
}
if ($sort_mode == 'size_asc') {
$sort_mode = 'page_size_asc';
}
$select = '';
// If sort mode is versions, links or backlinks then offset is 0, maxRecords is -1 (again) and sort_mode is nil
$need_everything = false;
if (in_array($sort_mode, ['versions_desc', 'versions_asc', 'links_asc', 'links_desc', 'backlinks_asc', 'backlinks_desc'])) {
$old_sort_mode = $sort_mode;
$sort_mode = 'user_desc';
$need_everything = true;
}
if (is_array($find)) { // you can use an array of pages
$mid = " where LOWER(`pageName`) IN (" . implode(',', array_fill(0, count($find), 'LOWER(?)')) . ")";
$bindvars = $find;
} elseif (is_string($find) && ! empty($find)) { // or a string
if (! $exact_match && $find) {
$find = preg_replace("/([^\s]+)/", "%\\1%", $find);
$f = preg_split("/[\s]+/", $find, -1, PREG_SPLIT_NO_EMPTY);
if (empty($f)) {//look for space...
$mid = " where LOWER(`pageName`) like LOWER('%$find%')";
} else {
$findop = $forListPages ? ' AND' : ' OR';
$mid = " where LOWER(`pageName`) like " . implode($findop . ' LOWER(`pageName`) like ', array_fill(0, count($f), 'LOWER(?)'));
$bindvars = $f;
}
} else {
$mid = " where LOWER(`pageName`) like LOWER(?) ";
$bindvars = [$find];
}
} else {
$bindvars = [];
$mid = '';
}
//check if exclude page is array and then add its values in bindvars and
if ($exclude_pages && is_array($exclude_pages)) { // you can use an array of pages
if (! empty($mid)) {
$mid .= " AND (LOWER(`pageName`) NOT IN (" . implode(',', array_fill(0, count($exclude_pages), 'LOWER(?)')) . "))";
} else {
$mid = " where LOWER(`pageName`) NOT IN (" . implode(',', array_fill(0, count($exclude_pages), 'LOWER(?)')) . ")";
}
foreach ($exclude_pages as $epKey => $epVal) {
$bindvars[] = $epVal;
}
}
$categlib = TikiLib::lib('categ');
$category_jails = $categlib->get_jail();
if (! isset($filter['andCategId']) && ! isset($filter['categId']) && empty($filter['noCateg']) && ! empty($category_jails)) {
$filter['categId'] = $category_jails;
}
// If language is set to '', assume that no language filtering should be done.
if (isset($filter['lang']) && $filter['lang'] == '') {
unset($filter['lang']);
}
$distinct = '';
if (! empty($filter)) {
$tmp_mid = [];
foreach ($filter as $type => $val) {
if ($type == 'andCategId') {
$categories = $categlib->get_jailed((array) $val);
$join_tables .= " inner join `tiki_objects` as tob on (tob.`itemId`= tp.`pageName` and tob.`type`= ?) ";
$join_bindvars[] = 'wiki page';
foreach ($categories as $i => $categId) {
$join_tables .= " inner join `tiki_category_objects` as tc$i on (tc$i.`catObjectId`=tob.`objectId` and tc$i.`categId` =?) ";
$join_bindvars[] = $categId;
}
} elseif ($type == 'categId') {
$categories = $categlib->get_jailed((array) $val);
$categories[] = -1;
$cat_count = count($categories);
$join_tables .= " inner join `tiki_objects` as tob on (tob.`itemId`= tp.`pageName` and tob.`type`= ?) inner join `tiki_category_objects` as tc on (tc.`catObjectId`=tob.`objectId` and tc.`categId` IN(" . implode(', ', array_fill(0, $cat_count, '?')) . ")) ";
if ($cat_count > 1) {
$distinct = ' DISTINCT ';
}
$join_bindvars = array_merge(['wiki page'], $categories);
} elseif ($type == 'noCateg') {
$join_tables .= ' left join `tiki_objects` as tob on (tob.`itemId`= tp.`pageName` and tob.`type`= ?) left join `tiki_categorized_objects` as tcdo on (tcdo.`catObjectId`=tob.`objectId`) left join `tiki_category_objects` as tco on (tcdo.`catObjectId`=tco.`catObjectId`)';
$join_bindvars[] = 'wiki page';
$tmp_mid[] = '(tco.`categId` is null)';
} elseif ($type == 'notCategId') {
foreach ($val as $v) {
$tmp_mid[] = '(tp.`pageName` NOT IN(SELECT itemId FROM tiki_objects INNER JOIN tiki_category_objects ON catObjectId = objectId WHERE type = "wiki page" AND categId = ?))';
$bindvars[] = $v;
}
} elseif ($type == 'lang') {
$tmp_mid[] = 'tp.`lang`=?';
$bindvars[] = $val;
} elseif ($type == 'structHead') {
$join_tables .= " inner join `tiki_structures` as ts on (ts.`page_id` = tp.`page_id` and ts.`parent_id` = 0) ";
$select .= ',ts.`page_alias`';
} elseif ($type == 'langOrphan') {
$join_tables .= " left join `tiki_translated_objects` tro on (tro.`type` = 'wiki page' AND tro.`objId` = tp.`page_id`) ";
$tmp_mid[] = "( (tro.`traId` IS NULL AND tp.`lang` != ?) OR tro.`traId` NOT IN(SELECT `traId` FROM `tiki_translated_objects` WHERE `lang` = ?))";
$bindvars[] = $val;
$bindvars[] = $val;
} elseif ($type == 'structure_orphans') {
$join_tables .= " left join `tiki_structures` as tss on (tss.`page_id` = tp.`page_id`) ";
$tmp_mid[] = "(tss.`page_ref_id` is null)";
} elseif ($type == 'translationOrphan') {
$multilinguallib = TikiLib::lib('multilingual');
$multilinguallib->sqlTranslationOrphan('wiki page', 'tp', 'page_id', $val, $join_tables, $midto, $bindvars);
$tmp_mid[] = $midto;
}
}
if (! empty($tmp_mid)) {
$mid .= empty($mid) ? ' where (' : ' and (';
$mid .= implode(' and ', $tmp_mid) . ')';
}
}
if (! empty($initial)) {
$mid .= empty($mid) ? ' where (' : ' and (';
$tmp_mid = '';
if (is_array($initial)) {
foreach ($initial as $i) {
if (! empty($tmp_mid)) {
$tmp_mid .= ' or ';
}
$tmp_mid .= ' `pageName` like ? ';
$bindvars[] = $i . '%';
}
} else {
$tmp_mid = " `pageName` like ? ";
$bindvars[] = $initial . '%';
}
$mid .= $tmp_mid . ')';
}
if ($only_orphan_pages) {
$join_tables .= ' left join `tiki_links` as tl on tp.`pageName` = tl.`toPage` left join `tiki_structures` as tsoo on tp.`page_id` = tsoo.`page_id`';
$mid .= ( $mid == '' ) ? ' where ' : ' and ';
$mid .= 'tl.`toPage` IS NULL and tsoo.`page_id` IS NULL';
}
if ($prefs['rating_advanced'] == 'y') {
$ratinglib = TikiLib::lib('rating');
$join_tables .= $ratinglib->convert_rating_sort($sort_mode, 'wiki page', '`page_id`');
}
if ($tiki_p_wiki_view_ratings === 'y' && $prefs['feature_polls'] == 'y' && $prefs['feature_wiki_ratings'] == 'y') {
$select .= ', (select sum(`tiki_poll_options`.`title`*`tiki_poll_options`.`votes`) as rating ' . // Multiply the option's label (title) by the number of votes for that option. Titles can be numbers but even then, this computation has doubtful relevance. A page with 5 ratings of "1" (on a scale of 5) would obtain a higher rating than a page with 1 rating of "4". This is most particular and apparently undocumented. Chealer 2017-05-22
'from `tiki_objects` as tobt, `tiki_poll_objects` as tpo, `tiki_poll_options` where tobt.`itemId`= tp.`pageName` and tobt.`type`=\'wiki page\' and tobt.`objectId`=tpo.`catObjectId` and `tiki_poll_options`.`pollId`=tpo.`pollId` group by `tiki_poll_options`.`pollId`) as rating';
}
if (! empty($join_bindvars)) {
$bindvars = empty($bindvars) ? $join_bindvars : array_merge($join_bindvars, $bindvars);
}
$query = "select $distinct"
. ( $onlyCant ? "tp.`pageName`" : "tp.* " . $select )
. " from `tiki_pages` as tp $join_tables $mid order by " . $this->convertSortMode($sort_mode);
$countquery = "select count($distinct tp.`pageName`) from `tiki_pages` as tp $join_tables $mid";
$pageCount = $this->getOne($countquery, $bindvars);
// HOTFIX (svn Rev. 22969 or near there)
// Chunk loading. Because we cannot know what pages are visible, we load chunks of pages
// and use Perms::filter to see what remains. Stop, if we have enough.
$cant = 0;
$n = -1;
$ret = [];
$raw = [];
$offset_tmp = 0;
$haveEnough = false;
$filterPerms = empty($ref) ? 'view' : ['view', 'wiki_view_ref'];
while (! $haveEnough) {
$rawTemp = $this->fetchAll($query, $bindvars, $maxRecords, $offset_tmp);
$offset_tmp += $maxRecords; // next offset
if (count($rawTemp) == 0) {
$haveEnough = true; // end of table
}
$rawTemp = Perms::filter([ 'type' => 'wiki page' ], 'object', $rawTemp, ['object' => 'pageName', 'creator' => 'creator'], $filterPerms);
$raw = array_merge($raw, $rawTemp);
if ((count($raw) >= $offset + $maxRecords) || $maxRecords == -1) {
$haveEnough = true; // now we have enough records
}
} // prbably this brace has to include the next foreach??? I am unsure.
// but if yes, the next lines have to be reviewed.
$history = $this->table('tiki_history');
$links = $this->table('tiki_links');
foreach ($raw as $res) {
if ($initial) {
$valid = false;
$verified = self::take_away_accent($res['pageName']);
foreach ((array) $initial as $candidate) {
if (stripos($verified, $candidate) === 0) {
$valid = true;
break;
}
}
if (! $valid) {
continue;
}
}
//WYSIWYCA
$res['perms'] = $this->get_perm_object($res['pageName'], 'wiki page', $res, false);
$n++;
if (! $need_everything && $offset != -1 && $n < $offset) {
continue;
}
if (! $onlyCant && ( $need_everything || $maxRecords == -1 || $cant < $maxRecords )) {
if ($onlyName) {
$res = ['pageName' => $res['pageName']];
} else {
$page = $res['pageName'];
$res['len'] = $res['page_size'];
unset($res['page_size']);
$res['flag'] = $res['flag'] == 'L' ? 'locked' : 'unlocked';
if ($forListPages && $prefs['wiki_list_versions'] == 'y') {
$res['versions'] = $history->fetchCount(['pageName' => $page]);
}
if ($forListPages && $prefs['wiki_list_links'] == 'y') {
$res['links'] = $links->fetchCount(['fromPage' => $page]);
}
if ($forListPages && $prefs['wiki_list_backlinks'] == 'y') {
$res['backlinks'] = $links->fetchCount(['toPage' => $page, 'fromPage' => $links->unlike('objectlink:%')]);
}
// backlinks do not include links from non-page objects TODO: full feature allowing this with options
}
if ($loadCategories) {
$cats = $categlib->get_object_categories('wiki page', $res['pageName']);
$res['categpath'] = [];
$res['categname'] = [];
foreach ($cats as $cat) {
$res['categpath'][] = $cp = $categlib->get_category_path_string($cat);
if ($s = strrchr($cp, ':')) {
$res['categname'][] = substr($s, 1);
} else {
$res['categname'][] = $cp;
}
}
}
$ret[] = $res;
}
$cant++;
}
if (! $need_everything) {
$cant += $offset;
}
// If sortmode is versions, links or backlinks sort using the ad-hoc function and reduce using old_offset and old_maxRecords
if ($need_everything) {
switch ($old_sort_mode) {
case 'versions_asc':
usort($ret, 'compare_versions');
break;
case 'versions_desc':
usort($ret, 'r_compare_versions');
break;
case 'links_desc':
usort($ret, 'compare_links');
break;
case 'links_asc':
usort($ret, 'r_compare_links');
break;
case 'backlinks_desc':
usort($ret, 'compare_backlinks');
break;
case 'backlinks_asc':
usort($ret, 'r_compare_backlinks');
break;
}
}
$retval = [];
$retval['data'] = $ret;
$retval['cant'] = $pageCount; // this is not exact. Workaround.
return $retval;
}
// Function that checks for:
// - tiki_p_admin
// - the permission itself
// - individual permission
// - category permission
// if O.K. this function shall replace similar constructs in list_pages and other functions above.
// $categperm is the category permission that should grant $perm. if none, pass 0
// If additional perm arguments are specified, the user must have all the perms to pass the test
/**
* @param $usertocheck
* @param $object
* @param $objtype
* @param $perm1
* @param null $perm2
* @param null $perm3
* @return bool
*/
public function user_has_perm_on_object($usertocheck, $object, $objtype, $perm1, $perm2 = null, $perm3 = null)
{
$accessor = $this->get_user_permission_accessor($usertocheck, $objtype, $object);
$chk1 = $perm1 != null ? $accessor->$perm1 : true;
$chk2 = $perm2 != null ? $accessor->$perm2 : true;
$chk3 = $perm3 != null ? $accessor->$perm3 : true;
return $chk1 && $chk2 && $chk3;
}
public function get_user_permission_accessor($usertocheck, $type = null, $object = null)
{
global $user;
if ($type && $object) {
$context = [ 'type' => $type, 'object' => $object ];
$accessor = Perms::get($context);
} else {
$accessor = Perms::get();
}
// Do not override perms for current users otherwise security tokens won't work
if ($usertocheck != $user) {
$groups = $this->get_user_groups($usertocheck);
$accessor->setGroups($groups);
}
return $accessor;
}
/* get all the perm of an object either in a table or global+smarty set
* OPTIMISATION: better to test tiki_p_admin outside for global=false
* TODO: all the objectTypes
* TODO: replace switch with object
* global = true set the global perm and smarty var, otherwise return an array of perms
*/
/**
* @param $objectId
* @param $objectType
* @param string $info
* @param bool $global
* @return array|bool
*/
public function get_perm_object($objectId, $objectType, $info = '', $global = true, $parentId = null)
{
global $user;
$smarty = TikiLib::lib('smarty');
$userlib = TikiLib::lib('user');
$perms = Perms::get([ 'type' => $objectType, 'object' => $objectId, 'parentId' => $parentId ]);
if (empty($perms->getGroups())) {
$perms->setGroups($this->get_user_groups($user));
}
$permNames = $userlib->get_permission_names_for($this->get_permGroup_from_objectType($objectType));
$ret = [];
foreach ($permNames as $perm) {
$ret[$perm] = $perms->$perm ? 'y' : 'n';
if ($global) {
$smarty->assign($perm, $ret[$perm]);
$GLOBALS[ $perm ] = $ret[$perm];
}
}
// Skip those 'local' permissions for admin users and when global is not requested.
if ($global && ! Perms::get()->admin) {
$ret2 = $this->get_local_perms($user, $objectId, $objectType, $info, true);
if ($ret2) {
$ret = $ret2;
}
}
return $ret;
}
/**
* @param $objectType
* @return string
*/
public function get_permGroup_from_objectType($objectType)
{
switch ($objectType) {
case 'tracker':
case 'trackeritem':
return 'trackers';
case 'file gallery':
case 'file':
return 'file galleries';
case 'article':
case 'submission':
case 'topic':
return 'cms';
case 'forum':
case 'thread':
return 'forums';
case 'blog':
case 'blog post':
return 'blogs';
case 'wiki page':
case 'history':
return 'wiki';
case 'faq':
return 'faqs';
case 'survey':
return 'surveys';
case 'newsletter':
return 'newsletters';
case 'calendaritem':
return 'calendar';
/* TODO */
default:
return $objectType;
}
}
/**
* @param $objectType
* @return string
*/
public function get_adminPerm_from_objectType($objectType)
{
switch ($objectType) {
case 'tracker':
return 'tiki_p_admin_trackers';
case 'image gallery':
case 'image':
return 'tiki_p_admin_galleries';
case 'file gallery':
case 'file':
return 'tiki_p_admin_file_galleries';
case 'article':
case 'submission':
return 'tiki_p_admin_cms';
case 'forum':
return 'tiki_p_admin_forum';
case 'blog':
case 'blog post':
return 'tiki_p_blog_admin';
case 'wiki page':
case 'history':
return 'tiki_p_admin_wiki';
case 'faq':
return 'tiki_p_admin_faqs';
case 'survey':
return 'tiki_p_admin_surveys';
case 'newsletter':
return 'tiki_p_admin_newsletters';
/* TODO */
default:
return "tiki_p_admin_$objectType";
}
}
/* deal all the special perm */
/**
* @param $user
* @param $objectId
* @param $objectType
* @param $info
* @param $global
* @return array|bool
*/
public function get_local_perms($user, $objectId, $objectType, $info, $global)
{
global $prefs;
$smarty = TikiLib::lib('smarty');
$userlib = TikiLib::lib('user');
$ret = [];
switch ($objectType) {
case 'wiki page':
case 'wiki':
if ($prefs['wiki_creator_admin'] == 'y' && ! empty($user) && ! empty($info) && $info['creator'] == $user) { //can admin his page
$perms = $userlib->get_permission_names_for($this->get_permGroup_from_objectType($objectType));
foreach ($perms as $perm) {
$ret[$perm] = 'y';
if ($global) {
$GLOBALS[$perm] = 'y';
$smarty->assign($perm, 'y');
}
}
return $ret;
}
// Enabling userpage is not enough, the prefix must be present, otherwise, permissions will be messed-up on new page creation
if ($prefs['feature_wiki_userpage'] == 'y' && ! empty($prefs['feature_wiki_userpage_prefix']) && ! empty($user) && strcasecmp($prefs['feature_wiki_userpage_prefix'], substr($objectId, 0, strlen($prefs['feature_wiki_userpage_prefix']))) == 0) {
if (strcasecmp($objectId, $prefs['feature_wiki_userpage_prefix'] . $user) == 0) { //can edit his page
if (! $global) {
$perms = $userlib->get_permission_names_for($this->get_permGroup_from_objectType($objectType));
foreach ($perms as $perm) {
if ($perm == 'tiki_p_view' || $perm == 'tiki_p_edit') {
$ret[$perm] = 'y';
} else {
$ret[$perm] = $GLOBALS[$perm];
}
}
} else {
global $tiki_p_edit, $tiki_p_view;
$tiki_p_view = 'y';
$smarty->assign('tiki_p_view', 'y');
$tiki_p_edit = 'y';
$smarty->assign('tiki_p_edit', 'y');
}
} else {
if (! $global) {
$ret['tiki_p_edit'] = 'n';
} else {
global $tiki_p_edit;
$tiki_p_edit = 'n';
$smarty->assign('tiki_p_edit', 'n');
}
}
if (! $global) {
$ret['tiki_p_rename'] = 'n';
$ret['tiki_p_rollback'] = 'n';
$ret['tiki_p_lock'] = 'n';
$ret['tiki_p_assign_perm_wiki_page'] = 'n';
} else {
global $tiki_p_rename, $tiki_p_rollback, $tiki_p_lock, $tiki_p_assign_perm_wiki_page;
$tiki_p_rename = $tiki_p_rollback = $tiki_p_lock = $tiki_p_assign_perm_wiki_page = 'n';
$smarty->assign('tiki_p_rename', 'n');
$smarty->assign('tiki_p_rollback', 'n');
$smarty->assign('tiki_p_lock', 'n');
$smarty->assign('tiki_p_assign_perm_wiki_page', 'n');
}
}
break;
case 'file gallery':
case 'file':
global $tiki_p_userfiles;
if ($objectType === 'file') {
$gal_info = TikiLib::lib('filegal')->get_file_gallery_info($info['galleryId']);
if ($gal_info['user'] === $user) {
$info['type'] = 'user'; // show my files as mine
} else {
$info['type'] = '';
}
}
if (
$prefs['feature_use_fgal_for_user_files'] === 'y' &&
$info['type'] === 'user' && $info['user'] === $user && $tiki_p_userfiles === 'y'
) {
foreach (
['tiki_p_download_files',
'tiki_p_upload_files',
'tiki_p_view_file_gallery',
'tiki_p_remove_files',
'tiki_p_create_file_galleries',
'tiki_p_edit_gallery_file',
] as $perm
) {
$GLOBALS[$perm] = 'y';
$smarty->assign($perm, 'y');
$ret[$perm] = 'y';
}
return $ret;
}
break;
default:
break;
}
return false;
}
// Returns a string-indexed array of modified preferences (those with a value other than the default). Keys are preference names. Values are preference values.
// NOTE: prefslib contains a similar method now called getModifiedPrefsForExport
/**
* @return array
*/
public function getModifiedPreferences()
{
$defaults = get_default_prefs();
$modified = [];
$results = $this->table('tiki_preferences')->fetchAll(['name', 'value'], []);
foreach ($results as $result) {
$name = $result['name'];
$value = $result['value'];
$strDef = "";
if (isset($defaults[$name]) && is_array($defaults[$name])) {
$strDef = implode(" ", $defaults[$name]);
} else {
$strDef = isset($defaults[$name]) ? $defaults[$name] : "";
}
if (empty($strDef) || ($strDef != (string) $value)) {
$modified[$name] = $value;
}
}
return $modified;
}
/**
* @param $names
* @param bool $exact_match
* @param bool $no_return
* @return array|bool
*/
public function get_preferences($names, $exact_match = false, $no_return = false)
{
global $prefs;
$preferences = [];
if ($exact_match) {
if (is_array($names)) {
$this->_get_values('tiki_preferences', 'name', $names, $prefs);
if (! $no_return) {
foreach ($names as $name) {
$preferences[$name] = $prefs[$name];
}
}
} else {
$this->get_preference($names);
if (! $no_return) {
$preferences = [ $names => $prefs[$names] ];
}
}
} else {
if (is_array($names)) {
//Only handle $filtername as array with exact_matches
return false;
} else {
$tikiPreferences = $this->table('tiki_preferences');
$preferences = $tikiPreferences->fetchMap('name', 'value', ['name' => $tikiPreferences->like($names)]);
}
}
return $preferences;
}
/**
* @param $name
* @param string $default
* @param bool $expectArray
* @return mixed|string
*/
public function get_preference($name, $default = '', $expectArray = false)
{
global $prefs;
$value = isset($prefs[$name]) ? $prefs[$name] : $default;
if (empty($value)) {
if ($expectArray) {
return [];
} else {
return $value;
}
}
if ($expectArray && is_string($value)) {
return unserialize($value);
} else {
return $value;
}
}
/**
* @param $name
*/
public function delete_preference($name)
{
global $user_overrider_prefs, $user_preferences, $user, $prefs;
$prefslib = TikiLib::lib('prefs');
$this->table('tiki_preferences')->delete(['name' => $name]);
$cachelib = TikiLib::lib('cache');
$cachelib->invalidate('global_preferences');
$definition = $prefslib->getPreference($name);
$value = $definition['default'];
if (isset($prefs)) {
if (in_array($name, $user_overrider_prefs)) {
$prefs['site_' . $name] = $value;
} elseif (isset($user_preferences[$user][$name])) {
$prefs[$name] = $user_preferences[$user][$name];
} else {
$prefs[$name] = $value;
}
}
}
/**
* @param $name
* @param $value
* @return bool
*/
public function set_preference($name, $value)
{
global $user_overrider_prefs, $user_preferences, $user, $prefs;
$prefslib = TikiLib::lib('prefs');
$definition = $prefslib->getPreference($name);
if ($definition && ! $definition['available']) {
return false;
}
$preferences = $this->table('tiki_preferences');
$preferences->insertOrUpdate(['value' => is_array($value) ? serialize($value) : $value], ['name' => $name]);
if (isset($prefs)) {
if (in_array($name, $user_overrider_prefs)) {
$prefs['site_' . $name] = $value;
} elseif (isset($user_preferences[$user][$name])) {
$prefs[$name] = $user_preferences[$user][$name];
} else {
$prefs[$name] = $value;
}
}
// Invalidate cache only after writing to DB to avoid other processes to cache the old info
$menulib = TikiLib::lib('menu');
$menulib->empty_menu_cache();
$cachelib = TikiLib::lib('cache');
$cachelib->invalidate('global_preferences');
return true;
}
/**
* @param $table
* @param $field_name
* @param null $var_names
* @param $global_ref
* @param string $query_cond
* @param null $bindvars
* @return bool
*/
public function _get_values($table, $field_name, $var_names = null, &$global_ref = [], $query_cond = '', $bindvars = null)
{
if (empty($table) || empty($field_name)) {
return false;
}
$needed = [];
$defaults = null;
if (is_array($var_names)) {
// Detect if var names are specified as keys (then values are considered as var defaults)
// by looking at the type of the first key
$defaults = ! is_integer(key($var_names));
// Check if we need to get the value from DB by looking in the global $user_preferences array
// (this is able to handle more than one var at a time)
// ... and store the default values as well, just in case we needs them later
if ($defaults) {
foreach ($var_names as $var => $default) {
if (! isset($global_ref[$var])) {
$needed[$var] = $default;
}
}
} else {
foreach ($var_names as $var) {
if (! isset($global_ref[$var])) {
$needed[$var] = null;
}
}
}
} elseif ($var_names !== null) {
return false;
}
$where = $query_cond;
if (empty($where)) {
$where = '1';
}
if (is_null($bindvars)) {
$bindvars = [];
}
if (count($needed) > 0) {
$where .= ' AND (0';
foreach ($needed as $var => $def) {
$where .= " or `$field_name`=?";
$bindvars[] = $var;
}
$where .= ')';
}
$query = "select `$field_name`, `value` from `$table` where $where";
$result = $this->fetchAll($query, $bindvars);
foreach ($result as $res) {
// store the db value in the global array
$global_ref[$res[$field_name]] = $res['value'];
// remove vars that have a value in db from the $needed array to avoid affecting them a default value
unset($needed[$res[$field_name]]);
}
// set defaults values if needed and if there is no value in database and if it's default was not null
if ($defaults) {
foreach ($needed as $var => $def) {
if (! is_null($def)) {
$global_ref[$var] = $def;
}
}
}
return true;
}
public function clear_cache_user_preferences()
{
global $user_preferences;
unset($user_preferences);
}
/**
* @param $my_user
* @param null $names
* @return bool
*/
public function get_user_preferences($my_user, $names = null)
{
global $user_preferences;
// $my_user must be specified
if (! is_string($my_user) || $my_user == '') {
return false;
}
global $user_preferences;
if (! is_array($user_preferences) || ! array_key_exists($my_user, $user_preferences)) {
$user_preferences[$my_user] = [];
}
$global_ref =& $user_preferences[$my_user];
$return = $this->_get_values('tiki_user_preferences', 'prefName', $names, $global_ref, '`user`=?', [$my_user]);
// Handle special display_timezone values
if (
isset($user_preferences[$my_user]['display_timezone']) && $user_preferences[$my_user]['display_timezone'] != 'Site' && $user_preferences[$my_user]['display_timezone'] != 'Local'
&& ! TikiDate::TimezoneIsValidId($user_preferences[$my_user]['display_timezone'])
) {
unset($user_preferences[$my_user]['display_timezone']);
}
return $return;
}
// Returns a boolean indicating whether the specified user (anonymous or not, the current user by default) has the specified preference set
/**
* @param $preference
* @param bool $username
* @return bool
*/
public function userHasPreference($preference, $username = false)
{
global $user, $user_preferences;
if ($username === false) {
$username = $user;
}
if ($username) {
if (! isset($user_preferences[$username])) {
$this->get_user_preferences($username);
}
return isset($user_preferences[$username][$preference]);
} else { // If $username is empty, we must be Anonymous looking up one of our own preferences
return isset($_SESSION['preferences'][$preference]);
}
}
/**
* @param $my_user
* @param $name
* @param null $default
* @return null
*/
public function get_user_preference($my_user, $name, $default = null)
{
global $user_preferences, $user;
if ($my_user) {
if ($user != $my_user && ! isset($user_preferences[$my_user])) {
$this->get_user_preferences($my_user);
}
if (isset($user_preferences) && isset($user_preferences[$my_user]) && isset($user_preferences[$my_user][$name])) {
return $user_preferences[$my_user][$name];
}
} else { // If $my_user is empty, we must be Anonymous getting one of our own preferences
if (isset($_SESSION['preferences'][$name])) {
return $_SESSION['preferences'][$name];
}
}
return $default;
}
/**
* @param $my_user
* @param $name
* @param $value
*
* @return bool|TikiDb_Pdo_Result|TikiDb_Adodb_Result
* @throws Exception
*/
public function set_user_preference($my_user, $name, $value)
{
global $user_preferences, $prefs, $user, $user_overrider_prefs;
if ($my_user) {
$cachelib = TikiLib::lib('cache');
$cachelib->invalidate('user_details_' . $my_user);
if ($name == "realName") {
// attempt to invalidate userlink cache (does not cover all options - only the default)
$cachelib->invalidate('userlink.' . $user . '.' . $my_user . '0');
$cachelib->invalidate('userlink.' . $my_user . '0');
}
$userPreferences = $this->table('tiki_user_preferences', false);
$userPreferences->delete(['user' => $my_user, 'prefName' => $name]);
$result = $userPreferences->insert(['user' => $my_user, 'prefName' => $name, 'value' => $value]);
$user_preferences[$my_user][$name] = $value;
if ($my_user == $user) {
$prefs[$name] = $value;
if ($name == 'theme' && $prefs['change_theme'] == 'y') {
$prefs['users_prefs_theme'] = $value;
if ($value == '') {
$userPreferences->delete(['user' => $my_user, 'prefName' => $name]);
}
} elseif ($name == 'theme_option' && $prefs['change_theme'] == 'y') {
$prefs['users_prefs_theme-option'] = $value;
if ($value == '') {
$userPreferences->delete(['user' => $my_user, 'prefName' => $name]);
}
} elseif ($value == '') {
if (in_array($name, $user_overrider_prefs)) {
$prefs[$name] = $prefs['site_' . $name];
$userPreferences->delete(['user' => $my_user, 'prefName' => $name]);
}
}
return $result;
}
} else { // If $my_user is empty, we must be Anonymous updating one of our own preferences
if ($name == 'theme' && $prefs['change_theme'] == 'y') {
$prefs['theme'] = $value;
$_SESSION['preferences']['theme'] = $value;
if ($value == '') {
unset($_SESSION['preferences']['theme']);
unset($_SESSION['preferences']['theme_option']);
}
} elseif ($name == 'theme_option' && $prefs['change_theme'] == 'y' && ! empty($_SESSION['preferences']['theme'])) {
$prefs['theme_option'] = $value;
$_SESSION['preferences']['theme_option'] = $value;
} elseif ($value == '') {
if (in_array($name, $user_overrider_prefs)) {
$prefs[$name] = $prefs['site_' . $name];
unset($_SESSION['preferences'][$name]);
}
} else {
$prefs[$name] = $value;
$_SESSION['preferences'][$name] = $value;
}
return true;
}
}
// similar to set_user_preference, but set all at once.
/**
* @param $my_user
* @param $preferences
* @return bool
*/
public function set_user_preferences($my_user, &$preferences)
{
global $user_preferences, $prefs, $user;
$cachelib = TikiLib::lib('cache');
$cachelib->invalidate('user_details_' . $my_user);
$userPreferences = $this->table('tiki_user_preferences', false);
$userPreferences->deleteMultiple(['user' => $my_user]);
foreach ($preferences as $prefName => $value) {
$userPreferences->insert(['user' => $my_user, 'prefName' => $prefName, 'value' => $value]);
}
$user_preferences[$my_user] =& $preferences;
if ($my_user == $user) {
$prefs = array_merge($prefs, $preferences);
$_SESSION['s_prefs'] = array_merge($_SESSION['s_prefs'], $preferences);
}
return true;
}
// This implements all the functions needed to use Tiki
/*shared*/
// Returns whether a page named $pageName exists. Unless $casesensitive is set to true, the check is case-insensitive.
/**
* @param $pageName
* @param bool $casesensitive
* @return int
*/
public function page_exists($pageName, $casesensitive = false)
{
$page_info = $this->get_page_info($pageName, false);
return ( $page_info !== false && ( ! $casesensitive || $page_info['pageName'] == $pageName ) ) ? 1 : 0;
}
/**
* @param $pageName
* @return mixed
*/
public function page_exists_desc(&$pageName)
{
$page_info = $this->get_page_info($pageName, false);
return empty($page_info['description']) ? $pageName : $page_info['description'];
}
/**
* @param $pageName
* @return bool|int
*/
public function page_exists_modtime($pageName)
{
$page_info = $this->get_page_info($pageName, false);
if ($page_info === false) {
return false;
}
return empty($page_info['lastModif']) ? 0 : $page_info['lastModif'];
}
/**
* @param $pageName
* @return bool
*/
public function add_hit($pageName)
{
global $prefs;
if (StatsLib::is_stats_hit()) {
$pages = $this->table('tiki_pages');
$pages->update(['hits' => $pages->increment(1)], ['pageName' => $pageName]);
}
return true;
}
/** Create a wiki page
@param array $hash- lock_it,contributions, contributors
**/
public function create_page($name, $hits, $data, $lastModif, $comment, $user = 'admin', $ip = '0.0.0.0', $description = '', $lang = '', $is_html = false, $hash = null, $wysiwyg = null, $wiki_authors_style = '', $minor = 0, $created = '', $wiki_syntax = '')
{
global $prefs, $tracer;
$parserlib = TikiLib::lib('parser');
$tracer->trace('tikilib.create_page', "** invoked");
if (! $is_html) {
$data = str_replace('<x>', '', $data);
}
$name = trim($name); // to avoid pb with trailing space http://dev.mysql.com/doc/refman/5.1/en/char.html
if (! $user) {
$user = 'anonymous';
}
if (empty($wysiwyg)) {
$wysiwyg = $prefs['wysiwyg_default'];
if ($wysiwyg === 'y') {
$is_html = $prefs['wysiwyg_htmltowiki'] !== 'y';
}
}
// Collect pages before modifying data
$pointedPages = $parserlib->get_pages($data, true);
if (! isset($_SERVER["SERVER_NAME"])) {
$_SERVER["SERVER_NAME"] = $_SERVER["HTTP_HOST"] ?? '';
}
if ($this->page_exists($name)) {
Feedback::error(tr('TikiLib::create_page: Cannot create page "%0", it already exists.)', $name));
return false;
}
$tracer->trace('tikilib.create_page', "** TikiLib::lib...");
$tracer->trace('tikilib.create_page', "** invoking process_save_plugins, \$parserlib=" . get_class($parserlib));
$data = $parserlib->process_save_plugins(
$data,
[
'type' => 'wiki page',
'itemId' => $name,
'user' => $user,
]
);
$html = $is_html ? 1 : 0;
if ($html && $prefs['feature_purifier'] != 'n') {
$noparsed = [];
$parserlib->plugins_remove($data, $noparsed);
require_once('lib/htmlpurifier_tiki/HTMLPurifier.tiki.php');
$data = HTMLPurifier($data);
$parserlib->plugins_replace($data, $noparsed, true);
}
$insertData = [
'pageName' => $name,
'pageSlug' => TikiLib::lib('slugmanager')->generate($prefs['wiki_url_scheme'] ?: 'dash', $name, $prefs['url_only_ascii'] === 'y'),
'hits' => (int) $hits,
'data' => $data,
'description' => $description,
'lastModif' => (int) $lastModif,
'comment' => $comment,
'version' => 1,
'version_minor' => $minor,
'user' => $user,
'ip' => $ip,
'creator' => $user,
'page_size' => strlen($data),
'is_html' => $html,
'created' => empty($created) ? $this->now : $created,
'wysiwyg' => $wysiwyg,
'wiki_authors_style' => $wiki_authors_style,
'wiki_syntax' => $wiki_syntax,
];
if ($lang) {
$insertData['lang'] = $lang;
}
if (! empty($hash['lock_it']) && ($hash['lock_it'] == 'y' || $hash['lock_it'] == 'on')) {
$insertData['flag'] = 'L';
$insertData['lockedby'] = $user;
} elseif (empty($hash['lock_it']) || $hash['lock_it'] == 'n') {
$insertData['flag'] = '';
$insertData['lockedby'] = '';
}
if ($prefs['wiki_comments_allow_per_page'] != 'n') {
if (! empty($hash['comments_enabled']) && $hash['comments_enabled'] == 'y') {
$insertData['comments_enabled'] = 'y';
} elseif (empty($hash['comments_enabled']) || $hash['comments_enabled'] == 'n') {
$insertData['comments_enabled'] = 'n';
}
}
if (empty($hash['contributions'])) {
$hash['contributions'] = '';
}
if (empty($hash['contributors'])) {
$hash2 = '';
} else {
foreach ($hash['contributors'] as $c) {
$hash3['contributor'] = $c;
$hash2[] = $hash3;
}
}
$pages = $this->table('tiki_pages');
$page_id = $pages->insert($insertData);
//update status, page storage was updated in tiki 9 to be non html encoded
$wikilib = TikiLib::lib('wiki');
$converter = new convertToTiki9();
$converter->saveObjectStatus($page_id, 'tiki_pages');
$this->replicate_page_to_history($name);
$this->clear_links($name);
// Pages are collected before adding slashes
foreach ($pointedPages as $pointedPage => $types) {
$this->replace_link($name, $pointedPage, $types);
}
$wikilib->update_wikicontent_relations($data, 'wiki page', $name);
// Update the log
if (strtolower($name) != 'sandbox') {
$logslib = TikiLib::lib('logs');
$logslib->add_action("Created", $name, 'wiki page', 'add=' . strlen($data), $user, '', '', $created, $hash['contributions'], $hash2);
//get_strings tra("Created");
// Need to categorize new pages before sending mail notifications to make sure category permissions are considered
if (! empty($_REQUEST['cat_categories']) && ! empty($_REQUEST["page"])) {
// these variables are used in categorize.php
$cat_type = 'wiki page';
$cat_objid = $_REQUEST["page"];
include_once("categorize.php");
}
// Deal with mail notifications.
include_once(__DIR__ . '/notifications/notificationemaillib.php');
$foo = parse_url($_SERVER["REQUEST_URI"] ?? '');
$machine = self::httpPrefix(true) . dirname($foo["path"]);
sendWikiEmailNotification('wiki_page_created', $name, $user, $comment, 1, $data, $machine, '', false, $hash['contributions']);
if ($prefs['feature_contribution'] == 'y') {
$contributionlib = TikiLib::lib('contribution');
$contributionlib->assign_contributions($hash['contributions'], $name, 'wiki page', $description, $name, "tiki-index.php?page=" . urlencode($name));
}
}
//if there are links to this page, clear cache to avoid linking to edition
$toInvalidate = $this->table('tiki_links')->fetchColumn('fromPage', ['toPage' => $name]);
foreach ($toInvalidate as $res) {
$this->invalidate_cache($res);
}
TikiLib::events()->trigger(
'tiki.wiki.create',
[
'type' => 'wiki page',
'object' => $name,
'namespace' => $wikilib->get_namespace($name),
'user' => $GLOBALS['user'],
'page_id' => $page_id,
'version' => 1,
'data' => $data,
'old_data' => '',
]
);
// Update links to the URL of the new page from HTML wiki pages (when wysiwyg is in use).
// This is not an elegant fix but will do for now until the "use wiki syntax in WYSIWYG" feature is ready (if that ever replaces HTML-mode WYSIWYG completely).
if ($prefs['feature_wysiwyg'] == 'y' && $prefs['wysiwyg_htmltowiki'] != 'y') {
$wikilib = TikiLib::lib('wiki');
$temppage = md5($this->now . $name);
$wikilib->wiki_rename_page($name, $temppage, false, $user);
$wikilib->wiki_rename_page($temppage, $name, false, $user);
}
$tracer->trace('tikilib.create_page', "** Returning");
return true;
}
/**
* @param $pageName
* @return bool|mixed
*/
protected function replicate_page_to_history($pageName)
{
if (strtolower($pageName) == 'sandbox') {
return false;
}
$query = "INSERT IGNORE INTO `tiki_history`(`pageName`, `version`, `version_minor`, `lastModif`, `user`, `ip`, `comment`, `data`, `description`,`is_html`)
SELECT `pageName`, `version`, `version_minor`, `lastModif`, `user`, `ip`, `comment`, `data`, `description`,`is_html`
FROM tiki_pages
WHERE pageName = ?
LIMIT 1";
$this->query($query, [$pageName]);
$id = $this->lastInsertId();
//update status, we don't want the page to be decoded later
$wikilib = TikiLib::lib('wiki');
$converter = new convertToTiki9();
$converter->saveObjectStatus($id, 'tiki_history');
return $id;
}
/**
* @param $pageName
* @return bool|mixed
*/
public function restore_page_from_history($pageName, $version = null)
{
if (strtolower($pageName) == 'sandbox') {
return false;
}
$query = "SELECT `version`, `version_minor`, `lastModif`, `user`, `ip`, `comment`, `data`, `description`,`is_html`
FROM tiki_history
WHERE pageName = ? ";
$bindvars = [$pageName];
if ($version === null) {
$query .= "ORDER BY version DESC";
} else {
$query .= "AND `version`=?";
$bindvars[] = $version;
}
$result = $this->query($query, $bindvars, 1);
if ($res = $result->fetchRow()) {
$query = "UPDATE `tiki_pages`
SET `version` = ?, `version_minor` = ?, `lastModif` = ?, `user` = ?, `ip` = ?, `comment` = ?, `data` = ?, `description` = ?,`is_html` = ?
WHERE pageName = ?";
$bindvars = [$res['version'], $res['version_minor'], $res['lastModif'], $res['user'], $res['ip'], $res['comment'], $res['data'], $res['description'], $res['is_html'], $pageName];
$this->query($query, $bindvars);
}
$bindvars = [$pageName];
$query = "SELECT `page_id` from `tiki_pages` WHERE pageName = ?";
$this->query($query, $bindvars);
if ($res = $result->fetchRow()) {
$id = $res['version'];
}
// FIXME: Are these lines necessary? If so, what is the proper status to use?
//$converter = new convertToTiki9();
//$converter->saveObjectStatus($id, 'tiki_pages', 'conv9.0');
return $id;
}
/**
* @param $user
* @param $max
* @param string $who
* @return mixed
*/
public function get_user_pages($user, $max, $who = 'user')
{
return $this->table('tiki_pages')->fetchAll(['pageName'], [$who => $user], $max);
}
/**
* @param $pageName
* @return bool
*/
public function get_page_print_info($pageName)
{
$query = "SELECT `pageName`, `data` as `parsed`, `is_html` FROM `tiki_pages` WHERE `pageName`=?";
$result = $this->query($query, [$pageName]);
if (! $result->numRows()) {
return false;
} else {
$page_info = $result->fetchRow();
$page_info['parsed'] = TikiLib::lib('parser')->parse_data($page_info['parsed'], ['is_html' => $page_info['is_html'], 'print' => 'y', 'page' => $pageName]);
$page_info['h'] = 1;
}
return $page_info;
}
/**
* @param $pageName
* @param bool $retrieve_datas
* @param bool $skipCache
* @return bool
*/
public function get_page_info($pageName, $retrieve_datas = true, $skipCache = false)
{
global $prefs;
$pageNameEncode = urlencode($pageName);
if (
! $skipCache && isset($this->cache_page_info[$pageNameEncode])
&& (! $retrieve_datas || ! empty($this->cache_page_info[$pageNameEncode]['data']))
) {
return $this->cache_page_info[$pageNameEncode];
}
if ($retrieve_datas) {
$query = "SELECT * FROM `tiki_pages` WHERE `pageName`=?";
} else {
$query = "SELECT `page_id`, `pageName`, `hits`, `description`, `lastModif`, `comment`, `version`, `version_minor`, `user`, `ip`, `flag`, `points`, `votes`, `wiki_cache`, `cache_timestamp`, `pageRank`, `creator`, `page_size`, `lang`, `lockedby`, `is_html`, `created`, `wysiwyg`, `wiki_authors_style`, `comments_enabled`, `wiki_syntax` FROM `tiki_pages` WHERE `pageName`=?";
}
$result = $this->query($query, [$pageName]);
if (! $result->numRows()) {
return false;
} else {
$row = $result->fetchRow();
$row['baseName'] = TikiLib::lib('wiki')->get_without_namespace($row['pageName']);
$row['prettyName'] = TikiLib::lib('wiki')->get_readable($row['pageName']);
$row['namespace'] = TikiLib::lib('wiki')->get_namespace($row['pageName']);
$row['namespace_parts'] = TikiLib::lib('wiki')->get_namespace_parts($row['pageName']);
// Be sure to have the correct character case (because DB is caseinsensitive)
$pageNameEncode = urlencode($row['pageName']);
// Limit memory usage of the page cache. No
// intelligence is attempted here whatsoever. This was
// done because a few thousand ((page)) links would blow
// up memory, even with the limit at 128MiB.
// Information on 128 pages really should be plenty.
while (count($this->cache_page_info) >= 128) {
// Need to delete something; pick at random
$keys = array_keys($this->cache_page_info);
$num = rand(0, count($keys));
if (isset($keys[$num])) {
unset($this->cache_page_info[$keys[$num]]);
}
}
$row['outputType'] = ''; // TODO remove as redundant?
$this->cache_page_info[$pageNameEncode] = $row;
return $this->cache_page_info[$pageNameEncode];
}
}
/**
* @param $page_id
* @return mixed
*/
public function get_page_info_from_id($page_id)
{
return $this->table('tiki_pages')->fetchFullRow(['page_id' => $page_id]);
}
/**
* @param $page_id
* @return mixed
*/
public function get_page_name_from_id($page_id)
{
return $this->table('tiki_pages')->fetchOne('pageName', ['page_id' => $page_id]);
}
/**
* @param $page
* @return mixed
*/
public function get_page_id_from_name($page)
{
return $this->table('tiki_pages')->fetchOne('page_id', ['pageName' => $page]);
}
/**
* @param $str
* @param $car
* @return int
*/
public static function how_many_at_start($str, $car)
{
$cant = 0;
$i = 0;
while (($i < strlen($str)) && (isset($str[$i])) && ($str[$i] == $car)) {
$i++;
$cant++;
}
return $cant;
}
/**
* @param $name
* @param $domain
* @param string $sep
* @return string
*/
public static function protect_email($name, $domain, $sep = '@')
{
TikiLib::lib('header')->add_jq_onready(
'$(".convert-mailto").removeClass("convert-mailto").each(function () {
var address = $(this).data("encode-name") + "@" + $(this).data("encode-domain");
$(this).attr("href", "mailto:" + address).text(address);
});'
);
return "<a class=\"convert-mailto\" href=\"mailto:nospam@example.com\" data-encode-name=\"$name\" data-encode-domain=\"$domain\">$name " . tra("at", "", true) . " $domain</a>";
}
//Updates a dynamic variable found in some object
/*Shared*/
/**
* @param $name
* @param $value
* @param null $lang
* @return bool
*/
public function update_dynamic_variable($name, $value, $lang = null)
{
$dynamicVariables = $this->table('tiki_dynamic_variables');
$dynamicVariables->delete(['name' => $name, 'lang' => $lang]);
$dynamicVariables->insert(['name' => $name, 'data' => $value, 'lang' => $lang]);
return true;
}
/**
* @param $page
*/
public function clear_links($page)
{
$this->table('tiki_links')->deleteMultiple(['fromPage' => $page]);
$objectRelations = $this->table('tiki_object_relations');
$objectRelations->deleteMultiple(
[
'source_type' => 'wiki page',
'source_itemId' => $page,
'target_type' => 'wiki page',
'relation' => $objectRelations->like('tiki.link.%'),
]
);
}
/**
* @param $pageFrom
* @param $pageTo
* @param array $types
*/
public function replace_link($pageFrom, $pageTo, $types = [])
{
global $prefs;
if (
$prefs['namespace_enabled'] == 'y' && $prefs['namespace_force_links'] == 'y'
&& TikiLib::lib('wiki')->get_namespace($pageFrom)
&& ! TikiLib::lib('wiki')->get_namespace($pageTo)
) {
$namespace = TikiLib::lib('wiki')->get_namespace($pageFrom);
$pageTo = $namespace . $prefs['namespace_separator'] . $pageTo;
}
// The max pagename length is 160 characters ( tiki_pages.pageName varchar(160) ).
// However, wiki_rename_page stores a page in the format: $tmpName = "~".$newName."~";
// So, actual max page name length is 160 - 2 = 158
// Strip excess characters (silently) and proceed.
$pageTo = substr($pageTo, 0, 158);
$links = $this->table('tiki_links');
$links->insert(['fromPage' => $pageFrom, 'toPage' => $pageTo], true);
$relationlib = TikiLib::lib('relation');
foreach ($types as $type) {
$relationlib->add_relation("tiki.link.$type", 'wiki page', $pageFrom, 'wiki page', $pageTo);
}
}
/**
* @param $page
*/
public function invalidate_cache($page)
{
unset($this->cache_page_info[urlencode($page)]);
$this->table('tiki_pages')->update(['cache_timestamp' => 0], ['pageName' => $page]);
$pageCache = Tiki_PageCache::create()
->checkMeta('wiki-page-output-meta-timestamp', ['page' => $page ])
->invalidate();
}
/** Update a wiki page
@param array $hash- lock_it,contributions, contributors
@param int $saveLastModif - modification time - pass null for now, unless importing a Wiki page
**/
public function update_page($pageName, $edit_data, $edit_comment, $edit_user, $edit_ip, $edit_description = null, $edit_minor = 0, $lang = '', $is_html = null, $hash = null, $saveLastModif = null, $wysiwyg = '', $wiki_authors_style = '', $wiki_syntax = '')
{
global $prefs;
$histlib = TikiLib::lib('hist');
$parserlib = TikiLib::lib('parser');
if (! $edit_user) {
$edit_user = 'anonymous';
}
$this->invalidate_cache($pageName);
// Collect pages before modifying edit_data (see update of links below)
$pages = $parserlib->get_pages($edit_data, true);
if (! $this->page_exists($pageName)) {
return false;
}
// Get this page information
$info = $this->get_page_info($pageName);
if ($edit_description === null) {
$edit_description = $info['description'];
}
$user = $info["user"] ? $info["user"] : 'anonymous';
$data = $info["data"];
$willDoHistory = ($prefs['feature_wiki_history_full'] == 'y' || $data != $edit_data || $info['description'] != $edit_description || $info["comment"] != $edit_comment );
$version = $histlib->get_page_next_version($pageName, $willDoHistory);
$old_version = $version - 1; // this doesn't really make sense but is needed to make diff links work properly - regression from r65651
if ($is_html === null) {
$html = $info['is_html'];
} else {
$html = $is_html ? 1 : 0;
}
if ($wysiwyg == '') {
$wysiwyg = $info['wysiwyg'];
}
if ($wysiwyg == 'y' && $html != 1 && $prefs['wysiwyg_htmltowiki'] != 'y') { // correct for html only wysiwyg
$html = 1;
}
$edit_data = $parserlib->process_save_plugins(
$edit_data,
[
'type' => 'wiki page',
'itemId' => $pageName,
'user' => $user,
]
);
if ($html == 1 && $prefs['feature_purifier'] != 'n') {
$noparsed = [];
$parserlib->plugins_remove($edit_data, $noparsed);
require_once('lib/htmlpurifier_tiki/HTMLPurifier.tiki.php');
$edit_data = HTMLPurifier($edit_data);
$parserlib->plugins_replace($edit_data, $noparsed, true);
}
if (is_null($saveLastModif)) {
$saveLastModif = $this->now;
}
$queryData = [
'description' => $edit_description,
'data' => $edit_data,
'comment' => $edit_comment,
'lastModif' => (int) $saveLastModif,
'version' => $version,
'version_minor' => $edit_minor,
'user' => $edit_user,
'ip' => $edit_ip,
'page_size' => strlen($edit_data),
'is_html' => $html,
'wysiwyg' => $wysiwyg,
'wiki_authors_style' => $wiki_authors_style,
'lang' => $lang,
'wiki_syntax' => $wiki_syntax,
];
if ($hash !== null) {
if (! empty($hash['lock_it']) && ($hash['lock_it'] == 'y' || $hash['lock_it'] == 'on')) {
$queryData['flag'] = 'L';
$queryData['lockedby'] = $user;
} elseif (empty($hash['lock_it']) || $hash['lock_it'] == 'n') {
$queryData['flag'] = '';
$queryData['lockedby'] = '';
}
}
if ($prefs['wiki_comments_allow_per_page'] != 'n') {
if (! empty($hash['comments_enabled']) && $hash['comments_enabled'] == 'y') {
$queryData['comments_enabled'] = 'y';
} elseif (empty($hash['comments_enabled']) || $hash['comments_enabled'] == 'n') {
$queryData['comments_enabled'] = 'n';
}
}
if (empty($hash['contributions'])) {
$hash['contributions'] = '';
}
if (empty($hash['contributors'])) {
$hash2 = '';
} else {
foreach ($hash['contributors'] as $c) {
$hash3['contributor'] = $c;
$hash2[] = $hash3;
}
}
$this->table('tiki_pages')->update($queryData, ['pageName' => $pageName]);
// Synchronize object comment
if ($prefs['feature_wiki_description'] == 'y') {
$query = 'update `tiki_objects` set `description`=? where `itemId`=? and `type`=?';
$this->query($query, [ $edit_description, $pageName, 'wiki page']);
}
//update status, page storage was updated in tiki 9 to be non html encoded
$wikilib = TikiLib::lib('wiki');
$converter = new convertToTiki9();
$converter->saveObjectStatus($this->getOne("SELECT page_id FROM tiki_pages WHERE pageName = ?", [$pageName]), 'tiki_pages');
// Parse edit_data updating the list of links from this page
$this->clear_links($pageName);
// Pages collected above
foreach ($pages as $page => $types) {
$this->replace_link($pageName, $page, $types);
}
$wikilib->update_wikicontent_relations($edit_data, 'wiki page', $pageName);
if (strtolower($pageName) != 'sandbox' && ! $edit_minor) {
$maxversions = $prefs['maxVersions'];
if ($maxversions && ($nb = $histlib->get_nb_history($pageName)) > $maxversions) {
// Select only versions older than keep_versions days
$keep = $prefs['keep_versions'];
$oktodel = $saveLastModif - ($keep * 24 * 3600) + 1;
$history = $this->table('tiki_history');
$result = $history->fetchColumn(
'version',
['pageName' => $pageName,'lastModif' => $history->lesserThan($oktodel)],
$nb - $maxversions,
0,
['lastModif' => 'ASC']
);
foreach ($result as $toRemove) {
$histlib->remove_version($pageName, $toRemove);
}
}
}
// This if no longer checks for minor-ness of the change; sendWikiEmailNotification does that.
if ($willDoHistory) {
$this->replicate_page_to_history($pageName);
if (strtolower($pageName) != 'sandbox') {
if ($prefs['feature_contribution'] == 'y') {// transfer page contributions to the history
$contributionlib = TikiLib::lib('contribution');
$history = $this->table('tiki_history');
$historyId = $history->fetchOne($history->max('historyId'), ['pageName' => $pageName, 'version' => (int) $old_version]);
$contributionlib->change_assigned_contributions($pageName, 'wiki page', $historyId, 'history', '', $pageName . '/' . $old_version, "tiki-pagehistory.php?page=$pageName&preview=$old_version");
}
}
include_once('lib/diff/difflib.php');
if (strtolower($pageName) != 'sandbox') {
$logslib = TikiLib::lib('logs');
$bytes = diff2($data, $edit_data, 'bytes');
$logslib->add_action('Updated', $pageName, 'wiki page', $bytes, $edit_user, $edit_ip, '', $saveLastModif, $hash['contributions'], $hash2);
if ($prefs['feature_contribution'] == 'y') {
$contributionlib = TikiLib::lib('contribution');
$contributionlib->assign_contributions($hash['contributions'], $pageName, 'wiki page', $edit_description, $pageName, "tiki-index.php?page=" . urlencode($pageName));
}
}
if ($prefs['feature_multilingual'] == 'y' && $lang) {
// Need to update the translated objects table when an object's language changes.
$this->table('tiki_translated_objects')->update(['lang' => $lang], ['type' => 'wiki page', 'objId' => $info['page_id']]);
}
if ($prefs['wiki_watch_minor'] != 'n' || ! $edit_minor) {
// Deal with mail notifications.
include_once(__DIR__ . '/notifications/notificationemaillib.php');
$histlib = TikiLib::lib('hist');
$old = $histlib->get_version($pageName, $old_version);
$foo = parse_url($_SERVER["REQUEST_URI"]);
$machine = self::httpPrefix(true) . dirname($foo["path"]);
$oldPagePlugins = WikiParser_PluginMatcher::match($old["data"]);
$editedPagePlugins = WikiParser_PluginMatcher::match($edit_data);
// Diagram data can be too big to diff, convert the content to md5 that will help to check for differences
$replacedOldPagePlugins = DiagramHelper::md5WikiPluginDiagramContent($oldPagePlugins);
$replacedEditedPagePlugins = DiagramHelper::md5WikiPluginDiagramContent($editedPagePlugins);
$oldPagePlugins->rewind();
$editedPagePlugins->rewind();
$parsedOldPage = $oldPagePlugins->getText();
$parsedNewPage = $editedPagePlugins->getText();
TikiLib::lib('smarty')->assign('has_md5_content_diagrams', $replacedOldPagePlugins || $replacedUpdatedPagePlugins);
$diff = diff2($parsedOldPage, $parsedNewPage, "unidiff"); // TODO: Only compute if we have at least one notification to send
sendWikiEmailNotification('wiki_page_changed', $pageName, $edit_user, $edit_comment, $old_version, $edit_data, $machine, $diff, $edit_minor, $hash['contributions'], 0, 0, $lang);
}
}
$tx = $this->begin();
TikiLib::events()->trigger(
'tiki.wiki.update',
[
'type' => 'wiki page',
'object' => $pageName,
'namespace' => $wikilib->get_namespace($pageName),
'reply_action' => 'comment',
'user' => $GLOBALS['user'],
'page_id' => $info['page_id'],
'version' => $version,
'old_version' => $old_version,
'data' => $edit_data,
'old_data' => $info['data'],
'edit_comment' => $edit_comment,
]
);
$tx->commit();
}
/**
* @param $context
* @param $data
*/
public function object_post_save($context, $data)
{
global $prefs;
if (is_array($data)) {
if (isset($data['content']) && $prefs['feature_file_galleries'] == 'y') {
$filegallib = TikiLib::lib('filegal');
$filegallib->syncFileBacklinks($data['content'], $context);
}
if (isset($data['content'])) {
$this->plugin_post_save_actions($context, $data);
}
} else {
if (isset($context['content']) && $prefs['feature_file_galleries'] == 'y') {
$filegallib = TikiLib::lib('filegal');
$filegallib->syncFileBacklinks($context['content'], $context);
}
$this->plugin_post_save_actions($context);
}
}
/**
* Foreach plugin used in a object content call its save handler,
* if one exist, and send email notifications when it has pending
* status, if preference is enabled.
*
* A plugin save handler is a function defined on the plugin file
* with the following format: wikiplugin_$pluginName_save()
*
* This function is called from object_post_save. Do not call directly.
*
* @param array $context object type and id
* @param array $data
* @return void
*/
private function plugin_post_save_actions($context, $data = null)
{
global $prefs;
$parserlib = TikiLib::lib('parser');
if (is_null($data)) {
$content = [];
if (isset($context['values'])) {
$content = $context['values'];
}
if (isset($context['data'])) {
$content[] = $context['data'];
}
$data['content'] = implode(' ', $content);
}
$argumentParser = new WikiParser_PluginArgumentParser();
$matches = WikiParser_PluginMatcher::match($data['content']);
foreach ($matches as $match) {
$plugin_name = $match->getName();
$body = $match->getBody();
$arguments = $argumentParser->parse($match->getArguments());
$dummy_output = '';
if ($parserlib->plugin_enabled($plugin_name, $dummy_output)) {
$status = $parserlib->plugin_can_execute($plugin_name, $body, $arguments, true);
// when plugin status is pending, $status equals plugin fingerprint
if ($prefs['wikipluginprefs_pending_notification'] == 'y' && $status !== true && $status != 'rejected') {
$this->plugin_pending_notification($plugin_name, $body, $arguments, $context);
}
WikiPlugin_Negotiator_Wiki_Alias::findImplementation($plugin_name, $body, $arguments);
$func_name = 'wikiplugin_' . $plugin_name . '_save';
if (function_exists($func_name)) {
$func_name($context, $body, $arguments);
}
}
}
}
/**
* Send notification by email that a plugin is waiting to be
* approved to everyone with permission to approve it.
*
* @param string $plugin_name
* @param string $body plugin body
* @param array $arguments plugin arguments
* @param array $context object type and id
* @return void
*/
private function plugin_pending_notification($plugin_name, $body, $arguments, $context)
{
require_once('lib/webmail/tikimaillib.php');
global $prefs, $base_url;
$mail = new TikiMail(null, $prefs['sender_email']);
$mail->setSubject(tr("Plugin %0 pending approval", $plugin_name));
$smarty = TikiLib::lib('smarty');
$smarty->assign('plugin_name', $plugin_name);
$smarty->assign('type', $context['type']);
$smarty->assign('objectId', $context['object']);
$smarty->assign('arguments', $arguments);
$smarty->assign('body', $body);
$mail->setHtml(nl2br($smarty->fetch('mail/plugin_pending_notification.tpl')));
$recipients = $this->plugin_get_email_users_with_perm();
$mail->setBcc($recipients);
if (! empty($prefs['sender_email'])) {
$mail->send([$prefs['sender_email']]);
} elseif ($admin_email = TikiLib::lib('user')->get_user_email('admin')) {
$recipients = array_diff($recipients, [$admin_email]);
$mail->setBcc($recipients);
$mail->send([$admin_email]);
}
}
/**
* Return a list of e-mails from the users with permission
* to approve a plugin.
*
* @return array
*/
private function plugin_get_email_users_with_perm()
{
global $prefs;
$userlib = TikiLib::lib('user');
$allGroups = $userlib->get_groups();
$accessor = Perms::get();
// list of groups with permission to approve plugin on this object
$groups = [];
foreach ($allGroups['data'] as $group) {
$accessor->setGroups([$group['groupName']]);
if ($accessor->plugin_approve) {
$groups[] = $group['groupName'];
}
}
$recipients = [];
foreach ($groups as $group) {
$recipients = array_merge($recipients, $userlib->get_group_users($group, 0, -1, 'email'));
}
$recipients = array_filter($recipients);
$recipients = array_unique($recipients);
if ($prefs['user_plugin_approval_watch_editor'] === 'y') {
# Do not self-notify, therefore, remove user's email from the list of recipients
$useremail = TikiLib::lib('user')->get_user_email($user);
$recipients = array_diff($recipients, array($useremail));
}
return $recipients;
}
/**
* @param bool $_user
* @return null|string
*/
public function get_display_timezone($_user = false)
{
global $prefs, $user;
if ($_user === false || $_user == $user) {
// If the requested timezone is the current user timezone
$tz = $prefs['display_timezone'];
} elseif ($_user) {
// ... else, get the user timezone preferences from DB
$tz = $this->get_user_preference($_user, 'display_timezone');
}
if (! TikiDate::TimezoneIsValidId($tz)) {
$tz = $prefs['server_timezone'];
}
if (! TikiDate::TimezoneIsValidId($tz)) {
$tz = 'UTC';
}
return $tz;
}
public function set_display_timezone($user)
{
global $prefs, $user_preferences;
if (
$prefs['users_prefs_display_timezone'] == 'Site' ||
(isset($user_preferences[$user]['display_timezone']) && $user_preferences[$user]['display_timezone'] == 'Site')
) {
// Stay in the time zone of the server
$prefs['display_timezone'] = $prefs['server_timezone'];
} elseif (empty($user_preferences[$user]['display_timezone']) || $user_preferences[$user]['display_timezone'] == 'Local') {
// If the display timezone is not known ...
if (isset($_COOKIE['local_tz'])) {
// ... we try to use the timezone detected by javascript and stored in cookies
if (TikiDate::TimezoneIsValidId($_COOKIE['local_tz'])) {
$prefs['display_timezone'] = $_COOKIE['local_tz'];
} elseif (in_array(strtolower($_COOKIE['local_tz']), TikiDate::getTimezoneAbbreviations())) { // abbreviation like BST or CEST
// timezone_offset in seconds
$prefs['timezone_offset'] = isset($_COOKIE['local_tzoffset']) ? (int) $_COOKIE['local_tzoffset'] * 60 * 60 : -1;
$tzname = timezone_name_from_abbr($_COOKIE['local_tz'], $prefs['timezone_offset']);
if (TikiDate::TimezoneIsValidId($tzname)) { // double check
$prefs['display_timezone'] = $tzname;
} else {
$prefs['display_timezone'] = $_COOKIE['local_tz'];
}
} elseif ($_COOKIE['local_tz'] == 'HAEC') {
// HAEC, returned by Safari on Mac, is not recognized as a DST timezone (with daylightsavings)
// ... So use one equivalent timezone name
$prefs['display_timezone'] = 'Europe/Paris';
} else {
$prefs['display_timezone'] = $prefs['server_timezone'];
}
} else {
// ... and we fallback to the server timezone if the cookie value is not available
$prefs['display_timezone'] = $prefs['server_timezone'];
}
}
}
public function get_long_date_format()
{
global $prefs;
return $prefs['long_date_format'];
}
public function get_short_date_format()
{
global $prefs;
return $prefs['short_date_format'];
}
public function get_long_time_format()
{
global $prefs;
return $prefs['long_time_format'];
}
public function get_short_time_format()
{
global $prefs;
return $prefs['short_time_format'];
}
/**
* @return string
*/
public function get_long_datetime_format()
{
static $long_datetime_format = false;
if (! $long_datetime_format) {
$t = trim($this->get_long_time_format());
if (! empty($t)) {
$t = ' ' . $t;
}
$long_datetime_format = $this->get_long_date_format() . $t;
}
return $long_datetime_format;
}
/**
* @return string
*/
public function get_short_datetime_format()
{
static $short_datetime_format = false;
if (! $short_datetime_format) {
$t = trim($this->get_short_time_format());
if (! empty($t)) {
$t = ' ' . $t;
}
$short_datetime_format = $this->get_short_date_format() . $t;
}
return $short_datetime_format;
}
/**
* @param $format
* @param bool $timestamp
* @param bool $_user
* @param int $input_format
* @return string
*/
public static function date_format2($format, $timestamp = false, $_user = false, $input_format = 5/*DATE_FORMAT_UNIXTIME*/)
{
return TikiLib::date_format($format, $timestamp, $_user, $input_format, false);
}
/**
* @param $format
* @param bool $timestamp
* @param bool $_user
* @param int $input_format
* @param bool $is_strftime_format
* @return string
*/
public static function date_format($format, $timestamp = false, $_user = false, $input_format = 5/*DATE_FORMAT_UNIXTIME*/, $is_strftime_format = true, $use_display_tz = true)
{
$tikilib = TikiLib::lib('tiki');
static $currentUserDateByFormat = [];
if (! $timestamp) {
$timestamp = $tikilib->now;
}
if ($_user === false && $is_strftime_format && $timestamp == $tikilib->now && isset($currentUserDateByFormat[ $format . $timestamp ])) {
return $currentUserDateByFormat[ $format . $timestamp ];
}
$tikidate = TikiLib::lib('tikidate');
try {
$tikidate->setDate($timestamp, 'UTC');
} catch (Exception $e) {
return $e->getMessage();
}
$tz = $tikilib->get_display_timezone($_user);
// If user timezone is not also in UTC, convert the date
if ($tz != 'UTC' && $use_display_tz) {
$tikidate->setTZbyID($tz);
}
$return = $tikidate->format($format, $is_strftime_format);
if ($is_strftime_format) {
$currentUserDateByFormat[ $format . $timestamp ] = $return;
}
return $return;
}
/**
* @param $hour
* @param $minute
* @param $second
* @param $month
* @param $day
* @param $year
* @return int
*/
public function make_time($hour, $minute, $second, $month, $day, $year)
{
$tikilib = TikiLib::lib('tiki');
$tikidate = TikiLib::lib('tikidate');
$display_tz = $tikilib->get_display_timezone();
if ($display_tz == '') {
$display_tz = 'UTC';
}
$tikidate->setTZbyID($display_tz);
$tikidate->setLocalTime($day, $month, $year, $hour, $minute, $second, 0);
return $tikidate->getTime();
}
/**
* @param $timestamp
* @param bool $user
* @return string
*/
public function get_long_date($timestamp, $user = false)
{
return $this->date_format($this->get_long_date_format(), $timestamp, $user);
}
/**
* @param $timestamp
* @param bool $user
* @return string
*/
public function get_short_date($timestamp, $user = false)
{
return $this->date_format($this->get_short_date_format(), $timestamp, $user);
}
/**
* @param $timestamp
* @param bool $user
* @return string
*/
public function get_long_time($timestamp, $user = false)
{
return $this->date_format($this->get_long_time_format(), $timestamp, $user);
}
/**
* @param $timestamp
* @param bool $user
* @return string
*/
public function get_short_time($timestamp, $user = false)
{
return $this->date_format($this->get_short_time_format(), $timestamp, $user);
}
/**
* @param $timestamp
* @param bool $user
* @return string
*/
public function get_long_datetime($timestamp, $user = false)
{
return $this->date_format($this->get_long_datetime_format(), $timestamp, $user);
}
/**
* @param $timestamp
* @param bool $user
* @return string
*/
public function get_short_datetime($timestamp, $user = false)
{
return $this->date_format($this->get_short_datetime_format(), $timestamp, $user);
}
/**
Per http://www.w3.org/TR/NOTE-datetime
*/
public function get_iso8601_datetime($timestamp, $user = false)
{
return $this->date_format('%Y-%m-%dT%H:%M:%S%O', $timestamp, $user);
}
/**
* @param $timestamp
* @param bool $user
* @return string
*/
public function get_compact_iso8601_datetime($timestamp, $user = false)
{
// no dashes and no tz info - latter should be fixed
return $this->date_format('%Y%m%dT%H%M%S', $timestamp, $user);
}
/**
* @return array of css files in the style dir
*/
public function list_styles()
{
global $tikidomain;
$csslib = TikiLib::lib('css');
$sty = [];
$style_base_path = $this->get_style_path(); // knows about $tikidomain
if ($style_base_path) {
$sty = $csslib->list_css($style_base_path);
}
if ($tikidomain) {
$sty = array_unique(array_merge($sty, $csslib->list_css('styles')));
}
foreach ($sty as &$s) {
if (in_array($s, ['mobile', '960_gs'])) {
$s = '';
} elseif (substr($s, -4) == '-rtl' || substr($s, -6) == '-print') {
$s = '';
} else {
$s .= '.css'; // add the .css back onto the end of the style names
}
}
$sty = array_filter($sty);
sort($sty);
return $sty;
/* What is this $tikidomain section?
* Some files that call this method used to list styles without considering
* $tikidomain, now they do. They're listed below:
*
* tiki-theme_control.php
* tiki-theme_control_objects.php
* tiki-theme_control_sections.php
* tiki-my_tiki.php
* modules/mod-switch_theme.php
*
* lfagundes
*
* Tiki 3.0 - now handled by get_style_path()
* jonnybradley
*/
}
/**
* @param $a_style - main style (e.g. "thenews.css")
* @return array of css files in the style options dir
*/
public function list_style_options($a_style = '')
{
global $prefs;
$csslib = TikiLib::lib('css');
if (empty($a_style)) {
$a_style = $prefs['style'];
}
$sty = [];
$option_base_path = $this->get_style_path($a_style) . 'options/';
if (is_dir($option_base_path)) {
$sty = $csslib->list_css($option_base_path);
}
if (count($sty)) {
foreach ($sty as &$s) { // add .css back as above
$s .= '.css';
}
sort($sty);
return $sty;
} else {
return false;
}
}
/**
* @param $stl - main style (e.g. "thenews.css")
* @return string - style passed in up to - | or . char (e.g. "thenews")
*/
public function get_style_base($stl)
{
$parts = preg_split('/[\-\.]/', $stl);
if (count($parts) > 0) {
return $parts[0];
} else {
return '';
}
}
/**
* @param $stl - main style (e.g. "thenews.css" - can be empty to return main styles dir)
* @param $opt - optional option file name (e.g. "purple.css")
* @param $filename - optional filename to look for (e.g. "purple.png")
* @return path to dir or file if found or empty if not - e.g. "styles/mydomain.tld/thenews/options/purple/"
*/
public function get_style_path($stl = '', $opt = '', $filename = '')
{
global $tikidomain;
$path = '';
$dbase = '';
if ($tikidomain && is_dir("styles/$tikidomain")) {
$dbase = $tikidomain . '/';
}
$sbase = '';
if (! empty($stl)) {
$sbase = $this->get_style_base($stl) . '/';
}
$obase = '';
if (! empty($opt)) {
$obase = 'options/';
if ($opt != $filename) { // exception for getting option.css as it doesn't live in it's own dir
$obase .= substr($opt, 0, strlen($opt) - 4) . '/';
}
}
if (empty($filename)) {
if (is_dir('styles/' . $dbase . $sbase . $obase)) {
$path = 'styles/' . $dbase . $sbase . $obase;
} elseif (is_dir('styles/' . $dbase . $sbase)) {
$path = 'styles/' . $dbase . $sbase; // try "parent" style dir if no option one
} elseif (is_dir('styles/' . $sbase . $obase)) {
$path = 'styles/' . $sbase . $obase; // try root style dir if no domain one
} else {
$path = 'styles/' . $sbase; // fall back to "parent" style dir if no option one
}
} else {
if (is_file('styles/' . $dbase . $sbase . $obase . $filename)) {
$path = 'styles/' . $dbase . $sbase . $obase . $filename;
} elseif (is_file('styles/' . $dbase . $sbase . $filename)) { // try "parent" style dir if no option one
$path = 'styles/' . $dbase . $sbase . $filename;
} elseif (is_file('styles/' . $sbase . $obase . $filename)) { // try non-tikidomain dirs if not found
$path = 'styles/' . $sbase . $obase . $filename;
} elseif (is_file('styles/' . $sbase . $filename)) {
$path = 'styles/' . $sbase . $filename; // fall back to "parent" style dir if no option
} elseif (is_file('styles/' . $dbase . $filename)) {
$path = 'styles/' . $dbase . $filename; // tikidomain root style dir?
} elseif (is_file('styles/' . $dbase . $filename)) {
$path = 'styles/' . $filename; // root style dir?
}
}
return $path;
}
/**
* @param bool $user
* @return null
*/
public function get_language($user = false)
{
global $prefs;
static $language = false;
if (! $language) {
if ($user) {
$language = $this->get_user_preference($user, 'language', 'default');
if (! $language || $language == 'default') {
$language = $prefs['language'];
}
} else {
$language = $prefs['language'];
}
}
return $language;
}
/**
* @param $text
* @return string
*/
public function read_raw($text, $preserve = false)
{
$file = explode("\n", $text);
$back = [];
// When the fieldID is not preserved, ensure uniqueness of the $var key even if the fieldID is duplicated in the input
$i = 0;
foreach ($file as $line) {
$r = $s = '';
if (substr($line, 0, 1) != "#") {
if (preg_match("/^\[([A-Z0-9]+)\]/", $line, $r)) {
if ($preserve) {
$var = strtolower($r[1]);
} else {
$i++;
$var = 'id' . $i . strtolower($r[1]);
}
}
if (isset($var) and (preg_match("/^([-_\/ a-zA-Z0-9]+)[ \t]+[:=][ \t]+(.*)/", $line, $s))) {
$back[$var][trim($s[1])] = trim($s[2]);
}
}
}
return $back;
}
/**
* Get URL Scheme (http / https)
* Considers the use of a reverse proxy / ssl offloader. I.e If request is https -> ssl offloader -> http tiki, then it will correctly return https
* @return string http | https
*/
public static function httpScheme()
{
global $url_scheme;
return $url_scheme;
}
/**
* @param bool $isUserSpecific
* @return string
*/
public static function httpPrefix($isUserSpecific = false)
{
global $url_scheme, $url_host, $url_port, $prefs;
if ($isUserSpecific && $prefs['https_login'] != 'disabled' && $prefs['https_external_links_for_users'] == 'y') {
$scheme = 'https';
} else {
$scheme = $url_scheme;
}
return $scheme . '://' . $url_host . (($url_port != '') ? ":$url_port" : '');
}
/**
* Includes the full tiki path in the links for external link generation.
* @param string $relative
* @param array $args
* @return string
*/
public static function tikiUrl($relative = "", $args = [])
{
global $tikiroot;
if (preg_match('/^http(s?):/', $relative)) {
$base = $relative;
} else {
$base = self::httpPrefix() . $tikiroot . $relative;
}
if (count($args)) {
$base .= '?';
$base .= http_build_query($args, '', '&');
}
return $base;
}
/**
* Include the full tiki path if requested in an external context.
* Otherwise, leave as-is.
*
* @param string $relative
* @param array $args
* @return string
*/
public static function tikiUrlOpt($relative)
{
if (self::$isExternalContext) {
return self::tikiUrl($relative);
} else {
return $relative;
}
}
public static function setExternalContext($isExternal)
{
$oldValue = self::$isExternalContext;
self::$isExternalContext = (bool) $isExternal;
return $oldValue;
}
public static function contextualizeKey($key, $param1 = null, $param2 = null)
{
global $prefs;
$args = func_get_args();
array_shift($args);
foreach ($args as $arg) {
if ($arg == 'language') {
$language = isset($prefs['language']) ? $prefs['language'] : 'en';
$key .= "_{$language}";
} elseif ($arg == 'external') {
$key .= (int) self::$isExternalContext;
}
}
return $key;
}
/**
* Removes the protocol, host and path from a URL if they match
*
* @param string $url URL to be converted
* @return string relative URL if possible
*/
public static function makeAbsoluteLinkRelative($url)
{
global $base_url;
if (strpos($url, $base_url) !== false) {
$out = substr($url, strlen($base_url));
} else {
$out = $url;
}
return $out;
}
/**
* @param $lat1
* @param $lon1
* @param $lat2
* @param $lon2
* @return int
*/
public function distance($lat1, $lon1, $lat2, $lon2)
{
// This function uses a pure spherical model
// it could be improved to use the WGS84 Datum
// Franck Martin
$lat1rad = deg2rad($lat1);
$lon1rad = deg2rad($lon1);
$lat2rad = deg2rad($lat2);
$lon2rad = deg2rad($lon2);
$distance = 6367 * acos(sin($lat1rad) * sin($lat2rad) + cos($lat1rad) * cos($lat2rad) * cos($lon1rad - $lon2rad));
return($distance);
}
/**
* returns a list of usergroups where the user is a member and the group has the right perm
* sir-b
**/
public function get_groups_to_user_with_permissions($user, $perm)
{
$userid = $this->get_user_id($user);
$query = "SELECT DISTINCT `users_usergroups`.`groupName` AS `groupName`";
$query .= "FROM `users_grouppermissions`, `users_usergroups` ";
$query .= "WHERE `users_usergroups`.`userId` = ? AND ";
$query .= "`users_grouppermissions`.`groupName` = `users_usergroups`.`groupName` AND ";
$query .= "`users_grouppermissions`.`permName` = ? ";
$query .= "ORDER BY `groupName`";
return $this->fetchAll($query, [(int)$userid, $perm]);
}
/**
* @param $tab
* @param $valField1
* @param $field1
* @param $field2
* @return mixed
*/
public function other_value_in_tab_line($tab, $valField1, $field1, $field2)
{
foreach ($tab as $line) {
if ($line[$field1] == $valField1) {
return $line[$field2];
}
}
}
/**
* @param $file_name
* @return string
*/
public function get_attach_hash_file_name($file_name)
{
global $prefs;
do {
$fhash = md5($file_name . date('U') . rand());
} while (file_exists($prefs['w_use_dir'] . $fhash));
return $fhash;
}
/**
* @param $file_name
* @param $file_tmp_name
* @param $store_type
* @return array
*/
public function attach_file($file_name, $file_tmp_name, $store_type)
{
global $prefs;
$tmp_dest = $prefs['tmpDir'] . "/" . $file_name . ".tmp";
if (! is_writable(dirname($tmp_dest))) {
return ["ok" => false, "error" => tra('Temporary directory destination not writable: '.dirname($tmp_dest))];
}
if (! move_uploaded_file($file_tmp_name, $tmp_dest)) {
return ["ok" => false, "error" => tra('Unable to move uploaded file to temporary destination.')];
}
try {
$filegallib = TikiLib::lib('filegal');
$filegallib->assertUploadedFileIsSafe($tmp_dest, $file_name);
} catch (Exception $e) {
return ['ok' => false, 'error' => $e->getMessage()];
}
$fp = fopen($tmp_dest, "rb");
$data = '';
$fhash = '';
$chunk = '';
if ($store_type == 'dir') {
$fhash = $this->get_attach_hash_file_name($file_name);
$fw = fopen($prefs['w_use_dir'] . $fhash, "wb");
if (! $fw) {
return ["ok" => false, "error" => tra('Cannot write to this file:') . $prefs['w_use_dir'] . $fhash];
}
}
while (! feof($fp)) {
$chunk = fread($fp, 8192 * 16);
if ($store_type == 'dir') {
fwrite($fw, $chunk);
}
$data .= $chunk;
}
fclose($fp);
unlink($tmp_dest);
if ($store_type == 'dir') {
fclose($fw);
$data = "";
}
return ["ok" => true, "data" => $data, "fhash" => $fhash];
}
/* to get the length of a data without the quoted part (very
approximative) */
/**
* @param $data
* @return int
*/
public function strlen_quoted($data)
{
global $prefs;
if ($prefs['feature_use_quoteplugin'] != 'y') {
$data = preg_replace('/^>.*\\n?/m', '', $data);
} else {
$data = preg_replace('/{QUOTE\([^\)]*\)}.*{QUOTE}/Ui', '', $data);
}
return strlen($data);
}
/**
* @param $id
* @param int $offset
* @param $maxRecords
* @param string $sort_mode
* @param string $find
* @param string $table
* @param string $column
* @param string $from
* @param string $to
* @return array
*/
public function list_votes($id, $offset = 0, $maxRecords = -1, $sort_mode = 'user_asc', $find = '', $table = '', $column = '', $from = '', $to = '')
{
$mid = 'where `id`=?';
$bindvars[] = $id;
$select = '';
$join = '';
if (! empty($find)) {
$mid .= ' and (`user` like ? or `title` like ? or `ip` like ?)';
$bindvars[] = '%' . $find . '%';
$bindvars[] = '%' . $find . '%';
$bindvars[] = '%' . $find . '%';
}
if (! empty($from) && ! empty($to)) {
$mid .= ' and ((time >= ? and time <= ?) or time = ?)';
$bindvars[] = $from;
$bindvars[] = $to;
$bindvars[] = 0;
}
if (! empty($table) && ! empty($column)) {
$select = ", `$table`.`$column` as title";
$join = "left join `$table` on (`tiki_user_votings`.`optionId` = `$table`.`optionId`)";
}
$query = "select * $select from `tiki_user_votings` $join $mid order by " . $this->convertSortMode($sort_mode);
$query_cant = "select count(*) from `tiki_user_votings` $join $mid";
$ret = $this->fetchAll($query, $bindvars, $maxRecords, $offset);
$cant = $this->getOne($query_cant, $bindvars);
$retval = [];
$retval["data"] = $ret;
$retval["cant"] = $cant;
return $retval;
}
/**
* Returns explicit message on upload problem
*
* @params: $iError: php status of the file uploading (documented in http://uk2.php.net/manual/en/features.file-upload.errors.php )
*
*/
public function uploaded_file_error($iError)
{
switch ($iError) {
case UPLOAD_ERR_OK:
return tra('The file was successfully uploaded.');
case UPLOAD_ERR_INI_SIZE:
return tra('The uploaded file exceeds the upload_max_filesize directive in php.ini.');
case UPLOAD_ERR_FORM_SIZE:
return tra('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.');
case UPLOAD_ERR_PARTIAL:
return tra('The file was only partially uploaded.');
case UPLOAD_ERR_NO_FILE:
return tra('No file was uploaded. Was a file selected ?');
case UPLOAD_ERR_NO_TMP_DIR:
return tra('A temporary folder is missing.');
case UPLOAD_ERR_CANT_WRITE:
return tra('Failed to write file to disk.');
case UPLOAD_ERR_EXTENSION:
return tra('File upload stopped by extension.');
default:
return tra('Unknown error.');
}
}
// from PHP manual (ini-get function example)
/**
* @param string $val php.ini key returning memory string i.e. 32M
* @return int size in bytes
*/
public function return_bytes($val)
{
$val = trim($val);
$bytes = (int) $val;
$lastCharacter = strtolower($val[strlen($val) - 1]);
$units = ['k' => 1, 'm' => 2, 'g' => 3];
if (array_key_exists($lastCharacter, $units)) {
$bytes = $bytes * (1024 ** $units[$lastCharacter]);
}
return $bytes;
}
/**
* @return int bytes of memory available for PHP
*/
public function get_memory_avail()
{
return $this->get_memory_limit() - memory_get_usage(true);
}
/**
* @return int
*/
public function get_memory_limit()
{
return $this->return_bytes(ini_get('memory_limit'));
}
/**
* @param bool $with_names
* @param bool $translate
* @param bool $sort_names
* @return array|mixed
*/
public function get_flags($with_names = false, $translate = false, $sort_names = false, $langsort = false)
{
global $prefs;
$cachelib = TikiLib::lib('cache');
$args = func_get_args();
$cacheKey = serialize($args) . $prefs['language'];
if ($data = $cachelib->getSerialized($cacheKey, 'flags')) {
return $data;
}
$flags = [];
$h = opendir("img/flags/");
while ($file = readdir($h)) {
if (strstr($file, ".png")) {
$parts = explode('.', $file);
$flags[] = $parts[0];
}
}
closedir($h);
if ($langsort) {
foreach ($flags as $flagname => $flagtra) {
unset($flags[$flagname]);
$flags[$flagtra] = tra($flagtra);
}
}
natcasesort($flags);
if ($with_names) {
$ret = [];
$names = [];
foreach ($flags as $f) {
$ret[$f] = strtr($f, '_', ' ');
if ($translate) {
$ret[$f] = tra($ret[$f]);
}
if ($sort_names) {
$names[$f] = strtolower($this->take_away_accent($ret[$f]));
}
}
if ($sort_names) {
array_multisort($names, $ret);
}
$flags = $ret;
}
$cachelib->cacheItem($cacheKey, serialize($flags), 'flags');
return $flags;
}
/**
* @param {string} $data
* @return string
*/
public function strip_tags($data)
{
$result = preg_replace('/[<]style[^>]*?[>](.|\n|\r)*?[<][\/]style[>]/', '', $data);
$result = strip_tags($result);
return $result;
}
/**
* @param $data
* @param string $outputType
* @param boolean $is_html
* @param string $highlight
* @param int $length
* @param string $start
* @param string $end
* @return string
*/
public function get_snippet($data, $outputType = '', $is_html = false, $highlight = '', $length = 240, $start = '', $end = '')
{
global $prefs;
if ($prefs['search_parsed_snippet'] == 'y') {
$data = preg_replace('/{(:?make)?toc[^}]*}/', '', $data);
$_REQUEST['redirectpage'] = 'y'; //do not interpret redirect
$data = TikiLib::lib('parser')->parse_data($data, ['is_html' => $is_html, 'stripplugins' => true, 'parsetoc' => true]);
}
$data = strip_tags($data);
if ($length > 0) {
if (function_exists('mb_substr')) {
return mb_substr($data, 0, $length);
} else {
return substr($data, 0, $length);
}
}
if (! empty($start) && ($i = strpos($data, $start))) {
$data = substr($data, $i + strlen($start));
}
if (! empty($end) && ($i = strpos($data, $end))) {
$data = substr($data, 0, $i);
}
return $data;
}
/**
* @param $string
* @param int $quote_style
* @param int $translation_table
* @return string
*/
public static function htmldecode($string, $quote_style = ENT_COMPAT, $translation_table = HTML_ENTITIES)
{
if ($translation_table == HTML_ENTITIES) {
$string = html_entity_decode($string, $quote_style, 'utf-8');
} elseif ($translation_table === HTML_SPECIALCHARS) {
$string = htmlspecialchars_decode($string, $quote_style);
}
return $string;
}
/**
* * Unaccent the input string string. An example string like `ÀØėÿᾜὨζὅБю`
* will be translated to `AOeyIOzoBY`
* @param $str
* @return string unaccented string
*/
public static function take_away_accent($str)
{
$transliteration = array(
'IJ' => 'I', 'Ö' => 'O','Œ' => 'O','Ü' => 'U','ä' => 'a','æ' => 'a',
'ij' => 'i','ö' => 'o','œ' => 'o','ü' => 'u','ß' => 's','ſ' => 's',
'À' => 'A','Á' => 'A','Â' => 'A','Ã' => 'A','Ä' => 'A','Å' => 'A',
'Æ' => 'A','Ā' => 'A','Ą' => 'A','Ă' => 'A','Ç' => 'C','Ć' => 'C',
'Č' => 'C','Ĉ' => 'C','Ċ' => 'C','Ď' => 'D','Đ' => 'D','È' => 'E',
'É' => 'E','Ê' => 'E','Ë' => 'E','Ē' => 'E','Ę' => 'E','Ě' => 'E',
'Ĕ' => 'E','Ė' => 'E','Ĝ' => 'G','Ğ' => 'G','Ġ' => 'G','Ģ' => 'G',
'Ĥ' => 'H','Ħ' => 'H','Ì' => 'I','Í' => 'I','Î' => 'I','Ï' => 'I',
'Ī' => 'I','Ĩ' => 'I','Ĭ' => 'I','Į' => 'I','İ' => 'I','Ĵ' => 'J',
'Ķ' => 'K','Ľ' => 'K','Ĺ' => 'K','Ļ' => 'K','Ŀ' => 'K','Ł' => 'L',
'Ñ' => 'N','Ń' => 'N','Ň' => 'N','Ņ' => 'N','Ŋ' => 'N','Ò' => 'O',
'Ó' => 'O','Ô' => 'O','Õ' => 'O','Ø' => 'O','Ō' => 'O','Ő' => 'O',
'Ŏ' => 'O','Ŕ' => 'R','Ř' => 'R','Ŗ' => 'R','Ś' => 'S','Ş' => 'S',
'Ŝ' => 'S','Ș' => 'S','Š' => 'S','Ť' => 'T','Ţ' => 'T','Ŧ' => 'T',
'Ț' => 'T','Ù' => 'U','Ú' => 'U','Û' => 'U','Ū' => 'U','Ů' => 'U',
'Ű' => 'U','Ŭ' => 'U','Ũ' => 'U','Ų' => 'U','Ŵ' => 'W','Ŷ' => 'Y',
'Ÿ' => 'Y','Ý' => 'Y','Ź' => 'Z','Ż' => 'Z','Ž' => 'Z','à' => 'a',
'á' => 'a','â' => 'a','ã' => 'a','ā' => 'a','ą' => 'a','ă' => 'a',
'å' => 'a','ç' => 'c','ć' => 'c','č' => 'c','ĉ' => 'c','ċ' => 'c',
'ď' => 'd','đ' => 'd','è' => 'e','é' => 'e','ê' => 'e','ë' => 'e',
'ē' => 'e','ę' => 'e','ě' => 'e','ĕ' => 'e','ė' => 'e','ƒ' => 'f',
'ĝ' => 'g','ğ' => 'g','ġ' => 'g','ģ' => 'g','ĥ' => 'h','ħ' => 'h',
'ì' => 'i','í' => 'i','î' => 'i','ï' => 'i','ī' => 'i','ĩ' => 'i',
'ĭ' => 'i','į' => 'i','ı' => 'i','ĵ' => 'j','ķ' => 'k','ĸ' => 'k',
'ł' => 'l','ľ' => 'l','ĺ' => 'l','ļ' => 'l','ŀ' => 'l','ñ' => 'n',
'ń' => 'n','ň' => 'n','ņ' => 'n','ʼn' => 'n','ŋ' => 'n','ò' => 'o',
'ó' => 'o','ô' => 'o','õ' => 'o','ø' => 'o','ō' => 'o','ő' => 'o',
'ŏ' => 'o','ŕ' => 'r','ř' => 'r','ŗ' => 'r','ś' => 's','š' => 's',
'ť' => 't','ù' => 'u','ú' => 'u','û' => 'u','ū' => 'u','ů' => 'u',
'ű' => 'u','ŭ' => 'u','ũ' => 'u','ų' => 'u','ŵ' => 'w','ÿ' => 'y',
'ý' => 'y','ŷ' => 'y','ż' => 'z','ź' => 'z','ž' => 'z','Α' => 'A',
'Ά' => 'A','Ἀ' => 'A','Ἁ' => 'A','Ἂ' => 'A','Ἃ' => 'A','Ἄ' => 'A',
'Ἅ' => 'A','Ἆ' => 'A','Ἇ' => 'A','ᾈ' => 'A','ᾉ' => 'A','ᾊ' => 'A',
'ᾋ' => 'A','ᾌ' => 'A','ᾍ' => 'A','ᾎ' => 'A','ᾏ' => 'A','Ᾰ' => 'A',
'Ᾱ' => 'A','Ὰ' => 'A','ᾼ' => 'A','Β' => 'B','Γ' => 'G','Δ' => 'D',
'Ε' => 'E','Έ' => 'E','Ἐ' => 'E','Ἑ' => 'E','Ἒ' => 'E','Ἓ' => 'E',
'Ἔ' => 'E','Ἕ' => 'E','Ὲ' => 'E','Ζ' => 'Z','Η' => 'I','Ή' => 'I',
'Ἠ' => 'I','Ἡ' => 'I','Ἢ' => 'I','Ἣ' => 'I','Ἤ' => 'I','Ἥ' => 'I',
'Ἦ' => 'I','Ἧ' => 'I','ᾘ' => 'I','ᾙ' => 'I','ᾚ' => 'I','ᾛ' => 'I',
'ᾜ' => 'I','ᾝ' => 'I','ᾞ' => 'I','ᾟ' => 'I','Ὴ' => 'I','ῌ' => 'I',
'Θ' => 'T','Ι' => 'I','Ί' => 'I','Ϊ' => 'I','Ἰ' => 'I','Ἱ' => 'I',
'Ἲ' => 'I','Ἳ' => 'I','Ἴ' => 'I','Ἵ' => 'I','Ἶ' => 'I','Ἷ' => 'I',
'Ῐ' => 'I','Ῑ' => 'I','Ὶ' => 'I','Κ' => 'K','Λ' => 'L','Μ' => 'M',
'Ν' => 'N','Ξ' => 'K','Ο' => 'O','Ό' => 'O','Ὀ' => 'O','Ὁ' => 'O',
'Ὂ' => 'O','Ὃ' => 'O','Ὄ' => 'O','Ὅ' => 'O','Ὸ' => 'O','Π' => 'P',
'Ρ' => 'R','Ῥ' => 'R','Σ' => 'S','Τ' => 'T','Υ' => 'Y','Ύ' => 'Y',
'Ϋ' => 'Y','Ὑ' => 'Y','Ὓ' => 'Y','Ὕ' => 'Y','Ὗ' => 'Y','Ῠ' => 'Y',
'Ῡ' => 'Y','Ὺ' => 'Y','Φ' => 'F','Χ' => 'X','Ψ' => 'P','Ω' => 'O',
'Ώ' => 'O','Ὠ' => 'O','Ὡ' => 'O','Ὢ' => 'O','Ὣ' => 'O','Ὤ' => 'O',
'Ὥ' => 'O','Ὦ' => 'O','Ὧ' => 'O','ᾨ' => 'O','ᾩ' => 'O','ᾪ' => 'O',
'ᾫ' => 'O','ᾬ' => 'O','ᾭ' => 'O','ᾮ' => 'O','ᾯ' => 'O','Ὼ' => 'O',
'ῼ' => 'O','α' => 'a','ά' => 'a','ἀ' => 'a','ἁ' => 'a','ἂ' => 'a',
'ἃ' => 'a','ἄ' => 'a','ἅ' => 'a','ἆ' => 'a','ἇ' => 'a','ᾀ' => 'a',
'ᾁ' => 'a','ᾂ' => 'a','ᾃ' => 'a','ᾄ' => 'a','ᾅ' => 'a','ᾆ' => 'a',
'ᾇ' => 'a','ὰ' => 'a','ᾰ' => 'a','ᾱ' => 'a','ᾲ' => 'a','ᾳ' => 'a',
'ᾴ' => 'a','ᾶ' => 'a','ᾷ' => 'a','β' => 'b','γ' => 'g','δ' => 'd',
'ε' => 'e','έ' => 'e','ἐ' => 'e','ἑ' => 'e','ἒ' => 'e','ἓ' => 'e',
'ἔ' => 'e','ἕ' => 'e','ὲ' => 'e','ζ' => 'z','η' => 'i','ή' => 'i',
'ἠ' => 'i','ἡ' => 'i','ἢ' => 'i','ἣ' => 'i','ἤ' => 'i','ἥ' => 'i',
'ἦ' => 'i','ἧ' => 'i','ᾐ' => 'i','ᾑ' => 'i','ᾒ' => 'i','ᾓ' => 'i',
'ᾔ' => 'i','ᾕ' => 'i','ᾖ' => 'i','ᾗ' => 'i','ὴ' => 'i','ῂ' => 'i',
'ῃ' => 'i','ῄ' => 'i','ῆ' => 'i','ῇ' => 'i','θ' => 't','ι' => 'i',
'ί' => 'i','ϊ' => 'i','ΐ' => 'i','ἰ' => 'i','ἱ' => 'i','ἲ' => 'i',
'ἳ' => 'i','ἴ' => 'i','ἵ' => 'i','ἶ' => 'i','ἷ' => 'i','ὶ' => 'i',
'ῐ' => 'i','ῑ' => 'i','ῒ' => 'i','ῖ' => 'i','ῗ' => 'i','κ' => 'k',
'λ' => 'l','μ' => 'm','ν' => 'n','ξ' => 'k','ο' => 'o','ό' => 'o',
'ὀ' => 'o','ὁ' => 'o','ὂ' => 'o','ὃ' => 'o','ὄ' => 'o','ὅ' => 'o',
'ὸ' => 'o','π' => 'p','ρ' => 'r','ῤ' => 'r','ῥ' => 'r','σ' => 's',
'ς' => 's','τ' => 't','υ' => 'y','ύ' => 'y','ϋ' => 'y','ΰ' => 'y',
'ὐ' => 'y','ὑ' => 'y','ὒ' => 'y','ὓ' => 'y','ὔ' => 'y','ὕ' => 'y',
'ὖ' => 'y','ὗ' => 'y','ὺ' => 'y','ῠ' => 'y','ῡ' => 'y','ῢ' => 'y',
'ῦ' => 'y','ῧ' => 'y','φ' => 'f','χ' => 'x','ψ' => 'p','ω' => 'o',
'ώ' => 'o','ὠ' => 'o','ὡ' => 'o','ὢ' => 'o','ὣ' => 'o','ὤ' => 'o',
'ὥ' => 'o','ὦ' => 'o','ὧ' => 'o','ᾠ' => 'o','ᾡ' => 'o','ᾢ' => 'o',
'ᾣ' => 'o','ᾤ' => 'o','ᾥ' => 'o','ᾦ' => 'o','ᾧ' => 'o','ὼ' => 'o',
'ῲ' => 'o','ῳ' => 'o','ῴ' => 'o','ῶ' => 'o','ῷ' => 'o','А' => 'A',
'Б' => 'B','В' => 'V','Г' => 'G','Д' => 'D','Е' => 'E','Ё' => 'E',
'Ж' => 'Z','З' => 'Z','И' => 'I','Й' => 'I','К' => 'K','Л' => 'L',
'М' => 'M','Н' => 'N','О' => 'O','П' => 'P','Р' => 'R','С' => 'S',
'Т' => 'T','У' => 'U','Ф' => 'F','Х' => 'K','Ц' => 'T','Ч' => 'C',
'Ш' => 'S','Щ' => 'S','Ы' => 'Y','Э' => 'E','Ю' => 'Y','Я' => 'Y',
'а' => 'A','б' => 'B','в' => 'V','г' => 'G','д' => 'D','е' => 'E',
'ё' => 'E','ж' => 'Z','з' => 'Z','и' => 'I','й' => 'I','к' => 'K',
'л' => 'L','м' => 'M','н' => 'N','о' => 'O','п' => 'P','р' => 'R',
'с' => 'S','т' => 'T','у' => 'U','ф' => 'F','х' => 'K','ц' => 'T',
'ч' => 'C','ш' => 'S','щ' => 'S','ы' => 'Y','э' => 'E','ю' => 'Y',
'я' => 'Y','ð' => 'd','Ð' => 'D','þ' => 't','Þ' => 'T','ა' => 'a',
'ბ' => 'b','გ' => 'g','დ' => 'd','ე' => 'e','ვ' => 'v','ზ' => 'z',
'თ' => 't','ი' => 'i','კ' => 'k','ლ' => 'l','მ' => 'm','ნ' => 'n',
'ო' => 'o','პ' => 'p','ჟ' => 'z','რ' => 'r','ს' => 's','ტ' => 't',
'უ' => 'u','ფ' => 'p','ქ' => 'k','ღ' => 'g','ყ' => 'q','შ' => 's',
'ჩ' => 'c','ც' => 't','ძ' => 'd','წ' => 't','ჭ' => 'c','ხ' => 'k',
'ჯ' => 'j','ჰ' => 'h'
);
$str = str_replace(array_keys($transliteration), array_values($transliteration), $str);
return $str;
}
/**
* @param $str
* @return mixed
*/
public static function substituteSeparators($str)
{
$subst = explode(' ', '+ \' : ;');
$convs = explode(' ', '_ _ _ _');
$ret = str_replace($subst, $convs, $str);
$ret = str_replace(' ', '_', $ret);
return $ret;
}
/**
* @param $str
* @return mixed
*/
public function urlencode_accent($str)
{
$convs = [];
preg_match_all('/[\x80-\xFF| ]/', $str, $matches);
$accents = $matches[0];
foreach ($accents as $a) {
$convs[] = rawurlencode($a);
}
return str_replace($accents, $convs, $str);
}
/**
* Remove all "non-word" characters and accents from a string
* Can be used for DOM elements and preferences etc
*
* @static
* @param string $str
* @return string cleaned
*/
public static function remove_non_word_characters_and_accents($str)
{
return preg_replace('/\W+/', '_', TikiLib::take_away_accent($str));
}
/* return the positions in data where the hdr-nth header is find
*/
/**
* @param $data
* @param $hdr
* @return array
*/
public function get_wiki_section($data, $hdr)
{
$start = 0;
$end = strlen($data);
$lines = explode("\n", $data);
$header = 0;
$pp_level = 0;
$np_level = 0;
for ($i = 0, $count_lines = count($lines); $i < $count_lines; ++$i) {
$pp_level += preg_match('/~pp~/', $lines[$i]);
$pp_level -= preg_match('/~\/pp~/', $lines[$i]);
$np_level += preg_match('/~np~/', $lines[$i]);
$np_level -= preg_match('/~\/np~/', $lines[$i]);
// We test if we are inside nonparsed or pre section to ignore !*
if ($pp_level % 2 == 0 and $np_level % 2 == 0) {
if (substr($lines[$i], 0, 1) == '!') {
++$header;
if ($header == $hdr) { // we are on it - now find the next header at same or lower level
$level = $this->how_many_at_start($lines[$i], '!');
$end = strlen($lines[$i]) + 1;
for (++$i; $i < $count_lines; ++$i) {
if (substr($lines[$i], 0, 1) == '!' && $level >= $this->how_many_at_start($lines[$i], '!')) {
return ([$start, $end]);
}
$end += strlen($lines[$i]) + 1;
}
break;
}
}
}
$start += strlen($lines[$i]) + 1;
}
return ([$start, $end]);
}
/**
* \brief Function to embed a flash object (using JS method by default when JS in user's browser is detected)
*
* So far it's being called from wikiplugin_flash.php and tiki-edit_banner.php
*
* @param javascript = y or n to force to generate a version with javascript or not, ='' user prefs
*/
public function embed_flash($params, $javascript = '', $flashvars = false)
{
global $prefs;
$headerlib = TikiLib::lib('header');
if (! isset($params['movie'])) {
return false;
}
$defaults = [
'width' => 425,
'height' => 350,
'quality' => 'high',
'version' => '9.0.0',
'wmode' => 'transparent',
];
$params = array_merge($defaults, $params);
if (preg_match('/^(\/|https?:)/', $params['movie'])) {
$params['allowscriptaccess'] = 'always';
}
if (((empty($javascript) && $prefs['javascript_enabled'] == 'y') || $javascript == 'y')) {
$myId = (! empty($params['id'])) ? ($params['id']) : 'wp-flash-' . uniqid();
$movie = '"' . $params['movie'] . '"';
$div = json_encode($myId);
$width = (int) $params['width'];
$height = (int) $params['height'];
$version = json_encode($params['version']);
if (! empty($params['altimg'])) {
$alt = '<img src="' . $params['altimg'] . '" width="' . $width . '" height="' . $height . '" alt=\"\" />';
} else {
$alt = ''; // Must be blank otherwise for a split second before Flash loads you can see any text that is set
}
unset($params['movie'], $params['width'], $params['height'], $params['version'], $params['altimg']);
$params = json_encode($params);
if (! $flashvars) {
$flashvars = '{}';
} else {
$flashvars = json_encode($flashvars);
$flashvars = str_replace('\\/', '/', $flashvars);
}
$js = <<<JS
swfobject.embedSWF( $movie, $div, $width, $height, $version, 'vendor_bundled/vendor/bower-asset/swfobject/swfobject/expressInstall.swf', $flashvars, $params, {} );
JS;
$headerlib->add_js($js);
return "<div id=\"$myId\">" . $alt . "</div>";
} else { // link on the movie will not work with IE6
$asetup = "<object classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" codebase=\"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0\" width=\"{$params['width']}\" height=\"{$params['height']}\">";
$asetup .= "<param name=\"movie\" value=\"{$params['movie']}\" />";
$asetup .= "<param name=\"quality\" value=\"{$params['quality']}\" />";
$asetup .= "<param name=\"wmode\" value=\"transparent\" />";
if (! empty($params['allowscriptaccess'])) {
$asetup .= "<param name=\"allowscriptaccess\" value=\"always\" />";
}
if (! empty($params['allowFullScreen'])) {
$asetup .= '<param name="allowFullScreen" value="' . $params['allowFullScreen'] . '"></param>';
}
if (! empty($params['altimg'])) {
$asetup .= '<img src="' . $params['altimg'] . '" width="' . $params['width'] . '" height="' . $params['height'] . '" alt=\"\" />';
}
$asetup .= "<embed src=\"{$params['movie']}\" quality=\"{$params['quality']}\" pluginspage=\"http://www.macromedia.com/go/getflashplayer\"" .
" type=\"application/x-shockwave-flash\" width=\"{$params['width']}\" height=\"{$params['height']}\" wmode=\"transparent\"></embed></object>";
return $asetup;
}
}
/**
* @param bool $descendants The default is to get all descendents of the jailed categories, but for unified search
* we only need the "root" jailed categories as the search does a deep_categories search on them
* @return array
*/
public function get_jail($descendants = true)
{
global $prefs;
// if jail is zero, we should allow non-categorized objects to be seen as well, i.e. consider as no jail
if (
! empty($prefs['feature_categories']) && $prefs['feature_categories'] == 'y' &&
! empty($prefs['category_jail']) && $prefs['category_jail'] != [0 => 0]
) {
$expanded = [];
if ($descendants) {
$categlib = TikiLib::lib('categ');
foreach ($prefs['category_jail'] as $categId) {
$expanded = array_merge($expanded, $categlib->get_category_descendants($categId));
}
} else {
$expanded = $prefs['category_jail'];
}
return $expanded;
} else {
return [];
}
}
/**
* @param $type
* @param $old
* @param $new
*/
protected function rename_object($type, $old, $new, $user = '')
{
global $prefs;
// comments
$this->table('tiki_comments')->updateMultiple(['object' => $new], ['object' => $old, 'objectType' => $type]);
// Move email notifications
$oldId = str_replace($type, ' ', '') . $old;
$newId = str_replace($type, ' ', '') . $new;
$this->table('tiki_user_watches')->updateMultiple(['object' => $newId], ['object' => $oldId]);
$this->table('tiki_group_watches')->updateMultiple(['object' => $newId], ['object' => $oldId]);
// theme_control_objects(objId,name)
$oldId = md5($type . $old);
$newId = md5($type . $new);
$this->table('tiki_theme_control_objects')->updateMultiple(['objId' => $newId, 'name' => $new], ['objId' => $oldId]);
// polls
if ($prefs['feature_polls'] == 'y') {
$query = "update `tiki_polls` tp inner join `tiki_poll_objects` tpo on tp.`pollId` = tpo.`pollId` inner join `tiki_objects` tob on tpo.`catObjectId` = tob.`objectId` set tp.`title`=? where tp.`title`=? and tob.`type` = ?";
$this->query($query, [ $new, $old, $type ]);
}
// Move custom permissions
$oldId = md5($type . TikiLib::strtolower($old));
$newId = md5($type . TikiLib::strtolower($new));
$this->table('users_objectpermissions')->updateMultiple(['objectId' => $newId], ['objectId' => $oldId, 'objectType' => $type]);
// Logs
if ($prefs['feature_actionlog'] == 'y') {
$logslib = TikiLib::lib('logs');
$logslib->add_action('Renamed', $new, 'wiki page', 'old=' . $old . '&new=' . $new, $user, '', '', '', '', [['rename' => $old]]);
$logslib->rename($type, $old, $new);
}
// Attributes
$this->table('tiki_object_attributes')->updateMultiple(['itemId' => $new], ['itemId' => $old, 'type' => $type]);
$this->table('tiki_object_relations')->updateMultiple(['source_itemId' => $new], ['source_itemId' => $old, 'source_type' => $type]);
$this->table('tiki_object_relations')->updateMultiple(['target_itemId' => $new], ['target_itemId' => $old, 'target_type' => $type]);
$menulib = TikiLib::lib('menu');
$menulib->rename_wiki_page($old, $new);
}
/**
* @param $delimiters
* @param $string
* @return array
*/
public function multi_explode($delimiters, $string)
{
global $prefs;
if (is_array($delimiters) == false) {
$delimiters = [$delimiters];
}
$delimiter = array_shift($delimiters);
$temp = explode($delimiter, $string);
$array = [];
$keep = false;
$ignore_chars = array_unique(str_split($prefs['namespace_separator']));
foreach ($temp as $v) {
$filtered = str_replace($ignore_chars, '', $v);
if ($filtered == '' && $v != '') {
if (! $keep) {
$array[count($array) - 1] .= $delimiter;
}
$array[count($array) - 1] .= $v . $delimiter;
$keep = true;
} elseif ($keep) {
$array[count($array) - 1] .= $v;
$keep = false;
} else {
$array[] = $v;
}
}
if ($delimiters != null) {
foreach ($array as $key => $val) {
$array[$key] = $this->multi_explode($delimiters, $val);
}
}
return $array;
}
/**
* @param $delimiters
* @param $string
* @return string
*/
public function multi_implode($delimiters, $array)
{
$delimiters = (array) $delimiters;
$delimiter = array_shift($delimiters);
if (count($delimiters)) {
$self = $this;
$array = array_map(
function ($value) use ($delimiters, $self) {
return $self->multi_implode($delimiters, $value);
},
$array
);
}
return implode($delimiter, $array);
}
/**
* @param $vals
* @param $filter
* @return string
*/
public function array_apply_filter($vals, $filter)
{
if (is_array($vals) == true) {
foreach ($vals as $key => $val) {
$vals[$key] = $this->array_apply_filter($val, $filter);
}
return $vals;
} else {
return trim($filter->filter($vals));
}
}
/**
* @param $type
* @param $object
* @param bool $process
* @return bool
*/
public function refresh_index($type, $object, $process = true)
{
require_once __DIR__ . '/search/refresh-functions.php';
return refresh_index($type, $object, $process);
}
/**
* Possibly enhanced version of strtolower(), using multi-byte if mbstring is available
*
* Since Tiki 17, mb_strtolower() can be used directly instead since Tiki indirectly depends on the symfony/polyfill-mbstring compatibility library.
*
* @param $string
* @return string
*/
public static function strtolower($string)
{
if (function_exists('mb_strtolower')) {
return mb_strtolower($string, 'UTF-8');
} else {
return strtolower($string);
}
}
/**
* Possibly enhanced version of strtoupper(), using multi-byte if mbstring is available
*
* Since Tiki 17, mb_strtoupper() can be used directly instead since Tiki indirectly depends on the symfony/polyfill-mbstring compatibility library.
*
* @param $string
* @return string
*/
public static function strtoupper($string)
{
if (function_exists('mb_strtoupper')) {
return mb_strtoupper($string, 'UTF-8');
} else {
return strtoupper($string);
}
}
/**
* @param $string
* @return string UTF-8
*/
public static function urldecode($string)
{
return TikiInit::to_utf8(urldecode($string));
}
/**
* @param $string
* @return string UTF-8
*/
public static function rawurldecode($string)
{
return TikiInit::to_utf8(rawurldecode($string));
}
/**
* Unparse an array of url parts, e.g. the result of parse_url()
* Thanks to http://php.net/manual/en/function.parse-url.php#106731
*
* @param $parsed_url
* @return string
*/
public static function unparse_url($parsed_url)
{
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '//';
$host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
$port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
$user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
$pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
$pass = ($user || $pass) ? "$pass@" : '';
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
$query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
/**
* Return the request URI.
* Assumes http or https is used. Non-standard ports are taken into account
* @return Full URL to the current page
* \static
*/
// Note: this is unused as of r37658, but quite generic.
public static function curPageURL()
{
$pageURL = 'http';
if (isset($_SERVER["HTTPS"]) && ($_SERVER["HTTPS"] == "on")) {
$pageURL .= 's';
}
$pageURL .= '://';
if ($_SERVER['SERVER_PORT'] != '80') {
$pageURL .= $_SERVER['SERVER_NAME'] . ":" . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'];
} else {
$pageURL .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
}
return $pageURL;
}
/**
* @param array $data
* @return array
*/
public static function array_flat(array $data)
{
$out = [];
foreach ($data as $entry) {
if (is_array($entry)) {
$out = array_merge($out, self::array_flat($entry));
} else {
$out[] = $entry;
}
}
return $out;
}
/**
* This checks the modifier array and scans the template directory for templates
* that match the modifiers.
* Example: if we are looking at modifier "blog" for the articles.tpl, this function
* looks for the existence of articles--blog.tpl to use before using the standard articles.tpl
*
* @param $basetpl
* @param $modifier_arr
* @return string
* @throws Exception
*/
public static function custom_template($basetpl, $modifier_arr)
{
//if it's an item passed and not an array, put the item in an array
if (! is_array($modifier_arr)) {
$modifier_arr = [$modifier_arr];
}
//strip the .tpl
$temp = explode('.', $basetpl);
$ext = array_pop($temp);
$base = implode('.', $temp);
$smarty = TikiLib::lib('smarty');
foreach ($modifier_arr as $modifier) {
if ($smarty->templateExists("$base--$modifier.tpl")) {
return "$base--$modifier.tpl";
}
}
return "$base.tpl";
}
/**
* @param $page
* @return mixed
*/
public function removePageReference($page)
{
$page_id = $this->get_page_id_from_name($page);
$query = "DELETE FROM `tiki_page_references` WHERE `page_id`=?";
$result = $this->query($query, [$page_id]);
return $result;
}
/**
* @param array $new_toolbars
* @param string $section
* @param string $action
*/
public function saveEditorToolbars($new_toolbars = [], $section = 'global', $action = 'add')
{
global $prefs;
$prefName = 'toolbar_' . $section;
$toolbars = explode(',', $prefs[$prefName]);
if ($action == 'add') {
foreach ($new_toolbars as $key => $value) {
if (! in_array($value, $toolbars)) {
$toolbars[] = $value;
}
}
} else {//remove the toolbars
$toolbars = array_diff($toolbars, $new_toolbars);
}
$toolbars = implode(',', $toolbars);
$this->set_preference($prefName, $toolbars);
}
/**
* @param $haystack
* @param $needle
* @return bool
*/
public static function startsWith($haystack, $needle)
{
$length = strlen($needle);
return (substr($haystack, 0, $length) === $needle);
}
/**
* @param $haystack
* @param $needle
* @return bool
*/
public static function endsWith($haystack, $needle)
{
$length = strlen($needle);
if ($length == 0) {
return true;
}
$start = $length * -1; //negative
return (substr($haystack, $start) === $needle);
}
/**
* Checks if all link aliases contained in a page are valid, it automatically flashes the error in case there are invalid aliases
* @param String $edit Contains page edit content
* @param String $page Page name
* @return bool returns false if there is at least one invalid alias
* @throws Exception
*/
public function check_duplicate_alias($edit, $page)
{
$errors = [];
$parserlib = TikiLib::lib('parser');
$table = $this->table('tiki_object_relations');
$smarty = TikiLib::lib('smarty');
$smarty->loadPlugin('smarty_modifier_sefurl');
foreach ($parserlib->get_pages($edit, true) as $pointedPage => $types) {
if (empty($types[0]) || $types[0] != 'alias') {
continue;
}
$conflictPages = $table->fetchColumn('source_itemId', [
'target_itemId' => $pointedPage,
'source_itemId' => $table->not($page),
'relation' => $table->like('%alias%')
]);
if (empty($conflictPages)) {
continue;
}
$url = [];
foreach ($conflictPages as $pageName) {
$url[] = sprintf('<a href="%s">%s</a>', smarty_modifier_sefurl($pageName, 'wiki'), $pageName);
}
$errors[] = tr('Alias <b>%0</b> link already present in %1 page(s)', $pointedPage, implode(', ', $url));
}
if (! empty($errors)) {
Feedback::error(implode('<br>', $errors));
}
return empty($errors);
}
/**
* @param $arr - array of data to convert to csv
* @return string - csv formatted string
*/
public function str_putcsv($arr)
{
$fh = fopen('php://temp', 'rw');
fputcsv($fh, $arr);
rewind($fh);
$csv = stream_get_contents($fh);
fclose($fh);
return trim($csv);
}
/**
* Find a text inside string range
*
* @param string $text
* @param string $string
* @param int $from
* @param int $to
* @return mixed
*/
public function findText($text, $string, $from, $to)
{
if ($from >= strlen($text)) {
return false;
}
$pos = strpos($text, $string, $from);
if ($pos === false || $pos + strlen($string) > $to) {
return false;
}
return $pos;
}
/**
* Return wiki markers
*
* @return array
*/
public function getWikiMarkers()
{
$listMarkers = [
['~np~', '~/np~'],
['-+', '+-'],
['~pp~', '~/pp~'],
['~pre~', '~/pre~'],
['-=', '=-'],
];
return $listMarkers;
}
}
// end of class ------------------------------------------------------
// function to check if a file or directory is in the path
// returns FALSE if incorrect
// returns the canonicalized absolute pathname otherwise
/**
* @param $file
* @param $dir
* @return bool|string
*/
function inpath($file, $dir)
{
$realfile = realpath($file);
$realdir = realpath($dir);
if (! $realfile) {
return (false);
}
if (! $realdir) {
return (false);
}
if (substr($realfile, 0, strlen($realdir)) != $realdir) {
return(false);
} else {
return($realfile);
}
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function compare_links($ar1, $ar2)
{
return $ar1["links"] - $ar2["links"];
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function compare_backlinks($ar1, $ar2)
{
return $ar1["backlinks"] - $ar2["backlinks"];
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function r_compare_links($ar1, $ar2)
{
return $ar2["links"] - $ar1["links"];
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function r_compare_backlinks($ar1, $ar2)
{
return $ar2["backlinks"] - $ar1["backlinks"];
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function compare_images($ar1, $ar2)
{
return $ar1["images"] - $ar2["images"];
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function r_compare_images($ar1, $ar2)
{
return $ar2["images"] - $ar1["images"];
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function compare_versions($ar1, $ar2)
{
return $ar1["versions"] - $ar2["versions"];
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function r_compare_versions($ar1, $ar2)
{
return $ar2["versions"] - $ar1["versions"];
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function compare_changed($ar1, $ar2)
{
return $ar1["lastChanged"] - $ar2["lastChanged"];
}
/**
* @param $ar1
* @param $ar2
* @return mixed
*/
function r_compare_changed($ar1, $ar2)
{
return $ar2["lastChanged"] - $ar1["lastChanged"];
}
/**
* @param $ar1
* @param $ar2
* @return int
*/
function compare_names($ar1, $ar2)
{
return strcasecmp(tra($ar1["name"]), tra($ar2["name"]));
}
function chkgd2()
{
return function_exists('imagecreatetruecolor');
}
/**
* @return string
*/
function detect_browser_language()
{
global $prefs;
// Get supported languages
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$supported = preg_split('/\s*,\s*/', preg_replace('/;q=[0-9.]+/', '', $_SERVER['HTTP_ACCEPT_LANGUAGE']));
} else {
return '';
}
// Get available languages
$available = [];
$available_aprox = [];
if (is_dir("lang")) {
$dh = opendir("lang");
while ($lang = readdir($dh)) {
if (! strpos($lang, '.') and is_dir("lang/$lang") and file_exists("lang/$lang/language.php") and ($prefs['restrict_language'] === 'n' || empty($prefs['available_languages']) || in_array($lang, $prefs['available_languages']))) {
$available[strtolower($lang)] = $lang;
$available_aprox[substr(strtolower($lang), 0, 2)] = $lang;
}
}
}
// Check better language
// Priority has been changed in 2.0 to that defined in RFC 4647
$aproximate_lang = '';
foreach ($supported as $supported_lang) {
$lang = strtolower($supported_lang);
if (in_array($lang, array_keys($available))) {
// exact match is always good
return $available[$lang];
} elseif (in_array($lang, array_keys($available_aprox))) {
// otherwise if supported language matches any available dialect, ok also
return $available_aprox[$lang];
} elseif ($aproximate_lang == '') {
// otherwise if supported dialect matches language, store as possible fallback
$lang = substr($lang, 0, 2);
if (in_array($lang, array_keys($available_aprox))) {
$aproximate_lang = $available_aprox[$lang];
}
}
}
return $aproximate_lang;
}
/**
* Validates an email address, using a domain check if $validate == 'y'
*
* @param string $email email to validate
* @param string $validate n|y|d (d = deep) defaults to pref validateEmail
* @return bool
*/
function validate_email($email, $validate = null)
{
global $prefs;
if (empty($validate)) {
$validate = $prefs['validateEmail'];
}
$options = ['allow' => Laminas\Validator\Hostname::ALLOW_ALL,];
if ($validate === 'n') {
return true;
} else {
$options['useDomainCheck'] = true; // both y and d
}
if ($validate === 'd') { // deep mx check
$options['useMxCheck'] = true;
$options['useDeepMxCheck'] = true;
}
$validator = new Laminas\Validator\EmailAddress($options);
return $validator->isValid($email);
}
/**
* @param $val
* @param $default
* @return string
*/
function makeBool($val, $default)
{
// Warning: This function is meant to return a string 'true' or 'false' to be used in JS, not a real boolean value
if (isset($val) && ! empty($val)) {
$val = ($val == 'y' ? true : false);
} else {
$val = $default;
}
return ($val ? 'true' : 'false');
}
/* Editor configuration
Local Variables:
tab-width: 4
c-basic-offset: 4
End:
* vim: fdm=marker tabstop=4 shiftwidth=4 noet:
*/
/**
*
* Writes a temporary directory and/or file in a cryptographically secure way.
*
* @param string|null $data Data to be written to file, null if we are creating directories only.
* @param string $directory Directory for the file to be created in. using the string 'random' will generate a random directory. Sending NULL will create a directory only.
* @param bool $system If files should be stored in the system directory (outside the web root), will fall back to tiki /temp directory upon failure.
* @param string $prefix A string to add to the beginning of the file name.
* @param string $append A string to append the file name, such as an extension.
*
* @return string The path and filename of the file written.
* @throws exception If a file can not be created, an exception will be thrown.
*/
function writeTempFile(?string $data, string $directory = '', bool $system = true, string $prefix = '', string $append = ''): ?string
{
global $prefs;
$fileName = '';
if ($directory === 'random') {
if (is_callable('random_bytes')) {
$directory = bin2hex(random_bytes(16)) . '/';
} else {
$directory = dechex(rand(0, 2 ** 62)) . dechex(rand(0, 2 ** 62)) . '/';
}
}
if (strlen($prefix) + strlen($append) > 223) {
throw new Exception('File name must be under 255 characters.');
}
if ($system) {
$tmpDir = $prefs['tmpDir'];
if (substr($tmpDir, -1) !== '/') {
$tmpDir = $tmpDir . '/';
}
if (file_exists($tmpDir . $directory)) {
$dirName = $tmpDir . $directory;
} elseif (@mkdir($tmpDir . $directory)) {
$dirName = $tmpDir . $directory;
}
// if the system directory is not writable, then fall back to Tiki tmp directory.
if (! is_writable($tmpDir . $directory)) {
unset($dirName);
}
}
if (! isset($dirName)) {
if (file_exists('temp/' . $directory)) {
$dirName = 'temp/' . $directory;
} elseif (@mkdir('temp/' . $directory)) {
$dirName = 'temp/' . $directory;
@file_put_contents('temp/' . $directory . 'index.php', '');
} else {
throw new Exception("Can not create temp/$directory directory.");
}
}
if (! is_null($data)) {
do {
if (is_callable('random_bytes')) {
$fileName = $prefix . bin2hex(random_bytes(16)) . $append;
} else {
$fileName = $prefix . dechex(rand(0, 2 ** 62)) . dechex(rand(0, 2 ** 62)) . $append;
}
} while (file_exists($dirName . $fileName));
if (@file_put_contents($dirName . $fileName, $data) === false) {
throw new exception("Can not write to $dirName$fileName file.");
}
}
return $dirName . $fileName;
}