mvp
This commit is contained in:
@ -13,6 +13,7 @@ namespace TheTempusProject\Classes;
|
||||
|
||||
use TheTempusProject\Houdini\Classes\Issues;
|
||||
use TheTempusProject\Houdini\Classes\Forms;
|
||||
use TheTempusProject\Houdini\Classes\Template;
|
||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
||||
use TheTempusProject\Bedrock\Functions\Check;
|
||||
use TheTempusProject\Bedrock\Functions\Upload;
|
||||
@ -186,17 +187,90 @@ class Preferences {
|
||||
}
|
||||
|
||||
public function getFormHtml( $populated = [] ) {
|
||||
// dv( self::$preferences );
|
||||
$form = '';
|
||||
// Added so i can force some sort of ordering
|
||||
$inputTypes = [
|
||||
'file' => [],
|
||||
'select' => [],
|
||||
'timezone' => [],
|
||||
'checkbox' => [],
|
||||
'switch' => [],
|
||||
];
|
||||
foreach ( self::$preferences as $name => $details ) {
|
||||
$tempPrefsArray = $this->normalizePreferenceArray( $name, $details );
|
||||
if ( isset( $populated[ $name ] ) ) {
|
||||
$tempPrefsArray['default'] = $populated[$name];
|
||||
}
|
||||
$form .= Forms::getFormFieldHtml( $name, $tempPrefsArray['pretty'], $tempPrefsArray['type'], $tempPrefsArray['default'], $tempPrefsArray['options'] );
|
||||
// $form .= Forms::getFormFieldHtml( $name, $tempPrefsArray['pretty'], $tempPrefsArray['type'], $tempPrefsArray['default'], $tempPrefsArray['options'] );
|
||||
if ( $tempPrefsArray['type'] == 'checkbox' ) {
|
||||
$tempPrefsArray['type'] = 'switch';
|
||||
}
|
||||
$inputTypes[ $tempPrefsArray['type'] ][] = self::getFormFieldHtml( $name, $tempPrefsArray['pretty'], $tempPrefsArray['type'], $tempPrefsArray['default'], $tempPrefsArray['options'] );
|
||||
}
|
||||
foreach ( $inputTypes as $skip => $items ) {
|
||||
$form .= implode( ' ', $items );
|
||||
}
|
||||
return $form;
|
||||
}
|
||||
|
||||
public static function getFormFieldHtml( $fieldname, $fieldTitle, $type, $defaultValue = '', $options = null ) {
|
||||
$html = '';
|
||||
switch ( $type ) {
|
||||
case 'radio':
|
||||
case 'bool':
|
||||
case 'boolean':
|
||||
$fieldHtml = Forms::getRadioHtml( $fieldname, [ 'true', 'false' ], $defaultValue );
|
||||
break;
|
||||
case 'select':
|
||||
$fieldHtml = Forms::getSelectHtml( $fieldname, $options, $defaultValue );
|
||||
break;
|
||||
case 'customSelect':
|
||||
if ( empty( $options ) ) {
|
||||
$options = '{' . $fieldname . '-options}';
|
||||
}
|
||||
$fieldHtml = Forms::getSelectHtml( $fieldname, $options, $defaultValue );
|
||||
break;
|
||||
case 'block':
|
||||
$fieldHtml = Forms::getTextBlockHtml( $fieldname, $defaultValue );
|
||||
break;
|
||||
case 'text':
|
||||
case 'url':
|
||||
$fieldHtml = Forms::getTextHtml( $fieldname, $defaultValue );
|
||||
break;
|
||||
case 'checkbox':
|
||||
$fieldHtml = Forms::getCheckboxHtml( $fieldname, $defaultValue );
|
||||
break;
|
||||
case 'switch':
|
||||
$fieldHtml = Forms::getSwitchHtml( $fieldname, $defaultValue );
|
||||
break;
|
||||
case 'timezone':
|
||||
$fieldHtml = Forms::getTimezoneHtml( $defaultValue );
|
||||
break;
|
||||
case 'file':
|
||||
$fieldHtml = Forms::getFileHtml( $fieldname );
|
||||
break;
|
||||
default:
|
||||
Debug::error( "unknown field type: $type" );
|
||||
break;
|
||||
}
|
||||
|
||||
$html .= '<div class="mb-3 row">';
|
||||
$html .= '<label for="' . $fieldname . '" class="col-lg-6 col-form-label text-end">' . $fieldTitle . '</label>';
|
||||
$html .= '<div class="col-lg-6">';
|
||||
$html .= $fieldHtml;
|
||||
$html .= '</div>';
|
||||
if ( 'file' === $type ) {
|
||||
$html .= '<div class="mb-3 row">';
|
||||
$html .= '<h4 class="col-lg-6 col-form-label text-end">Current Image</h4>';
|
||||
$html .= '<div class="col-lg-6">';
|
||||
$html .= '<img alt="User Avatar" src="{ROOT_URL}' . $defaultValue . '" class="img-circle img-fluid p-2 avatar-125">';
|
||||
$html .= '</div>';
|
||||
}
|
||||
$html .= '</div>';
|
||||
return Template::parse( $html );
|
||||
}
|
||||
|
||||
public function convertFormToArray( $fillMissing = true, $defaultsOnly = true ) {
|
||||
$prefsArray = [];
|
||||
foreach ( self::$preferences as $name => $details ) {
|
||||
|
@ -135,3 +135,8 @@ body {
|
||||
.text-shadow-3 {
|
||||
text-shadow: 0 .5rem 1.5rem rgba(255, 255, 255, .25);
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background-color: #1f1f1f;
|
||||
color: #e0e0e0;
|
||||
}
|
@ -317,31 +317,54 @@ body {
|
||||
a.atb-green:hover {
|
||||
color: #1b947f;
|
||||
}
|
||||
button.atb-green:hover {
|
||||
color: #1b947f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.atb-green-outline-only {
|
||||
border-color: #85bd3e;
|
||||
}
|
||||
a.atb-green-outline-only:hover {
|
||||
border-color: #85bd3e;
|
||||
}
|
||||
button.atb-green-outline-only:hover {
|
||||
border-color: #85bd3e;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.atb-green-outline {
|
||||
color: #85bd3e;
|
||||
border-color: #85bd3e;
|
||||
}
|
||||
.atb-green-outline-only {
|
||||
border-color: #85bd3e;
|
||||
}
|
||||
a.atb-green-outline:hover {
|
||||
color: #1b947f;
|
||||
border-color: #1b947f;
|
||||
}
|
||||
button.atb-green-outline:hover {
|
||||
color: #1b947f;
|
||||
border-color: #1b947f;
|
||||
}
|
||||
|
||||
|
||||
.atb-green-bg {
|
||||
color: #fff;
|
||||
background-color: #85bd3e;
|
||||
border-color: #85bd3e;
|
||||
}
|
||||
|
||||
a.atb-green-bg:hover {
|
||||
background-color: #1b947f;
|
||||
border-color: #1b947f;
|
||||
/* background-color: #44a466; */
|
||||
/* background-color: #3fa269; */
|
||||
}
|
||||
button.atb-green-bg:hover {
|
||||
background-color: #1b947f;
|
||||
border-color: #1b947f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.bookmark-card.dragging {
|
||||
opacity: 0.7;
|
||||
|
@ -71,6 +71,7 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function index() {
|
||||
self::$title = 'Manage Bookmarks - {SITENAME}';
|
||||
if ( Input::exists('submit') ) {
|
||||
$prefs = new Preferences;
|
||||
$user = new User;
|
||||
@ -116,6 +117,7 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function unsorted() {
|
||||
self::$title = 'Unsorted Bookmarks - {SITENAME}';
|
||||
$bookmarks = self::$bookmarks->noFolder();
|
||||
Views::view( 'bookmarks.bookmarks.unsorted', $bookmarks );
|
||||
}
|
||||
@ -147,6 +149,7 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function createBookmark( $id = null ) {
|
||||
self::$title = 'Add Bookmark - {SITENAME}';
|
||||
$folderID = Input::get('folder_id') ? Input::get('folder_id') : $id;
|
||||
$folderID = Input::post('folder_id') ? Input::post('folder_id') : $id;
|
||||
$this->setFolderSelect( $folderID );
|
||||
@ -182,6 +185,7 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function editBookmark( $id = null ) {
|
||||
self::$title = 'Edit Bookmark - {SITENAME}';
|
||||
$folderID = Input::exists('folder_id') ? Input::post('folder_id') : '';
|
||||
|
||||
$bookmark = self::$bookmarks->findById( $id );
|
||||
@ -263,6 +267,7 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function createFolder( $id = 0 ) {
|
||||
self::$title = 'Create Folder - {SITENAME}';
|
||||
$folderID = Input::exists('folder_id') ? Input::post('folder_id') : $id;
|
||||
$folders = self::$folders->simpleByUser();
|
||||
if ( ! empty( $folders ) ) {
|
||||
@ -287,6 +292,7 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function editFolder( $id = null ) {
|
||||
self::$title = 'Edit Folder - {SITENAME}';
|
||||
$folder = self::$folders->findById( $id );
|
||||
|
||||
if ( $folder == false ) {
|
||||
@ -345,6 +351,11 @@ class Bookmarks extends Controller {
|
||||
* Dashboards
|
||||
*/
|
||||
public function addDash() {
|
||||
self::$title = 'Add Dashboard - {SITENAME}';
|
||||
if ( !App::$isMember ) {
|
||||
Issues::add( 'notice', 'You must have an active membership to add dashboards.' );
|
||||
return $this->index();
|
||||
}
|
||||
$folders = self::$folders->byUser() ?? [];
|
||||
|
||||
if ( !empty( $folders ) ) {
|
||||
@ -387,6 +398,11 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function editDash( $id = null ) {
|
||||
self::$title = 'Edit Dashboard - {SITENAME}';
|
||||
if ( !App::$isMember ) {
|
||||
Issues::add( 'notice', 'You must have an active membership to edit dashboards.' );
|
||||
return $this->index();
|
||||
}
|
||||
$dash = self::$dashboards->findById( $id );
|
||||
|
||||
if ( $dash == false ) {
|
||||
@ -449,6 +465,10 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function deleteDash( $id = null ) {
|
||||
if ( !App::$isMember ) {
|
||||
Issues::add( 'notice', 'You must have an active membership to delete dashboards.' );
|
||||
return $this->index();
|
||||
}
|
||||
$dash = self::$dashboards->findById( $id );
|
||||
if ( $dash == false ) {
|
||||
Issues::add( 'error', 'Unknown Dashboard' );
|
||||
@ -468,6 +488,11 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function dashboard( $uuid = null ) {
|
||||
self::$title = 'Bookmark Dashboard - {SITENAME}';
|
||||
if ( !App::$isMember ) {
|
||||
Issues::add( 'notice', 'You must have an active membership to view dashboards.' );
|
||||
return $this->index();
|
||||
}
|
||||
$dash = self::$dashboards->findByUuid( $uuid );
|
||||
if ( $dash == false ) {
|
||||
return $this->dashboards();
|
||||
@ -476,6 +501,32 @@ class Bookmarks extends Controller {
|
||||
Issues::add( 'error', 'You do not have permission to view this dash.' );
|
||||
return $this->dashboards();
|
||||
}
|
||||
if ( Input::exists( 'submit' ) ) {
|
||||
if ( Forms::check( 'updateDashboard' ) ) {
|
||||
$filters = '';
|
||||
$folders = '';
|
||||
|
||||
if ( is_array( Input::post('link_filter') ) && ! empty( Input::post('link_filter') ) ) {
|
||||
$filters = implode( ',', Input::post('link_filter') );
|
||||
}
|
||||
|
||||
if ( ! empty( Input::post('link_order') ) ) {
|
||||
$folders = Input::post('link_order');
|
||||
}
|
||||
|
||||
$result = self::$dashboards->updateDash( $dash->ID, $filters, $folders );
|
||||
|
||||
if ( !$result ) {
|
||||
Issues::add( 'error', [ 'There was an error saving your dashboard.' => Check::userErrors() ] );
|
||||
} else {
|
||||
Issues::add( 'success', 'Your dashboard has been saved.' );
|
||||
}
|
||||
} else {
|
||||
Issues::add( 'error', [ 'There was an error saving your dashboard.' => Check::userErrors() ] );
|
||||
}
|
||||
unset( $_POST );
|
||||
}
|
||||
$dash = self::$dashboards->findByUuid( $uuid );
|
||||
|
||||
$foldersArray = [];
|
||||
if ( ! empty( $dash->link_order ) ) {
|
||||
@ -504,12 +555,19 @@ class Bookmarks extends Controller {
|
||||
|
||||
if ( ! empty( $dash->saved_prefs ) ) {
|
||||
$this->setDashToggles( explode( ',', $dash->saved_prefs ) );
|
||||
} else {
|
||||
$this->setDashToggles( [] );
|
||||
}
|
||||
|
||||
return Views::view( 'bookmarks.dashboards.view', $dash );
|
||||
}
|
||||
|
||||
public function dashboards() {
|
||||
self::$title = 'Bookmark Dashboards - {SITENAME}';
|
||||
if ( !App::$isMember ) {
|
||||
Issues::add( 'notice', 'You must have an active membership to manage dashboards.' );
|
||||
return $this->index();
|
||||
}
|
||||
$dashboards = self::$dashboards->byUser();
|
||||
return Views::view( 'bookmarks.dashboards.list', $dashboards );
|
||||
}
|
||||
@ -517,7 +575,7 @@ class Bookmarks extends Controller {
|
||||
/**
|
||||
* Functionality
|
||||
*/
|
||||
public function publish( $id = null ) {
|
||||
public function publish( $id = null ) {
|
||||
$bookmark = self::$bookmarks->findById( $id );
|
||||
if ( $bookmark == false ) {
|
||||
Session::flash( 'error', 'Bookmark not found.' );
|
||||
@ -627,6 +685,7 @@ class Bookmarks extends Controller {
|
||||
}
|
||||
|
||||
public function import() {
|
||||
self::$title = 'Bookmark Import - {SITENAME}';
|
||||
if ( !App::$isMember ) {
|
||||
Issues::add( 'notice', 'You must have an active membership to import bookmarks.' );
|
||||
return $this->index();
|
||||
@ -641,50 +700,44 @@ class Bookmarks extends Controller {
|
||||
return Views::view( 'bookmarks.import' );
|
||||
}
|
||||
|
||||
if (isset($_FILES['bookmark_file'])) {
|
||||
if ( isset( $_FILES['bookmark_file'] ) ) {
|
||||
$file = $_FILES['bookmark_file'];
|
||||
|
||||
// Check file size
|
||||
if ($file['size'] > 1024 * 1024) { // 1024 KB = 1 MB
|
||||
die('The file is too large. Maximum size is 1 MB.');
|
||||
Issues::add( 'error', 'There is a 1 meg limit on bookmark imports at this time.' );
|
||||
return Views::view( 'bookmarks.import' );
|
||||
}
|
||||
|
||||
// Check file extension
|
||||
$fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
||||
if ($fileExtension !== 'html') {
|
||||
die('Invalid file type. Only .html files are allowed.');
|
||||
Issues::add( 'error', 'Invalid file type. Only .html files are allowed.' );
|
||||
return Views::view( 'bookmarks.import' );
|
||||
}
|
||||
|
||||
// Proceed with file processing
|
||||
$fileContent = file_get_contents($file['tmp_name']);
|
||||
} else {
|
||||
die('No file was uploaded.');
|
||||
Issues::add( 'error', 'No Import detected' );
|
||||
return Views::view( 'bookmarks.import' );
|
||||
}
|
||||
|
||||
$out = $this->parseBookmarks($fileContent);
|
||||
$date = 'today';
|
||||
$date = date('F j, Y');
|
||||
$description = 'Imported on ' . $date . ' from file: ' . $file['name'];
|
||||
|
||||
$importFolder = self::$folders->create( 'New Import', 0, $description );
|
||||
// $importFolder = 1;
|
||||
// echo 'make import folder: ' . PHP_EOL;
|
||||
foreach ($out as $folder => $bookmarks) {
|
||||
// echo 'make folder: ' . $folder . PHP_EOL;
|
||||
$currentFolder = self::$folders->create( $folder, $importFolder, $description );
|
||||
foreach ($bookmarks as $index => $bookmark) {
|
||||
// echo 'make folder: ' . $bookmark['url'] . PHP_EOL;
|
||||
self::$bookmarks->create( $bookmark['name'], $bookmark['url'], $currentFolder);
|
||||
}
|
||||
}
|
||||
|
||||
Session::flash( 'success', 'Your Bookmark has been created.' );
|
||||
Redirect::to( 'bookmarks/bookmarks/'. $importFolder );
|
||||
// echo '</pre>';
|
||||
// exit;
|
||||
// dv ( $out );
|
||||
}
|
||||
|
||||
public function export() {
|
||||
self::$title = 'Bookmark Export - {SITENAME}';
|
||||
if ( !App::$isMember ) {
|
||||
Issues::add( 'notice', 'You must have an active membership to export bookmarks.' );
|
||||
return $this->index();
|
||||
@ -720,7 +773,11 @@ class Bookmarks extends Controller {
|
||||
Session::flash( 'error', 'Folder not found.' );
|
||||
return Redirect::to( 'bookmarks/index' );
|
||||
}
|
||||
$links = self::$bookmarks->byFolder( $folder->ID );
|
||||
|
||||
$hiddenExcluded = ! ( Input::post('hiddenIncluded') ?? false );
|
||||
$archivedExcluded = ! ( Input::post('archivedIncluded') ?? false );
|
||||
|
||||
$links = self::$bookmarks->byFolder( $folder->ID, $hiddenExcluded, $archivedExcluded );
|
||||
$htmlDoc .= $this->exportFolder( $folder->title, $folder->createdAt, $folder->createdAt, $links );
|
||||
}
|
||||
$htmlDoc .= '</DL><p>' . PHP_EOL;
|
||||
@ -774,6 +831,10 @@ class Bookmarks extends Controller {
|
||||
return Views::view( 'bookmarks.share', $panelArray );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Private
|
||||
*/
|
||||
private function exportFolder( $title, $editedAt, $createdAt, $links ) {
|
||||
$htmlDoc = '<DT><H3 ADD_DATE="'.$createdAt.'" LAST_MODIFIED="'.$editedAt.'">'.$title.'</H3>' . PHP_EOL;
|
||||
$htmlDoc .= '<DL><p>' . PHP_EOL;
|
||||
|
@ -29,6 +29,7 @@ class BookmarksForms extends Forms {
|
||||
self::addHandler( 'exportBookmarks', __CLASS__, 'exportBookmarks' );
|
||||
self::addHandler( 'createDashboard', __CLASS__, 'createDashboard' );
|
||||
self::addHandler( 'editDashboard', __CLASS__, 'editDashboard' );
|
||||
self::addHandler( 'updateDashboard', __CLASS__, 'updateDashboard' );
|
||||
}
|
||||
|
||||
public static function createBookmark() {
|
||||
@ -190,6 +191,17 @@ class BookmarksForms extends Forms {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function updateDashboard() {
|
||||
if ( ! Input::exists( 'submit' ) ) {
|
||||
return false;
|
||||
}
|
||||
if ( !self::token() ) {
|
||||
Check::addUserError( 'There was an issue with your request.' );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function editDashboard() {
|
||||
if ( ! Input::exists( 'submit' ) ) {
|
||||
return false;
|
||||
|
@ -1,30 +1,9 @@
|
||||
let masonryInstance;
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Handle all bootstrap popover inits
|
||||
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
|
||||
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))
|
||||
|
||||
const masonryContainer = document.querySelector('[data-masonry]');
|
||||
if ( ! masonryContainer ) {
|
||||
return;
|
||||
}
|
||||
const masonryInstance = new Masonry(masonryContainer, {
|
||||
columnHeight: '.accordion',
|
||||
percentPosition: true
|
||||
});
|
||||
const updateMasonryLayout = () => masonryInstance.layout();
|
||||
|
||||
masonryContainer.addEventListener('hidden.bs.collapse', updateMasonryLayout);
|
||||
masonryContainer.addEventListener('shown.bs.collapse', updateMasonryLayout);
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
updateMasonryLayout();
|
||||
});
|
||||
|
||||
// Observe all cards for changes in the DOM
|
||||
document.querySelectorAll('.card').forEach((card) => {
|
||||
observer.observe(card, { childList: true, subtree: true });
|
||||
});
|
||||
|
||||
// Initialize all toggle functions
|
||||
toggleVisibility('editModeSwitch', 'edit-mode');
|
||||
toggleVisibility('showArchivedSwitch', 'link-archived');
|
||||
@ -39,6 +18,119 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
toggleVisibility('dashShowHiddenSwitch', 'link-hidden');
|
||||
toggleVisibility('dashAddButtonSwitch', 'btn-addlink');
|
||||
toggleVisibility('dashShareButtonSwitch', 'btn-share');
|
||||
|
||||
// Retrieve the list of collapsed folderCard IDs from local storage
|
||||
const collapsedFolders = JSON.parse(localStorage.getItem( 'collapsedFolders' )) || [];
|
||||
|
||||
// Collapse the elements stored in local storage when the page loads
|
||||
collapsedFolders.forEach((folderId) => {
|
||||
const collapseElement = document.querySelector(`#Collapse${folderId}`);
|
||||
if (collapseElement) {
|
||||
collapseElement.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
// Add event listeners to track expand and collapse actions
|
||||
document.querySelectorAll('.accordion-item').forEach((item) => {
|
||||
const folderCardId = item.closest('.bookmark-card')?.id?.replace('folderCard', '');
|
||||
|
||||
if ( ! folderCardId ) return;
|
||||
|
||||
const collapseElement = item.querySelector('.collapse');
|
||||
|
||||
// Listen for collapse and expand events
|
||||
collapseElement.addEventListener('hidden.bs.collapse', () => {
|
||||
// Add the folderCard ID to local storage when collapsed
|
||||
if (!collapsedFolders.includes(folderCardId)) {
|
||||
collapsedFolders.push(folderCardId);
|
||||
localStorage.setItem( 'collapsedFolders' , JSON.stringify(collapsedFolders));
|
||||
}
|
||||
});
|
||||
|
||||
collapseElement.addEventListener('shown.bs.collapse', () => {
|
||||
// Remove the folderCard ID from local storage when expanded
|
||||
const index = collapsedFolders.indexOf(folderCardId);
|
||||
if (index > -1) {
|
||||
collapsedFolders.splice(index, 1);
|
||||
localStorage.setItem( 'collapsedFolders' , JSON.stringify(collapsedFolders));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const masonryContainer = document.getElementById("bookmarkSort");
|
||||
|
||||
if ( ! masonryContainer ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( localStorage.getItem('manageFolderOrder') ) {
|
||||
loadDashLinkOrder();
|
||||
}
|
||||
|
||||
masonryInstance = new Masonry(masonryContainer, {
|
||||
columnHeight: '.accordion',
|
||||
itemSelector: ".bookmark-card",
|
||||
percentPosition: false
|
||||
});
|
||||
|
||||
const test = () => masonryInstance.layout();
|
||||
|
||||
masonryContainer.addEventListener('hidden.bs.collapse', test );
|
||||
masonryContainer.addEventListener('shown.bs.collapse', test );
|
||||
|
||||
// Select all folder-raise and folder-lower buttons
|
||||
|
||||
const raiseButtons = document.querySelectorAll(".folder-raise");
|
||||
const lowerButtons = document.querySelectorAll(".folder-lower");
|
||||
|
||||
// Function to move the folderCard up
|
||||
function moveUp(event) {
|
||||
const folderCard = event.target.closest(".bookmark-card");
|
||||
const bookmarkSort = document.getElementById("bookmarkSort");
|
||||
const orderId = document.getElementById("dashLinkOrder");
|
||||
|
||||
if (folderCard) {
|
||||
const previousSibling = folderCard.previousElementSibling;
|
||||
if (previousSibling) {
|
||||
// Remove and reinsert the element before the previous sibling
|
||||
bookmarkSort.removeChild(folderCard);
|
||||
bookmarkSort.insertBefore(folderCard, previousSibling);
|
||||
masonryInstance.reloadItems();
|
||||
masonryInstance.layout();
|
||||
updateDashLinkOrder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to move the folderCard down
|
||||
function moveDown(event) {
|
||||
const folderCard = event.target.closest(".bookmark-card");
|
||||
const bookmarkSort = document.getElementById("bookmarkSort");
|
||||
|
||||
if (folderCard) {
|
||||
const nextSibling = folderCard.nextElementSibling;
|
||||
if (nextSibling) {
|
||||
// Remove and reinsert the element after the next sibling
|
||||
bookmarkSort.removeChild(folderCard);
|
||||
bookmarkSort.insertBefore(folderCard, nextSibling.nextSibling);
|
||||
masonryInstance.reloadItems();
|
||||
masonryInstance.layout();
|
||||
updateDashLinkOrder();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add event listeners to the raise buttons
|
||||
raiseButtons.forEach(button => {
|
||||
button.addEventListener("click", moveUp);
|
||||
});
|
||||
|
||||
// Add event listeners to the lower buttons
|
||||
lowerButtons.forEach(button => {
|
||||
button.addEventListener("click", moveDown);
|
||||
});
|
||||
// after hiding/showing elements
|
||||
masonryInstance.layout();
|
||||
});
|
||||
|
||||
// Function to handle showing or hiding elements based on the checkbox state
|
||||
@ -52,143 +144,72 @@ function toggleVisibility(switchId, className) {
|
||||
|
||||
// Listen for changes to the checkbox
|
||||
switchElement.addEventListener('change', () => {
|
||||
if (switchElement.checked) {
|
||||
elementsToToggle.forEach(element => {
|
||||
element.style.display = ''; // Show the element (default display)
|
||||
});
|
||||
} else {
|
||||
elementsToToggle.forEach(element => {
|
||||
element.style.display = 'none'; // Hide the element
|
||||
});
|
||||
}
|
||||
if (switchElement.checked) {
|
||||
elementsToToggle.forEach(element => {
|
||||
element.style.display = '';
|
||||
});
|
||||
} else {
|
||||
elementsToToggle.forEach(element => {
|
||||
element.style.display = 'none';
|
||||
});
|
||||
}
|
||||
if (typeof masonryInstance !== 'undefined') {
|
||||
masonryInstance.reloadItems();
|
||||
masonryInstance.layout()
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger the toggle initially to set the correct visibility on page load
|
||||
switchElement.dispatchEvent(new Event('change'));
|
||||
}
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const bookmarkSort = document.getElementById('bookmarkSort');
|
||||
if ( ! bookmarkSort ) {
|
||||
return;
|
||||
|
||||
function updateDashLinkOrder() {
|
||||
const bookmarkCards = document.querySelectorAll("#bookmarkSort .bookmark-card");
|
||||
const ids = Array.from( bookmarkCards ).map( card => {
|
||||
const match = card.id.match(/folderCard(\d+)/);
|
||||
return match ? match[1] : null;
|
||||
}).filter( id => id !== null );
|
||||
|
||||
const dashLinkOrder = document.getElementById("dashLinkOrder");
|
||||
if (dashLinkOrder) {
|
||||
dashLinkOrder.value = ids.join(",");
|
||||
} else {
|
||||
localStorage.setItem('manageFolderOrder', ids.join(","));
|
||||
}
|
||||
let draggingElement = null;
|
||||
let placeholder = null;
|
||||
let initialX = 0;
|
||||
let initialY = 0;
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
}
|
||||
|
||||
// Function to handle the drag start
|
||||
const handleDragStart = (e, element) => {
|
||||
draggingElement = element;
|
||||
function loadDashLinkOrder() {
|
||||
const storedOrder = localStorage.getItem("manageFolderOrder"); // Get the saved order
|
||||
const bookmarkSort = document.getElementById("bookmarkSort");
|
||||
|
||||
// Create a placeholder to maintain layout
|
||||
placeholder = document.createElement('div');
|
||||
placeholder.style.height = `${draggingElement.offsetHeight}px`;
|
||||
placeholder.style.marginBottom = getComputedStyle(draggingElement).marginBottom;
|
||||
placeholder.classList.add('placeholder');
|
||||
draggingElement.parentNode.insertBefore(placeholder, draggingElement);
|
||||
if (!storedOrder || !bookmarkSort) return; // Exit if no saved order or no container
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
initialX = e.clientX;
|
||||
initialY = e.clientY;
|
||||
offsetX = e.clientX - rect.left;
|
||||
offsetY = e.clientY - rect.top;
|
||||
const orderArray = storedOrder.split(","); // Convert the saved order into an array
|
||||
const bookmarkCards = Array.from(document.querySelectorAll("#bookmarkSort .bookmark-card"));
|
||||
|
||||
// Prevent the element from moving in the normal flow
|
||||
element.classList.add('dragging');
|
||||
element.style.position = 'absolute';
|
||||
draggingElement.style.width = `${rect.width}px`; // Preserve width
|
||||
draggingElement.style.height = `${rect.height}px`; // Preserve height
|
||||
element.style.zIndex = 1000; // Bring the element on top of others
|
||||
element.style.left = `${rect.left}px`;
|
||||
element.style.top = `${rect.top}px`;
|
||||
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// Function to handle dragging movement
|
||||
const handleDragMove = (e) => {
|
||||
if (draggingElement) {
|
||||
const dx = e.clientX - initialX + offsetX;
|
||||
const dy = e.clientY - initialY + offsetY;
|
||||
|
||||
draggingElement.style.left = `${dx}px`;
|
||||
draggingElement.style.top = `${dy}px`;
|
||||
}
|
||||
};
|
||||
|
||||
// Function to handle the drag end
|
||||
const handleDragEnd = () => {
|
||||
if (draggingElement) {
|
||||
const rect = draggingElement.getBoundingClientRect();
|
||||
|
||||
// Reset the position styles of the dragged element
|
||||
draggingElement.style.position = '';
|
||||
draggingElement.style.zIndex = ''; // Reset z-index
|
||||
draggingElement.style.left = '';
|
||||
draggingElement.style.top = '';
|
||||
draggingElement.classList.remove('dragging');
|
||||
|
||||
// Re-insert the element back into the DOM properly
|
||||
const closestElement = getClosestElement(rect.left, rect.top);
|
||||
console.error(closestElement.id);
|
||||
|
||||
if (closestElement) {
|
||||
bookmarkSort.insertBefore(draggingElement, closestElement);
|
||||
console.log( 'insertBefore' );
|
||||
} else {
|
||||
bookmarkSort.appendChild(draggingElement);
|
||||
console.log( 'append' );
|
||||
}
|
||||
|
||||
// Reorder the elements after the drag ends
|
||||
reorderElements();
|
||||
draggingElement = null;
|
||||
}
|
||||
};
|
||||
|
||||
// Function to reorder the elements inside the container
|
||||
const reorderElements = () => {
|
||||
const elements = Array.from(bookmarkSort.children);
|
||||
const sortedIds = elements.map(element => element.id);
|
||||
console.log('New order:', sortedIds);
|
||||
};
|
||||
|
||||
// Function to handle the drop event
|
||||
const handleDrop = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// Helper function to find the closest element based on mouse position
|
||||
const getClosestElement = (x, y) => {
|
||||
const elements = Array.from(bookmarkSort.children).filter(
|
||||
(el) => el !== placeholder && el !== draggingElement
|
||||
);
|
||||
|
||||
let closest = null;
|
||||
let closestDistance = Number.POSITIVE_INFINITY;
|
||||
|
||||
elements.forEach((child) => {
|
||||
const rect = child.getBoundingClientRect();
|
||||
const distance = Math.abs(rect.top - y);
|
||||
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
closest = child;
|
||||
}
|
||||
});
|
||||
|
||||
return closest;
|
||||
};
|
||||
|
||||
// Attach event listeners to all .card-header elements for dragging
|
||||
Array.from(document.querySelectorAll('.mover-arrow')).forEach(cardHeader => {
|
||||
cardHeader.addEventListener('mousedown', (e) => handleDragStart(e, cardHeader.closest('.bookmark-card')));
|
||||
// Create a map for quick lookup of cards by their ID
|
||||
const cardMap = new Map();
|
||||
bookmarkCards.forEach(card => {
|
||||
const match = card.id.match(/folderCard(\d+)/);
|
||||
if (match) cardMap.set(match[1], card);
|
||||
});
|
||||
|
||||
// Handle the dragging process
|
||||
document.addEventListener('mousemove', handleDragMove);
|
||||
document.addEventListener('mouseup', handleDragEnd);
|
||||
bookmarkSort.addEventListener('dragover', handleDrop); // Listen for the drop event to update the order
|
||||
});
|
||||
// Reorder elements based on the saved order
|
||||
const orderedElements = [];
|
||||
orderArray.forEach(id => {
|
||||
if (cardMap.has(id)) {
|
||||
orderedElements.push(cardMap.get(id)); // Add elements in the specified order
|
||||
cardMap.delete(id); // Remove from the map once processed
|
||||
}
|
||||
});
|
||||
|
||||
// Add any remaining (unspecified) elements to the end of the list
|
||||
const remainingElements = Array.from(cardMap.values());
|
||||
orderedElements.push(...remainingElements);
|
||||
|
||||
// Clear the container and append the elements in the new order
|
||||
bookmarkSort.innerHTML = ""; // Remove all children
|
||||
orderedElements.forEach(element => bookmarkSort.appendChild(element)); // Append in order
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class BookmarkDashboards extends DatabaseModel {
|
||||
|
||||
public function create( $title, $saved_prefs, $link_order, $description = '' ) {
|
||||
if ( ! Check::dataTitle( $title ) ) {
|
||||
Debug::info( 'Views: illegal title.' );
|
||||
Debug::info( 'Dash: illegal title.' );
|
||||
return false;
|
||||
}
|
||||
$fields = [
|
||||
@ -54,8 +54,8 @@ class BookmarkDashboards extends DatabaseModel {
|
||||
'createdAt' => time(),
|
||||
];
|
||||
if ( ! self::$db->insert( $this->tableName, $fields ) ) {
|
||||
new CustomException( 'viewCreate' );
|
||||
Debug::error( "Views: not created " . var_export($fields,true) );
|
||||
new CustomException( 'dashCreate' );
|
||||
Debug::error( "Dash: not created " . var_export($fields,true) );
|
||||
return false;
|
||||
}
|
||||
return self::$db->lastId();
|
||||
@ -63,11 +63,11 @@ class BookmarkDashboards extends DatabaseModel {
|
||||
|
||||
public function update( $id, $title, $saved_prefs, $link_order, $description = '' ) {
|
||||
if ( !Check::id( $id ) ) {
|
||||
Debug::info( 'Views: illegal ID.' );
|
||||
Debug::info( 'Dash: illegal ID.' );
|
||||
return false;
|
||||
}
|
||||
if ( !Check::dataTitle( $title ) ) {
|
||||
Debug::info( 'Views: illegal title.' );
|
||||
Debug::info( 'Dash: illegal title.' );
|
||||
return false;
|
||||
}
|
||||
$fields = [
|
||||
@ -77,8 +77,29 @@ class BookmarkDashboards extends DatabaseModel {
|
||||
'link_order' => $link_order,
|
||||
];
|
||||
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
||||
new CustomException( 'viewUpdate' );
|
||||
Debug::error( "Views: $id not updated" );
|
||||
new CustomException( 'dashUpdate' );
|
||||
Debug::error( "Dash: $id not updated" );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function updateDash( $id, $saved_prefs = '', $link_order = '' ) {
|
||||
if ( !Check::id( $id ) ) {
|
||||
Debug::info( 'Dash: illegal ID.' );
|
||||
return false;
|
||||
}
|
||||
$fields = [];
|
||||
$fields['saved_prefs'] = $saved_prefs;
|
||||
if ( ! empty( $link_order ) ) {
|
||||
$fields['link_order'] = $link_order;
|
||||
}
|
||||
if ( empty( $fields ) ) {
|
||||
return true;
|
||||
}
|
||||
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
||||
new CustomException( 'dashUpdate' );
|
||||
Debug::error( "Dash: $id not updated" );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -146,16 +146,17 @@ class Bookmarks extends DatabaseModel {
|
||||
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] );
|
||||
public function byFolder( $id, $hiddenExcluded = false, $archivedExcluded = false ) {
|
||||
$whereClause = ['createdBy', '=', App::$activeUser->ID ];
|
||||
$whereClause = array_merge( $whereClause, [ 'AND', 'folderID', '=', $id ] );
|
||||
if ( ! empty( $hiddenExcluded ) ) {
|
||||
$whereClause = array_merge( $whereClause, [ 'AND', 'hiddenAt', 'is', 'NULL'] );
|
||||
}
|
||||
if ( !$bookmarks->count() ) {
|
||||
if ( ! empty( $archivedExcluded ) ) {
|
||||
$whereClause = array_merge( $whereClause, [ 'AND', 'archivedAt', 'is', 'NULL'] );
|
||||
}
|
||||
$bookmarks = self::$db->get( $this->tableName, $whereClause );
|
||||
if ( ! $bookmarks->count() ) {
|
||||
Debug::info( 'No Bookmarks found.' );
|
||||
return false;
|
||||
}
|
||||
@ -343,17 +344,18 @@ class Bookmarks extends DatabaseModel {
|
||||
$instance = $data;
|
||||
$end = true;
|
||||
}
|
||||
$base_url = $this->getBaseUrl( $instance->url );
|
||||
|
||||
if ( empty( $instance->icon ) ) {
|
||||
$instance->iconHtml = '<i class="fa fa-fw fa-link"></i>';
|
||||
} else {
|
||||
if (strpos($instance->icon, 'http') !== false) {
|
||||
$instance->iconHtml = '<img src="' . $instance->icon .'">';
|
||||
} else {
|
||||
$instance->iconHtml = '<img src="' . $base_url . ltrim( $instance->icon, '/' ) .'">';
|
||||
$instance->iconHtml = '<i class="fa fa-fw fa-link"></i>';
|
||||
if ( ! empty( $instance->icon ) ) {
|
||||
if ( strpos($instance->icon, 'http') !== false) {
|
||||
$instance->iconHtml = '<img src="' . $instance->icon .'">';
|
||||
} else {
|
||||
if ( ! empty( $instance->url ) ) {
|
||||
$base_url = $this->getBaseUrl( $instance->url );
|
||||
$instance->iconHtml = '<img src="' . $base_url . ltrim( $instance->icon, '/' ) .'">';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( $instance->privacy == 'private' ) {
|
||||
$instance->privacyBadge = '<span class="mx-2 translate-center badge bg-success rounded-pill">Private</span>';
|
||||
@ -439,7 +441,7 @@ class Bookmarks extends DatabaseModel {
|
||||
return false;
|
||||
}
|
||||
$fields = [
|
||||
'hiddenAt' => 0,
|
||||
'hiddenAt' => NULL,
|
||||
];
|
||||
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
||||
new CustomException( 'bookmarkUpdate' );
|
||||
@ -503,7 +505,7 @@ class Bookmarks extends DatabaseModel {
|
||||
return false;
|
||||
}
|
||||
$fields = [
|
||||
'archivedAt' => 0,
|
||||
'archivedAt' => NULL,
|
||||
];
|
||||
if ( !self::$db->update( $this->tableName, $id, $fields ) ) {
|
||||
new CustomException( 'bookmarkUpdate' );
|
||||
@ -779,7 +781,7 @@ class Bookmarks extends DatabaseModel {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidImageUrl($url) {
|
||||
public function isValidImageUrl($url) {
|
||||
$headers = @get_headers($url);
|
||||
if ($headers && strpos($headers[0], '200') !== false) {
|
||||
|
||||
|
@ -62,37 +62,37 @@ class Bookmarks extends Plugin {
|
||||
'default' => 'false',
|
||||
],
|
||||
'showArchivedSwitch' => [
|
||||
'pretty' => 'Bookmarks default setting for showing archived bookmarks',
|
||||
'pretty' => 'Show archived bookmarks by default',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'false',
|
||||
],
|
||||
'showHiddenSwitch' => [
|
||||
'pretty' => 'Bookmarks default setting for showing hidden bookmarks',
|
||||
'pretty' => 'Show hidden bookmarks by default',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'false',
|
||||
],
|
||||
'archiveButtonSwitch' => [
|
||||
'pretty' => 'Bookmarks default setting for showing the archive buttons',
|
||||
'pretty' => 'Show the archive buttons by default',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'false',
|
||||
],
|
||||
'visibilityButtonSwitch' => [
|
||||
'pretty' => 'Bookmarks default setting for showing the visibility buttons',
|
||||
'pretty' => 'Show the visibility buttons by default',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'false',
|
||||
],
|
||||
'privacyButtonSwitch' => [
|
||||
'pretty' => 'Bookmarks default setting for showing the privacy buttons',
|
||||
'pretty' => 'Show the privacy buttons by default',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'true',
|
||||
],
|
||||
'shareButtonSwitch' => [
|
||||
'pretty' => 'Bookmarks default setting for showing the share buttons',
|
||||
'pretty' => 'Show the share buttons by default',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'true',
|
||||
],
|
||||
'addButtonSwitch' => [
|
||||
'pretty' => 'Bookmarks default setting for showing the add buttons',
|
||||
'pretty' => 'Show the add buttons by default',
|
||||
'type' => 'checkbox',
|
||||
'default' => 'true',
|
||||
],
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Create</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn atb-green-bg btn-lg">Create</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Save</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn atb-green-bg btn-lg">Save</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<td style="text-align: center;">
|
||||
{privacy}
|
||||
</td>
|
||||
<td><a href="{ROOT_URL}bookmarks/bookmark/{ID}" class="btn btn-sm btn-outline-primary"><i class="fa fa-fw fa-upload"></i></a></td>
|
||||
<td><a href="{ROOT_URL}bookmarks/bookmark/{ID}" class="btn btn-sm atb-green-outline"><i class="fa fa-fw fa-upload"></i></a></td>
|
||||
<td><a href="{ROOT_URL}bookmarks/editBookmark/{ID}" class="btn btn-sm btn-outline-warning"><i class="fa fa-fw fa-pencil"></i></a></td>
|
||||
<td><a href="{ROOT_URL}bookmarks/deleteBookmark/{ID}" class="btn btn-sm btn-outline-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
</tr>
|
||||
@ -34,5 +34,5 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center">
|
||||
<a href="{ROOT_URL}bookmarks/createBookmark" class="btn btn-md btn-primary">Create</a>
|
||||
<a href="{ROOT_URL}bookmarks/createBookmark" class="btn btn-md atb-green-bg">Create</a>
|
||||
</div>
|
@ -23,7 +23,7 @@
|
||||
<td style="text-align: center;">
|
||||
{privacy}
|
||||
</td>
|
||||
<td><a href="{ROOT_URL}bookmarks/bookmark/{ID}" class="btn btn-sm btn-outline-primary atb-green-outline"><i class="fa fa-fw fa-upload"></i></a></td>
|
||||
<td><a href="{ROOT_URL}bookmarks/bookmark/{ID}" class="btn btn-sm atb-green-outline"><i class="fa fa-fw fa-upload"></i></a></td>
|
||||
<td><a href="{ROOT_URL}bookmarks/editBookmark/{ID}" class="btn btn-sm btn-outline-warning"><i class="fa fa-fw fa-pencil"></i></a></td>
|
||||
<td><a href="{ROOT_URL}bookmarks/deleteBookmark/{ID}" class="btn btn-sm btn-outline-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
</tr>
|
||||
@ -38,7 +38,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center">
|
||||
<a href="{ROOT_URL}bookmarks/createBookmark" class="btn btn-lg btn-primary atb-green-bg">Add</a>
|
||||
<a href="{ROOT_URL}bookmarks/createBookmark" class="btn btn-lg atb-green-bg">Add</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -3,9 +3,12 @@
|
||||
<div class="card m-3 accordion">
|
||||
<div class="accordion-item">
|
||||
<div class="card-header accordion-header text-center bg-{color} context-main">
|
||||
<span class="h4 float-left mover-arrow"><i class="fa-solid fa-arrows-up-down-left-right"></i></span>
|
||||
<span class="float-start">
|
||||
<button class="btn btn-sm pe-3 folder-raise context-main" type="button"><i class="fa-solid fa-arrow-up"></i></button>
|
||||
<button class="btn btn-sm ps-3 folder-lower context-main" type="button"><i class="fa-solid fa-arrow-down"></i></button>
|
||||
</span>
|
||||
<span class="h4 text-center mx-5" data-bs-target="#Collapse{ID}" data-bs-toggle="collapse" aria-expanded="true" aria-controls="Collapse{ID}">{title}</span>
|
||||
<a class="btn btn-sm btn-primary btn-rounded float-end btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<a class="btn btn-sm atb-green-bg btn-rounded float-end btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<i class="fa fa-fw fa-share"></i>
|
||||
</a>
|
||||
</div>
|
||||
@ -35,8 +38,8 @@
|
||||
<div class="card-footer d-flex justify-content-center align-items-center context-main-bg">
|
||||
<a href="{ROOT_URL}bookmarks/createBookmark/{ID}" class="btn btn-sm btn-success btn-addlink"><i class="fa fa-fw fa-plus"></i></a></td>
|
||||
<span class="ms-auto">
|
||||
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm btn-outline-primary"><i class="fa fa-fw fa-list"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm btn-outline-primary"><i class="fa fa-fw fa-info-circle"></i></a></td>
|
||||
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm atb-green-outline"><i class="fa fa-fw fa-list"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm atb-green-outline"><i class="fa fa-fw fa-info-circle"></i></a></td>
|
||||
<a href="{ROOT_URL}bookmarks/editFolder/{ID}" class="btn btn-sm btn-warning edit-mode"><i class="fa fa-fw fa-pencil"></i></a></td>
|
||||
<a href="{ROOT_URL}bookmarks/deleteFolder/{ID}" class="btn btn-sm btn-danger edit-mode"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
</span>
|
||||
|
@ -1,14 +1,14 @@
|
||||
{LOOP}
|
||||
<li class="list-group-item context-main-bg bg-{color} {hidden_class} {archived_class} mb-1">
|
||||
<a href="{ROOT_URL}bookmarks/bookmark/{ID}" class="context-main">{iconHtml}</a>
|
||||
<a href="{url}"> {title}</a>
|
||||
<a href="{url}" class="text-decoration-none atb-green"> {title}</a>
|
||||
<span class="float-end">
|
||||
<span class="btn-hideshow">{hideBtn}</span>
|
||||
<span class="btn-archive">{archiveBtn}</span>
|
||||
<span class="btn-publish">{publish}</span>
|
||||
<a href="{ROOT_URL}bookmarks/editBookmark/{ID}" class="btn btn-sm btn-warning edit-mode"><i class="fa fa-fw fa-pencil"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/deleteBookmark/{ID}" class="btn btn-sm btn-danger edit-mode"><i class="fa fa-fw fa-trash"></i></a>
|
||||
<a class="btn btn-sm btn-primary btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<a class="btn btn-sm atb-green-bg btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<i class="fa fa-fw fa-share"></i>
|
||||
</a>
|
||||
<div class="modal fade" id="linkShare{ID}" tabindex="-1" style="display: none;" aria-hidden="true">
|
||||
|
@ -3,7 +3,7 @@
|
||||
<a href="{ROOT_URL}bookmarks/bookmark/{ID}" class="context-main">{iconHtml}</a>
|
||||
<a href="{url}"> {title}</a>
|
||||
<span class="float-end">
|
||||
<a class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<a class="btn btn-sm atb-green-bg" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<i class="fa fa-fw fa-share"></i>
|
||||
</a>
|
||||
<div class="modal fade" id="linkShare{ID}" tabindex="-1" style="display: none;" aria-hidden="true">
|
||||
|
@ -9,7 +9,7 @@
|
||||
<span class="text-center px-5">{privacyBadge}</span>
|
||||
<span class="h4 text-center px-5">{title}</span>
|
||||
</span>
|
||||
<a class="btn btn-sm btn-primary btn-rounded btn-share atb-green-bg" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<a class="btn btn-sm btn-rounded btn-share atb-green-bg" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<i class="fa fa-fw fa-share"></i>
|
||||
</a>
|
||||
</div>
|
||||
@ -39,8 +39,8 @@
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-center align-items-center context-main-bg">
|
||||
<span class="ms-auto">
|
||||
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm btn-outline-primary atb-green-outline"><i class="fa fa-fw fa-list"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm btn-outline-primary atb-green-outline"><i class="fa fa-fw fa-info-circle"></i></a></td>
|
||||
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm atb-green-outline"><i class="fa fa-fw fa-list"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm atb-green-outline"><i class="fa fa-fw fa-info-circle"></i></a></td>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<a href="{url}"> {title}</a>{privacyBadge}
|
||||
<span class="float-end">
|
||||
{publish}
|
||||
<a class="btn btn-sm btn-primary btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<a class="btn btn-sm atb-green-bg btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<i class="fa fa-fw fa-share"></i>
|
||||
</a>
|
||||
<div class="modal fade" id="linkShare{ID}" tabindex="-1" style="display: none;" aria-hidden="true">
|
||||
|
@ -3,7 +3,7 @@
|
||||
<a href="{ROOT_URL}bookmarks/bookmark/{ID}" class="context-main">{iconHtml}</a>
|
||||
<a href="{url}"> {title}</a>
|
||||
<span class="float-end">
|
||||
<a class="btn btn-sm btn-primary btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<a class="btn btn-sm atb-green-bg btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<i class="fa fa-fw fa-share"></i>
|
||||
</a>
|
||||
<div class="modal fade" id="linkShare{ID}" tabindex="-1" style="display: none;" aria-hidden="true">
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Create</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn atb-green-bg btn-lg">Create</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
@ -25,5 +25,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Save</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn atb-green-bg btn-lg">Save</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
@ -3,9 +3,12 @@
|
||||
<div class="card m-3 accordion">
|
||||
<div class="accordion-item">
|
||||
<div class="card-header accordion-header text-center bg-{color} context-main">
|
||||
<span class="h4 float-left mover-arrow"><i class="fa-solid fa-arrows-up-down-left-right"></i></span>
|
||||
<span class="float-start">
|
||||
<button class="btn btn-sm pe-3 folder-raise atb-green" type="button"><i class="fa-solid fa-arrow-up"></i></button>
|
||||
<button class="btn btn-sm ps-3 folder-lower atb-green" type="button"><i class="fa-solid fa-arrow-down"></i></button>
|
||||
</span>
|
||||
<span class="h4 text-center mx-5" data-bs-target="#Collapse{ID}" data-bs-toggle="collapse" aria-expanded="true" aria-controls="Collapse{ID}">{title}</span>
|
||||
<a class="btn btn-sm btn-primary btn-rounded float-end btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<a class="btn btn-sm atb-green-bg btn-rounded float-end btn-share" data-bs-toggle="modal" data-bs-target="#linkShare{ID}">
|
||||
<i class="fa fa-fw fa-share"></i>
|
||||
</a>
|
||||
</div>
|
||||
@ -35,8 +38,8 @@
|
||||
<div class="card-footer d-flex justify-content-center align-items-center context-main-bg">
|
||||
<a href="{ROOT_URL}bookmarks/createBookmark/{ID}" class="btn btn-sm btn-success btn-addlink"><i class="fa fa-fw fa-plus"></i></a></td>
|
||||
<span class="ms-auto">
|
||||
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm btn-outline-primary"><i class="fa fa-fw fa-list"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm btn-outline-primary"><i class="fa fa-fw fa-info-circle"></i></a></td>
|
||||
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-sm atb-green-outline"><i class="fa fa-fw fa-list"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm atb-green-outline"><i class="fa fa-fw fa-info-circle"></i></a></td>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<p>From here you can easily create, update, or remove any bookmark dashboards you have created.</p>
|
||||
<p>Dashboards are a feature that allow you to build customized bookmark pages that you can easily save and open la</p>
|
||||
</div>
|
||||
<div class="row g-3 text-center p-2" data-masonry='{ "percentPosition": false }' id="bookmarkSort">
|
||||
<div class="row g-3 text-center p-2">
|
||||
<table class="table context-main">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -22,7 +22,7 @@
|
||||
<tr>
|
||||
<td class="">{title}</td>
|
||||
<td>{description}</td>
|
||||
<td><a href="{ROOT_URL}bookmarks/dashboard/{uuid}" class="btn btn-sm btn-primary atb-green-bg"><i class="fa fa-fw fa-upload"></i></a></td>
|
||||
<td><a href="{ROOT_URL}bookmarks/dashboard/{uuid}" class="btn btn-sm atb-green-bg"><i class="fa fa-fw fa-upload"></i></a></td>
|
||||
<td><a href="{ROOT_URL}bookmarks/editDash/{ID}" class="btn btn-sm btn-warning"><i class="fa fa-fw fa-pencil"></i></a></td>
|
||||
<td><a href="{ROOT_URL}bookmarks/deleteDash/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
</tr>
|
||||
@ -37,7 +37,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="">
|
||||
<a href="{ROOT_URL}bookmarks/addDash" class="btn btn-lg btn-primary atb-green-bg">Create</a>
|
||||
<a href="{ROOT_URL}bookmarks/addDash" class="btn btn-lg atb-green-bg">Create</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,7 +4,9 @@
|
||||
<hr>
|
||||
<form method="post">
|
||||
<fieldset>
|
||||
<div class="row g-3" data-masonry='{ "percentPosition": false }' id="bookmarkSort">
|
||||
<input type="hidden" name="token" value="{TOKEN}">
|
||||
<input type="hidden" name="link_order" value="{link_order}" id="dashLinkOrder">
|
||||
<div class="row g-3" id="bookmarkSort">
|
||||
{folderPanels}
|
||||
</div>
|
||||
<hr>
|
||||
@ -15,7 +17,7 @@
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="h-100 d-flex align-items-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg mx-5">Save</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-lg mx-5 atb-green-bg">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,9 +2,9 @@
|
||||
<div class="offset-2 col-8 p-3 context-main-bg">
|
||||
<legend class="text-center">Bookmark Export</legend>
|
||||
<hr>
|
||||
<h3 class="text-center text-muted">Select which folders to include in the export</h3>
|
||||
<h3 class="text-center">Select which folders to include in the export</h3>
|
||||
<form action="" method="post">
|
||||
<div class="row g-3 col-4 offset-4" data-masonry='{ "percentPosition": false }' id="bookmarkSort">
|
||||
<div class="row g-3 col-4 offset-4">
|
||||
<table class="table context-main">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -39,9 +39,28 @@
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="title" class="col-lg-3 col-form-label text-end align-items-center">Bookmark Filters:</label>
|
||||
<div class="col-lg-6">
|
||||
<div class="p-2">
|
||||
<!-- Status Filters -->
|
||||
<div class="d-flex align-items-center border p-2 rounded mb-3 context-main-bg">
|
||||
<div class="me-3 fw-bold">Status:</div>
|
||||
<div class="form-check form-switch me-3">
|
||||
<input class="form-check-input" type="checkbox" value="true" id="archivedIncluded" name="archivedIncluded">
|
||||
<label class="form-check-label" for="archivedIncluded">Include Archived</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" value="true" id="hiddenIncluded" name="hiddenIncluded">
|
||||
<label class="form-check-label" for="hiddenIncluded">Include Hidden</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p>Literally every browser is chromium based now, so they all have a standard import/export.</p>
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg atb-green-bg">Create Export</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn atb-green-bg btn-lg">Create Export</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
<!-- Call to Action -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="/brave" class="btn btn-primary atb-green-bg btn-lg">
|
||||
<a href="/brave" class="btn atb-green-bg btn-lg">
|
||||
<i class="fas fa-download me-2"></i>Get the Addon for Brave
|
||||
</a>
|
||||
</div>
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
<!-- Call to Action -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="/chrome" class="btn btn-primary atb-green-bg btn-lg">
|
||||
<a href="/chrome" class="btn atb-green-bg btn-lg">
|
||||
<i class="fas fa-download me-2"></i>Get the Addon for Chrome
|
||||
</a>
|
||||
</div>
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
<!-- Call to Action -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="#" class="btn btn-primary atb-green-bg btn-lg">
|
||||
<a href="#" class="btn atb-green-bg btn-lg">
|
||||
<i class="fas fa-download me-2"></i>Get the Addon for Edge
|
||||
</a>
|
||||
</div>
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
<!-- Call to Action -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="/firefox" class="btn btn-primary atb-green-bg btn-lg">
|
||||
<a href="/firefox" class="btn atb-green-bg btn-lg">
|
||||
<i class="fas fa-download me-2"></i>Get the Addon for Firefox
|
||||
</a>
|
||||
</div>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<i class="fab fa-chrome fa-3x text-primary mb-3"></i>
|
||||
<h5 class="card-title">Chrome Extension</h5>
|
||||
<p class="card-text">Enhance your Chrome browser with advanced features and effortless organization.</p>
|
||||
<a href="/extensions/chrome" class="btn btn-primary atb-green-bg btn-sm">Learn More</a>
|
||||
<a href="/extensions/chrome" class="btn atb-green-bg btn-sm">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26,7 +26,7 @@
|
||||
<i class="fab fa-firefox fa-3x text-warning mb-3"></i>
|
||||
<h5 class="card-title">Firefox Extension</h5>
|
||||
<p class="card-text">Seamlessly integrate with Firefox for a smoother browsing experience.</p>
|
||||
<a href="/extensions/firefox" class="btn btn-primary atb-green-bg btn-sm">Learn More</a>
|
||||
<a href="/extensions/firefox" class="btn atb-green-bg btn-sm">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -38,7 +38,7 @@
|
||||
<i class="fab fa-opera fa-3x text-danger mb-3"></i>
|
||||
<h5 class="card-title">Opera Extension</h5>
|
||||
<p class="card-text">Boost your Opera browser with intuitive features and tools.</p>
|
||||
<a href="/extensions/opera" class="btn btn-primary atb-green-bg btn-sm">Learn More</a>
|
||||
<a href="/extensions/opera" class="btn atb-green-bg btn-sm">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -50,7 +50,7 @@
|
||||
<i class="fab fa-brave fa-3x text-orange mb-3"></i>
|
||||
<h5 class="card-title">Brave Extension</h5>
|
||||
<p class="card-text">Enjoy secure and private browsing with Brave, enhanced by our extension.</p>
|
||||
<a href="/extensions/brave" class="btn btn-primary atb-green-bg btn-sm">Learn More</a>
|
||||
<a href="/extensions/brave" class="btn atb-green-bg btn-sm">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -62,7 +62,7 @@
|
||||
<i class="fab fa-edge fa-3x text-info mb-3"></i>
|
||||
<h5 class="card-title">Edge Extension</h5>
|
||||
<p class="card-text">Maximize productivity on Microsoft Edge with our extension.</p>
|
||||
<a href="/extensions/edge" class="btn btn-primary atb-green-bg btn-sm">Learn More</a>
|
||||
<a href="/extensions/edge" class="btn atb-green-bg btn-sm">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -55,7 +55,7 @@
|
||||
|
||||
<!-- Call to Action -->
|
||||
<div class="text-center mt-5">
|
||||
<a href="#" class="btn btn-primary atb-green-bg btn-lg">
|
||||
<a href="#" class="btn atb-green-bg btn-lg">
|
||||
<i class="fas fa-download me-2"></i>Get the Addon for Opera
|
||||
</a>
|
||||
</div>
|
||||
|
@ -40,7 +40,7 @@
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Create</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn atb-green-bg btn-lg">Create</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
@ -38,7 +38,7 @@
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg">Save</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn atb-green-bg btn-lg">Save</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<td>{prettyPrivacy}</td>
|
||||
<td>{description}</td>
|
||||
<td>
|
||||
<a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm btn-outline-primary atb-green-outline mx-1"><i class="fa fa-fw fa-info-circle"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/folders/{ID}" class="btn btn-sm atb-green-outline mx-1"><i class="fa fa-fw fa-info-circle"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/editFolder/{ID}" class="btn btn-sm btn-outline-warning mx-1"><i class="fa fa-fw fa-pencil"></i></a>
|
||||
<a href="{ROOT_URL}bookmarks/deleteFolder/{ID}" class="btn btn-sm btn-outline-danger mx-1"><i class="fa fa-fw fa-trash"></i></a>
|
||||
</td>
|
||||
@ -30,5 +30,5 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center">
|
||||
<a href="{ROOT_URL}bookmarks/createFolder" class="btn btn-md btn-primary">Create</a>
|
||||
<a href="{ROOT_URL}bookmarks/createFolder" class="btn btn-md atb-green-bg">Create</a>
|
||||
</div>
|
@ -45,7 +45,7 @@
|
||||
|
||||
<!-- Admin Controls -->
|
||||
<div class="card-footer text-center">
|
||||
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn btn-primary atb-green-bg btn-sm me-2" data-bs-toggle="tooltip" title="Broadcast Message">
|
||||
<a href="{ROOT_URL}bookmarks/bookmarks/{ID}" class="btn atb-green-bg btn-sm me-2" data-bs-toggle="tooltip" title="Broadcast Message">
|
||||
<i class="fa fa-list"></i> Bookmarks
|
||||
</a>
|
||||
<a href="{ROOT_URL}bookmarks/editFolder/{ID}" class="btn btn-warning btn-sm me-2" data-bs-toggle="tooltip" title="Edit User">
|
||||
|
@ -8,7 +8,7 @@
|
||||
<input type="file" name="bookmark_file" id="bookmark_file" accept=".html">
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg center-block atb-green-bg">Import</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-lg center-block atb-green-bg">Import</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -58,7 +58,7 @@
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="card-footer d-flex justify-content-center align-items-center context-main-bg">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-md btn-outline-primary my-2">Save as Default</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-md atb-green-outline my-2">Save as Default</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
@ -19,6 +19,8 @@ use TheTempusProject\Classes\AdminController;
|
||||
use TheTempusProject\Plugins\Members as MemberModel;
|
||||
use TheTempusProject\Houdini\Classes\Issues;
|
||||
use TheTempusProject\Bedrock\Functions\Input;
|
||||
use TheTempusProject\Hermes\Functions\Route as Routes;
|
||||
use TheTempusProject\Hermes\Functions\Redirect;
|
||||
|
||||
class Members extends AdminController {
|
||||
public function __construct() {
|
||||
@ -28,13 +30,46 @@ class Members extends AdminController {
|
||||
}
|
||||
|
||||
public function index( $data = null ) {
|
||||
self::$title = 'Admin - Membership Scripts';
|
||||
Views::view( 'members.admin.scripts' );
|
||||
}
|
||||
|
||||
public function orphans( $data = null, $id = null ) {
|
||||
self::$title = 'Admin - Orphaned PRoducts';
|
||||
if ( $data = 'abandon' && ! empty( $id ) ) {
|
||||
MemberModel::orphanAbandon( $id );
|
||||
Redirect::to('admin/members/orphans');
|
||||
}
|
||||
$orphans = MemberModel::findOrphans();
|
||||
Views::view( 'members.admin.orphans', $orphans );
|
||||
}
|
||||
|
||||
public function webhooks( $data = null, $id = null ) {
|
||||
self::$title = 'Admin - Membership Webhooks';
|
||||
|
||||
if ( $data = 'delete' && ! empty( $id ) ) {
|
||||
MemberModel::webhookRemove( $id );
|
||||
Redirect::to('admin/members/webhooks');
|
||||
}
|
||||
|
||||
$data = [];
|
||||
$webhooks = MemberModel::webhookList();
|
||||
foreach ($webhooks->data as $key => $webhook) {
|
||||
$hook = new \stdClass;
|
||||
$hook->id = $webhook->id;
|
||||
$hook->enabled_events = implode( ', <br>', $webhook->enabled_events );
|
||||
$hook->status = $webhook->status;
|
||||
$hook->url = $webhook->url;
|
||||
$data[] = $hook;
|
||||
}
|
||||
Components::set( 'urltouse', Routes::getAddress() );
|
||||
|
||||
if ( !Input::exists( 'submit' ) ) {
|
||||
return Views::view( 'members.admin.webhooks' );
|
||||
return Views::view( 'members.admin.webhooks', $data );
|
||||
}
|
||||
MemberModel::webhookSetup();
|
||||
Issues::add( 'success', 'Webhooks Generated' );
|
||||
Issues::add( 'error', 'Now, LEAVE!' );
|
||||
Redirect::to('admin/members/webhooks');
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class Member extends Controller {
|
||||
}
|
||||
try {
|
||||
$session = self::$stripe->billingPortal->sessions->create([
|
||||
'customer' => $customer,
|
||||
'customer' => $customer->stripe_customer,
|
||||
'return_url' => Routes::getAddress() . 'member/manage',
|
||||
]);
|
||||
} catch (\Stripe\Exception\InvalidRequestException $e) {
|
||||
@ -174,7 +174,7 @@ class Member extends Controller {
|
||||
Session::flash( 'success', 'We aren\'t currently accepting new members, please check back soon!' );
|
||||
return Redirect::home();
|
||||
}
|
||||
Views::view( 'members.landing1', $product );
|
||||
Views::view( 'members.landing', $product );
|
||||
}
|
||||
|
||||
public function checkout( $plan = 'monthly' ) {
|
||||
@ -187,6 +187,19 @@ class Member extends Controller {
|
||||
Issues::add( 'error', 'no customer' );
|
||||
return $this->index();
|
||||
}
|
||||
|
||||
$plan = strtolower( $plan );
|
||||
if ( ! in_array( $plan, ['monthly','yearly','upgrade'] ) ) {
|
||||
Session::flash( 'error', 'Unknown plan' );
|
||||
return Redirect::to( 'home/index' );
|
||||
}
|
||||
if ( $plan === 'upgrade' ) {
|
||||
$plan = 'yearly';
|
||||
$successUrl = Routes::getAddress() . 'member/payment/upgrade?session_id={CHECKOUT_SESSION_ID}';
|
||||
} else {
|
||||
$successUrl = Routes::getAddress() . 'member/payment/complete?session_id={CHECKOUT_SESSION_ID}';
|
||||
}
|
||||
|
||||
$stripePrice = $this->findPrice( $plan );
|
||||
|
||||
$session = self::$stripe->checkout->sessions->create([
|
||||
@ -197,7 +210,7 @@ class Member extends Controller {
|
||||
'quantity' => 1,
|
||||
]],
|
||||
'mode' => 'subscription',
|
||||
'success_url' => Routes::getAddress() . 'member/payment/complete?session_id={CHECKOUT_SESSION_ID}',
|
||||
'success_url' => $successUrl,
|
||||
'cancel_url' => Routes::getAddress() . 'member/payment/cancel',
|
||||
]);
|
||||
header('Location: ' . $session->url);
|
||||
@ -206,7 +219,7 @@ class Member extends Controller {
|
||||
|
||||
public function payment( $type = '' ) {
|
||||
$type = strtolower( $type );
|
||||
if ( ! in_array( $type, ['cancel','complete'] ) ) {
|
||||
if ( ! in_array( $type, ['cancel','complete','upgrade'] ) ) {
|
||||
Session::flash( 'error', 'Unknown Payment' );
|
||||
return Redirect::to( 'home/index' );
|
||||
}
|
||||
@ -216,6 +229,11 @@ class Member extends Controller {
|
||||
return Views::view( 'members.paymentcanceled' );
|
||||
}
|
||||
|
||||
if ( $type == 'upgrade' ) {
|
||||
self::$title = 'Better Members Area';
|
||||
return Views::view( 'members.upgradeCompleted' );
|
||||
}
|
||||
|
||||
self::$title = '(almost) Members Area';
|
||||
Views::view( 'members.paymentcomplete' );
|
||||
}
|
||||
|
@ -27,8 +27,8 @@ class MembershipProducts extends DatabaseModel {
|
||||
public $databaseMatrix = [
|
||||
[ 'name', 'varchar', '128' ],
|
||||
[ 'description', 'text', '' ],
|
||||
[ 'monthly_price', 'int', '10' ], // must be int value greater than 99 IE $1.00 === 100, $4399.22 === 439922
|
||||
[ 'yearly_price', 'int', '10' ], // must be int value greater than 99 IE $1.00 === 100, $4399.22 === 439922
|
||||
[ 'monthly_price', 'int', '10' ], // must be int value greater than 99 IE $1.00 === 100, $4,399.22 === 439922
|
||||
[ 'yearly_price', 'int', '10' ], // must be int value greater than 99 IE $1.00 === 100, $4,399.22 === 439922
|
||||
[ 'stripe_product', 'varchar', '64' ], // not-required to create - generated by stripe after
|
||||
[ 'stripe_price_monthly', 'varchar', '64' ], // not-required to create - generated by stripe after
|
||||
[ 'stripe_price_yearly', 'varchar', '64' ], // not-required to create - generated by stripe after
|
||||
@ -166,7 +166,7 @@ class MembershipProducts extends DatabaseModel {
|
||||
|
||||
public function updateStripeYearlyPrice( $product, $yearly_price ) {
|
||||
$stripe->prices->update(
|
||||
$yearly->id,
|
||||
$yearly_price,
|
||||
['lookup_key' => ""]
|
||||
);
|
||||
return $this->createStripeYearlyPrice( $product, $yearly_price );
|
||||
@ -174,7 +174,7 @@ class MembershipProducts extends DatabaseModel {
|
||||
|
||||
public function updateStripeMonthlyPrice( $product, $monthly_price ) {
|
||||
$stripe->prices->update(
|
||||
$monthly->id,
|
||||
$monthly_price,
|
||||
['lookup_key' => ""]
|
||||
);
|
||||
return $this->createStripeMonthlyPrice( $product, $monthly_price );
|
||||
@ -208,7 +208,7 @@ class MembershipProducts extends DatabaseModel {
|
||||
if ( ! $data->count() ) {
|
||||
return false;
|
||||
}
|
||||
return $data->first();
|
||||
return $this->filter( $data->first() );
|
||||
}
|
||||
|
||||
public function mainProduct() {
|
||||
|
@ -19,6 +19,7 @@ use TheTempusProject\Bedrock\Classes\Config;
|
||||
use TheTempusProject\Hermes\Functions\Route as Routes;
|
||||
use TheTempusProject\Models\Memberships;
|
||||
use TheTempusProject\Models\MembershipProducts as Products;
|
||||
use TheTempusProject\Canary\Bin\Canary as Debug;
|
||||
|
||||
class Members extends Plugin {
|
||||
public static $stripe;
|
||||
@ -46,17 +47,17 @@ class Members extends Plugin {
|
||||
'text' => '<i class="fa fa-fw fa-arrow-down"></i> Memberships',
|
||||
'url' => [
|
||||
[
|
||||
'text' => '<i class="fa fa-fw fa-database"></i> Products',
|
||||
'text' => '<i class="fa fa-fw fa-barcode"></i> Products',
|
||||
'url' => '{ROOT_URL}admin/products',
|
||||
],
|
||||
[
|
||||
'text' => '<i class="fa fa-fw fa-database"></i> Subscriptions',
|
||||
'text' => '<i class="fa fa-fw fa-bag-shopping"></i> Subscriptions',
|
||||
'url' => '{ROOT_URL}admin/records',
|
||||
],
|
||||
// [
|
||||
// 'text' => '<i class="fa fa-fw fa-database"></i> Invoices',
|
||||
// 'url' => '{ROOT_URL}admin/invoices',
|
||||
// ],
|
||||
[
|
||||
'text' => '<i class="fa fa-fw fa-code"></i> Scripts',
|
||||
'url' => '{ROOT_URL}admin/members',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
@ -116,41 +117,52 @@ class Members extends Plugin {
|
||||
];
|
||||
|
||||
public function __construct( $load = false ) {
|
||||
if ( ! self::$loaded ) {
|
||||
App::$userCPlinks[] = (object) self::$userLinks;
|
||||
self::$loaded = true;
|
||||
if ( ! self::$loaded && $load ) {
|
||||
if ( App::$isLoggedIn ) {
|
||||
App::$isMember = $this->groupHasMemberAccess( App::$activeGroup );
|
||||
if ( empty( App::$isMember ) ) {
|
||||
App::$isMember = $this->userHasActiveMembership( App::$activeUser->ID );
|
||||
}
|
||||
}
|
||||
$this->filters[] = [
|
||||
'name' => 'member',
|
||||
'find' => '#{MEMBER}(.*?){/MEMBER}#is',
|
||||
'replace' => ( App::$isMember ? '$1' : '' ),
|
||||
'enabled' => true,
|
||||
];
|
||||
$this->filters[] = [
|
||||
'name' => 'nonmember',
|
||||
'find' => '#{NONMEMBER}(.*?){/NONMEMBER}#is',
|
||||
'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,
|
||||
];
|
||||
}
|
||||
if ( App::$isLoggedIn ) {
|
||||
App::$isMember = $this->groupHasMemberAccess( App::$activeGroup );
|
||||
if ( empty( App::$isMember ) ) {
|
||||
App::$isMember = $this->userHasActiveMembership( App::$activeUser->ID );
|
||||
|
||||
parent::__construct( $load );
|
||||
|
||||
if ( $this->checkEnabled() && App::$isLoggedIn ) {
|
||||
if ( ! self::$loaded && $load ) {
|
||||
App::$userCPlinks[] = (object) self::$userLinks;
|
||||
App::$topNavRightDropdown .= '<li><a href="{ROOT_URL}member/manage" class="dropdown-item"><i class="fa fa-fw fa-credit-card"></i> Subscriptions</a></li>';
|
||||
}
|
||||
}
|
||||
$this->filters[] = [
|
||||
'name' => 'member',
|
||||
'find' => '#{MEMBER}(.*?){/MEMBER}#is',
|
||||
'replace' => ( App::$isMember ? '$1' : '' ),
|
||||
'enabled' => true,
|
||||
];
|
||||
$this->filters[] = [
|
||||
'name' => 'nonmember',
|
||||
'find' => '#{NONMEMBER}(.*?){/NONMEMBER}#is',
|
||||
'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,
|
||||
];
|
||||
|
||||
if ( ! self::$loaded && $load ) {
|
||||
self::$loaded = true;
|
||||
}
|
||||
|
||||
$api_key = Config::getValue( 'memberships/stripeSecret' );
|
||||
if ( $api_key == 'sk_xxxxxxxxxxxxxxx' || empty($api_key) ) {
|
||||
self::$stripe = false;
|
||||
} else {
|
||||
self::$stripe = new StripeClient( $api_key );
|
||||
}
|
||||
parent::__construct( $load );
|
||||
}
|
||||
|
||||
public function groupHasMemberAccess( $activeGroup ) {
|
||||
@ -169,6 +181,10 @@ class Members extends Plugin {
|
||||
|
||||
$products = new Products;
|
||||
$product = $products->findByPriceID( $membership->subscription_price_id );
|
||||
if ( empty( $product ) ) {
|
||||
Debug::error('Active membership on non-existent product');
|
||||
return false;
|
||||
}
|
||||
if ( $product->stripe_price_monthly == $membership->subscription_price_id ) {
|
||||
return 'monthly';
|
||||
}
|
||||
@ -177,7 +193,6 @@ class Members extends Plugin {
|
||||
|
||||
public static function webhookSetup() {
|
||||
$root = Routes::getAddress();
|
||||
// $root = "https://stripe.joeykimsey.com/";
|
||||
$response = self::$stripe->webhookEndpoints->create([
|
||||
'enabled_events' => self::$webhookEvents,
|
||||
'url' => $root . 'api/stripe/webhook',
|
||||
@ -185,13 +200,66 @@ class Members extends Plugin {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// public static function webhookSetup() {
|
||||
// $root = Routes::getAddress();
|
||||
// $root = "https://stripe.joeykimsey.com/";
|
||||
// $response = self::$stripe->webhookEndpoints->create([
|
||||
// 'enabled_events' => self::$webhookEvents,
|
||||
// 'url' => $root . 'api/stripe/webhook',
|
||||
// ]);
|
||||
// return $response;
|
||||
// }
|
||||
public static function webhookList() {
|
||||
$response = self::$stripe->webhookEndpoints->all(['limit' => 25]);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function webhookRemove( $id ) {
|
||||
$response = self::$stripe->webhookEndpoints->delete( $id, []);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function orphanAbandon( $id ) {
|
||||
$response = self::$stripe->prices->update(
|
||||
$id,
|
||||
['lookup_key' => ""]
|
||||
);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function findOrphans() {
|
||||
$orphans = [];
|
||||
// any prices with keys not represented in our local db will show as an orphan
|
||||
|
||||
$result = self::$stripe->prices->search([
|
||||
'query' => 'lookup_key:"membership-monthly"',
|
||||
]);
|
||||
|
||||
$products = new Products;
|
||||
if ( ! empty( $result->data ) ) {
|
||||
$product = $products->findByPriceID( $result->data[0]->id );
|
||||
|
||||
if ( empty( $product ) ) {
|
||||
$data = $result->data[0];
|
||||
|
||||
$child = new \stdClass;
|
||||
$child->price_id = $data->id;
|
||||
$child->product = $data->product;
|
||||
$child->lookup_key = $data->lookup_key;
|
||||
$child->amount = $data->unit_amount;
|
||||
$orphans[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
$result = self::$stripe->prices->search([
|
||||
'query' => 'lookup_key:"membership-yearly"',
|
||||
]);
|
||||
if ( ! empty( $result->data ) ) {
|
||||
$product = $products->findByPriceID( $result->data[0]->id );
|
||||
|
||||
if ( empty( $product ) ) {
|
||||
$data = $result->data[0];
|
||||
|
||||
$child = new \stdClass;
|
||||
$child->price_id = $data->id;
|
||||
$child->product = $data->product;
|
||||
$child->lookup_key = $data->lookup_key;
|
||||
$child->amount = $data->unit_amount;
|
||||
$orphans[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
return $orphans;
|
||||
}
|
||||
}
|
||||
|
35
app/plugins/members/views/admin/orphans.html
Normal file
35
app/plugins/members/views/admin/orphans.html
Normal file
@ -0,0 +1,35 @@
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Stripe Webhook Generation</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<p>Orphans are stripe prices that have unique lookup_keys used by the membership system, but have no products currently saved.</p>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10%">price id</th>
|
||||
<th style="width: 10%">amount</th>
|
||||
<th style="width: 50%">lookup key</th>
|
||||
<th style="width: 10%">url</th>
|
||||
<th style="width: 10%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{LOOP}
|
||||
<tr>
|
||||
<td>{price_id}</td>
|
||||
<td>{amount}</td>
|
||||
<td>{lookup_key}</td>
|
||||
<td><a href="{ROOT_URL}admin/members/orphans/adopt/{price_id}" class="btn btn-sm btn-success"><i class="fa fa-fw fa-download"></i></a></td>
|
||||
<td><a href="{ROOT_URL}admin/members/orphans/abandon/{price_id}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
</tr>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
<tr>
|
||||
<td class="text-center" colspan="5">
|
||||
No results to show.
|
||||
</td>
|
||||
</tr>
|
||||
{/ALT}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
@ -45,7 +45,7 @@
|
||||
|
||||
<!-- Submit Button -->
|
||||
<div class="text-center">
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg center-block">Send</button>
|
||||
<button type="submit" name="submit" value="submit" class="btn btn-primary btn-lg center-block">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
13
app/plugins/members/views/admin/scripts.html
Normal file
13
app/plugins/members/views/admin/scripts.html
Normal file
@ -0,0 +1,13 @@
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Membership Scripts</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<ul class="list-unstyled">
|
||||
<li>
|
||||
<a href="{ROOT_URL}admin/members/webhooks">Webhook Generation</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{ROOT_URL}admin/members/orphans">Orphan Products</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
@ -1,10 +1,43 @@
|
||||
<legend>WARNING: Regenerating existing webhooks makes joey sad, don't do iit!</legend>
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<label for="submit" class="col-lg-3 control-label"></label>
|
||||
<div class="col-lg-3">
|
||||
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block ">Submit</button>
|
||||
</div>
|
||||
<div class="context-main-bg context-main p-3">
|
||||
<legend class="text-center">Stripe Webhook Generation</legend>
|
||||
<hr>
|
||||
{ADMIN_BREADCRUMBS}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 10%">id</th>
|
||||
<th style="width: 10%">status</th>
|
||||
<th style="width: 50%">enabled_events</th>
|
||||
<th style="width: 10%">url</th>
|
||||
<th style="width: 10%"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{LOOP}
|
||||
<tr>
|
||||
<td>{id}</td>
|
||||
<td>{status}</td>
|
||||
<td>{enabled_events}</td>
|
||||
<td>{url}</td>
|
||||
<td><a href="{ROOT_URL}admin/members/webhooks/delete/{ID}" class="btn btn-sm btn-danger"><i class="fa fa-fw fa-trash"></i></a></td>
|
||||
</tr>
|
||||
{/LOOP}
|
||||
{ALT}
|
||||
<tr>
|
||||
<td class="text-center" colspan="5">
|
||||
No results to show.
|
||||
</td>
|
||||
</tr>
|
||||
{/ALT}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="text-center">
|
||||
<h5>WARNING: Regenerating existing webhooks makes joey sad, don't do iit!</h5>
|
||||
<p>The new webhooks will be generated using the base url: <strong>{urltouse}</strong></p>
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
<div class="form-group">
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-danger">re-generate</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
@ -1,21 +1,20 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2 text-center">
|
||||
<h2>Are You Sure You Want to Cancel?</h2>
|
||||
<div class="container py-4 context-main-bg my-4">
|
||||
<h2 class="text-center">Are You Sure You Want to Cancel?</h2>
|
||||
<hr>
|
||||
<div class="text-center">
|
||||
<p class="lead">
|
||||
Cancelling your subscription means you'll miss out on exclusive features, updates, and benefits.
|
||||
Cancelling your subscription means you'll miss out on exclusive features, updates, and benefits.
|
||||
</p>
|
||||
<p>
|
||||
Consider staying to continue enjoying the full experience of our service.
|
||||
Consider staying to continue enjoying the full experience of our service.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a href="{ROOT_URL}member/cancelconfirm/{cancelid}" class="btn btn-lg btn-danger btn-block">Cancel Subscription</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="{ROOT_URL}member/manage" class="btn btn-lg btn-primary btn-block">Go Back</a>
|
||||
</div>
|
||||
<div class="col-md-3 offset-3">
|
||||
<a href="{ROOT_URL}member/cancelconfirm/{cancelid}" class="btn btn-lg btn-outline-danger btn-block">Cancel Subscription</a>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<a href="{ROOT_URL}member/manage" class="btn btn-lg btn-primary btn-block atb-green-bg">Go Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,4 +1,3 @@
|
||||
|
||||
<!-- Compare plans -->
|
||||
<div class="table-responsive pricing-container container pb-4" id="compare">
|
||||
<h1 class="display-6 text-center my-4">Compare plans</h1>
|
||||
@ -31,7 +30,7 @@
|
||||
<td><i class="fa fa-fw fa-check atb-green"></i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" class="text-start">Share bookmarks abd folders</th>
|
||||
<th scope="row" class="text-start">Share bookmarks and folders</th>
|
||||
<td><i class="fa fa-fw fa-check"></i></td>
|
||||
<td><i class="fa fa-fw fa-check atb-green"></i></td>
|
||||
<td><i class="fa fa-fw fa-check atb-green"></i></td>
|
||||
@ -88,9 +87,6 @@
|
||||
<li>Access from any device</li>
|
||||
<li>Share access with anyone</li>
|
||||
</ul>
|
||||
<a href="/register" class="mt-auto w-100 btn btn-lg atb-green-outline">
|
||||
Sign-Up for Free
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -100,7 +96,7 @@
|
||||
<h4 class="my-0 fw-normal">Monthly</h4>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h1 class="card-title pricing-card-title">$4.99<small class="text-muted fw-light">/month</small></h1>
|
||||
<h1 class="card-title pricing-card-title">{prettyPriceMonthly}<small class="text-muted fw-light">/month</small></h1>
|
||||
<ul class="list-unstyled mt-3 mb-4">
|
||||
<li>Import/Export Features</li>
|
||||
<li>Integration with TempusTools App (WIP)</li>
|
||||
@ -108,7 +104,7 @@
|
||||
<li>Direct control of Feature Development</li>
|
||||
<li>Early Access to new features</li>
|
||||
</ul>
|
||||
<a href="/member/signup/monthly" class="mt-auto w-100 btn btn-lg atb-green-bg">
|
||||
<a href="/member/checkout/monthly" class="mt-auto w-100 btn btn-lg atb-green-bg">
|
||||
Get started
|
||||
</a>
|
||||
</div>
|
||||
@ -120,11 +116,11 @@
|
||||
<h4 class="my-0 fw-normal">Yearly</h4>
|
||||
</div>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h1 class="card-title pricing-card-title">$19.99<small class="text-muted fw-light">/year</small></h1>
|
||||
<h1 class="card-title pricing-card-title">{prettyPriceYearly}<small class="text-muted fw-light">/year</small></h1>
|
||||
<ul class="list-unstyled mt-3 mb-4">
|
||||
<li>Its cheaper if you like the product</li>
|
||||
</ul>
|
||||
<a href="/member/signup/yearly" class="mt-auto w-100 btn btn-lg atb-green-bg">
|
||||
<a href="/member/checkout/yearly" class="mt-auto w-100 btn btn-lg atb-green-bg">
|
||||
Get started
|
||||
</a>
|
||||
</div>
|
@ -1,7 +1,8 @@
|
||||
<div class="container py-4">
|
||||
<div class="container py-4 context-main-bg my-4">
|
||||
<h3 class="mb-4 text-center">Manage Memberships</h3>
|
||||
<hr>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<legend>Memberships</legend>
|
||||
<table class="table text-center context-main">
|
||||
<thead>
|
||||
<tr>
|
||||
@ -23,13 +24,13 @@
|
||||
<td>{DTC=date}{current_period_start}{/DTC}</td>
|
||||
<td>{DTC=date}{current_period_end}{/DTC}</td>
|
||||
<td>
|
||||
<a href="{ROOT_URL}member/pause/{ID}" class="btn btn-sm btn-primary">
|
||||
<a href="{ROOT_URL}member/pause/{ID}" class="btn btn-sm btn-outline-warning">
|
||||
<i class="fa fa-fw fa-pause"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{ROOT_URL}member/cancel/{ID}" class="btn btn-sm btn-danger">
|
||||
<i class="fa fa-fw fa-trash"></i>
|
||||
<a href="{ROOT_URL}member/cancel/{ID}" class="btn btn-sm btn-outline-danger">
|
||||
<i class="fa fa-fw fa-ban"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@ -43,7 +44,7 @@
|
||||
{/ALT}
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="{ROOT_URL}member/managepayment" class="btn btn-sm btn-primary">
|
||||
<a href="{ROOT_URL}member/managepayment" class="btn btn-sm btn-primary atb-green-bg">
|
||||
Manage Payment Method
|
||||
</a>
|
||||
</div>
|
||||
|
@ -11,7 +11,8 @@
|
||||
Right now, this entire system was built and managed by myself. I have used my own version of this for years, but translating it to a publicly available product is not a 1-to-1 job. There may be bugs or issues encountered while you use the product. I can't guarantee a fix for every need in every case immediately, but I do actively keep track of bugs and work hard to ensure everyone has a great experience using the app.
|
||||
</p>
|
||||
<div class="text-center mt-4 pb-4">
|
||||
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5 atb-green-bg">Report a Bug</a>{/loggedin}
|
||||
{loggedin}<a href="/bugreport" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Report a Bug</a>{/loggedin}
|
||||
<a href="/member/manage" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-bg">Manage Membership</a>
|
||||
<a href="/contact" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Contact Us</a>
|
||||
</div>
|
||||
</div>
|
@ -1,21 +1,20 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-8 col-md-offset-2 text-center">
|
||||
<h2>Are You Sure You Want to Pause?</h2>
|
||||
<div class="container py-4 context-main-bg my-4">
|
||||
<h2 class="text-center">Are You Sure You Want to Pause?</h2>
|
||||
<hr>
|
||||
<div class="text-center">
|
||||
<p class="lead">
|
||||
Pausing your subscription means you'll miss out on exclusive features, updates, and benefits.
|
||||
Pausing your subscription means you'll miss out on exclusive features, updates, and benefits.
|
||||
</p>
|
||||
<p>
|
||||
Consider staying to continue enjoying the full experience of our service.
|
||||
Consider staying to continue enjoying the full experience of our service.
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a href="{ROOT_URL}member/pauseconfirm/{pauseid}" class="btn btn-lg btn-danger btn-block">Pause Subscription</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="{ROOT_URL}member/manage" class="btn btn-lg btn-primary btn-block">Go Back</a>
|
||||
</div>
|
||||
<div class="col-md-3 offset-3">
|
||||
<a href="{ROOT_URL}member/pauseconfirm/{pauseid}" class="btn btn-lg btn-outline-danger btn-block">Pause Subscription</a>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<a href="{ROOT_URL}member/manage" class="btn btn-lg btn-primary btn-block atb-green-bg">Go Back</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,4 +1,14 @@
|
||||
<div class="jumbotron text-center">
|
||||
<h1>Take your time, its not a sprint, its a marathon.</h1>
|
||||
<p>Its ok, no-one wants to checkout too fast, its embarrassing.</p>
|
||||
<div class="col-8 mx-auto p-4 rounded shadow-sm context-main-bg my-4">
|
||||
<h2 class="text-center atb-green mb-4">Almost there!</h2>
|
||||
<p class="lead">
|
||||
Take your time, its not a sprint, its a marathon.
|
||||
Nno-one wants to checkout too fast, its embarrassing.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
If you think this is a tool that you could really use and can't afford it, use the contact form below and tell me why you need it.
|
||||
</p>
|
||||
<div class="text-center mt-4 pb-4">
|
||||
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5 atb-green-bg">Report a Bug</a>{/loggedin}
|
||||
<a href="/contact" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Contact Us</a>
|
||||
</div>
|
||||
</div>
|
@ -1,5 +1,14 @@
|
||||
<div class="jumbotron text-center">
|
||||
<h1>Thanks for joining!</h1>
|
||||
<p>Its people like you who keep the pixels on around here.
|
||||
Its people like me who tell you life is unfair and it could take up to an hour for your membership to activate.</p>
|
||||
<div class="col-8 mx-auto p-4 rounded shadow-sm context-main-bg my-4">
|
||||
<h2 class="text-center atb-green mb-4">Thanks for joining!</h2>
|
||||
<p class="lead">
|
||||
Its people like you who keep the pixels on around here.
|
||||
Its people like me who tell people like you that unfortunately, it could take up to an hour for your membership to activate.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
With that said, its usually instant, and if its taking too long, just reach out to us via the contact form.
|
||||
</p>
|
||||
<div class="text-center mt-4 pb-4">
|
||||
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5 atb-green-bg">Report a Bug</a>{/loggedin}
|
||||
<a href="/contact" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Contact Us</a>
|
||||
</div>
|
||||
</div>
|
@ -1,6 +1,6 @@
|
||||
<div class="col-8 mx-auto p-4 rounded shadow-sm mb-5 context-main-bg mt-4 text-center">
|
||||
<div class="row">
|
||||
<h2 class="text-primary mb-4">Upgrade to a Yearly Plan</h2>
|
||||
<h2 class="mb-4 atb-green">Upgrade to a Yearly Plan</h2>
|
||||
<p class="lead">
|
||||
Save more and enjoy uninterrupted access to all features with our yearly plan!
|
||||
</p>
|
||||
@ -10,12 +10,12 @@
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<a href="/member/checkout/yearly" class="btn btn-lg btn-success btn-block">
|
||||
<a href="/member/checkout/upgrade" class="btn btn-lg btn-block atb-green-bg">
|
||||
Upgrade to Yearly
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<a href="/member/upgrade" class="btn btn-lg btn-primary btn-block">
|
||||
<a href="/member" class="btn btn-lg btn-block atb-green-outline">
|
||||
Stay on Monthly
|
||||
</a>
|
||||
</div>
|
||||
|
13
app/plugins/members/views/upgradeCompleted.html
Normal file
13
app/plugins/members/views/upgradeCompleted.html
Normal file
@ -0,0 +1,13 @@
|
||||
<div class="col-8 mx-auto p-4 rounded shadow-sm context-main-bg my-4">
|
||||
<h2 class="text-center atb-green mb-4">Thanks for the vote of confidence!</h2>
|
||||
<p class="lead">
|
||||
While you were more profitable as a monthly user, I do appreciate the vote of confidence it takes to commit to 4x the cost at once.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
There really is no more benefit than you saving money, so enjoy the features you have already come to know and enjoy.
|
||||
</p>
|
||||
<div class="text-center mt-4 pb-4">
|
||||
{loggedin}<a href="/bugreport" class="btn btn-primary btn-lg px-5 atb-green-bg">Report a Bug</a>{/loggedin}
|
||||
<a href="/contact" class="btn btn-outline-secondary btn-lg px-5 ms-3 atb-green-outline">Contact Us</a>
|
||||
</div>
|
||||
</div>
|
@ -4,7 +4,7 @@
|
||||
<div class="col-lg-6 mx-auto">
|
||||
<p class="lead mb-4">Quickly add and manage an internet worth of bookmarks with AllTheBookmarks, my personal favorite app for keeping track of dead memes long after they outlive their hilarity.</p>
|
||||
<div class="d-grid gap-2 d-sm-flex justify-content-sm-center mb-5">
|
||||
<a href="#pricing" class="btn btn-primary atb-green-bg btn-lg px-4 me-sm-3">
|
||||
<a href="#pricing" class="btn atb-green-bg btn-lg px-4 me-sm-3">
|
||||
Buy Now (Too Fast)
|
||||
</a>
|
||||
<a href="#features" class="btn btn-outline-secondary btn-lg px-4 atb-green-outline">
|
||||
@ -247,7 +247,7 @@
|
||||
<td><i class="fa fa-fw fa-check atb-green"></i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row" class="text-start">Share bookmarks abd folders</th>
|
||||
<th scope="row" class="text-start">Share bookmarks and folders</th>
|
||||
<td><i class="fa fa-fw fa-check"></i></td>
|
||||
<td><i class="fa fa-fw fa-check atb-green"></i></td>
|
||||
<td><i class="fa fa-fw fa-check atb-green"></i></td>
|
||||
|
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
<input type="hidden" name="token" value="{TOKEN}">
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-primary">Update</button>
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-primary atb-green-bg">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -19,7 +19,7 @@
|
||||
</div>
|
||||
</fieldset>
|
||||
<input type="hidden" name="token" value="{TOKEN}">
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-primary">Update</button>
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-primary atb-green-bg">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,13 +2,13 @@
|
||||
<h3 class="mb-4">Preferences</h3>
|
||||
<hr>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-6">
|
||||
<form action="" method="post" class="">
|
||||
<fieldset>
|
||||
{PREFERENCES_FORM}
|
||||
</fieldset>
|
||||
<input type="hidden" name="token" value="{TOKEN}">
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block">Update</button><br>
|
||||
<button name="submit" value="submit" type="submit" class="btn btn-lg btn-primary center-block atb-green-bg">Update</button><br>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user