first commit

This commit is contained in:
Sven Wappler
2021-08-16 16:15:21 +02:00
commit fde2759722
32 changed files with 1841 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
<?php
/*
* This file is part of the package buepro/bookmark_pages.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/
namespace WapplerSystems\BookmarkPages\Controller;
use WapplerSystems\BookmarkPages\Model\Bookmark;
use WapplerSystems\BookmarkPages\Model\Bookmarks;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
/**
* Plugin controller
*/
class BookmarksController extends ActionController
{
/**
* display bookmarks list
*/
public function indexAction()
{
$bookmark = Bookmark::createFromCurrent();
$this->view->assignMultiple([
'bookmark' => $bookmark->toArray()
]);
}
/**
* Adds the current page as bookmark and renders/returns updated list as html
*
* This is meant to be called by ajax (typoscript_rendering)
*
* @param array $localBookmarks
*/
public function bookmarkAction($localBookmarks = [])
{
// use the parameter directly and ignore chash because url is submitted by JS
$url = GeneralUtility::_GP('url');
$url = $url ? $url : null;
$bookmark = Bookmark::createFromCurrent($url);
$bookmarks = new Bookmarks();
$bookmarks->merge($localBookmarks);
$bookmarks->addBookmark($bookmark);
$bookmarks->persist();
$this->updateAndSendList($bookmarks);
}
/**
* Remove a bookmark from list and renders/returns updated list as html
*
* This is meant to be called by ajax (typoscript_rendering)
*
* @param string $id
* @param array $localBookmarks
*/
public function deleteAction($id = '', $localBookmarks = [])
{
$bookmarks = new Bookmarks();
$bookmarks->merge($localBookmarks);
if ($id) {
$bookmarks->removeBookmark($id);
$bookmarks->persist();
}
$this->updateAndSendList($bookmarks);
}
/**
* Action to get bookmark list
*
* @param array $localBookmarks
*/
public function listEntriesAction($localBookmarks = [])
{
$bookmarks = new Bookmarks();
$bookmarks->merge($localBookmarks);
$this->updateAndSendList($bookmarks);
}
/**
* This is for ajax requests
*
* @param Bookmarks $bookmarks
*/
public function updateAndSendList(Bookmarks $bookmarks)
{
// check if we bookmarked the current page
$bookmark = Bookmark::createFromCurrent();
$isBookmarked = $bookmarks->bookmarkExists($bookmark);
// build the ajax response data
$response = [
'isBookmarked' => $isBookmarked,
'bookmarks' => $bookmarks->getBookmarksForLocalStorage()
];
header('Content-Type: application/json');
echo json_encode($response);
die();
}
}

237
Classes/Model/Bookmark.php Normal file
View File

@@ -0,0 +1,237 @@
<?php
/*
* This file is part of the package buepro/bookmark_pages.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/
namespace WapplerSystems\BookmarkPages\Model;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
*
*/
class Bookmark
{
/**
* @var string
*/
protected $id;
/**
* @var string
*/
protected $title;
/**
* @var string
*/
protected $url;
/**
* @var int
*/
protected $pid;
/**
* @var string
*/
protected $parameter;
/**
* Bookmark constructor.
* Initialize the bookmark with data
*
* @param string|array $url Full url or bookmark data array (same as array from toArray())
* @param null $title
* @param null $pid page id
* @param null $parameter
*/
public function __construct($url, $title=null, $pid=null, $parameter=null)
{
if (is_array($url)) {
$this->id = $url['id'];
$this->title = $url['title'];
$this->url = $url['url'];
$this->pid = $url['pid'];
$this->parameter = $url['parameter'];
} else {
$this->id = md5($pid . ':' . $parameter);
$this->title = $title;
$this->url = $url;
$this->pid = $pid;
$this->parameter = $parameter;
}
}
/**
* Create bookmark from the current TSFE page
*
* @param string url to bookmark, if null TYPO3_REQUEST_URL will be used - which is wrong when we're in ajax context, then we use HTTP_REFERER
* @return Bookmark
*/
public static function createFromCurrent($url = null)
{
if ($url === null) {
if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
//request is ajax
$url = GeneralUtility::getIndpEnv('HTTP_REFERER');
} else {
$url = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
}
}
$pid = self::getFrontend()->id;
$title = self::getCurrentPageTitle();
/*
The idea was to store get parameters to make bookmark handling more flexible.
Unfortunately that didn't worked out.
When we use ajax to trigger bookmarking the current page, we can pass the current url as parameter.
But the url doesn't have the parameters in it when you use speaking urls (realurl, simulatestatic, ...).
The problem is that there's no common api to decode urls and get the parameters.
One solution would be to make the parameters available to the ajax javascript during page rendering.
We skip all this and use a bit from the url for hashing and add the page id.
*/
$urlParts = parse_url($url);
$parameter = $urlParts['path'] . '?' . $urlParts['query'] . '#' . $urlParts['fragment'];
return new self($url, $title, $pid, $parameter);
/*
* So what is the idea of storing the pid and the get vars?
*
* This might makes sense if urls changed for the same page (realurl).
* With this information the new working url can be restored.
*
* Not sure which way is better ...
*/
// $parameter = (array)GeneralUtility::_GET();
// unset($parameter['id']);
// // @todo remove cHash?
// ksort($parameter);
// $parameter = $parameter ? GeneralUtility::implodeArrayForUrl(false, $parameter) : '';
//
// return new self($url, $title, $pid, $parameter);
}
/**
* @return string
*/
public function getId()
{
return $this->id;
}
/**
* @param string $id
*/
public function setId($id)
{
$this->id = $id;
}
/**
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* @param string $title
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* @return string
*/
public function getUrl()
{
return $this->url;
}
/**
* @param string $url
*/
public function setUrl($url)
{
$this->url = $url;
}
/**
* @return int
*/
public function getPid()
{
return $this->pid;
}
/**
* @param int $pid
*/
public function setPid($pid)
{
$this->pid = $pid;
}
/**
* @return string
*/
public function getParameter()
{
return $this->parameter;
}
/**
* @param string $parameter
*/
public function setParameter($parameter)
{
$this->parameter = $parameter;
}
/**
* Returns the bookmark data as array
*
* @return array
*/
public function toArray()
{
return [
'id' => $this->id,
'title' => $this->title,
'url' => $this->url,
'pid' => $this->pid,
'parameter' => $this->parameter,
];
}
/**
* Get the current page title
* @return string
*/
protected static function getCurrentPageTitle()
{
return self::getFrontend()->altPageTitle? self::getFrontend()->altPageTitle : self::getFrontend()->page['title'];
}
/**
*
* @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController
*/
protected static function getFrontend()
{
return $GLOBALS['TSFE'];
}
}

241
Classes/Model/Bookmarks.php Normal file
View File

@@ -0,0 +1,241 @@
<?php
/*
* This file is part of the package buepro/bookmark_pages.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/
namespace WapplerSystems\BookmarkPages\Model;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Provide access to a list of Bookmark
*/
class Bookmarks
{
/**
* column in db
*/
const BOOKMARKS_COLUMN = 'tx_bookmarks_pages';
/**
* @var array|Bookmark[]
*/
protected $bookmarks = [];
/**
* @var bool Flag if bookmarks has changed an need to be saved
*/
protected $changeFlag = false;
/**
* Initialize bookmarks
*/
public function __construct()
{
// is login user?
if (is_array($this->getUser()->user) && $this->getUser()->user[$this->getUser()->userid_column]) {
$bookmarks = $this->getUser()->user[self::BOOKMARKS_COLUMN];
$bookmarks = (array)GeneralUtility::xml2array($bookmarks);
foreach ($bookmarks as $bookmark) {
if (isset($bookmark['id'])) {
$this->bookmarks[$bookmark['id']] = new Bookmark($bookmark);
}
}
}
}
/**
* persist bookmarks if needed
*/
public function __destruct()
{
$this->persist();
}
/**
* Get all Bookmarks
*
* @return array|Bookmark[]
*/
public function getBookmarks()
{
return (array)$this->bookmarks;
}
/**
* clear all bookmarks
*/
public function clearBookmarks()
{
$this->bookmarks = [];
$this->changeFlag = true;
}
/**
* Add a bookmark
*
* @param Bookmark $bookmark
*/
public function addBookmark(Bookmark $bookmark)
{
$this->bookmarks[$bookmark->getId()] = $bookmark;
$this->changeFlag = true;
}
/**
* Get Bookmark by given id
*
* @param string $id
* @return Bookmark|mixed
*/
public function getBookmark($id)
{
return $this->bookmarks[$id];
}
/**
* Check if a given bookmark is stored already
*
* @param Bookmark $bookmark
* @return bool
*/
public function bookmarkExists(Bookmark $bookmark)
{
return isset($this->bookmarks[$bookmark->getId()]);
}
/**
* Remove bookmark by given id
*
* @param string $id
*/
public function removeBookmark($id)
{
unset($this->bookmarks[$id]);
$this->changeFlag = true;
}
/**
* persist bookmarks if needed
*/
public function persist()
{
if ($this->changeFlag && is_array($this->getUser()->user) && $this->getUser()->user[$this->getUser()->userid_column]) {
$bookmarks = [];
foreach ($this->bookmarks as $bookmark) {
$bookmarks[] = $bookmark->toArray();
}
/*
* Why xml?
*
* Why not! You can even process it in the db if you like
* (And dooon't tell me json would be a good idea, or serialized php ... haaahaaaaaa)
*/
$bookMarksXml = GeneralUtility::array2xml($bookmarks);
/** @var QueryBuilder $queryBuilder */
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable($this->getUser()->user_table);
$queryBuilder
->update($this->getUser()->user_table)
->where(
$queryBuilder->expr()->eq(
$this->getUser()->userid_column,
$queryBuilder->createNamedParameter((int)$this->getUser()->user[$this->getUser()->userid_column], \PDO::PARAM_INT)
)
)
->set(self::BOOKMARKS_COLUMN, $bookMarksXml)
->execute();
$this->changeFlag = false;
}
}
/**
* Get global frontend user
* @return \TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
*/
protected function getUser()
{
return $GLOBALS['TSFE']->fe_user;
}
/**
* @return array
*/
private function getAccessibleBookmarks()
{
$bookmarks = $this->getBookmarks();
if (!$bookmarks) {
return [];
}
// Create an array association the page uid with the bookmark id (uid => id)
$pageMap = array_flip(array_map(static function ($bookmark) {
return (int) $bookmark->getPid();
}, $bookmarks));
// Get accessible pages
/** @var QueryBuilder $queryBuilder */
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
$queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
$pages = $queryBuilder
->select('uid')
->from('pages')
->where($queryBuilder->expr()->in('uid', array_keys($pageMap)))
->execute()
->fetchAll();
// Collect accessible bookmarks
$accessibleBookmarks = [];
foreach ($pages as $page) {
if (isset($pageMap[$page['uid']])) {
$accessibleBookmarks[$pageMap[$page['uid']]] = $bookmarks[$pageMap[$page['uid']]];
}
}
// Order the bookmarks (same sequence as at the beginning)
return array_intersect_key($bookmarks, $accessibleBookmarks);
}
/**
* Merge bookmarks into the current ones.
*
* @param $bookmarks
* @return array|Bookmark[]
*/
public function merge($bookmarks)
{
$bookmarksChanged = false;
foreach ($bookmarks as $id => $bookmark) {
if (!isset($this->bookmarks[$id])) {
$bookmarksChanged = true;
$this->bookmarks[$id] = new Bookmark($bookmark);
}
}
if ($bookmarksChanged) {
$this->persist();
}
return $this->getBookmarks();
}
/**
* Get bookmarks for local storage in browser
*/
public function getBookmarksForLocalStorage(): array
{
$result = [];
foreach ($this->getAccessibleBookmarks() as $bookmark) {
$result[$bookmark->getId()] = $bookmark->toArray();
}
return $result;
}
}