123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624 |
- <?php
- /**
- * Insipid
- * Personal web-bookmark-system
- *
- * Copyright 2016-2023 Johannes Keßler
- *
- * Development starting from 2011: Johannes Keßler
- * https://www.bananas-playground.net/projekt/insipid/
- *
- * creator:
- * Luke Reeves <luke@neuro-tech.net>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see http://www.gnu.org/licenses/gpl-3.0.
- *
- */
- /**
- * Class Link
- */
- class Link {
- /**
- * the database object
- *
- * @var mysqli
- */
- private mysqli $DB;
- /**
- * the current loaded link data
- *
- * @var array
- */
- private array $_data;
- /**
- * Link constructor.
- *
- * @param mysqli $databaseConnectionObject
- */
- public function __construct(mysqli $databaseConnectionObject) {
- $this->DB = $databaseConnectionObject;
- }
- /**
- * load all the info we have about a link by given hash
- *
- * @param string $hash
- * @return array
- */
- public function load(string $hash): array {
- $this->_data = array();
- if (!empty($hash)) {
- $queryStr = "SELECT
- `id`,
- `link`,
- `created`,
- `updated`,
- `status`,
- `description`,
- `title`,
- `image`,
- `hash`
- FROM `".DB_PREFIX."_link`
- WHERE `hash` = '" . $this->DB->real_escape_string($hash) . "'";
- if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr));
- try {
- $query = $this->DB->query($queryStr);
- if (!empty($query) && $query->num_rows == 1) {
- $this->_data = $query->fetch_assoc();
- # add stuff
- $this->_tags();
- $this->_categories();
- $this->_image();
- $this->_private();
- $this->_snapshot();
- $this->_pageScreenshot();
- }
- } catch (Exception $e) {
- Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
- }
- }
- return $this->_data;
- }
- /**
- * loads only the info needed to display the link
- * for edit use $this->load
- *
- * @param string $hash
- * @return array
- */
- public function loadShortInfo(string $hash): array {
- $this->_data = array();
- if (!empty($hash)) {
- $queryStr = "SELECT `id`,`link`,`description`,`title`,`image`,`hash`, `created`
- FROM `".DB_PREFIX."_link`
- WHERE `hash` = '" . $this->DB->real_escape_string($hash) . "'";
- if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr));
- try {
- $query = $this->DB->query($queryStr);
- if (!empty($query) && $query->num_rows == 1) {
- $this->_data = $query->fetch_assoc();
- # add stuff
- $this->_image();
- }
- } catch (Exception $e) {
- Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
- }
- }
- return $this->_data;
- }
- /**
- * Get shortinfo from given data array
- *
- * @param array $data
- * @return array
- */
- public function loadFromDataShortInfo(array $data): array {
- $this->_data = array();
- if(isset($data['id']) && isset($data['link']) && isset($data['created']) && isset($data['status'])
- && isset($data['title']) && isset($data['hash']) && isset($data['description']) && isset($data['image'])) {
- $this->_data = $data;
- $this->_image();
- }
- return $this->_data;
- }
- /**
- * return all or data for given key on the current loaded link
- *
- * @param string $key
- * @return string|array
- */
- public function getData(string $key = ''): string|array {
- $ret = $this->_data;
- if (!empty($key) && isset($this->_data[$key])) {
- $ret = $this->_data[$key];
- }
- return $ret;
- }
- /**
- * reload the current id from DB
- *
- * @return void
- */
- public function reload(): void {
- $this->load($this->_data['hash']);
- }
- /**
- * create a new link with the given data
- *
- * @param array $data
- * @param bool $returnId
- * @return string
- */
- public function create(array $data, bool $returnId = false): string {
- $ret = '';
- if (!isset($data['link']) || empty($data['link'])) return $ret;
- if (!isset($data['hash']) || empty($data['hash'])) return $ret;
- if (!isset($data['title']) || empty($data['title'])) return $ret;
- $_t = parse_url($data['link']);
- $data['search'] = $data['title'];
- $data['search'] .= ' '.$data['description'];
- $data['search'] .= ' '.implode(" ",$data['tagArr']);
- $data['search'] .= ' '.implode(" ",$data['catArr']);
- $data['search'] .= ' '.$_t['host'];
- $data['search'] .= ' '.implode(' ',explode('/',$_t['path']));
- $data['search'] = trim($data['search']);
- $data['search'] = strtolower($data['search']);
- $queryStr = "INSERT INTO `" . DB_PREFIX . "_link` SET
- `link` = '" . $this->DB->real_escape_string($data['link']) . "',
- `created` = NOW(),
- `status` = '" . $this->DB->real_escape_string($data['status']) . "',
- `description` = '" . $this->DB->real_escape_string($data['description']) . "',
- `title` = '" . $this->DB->real_escape_string($data['title']) . "',
- `image` = '" . $this->DB->real_escape_string($data['image']) . "',
- `hash` = '" . $this->DB->real_escape_string($data['hash']) . "',
- `search` = '" . $this->DB->real_escape_string($data['search']) . "'";
- if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr));
- try {
- $this->DB->query($queryStr);
- if ($returnId === true) {
- $ret = $this->DB->insert_id;
- }
- else {
- Summoner::sysLog('ERROR Failed to create link: '.var_export($data,true));
- }
- } catch (Exception $e) {
- Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
- }
- return $ret;
- }
- /**
- * update the current loaded link with the given data
- *
- * @param array $data
- * @return boolean
- */
- public function update(array $data): bool {
- $ret = false;
- if (isset($data['title']) && !empty($data['title']) && !empty($this->_data)) {
- # categories and tag stuff
- $catArr = Summoner::prepareTagOrCategoryStr($data['category']);
- $tagArr = Summoner::prepareTagOrCategoryStr($data['tag']);
- $_t = parse_url($this->_data['link']);
- $search = $data['title'];
- $search .= ' '.$data['description'];
- $search .= ' '.implode(" ", $tagArr);
- $search .= ' '.implode(" ", $catArr);
- $search .= ' '.$_t['host'];
- if(isset($_t['path'])) {
- $search .= ' '.implode(' ',explode('/',$_t['path']));
- }
- $search = trim($search);
- $search = strtolower($search);
- # did the image url change?
- $_imageUrlChanged = false;
- if ($this->_data['image'] != $data['image']) {
- $_imageUrlChanged = true;
- }
- $queryStr = "UPDATE `" . DB_PREFIX . "_link` SET
- `status` = '" . $this->DB->real_escape_string($data['private']) . "',
- `description` = '" . $this->DB->real_escape_string($data['description']) . "',
- `title` = '" . $this->DB->real_escape_string($data['title']) . "',
- `image` = '" . $this->DB->real_escape_string($data['image']) . "',
- `search` = '" . $this->DB->real_escape_string($search) . "'
- WHERE `hash` = '" . $this->DB->real_escape_string($this->_data['hash']) . "'";
- if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr));
- $this->DB->begin_transaction(MYSQLI_TRANS_START_READ_WRITE);
- try {
- $query = $this->DB->query($queryStr);
- } catch (Exception $e) {
- Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
- }
- if ($query !== false) {
- $catObj = new Category($this->DB);
- $tagObj = new Tag($this->DB);
- // clean the relations first
- $this->_removeTagRelation();
- $this->_removeCategoryRelation();
- if (!empty($catArr)) {
- foreach ($catArr as $c) {
- $catObj->initbystring($c);
- $catObj->setRelation($this->_data['id']);
- }
- }
- if (!empty($tagArr)) {
- foreach ($tagArr as $t) {
- $tagObj->initbystring($t);
- $tagObj->setRelation($this->_data['id']);
- }
- }
- $this->DB->commit();
- # decide to store or remove the image
- if (isset($data['localImage'])) {
- $image = ABSOLUTE_PATH . '/' . LOCAL_STORAGE . '/thumbnail-' . $this->_data['hash'].'.jpg';
- if(DEBUG) Summoner::sysLog("DEBUG Try to save local image to: $image");
- if ($data['localImage'] === true) {
- if(DEBUG) Summoner::sysLog("DEBUG want to save local image to: $image");
- if (!file_exists($image) || $_imageUrlChanged === true) {
- if(DEBUG) Summoner::sysLog("DEBUG Image new or not there yet: $image");
- Summoner::downloadFile($data['image'], $image);
- }
- } elseif ($data['localImage'] === false) {
- if(DEBUG) Summoner::sysLog("DEBUG Image to be removed: $image");
- if (file_exists($image)) {
- unlink($image);
- }
- }
- }
- # decide if we want to make a local snapshot
- if(isset($data['snapshot'])) {
- $snapshot = ABSOLUTE_PATH . '/' . LOCAL_STORAGE . '/snapshot-' . $this->_data['hash'].'.jpg';
- if ($data['snapshot'] === true) {
- if (!file_exists($snapshot) || $_imageUrlChanged === true) {
- require_once 'lib/snapshot.class.php';
- $snap = new Snapshot();
- $do = $snap->doSnapshot($this->_data['link'], $snapshot);
- if(empty($do)) {
- Summoner::sysLog('ERROR Failed to create snapshot: '.var_export($data,true));
- }
- }
- } elseif ($data['snapshot'] === false) {
- if (file_exists($snapshot)) {
- unlink($snapshot);
- }
- }
- }
- # decide if we want to make a local full page screenshot
- if(isset($data['pagescreenshot'])) {
- $pagescreenshot = ABSOLUTE_PATH . '/' . LOCAL_STORAGE . '/pagescreenshot-' . $this->_data['hash'].'.jpg';
- if ($data['pagescreenshot'] === true) {
- if (!file_exists($pagescreenshot) || $_imageUrlChanged === true) {
- require_once 'lib/snapshot.class.php';
- $snap = new Snapshot();
- $do = $snap->wholePageSnapshot($this->_data['link'], $pagescreenshot);
- if(!empty($do)) {
- Summoner::sysLog('ERROR Failed to create snapshot: '.var_export($data,true));
- }
- }
- } elseif ($data['pagescreenshot'] === false) {
- if (file_exists($pagescreenshot)) {
- unlink($pagescreenshot);
- }
- }
- }
- $ret = true;
- } else {
- $this->DB->rollback();
- Summoner::sysLog('ERROR Failed to update link: '.var_export($data,true));
- }
- }
- return $ret;
- }
- /**
- * call this to delete all the relations to this link.
- * To completely remove the link use Management->deleteLink()
- *
- * @return void
- */
- public function deleteRelations(): void {
- $this->_removeTagRelation();
- $this->_removeCategoryRelation();
- $this->_deleteImage();
- $this->_deleteSnapshot();
- $this->_deletePageScreenshot();
- }
- /**
- * load all the tags we have to the already loaded link
- * needs $this->load called first
- *
- * @return void
- */
- private function _tags(): void {
- $ret = array();
- if (!empty($this->_data['hash'])) {
- $queryStr = "SELECT
- DISTINCT tag, tagId
- FROM `" . DB_PREFIX . "_combined`
- WHERE `hash` = '" . $this->DB->real_escape_string($this->_data['hash']) . "'";
- 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()) {
- if ($result['tag'] !== NULL) {
- $ret[$result['tagId']] = $result['tag'];
- }
- }
- }
- } catch (Exception $e) {
- Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
- }
- }
- $this->_data['tags'] = $ret;
- }
- /**
- * load all the categories we have to the already loaded link
- * needs $this->load called first
- *
- * @return void
- */
- private function _categories(): void {
- $ret = array();
- if (!empty($this->_data['hash'])) {
- $queryStr = "SELECT
- DISTINCT category, categoryId
- FROM `" . DB_PREFIX . "_combined`
- WHERE `hash` = '" . $this->DB->real_escape_string($this->_data['hash']) . "'";
- 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()) {
- if ($result['category'] !== NULL) {
- $ret[$result['categoryId']] = $result['category'];
- }
- }
- }
- } catch (Exception $e) {
- Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
- }
- }
- $this->_data['categories'] = $ret;
- }
- /**
- * remove all or given tag relation to the current loaded link
- *
- * @param string $tagid
- * @return void
- */
- private function _removeTagRelation(string $tagid = ''): void {
- if (!empty($this->_data['id'])) {
- $queryStr = '';
- if (is_numeric($tagid)) {
- $queryStr = "DELETE
- FROM `" . DB_PREFIX . "_tagrelation`
- WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "'
- AND `tagid` = '" . $this->DB->real_escape_string($tagid) . "'";
- } else {
- $queryStr = "DELETE
- FROM `" . DB_PREFIX . "_tagrelation`
- WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "'";
- }
- if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr));
- if (!empty($queryStr)) {
- try {
- $this->DB->query($queryStr);
- } catch (Exception $e) {
- Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
- }
- }
- }
- }
- /**
- * remove all or given category relation to the current loaded link
- *
- * @param string $categoryid
- * @return void
- */
- private function _removeCategoryRelation(string $categoryid=''): void {
- if (!empty($this->_data['id'])) {
- $queryStr = '';
- if (is_numeric($categoryid)) {
- $queryStr = "DELETE
- FROM `" . DB_PREFIX . "_categoryrelation`
- WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "'
- AND `categoryid` = '" . $this->DB->real_escape_string($categoryid) . "'";
- } else {
- $queryStr = "DELETE
- FROM `" . DB_PREFIX . "_categoryrelation`
- WHERE `linkid` = '" . $this->DB->real_escape_string($this->_data['id']) . "'";
- }
- if(QUERY_DEBUG) Summoner::sysLog("[QUERY] ".__METHOD__." query: ".Summoner::cleanForLog($queryStr));
- if (!empty($queryStr)) {
- try {
- $this->DB->query($queryStr);
- } catch (Exception $e) {
- Summoner::sysLog("[ERROR] ".__METHOD__." mysql catch: ".$e->getMessage());
- }
- }
- }
- }
- /**
- * determine of we have a local stored image
- * if so populate the localImage attribute
- *
- * @return void
- */
- private function _image(): void {
- if (!empty($this->_data['hash'])) {
- $this->_data['imageToShow'] = $this->_data['image'];
- $image = ABSOLUTE_PATH.'/'.LOCAL_STORAGE.'/thumbnail-'.$this->_data['hash'].'.jpg';
- if (file_exists($image)) {
- $this->_data['imageToShow'] = LOCAL_STORAGE.'/thumbnail-'.$this->_data['hash'].'.jpg';
- $this->_data['localImage'] = true;
- }
- }
- }
- /**
- * determine if we have a local stored snapshot
- * if so populate the snapshotLink attribute
- *
- * @return void
- */
- private function _snapshot(): void {
- if (!empty($this->_data['hash'])) {
- $snapshot = ABSOLUTE_PATH.'/'.LOCAL_STORAGE.'/snapshot-'.$this->_data['hash'].'.jpg';
- if (file_exists($snapshot)) {
- $this->_data['snapshotLink'] = LOCAL_STORAGE.'/snapshot-'.$this->_data['hash'].'.jpg';
- $this->_data['snapshot'] = true;
- }
- }
- }
- /**
- * determine if we have a local full page screenshot
- * if so populate the pagescreenshotLink attribute
- *
- * @return void
- */
- private function _pageScreenshot(): void {
- if (!empty($this->_data['hash'])) {
- $pagescreenshot = ABSOLUTE_PATH.'/'.LOCAL_STORAGE.'/pagescreenshot-'.$this->_data['hash'].'.jpg';
- if (file_exists($pagescreenshot)) {
- $this->_data['pagescreenshotLink'] = LOCAL_STORAGE.'/pagescreenshot-'.$this->_data['hash'].'.jpg';
- $this->_data['pagescreenshot'] = true;
- }
- }
- }
- /**
- * remove the local stored image
- *
- * @return void
- */
- private function _deleteImage(): void {
- if (!empty($this->_data['hash']) && !empty($this->_data['imageToShow'])) {
- $image = ABSOLUTE_PATH.'/'.$this->_data['imageToShow'];
- if (file_exists($image)) {
- unlink($image);
- }
- }
- }
- /**
- * remove the local stored snapshot
- *
- * @return void
- */
- private function _deleteSnapshot(): void {
- if (!empty($this->_data['hash']) && !empty($this->_data['snapshotLink'])) {
- $snapshot = LOCAL_STORAGE.'/snapshot-'.$this->_data['hash'].'.jpg';
- if (file_exists($snapshot)) {
- unlink($snapshot);
- }
- }
- }
- /**
- * remove the local stored pagescreenshot
- *
- * @return void
- */
- private function _deletePageScreenshot(): void {
- if (!empty($this->_data['hash']) && !empty($this->_data['pagescreenshotLink'])) {
- $pagescreenshot = LOCAL_STORAGE.'/pagescreenshot-'.$this->_data['hash'].'.jpg';
- if (file_exists($pagescreenshot)) {
- unlink($pagescreenshot);
- }
- }
- }
- /**
- * check if the status is private and set the info
- *
- * @return void
- */
- private function _private(): void {
- if (!empty($this->_data['status']) && $this->_data['status'] == "1") {
- $this->_data['private'] = "1";
- }
- }
- }
|