*/ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); require_once(DOKU_PLUGIN . 'syntax.php'); require_once(DOKU_INC . 'inc/infoutils.php'); /** * This is the base class for all syntax classes, providing some general stuff */ class helper_plugin_dataau extends DokuWiki_Plugin { /** * @var helper_plugin_sqlite initialized via _getDb() */ protected $db = null; /** * @var array stores the alias definitions */ protected $aliases = null; /** * @var array stores custom key localizations */ protected $locs = array(); /** * Constructor * * Loads custom translations */ public function __construct() { $this->loadLocalizedLabels(); } private function loadLocalizedLabels() { $lang = array(); $path = DOKU_CONF . '/lang/en/dataau-plugin.php'; if(file_exists($path)) include($path); $path = DOKU_CONF . '/lang/' . $this->determineLang() . '/dataau-plugin.php'; if(file_exists($path)) include($path); foreach($lang as $key => $val) { $lang[utf8_strtolower($key)] = $val; } $this->locs = $lang; } /** * Return language code * * @return mixed */ protected function determineLang() { /** @var helper_plugin_translation $trans */ $trans = plugin_load('helper', 'translation'); if($trans) { $value = $trans->getLangPart(getID()); if($value) return $value; } global $conf; return $conf['lang']; } /** * Simple function to check if the database is ready to use * * @return bool */ public function ready() { return (bool) $this->_getDB(); } /** * load the sqlite helper * * @return helper_plugin_sqlite|false plugin or false if failed */ function _getDB() { if($this->db === null) { $this->db = plugin_load('helper', 'sqlite'); if($this->db === null) { msg('The dataau plugin needs the sqlite plugin', -1); return false; } if(!$this->db->init('dataau', dirname(__FILE__) . '/db/')) { $this->db = null; return false; } $this->db->create_function('DATARESOLVE', array($this, '_resolveData'), 2); } return $this->db; } /** * Makes sure the given data fits with the given type * * @param string $value * @param string|array $type * @return string */ function _cleanData($value, $type) { $value = trim($value); if(!$value AND $value !== '0') { return ''; } if(is_array($type)) { if(isset($type['enum']) && !preg_match('/(^|,\s*)' . preg_quote_cb($value) . '($|\s*,)/', $type['enum'])) { return ''; } $type = $type['type']; } switch($type) { case 'dt': if(preg_match('/^(\d\d?)-(\d\d?)-(\d\d\d\d)$/', $value, $m)) { return sprintf('%02d-%02d-%d', $m[1], $m[2], $m[3]); } if($value === '%now%') { return $value; } return ''; case 'url': if(!preg_match('!^[a-z]+://!i', $value)) { $value = 'http://' . $value; } return $value; case 'mail': $email = ''; $name = ''; $parts = preg_split('/\s+/', $value); do { $part = array_shift($parts); if(!$email && mail_isvalid($part)) { $email = strtolower($part); continue; } $name .= $part . ' '; } while($part); return trim($email . ' ' . $name); case 'page': case 'nspage': return cleanID($value); default: return $value; } } /** * Add pre and postfixs to the given value * * $type may be an column array with pre and postfixes * * @param string|array $type * @param string $val * @param string $pre * @param string $post * @return string */ function _addPrePostFixes($type, $val, $pre = '', $post = '') { if(is_array($type)) { if(isset($type['prefix'])) { $pre = $type['prefix']; } if(isset($type['postfix'])) { $post = $type['postfix']; } } $val = $pre . $val . $post; $val = $this->replacePlaceholders($val); return $val; } /** * Resolve a value according to its column settings * * This function is registered as a SQL function named DATARESOLVE * * @param string $value * @param string $colname * @return string */ function _resolveData($value, $colname) { // resolve pre and postfixes $column = $this->_column($colname); $value = $this->_addPrePostFixes($column['type'], $value); // for pages, resolve title $type = $column['type']; if(is_array($type)) { $type = $type['type']; } if($type == 'title' || ($type == 'page' && useHeading('content'))) { $id = $value; if($type == 'title') { list($id,) = explode('|', $value, 2); } //DATARESOLVE is only used with the 'LIKE' comparator, so concatenate the different strings is fine. $value .= ' ' . p_get_first_heading($id); } return $value; } public function ensureAbsoluteId($id) { if (substr($id,0,1) !== ':') { $id = ':' . $id; } return $id; } /** * Return XHTML formated data, depending on column type * * @param array $column * @param string $value * @param Doku_Renderer_xhtml $R * @return string */ function _formatData($column, $value, Doku_Renderer_xhtml $R) { global $conf; $vals = explode("\n", $value); $outs = array(); //multivalued line from db result for pageid and wiki has only in first value the ID $storedID = ''; foreach($vals as $val) { $val = trim($val); if($val == '') continue; $type = $column['type']; if(is_array($type)) { $type = $type['type']; } switch($type) { case 'page': $val = $this->_addPrePostFixes($column['type'], $val); $val = $this->ensureAbsoluteId($val); $outs[] = $R->internallink($val, null, null, true); break; case 'title': list($id, $title) = explode('|', $val, 2); $id = $this->_addPrePostFixes($column['type'], $id); $id = $this->ensureAbsoluteId($id); $outs[] = $R->internallink($id, $title, null, true); break; case 'pageid': list($id, $title) = explode('|', $val, 2); //use ID from first value of the multivalued line if($title == null) { $title = $id; if(!empty($storedID)) { $id = $storedID; } } else { $storedID = $id; } $id = $this->_addPrePostFixes($column['type'], $id); $outs[] = $R->internallink($id, $title, null, true); break; case 'nspage': // no prefix/postfix here $val = ':' . $column['key'] . ":$val"; $outs[] = $R->internallink($val, null, null, true); break; case 'mail': list($id, $title) = explode(' ', $val, 2); $id = $this->_addPrePostFixes($column['type'], $id); $id = obfuscate(hsc($id)); if(!$title) { $title = $id; } else { $title = hsc($title); } if($conf['mailguard'] == 'visible') { $id = rawurlencode($id); } $outs[] = '' . $title . ''; break; case 'url': $val = $this->_addPrePostFixes($column['type'], $val); $outs[] = $this->external_link($val, false, 'urlextern'); break; case 'tag': // per default use keyname as target page, but prefix on aliases if(!is_array($column['type'])) { $target = $column['key'] . ':'; } else { $target = $this->_addPrePostFixes($column['type'], ''); } $outs[] = '' . hsc($val) . ''; break; case 'timestamp': $outs[] = dformat($val); break; case 'wiki': global $ID; $oldid = $ID; list($ID, $dataau) = explode('|', $val, 2); //use ID from first value of the multivalued line if($dataau == null) { $dataau = $ID; $ID = $storedID; } else { $storedID = $ID; } $dataau = $this->_addPrePostFixes($column['type'], $dataau); // Trim document_{start,end}, p_{open,close} from instructions $allinstructions = p_get_instructions($dataau); $wraps = 1; if(isset($allinstructions[1]) && $allinstructions[1][0] == 'p_open') { $wraps ++; } $instructions = array_slice($allinstructions, $wraps, -$wraps); $outs[] = p_render('xhtml', $instructions, $byref_ignore); $ID = $oldid; break; default: $val = $this->_addPrePostFixes($column['type'], $val); //type '_img' or '_img' if(substr($type, 0, 3) == 'img') { $width = (int) substr($type, 3); if(!$width) { $width = $this->getConf('image_width'); } list($mediaid, $title) = explode('|', $val, 2); if($title === null) { $title = $column['key'] . ': ' . basename(str_replace(':', '/', $mediaid)); } else { $title = trim($title); } if(media_isexternal($val)) { $html = $R->externalmedia($mediaid, $title, $align = null, $width, $height = null, $cache = null, $linking = 'direct', true); } else { $html = $R->internalmedia($mediaid, $title, $align = null, $width, $height = null, $cache = null, $linking = 'direct', true); } if(strpos($html, 'mediafile') === false) { $html = str_replace('href', 'rel="lightbox" href', $html); } $outs[] = $html; } else { $outs[] = hsc($val); } } } return join(', ', $outs); } /** * Split a column name into its parts * * @param string $col column name * @returns array with key, type, ismulti, title, opt */ function _column($col) { preg_match('/^([^_]*)(?:_(.*))?((? $col, 'multi' => ($matches[3] === 's'), 'key' => utf8_strtolower($matches[1]), 'origkey' => $matches[1], //similar to key, but stores upper case 'title' => $matches[1], 'type' => utf8_strtolower($matches[2]) ); // fix title for special columns static $specials = array( '%title%' => array('page', 'title'), '%pageid%' => array('title', 'page'), '%class%' => array('class'), '%lastmod%' => array('lastmod', 'timestamp') ); if(isset($specials[$column['title']])) { $s = $specials[$column['title']]; $column['title'] = $this->getLang($s[0]); if($column['type'] === '' && isset($s[1])) { $column['type'] = $s[1]; } } // check if the type is some alias $aliases = $this->_aliases(); if(isset($aliases[$column['type']])) { $column['origtype'] = $column['type']; $column['type'] = $aliases[$column['type']]; } // use custom localization for keys if(isset($this->locs[$column['key']])) { $column['title'] = $this->locs[$column['key']]; } return $column; } /** * Load defined type aliases * * @return array */ function _aliases() { if(!is_null($this->aliases)) return $this->aliases; $sqlite = $this->_getDB(); if(!$sqlite) return array(); $this->aliases = array(); $res = $sqlite->query("SELECT * FROM aliases"); $rows = $sqlite->res2arr($res); foreach($rows as $row) { $name = $row['name']; unset($row['name']); $this->aliases[$name] = array_filter(array_map('trim', $row)); if(!isset($this->aliases[$name]['type'])) { $this->aliases[$name]['type'] = ''; } } return $this->aliases; } /** * Parse a filter line into an array * * @param $filterline * @return array|bool - array on success, false on error */ function _parse_filter($filterline) { //split filterline on comparator if(preg_match('/^(.*?)([\*=<>!~]{1,2})(.*)$/', $filterline, $matches)) { $column = $this->_column(trim($matches[1])); $com = $matches[2]; $aliasses = array( '<>' => '!=', '=!' => '!=', '~!' => '!~', '==' => '=', '~=' => '~', '=~' => '~' ); if(isset($aliasses[$com])) { $com = $aliasses[$com]; } elseif(!preg_match('/(!?[=~])|([<>]=?)|(\*~)/', $com)) { msg('Failed to parse comparison "' . hsc($com) . '"', -1); return false; } $val = trim($matches[3]); if($com == '~~') { $com = 'IN('; } if(strpos($com, '~') !== false) { if($com === '*~') { $val = '*' . $val . '*'; $com = '~'; } $val = str_replace('*', '%', $val); if($com == '!~') { $com = 'NOT LIKE'; } else { $com = 'LIKE'; } } else { // Clean if there are no asterisks I could kill $val = $this->_cleanData($val, $column['type']); } $sqlite = $this->_getDB(); if(!$sqlite) return false; $val = $sqlite->escape_string($val); //pre escape if($com == 'IN(') { $val = explode(',', $val); $val = array_map('trim', $val); $val = implode("','", $val); } return array( 'key' => $column['key'], 'value' => $val, 'compare' => $com, 'colname' => $column['colname'], 'type' => $column['type'] ); } msg('Failed to parse filter "' . hsc($filterline) . '"', -1); return false; } /** * Replace placeholders in sql */ function _replacePlaceholdersInSQL(&$dataau) { global $USERINFO; // allow current user name in filter: $dataau['sql'] = str_replace('%user%', $_SERVER['REMOTE_USER'], $dataau['sql']); $dataau['sql'] = str_replace('%groups%', implode("','", (array) $USERINFO['grps']), $dataau['sql']); // allow current date in filter: $dataau['sql'] = str_replace('%now%', dformat(null, '%d-%m-%Y'), $dataau['sql']); // language filter $dataau['sql'] = $this->makeTranslationReplacement($dataau['sql']); } /** * Replace translation related placeholders in given string * * @param string $data * @return string */ public function makeTranslationReplacement($dataau) { global $conf; global $ID; $patterns[] = '%lang%'; if(isset($conf['lang_before_translation'])) { $values[] = $conf['lang_before_translation']; } else { $values[] = $conf['lang']; } // if translation plugin available, get current translation (empty for default lang) $patterns[] = '%trans%'; /** @var helper_plugin_translation $trans */ $trans = plugin_load('helper', 'translation'); if($trans) { $local = $trans->getLangPart($ID); if($local === '') { $local = $conf['lang']; } $values[] = $local; } else { $values[] = ''; } return str_replace($patterns, $values, $dataau); } /** * Get filters given in the request via GET or POST * * @return array */ function _get_filters() { $filters = array(); if(!isset($_REQUEST['dataflt'])) { $flt = array(); } elseif(!is_array($_REQUEST['dataflt'])) { $flt = (array) $_REQUEST['dataflt']; } else { $flt = $_REQUEST['dataflt']; } foreach($flt as $key => $line) { // we also take the column and filtertype in the key: if(!is_numeric($key)) { $line = $key . $line; } $f = $this->_parse_filter($line); if(is_array($f)) { $f['logic'] = 'AND'; $filters[] = $f; } } return $filters; } /** * prepare an array to be passed through buildURLparams() * * @param string $name keyname * @param string|array $array value or key-value pairs * @return array */ function _a2ua($name, $array) { $urlarray = array(); foreach((array) $array as $key => $val) { $urlarray[$name . '[' . $key . ']'] = $val; } return $urlarray; } /** * get current URL parameters * * @param bool $returnURLparams * @return array with dataflt, datasrt and dataofs parameters */ function _get_current_param($returnURLparams = true) { $cur_params = array(); if(isset($_REQUEST['dataflt'])) { $cur_params = $this->_a2ua('dataflt', $_REQUEST['dataflt']); } if(isset($_REQUEST['dataausrt'])) { $cur_params['dataausrt'] = $_REQUEST['dataausrt']; } if(isset($_REQUEST['dataauofs'])) { $cur_params['dataauofs'] = $_REQUEST['dataauofs']; } //combine key and value if(!$returnURLparams) { $flat_param = array(); foreach($cur_params as $key => $val) { $flat_param[] = $key . $val; } $cur_params = $flat_param; } return $cur_params; } /** * Get url parameters, remove all filters for given column and add filter for desired tag * * @param array $column * @param string $tag * @return array of url parameters */ function _getTagUrlparam($column, $tag) { $param = array(); if(isset($_REQUEST['dataflt'])) { $param = (array) $_REQUEST['dataflt']; //remove all filters equal to column foreach($param as $key => $flt) { if(!is_numeric($key)) { $flt = $key . $flt; } $filter = $this->_parse_filter($flt); if($filter['key'] == $column['key']) { unset($param[$key]); } } } $param[] = $column['key'] . "_=$tag"; $param = $this->_a2ua('dataflt', $param); if(isset($_REQUEST['dataausrt'])) { $param['dataausrt'] = $_REQUEST['dataausrt']; } if(isset($_REQUEST['dataauofs'])) { $param['dataauofs'] = $_REQUEST['dataauofs']; } return $param; } /** * Perform replacements on the output values * * @param string $value * @return string */ private function replacePlaceholders($value) { return $this->makeTranslationReplacement($value); } }