Compare commits

...

38 Commits
b2.5.1 ... beta

Author SHA1 Message Date
Ro 62e2dea287
Beta 2.6.1 update 2 years ago
Ro f824b53f2a
Update versioning to 2.6.1
All the issues in milestone 2.6.1 have been completed, so now it's time for an update
2 years ago
Ro 8461d88dd6
Settings CSS Cleanup
The Settings UI needed some responsive polish, so that's been given a
bit more love.
2 years ago
Ro a9c88f1430
Edits for #86 and #92
Fixed the issue where the text edit controller would scroll right off
the screen. Now it stick when it's the bottom of the header.

Also changed the background color of page links on the Start and Book
pages to indicate there is no image. It's just cleaner
2 years ago
Ro fa4b252d9c
Fix for #94
Header images were missing from the archive and tags pages, so this is a
patch to make sure those are working again.
2 years ago
Ro 181225329a
CSS and icon fixes
Fixes for issues #93, #88, #87
2 years ago
Ro 4876c1336e
Updated Composer packages; empty field fix
Composer dependencies were pretty old, so they needed to be upgraded to
the latest.

There was also a minor bug that was triggered when a new page was saved
with empty tags and no images or documents, so that's been patched as
well.
2 years ago
Ro 8ce253418d
Adding site creation hot fix
Adding the fixes for site install process errors to the branch.
2 years ago
Ro f1850ce7f7
Site Creation Hot Fix
There was an error in the request to set up a fresh site on the front
end and handling the respective object on the backend that was causing
the process to error out.

Also added the source map for the dash script because it's eventually
going to be fully transparent anyway.
2 years ago
Ro 8622ba5941
Minor js hotfixes
Updating the script and the html to call the new file name, which is
called 'dash.js'
2 years ago
Ro e7cd52bd12
Fixed script after removing dependency
Removed 'carot-ps' from dependencies so it had to be removed from
scripts calling that package.

Also renamed dash script to 'dash.js' just so it's clear it is for the
dashboard
2 years ago
Ro f9190c2a41
removed carot-pos from dependecies 2 years ago
Ro 302362a478
Merge 'develop' 2.6.0 upadte into beta
Massive update covering issues #71, #80, #81, #83 as well as updated
responsive styles and an overhauled File Manager
2 years ago
Ro 8885ae4c63
updated version nunmber 2 years ago
Ro 405be1a6ed
Emplty Layout Hotfix
When creating new pages, there is no layout, so the system was pushing a
null error when trying to use string_contains and a null string, so
cleaned that up so it defaults to 'page' when that string is empty
2 years ago
Ro 3f9506ac6b
Responsive P 4 - Dash, Index, Editor, Nav, Menu
Cleaned up resonsive for the rest of the remaining pages: the dash
index, page index, page editor, navigation editor and plugged in a
mobile nav that activates when the viewport gets skinny.

Whew.
2 years ago
Ro e7fd91c152
Responsive Pt 3 - Pass Reset, Site Create/Restore
Rebuilt forms for resetting the password, creating a fresh site and
restoring a site from a backup, as well as adjusting the accompanying
scripts that handle those processes.
2 years ago
Ro 5adf196783
Responsive Part 2 - Login
Updated Login CSS and cleaned up login notifications to alert when
something is amiss.
2 years ago
Ro 2ce86fad2e
Settings CSS Remix
Wasn't feeling the previous CSS responsive structure, so edited it to be
a bit more streamline.

Also fixed small issue with the backup API request.

Made a small change to notifications so the alert stays live while the
system is processing a request rather than going back to an unalert
state.
2 years ago
Ro bfb0873f5f
Reponsive: Part 1 - Settings
Started cleaning up responsive styles for the site starting with the
Settings section. Still needs some tweaking but the structure for that
section is in so it's just a matter of police.

Some changes need to be made to the main nav as reduced viewport throws
off the alignment.
2 years ago
Ro 97278e3a90
Notifications Rework #81
Integrated the Notifications UI component into the header to streamline
user alerts into the overall experience.

Also added titles to use the space created by moving the notifications
compoenent to it's own space.
2 years ago
Ro 78bfe4596b
Styled Nav Editor
Created new CSS styles for dash nav editor and updated the appropriate
controller scripts.

Also updated the icons for the main nav.
2 years ago
Ro 1b89d1d072
Restyled Settings UI
Rebuilt the css for the Settings UI, which also led to some changed in
the FilesAPI so it knows what to do with specific targets. There's still
some additional styling needed to polish it, but the core structure is
in place so now it can just be tweaked. The controller for this page was
adjusted as needed.

Also moved the settings sub nav convtrols to the header menu since it's
sticky now.
2 years ago
Ro fcca7357bc
Fixes for removing media items and page deletion
The upload process changed, so some tweaks needed to me made to the page
deletion process, which just marks the page as deleted but keeps the
file. Also updated the file manager to properly delete items from the
display list.

The css for page listings also had to updated [forgot to put that on the
list] so the styles for that were updated and the template pages
adjusted accordingnly.

Also forgot to mention changes to the notification display in the last
commit. It's basic as of right now but it will be enhanced as needed.
2 years ago
Ro 07b422a9c3
CSS Overhaul Part 1
This one is a doozy, so let's breakt it down into what areas where
touched.

-   updated package json to remove unneeded dependencies.
-   rebuilt file uploading to simply a very convuluted process
-   began proces to replace icons with https://tabler-icons.io
-   began process of removing the need for css preprocessor and using
    pure css
        - login completed
        - dashboard index completed
        - page edit ui completed
- page edit ui text editor tweaked so syntax highlighting is cleaner and
  more accurate

The settings and navigation UIs still remain and then polishing the
responsive for the new css structure
2 years ago
Ro ec1dc49ba1
Login Hotfix
The script that handles logggin in and the form for getting that
information were both posting the info which would result in an
intemittent uncaught error.

An attribute was added to the form so it does not submit at the same
time the JS sends a request.

A minor bug but it was annoying.
2 years ago
Ro 61ae73a9e5
Issue #83 Round 1
First pass for CSS refactor for the dashboard, including the login and
index templates. Still rough but the basic structure is in place for
both as well as the re-worked css that will be added to the repo later
once all the pages have been updated.

Lots to do still but a good start.
2 years ago
Ro 859b75e9f3
Removed links to old repo from ReadMe
I'll probably miss some as the migration completes but let's start with
scrubbing the old repo from the new one.
2 years ago
Ro a14d4a0a08
Move Repo test
Just a quick update to see if I got all the setting right as I'm
migrating the show to a fresh repo
2 years ago
Ro 7890715ea6
PHP Linting Tweaks
PHP syntax checking was being weird so I spent some time to make some
corrections. The problem was I was just using the wrong protocal, PEAR
when I've been coding to the PSR12 standard. Easy fix.
2 years ago
Ro 77eb8dd1a8
HTTP Method notes for RouteControl
Currently only two http methods are being utilized for route traffic so
classes are getting jumped trying to stuff every action in on or the
other. More methods need to be implemented to better organize route
pathing and subsequent requests
2 years ago
Ro e431f1afa4
Sign commit test
Testing out commit signing for a bit of extra security.
2 years ago
Ro 254a7f1c38 Scrubbed Moment from codebase
Moment was still being used in some classes so found and replaced all
those instances with Carbon and uninstalled the packaged from composer.
2 years ago
Ro c2b3b234fa Merging autoload changes 2 years ago
Ro b092645733 Removed Fipamo classes from composer autoloading
I didn't like the extra step that had to be taken to register new
classes from the command line using composer's auto dump, so a quick
script was implemented to handle Fipamo loading classes seperately so
composer can manage itself, removing the need for updating it whenever I
add a new classs to the codebase
2 years ago
Ro fce378d437 Merging dependency updates and Moment removal
Bringing over changes from develop into the beta branch, including
updating composer package dependencies and getting rid of Moment in
favor of Carbon for date formatting
2 years ago
Ro 73e4243231 Removed TODO note for Carbon
Left a stray TODO in there that needed to be removed since Moment has
been replaced with Carbon.
2 years ago
Ro 3260e3b76b Updated dependencies, replaced Moment
Composer package dependencies hadn't been updated in awhile, so a part
of the clean up for the php 8.1 install, that has been handled

Moment was being used to handle date formatting but it hasn't been
updated in awhile either, so I switched to Carbon which is still in
active development.
2 years ago

11
.gitignore vendored

@ -12,13 +12,21 @@ public/*
public/assets/*
!public/assets/css
public/assets/css/*
!public/assets/css/dash.css
!public/assets/css/dash
!public/assets/scripts
public/assets/scripts/*
<<<<<<< HEAD
!public/assets/scripts/Start.js
/public/assets/images/global/rikc-logo.svg
=======
!public/assets/scripts/dash.js
<<<<<<< HEAD
>>>>>>> develop
=======
!public/assets/scripts/dash.js.map
>>>>>>> develop
!public/assets/images
public/assets/images/*
!public/assets/images/global/
@ -47,3 +55,4 @@ config.codekit3
src/com/*
src/styles/*
src/com/ui/TextEditor.js

@ -1,6 +1,8 @@
<?php
return (new PhpCsFixer\Config())
$config = new PhpCsFixer\Config();
return $config
->setRiskyAllowed(true)
->setRules([
'@PSR12' => true,
'array_indentation' => true,
@ -20,7 +22,7 @@ return (new PhpCsFixer\Config())
'multiline_whitespace_before_semicolons' => [
'strategy' => 'no_multi_line',
],
'single_quote' => true,
'single_quote' => false,
'binary_operator_spaces' => [
'default' => 'single_space',
@ -50,7 +52,6 @@ return (new PhpCsFixer\Config())
'extra',
'parenthesis_brace_block',
'throw',
]
],
'no_multiline_whitespace_around_double_arrow' => true,
@ -68,5 +69,6 @@ return (new PhpCsFixer\Config())
'ordered_imports' => [
'sort_algorithm' => 'none',
],
//Other rules here...
])
->setLineEnding("\n");

@ -1,6 +1,3 @@
{
"extends": [
"stylelint-config-standard-scss",
"stylelint-config-prettier-scss"
]
"extends": ["stylelint-config-standard"]
}

@ -1,13 +1,9 @@
![This is Fipamo](https://playvicio.us/base-assets/images/fipamo-brand.png)
# Fipamo means to save
The Fipamo project was born from a need for a simple, easy to use no data blog platform that doesn't require much effort to set up and maintain. Fipamo uses Markdown to handle posts and renders them to flat html so you can serve them from anywhere. No complicated set ups. No long list of dependencies. Just set up and go.
Because nobody has time for all that.
## Check the (WIP) Docs to get you started. <br>
## Check the (WIP) Docs to get you started. <br>
[Getting Started](https://code.playvicio.us/Are0h/Fipamo/wiki/00---Start) <br>
[Install](https://code.playvicio.us/Are0h/Fipamo/wiki/01---Install) <br>
[Using Fipamo](https://code.playvicio.us/Are0h/Fipamo/wiki/02-Usage) <br>
[Getting Started](https://koodu.ubiqueros.com/are0h/Fipamo/wiki/00---Start) <br>

@ -0,0 +1,10 @@
<?php
spl_autoload_register(function ($className) {
$file = dirname(__DIR__) . '\\' . $className . '.php';
$file = str_replace('\\', DIRECTORY_SEPARATOR, $file);
//echo $file;
if (file_exists($file)) {
include $file;
}
});

@ -0,0 +1,73 @@
<?php
namespace brain\api\v1;
use brain\utility\FileUploader;
use brain\data\Settings;
use brain\data\Member;
class FilesAPI
{
public function __construct()
{
}
public static function uploadFiles($request, $type = null)
{
$upload = $request->getUploadedFiles(); //grab uploaded files
$options = $request->getParsedBody();
$file = $upload['upload_files'][0]; //front end sends one by one for progress tracking, so grab first
$type = $file->getClientMediaType();
$filesPath = '';
$path = date('Y') . '/' . date('m');
$response = [];
switch ($type) {
case 'image/jpeg':
case 'image/png':
case 'image/gif':
case 'image/svg':
if (isset($options["source"])) {
if ($options["source"] == "avatar-upload") {
$filesPath = '/assets/images/user/' . $path . '/';
Member::updateData(
'avi',
$filesPath . $file->getClientFileName()
);
} else {
$filesPath = '/assets/images/user/' . $path . '/';
Settings::updateGlobalData(
'background',
$filesPath . '/' . $file->getClientFileName()
);
}
} else {
$filesPath = '/assets/images/blog/' . $path . '/';
}
break;
case 'video/mp4':
$filesPath = '/assets/video/blog/' . $path . '/';
break;
case 'audio/mpeg':
$filesPath = '/assets/sound/blog/' . $path . '/';
break;
case 'application/pdf':
case 'text/plain':
case 'text/rtf':
$filesPath = '/assets/docs/blog/' . $path . '/';
break;
}
FileUploader::uploadFile('../public' . $filesPath, $file);
$response = [
'message' => "File Uploaded. Great!",
"filePath" => $filesPath . urlencode($file->getClientFileName()),
"fileName" => urlencode($file->getClientFileName()),
'type' => $type,
];
return $response;
}
}

@ -111,7 +111,7 @@ class PagesAPI
case 'delete':
case 'create':
case 'write':
$body = $request->getParsedBody();
$body = json_decode(file_get_contents("php://input"), true);
$passed = true;
if (!isset($body['form_token'])) {
$result = [
@ -120,7 +120,6 @@ class PagesAPI
];
} else {
if ($body['form_token'] == Session::get('form_token')) {
//TODO: Verify form fields
$keys = [
'id',
'uuid',
@ -135,12 +134,15 @@ class PagesAPI
'featured',
'published',
'form_token',
'feature_image',
'imageList',
"fileList",
"remote"
];
foreach ($body as $key => $item) {
if (!in_array($key, $keys)) {
//found unnecessary key, so reject submission
var_dump($key);
$passed = false;
}
}

@ -38,6 +38,7 @@ class SettingsAPI
} else {
$render = new Render();
if (isset($themeConfig['render'])) {
//rendering for one page sites
if (!$themeConfig['render'] || $themeConfig['render'] === 'false') {
$render->renderIndex();
$result = [

@ -6,6 +6,7 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use brain\api\v1\AuthAPI;
use brain\api\v1\PagesAPI;
use brain\api\v1\FilesAPI;
use brain\api\v1\SettingsAPI;
use brain\api\v1\InitAPI;
use brain\api\v1\MailerAPI;
@ -22,7 +23,7 @@ class APIControl
$filename = '';
switch (isset($args['third']) ? $args['third'] : 'none') {
case 'status':
$result = AuthAPI::status();
$result = AuthAPI::status();
break;
case 'page':
@ -188,6 +189,24 @@ class APIControl
];
}
break;
case "files":
$token = $request->getHeader('fipamo-access-token');
if (isset($token[0])) {
if (Session::verifyToken($token[0])) {
$result = FilesAPI::uploadFiles($request, $args);
} else {
$result = [
'message' => 'Invalid token, API access denied, homie',
'type' => 'API_ERROR',
];
}
} else {
$result = [
'message' => 'No token, API access denied, homie',
'type' => 'API_ERROR',
];
}
break;
case 'settings':
if (isset($body)) {
@ -225,7 +244,6 @@ class APIControl
];
break;
}
$response->getBody()->write(json_encode($result));
return $response->withHeader('Content-Type', 'application/json');
}

@ -7,6 +7,8 @@ use brain\data\Session;
use brain\data\Settings;
use brain\data\Themes;
use brain\utility\Setup;
use brain\utility\Sorting;
use Carbon\Carbon;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Views\Twig;
@ -31,9 +33,9 @@ class DashControl
$template = 'dash/settings.twig';
$member = Session::get('member');
$form_token = Session::get('form_token');
$updated = new \Moment\Moment($settings['global']['last_backup']);
$updated = new Carbon($settings['global']['last_backup']);
$pageOptions = [
'title' => 'Dash Settings',
'title' => 'Settings',
'private' => $settings['global']['private'],
'renderOnSave' => $settings['global']['renderOnSave'],
'background' => $settings['global']['background'],
@ -69,7 +71,7 @@ class DashControl
$settings = $config->getSettings();
$template = 'dash/navigation.twig';
$pageOptions = [
'title' => 'Edit Dash Navigation',
'title' => 'Edit Menu',
'status' => Session::active(),
'menu' => $settings['menu'],
];
@ -110,14 +112,22 @@ class DashControl
case 'edit':
$page = (new Book())->findPageById($uuid);
$views = [];
if (!isset($page['layout'])) {
$page['layout'] = "page";
}
if (str_contains($page['layout'], 'index')) {
$views = (new Themes())->getCustomIndex();
} else {
$views = (new Themes())->getCustomViews();
}
$imageList = explode(',', $page['feature']);
$fileList = explode(',', $page['files']);
$imageList = [];
$fileList = [];
if (isset($page['feature'])) {
$imageList = explode(',', $page['feature']);
}
if (isset($page['files'])) {
$fileList = explode(',', $page['files']);
}
$images = [];
$files = [];
@ -136,7 +146,7 @@ class DashControl
}
$pageOptions = [
'title' => 'Fipamo | Edit Page',
'title' => $page['title'],
'page' => $page,
'mode' => $mode,
'token' => Session::get('form_token'),
@ -150,22 +160,21 @@ class DashControl
$config = new Settings();
$settings = $config->getSettings();
$loader = new \Twig\Loader\FilesystemLoader(
'../content/themes'
'../content/themes/' . $settings['global']['theme'] .
'/'
);
$display = new \Twig\Environment($loader, []);
$book = new Book();
$page = $book->findPageById($uuid);
$pageOptions = Sorting::page($page);
$preview = $settings['global']['theme'] .
'/' .
$page['layout'] .
$preview = $page['layout'] .
'.twig';
$html = $display->render($preview, $pageOptions);
$response->getBody()->write($html);
return $response;
break;
break;
default:
$pageOptions = [
'title' => 'Fipamo | Create Page',
@ -184,7 +193,7 @@ class DashControl
Session::kill();
header('Location: /dashboard');
exit();
break;
break;
case 'reset-password':
$template = 'dash/reset-password.twig';
$pageOptions = [
@ -195,7 +204,7 @@ class DashControl
$template = 'dash/start.twig';
if (Session::active()) {
$pageOptions = [
'title' => 'Welcome Back',
'title' => 'Start',
'status' => Session::active(),
'data' => (new Book())->getPages(1, 4),
];

@ -7,6 +7,7 @@ use Psr\Http\Message\ServerRequestInterface;
class RouteControl
{
//TODO: Add additional HTTP Methods to better organize API control paths
public function get(
ServerRequestInterface $request,
ResponseInterface $response,
@ -32,14 +33,15 @@ class RouteControl
): ResponseInterface {
switch (isset($args['first']) ? $args['first'] : 'index') {
case 'api':
//$result = APIControl::post($request, $response, $args);
//var_dump($result);
return APIControl::post($request, $response, $args);
break;
default:
//echo "YES";
//return IndexControl::start($request, $response, $args);
break;
$result = [
'message' => "Nothing matches this route. That's unfortunate",
'type' => 'TASK_NONE',
];
$response->getBody()->write(json_encode($result));
return $response->withHeader('Content-Type', 'application/json');
}
}
}

@ -2,9 +2,9 @@
namespace brain\data;
use Carbon\Carbon;
use brain\utility\DocTools;
use brain\utility\StringTools;
use brain\utility\FileUploader;
use function _\find;
use function _\filter;
@ -38,13 +38,8 @@ class Book
public function editPage($task, $request)
{
$content = $this->getContents();
if ($task == 'delete') {
// $parsed = json_decode(file_get_contents("php://input"), true);
// $body = find($content, ["uuid" => $parsed["id"]]);
$body = $request->getParsedBody();
} else {
$body = $request->getParsedBody();
}
$body = json_decode(file_get_contents("php://input"), true);
//$body = find($content, ["uuid" => $parsed["id"]]);
$page = find($content, ['uuid' => $body['uuid']]);
$files = $request->getUploadedFiles();
@ -62,64 +57,6 @@ class Book
$page_feature = '';
$page_files = '';
if (isset($files['page_files'])) {
$imageList = '';
$fileList = '';
// var_dump($files["page_files"] );
foreach ($files['page_files'] as $file) {
$type = $file->getClientMediaType();
switch ($type) {
case 'image/jpeg':
case 'image/png':
case 'image/gif':
case 'image/svg':
$imagesPath = '/assets/images/blog/' . $path . '/';
$imageList = $imageList . $imagesPath . urlencode($file->getClientFileName()) . ', ';
FileUploader::uploadFile(
'../public/assets/images/blog/' . $path . '/',
$file
);
break;
case 'video/mp4':
$videosPath = '/assets/video/blog/' . $path . '/';
$imageList = $imageList . $videosPath . urlencode($file->getClientFileName()) . ', ';
FileUploader::uploadFile(
'../public/assets/video/blog/' . $path . '/',
$file
);
break;
case 'audio/mpeg':
$soundPath = '/assets/sound/blog/' . $path . '/';
$fileList = $fileList . $soundPath . urlencode($file->getClientFileName()) . ', ';
FileUploader::uploadFile(
'../public/assets/sound/blog/' . $path . '/',
$file
);
break;
case 'application/pdf':
case 'text/plain':
case 'text/rtf':
$docPath = '/assets/docs/blog/' . $path . '/';
$fileList = $fileList . $docPath . urlencode($file->getClientFileName()) . ', ';
FileUploader::uploadFile(
'../public/assets/docs/blog/' . $path . '/',
$file
);
break;
}
}
$page_feature = $imageList;
$page_files = $fileList;
} else {
// if no files, just reset string from page object
$page_feature = $page['feature'];
$page_files = $page['files'];
}
if ($task == 'delete') {
$deleted = 'true';
$body['menu'] = 'false';
@ -129,20 +66,18 @@ class Book
$deleted = isset($page['deleted']) ? $page['deleted'] : 'false';
}
$created = $task != 'create'
? new \Moment\Moment($page['rawCreated'])
: new \Moment\Moment();
$updated = new \Moment\Moment();
$created = $task != 'create' ? new Carbon($page['rawCreated']) : Carbon::now();
$updated = Carbon::now();
// grab current index from settings and update
$id = $task != 'create' ? $body['id'] : Settings::getCurrentIndex();
$uuid = $task != 'create' ? $body['uuid'] : StringTools::createUUID();
// now that variables are done, set to body object and then convert to markdown to save
$body['id'] = $id;
$body['uuid'] = $uuid;
$body['feature'] = $page_feature;
$body['files'] = $page_files;
$body['id'] = $id;
$body['uuid'] = $uuid;
//$body['feature'] = $page_feature;
//$body['files'] = $page_files;
$body['path'] = $path;
$body['author'] = $member['handle'];
$body['created'] = $created->format("Y-m-d\TH:i:sP");

@ -108,10 +108,14 @@ class Contents
];
$sanitizer = $builder->build($detergent);
$scrubbed = $sanitizer->sanitize($result->getContent());
if (isset($meta['feature'])) {
$featureList = explode(',', $meta['feature']);
} else {
$featureList = "";
}
$scrubbed = $sanitizer->sanitize($result->getContent());
$featureList = explode(',', $meta['feature']);
$docs = '';
$docs = '';
if (isset($meta['files'])) {
$fileList = explode(',', $meta['files']);
$docs = $meta['files'];
@ -122,19 +126,23 @@ class Contents
$media = [];
$files = [];
foreach ($featureList as $file) {
$item = trim($file);
$ext = pathinfo($item, PATHINFO_EXTENSION);
if ($item != null || $item != '') {
array_push($media, ['file' => $item, 'type' => trim($ext)]);
if ($featureList != '') {
foreach ($featureList as $file) {
$item = trim($file);
$ext = pathinfo($item, PATHINFO_EXTENSION);
if ($item != null || $item != '') {
array_push($media, ['file' => $item, 'type' => trim($ext)]);
}
}
}
foreach ($fileList as $file) {
$item = trim($file);
$ext = pathinfo($item, PATHINFO_EXTENSION);
if ($item != null || $item != '') {
array_push($files, ['file' => $item, 'type' => trim($ext)]);
if ($fileList != "") {
foreach ($fileList as $file) {
$item = trim($file);
$ext = pathinfo($item, PATHINFO_EXTENSION);
if ($item != null || $item != '') {
array_push($files, ['file' => $item, 'type' => trim($ext)]);
}
}
}

@ -2,6 +2,7 @@
namespace brain\data;
use Carbon\Carbon;
use brain\utility\DocTools;
use function _\find;
@ -38,7 +39,7 @@ class Member
}
$found[$key] = $data;
//record time updated
$updated = new \Moment\Moment();
$updated = Carbon::now();
$found['updated'] = $updated->format("Y-m-d\TH:i:sP");
$newFolks = [];
array_push($newFolks, $found);

@ -107,7 +107,7 @@ class Render
}
$template = $layout . '.twig';
if (str_contains($page['layout'], 'index')) {
if (str_contains($layout, 'index')) {
$location = '../public/index.html';
$dir = null;
} else {
@ -136,6 +136,7 @@ class Render
'archives' => $archive,
'info' => $this->pageInfo,
'menu' => $this->menu,
'media' => [['file' => $this->pageInfo['image'], 'type' => trim(pathinfo($this->pageInfo['image'], PATHINFO_EXTENSION))]],
];
$html = $this->twig->render($template, $pageOptions);
@ -154,6 +155,7 @@ class Render
'tag_list' => $item['pages'],
'info' => $this->pageInfo,
'menu' => $this->menu,
'media' => [['file' => $this->pageInfo['image'], 'type' => trim(pathinfo($this->pageInfo['image'], PATHINFO_EXTENSION))]],
];
$html = $this->twig->render($template, $pageOptions);

@ -4,6 +4,7 @@ namespace brain\data;
use brain\utility\DocTools;
use brain\utility\Sorting;
use Carbon\Carbon;
use function _\find;
use function _\pull;
@ -67,8 +68,8 @@ class Settings
$page['deleted']
? ($page['deleted'] = 'true')
: ($page['deleted'] = 'false');
$updated = new \Moment\Moment();
$created = new \Moment\Moment($page['rawCreated']);
$updated = Carbon::now();
$created = new Carbon($page['rawCreated']);
$page['created'] = $created->format("Y-m-d\TH:i:sP");
$page['updated'] = $updated->format("Y-m-d\TH:i:sP");

@ -99,10 +99,10 @@ class DocTools
"'" .
"\n" .
'feature: ' .
$object['feature'] .
$object['imageList'] .
"\n" .
'files: ' .
$object['files'] .
$object['fileList'] .
"\n" .
'path: ' .
$object['path'] .

@ -3,6 +3,7 @@
namespace brain\utility;
use brain\data\Settings;
use Carbon\Carbon;
class Maintenance
{
@ -90,7 +91,7 @@ class Maintenance
$zip->close();
//update settings file with latest back up date
$updated = new \Moment\Moment();
$updated = Carbon::now();
Settings::updateGlobalData(
'last_backup',
$updated->format("Y-m-d\TH:i:sP")

@ -2,6 +2,8 @@
namespace brain\utility;
use Carbon\Carbon;
use function _\find;
class SetUp
@ -33,7 +35,7 @@ class SetUp
$pass = $body['new_member_pass'];
$title = $body['new_member_title'];
$now = new \Moment\Moment();
$now = Carbon::now();
//setup folks config
$hash = password_hash($pass, PASSWORD_DEFAULT);
$newFolks[0]['id'] = 0;
@ -55,8 +57,8 @@ class SetUp
'id' => 1,
'uuid' => StringTools::createUUID(),
'title' => 'FIRST!',
'feature' => '/assets/images/global/default-bg.jpg',
'files' => '',
'imageList' => '/assets/images/global/default-bg.jpg',
'fileList' => '',
'path' => 'content/pages/start',
'layout' => 'index',
'tags' => 'start, welcome',

@ -2,12 +2,13 @@
namespace brain\utility;
use function _\filter;
use function _\find;
use brain\data\Book;
use brain\data\Settings;
use Mni\FrontYAML\Parser;
use function _\filter;
use function _\find;
class Sorting
{
private static $p_tags = [];
@ -16,42 +17,49 @@ class Sorting
public function __construct()
{
}
public static function tags()
{
$pages = (new Book('../content/pages'))->getContents();
foreach ($pages as $page) {
$temp = [];
$temp = explode(',', $page['tags']);
foreach ($temp as $tag) {
$label = trim($tag);
if (!find(self::$p_tags, ['tag_name' => $label])) {
array_push(self::$p_tags, [
'tag_name' => $label,
'slug' => StringTools::safeString($label),
'pages' => self::tagPages($label, $pages),
]);
if (isset($page['tags'])) {
$temp = explode(',', $page['tags']);
foreach ($temp as $tag) {
$label = trim($tag);
if (!find(self::$p_tags, ['tag_name' => $label])) {
array_push(self::$p_tags, [
'tag_name' => $label,
'slug' => StringTools::safeString($label),
'pages' => self::tagPages($label, $pages),
]);
}
}
}
}
return self::$p_tags;
}
private static function tagPages($tag, $pages)
{
$tagged = [];
foreach ($pages as $page) {
if (strpos($page['tags'], $tag) !== false) {
array_push($tagged, [
'title' => $page['title'],
'slug' => $page['slug'],
'path' => $page['path'],
'feature' => $page['feature'],
]);
if (isset($page['tags'])) {
if (strpos($page['tags'], $tag) !== false) {
array_push($tagged, [
'title' => $page['title'],
'slug' => $page['slug'],
'path' => $page['path'],
'feature' => $page['feature'],
]);
}
}
}
return $tagged;
}
public static function archive()
{
$pages = (new Book('../content/pages'))->getContents();
@ -64,10 +72,13 @@ class Sorting
if (!find($years, ['year' => trim($date[0])])) {
$findPages = filter($pages, ['createdYear' => trim($date[0])]);
// var_dump($findPages);
array_push($years, [
'year' => trim($date[0]),
'count' => count($findPages),
]);
array_push(
$years,
[
'year' => trim($date[0]),
'count' => count($findPages),
]
);
}
}
foreach ($years as $year) {
@ -77,12 +88,15 @@ class Sorting
foreach ($filtered as $obj) {
$month = date('m', date($obj['rawCreated']));
if (!find($sorted, ['month' => $month])) {
$perMonth = filter($pages, [
'path' => $year['year'] . '/' . $month,
'deleted' => false,
'published' => true,
'layout' => 'page',
]);
$perMonth = filter(
$pages,
[
'path' => $year['year'] . '/' . $month,
'deleted' => false,
'published' => true,
'layout' => 'page',
]
);
array_push($sorted, [
'month' => $month,
'full_month' => date('F', date($obj['rawCreated'])),
@ -99,6 +113,7 @@ class Sorting
return self::$p_archive;
}
public static function page($page)
{
$config = new Settings();
@ -113,15 +128,16 @@ class Sorting
'image' => $settings['global']['base_url'] . $settings['global']['background'],
'baseURL' => $settings['global']['base_url'],
];
$taglist = explode(',', $page['tags']);
$tags = [];
foreach ($taglist as $tag) {
$label = trim($tag);
array_push($tags, [
'label' => $label . ' ',
'slug' => StringTools::safeString($label),
]);
$tags = [];
if (isset($page['tags'])) {
$taglist = explode(',', $page['tags']);
foreach ($taglist as $tag) {
$label = trim($tag);
array_push($tags, [
'label' => $label . ' ',
'slug' => StringTools::safeString($label),
]);
}
}
$meta = [

@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="theme-color" content="#FFFFFF"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>
{% block title %}
{{ title }}
@ -11,56 +12,38 @@
{% block stylesheets %}{% endblock %}
</head>
<body>
<div id="notifications" class="notifications">
<div id="notify-message" class="notify-message">
<div id="notify-good" class="notify-icon">
<svg viewbox="0 0 20 20" class="icons"><use xlink:href="/assets/images/global/sprite.svg#entypo-emoji-flirt"/></svg>
</div>
<div id="notify-lame" class="notify-icon">
<svg viewbox="0 0 20 20" class="icons"><use xlink:href="/assets/images/global/sprite.svg#entypo-emoji-sad"/></svg>
</div>
<div id="notify-working" class="notify-icon">
<svg id="notify-working-icon" viewbox="0 0 20 20" class="icons"><use xlink:href="/assets/images/global/sprite.svg#entypo-cog"/></svg>
</div>
<div id="notify-text">
<div id="notify-progress"></div>
<p id="message-text">MESSAGE TEXT</p>
</div>
</div>
</div>
<div id="main-content" class="main-container">
<section id="dash-index-content">
{% if status %}
<header id="header">
<div id="wrapper">
{% if status %}
<header>
{% apply spaceless %}
<nav role="top-nav">
<div role="nav-left">
<a href="/dashboard"><img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/></a>
</div>
<div role="title">
<h1>{{ title }}</h1>
</div>
<div role="nav-right">
{% if status %}
{% apply spaceless %}
<div id="left">
<a href="/dashboard"><img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/></a>
</div>
<div id="right">
{% if status %}
{% apply spaceless %}
{{ include("dash/partials/navigation.twig") }}
{% endapply %}
{% endif %}
</div>
{{ include("dash/partials/navigation.twig") }}
{% endapply %}
</div>
</header>
{% endif %}
{% apply spaceless %}
{% block mainContent %}{% endblock %}
{% endif %}
</div>
</nav>
<div role="notify">
{% apply spaceless %}
{{ include("dash/partials/notifications.twig") }}
{% endapply %}
</div>
{% endapply %}
</section>
</div>
{% endif %}
</header>
<main>
{% apply spaceless %}
{% block mainContent %}{% endblock %}
{% endapply %}
</main>
<footer></footer>
{% block javascripts %}{% endblock %}
<script type="module" src="/assets/scripts/dash.js"></script>
</body>
</html>

@ -1,102 +1,80 @@
{% extends "dash/_frame.twig" %}
{% block title %}
{{ title }}
{{ title }}
{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash.css?=dfvgy">
{% endblock %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash/start.css?=vdthg">
{% endblock %}
{% block mainContent %}
<div id="post-index">
<div id="post-index-wrapper">
<div id="post-index-header">
<div id="post-index-header-left">
{{ filter }} Pages
</div>
<div id="post-index-header-right">
<a href="/dashboard/pages/all" title="view all pages">
<button>
<svg >
<use xlink:href="/assets/images/global/sprite.svg#entypo-archive"/>
</svg>
{{ stats['all'] }}
</button>
</a>
<a href="/dashboard/pages/published" title="view publised pages">
<button>
<svg >
<use xlink:href="/assets/images/global/sprite.svg#entypo-globe"/>
</svg>
{{ stats['published'] }}
</button>
</a>
<a href="/dashboard/pages/deleted" title="view deleted pages">
<button>
<svg >
<use xlink:href="/assets/images/global/sprite.svg#entypo-circle-with-cross"/>
</svg>
{{ stats['deleted'] }}
</button>
</a>
</div>
</div>
<div id="posts-list">
{% for page in pages %}
{% if page.media[0].type == 'mp4' %}
<a href="/dashboard/page/edit/{{ page.uuid }}" id="{{ page.uuid }}" class="page-link">
<div class="page-video">
<video class="post-video" loop muted autoplay>
<source src="{{ page.media[0].file }}" type="video/mp4">
{% block mainContent %}
<section role="book-index-header">
<div role="book-index-header-left">
{{ filter }}
Pages
</div>
<div role="book-index-header-right">
<a href="/dashboard/pages/all" title="view all pages">
<button>
<i class="ti ti-clipboard-list"></i>
{{ stats['all'] }}
</button>
</a>
<a href="/dashboard/pages/published" title="view publised pages">
<button>
<i class="ti ti-clipboard-check"></i>
{{ stats['published'] }}
</button>
</a>
<a href="/dashboard/pages/deleted" title="view deleted pages">
<button>
<i class="ti ti-clipboard-off"></i>
{{ stats['deleted'] }}
</button>
</a>
</section>
<section role="book-index-pages">
{% for page in pages %}
{% if page.media[0].type == 'mp4' %}
<a href="/dashboard/page/edit/{{ page.uuid }}" id="{{ page.uuid }}" class="page-link">
<div class="page-video">
<video class="post-video" loop muted autoplay>
<source src="{{ page.media[0].file }}" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
<label>
{{ page.title }}
</label>
<div id="meta">
{{ include("dash/partials/recent-options.twig") }}
</div>
</div>
</a>
{% else %}
<a class="page-link" href="/dashboard/page/edit/{{ page.uuid }}">
<div class="page-bg" style="background: url({{ page.media[0].file }}) no-repeat center center / cover">
<label>
{{ page.title }}
</label>
<div id="meta">
{{ include("dash/partials/recent-options.twig") }}
</div>
</div>
</a>
{% endif %}
{% endfor %}
{% if numOfPages > 1 %}
<div class="paginate">
<a class="page-btns" href="/dashboard/pages/{{ paginate['sort'] }}/{{ paginate['prevPage'] }}">
<svg viewbox="0 0 20 20" class="icons"><use xlink:href="/assets/images/global/sprite.svg#entypo-chevron-left"/></svg>
</a>
<span class="count">
{{ currentPage }}
of
{{ numOfPages }}
</span>
<a class="page-btns" href="/dashboard/pages/{{ paginate['sort'] }}/{{ paginate['nextPage'] }}">
<svg viewbox="0 0 20 20" class="icons"><use xlink:href="/assets/images/global/sprite.svg#entypo-chevron-right"/></svg>
</a>
</div>
{% endif %}
Sorry, your browser doesn't support embedded videos.
</video>
<div id="meta">
{{ include("dash/partials/recent-meta.twig") }}
</div>
</div>
</a>
{% else %}
<a class="page-link" href="/dashboard/page/edit/{{ page.uuid }}">
<div class="page-bg" style="background: url({{ page.media[0].file }}) no-repeat center center / cover #fc6399">
<div id="meta">
{{ include("dash/partials/recent-meta.twig") }}
</div>
</div>
</a>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% endfor %}
{% if numOfPages > 1 %}
<div role="paginate">
<a class="page-btns" href="/dashboard/pages/{{ paginate['sort'] }}/{{ paginate['prevPage'] }}">
<i class="ti ti-square-arrow-left"></i>
</a>
<span class="count">
{{ currentPage }}
of
{{ numOfPages }}
</span>
<a class="page-btns" href="/dashboard/pages/{{ paginate['sort'] }}/{{ paginate['nextPage'] }}">
<i class="ti ti-square-arrow-right"></i>
</a>
</div>
{% endif %}
{% block javascripts %}
<script src="/assets/scripts/Start.js" type="text/javascript"></script>
{% endblock %}
</section>
{% endblock %}

@ -0,0 +1,16 @@
<div>
<a href="/dashboard">
<img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/>
</a>
</div>
<form id="init-restore" method="POST">
<input type="text" name="restore_member_handle" id="restore_member_handle" placeholder="handle"/><input type="password" name="restore_member_pass" id="restore_member_pass" placeholder="password"/>
<div>
<label>Grab your backup zip</label>
<input id="backup-upload" type="file" name="backup-upload" placeholder="Backup Zip"/>
</div>
<br/><br/>
<button id="blog-restore" data-action='blog-restore' type='submit'>RESTORE</button>
<br/><br/>
<button class="init-option" id="init-switch-fresh">OR INSTALL FROM SCRATCH</button>
</form>

@ -0,0 +1,15 @@
<div>
<a href="/dashboard">
<img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/>
</a>
</div>
<form id="init-form" method="POST" onsubmit="return false;">
<input type="text" name="new_member_handle" id="new_member_handle" placeholder="handle"/>
<input type="text" name="new_member_email" id="new_member_email" placeholder="email"/>
<input type="text" name="new_member_pass" id="new_member_pass" placeholder="password"/>
<input type="text" name="new_member_pass2" id="new_member_pass2" placeholder="password confirm"/>
<input type="text" name="new_member_title" id="new_member_title" placeholder="title"/>
<button id="init-blog" data-action='blog-init' type='submit'>SET UP YOUR SITE</button>
<br/><br/>
<button class="init-option" id="init-switch-restore">RESTORE FROM BACKUP</button>
</form>

@ -1,13 +1,13 @@
<div id="dash-login">
<div id="dash-form" class="dash-form">
<img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/>
<form id="login" class='login' name="login" method="POST">
<input type="text" name="handle" class="form-control" placeholder="Handle" required ">
<input type="password" name="password" class="form-control" placeholder="Password" required">
<button id="login-btn" class='login-btn' type='submit'>
ID, PLEASE
</button><br /><br />
<a href="/dashboard/reset-password"> Forgot Password?</a>
</form>
</div>
</div>
<section role="login">
<div>
<img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/>
</div>
<form id="login" class='login' name="login" method="POST" onsubmit="return false;">
<input type="text" name="handle" class="form-control" placeholder="Handle" required/>
<input type="password" name="password" class="form-control" placeholder="Password" required/>
<button id="login-btn" class='login-btn'>
ID, PLEASE
</button>
<a href="/dashboard/reset-password">?</a>
</form>
</section>

@ -0,0 +1,21 @@
<div>
<a href="/dashboard">
<img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/>
</a>
</div>
<form id="reset" class='login' name="reset" action="/dashboard/login" method="POST">
<input type="password" id="new_password" name="new_password" class="form-control" placeholder="New Password" required/>
<input type="password" id="new_password2" name="new_password2" class="form-control" placeholder="New Password Confirm" required">
<input type="password" id="secret" name="secret" class="form-control" placeholder="Account Secret" required/>
<button id="reset-btn" class='login-btn' type='submit'>
RESET PASSWORD
</button><br/>
<p>
Use this to get your secret to verify it's you. If your email is set up, the secret will be sent there. If not, the form will be updated automatically(but please set up your email, once you reset your password).
</p>
<input type="text" id="email" name="email" class="form-control" placeholder="email to verify" required/>
<button id="get-secret-btn" class='login-btn' type='submit'>
VERIFY EMAIL
</button><br/><br/>
</form>

@ -5,44 +5,20 @@
{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash.css?=adfa">
<link rel="stylesheet" type="text/css" href="/assets/css/dash/start.css">
{% endblock %}
{% block mainContent %}
<div id="dash-index">
<div id="dash-index-wrapper">
<div id="dash-init" class="dash-init">
<form id="init-form" method="POST">
<img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/>
<input type="text" name="new_member_handle" id="new_member_handle" placeholder="handle"/>
<input type="text" name="new_member_email" id="new_member_email" placeholder="email"/>
<input type="text" name="new_member_pass" id="new_member_pass" placeholder="password"/>
<input type="text" name="new_member_pass2" id="new_member_pass2" placeholder="password confirm"/>
<input type="text" name="new_member_title" id="new_member_title" placeholder="title"/>
<button id="init-blog" data-action='blog-init' type='submit'>SET UP YOUR SITE</button>
<br /><br />
<button class="init-option" id="init-switch-restore">RESTORE FROM BACKUP</button>
</form>
</div>
<div id="dash-restore" class="dash-restore">
<form id="init-restore" method="POST">
<img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/>
<input type="text" name="restore_member_handle" id="restore_member_handle" placeholder="handle"/><input type="password" name="restore_member_pass" id="restore_member_pass" placeholder="password"/>
<div>
<label>Grab your backup zip</label>
<input id="backup-upload" type="file" name="backup-upload" placeholder="Backup Zip"/>
</div>
<br /><br />
<button id="blog-restore" data-action='blog-restore' type='submit'>RESTORE</button>
<br /><br />
<button class="init-option" id="init-switch-fresh">OR INSTALL FROM SCRATCH</button>
</form>
</div>
</div>
</div>
<article role="site-restore">
<section role="restore-fresh">
{% apply spaceless %}
{{ include("dash/forms/init-fresh.twig") }}
{% endapply %}
</section>
<section role="restore-backup">
{% apply spaceless %}
{{ include("dash/forms/init-backup.twig") }}
{% endapply %}
</section>
</article>
{% endblock %}
{% block javascripts %}
<script src="/assets/scripts/Start.js?=sdfsdf" type="text/javascript"></script>
{% endblock %}

@ -5,39 +5,26 @@
{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash.css?=sdsdsds">
{% endblock %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash/start.css?=sdsdsds">
{% endblock %}
{% block mainContent %}
<div id="nav-index">
<div id="nav-index-wrapper">
<div id="nav-pages">
{% block mainContent %}
<article role="navigation">
<section id="nav-items">
{% for item in menu %}
<div id="{{item.id}}" class="nav-item" data-slug="{{item.slug}}" data-uuid="{{item.uuid}}" data-path="{{item.path}}">
<svg id="item-arrows">
<use xlink:href="/assets/images/global/sprite.svg#entypo-select-arrows"/>
</svg>
<label>{{item.title}}</label>
<div id="{{ item.id }}" class="nav-item" data-slug="{{ item.slug }}" data-uuid="{{ item.uuid }}" data-path="{{ item.path }}">
<i class="ti ti-arrows-move-vertical"></i>
<label>{{ item.title }}</label>
<div id="nav-btns">
<button id="edit-item" class="nav-btn" data-id="{{item.uuid}}" title="edit page">
<svg>
<use xlink:href="/assets/images/global/sprite.svg#entypo-edit"/>
</svg>
<button id="edit-item" class="nav-btn" data-id="{{ item.uuid }}" title="edit page">
<i class="ti ti-edit"></i>
</button>
<button id="remove-item" class="nav-btn" data-uuid="{{item.uuid}}" data-id="{{item.id}}" title="delete from menu">
<svg>
<use xlink:href="/assets/images/global/sprite.svg#entypo-cross"/>
</svg>
<button id="remove-item" class="nav-btn" data-uuid="{{ item.uuid }}" data-id="{{ item.id }}" title="delete from menu">
<i class="ti ti-x"></i>
</button>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %}
{% block javascripts %}
<script src="/assets/scripts/Start.js?=cvfggt" type="text/javascript"></script>
{% endblock %}
</section>
</article>
{% endblock %}

@ -9,7 +9,6 @@
{% set slug = page['slug'] %}
{% set layout = page['layout'] %}
{% set feature = page['feature'] %}
{% set title = page['title'] %}
{% set tags = page['tags'] %}
{% set content = page['content'] %}
{% set date = page['created'] %}
@ -36,163 +35,172 @@
{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash.css?=dfdf">
<link rel="stylesheet" type="text/css" href="/assets/css/dash/start.css?=vdthg">
{% endblock %}
{% block mainContent %}
<div id="post-edit-index" data-index="{{ id }}" data-uuid="{{ uuid }}" data-slug="{{ slug }}" data-layout="{{ layout }}">
<div id="post-edit-index-wrapper">