diff --git a/.gitignore b/.gitignore
index 2c74cbb..fb931a2 100755
--- a/.gitignore
+++ b/.gitignore
@@ -65,3 +65,4 @@ vendor/canary/logs/*
components/*
mailhog.log
uploads/*
+images/qr-codes/*
diff --git a/app/config/constants.php b/app/config/constants.php
index 2f1863c..ab609ba 100755
--- a/app/config/constants.php
+++ b/app/config/constants.php
@@ -39,7 +39,7 @@ if ( ! defined( 'CONFIG_DIRECTORY' ) ) {
define( 'CANARY_SECURE_HASH', 'd73ed7591a30f0ca7d686a0e780f0d05' );
# Tempus Project Core
define( 'APP_NAME', 'The Tempus Project');
-define( 'TP_DEFAULT_LOGO', 'images/logo.png');
+define( 'TP_DEFAULT_LOGO', 'images/logoWhite.png');
// Check
define( 'MINIMUM_PHP_VERSION', 8.1);
// Cookies
diff --git a/app/controllers/admin/images.php b/app/controllers/admin/images.php
index 690cdaf..20ca7b1 100644
--- a/app/controllers/admin/images.php
+++ b/app/controllers/admin/images.php
@@ -76,25 +76,6 @@ class Images extends AdminController {
Views::view( 'admin.images.upload' );
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
private function getFolderObject( $folder, $subdirs = '' ) {
$names = explode( DIRECTORY_SEPARATOR, $folder );
$folderName = array_pop( $names );
@@ -155,28 +136,6 @@ class Images extends AdminController {
return $dirs;
}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
public function __construct() {
parent::__construct();
self::$title = 'Admin - Images';
@@ -204,25 +163,6 @@ class Images extends AdminController {
Debug::error( 'There was an error with your upload.');
Issues::add( 'error', [ 'There was an error with your upload.' => Check::userErrors() ] );
}
-
-
-
-
-
-
-
-
-
- // if ( self::$token->create(
- // Input::post( 'name' ),
- // Input::post( 'notes' ),
- // Input::post( 'token_type' )
- // ) ) {
- // Session::flash( 'success', 'Token Created' );
- // Redirect::to( 'admin/images' );
- // }
-
-
}
Views::view( 'admin.images.create' );
}
@@ -261,7 +201,6 @@ class Images extends AdminController {
}
public function rename() {
-
if ( ! Input::exists( 'fileLocation' ) ) {
Session::flash( 'warning', 'Unknown image.' );
Redirect::to( 'admin/images' );
@@ -274,7 +213,6 @@ class Images extends AdminController {
Issues::add( 'error', [ 'There was an error renaming the image.' => Check::userErrors() ] );
} else {
$result = $this->renameFile( Input::post( 'filelocation' ), Input::post( 'newname' ) );
-
if ( ! empty( $result ) ) {
Session::flash( 'success', 'Image has been renamed.' );
Redirect::to( 'admin/images' );
diff --git a/app/css/main-dark.css b/app/css/main-dark.css
index f2e8eb0..f6306ae 100755
--- a/app/css/main-dark.css
+++ b/app/css/main-dark.css
@@ -8,6 +8,14 @@
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
+ .context-popover {
+ background-color: #383838;
+ color: white;
+}
+
+.context-popover .popover-header {
+ background-color: #2c2c2c;
+}
.context-main-border {
border-color: #f5f5f5!important;
diff --git a/app/css/main.css b/app/css/main.css
index 6651339..86cd59f 100755
--- a/app/css/main.css
+++ b/app/css/main.css
@@ -8,6 +8,40 @@
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
+.facebook {
+ border-color: #1877F2 !important; /* Facebook Blue */
+ color: #1877F2 !important;
+}
+
+.x-black {
+ border-color: #000000 !important; /* X (formerly Twitter) Black */
+ color: #000000 !important;
+}
+
+.reddit {
+ border-color: #FF4500 !important; /* Reddit Orange */
+ color: #FF4500 !important;
+}
+
+.opera {
+ border-color: #FF1B2D !important; /* Opera Red */
+ color: #FF1B2D !important;
+}
+
+.firefox {
+ border-color: #FF7139 !important; /* Firefox Orange */
+ color: #FF7139 !important;
+}
+
+.edge {
+ border-color: #0078D7 !important; /* Microsoft Edge Blue */
+ color: #0078D7 !important;
+}
+
+.safari {
+ border-color: #0B78E3 !important; /* Safari Blue */
+ color: #0B78E3 !important;
+}
.context-main-border {
border-color: #1e1e1e!important;
diff --git a/app/js/main.js b/app/js/main.js
index c22fc1f..95869a2 100755
--- a/app/js/main.js
+++ b/app/js/main.js
@@ -8,6 +8,94 @@
* @link https://TheTempusProject.com
* @license https://opensource.org/licenses/MIT [MIT LICENSE]
*/
+/**
+ * Progressive Web-App
+ **/
+let deferredPrompt;
+const installPrompt = document.getElementById("install-prompt");
+const chromeMessage = document.getElementById("chrome-install-message");
+const iosMessage = document.getElementById("ios-install-message");
+const installButton = document.getElementById("install-button");
+const dismissButton = document.querySelector("#install-prompt .btn-close");
+
+// Check if the user previously dismissed the prompt
+if ( ! localStorage.getItem("pwaInstallDismissed") ) {
+ window.addEventListener("beforeinstallprompt", (event) => {
+ event.preventDefault();
+ deferredPrompt = event;
+ installPrompt.classList.remove("d-none");
+ installPrompt.classList.add("d-block"); // Show the alert
+ chromeMessage.classList.remove("d-none");
+ chromeMessage.classList.add("d-block"); // Show the prompt
+ });
+
+ if ( isIos() && ! isInStandaloneMode() ) {
+ installPrompt.classList.remove("d-none");
+ installPrompt.classList.add("d-block"); // Show the alert
+ iosMessage.classList.remove("d-none");
+ iosMessage.classList.add("d-block"); // Show the prompt
+ }
+}
+
+// ios REQUIRES a service worker
+if ( 'serviceWorker' in navigator ) {
+ navigator.serviceWorker.register('app/js/sw.js')
+ .then(() => console.log('Service Worker Registered'));
+}
+// self.addEventListener('install', () => self.skipWaiting());
+// self.addEventListener('activate', () => self.clients.claim());
+// self.addEventListener('fetch', () => {}); // No file interception
+
+// Handle Install Button Click
+if ( installButton ) {
+ installButton.addEventListener("click", async () => {
+ if ( deferredPrompt ) {
+ deferredPrompt.prompt();
+ const { outcome } = await deferredPrompt.userChoice;
+
+ if (outcome === "dismissed") {
+ setInstallDismissed(); // Store that the user dismissed the prompt
+ }
+
+ deferredPrompt = null; // Reset prompt
+ installPrompt.classList.remove("d-block");
+ installPrompt.classList.add("d-none");
+ }
+ });
+}
+
+// Handle Close Button Click
+if ( dismissButton ) {
+ dismissButton.addEventListener("click", () => {
+ setInstallDismissed(); // Store that the user dismissed the prompt
+ });
+}
+
+// Function to remember user choice for 7 days
+function setInstallDismissed() {
+ localStorage.setItem("pwaInstallDismissed", Date.now() + 7 * 24 * 60 * 60 * 1000);
+ installPrompt.classList.remove("d-block"); // Hide the prompt
+ installPrompt.classList.add("d-none");
+}
+
+// Check if the 7-day period has passed
+if (localStorage.getItem("pwaInstallDismissed")) {
+ const dismissUntil = parseInt(localStorage.getItem("pwaInstallDismissed"), 10);
+ if (Date.now() < dismissUntil) {
+ //
+ } else {
+ localStorage.removeItem("pwaInstallDismissed"); // Reset after 7 days
+ }
+}
+
+function isIos() {
+ return /iphone|ipad|ipod/i.test(navigator.userAgent);
+}
+
+function isInStandaloneMode() {
+ return window.matchMedia('(display-mode: standalone)').matches || window.navigator.standalone;
+}
+
/**
* Automatically selects/de-selects all check boxes associated with that field
**/
@@ -215,6 +303,8 @@ document.querySelectorAll('[data-bs-toggle="collapse"]').forEach(button => {
document.addEventListener("DOMContentLoaded", function () {
var popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
var popoverList = popoverTriggerList.map(function (popoverTriggerEl) {
- return new bootstrap.Popover(popoverTriggerEl);
+ return new bootstrap.Popover(popoverTriggerEl, {
+ customClass: 'context-popover',
+ });
});
});
\ No newline at end of file
diff --git a/app/js/sw.js b/app/js/sw.js
new file mode 100644
index 0000000..89c3448
--- /dev/null
+++ b/app/js/sw.js
@@ -0,0 +1 @@
+self.addEventListener('fetch', () => {});
\ No newline at end of file
diff --git a/app/models/sessions.php b/app/models/sessions.php
index 2070dde..df5f2b2 100755
--- a/app/models/sessions.php
+++ b/app/models/sessions.php
@@ -59,7 +59,7 @@ class Sessions extends DatabaseModel {
public function checkSession( $sessionID ) {
$user = new User;
// @todo lets put this on some sort of realistic checking regime other than check everything every time
- if ( $sessionID == false ) {
+ if ( empty( $sessionID ) ) {
Debug::log( 'sessionID false' );
return false;
}
diff --git a/app/templates/default/default.inc.php b/app/templates/default/default.inc.php
index 95ff8c6..6747556 100755
--- a/app/templates/default/default.inc.php
+++ b/app/templates/default/default.inc.php
@@ -19,6 +19,15 @@ use TheTempusProject\Houdini\Classes\Components;
use TheTempusProject\Bedrock\Classes\Config;
use TheTempusProject\Bedrock\Functions\Input;
use TheTempusProject\TheTempusProject as App;
+use Endroid\QrCode\Builder\Builder;
+use Endroid\QrCode\Encoding\Encoding;
+use Endroid\QrCode\ErrorCorrectionLevel;
+use Endroid\QrCode\Label\LabelAlignment;
+use Endroid\QrCode\Label\Font\OpenSans;
+use Endroid\QrCode\RoundBlockSizeMode;
+use Endroid\QrCode\Writer\PngWriter;
+use TheTempusProject\Hermes\Functions\Route as Routes;
+use TheTempusProject\Canary\Bin\Canary as Debug;
class DefaultLoader extends Loader {
private static $loaded = false;
@@ -41,12 +50,61 @@ class DefaultLoader extends Loader {
}
$this->addJs( '' );
Components::setIfNull( 'LOGO', Config::getValue( 'main/logo' ) ?? TP_DEFAULT_LOGO );
+
+ if ( ! empty( Config::getValue( 'share/enabled' ) ) ) {
+ $currentUrl = Routes::getAddress() . Input::get( 'url' );
+ $folder = IMAGE_DIRECTORY . 'qr-codes' . DIRECTORY_SEPARATOR ;
+ $filename = md5( $currentUrl ) . '.png';
+
+ if ( ! file_exists( $folder ) ) {
+ Debug::Info( 'Creating Directory because it does not exist' );
+ mkdir( $folder, 0777, true );
+ }
+ if ( ! empty( Config::getValue( 'share/qr' ) ) ) {
+ if ( ! file_exists( $folder . $filename ) ) {
+ Debug::Info( 'Creating qr-image because it does not exist' );
+ $builder = new Builder(
+ writer: new PngWriter(),
+ writerOptions: [],
+ validateResult: false,
+ data: Routes::getAddress() . Input::get( 'url' ),
+ encoding: new Encoding('UTF-8'),
+ errorCorrectionLevel: ErrorCorrectionLevel::High,
+ size: 200,
+ margin: 10,
+ roundBlockSizeMode: RoundBlockSizeMode::Margin,
+ logoPath: APP_ROOT_DIRECTORY . DIRECTORY_SEPARATOR . Config::getValue( 'main/logo' ),
+ logoResizeToWidth: 30,
+ logoPunchoutBackground: true,
+ labelText: Config::getValue( 'main/name' ),
+ labelFont: new OpenSans(14),
+ labelAlignment: LabelAlignment::Center
+ );
+ $result = $builder->build();
+ $result->saveToFile( $folder . $filename );
+ }
+ Components::set( 'QR_CODE','' );
+ } else {
+ Components::setIfNull( 'QR_CODE', '' );
+ }
+ Components::setIfNull( 'SHARE_IMAGE', Views::simpleView( 'footer.share' ) );
+ } else {
+ Components::setIfNull( 'SHARE_IMAGE', '' );
+ }
+
+ if ( ! empty( Config::getValue( 'main/pwa' ) ) ) {
+ Components::setIfNull( 'PWA', Views::simpleView( 'pwa') );
+ } else {
+ Components::setIfNull( 'PWA', '' );
+ }
+
Components::setIfNull( 'COPY', Views::simpleView( 'footer.copy') );
Components::setIfNull( 'SOCIAL', Views::simpleView( 'footer.social') );
Components::prepend( 'FOOTER_LEFT', Views::simpleView( 'footer.left', Navigation::getMenuLinks( App::CONTACT_FOOTER_MENU_NAME ) ) );
Components::prepend( 'FOOTER_CENTER', Views::simpleView( 'footer.center', Navigation::getMenuLinks( App::INFO_FOOTER_MENU_NAME ) ) );
Components::prepend( 'FOOTER_RIGHT', Views::simpleView( 'footer.right') );
Components::setIfNull( 'FOOT', Views::simpleView( 'footer.container') );
+
/**
* Top-Nav
*/
diff --git a/app/templates/default/default.tpl b/app/templates/default/default.tpl
index 2cda303..24e5a02 100755
--- a/app/templates/default/default.tpl
+++ b/app/templates/default/default.tpl
@@ -28,7 +28,12 @@
{AUTHOR}
{ROBOT}
+
+
+
+
+
@@ -91,6 +96,7 @@
{/ISSUES}
+ {PWA}
{CONTENT}
diff --git a/app/views/faq.html b/app/views/faq.html
index 956f2b6..b914758 100755
--- a/app/views/faq.html
+++ b/app/views/faq.html
@@ -56,8 +56,8 @@
- {SITENAME} is open source and available free of charge through GitLab and Packagist.
- The developer behind the project is Joey Kimsey and he can be contacted through his website for development services.
+ {SITENAME} is open source and available free of charge through GitLab and Packagist.
+ The developer behind the project is Joey Kimsey and he can be contacted through his website for development services.
diff --git a/app/views/footer/share.html b/app/views/footer/share.html
new file mode 100644
index 0000000..53cb6a6
--- /dev/null
+++ b/app/views/footer/share.html
@@ -0,0 +1,17 @@
+
+
Share
+
+
+
+
+
\ No newline at end of file
diff --git a/app/views/index.html b/app/views/index.html
index db1b777..c27723b 100755
--- a/app/views/index.html
+++ b/app/views/index.html
@@ -101,21 +101,21 @@
Subscribe
-
Building your list should always be a part of your application and services and it made as simple as can be with this plugin.
+
Building your list should always be a part of your business. Our subscriptions plugin makes it as simple as can be.
WIP (Work in Progress)
-
A light-weight and simple plugin that allows you to keep a running list of works in progress to share with site visitors..
+
A light-weight and simple plugin that allows you to keep a running list of work in progress to share with site visitors.
Memberships / Payments
-
Our membership plugin integrates with Stripe to allow incredibly simple membership setup and can be easily expanded to sell any of your products.
+
Our membership plugin integrates with Stripe to allow incredibly simple membership setup and can be easily expanded to sell any of your products. We also have a donations integration with stripe to allow you to accept donation right from the app.
diff --git a/app/views/pwa.html b/app/views/pwa.html
new file mode 100644
index 0000000..45bfae8
--- /dev/null
+++ b/app/views/pwa.html
@@ -0,0 +1,18 @@
+
+
+
+
+ {SITENAME} is now available as a Progressive Web App, click the button to install now.
+
+
+
+
+
+
+
+ {SITENAME} is now available as a Progressive-Web-App, tap the share icon and then "Add to Home Screen".
+