From ab2f009e5b388f7b6a1a0ce9820e0855cbf5fdfd Mon Sep 17 00:00:00 2001 From: Joey Kimsey Date: Sun, 15 Dec 2024 17:20:57 -0500 Subject: [PATCH] Add bookmark exports and sharing + various fixes --- .../bookmarks/controllers/bookmarks.php | 149 ++++++++++++- app/plugins/bookmarks/controllers/shared.php | 127 +++++++++++ app/plugins/bookmarks/forms.php | 27 +++ app/plugins/bookmarks/models/bookmarks.php | 68 ++++++ .../bookmarks/models/bookmarkviews.php | 1 + app/plugins/bookmarks/models/folders.php | 15 ++ .../bookmarks/views/bookmarks/view.html | 26 +-- .../views/components/shareListPanel.html | 45 ++++ .../views/components/shareListRows.html | 30 +++ app/plugins/bookmarks/views/export.html | 48 ++++ .../bookmarks/views/folders/listPage.html | 10 +- app/plugins/bookmarks/views/folders/view.html | 20 +- app/plugins/bookmarks/views/import.html | 25 +-- .../bookmarks/views/nav/folderTabs.html | 2 + app/plugins/bookmarks/views/share.html | 19 ++ app/plugins/bookmarks/views/shareFolder.html | 48 ++++ app/plugins/bookmarks/views/shareLink.html | 83 +++++++ app/plugins/members/controllers/member.php | 205 +++++++++--------- app/plugins/members/models/memberships.php | 2 - app/plugins/members/plugin.php | 42 +++- app/plugins/members/views/landing1.html | 190 ++++++++++------ app/plugins/members/views/landing2.html | 4 +- app/plugins/members/views/upgrade.html | 35 ++- app/plugins/suggestions/views/view.html | 36 +-- app/plugins/wip/plugin.php | 2 +- app/views/index.html | 13 +- 26 files changed, 975 insertions(+), 297 deletions(-) create mode 100644 app/plugins/bookmarks/controllers/shared.php create mode 100644 app/plugins/bookmarks/views/components/shareListPanel.html create mode 100644 app/plugins/bookmarks/views/components/shareListRows.html create mode 100644 app/plugins/bookmarks/views/export.html create mode 100644 app/plugins/bookmarks/views/share.html create mode 100644 app/plugins/bookmarks/views/shareFolder.html create mode 100644 app/plugins/bookmarks/views/shareLink.html diff --git a/app/plugins/bookmarks/controllers/bookmarks.php b/app/plugins/bookmarks/controllers/bookmarks.php index 490ba73..3b5cffe 100644 --- a/app/plugins/bookmarks/controllers/bookmarks.php +++ b/app/plugins/bookmarks/controllers/bookmarks.php @@ -1,10 +1,10 @@ * @link https://TheTempusProject.com @@ -27,6 +27,7 @@ use TheTempusProject\Houdini\Classes\Components; use TheTempusProject\Houdini\Classes\Forms as HoudiniForms; use TheTempusProject\Houdini\Classes\Navigation; use TheTempusProject\Houdini\Classes\Template; +use TheTempusProject\Hermes\Functions\Route as Routes; class Bookmarks extends Controller { protected static $bookmarks; @@ -53,6 +54,7 @@ class Bookmarks extends Controller { $userFolderTabsView = ''; } Components::set( 'userFolderTabs', $userFolderTabsView ); + Components::set( 'SITE_URL', Routes::getAddress() ); Views::raw( $tabsView ); } @@ -150,9 +152,13 @@ class Bookmarks extends Controller { Issues::add( 'error', [ 'There was an error creating your bookmark.' => Check::userErrors() ] ); return Views::view( 'bookmarks.bookmarks.create' ); } - self::$bookmarks->refreshInfo( $result ); + // self::$bookmarks->refreshInfo( $result ); Session::flash( 'success', 'Your Bookmark has been created.' ); - Redirect::to( 'bookmarks/bookmarks/'. $folderID ); + if ( ! empty( $folderID ) ) { + Redirect::to( 'bookmarks/bookmarks/'. $folderID ); + } else { + Redirect::to( 'bookmarks/index' ); + } } public function editBookmark( $id = null ) { @@ -318,6 +324,36 @@ class Bookmarks extends Controller { /** * Functionality */ + public function publish( $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->publish( $id ); + Session::flash( 'success', 'Bookmark mad Public.' ); + return Redirect::to( 'bookmarks/share' ); + } + + public function retract( $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->retract( $id ); + Session::flash( 'success', 'Bookmark made Private.' ); + return Redirect::to( 'bookmarks/share' ); + } + public function hideBookmark( $id = null ) { $bookmark = self::$bookmarks->findById( $id ); if ( $bookmark == false ) { @@ -452,6 +488,108 @@ class Bookmarks extends Controller { // dv ( $out ); } + public function export() { + $folders = self::$folders->byUser(); + + if ( ! Input::exists('submit') ) { + return Views::view( 'bookmarks.export', $folders ); + } + + if ( ! Forms::check( 'exportBookmarks' ) ) { + Issues::add( 'error', [ 'There was an error exporting your bookmarks.' => Check::userErrors() ] ); + return Views::view( 'bookmarks.export', $folders ); + } + + $htmlDoc = ''; + $htmlDoc .= '' . PHP_EOL; + $htmlDoc .= '' . PHP_EOL; + $htmlDoc .= '' . PHP_EOL; + $htmlDoc .= 'Bookmarks' . PHP_EOL; + $htmlDoc .= '

Bookmarks

' . PHP_EOL; + $htmlDoc .= '

' . PHP_EOL; + foreach ( Input::post('BF_') as $key => $id ) { + if ( $id == 'unsorted' ) { + continue; + } + + $folder = self::$folders->findById( $id ); + if ( $folder == false ) { + Session::flash( 'error', 'Folder not found.' ); + return Redirect::to( 'bookmarks/index' ); + } + $links = self::$bookmarks->byFolder( $folder->ID ); + $htmlDoc .= $this->exportFolder( $folder->title, $folder->createdAt, $folder->createdAt, $links ); + } + $htmlDoc .= '

' . PHP_EOL; + + $folder = UPLOAD_DIRECTORY . App::$activeUser->username; + if ( !file_exists( $folder ) ) { + mkdir( $folder, 0777, true ); + } + $file = $folder . DIRECTORY_SEPARATOR . 'export.html'; + + $result = file_put_contents( $file, $htmlDoc ); + + if ($result !== false) { + $filename = basename($file); + $downloadUrl = '/uploads/' . App::$activeUser->username . '/export.html'; + + Session::flash( 'success', 'Your Export is available for download here.' ); + Redirect::to( 'bookmarks/export' ); + } else { + Session::flash( 'error', 'There was an issue exporting your bookmarks, please try again.' ); + return Redirect::to( 'bookmarks/export' ); + } + } + + private function exportFolder( $title, $editedAt, $createdAt, $links ) { + $htmlDoc = '

'.$title.'

' . PHP_EOL; + $htmlDoc .= '

' . PHP_EOL; + if ( ! empty( $links ) ) { + foreach ( $links as $key => $link ) { + $htmlDoc .= $this->exportLink( $link->url, $link->icon, $link->createdAt, $link->title ); + } + } + $htmlDoc .= '

' . PHP_EOL; + return $htmlDoc; + } + + private function exportLink( $url, $icon, $createdAt, $title ) { + $htmlDoc = '

'.$title.'' . PHP_EOL; + return $htmlDoc; + } + + public function share( $id = '' ) { + $panelArray = []; + $folders = self::$folders->byUser(); + foreach ( $folders as $key => $folder ) { + $panel = new \stdClass(); + $folderObject = new \stdClass(); + if ( $folder->privacy == 'private' ) { + $folderObject->privacyBadge = 'Private'; + $links = self::$bookmarks->publicByFolder( $folder->ID ); + } else { + $folderObject->privacyBadge = 'Public'; + $links = self::$bookmarks->byFolder( $folder->ID ); + } + $folderObject->bookmarks = $links; + + $folderObject->ID = $folder->ID; + $folderObject->uuid = $folder->uuid; + $folderObject->title = $folder->title; + $folderObject->color = $folder->color; + + + + $folderObject->bookmarkListRows = Views::simpleView( 'bookmarks.components.shareListRows', $folderObject->bookmarks ); + $panel->panel = Views::simpleView( 'bookmarks.components.shareListPanel', [$folderObject] ); + $panelArray[] = $panel; + } + return Views::view( 'bookmarks.share', $panelArray ); + } + public function parseBookmarks($htmlContent) { $started = false; @@ -511,9 +649,6 @@ class Bookmarks extends Controller { return $out; } - - - private function setFolderSelect( $folderID ) { $options = self::$folders->simpleByUser(); $out = ''; diff --git a/app/plugins/bookmarks/controllers/shared.php b/app/plugins/bookmarks/controllers/shared.php new file mode 100644 index 0000000..55c4b72 --- /dev/null +++ b/app/plugins/bookmarks/controllers/shared.php @@ -0,0 +1,127 @@ + + * @link https://TheTempusProject.com + * @license https://opensource.org/licenses/MIT [MIT LICENSE] + */ +namespace TheTempusProject\Controllers; + +use TheTempusProject\Hermes\Functions\Redirect; +use TheTempusProject\Bedrock\Functions\Session; +use TheTempusProject\Houdini\Classes\Views; +use TheTempusProject\Classes\Controller; +use TheTempusProject\Models\Bookmarks as Bookmark; +use TheTempusProject\Models\Folders; +use TheTempusProject\TheTempusProject as App; +use TheTempusProject\Houdini\Classes\Components; +use TheTempusProject\Hermes\Functions\Route as Routes; + +class Shared extends Controller { + protected static $bookmarks; + protected static $folders; + + public function __construct() { + parent::__construct(); + self::$bookmarks = new Bookmark; + self::$folders = new Folders; + self::$title = 'Bookmarks - {SITENAME}'; + self::$pageDescription = 'Add and save url bookmarks here.'; + Components::set( 'SITE_URL', Routes::getAddress() ); + } + + 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 ); + $panelArray[] = $folderObject; + } + } + Components::set( 'foldersList', Views::simpleView( 'bookmarks.folders.list', $folders ) ); + Components::set( 'folderPanels', Views::simpleView( 'bookmarks.components.bookmarkListPanel', $panelArray ) ); + Components::set( 'bookmarksList', Views::simpleView( 'bookmarks.bookmarks.list', $bookmarks ) ); + return Views::view( 'bookmarks.dash' ); + } + + public function shared( $type = '', $id = '' ) { + if ( empty( $type ) ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + + if ( empty( $id ) ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + + $type = strtolower( $type ); + if ( ! in_array( $type, ['link','folder'] ) ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + + $this->$type( $id ); + } + + public function link( $id = '' ) { + if ( empty( $id ) ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + $bookmark = self::$bookmarks->findByUuid( $id ); + if ( $bookmark == false ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + if ( $bookmark->createdBy != App::$activeUser->ID ) { + if ( $bookmark->privacy == 'private' ) { + if ( empty( $bookmark->folderID ) ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + $folder = self::$folders->findByUuid( $bookmark->folderID ); + if ( $folder == false ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + if ( $folder->privacy == 'private' ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + } + } + return Views::view( 'bookmarks.shareLink', $bookmark ); + } + + public function folder( $id = '' ) { + if ( empty( $id ) ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + $folder = self::$folders->findByUuid( $id ); + if ( $folder == false ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + if ( $folder->privacy == 'private' ) { + Session::flash( 'error', 'Unknown share' ); + return Redirect::to( 'home/index' ); + } + return Views::view( 'bookmarks.shareFolder', $folder ); + } +} diff --git a/app/plugins/bookmarks/forms.php b/app/plugins/bookmarks/forms.php index a42c6b7..d6a48f3 100644 --- a/app/plugins/bookmarks/forms.php +++ b/app/plugins/bookmarks/forms.php @@ -26,6 +26,7 @@ class BookmarksForms extends Forms { self::addHandler( 'editBookmark', __CLASS__, 'editBookmark' ); self::addHandler( 'editFolder', __CLASS__, 'editFolder' ); self::addHandler( 'importBookmarks', __CLASS__, 'importBookmarks' ); + self::addHandler( 'exportBookmarks', __CLASS__, 'exportBookmarks' ); } public static function createBookmark() { @@ -141,6 +142,32 @@ class BookmarksForms extends Forms { // } return true; } + + public static function exportBookmarks() { + if ( ! Input::exists( 'submit' ) ) { + return false; + } + if ( ! Input::exists( 'BF_' ) ) { + 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 index 3ad8c84..0c420d9 100644 --- a/app/plugins/bookmarks/models/bookmarks.php +++ b/app/plugins/bookmarks/models/bookmarks.php @@ -62,6 +62,7 @@ class Bookmarks extends DatabaseModel { 'color' => $color, 'privacy' => $privacy, 'createdBy' => $user, + 'uuid' => generateUuidV4(), 'createdAt' => time(), ]; if ( !empty( $folderID ) ) { @@ -102,6 +103,20 @@ class Bookmarks extends DatabaseModel { return true; } + public function findByUuid( $id ) { + $whereClause = ['uuid', '=', $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->first() ); + } + public function byUser( $limit = null ) { $whereClause = ['createdBy', '=', App::$activeUser->ID]; if ( empty( $limit ) ) { @@ -132,6 +147,21 @@ class Bookmarks extends DatabaseModel { return $this->filter( $bookmarks->results() ); } + public function publicByFolder( $id, $limit = null ) { + $whereClause = ['createdBy', '=', App::$activeUser->ID, 'AND']; + $whereClause = array_merge( $whereClause, [ 'folderID', '=', $id, 'AND', 'privacy', '=', 'public' ] ); + 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 ) ) { @@ -318,6 +348,12 @@ class Bookmarks extends DatabaseModel { $instance->iconHtml = ''; } } + + if ( $instance->privacy == 'private' ) { + $instance->privacyBadge = 'Private'; + } else { + $instance->privacyBadge = 'Public'; + } if ( empty( $instance->hiddenAt ) ) { $instance->hideBtn = ' @@ -392,6 +428,38 @@ class Bookmarks extends DatabaseModel { } return true; } + + public function publish( $id ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Bookmarks: illegal ID.' ); + return false; + } + $fields = [ + 'privacy' => 'public', + ]; + if ( !self::$db->update( $this->tableName, $id, $fields ) ) { + new CustomException( 'bookmarkUpdate' ); + Debug::error( "Bookmarks: $id not updated" ); + return false; + } + return true; + } + public function retract( $id ) { + if ( !Check::id( $id ) ) { + Debug::info( 'Bookmarks: illegal ID.' ); + return false; + } + $fields = [ + 'privacy' => 'private', + ]; + 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 ) ) { diff --git a/app/plugins/bookmarks/models/bookmarkviews.php b/app/plugins/bookmarks/models/bookmarkviews.php index 624b336..f1a6842 100644 --- a/app/plugins/bookmarks/models/bookmarkviews.php +++ b/app/plugins/bookmarks/models/bookmarkviews.php @@ -56,6 +56,7 @@ class Bookmarkviews extends DatabaseModel { 'title' => $title, 'description' => $description, 'privacy' => $privacy, + 'uuid' => generateUuidV4(), 'createdBy' => App::$activeUser->ID, 'createdAt' => time(), ]; diff --git a/app/plugins/bookmarks/models/folders.php b/app/plugins/bookmarks/models/folders.php index 7591fbe..2b042e7 100644 --- a/app/plugins/bookmarks/models/folders.php +++ b/app/plugins/bookmarks/models/folders.php @@ -38,6 +38,20 @@ class Folders extends DatabaseModel { parent::__construct(); } + public function findByUuid( $id ) { + $whereClause = ['uuid', '=', $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->first() ); + } + public function create( $title, $folderID = 0, $description = '', $color = 'default', $privacy = 'private', $user = null ) { if ( empty( $user ) ) { $user = App::$activeUser->ID; @@ -51,6 +65,7 @@ class Folders extends DatabaseModel { 'description' => $description, 'color' => $color, 'privacy' => $privacy, + 'uuid' => generateUuidV4(), 'createdBy' => $user, 'createdAt' => time(), ]; diff --git a/app/plugins/bookmarks/views/bookmarks/view.html b/app/plugins/bookmarks/views/bookmarks/view.html index 6ab9258..b0f17b0 100644 --- a/app/plugins/bookmarks/views/bookmarks/view.html +++ b/app/plugins/bookmarks/views/bookmarks/view.html @@ -1,19 +1,3 @@ - - - - - - - - - - - - - - - -
@@ -109,12 +93,4 @@
- - - - - - - - - + \ No newline at end of file diff --git a/app/plugins/bookmarks/views/components/shareListPanel.html b/app/plugins/bookmarks/views/components/shareListPanel.html new file mode 100644 index 0000000..a38e767 --- /dev/null +++ b/app/plugins/bookmarks/views/components/shareListPanel.html @@ -0,0 +1,45 @@ +{LOOP} +
+
+
+
+ {title}{privacyBadge} + + + + +
+
+
+
    + {bookmarkListRows} +
+
+ +
+
+
+
+{/LOOP} +{ALT} +
+

no folders

+
+{/ALT} \ No newline at end of file diff --git a/app/plugins/bookmarks/views/components/shareListRows.html b/app/plugins/bookmarks/views/components/shareListRows.html new file mode 100644 index 0000000..a596cae --- /dev/null +++ b/app/plugins/bookmarks/views/components/shareListRows.html @@ -0,0 +1,30 @@ +{LOOP} +
  • + {iconHtml} + {title}{privacyBadge} + + + + + + + +
  • +{/LOOP} +{ALT} +
  • +

    No Bookmarks

    +
  • +{/ALT} \ No newline at end of file diff --git a/app/plugins/bookmarks/views/export.html b/app/plugins/bookmarks/views/export.html new file mode 100644 index 0000000..aeebc4d --- /dev/null +++ b/app/plugins/bookmarks/views/export.html @@ -0,0 +1,48 @@ +
    +
    + Bookmark Export +
    +

    Select which folders to include in the export.

    +
    +
    + + + + + + + + + + + + + {LOOP} + + + + + {/LOOP} + {ALT} + + + + {/ALT} + +
    Title + +
    Unsorted + +
    {prettyTitle} + +
    + No Folders To Export +
    +
    +
    +

    Literally every browser is chromium based now, so they all have a standard import/export.

    + +
    + +
    +
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/folders/listPage.html b/app/plugins/bookmarks/views/folders/listPage.html index c6324f0..de5e22e 100644 --- a/app/plugins/bookmarks/views/folders/listPage.html +++ b/app/plugins/bookmarks/views/folders/listPage.html @@ -1,8 +1,6 @@ -
    -
    -
    - Folders List -
    - {foldersList} +
    +
    + Folders List
    + {foldersList}
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/folders/view.html b/app/plugins/bookmarks/views/folders/view.html index 2136b76..4094cfa 100644 --- a/app/plugins/bookmarks/views/folders/view.html +++ b/app/plugins/bookmarks/views/folders/view.html @@ -1,13 +1,3 @@ - - - - - - - - - -
    @@ -68,12 +58,4 @@
    -
    - - - - - - - - +
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/import.html b/app/plugins/bookmarks/views/import.html index ec0c98d..e79d544 100644 --- a/app/plugins/bookmarks/views/import.html +++ b/app/plugins/bookmarks/views/import.html @@ -1,18 +1,15 @@ -
    - Import Bookmarks -
    -
    - -
    +
    +
    + Import Bookmarks +
    +
    + + -
    +
    - -
    - -
    - -
    +
    +
    - +
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/nav/folderTabs.html b/app/plugins/bookmarks/views/nav/folderTabs.html index d3e18d9..f73debb 100644 --- a/app/plugins/bookmarks/views/nav/folderTabs.html +++ b/app/plugins/bookmarks/views/nav/folderTabs.html @@ -2,5 +2,7 @@ + + {userFolderTabs} \ No newline at end of file diff --git a/app/plugins/bookmarks/views/share.html b/app/plugins/bookmarks/views/share.html new file mode 100644 index 0000000..ea2be1f --- /dev/null +++ b/app/plugins/bookmarks/views/share.html @@ -0,0 +1,19 @@ +
    +
    + Share +
    +
    +

    Any link or folder can be shared. By default, the extensions and app both default to private. On this page, you can quickly see a list of any public links and folders. These "public" items can be shared with anyone who has the link.

    +
    + {LOOP} +
    + {panel} +
    + {/LOOP} + {ALT} +
    +

    No public folders found.

    +
    + {/ALT} +
    +
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/shareFolder.html b/app/plugins/bookmarks/views/shareFolder.html new file mode 100644 index 0000000..25ebcb3 --- /dev/null +++ b/app/plugins/bookmarks/views/shareFolder.html @@ -0,0 +1,48 @@ +
    +
    +
    +
    + +
    +

    Bookmark Folder

    +
    + + +
    +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Title:{title}
    Privacy:{privacy}
    Color:{color}
    Created:{DTC}{createdAt}{/DTC}
    Description
    {description}
    +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/app/plugins/bookmarks/views/shareLink.html b/app/plugins/bookmarks/views/shareLink.html new file mode 100644 index 0000000..61943d6 --- /dev/null +++ b/app/plugins/bookmarks/views/shareLink.html @@ -0,0 +1,83 @@ +
    +
    +
    +
    + +
    +

    Bookmark

    +
    + + +
    +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Title:{title}
    URL:{url}
    Type:{linkType}
    Privacy:{privacy}
    Color:{color}
    Created:{DTC}{createdAt}{/DTC}
    Archived:{DTC}{archivedAt}{/DTC}
    Hidden:{DTC}{hiddenAt}{/DTC}
    Last Refreshed:{DTC}{refreshedAt}{/DTC}
    Description
    {description}
    Icon
    {iconHtml}
    {icon}
    Meta
    {meta}
    +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/app/plugins/members/controllers/member.php b/app/plugins/members/controllers/member.php index 462c2e0..21b6143 100644 --- a/app/plugins/members/controllers/member.php +++ b/app/plugins/members/controllers/member.php @@ -30,42 +30,46 @@ use TheTempusProject\Houdini\Classes\Components; use TheTempusProject\Classes\Forms; use TheTempusProject\Bedrock\Functions\Hash; use TheTempusProject\Canary\Bin\Canary as Debug; +use Stripe\StripeClient; class Member extends Controller { public static $customers; public static $products; + public static $stripe; + private static $loaded = false; public function __construct() { parent::__construct(); - Template::noIndex(); - $api_key = Config::getValue( 'memberships/stripeSecret' ); - self::$customers = new MembershipCustomers; - self::$products = new MembershipProducts; + + if ( ! self::$loaded ) { + Template::noIndex(); + self::$customers = new MembershipCustomers; + self::$products = new MembershipProducts; + $api_key = Config::getValue( 'memberships/stripeSecret' ); + if ( $api_key == 'sk_xxxxxxxxxxxxxxx' || empty($api_key) ) { + Debug::error( "Memberships:__construct No Stripe Key found" ); + } else { + self::$stripe = new StripeClient( $api_key ); + } + self::$loaded = true; + } } public function index() { + $this->confirmAuth(); self::$title = 'Members Area'; - if ( !App::$isMember ) { - Session::flash( 'error', 'You do not have permission to view this page.' ); - return Redirect::home(); - } Views::view( 'members.members' ); } public function managepayment( $id = null ) { - - - - $api_key = Config::getValue( 'memberships/stripeSecret' ); - $stripe = new \Stripe\StripeClient( $api_key ); - - $customer = self::$customers->findOrCreate( App::$activeUser->ID ); + $this->confirmAuth(); + $customer = self::$customers->findByUserID( App::$activeUser->ID ); if ( empty( $customer ) ) { Session::flash( 'error', 'You do not have any active payment methods. You can subscribe by going here' ); return Redirect::to( 'member/manage' ); } try { - $session = $stripe->billingPortal->sessions->create([ + $session = self::$stripe->billingPortal->sessions->create([ 'customer' => $customer, 'return_url' => Routes::getAddress() . 'member/manage', ]); @@ -76,21 +80,14 @@ class Member extends Controller { return Redirect::to( 'member/manage' ); } - - - - - - - header('Location: ' . $session->url); exit; } public function cancelconfirm( $id = null ) { + $this->confirmAuth(); $memberships = new Memberships; $result = $memberships->cancel( $id ); - // dv( $result ); if ( ! empty( $result ) ) { Session::flash( 'success', 'Your Membership has been paused.' ); Redirect::to( 'member/manage' ); @@ -101,9 +98,9 @@ class Member extends Controller { } public function pauseconfirm( $id = null ) { + $this->confirmAuth(); $memberships = new Memberships; $result = $memberships->cancel( $id ); - // dv( $result ); if ( ! empty( $result ) ) { Session::flash( 'success', 'Your Membership has been paused.' ); Redirect::to( 'member/manage' ); @@ -114,42 +111,64 @@ class Member extends Controller { } public function pause( $id = null ) { + $this->confirmAuth(); self::$title = 'pause Membership'; Components::set( 'pauseid', $id ); Views::view( 'members.pause' ); } public function resume( $id = null ) { + $this->confirmAuth(); self::$title = 'resume Membership'; Views::view( 'members.resume' ); } public function cancel( $id = null ) { + $this->confirmAuth(); self::$title = 'Cancel Membership'; Components::set( 'cancelid', $id ); Views::view( 'members.cancel' ); } public function manage( $id = null ) { + if ( ! App::$isLoggedIn ) { + Session::flash( 'error', 'You do not have permission to access this page.' ); + return Redirect::home(); + } self::$title = 'Manage Membership'; - + $menu = Views::simpleView( 'nav.usercp', App::$userCPlinks ); Navigation::activePageSelect( $menu, null, true, true ); - + $memberships = new Memberships; $userMemberships = $memberships->getUserSubs(); - Views::view( 'members.manage', $userMemberships ); } - + public function upgrade( $id = null ) { + if ( ! App::$isLoggedIn ) { + Session::flash( 'error', 'You do not have permission to access this page.' ); + return Redirect::home(); + } self::$title = 'Upgrade Membership'; Views::view( 'members.upgrade' ); } - public function join( $id = null ) { + public function join( $plan = 'monthly' ) { + if ( ! App::$isLoggedIn ) { + Session::flash( 'error', 'You do not have permission to access this page.' ); + return Redirect::home(); + } + $plan = strtolower( $plan ); + if ( ! in_array( $plan, ['monthly','yearly'] ) ) { + Session::flash( 'error', 'Unknown plan' ); + return Redirect::to( 'home/index' ); + } + self::$title = 'Join {SIITENAME}!'; - $product = self::$products->findById( $id ); + $stripePrice = $this->findPrice( $plan ); + + $product = self::$products->findByPriceID( $stripePrice ); if ( empty( $product ) ) { Session::flash( 'success', 'We aren\'t currently accepting new members, please check back soon!' ); return Redirect::home(); @@ -157,92 +176,50 @@ class Member extends Controller { Views::view( 'members.landing1', $product ); } - public function getyearly( $id = null ) { - if ( empty( $id ) ) { - Issues::add( 'error', 'no id' ); - return $this->index(); - } - $product = self::$products->findById( $id ); - if ( empty( $product ) ) { - Issues::add( 'error', 'no product' ); - return $this->index(); + public function checkout( $plan = 'monthly' ) { + if ( ! App::$isLoggedIn ) { + Session::flash( 'error', 'You do not have permission to access this page.' ); + return Redirect::home(); } $customer = self::$customers->findOrCreate( App::$activeUser->ID ); if ( empty( $customer ) ) { Issues::add( 'error', 'no customer' ); return $this->index(); } + $stripePrice = $this->findPrice( $plan ); - self::$title = 'Purchase'; - $price = $product->stripe_price_yearly; - $api_key = Config::getValue( 'memberships/stripeSecret' ); - $stripe = new \Stripe\StripeClient( $api_key ); - $session = $stripe->checkout->sessions->create([ + $session = self::$stripe->checkout->sessions->create([ 'payment_method_types' => ['card'], 'customer' => $customer, 'line_items' => [[ - 'price' => $price, + 'price' => $stripePrice, 'quantity' => 1, ]], 'mode' => 'subscription', - 'success_url' => Routes::getAddress() . 'member/paymentcomplete?session_id={CHECKOUT_SESSION_ID}', - 'cancel_url' => Routes::getAddress() . 'member/paymentcanceled', + 'success_url' => Routes::getAddress() . 'member/payment/complete?session_id={CHECKOUT_SESSION_ID}', + 'cancel_url' => Routes::getAddress() . 'member/payment/cancel', ]); header('Location: ' . $session->url); exit; } - public function getmonthly( $id = null ) { - if ( empty( $id ) ) { - Issues::add( 'error', 'no id' ); - return $this->index(); - } - $product = self::$products->findById( $id ); - if ( empty( $product ) ) { - Issues::add( 'error', 'no product' ); - return $this->index(); - } - $customer = self::$customers->findOrCreate( App::$activeUser->ID ); - if ( empty( $customer ) ) { - Issues::add( 'error', 'no customer' ); - return $this->index(); + public function payment( $type = '' ) { + $type = strtolower( $type ); + if ( ! in_array( $type, ['cancel','complete'] ) ) { + Session::flash( 'error', 'Unknown Payment' ); + return Redirect::to( 'home/index' ); } - self::$title = 'Purchase'; - $price = $product->stripe_price_monthly; - $api_key = Config::getValue( 'memberships/stripeSecret' ); - $stripe = new \Stripe\StripeClient( $api_key ); - $session = $stripe->checkout->sessions->create([ - 'payment_method_types' => ['card'], - 'customer' => $customer, - 'line_items' => [[ - 'price' => $price, - 'quantity' => 1, - ]], - 'mode' => 'subscription', - 'success_url' => Routes::getAddress() . 'member/paymentcomplete?session_id={CHECKOUT_SESSION_ID}', - 'cancel_url' => Routes::getAddress() . 'member/paymentcanceled', - ]); - header('Location: ' . $session->url); - exit; - } - - public function paymentcanceled() { - self::$title = '(almost) Members Area'; - Views::view( 'members.paymentcanceled' ); - } + if ( $type == 'cancel' ) { + self::$title = '(almost) Members Area'; + return Views::view( 'members.paymentcanceled' ); + } - public function paymentcomplete() { self::$title = '(almost) Members Area'; Views::view( 'members.paymentcomplete' ); } - - - - - - + // This combines a registration with a checkout public function signup( $plan = 'monthly' ) { $plan = strtolower( $plan ); if ( ! in_array( $plan, ['monthly','yearly'] ) ) { @@ -255,11 +232,10 @@ class Member extends Controller { Session::flash( 'error', 'Unknown product' ); return Redirect::to( 'home/index' ); } + + $stripePrice = $this->findPrice( $plan ); - $index = 'stripe_price_' . $plan; $pretty = 'prettyPrice' . ucfirst( $plan ); - - $stripePrice = $product->$index; $prettyPrice = $product->$pretty; self::$title = 'Sign up for {SITENAME} ' . ucfirst( $plan ); @@ -267,17 +243,21 @@ class Member extends Controller { Components::set( 'planName', ucfirst( $plan ) ); Components::set( 'prettyPrice', $prettyPrice ); Components::set( 'TERMS', Views::simpleView( 'terms' ) ); + if ( App::$isLoggedIn ) { - Session::flash( 'notice', 'You are already logged in, were you looking for information on Upgrading?' ); + Session::flash( 'notice', 'You are already logged in, you can subscribe here, or upgrade here.' ); return Redirect::to( 'home/index' ); } + if ( !Input::exists() ) { return Views::view( 'members.register' ); } + if ( ! Forms::check( 'register' ) ) { Issues::add( 'error', [ 'There was an error with your registration.' => Check::userErrors() ] ); return Views::view( 'members.register' ); } + self::$user->create( [ 'username' => Input::post( 'username' ), 'password' => Hash::make( Input::post( 'password' ) ), @@ -297,10 +277,7 @@ class Member extends Controller { Session::flash( 'error', 'Thank you for registering! Unfortunately, there was an issue communicating with Stripe and we can\'t collect payment right now.' ); return Redirect::to( 'home/index' ); } - - $api_key = Config::getValue( 'memberships/stripeSecret' ); - $stripe = new \Stripe\StripeClient( $api_key ); - $session = $stripe->checkout->sessions->create([ + $session = self::$stripe->checkout->sessions->create([ 'payment_method_types' => ['card'], 'customer' => $customer, 'line_items' => [[ @@ -308,10 +285,34 @@ class Member extends Controller { 'quantity' => 1, ]], 'mode' => 'subscription', - 'success_url' => Routes::getAddress() . 'member/paymentcomplete?session_id={CHECKOUT_SESSION_ID}', - 'cancel_url' => Routes::getAddress() . 'member/paymentcanceled', + 'success_url' => Routes::getAddress() . 'member/payment/complete?session_id={CHECKOUT_SESSION_ID}', + 'cancel_url' => Routes::getAddress() . 'member/payment/cancel', ]); header('Location: ' . $session->url); exit; } + + private function findPrice( $plan ) { + $plan = strtolower( $plan ); + if ( ! in_array( $plan, ['monthly','yearly'] ) ) { + Session::flash( 'error', 'Unknown plan' ); + return Redirect::to( 'home/index' ); + } + + $product = self::$products->mainProduct(); + if ( empty( $product ) ) { + Session::flash( 'error', 'Unknown product' ); + return Redirect::to( 'home/index' ); + } + $index = 'stripe_price_' . $plan; + $stripePrice = $product->$index; + return $stripePrice; + } + + private function confirmAuth() { + if ( ! App::$isLoggedIn || ! App::$isMember ) { + Session::flash( 'error', 'You do not have permission to access this page.' ); + return Redirect::home(); + } + } } \ No newline at end of file diff --git a/app/plugins/members/models/memberships.php b/app/plugins/members/models/memberships.php index 9c7f853..43e5916 100644 --- a/app/plugins/members/models/memberships.php +++ b/app/plugins/members/models/memberships.php @@ -49,8 +49,6 @@ class Memberships extends DatabaseModel { } self::$loaded = true; } - - } public function filter( $postArray, $params = [] ) { diff --git a/app/plugins/members/plugin.php b/app/plugins/members/plugin.php index 11c4f8b..9651282 100644 --- a/app/plugins/members/plugin.php +++ b/app/plugins/members/plugin.php @@ -18,6 +18,7 @@ use Stripe\StripeClient; use TheTempusProject\Bedrock\Classes\Config; use TheTempusProject\Hermes\Functions\Route as Routes; use TheTempusProject\Models\Memberships; +use TheTempusProject\Models\MembershipProducts as Products; class Members extends Plugin { public static $stripe; @@ -42,7 +43,7 @@ class Members extends Plugin { ]; public $admin_links = [ [ - 'text' => ' Memberships', + 'text' => ' Memberships', 'url' => [ [ 'text' => ' Products', @@ -60,15 +61,20 @@ class Members extends Plugin { ], ]; public $main_links = [ + // [ + // 'text' => 'Members', + // 'url' => '{ROOT_URL}member/index', + // 'filter' => 'member', + // ], [ - 'text' => 'Members', - 'url' => '{ROOT_URL}member/index', - 'filter' => 'member', + 'text' => 'Subscribe', + 'url' => '{ROOT_URL}member/join', + 'filter' => 'nonmember', ], [ - 'text' => 'Become a Member', - 'url' => '{ROOT_URL}member/join/1', - 'filter' => 'nonmember', + 'text' => 'Upgrade', + 'url' => '{ROOT_URL}member/upgrade', + 'filter' => 'upgrade', ], ]; public $resourceMatrix = [ @@ -129,7 +135,13 @@ class Members extends Plugin { $this->filters[] = [ 'name' => 'nonmember', 'find' => '#{NONMEMBER}(.*?){/NONMEMBER}#is', - 'replace' => ( App::$isLoggedIn && ! App::$isMember ? '$1' : '' ), + 'replace' => ( App::$isLoggedIn && App::$isMember == false ? '$1' : '' ), + 'enabled' => true, + ]; + $this->filters[] = [ + 'name' => 'upgrade', + 'find' => '#{UPGRADE}(.*?){/UPGRADE}#is', + 'replace' => ( App::$isLoggedIn && ( App::$isMember === 'monthly' ) ? '$1' : '' ), 'enabled' => true, ]; $api_key = Config::getValue( 'memberships/stripeSecret' ); @@ -147,14 +159,20 @@ class Members extends Plugin { } return false; } - + public function userHasActiveMembership( $user_id ) { - self::$memberships = new Memberships; - $membership = self::$memberships->findActiveByUserID( $user_id ); + $memberships = new Memberships; + $membership = $memberships->findActiveByUserID( $user_id ); if ( empty( $membership ) ) { return false; } - return true; + + $products = new Products; + $product = $products->findByPriceID( $membership->subscription_price_id ); + if ( $product->stripe_price_monthly == $membership->subscription_price_id ) { + return 'monthly'; + } + return 'yearly'; } public static function webhookSetup() { diff --git a/app/plugins/members/views/landing1.html b/app/plugins/members/views/landing1.html index 67d8c30..21cb598 100644 --- a/app/plugins/members/views/landing1.html +++ b/app/plugins/members/views/landing1.html @@ -1,62 +1,128 @@ -
    -
    -
    -

    Organize Your Bookmarks

    -

    Keep all your bookmarks organized with custom categories and tags.

    -
    -
    -

    Import and Export

    -

    Seamlessly import and export your bookmarks (paid plans).

    -
    -
    -

    Accessible Anywhere

    -

    Your bookmarks are securely stored and accessible from any device.

    -
    -
    -
    -
    -
    -

    Pricing

    -
    -
    -
    -
    -

    Free

    -
    -
    -

    Basic bookmark storage

    -

    No import/export

    -

    Completely free

    - Sign Up -
    -
    -
    -
    -
    -
    -

    {name} Monthly

    -
    -
    -

    Import/export bookmarks

    -

    Advanced curation tools

    -

    {prettyPriceMonthly}/month

    - Sign Up -
    -
    -
    -
    -
    -
    -

    {name} Yearly

    -
    -
    -

    Save with annual billing

    -

    All features included

    -

    {prettyPriceYearly}/year

    - Sign Up -
    -
    -
    -
    -
    -
    \ No newline at end of file + +
    +

    Compare plans

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FreeProEnterprise
    Add and Manage Bookmarks
    Extensions for all major browsers
    Access from any device
    Import/Export Features
    Customizable Dashboards / Pages
    Request/Influence Development
    Early Access
    Cheaper
    +
    + +
    + + +
    +
    +
    +
    +
    +

    Free

    +
    +
    +

    $0/mo

    +
      +
    • Add / Manage your bookmarks
    • +
    • Extensions for all major browsers
    • +
    • Access from any device
    • +
    + + Sign-Up for Free + +
    +
    +
    +
    +
    +
    +

    Monthly

    +
    +
    +

    $4.99/month

    +
      +
    • Import/Export Features
    • +
    • Integration with TempusTools App (WIP)
    • +
    • Customizable Dashboards / Pages
    • +
    • Direct control of Feature Development
    • +
    • Early Access to new features
    • +
    + + Get started + +
    +
    +
    +
    +
    +
    +

    Yearly

    +
    +
    +

    $19.99/year

    +
      +
    • Its cheaper if you like the product
    • +
    + + Get started + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/app/plugins/members/views/landing2.html b/app/plugins/members/views/landing2.html index fe76c66..4854f99 100644 --- a/app/plugins/members/views/landing2.html +++ b/app/plugins/members/views/landing2.html @@ -20,7 +20,7 @@

    {prettyPriceMonthly}/month

    All pro features unlocked

    - Get Started + Get Started
    @@ -30,7 +30,7 @@

    {prettyPriceYearly}/year

    Save {prettySavings} annually!

    - Sign Up + Sign Up
    diff --git a/app/plugins/members/views/upgrade.html b/app/plugins/members/views/upgrade.html index f28404e..176c39d 100644 --- a/app/plugins/members/views/upgrade.html +++ b/app/plugins/members/views/upgrade.html @@ -1,27 +1,24 @@ -
    +
    -
    -

    Upgrade to a Yearly Plan

    +

    Upgrade to a Yearly Plan

    - Save more and enjoy uninterrupted access to all features with our yearly plan! + Save more and enjoy uninterrupted access to all features with our yearly plan!

    - Upgrading now means you'll save X% compared to the monthly plan. - Stay committed and make the most of our service. + Upgrading now means you'll save nearly $40 a year compared to the monthly plan. + Stay committed and make the most of our service.

    -
    - -
    -
    - -
    + +
    -
    -
    - \ No newline at end of file +
    \ No newline at end of file diff --git a/app/plugins/suggestions/views/view.html b/app/plugins/suggestions/views/view.html index c3645b9..fc3f097 100644 --- a/app/plugins/suggestions/views/view.html +++ b/app/plugins/suggestions/views/view.html @@ -1,18 +1,20 @@ -
    -
    -
    -

    {title}

    -
    - - {suggestion} - {ADMIN} +
    +
    +
    +
    +

    {title}


    - Delete - Edit -
    - {/ADMIN} -
    - {COMMENTS} - {NEWCOMMENT} -
    -
    \ No newline at end of file + + {suggestion} + {ADMIN} +
    + Delete + Edit +
    + {/ADMIN} +
    + {COMMENTS} + {NEWCOMMENT} +
    +
    +
    diff --git a/app/plugins/wip/plugin.php b/app/plugins/wip/plugin.php index cdede98..845cdfe 100644 --- a/app/plugins/wip/plugin.php +++ b/app/plugins/wip/plugin.php @@ -37,7 +37,7 @@ class Wip extends Plugin { ]; public $admin_links = [ [ - 'text' => ' Wip', + 'text' => ' Wip', 'url' => '{ROOT_URL}admin/wip', ], ]; diff --git a/app/views/index.html b/app/views/index.html index efc107b..558e5fd 100644 --- a/app/views/index.html +++ b/app/views/index.html @@ -14,13 +14,11 @@
    - Example image + Example image
    - -
    @@ -70,7 +68,7 @@
    - Bootstrap Themes + Bootstrap Themes

    It can be difficult to keep track of... all of the internet

    @@ -180,7 +178,7 @@
    - +
    @@ -346,9 +344,6 @@ - - -
    \ No newline at end of file + \ No newline at end of file