From: Banana Date: Mon, 11 Sep 2023 10:06:15 +0000 (+0200) Subject: better intend and updated tag class X-Git-Tag: 2.8.2_20230914~9 X-Git-Url: http://91.132.146.200/gitweb/?a=commitdiff_plain;h=cece42c1f7a5f5a563106f182c2a3cf5d6dc1ea4;p=insipid.git better intend and updated tag class --- diff --git a/webroot/lib/simple-imap.class.php b/webroot/lib/simple-imap.class.php index bc570c4..8b9a914 100644 --- a/webroot/lib/simple-imap.class.php +++ b/webroot/lib/simple-imap.class.php @@ -35,115 +35,115 @@ */ class SimpleImap { - private IMAP\Connection $_connection; - - private string $_server = EMAIL_SERVER; - private string $_user = EMAIL_SERVER_USER; - private string $_pass = EMAIL_SERVER_PASS; - private int $_port = EMAIL_SERVER_PORT_IMAP; - private string $_mailbox = EMAIL_SERVER_MAILBOX; - - private string $_connectionstring = ''; - - /** - * SimpleImap constructor. - */ - function __construct() { - # create the mailboxstring - $this->_connectionstring = '{'.$this->_server.':'.$this->_port.'/imap/ssl/novalidate-cert}'; - } - - /** - * - */ - function __destruct() { - //imap_close($this->_connection); - } - - /** - * connect to the e-mail server - * with this code SSL/TLS only - * - * @see http://ca.php.net/manual/en/function.imap-open.php - * @throws Exception - */ - public function connect(): void { - - if(empty($this->_server)) { - throw new Exception('Missing EMAIL_SERVER'); - } - if(empty($this->_port)) { - throw new Exception('Missing EMAIL_SERVER_PORT'); - } - if(empty($this->_user)) { - throw new Exception('Missing EMAIL_SERVER_USER'); - } + private IMAP\Connection $_connection; + + private string $_server = EMAIL_SERVER; + private string $_user = EMAIL_SERVER_USER; + private string $_pass = EMAIL_SERVER_PASS; + private int $_port = EMAIL_SERVER_PORT_IMAP; + private string $_mailbox = EMAIL_SERVER_MAILBOX; + + private string $_connectionstring = ''; + + /** + * SimpleImap constructor. + */ + function __construct() { + # create the mailboxstring + $this->_connectionstring = '{'.$this->_server.':'.$this->_port.'/imap/ssl/novalidate-cert}'; + } + + /** + * + */ + function __destruct() { + //imap_close($this->_connection); + } + + /** + * connect to the e-mail server + * with this code SSL/TLS only + * + * @see http://ca.php.net/manual/en/function.imap-open.php + * @throws Exception + */ + public function connect(): void { + + if(empty($this->_server)) { + throw new Exception('Missing EMAIL_SERVER'); + } + if(empty($this->_port)) { + throw new Exception('Missing EMAIL_SERVER_PORT'); + } + if(empty($this->_user)) { + throw new Exception('Missing EMAIL_SERVER_USER'); + } # create the connection - $this->_connection = imap_open($this->_connectionstring.$this->_mailbox, $this->_user, $this->_pass); - - if(!$this->_connection) { - throw new Exception('Failed IMAP connection: '.var_export(imap_last_error(),true)); - } - } - - /** - * process the given mailbox and check for the special messages - * return the body and headers from the found message - * - * @param string $subjectmarker - * @return array emailId => array(body, header); - * @throws Exception - */ - function messageWithValidSubject(string $subjectmarker): array { - $ret = array(); - - $messagecount = imap_num_msg($this->_connection); - - if($messagecount === false) { - throw new Exception('Can not read the messages in given mailbox'); - } - - $processedmessagescount = 0; - for($i = 1; $i <= $messagecount; $i++) { - $subject = $this->_extractSubject($i); - - if(!empty($subject)) { - # check the special stuff - $markerextract = substr($subject, 0, strlen($subjectmarker)); - if($markerextract == $subjectmarker) { - $processedmessagescount++; - # valid message - # get the body - $ret[$i]['body'] = $this->_extractBody($i); - $ret[$i]['header'] = $this->emailHeaders($i); - $ret[$i]['header_rfc822'] = $this->emailHeaders_rfc822($i); - $ret[$i]['header_array'] = $this->emailHeadersAsArray($i); - # @see https://www.php.net/manual/en/function.imap-uid.php + $this->_connection = imap_open($this->_connectionstring.$this->_mailbox, $this->_user, $this->_pass); + + if(!$this->_connection) { + throw new Exception('Failed IMAP connection: '.var_export(imap_last_error(),true)); + } + } + + /** + * process the given mailbox and check for the special messages + * return the body and headers from the found message + * + * @param string $subjectmarker + * @return array emailId => array(body, header); + * @throws Exception + */ + function messageWithValidSubject(string $subjectmarker): array { + $ret = array(); + + $messagecount = imap_num_msg($this->_connection); + + if($messagecount === false) { + throw new Exception('Can not read the messages in given mailbox'); + } + + $processedmessagescount = 0; + for($i = 1; $i <= $messagecount; $i++) { + $subject = $this->_extractSubject($i); + + if(!empty($subject)) { + # check the special stuff + $markerextract = substr($subject, 0, strlen($subjectmarker)); + if($markerextract == $subjectmarker) { + $processedmessagescount++; + # valid message + # get the body + $ret[$i]['body'] = $this->_extractBody($i); + $ret[$i]['header'] = $this->emailHeaders($i); + $ret[$i]['header_rfc822'] = $this->emailHeaders_rfc822($i); + $ret[$i]['header_array'] = $this->emailHeadersAsArray($i); + # @see https://www.php.net/manual/en/function.imap-uid.php $ret[$i]['uid'] = imap_uid($this->_connection,$i); - } - } - } + } + } + } - # log messages processed to all messages + # log messages processed to all messages Summoner::sysLog("INFO Read ".$messagecount." messages"); Summoner::sysLog("INFO Processed ".$processedmessagescount." messages"); - return $ret; - - } + return $ret; - /** - * the current status about the mail connection and INBOX - * kinda debug only - * - * @see http://ca.php.net/manual/en/function.imap-status.php - */ - public function mailboxStatus(): void { - if($this->_connection !== false) { - $status = imap_status($this->_connection, $this->_connectionstring.$this->_mailbox, SA_ALL); + } - if(DEBUG === true) { + /** + * the current status about the mail connection and INBOX + * kinda debug only + * + * @see http://ca.php.net/manual/en/function.imap-status.php + */ + public function mailboxStatus(): void { + if($this->_connection !== false) { + $status = imap_status($this->_connection, $this->_connectionstring.$this->_mailbox, SA_ALL); + + if(DEBUG === true) { Summoner::sysLog("messages " . $status->messages); Summoner::sysLog("recent " . $status->recent); Summoner::sysLog("unseen " . $status->unseen); @@ -151,108 +151,108 @@ class SimpleImap { Summoner::sysLog("uidvalidity " . $status->uidvalidity); } - $list = imap_getmailboxes($this->_connection, $this->_connectionstring, "*"); - if (is_array($list)) { - foreach ($list as $key => $val) { - echo "($key) "; - echo imap_utf7_decode($val->name) . ","; - echo "'" . $val->delimiter . "',"; - echo $val->attributes . "
\n"; - } - } else { + $list = imap_getmailboxes($this->_connection, $this->_connectionstring, "*"); + if (is_array($list)) { + foreach ($list as $key => $val) { + echo "($key) "; + echo imap_utf7_decode($val->name) . ","; + echo "'" . $val->delimiter . "',"; + echo $val->attributes . "
\n"; + } + } else { Summoner::sysLog("ERROR imap_getmailboxes failed: ".var_export(imap_last_error())); - } - } - } - - /** - * This function causes a fetch of the complete, unfiltered RFC2822 format header of the specified message. - * - * @param integer $messagenum - * @return string - */ - public function emailHeaders(int $messagenum): string { - return imap_fetchheader($this->_connection, $messagenum); - } - - /** - * return the email headers by given emailid - * - * @param integer $messagenum - * @return object - */ - public function emailHeaders_rfc822(int $messagenum): object { - return imap_rfc822_parse_headers($this->emailHeaders($messagenum)); - } - - /** - * Email headers parsed as an array - * - * @param integer $messagenum - * @return array - */ - public function emailHeadersAsArray(int $messagenum): array { - preg_match_all('/([^: ]+): (.+?(?:\r\n\s(?:.+?))*)\r\n/m', $this->emailHeaders($messagenum), $matches ); - return array_combine( $matches[1], $matches[2]); - } - - /** - * Move given message to given folder - * - * @param integer $messageUid This is the message Uid as an int - * @param string $folder This is the target folder. Default is EMAIL_ARCHIVE_FOLDER - */ - public function moveMessage(int $messageUid, string $folder=EMAIL_ARCHIVE_FOLDER): void { - if(!empty($messageUid) && !empty($folder)) { - $messageUid = (string)$messageUid; - imap_setflag_full($this->_connection,$messageUid,"\SEEN", ST_UID); + } + } + } + + /** + * This function causes a fetch of the complete, unfiltered RFC2822 format header of the specified message. + * + * @param integer $messagenum + * @return string + */ + public function emailHeaders(int $messagenum): string { + return imap_fetchheader($this->_connection, $messagenum); + } + + /** + * return the email headers by given emailid + * + * @param integer $messagenum + * @return object + */ + public function emailHeaders_rfc822(int $messagenum): object { + return imap_rfc822_parse_headers($this->emailHeaders($messagenum)); + } + + /** + * Email headers parsed as an array + * + * @param integer $messagenum + * @return array + */ + public function emailHeadersAsArray(int $messagenum): array { + preg_match_all('/([^: ]+): (.+?(?:\r\n\s(?:.+?))*)\r\n/m', $this->emailHeaders($messagenum), $matches ); + return array_combine( $matches[1], $matches[2]); + } + + /** + * Move given message to given folder + * + * @param integer $messageUid This is the message Uid as an int + * @param string $folder This is the target folder. Default is EMAIL_ARCHIVE_FOLDER + */ + public function moveMessage(int $messageUid, string $folder=EMAIL_ARCHIVE_FOLDER): void { + if(!empty($messageUid) && !empty($folder)) { + $messageUid = (string)$messageUid; + imap_setflag_full($this->_connection,$messageUid,"\SEEN", ST_UID); imap_mail_move($this->_connection, $messageUid, $folder,CP_UID); imap_expunge($this->_connection); } } - /** - * extract the subject from the email headers and decode - * A subject can be split into multiple parts... - * - * @param int $messagenum - * @return string - */ - private function _extractSubject(int $messagenum): string { - $ret = ''; - - $headerinfo = $this->emailHeaders_rfc822($messagenum); - $subjectArr = imap_mime_header_decode($headerinfo->subject); - foreach ($subjectArr as $el) { - $ret .= $el->text; - } - - return $ret; - } - - /** - * extract the body of the given message - * - * @see http://php.net/manual/en/function.imap-fetchstructure.php - * - * @param int $messagenum - * @return string - */ - private function _extractBody(int $messagenum): string { - $ret = ''; - - $emailstructure = imap_fetchstructure($this->_connection, $messagenum); - - # simple or multipart? - if(isset($emailstructure->parts)) { - exit("multipart todo"); - } - else { - $body = imap_body($this->_connection, $messagenum); - } - - # encoding - switch ($emailstructure->encoding) { + /** + * extract the subject from the email headers and decode + * A subject can be split into multiple parts... + * + * @param int $messagenum + * @return string + */ + private function _extractSubject(int $messagenum): string { + $ret = ''; + + $headerinfo = $this->emailHeaders_rfc822($messagenum); + $subjectArr = imap_mime_header_decode($headerinfo->subject); + foreach ($subjectArr as $el) { + $ret .= $el->text; + } + + return $ret; + } + + /** + * extract the body of the given message + * + * @see http://php.net/manual/en/function.imap-fetchstructure.php + * + * @param int $messagenum + * @return string + */ + private function _extractBody(int $messagenum): string { + $ret = ''; + + $emailstructure = imap_fetchstructure($this->_connection, $messagenum); + + # simple or multipart? + if(isset($emailstructure->parts)) { + exit("multipart todo"); + } + else { + $body = imap_body($this->_connection, $messagenum); + } + + # encoding + switch ($emailstructure->encoding) { case ENC8BIT: # 1 8BIT $ret = quoted_printable_decode(imap_8bit($body)); break; @@ -270,7 +270,7 @@ class SimpleImap { break; case ENC7BIT: # 0 7BIT - // imap_qprint($body); does throws notices + // imap_qprint($body); does throws notices $ret = quoted_printable_decode($body); break; @@ -280,13 +280,13 @@ class SimpleImap { $ret = $body; } - return $ret; - } + return $ret; + } - /** - * close the imap connection - */ - function close(): void { - imap_close($this->_connection); - } + /** + * close the imap connection + */ + function close(): void { + imap_close($this->_connection); + } } diff --git a/webroot/lib/snapshot.class.php b/webroot/lib/snapshot.class.php index cc920d2..13ebce8 100644 --- a/webroot/lib/snapshot.class.php +++ b/webroot/lib/snapshot.class.php @@ -32,90 +32,90 @@ * right now it uses google pagespeedonline. */ class Snapshot { - /** - * @var string - */ - private string $_googlePageSpeed = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url='; + /** + * @var string + */ + private string $_googlePageSpeed = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url='; - /** - * @var string - */ - private string $_wkhtmltoimageOptions = '--load-error-handling ignore --quality 80 --quiet --width 1900'; + /** + * @var string + */ + private string $_wkhtmltoimageOptions = '--load-error-handling ignore --quality 80 --quiet --width 1900'; - /** - * Snapshot constructor - */ - public function __constructor(): void {} + /** + * Snapshot constructor + */ + public function __constructor(): void {} - /** - * call given url with google PageSpeed API - * to receive image data - * - * @param String $url URL to take a thumbnail from - * @param string $filename - * @return boolean - */ - public function doSnapshot(string $url, string $filename): bool { - $ret = false; + /** + * call given url with google PageSpeed API + * to receive image data + * + * @param String $url URL to take a thumbnail from + * @param string $filename + * @return boolean + */ + public function doSnapshot(string $url, string $filename): bool { + $ret = false; - if(!empty($url) && is_writable(dirname($filename))) { - if(DEBUG) { + if(!empty($url) && is_writable(dirname($filename))) { + if(DEBUG) { Summoner::sysLog("[DEBUG] try to save to $filename with $this->_googlePageSpeed for $url"); - } - $theCall = Summoner::curlCall($this->_googlePageSpeed.urlencode($url).'&screenshot=true'); - if(!empty($theCall)) { - $jsonData = json_decode($theCall,true); - if(DEBUG) { + } + $theCall = Summoner::curlCall($this->_googlePageSpeed.urlencode($url).'&screenshot=true'); + if(!empty($theCall)) { + $jsonData = json_decode($theCall,true); + if(DEBUG) { Summoner::sysLog("[DEBUG] Call result data: ".var_export($jsonData, true)); - } - if(!empty($jsonData) && isset($jsonData['lighthouseResult']['audits']['full-page-screenshot']['details']['screenshot']['data'])) { - $imageData = $jsonData['lighthouseResult']['audits']['full-page-screenshot']['details']['screenshot']['data']; + } + if(!empty($jsonData) && isset($jsonData['lighthouseResult']['audits']['full-page-screenshot']['details']['screenshot']['data'])) { + $imageData = $jsonData['lighthouseResult']['audits']['full-page-screenshot']['details']['screenshot']['data']; - $source = fopen($imageData, 'r'); - $destination = fopen($filename, 'w'); - if(stream_copy_to_stream($source, $destination)) { - $ret = $filename; - } - fclose($source); - fclose($destination); - } elseif(DEBUG) { + $source = fopen($imageData, 'r'); + $destination = fopen($filename, 'w'); + if(stream_copy_to_stream($source, $destination)) { + $ret = $filename; + } + fclose($source); + fclose($destination); + } elseif(DEBUG) { Summoner::sysLog("[DEBUG] invalid json data. Path ['lighthouseResult']['audits']['full-page-screenshot']['details']['screenshot']['data'] not found in : ".var_export($jsonData, true)); - } - } elseif(DEBUG) { + } + } elseif(DEBUG) { Summoner::sysLog("[DEBUG] curl call failed"); - } - } + } + } - return $ret; - } + return $ret; + } - /** - * use configured COMPLETE_PAGE_SCREENSHOT_COMMAND to create a whole page screenshot - * of the given link and store it locally - * - * @TODO: TBD - * - * @param String $url URL to take the screenshot from - * @param string $filename - * @return boolean - */ - public function wholePageSnapshot(string $url, string $filename): bool { - $ret = false; + /** + * use configured COMPLETE_PAGE_SCREENSHOT_COMMAND to create a whole page screenshot + * of the given link and store it locally + * + * @TODO: TBD + * + * @param String $url URL to take the screenshot from + * @param string $filename + * @return boolean + */ + public function wholePageSnapshot(string $url, string $filename): bool { + $ret = false; - require_once 'lib/shellcommand.class.php'; + require_once 'lib/shellcommand.class.php'; - if(!empty($url) && is_writable(dirname($filename))) { - $cmd = COMPLETE_PAGE_SCREENSHOT_COMMAND; - $params = $this->_wkhtmltoimageOptions." ".$url." ".$filename; - $command = new ShellCommand($cmd." ".$params); - if ($command->execute()) { - $ret = $command->getOutput(); - } else { - error_log($command->getError()); - $ret = $command->getExitCode(); - } - } + if(!empty($url) && is_writable(dirname($filename))) { + $cmd = COMPLETE_PAGE_SCREENSHOT_COMMAND; + $params = $this->_wkhtmltoimageOptions." ".$url." ".$filename; + $command = new ShellCommand($cmd." ".$params); + if ($command->execute()) { + $ret = $command->getOutput(); + } else { + error_log($command->getError()); + $ret = $command->getExitCode(); + } + } - return $ret; - } + return $ret; + } } diff --git a/webroot/lib/summoner.class.php b/webroot/lib/summoner.class.php index 184153d..bb6a693 100644 --- a/webroot/lib/summoner.class.php +++ b/webroot/lib/summoner.class.php @@ -31,435 +31,435 @@ */ class Summoner { - private const BROWSER_AGENT_STRING = 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'; - - /** - * validate the given string with the given type. Optional check the string - * length - * - * @param string $input The string to check - * @param string $mode How the string should be checked - * @param int $limit If int given the string is checked for length - * - * @see http://de.php.net/manual/en/regexp.reference.unicode.php - * http://www.sql-und-xml.de/unicode-database/#pc - * - * the pattern replaces all that is allowed. the correct result after - * the replace should be empty, otherwise are there chars which are not - * allowed - * - * @return bool - */ - static function validate(string $input, string $mode='text', int $limit=0): bool { - // check if we have input - $input = trim($input); - - if($input == "") return false; - - $ret = false; - - switch ($mode) { - case 'mail': - if(filter_var($input,FILTER_VALIDATE_EMAIL) === $input) { - return true; - } - else { - return false; - } - break; - - case 'url': - if(filter_var($input,FILTER_VALIDATE_URL) === $input) { - return true; - } - else { - return false; - } - break; - - case 'nospace': - // text without any whitespace and special chars - $pattern = '/[\p{L}\p{N}]/u'; - break; - - case 'nospaceP': - // text without any whitespace and special chars - // but with Punctuation other - # http://www.sql-und-xml.de/unicode-database/po.html - $pattern = '/[\p{L}\p{N}\p{Po}\-_]/u'; - break; - - case 'digit': - // only numbers and digit - // warning with negative numbers... - $pattern = '/[\p{N}\-]/'; - break; - - case 'pageTitle': - // text with whitespace and without special chars - // but with Punctuation - $pattern = '/[\p{L}\p{N}\p{Po}\p{Z}\s\-_]/u'; - break; - - # strange. the \p{M} is needed.. don't know why.. - case 'filename': - $pattern = '/[\p{L}\p{N}\p{M}\-_\.\p{Zs}]/u'; - break; - - case 'text': - default: - $pattern = '/[\p{L}\p{N}\p{P}\p{S}\p{Z}\p{M}\s]/u'; - } - - $value = preg_replace($pattern, '', $input); - - if($value === "") { - $ret = true; - } - - if(!empty($limit)) { - # isset starts with 0 - if(isset($input[$limit])) { - # too long - $ret = false; - } - } - - return $ret; - } - - - /** - * execute a curl call to the given $url - * - * @param string $url The request url - * @param int $port - * @return string - */ - static function curlCall(string $url, int $port=0): string { - $ret = ''; - - $ch = curl_init(); - - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); - curl_setopt($ch, CURLOPT_TIMEOUT, 30); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_MAXREDIRS, 2); - curl_setopt($ch, CURLOPT_USERAGENT,self::BROWSER_AGENT_STRING); - - // curl_setopt($ch, CURLOPT_VERBOSE, true); - // curl_setopt($ch, CURLOPT_HEADER, true); - - if(!empty($port)) { - curl_setopt($ch, CURLOPT_PORT, $port); - } - - $do = curl_exec($ch); - - if(is_string($do) === true) { - $ret = $do; - } - else { - error_log('ERROR '.var_export(curl_error($ch),true)); - } - - curl_close($ch); - - return $ret; - } - - /** - * Download given url to given file - * - * @param string $url - * @param string $whereToStore - * @param int $port - * @return bool - */ - static function downloadFile(string $url, string $whereToStore, int $port=0): bool { - $fh = fopen($whereToStore, 'w+'); - - $ret = false; - - if($fh !== false) { - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_FILE, $fh); - - curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); - curl_setopt($ch, CURLOPT_TIMEOUT, 30); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_MAXREDIRS, 2); - curl_setopt($ch, CURLOPT_USERAGENT, self::BROWSER_AGENT_STRING); - - if(!empty($port)) { - curl_setopt($ch, CURLOPT_PORT, $port); - } - curl_exec($ch); - curl_close($ch); - - $ret = true; - } - - fclose($fh); - - return $ret; - } - - /** - * simulate the Null coalescing operator in php5 - * this only works with arrays and checking if the key is there and echo/return it. - * http://php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op - * - * @param array $array - * @param string $key - * @return mixed - */ - static function ifset(array $array, string $key): mixed { - return $array[$key] ?? false; - } - - /** - * try to gather meta information from given URL - * - * @param string $url - * @return array - */ - static function gatherInfoFromURL(string $url): array { - $ret = array(); - - if(self::validate($url,'url')) { - $data = self::curlCall($url); - if(!empty($data)) { - $ret = self::socialMetaInfos($data); - } - } - - return $ret; - } - - /** - * get as much as possible social meta infos from given string - * the string is usually a HTML source - * - * @param string $string - * @return array - */ - static function socialMetaInfos(string $string): array { - #http://www.w3bees.com/2013/11/fetch-facebook-og-meta-tags-with-php.html - #http://www.9lessons.info/2014/01/social-meta-tags-for-google-twitter-and.html - #http://ogp.me/ - #https://moz.com/blog/meta-data-templates-123 - - $dom = new DomDocument; - # surpress invalid html warnings - @$dom->loadHTML($string); - - $xpath = new DOMXPath($dom); - $metas = $xpath->query('//*/meta'); - - $mediaInfos = array(); - - # meta tags - foreach($metas as $meta) { - if($meta->getAttribute('property')) { - $prop = $meta->getAttribute('property'); - $prop = mb_strtolower($prop); - - # minimum required information - # http://ogp.me/#metadata - if($prop == "og:title") { - - $mediaInfos['title'] = $meta->getAttribute('content'); - } - elseif($prop == "og:image") { - $mediaInfos['image'] = $meta->getAttribute('content'); - } - elseif($prop == "og:url") { - $mediaInfos['link'] = $meta->getAttribute('content'); - } - elseif($prop == "og:description") { - $mediaInfos['description'] = $meta->getAttribute('content'); - } - } - elseif($meta->getAttribute('name')) { - $name = $meta->getAttribute('name'); - $name = mb_strtolower($name); - - # twitter - # https://dev.twitter.com/cards/overview - - if($name == "twitter:title") { - $mediaInfos['title'] = $meta->getAttribute('content'); - } - elseif($name == "twitter:description") { - $mediaInfos['description'] = $meta->getAttribute('content'); - } - elseif($name == "twitter:image") { - $mediaInfos['image'] = $meta->getAttribute('content'); - } - elseif($name == "description") { - $mediaInfos['description'] = $meta->getAttribute('content'); - } - - } - elseif($meta->getAttribute('itemprop')) { - $itemprop = $meta->getAttribute('itemprop'); - $itemprop = mb_strtolower($itemprop); - - # google plus - if($itemprop == "name") { - $mediaInfos['title'] = $meta->getAttribute('content'); - } - elseif($itemprop == "description") { - $mediaInfos['description'] = $meta->getAttribute('content'); - } - elseif($itemprop == "image") { - $mediaInfos['image'] = $meta->getAttribute('content'); - } - - } - } - - - if(!isset($mediaInfos['title'])) { - $titleDom = $xpath->query('//title'); - $mediaInfos['title'] = $titleDom->item(0)->nodeValue; - } - - return $mediaInfos; - } - - /** - * at creation a category or tag can be a string with multiple values. - * separated with space or , - * category and tag is a single string without any separators - * - * @param string $string - * @return array - */ - static function prepareTagOrCategoryStr(string $string): array { - $ret = array(); + private const BROWSER_AGENT_STRING = 'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0'; + + /** + * validate the given string with the given type. Optional check the string + * length + * + * @param string $input The string to check + * @param string $mode How the string should be checked + * @param int $limit If int given the string is checked for length + * + * @see http://de.php.net/manual/en/regexp.reference.unicode.php + * http://www.sql-und-xml.de/unicode-database/#pc + * + * the pattern replaces all that is allowed. the correct result after + * the replace should be empty, otherwise are there chars which are not + * allowed + * + * @return bool + */ + static function validate(string $input, string $mode='text', int $limit=0): bool { + // check if we have input + $input = trim($input); + + if($input == "") return false; + + $ret = false; + + switch ($mode) { + case 'mail': + if(filter_var($input,FILTER_VALIDATE_EMAIL) === $input) { + return true; + } + else { + return false; + } + break; + + case 'url': + if(filter_var($input,FILTER_VALIDATE_URL) === $input) { + return true; + } + else { + return false; + } + break; + + case 'nospace': + // text without any whitespace and special chars + $pattern = '/[\p{L}\p{N}]/u'; + break; + + case 'nospaceP': + // text without any whitespace and special chars + // but with Punctuation other + # http://www.sql-und-xml.de/unicode-database/po.html + $pattern = '/[\p{L}\p{N}\p{Po}\-_]/u'; + break; + + case 'digit': + // only numbers and digit + // warning with negative numbers... + $pattern = '/[\p{N}\-]/'; + break; + + case 'pageTitle': + // text with whitespace and without special chars + // but with Punctuation + $pattern = '/[\p{L}\p{N}\p{Po}\p{Z}\s\-_]/u'; + break; + + # strange. the \p{M} is needed.. don't know why.. + case 'filename': + $pattern = '/[\p{L}\p{N}\p{M}\-_\.\p{Zs}]/u'; + break; + + case 'text': + default: + $pattern = '/[\p{L}\p{N}\p{P}\p{S}\p{Z}\p{M}\s]/u'; + } + + $value = preg_replace($pattern, '', $input); + + if($value === "") { + $ret = true; + } + + if(!empty($limit)) { + # isset starts with 0 + if(isset($input[$limit])) { + # too long + $ret = false; + } + } + + return $ret; + } + + + /** + * execute a curl call to the given $url + * + * @param string $url The request url + * @param int $port + * @return string + */ + static function curlCall(string $url, int $port=0): string { + $ret = ''; + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_MAXREDIRS, 2); + curl_setopt($ch, CURLOPT_USERAGENT,self::BROWSER_AGENT_STRING); + + // curl_setopt($ch, CURLOPT_VERBOSE, true); + // curl_setopt($ch, CURLOPT_HEADER, true); + + if(!empty($port)) { + curl_setopt($ch, CURLOPT_PORT, $port); + } + + $do = curl_exec($ch); + + if(is_string($do) === true) { + $ret = $do; + } + else { + error_log('ERROR '.var_export(curl_error($ch),true)); + } + + curl_close($ch); + + return $ret; + } + + /** + * Download given url to given file + * + * @param string $url + * @param string $whereToStore + * @param int $port + * @return bool + */ + static function downloadFile(string $url, string $whereToStore, int $port=0): bool { + $fh = fopen($whereToStore, 'w+'); + + $ret = false; + + if($fh !== false) { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_FILE, $fh); + + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_MAXREDIRS, 2); + curl_setopt($ch, CURLOPT_USERAGENT, self::BROWSER_AGENT_STRING); + + if(!empty($port)) { + curl_setopt($ch, CURLOPT_PORT, $port); + } + curl_exec($ch); + curl_close($ch); + + $ret = true; + } + + fclose($fh); + + return $ret; + } + + /** + * simulate the Null coalescing operator in php5 + * this only works with arrays and checking if the key is there and echo/return it. + * http://php.net/manual/en/migration70.new-features.php#migration70.new-features.null-coalesce-op + * + * @param array $array + * @param string $key + * @return mixed + */ + static function ifset(array $array, string $key): mixed { + return $array[$key] ?? false; + } + + /** + * try to gather meta information from given URL + * + * @param string $url + * @return array + */ + static function gatherInfoFromURL(string $url): array { + $ret = array(); + + if(self::validate($url,'url')) { + $data = self::curlCall($url); + if(!empty($data)) { + $ret = self::socialMetaInfos($data); + } + } + + return $ret; + } + + /** + * get as much as possible social meta infos from given string + * the string is usually a HTML source + * + * @param string $string + * @return array + */ + static function socialMetaInfos(string $string): array { + #http://www.w3bees.com/2013/11/fetch-facebook-og-meta-tags-with-php.html + #http://www.9lessons.info/2014/01/social-meta-tags-for-google-twitter-and.html + #http://ogp.me/ + #https://moz.com/blog/meta-data-templates-123 + + $dom = new DomDocument; + # surpress invalid html warnings + @$dom->loadHTML($string); + + $xpath = new DOMXPath($dom); + $metas = $xpath->query('//*/meta'); + + $mediaInfos = array(); + + # meta tags + foreach($metas as $meta) { + if($meta->getAttribute('property')) { + $prop = $meta->getAttribute('property'); + $prop = mb_strtolower($prop); + + # minimum required information + # http://ogp.me/#metadata + if($prop == "og:title") { + + $mediaInfos['title'] = $meta->getAttribute('content'); + } + elseif($prop == "og:image") { + $mediaInfos['image'] = $meta->getAttribute('content'); + } + elseif($prop == "og:url") { + $mediaInfos['link'] = $meta->getAttribute('content'); + } + elseif($prop == "og:description") { + $mediaInfos['description'] = $meta->getAttribute('content'); + } + } + elseif($meta->getAttribute('name')) { + $name = $meta->getAttribute('name'); + $name = mb_strtolower($name); + + # twitter + # https://dev.twitter.com/cards/overview + + if($name == "twitter:title") { + $mediaInfos['title'] = $meta->getAttribute('content'); + } + elseif($name == "twitter:description") { + $mediaInfos['description'] = $meta->getAttribute('content'); + } + elseif($name == "twitter:image") { + $mediaInfos['image'] = $meta->getAttribute('content'); + } + elseif($name == "description") { + $mediaInfos['description'] = $meta->getAttribute('content'); + } + + } + elseif($meta->getAttribute('itemprop')) { + $itemprop = $meta->getAttribute('itemprop'); + $itemprop = mb_strtolower($itemprop); + + # google plus + if($itemprop == "name") { + $mediaInfos['title'] = $meta->getAttribute('content'); + } + elseif($itemprop == "description") { + $mediaInfos['description'] = $meta->getAttribute('content'); + } + elseif($itemprop == "image") { + $mediaInfos['image'] = $meta->getAttribute('content'); + } + + } + } + + + if(!isset($mediaInfos['title'])) { + $titleDom = $xpath->query('//title'); + $mediaInfos['title'] = $titleDom->item(0)->nodeValue; + } + + return $mediaInfos; + } + + /** + * at creation a category or tag can be a string with multiple values. + * separated with space or , + * category and tag is a single string without any separators + * + * @param string $string + * @return array + */ + static function prepareTagOrCategoryStr(string $string): array { + $ret = array(); $_ret = array(); - $string = trim($string, ", "); - if(strstr($string, ",")) { - $_t = explode(",", $string); - foreach($_t as $n) { - $_ret[$n] = $n; - } - unset($_t); - unset($n); - - foreach($_ret as $e) { - if(strstr($e, " ")) { - unset($ret[$e]); - $_t = explode(" ", $e); - foreach($_t as $new) { - $new = trim($new); - $_c = self::validate($new,'nospace'); - if(!empty($new) && $_c === true) { - $ret[$new] = $new; - } - } - } - else { + $string = trim($string, ", "); + if(strstr($string, ",")) { + $_t = explode(",", $string); + foreach($_t as $n) { + $_ret[$n] = $n; + } + unset($_t); + unset($n); + + foreach($_ret as $e) { + if(strstr($e, " ")) { + unset($ret[$e]); + $_t = explode(" ", $e); + foreach($_t as $new) { + $new = trim($new); + $_c = self::validate($new,'nospace'); + if(!empty($new) && $_c === true) { + $ret[$new] = $new; + } + } + } + else { $new = trim($e); $_c = self::validate($new,'nospace'); if(!empty($new) && $_c === true) { $ret[$new] = $new; } } - } - } - else { - $_t = explode(" ", $string); - foreach($_t as $new) { - $new = trim($new); + } + } + else { + $_t = explode(" ", $string); + foreach($_t as $new) { + $new = trim($new); $_c = self::validate($new,'nospace'); - if(!empty($new) && $_c === true) { - $ret[$new] = $new; - } - } - } - - - return $ret; - } - - /** - * a very simple HTTP_AUTH authentication. - */ - static function simpleAuth(): void { - if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) - || $_SERVER['PHP_AUTH_USER'] !== FRONTEND_USERNAME || $_SERVER['PHP_AUTH_PW'] !== FRONTEND_PASSWORD - ) { - header('WWW-Authenticate: Basic realm="Insipid edit area"'); - header('HTTP/1.0 401 Unauthorized'); - echo 'No Access.'; - exit; - } - } - - /** - * check if we have a valid auth. Nothing more. - * - * @see Summoner::simpleAuth to trigger the auth - * @return bool - */ - static function simpleAuthCheck(): bool { - if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) - && $_SERVER['PHP_AUTH_USER'] === FRONTEND_USERNAME && $_SERVER['PHP_AUTH_PW'] === FRONTEND_PASSWORD - ) { - return true; - } - - return false; - } - - /** - * Checks if in the given urlstring a scheme is existent. If not add http:// to it - * - * @param string $urlString - * @return string - */ - static function addSchemeToURL(string $urlString): string { - $ret = $urlString; - - if(empty(parse_url($ret, PHP_URL_SCHEME))) { - $ret = "http://".$ret; - } - - return $ret; - } - - /** - * retrieve the folder size with its children of given folder path - * - * @param string $folder - * @return int - */ - static function folderSize(string $folder): int { - $ret = 0; - - if(file_exists($folder) && is_readable($folder)) { - foreach (glob(rtrim($folder, '/') . '/*', GLOB_NOSORT) as $each) { - $ret += is_file($each) ? filesize($each) : self::folderSize($each); - } - } - - return $ret; - } - - /** - * Calculate the given byte size in more human readable format. - * - * @param integer $size - * @param string $unit - * @return string - */ - static function humanFileSize(int $size, string $unit=""): string { + if(!empty($new) && $_c === true) { + $ret[$new] = $new; + } + } + } + + + return $ret; + } + + /** + * a very simple HTTP_AUTH authentication. + */ + static function simpleAuth(): void { + if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) + || $_SERVER['PHP_AUTH_USER'] !== FRONTEND_USERNAME || $_SERVER['PHP_AUTH_PW'] !== FRONTEND_PASSWORD + ) { + header('WWW-Authenticate: Basic realm="Insipid edit area"'); + header('HTTP/1.0 401 Unauthorized'); + echo 'No Access.'; + exit; + } + } + + /** + * check if we have a valid auth. Nothing more. + * + * @see Summoner::simpleAuth to trigger the auth + * @return bool + */ + static function simpleAuthCheck(): bool { + if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW']) + && $_SERVER['PHP_AUTH_USER'] === FRONTEND_USERNAME && $_SERVER['PHP_AUTH_PW'] === FRONTEND_PASSWORD + ) { + return true; + } + + return false; + } + + /** + * Checks if in the given urlstring a scheme is existent. If not add http:// to it + * + * @param string $urlString + * @return string + */ + static function addSchemeToURL(string $urlString): string { + $ret = $urlString; + + if(empty(parse_url($ret, PHP_URL_SCHEME))) { + $ret = "http://".$ret; + } + + return $ret; + } + + /** + * retrieve the folder size with its children of given folder path + * + * @param string $folder + * @return int + */ + static function folderSize(string $folder): int { + $ret = 0; + + if(file_exists($folder) && is_readable($folder)) { + foreach (glob(rtrim($folder, '/') . '/*', GLOB_NOSORT) as $each) { + $ret += is_file($each) ? filesize($each) : self::folderSize($each); + } + } + + return $ret; + } + + /** + * Calculate the given byte size in more human readable format. + * + * @param integer $size + * @param string $unit + * @return string + */ + static function humanFileSize(int $size, string $unit=""): string { $ret = number_format($size)." bytes"; if((!$unit && $size >= 1<<30) || $unit == "GB") { @@ -473,18 +473,18 @@ class Summoner { } return $ret; - } - - /** - * delete and/or empty a directory - * - * $empty = true => empty the directory but do not delete it - * - * @param string $directory - * @param boolean $empty - * @param int $fTime If not false remove files older then this value in sec. - * @return boolean - */ + } + + /** + * delete and/or empty a directory + * + * $empty = true => empty the directory but do not delete it + * + * @param string $directory + * @param boolean $empty + * @param int $fTime If not false remove files older then this value in sec. + * @return boolean + */ static function recursive_remove_directory(string $directory, bool $empty=false, int $fTime=0): bool { if(substr($directory,-1) == '/') { $directory = substr($directory,0,-1); @@ -532,15 +532,15 @@ class Summoner { } } - /** - * http_build_query with modify array - * modify will add: key AND value not empty - * modify will remove: only key with no value - * - * @param array $array - * @param array $modify - * @return string - */ + /** + * http_build_query with modify array + * modify will add: key AND value not empty + * modify will remove: only key with no value + * + * @param array $array + * @param array $modify + * @return string + */ static function createFromParameterLinkQuery(array $array, array $modify=array()): string { $ret = ''; @@ -562,25 +562,25 @@ class Summoner { return $ret; } - /** - * Make the input more safe for logging - * - * @param string $input The string to be made more safe - * @return string - */ - static function cleanForLog($input): string { - $input = var_export($input, true); - $input = preg_replace( "/[\t\n\r]/", " ", $input); - return addcslashes($input, "\000..\037\177..\377\\"); - } - - /** - * error_log with a dedicated destination - * Uses LOGFILE const - * - * @param string $msg The string to be written to the log - */ - static function sysLog(string $msg): void { - error_log(date("c")." ".$msg."\n", 3, LOGFILE); - } + /** + * Make the input more safe for logging + * + * @param string $input The string to be made more safe + * @return string + */ + static function cleanForLog($input): string { + $input = var_export($input, true); + $input = preg_replace( "/[\t\n\r]/", " ", $input); + return addcslashes($input, "\000..\037\177..\377\\"); + } + + /** + * error_log with a dedicated destination + * Uses LOGFILE const + * + * @param string $msg The string to be written to the log + */ + static function sysLog(string $msg): void { + error_log(date("c")." ".$msg."\n", 3, LOGFILE); + } } diff --git a/webroot/lib/tag.class.php b/webroot/lib/tag.class.php index 2fd46ce..d6a131f 100644 --- a/webroot/lib/tag.class.php +++ b/webroot/lib/tag.class.php @@ -3,7 +3,7 @@ * Insipid * Personal web-bookmark-system * - * Copyright 2016-2022 Johannes Keßler + * Copyright 2016-2023 Johannes Keßler * * Development starting from 2011: Johannes Keßler * https://www.bananas-playground.net/projekt/insipid/ @@ -30,199 +30,235 @@ * Class Tag */ class Tag { - /** - * the database object - * - * @var mysqli - */ - private $DB; - - /** - * the current loaded tag by DB id - * - * @var int - */ - private $_id; - - /** - * current loaded tag data - * - * @var array - */ - private $_data; - - /** - * Tag constructor. - * - * @param mysqli $databaseConnectionObject - */ - public function __construct($databaseConnectionObject) { - $this->DB = $databaseConnectionObject; - } - - /** - * by given string load the info from the DB and even create if not existing - * - * @param string $string - * @param bool $doNotCreate - * @return int 0=fail, 1=existing, 2=new, 3=newNotCreated - */ - public function initbystring(string $string, $doNotCreate=false): int { - $ret = 0; - $this->_id = false; - if(!empty($string)) { - $queryStr = "SELECT `id`,`name` FROM `".DB_PREFIX."_tag` - WHERE `name` = '".$this->DB->real_escape_string($string)."'"; - $query = $this->DB->query($queryStr); - if(!empty($query) && $query->num_rows > 0) { - $result = $query->fetch_assoc(); - $this->_id = $result['id']; - $this->_data = $result; - $ret = 1; - } - else { - if(!$doNotCreate) { - $queryStr = "INSERT INTO `" . DB_PREFIX . "_tag` - SET `name` = '" . $this->DB->real_escape_string($string) . "'"; - $this->DB->query($queryStr); - if (!empty($this->DB->insert_id)) { - $this->_id = $this->DB->insert_id; - $this->_data['id'] = $this->_id; - $this->_data['name'] = $string; - $ret = 2; + /** + * the database object + * + * @var mysqli + */ + private mysqli $DB; + + /** + * the current loaded tag by DB id + * + * @var string + */ + private string $_id; + + /** + * current loaded tag data + * + * @var array + */ + private array $_data; + + /** + * Tag constructor. + * + * @param mysqli $databaseConnectionObject + */ + public function __construct(mysqli $databaseConnectionObject) { + $this->DB = $databaseConnectionObject; + } + + /** + * by given string load the info from the DB and even create if not existing + * + * @param string $string + * @param bool $doNotCreate + * @return int 0=fail, 1=existing, 2=new, 3=newNotCreated + */ + public function initbystring(string $string, $doNotCreate=false): int { + $ret = 0; + $this->_id = false; + if(!empty($string)) { + $queryStr = "SELECT `id`,`name` FROM `".DB_PREFIX."_tag` + WHERE `name` = '".$this->DB->real_escape_string($string)."'"; + + if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr)); + + try { + $query = $this->DB->query($queryStr); + if(!empty($query) && $query->num_rows > 0) { + $result = $query->fetch_assoc(); + $this->_id = $result['id']; + $this->_data = $result; + $ret = 1; + } + else { + if(!$doNotCreate) { + $queryStr = "INSERT INTO `" . DB_PREFIX . "_tag` + SET `name` = '" . $this->DB->real_escape_string($string) . "'"; + + if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr)); + + $this->DB->query($queryStr); + if (!empty($this->DB->insert_id)) { + $this->_id = $this->DB->insert_id; + $this->_data['id'] = $this->_id; + $this->_data['name'] = $string; + $ret = 2; + } + } + else { + $ret=3; } } - else { - $ret=3; + } catch (Exception $e) { + Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage()); + } + } + return $ret; + } + + /** + * by given DB table id load all the info we need + * + * @param int $id + * @return int + */ + public function initbyid(int $id): int { + $this->_id = 0; + + if(!empty($id)) { + $queryStr = "SELECT `id`,`name` FROM `".DB_PREFIX."_tag` + WHERE `id` = '".$this->DB->real_escape_string($id)."'"; + + if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr)); + + try { + $query = $this->DB->query($queryStr); + if(!empty($query) && $query->num_rows > 0) { + $result = $query->fetch_assoc(); + $this->_id = $result['id']; + $this->_data = $result; } - } - } - return $ret; - } - - /** - * by given DB table id load all the info we need - * - * @param int $id - * @return int - */ - public function initbyid(int $id): int { - $this->_id = 0; - - if(!empty($id)) { - $queryStr = "SELECT `id`,`name` FROM `".DB_PREFIX."_tag` - WHERE `id` = '".$this->DB->real_escape_string($id)."'"; - $query = $this->DB->query($queryStr); - if(!empty($query) && $query->num_rows > 0) { - $result = $query->fetch_assoc(); - $this->_id = $result['id']; - $this->_data = $result; - } - } - - return $this->_id; - } - - /** - * return all or data fpr given key on the current loaded tag - * - * @param bool $key - * @return array|string - */ - public function getData($key=false) { - $ret = $this->_data; - - if(!empty($key) && isset($this->_data[$key])) { - $ret = $this->_data[$key]; - } - - return $ret; - } - - /** - * set the relation to the given link to the loaded tag - * - * @param int $linkid - * @return void - */ - public function setRelation(int $linkid) { - if(!empty($linkid) && !empty($this->_id)) { - $queryStr = "INSERT IGNORE INTO `".DB_PREFIX."_tagrelation` - SET `linkid` = '".$this->DB->real_escape_string($linkid)."', - `tagid` = '".$this->DB->real_escape_string($this->_id)."'"; - $this->DB->query($queryStr); - } - } + } catch (Exception $e) { + Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage()); + } + } + + return $this->_id; + } + + /** + * return all or data fpr given key on the current loaded tag + * + * @param bool $key + * @return array|string + */ + public function getData($key=false) { + $ret = $this->_data; + + if(!empty($key) && isset($this->_data[$key])) { + $ret = $this->_data[$key]; + } + + return $ret; + } + + /** + * set the relation to the given link to the loaded tag + * + * @param int $linkid + * @return void + */ + public function setRelation(int $linkid): void { + if(!empty($linkid) && !empty($this->_id)) { + $queryStr = "INSERT IGNORE INTO `".DB_PREFIX."_tagrelation` + SET `linkid` = '".$this->DB->real_escape_string($linkid)."', + `tagid` = '".$this->DB->real_escape_string($this->_id)."'"; + if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr)); + + try { + $this->DB->query($queryStr); + } catch (Exception $e) { + Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage()); + } + } + } /** * Return an array of any linkid related to the current loaded tag - * + * * @return array */ - public function getReleations(): array { - $ret = array(); - - $queryStr = "SELECT linkid - FROM `".DB_PREFIX."_tagrelation` - WHERE tagid = '".$this->DB->real_escape_string($this->_id)."'"; - $query = $this->DB->query($queryStr); - if(!empty($query) && $query->num_rows > 0) { - while($result = $query->fetch_assoc()) { - $ret[] = $result['linkid']; + public function getReleations(): array { + $ret = array(); + + $queryStr = "SELECT linkid + FROM `".DB_PREFIX."_tagrelation` + WHERE tagid = '".$this->DB->real_escape_string($this->_id)."'"; + + if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr)); + + try { + $query = $this->DB->query($queryStr); + if(!empty($query) && $query->num_rows > 0) { + while($result = $query->fetch_assoc()) { + $ret[] = $result['linkid']; + } + } + } catch (Exception $e) { + Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage()); + } + + return $ret; + } + + /** + * deletes the current loaded tag from db + * + * @return boolean + */ + public function delete(): bool { + $ret = false; + + if(!empty($this->_id)) { + $this->DB->begin_transaction(MYSQLI_TRANS_START_READ_WRITE); + + try { + $queryStr = "DELETE + FROM `".DB_PREFIX."_tagrelation` + WHERE `tagid` = '".$this->DB->real_escape_string($this->_id)."'"; + $this->DB->query($queryStr); + + $queryStr = "DELETE + FROM `".DB_PREFIX."_tag` + WHERE `id` = '".$this->DB->real_escape_string($this->_id)."'"; + + if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr)); + + $this->DB->query($queryStr); + $this->DB->commit(); + } catch (Exception $e) { + Summoner::sysLog('[ERROR] Failed to remove tag: '.var_export($e->getMessage(),true)); + + $this->DB->rollback(); } } return $ret; } - /** - * deletes the current loaded tag from db - * - * @return boolean - */ - public function delete(): bool { - $ret = false; - - if(!empty($this->_id)) { - $this->DB->begin_transaction(MYSQLI_TRANS_START_READ_WRITE); - - try { - $queryStr = "DELETE - FROM `".DB_PREFIX."_tagrelation` - WHERE `tagid` = '".$this->DB->real_escape_string($this->_id)."'"; - $this->DB->query($queryStr); - - $queryStr = "DELETE - FROM `".DB_PREFIX."_tag` - WHERE `id` = '".$this->DB->real_escape_string($this->_id)."'"; - $this->DB->query($queryStr); - - $this->DB->commit(); - } catch (Exception $e) { - if(DEBUG) { - var_dump($e->getMessage()); - } - error_log('ERROR Failed to remove tag: '.var_export($e->getMessage(),true)); - - $this->DB->rollback(); - } - } - - return $ret; - } - - /** - * Rename current loaded tag name - * - * @param string $newValue - * @return void - */ - public function rename(string $newValue) { - if(!empty($newValue)) { - $queryStr = "UPDATE `".DB_PREFIX."_tag` - SET `name` = '".$this->DB->real_escape_string($newValue)."' - WHERE `id` = '".$this->DB->real_escape_string($this->_id)."'"; - $this->DB->query($queryStr); + /** + * Rename current loaded tag name + * + * @param string $newValue + * @return void + */ + public function rename(string $newValue): void { + if(!empty($newValue)) { + $queryStr = "UPDATE `".DB_PREFIX."_tag` + SET `name` = '".$this->DB->real_escape_string($newValue)."' + WHERE `id` = '".$this->DB->real_escape_string($this->_id)."'"; + + if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr)); + + try { + $this->DB->query($queryStr); + } catch (Exception $e) { + Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage()); + } $this->_data['name'] = $newValue; } }