From 03aedc3020096f678273b5eebf4a7b6b6517372c Mon Sep 17 00:00:00 2001 From: Joey Kimsey Date: Tue, 3 Dec 2024 00:16:51 -0500 Subject: [PATCH] add memberships and bookmarks --- .../bookmarks/controllers/bookmarks.php | 402 ++++++++++ app/plugins/bookmarks/forms.php | 122 +++ app/plugins/bookmarks/models/bookmarks.php | 705 ++++++++++++++++++ .../bookmarks/models/bookmarkviews.php | 149 ++++ app/plugins/bookmarks/models/folders.php | 157 ++++ app/plugins/bookmarks/plugin.php | 62 ++ .../bookmarks/views/bookmarks/create.html | 45 ++ .../bookmarks/views/bookmarks/edit.html | 45 ++ .../bookmarks/views/bookmarks/list.html | 37 + .../bookmarks/views/bookmarks/view.html | 84 +++ .../views/components/bookmarkListPanel.html | 21 + .../views/components/bookmarkListRows.html | 18 + .../views/components/folderPanelList.html | 10 + app/plugins/bookmarks/views/dash.html | 16 + .../bookmarks/views/folders/create.html | 39 + app/plugins/bookmarks/views/folders/edit.html | 39 + app/plugins/bookmarks/views/folders/list.html | 33 + app/plugins/bookmarks/views/folders/view.html | 47 ++ .../bookmarks/views/nav/folderTabs.html | 5 + .../bookmarks/views/nav/userFolderTabs.html | 9 + .../members/controllers/admin/member.php | 50 ++ app/plugins/members/controllers/member.php | 36 + app/plugins/members/models/members.php | 36 + .../members/models/membershipassociations.php | 33 + .../members/models/membershiprecords.php | 32 + app/plugins/members/plugin.php | 75 ++ app/plugins/members/views/admin/list.html | 1 + app/plugins/members/views/members.html | 6 + 28 files changed, 2314 insertions(+) create mode 100644 app/plugins/bookmarks/controllers/bookmarks.php create mode 100644 app/plugins/bookmarks/forms.php create mode 100644 app/plugins/bookmarks/models/bookmarks.php create mode 100644 app/plugins/bookmarks/models/bookmarkviews.php create mode 100644 app/plugins/bookmarks/models/folders.php create mode 100644 app/plugins/bookmarks/plugin.php create mode 100644 app/plugins/bookmarks/views/bookmarks/create.html create mode 100644 app/plugins/bookmarks/views/bookmarks/edit.html create mode 100644 app/plugins/bookmarks/views/bookmarks/list.html create mode 100644 app/plugins/bookmarks/views/bookmarks/view.html create mode 100644 app/plugins/bookmarks/views/components/bookmarkListPanel.html create mode 100644 app/plugins/bookmarks/views/components/bookmarkListRows.html create mode 100644 app/plugins/bookmarks/views/components/folderPanelList.html create mode 100644 app/plugins/bookmarks/views/dash.html create mode 100644 app/plugins/bookmarks/views/folders/create.html create mode 100644 app/plugins/bookmarks/views/folders/edit.html create mode 100644 app/plugins/bookmarks/views/folders/list.html create mode 100644 app/plugins/bookmarks/views/folders/view.html create mode 100644 app/plugins/bookmarks/views/nav/folderTabs.html create mode 100644 app/plugins/bookmarks/views/nav/userFolderTabs.html create mode 100644 app/plugins/members/controllers/admin/member.php create mode 100644 app/plugins/members/controllers/member.php create mode 100644 app/plugins/members/models/members.php create mode 100644 app/plugins/members/models/membershipassociations.php create mode 100644 app/plugins/members/models/membershiprecords.php create mode 100644 app/plugins/members/plugin.php create mode 100644 app/plugins/members/views/admin/list.html create mode 100644 app/plugins/members/views/members.html diff --git a/app/plugins/bookmarks/controllers/bookmarks.php b/app/plugins/bookmarks/controllers/bookmarks.php new file mode 100644 index 0000000..3641faa --- /dev/null +++ b/app/plugins/bookmarks/controllers/bookmarks.php @@ -0,0 +1,402 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Controllers; + +use TheTempusProject\Hermes\Functions\Redirect; +use TheTempusProject\Bedrock\Functions\Check; +use TheTempusProject\Bedrock\Functions\Input; +use TheTempusProject\Bedrock\Functions\Session; +use TheTempusProject\Houdini\Classes\Issues; +use TheTempusProject\Houdini\Classes\Views; +use TheTempusProject\Classes\Controller; +use TheTempusProject\Classes\Forms; +use TheTempusProject\Models\Bookmarks as Bookmark; +use TheTempusProject\Models\Folders; +use TheTempusProject\TheTempusProject as App; +use TheTempusProject\Houdini\Classes\Components; +use TheTempusProject\Houdini\Classes\Forms as HoudiniForms; +use TheTempusProject\Houdini\Classes\Navigation; + +class Bookmarks extends Controller { + protected static $bookmarks; + protected static $folders; + + public function __construct() { + parent::__construct(); + if ( !App::$isLoggedIn ) { + Session::flash( 'notice', 'You must be logged in to create or manage bookmarks.' ); + return Redirect::home(); + } + self::$bookmarks = new Bookmark; + self::$folders = new Folders; + self::$title = 'Bookmarks - {SITENAME}'; + self::$pageDescription = 'Add and save url bookmarks here.'; + + $folderTabs = Views::simpleView( 'bookmarks.nav.folderTabs' ); + if ( stripos( Input::get('url'), 'bookmarks/bookmarks' ) !== false ) { + $tabsView = Navigation::activePageSelect( $folderTabs, '/bookmarks/folders/', false, true ); + $userFolderTabs = Views::simpleView('bookmarks.nav.userFolderTabs', self::$folders->simpleObjectByUser(true) ); + $userFolderTabsView = Navigation::activePageSelect( $userFolderTabs, Input::get( 'url' ), false, true ); + } else { + $tabsView = Navigation::activePageSelect( $folderTabs, Input::get( 'url' ), false, true ); + $userFolderTabsView = ''; + } + Components::set( 'userFolderTabs', $userFolderTabsView ); + Views::raw( $tabsView ); + } + + public function index() { + $bookmarks = self::$bookmarks->noFolder(); + $folders = self::$folders->byUser(); + + $panelArray = []; + if ( !empty( $folders ) ) { + foreach ( $folders as $folder ) { + $panel = new \stdClass(); + $folderObject = new \stdClass(); + $folderObject->bookmarks = self::$bookmarks->byFolder( $folder->ID ); + $folderObject->ID = $folder->ID; + $folderObject->title = $folder->title; + $folderObject->color = $folder->color; + $folderObject->bookmarkListRows = Views::simpleView( 'bookmarks.components.bookmarkListRows', $folderObject->bookmarks ); + $panel->panel = Views::simpleView( 'bookmarks.components.bookmarkListPanel', [$folderObject] ); + $panelArray[] = $panel; + } + } + Components::set( 'foldersList', Views::simpleView( 'bookmarks.folders.list', $folders ) ); + Components::set( 'folderPanels', Views::simpleView( 'bookmarks.components.folderPanelList', $panelArray ) ); + Components::set( 'bookmarksList', Views::simpleView( 'bookmarks.bookmarks.list', $bookmarks ) ); + return Views::view( 'bookmarks.dash' ); + } + + /** + * Bookmarks + */ + public function bookmark( $id = 0 ) { + $bookmark = self::$bookmarks->findById( $id ); + if ( $bookmark == false ) { + Session::flash( 'error', 'Bookmark not found.' ); + return Redirect::to( 'bookmarks/index' ); + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + Session::flash( 'error', 'You do not have permission to modify this bookmark.' ); + return Redirect::to( 'bookmarks/index' ); + } + Navigation::setCrumbComponent( 'BookmarkBreadCrumbs', 'bookmarks/bookmark/' . $id ); + return Views::view( 'bookmarks.bookmarks.view', $bookmark ); + } + + public function bookmarks( $id = null ) { + $folder = self::$folders->findById( $id ); + if ( $folder == false ) { + Session::flash( 'error', 'Folder not found.' ); + return Redirect::to( 'bookmarks/index' ); + } + if ( $folder->createdBy != App::$activeUser->ID ) { + Session::flash( 'error', 'You do not have permission to view this folder.' ); + return Redirect::to( 'bookmarks/index' ); + } + Navigation::setCrumbComponent( 'BookmarkBreadCrumbs', 'bookmarks/bookmarks/' . $id ); + + + $bookmarks = self::$bookmarks->noFolder(); + + $panelArray = []; + $panel = new \stdClass(); + $folderObject = new \stdClass(); + $folderObject->bookmarks = self::$bookmarks->byFolder( $folder->ID ); + $folderObject->ID = $folder->ID; + $folderObject->title = $folder->title; + $folderObject->color = $folder->color; + $folderObject->bookmarkListRows = Views::simpleView( 'bookmarks.components.bookmarkListRows', $folderObject->bookmarks ); + $panel->panel = Views::simpleView( 'bookmarks.components.bookmarkListPanel', [$folderObject] ); + $panelArray[] = $panel; + + return Views::view( 'bookmarks.components.folderPanelList', $panelArray ); + } + + public function createBookmark( $id = null ) { + $folderID = Input::get('folder_id') ? Input::get('folder_id') : $id; + $folderID = Input::post('folder_id') ? Input::post('folder_id') : $id; + $folderSelect = HoudiniForms::getFormFieldHtml( 'folder_id', 'Folder', 'select', $folderID, self::$folders->simpleByUser() ); + Components::set( 'folderSelect', $folderSelect ); + + if ( ! Input::exists() ) { + return Views::view( 'bookmarks.bookmarks.create' ); + } + if ( ! Forms::check( 'createBookmark' ) ) { + Issues::add( 'error', [ 'There was an error with your form.' => Check::userErrors() ] ); + return Views::view( 'bookmarks.bookmarks.create' ); + } + + $result = self::$bookmarks->create( + Input::post('title'), + Input::post('url'), + $folderID, + Input::post('description'), + Input::post('color'), + Input::post('privacy'), + ); + + if ( ! $result ) { + Issues::add( 'error', [ 'There was an error creating your bookmark.' => Check::userErrors() ] ); + return Views::view( 'bookmarks.bookmarks.create' ); + } + self::$bookmarks->refreshInfo( $result ); + Session::flash( 'success', 'Your Bookmark has been created.' ); + Redirect::to( 'bookmarks/bookmarks/'. $folderID ); + } + + public function editBookmark( $id = null ) { + $folderID = Input::exists('folder_id') ? Input::post('folder_id') : ''; + + $bookmark = self::$bookmarks->findById( $id ); + if ( $bookmark == false ) { + Issues::add( 'error', 'Bookmark not found.' ); + return Redirect::to( 'bookmarks/index' ); + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + Issues::add( 'error', 'You do not have permission to modify this bookmark.' ); + return Redirect::to( 'bookmarks/index' ); + } + if ( empty( $folderID ) ) { + $folderID = $bookmark->folderID; + } + + $folderSelect = HoudiniForms::getFormFieldHtml( 'folder_id', 'Folder', 'select', $folderID, self::$folders->simpleByUser() ); + Components::set( 'folderSelect', $folderSelect ); + Components::set( 'color', $bookmark->color ); + + if ( ! Input::exists( 'submit' ) ) { + return Views::view( 'bookmarks.bookmarks.edit', $bookmark ); + } + if ( ! Forms::check( 'editBookmark' ) ) { + Issues::add( 'error', [ 'There was an error updating your bookmark.' => Check::userErrors() ] ); + return Views::view( 'bookmarks.bookmarks.edit', $bookmark ); + } + + $result = self::$bookmarks->update( + $id, + Input::post('title'), + Input::post('url'), + $folderID, + Input::post('description'), + Input::post('color'), + Input::post('privacy'), + ); + if ( ! $result ) { + Issues::add( 'error', [ 'There was an error updating your bookmark.' => Check::userErrors() ] ); + return Views::view( 'bookmarks.bookmarks.edit', $bookmark ); + } + Session::flash( 'success', 'Your Bookmark has been updated.' ); + Redirect::to( 'bookmarks/folders/'. $bookmark->folderID ); + } + + public function deleteBookmark( $id = null ) { + $bookmark = self::$bookmarks->findById( $id ); + if ( $bookmark == false ) { + Issues::add( 'error', 'Bookmark not found.' ); + return $this->index(); + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + Issues::add( 'error', 'You do not have permission to modify this bookmark.' ); + return $this->index(); + } + $result = self::$bookmarks->delete( $id ); + if ( !$result ) { + Session::flash( 'error', 'There was an error deleting the bookmark(s)' ); + } else { + Session::flash( 'success', 'Bookmark deleted' ); + } + Redirect::to( 'bookmarks/folders/'. $bookmark->folderID ); + } + + /** + * Folders + */ + public function folders( $id = null) { + $folder = self::$folders->findById( $id ); + if ( $folder == false ) { + $folders = self::$folders->byUser(); + return Views::view( 'bookmarks.folders.list', $folders ); + } + if ( $folder->createdBy != App::$activeUser->ID ) { + Session::flash( 'error', 'You do not have permission to view this folder.' ); + return Redirect::to( 'bookmarks/index' ); + } + Navigation::setCrumbComponent( 'BookmarkBreadCrumbs', 'bookmarks/folders/' . $id ); + return Views::view( 'bookmarks.folders.view', $folder ); + } + + public function createFolder( $id = 0 ) { + $folderID = Input::exists('folder_id') ? Input::post('folder_id') : $id; + $folders = self::$folders->simpleByUser(); + if ( ! empty( $folders ) ) { + $folderSelect = HoudiniForms::getFormFieldHtml( 'folder_id', 'Folder', 'select', $folderID, $folders ); + } else { + $folderSelect = ''; + } + Components::set( 'folderSelect', $folderSelect ); + if ( ! Input::exists() ) { + return Views::view( 'bookmarks.folders.create' ); + } + if ( ! Forms::check( 'createFolder' ) ) { + Issues::add( 'error', [ 'There was an error creating your folder.' => Check::userErrors() ] ); + return Views::view( 'bookmarks.folders.create' ); + } + $folder = self::$folders->create( Input::post('title'), $folderID, Input::post('description'), Input::post('color'), Input::post('privacy') ); + if ( ! $folder ) { + return Views::view( 'bookmarks.folders.create' ); + } + Session::flash( 'success', 'Your Folder has been created.' ); + Redirect::to( 'bookmarks/folders' ); + } + + public function editFolder( $id = null ) { + $folder = self::$folders->findById( $id ); + + if ( $folder == false ) { + Issues::add( 'error', 'Folder not found.' ); + return $this->index(); + } + + if ( $folder->createdBy != App::$activeUser->ID ) { + Issues::add( 'error', 'You do not have permission to modify this folder.' ); + return $this->index(); + } + $folderID = ( false === Input::exists('folder_id') ) ? $folder->ID : Input::post('folder_id'); + + $folderSelect = HoudiniForms::getFormFieldHtml( 'folder_id', 'Folder', 'select', $folderID, self::$folders->simpleByUser() ); + Components::set( 'folderSelect', $folderSelect ); + Components::set( 'color', $folder->color ); + + if ( ! Input::exists( 'submit' ) ) { + return Views::view( 'bookmarks.folders.edit', $folder ); + } + + if ( !Forms::check( 'editFolder' ) ) { + Issues::add( 'error', [ 'There was an error editing your folder.' => Check::userErrors() ] ); + return Views::view( 'bookmarks.folders.edit', $folder ); + } + + $result = self::$folders->update( $id, Input::post('title'), $folderID, Input::post('description'), Input::post('color'), Input::post('privacy') ); + if ( !$result ) { + Issues::add( 'error', [ 'There was an error updating your folder.' => Check::userErrors() ] ); + return Views::view( 'bookmarks.folders.edit', $folder ); + } + Session::flash( 'success', 'Your Folder has been updated.' ); + Redirect::to( 'bookmarks/folders/'. $folder->ID ); + } + + public function deleteFolder( $id = null ) { + $folder = self::$folders->findById( $id ); + if ( $folder == false ) { + Issues::add( 'error', 'Folder not found.' ); + return $this->index(); + } + if ( $folder->createdBy != App::$activeUser->ID ) { + Issues::add( 'error', 'You do not have permission to modify this folder.' ); + return $this->index(); + } + $results = self::$bookmarks->deleteByFolder( $id ); + $result = self::$folders->delete( $id ); + if ( !$result ) { + Session::flash( 'error', 'There was an error deleting the folder(s)' ); + } else { + Session::flash( 'success', 'Folder deleted' ); + } + Redirect::to( 'bookmarks/folders' ); + } + + /** + * Functionality + */ + public function hideBookmark( $id = null ) { + $bookmark = self::$bookmarks->findById( $id ); + if ( $bookmark == false ) { + Session::flash( 'error', 'Bookmark not found.' ); + return Redirect::to( 'bookmarks/index' ); + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + Session::flash( 'error', 'You do not have permission to modify this bookmark.' ); + return Redirect::to( 'bookmarks/index' ); + } + self::$bookmarks->hide( $id ); + Session::flash( 'success', 'Bookmark hidden.' ); + return Redirect::to( 'bookmarks/index' ); + } + + public function archiveBookmark( $id = null ) { + $bookmark = self::$bookmarks->findById( $id ); + if ( $bookmark == false ) { + Session::flash( 'error', 'Bookmark not found.' ); + return Redirect::to( 'bookmarks/index' ); + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + Session::flash( 'error', 'You do not have permission to modify this bookmark.' ); + return Redirect::to( 'bookmarks/index' ); + } + self::$bookmarks->archive( $id ); + Session::flash( 'success', 'Bookmark archived.' ); + return Redirect::to( 'bookmarks/index' ); + } + + public function showBookmark( $id = null ) { + $bookmark = self::$bookmarks->findById( $id ); + if ( $bookmark == false ) { + Session::flash( 'error', 'Bookmark not found.' ); + return Redirect::to( 'bookmarks/index' ); + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + Session::flash( 'error', 'You do not have permission to modify this bookmark.' ); + return Redirect::to( 'bookmarks/index' ); + } + self::$bookmarks->show( $id ); + Session::flash( 'success', 'Bookmark shown.' ); + return Redirect::to( 'bookmarks/index' ); + } + + public function unarchiveBookmark( $id = null ) { + $bookmark = self::$bookmarks->findById( $id ); + if ( $bookmark == false ) { + Session::flash( 'error', 'Bookmark not found.' ); + return Redirect::to( 'bookmarks/index' ); + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + Session::flash( 'error', 'You do not have permission to modify this bookmark.' ); + return Redirect::to( 'bookmarks/index' ); + } + self::$bookmarks->unarchive( $id ); + Session::flash( 'success', 'Bookmark un-archived.' ); + return Redirect::to( 'bookmarks/index' ); + } + + public function refreshBookmark( $id = null ) { + $bookmark = self::$bookmarks->findById( $id ); + if ( $bookmark == false ) { + Session::flash( 'error', 'Bookmark not found.' ); + return Redirect::to( 'bookmarks/index' ); + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + Session::flash( 'error', 'You do not have permission to modify this bookmark.' ); + return Redirect::to( 'bookmarks/index' ); + } + $info = self::$bookmarks->refreshInfo( $id ); + if ( false == $info ) { + Session::flash( 'error', 'Issue refreshing your bookmark.' ); + return Redirect::to( 'bookmarks/bookmark/' . $bookmark->ID ); + } + Session::flash( 'success', 'Bookmark data refreshed.' ); + return Redirect::to( 'bookmarks/bookmark/' . $bookmark->ID ); + } +} diff --git a/app/plugins/bookmarks/forms.php b/app/plugins/bookmarks/forms.php new file mode 100644 index 0000000..d0fd2b5 --- /dev/null +++ b/app/plugins/bookmarks/forms.php @@ -0,0 +1,122 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Plugins\Bookmarks; + +use TheTempusProject\Bedrock\Functions\Input; +use TheTempusProject\Bedrock\Functions\Check; +use TheTempusProject\Classes\Forms; + +class BookmarksForms extends Forms { + /** + * Adds these functions to the form list. + */ + public function __construct() { + self::addHandler( 'createBookmark', __CLASS__, 'createBookmark' ); + self::addHandler( 'createFolder', __CLASS__, 'createFolder' ); + self::addHandler( 'editBookmark', __CLASS__, 'editBookmark' ); + self::addHandler( 'editFolder', __CLASS__, 'editFolder' ); + } + + public static function createBookmark() { + // if ( ! Input::exists( 'title' ) ) { + // Check::addUserError( 'You must include a title.' ); + // return false; + // } + if ( ! Input::exists( 'url' ) ) { + Check::addUserError( 'You must include a url.' ); + return false; + } + // if ( ! Input::exists( 'color' ) ) { + // Check::addUserError( 'You must include a color.' ); + // return false; + // } + // if ( ! Input::exists( 'privacy' ) ) { + // Check::addUserError( 'You must include a privacy.' ); + // return false; + // } + // if ( !self::token() ) { + // Check::addUserError( 'token - comment out later.' ); + // return false; + // } + return true; + } + + public static function createFolder() { + if ( ! Input::exists( 'title' ) ) { + Check::addUserError( 'You must include a title.' ); + return false; + } + // if ( ! Input::exists( 'color' ) ) { + // Check::addUserError( 'You must include a color.' ); + // return false; + // } + // if ( ! Input::exists( 'privacy' ) ) { + // Check::addUserError( 'You must include a privacy.' ); + // return false; + // } + // if ( ! self::token() ) { + // Check::addUserError( 'token - comment out later.' ); + // return false; + // } + return true; + } + + public static function editBookmark() { + // if ( ! Input::exists( 'title' ) ) { + // Check::addUserError( 'You must include a title.' ); + // return false; + // } + if ( ! Input::exists( 'url' ) ) { + Check::addUserError( 'You must include a url.' ); + return false; + } + // if ( ! Input::exists( 'color' ) ) { + // Check::addUserError( 'You must include a color.' ); + // return false; + // } + // if ( ! Input::exists( 'privacy' ) ) { + // Check::addUserError( 'You must include a privacy.' ); + // return false; + // } + // if ( !self::token() ) { + // Check::addUserError( 'token - comment out later.' ); + // return false; + // } + return true; + } + + public static function editFolder() { + if ( ! Input::exists( 'submit' ) ) { + return false; + } + if ( ! Input::exists( 'title' ) ) { + Check::addUserError( 'You must include a title.' ); + return false; + } + // if ( ! Input::exists( 'color' ) ) { + // Check::addUserError( 'You must include a color.' ); + // return false; + // } + // if ( ! Input::exists( 'privacy' ) ) { + // Check::addUserError( 'You must include a privacy.' ); + // return false; + // } + // if ( !self::token() ) { + // Check::addUserError( 'token - comment out later.' ); + // return false; + // } + return true; + } +} + +new BookmarksForms; \ No newline at end of file diff --git a/app/plugins/bookmarks/models/bookmarks.php b/app/plugins/bookmarks/models/bookmarks.php new file mode 100644 index 0000000..726e4a6 --- /dev/null +++ b/app/plugins/bookmarks/models/bookmarks.php @@ -0,0 +1,705 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Models; + +use TheTempusProject\Bedrock\Classes\Config; +use TheTempusProject\Bedrock\Functions\Check; +use TheTempusProject\Canary\Canary as Debug; +use TheTempusProject\Classes\DatabaseModel; +use TheTempusProject\TheTempusProject as App; +use TheTempusProject\Houdini\Classes\Filters; +use TheTempusProject\Canary\Classes\CustomException; + +class Bookmarks extends DatabaseModel { + public $tableName = 'bookmarks'; + public $linkTypes = [ + 'Open in New Tab' => 'external', + 'Open in Same Tab' => 'internal', + ]; + + public $databaseMatrix = [ + [ 'title', 'varchar', '256' ], + [ 'url', 'text', '' ], + [ 'color', 'varchar', '48' ], + [ 'privacy', 'varchar', '48' ], + [ 'folderID', 'int', '11' ], + [ 'description', 'text', '' ], + [ 'createdBy', 'int', '11' ], + [ 'createdAt', 'int', '11' ], + [ 'meta', 'text', '' ], + [ 'icon', 'text', '' ], + [ 'archivedAt', 'int', '11' ], + [ 'refreshedAt', 'int', '11' ], + [ 'hiddenAt', 'int', '11' ], + [ 'order', 'int', '11' ], + [ 'linkType', 'varchar', '32' ], + ]; + + /** + * The model constructor. + */ + public function __construct() { + parent::__construct(); + } + + public function create( $title, $url, $folderID = 0, $description = '', $color = 'default', $privacy = 'private', $type = 'external' ) { + $fields = [ + 'title' => $title, + 'url' => $url, + 'description' => $description, + 'color' => $color, + 'privacy' => $privacy, + 'createdBy' => App::$activeUser->ID, + 'createdAt' => time(), + ]; + if ( !empty( $folderID ) ) { + $fields['folderID'] = $folderID; + } else { + $fields['folderID'] = null; + } + if ( ! self::$db->insert( $this->tableName, $fields ) ) { + new CustomException( 'bookmarkCreate' ); + Debug::error( "Bookmarks: not created " . var_export($fields,true) ); + return false; + } + return self::$db->lastId(); + } + + public function update( $id, $title, $url, $folderID = 0, $description = '', $color = 'default', $privacy = 'private', $type = 'external', $order = 0 ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Bookmarks: illegal ID.' ); + return false; + } + $fields = [ + 'title' => $title, + 'url' => $url, + 'description' => $description, + 'color' => $color, + 'privacy' => $privacy, + // 'linkType' => $type, + // 'order' => $order, + ]; + if ( !empty( $folderID ) ) { + $fields['folderID'] = $folderID; + } + if ( !self::$db->update( $this->tableName, $id, $fields ) ) { + new CustomException( 'bookmarkUpdate' ); + Debug::error( "Bookmarks: $id not updated" ); + return false; + } + return true; + } + + public function byUser( $limit = null ) { + $whereClause = ['createdBy', '=', App::$activeUser->ID]; + if ( empty( $limit ) ) { + $bookmarks = self::$db->get( $this->tableName, $whereClause ); + } else { + $bookmarks = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] ); + } + if ( !$bookmarks->count() ) { + Debug::info( 'No Bookmarks found.' ); + return false; + } + return $this->filter( $bookmarks->results() ); + } + + public function byFolder( $id, $limit = null ) { + $whereClause = ['createdBy', '=', App::$activeUser->ID, 'AND']; + + $whereClause = array_merge( $whereClause, [ 'folderID', '=', $id ] ); + if ( empty( $limit ) ) { + $bookmarks = self::$db->get( $this->tableName, $whereClause ); + } else { + $bookmarks = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] ); + } + if ( !$bookmarks->count() ) { + Debug::info( 'No Bookmarks found.' ); + return false; + } + return $this->filter( $bookmarks->results() ); + } + + public function noFolder( $id = 0, $limit = 10 ) { + $whereClause = ['createdBy', '=', App::$activeUser->ID, 'AND']; + if ( !empty( $id ) ) { + $whereClause = array_merge( $whereClause, ['folderID', '!=', $id] ); + } else { + $whereClause = array_merge( $whereClause, [ 'folderID', 'IS', null] ); + } + if ( empty( $limit ) ) { + $bookmarks = self::$db->get( $this->tableName, $whereClause ); + } else { + $bookmarks = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [ 0, $limit ] ); + } + if ( !$bookmarks->count() ) { + Debug::info( 'No Bookmarks found.' ); + return false; + } + return $this->filter( $bookmarks->results() ); + } + + public function getName( $id ) { + $bookmarks = self::findById( $id ); + if (false == $bookmarks) { + return 'unknown'; + } + return $bookmarks->title; + } + + public function getColor( $id ) { + $bookmarks = self::findById( $id ); + if (false == $bookmarks) { + return 'default'; + } + return $bookmarks->color; + } + + public function simpleByUser() { + $whereClause = ['createdBy', '=', App::$activeUser->ID]; + $bookmarks = self::$db->get( $this->tableName, $whereClause ); + if ( !$bookmarks->count() ) { + Debug::warn( 'Could not find any bookmarks' ); + return false; + } + + $bookmarks = $bookmarks->results(); + $out = []; + foreach ( $bookmarks as $bookmarks ) { + $out[ $bookmarks->title ] = $bookmarks->ID; + } + return $out; + } + + public function simpleObjectByUser() { + $whereClause = ['createdBy', '=', App::$activeUser->ID]; + $bookmarks = self::$db->get( $this->tableName, $whereClause ); + if ( !$bookmarks->count() ) { + Debug::warn( 'Could not find any bookmarks' ); + return false; + } + + $bookmarks = $bookmarks->results(); + $out = []; + foreach ( $bookmarks as $bookmarks ) { + $obj = new \stdClass(); + $obj->title = $bookmarks->title; + $obj->ID = $bookmarks->ID; + $out[] = $obj; + } + return $out; + } + + public function deleteByFolder( $folderID ) { + $whereClause = [ 'createdBy', '=', App::$activeUser->ID, 'AND' ]; + $whereClause = array_merge( $whereClause, [ 'folderID', '=', $folderID ] ); + $bookmarks = self::$db->get( $this->tableName, $whereClause ); + if ( ! $bookmarks->count() ) { + Debug::info( 'No ' . $this->tableName . ' data found.' ); + return []; + } + foreach( $bookmarks->results() as $bookmark ) { + $this->delete( $bookmark->ID ); + } + return true; + } + + private function resolveShortenedUrl( $url ) { + $ch = curl_init($url); + + // Set curl options + curl_setopt($ch, CURLOPT_NOBODY, true); // We don't need the body + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Follow redirects + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // Return the response + curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Set a timeout + // curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // Maybe sketchy? + // Maybe sketchy? + // curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // Disable SSL host verification + // curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disable SSL peer verification + // curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); + + // added to support the regex site + // curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + + // = + // curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, 'AES256+EECDH:AES256+EDH' ); + + + + + // Execute curl + $response = curl_exec( $ch ); + + // Check if there was an error + if ( curl_errno( $ch ) ) { + + + // Get error details + $errorCode = curl_errno($ch); + $errorMessage = curl_error($ch); + // Log or display the error details + dv('cURL Error: ' . $errorMessage . ' (Error Code: ' . $errorCode . ')'); + + curl_close($ch); + // return $url; // Return the original URL if there was an error + + + $url = rtrim( $url, '/' ); + $ch2 = curl_init($url); + curl_setopt($ch2, CURLOPT_NOBODY, true); // We don't need the body + curl_setopt($ch2, CURLOPT_FOLLOWLOCATION, true); // Follow redirects + curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); // Return the response + curl_setopt($ch2, CURLOPT_TIMEOUT, 5); // Set a timeout + curl_exec($ch2); + + if ( curl_errno( $ch2 ) ) { + + } + curl_close( $ch ); + return $url; + } + + // Get the effective URL (the final destination after redirects) + $finalUrl = curl_getinfo( $ch, CURLINFO_EFFECTIVE_URL ); + curl_close( $ch ); + + return $finalUrl; + + + + + + + + + + + // $headers = get_headers( $url, 1 ); + $headers = @get_headers($url, 1); + if ( $headers === false ) { + } + if ( isset( $headers['Location'] ) ) { + if (is_array($headers['Location'])) { + return end($headers['Location']); + } else { + return $headers['Location']; + } + } else { + return $url; + } + } + + public function filter( $data, $params = [] ) { + foreach ( $data as $instance ) { + if ( !is_object( $instance ) ) { + $instance = $data; + $end = true; + } + $base_url = $this->getBaseUrl( $instance->url ); + + if ( empty( $instance->icon ) ) { + $instance->iconHtml = ''; + } else { + if (strpos($instance->icon, 'http') !== false) { + $instance->iconHtml = ''; + } else { + $instance->iconHtml = ''; + } + } + if ( empty( $instance->hiddenAt ) ) { + $instance->hideBtn = ' + + + '; + } else { + $instance->hideBtn = ' + + + '; + } + if ( empty( $instance->archivedAt ) ) { + $instance->archiveBtn = ' + + + '; + } else { + $instance->archiveBtn = ' + + + '; + } + if ( ! empty( $instance->refreshedAt ) && time() < ( $instance->refreshedAt + ( 60 * 10 ) ) ) { + $instance->refreshBtn = ' + + + '; + } else { + $instance->refreshBtn = ' + + + '; + } + + $out[] = $instance; + if ( !empty( $end ) ) { + $out = $out[0]; + break; + } + } + return $out; + } + + public function hide( $id ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Bookmarks: illegal ID.' ); + return false; + } + $fields = [ + 'hiddenAt' => time(), + ]; + if ( !self::$db->update( $this->tableName, $id, $fields ) ) { + new CustomException( 'bookmarkUpdate' ); + Debug::error( "Bookmarks: $id not updated" ); + return false; + } + return true; + } + + public function show( $id ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Bookmarks: illegal ID.' ); + return false; + } + $fields = [ + 'hiddenAt' => 0, + ]; + if ( !self::$db->update( $this->tableName, $id, $fields ) ) { + new CustomException( 'bookmarkUpdate' ); + Debug::error( "Bookmarks: $id not updated" ); + return false; + } + return true; + } + + public function archive( $id ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Bookmarks: illegal ID.' ); + return false; + } + $fields = [ + 'archivedAt' => time(), + ]; + if ( !self::$db->update( $this->tableName, $id, $fields ) ) { + new CustomException( 'bookmarkUpdate' ); + Debug::error( "Bookmarks: $id not updated" ); + return false; + } + return true; + } + + public function unarchive( $id ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Bookmarks: illegal ID.' ); + return false; + } + $fields = [ + 'archivedAt' => 0, + ]; + if ( !self::$db->update( $this->tableName, $id, $fields ) ) { + new CustomException( 'bookmarkUpdate' ); + Debug::error( "Bookmarks: $id not updated" ); + return false; + } + return true; + } + + public function extractMetaTags($htmlContent) { + $doc = new \DOMDocument(); + @$doc->loadHTML($htmlContent); + $metaTags = []; + foreach ($doc->getElementsByTagName('meta') as $meta) { + $name = $meta->getAttribute('name') ?: $meta->getAttribute('property'); + $content = $meta->getAttribute('content'); + if ($name && $content) { + $metaTags[$name] = $content; + } + } + return $metaTags; + } + + public function fetchUrlData($url) { + $ch = curl_init(); + + // Set cURL options + curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, true); // Include headers in the output + curl_setopt($ch, CURLOPT_NOBODY, false); // Include the body in the output + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // Follow redirects + curl_setopt($ch, CURLOPT_TIMEOUT, 30); // Set a timeout + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // Disable SSL host verification for testing + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disable SSL peer verification for testing + + // Execute cURL request + $response = curl_exec($ch); + + // Check if there was an error + if (curl_errno($ch)) { + $errorMessage = curl_error($ch); + curl_close($ch); + throw new \Exception('cURL Error: ' . $errorMessage); + } + + // Get HTTP status code + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + + // Separate headers and body + $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); + $headers = substr($response, 0, $headerSize); + $body = substr($response, $headerSize); + + curl_close($ch); + + // Parse headers into an associative array + $headerLines = explode("\r\n", trim($headers)); + $headerArray = []; + foreach ($headerLines as $line) { + $parts = explode(': ', $line, 2); + if (count($parts) == 2) { + $headerArray[$parts[0]] = $parts[1]; + } + } + + return [ + 'http_code' => $httpCode, + 'headers' => $headerArray, + 'body' => $body, + ]; + } + + private function getMetaTagsAndFavicon( $url ) { + try { + // $url = 'https://runescape.wiki'; + $data = $this->fetchUrlData($url); + + // iv($data); + + // Get headers + $headers = $data['headers']; + iv($headers); + + // Get meta tags + $metaTags = $this->extractMetaTags($data['body']); + } catch (Exception $e) { + dv( 'Error: ' . $e->getMessage()); + + } + $metaInfo = [ + 'url' => $url, + 'title' => null, + 'description' => null, + 'image' => null, + 'favicon' => null + ]; + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + $html = curl_exec($ch); + curl_close($ch); + + if ($html === false) { + return null; + } + + $meta = @get_meta_tags( $url ); + + $dom = new \DOMDocument('1.0', 'utf-8'); + $dom->strictErrorChecking = false; + $dom->loadHTML($html, LIBXML_NOERROR); + $xml = simplexml_import_dom($dom); + $arr = $xml->xpath('//link[@rel="shortcut icon"]'); + + // Get the title of the page + $titles = $dom->getElementsByTagName('title'); + if ($titles->length > 0) { + $metaInfo['title'] = $titles->item(0)->nodeValue; + } + + // Get the meta tags + $metaTags = $dom->getElementsByTagName('meta'); + $metadata = []; + foreach ($metaTags as $meta) { + $metadata[] = [ + 'name' => $meta->getAttribute('name'), + 'property' => $meta->getAttribute('property'), + 'content' => $meta->getAttribute('content') + ]; + if ($meta->getAttribute('name') === 'description') { + $metaInfo['description'] = $meta->getAttribute('content'); + } + if ($meta->getAttribute('itemprop') === 'image') { + $metaInfo['google_image'] = $meta->getAttribute('content'); + } + if ($meta->getAttribute('property') === 'og:image') { + $metaInfo['image'] = $meta->getAttribute('content'); + } + } + + // Get the link tags to find the favicon + $linkTags = $dom->getElementsByTagName('link'); + $metadata['links'] = []; + foreach ($linkTags as $link) { + $metadata['links'][] = [ $link->getAttribute('rel') => $link->getAttribute('href') ]; + if ( $link->getAttribute('rel') === 'icon' || $link->getAttribute('rel') === 'shortcut icon') { + $metaInfo['favicon'] = $link->getAttribute('href'); + break; + } + } + + $metaInfo['metadata'] = $metadata; + + return $metaInfo; + } + + public function retrieveInfo( $url ) { + $finalDestination = $this->resolveShortenedUrl( $url ); + $info = $this->getMetaTagsAndFavicon( $finalDestination ); + $base_url = $this->getBaseUrl( $finalDestination ); + + if ( ! empty( $info['favicon'] ) ) { + echo 'favicon exists' . PHP_EOL; + if ( stripos( $info['favicon'], 'http' ) !== false) { + echo 'favicon is full url' . PHP_EOL; + $imageUrl = $info['favicon']; + } else { + echo 'favicon is not full url' . PHP_EOL; + $imageUrl = trim( $base_url, '/' ) . '/' . ltrim( $info['favicon'], '/' ); + } + if ( $this->isValidImageUrl( $imageUrl ) ) { + echo 'image is valid' . PHP_EOL; + $info['favicon'] = $imageUrl; + } else { + echo 'image is not valid' . PHP_EOL; + $base_info = $this->getMetaTagsAndFavicon( $base_url ); + if ( ! empty( $base_info['favicon'] ) ) { + echo 'parent favicon exists!'; + if ( stripos( $base_info['favicon'], 'http' ) !== false) { + echo 'parent favicon is full url' . PHP_EOL; + $imageUrl = $base_info['favicon']; + } else { + echo 'parent favicon is not full url' . PHP_EOL; + $imageUrl = trim( $base_url, '/' ) . '/' . ltrim( $base_info['favicon'], '/' ); + } + if ( $this->isValidImageUrl( $imageUrl ) ) { + echo 'parent favicon image is valid' . PHP_EOL; + $info['favicon'] = $imageUrl; + } else { + echo 'parent favicon image is not valid' . PHP_EOL; + } + } + } + } else { + echo 'favicon does not exist' . PHP_EOL; + $base_info = $this->getMetaTagsAndFavicon( $base_url ); + if ( ! empty( $base_info['favicon'] ) ) { + echo 'parent favicon exists!' . PHP_EOL; + if ( stripos( $base_info['favicon'], 'http' ) !== false) { + echo 'parent favicon is full url' . PHP_EOL; + $imageUrl = $base_info['favicon']; + } else { + echo 'parent favicon is not full url' . PHP_EOL; + $imageUrl = trim( $base_url, '/' ) . '/' . ltrim( $base_info['favicon'], '/' ); + } + if ( $this->isValidImageUrl( $imageUrl ) ) { + echo 'parent favicon image is valid' . PHP_EOL; + $info['favicon'] = $imageUrl; + } else { + echo 'parent favicon image is not valid' . PHP_EOL; + } + } + } + + return $info; + } + + public function refreshInfo( $id ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Bookmarks: illegal ID.' ); + return false; + } + $bookmark = self::findById( $id ); + if ( $bookmark == false ) { + Debug::info( 'Bookmarks not found.' ); + return false; + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + Debug::info( 'You do not have permission to modify this bookmark.' ); + return false; + } + if ( time() < ( $bookmark->refreshedAt + ( 60 * 10 ) ) ) { + Debug::info( 'You may only fetch bookmarks once every 10 minutes.' ); + return false; + } + + $info = $this->retrieveInfo( $bookmark->url ); + + $fields = [ + // 'refreshedAt' => time(), + ]; + + if ( empty( $bookmark->title ) && ! empty( $info['title'] ) ) { + $fields['title'] = $info['title']; + } + if ( empty( $bookmark->description ) && ! empty( $info['description'] ) ) { + $fields['description'] = $info['description']; + } + + if ( ( empty( $bookmark->icon ) || ! $this->isValidImageUrl( $bookmark->icon ) ) && ! empty( $info['favicon'] ) ) { + $fields['icon'] = $info['favicon']; + } + $fields['meta'] = json_encode( $info['metadata'] ); + + if ( !self::$db->update( $this->tableName, $id, $fields ) ) { + new CustomException( 'bookmarkUpdate' ); + Debug::error( "Bookmarks: $id not updated" ); + return false; + } + return true; + } + + private function getBaseUrl ($url ) { + $parsedUrl = parse_url($url); + + if (isset($parsedUrl['scheme']) && isset($parsedUrl['host'])) { + return $parsedUrl['scheme'] . '://' . $parsedUrl['host'] . '/'; + } else { + return null; // URL is not valid or cannot be parsed + } + } + + function isValidImageUrl($url) { + $headers = @get_headers($url); + if ($headers && strpos($headers[0], '200') !== false) { + + return true; + // Further check to ensure it's an image + foreach ($headers as $header) { + if (strpos(strtolower($header), 'content-type:') !== false) { + if (strpos(strtolower($header), 'image/') !== false) { + return true; + } + } + } + } + return false; + } +} diff --git a/app/plugins/bookmarks/models/bookmarkviews.php b/app/plugins/bookmarks/models/bookmarkviews.php new file mode 100644 index 0000000..e25d50f --- /dev/null +++ b/app/plugins/bookmarks/models/bookmarkviews.php @@ -0,0 +1,149 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Models; + +use TheTempusProject\Bedrock\Classes\Config; +use TheTempusProject\Bedrock\Functions\Check; +use TheTempusProject\Canary\Bin\Canary as Debug; +use TheTempusProject\Classes\DatabaseModel; +use TheTempusProject\TheTempusProject as App; +use TheTempusProject\Houdini\Classes\Filters; +use TheTempusProject\Canary\Classes\CustomException; + +class Bookmarkviews extends DatabaseModel { + public $tableName = 'bookmark_views'; + public $databaseMatrix = [ + [ 'title', 'varchar', '256' ], + [ 'description', 'text', '' ], + [ 'privacy', 'varchar', '48' ], + + + + + + + + + + + [ 'createdBy', 'int', '11' ], + [ 'createdAt', 'int', '11' ], + [ 'updatedAt', 'int', '11' ], + ]; + + /** + * The model constructor. + */ + public function __construct() { + parent::__construct(); + } + + public function create( $title, $description = '', $privacy = 'private' ) { + if ( ! Check::dataTitle( $title ) ) { + Debug::info( 'Views: illegal title.' ); + return false; + } + $fields = [ + 'title' => $title, + 'description' => $description, + 'privacy' => $privacy, + 'createdBy' => App::$activeUser->ID, + 'createdAt' => time(), + ]; + if ( ! self::$db->insert( $this->tableName, $fields ) ) { + new CustomException( 'viewCreate' ); + Debug::error( "Views: not created " . var_export($fields,true) ); + return false; + } + return self::$db->lastId(); + } + + public function update( $id, $title, $description = '', $privacy = 'private' ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Views: illegal ID.' ); + return false; + } + if ( !Check::dataTitle( $title ) ) { + Debug::info( 'Views: illegal title.' ); + return false; + } + $fields = [ + 'title' => $title, + 'description' => $description, + 'privacy' => $privacy, + ]; + if ( !self::$db->update( $this->tableName, $id, $fields ) ) { + new CustomException( 'viewUpdate' ); + Debug::error( "Views: $id not updated: $fields" ); + return false; + } + return true; + } + + public function byUser( $limit = null ) { + $whereClause = ['createdBy', '=', App::$activeUser->ID]; + if ( empty( $limit ) ) { + $views = self::$db->get( $this->tableName, $whereClause ); + } else { + $views = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] ); + } + if ( !$views->count() ) { + Debug::info( 'No Views found.' ); + return false; + } + return $this->filter( $views->results() ); + } + + public function getName( $id ) { + $views = self::findById( $id ); + if (false == $views) { + return 'unknown'; + } + return $views->title; + } + + public function simpleByUser() { + $whereClause = ['createdBy', '=', App::$activeUser->ID]; + $views = self::$db->get( $this->tableName, $whereClause ); + if ( !$views->count() ) { + Debug::warn( 'Could not find any Views' ); + return false; + } + + $views = $views->results(); + $out = []; + foreach ( $views as $view ) { + $out[ $view->title ] = $view->ID; + } + return $out; + } + + public function simpleObjectByUser() { + $whereClause = ['createdBy', '=', App::$activeUser->ID]; + $views = self::$db->get( $this->tableName, $whereClause ); + if ( !$views->count() ) { + Debug::warn( 'Could not find any Views' ); + return false; + } + + $views = $views->results(); + $out = []; + foreach ( $views as $view ) { + $obj = new \stdClass(); + $obj->title = $view->title; + $obj->ID = $view->ID; + $out[] = $obj; + } + return $out; + } +} diff --git a/app/plugins/bookmarks/models/folders.php b/app/plugins/bookmarks/models/folders.php new file mode 100644 index 0000000..218f6c2 --- /dev/null +++ b/app/plugins/bookmarks/models/folders.php @@ -0,0 +1,157 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Models; + +use TheTempusProject\Bedrock\Classes\Config; +use TheTempusProject\Bedrock\Functions\Check; +use TheTempusProject\Canary\Canary as Debug; +use TheTempusProject\Classes\DatabaseModel; +use TheTempusProject\TheTempusProject as App; +use TheTempusProject\Houdini\Classes\Filters; +use TheTempusProject\Canary\Classes\CustomException; + +class Folders extends DatabaseModel { + public $tableName = 'folders'; + public $databaseMatrix = [ + [ 'title', 'varchar', '256' ], + [ 'color', 'varchar', '48' ], + [ 'privacy', 'varchar', '48' ], + [ 'description', 'text', '' ], + [ 'folderID', 'int', '11' ], + [ 'createdBy', 'int', '11' ], + [ 'createdAt', 'int', '11' ], + ]; + + /** + * The model constructor. + */ + public function __construct() { + parent::__construct(); + } + + public function create( $title, $folderID = 0, $description = '', $color = 'default', $privacy = 'private' ) { + if ( ! Check::dataTitle( $title ) ) { + Debug::info( 'Folders: illegal title.' ); + return false; + } + $fields = [ + 'title' => $title, + 'description' => $description, + 'color' => $color, + 'privacy' => $privacy, + 'createdBy' => App::$activeUser->ID, + 'createdAt' => time(), + ]; + if ( !empty( $folderID ) ) { + $fields['folderID'] = $folderID; + } + if ( ! self::$db->insert( $this->tableName, $fields ) ) { + new CustomException( 'folderCreate' ); + Debug::error( "Folders: not created " . var_export($fields,true) ); + return false; + } + return self::$db->lastId(); + } + + public function update( $id, $title, $folderID = 0, $description = '', $color = 'default', $privacy = 'private' ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Folders: illegal ID.' ); + return false; + } + if ( !Check::dataTitle( $title ) ) { + Debug::info( 'Folders: illegal title.' ); + return false; + } + $fields = [ + 'title' => $title, + 'description' => $description, + 'color' => $color, + 'privacy' => $privacy, + ]; + if ( !empty( $folderID ) ) { + $fields['folderID'] = $folderID; + } + if ( !self::$db->update( $this->tableName, $id, $fields ) ) { + new CustomException( 'folderUpdate' ); + Debug::error( "Folders: $id not updated: $fields" ); + return false; + } + return true; + } + + public function byUser( $limit = null ) { + $whereClause = ['createdBy', '=', App::$activeUser->ID]; + if ( empty( $limit ) ) { + $folders = self::$db->get( $this->tableName, $whereClause ); + } else { + $folders = self::$db->get( $this->tableName, $whereClause, 'ID', 'DESC', [0, $limit] ); + } + if ( !$folders->count() ) { + Debug::info( 'No Folders found.' ); + return false; + } + return $this->filter( $folders->results() ); + } + + public function getName( $id ) { + $folder = self::findById( $id ); + if (false == $folder) { + return 'unknown'; + } + return $folder->title; + } + + public function getColor( $id ) { + $folder = self::findById( $id ); + if (false == $folder) { + return 'default'; + } + return $folder->color; + } + + public function simpleByUser() { + $whereClause = ['createdBy', '=', App::$activeUser->ID]; + $folders = self::$db->get( $this->tableName, $whereClause ); + if ( !$folders->count() ) { + Debug::warn( 'Could not find any folders' ); + return false; + } + + $folders = $folders->results(); + $out = []; + $out[ 'No Folder' ] = 0; + foreach ( $folders as $folder ) { + $out[ $folder->title ] = $folder->ID; + } + return $out; + } + + public function simpleObjectByUser() { + $whereClause = ['createdBy', '=', App::$activeUser->ID]; + $folders = self::$db->get( $this->tableName, $whereClause ); + if ( !$folders->count() ) { + Debug::warn( 'Could not find any folders' ); + return false; + } + + $folders = $folders->results(); + $out = []; + foreach ( $folders as $folder ) { + $obj = new \stdClass(); + $obj->title = $folder->title; + $obj->ID = $folder->ID; + $out[] = $obj; + } + return $out; + } +} diff --git a/app/plugins/bookmarks/plugin.php b/app/plugins/bookmarks/plugin.php new file mode 100644 index 0000000..45b5b43 --- /dev/null +++ b/app/plugins/bookmarks/plugin.php @@ -0,0 +1,62 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Plugins; + +use TheTempusProject\Classes\Plugin; +use TheTempusProject\Models\Events; +use TheTempusProject\Models\Bookmarks as Bookmark; +use TheTempusProject\Models\Folders; +use TheTempusProject\Houdini\Classes\Components; +use TheTempusProject\Houdini\Classes\Template; + +class Bookmarks extends Plugin { + public $pluginName = 'TP Bookmarks'; + public $configName = 'bookmarks'; + public $pluginAuthor = 'JoeyK'; + public $pluginWebsite = 'https://TheTempusProject.com'; + public $modelVersion = '1.0'; + public $pluginVersion = '3.0'; + public $pluginDescription = 'A simple plugin which adds a site wide bookmark system.'; + public $permissionMatrix = [ + 'useBookmarks' => [ + 'pretty' => 'Can use the bookmarks feature', + 'default' => false, + ], + 'createEvents' => [ + 'pretty' => 'Can add events to bookmarks', + 'default' => false, + ], + ]; + public $main_links = [ + [ + 'text' => 'Bookmarks', + 'url' => '{ROOT_URL}bookmarks/index', + 'filter' => 'loggedin', + ], + ]; + public $configMatrix = [ + 'enabled' => [ + 'type' => 'radio', + 'pretty' => 'Enable Bookmarks.', + 'default' => true, + ], + ]; + public $bookmarks; + public $folders; + + public function __construct( $load = false ) { + $this->bookmarks = new Bookmark; + $this->folders = new Folders; + parent::__construct( $load ); + } +} diff --git a/app/plugins/bookmarks/views/bookmarks/create.html b/app/plugins/bookmarks/views/bookmarks/create.html new file mode 100644 index 0000000..2a42103 --- /dev/null +++ b/app/plugins/bookmarks/views/bookmarks/create.html @@ -0,0 +1,45 @@ +Create Bookmark +{BookmarkBreadCrumbs} +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ {folderSelect} +
+ +
+ +
+
+
+ +
+ {colorSelect} +
+
+
+ +
+ +
+
+
\ No newline at end of file diff --git a/app/plugins/bookmarks/views/bookmarks/edit.html b/app/plugins/bookmarks/views/bookmarks/edit.html new file mode 100644 index 0000000..e804d91 --- /dev/null +++ b/app/plugins/bookmarks/views/bookmarks/edit.html @@ -0,0 +1,45 @@ +Edit Bookmark +{BookmarkBreadCrumbs} +
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ {folderSelect} +
+ +
+ +
+
+
+ +
+ {colorSelect} +
+
+
+ +
+ +
+
+
\ No newline at end of file diff --git a/app/plugins/bookmarks/views/bookmarks/list.html b/app/plugins/bookmarks/views/bookmarks/list.html new file mode 100644 index 0000000..32b8a5e --- /dev/null +++ b/app/plugins/bookmarks/views/bookmarks/list.html @@ -0,0 +1,37 @@ +{BookmarkBreadCrumbs} + + + + + + + + + + + + {LOOP} + + + + + + + + {/LOOP} + {ALT} + + + + {/ALT} + +
Bookmark
+ + {title} + + + {privacy} +
+ No results to show. +
+Create \ No newline at end of file diff --git a/app/plugins/bookmarks/views/bookmarks/view.html b/app/plugins/bookmarks/views/bookmarks/view.html new file mode 100644 index 0000000..7b4e431 --- /dev/null +++ b/app/plugins/bookmarks/views/bookmarks/view.html @@ -0,0 +1,84 @@ +{BookmarkBreadCrumbs}
+
+
+
+
+

Bookmark

+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Title{title}
URL{url}
Type{linkType}
Privacy{privacy}
Color{color}
Description
{description}
Icon
{iconHtml}
{icon}
Meta
{meta}
Created{DTC}{createdAt}{/DTC}
Archived{DTC}{archivedAt}{/DTC}
Hidden{DTC}{hiddenAt}{/DTC}
Last Refreshed{DTC}{refreshedAt}{/DTC}
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/app/plugins/bookmarks/views/components/bookmarkListPanel.html b/app/plugins/bookmarks/views/components/bookmarkListPanel.html new file mode 100644 index 0000000..29d7398 --- /dev/null +++ b/app/plugins/bookmarks/views/components/bookmarkListPanel.html @@ -0,0 +1,21 @@ +
+
+ {title} +
+
+
+
    + {bookmarkListRows} +
+
+ +
+
\ No newline at end of file diff --git a/app/plugins/bookmarks/views/components/bookmarkListRows.html b/app/plugins/bookmarks/views/components/bookmarkListRows.html new file mode 100644 index 0000000..fcfb01f --- /dev/null +++ b/app/plugins/bookmarks/views/components/bookmarkListRows.html @@ -0,0 +1,18 @@ + +{LOOP} +
  • + {iconHtml} + {title} + + {hideBtn} + {archiveBtn} + + + +
  • +{/LOOP} +{ALT} +
  • + No Bookmarks +
  • +{/ALT} \ No newline at end of file diff --git a/app/plugins/bookmarks/views/components/folderPanelList.html b/app/plugins/bookmarks/views/components/folderPanelList.html new file mode 100644 index 0000000..9188a1e --- /dev/null +++ b/app/plugins/bookmarks/views/components/folderPanelList.html @@ -0,0 +1,10 @@ +{LOOP} +
    + {panel} +
    +{/LOOP} +{ALT} +
    +

    no folders

    +
    +{/ALT} \ No newline at end of file diff --git a/app/plugins/bookmarks/views/dash.html b/app/plugins/bookmarks/views/dash.html new file mode 100644 index 0000000..030da69 --- /dev/null +++ b/app/plugins/bookmarks/views/dash.html @@ -0,0 +1,16 @@ + +{BookmarkBreadCrumbs} +
    +
    + Unsorted Bookmarks + {bookmarksList} +
    +
    + Folders List + {foldersList} +
    +
    +Folders +
    + {folderPanels} +
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/folders/create.html b/app/plugins/bookmarks/views/folders/create.html new file mode 100644 index 0000000..42f4adf --- /dev/null +++ b/app/plugins/bookmarks/views/folders/create.html @@ -0,0 +1,39 @@ +Create Folder +{BookmarkBreadCrumbs} +
    + +
    + +
    + +
    +
    +
    + +
    + +
    +
    + {folderSelect} +
    + +
    + +
    +
    +
    + +
    + {colorSelect} +
    +
    +
    + +
    + +
    +
    +
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/folders/edit.html b/app/plugins/bookmarks/views/folders/edit.html new file mode 100644 index 0000000..6c9fa3f --- /dev/null +++ b/app/plugins/bookmarks/views/folders/edit.html @@ -0,0 +1,39 @@ +Edit Folder +{BookmarkBreadCrumbs} +
    + +
    + +
    + +
    +
    +
    + +
    + +
    +
    + {folderSelect} +
    + +
    + +
    +
    +
    + +
    + {colorSelect} +
    +
    +
    + +
    + +
    +
    +
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/folders/list.html b/app/plugins/bookmarks/views/folders/list.html new file mode 100644 index 0000000..8e5f510 --- /dev/null +++ b/app/plugins/bookmarks/views/folders/list.html @@ -0,0 +1,33 @@ +{BookmarkBreadCrumbs} + + + + + + + + + + + + + {LOOP} + + + + + + + + + {/LOOP} + {ALT} + + + + {/ALT} + +
    TitlePrivacyDescription
    {title}{privacy}{description}
    + No results to show. +
    +Create \ No newline at end of file diff --git a/app/plugins/bookmarks/views/folders/view.html b/app/plugins/bookmarks/views/folders/view.html new file mode 100644 index 0000000..d4aca97 --- /dev/null +++ b/app/plugins/bookmarks/views/folders/view.html @@ -0,0 +1,47 @@ +{BookmarkBreadCrumbs}
    +
    +
    +
    +
    +

    Bookmark Folder

    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Title{title}
    Privacy{privacy}
    Color{color}
    Description
    {description}
    Created{DTC}{createdAt}{/DTC}
    +
    +
    +
    + +
    +
    +
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/nav/folderTabs.html b/app/plugins/bookmarks/views/nav/folderTabs.html new file mode 100644 index 0000000..2ab6a13 --- /dev/null +++ b/app/plugins/bookmarks/views/nav/folderTabs.html @@ -0,0 +1,5 @@ + +{userFolderTabs} \ No newline at end of file diff --git a/app/plugins/bookmarks/views/nav/userFolderTabs.html b/app/plugins/bookmarks/views/nav/userFolderTabs.html new file mode 100644 index 0000000..45a5a93 --- /dev/null +++ b/app/plugins/bookmarks/views/nav/userFolderTabs.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/app/plugins/members/controllers/admin/member.php b/app/plugins/members/controllers/admin/member.php new file mode 100644 index 0000000..4977a47 --- /dev/null +++ b/app/plugins/members/controllers/admin/member.php @@ -0,0 +1,50 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Controllers\Admin; + +use TheTempusProject\Houdini\Classes\Views; +use TheTempusProject\Houdini\Classes\Navigation; +use TheTempusProject\Houdini\Classes\Components; +use TheTempusProject\Classes\AdminController; +use TheTempusProject\Models\Members as MemberModel; + +class Member extends AdminController { + public static $memberships; + + public function __construct() { + parent::__construct(); + self::$title = 'Admin - Memberships'; + self::$memberships = new MemberModel; + $view = Navigation::activePageSelect( 'nav.admin', '/admin/member' ); + Components::set( 'ADMINNAV', $view ); + } + + public function index( $data = null ) { + Views::view( 'members.admin.list', self::$memberships->list() ); + } + + public function create( $data = null ) { + } + + public function edit( $data = null ) { + } + + public function view( $data = null ) { + } + + public function delete( $data = null ) { + } + + public function preview( $data = null ) { + } +} diff --git a/app/plugins/members/controllers/member.php b/app/plugins/members/controllers/member.php new file mode 100644 index 0000000..a43366f --- /dev/null +++ b/app/plugins/members/controllers/member.php @@ -0,0 +1,36 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Controllers; + +use TheTempusProject\Houdini\Classes\Template; +use TheTempusProject\Houdini\Classes\Views; +use TheTempusProject\Houdini\Classes\Issues; +use TheTempusProject\Classes\Controller; +use TheTempusProject\TheTempusProject as App; +use TheTempusProject\Hermes\Functions\Redirect; +use TheTempusProject\Bedrock\Functions\Session; + +class Member extends Controller { + public function __construct() { + parent::__construct(); + Template::noIndex(); + if ( !App::$isMember ) { + Session::flash( 'error', 'You do not have permission to view this page.' ); + return Redirect::home(); + } + } + + public function index() { + self::$title = 'Members Area'; + Views::view( 'members.members' ); + } +} diff --git a/app/plugins/members/models/members.php b/app/plugins/members/models/members.php new file mode 100644 index 0000000..b991e38 --- /dev/null +++ b/app/plugins/members/models/members.php @@ -0,0 +1,36 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Models; + +use TheTempusProject\Canary\Bin\Canary as Debug; +use TheTempusProject\Bedrock\Functions\Check; +use TheTempusProject\Bedrock\Functions\Sanitize; +use TheTempusProject\Classes\DatabaseModel; +use TheTempusProject\TheTempusProject as App; + +class Members extends DatabaseModel { + public $tableName = 'memberships'; + public $databaseMatrix = [ + [ 'name', 'varchar', '155' ], + // renews? + // does this renew periodically? + // renewal period? + // if periodic, how frequent? + // renewal type? + // automatic, manual review, paid + ]; + + public function __construct() { + parent::__construct(); + } +} \ No newline at end of file diff --git a/app/plugins/members/models/membershipassociations.php b/app/plugins/members/models/membershipassociations.php new file mode 100644 index 0000000..78a0e9f --- /dev/null +++ b/app/plugins/members/models/membershipassociations.php @@ -0,0 +1,33 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ + +namespace TheTempusProject\Models; + +use TheTempusProject\Canary\Bin\Canary as Debug; +use TheTempusProject\Bedrock\Functions\Check; +use TheTempusProject\Bedrock\Functions\Sanitize; +use TheTempusProject\Classes\DatabaseModel; +use TheTempusProject\TheTempusProject as App; + +class Membershipassociations extends DatabaseModel { + public $tableName = 'membership_associations'; + public $databaseMatrix = [ + [ 'model_type', 'varchar', '64' ], + [ 'model_id', 'int', '10' ], + [ 'membership_id', 'int', '10' ], + ]; + + public function __construct() { + parent::__construct(); + } +} \ No newline at end of file diff --git a/app/plugins/members/models/membershiprecords.php b/app/plugins/members/models/membershiprecords.php new file mode 100644 index 0000000..dd49724 --- /dev/null +++ b/app/plugins/members/models/membershiprecords.php @@ -0,0 +1,32 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Models; + +use TheTempusProject\Canary\Bin\Canary as Debug; +use TheTempusProject\Bedrock\Functions\Check; +use TheTempusProject\Bedrock\Functions\Sanitize; +use TheTempusProject\Classes\DatabaseModel; +use TheTempusProject\TheTempusProject as App; + +class Membershiprecords extends DatabaseModel { + public $tableName = 'membership_records'; + public $databaseMatrix = [ + [ 'membership_id', 'int', '10' ], + [ 'recorded_at', 'int', '10' ], + [ 'record_type', 'varchar', '64' ], + ]; + + public function __construct() { + parent::__construct(); + } +} \ No newline at end of file diff --git a/app/plugins/members/plugin.php b/app/plugins/members/plugin.php new file mode 100644 index 0000000..1b52d5b --- /dev/null +++ b/app/plugins/members/plugin.php @@ -0,0 +1,75 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Plugins; + +use TheTempusProject\TheTempusProject as App; +use TheTempusProject\Classes\Plugin; + +class Members extends Plugin { + public $pluginName = 'TP Membership'; + public $configName = 'membership'; + public $pluginAuthor = 'JoeyK'; + public $pluginWebsite = 'https://TheTempusProject.com'; + public $modelVersion = '1.0'; + public $pluginVersion = '3.0'; + public $pluginDescription = 'A simple plugin which adds a site wide membership system.'; + public $permissionMatrix = [ + 'memberAccess' => [ + 'pretty' => 'Access Member Areas', + 'default' => false, + ], + 'controlMemberships' => [ + 'pretty' => 'User can Access and Control user memberships.', + 'default' => false, + ], + ]; + public $admin_links = [ + [ + 'text' => ' Memberships', + 'url' => '{ROOT_URL}admin/member', + ], + ]; + public $main_links = [ + [ + 'text' => 'Members', + 'url' => '{ROOT_URL}member/index', + 'filter' => 'member', + ], + ]; + public $resourceMatrix = [ + 'groups' => [ + [ + 'name' => 'Member', + 'permissions' => '{"adminAccess":false}', + ] + ], + ]; + public function __construct( $load = false ) { + if ( App::$isLoggedIn ) { + App::$isMember = $this->hasMemberAccess( App::$activeGroup ); + if ( empty( App::$isMember ) ) { + App::$isMember = $this->hasMemberAccess( App::$activeUser ); + } + } + $this->filters[] = [ + 'name' => 'member', + 'find' => '#{MEMBER}(.*?){/MEMBER}#is', + 'replace' => ( App::$isMember ? '$1' : '' ), + 'enabled' => true, + ]; + parent::__construct( $load ); + } + public function hasMemberAccess( $input ) { + return true; + } +} diff --git a/app/plugins/members/views/admin/list.html b/app/plugins/members/views/admin/list.html new file mode 100644 index 0000000..f619767 --- /dev/null +++ b/app/plugins/members/views/admin/list.html @@ -0,0 +1 @@ +list here \ No newline at end of file diff --git a/app/plugins/members/views/members.html b/app/plugins/members/views/members.html new file mode 100644 index 0000000..f415a07 --- /dev/null +++ b/app/plugins/members/views/members.html @@ -0,0 +1,6 @@ +

    Members' Area

    +
    +

    Welcome!

    +

    This is the members section. You can give some groups permission to access these areas. the menu is hidden for normal users and if they get a link to a member's area, the authentication system will stop them from accessing any content protected this way.

    +

    You can even use this feature in-line with your views, hiding certain components from non-members

    +
    \ No newline at end of file