Compare commits

...

57 Commits
b2.5.0 ... beta

Author SHA1 Message Date
Ro 62e2dea287
Beta 2.6.1 update 1 year 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
1 year ago
Ro 8461d88dd6
Settings CSS Cleanup
The Settings UI needed some responsive polish, so that's been given a
bit more love.
1 year 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
1 year 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.
1 year ago
Ro 181225329a
CSS and icon fixes
Fixes for issues #93, #88, #87
1 year 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.
1 year ago
Ro 8ce253418d
Adding site creation hot fix
Adding the fixes for site install process errors to the branch.
1 year 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.
1 year ago
Ro 8622ba5941
Minor js hotfixes
Updating the script and the html to call the new file name, which is
called 'dash.js'
1 year 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
1 year ago
Ro f9190c2a41
removed carot-pos from dependecies 1 year 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
1 year ago
Ro 8885ae4c63
updated version nunmber 1 year 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
1 year 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.
1 year 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.
1 year ago
Ro 5adf196783
Responsive Part 2 - Login
Updated Login CSS and cleaned up login notifications to alert when
something is amiss.
1 year 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.
1 year 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.
1 year 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.
1 year 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.
1 year 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.
1 year 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.
1 year 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
1 year 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
Are0h 69fc689d38 Finally merging 2.5.1 updates into beta
2.5.1 is ready to go, so it's time to merge into the beta branch and
  test it to make sure everything is ship shape
2 years ago
Are0h a1c0d86580 update versioning, clean up npm
I updated the versioning to match in both composer.json and pacakge.json
config files. I need to start cleaning up the dependencies in the front
end scripting as well, so I started my updating them to see much work
needs to get done. Not too bad, but it's going to be a pretty
significant effort.
2 years ago
Are0h 197fb005de Added Markdown table conversion and version update
Added markdown table conversion to the html process so we get sexy
tables. Also updated the version number to prep for the latest beta
realease. Ha, took a minute but we're back on track
2 years ago
Are0h 00d41a3664 Update composer packages
Needed to update some packages rely on because it's been a minute since
they've been touched. I need to get better with doing that on a regular
basis.
2 years ago
Are0h 3fa3a9e0e6 removed stray console command for #78 updated
just had a stray trace in there that was removed and correct the issue
referene from the last update. oops.
2 years ago
Are0h 8734baf85e Fix for #76, editing upload que
Whew it took a bit but now the upload que on the page edit page is
editable. You can add and remove files as needed without having to add
all your files at once, which us how it previously worked. Still needs
to be tested a bit but the plumbing is up and running.

Also removed a stray php format config as it is no longer needed.
2 years ago
Are0h a31dff94cb Updated PHP lint to @PSR12 b/c @PSR2 is deprecated
So, @PSR12 is the recommended coding standard, so I updated the config
and reformatted the appropriate files.

Again. Whew.
2 years ago
Are0h 63eaba08e2 Added config for PHP formatting (PSR2)
I needed some consistent php formatting, so I plugged in a php fixer
config and then reformatted all PHP files so it's all consistent.

Fixed an ID issue with the page-edit template that was causing page
editing to fail.
2 years ago
Are0h d9c9f7744e Fixed white space issue, page edit style tweaks
There was a white space issue that made text displayed title and tags
text appear missaligned. It turned out to be a small layout problem that
was cleaned up by getting rid of white space in the textarea element
itself.

Also got in there and fixed some lingering css issues that was causing
the svg icon colors to be off in some of the butttons.
2 years ago
Are0h b2c7dae322 Page Editor style fixes
The page editor UI was looking very shaky, so I went in there and
cleaned it up. The nesting is a bit intense, so I'm gonna think about
better ways of laying that out that aren't so convoluted.
2 years ago
Are0h 2501a19685 UI design tweaks
There were some inconsistencies in the UI due to all the changes that
were made to the styleshets, so I fixed the obvious ones that were show
stoppers.

There's more in there to be smoothed out, so this is just the start.
2 years ago
Are0h b8b763637f finished scss stylint refactor for remaining pages
finished up refacting all of the style sheets to be brought in line with
the stylint scss standard. the standard will probably change as i tweak
the rules but the foundation is solid and it's rendering so it's a
great place to start

i'll go through the ui to make sure everything is in tact and i'll make
the necessary adjustments

i should probably refactor some structures as well as they nesting is a
bit overly complicated
2 years ago
Are0h 61b9acb280 I swapped out sass for scss file for styling because scss is a bit more
accessible because of its similarity with base css.

I also plugged in stylint to normalize a css standard throughout the
project to work with prettier, which handles formatting

The structure scss file has been brought in line with the new standard
but the remaining need to be adjusted as well. Those will be added with
an additional commit.
2 years ago
Are0h c546aa7b63 hotfix for editor img uploads, formatting tweaks 2 years ago
Are0h aa3301fb66 hotfix for images in editor uploads 2 years ago
Are0h 9baaed6d50 formatting changes 2 years ago
Are0h 6f2a8cfb4b hot fix for open graph image rendering 2 years ago
Are0h 2e30d6eb26 fix for open graph image render, minor tweaks to class importing 2 years ago
Are0h 7393e4572c readme tweak 2 years ago

16
.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/
@ -44,5 +52,7 @@ config/tags.json
config.codekit3
/config/backups
src/com/
src/styles/
src/com/*
src/styles/*
src/com/ui/TextEditor.js

@ -0,0 +1,74 @@
<?php
$config = new PhpCsFixer\Config();
return $config
->setRiskyAllowed(true)
->setRules([
'@PSR12' => true,
'array_indentation' => true,
'array_syntax' => [
'syntax' => 'short',
],
'combine_consecutive_unsets' => true,
'method_chaining_indentation' => true,
'class_attributes_separation' => [
'elements' => [
'const' => 'none',
'method' => 'one',
'property' => 'none',
'trait_import' => 'none',
],
],
'multiline_whitespace_before_semicolons' => [
'strategy' => 'no_multi_line',
],
'single_quote' => false,
'binary_operator_spaces' => [
'default' => 'single_space',
'operators' => [
'=' => 'align_single_space_minimal',
'=>' => 'align_single_space_minimal',
],
],
'braces' => [
'allow_single_line_closure' => true,
],
'concat_space' => [
'spacing' => 'one',
],
'declare_equal_normalize' => true,
'function_typehint_space' => true,
'single_line_comment_style' => [
'comment_types' => [
'hash',
],
],
'include' => true,
'lowercase_cast' => true,
'no_extra_blank_lines' => [
'tokens' => [
'curly_brace_block',
'extra',
'parenthesis_brace_block',
'throw',
]
],
'no_multiline_whitespace_around_double_arrow' => true,
'no_spaces_around_offset' => true,
'no_unused_imports' => true,
'no_whitespace_before_comma_in_array' => true,
'no_whitespace_in_blank_line' => true,
'object_operator_without_whitespace' => true,
'single_blank_line_before_namespace' => true,
'ternary_operator_spaces' => true,
'trim_array_spaces' => true,
'unary_operator_spaces' => true,
'whitespace_after_comma_in_array' => true,
'single_line_after_imports' => true,
'ordered_imports' => [
'sort_algorithm' => 'none',
],
//Other rules here...
])
->setLineEnding("\n");

@ -1,11 +1,27 @@
{
"overrides": [
{
"files": ".prettierrc",
"options": { "parser": "json" }
},
{
"files": "*.scss",
"options": {
"tabWidth": 4,
"semi": false,
"singleQuote": true,
"printWidth": 90
}
},
{
"files": "*.js",
"options": {
"arrowParens": "avoid",
"bracketSpacing": true,
"htmlWhitespaceSensitivity": "css",
"insertPragma": false,
"bracketSameLine": false,
"jsxSingleQuote": true,
"parser": "babel",
"proseWrap": "preserve",
"requirePragma": false,
"semi": true,
@ -14,4 +30,7 @@
"useTabs": true,
"tabWidth": 4,
"printWidth": 90
}
}
}
]
}

@ -0,0 +1,3 @@
{
"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 write and publish.
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;
}
});

@ -17,14 +17,14 @@ class AuthAPI
//internal check for admin action
if (Auth::status()) {
$result = [
"message" => "Authorized",
"type" => "apiUseAuthorized",
"token" => Session::get("token"),
'message' => 'Authorized',
'type' => 'apiUseAuthorized',
'token' => Session::get('token'),
];
} else {
$result = [
"message" => "Not Authorized",
"type" => "apiUseNotAuthorized",
'message' => 'Not Authorized',
'type' => 'apiUseNotAuthorized',
];
}
return $result;
@ -34,22 +34,22 @@ class AuthAPI
{
$result = [];
switch (Auth::login($body)) {
case "no_name":
case 'no_name':
$result = [
"message" => "Need to see some id, champ",
"type" => "requestLame",
'message' => 'Need to see some id, champ',
'type' => 'requestLame',
];
break;
case "bad_pass":
case 'bad_pass':
$result = [
"message" => "Check your password, sport",
"type" => "requestLame",
'message' => 'Check your password, sport',
'type' => 'requestLame',
];
break;
default:
$result = [
"message" => "Welcome back",
"type" => "requestGood",
'message' => 'Welcome back',
'type' => 'requestGood',
];
break;
}
@ -61,16 +61,18 @@ class AuthAPI
{
Auth::logout($body);
$result = [
"message" => "Till next time, g.",
"type" => "TASK_LOGOUT",
'message' => 'Till next time, g.',
'type' => 'TASK_LOGOUT',
];
return $result;
}
public static function requestSecret($body)
{
$result = Auth::findSecret($body);
return $result;
}
public static function resetPassword($body)
{
$result = Auth::makeNewPassword($body);

@ -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;
}
}

@ -4,6 +4,7 @@ namespace brain\api\v1;
use brain\data\Member;
use brain\data\Settings;
use brain\utility\FileUploader;
class ImagesAPI
{
@ -13,65 +14,62 @@ class ImagesAPI
public static function uploadImage($request, $type = null)
{
$file = $request->getUploadedFiles();
$uploadPath = "";
$path = date("Y") . "/" . date("m");
$response = [];
$file = $request->getUploadedFiles();
$uploadPath = '';
$path = date('Y') . '/' . date('m');
$response = [];
switch ($type) {
case "avatar":
$image = $file["avatar_upload"];
$uploadPath = "../public/assets/images/user/" . $path;
case 'avatar':
$image = $file['avatar_upload'];
$uploadPath = '../public/assets/images/user/' . $path;
break;
case "background":
$image = $file["background_upload"];
$uploadPath = "../public/assets/images/user/" . $path;
case 'background':
$image = $file['background_upload'];
$uploadPath = '../public/assets/images/user/' . $path;
break;
default:
$image = $file["post_image"];
$path = date("Y") . "/" . date("m");
$uploadPath = "../public/assets/images/blog/" . $path;
$image = $file['post_image'];
$path = date('Y') . '/' . date('m');
$uploadPath = '../public/assets/images/blog/' . $path;
break;
}
$result = FileUploader::uploadFile($uploadPath, $image);
switch ($type) {
case "avatar":
case 'avatar':
$response = [
"message" => "Avatar Added. You look great!",
"type" => "avatarUploaded",
"url" =>
"/assets/images/user/" . $path . "/" . $image->getClientFileName(),
'message' => 'Avatar Added. You look great!',
'type' => 'avatarUploaded',
'url' => '/assets/images/user/' . $path . '/' . $image->getClientFileName(),
];
//update member data
Member::updateData(
"avi",
"/assets/images/user/" . $path . "/" . $image->getClientFileName()
'avi',
'/assets/images/user/' . $path . '/' . $image->getClientFileName()
);
break;
case "background":
case 'background':
$response = [
"message" => "Background plugged in. That's nice!",
"type" => "siteBackgroundUploaded",
"url" =>
"/assets/images/user/" . $path . "/" . $image->getClientFileName(),
'message' => "Background plugged in. That's nice!",
'type' => 'siteBackgroundUploaded',
'url' => '/assets/images/user/' . $path . '/' . $image->getClientFileName(),
];
//update settings file
Settings::updateGlobalData(
"background",
"/assets/images/user/" . $path . "/" . $image->getClientFileName()
'background',
'/assets/images/user/' . $path . '/' . $image->getClientFileName()
);
break;
default:
$response = [
"message" => "Image Added. Very slick",
"type" => "postImageAdded",
"url" =>
"/assets/images/blog/" . $path . "/" . $image->getClientFileName(),
'message' => 'Image Added. Very slick',
'type' => 'postImageAdded',
'url' => '/assets/images/blog/' . $path . '/' . $image->getClientFileName(),
];
break;
}

@ -17,13 +17,13 @@ class InitAPI
//through settings.
if (Setup::status()) {
$result = ["type" => "blogInitFail", "message" => "Site already set up"];
$result = ['type' => 'blogInitFail', 'message' => 'Site already set up'];
} else {
switch ($task) {
case "init":
case 'init':
$result = Setup::init($request);
break;
case "restore":
case 'restore':
$result = Setup::restore($request);
break;
}

@ -13,14 +13,15 @@ class MailerAPI
public static function handleMail($request, $body, $response)
{
//if testing, verify session is active
if ($body["mail_task"] == "TESTING") {
// if testing, verify session is active
// add clean method for sending programmtic emails
if ($body['mail_task'] == 'TESTING') {
if (Session::active()) {
$result = Mailer::sendmail($body);
} else {
$result = [
"message" => "You need to be logged in for this, champ.",
"type" => "MAILER_ERROR",
'message' => 'You need to be logged in for this, champ.',
'type' => 'MAILER_ERROR',
];
}
} else {

@ -2,8 +2,6 @@
namespace brain\api\v1;
use Mni\FrontYAML\Parser;
use brain\api\v1\ImagesAPI;
use brain\data\Book;
use brain\data\Settings;
use brain\data\Session;
@ -19,87 +17,87 @@ class PagesAPI
public static function getPageContent($request, $args)
{
$task = $args["fourth"];
$pages = (new Book("../content/pages"))->getContents();
$task = $args['fourth'];
$pages = (new Book('../content/pages'))->getContents();
$content = [];
foreach ($pages as $page) {
$entry = [
"id" => $page["id"],
"uuid" => $page["uuid"],
"title" => $page["title"],
"feature" => $page["feature"],
"path" => $page["path"],
"layout" => $page["layout"],
"tags" => $page["tags"],
"author" => $page["author"],
"created" => $page["created"],
"updated" => $page["updated"],
"deleted" => $page["deleted"],
"menu" => $page["menu"],
"featured" => $page["featured"],
"published" => $page["published"],
"slug" => $page["slug"],
"content" => StringTools::sanitizeContent($page["content"]),
'id' => $page['id'],
'uuid' => $page['uuid'],
'title' => $page['title'],
'feature' => $page['feature'],
'path' => $page['path'],
'layout' => $page['layout'],
'tags' => $page['tags'],
'author' => $page['author'],
'created' => $page['created'],
'updated' => $page['updated'],
'deleted' => $page['deleted'],
'menu' => $page['menu'],
'featured' => $page['featured'],
'published' => $page['published'],
'slug' => $page['slug'],
'content' => StringTools::sanitizeContent($page['content']),
];
array_push($content, $entry);
}
switch ($task) {
case "published":
case 'published':
$published = filter($content, function ($item) {
return $item["published"] == true && $item["deleted"] == false;
return $item['published'] == true && $item['deleted'] == false;
});
$result = ["pages" => $published, "totalItems" => count($published)];
$result = ['pages' => $published, 'totalItems' => count($published)];
break;
case "featured":
case 'featured':
$featured = filter($content, function ($item) {
return $item["featured"] == true && $item["deleted"] == false;
return $item['featured'] == true && $item['deleted'] == false;
});
$result = [
"pages" => $featured,
"totalItems" => count($featured),
'pages' => $featured,
'totalItems' => count($featured),
];
break;
case "menu":
case 'menu':
$menu = filter($content, function ($item) {
return $item["menu"] == true && $item["deleted"] == false;
return $item['menu'] == true && $item['deleted'] == false;
});
$result = ["pages" => $menu, "totalItems" => count($menu)];
$result = ['pages' => $menu, 'totalItems' => count($menu)];
break;
case "single":
$uuid = $args["fifth"];
$page = (new Book("../content/pages"))->findPageById($uuid);
case 'single':
$uuid = $args['fifth'];
$page = (new Book('../content/pages'))->findPageById($uuid);
$entry = [
"id" => $page["id"],
"uuid" => $page["uuid"],
"title" => $page["title"],
"feature" => $page["feature"],
"path" => $page["path"],
"layout" => $page["layout"],
"tags" => $page["tags"],
"author" => $page["author"],
"created" => $page["created"],
"updated" => $page["updated"],
"deleted" => $page["deleted"],
"menu" => $page["menu"],
"featured" => $page["featured"],
"published" => $page["published"],
"slug" => $page["slug"],
"content" => StringTools::sanitizeContent($page["content"]),
'id' => $page['id'],
'uuid' => $page['uuid'],
'title' => $page['title'],
'feature' => $page['feature'],
'path' => $page['path'],
'layout' => $page['layout'],
'tags' => $page['tags'],
'author' => $page['author'],
'created' => $page['created'],
'updated' => $page['updated'],
'deleted' => $page['deleted'],
'menu' => $page['menu'],
'featured' => $page['featured'],
'published' => $page['published'],
'slug' => $page['slug'],
'content' => StringTools::sanitizeContent($page['content']),
];
$result = $entry;
break;
case "tags":
case 'tags':
$result = Settings::getTags();
break;
default:
$result = [
"message" => "Hm, no task. That's unfortunate",
"type" => "TASK_NONE",
'message' => "Hm, no task. That's unfortunate",
'type' => 'TASK_NONE',
];
break;
}
@ -108,41 +106,43 @@ class PagesAPI
public static function handlePageTask($request, $args)
{
$task = $args["fourth"];
$task = $args['fourth'];
switch ($task) {
case "delete":
case "create":
case "write":
$body = $request->getParsedBody();
case 'delete':
case 'create':
case 'write':
$body = json_decode(file_get_contents("php://input"), true);
$passed = true;
if (!isset($body["form_token"])) {
if (!isset($body['form_token'])) {
$result = [
"message" => "No form token. Not good, sport.",
"type" => "TASK_FORM_AUTH",
'message' => 'No form token. Not good, sport.',
'type' => 'TASK_FORM_AUTH',
];
} else {
if ($body["form_token"] == Session::get("form_token")) {
//TODO: Verify form fields
if ($body['form_token'] == Session::get('form_token')) {
$keys = [
"id",
"uuid",
"layout",
"current_title",
"content",
"title",
"created",
"slug",
"tags",
"menu",
"featured",
"published",
"form_token",
"feature_image",
'id',
'uuid',
'layout',
'current_title',
'content',
'title',
'created',
'slug',
'tags',
'menu',
'featured',
'published',
'form_token',
'imageList',
"fileList",
"remote"
];
foreach ($body as $key => $item) {
if (!in_array($key, $keys)) {
//found unnecessary key, so reject submission
var_dump($key);
$passed = false;
}
}
@ -150,27 +150,26 @@ class PagesAPI
$result = (new Book())->editPage($task, $request);
} else {
$result = [
"message" =>
"Unneccessary key found. Post not authorized, slick.",
"type" => "TASK_FORM_AUTH",
'message' => 'Unneccessary key found. Post not authorized, slick.',
'type' => 'TASK_FORM_AUTH',
];
}
} else {
$result = [
"message" => "Form token, auth failed. Uh oh.",
"type" => "TASK_FORM_AUTH",
'message' => 'Form token, auth failed. Uh oh.',
'type' => 'TASK_FORM_AUTH',
];
}
}
break;
case "add-entry-image":
case 'add-entry-image':
$result = ImagesAPI::uploadImage($request);
break;
default:
$result = [
"message" => "Hm, no task. That's unfortunate",
"type" => "TASK_NONE",
'message' => "Hm, no task. That's unfortunate",
'type' => 'TASK_NONE',
];
break;
}

@ -2,8 +2,6 @@
namespace brain\api\v1;
use Slim\Views\Twig;
use brain\api\v1\ImagesApi;
use brain\data\Render;
use brain\data\Settings;
use brain\data\Session;
@ -17,42 +15,43 @@ class SettingsAPI
public static function handleSettingsTask($request, $args, $body = null)
{
$task = $args["fourth"];
$task = $args['fourth'];
switch ($task) {
case "publish":
case 'publish':
//check settings to see if site is a one pager
$config = new Settings();
$settings = $config->getSettings();
$theme = $settings["global"]["theme"];
$config = new Settings();
$settings = $config->getSettings();
$theme = $settings['global']['theme'];
$themeConfig = json_decode(
file_get_contents("../content/themes/" . $theme . "/theme.json"),
file_get_contents('../content/themes/' . $theme . '/theme.json'),
true
);
//check to see if dynamic rendering is active
if (
isset($settings["global"]["dynamicRender"]) &&
$settings["global"]["dynamicRender"] === "true"
isset($settings['global']['dynamicRender']) &&
$settings['global']['dynamicRender'] === 'true'
) {
$result = [
"message" => "Dynamic Render Active! You're good!",
"type" => "RENDER_SUCCESS",
];
'message' => "Dynamic Render Active! You're good!",
'type' => 'RENDER_SUCCESS',
];
} else {
$render = new Render();
if (isset($themeConfig["render"])) {
if (!$themeConfig["render"] || $themeConfig["render"] === "false") {
if (isset($themeConfig['render'])) {
//rendering for one page sites
if (!$themeConfig['render'] || $themeConfig['render'] === 'false') {
$render->renderIndex();
$result = [
"message" => "Index Rendered. HAND CLAPS",
"type" => "RENDER_SUCCESS",
'message' => 'Index Rendered. HAND CLAPS',
'type' => 'RENDER_SUCCESS',
];
} else {
$render->renderTags();
$render->renderArchive();
$render->renderPages();
$result = [
"message" => "Site Rendered. GOOD EFFORT",
"type" => "RENDER_SUCCESS",
'message' => 'Site Rendered. GOOD EFFORT',
'type' => 'RENDER_SUCCESS',
];
}
} else {
@ -61,8 +60,8 @@ class SettingsAPI
$render->renderArchive();
$render->renderPages();
$result = [
"message" => "Site Rendered. GOOD EFFORT",
"type" => "RENDER_SUCCESS",
'message' => 'Site Rendered. GOOD EFFORT',
'type' => 'RENDER_SUCCESS',
];
}
}
@ -71,30 +70,30 @@ class SettingsAPI
//otherwise, render all pages according to theme template files
break;
case "add-avatar":
$result = ImagesAPI::uploadImage($request, "avatar");
case 'add-avatar':
$result = ImagesAPI::uploadImage($request, 'avatar');
break;
case "add-feature-background":
$result = ImagesAPI::uploadImage($request, "background");
case 'add-feature-background':
$result = ImagesAPI::uploadImage($request, 'background');
break;
case "sync":
case 'sync':
Settings::sync($body);
$result = [
"message" => "Settings Synced. You're doing great!",
"type" => "settingsUpdated",
'message' => "Settings Synced. You're doing great!",
'type' => 'settingsUpdated',
];
break;
case "nav-sync":
case 'nav-sync':
Settings::navSync($body);
$result = [
"message" => "Navigation updated. Very slick!",
"type" => "menuUpdated",
'message' => 'Navigation updated. Very slick!',
'type' => 'menuUpdated',
];
break;
default:
$result = [
"message" => "Hm, no task. That's unfortunate",
"type" => "TASK_NONE",
'message' => "Hm, no task. That's unfortunate",
'type' => 'TASK_NONE',
];
break;
}
@ -104,42 +103,42 @@ class SettingsAPI
public static function getInfo($request, $args)
{
$task = $args["fourth"];
$task = $args['fourth'];
switch ($task) {
case "site":
$config = new Settings();
case 'site':
$config = new Settings();
$settings = $config->getSettings();
$data = [
"title" => $settings["global"]["title"],
"base_url" => $settings["global"]["base_url"],
"description" => $settings["global"]["descriptions"],
$data = [
'title' => $settings['global']['title'],
'base_url' => $settings['global']['base_url'],
'description' => $settings['global']['descriptions'],
];
$result = [
"message" => "Settings Found",
"type" => "GET_SETTINGS",
"data" => $data,
'message' => 'Settings Found',
'type' => 'GET_SETTINGS',
'data' => $data,
];
break;
case "member":
case 'member':
if (Session::active()) {
$member = $member = Session::get("member");
$data = ["handle" => $member["handle"], "email" => $member["email"]];
$member = $member = Session::get('member');
$data = ['handle' => $member['handle'], 'email' => $member['email']];
$result = [
"message" => "Member Info Found",
"type" => "GET_MEMBER_INFO",
"data" => $data,
'message' => 'Member Info Found',
'type' => 'GET_MEMBER_INFO',
'data' => $data,
];
} else {
$result = [
"message" => "Not logged in. C'mon, bruh",
"type" => "TASK_NONE",
'message' => "Not logged in. C'mon, bruh",
'type' => 'TASK_NONE',
];
}
break;
default:
$result = [
"message" => "No Settings found. Frowny Face",
"type" => "TASK_NONE",
'message' => 'No Settings found. Frowny Face',
'type' => 'TASK_NONE',
];
break;
}

@ -5,8 +5,8 @@ namespace brain\controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use brain\api\v1\AuthAPI;
use brain\api\v1\ImagesAPI;
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;
@ -20,54 +20,54 @@ class APIControl
ResponseInterface $response,
array $args
): ResponseInterface {
$filename = "";
switch (isset($args["third"]) ? $args["third"] : "none") {
case "status":
$result = AuthAPI::status();
$filename = '';
switch (isset($args['third']) ? $args['third'] : 'none') {
case 'status':
$result = AuthAPI::status();
break;
case "page":
case 'page':
//echo
if (Member::verifyKey($_GET["key"])) {
if (Member::verifyKey($_GET['key'])) {
$result = PagesAPI::getPageContent($request, $args);
} else {
$result = [
"message" => "API access denied, homie",
"type" => "API_ERROR",
'message' => 'API access denied, homie',
'type' => 'API_ERROR',
];
}
break;
case "settings":
$token = $request->getHeader("fipamo-access-token");
case 'settings':
$token = $request->getHeader('fipamo-access-token');
//Verify token to get site info
if (isset($token[0])) {
if (Session::verifyToken($token[0])) {
$result = SettingsAPI::getInfo($request, $args);
} else {
$result = [
"message" => "Invalid token, API access denied, homie",
"type" => "API_ERROR",
'message' => 'Invalid token, API access denied, homie',
'type' => 'API_ERROR',
];
}
} else {
$result = [
"message" => "No token, API access denied, homie",
"type" => "API_ERROR",
'message' => 'No token, API access denied, homie',
'type' => 'API_ERROR',
];
}
break;
case "files":
case 'files':
if (Session::active()) {
if ($args["third"] == "backup") {
$filename = "../config/backups/latest_backup.zip";
if ($args['third'] == 'backup') {
$filename = '../config/backups/latest_backup.zip';
if (file_exists($filename)) {
header("Content-Type: application/zip");
header('Content-Type: application/zip');
header(
'Content-Disposition: attachment; filename="' .
basename($filename) .
'"'
);
header("Content-Length: " . filesize($filename));
header('Content-Length: ' . filesize($filename));
flush();
// return readfile($filename);
@ -78,8 +78,8 @@ class APIControl
}
} else {
$result = [
"message" => "API access denied, homie",
"type" => "API_ERROR",
'message' => 'API access denied, homie',
'type' => 'API_ERROR',
];
}
// no break
@ -89,46 +89,47 @@ class APIControl
$freshResponse = $response;
if ($args["third"] == "files") {
if ($args['third'] == 'files') {
$freshResponse
->getBody()
->write(file_get_contents("../config/backups/latest_back.zip"));
->write(file_get_contents('../config/backups/latest_back.zip'));
$freshResponse->withHeader("Content-Type", "application/zip");
$freshResponse->withHeader('Content-Type', 'application/zip');
return $freshResponse->withAddedHeader(
"Content-Disposition",
"attachment; filename=latest_backup.zip"
'Content-Disposition',
'attachment; filename=latest_backup.zip'
);
} else {
$response->getBody()->write(json_encode($result));
return $response->withHeader("Content-Type", "application/json");
return $response->withHeader('Content-Type', 'application/json');
}
}
public static function post(
ServerRequestInterface $request,
ResponseInterface $response,
array $args
): ResponseInterface {
$contentType = $request->getHeader("Content-Type");
$contentType = $request->getHeader('Content-Type');
switch ($contentType[0]) {
case "application/json":
$body = json_decode(file_get_contents("php://input"), true);
case 'application/json':
$body = json_decode(file_get_contents('php://input'), true);
break;
default:
break;
}
switch (isset($args["third"]) ? $args["third"] : "none") {
case "restore": //move to 'api/auth'
case "init": //move to 'api/auth'
$task = $args["third"];
switch (isset($args['third']) ? $args['third'] : 'none') {
case 'restore': //move to 'api/auth'
case 'init': //move to 'api/auth'
$task = $args['third'];
$result = InitApi::handleInitTasks(
$task,
$task == "init" ? $body : $request
$task == 'init' ? $body : $request
);
break;
case "backup": //move to 'api/auth'
$token = $request->getHeader("fipamo-access-token");
case 'backup': //move to 'api/auth'
$token = $request->getHeader('fipamo-access-token');
//Verify token for admin tasks
$result = SettingsAPI::createBackup();
/*
@ -143,15 +144,15 @@ class APIControl
}
*/
break;
case "login": //move to 'api/auth'
case 'login': //move to 'api/auth'
//check if request is remote and if so, verify token
if ($body["remote"] || $body["remote"] == "true") {
if (Member::verifyKey($body["key"])) {
if ($body['remote'] || $body['remote'] == 'true') {
if (Member::verifyKey($body['key'])) {
$result = AuthAPI::login($body);
} else {
$result = [
"message" => "API access denied, homie",
"type" => "API_ERROR",
'message' => 'API access denied, homie',
'type' => 'API_ERROR',
];
}
} else {
@ -160,46 +161,64 @@ class APIControl
}
break;
case "logout": //move to 'api/auth'
case 'logout': //move to 'api/auth'
$result = AuthAPI::logout($body);
break;
case "get-secret": //move to 'api/auth'
case 'get-secret': //move to 'api/auth'
$result = AuthAPI::requestSecret($body);
break;
case "reset-password": //move to 'api/auth'
case 'reset-password': //move to 'api/auth'
$result = AuthAPI::resetPassword($body);
break;
case "page":
$token = $request->getHeader("fipamo-access-token");
case 'page':
$token = $request->getHeader('fipamo-access-token');
//Verify token for admin tasks
if (isset($token[0])) {
if (Session::verifyToken($token[0])) {
$result = PagesAPI::handlePageTask($request, $args);
} else {
$result = [
"message" => "Invalid token, API access denied, homie",
"type" => "API_ERROR",
'message' => 'Invalid token, API access denied, homie',
'type' => 'API_ERROR',
];
}
} else {
$result = [
"message" => "No token, API access denied, homie",
"type" => "API_ERROR",
'message' => 'No token, API access denied, homie',
'type' => 'API_ERROR',
];
}
break;
case "settings":
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)) {
$postBody = $body;
} else {
$postBody = null;
}
$task = $args["fourth"];
if ($task == "add-feature-background" || $task == "add-avatar") {
$task = $args['fourth'];
if ($task == 'add-feature-background' || $task == 'add-avatar') {
$result = SettingsAPI::handleSettingsTask($request, $args, $postBody);
} else {
$token = $request->getHeader("fipamo-access-token");
$token = $request->getHeader('fipamo-access-token');
if (Session::verifyToken($token[0])) {
$result = SettingsAPI::handleSettingsTask(
$request,
@ -208,25 +227,24 @@ class APIControl
);
} else {
$result = [
"message" => "API access denied, homie",
"type" => "API_ERROR",
'message' => 'API access denied, homie',
'type' => 'API_ERROR',
];
}
}
break;
case "mailer":
case 'mailer':
$result = MailerAPI::handleMail($request, $body, $response);
break;
default:
$result = [
"message" => "Oh, nothing to do. That's unfortunate",
"type" => "TASK_NONE",
'message' => "Oh, nothing to do. That's unfortunate",
'type' => 'TASK_NONE',
];
break;
}
$response->getBody()->write(json_encode($result));
return $response->withHeader("Content-Type", "application/json");
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;
@ -18,44 +20,44 @@ class DashControl
ResponseInterface $response,
array $args
): ResponseInterface {
$view = Twig::fromRequest($request);
$view = Twig::fromRequest($request);
$pageOptions = [];
$template = '';
$template = '';
if (Setup::status()) {
switch (isset($args['second']) ? $args['second'] : 'index') {
case 'settings':
if (Session::active()) {
$config = new Settings();
$settings = $config->getSettings();
$themes = (new Themes())->getThemes(); // $config->getThemes();
$template = 'dash/settings.twig';
$member = Session::get('member');
$form_token = Session::get('form_token');
$updated = new \Moment\Moment($settings['global']['last_backup']);
$config = new Settings();
$settings = $config->getSettings();
$themes = (new Themes())->getThemes(); // $config->getThemes();
$template = 'dash/settings.twig';
$member = Session::get('member');
$form_token = Session::get('form_token');
$updated = new Carbon($settings['global']['last_backup']);
$pageOptions = [
'title' => 'Dash Settings',
'private' => $settings['global']['private'],
'renderOnSave' => $settings['global']['renderOnSave'],
'background' => $settings['global']['background'],
'member' => $member,
'ftoken' => $form_token,
'siteTitle' => $settings['global']['title'],
'baseUrl' => $settings['global']['base_url'],
'desc' => $settings['global']['descriptions'],
'lastBackup' => $updated->format('Y M D d'),
'currentTheme' => $settings['global']['theme'],
'themes' => $themes,
'apiStatus' => isset($settings['global']['externalAPI'])
? $settings['global']['externalAPI']
: 'false',
'dynamicRenderStatus' => isset(
$settings['global']['dynamicRender']
)
? $settings['global']['dynamicRender']
: 'false',
'mailOption' => $settings['email']['active'],
'mailConfig' => $settings['email'],
'status' => Session::active(),
'title' => 'Settings',
'private' => $settings['global']['private'],
'renderOnSave' => $settings['global']['renderOnSave'],
'background' => $settings['global']['background'],
'member' => $member,
'ftoken' => $form_token,
'siteTitle' => $settings['global']['title'],
'baseUrl' => $settings['global']['base_url'],
'desc' => $settings['global']['descriptions'],
'lastBackup' => $updated->format('Y M D d'),
'currentTheme' => $settings['global']['theme'],
'themes' => $themes,
'apiStatus' => isset($settings['global']['externalAPI'])
? $settings['global']['externalAPI']
: 'false',
'dynamicRenderStatus' => isset(
$settings['global']['dynamicRender']
)
? $settings['global']['dynamicRender']
: 'false',
'mailOption' => $settings['email']['active'],
'mailConfig' => $settings['email'],
'status' => Session::active(),
];
} else {
header('Location: /dashboard');
@ -65,13 +67,13 @@ class DashControl
break;
case 'navigation':
if (Session::active()) {
$config = new Settings();
$settings = $config->getSettings();
$template = 'dash/navigation.twig';
$config = new Settings();
$settings = $config->getSettings();
$template = 'dash/navigation.twig';
$pageOptions = [
'title' => 'Edit Dash Navigation',
'status' => Session::active(),
'menu' => $settings['menu'],
'title' => 'Edit Menu',
'status' => Session::active(),
'menu' => $settings['menu'],
];
} else {
header('Location: /dashboard');
@ -81,19 +83,19 @@ class DashControl
case 'pages':
if (Session::active()) {
$currentPage = isset($args['fourth']) ? $args['fourth'] : 1;
$filter = isset($args['third']) ? $args['third'] : 'all';
$data = (new Book())->getPages($currentPage, 4, $filter);
$template = 'dash/book.twig';
$filter = isset($args['third']) ? $args['third'] : 'all';
$data = (new Book())->getPages($currentPage, 4, $filter);
$template = 'dash/book.twig';
$pageOptions = [
'title' => 'Contents',
'entryCount' => $data['entryCount'],
'numOfPages' => $data['numOfPages'],
'currentPage' => $currentPage,
'filter' => $data['paginate']['sort'],
'stats' => $data['stats'],
'pages' => $data['pages'],
'paginate' => $data['paginate'],
'status' => Session::active(),
'title' => 'Contents',
'entryCount' => $data['entryCount'],
'numOfPages' => $data['numOfPages'],
'currentPage' => $currentPage,
'filter' => $data['paginate']['sort'],
'stats' => $data['stats'],
'pages' => $data['pages'],
'paginate' => $data['paginate'],
'status' => Session::active(),
];
} else {
header('Location: /dashboard');
@ -103,24 +105,32 @@ class DashControl
case 'page':
if (Session::active()) {
$template = 'dash/page-edit.twig';
$mode = $args['third'];
$uuid = $args['fourth'];
$mode = $args['third'];
$uuid = $args['fourth'];
switch ($mode) {
case 'edit':
$page = (new Book())->findPageById($uuid);
$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 = [];
$files = [];
foreach ($imageList as $item) {
$image = trim($item);
if (!empty($image)) {
@ -136,43 +146,41 @@ class DashControl
}
$pageOptions = [
'title' => 'Fipamo | Edit Page',
'page' => $page,
'mode' => $mode,
'token' => Session::get('form_token'),
'status' => Session::active(),
'images' => $images,
'files' => $files,
'views' => $views,
'title' => $page['title'],
'page' => $page,
'mode' => $mode,
'token' => Session::get('form_token'),
'status' => Session::active(),
'images' => $images,
'files' => $files,
'views' => $views,
];
break;
case 'preview':
$config = new Settings();
$config = new Settings();
$settings = $config->getSettings();
$loader = new \Twig\Loader\FilesystemLoader(
'../content/themes'
$loader = new \Twig\Loader\FilesystemLoader(
'../content/themes/' . $settings['global']['theme'] .
'/'
);
$display = new \Twig\Environment($loader, []);
$book = new Book();
$page = $book->findPageById($uuid);
$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',
'token' => Session::get('form_token'),
'mode' => $mode,
'status' => Session::active(),
'title' => 'Fipamo | Create Page',
'token' => Session::get('form_token'),
'mode' => $mode,
'status' => Session::active(),
];
break;
}
@ -185,31 +193,31 @@ class DashControl
Session::kill();
header('Location: /dashboard');
exit();
break;
break;
case 'reset-password':
$template = 'dash/reset-password.twig';
$template = 'dash/reset-password.twig';
$pageOptions = [
'title' => 'Reset Password',
'title' => 'Reset Password',
];
break;
default:
$template = 'dash/start.twig';
if (Session::active()) {
$pageOptions = [
'title' => 'Welcome Back',
'status' => Session::active(),
'data' => (new Book())->getPages(1, 4),
'title' => 'Start',
'status' => Session::active(),
'data' => (new Book())->getPages(1, 4),
];
} else {
$pageOptions = [
'title' => 'Welcome to Fipamo',
'status' => Session::active(),
'title' => 'Welcome to Fipamo',
'status' => Session::active(),
];
}
break;
}
} else {
$template = 'dash/init.twig';
$template = 'dash/init.twig';
$pageOptions = ['title' => 'Fipamo Setup'];
}

@ -17,54 +17,53 @@ class IndexControl
ResponseInterface $response,
array $args
): ResponseInterface {
//unset($_SESSION);
$config = new Settings();
//unset($_SESSION);
$config = new Settings();
$settings = $config->getSettings();
$view = Twig::fromRequest($request);
//checks dynamic render flag for site render status
if ($settings["global"]["dynamicRender"]) {
if ($settings["global"]["dynamicRender"] == "true") {
$loader = new \Twig\Loader\FilesystemLoader("../content/themes");
$display = new \Twig\Environment($loader, []);
$template = "";
$view = Twig::fromRequest($request);
//checks dynamic render flag for site render status
if ($settings['global']['dynamicRender']) {
if ($settings['global']['dynamicRender'] == 'true') {
$loader = new \Twig\Loader\FilesystemLoader('../content/themes');
$display = new \Twig\Environment($loader, []);
$template = '';
$pageOptions = [];
$pageInfo = [
"keywords" => isset($settings["global"]["keywords"])
? $settings["global"]["keywords"]
: "fipamo, blog, jamstack, php, markdown, js",
"description" => $settings["global"]["descriptions"],
"image" =>
$settings["global"]["base_url"] . $settings["global"]["background"],
"baseURL" => $settings["global"]["base_url"],
'keywords' => isset($settings['global']['keywords'])
? $settings['global']['keywords']
: 'fipamo, blog, jamstack, php, markdown, js',
'description' => $settings['global']['descriptions'],
'image' => $settings['global']['base_url'] . $settings['global']['background'],
'baseURL' => $settings['global']['base_url'],
];
if (isset($args["first"])) {
switch ($args["first"]) {
case "tags":
$template = $settings["global"]["theme"] . "/tags.twig";
$tag = trim($args["second"]);
$taglist = Sorting::tags();
$item = find($taglist, ["tag_name" => $tag]);
if (isset($args['first'])) {
switch ($args['first']) {
case 'tags':
$template = $settings['global']['theme'] . '/tags.twig';
$tag = trim($args['second']);
$taglist = Sorting::tags();
$item = find($taglist, ['tag_name' => $tag]);
$pageOptions = [
"title" => "Pages Tagged as " . $item["tag_name"],
"background" => $pageInfo["image"],
"tag_list" => $item["pages"],
"info" => $pageInfo,
"menu" => $settings["menu"],
"dynamicRender" => $settings["global"]["dynamicRender"],
'title' => 'Pages Tagged as ' . $item['tag_name'],
'background' => $pageInfo['image'],
'tag_list' => $item['pages'],
'info' => $pageInfo,
'menu' => $settings['menu'],
'dynamicRender' => $settings['global']['dynamicRender'],
];
break;
case "archives":
$archive = Sorting::archive();
$template = $settings["global"]["theme"] . "/archive.twig";
case 'archives':
$archive = Sorting::archive();
$template = $settings['global']['theme'] . '/archive.twig';
$pageOptions = [
"title" => "Archive",
"background" => $pageInfo["image"],
"archives" => $archive,
"info" => $pageInfo,
"menu" => $settings["menu"],
"dynamicRender" => $settings["global"]["dynamicRender"],
'title' => 'Archive',
'background' => $pageInfo['image'],
'archives' => $archive,
'info' => $pageInfo,
'menu' => $settings['menu'],
'dynamicRender' => $settings['global']['dynamicRender'],
];
break;
@ -72,23 +71,21 @@ class IndexControl
//check if page is a menu item, if not render along path as usual
$page = [];
$book = new Book();
if (is_numeric($args["first"])) {
$page = $book->findPageBySlug($args["third"]);
if (is_numeric($args['first'])) {
$page = $book->findPageBySlug($args['third']);
} else {
$page = $book->findPageBySlug($args["first"]);
$page = $book->findPageBySlug($args['first']);
}
$template =
$settings["global"]["theme"] . "/" . $page["layout"] . ".twig";
$template = $settings['global']['theme'] . '/' . $page['layout'] . '.twig';
$pageOptions = Sorting::page($page);
break;
}
} else {
//index
$template =
$settings["global"]["theme"] . "/" . $page["layout"] . ".twig";
$book = new Book("");
$page = $book->findPageBySlug();
//index
$template = $settings['global']['theme'] . '/' . $page['layout'] . '.twig';
$book = new Book('');
$page = $book->findPageBySlug();
$pageOptions = Sorting::page($page);
}
@ -96,16 +93,16 @@ class IndexControl
$response->getBody()->write($html);
return $response;
} else {
//if dynamic flag is false, load up html
//if dynamic flag is false, load up html
$view = Twig::fromRequest($request);
$html = file_get_contents("../public/index.html");
$html = file_get_contents('../public/index.html');
$response->getBody()->write($html);
return $response;
}
} else {
//if flag is not present, default to static html
//if flag is not present, default to static html
$view = Twig::fromRequest($request);
$html = file_get_contents("../public/index.html");
$html = file_get_contents('../public/index.html');
$response->getBody()->write($html);
return $response;
}

@ -4,22 +4,20 @@ namespace brain\controller;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use brain\controller\DashControl;
use brain\controller\APIControl;
use brain\controller\IndexControl;
class RouteControl
{
//TODO: Add additional HTTP Methods to better organize API control paths
public function get(
ServerRequestInterface $request,
ResponseInterface $response,
array $args
): ResponseInterface {
switch (isset($args["first"]) ? $args["first"] : "index") {
case "dashboard":
switch (isset($args['first']) ? $args['first'] : 'index') {
case 'dashboard':
return DashControl::start($request, $response, $args);
break;
case "api":
case 'api':
return APIControl::get($request, $response, $args);
break;
default:
@ -33,16 +31,17 @@ class RouteControl
ResponseInterface $response,
array $args
): ResponseInterface {
switch (isset($args["first"]) ? $args["first"] : "index") {
case "api":
//$result = APIControl::post($request, $response, $args);
//var_dump($result);
switch (isset($args['first']) ? $args['first'] : 'index') {
case 'api':
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');
}
}
}

@ -3,8 +3,6 @@
namespace brain\data;
use ReallySimpleJWT\Token;
use brain\data\Settings;
use brain\data\Session;
use function _\find;
@ -16,7 +14,7 @@ class Auth
public static function sessionStatus()
{
if (isset($_SESSION["member"])) {
if (isset($_SESSION['member'])) {
return true;
} else {
return false;
@ -26,7 +24,7 @@ class Auth
public static function status()
{
$result = "";
$result = '';
if (Session::active()) {
$result = true;
} else {
@ -39,39 +37,39 @@ class Auth
{
//grab member list
$folks = (new Settings())->getFolks();
$found = find($folks, ["handle" => $who["handle"]]);
$found = find($folks, ['handle' => $who['handle']]);
if ($found) {
//name is found, verify password
if (password_verify($who["password"], $found["password"])) {
if (password_verify($who['password'], $found['password'])) {
$member = [
"handle" => $found["handle"],
"email" => $found["email"],
"role" => $found["role"],
"avatar" => $found["avi"],
"key" => $found["key"],
'handle' => $found['handle'],
'email' => $found['email'],
'role' => $found['role'],
'avatar' => $found['avi'],
'key' => $found['key'],
];
$token = Token::create(
$found["key"],
$found["secret"],
$found['key'],
$found['secret'],
time() + 3600,
"localhost"
'localhost'
); //expires in an hour
$form_token = md5(uniqid(microtime(), true));
Session::start();
Session::set("member", $member);
Session::set("token", $token);
Session::set("form_token", $form_token);
Session::set('member', $member);
Session::set('token', $token);
Session::set('form_token', $form_token);
$result = "good_login";
$result = 'good_login';
} else {
$result = "bad_pass";
$result = 'bad_pass';
}
} else {
//if name is not found
$result = "no_name";
$result = 'no_name';
}
return $result;
}
@ -79,40 +77,37 @@ class Auth
public static function findSecret($data)
{
$result = [];
$folks = (new Settings())->getFolks();
$folks = (new Settings())->getFolks();
if (
!empty($data["email"]) &&
filter_var($data["email"], FILTER_VALIDATE_EMAIL)
) {
$found = find($folks, ["email" => $data["email"]]);
if (!empty($data['email']) && filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$found = find($folks, ['email' => $data['email']]);
if ($found) {
//if email is cool, check mail relay status
//if set up, send secret there, if not just return it
$config = new Settings();
$config = new Settings();
$settings = $config->getSettings();
$email = $settings["email"]["active"];
if ($email != "option-none") {
$data["mail_task"] = "SEND_SECRET";
$data["secret"] = $found["secret"];
$result = Mailer::sendmail($data);
$email = $settings['email']['active'];
if ($email != 'option-none') {
$data['mail_task'] = 'SEND_SECRET';
$data['secret'] = $found['secret'];
$result = Mailer::sendmail($data);
} else {
$result = [
"message" => "Valid email, but no email set up!",
"type" => "secretFound",
"secret" => $found["secret"],
'message' => 'Valid email, but no email set up!',
'type' => 'secretFound',
'secret' => $found['secret'],
];
}
} else {
$result = [
"message" => "No valid email, no goodies, pleighboi",
"type" => "secretNotFound",
'message' => 'No valid email, no goodies, pleighboi',
'type' => 'secretNotFound',
];
}
} else {
$result = [
"message" => "Aye, this address is not right, slick.",
"type" => "secretNotFound",
'message' => 'Aye, this address is not right, slick.',
'type' => 'secretNotFound',
];
}
@ -122,30 +117,30 @@ class Auth
public static function makeNewPassword($data)
{
//check if passwordsmatch
if ($data["newPass"] == $data["newPassConfirm"]) {
if ($data['newPass'] == $data['newPassConfirm']) {
//verify secret
$folks = (new Settings())->getFolks();
$found = find($folks, ["secret" => $data["secret"]]);
$found = find($folks, ['secret' => $data['secret']]);
if ($found) {
//create new pass and secret key, then update file
$hash = password_hash($data["newPass"], PASSWORD_DEFAULT);
$hash = password_hash($data['newPass'], PASSWORD_DEFAULT);
$freshSecret = StringTools::randomString(12);
Member::updateData("password", $hash, $data["secret"]);
Member::updateData("secret", $freshSecret, $data["secret"]);
Member::updateData('password', $hash, $data['secret']);
Member::updateData('secret', $freshSecret, $data['secret']);
$result = [
"message" => "Password Updated. Very nice!",
"type" => "passCreated",
'message' => 'Password Updated. Very nice!',
'type' => 'passCreated',
];
} else {
$result = [
"message" => "Secret key is invalid. Try to retrieve it again",
"type" => "passNotCreated",
'message' => 'Secret key is invalid. Try to retrieve it again',
'type' => 'passNotCreated',
];
}
} else {
$result = [
"message" => "Passwords don't match. Try it again.",
"type" => "passNotCreated",
'message' => "Passwords don't match. Try it again.",
'type' => 'passNotCreated',
];
}

@ -2,12 +2,13 @@
namespace brain\data;
use function _\filter;
use function _\find;
use Carbon\Carbon;
use brain\utility\DocTools;
use brain\utility\FileUploader;
use brain\utility\StringTools;
use function _\find;
use function _\filter;
class Book
{
public function __construct()
@ -17,7 +18,7 @@ class Book
public function findPageById(string $uuid)
{
$content = $this->getContents();
$page = find($content, ['uuid' => $uuid]);
$page = find($content, ['uuid' => $uuid]);
return $page;
}
@ -37,117 +38,48 @@ 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']]);
$page = find($content, ['uuid' => $body['uuid']]);
$files = $request->getUploadedFiles();
$member = Session::get('member');
if ($task != 'create') {
$path =
date('Y', date($page['rawCreated'])).
'/'.
$path = date('Y', date($page['rawCreated'])) .
'/' .
date('m', date($page['rawCreated']));
} else {
$path = date('Y').'/'.date('m');
$path = date('Y') . '/' . date('m');
}
$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'];
}
$page_files = '';
if ($task == 'delete') {
$deleted = 'true';
$body['menu'] = 'false';
$deleted = 'true';
$body['menu'] = 'false';
$body['published'] = 'false';
$body['featured'] = 'false';
$body['featured'] = 'false';
} else {
$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();
$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['id'] = $id;
$body['uuid'] = $uuid;
$body['feature'] = $page_feature;
$body['files'] = $page_files;
$body['path'] = $path;
$body['author'] = $member['handle'];
//$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");
$body['updated'] = $updated->format("Y-m-d\TH:i:sP");
$body['deleted'] = $deleted;
@ -159,15 +91,15 @@ class Book
if ($body['layout'] == 'index') {
$writePath = '../content/pages/start/index.md';
} else {
$writePath = '../content/pages/'.$path.'/'.$body['slug'].'.md';
$writePath = '../content/pages/' . $path . '/' . $body['slug'] . '.md';
}
$status = DocTools::writePages($task, $path, $writePath, $write);
if ($status) {
$config = new Settings();
$config = new Settings();
$settings = $config->getSettings();
$message = '';
$message = '';
if (
$settings['global']['renderOnSave'] == 'true' &&
@ -183,9 +115,9 @@ class Book
}
$response = [
'message' => $message,
'type' => $task == 'write' ? 'postUpdated' : 'postAdded',
'id' => $uuid,
'message' => $message,
'type' => $task == 'write' ? 'postUpdated' : 'postAdded',
'id' => $uuid,
];
// TODO: When form submission is successful, make new form token
@ -203,9 +135,9 @@ class Book
}
} else {
$response = [
'message' => "Uh oh. File save problem. Don't panic",
'type' => 'postError',
'id' => $uuid,
'message' => "Uh oh. File save problem. Don't panic",
'type' => 'postError',
'id' => $uuid,
];
}
@ -240,7 +172,7 @@ class Book
break;
}
$numOfPages = ceil(count($filtered) / ($limit + 1));
$folder = [];
$folder = [];
if (count($filtered) != 0) {
if (count($filtered) < $limit) {
@ -271,19 +203,19 @@ class Book
}
return [
'pages' => $folder,
'numOfPages' => $numOfPages,
'entryCount' => count($filtered),
'paginate' => [
'sort' => $sort,
'nextPage' => $next,
'prevPage' => $prev,
],
'stats' => [
'all' => count($all),
'published' => count($published),
'deleted' => count($deleted),
],
'pages' => $folder,
'numOfPages' => $numOfPages,
'entryCount' => count($filtered),
'paginate' => [
'sort' => $sort,
'nextPage' => $next,
'prevPage' => $prev,
],
'stats' => [
'all' => count($all),
'published' => count($published),
'deleted' => count($deleted),
],
];
}

@ -2,29 +2,31 @@
namespace brain\data;
use HtmlSanitizer\SanitizerBuilder;
use League\CommonMark\MarkdownConverter;
use League\CommonMark\Environment\Environment;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
use HtmlSanitizer\Extension\Basic\BasicExtension;
use HtmlSanitizer\Extension\Listing\ListExtension;
use HtmlSanitizer\Extension\Iframe\IframeExtension;
use League\CommonMark\Extension\Table\TableExtension;
use League\CommonMark\Extension\Attributes\AttributesExtension;
use League\CommonMark\Extension\FrontMatter\FrontMatterExtension;
use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
use League\CommonMark\Extension\Strikethrough\StrikethroughExtension;
use League\CommonMark\Extension\FrontMatter\Output\RenderedContentWithFrontMatter;
use League\CommonMark\MarkdownConverter;
use League\CommonMark\CommonMarkConverter;
use HtmlSanitizer\Extension\Basic\BasicExtension;
use HtmlSanitizer\Extension\Iframe\IframeExtension;
use HtmlSanitizer\Extension\Listing\ListExtension;
use HtmlSanitizer\SanitizerBuilder;
use function _\orderBy;
class Contents
{
public $files = [];
public $files = [];
public $config = [];
public function __construct($folder)
{
$this->read($folder);
}
public function read($folder)
{
$folders = glob("$folder/*", GLOB_ONLYDIR);
@ -32,7 +34,7 @@ class Contents
//$this->files[] = $folder . "/";
$this->read($folder);
}
$files = array_filter(glob("$folder/*md"), "is_file");
$files = array_filter(glob("$folder/*md"), 'is_file');
foreach ($files as $file) {
$this->files[] = $file;
}
@ -52,6 +54,9 @@ class Contents
//add attributes to elements in markdown
$environment->addExtension(new AttributesExtension());
//add table rendering
$environment->addExtension(new TableExtension());
// Instantiate the converter engine and start converting some Markdown!
$converter = new MarkdownConverter($environment);
@ -59,16 +64,16 @@ class Contents
foreach ($this->files as $file) {
//get meta and html from file
$result = $converter->convertToHtml(file_get_contents($file));
$meta = [];
$meta = [];
if ($result instanceof RenderedContentWithFrontMatter) {
$meta = $result->getFrontMatter();
}
//get raw markdown from file
$frontMatterExtension = new FrontMatterExtension();
$parsed = $frontMatterExtension
->getFrontMatterParser()
->parse(file_get_contents($file));
$parsed = $frontMatterExtension
->getFrontMatterParser()
->parse(file_get_contents($file));
//never trust the front end. clean it up
//add what sanitizer extensions we need manually
@ -76,6 +81,8 @@ class Contents
$builder->registerExtension(new BasicExtension());
$builder->registerExtension(new IframeExtension());
$builder->registerExtension(new ListExtension());
//just add it straight because classname is already in use
$builder->registerExtension(new \HtmlSanitizer\Extension\Table\TableExtension());
//relative-a and relative-image
$builder->registerExtension(
@ -86,84 +93,92 @@ class Contents
);
$detergent = [
"extensions" => ["basic", "list","relative-a", "relative-image", "iframe"],
"tags" => [
"div" => [
"allowed_attributes" => ["class", "title", "id", "style"],
],
"img" => [
"allowed_attributes" => ["src", "alt", "title", "class"],
],
"iframe" => [
"allowed_attributes" => ["height", "width", "title", "src"],
],
],
'extensions' => ['basic', 'list', 'relative-a', 'relative-image', 'iframe', 'table'],
'tags' => [
'div' => [
'allowed_attributes' => ['class', 'title', 'id', 'style'],
],
'img' => [
'allowed_attributes' => ['src', 'alt', 'title', 'class'],
],
'iframe' => [
'allowed_attributes' => ['height', 'width', 'title', 'src'],
],
],
];
$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 = '';
if (isset($meta["files"])) {
$fileList = explode(",", $meta["files"]);
$docs = $meta["files"];
if (isset($meta['files'])) {
$fileList = explode(',', $meta['files']);
$docs = $meta['files'];
} else {
$fileList = [];
$docs = '';
$docs = '';
}
$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)]);
}
}
}
//sort attributes into page object
$page = [
"id" => $meta["id"],
"uuid" => $meta["uuid"],
"title" => $meta["title"],
"feature" => $meta["feature"],
"files" => $docs,
"path" => $meta["path"],
"layout" => $meta["layout"],
"tags" => $meta["tags"],
"author" => $meta["author"],
"created" => date("Y M D d", $meta["created"]),
"updated" => date("Y M D d", $meta["updated"]),
"rawCreated" => $meta["created"],
"rawUpdated" => $meta["updated"],
"createdYear" => date("Y", $meta["created"]),
"createdMonth" => date("m", $meta["created"]),
"deleted" => $meta["deleted"],
"menu" => $meta["menu"],
"featured" => $meta["featured"],
"published" => $meta["published"],
"slug" => $meta["slug"],
"filePath" => $file,
"content" => $parsed->getContent(),
"html" => $scrubbed,
"media" => $media,
"docs" => $files
'id' => $meta['id'],
'uuid' => $meta['uuid'],
'title' => $meta['title'],
'feature' => $meta['feature'],
'files' => $docs,
'path' => $meta['path'],
'layout' => $meta['layout'],
'tags' => $meta['tags'],
'author' => $meta['author'],
'created' => date('Y M D d', $meta['created']),
'updated' => date('Y M D d', $meta['updated']),
'rawCreated' => $meta['created'],
'rawUpdated' => $meta['updated'],
'createdYear' => date('Y', $meta['created']),
'createdMonth' => date('m', $meta['created']),
'deleted' => $meta['deleted'],
'menu' => $meta['menu'],
'featured' => $meta['featured'],
'published' => $meta['published'],
'slug' => $meta['slug'],
'filePath' => $file,
'content' => $parsed->getContent(),
'html' => $scrubbed,
'media' => $media,
'docs' => $files
];
//checks for duplicates
$uuid = $meta["uuid"];
$uuid = $meta['uuid'];
$found = current(
array_filter($contents, function ($item) use ($uuid) {
return isset($item["uuid"]) && $uuid == $item["uuid"];
return isset($item['uuid']) && $uuid == $item['uuid'];
})
);
@ -172,7 +187,7 @@ class Contents
array_push($contents, $page);
}
}
$contents = orderBy($contents, ["id"], ["desc"]);
$contents = orderBy($contents, ['id'], ['desc']);
return $contents;
}
}

@ -2,8 +2,7 @@
namespace brain\data;
use brain\data\Settings;
use brain\data\Session;
use Carbon\Carbon;
use brain\utility\DocTools;
use function _\find;
@ -18,7 +17,7 @@ class Member
{
if (isset($key)) {
$folks = (new Settings())->getFolks();
$found = find($folks, ["key" => $key]);
$found = find($folks, ['key' => $key]);
if ($found) {
return true;
} else {
@ -33,30 +32,30 @@ class Member
{
$folks = (new Settings())->getFolks();
if (isset($secret)) {
$found = find($folks, ["secret" => $secret]);
$found = find($folks, ['secret' => $secret]);
} else {
$member = Session::get("member");
$found = find($folks, ["handle" => $member["handle"]]);
$member = Session::get('member');
$found = find($folks, ['handle' => $member['handle']]);
}
$found[$key] = $data;
//record time updated
$updated = new \Moment\Moment();
$found["updated"] = $updated->format("Y-m-d\TH:i:sP");
$newFolks = [];
$updated = Carbon::now();
$found['updated'] = $updated->format("Y-m-d\TH:i:sP");
$newFolks = [];
array_push($newFolks, $found);
//save updated file
DocTools::writeSettings("../config/folks.json", $newFolks);
DocTools::writeSettings('../config/folks.json', $newFolks);
//update member data in session
if (!isset($secret)) {
$member = [
"handle" => $found["handle"],
"email" => $found["email"],
"role" => $found["role"],
"avatar" => $found["avi"],
"key" => $found["key"],
'handle' => $found['handle'],
'email' => $found['email'],
'role' => $found['role'],
'avatar' => $found['avi'],
'key' => $found['key'],
];
Session::set("member", $member);
Session::set('member', $member);
}
}
}

@ -2,9 +2,6 @@
namespace brain\data;
use Mni\FrontYAML\Parser;
use brain\data\Settings;
use brain\data\Book;
use brain\utility\Sorting;
use brain\utility\DocTools;
@ -17,38 +14,38 @@ class Render
public $pageInfo;
public $menu;
public $background;
public function __construct()
{
$config = new Settings();
//TODO: Add theme folder to loader
$settings = $config->getSettings();
$this->menu = $settings["menu"];
$this->theme = $settings["global"]["theme"];
$this->loader = new \Twig\Loader\FilesystemLoader("../content/themes/" . $this->theme);
$this->twig = new \Twig\Environment($this->loader, []);
$settings = $config->getSettings();
$this->menu = $settings['menu'];
$this->theme = $settings['global']['theme'];
$this->loader = new \Twig\Loader\FilesystemLoader('../content/themes/' . $this->theme);
$this->twig = new \Twig\Environment($this->loader, []);
$this->pageInfo = [
"keywords" => isset($settings["global"]["keywords"])
? $settings["global"]["keywords"]
: "fipamo, blog, jamstack, php, markdown, js",
"description" => $settings["global"]["descriptions"],
"image" =>
$settings["global"]["base_url"] . $settings["global"]["background"],
"baseURL" => $settings["global"]["base_url"],
'keywords' => isset($settings['global']['keywords'])
? $settings['global']['keywords']
: 'fipamo, blog, jamstack, php, markdown, js',
'description' => $settings['global']['descriptions'],
'image' => $settings['global']['base_url'] . $settings['global']['background'],
'baseURL' => $settings['global']['base_url'],
];
//move global theme image assets to public folder
foreach (
new \DirectoryIterator("../content/themes/" . $this->theme . "/assets/images/global/") as $file
new \DirectoryIterator('../content/themes/' . $this->theme . '/assets/images/global/') as $file
) {
if ($file->isDot()) {
continue;
}
if (!is_file("../public/assets/images/global/" . $file->getFileName())) {
if (!is_file('../public/assets/images/global/' . $file->getFileName())) {
copy(
"../content/themes/" .
'../content/themes/' .
$this->theme .
"/assets/images/global/" .
'/assets/images/global/' .
$file->getFileName(),
"../public/assets/images/global/" . $file->getFileName()
'../public/assets/images/global/' . $file->getFileName()
);
} else {
//image is already there, so chill
@ -72,56 +69,55 @@ class Render
$scripts = glob('../public/assets/scripts/*'); // get all file names
foreach ($scripts as $file) { // iterate files
if (is_file($file)) {
if (!$file == "../public/assets/scripts/Start.js") {
if (!$file == '../public/assets/scripts/Start.js') {
unlink($file); // delete file
}
}
}
//copy theme assets to public
$newcss = glob("../content/themes/" . $this->theme . "/assets/css/*");
$newcss = glob('../content/themes/' . $this->theme . '/assets/css/*');
foreach ($newcss as $file) { // iterate files
if (is_file($file)) {
$path = explode("/", $file);
copy($file, "../public/assets/css/" . $path[6]);
$path = explode('/', $file);
copy($file, '../public/assets/css/' . $path[6]);
}
}
$newjs = glob("../content/themes/" . $this->theme . "/assets/scripts/*");
$newjs = glob('../content/themes/' . $this->theme . '/assets/scripts/*');
foreach ($newjs as $file) { // iterate files
if (is_file($file)) {
$path = explode("/", $file);
copy($file, "../public/assets/scripts/" . $path[6]);
$path = explode('/', $file);
copy($file, '../public/assets/scripts/' . $path[6]);
}
}
}
public function renderPages()
{
$pages = (new Book())->getContents();
$recent = [];
$pages = (new Book())->getContents();
$recent = [];
$featured = [];
$limit = 4;
$limit = 4;
foreach ($pages as $page) {
$pageOptions = Sorting::page($page);
$layout = $page["layout"];
$layout = $page['layout'];
//new pages have no layout, so defautl for now
if ($layout == "" || $layout == null) {
$layout = "page";
if ($layout == '' || $layout == null) {
$layout = 'page';
}
$template = $layout . ".twig";
if (str_contains($page["layout"], "index")) {
$location = "../public/index.html";
$dir = null;
$template = $layout . '.twig';
if (str_contains($layout, 'index')) {
$location = '../public/index.html';
$dir = null;
} else {
// if page is a menu item, render the page on public root
if ($page["menu"] == "true") {
$location = "../public/" . $page["slug"] . ".html";
$dir = "../public/";
if ($page['menu'] == 'true') {
$location = '../public/' . $page['slug'] . '.html';
$dir = '../public/';
} else {
$location =
"../public/" . $page["path"] . "/" . $page["slug"] . ".html";
$dir = "../public/" . $page["path"];
$location = '../public/' . $page['path'] . '/' . $page['slug'] . '.html';
$dir = '../public/' . $page['path'];
}
}
@ -132,18 +128,19 @@ class Render
public function renderArchive()
{
$archive = Sorting::archive();
$template = "archive.twig";
$archive = Sorting::archive();
$template = 'archive.twig';
$pageOptions = [
"title" => "Archive",
"background" => $this->pageInfo["image"],
"archives" => $archive,
"info" => $this->pageInfo,
"menu" => $this->menu,
'title' => 'Archive',
'background' => $this->pageInfo['image'],
'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);
$location = "../public/archives.html";
$html = $this->twig->render($template, $pageOptions);
$location = '../public/archives.html';
DocTools::writeHTML($location, $html);
}
@ -151,29 +148,30 @@ class Render
{
$list = Sorting::tags();
foreach ($list as $item) {
$template = "tags.twig";
$template = 'tags.twig';
$pageOptions = [
"title" => "Pages Tagged as " . $item["tag_name"],
"background" => $this->pageInfo["image"],
"tag_list" => $item["pages"],
"info" => $this->pageInfo,
"menu" => $this->menu,
'title' => 'Pages Tagged as ' . $item['tag_name'],
'background' => $this->pageInfo['image'],
'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);
$location = "../public/tags/" . $item["slug"] . ".html";
$location = '../public/tags/' . $item['slug'] . '.html';
//if tags folder doesn't exist, make it
if (!is_dir("../public/tags")) {
mkdir("../public/tags", 0755, true);
if (!is_dir('../public/tags')) {
mkdir('../public/tags', 0755, true);
} else {
}
if (!is_file($location)) {
file_put_contents($location, $html);
} else {
($new = fopen($location, "w")) or die("Unable to open file!");
($new = fopen($location, 'w')) or die('Unable to open file!');
fwrite($new, $html);
fclose($new);
}
@ -183,21 +181,21 @@ class Render
public function renderIndex()
{
//TODO: Need to fix this to account for new index templating system
$pages = (new Book())->getContents();
$index = find($pages, ["layout" => "index"]);
$template = "index.twig";
$location = "../public/index.html";
$dir = null;
$pages = (new Book())->getContents();
$index = find($pages, ['layout' => 'index']);
$template = 'index.twig';
$location = '../public/index.html';
$dir = null;
$meta = [
"who" => $index["author"],
"when" => $index["created"],
'who' => $index['author'],
'when' => $index['created'],
];
$pageOptions = [
"title" => $index["title"],
"background" => $index["feature"],
"meta" => $meta,
'title' => $index['title'],
'background' => $index['feature'],
'meta' => $meta,
];
$html = $this->twig->render($template, $pageOptions);

@ -4,22 +4,21 @@ namespace brain\data;
use ReallySimpleJWT\Token;
use function _\find;
class Session
{
private static $file = "../content/.session";
private static $file = '../content/.session';
private static $data = [
"member" => "",
"token" => "",
"form_token" => "",
'member' => '',
'token' => '',
'form_token' => '',
];
public static function start()
{
if (!is_file(self::$file)) {
file_put_contents(self::$file, json_encode(self::$data));
} else {
($new = fopen(self::$file, "w")) or die("Unable to open file!");
($new = fopen(self::$file, 'w')) or die('Unable to open file!');
fwrite($new, json_encode(self::$data));
fclose($new);
}
@ -31,14 +30,14 @@ class Session
return false;
} else {
$data = json_decode(file_get_contents(self::$file), true);
if ($data["member"] != null) {
$secret = (new Settings())->getFolks("secret");
if ($data['member'] != null) {
$secret = (new Settings())->getFolks('secret');
if ($secret == null) {
return false;
} else {
if (
Token::validate($data["token"], $secret) &&
Token::validateExpiration($data["token"], $secret)
Token::validate($data['token'], $secret) &&
Token::validateExpiration($data['token'], $secret)
) {
return true;
} else {
@ -54,8 +53,8 @@ class Session
public static function verifyToken($token)
{
$data = json_decode(file_get_contents(self::$file), true);
if ($data["member"] != null) {
$secret = (new Settings())->getFolks("secret");
if ($data['member'] != null) {
$secret = (new Settings())->getFolks('secret');
if (
Token::validate($token, $secret) &&
Token::validateExpiration($token, $secret)
@ -71,9 +70,9 @@ class Session
public static function set($key, $value)
{
$data = json_decode(file_get_contents(self::$file), true);
$data = json_decode(file_get_contents(self::$file), true);
$data[$key] = $value;
($fresh = fopen(self::$file, "w")) or die("Unable to open file!");
($fresh = fopen(self::$file, 'w')) or die('Unable to open file!');
fwrite($fresh, json_encode($data));
fclose($fresh);
}
@ -87,7 +86,7 @@ class Session
public static function kill()
{
($fresh = fopen(self::$file, "w")) or die("Unable to open file!");
($fresh = fopen(self::$file, 'w')) or die('Unable to open file!');
fwrite($fresh, json_encode(self::$data));
fclose($fresh);
}

@ -2,9 +2,9 @@
namespace brain\data;
use brain\data\Member;
use brain\utility\DocTools;
use brain\utility\Sorting;
use Carbon\Carbon;
use function _\find;
use function _\pull;
@ -19,92 +19,91 @@ class Settings
public function __construct()
{
//gets all settings files and converts to php objects
$this->folks = json_decode(file_get_contents("../config/folks.json"), true);
self::$tags = json_decode(file_get_contents("../config/tags.json"), true);
$this->folks = json_decode(file_get_contents('../config/folks.json'), true);
self::$tags = json_decode(file_get_contents('../config/tags.json'), true);
self::$settings = json_decode(
file_get_contents("../config/settings.json"),
file_get_contents('../config/settings.json'),
true
);
}
public static function sync($data)
{
$settings = self::$settings;
$settings["global"]["base_url"] = $data["global"]["base_url"];
$settings["global"]["title"] = $data["global"]["title"];
$settings["global"]["descriptions"] = $data["global"]["descriptions"];
$settings["global"]["base_url"] = $data["global"]["base_url"];
$settings["global"]["private"] = $data["global"]["private"];
$settings["global"]["renderOnSave"] = $data["global"]["renderOnSave"];
$settings["global"]["theme"] = $data["global"]["theme"];
$settings["global"]["externalAPI"] = $data["global"]["externalAPI"];
$settings["global"]["dynamicRender"] = $data["global"]["dynamicRender"];
Member::updateData("handle", $data["member"]["handle"]);
Member::updateData("email", $data["member"]["email"]);
$settings["email"]["active"] = $data["email"]["active"];
$settings["email"]["smtp"] = $data["email"]["smtp"];
$settings["email"]["mailgun"] = $data["email"]["mailgun"];
DocTools::writeSettings("../config/settings.json", $settings);
$settings = self::$settings;
$settings['global']['base_url'] = $data['global']['base_url'];
$settings['global']['title'] = $data['global']['title'];
$settings['global']['descriptions'] = $data['global']['descriptions'];
$settings['global']['base_url'] = $data['global']['base_url'];
$settings['global']['private'] = $data['global']['private'];
$settings['global']['renderOnSave'] = $data['global']['renderOnSave'];
$settings['global']['theme'] = $data['global']['theme'];
$settings['global']['externalAPI'] = $data['global']['externalAPI'];
$settings['global']['dynamicRender'] = $data['global']['dynamicRender'];
Member::updateData('handle', $data['member']['handle']);
Member::updateData('email', $data['member']['email']);
$settings['email']['active'] = $data['email']['active'];
$settings['email']['smtp'] = $data['email']['smtp'];
$settings['email']['mailgun'] = $data['email']['mailgun'];
DocTools::writeSettings('../config/settings.json', $settings);
}
public static function navSync($data)
{
$settings = self::$settings;
$remove = $data["remove"];
$remove = $data['remove'];
//if remove contains id, find nav item page and set menu to false
if ($remove != null || $remove != "") {
$page = (new Book("../content/pages"))->findPageById($remove);
$page["menu"] = "false";
$page["published"]
? ($page["published"] = "true")
: ($page["published"] = "false");
$page["featured"]
? ($page["featured"] = "true")
: ($page["featured"] = "false");
$page["deleted"]
? ($page["deleted"] = "true")
: ($page["deleted"] = "false");
$updated = new \Moment\Moment();
$created = new \Moment\Moment($page["rawCreated"]);
$page["created"] = $created->format("Y-m-d\TH:i:sP");
$page["updated"] = $updated->format("Y-m-d\TH:i:sP");
if ($remove != null || $remove != '') {
$page = (new Book('../content/pages'))->findPageById($remove);
$page['menu'] = 'false';
$page['published']
? ($page['published'] = 'true')
: ($page['published'] = 'false');
$page['featured']
? ($page['featured'] = 'true')
: ($page['featured'] = 'false');
$page['deleted']
? ($page['deleted'] = 'true')
: ($page['deleted'] = 'false');
$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");
$md = DocTools::objectToMD($page);
if ($page["layout"] == "index") {
$writePath = "../content/pages/start/index.md";
if ($page['layout'] == 'index') {
$writePath = '../content/pages/start/index.md';
} else {
$writePath =
"../content/pages/" . $page["path"] . "/" . $page["slug"] . ".md";
$writePath = '../content/pages/' . $page['path'] . '/' . $page['slug'] . '.md';
}
DocTools::writePages("write", $page["path"], $writePath, $md);
DocTools::writePages('write', $page['path'], $writePath, $md);
}
$settings["menu"] = [];
$items = $data["menu"];
$settings['menu'] = [];
$items = $data['menu'];
foreach ($items as $item) {
array_push($settings["menu"], [
"title" => $item["title"],
"id" => $item["id"],
"uuid" => $item["uuid"],
"slug" => $item["slug"],
"path" => $item["path"],
array_push($settings['menu'], [
'title' => $item['title'],
'id' => $item['id'],
'uuid' => $item['uuid'],
'slug' => $item['slug'],
'path' => $item['path'],
]);
}
DocTools::writeSettings("../config/settings.json", $settings);
DocTools::writeSettings('../config/settings.json', $settings);
}
public function getFolks($key = null)
{
if (isset($key)) {
$member = Session::get("member");
$found = find($this->folks, ["handle" => $member["handle"]]);
$member = Session::get('member');
$found = find($this->folks, ['handle' => $member['handle']]);
if ($found) {
return $found[$key];
}
@ -125,25 +124,24 @@ class Settings
public static function updateGlobalData($key, $data)
{
$settings = self::$settings;
$settings["global"][$key] = $data;
DocTools::writeSettings("../config/settings.json", $settings);
$settings = self::$settings;
$settings['global'][$key] = $data;
DocTools::writeSettings('../config/settings.json', $settings);
}
public static function getCurrentIndex()
{
$settings = self::$settings;
return $settings["library_stats"]["current_index"];
return $settings['library_stats']['current_index'];
}
public static function updateIndex()
{
$settings = self::$settings;
$settings["library_stats"]["current_index"] =
$settings["library_stats"]["current_index"] + 1;
$settings['library_stats']['current_index'] = $settings['library_stats']['current_index'] + 1;
DocTools::writeSettings("../config/settings.json", $settings);
DocTools::writeSettings('../config/settings.json', $settings);
}
public static function updateMenu($body)
@ -151,27 +149,27 @@ class Settings
$settings = self::$settings;
//$menu = $settings["menu"];
$item = [
"title" => $body["title"],
"id" => $body["id"],
"uuid" => $body["uuid"],
"slug" => $body["slug"],
"path" => $body["path"],
'title' => $body['title'],
'id' => $body['id'],
'uuid' => $body['uuid'],
'slug' => $body['slug'],
'path' => $body['path'],
];
if ($body["menu"] == "true") {
if (!find($settings["menu"], ["uuid" => $item["uuid"]])) {
array_push($settings["menu"], $item);
if ($body['menu'] == 'true') {
if (!find($settings['menu'], ['uuid' => $item['uuid']])) {
array_push($settings['menu'], $item);
}
} else {
if (find($settings["menu"], ["uuid" => $item["uuid"]])) {
pull($settings["menu"], $item);
if (find($settings['menu'], ['uuid' => $item['uuid']])) {
pull($settings['menu'], $item);
}
}
DocTools::writeSettings("../config/settings.json", $settings);
DocTools::writeSettings('../config/settings.json', $settings);
}
public static function updateTags()
{
$tags = Sorting::tags();
DocTools::writeSettings("../config/tags.json", $tags);
DocTools::writeSettings('../config/tags.json', $tags);
}
}

@ -2,19 +2,17 @@
namespace brain\data;
use brain\data\Settings;
class Themes
{
private $themes = [];
public function __construct()
{
$_themes = glob("../content/themes/*", GLOB_ONLYDIR);
$_themes = glob('../content/themes/*', GLOB_ONLYDIR);
foreach ($_themes as $theme) {
array_push(
$this->themes,
json_decode(file_get_contents($theme . "/theme.json"), true)
json_decode(file_get_contents($theme . '/theme.json'), true)
);
}
}
@ -26,17 +24,17 @@ class Themes
public function getCustomIndex()
{
$settings = (new Settings())->getSettings();
$currentTheme = $settings["global"]["theme"];
$folder = "../content/themes/" . $currentTheme;
$files = array_filter(glob("$folder/*twig"), "is_file");
$views = [];
$settings = (new Settings())->getSettings();
$currentTheme = $settings['global']['theme'];
$folder = '../content/themes/' . $currentTheme;
$files = array_filter(glob("$folder/*twig"), 'is_file');
$views = [];
foreach ($files as $file) {
$path = explode("/", $file);
$path = explode('/', $file);
$fileName = $path[4];
if (str_contains($fileName, "index")) {
$page = explode(".", $fileName);
if (str_contains($fileName, 'index')) {
$page = explode('.', $fileName);
$views[] = $page[0];
}
}
@ -45,17 +43,17 @@ class Themes
public function getCustomViews()
{
$settings = (new Settings())->getSettings();
$currentTheme = $settings["global"]["theme"];
$folder = "../content/themes/" . $currentTheme;
$files = array_filter(glob("$folder/*twig"), "is_file");
$views = [];
$settings = (new Settings())->getSettings();
$currentTheme = $settings['global']['theme'];
$folder = '../content/themes/' . $currentTheme;
$files = array_filter(glob("$folder/*twig"), 'is_file');
$views = [];
foreach ($files as $file) {
$path = explode("/", $file);
$path = explode('/', $file);
$fileName = $path[4];
if (str_contains($fileName, "page")) {
$page = explode(".", $fileName);
if (str_contains($fileName, 'page')) {
$page = explode('.', $fileName);
$views[] = $page[0];
}
}

@ -2,13 +2,10 @@
namespace brain\init;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use brain\utility\HandleCors;
use Slim\Factory\AppFactory;
use Slim\Views\Twig;
use Slim\Views\TwigMiddleware;
// Fipamo Core Classes
use brain\utility\HandleCors;
class App
{
@ -17,19 +14,19 @@ class App
// when a new class is made, run composer dump-autoload
// set up cors
new HandleCors();
$app = AppFactory::create();
$twig = Twig::create("../brain/views/");
$app = AppFactory::create();
$twig = Twig::create('../brain/views/');
$app->add(TwigMiddleware::create($app, $twig));
//set up routing
// set up routing
$app->get(
"/[{first}[/{second}[/{third}[/{fourth}[/{fifth}]]]]]",
'/[{first}[/{second}[/{third}[/{fourth}[/{fifth}]]]]]',
"brain\controller\RouteControl:get"
);
$app->post(
"/[{first}[/{second}[/{third}[/{fourth}]]]]",
'/[{first}[/{second}[/{third}[/{fourth}]]]]',
"brain\controller\RouteControl:post"
);
//start the app
// start the app
$app->run();
}

@ -11,14 +11,14 @@ class DocTools
public static function writePages($task, $path, $fileLocation, $fileContents)
{
try {
if ($task == "create") {
if (!is_dir("../content/pages/" . $path)) {
if ($task == 'create') {
if (!is_dir('../content/pages/' . $path)) {
//Directory does not exist, so lets create it.
mkdir("../content/pages/" . $path, 0755, true);
mkdir('../content/pages/' . $path, 0755, true);
}
file_put_contents($fileLocation, $fileContents);
} else {
($new = fopen($fileLocation, "w")) or die("Unable to open file!");
($new = fopen($fileLocation, 'w')) or die('Unable to open file!');
fwrite($new, $fileContents);
fclose($new);
}
@ -34,7 +34,7 @@ class DocTools
if (!is_file($fileLocation)) {
file_put_contents($fileLocation, json_encode($fileContents));
} else {
($new = fopen($fileLocation, "w")) or die("Unable to open file!");
($new = fopen($fileLocation, 'w')) or die('Unable to open file!');
fwrite($new, json_encode($fileContents));
fclose($new);
}
@ -51,7 +51,7 @@ class DocTools
if (!is_file($location)) {
file_put_contents($location, $html);
} else {
($new = fopen($location, "w")) or die("Unable to open file!");
($new = fopen($location, 'w')) or die('Unable to open file!');
fwrite($new, $html);
fclose($new);
}
@ -86,59 +86,58 @@ class DocTools
public static function objectToMD($object)
{
$markdown =
"---\n" .
"id: " .
$object["id"] .
$markdown = "---\n" .
'id: ' .
$object['id'] .
"\n" .
"uuid: " .
$object["uuid"] .
'uuid: ' .
$object['uuid'] .
"\n" .
"title: " .
'title: ' .
"'" .
$object["title"] .
$object['title'] .
"'" .
"\n" .
"feature: " .
$object["feature"] .
'feature: ' .
$object['imageList'] .
"\n" .
"files: " .
$object["files"] .
'files: ' .
$object['fileList'] .
"\n" .
"path: " .
$object["path"] .
'path: ' .
$object['path'] .
"\n" .
"layout: " .
$object["layout"] .
'layout: ' .
$object['layout'] .
"\n" .
"tags: " .
$object["tags"] .
'tags: ' .
$object['tags'] .
"\n" .
"author: " .
$object["author"] .
'author: ' .
$object['author'] .
"\n" .
"created: " .
$object["created"] .
'created: ' .
$object['created'] .
"\n" .
"updated: " .
$object["updated"] .
'updated: ' .
$object['updated'] .
"\n" .
"deleted: " .
$object["deleted"] .
'deleted: ' .
$object['deleted'] .
"\n" .
"slug: " .
$object["slug"] .
'slug: ' .
$object['slug'] .
"\n" .
"menu: " .
$object["menu"] .
'menu: ' .
$object['menu'] .
"\n" .
"published: " .
$object["published"] .
'published: ' .
$object['published'] .
"\n" .
"featured: " .
$object["featured"] .
'featured: ' .
$object['featured'] .
"\n---\n" .
$object["content"];
$object['content'];
return $markdown;
}

@ -21,9 +21,9 @@ class FileUploader
// echo "**FILE** " . $file->getClientFileName();
$file->moveTo($directory.'/'.urlencode($file->getClientFileName()));
$file->moveTo($directory . '/' . urlencode($file->getClientFileName()));
} catch (RuntimeException $e) {
echo 'ERROR '.$e->getMessage();
echo 'ERROR ' . $e->getMessage();
// echo "failed to upload image: " . $e->getMessage();
// throw new Error("Failed to upload image file");

@ -9,37 +9,38 @@ class HandleCors
public function __construct()
{
//look to see if settings file exists. kinda important
if (file_exists("../config/settings.json")) {
if (file_exists('../config/settings.json')) {
//check settings to see if external api access is allowed
$config = new Settings();
$config = new Settings();
$settings = $config->getSettings();
if ($settings["global"]["externalAPI"]) {
if ($settings['global']['externalAPI']) {
//echo "API STATUS: " . $settings["global"]["externalAPI"];
if ($settings["global"]["externalAPI"] == "true") {
if ($settings['global']['externalAPI'] == 'true') {
//echo "API ACCESS ACTIVE";
// checks to see if origin is set
if (isset($_SERVER["HTTP_ORIGIN"])) {
// You can decide if the origin in $_SERVER['HTTP_ORIGIN'] is something you want to allow, or as we do here, just allow all
header("Access-Control-Allow-Origin: {$_SERVER["HTTP_ORIGIN"]}");
if (isset($_SERVER['HTTP_ORIGIN'])) {
// You can decide if the origin in $_SERVER['HTTP_ORIGIN']
//is something you want to allow, or as we do here, just allow all
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
} else {
//No HTTP_ORIGIN set, so we allow any. You can disallow if needed here
//never allow just any domain, so turn CORS off if no No HTTP_ORIGIN is set
//header("Access-Control-Allow-Origin: *");
}
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Max-Age: 600"); // cache for 10 minutes
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 600'); // cache for 10 minutes
if ($_SERVER["REQUEST_METHOD"] == "OPTIONS") {
if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_METHOD"])) {
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
header(
"Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT"
'Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT'
);
} //Make sure you remove those you do not want to support
if (isset($_SERVER["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"])) {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
header(
"Access-Control-Allow-Headers: {$_SERVER["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"]}"
"Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"
);
}

@ -2,7 +2,6 @@
namespace brain\utility;
use Slim\Views\Twig;
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
use brain\data\Settings;
@ -12,60 +11,58 @@ class Mailer
{
public static function sendMail($body)
{
$config = new Settings();
$settings = $config->getSettings();
$mailConfig = $settings["email"];
$mail = new PHPMailer();
$config = new Settings();
$settings = $config->getSettings();
$mailConfig = $settings['email'];
$mail = new PHPMailer();
switch ($body["mail_task"]) {
case "TESTING":
$html =
"<h1>Hi! It's Fipamo!</h1><br>" .
switch ($body['mail_task']) {
case 'TESTING':
$html = "<h1>Hi! It's Fipamo!</h1><br>" .
"<strong>It's just a test</strong><br>" .
$body["content"];
$member = Session::get("member");
$mail->addAddress($member["email"], ""); //pull email address from current user
$mail->Subject = "A test email";
$body['content'];
$member = Session::get('member');
$mail->addAddress($member['email'], ''); //pull email address from current user
$mail->Subject = 'A test email';
break;
case "SEND_SECRET":
$html =
"<h1>Hi! It's Fipamo!</h1><br>" .
"<strong>This is your secret key.</strong><br><br>" .
"<h3>" .
$body["secret"] .
"</h3>" .
"<br> Use this key to reset your password.";
$mail->addAddress($body["email"], ""); //pull email address from current user
case 'SEND_SECRET':
$html = "<h1>Hi! It's Fipamo!</h1><br>" .
'<strong>This is your secret key.</strong><br><br>' .
'<h3>' .
$body['secret'] .
'</h3>' .
'<br> Use this key to reset your password.';
$mail->addAddress($body['email'], ''); //pull email address from current user
$mail->Subject = "Shhhh! It's a secret!";
break;
default:
return $result = [
"type" => "noMailService",
"message" => "Mail task is undefined. What are you doing??",
'type' => 'noMailService',
'message' => 'Mail task is undefined. What are you doing??',
];
break;
}
//set values based on current active protocol
switch ($mailConfig["active"]) {
case "option-smtp":
$mail->setFrom($mailConfig["smtp"]["email"], "System Email");
$mail->Host = "playvicio.us";
$mail->Username = $mailConfig["smtp"]["email"];
$mail->Password = $mailConfig["smtp"]["password"];
switch ($mailConfig['active']) {
case 'option-smtp':
$mail->setFrom($mailConfig['smtp']['email'], 'System Email');
$mail->Host = 'playvicio.us';
$mail->Username = $mailConfig['smtp']['email'];
$mail->Password = $mailConfig['smtp']['password'];
break;
case "option-mg":
$mail->setFrom($mailConfig["mailgun"]["domain"], "No Reply");
$mail->Host = "smtp.mailgun.org";
$mail->Username = $mailConfig["mailgun"]["domain"];
$mail->Password = $mailConfig["mailgun"]["key"];
case 'option-mg':
$mail->setFrom($mailConfig['mailgun']['domain'], 'No Reply');
$mail->Host = 'smtp.mailgun.org';
$mail->Username = $mailConfig['mailgun']['domain'];
$mail->Password = $mailConfig['mailgun']['key'];
break;
default:
//no mail service
return $result = [
"type" => "noMailService",
"message" => "Mail is not configured. Handle that.",
'type' => 'noMailService',
'message' => 'Mail is not configured. Handle that.',
];
break;
}
@ -73,9 +70,9 @@ class Mailer
$mail->Body = $html;
$mail->IsHTML(true);
$mail->isSMTP();
$mail->SMTPAuth = true;
$mail->SMTPSecure = "ssl";
$mail->Port = 465;
$mail->SMTPAuth = true;
$mail->SMTPSecure = 'ssl';
$mail->Port = 465;
// Uncomment for debug info
//$mail->SMTPDebug = 4;
@ -83,13 +80,13 @@ class Mailer
/* Finally send the mail. */
try {
$mail->send();
$result = ["type" => "mailSent", "message" => "Message Away!"];
$result = ['type' => 'mailSent', 'message' => 'Message Away!'];
} catch (Exception $e) {
//echo $e->errorMessage();
$result = [
"type" => "mailNotSent",
"message" => "Message Not Away!",
"error" => $e->errorMessage(),
'type' => 'mailNotSent',
'message' => 'Message Not Away!',
'error' => $e->errorMessage(),
];
}

@ -3,6 +3,7 @@
namespace brain\utility;
use brain\data\Settings;
use Carbon\Carbon;
class Maintenance
{
@ -13,92 +14,90 @@ class Maintenance
public static function makeBackup()
{
//make sure back directory is there
if (!is_dir("../config/backups")) {
mkdir("../config/backups", 0755, true);
if (!is_dir('../config/backups')) {
mkdir('../config/backups', 0755, true);
}
//creat backup zip
$zip = new \ZipArchive();
$zip->open(
"../config/backups/latest_back.zip",
'../config/backups/latest_back.zip',
\ZipArchive::CREATE | \ZipArchive::OVERWRITE
);
//gather data and path info for md pages
$pagePath = "../content/pages";
$yearPaths = glob($pagePath . "/*", GLOB_ONLYDIR);
$pagePath = '../content/pages';
$yearPaths = glob($pagePath . '/*', GLOB_ONLYDIR);
foreach ($yearPaths as $years) {
$year = explode("/", $years);
$year = explode('/', $years);
//grap the index and save it
if (trim($year[3]) == "start") {
if (trim($year[3]) == 'start') {
$options = [
"add_path" => "content/pages/" . $year[3] . "/",
"remove_all_path" => true,
'add_path' => 'content/pages/' . $year[3] . '/',
'remove_all_path' => true,
];
$zip->addGlob($years . "/*.md", GLOB_BRACE, $options);
$zip->addGlob($years . '/*.md', GLOB_BRACE, $options);
}
$monthsPath = glob($pagePath . "/" . $year[3] . "/*", GLOB_ONLYDIR);
$monthsPath = glob($pagePath . '/' . $year[3] . '/*', GLOB_ONLYDIR);
foreach ($monthsPath as $months) {
$month = explode("/", $months);
$month = explode('/', $months);
//once info is collected, add md pages to zip
$options = [
"add_path" => "content/pages/" . $year[3] . "/" . $month[4] . "/",
"remove_all_path" => true,
'add_path' => 'content/pages/' . $year[3] . '/' . $month[4] . '/',
'remove_all_path' => true,
];
$zip->addGlob($months . "/*.md", GLOB_BRACE, $options);
$zip->addGlob($months . '/*.md', GLOB_BRACE, $options);
}
}
//gather data and path info for blog images
$blogImagesPath = "../public/assets/images/blog";
$yearPaths = glob($blogImagesPath . "/*", GLOB_ONLYDIR);
$blogImagesPath = '../public/assets/images/blog';
$yearPaths = glob($blogImagesPath . '/*', GLOB_ONLYDIR);
foreach ($yearPaths as $years) {
$year = explode("/", $years);
$monthsPath = glob($blogImagesPath . "/" . $year[5] . "/*", GLOB_ONLYDIR);
$year = explode('/', $years);
$monthsPath = glob($blogImagesPath . '/' . $year[5] . '/*', GLOB_ONLYDIR);
foreach ($monthsPath as $months) {
$month = explode("/", $months);
$month = explode('/', $months);
//once info is collected, add images pages to zip
$options = [
"add_path" =>
"public/assets/images/blog/" . $year[5] . "/" . $month[6] . "/",
"remove_all_path" => true,
'add_path' => 'public/assets/images/blog/' . $year[5] . '/' . $month[6] . '/',
'remove_all_path' => true,
];
$zip->addGlob($months . "/*.*", GLOB_BRACE, $options);
$zip->addGlob($months . '/*.*', GLOB_BRACE, $options);
}
}
//gather data and path info for user images
$userImagesPath = "../public/assets/images/user";
$yearPaths = glob($userImagesPath . "/*", GLOB_ONLYDIR);
$userImagesPath = '../public/assets/images/user';
$yearPaths = glob($userImagesPath . '/*', GLOB_ONLYDIR);
foreach ($yearPaths as $years) {
$year = explode("/", $years);
$monthsPath = glob($userImagesPath . "/" . $year[5] . "/*", GLOB_ONLYDIR);
$year = explode('/', $years);
$monthsPath = glob($userImagesPath . '/' . $year[5] . '/*', GLOB_ONLYDIR);
foreach ($monthsPath as $months) {
$month = explode("/", $months);
$month = explode('/', $months);
//once info is collected, add images pages to zip
$options = [
"add_path" =>
"public/assets/images/user/" . $year[5] . "/" . $month[6] . "/",
"remove_all_path" => true,
'add_path' => 'public/assets/images/user/' . $year[5] . '/' . $month[6] . '/',
'remove_all_path' => true,
];
$zip->addGlob($months . "/*.*", GLOB_BRACE, $options);
$zip->addGlob($months . '/*.*', GLOB_BRACE, $options);
}
}
//add directory for settings and save them
$zip->addEmptyDir("settings");
$zip->addFile("../config/settings.json", "settings/settings.json");
$zip->addFile("../config/folks.json", "settings/folks.json");
$zip->addFile("../config/tags.json", "settings/tags.json");
$zip->addEmptyDir('settings');
$zip->addFile('../config/settings.json', 'settings/settings.json');
$zip->addFile('../config/folks.json', 'settings/folks.json');
$zip->addFile('../config/tags.json', 'settings/tags.json');
//save zip file
$zip->close();
//update settings file with latest back up date
$updated = new \Moment\Moment();
$updated = Carbon::now();
Settings::updateGlobalData(
"last_backup",
'last_backup',
$updated->format("Y-m-d\TH:i:sP")
);
$result = ["message" => "Backup created. THIS IS A SAFE SPACE!"];
$result = ['message' => 'Backup created. THIS IS A SAFE SPACE!'];
return $result;
}
}

@ -2,13 +2,15 @@
namespace brain\utility;
use Carbon\Carbon;
use function _\find;
class SetUp
{
public static function status()
{
if (file_exists("../config/settings.json")) {
if (file_exists('../config/settings.json')) {
return true;
} else {
return false;
@ -19,79 +21,78 @@ class SetUp
{
//grab template files
$newFolks = json_decode(
file_get_contents("../config/init/folks-template.json"),
file_get_contents('../config/init/folks-template.json'),
true
);
$newSettings = json_decode(
file_get_contents("../config/init/settings-template.json"),
file_get_contents('../config/init/settings-template.json'),
true
);
//get form values
//$body = $request->getParsedBody();
$handle = $body["new_member_handle"];
$email = $body["new_member_email"];
$pass = $body["new_member_pass"];
$title = $body["new_member_title"];
$handle = $body['new_member_handle'];
$email = $body['new_member_email'];
$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;
$newFolks[0]["handle"] = $handle;
$newFolks[0]["email"] = $email;
$newFolks[0]["password"] = $hash;
$newFolks[0]["key"] = password_hash($email, PASSWORD_DEFAULT);
$newFolks[0]["secret"] = StringTools::randomString(12);
$newFolks[0]["role"] = "hnic";
$newFolks[0]["created"] = $now->format("Y-m-d\TH:i:sP");
$newFolks[0]["updated"] = $now->format("Y-m-d\TH:i:sP");
$hash = password_hash($pass, PASSWORD_DEFAULT);
$newFolks[0]['id'] = 0;
$newFolks[0]['handle'] = $handle;
$newFolks[0]['email'] = $email;
$newFolks[0]['password'] = $hash;
$newFolks[0]['key'] = password_hash($email, PASSWORD_DEFAULT);
$newFolks[0]['secret'] = StringTools::randomString(12);
$newFolks[0]['role'] = 'hnic';
$newFolks[0]['created'] = $now->format("Y-m-d\TH:i:sP");
$newFolks[0]['updated'] = $now->format("Y-m-d\TH:i:sP");
//set up settings config
$newSettings["global"]["title"] = $title;
$newSettings['global']['title'] = $title;
//create index file
//$rightNow = $now->format("Y-m-d\TH:i:sP");
//var_dump($now->format("Y-m-d\TH:i:sP"));
$index = [
"id" => 1,
"uuid" => StringTools::createUUID(),
"title" => "FIRST!",
"feature" => "/assets/images/global/default-bg.jpg",
"files" => "",
"path" => "content/pages/start",
"layout" => "index",
"tags" => "start, welcome",
"author" => $handle,
"created" => $now->format("Y-m-d\TH:i:sP"),
"updated" => $now->format("Y-m-d\TH:i:sP"),
"deleted" => "false",
"slug" => "first",
"menu" => "false",
"featured" => "false",
"published" => "true",
"content" =>
"# F**k Yes \n\nIf you're seeing this, you're up and running. NICE WORK!\n\nFrom here, feel free to start dropping pages to your heart's content.\n\nFor some tips about using Fipamo, check out the ![docs](https://code.playvicio.us/Are0h/Fipamo/wiki/02-Usage)\n\nAll good? Feel free to edit this page to whatever you want!\n\nYOU'RE THE CAPTAIN NOW.",
'id' => 1,
'uuid' => StringTools::createUUID(),
'title' => 'FIRST!',
'imageList' => '/assets/images/global/default-bg.jpg',
'fileList' => '',
'path' => 'content/pages/start',
'layout' => 'index',
'tags' => 'start, welcome',
'author' => $handle,
'created' => $now->format("Y-m-d\TH:i:sP"),
'updated' => $now->format("Y-m-d\TH:i:sP"),
'deleted' => 'false',
'slug' => 'first',
'menu' => 'false',
'featured' => 'false',
'published' => 'true',
'content' => "# F**k Yes \n\nIf you're seeing this, you're up and running. NICE WORK!\n\nFrom here, feel free to start dropping pages to your heart's content.\n\nFor some tips about using Fipamo, check out the ![docs](https://code.playvicio.us/Are0h/Fipamo/wiki/02-Usage)\n\nAll good? Feel free to edit this page to whatever you want!\n\nYOU'RE THE CAPTAIN NOW.",
];
$freshIndex = DocTools::objectToMD($index);
//once all files created, write down
DocTools::writeSettings("../config/settings.json", $newSettings);
DocTools::writeSettings("../config/folks.json", $newFolks);
DocTools::writeSettings("../config/tags.json", []);
DocTools::writeSettings('../config/settings.json', $newSettings);
DocTools::writeSettings('../config/folks.json', $newFolks);
DocTools::writeSettings('../config/tags.json', []);
DocTools::writePages(
"create",
"start",
"../content/pages/start/index.md",
'create',
'start',
'../content/pages/start/index.md',
$freshIndex
);
//if there is an older session file, get rid of it
if (is_file("../content/.session")) {
unlink("../content/.session");
if (is_file('../content/.session')) {
unlink('../content/.session');
}
$result = ["type" => "blogInitGood", "message" => "Site Created"];
$result = ['type' => 'blogInitGood', 'message' => 'Site Created'];
return $result;
}
@ -99,39 +100,39 @@ class SetUp
public static function restore($request)
{
$result = [
"type" => "requestLame",
"message" => "Still working on it.",
'type' => 'requestLame',
'message' => 'Still working on it.',
];
$body = $request->getParsedBody();
$backup = $request->getUploadedFiles();
$file = $backup["backup-upload"];
$file = $backup['backup-upload'];
//NOTE: If this fails check 'post_max_size' in php.ini
$size = $file->getSize();
$name = $file->getClientFileName();
//park it so it can be read
$file->moveTo("../content" . "/" . $name);
$file->moveTo('../content' . '/' . $name);
//open it and get files to verify user
$zip = new \ZipArchive();
if ($zip->open("../content" . "/" . $name) === true) {
$folks = json_decode($zip->getFromName("settings/folks.json"), true);
$found = find($folks, ["handle" => $body["restore_member_handle"]]);
if ($zip->open('../content' . '/' . $name) === true) {
$folks = json_decode($zip->getFromName('settings/folks.json'), true);
$found = find($folks, ['handle' => $body['restore_member_handle']]);
//if member is found in back up, check pass
if ($found) {
if (password_verify($body["restore_member_pass"], $found["password"])) {
if (password_verify($body['restore_member_pass'], $found['password'])) {
//backup verified, restore site
//set new secret key for older folks configs
$newFolks = [];
if (!isset($found["secret"])) {
$found["secret"] = StringTools::randomString(12);
if (!isset($found['secret'])) {
$found['secret'] = StringTools::randomString(12);
}
array_push($newFolks, $found);
//dump files in folder
$zip->extractTo("../content");
$zip->extractTo('../content');
//move to appropriate spots
/*
@ -143,49 +144,49 @@ class SetUp
//load up old config file
$newConfig = json_decode(
file_get_contents("../content/settings/settings.json"),
file_get_contents('../content/settings/settings.json'),
true
);
//check for key, add if not there
if (!isset($newConfig["global"]["externalAPI"])) {
$newConfig["global"]["externalAPI"] = "false";
if (!isset($newConfig['global']['externalAPI'])) {
$newConfig['global']['externalAPI'] = 'false';
}
//write new config file
DocTools::writeSettings("../config/settings.json", $newConfig);
DocTools::writeSettings('../config/settings.json', $newConfig);
//rename("../content/settings/folks.json", "../config/folks.json");
DocTools::writeSettings("../config/folks.json", $newFolks);
DocTools::writeSettings('../config/folks.json', $newFolks);
rename("../content/settings/tags.json", "../config/tags.json");
rename('../content/settings/tags.json', '../config/tags.json');
//images path for blog and user
$blogImagePath = "../public/assets/images/blog";
$userImagePath = "../public/assets/images/user";
$blogImagePath = '../public/assets/images/blog';
$userImagePath = '../public/assets/images/user';
//check to see if image dirs are empty, if not chill
if ($globs = glob($blogImagePath . "/*")) {
if ($globs = glob($blogImagePath . '/*')) {
//directory not empty, relax
} else {
rename("../content/public/assets/images/blog", $blogImagePath);
rename('../content/public/assets/images/blog', $blogImagePath);
}
if ($globs = glob($userImagePath . "/*")) {
if ($globs = glob($userImagePath . '/*')) {
//directory not empty, relax
} else {
rename("../content/public/assets/images/user", $userImagePath);
rename('../content/public/assets/images/user', $userImagePath);
}
rename("../content/content/pages/", "../content/pages");
rename('../content/content/pages/', '../content/pages');
//legacy check for old file structure
if (is_file("../content/pages/index.md")) {
if (!is_dir("../content/pages/start")) {
if (is_file('../content/pages/index.md')) {
if (!is_dir('../content/pages/start')) {
//Directory does not exist, so lets create it.
mkdir("../content/pages/start", 0755, true);
mkdir('../content/pages/start', 0755, true);
//move start page to appropriate spot
rename(
"../content/pages/index.md",
"../content/pages/start/index.md"
'../content/pages/index.md',
'../content/pages/start/index.md'
);
}
} else {
@ -194,34 +195,34 @@ class SetUp
//clean up
DocTools::deleteFolder("../content/settings");
DocTools::deleteFolder("../content/public");
DocTools::deleteFolder("../content/content");
DocTools::deleteFolder('../content/settings');
DocTools::deleteFolder('../content/public');
DocTools::deleteFolder('../content/content');
$result = [
"type" => "requestGood",
"message" => "Site Restored! Redirecting",
'type' => 'requestGood',
'message' => 'Site Restored! Redirecting',
];
} else {
$result = [
"type" => "requestLame",
"message" => "Check that password, champ.",
'type' => 'requestLame',
'message' => 'Check that password, champ.',
];
}
} else {
$result = [
"type" => "requestLame",
"message" => "No member found by that name, hoss",
'type' => 'requestLame',
'message' => 'No member found by that name, hoss',
];
}
$zip->close();
$zipPath = "../content/" . $name;
$zipPath = '../content/' . $name;
//trash zip when done
unlink($zipPath);
} else {
$result = [
"type" => "requestLame",
"message" => "Could not open backup. RATS!",
'type' => 'requestLame',
'message' => 'Could not open backup. RATS!',
];
}
return $result;

@ -2,53 +2,58 @@
namespace brain\utility;
use Mni\FrontYAML\Parser;
use brain\data\Book;
use brain\data\Settings;
use Mni\FrontYAML\Parser;
use function _\find;
use function _\filter;
use function _\find;
class Sorting
{
private static $_tags = [];
private static $_archive = [];
private static $p_tags = [];
private static $p_archive = [];
public function __construct()
{
}
public static function tags()
{
$pages = (new Book("../content/pages"))->getContents();
$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::$_tags, ["tag_name" => $label])) {
array_push(self::$_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::$_tags;
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'],
]);
}
}
}
@ -57,157 +62,172 @@ class Sorting
public static function archive()
{
$pages = (new Book("../content/pages"))->getContents();
$years = [];
$pages = (new Book('../content/pages'))->getContents();
$years = [];
$archive = [];
foreach ($pages as $page) {
//$year = date("Y", date($page["rawCreated"]));
$date = explode("/", $page["path"]);
//echo $page["title"] . " : " . $year . "\n";
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),
]);
// $year = date("Y", date($page["rawCreated"]));
$date = explode('/', $page['path']);
// echo $page["title"] . " : " . $year . "\n";
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),
]
);
}
}
foreach ($years as $year) {
$sorted = [];
$filtered = filter($pages, ["createdYear" => $year["year"]]);
$sorted = [];
$filtered = filter($pages, ['createdYear' => $year['year']]);
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",
]);
$month = date('m', date($obj['rawCreated']));
if (!find($sorted, ['month' => $month])) {
$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"])),
"count" => count($perMonth),
"pages" => $perMonth,
]);
'month' => $month,
'full_month' => date('F', date($obj['rawCreated'])),
'count' => count($perMonth),
'pages' => $perMonth,
]);
}
}
array_push(self::$_archive, [
"year" => $year["year"],
"year_data" => $sorted,
array_push(self::$p_archive, [
'year' => $year['year'],
'year_data' => $sorted,
]);
}
return self::$_archive;
return self::$p_archive;
}
public static function page($page)
{
$config = new Settings();
$settings = $config->getSettings();
$config = new Settings();
$settings = $config->getSettings();
$pageOption = [];
$pageInfo = [
"keywords" => isset($settings["global"]["keywords"])
? $settings["global"]["keywords"]
: "fipamo, blog, jamstack, php, markdown, js",
"description" => $settings["global"]["descriptions"],
"image" =>
$settings["global"]["base_url"] . $settings["global"]["background"],
"baseURL" => $settings["global"]["base_url"],
'keywords' => isset($settings['global']['keywords'])
? $settings['global']['keywords']
: 'fipamo, blog, jamstack, php, markdown, js',
'description' => $settings['global']['descriptions'],
'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),
]);
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 = [
"who" => $page["author"],
"when" => $page["created"],
"tags" => $tags,
'who' => $page['author'],
'when' => $page['created'],
'tags' => $tags,
];
//render markdown content and clean it
$parser = new Parser();
$rendered = $parser->parse($page["content"]);
// render markdown content and clean it
$parser = new Parser();
$rendered = $parser->parse($page['content']);
$sanitizer = \HtmlSanitizer\Sanitizer::create([
"extensions" => ["basic", "image", "list", "code"],
"tags" => [
"img" => [
"allowed_attributes" => ["src", "alt", "title", "class"],
"allowed_hosts" => null,
],
],
'extensions' => ['basic', 'image', 'list', 'code'],
'tags' => [
'img' => [
'allowed_attributes' => ['src', 'alt', 'title', 'class'],
'allowed_hosts' => null,
],
],
]);
$preclean = $sanitizer->sanitize($rendered->getContent());
//just clean renderd string for now, Sanitize doesn't like relative img urls
//so another option is needed
// just clean renderd string for now, Sanitize doesn't like relative img urls
// so another option is needed
$cleaned = strip_tags($rendered->getContent(), [
"a",
"br",
"p",
"strong",
"br",
"img",
"iframe",
"ul",
"li",
"i",
"em",
"h1",
"h2",
"h3",
"pre",
"code",
'a',
'br',
'p',
'strong',
'br',
'img',
'iframe',
'ul',
'li',
'i',
'em',
'h1',
'h2',
'h3',
'pre',
'code',
]);
//$cleaned = preg_replace('/(?:\r\n|[\r\n]){2,}/', "\n\n", $cleaned);
//$cleaned = html_entity_decode($cleaned, ENT_QUOTES, "UTF-8");
//if page feature isn't empty, replace page info meta image
if ($page["feature"] != "" || $page["feature"] != null) {
$pageInfo["image"] = $pageInfo["baseURL"] . $page["feature"];
// if page feature isn't empty, find image from list and set it as background image
// if it is empty, just use global background
if ($page['feature'] != '' || $page['feature'] != null) {
$media = explode(',', $page['feature']);
$set = false;
foreach ($media as $file) {
$item = trim($file);
$ext = pathinfo($item, PATHINFO_EXTENSION);
if ($ext != 'mp4' && !$set) {
$pageInfo['image'] = $pageInfo['baseURL'] . $item;
$set = true;
}
}
}
if ($page["layout"] == "index") {
//$template = $this->theme . "/index.twig";
//$location = "../public/index.html";
//$dir = null;
if ($page['layout'] == 'index') {
// $template = $this->theme . "/index.twig";
// $location = "../public/index.html";
// $dir = null;
$recent = [];
$recent = [];
$featured = [];
$limit = 4;
$pages = (new Book())->getContents();
$limit = 4;
$pages = (new Book())->getContents();
foreach ($pages as $item) {
if (
!$item["deleted"] &&
$item["published"] &&
$item["menu"] != "true"
if (!$item['deleted'] &&
$item['published'] &&
$item['menu'] != 'true'
) {
if (count($recent) < $limit) {
array_push($recent, [
"path" => $item["path"],
"slug" => $item["slug"],
"title" => $item["title"],
"feature" => $item["feature"],
'path' => $item['path'],
'slug' => $item['slug'],
'title' => $item['title'],
'feature' => $item['feature'],
]);
}
if ($item["featured"] == true) {
if ($item['featured'] == true) {
if (count($featured) < $limit) {
array_push($featured, [
"path" => $item["path"],
"slug" => $item["slug"],
"title" => $item["title"],
"feature" => $item["feature"],
'path' => $item['path'],
'slug' => $item['slug'],
'title' => $item['title'],
'feature' => $item['feature'],
]);
}
}
@ -215,34 +235,35 @@ class Sorting
}
$pageOptions = [
"title" => $page["title"],
"background" => $page["feature"],
"content" => $page["html"], //$cleaned,
"meta" => $meta,
"recent" => $recent,
"featured" => $featured,
"info" => $pageInfo,
"menu" => $settings["menu"],
"dynamicRender" => $settings["global"]["dynamicRender"],
"media" => $page["media"],
"files" => $page["docs"],
'title' => $page['title'],
'background' => $page['feature'],
'content' => $page['html'], // $cleaned,
'meta' => $meta,
'recent' => $recent,
'featured' => $featured,
'info' => $pageInfo,
'menu' => $settings['menu'],
'dynamicRender' => $settings['global']['dynamicRender'],
'media' => $page['media'],
'files' => $page['docs'],
];
} else {
//$template = $this->theme . "/page.twig";
//$location = "../public/" . $page["path"] . "/" . $page["slug"] . ".html";
//$dir = "../public/" . $page["path"];
// $template = $this->theme . "/page.twig";
// $location = "../public/" . $page["path"] . "/" . $page["slug"] . ".html";
// $dir = "../public/" . $page["path"];
$pageOptions = [
"title" => $page["title"],
"background" => $page["feature"],
"content" => $page["html"], //$cleaned,
"meta" => $meta,
"info" => $pageInfo,
"menu" => $settings["menu"],
"dynamicRender" => $settings["global"]["dynamicRender"],
"media" => $page["media"],
"files" => $page["docs"],
'title' => $page['title'],
'background' => $page['feature'],
'content' => $page['html'], // $cleaned,
'meta' => $meta,
'info' => $pageInfo,
'menu' => $settings['menu'],
'dynamicRender' => $settings['global']['dynamicRender'],
'media' => $page['media'],
'files' => $page['docs'],
];
}
return $pageOptions;
}
}

@ -10,12 +10,12 @@ class StringTools
{
public static function createUUID()
{
if (function_exists("com_create_guid") === true) {
return trim(com_create_guid(), "{}");
if (function_exists('com_create_guid') === true) {
return trim(com_create_guid(), '{}');
}
return sprintf(
"%04X%04X-%04X-%04X-%04X-%04X%04X%04X",
'%04X%04X-%04X-%04X-%04X-%04X%04X%04X',
mt_rand(0, 65535),
mt_rand(0, 65535),
mt_rand(0, 65535),
@ -29,36 +29,36 @@ class StringTools
public static function sanitizeContent($entry)
{
$parser = new Parser();
$rendered = $parser->parse($entry);
$parser = new Parser();
$rendered = $parser->parse($entry);
$sanitizer = HtmlSanitizer\Sanitizer::create([
"extensions" => ["basic", "image", "list", "code"],
"tags" => [
"img" => [
"allowed_attributes" => ["src", "alt", "title", "class"],
"allowed_hosts" => null,
],
],
'extensions' => ['basic', 'image', 'list', 'code'],
'tags' => [
'img' => [
'allowed_attributes' => ['src', 'alt', 'title', 'class'],
'allowed_hosts' => null,
],
],
]);
$preclean = $sanitizer->sanitize($rendered->getContent());
$cleaned = strip_tags($rendered->getContent(), [
"a",
"br",
"p",
"strong",
"br",
"img",
"iframe",
"ul",
"li",
"i",
"h1",
"h2",
"h3",
"pre",
"code",
'a',
'br',
'p',
'strong',
'br',
'img',
'iframe',
'ul',
'li',
'i',
'h1',
'h2',
'h3',
'pre',
'code',
]);
return $cleaned;
@ -69,32 +69,31 @@ class StringTools
return strtolower(
trim(
preg_replace(
"~[^0-9a-z]+~i",
"_",
'~[^0-9a-z]+~i',
'_',
html_entity_decode(
preg_replace(
"~&([a-z]{1,2})(?:acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i",
'~&([a-z]{1,2})(?:acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i',
'$1',
htmlentities($string, ENT_QUOTES, "UTF-8")
htmlentities($string, ENT_QUOTES, 'UTF-8')
),
ENT_QUOTES,
"UTF-8"
'UTF-8'
)
),
"-"
'-'
)
);
}
public static function randomString(int $length)
{
$alphanum =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
$special = '*&!@%^#$';
$alphabet = $alphanum . $special;
$random = openssl_random_pseudo_bytes($length);
$alphanum = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$special = '*&!@%^#$';
$alphabet = $alphanum . $special;
$random = openssl_random_pseudo_bytes($length);
$alphabet_length = strlen($alphabet);
$string = "";
$string = '';
for ($i = 0; $i < $length; ++$i) {
$string .= $alphabet[ord($random[$i]) % $alphabet_length];
}
@ -102,7 +101,7 @@ class StringTools
//secret needs to be a valid token
if ($length == 12) {
try {
$secret = Token::create(12, $string, time() + 3600, "localhost");
$secret = Token::create(12, $string, time() + 3600, 'localhost');
return $string;
} catch (BuildException $e) {
//bad secret, so try agiain
@ -120,8 +119,8 @@ class StringTools
private static function checkSpecial($string)
{
$specials = ["*", "&", "!", "@", "%", "^", "#", "$"];
$valid = false;
$specials = ['*', '&', '!', '@', '%', '^', '#', '$'];
$valid = false;
foreach ($specials as $item) {
if (strpos($string, $item)) {
return $valid = true;

@ -1,65 +1,49 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>
{% block title %}
{{ title }}
{% endblock %}
</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
<div id="notifications" class="notifications">
<div id="notifyMessage" class="notifyMessage">
<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">
{% 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>
{% endapply %}
</div>
</header>
{% endif %}
{% apply spaceless %}
{% block mainContent %}{% endblock %}
{% endapply %}
</section>
</div>
<footer></footer>
{% block javascripts %}{% endblock %}
</body>
</html>
<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 }}
{% endblock %}
</title>
{% block stylesheets %}{% endblock %}
</head>
<body>
{% 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 %}
{{ include("dash/partials/navigation.twig") }}
{% endapply %}
{% endif %}
</div>
</nav>
<div role="notify">
{% apply spaceless %}
{{ include("dash/partials/notifications.twig") }}
{% endapply %}
</div>
{% endapply %}
{% endif %}
</header>
<main>
{% apply spaceless %}
{% block mainContent %}{% endblock %}
{% endapply %}
</main>
<footer></footer>
<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 %}

@ -1,181 +1,206 @@
{% extends "dash/_frame.twig" %}
{#
if page is in 'edit' mode, set variables
if not, just make them empty
#}
if page is in 'edit' mode, set variables
if not, just make them empty
#}
{% if mode == 'edit' %}
{% set id = page['id'] %}
{% set uuid = page['uuid'] %}
{% 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'] %}
{% set updated = page['updated'] %}
{% set media = page['media'] %}
{% set files = page['docs'] %}
{% set id = page['id'] %}
{% set uuid = page['uuid'] %}
{% set slug = page['slug'] %}
{% set layout = page['layout'] %}
{% set feature = page['feature'] %}
{% set tags = page['tags'] %}
{% set content = page['content'] %}
{% set date = page['created'] %}
{% set updated = page['updated'] %}
{% set media = page['media'] %}
{% set files = page['docs'] %}
{% else %}
{% set id = '' %}
{% set uuid = '' %}
{% set slug = '' %}
{% set layout = 'page' %}
{% set feature = '' %}
{% set title = '' %}
{% set tags = '' %}
{% set content = '' %}
{% set date = '' %}
{% set updated = '' %}
{% set media = '' %}
{% set files = '' %}
{% set id = '' %}
{% set uuid = '' %}
{% set slug = '' %}
{% set layout = 'page' %}
{% set feature = '' %}
{% set title = '' %}
{% set tags = '' %}
{% set content = '' %}
{% set date = '' %}
{% set updated = '' %}
{% set media = '' %}
{% set files = '' %}
{% endif %}
{% block title %}
{{ title }}
{{ title }}
{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash.css?=vbhj">
{% endblock %}
<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">
<div id="post-feature">
{% if page['feature'] == null %}
<div id="page-file-manager">
<div id="page-file-wrapper">
<div id="page-file-drop">
<label for="page-files-upload">DRAG AND DROP FILES OR CLICK TO SELECT</label>
</div>
IMAGES AND VIDEO
<div id="page-images-list"></div>
FILES
<div id="page-files-list"></div>
</div>
</div>
{% else %}
<div id="page-file-manager">
<div id="page-file-wrapper">
<div id="page-file-drop">
<label for="page-files-upload">DRAG AND DROP FILES OR CLICK TO SELECT</label>
</div>
IMAGES AND VIDEO
<div id="page-images-list">
{% if media|length > 1 %}
{% for item in media %}
{% if item.type == "mp4"%}
<div id="{{loop.index0}}" class="video-item" data-source="{{ item.file }}"></div>
{% else %}
<div id="{{loop.index0}}" class="img-item" style="background: url({{ item.file }}) no-repeat center center / cover"></div>
{% endif %}
{% endfor %}
{% else %}
{% if media[0] != '' %}
{% if media[0].type == "mp4"%}
<div id="0" class="video-item" data-source="{{ media[0].file }}"></div>
{% else %}
<div id="0" class="img-item" style="background: url({{ media[0].file }}) no-repeat center center / cover"></div>
{% endif %}
{% else %}
{% endif %}
{% endif %}
</div>
FILES
<div id="page-files-list">
{% if files|length > 1 %}
{% for item in files %}
{% if item.type == "mp3"%}
<div id="{{loop.index0}}" class="audio-item" data-source="{{ item.file }}"></div>
{% else %}
<div id="{{loop.index0}}" class="file-item" data-source="{{ item.file }}"></div>
{% endif %}
{% endfor %}
{% else %}
{% if files[0] != '' %}
{% if files[0].type == "mp3"%}
<div id="0" class="audio-item" data-source="{{ files[0].file }}"></div>
{% else %}
<div id="0" class="file-item" data-source="{{ files[0].file }}"></div>
{% endif %}
{% block mainContent %}
<section data-index="{{ id }}" data-uuid="{{ uuid }}" data-slug="{{ slug }}" data-layout="{{ layout }}" role="file-manager">
{% if page['feature'] == null %}
<div role="file-drop">
<label for="page-files-upload">DRAG AND DROP FILES OR CLICK TO SELECT</label>
</div>
<label role="list-title">IMAGES AND VIDEO</label>
<div role="page-images-list"></div>
<label role="list-title">FILES</label>
<div role="page-files-list"></div>
{% else %}
<div role="file-drop">
<label for="page-files-upload">DRAG AND DROP FILES OR CLICK TO SELECT</label>
</div>
<label role="list-title">IMAGES AND VIDEO</label>
<div role="page-images-list">
{% if media|length > 1 %}
{% for item in media %}
{% set fileName = item.file|split('/') %}
{% if item.type == "mp4" %}
{% else %}
<div id="{{ loop.index0 }}" class="video-item" data-source="{{ item.file }}">
<video>
<source src="{{ item.file }}"/>
</video>
<button id="{{ loop.index0 }}" class="media-remove">
<i class="ti ti-x"></i>
</button>
</div>
{% else %}
<div id="{{ loop.index0 }}" class="img-item" data-source="{{ item.file }}" style="background: url({{ item.file }}) no-repeat center center / cover">
<button id="{{ loop.index0 }}" class="media-remove">
<i class="ti ti-x"></i>
</button>
</div>
{% endif %}
{% endfor %}
{% else %}
{% if media[0] != '' %}
{% set fileName = media[0].file|split('/') %}
{% if media[0].type == "mp4" %}
<div id="0" class="video-item" data-source="{{ media[0].file }}">
<button id="{{ loop.index0 }}" class="media-remove">X</button>
</div>
{% endif %}
{% endif %}
{% else %}
<div id="0" class="img-item" data-source="{{ media[0].file }}" style="background: url({{ media[0].file }}) no-repeat center center / cover">
<button id="{{ loop.index0 }}" class="media-remove">
<i class="ti ti-x"></i>
</button>
</div>
{% endif %}
{% else %}
{% endif %}
{% endif %}
</div>
<label role="list-title">FILES</label>
<div role="page-files-list">
{% if files|length > 1 %}
{% for item in files %}
{% set fileName = item.file|split('/') %}
{% if item.type == "mp3" %}
<div id="{{ loop.index0 }}" class="audio-item" data-source="{{ item.file }}">
<audio controls>
<source src="{{ item.file }}"/>
</audio>
<button id="{{ loop.index0 }}" class="media-remove">
<i class="ti ti-x"></i>
</button>
</div>
{% else %}
<div id="{{ loop.index0 }}" class="file-item" data-source="{{ item.file }}">
<a href="{{ item.file }}" target="_blank">{{ fileName[6] }}"</a>
<button id="{{ loop.index0 }}" class="media-remove">
<i class="ti ti-x"></i>
</button>
</div>
{% endif %}
{% endfor %}
{% else %}
{% if files[0] != '' %}
{% set fileName = files[0].file|split('/') %}
{% if files[0].type == "mp3" %}
<div id="0" class="audio-item" data-source="{{ files[0].file }}">
<audio controls>
<source src="{{ files[0].file }}"/>
</audio>
<button id="{{ loop.index0 }}" class="media-remove">
<i class="ti ti-x"></i>
</button>
</div>
{% else %}
<div id="0" class="file-item" data-source="{{ files[0].file }}">
<a href="{{ item.file }}" target="_blank">{{ fileName[6] }}"</a>
<button id="{{ loop.index0 }}" class="media-remove">
<i class="ti ti-x"></i>
</button>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
{% else %}
</div>
<div id="post-header">
<div id="post-header-wrapper" class="columns">
<div id="post-title" class="column">
<label>TITLE</label>
<textarea id="post_title" type="text" name="post_title" class="post-edit" placeholder="TITLE">
{{- _title -}}
</textarea>
<div id="layouts">
<label>LAYOUTS</label>
<select id="page-templates">
{% for view in views %}
{% if view == page['layout'] %}
<option value={{ view }} selected>{{ view }}</option>
{% else %}
<option value={{ view }}>{{ view }}</option>
{% endif %}
{% endfor %}
</select>
</div>
<label>CREATED</label>
<span id="post-date" type="text">
{{ date }}
</span>
</div>
<div id="post-meta" class="column">
<label>TAGS</label>
<textarea id="post_tags" type="text" name="post_tags" class="form-control" placeholder="tags [comma seperated]">
{{- tags -}}
</textarea>
<label>OPTIONS</label>
{% apply spaceless %}
{{ include("dash/partials/options.twig") }}
{% endapply %}
<label>UPDATED</label>
<span id="post-date" type="text">
{{ updated }}
</span>
<input id="page-files-upload" type="file" name="page-files-upload" multiple/>
<input id="post-image-upload" type="file" name="post-image-upload"/>
<input id="form_token" name="token" type="hidden" value="{{ token }}">
</div>
</div>
</div>
<div id="edit-post">
{% apply spaceless %}
{{ include("dash/partials/editor.twig") }}
{% endapply %}
<div id="edit-post-wrapper">
<textarea id="edit" spellcheck="false">{{- content -}}</textarea>
<pre id="highlight">
<code id="highlight-content" class="language-md">
</code>
</pre>
</div>
</div>
</div>
</div>
{% endblock %}
{% endif %}
{% endif %}
{% block javascripts %}
<script src="/assets/scripts/Start.js?=erty" type="text/javascript"></script>
{% endblock %}
</div>
{% endif %}
</section>
<section role="page-meta">
<div role="page-meta-wrapper">
<div role="page-title">
<strong>TITLE</strong>
<textarea id="post-title-text" type="text" name="post-title-text" class="post-edit" placeholder="TITLE">{{ title }}</textarea>
</div>
<div role="page-tags">
<strong>TAGS</strong>
<textarea id="post-tags" type="text" name="post-tags" class="form-control" placeholder="tags [comma seperated]">{{ tags }}</textarea>
</div>
<div role="page-layouts">
<strong>LAYOUTS</strong>
<select id="page-templates">
{% for view in views %}
{% if view == page['layout'] %}
<option value={{ view }} selected>{{ view }}</option>
{% else %}
<option value={{ view }}>{{ view }}</option>
{% endif %}
{% endfor %}
</select>
</div>
<div role="page-options">
<strong>OPTIONS</strong>
{% apply spaceless %}
{{ include("dash/partials/options.twig") }}
{% endapply %}
</div>
<div role="page-updated">
<strong>UPDATED</strong>
<span id="post-date" type="text">
{{ updated }}
</span>
</div>
<div role="page-created">
<strong>CREATED</strong>
<span id="post-date" type="text">
{{ date }}
</span>
<input id="page-files-upload" type="file" name="page-files-upload" multiple/>
<input id="post-image-upload" type="file" name="post-image-upload"/>
<input id="form_token" name="token" type="hidden" value="{{ token }}"></div>
</div>
</div>
</section>
<section role="text-editor">
{% apply spaceless %}
{{ include("dash/partials/editor.twig") }}
{% endapply %}
<div role="edit-post-wrapper">
<textarea id="edit" spellcheck="false" class="language-md">{{ content }}</textarea>
<pre id="highlight">
<code id="highlight-content" class="language-md"></code>
</pre>
</div>
</section>
{% endblock %}

@ -1,45 +1,38 @@
<div id="edit-control">
<button id="edit-bold" class="content-editor-btn-text editor-button" title="bold">B</button>
<button id="edit-italic" class="content-editor-btn-text editor-button" title="italic">I</button>
<button id="edit-strikethrough" class="content-editor-btn-text editor-button" title="strikethrough">D</button>
<div role="text-editor-control">
<button id="edit-bold" class="content-editor-btn-text editor-button" title="bold">
<i id="edit-bold" class="ti ti-bold"></i>
</button>
<button id="edit-italic" class="content-editor-btn-text editor-button" title="italic">
<i id="edit-italic" class="ti ti-italic"></i>
</button>
<button id="edit-strikethrough" class="content-editor-btn-text editor-button" title="strikethrough">
<i id="edit-strikethrough" class="ti ti-strikethrough"></i>
</button>
<button id="edit-link" class="content-editor-btn-icon editor-button" title="insert link">
<svg id="edit-link" viewbox="0 0 20 20" class="icons">
<use id="edit-link" xlink:href="/assets/images/global/sprite.svg#entypo-link"/>
</svg>
<i id="edit-link" class="ti ti-link"></i>
</button>
<button id="edit-header1" class="content-editor-btn-text editor-button" title="header 1">
<i id="edit-header1" class="ti ti-h-1"></i>
</button>
<button id="edit-header2" class="content-editor-btn-text editor-button" title="header 2">
<i id="edit-header2" class="ti ti-h-2"></i>
</button>
<button id="edit-header3" class="content-editor-btn-text editor-button" title="header 3">
<i id="edit-header3" class="ti ti-h-3"></i>
</button>
<button id="edit-header1" class="content-editor-btn-text editor-button" title="header 1">H1</button>
<button id="edit-header2" class="content-editor-btn-text editor-button" title="header 2">H2</button>
<button id="edit-header3" class="content-editor-btn-text editor-button" title="header 3">H3</button>
<button id="edit-image" class="content-editor-btn-icon editor-button" title="insert image">
<svg id="edit-image" viewbox="0 0 20 20" class="icons">
<use id="edit-image" xlink:href="/assets/images/global/sprite.svg#entypo-image"/>
</svg>
<i id="edit-image" class="ti ti-photo"></i>
</button>
{% if mode == "edit" %}
<button id="edit-update" class="post-sumbit-btn submit-start editor-button" data-action='blog-update' data-id=page.id type='submit' title="bold">
<svg id="submit-update" viewbox="0 0 20 20" class="icons">
<use id="submit-update" xlink:href="/assets/images/global/sprite.svg#entypo-save" data-action='blog-update' data-id="{{ page['uuid'] }}"/>
</svg>
<svg id="submit-good" class="icon-hide" viewbox="0 0 20 20" class="icons">
<use xlink:href="/assets/images/global/sprite.svg#entypo-thumbs-up"/>
</svg>
<svg id="submit-error" class="icon-hide" viewbox="0 0 20 20" class="icons">
<use xlink:href="/assets/images/global/sprite.svg#entypo-thumbs-down"/>
</svg>
<button id="edit-update" class="post-sumbit-btn submit-start editor-button" data-action='blog-update' data-id="{{ page['uuid'] }} type='submit' title=" bold">
<i id="edit-update" class="ti ti-device-floppy"></i>
</button>
<button id="edit-delete" class="content-editor-btn-icon editor-button submit-delete" for="post-delete" title='delete post'>
<svg id="edit-delete" viewbox="0 0 20 20" class="icons">
<use id="edit-delete" xlink:href="/assets/images/global/sprite.svg#entypo-cross"/>
</svg>
<i id="edit-delete" class="ti ti-x"></i>
</button>
{% else %}
<button id="edit-save" class="post-sumbit-btn submit-start editor-button" data-action='blog-add' type='submit'>
<svg id="submit-save" viewbox="0 0 20 20" class="icons">
<use id="submit-save" xlink:href="/assets/images/global/sprite.svg#entypo-plus"/>
</svg>
<i id="edit-save" class="ti ti-file-plus"></i>
</button>
{% endif %}
</div>
</div>

@ -1,58 +1,30 @@
<div id="dash-recent">
<div id="recent-list">
<div class="recent-header">
<div class="index-header-left">
Recent
</div>
<div class="index-header-right">
<a href='/dashboard/pages' title="view pages">
<button>
<svg class="page-link">
<use xlink:href="/assets/images/global/sprite.svg#entypo-archive"/>
</svg>
</button>
</a>
<a href='/dashboard/page/add/new' title="add new page">
<button>
<svg class="page-link">
<use xlink:href="/assets/images/global/sprite.svg#entypo-plus"/>
</svg>
</button>
</a>
</div>
</div>
<br/>
{% if data["entryCount"] != 0 %}
{% for page in data['pages'] %}
{% if page.media[0].type == 'mp4' %}
<section role="index-header">
<div role="index-header-left">
<h1>Recent</h1>
</div>
<div role="index-header-right"></div>
</section>
<section role="index-recent-pages">
{% if data["entryCount"] != 0 %}
{% for page in data['pages'] %}
{% if page.media[0].type == 'mp4' %}
<a href="/dashboard/page/edit/{{ page.uuid }}" id="{{ page.uuid }}" class="post-video-link">
<video class="post-video" loop muted autoplay>
<source src="{{ page.media[0].file }}" type="video/mp4">
<a href="/dashboard/page/edit/{{ page.uuid }}" id="{{ page.uuid }}" class="post-video-link recent-link">
{{ include("dash/partials/recent-meta.twig") }}
<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>
</a>
Sorry, your browser doesn't support embedded videos.
</video>
<label>
{{ page.title }}
</label>
{{ include("dash/partials/recent-options.twig") }}
</a>
{% else %}
<a href="/dashboard/page/edit/{{ page.uuid }}" id="{{ page.uuid }}" class="post-link recent-link" style="background: url({{ page.media[0].file }}) no-repeat center center / cover #fc6399">
{{ include("dash/partials/recent-meta.twig") }}
</a>
{% endif %}
{% endfor %}
{% else %}
There are no pages
{% endif %}
{% else %}
<a href="/dashboard/page/edit/{{ page.uuid }}" id="{{ page.uuid }}" class="post-link" style="background: url({{ page.media[0].file }}) no-repeat center center / cover">
<label>
{{ page.title }}
</label>
{{ include("dash/partials/recent-options.twig") }}
</a>
{% endif %}
{% endfor %}
{% else %}
There are no pages
{% endif %}
</div>
</div>
</section>

@ -1,23 +1,57 @@
<div id="dash-menu">
<div role="menu">
{% if title == "Settings" %}
{% apply spaceless %}
{{ include("dash/partials/submenu_settings.twig") }}
{% endapply %}
{% elseif title=="Start" %}
{% apply spaceless %}
{{ include("dash/partials/submenu_start.twig") }}
{% endapply %}
{% endif %}
<a id="settings" href="/dashboard/settings" title="settings">
<button>
<svg class="menu">
<use xlink:href="/assets/images/global/sprite.svg#entypo-sound-mix"/>
</svg>
<i class="ti ti-home-cog"></i>
</button>
</a> .
</a>
<a id="navigation" href="/dashboard/navigation" title="edit navigation">
<button>
<i class="ti ti-arrow-autofit-height"></i>
</button>
</a>
<a id="navigation" href="/dashboard/logout" title="log out">
<button>
<i class="ti ti-logout"></i>
</button>
</a>
</div>
<button role="menu-toggle">
<i class="ti ti-menu-2"></i>
</button>
<div role="mobile-menu">
{% if title == "Settings" %}
{% apply spaceless %}
{{ include("dash/partials/submenu_settings.twig") }}
{% endapply %}
{% elseif title=="Start" %}
{% apply spaceless %}
{{ include("dash/partials/submenu_start.twig") }}
{% endapply %}
{% endif %}
<a id="settings" href="/dashboard/settings" title="settings">
<button>
<i class="ti ti-home-cog"></i>
</button>
</a>
<a id="navigation" href="/dashboard/navigation" title="edit navigation">
<button>
<svg class="menu">
<use xlink:href="/assets/images/global/sprite.svg#entypo-list"/>
</svg>
<i class="ti ti-arrow-autofit-height"></i>
</button>
</a> .
</a>
<a id="navigation" href="/dashboard/logout" title="log out">
<button>
<svg class="menu">
<use xlink:href="/assets/images/global/sprite.svg#entypo-log-out"/>
</svg>
<i class="ti ti-logout"></i>
</button>
</a>
</div>
</div>

@ -0,0 +1,10 @@
<div role="notify-message">
<div role="notify-icons">
<i class="ti ti-mood-smile" role="notify-good"></i>
<i class="ti ti-mood-sad" role="notify-notgood"></i>
<i class="ti ti-settings" role="notify-working"></i>
</div>
<div role="notify-text">
<span role="response-text">Hey Hey</span>
</div>
</div>

@ -1,42 +1,40 @@
{% if page['menu'] %}
{% if page['menu'] %}
{% set menu = 'true' %}
{% else %}
{% set menu = 'false' %}
{% endif %}
{% if page['featured'] %}
{% if page['featured'] %}
{% set featured = 'true' %}
{% else %}
{% set featured = 'false' %}
{% endif %}
{% if page['published'] %}
{% if page['published'] %}
{% set published = 'true' %}
{% else %}
{% set published = 'false' %}
{% endif %}
<div id="post-options">
<br>
<button id="option-menu-pin" class="option-inactive post-option-btn" data-active="{{ menu }}" title='Pin to Menu'>
<svg id="option-page-icon" viewbox="0 0 20 20" class="icons">
<use id="option-page-icon" xlink:href="/assets/images/global/sprite.svg#entypo-add-to-list"/>
<svg id="option-page-icon" role="icon">
<use id="option-page-icon" xlink:href="/assets/images/global/sprite.svg#entypo-add-to-list"/>
</svg>
</button>
<button id="option-feature" class="option-inactive post-option-btn" data-active="{{ featured }}" title='Feature'>
<svg id="option-feature-icon" role="icon">
<use id="option-feature-icon" xlink:href="/assets/images/global/sprite.svg#entypo-star"/>
</svg>
</button>
<button id="option-published" class="option-inactive post-option-btn" data-active="{{ published }}" title='Published'>
<svg id="option-published-icon" role="icon">
<use id="option-published-icon" xlink:href="/assets/images/global/sprite.svg#entypo-globe"/>
</svg>
</button>
<button id="option-feature" class="option-inactive post-option-btn" data-active="{{ featured }}" title='Feature'>
<svg id="option-feature-icon" viewbox="0 0 20 20" class="icons">
<use id="option-feature-icon" xlink:href="/assets/images/global/sprite.svg#entypo-star"/>
</svg>
</button>
<button id="option-published" class="option-inactive post-option-btn" data-active="{{ published }}" title='Published'>
<svg id="option-published-icon" viewbox="0 0 20 20" class="icons">
<use id="option-published-icon" xlink:href="/assets/images/global/sprite.svg#entypo-globe"/>
</svg>
</button>
<a href="/dashboard/page/preview/{{ uuid }}" target="_blank">
<button id="option-preview" class="option-inactive post-option-btn" data-active="false" title='preview page'>
<svg id="option-preview-icon" viewbox="0 0 20 20" class="icons">
<use id="option-preview-icon" xlink:href="/assets/images/global/sprite.svg#entypo-eye"/>
</button>
<a href="/dashboard/page/preview/{{ uuid }}" target="_blank">
<button id="option-preview" class="option-inactive post-option-btn" data-active="false" title='preview page'>
<svg id="option-preview-icon" role="icon">
<use id="option-preview-icon" xlink:href="/assets/images/global/sprite.svg#entypo-eye"/>
</svg>
</button>
</a>
</div>
</a>

@ -0,0 +1,37 @@
{% if page.menu == 'true' %}
{% set menu = "true" %}
{% else %}
{% set menu = "false" %}
{% endif %}
{% if page.published == 'true' %}
{% set published = "true" %}
{% else %}
{% set published = "false" %}
{% endif %}
{% if page.featured == 'true' %}
{% set featured = "true" %}
{% else %}
{% set featured = "false" %}
{% endif %}
<aside>
<strong>
{{ page.updated }}
</strong>
<hr/>
<strong>
{{ page.title }}
</strong>
<hr/>
<button data-active="{{ menu }}">
<i class="ti ti-navigation"></i>
</button>
<button data-active="{{ published }}">
<i class="ti ti-world"></i>
</button>
<button data-active="{{ featured }}">
<i class="ti ti-star"></i>
</button>
</aside>

@ -1,42 +0,0 @@
<div id="options">
{% if page.menu == 'true' %}
{% set menu = "true" %}
{% else %}
{% set menu = "false" %}
{% endif %}
{% if page.published == 'true' %}
{% set published = "true" %}
{% else %}
{% set published = "false" %}
{% endif %}
{% if page.featured == 'true' %}
{% set featured = "true" %}
{% else %}
{% set featured = "false" %}
{% endif %}
<div id="option-left">
<button data-active="{{ menu }}">
<svg>
<use xlink:href="/assets/images/global/sprite.svg#entypo-add-to-list"/>
</svg>
</button>
<button data-active="{{ published }}">
<svg>
<use xlink:href="/assets/images/global/sprite.svg#entypo-globe"/>
</svg>
</button>
<button data-active="{{ featured }}">
<svg>
<use xlink:href="/assets/images/global/sprite.svg#entypo-star"/>
</svg>
</button>
</div>
<div id="option-right">
<span>
{{ page.updated }}
</span>
</div>
</div>

@ -0,0 +1,11 @@
<div role="submenu">
<button id="save-toggle" title="save settings">
<i class="ti ti-device-floppy"></i>
</button>
<button id="publish-pages" title="publish site">
<i class="ti ti-world-upload"></i>
</button>
<button id="render-toggle" title="render on save toggle" data-render="{{ renderOnSave }}">
<i class="ti ti-circle-dashed"></i>
</button>
</div>

@ -0,0 +1,12 @@
<div role="submenu">
<a href='/dashboard/pages' title="view pages">
<button>
<i class="ti ti-book-2"></i>
</button>
</a>
<a href='/dashboard/page/add/new' title="add new page">
<button>
<i class="ti ti-square-plus"></i>
</button>
</a>
</div>

@ -5,37 +5,14 @@
{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash.css?=dfdfdf">
<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-login">
<div id="dash-reset" class="dash-reset">
<img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/>
<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>
</div>
</div>
</div>
</div>
{% endblock %}
<section role="password-reset">
{% apply spaceless %}
{{ include("dash/forms/reset.twig") }}
{% endapply %}
{% block javascripts %}
<script src="/assets/scripts/Start.js" type="text/javascript"></script>
{% endblock %}
</section>
{% endblock %}

@ -5,188 +5,119 @@
{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash.css?=cvnbm">
<link rel="stylesheet" type="text/css" href="/assets/css/dash/start.css?=cvnbm">
{% endblock %}
{% block mainContent %}
<div id="settings-actions">
<div id="buttons">
<button id="save-toggle" title="save settings">
<svg id="submit-update" class="icons">
<use id="submit-update" xlink:href="/assets/images/global/sprite.svg#entypo-save"/>
</svg>
</button>
<button id="publish-pages" title="publish site">
<svg id="submit-update" class="icons">
<use id="submit-update" xlink:href="/assets/images/global/sprite.svg#entypo-publish"/>
</svg>
</button>
<button id="render-toggle" title="render on save toggle" data-render="{{ renderOnSave }}">
<svg id="render-toggle-icon" class="icons">
<use xlink:href="/assets/images/global/sprite.svg#entypo-circular-graph"/>
</svg>
</button>
</div>
</div>
<div id="settings-index">
<div id="settings-index-wrapper">
<div id="member-settings">
<div id="member-images" class="columns">
<div class="column is-one-third">
<div id="member-avatar-drop">
<img id="avatar" src="{{member['avatar']}}" for="avatar-upload"/>
<input id="avatar-upload" type="file" name="avatar-upload" />
</div>
</div>
<div class="column is-two-thirds">
<div id="site-background">
<img id="background" src="{{background}}" alt="image for site background" for="background-upload"/>
<input id="background-upload" type="file" name="backgrond-upload" />
</div>
</div>
</div>
<div id="member-meta" class="columns">
<div class="column is-one-third">
<input type='text' name='handle' id='settings-handle' placeholder='handle' value="{{member['handle']}}" autofocus />
<input type='text' name='email' id='settings-email' placeholder='email' value="{{member['email']}}" autofocus />
</div>
<div class="column is-one-third">
<input type='text' name='base-url' id='settings-url' placeholder='url' value="{{baseUrl}}" autofocus />
<input type='text' name='base-title' id='settings-title' placeholder='site title' value="{{siteTitle}}" autofocus />
</div>
<div class="column is-one-third">
<textarea id="settings-desc" type='text' name='settings_desc' class='settings-dec' placeholder='description stuff', autofocus>{{desc}}</textarea>
</div>
</div>
<article role="settings">
<section role="member-settings">
<div role="member-avatar">
<div role="avatar" style="background: url({{ member['avatar'] }} ) no-repeat center center / cover"></div>
<input id="avatar-upload" type="file" name="avatar-upload"/>
</div>
<div id="feature-settings">
<div id="features" class="columns">
<div class="column">
<div id="feature-api">
{% if apiStatus is defined and apiStatus == "true" %}
<button id="api-access-toggle" title="allow external api" data-enabled="true">
<svg id="api-access-toggle" class="icons">
<use id="api-access-toggle" xlink:href="/assets/images/global/sprite.svg#entypo-landline"/>
</svg>
<span id="api-status">EXTERNAL API ACCESS ENABLED</span>
</button>
{% else %}
<button id="api-access-toggle" title="allow external api" data-enabled="false">
<svg id="api-access-toggle" class="icons">
<use id="api-access-toggle" xlink:href="/assets/images/global/sprite.svg#entypo-landline"/>
</svg>
<span id="api-status">EXTERNAL API ACCESS NOT ENABLED</span>
</button>
{% endif %}
</div>
</div>
<div class="column">
<div id="dynamic-api">
{% if dynamicRenderStatus is defined and dynamicRenderStatus == "true" %}
<button id="dynamic-render-toggle" title="allow external api" data-enabled="true">
<svg id="dynamic-render-toggle" class="icons">
<use id="dynamic-render-toggle" xlink:href="/assets/images/global/sprite.svg#entypo-text-document-inverted"/>
</svg>
<span id="dynamic-render-status">DYNAMIC PAGE RENDERING</span>
</button>
{% else %}
<button id="dynamic-render-toggle" title="allow external api" data-enabled="false">
<svg id="dynamic-render-toggle" class="icons">
<use id="dynamic-render-toggle" xlink:href="/assets/images/global/sprite.svg#entypo-text-document-inverted"/>
</svg>
<span id="dynamic-render-status">STATIC PAGE RENDERING</span>
</button>
{% endif %}
</div>
</div>
<div class="column"></div>
<div class="column"></div>
</div>
<div role="site-background">
<div role="background" style="background: url({{ background }} ) no-repeat center center / cover"></div>
<input id="background-upload" type="file" name="backgrond-upload"/>
</div>
<div id="option-settings" class="columns">
<div id="theme-settings" class="column">
<label>THEMES</label>
{% for theme in themes %}
{% if theme.name == currentTheme %}
<a href="#" id="{{theme.name}}" class="theme-select" data-enabled="true">{{theme['display-name']}}</a>
{% else %}
<a href="#" id="{{theme.name}}" class="theme-select" data-enabled="false">{{theme['display-name']}}</a>
{% endif %}
{% endfor %}
</div>
<div id="mail-settings" class="column">
<label>EMAIL</label>
{% if mailOption == "option-none" or mailOption == "" %}
<a href="#" class="mail-option" id="option-none" data-enabled="true">NONE</a>
{% else %}
<a href="#" class="mail-option" id="option-none" data-enabled="false">NONE</a>
{% endif %}
{% if mailOption == "option-mg" or mailOption == "" %}
<a href="#" class="mail-option" id="option-mg" data-enabled="true">MAILGUN</a>
{% else %}
<a href="#" class="mail-option" id="option-mg" data-enabled="false">MAILGUN</a>
{% endif %}
{% if mailOption == "option-smtp" or mailOption == "" %}
<a href="#" class="mail-option" id="option-smtp" data-enabled="true">SMTP</a>
<div>
<input type='text' name='handle' id='settings-handle' placeholder='handle' value="{{ member['handle'] }}" autofocus/>
<input type='text' name='email' id='settings-email' placeholder='email' value="{{ member['email'] }}" autofocus/>
<input type='text' name='base-url' id='settings-url' placeholder='url' value="{{ baseUrl }}" autofocus/>
<input type='text' name='base-title' id='settings-title' placeholder='site title' value="{{ siteTitle }}" autofocus/>
<textarea id="settings-desc" type='text' name='settings_desc' class='settings-dec' placeholder='description stuff' , autofocus>{{ desc }}</textarea>
</div>
<div>
<button id="create-backup">
<i class="ti ti-disc"></i>
<span>BACK UP YOUR SITE</span>
</button><br/>
{% if lastBackup != '' %}
LAST BACK UP<br/>
<a href="/api/v1/files">{{ lastBackup }}</a><br/>
{% else %}
<span>span No back ups. Frowny face.</span>
{% endif %}
</div>
<div role="features">
{% if apiStatus is defined and apiStatus == "true" %}
<button id="api-access-toggle" title="allow external api" data-enabled="true">
<i class="ti ti-phone-incoming"></i>
<span id="api-status">API ACCESS ENABLED</span>
</button>
{% else %}
<button id="api-access-toggle" title="allow external api" data-enabled="false">
<i class="ti ti-phone-incoming"></i>
<span id="api-status">API ACCESS NOT ENABLED</span>
</button>
{% endif %}
{% if dynamicRenderStatus is defined and dynamicRenderStatus == "true" %}
<button id="dynamic-render-toggle" title="allow external api" data-enabled="true">
<i class="ti ti-arrow-merge"></i>
<span id="dynamic-render-status">DYNAMIC PAGE RENDERING</span>
</button>
{% else %}
<button id="dynamic-render-toggle" title="allow external api" data-enabled="false">
<i class="ti ti-arrow-merge"></i>
<span id="dynamic-render-status">STATIC PAGE RENDERING</span>
</button>
{% endif %}
</div>
<div role="theme">
<label>Themes</label><br/>
{% for theme in themes %}
{% if theme.name == currentTheme %}
<button id="{{ theme.name }}" class="theme-select" data-enabled="true">
<i class="ti ti-brush"></i>
<span>{{ theme['display-name'] }}</span>
</button>
{% else %}
<a href="#" class="mail-option" id="option-smtp" data-enabled="false">SMTP</a>
<button href="#" id="{{ theme.name }}" class="theme-select" data-enabled="false">
<i class="ti ti-brush-off"></i>
<span>{{ theme['display-name'] }}</span>
</button>
{% endif %}
{% apply spaceless %}
{{ include("dash/partials/mailforms.twig") }}
{% endapply %}
<button id="send-mail">TEST MAIL</button>
<br /><br />
</div>
{% endfor %}
</div>
<div id="token-settings">
<div id="keys-tokens" class="columns">
<div class="column">
<label>API KEY</label>
<div id="member-api-key">
{{member['key']}}
</div>
</div>
<div class="column">
<label>FORM TOKEN</label>
<div id="form-token">
{{ftoken}}
</div>
</div>
</div>
<div role="mail">
<label>Email</label><br/>
{% if mailOption == "option-none" or mailOption == "" %}
<a href="#" class="mail-option" id="option-none" data-enabled="true">NONE</a>
{% else %}
<a href="#" class="mail-option" id="option-none" data-enabled="false">NONE</a>
{% endif %}
{% if mailOption == "option-mg" or mailOption == "" %}
<a href="#" class="mail-option" id="option-mg" data-enabled="true">MAILGUN</a>
{% else %}
<a href="#" class="mail-option" id="option-mg" data-enabled="false">MAILGUN</a>
{% endif %}
{% if mailOption == "option-smtp" or mailOption == "" %}
<a href="#" class="mail-option" id="option-smtp" data-enabled="true">SMTP</a>
{% else %}
<a href="#" class="mail-option" id="option-smtp" data-enabled="false">SMTP</a>
{% endif %}
{% apply spaceless %}
{{ include("dash/partials/mailforms.twig") }}
{% endapply %}
<button id="send-mail">
<i class="ti ti-mailbox"></i>
<span>TEST MAIL</span>
</button>
</div>
<div id="backup-settings">
<div class="columns">
<div class="column">
<button id="create-backup">BACK UP YOUR SITE</button><br />
</div>
<div class="column">
{% if lastBackup != '' %}
<div class="backup-meta">
LAST BACK UP <a href="/api/v1/files">{{lastBackup}}</a><br />
</div>
{% else %}
<span>span No back ups. Frowny face.</span>
{% endif %}
</div>
</div>
<div>
<label>API KEY</label>
<br/>
{{ member['key'] }}
<br/>
<label>FORM TOKEN</label><br/>
{{ ftoken }}
</div>
</div>
</div>
</section>
</article>
{% endblock %}
{% block javascripts %}
<script src="/assets/scripts/Start.js" type="text/javascript"></script>
{% endblock %}

@ -1,27 +1,19 @@
{% extends "dash/_frame.twig" %}
{% block title %}
{{ title }}
{{ title }}
{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash.css?=adfdff">
{% endblock %}
<link rel="stylesheet" type="text/css" href="/assets/css/dash/start.css?=dfdfrtr">
{% endblock %}
{% block mainContent %}
<div id="dash-index">
<div id="dash-index-wrapper">
{% if status %}
{% apply spaceless %}
{{ include("dash/partials/index.twig") }}
{% endapply %}
{% else %}
{{ include("dash/forms/login.twig") }}
{% endif %}
</div>
</div>
{% endblock %}
{% block javascripts %}
<script src="/assets/scripts/Start.js?=dfdfsdf" type="text/javascript"></script>
{% endblock %}
{% block mainContent %}
{% if status %}
{% apply spaceless %}
{{ include("dash/partials/index.twig") }}
{% endapply %}
{% else %}
{{ include("dash/forms/login.twig") }}
{% endif %}
{% endblock %}

@ -1,7 +1,7 @@
{
"name": "are0h/fipamo",
"descriptions": "The most chill no database blog framework ever.",
"version": "2.1.1-beta",
"version": "2.6.1-beta",
"homepage": "https://fipamo.blog",
"authors": [
{
@ -9,15 +9,10 @@
"homepage": "https://roiskinda.cool"
}
],
"autoload": {
"psr-4": {
"brain\\":"brain"
}
},
"support": {
"source": "https://code.playvicio.us/Are0h/Fipamo",
"wiki": "https://code.playvicio.us/Are0h/Fipamo/wiki/_pages",
"issues": "https://code.playvicio.us/Are0h/Fipamo/issues"
"source": "https://koodu.ubiqueros.com/are0h/Fipamo",
"wiki": "https://koodu.ubiqueros.com/are0h/Fipamo/wiki/?action=_pages",
"issues": "https://koodu.ubiqueros.com/are0h/Fipamo/issues"
},
"require": {
"slim/slim": "4.*",
@ -27,11 +22,16 @@
"mnapoli/front-yaml": "^1.8",
"lodash-php/lodash-php": "^0.0.7",
"rbdwllr/reallysimplejwt": "^4.0",
"fightbulc/moment": "^1.33",
"tgalopin/html-sanitizer": "^1.4",
"phpmailer/phpmailer": "^6.4",
"league/commonmark": "^2.1",
"symfony/yaml": "^5.4",
"olegatro/html-sanitizer-relative": "^1.0"
}
"olegatro/html-sanitizer-relative": "^1.0",
"nesbot/carbon": "^2.62"
},
"scripts": {
"start": [
"@php -S localhost:8000 -t public/"
]
}
}

985
composer.lock generated

File diff suppressed because it is too large Load Diff

@ -13,100 +13,100 @@
{{ title }}
{% endblock %}
</title>
<meta charset="UTF-8" />
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<meta name="keywords" content="{{ info['keywords'] }}" />
<meta name="description" content="{{info['description']}} " />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta property="og:image" content="{{info["image"]}}" />
<meta name="twitter:image" content="{{info["image"]}}" />
<meta charset="UTF-8"/>
<meta name='viewport' content='width=device-width, initial-scale=1.0'/>
<meta name="keywords" content="{{ info['keywords'] }}"/>
<meta name="description" content="{{info['description']}} "/>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<meta property="og:image" content="{{info["image"]}}"/>
<meta name="twitter:image" content="{{info["image"]}}"/>
<link rel="stylesheet" type="text/css" href="{{ assetPath~"css/base.css?=dfvbghh" }}">
</head>
<body>
<!--
<header style="background: url({{ background }}) no-repeat center center; background-size: cover">
-->
<header style="background: url({{ background }}) no-repeat center center; background-size: cover">
-->
<header>
<div id="media">
{% if media|length > 1 %}
{% for item in media %}
{% if item.type == "mp4"%}
<div id="{{loop.index0}}" class="slide">
<video controls autoplay muted>
{% for item in media %}
{% if item.type == "mp4"%}
<div id="{{loop.index0}}" class="slide">
<video controls autoplay muted>
<source src="{{item.file}}" type="video/mp4">
<source src="{{item.file}}" type="video/mp4">
Please get a better browser. They're free.
</video>
</div>
{% else %}
<div id="{{loop.index0}}" class="slide hide" style="background: url({{ item.file }}) no-repeat center center / cover"></div>
{% endif %}
{% endfor %}
{% else %}
{% if media[0] != '' %}
{% if media[0].type == "mp4"%}
<div id="0" class="slide">
<video controls autoplay muted>
Please get a better browser. They're free.
</video>
</div>
{% else %}
<div id="{{loop.index0}}" class="slide hide" style="background: url({{ item.file }}) no-repeat center center / cover"></div>
{% endif %}
{% endfor %}
{% else %}
{% if media[0] != '' %}
{% if media[0].type == "mp4"%}
<div id="0" class="slide">
<video controls autoplay muted>
<source src="{{media[0].file}}" type="video/mp4">
<source src="{{media[0].file}}" type="video/mp4">
Please get a better browser. They're free.
</video>
</div>
{% else %}
<div id="0" class="slide" style="background: url({{ media[0].file }}) no-repeat center center / cover"></div>
{% endif %}
{% else %}
{% endif %}
{% endif %}
Please get a better browser. They're free.
</video>
</div>
{% else %}
<div id="0" class="slide" style="background: url({{ media[0].file }}) no-repeat center center / cover"></div>
{% endif %}
{% else %}
{% endif %}
{% endif %}
</div>
<nav>
{% apply spaceless %}
<div class="left">
<a href="/" class="logo-link">
<img id="logo" src="{{ assetPath~"/images/global/the-logo.svg" }}" />
</a>
</div>
<div class="right">
{% if menu is defined %}
{% for link in menu %}
{% if dynamicRender is defined %}
{% if dynamicRender == 'true' %}
<a href="{{"/"~link.slug}}" class="menu-link">{{link.title}}</a><br />
{% else %}
<a href="{{"/"~link.slug~".html"}}" class="menu-link">{{link.title}}</a><br />
{% endif %}
<div class="left">
<a href="/" class="logo-link">
<img id="logo" src="{{ assetPath~"/images/global/the-logo.svg" }}"/>
</a>
</div>
<div class="right">
{% if menu is defined %}
{% for link in menu %}
{% if dynamicRender is defined %}
{% if dynamicRender == 'true' %}
<a href="{{"/"~link.slug}}" class="menu-link">{{link.title}}</a><br/>
{% else %}
<a href="{{"/"~link.slug~".html"}}" class="menu-link">{{link.title}}</a><br />
<a href="{{"/"~link.slug~".html"}}" class="menu-link">{{link.title}}</a><br/>
{% endif %}
{% endfor %}
{% endif %}
</div>
{% else %}
<a href="{{"/"~link.slug~".html"}}" class="menu-link">{{link.title}}</a><br/>
{% endif %}
{% endfor %}
{% endif %}
</div>
{% endapply %}
</nav>
</header>
<div id="main-content" class="container">
{% apply spaceless %}
{% block mainContent %}{% endblock %}
{% block mainContent %}{% endblock %}
{% endapply %}
</div>
<footer>
<div class="inner">
{% if dynamicRender is defined %}
{% if dynamicRender == 'true' %}
<a href="/archives">Archives</a><br />
<a href="/archives">Archives</a><br/>
{% else %}
<a href="/archives.html">Archives</a><br />
<a href="/archives.html">Archives</a><br/>
{% endif %}
{% else %}
<a href="/archives.html">Archives</a><br />
<a href="/archives.html">Archives</a><br/>
{% endif %}
© 2020 By Fipamo
</div>
</footer>
<script src="{{ assetPath~"scripts/ThemeStart.js" }}" type="text/javascript"></script>
</body>
</html>
</html>

@ -4,56 +4,59 @@
{{ title }}
{% endblock %}
{% block mainContent %}
<section>
<div class="page-title">
<span>{{title}}</span>
</div>
</section>
<article>
<div class="page">
<p>{{content | raw}}</p>
<div>
<div class="page_files">
<div class="page_doc">
{% block mainContent %}
<section>
<div class="page-title">
<span>{{title}}</span>
</div>
</section>
<article>
<div class="page">
<p>{{content | raw}}</p>
<div>
<div class="page_files">
<div class="page_doc">
<strong>Files</strong><br/>
{% for doc in files %}
{% if doc.type != "mp3" %}
{% set path = doc.file|split('/') %}
<a href="{{doc.file}}">{{path[6]}}</a>
{% endif %}
{% endfor %}
</div>
<div class="page_sounds">
{% for doc in files %}
{% if doc.type != "mp3" %}
{% set path = doc.file|split('/') %}
<a href="{{doc.file}}">{{path[6]}}</a>
{% endif %}
{% endfor %}
</div>
<div class="page_sounds">
<strong>Sounds</strong><br/>
{% for doc in files %}
{% for doc in files %}
{% if doc.type == "mp3" %}
<audio controls>
<source src="{{doc.file}}" type="audio/mpeg">
Your browser does not support the audio element.
</audio>
</audio>
{% endif %}
{% endfor %}
</div>
{% endfor %}
</div>
<br/>
{{meta['who']}} dropped this {{ meta['when'] }}<br />
<strong>tags: </strong>
{% for tag in meta['tags'] %}
{% if dynamicRender is defined %}
{% if dynamicRender == 'true' %}
<a href="{{ "/tags/"~tag.slug }}">{{ tag.label }}</a>
{% else %}
<a href="{{ "/tags/"~tag.slug~".html" }}">{{ tag.label }}</a>
{% endif %}
</div>
<br/>
{{meta['who']}}
dropped this
{{ meta['when'] }}<br/>
<strong>tags:
</strong>
{% for tag in meta['tags'] %}
{% if dynamicRender is defined %}
{% if dynamicRender == 'true' %}
<a href="{{ "/tags/"~tag.slug }}">{{ tag.label }}</a>
{% else %}
<a href="{{ "/tags/"~tag.slug~".html" }}">{{ tag.label }}</a>
<a href="{{ "/tags/"~tag.slug~".html" }}">{{ tag.label }}</a>
{% endif %}
{% endfor %}
</div>
{% else %}
<a href="{{ "/tags/"~tag.slug~".html" }}">{{ tag.label }}</a>
{% endif %}
{% endfor %}
</div>
</article>
{% endblock %}
</div>
</article>
{% endblock %}

33311
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,40 +1,34 @@
{
"name": "fipamo-dash",
"version": "2.5.0",
"private": true,
"apidoc": {
"name": "Fipamo API",
"version": "1.0.0",
"description": "The most chill API for the most chill blog framework"
},
"devDependencies": {
"@babel/preset-env": "^7.16.5",
"babel-cli": "^6.26.0",
"eslint": "^8.11.0",
"eslint-plugin-babel": "^5.3.1",
"parcel": "^2.0.1",
"prettier": "^2.6.0"
},
"dependencies": {
"@babel/core": "^7.16.5",
"@babel/eslint-parser": "^7.16.5",
"animejs": "^3.2.1",
"babel-plugin-prismjs": "^2.1.0",
"babel-preset-env": "^1.7.0",
"bulma": "^0.9.3",
"caret-pos": "^2.0.0",
"jsdoc": "^3.6.7",
"minami": "^1.2.3",
"prismjs": "^1.25.0",
"sass": "^1.45.1",
"sortablejs": "^1.14.0"
},
"license": "UNLICENSED",
"author": "Are0h",
"scripts": {
"watch": "sass --watch src/styles:public/assets/css & npx parcel watch src/com/Start.js --dist-dir public/assets/scripts --public-url /assets/scripts",
"build": "sass src/styles:public/assets/css & npx parcel build src/com/Start.js --dist-dir public/assets/scripts --public-url /assets/scripts"
},
"description": "Front end script for the most chill blog framework ever.",
"repository": "https://code.playvicio.us/Are0h/Fipamo"
"name": "fipamo-dash",
"version": "2.6.1-beta",
"private": true,
"apidoc": {
"name": "Fipamo API",
"version": "1.0.0",
"description": "The most chill API for the most chill blog framework"
},
"source": "src/com/Start.js",
"main": "public/assets/scripts/dash.js",
"targets": {
"main": {
"includeNodeModules": true
}
},
"scripts": {
"watch": "parcel watch",
"build": "parcel build"
},
"devDependencies": {
"@babel/core": "^7.21.3",
"babel-plugin-prismjs": "^2.1.0",
"parcel": "^2.8.3",
"prettier": "^2.8.4",
"stylelint": "^15.3.0",
"stylelint-config-standard": "^31.0.0"
},
"dependencies": {
"animejs": "^3.2.1",
"prismjs": "^1.29.0",
"sortablejs": "^1.15.0"
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,109 @@
section[role="book-index-header"] {
display: grid;
grid-template-columns: 1fr 1fr;
margin: 100px auto 20px;
width: 100%;
max-width: 900px;
}
section[role="book-index-header"] > div[role="book-index-header-left"] {
text-transform: capitalize;
display: inline-block;
color: var(--white);
font-size: 3em;
}
section[role="book-index-header"] > div[role="book-index-header-right"] {
text-align: right;
display: inline-block;
}
section[role="book-index-pages"] {
margin: 0 auto;
width: 100%;
max-width: 900px;
}
section[role="book-index-pages"] > a.page-link {
vertical-align: top;
display: inline-block;
width: 100%;
text-decoration: none;
margin: 0 0 20px;
border-radius: 3px;
height: 350px;
overflow: hidden;
}
section[role="book-index-pages"] > a.page-link div.page-video {
width: 100%;
height: 350px;
display: flex;
justify-content: left;
align-items: center;
border-radius: 5px;
border-bottom: none;
position: relative;
}
section[role="book-index-pages"] > a.page-link video {
width: 100%;
height: 100%;
position: absolute;
object-fit: cover;
}
section[role="book-index-pages"] > a.page-link > div.page-bg {
width: 100%;
height: 350px;
display: flex;
justify-content: left;
align-items: center;
border-radius: 5px;
border-bottom: none;
position: relative;
}
section[role="book-index-pages"] aside {
font-size: 1.1em;
color: var(--white);
text-shadow: 2px 2px 2px var(--black);
padding: 10px;
position: relative;
}
section[role="book-index-pages"] hr {
color: var(--white);
border: 0.1px solid;
margin: 7px 0;
}
section[role="book-index-pages"] button[data-active="true"] {
background: var(--primary);
color: var(--tertiary);
}
section[role="book-index-pages"] button[data-active="false"] {
background: var(--secondary);
}
section[role="book-index-pages"] > div[role="paginate"] {
width: fit-content;
margin: 0 auto;
font-size: 1.5em;
color: var(--highlight);
}
section[role="book-index-pages"] > div[role="paginate"] i {
color: var(--secondary);
}
section[role="book-index-pages"] > div[role="paginate"] a,
section[role="book-index-pages"] > div[role="paginate"] span {
display: inline;
}
section[role="book-index-pages"] > div[role="paginate"] span {
position: relative;
top: -15px;
}

@ -0,0 +1,22 @@
:root {
/* BASE COLORS */
--primary: #1d3040;
--secondary: #b2cce5;
--tertiary: #f5ab35;
--highlight: #fc6399;
--white: #efebe3;
--grey: #abb7b7;
--black: #32302f;
/* EDITOR COLORS */
--event-cool: #32cd32;
--event-lame: #f64747;
--editor-primary: #fde3a7;
--editor-secondary: #e7903c;
--editor-tertiary: #6bb9f0;
--editor-string: #dcc6e0;
--editor-tag: #e73c4e;
/* RGB Versions */
--primary-rgb: 29 28 24;
}

@ -0,0 +1,54 @@
a {
color: var(--primary);
}
p {
background: var(--tertiary);
color: var(--primary);
padding: 5px;
display: block;
border-radius: 5px;
text-align: left;
}
input[type="email"],
input[type="password"],
input[type="text"] {
border: 0;
border-radius: 5px;
font: 18px var(--base-type);
display: inline-block;
background: var(--white);
color: var(--primary);
}
textarea {
border: 0;
border-radius: 3px;
color: var(--primary);
background: var(--white);
}
button,
input[type="submit"] {
background: var(--highlight);
color: var(--primary);
font: 20px var(--base-type);
border-radius: 5px;
position: relative;
cursor: pointer;
border: 0;
transition: all 0.3s linear;
}
select {
font: 14px var(--base-type);
border: 1px solid var(--secondary);
appearance: none;
color: var(--primary);
}
::placeholder {
font: 25px var(--base-type);
color: var(--black);
}

@ -0,0 +1,151 @@
html {
width: 100%;
height: 100%;
overflow: hidden;
font: 400 1.2em/1.4em var(--base-type);
}
html body {
background: var(--primary);
margin: 0;
padding: 0;
height: 100%;
width: 100%;
overflow-y: scroll;
overflow-x: hidden;
}
/* GLOBALS */
a {
color: var(--primary);
text-decoration: none;
border-bottom: 0;
transition: all 0.2s linear;
}
sup {
background: var(--black);
color: var(--white);
padding: 3px;
border-radius: 3px;
}
::selection {
background: var(--highlight);
color: var(--white);
}
#notifications {
display: none;
visibility: hidden;
}
pre,
code {
background: var(--black);
color: var(--highlight);
border-radius: 3px;
padding: 3px;
}
svg[role="icon"] {
fill: var(--white);
width: 25px;
height: 25px;
padding-top: 5px;
}
/* HEADER
Navigation
Notificiations
*/
header {
width: 100%;
max-width: 900px;
margin: 10px auto;
height: 50px;
border-radius: 5px;
left: 50%;
transform: translate(-50%, 0);
position: fixed;
z-index: 500;
}
header > nav > div[role="nav-left"] img {
width: 40px;
padding: 5px;
}
header > nav > div[role="title"] {
text-align: left;
height: 100%;
}
header > nav > div[role="title"] h1 {
color: var(--primary);
margin: 15px;
}
header > nav > div[role="nav-right"] {
padding: 5px;
}
header > nav > div > div[role="mobile-menu"] {
display: none;
position: fixed;
z-index: 1000;
left: 0;
transition: all 0.2s linear;
background: var(--white);
}
header > nav > div[role="nav-right"] button {
width: 40px;
height: 40px;
margin-left: 5px;
font-size: 0.8em;
color: var(--white);
}
header > nav > div[role="nav-right"] > button[role="menu-toggle"] {
display: none;
}
header > nav > div[role="nav-right"] div[role="submenu"] {
display: inline;
}
header > nav > div[role="nav-right"] div[role="submenu"] button {
background: var(--primary);
color: var(--white);
font-size: 0.8em;
}
header
> nav
> div[role="nav-right"]
div[role="submenu"]
button[data-render="true"] {
background: var(--tertiary);
color: var(--primary);
}
/* RESPONSIVE */
@media only screen and (max-width: 900px) {
header {
width: 97%;
}
}
@media only screen and (max-width: 530px) {
header > nav > div[role="nav-right"] > button[role="menu-toggle"] {
display: inline;
}
header > nav > div > div[role="menu"] {
display: none;
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,149 @@
section[role="index-header"] {
display: grid;
grid-template-columns: 1fr 1fr;
width: 100%;
max-width: 900px;
margin: 60px auto 0;
}
section[role="index-recent-pages"] a {
width: 100%;
height: 100%;
display: flex;
justify-content: left;
align-items: center;
border-radius: 5px;
border-bottom: none;
position: relative;
}
section[role="index-recent-pages"] a video {
width: 100%;
position: absolute;
object-fit: cover;
height: 100%;
border-radius: 5px;
}
section[role="index-recent-pages"] a:nth-child(1) {
grid-column: 1/4;
grid-row: 1/3;
}
section[role="index-recent-pages"] a:nth-child(2) {
grid-row: 3/6;
}
section[role="index-recent-pages"] a:nth-child(3) {
grid-column: 2/4;
grid-row: 3/5;
}
section[role="index-header"] div[role="index-header-right"] {
display: flex;
justify-content: right;
align-items: center;
}
section[role="index-header"] div[role="index-header-right"] a {
border-bottom: none;
margin-left: 5px;
}
section[role="index-recent-pages"] {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-auto-rows: minmax(200px, auto);
gap: 10px;
width: 100%;
max-width: 900px;
margin: 10px auto;
}
section[role="index-recent-pages"] a button {
padding: 1px 5px;
}
section[role="index-recent-pages"] button i {
font-size: 1.8em;
}
section[role="index-recent-pages"] button[data-active="true"] {
background: var(--primary);
}
section[role="index-recent-pages"] button[data-active="true"] i {
color: var(--tertiary);
}
section[role="index-recent-pages"] button[data-active="false"] {
background: var(--secondary);
}
section[role="index-recent-pages"] button[data-active="false"] i {
fill: var(--primary);
}
section[role="index-recent-pages"] aside {
font-size: 1.1em;
color: var(--white);
text-shadow: 2px 2px 2px var(--black);
padding: 10px;
z-index: 10;
}
section[role="index-recent-pages"] hr {
color: var(--white);
border: 0.1px solid;
margin: 7px 0;
}
@media only screen and (max-width: 900px) {
section[role="index-header"],
section[role="index-recent-pages"] {
width: 97%;
}
}
@media only screen and (max-width: 520px) {
section[role="index-recent-pages"] {
grid-template-columns: 1fr 1fr;
}
section[role="index-recent-pages"] a:nth-child(1) {
grid-column: 1/3;
grid-row: 1/2;
}
section[role="index-recent-pages"] a:nth-child(2) {
grid-row: 2/3;
grid-column: 1/2;
}
section[role="index-recent-pages"] a:nth-child(3) {
grid-column: 2/3;
grid-row: 2/3;
}
}
@media only screen and (max-width: 350px) {
section[role="index-recent-pages"] a:nth-child(2) {
grid-column: 1/3;
grid-row: 2/3;
}
section[role="index-recent-pages"] a:nth-child(3) {
grid-column: 1/3;
grid-row: 3/4;
}
section[role="index-recent-pages"] a:nth-child(4) {
grid-column: 1/3;
grid-row: 4/5;
}
section[role="index-recent-pages"] a:nth-child(5) {
grid-column: 1/3;
grid-row: 5/6;
}
}

@ -0,0 +1,113 @@
/* LOGIN */
section[role="login"],
section[role="password-reset"],
section[role="restore-fresh"],
section[role="restore-backup"] {
margin: 15% auto;
padding: 10px;
width: 500px;
border-radius: 5px;
display: grid;
grid-template-columns: 28.5% 1fr;
gap: 10px;
visibility: visible;
}
section[role="restore-backup"] {
display: none;
visibility: hidden;
color: var(--white);
}
section[role="login"] form input {
width: 95%;
height: 30px;
padding: 5px;
margin-bottom: 10px;
}
section[role="login"] form button {
padding: 10px 5px;
width: 82%;
}
section[role="login"] form a {
padding: 10px 5px;
border-radius: 5px;
width: 10%;
height: 20px;
display: inline-block;
background: var(--tertiary);
vertical-align: top;
text-align: center;
margin: 0 0 0 10px;
font-weight: 600;
}
/* PASSWORD-RESET */
section[role="password-reset"] form button {
padding: 10px 5px;
width: 82%;
}
section[role="password-reset"] form input {
width: 95%;
height: 30px;
padding: 5px;
margin-bottom: 10px;
}
/* SITE RESTORE */
section[role="restore-fresh"] form button {
padding: 10px 5px;
width: 82%;
}
section[role="restore-fresh"] form input {
width: 95%;
height: 30px;
padding: 5px;
margin-bottom: 10px;
}
section[role="restore-backup"] form button {
padding: 10px 5px;
width: 82%;
}
section[role="restore-backup"] form input {
width: 95%;
height: 30px;
padding: 5px;
margin-bottom: 10px;
}
/* RESPONSIVE */
@media only screen and (max-width: 500px) {
section[role="login"],
section[role="password-reset"],
section[role="restore-fresh"],
section[role="restore-backup"] {
width: 97%;
}
}
@media only screen and (max-width: 375px) {
section[role="login"],
section[role="password-reset"],
section[role="restore-fresh"],
section[role="restore-backup"] {
grid-template-columns: 1fr;
}
section[role="login"] img,
section[role="password-reset"] img,
section[role="restore-fresh"] img,
section[role="restore-backup"] img {
width: 50px;
}
}

@ -0,0 +1,46 @@
article[role="navigation"] {
width: 100%;
max-width: 900px;
margin: 100px auto;
}
article[role="navigation"] > section > div.nav-item {
display: block;
width: 98%;
background: var(--white);
border-radius: 3px;
color: var(--highlight);
margin: 0 0 10px;
font-size: 1.5em;
cursor: move;
}
article[role="navigation"] > section > div.nav-item > label {
display: inline-block;
padding: 5px;
margin: 12px 0 0 10px;
vertical-align: top;
cursor: move;
}
article[role="navigation"] > section > div.nav-item > div#nav-btns {
float: right;
padding: 5px;
position: relative;
}
article[role="navigation"] > section > div.nav-item > div#nav-btns button {
margin-left: 5px;
}
@media only screen and (max-width: 500px) {
article[role="navigation"] > section > div.nav-item > label {
width: 55%;
margin: 0;
font-size: 0.8em;
}
article[role="navigation"] > section i {
font-size: 1.5em;
}
}

@ -0,0 +1,91 @@
header > nav[role="top-nav"] {
display: grid;
text-align: right;
grid-template-columns: 50px auto auto;
height: 100%;
position: relative;
background: var(--white);
border-radius: 3px;
transform-style: preserve-3d;
transform-origin: 100% 50%;
transform: rotateX(0deg);
transition: all 0.1s ease-out;
perspective: 500px;
backface-visibility: hidden;
box-shadow: 2px 2px 0 rgba(var(--primary-rgb) / 30%);
}
header > div[role="notify"] {
display: grid;
height: 100%;
position: relative;
background: var(--black);
border-radius: 3px;
transform-style: preserve-3d;
transform-origin: 100% 50%;
transform: rotateX(180deg);
transition: all 0.3s ease-out;
perspective: 500px;
backface-visibility: hidden;
margin-top: -50px;
box-shadow: 2px 2px 0 rgba(var(--primary-rgb) / 30%);
}
header > div[role="notify"] > div[role="notify-message"] {
display: flex;
height: 86%;
}
header > div[role="notify"] > div[role="notify-message"] div {
display: inline-block;
transition: all 0.2s linear;
}
header
> div[role="notify"]
> div[role="notify-message"]
> div[role="notify-text"] {
color: var(--white);
border-radius: 5px;
height: 79%;
margin-top: 8px;
opacity: 0;
}
header
> div[role="notify"]
> div[role="notify-message"]
> div[role="notify-icons"] {
margin: 5px;
width: 40px;
opacity: 0;
}
header
> div[role="notify"]
> div[role="notify-message"]
> div[role="notify-text"]
span {
display: block;
padding: 5px;
}
header > div[role="notify"] > div[role="notify-message"] i {
display: none;
color: var(--white);
}
i[role="notify-working"] {
animation: 2s infinite linear spin;
height: 40px;
width: 40px;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@ -0,0 +1,111 @@
code[class*="language-"],
pre[class*="language-"] {
color: var(--editor-primary);
background: none;
font-family: var(--mono-type);
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
tab-size: 4;
hyphens: none;
}
pre[class*="language-"] {
margin: 0.1em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: var(--primary);
}
:not(pre) {
& > code[class*="language-"] {
padding: 0.1em;
border-radius: 0.3em;
white-space: normal;
}
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #8292a2;
}
.token.punctuation {
color: var(--editor-secondary);
}
.token.namespace {
opacity: 0.6;
}
.token.keyword {
color: #66d9ef;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.content {
color: var(--editor-tertiary);
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: var(--editor-tag);
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: var(--editor-string);
}
.token.atrule,
.token.attr-value,
.token.function,
.token.class-name {
color: #e6db74;
}
.token.regex,
.token.important {
color: var(--editor-secondary);
}
.token.important,
.token.bold {
font-weight: normal;
}

@ -0,0 +1,446 @@
/* FILE MANAGER */
main > section[role="file-manager"] {
width: 100%;
background: var(--tertiary);
padding: 20px 0;
margin-top: 75px;
}
main > section[role="file-manager"] label[role="list-title"] {
width: 100%;
max-width: 900px;
margin: 0 auto;
display: block;
color: var(--primary);
}
main > section[role="file-manager"] > div[role="file-drop"] {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 100px;
background: var(--white);
color: var(--primary);
vertical-align: middle;
border-radius: 5px;
max-width: 900px;
margin: 10px auto;
}
main > section[role="file-manager"] > div[role="page-images-list"],
main > section[role="file-manager"] > div[role="page-files-list"] {
max-width: 900px;
width: 100%;
margin: 10px auto;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
gap: 10px;
}
main > section[role="file-manager"] > div[role="page-images-list"] > div,
main > section[role="file-manager"] > div[role="page-files-list"] > div {
width: 100%;
height: 150px;
border-radius: 3px;
overflow: hidden;
position: relative;
cursor: pointer;
}
main
> section[role="file-manager"]
> div[role="page-images-list"]
> div
> div.item-progress {
width: 100%;
height: 100%;
background: var(--primary);
}
main > section[role="text-editor"] > div[role="text-editor-control"] button {
border-radius: 0;
}
main
> section[role="text-editor"]
> div[role="text-editor-control"]
button
> i {
font-size: 1.6em;
}
main
> section[role="text-editor"]
> div[role="text-editor-control"]
> button:nth-child(1) {
border-radius: 3px 0 0 3px;
}
main
> section[role="text-editor"]
> div[role="text-editor-control"]
> button:nth-child(10) {
border-radius: 0 3px 3px 0;
}
main
> section[role="file-manager"]
> div[role="page-images-list"]
> div
> button.media-remove,
main
> section[role="file-manager"]
> div[role="page-files-list"]
> div
> button.media-remove {
color: var(--white);
margin: 5px;
}
main
> section[role="file-manager"]
> div[role="page-images-list"]
> div.video-item
> video {
object-fit: cover;
height: 100%;
width: 100%;
}
main
> section[role="file-manager"]
> div[role="page-images-list"]
> div.video-item
> button,
main
> section[role="file-manager"]
> div[role="page-files-list"]
> div.audio-item
> button,
main
> section[role="file-manager"]
> div[role="page-files-list"]
> div.file-item
> button {
position: absolute;
top: 0;
left: 0;
}
main
> section[role="file-manager"]
> div[role="page-files-list"]
> div.audio-item {
background: url("/assets/images/global/upload-audio.png") no-repeat center
center / cover;
}
main
> section[role="file-manager"]
> div[role="page-files-list"]
> div.file-item {
background: url("/assets/images/global/upload-doc.png") no-repeat center
center / cover;
}
main
> section[role="file-manager"]
> div[role="page-files-list"]
> div.file-item
> a {
position: absolute;
bottom: 0;
background: var(--secondary);
padding: 2px;
}
main
> section[role="file-manager"]
> div[role="page-files-list"]
> div.audio-item
> audio {
height: 100%;
width: 100%;
}
/* PAGE META */
main > section[role="page-meta"] {
width: 100%;
background: var(--highlight);
}
main > section[role="page-meta"] > div[role="page-meta-wrapper"] {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
width: 100%;
max-width: 900px;
margin: 10px auto;
color: var(--white);
}
main section[role="page-meta"] textarea#post-title-text {
background: var(--white);
font-family: var(--base-type);
width: 100%;
height: 80px;
font-size: 2em;
color: var(--primary);
}
main section[role="page-meta"] textarea#post-tags {
background: var(--white);
font-family: var(--base-type);
width: 100%;
height: 80px;
color: var(--primary);
}
main section[role="page-meta"] select {
background: var(--primary);
color: var(--secondary);
border-radius: 3px;
border-color: var(--primary);
width: 100%;
height: 45px;
padding: 5px;
font-size: 1.5em;
}
main section[role="page-meta"] div[role="page-options"] {
width: 100%;
}
main
section[role="page-meta"]
div[role="page-meta-wrapper"]
div[role="page-options"]
button {
width: 25%;
height: 45px;
transition: all 0.3s linear;
margin: 0;
border-radius: 0;
display: inline-block;
vertical-align: top;
text-align: center;
}
main
> section[role="page-meta"]
> div[role="page-meta-wrapper"]
> div[role="page-options"]
> button.post-option-btn:nth-child(3) {
border-radius: 3px 0 0 3px;
}
main
> section[role="page-meta"]
> div[role="page-meta-wrapper"]
> div[role="page-options"]
> a
> button {
border-radius: 0 3px 3px 0;
}
main
section[role="page-meta"]
div[role="page-meta-wrapper"]
button[data-active="false"] {
background: var(--primary);
}
main
section[role="page-meta"]
div[role="page-meta-wrapper"]
button[data-active="false"]
svg {
fill: var(--secondary);
}
main
section[role="page-meta"]
div[role="page-meta-wrapper"]
div[role="page-options"]
button[data-active="true"] {
background: var(--tertiary);
}
main
section[role="page-meta"]
div[role="page-meta-wrapper"]
div[role="page-options"]
button[data-active="true"]
svg {
fill: var(--primary);
}
main
section[role="page-meta"]
div[role="page-meta-wrapper"]
div[role="page-created"]
input {
display: none;
visibility: hidden;
}
/* TEXT EDITOR */
main > section[role="text-editor"] {
width: 100%;
max-width: 900px;
margin: 0 auto;
}
main section[role="text-editor"] .icon-hide {
display: none;
visibility: hidden;
}
main > section[role="text-editor"] > div[role="text-editor-control"] {
display: grid;
grid-template-columns: repeat(10, 1fr);
}
.control-freeze {
position: fixed;
z-index: 300;
width: 97%;
top: 65px;
}
main > section[role="text-editor"] > div[role="edit-post-wrapper"] {
width: 100%;
max-width: 900px;
border-radius: 5px;
position: relative;
margin: 10px 0;
}
main
> section[role="text-editor"]
> div[role="edit-post-wrapper"]
textarea:focus {
outline: none;
border-color: var(--highlight);
}
main section[role="text-editor"] div[role="edit-post-wrapper"] #edit,
main section[role="text-editor"] div[role="edit-post-wrapper"] #highlight {
font-family: var(--mono-type);
border: 0;
width: 100%;
min-height: 300px;
height: auto;
position: absolute;
top: 0;
left: 0;
overflow: auto;
word-wrap: normal;
white-space: pre-wrap;
line-break: normal;
font-size: 1.1em;
line-height: 1.2;
padding: 0;
margin: 0;
}
main
section[role="text-editor"]
div[role="edit-post-wrapper"]
#highlight-content {
word-wrap: normal;
white-space: pre-wrap;
line-break: normal;
}
main > section[role="text-editor"] > div[role="edit-post-wrapper"] > #edit {
z-index: 1;
background: transparent;
color: transparent;
caret-color: var(--highlight);
}
main
> section[role="text-editor"]
> div[role="edit-post-wrapper"]
> #highlight {
z-index: 0;
}
main section[role="text-editor"] div[role="edit-post-wrapper"] pre,
main section[role="text-editor"] div[role="edit-post-wrapper"] pre code {
padding: 0;
margin: 0;
}
/* RESPONSIVE */
@media only screen and (max-width: 900px) {
main > section[role="file-manager"] > div[role="file-drop"],
main > section[role="file-manager"] > div[role="page-images-list"],
main > section[role="file-manager"] > div[role="page-files-list"],
main > section[role="page-meta"] > div[role="page-meta-wrapper"],
main > section[role="text-editor"] {
width: 97%;
}
}
@media only screen and (max-width: 480px) {
main > section[role="file-manager"] > div[role="page-images-list"],
main > section[role="file-manager"] > div[role="page-files-list"] {
grid-template-columns: 1fr 1fr 1fr;
}
main > section[role="file-manager"] > div[role="file-drop"],
main > section[role="file-manager"] > div[role="page-images-list"],
main > section[role="file-manager"] > div[role="page-files-list"],
main > section[role="page-meta"] > div[role="page-meta-wrapper"],
main > section[role="text-editor"] {
width: 95%;
}
main > section[role="page-meta"] > div[role="page-meta-wrapper"] {
grid-template-columns: 1fr;
}
main > section[role="text-editor"] > div[role="text-editor-control"] {
display: grid;
grid-template-columns: repeat(5, 1fr);
}
main
> section[role="text-editor"]
> div[role="text-editor-control"]
> button:nth-child(1) {
border-radius: 3px 0;
}
main
> section[role="text-editor"]
> div[role="text-editor-control"]
> button:nth-child(5) {
border-radius: 0 3px;
}
main
> section[role="text-editor"]
> div[role="text-editor-control"]
> button:nth-child(6) {
border-radius: 0 0 0 3px;
}
main
> section[role="text-editor"]
> div[role="text-editor-control"]
> button:nth-child(10) {
border-radius: 0 0 3px;
}
main section[role="text-editor"] div[role="edit-post-wrapper"] #edit,
main section[role="text-editor"] div[role="edit-post-wrapper"] #highlight {
font-size: 0.8em;
}
}

@ -0,0 +1,275 @@
article[role="settings"] {
margin: 100px auto;
width: 100%;
max-width: 900px;
transition: all 0.8s linear;
}
article[role="settings"] h1 {
color: var(--white);
}
article[role="settings"] label {
color: var(--secondary);
margin-bottom: 10px;
display: inline-block;
font-weight: 400;
}
section[role="member-settings"] {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-auto-columns: auto;
gap: 15px;
width: 100%;
max-width: 900px;
margin: 10px auto;
}
section[role="member-settings"] > div:nth-child(1) {
grid-column: 1/2;
grid-row: 1/3;
}
section[role="member-settings"] > div:nth-child(2) {
grid-column: 2/4;
grid-row: 1/3;
}
section[role="member-settings"] > div:nth-child(3) {
grid-row: 1/3;
}
section[role="member-settings"] > div:nth-child(4) {
color: var(--white);
}
section[role="member-settings"] > div:nth-child(4) a {
font-weight: bold;
text-decoration: underline;
color: var(--tertiary);
}
section[role="member-settings"] > div:nth-child(5) {
grid-column: 2/4;
}
section[role="member-settings"] > div:nth-child(6) {
grid-column: 1/3;
}
section[role="member-settings"] > div:nth-child(7) {
grid-column: 3/5;
min-height: 325px;
}
section[role="member-settings"] > div:nth-child(8) {
grid-column: 1/4;
color: var(--white);
word-wrap: break-word;
}
section[role="member-settings"] > div[role="member-avatar"] div,
section[role="member-settings"] > div[role="site-background"] div {
height: 100%;
border-radius: 3px;
}
section[role="member-settings"] div input[type="file"] {
display: none;
visibility: hidden;
}
section[role="member-settings"] div input[type="text"] {
width: 98.4%;
height: 40px;
margin-bottom: 10px;
}
section[role="member-settings"] div textarea {
width: 98.4%;
height: 63%;
}
article[role="settings"] button {
width: 100%;
text-align: left;
height: 50px;
margin-bottom: 5px;
}
article[role="settings"] button > span {
font-size: 0.8em;
display: inline;
top: -10px;
position: relative;
}
section[role="member-settings"]
> div[role="features"]
button[data-enabled="true"] {
color: var(--white);
}
section[role="member-settings"]
> div[role="features"]
button[data-enabled="false"] {
color: var(--primary);
background: var(--secondary);
}
section[role="member-settings"]
> div[role="theme"]
button[data-enabled="true"] {
color: var(--white);
background: var(--highlight);
display: block;
border-radius: 3px;
padding: 3px;
}
section[role="member-settings"]
> div[role="theme"]
button[data-enabled="false"] {
color: var(--primary);
background: var(--secondary);
display: block;
border-radius: 3px;
padding: 3px;
}
section[role="member-settings"] > div[role="mail"] a {
margin-right: 3px;
color: var(--secondary);
display: inline-block;
height: 40px;
padding: 15px 0 0;
border-top: var(--primary) 1px solid;
}
section[role="member-settings"] > div[role="mail"] a[data-enabled="true"] {
color: var(--tertiary);
font-weight: bold;
border-top: var(--highlight) 1px solid;
}
section[role="member-settings"] > div[role="mail"] div[data-enabled="false"] {
visibility: hidden;
display: none;
}
section[role="member-settings"] > div[role="mail"] input {
width: 98.4%;
height: 40px;
padding-left: 5px;
margin-bottom: 10px;
}
section[role="data-settings"] {
color: var(--white);
}
@media only screen and (max-width: 900px) {
main > article[role="settings"] {
width: 97%;
}
}
@media only screen and (max-width: 650px) {
section[role="member-settings"] div input[type="text"] {
width: 99.3%;
height: 40px;
margin-bottom: 10px;
}
section[role="member-settings"] {
grid-template-columns: 1fr 1fr;
}
section[role="member-settings"] > div[role="mail"] input {
width: 97.7%;
height: 40px;
padding-left: 5px;
margin-bottom: 10px;
}
section[role="member-settings"] div textarea {
height: 80px;
width: 99.3%;
}
section[role="member-settings"] > div:nth-child(1) {
grid-column: 1/2;
grid-row: 1/3;
height: 200px;
}
section[role="member-settings"] > div:nth-child(2) {
grid-column: 2/3;
grid-row: 1/3;
}
section[role="member-settings"] > div:nth-child(3) {
grid-column: 1/3;
grid-row: 3/5;
}
section[role="member-settings"] > div:nth-child(4) {
grid-column: 1/2;
}
section[role="member-settings"] > div:nth-child(5) {
grid-column: 2/3;
}
section[role="member-settings"] > div:nth-child(6) {
grid-column: 1/2;
}
section[role="member-settings"] > div:nth-child(7) {
grid-column: 2/3;
min-height: 325px;
}
section[role="member-settings"] > div:nth-child(8) {
grid-column: 1/3;
}
}
@media only screen and (max-width: 530px) {
section[role="member-settings"] > div[role="mail"] input {
width: 98.5%;
}
section[role="member-settings"] {
grid-template-columns: 100%;
}
section[role="member-settings"] div textarea {
height: 80px;
}
section[role="member-settings"] > div:nth-child(1) {
grid-row: 1/3;
height: 200px;
}
section[role="member-settings"] > div:nth-child(2) {
grid-row: 3/5;
grid-column: 1;
height: 200px;
}
section[role="member-settings"] > div:nth-child(3) {
grid-column: 1;
grid-row: 5/7;
}
section[role="member-settings"] > div:nth-child(4),
section[role="member-settings"] > div:nth-child(5),
section[role="member-settings"] > div:nth-child(6),
section[role="member-settings"] > div:nth-child(7),
section[role="member-settings"] > div:nth-child(8) {
grid-column: 1;
}
}

@ -0,0 +1,13 @@
@import url("colors.css");
@import url("forms.css");
@import url("typography.css");
@import url("frame.css");
@import url("icons.css");
@import url("notifications.css");
@import url("init.css");
@import url("index.css");
@import url("book.css");
@import url("page-editor.css");
@import url("page-editor-highlights.css");
@import url("settings.css");
@import url("navigation.css");

@ -0,0 +1,29 @@
:root {
--base-type: helvetica, arial, sans-serif;
--mono-type: "Lucida Console", monaco, monospace;
}
h1,
h2,
h3 {
color: var(--white);
}
h1 {
font-size: 2em;
font-weight: 700;
}
h2 {
font-size: 1.6em;
font-weight: 600;
}
h3 {
font-size: 1.3em;
font-weight: 500;
}
main > article > h1 {
color: var(--primary);
}

@ -1,33 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 486 678" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g id="Logo" transform="matrix(1.36867,0,0,1.36867,-351.696,-71.9183)">
<g transform="matrix(2.31599,0,0,2.31599,218.53,-99.4797)">
<path d="M93.67,140.92L93.67,140.921C105.569,140.921 115.216,150.567 115.216,162.467L115.216,172.724L115.216,172.724L115.216,182.262C115.216,194.161 105.569,203.808 93.669,203.808C81.976,203.217 74.12,195.969 72.237,184.474L72.282,182.737L72.282,162.467L72.205,160.847C72.775,149.587 82.728,141.121 93.67,140.92Z" style="fill:rgb(252,99,153);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.31599,0,0,2.31599,218.53,-437.697)">
<path d="M93.67,211.678L93.67,211.678C105.569,211.678 115.216,221.324 115.216,233.224L115.216,243.481L115.216,243.481L115.216,253.019C115.216,264.919 105.569,274.565 93.669,274.565C81.976,273.975 74.12,266.726 72.237,255.232L72.282,253.495L72.282,233.224L72.205,231.604C72.775,220.344 82.728,211.878 93.67,211.678Z" style="fill:rgb(252,99,153);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.31599,0,0,2.31599,218.53,241.338)">
<path d="M93.67,69.288L93.67,69.288C105.569,69.288 115.216,78.934 115.216,90.834L115.216,101.091L115.216,101.091L115.216,110.629C115.216,122.528 105.569,132.175 93.669,132.175C81.976,131.584 74.12,124.336 72.237,112.842L72.282,111.105L72.282,90.834L72.205,89.214C72.775,77.954 82.728,69.488 93.67,69.288Z" style="fill:rgb(171,183,183);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.31599,0,0,2.31599,218.53,-104.112)">
<path d="M38.059,142.92L38.059,142.921C49.958,142.921 59.605,152.567 59.605,164.467L59.605,174.724L59.605,174.724L59.605,184.262C59.605,196.161 49.958,205.808 38.058,205.808C26.365,205.217 18.509,197.969 16.626,186.474L16.671,184.737L16.671,164.467L16.594,162.847C17.164,151.587 27.117,143.121 38.059,142.92Z" style="fill:rgb(252,99,153);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.31599,0,0,2.31599,218.53,-104.112)">
<path d="M148.331,142.92L148.331,142.921C160.23,142.921 169.877,152.567 169.877,164.467L169.877,174.724L169.877,174.724L169.877,184.262C169.877,196.161 160.23,205.808 148.331,205.808C136.637,205.217 128.782,197.969 126.898,186.474L126.943,184.737L126.943,164.467L126.867,162.847C127.436,151.587 137.389,143.121 148.331,142.92Z" style="fill:rgb(171,183,183);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.31599,0,0,2.31599,218.53,-437.697)">
<path d="M38.059,211.678L38.059,211.678C49.958,211.678 59.605,221.324 59.605,233.224L59.605,243.481L59.605,243.481L59.605,253.019C59.605,264.919 49.958,274.565 38.058,274.565C26.365,273.975 18.509,266.726 16.626,255.232L16.671,253.495L16.671,233.224L16.594,231.604C17.164,220.344 27.117,211.878 38.059,211.678Z" style="fill:rgb(252,99,153);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.31599,0,0,2.31599,218.53,-437.697)">
<path d="M148.331,211.678L148.331,211.678C160.23,211.678 169.877,221.324 169.877,233.224L169.877,243.481L169.877,243.481L169.877,253.019C169.877,264.919 160.23,274.565 148.331,274.565C136.637,273.975 128.782,266.726 126.898,255.232L126.943,253.495L126.943,233.224L126.867,231.604C127.436,220.344 137.389,211.878 148.331,211.678Z" style="fill:rgb(252,99,153);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.31599,0,0,2.31599,218.53,241.338)">
<path d="M38.059,69.288L38.059,69.288C49.958,69.288 59.605,78.934 59.605,90.834L59.605,101.091L59.605,101.091L59.605,110.629C59.605,122.528 49.958,132.175 38.058,132.175C26.365,131.584 18.509,124.336 16.626,112.842L16.671,111.105L16.671,90.834L16.594,89.214C17.164,77.954 27.117,69.488 38.059,69.288Z" style="fill:rgb(252,99,153);fill-rule:nonzero;"/>
</g>
<g transform="matrix(2.31599,0,0,2.31599,218.53,241.338)">
<path d="M148.331,69.288L148.331,69.288C160.23,69.288 169.877,78.934 169.877,90.834L169.877,101.091L169.877,101.091L169.877,110.629C169.877,122.528 160.23,132.175 148.331,132.175C136.637,131.584 128.782,124.336 126.898,112.842L126.943,111.105L126.943,90.834L126.867,89.214C127.436,77.954 137.389,69.488 148.331,69.288Z" style="fill:rgb(171,183,183);fill-rule:nonzero;"/>
</g>
</g>
<svg width="100%" height="100%" viewBox="0 0 462 462" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1.58717,0,0,1.58717,-403.964,-376.506)">
<g transform="matrix(1,0,0,1,200.753,94.1743)">
<circle cx="92.268" cy="181.547" r="38.502" style="fill:rgb(252,99,153);"/>
</g>
<g transform="matrix(1,0,0,1,200.753,201.192)">
<circle cx="92.268" cy="181.547" r="38.502" style="fill:rgb(252,99,153);"/>
</g>
<g transform="matrix(1,0,0,1,307.732,201.192)">
<circle cx="92.268" cy="181.547" r="38.502" style="fill:rgb(252,99,153);"/>
</g>
<g transform="matrix(1,0,0,1,414.761,201.192)">
<circle cx="92.268" cy="181.547" r="38.502" style="fill:rgb(171,183,183);"/>
</g>
<g transform="matrix(1,0,0,1,200.753,308.228)">
<circle cx="92.268" cy="181.547" r="38.502" style="fill:rgb(252,99,153);"/>
</g>
<g transform="matrix(1,0,0,1,307.732,308.228)">
<circle cx="92.268" cy="181.547" r="38.502" style="fill:rgb(171,183,183);"/>
</g>
<g transform="matrix(1,0,0,1,414.761,308.228)">
<circle cx="92.268" cy="181.547" r="38.502" style="fill:rgb(171,183,183);"/>
</g>
<g transform="matrix(1,0,0,1,414.761,94.1743)">
<circle cx="92.268" cy="181.547" r="38.502" style="fill:rgb(252,99,153);"/>
</g>
<g transform="matrix(6.12323e-17,1,-1,6.12323e-17,581.547,183.453)">
<circle cx="92.268" cy="181.547" r="38.502" style="fill:rgb(252,99,153);"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -1,7 +1,11 @@
<?php
require "../vendor/autoload.php";
//load Composer Classes
require '../vendor/autoload.php';
//load Fipamo Classes
require '../brain/_loader.php';
use brain\init\App as App;
use brain\init\App;
//start app
new App();

@ -1,70 +1,70 @@
//** REQUEST TYPES **//
export const REQUEST_TYPE_POST = "POST";
export const REQUEST_TYPE_GET = "GET";
export const REQUEST_TYPE_PUT = "PUT";
export const REQUEST_TYPE_DELETE = "DELETE";
export const REQUEST_TYPE_POST = 'POST';
export const REQUEST_TYPE_GET = 'GET';
export const REQUEST_TYPE_PUT = 'PUT';
export const REQUEST_TYPE_DELETE = 'DELETE';
//** POST CONTENT TYPES **//
export const CONTENT_TYPE_JSON = "json";
export const CONTENT_TYPE_FORM = "x-www-form-urlencoded";
export const CONTENT_TYPE_JSON = 'json';
export const CONTENT_TYPE_FORM = 'x-www-form-urlencoded';
//** API URLS **//
export const API_STATUS = "/api/v1/status";
export const API_GET_SETTINGS = "/api/v1/settings/site";
export const API_GET_MEMBER_INFO = "/api/v1/settings/member";
export const API_NEW_PAGE = "/api/v1/page/create";
export const API_EDIT_PAGE = "/api/v1/page/write";
export const API_DELETE_PAGE = "/api/v1/page/delete";
export const API_SETTINGS_SYNC = "/api/v1/settings/sync";
export const API_PUBLISH_PAGES = "/api/v1/settings/publish";
export const API_NAV_SYNC = "/api/v1/settings/nav-sync";
export const API_REINDEX_PAGES = "/api/v1/settings/reindex";
export const API_SEND_MAIL = "/api/v1/mailer";
export const API_LOGIN = "/api/v1/login";
export const API_STATUS = '/api/v1/status';
export const API_GET_SETTINGS = '/api/v1/settings/site';
export const API_GET_MEMBER_INFO = '/api/v1/settings/member';
export const API_NEW_PAGE = '/api/v1/page/create';
export const API_EDIT_PAGE = '/api/v1/page/write';
export const API_DELETE_PAGE = '/api/v1/page/delete';
export const API_SETTINGS_SYNC = '/api/v1/settings/sync';
export const API_PUBLISH_PAGES = '/api/v1/settings/publish';
export const API_NAV_SYNC = '/api/v1/settings/nav-sync';
export const API_REINDEX_PAGES = '/api/v1/settings/reindex';
export const API_SEND_MAIL = '/api/v1/mailer';
export const API_LOGIN = '/api/v1/login';
//** API TASKS **//
export const AUTH_STATUS = "getAuthStatus";
export const TASK_SETTINGS_WRITE = "writeSettings";
export const TASK_PUBLISH_SITE = "publishSite";
export const TASK_PAGE_CREATE = "createNewPage";
export const TASK_PAGE_EDIT = "editPage";
export const TASK_PAGE_DELETE = "deletePage";
export const TASK_SEND_MAIL = "sendMail";
export const TASK_REINDEX_PAGE = "reIndexPages";
export const TASK_SYNC_SETTNIGS = "syncSite";
export const TASK_SYNC_NAV = "syncNav";
export const TASK_GET_SETTINGS = "getSiteSettings";
export const TASK_GET_MEMBER_INFO = "getMemberInfo";
export const AUTH_STATUS = 'getAuthStatus';
export const TASK_SETTINGS_WRITE = 'writeSettings';
export const TASK_PUBLISH_SITE = 'publishSite';
export const TASK_PAGE_CREATE = 'createNewPage';
export const TASK_PAGE_EDIT = 'editPage';
export const TASK_PAGE_DELETE = 'deletePage';
export const TASK_SEND_MAIL = 'sendMail';
export const TASK_REINDEX_PAGE = 'reIndexPages';
export const TASK_SYNC_SETTNIGS = 'syncSite';
export const TASK_SYNC_NAV = 'syncNav';
export const TASK_GET_SETTINGS = 'getSiteSettings';
export const TASK_GET_MEMBER_INFO = 'getMemberInfo';
//** API STATUS **//
export const API_ACCESS_GOOD = "apiUseAuthorized";
export const API_ACCESS_BAD = "apiUseNotAuthorized";
export const API_ACCESS_GOOD = 'apiUseAuthorized';
export const API_ACCESS_BAD = 'apiUseNotAuthorized';
/**
* A can of methods used to edit install settings, navigation pages and content pages
*/
class FipamoAdminAPI {
/**
* @constructor
* @param {string} baseURL - url of site; uses local when empty
* @param {object} progressBar - element to be used to display upload progress
*/
constructor(baseURL = null, progressBar = null) {
this.percentComplete = 0; //for later
this.baseURL = null;
this.progressBar = progressBar;
this.status = false;
if (baseURL) this.baseURL = baseURL;
//asks server if a session is active
this._request(this.baseURL ? this.baseURL + API_STATUS : API_STATUS).then(
(response) => {
if (response.type === API_ACCESS_GOOD) {
this.token = response.token;
} else {
//don't set token
//console.log("NO TOKEN");
}
}
);
}
/**
/**
* @constructor
* @param {string} baseURL - url of site; uses local when empty
* @param {object} progressBar - element to be used to display upload progress
*/
constructor(baseURL = null, progressBar = null) {
this.percentComplete = 0; //for later
this.baseURL = null;
this.progressBar = progressBar;
this.status = false;
if (baseURL) this.baseURL = baseURL;
//asks server if a session is active
this._request(this.baseURL ? this.baseURL + API_STATUS : API_STATUS).then(
response => {
if (response.type === API_ACCESS_GOOD) {
this.token = response.token;
} else {
//don't set token
//console.log("NO TOKEN");
}
}
);
}
/**
* Promise method for authenticating and starting a session\
* **POST**`/api/v1/login`
* @param {Object[]} data - json object that contains data for set up
@ -82,26 +82,26 @@ class FipamoAdminAPI {
}
```
*/
login(data) {
return new Promise((resolve, reject) => {
this.baseURL ? (data.remote = true) : (data.remote = false);
this.key ? (data.key = this.key) : (data.key = null);
this._request(
this.baseURL ? this.baseURL + API_LOGIN : API_LOGIN,
AUTH_STATUS,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
data
)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
/**
login(data) {
return new Promise((resolve, reject) => {
this.baseURL ? (data.remote = true) : (data.remote = false);
this.key ? (data.key = this.key) : (data.key = null);
this._request(
this.baseURL ? this.baseURL + API_LOGIN : API_LOGIN,
AUTH_STATUS,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
data
)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
* Method for saving site and navigation settings\
* **POST**`/api/v1/settings/:task`
* @param {string} task - settings being synced `config | navigation`
@ -140,34 +140,34 @@ class FipamoAdminAPI {
}
```
*/
sync(task, data) {
return new Promise((resolve, reject) => {
let url = "";
switch (task) {
case "syncSite":
url = API_SETTINGS_SYNC;
break;
case "syncNav":
url = API_NAV_SYNC;
break;
}
this._request(
this.baseURL ? this.baseURL + url : url,
TASK_SETTINGS_WRITE,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
data
)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
sync(task, data) {
return new Promise((resolve, reject) => {
let url = '';
switch (task) {
case 'syncSite':
url = API_SETTINGS_SYNC;
break;
case 'syncNav':
url = API_NAV_SYNC;
break;
}
this._request(
this.baseURL ? this.baseURL + url : url,
TASK_SETTINGS_WRITE,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
data
)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
/**
* Method for retrieving user authorizing user login
* @param {object[]} data - json object that contains task
* @param {string} data[].task - publishing task
@ -183,26 +183,26 @@ class FipamoAdminAPI {
}
* ```
*/
publish(data) {
return new Promise((resolve, reject) => {
this._request(
//API_PUBLISH_PAGES,
this.baseURL ? this.baseURL + API_PUBLISH_PAGES : API_PUBLISH_PAGES,
TASK_PUBLISH_SITE,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
data
)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
publish(data) {
return new Promise((resolve, reject) => {
this._request(
//API_PUBLISH_PAGES,
this.baseURL ? this.baseURL + API_PUBLISH_PAGES : API_PUBLISH_PAGES,
TASK_PUBLISH_SITE,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
data
)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
/**
* Method for handling page creating and editing\
* **POST**`/api/v1/page/:task`
* @param {string} task - current page action
@ -233,55 +233,55 @@ class FipamoAdminAPI {
}
```
*/
pageActions(task, data) {
let url, event, content;
switch (task) {
case TASK_PAGE_CREATE:
url = API_NEW_PAGE;
event = TASK_PAGE_CREATE;
content = CONTENT_TYPE_FORM;
break;
case TASK_PAGE_EDIT:
url = API_EDIT_PAGE;
event = TASK_PAGE_EDIT;
content = CONTENT_TYPE_FORM;
break;
pageActions(task, data) {
let url, event, content;
switch (task) {
case TASK_PAGE_CREATE:
url = API_NEW_PAGE;
event = TASK_PAGE_CREATE;
content = CONTENT_TYPE_JSON;
break;
case TASK_PAGE_EDIT:
url = API_EDIT_PAGE;
event = TASK_PAGE_EDIT;
content = CONTENT_TYPE_JSON;
break;
case TASK_PAGE_DELETE:
url = API_DELETE_PAGE;
event = TASK_PAGE_DELETE;
content = CONTENT_TYPE_FORM;
break;
case TASK_PAGE_DELETE:
url = API_DELETE_PAGE;
event = TASK_PAGE_DELETE;
content = CONTENT_TYPE_JSON;
break;
default:
break;
}
default:
break;
}
if (this.baseURL) {
//data.key = this.key;
data.remote = true;
} else {
data.remote = false;
}
if (this.baseURL) {
//data.key = this.key;
data.remote = true;
} else {
data.remote = false;
}
return new Promise((resolve, reject) => {
this._request(
this.baseURL ? this.baseURL + url : url,
event,
REQUEST_TYPE_POST,
content,
data
)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
return new Promise((resolve, reject) => {
this._request(
this.baseURL ? this.baseURL + url : url,
event,
REQUEST_TYPE_POST,
content,
data
)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
/**
* Method for sending mail (if completed in settings)\
* **POST**`/api/v1/mailer`
* @param {object[]} message - json object that contains items to be included in main site navigation
@ -298,25 +298,25 @@ class FipamoAdminAPI {
}
```
*/
sendMail(message) {
return new Promise((resolve, reject) => {
this._request(
this.baseURL ? this.baseURL + API_SEND_MAIL : API_SEND_MAIL,
TASK_SEND_MAIL,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
message
)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
sendMail(message) {
return new Promise((resolve, reject) => {
this._request(
this.baseURL ? this.baseURL + API_SEND_MAIL : API_SEND_MAIL,
TASK_SEND_MAIL,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
message
)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
/**
* *Promise method for retrieving site and member info*\
* **GET** `/api/v1/settings/:type`
* @param {string} type - type of info requested ['site'|'member'];
@ -336,97 +336,97 @@ class FipamoAdminAPI {
* ```
*/
getInfo(type) {
let url, task;
if (type == "site") {
url = API_GET_SETTINGS;
task = TASK_GET_SETTINGS;
} else {
url = API_GET_MEMBER_INFO;
task = TASK_GET_MEMBER_INFO;
}
return new Promise((resolve, reject) => {
this._request(this.baseURL ? this.baseURL + url : url, task)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
}
getInfo(type) {
let url, task;
if (type == 'site') {
url = API_GET_SETTINGS;
task = TASK_GET_SETTINGS;
} else {
url = API_GET_MEMBER_INFO;
task = TASK_GET_MEMBER_INFO;
}
return new Promise((resolve, reject) => {
this._request(this.baseURL ? this.baseURL + url : url, task)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
//--------------------------
// private
//--------------------------
_request(
requestURL,
eventType,
requestType = REQUEST_TYPE_GET,
contentType = CONTENT_TYPE_JSON,
requestData = null
) {
var self = this;
return new Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
request.upload.addEventListener("progress", (e) =>
self.handleLoadProgress(e, self.progressBar)
);
request.open(requestType, requestURL, true);
request.onload = () => {
if (request.status == 200) {
let response = JSON.parse(request["response"]);
resolve(response);
} else {
let error = JSON.parse(request["response"]);
reject(error);
}
};
if (requestType == REQUEST_TYPE_PUT || requestType == REQUEST_TYPE_POST) {
if (
eventType === TASK_SETTINGS_WRITE ||
eventType === TASK_PAGE_EDIT ||
eventType === TASK_PAGE_CREATE ||
eventType === TASK_PAGE_DELETE ||
eventType === TASK_PUBLISH_SITE ||
eventType === TASK_REINDEX_PAGE
)
request.setRequestHeader("fipamo-access-token", self.token);
//--------------------------
// private
//--------------------------
_request(
requestURL,
eventType,
requestType = REQUEST_TYPE_GET,
contentType = CONTENT_TYPE_JSON,
requestData = null
) {
var self = this;
return new Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
request.upload.addEventListener('progress', e =>
self.handleLoadProgress(e, self.progressBar)
);
request.open(requestType, requestURL, true);
request.onload = () => {
if (request.status == 200) {
let response = JSON.parse(request['response']);
resolve(response);
} else {
let error = JSON.parse(request['response']);
reject(error);
}
};
if (requestType == REQUEST_TYPE_PUT || requestType == REQUEST_TYPE_POST) {
if (
eventType === TASK_SETTINGS_WRITE ||
eventType === TASK_PAGE_EDIT ||
eventType === TASK_PAGE_CREATE ||
eventType === TASK_PAGE_DELETE ||
eventType === TASK_PUBLISH_SITE ||
eventType === TASK_REINDEX_PAGE
)
request.setRequestHeader('fipamo-access-token', self.token);
switch (contentType) {
case CONTENT_TYPE_JSON:
request.setRequestHeader(
"Content-type",
"application/" + contentType
);
request.send(JSON.stringify(requestData));
break;
case CONTENT_TYPE_FORM:
request.send(requestData);
break;
}
} else {
if (
eventType === TASK_GET_SETTINGS ||
eventType === TASK_GET_MEMBER_INFO
) {
request.setRequestHeader("fipamo-access-token", self.token);
}
request.send();
}
});
}
switch (contentType) {
case CONTENT_TYPE_JSON:
request.setRequestHeader(
'Content-type',
'application/' + contentType
);
request.send(JSON.stringify(requestData));
break;
case CONTENT_TYPE_FORM:
request.send(requestData);
break;
}
} else {
if (
eventType === TASK_GET_SETTINGS ||
eventType === TASK_GET_MEMBER_INFO
) {
request.setRequestHeader('fipamo-access-token', self.token);
}
request.send();
}
});
}
//--------------------------
// event handlers
//--------------------------
handleLoadProgress(e, progressBar) {
let percent = Math.ceil((e.loaded / e.total) * 100);
//if a progress bar element is present, talk to it
if (progressBar != null) {
progressBar.style.width = percent + "%";
}
}
//--------------------------
// event handlers
//--------------------------
handleLoadProgress(e, progressBar) {
let percent = Math.ceil((e.loaded / e.total) * 100);
//if a progress bar element is present, talk to it
if (progressBar != null) {
progressBar.style.width = percent + '%';
}
}
}
export { FipamoAdminAPI as default };

@ -0,0 +1,43 @@
@use "sass:color";
//CSS
//Bulma
@import "../../node_modules/bulma/sass/utilities/_all";
@import "../../node_modules/bulma/sass/grid/columns";
//Colors
@import "main/_colors";
//Mixins
@import "main/_mixins";
//Normalize
@import "main/_normalize";
//Typography
@import "main/_typography";
//Main Structure
@import "main/_structure";
//Index
@import "main/_index";
//Settings
@import "main/_settings";
//Error
@import "main/_error";
//Navigation
@import "main/_navigation";
//Forms
@import "main/_forms";
//Posts
@import "main/_posts";
//Editor
@import "main/_calendar";
@import "main/_editor";
@import "main/_editor-highlight";

@ -0,0 +1,327 @@
.dp-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 2000;
}
.dp {
position: relative;
background: $primary;
box-shadow: 2px 2px 16px rgba(#000, 0.25);
line-height: 1.4;
border-radius: 4px;
max-height: 400px;
z-index: 5000;
padding-top: 6px;
overflow: hidden;
-webkit-tap-highlight-color: transparent;
}
.dp::before {
content: ' ';
height: 6px;
position: absolute;
top: 0;
left: 0;
right: 0;
background: $highlight;
}
.dp-permanent .dp {
padding-top: 0;
border: 1px solid #eee;
box-shadow: none;
}
.dp-permanent .dp::before {
display: none;
}
.dp-cal {
min-height: 300px;
}
.dp-below {
position: absolute;
font-size: 0.8em;
width: 400px;
max-width: 90vw;
}
.dp-permanent {
position: relative;
font-size: 0.8em;
width: 400px;
max-width: 100vw;
}
.dp-permanent .dp {
z-index: 0;
}
.dp-modal .dp {
position: absolute;
top: 50%;
left: 50%;
max-width: 600px;
width: calc(100% - 4em);
transform: translate(-50%, -50%);
animation: slide-up 0.3s forwards;
}
.dp-months {
padding: 24px;
}
.dp-years {
box-sizing: border-box;
max-height: 400px;
padding: 8px 0;
/* HACK for Chrome on Android */
overflow: auto !important;
}
.dp-cal-month,
.dp-cal-year,
.dp-day,
.dp-month,
.dp-year {
box-sizing: border-box;
text-align: center;
text-decoration: none;
position: relative;
color: $white;
border-radius: 2px;
border: 0;
background: transparent;
}
.dp-cal-header {
position: relative;
text-align: center;
padding-bottom: 16px;
background: color.adjust($primary, $lightness: -10%);
}
.dp-next,
.dp-prev {
position: absolute;
width: 30px;
height: 30px;
overflow: hidden;
top: 14px;
color: color.adjust($primary, $lightness: -50%);
border-radius: 2px;
border: 0;
background: transparent;
}
.dp-prev {
left: 24px;
}
.dp-next {
right: 24px;
}
.dp-next:focus,
.dp-prev:focus,
.dp-next:hover,
.dp-prev:hover {
outline: none;
color: inherit;
}
.dp-prev::before,
.dp-next::before {
content: '';
border: 2px solid;
width: 10px;
height: 10px;
display: inline-block;
transform: rotate(-45deg);
transition: border-color 0.2s;
margin: 9px 0 40px 4px;
}
.dp-prev::before {
border-right: 0;
border-bottom: 0;
}
.dp-next::before {
border-left: 0;
border-top: 0;
margin-left: 0;
margin-right: 4px;
}
.dp-cal-month,
.dp-cal-year {
display: inline-block;
font-size: 1.4em;
padding: 16px 8px 8px;
outline: none;
}
.dp-cal-footer {
text-align: center;
background: color.adjust($primary, $lightness: -10%);
}
.dp-day-today::after {
content: '';
height: 0;
width: 0;
border: 7px solid $highlight;
border-bottom-color: transparent;
border-left-color: transparent;
position: absolute;
top: 0;
right: 0;
}
.dp-close,
.dp-clear,
.dp-today {
box-sizing: border-box;
display: inline-block;
width: 33%;
padding: 8px;
text-decoration: none;
color: color.adjust($primary, $lightness: -50%);
border: 0;
background: transparent;
}
.dp-permanent .dp-close,
.dp-permanent .dp-clear {
display: none;
}
.dp-close:active,
.dp-clear:active,
.dp-today:active,
.dp-next:active,
.dp-prev:active,
.dp-cal-month:active,
.dp-cal-year:active {
background: $highlight;
color: $white;
}
@media screen and (min-device-width: 1200px) {
.dp-close:hover,
.dp-close:focus,
.dp-clear:hover,
.dp-clear:focus,
.dp-today:hover,
.dp-today:focus,
.dp-next:hover,
.dp-next:focus,
.dp-prev:hover,
.dp-prev:focus,
.dp-cal-month:focus,
.dp-cal-month:hover,
.dp-cal-year:hover,
.dp-cal-year:focus {
background: $highlight;
color: $white;
}
}
.dp-col-header,
.dp-day {
width: 14.2857%;
display: inline-block;
padding: 8px;
text-align: center;
}
.dp-col-header {
color: #aaa;
text-transform: uppercase;
font-weight: 300;
font-size: 0.8em;
padding: 8px 0;
}
.dp-month {
width: 33%;
display: inline-block;
padding: 8px;
}
.dp-year {
display: block;
padding: 8px 40px;
width: 100%;
}
.dp-edge-day {
color: #aaa;
}
.dp-current,
.dp-day:hover,
.dp-month:hover,
.dp-year:hover,
.dp-current:focus,
.dp-day:focus,
.dp-month:focus,
.dp-year:focus {
outline: none;
background: color.adjust($primary, $lightness: -40%);
color: $white;
}
.dp-selected:hover,
.dp-selected:focus,
.dp-selected {
background: $highlight;
color: color.adjust($primary, $lightness: -60%);
}
.dp-day-disabled {
background: transparent;
color: #ddd;
}
.dp-day-disabled:focus,
.dp-day-disabled:hover {
background: #ddd;
}
.dp-focuser {
position: absolute;
z-index: 0;
top: 50%;
left: 50%;
}
// Responsive
@media (max-width: 480px), (max-height: 480px) {
.dp-modal .dp {
font-size: 0.9em;
width: auto;
width: 100%;
}
.dp-day-of-week,
.dp-day {
padding: 8px;
}
}
@keyframes slide-up {
0% {
transform: translate(-50%, 100%);
}
100% {
transform: translate(-50%, -50%);
}
} ;

@ -0,0 +1,16 @@
$primary: #1d3040;
$secondary: #b2cce5;
$tertiary: #f5ab35;
$highlight: #fc6399;
$white: #efebe3;
$grey: #abb7b7;
$black: #32302f;
// editor colors
$event-cool: #32cd32;
$event-lame: #f64747;
$editor-primary: #fde3a7;
$editor-secondary: #e7903c;
$editor-tertiary: #6bb9f0;
$editor-string: #dcc6e0;
$editor-tag: #e73c4e;

@ -0,0 +1,117 @@
code[class*='language-'],
pre[class*='language-'] {
color: $editor-primary;
background: none;
text-shadow: 0 1px rgba(#000, 0.3);
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
tab-size: 4;
hyphens: none;
}
pre[class*='language-'] {
// padding: 1em
margin: 0.1em 0;
overflow: auto;
border-radius: 0.3em;
}
:not(pre) > code[class*='language-'],
pre[class*='language-'] {
background: $primary;
}
:not(pre) {
& > code[class*='language-'] {
padding: 0.1em;
border-radius: 0.3em;
white-space: normal;
}
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #8292a2;
}
.token {
&.punctuation {
color: $editor-secondary;
}
&.namespace {
opacity: 0.6;
}
&.keyword {
color: #66d9ef;
}
&.italic {
font-style: italic;
}
&.entity {
cursor: help;
}
&.content {
color: $editor-tertiary;
}
}
.token.property,
.token.tag,
.token.constant,
.token.symbol,
.token.deleted {
color: $editor-tag;
}
.token.boolean,
.token.number {
color: #ae81ff;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #a6e22e;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string,
.token.variable {
color: $editor-string;
}
.token.atrule,
.token.attr-value,
.token.function,
.token.class-name {
color: #e6db74;
}
.token.regex,
.token.important {
color: $editor-secondary;
}
.token.important,
.token.bold {
font-weight: bold;
}

@ -0,0 +1,120 @@
#edit-control {
// margin 10px
top: 1px;
border-radius: 3px;
width: 100%;
max-width: 880px;
margin-top: 30px;
z-index: 2000;
button {
background: $secondary;
width: 10%;
height: 39px;
transition: all 0.3s linear;
margin: 0;
border-radius: 0;
display: inline-block;
vertical-align: top;
text-align: center;
svg.icons {
fill: $primary;
}
}
button:nth-child(1) {
border-radius: 3px 0 0 3px;
}
button:nth-child(10) {
border-radius: 0 3px 3px 0;
}
button:hover {
background: color.adjust($secondary, $lightness: -20%);
}
#option-update {
padding: 5px 5px 1px;
display: inline-block;
vertical-align: top;
text-align: center;
}
.icon-hide {
display: none;
visibility: hidden;
}
.submit-start {
background: $white;
svg {
fill: $event-cool !important;
}
}
.submit-cool {
background: $event-cool;
svg {
fill: $white;
}
}
.submit-delete {
background: $event-lame !important;
svg {
fill: $white !important;
}
}
#option-date {
height: 30px;
padding-top: 6px;
svg {
margin: -13px 5px 0 0;
display: inline-block;
vertical-align: top;
fill: $white;
}
}
.content-editor-btn-icon {
padding: 5px 5px 1px;
// border-radius 20px
color: $primary;
svg.edit-btn-icon {
fill: $primary;
}
}
.content-editor-btn-text {
padding: 5px;
// border-radius 20px
color: $primary;
}
#option-bold {
font-weight: bold;
text-decoration: none;
}
#option-italic {
font-weight: bold;
text-decoration: none;
font-style: italic;
}
#option-strikethrough {
font-weight: bold;
text-decoration: line-through;
font-style: italic;
}
}

@ -0,0 +1,27 @@
#error-index {
width: 100%;
max-width: 900px;
margin: 0 auto;
padding: 10px;
height: 100%;
z-index: 10;
position: relative;
label#title {
font-size: 100px;
color: $highlight;
font-weight: 500;
}
label#message {
font-size: 50px;
color: $tertiary;
font-weight: 500;
}
label#error {
font-size: 25px;
color: $event-lame;
font-weight: 500;
}
}

@ -0,0 +1,71 @@
form {
display: inline-block;
a {
color: $primary;
}
p {
background: $tertiary;
color: $primary;
padding: 5px;
display: block;
border-radius: 5px;
text-align: left;
}
}
input[type="email"],
input[type="password"],
input[type="text"] {
border: 0;
border-radius: 5px;
padding: 5px;
margin: 10px 5px 0 0;
font: 18px $basetype;
display: inline-block;
background: $primary;
color: $tertiary;
}
textarea {
border: 0;
border-radius: 3px;
color: $white;
font: 15px $basetype;
background: $primary;
}
button,
input[type="submit"] {
background: $highlight;
color: $primary;
font: 20px $basetype;
border-radius: 5px;
position: relative;
cursor: pointer;
border: 0;
padding: 10px 0 5px 0;
transition: all 0.3s linear;
}
select {
font: 14px $basetype;
border: 1px solid $secondary;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
color: $primary;
}
::-webkit-input-placeholder {
font: 25px $basetype;
color: $white;
}
:-moz-placeholder {
/* Firefox 18- */
font: 25px $basetype;
color: $white;
}
::-moz-placeholder {
/* Firefox 19+ */
font: 15px $basetype;
color: $white;
}
:-ms-input-placeholder {
font: 25px $basetype;
color: $white;
}

@ -0,0 +1,381 @@
#dash-index-content {
width: 100%;
height: 100%;
margin: 0 auto;
#dash-index {
width: 100%;
height: 100%;
z-index: 10;
position: relative;
#dash-index-wrapper {
width: 100%;
height: 100%;
margin: 0 auto;
.dash-init,
.dash-restore {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
color: $primary;
form {
background: $white;
padding: 15px;
width: 300px;
border-radius: 5px;
text-align: center;
#the-logo {
width: 40px;
margin: 20px;
}
input {
width: 290px;
margin: 0 0 10px;
height: 30px;
}
button {
width: 300px;
}
div {
background: $primary;
color: $white;
border-radius: 3px;
padding: 5px;
label {
display: block;
padding: 5px;
color: $tertiary;
}
}
}
}
.dash-restore {
display: none;
visibility: hidden;
}
#dash-login {
width: 100%;
height: 100%;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
#dash-form,
#dash-reset {
width: 300px;
padding: 0.75em;
background: $white;
border-radius: 5px;
color: $white;
text-align: center;
#the-logo {
width: 40px;
margin: 20px;
}
input {
width: 290px;
margin: 0 0 10px;
height: 30px;
}
button {
width: 300px;
}
}
}
#dash-menu {
padding: 10px;
width: 90%;
max-width: 900px;
margin: 50px auto;
a {
display: inline-block;
vertical-align: top;
background: color.adjust($primary, $lightness: -60%);
width: 30%;
padding: 5px;
border-radius: 3px;
color: $white;
margin: 0 10px 10px 0;
&:hover {
background: color.adjust($primary, $lightness: -60%);
}
svg {
display: inline-block;
vertical-align: top;
fill: $white;
}
label {
display: inline-block;
margin-top: 5px;
width: 85%;
text-align: center;
cursor: pointer;
}
}
}
#dash-recent {
width: 100%;
max-width: 900px;
height: 100%;
padding: 5px 0 0;
margin: 0 auto;
#recent-list {
// padding: 5px
position: relative;
.recent-header {
height: 50px;
margin-top: 5px;
.index-header-left {
vertical-align: top;
display: inline-block;
width: 50%;
color: $white;
font-size: 3em;
}
.index-header-right {
width: 50%;
text-align: right;
vertical-align: top;
display: inline-block;
right: 10px;
color: $white;
a {
button {
border-radius: 3px;
margin-left: 10px;
svg {
transition: all 0.2s linear;
width: 40px;
height: 20px;
fill: $white;
}
}
}
}
}
a.post-link,
a.post-video-link {
font-size: 1.5em;
font-weight: 300;
display: inline-block;
border-radius: 3px;
vertical-align: top;
text-decoration: none;
position: relative;
overflow: hidden;
.post-video {
width: 100%;
height: 100%;
object-fit: cover;
position: absolute;
}
label {
font-size: 1.4em;
font-weight: 700;
color: $white;
padding: 5px;
vertical-align: top;
display: inline-block;
word-wrap: break-word;
width: 100%;
text-align: center;
position: relative;
top: 35%;
text-shadow: 2px 2px 0 rgba($black, 1);
}
div#options {
width: 100%;
position: absolute;
bottom: 0;
border-radius: 0 0 3px 3px;
background: linear-gradient(
to bottom,
rgba(#000, 0) 0%,
rgba(#000, 0.65) 100%
);
#option-left {
display: inline-block;
vertical-align: top;
width: 50%;
position: relative;
background: none;
button {
border-radius: 3px;
background: $primary;
margin: 0 0 10px 10px;
svg {
// @include object-transitions(0.1s)
width: 40px;
height: 20px;
fill: $secondary;
}
}
.item-options {
border-radius: 3px;
margin: 5px;
display: inline-block;
}
button[data-active='false'] {
background: $primary;
svg {
fill: $secondary;
}
}
button[data-active='true'] {
background: $tertiary;
svg {
fill: $primary;
}
}
}
#option-right {
display: inline-block;
width: 50%;
text-align: right;
span {
font-weight: bold;
display: block;
background: $white;
color: $primary;
border-radius: 3px;
font-size: 0.6em;
text-align: center;
position: relative;
padding: 5px;
float: right;
margin: 0 10px 0 0;
bottom: -15px;
}
}
}
}
a.recent-link:nth-child(3) {
width: 100%;
margin-bottom: 20px;
height: 500px;
}
a.recent-link:nth-child(4),
a.recent-link:nth-child(6) {
width: 48.6%;
height: 350px;
margin: 0 10px 20px 0;
}
a.recent-link:nth-child(5),
a.recent-link:nth-child(7) {
width: 48.6%;
height: 350px;
margin: 0 0 20px 10px;
}
}
}
}
}
}
@media only screen and (max-width: 768px) {
#dash-index-content {
#dash-index {
#dash-index-wrapper {
#dash-recent {
#recent-list {
a:nth-child(4),
a:nth-child(6) {
width: 48.9%;
}
}
}
}
}
}
}
@media only screen and (max-width: 640px) {
#dash-index-content {
#dash-index {
#dash-index-wrapper {
#dash-recent {
#recent-list {
a:nth-child(4),
a:nth-child(6) {
width: 48.5%;
}
}
}
}
}
}
}
@media only screen and (max-width: 480px) {
#dash-index-content {
#dash-index {
#dash-index-wrapper {
#dash-recent {
#recent-list {
.recent-header {
h3 {
width: 40%;
}
.index-menu {
width: 60%;
}
}
a.dash-link:nth-child(3),
a.dash-link:nth-child(4),
a.dash-link:nth-child(5),
a.dash-link:nth-child(6),
a.dash-link:nth-child(7) {
width: 100%;
margin: 15px 0 0;
height: 400px;
}
}
}
}
}
}
} ;

@ -0,0 +1,7 @@
@mixin background-opacity($rgb_value, $opacity) {
background: rgba($rgb_value, $opacity);
}
@mixin custom-header($weight, $size, $line_height, $color) {
font: $weight $size/$line_height $bodyTypeSans;
color: $color;
}

@ -0,0 +1,93 @@
#nav-index {
width: 100%;
max-width: 900px;
margin: 0 auto;
#nav-index-wrapper {
#nav-pages {
.nav-item {
display: block;
width: 98%;
background: $white;
border-radius: 3px;
color: $highlight;
height: 30px;
padding: 10px;
margin: 0 0 10px;
font-size: 1.5em;
cursor: move;
#item-arrows {
fill: $primary;
width: 40px;
height: 30px;
}
}
label {
display: inline-block;
vertical-align: middle;
padding: 0;
margin: -15px 0 0 10px;
cursor: move;
}
#nav-btns {
float: right;
top: -5px;
position: relative;
button {
color: $white;
border-radius: 3px;
width: 40px;
margin: 0 10px;
svg {
fill: $white;
width: 25px;
height: 20px;
}
}
}
}
}
}
@media only screen and (max-width: 375px) {
#nav-index {
#nav-index-wrapper {
#nav-pages {
.nav-item {
width: 94.5%;
font-size: 1em;
label {
width: 40%;
vertical-align: top;
margin-top: 0;
line-height: 1em;
}
}
}
}
}
}
@media only screen and (max-width: 320px) {
#nav-index {
#nav-index-wrapper {
#nav-pages {
.nav-item {
width: 94.5%;
font-size: 1em;
label {
width: 37%;
vertical-align: top;
margin-top: 0;
line-height: 1em;
}
}
}
}
}
} ;

@ -0,0 +1,351 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
// -webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input {
/* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select {
/* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type='button'],
[type='reset'],
[type='submit'] {
// -webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type='button']::-moz-focus-inner,
[type='reset']::-moz-focus-inner,
[type='submit']::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type='button']:-moz-focusring,
[type='reset']:-moz-focusring,
[type='submit']:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type='checkbox'],
[type='radio'] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type='number']::-webkit-inner-spin-button,
[type='number']::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type='search'] {
// -webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type='search']::-webkit-search-decoration {
// -webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
// -webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

@ -0,0 +1,701 @@
#post-index {
width: 100%;
max-width: 900px;
margin: 0 auto;
#post-index-wrapper {
// padding: 0.75rem
overflow: hidden;
#post-index-header {
margin: 10px 0 0;
#post-index-header-left {
text-transform: capitalize;
display: inline-block;
width: 50%;
color: $white;
font-size: 3em;
}
#post-index-header-right {
text-align: right;
display: inline-block;
width: 50%;
a {
button {
color: $white;
border-radius: 3px;
margin-left: 10px;
width: 55px;
}
svg {
transition: all 0.1s linear;
width: 20px;
height: 17px;
fill: $white;
}
}
.current-filter {
color: $highlight;
text-decoration-color: $secondary;
}
}
}
#posts-list {
margin: 20px 0 0;
a.page-link {
background: $white;
display: inline-block;
vertical-align: top;
width: 100%;
text-decoration: none;
margin: 0 0 20px;
border-radius: 3px;
overflow: hidden;
color: color.adjust($primary, $lightness: -60%);
label {
font-size: 2em;
font-weight: 500;
padding: 10px;
display: inline-block;
vertical-align: top;
width: 100%;
}
div.page-bg,
div.page-video {
video {
width: 100%;
position: absolute;
}
width: 100%;
height: 350px;
background-color: $highlight;
position: relative;
label {
font-size: 2em;
font-weight: 700;
color: $white;
padding: 5px;
vertical-align: top;
display: inline-block;
word-wrap: break-word;
width: 100%;
text-align: center;
position: relative;
top: 35%;
text-shadow: 2px 2px 0 rgba($black, 1);
}
#meta {
width: 100%;
background: linear-gradient(
to bottom,
rgba(#000, 0%) 0%,
rgba(#000, 0.65%) 100%
);
border-radius: 3px;
margin: auto;
bottom: 0;
position: absolute;
padding: 0 0 20px;
#options {
width: 100%;
bottom: 0;
position: absolute;
#option-left {
display: inline-block;
vertical-align: top;
width: 50%;
position: relative;
background: none;
button {
border-radius: 3px;
background: $primary;
margin: 0 0 10px 10px;
}
svg {
transition: all 0.2s linear;
width: 40px;
height: 20px;
fill: $secondary;
}
.item-options {
border-radius: 3px;
margin: 5px;
display: inline-block;
}
button[data-active='false'] {
background: $primary;
svg {
fill: $secondary;
}
}
button[data-active='true'] {
background: $tertiary;
svg {
fill: $primary;
}
}
}
#option-right {
display: inline-block;
width: 50%;
text-align: right;
span {
font-weight: bold;
display: block;
background: $white;
color: $primary;
border-radius: 3px;
font-size: 0.6em;
text-align: center;
position: relative;
padding: 5px;
float: right;
margin: 0 10px 0 0;
bottom: -15px;
}
}
}
}
}
p {
padding: 5px 10px;
font-size: 1.2em;
font-weight: 400;
}
}
}
.paginate {
width: 260px;
display: block;
margin: 0 auto;
a.paginate-link {
display: inline-block;
vertical-align: top;
}
span.count {
text-align: center;
padding: 5px;
margin-top: -2px;
display: inline-block;
width: 190px;
font-size: 1.5em;
color: $tertiary;
}
}
}
}
#post-edit-index {
width: 100%;
overflow: hidden;
#post-edit-index-wrapper {
width: 100%;
#post-header {
// width 100%
background: $highlight;
#post-header-wrapper {
max-width: 900px;
margin: 0 auto;
padding: 0.75rem;
label {
color: $white;
font-size: 0.9em;
font-family: $basetype;
font-weight: 600;
}
span#post-span {
color: $primary;
font-size: 0.9em;
font-weight: 600;
text-transform: uppercase;
float: right;
}
#post-edit-title {
textarea#post-title-text {
background: $white;
font-family: $basetype;
width: 97.6%;
height: 80px;
font-size: 2em;
color: $primary;
padding: 5px;
margin: 0 0 5px;
}
#calendar-icon {
background: color.adjust($primary, $lightness: -15%);
border-radius: 3px 0 0 3px;
display: inline-block;
padding: 5.2px;
color: $secondary;
}
#layouts {
select {
background: $primary;
color: $secondary;
border-radius: 3px;
border-color: $primary;
margin: 0 0 10px;
width: 100%;
height: 45px;
padding: 5px;
font-size: 1.5em;
}
}
}
#post-meta {
#post-tags {
background: $white;
font-family: $basetype;
width: 97.6%;
height: 80px;
color: $primary;
padding: 5px;
margin: 0 0 5px;
}
#post-options {
display: inline-block;
vertical-align: top;
width: 100%;
padding: 0;
margin: 0 0 10px;
button.post-option-btn {
width: 25%;
height: 45px;
transition: all 0.3s linear;
margin: 0;
border-radius: 0;
display: inline-block;
vertical-align: top;
text-align: center;
}
button.post-option-btn:nth-child(1) {
border-radius: 3px 0 0 3px;
}
button.post-option-btn:nth-child(4) {
border-radius: 0 3px 3px 0;
}
a {
button.button-link {
border-radius: 0 3px 3px 0 !important;
}
}
button.post-option-btn[data-active='false'] {
background: $primary;
svg.svg-toggle {
fill: $secondary;
}
}
button.post-option-btn[data-active='true'] {
background: $tertiary;
svg.icons {
fill: $primary;
}
}
}
#page-files-upload,
#post-image-upload {
display: none;
}
}
}
}
#post-feature {
width: 100%;
#page-file-manager {
background: $tertiary;
width: 100%;
min-height: 300px;
#page-file-wrapper {
width: 100%;
max-width: 900px;
padding: 10px;
margin: 0 auto;
font-weight: bold;
color: $primary;
font-size: 1em;
#page-file-drop {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
min-height: 100px;
background: $white;
color: $primary;
vertical-align: middle;
border-radius: 5px;
margin: 0 0 10px;
label {
cursor: pointer;
font-weight: 600px;
text-transform: capitalize;
}
img {
width: 100%;
margin: 0;
padding: 0;
}
}
#page-images-list,
#page-files-list {
padding: 10px 0 0;
button.media-remove {
height: 35px;
width: 30px;
background: $event-lame;
color: $white;
float: right;
margin: 5px 5px 0;
}
.img-item {
height: 150px;
width: 23.8%;
border-radius: 3px;
margin: 0 10px 10px 0;
display: inline-block;
cursor: pointer;
}
.audio-item {
height: 150px;
width: 23.8%;
border-radius: 3px;
margin: 0 10px 10px 0;
display: inline-block;
cursor: pointer;
background: $primary;
background: url('/assets/images/global/upload-audio.png')
no-repeat center center / cover;
}
.video-item {
height: 150px;
width: 23.8%;
border-radius: 3px;
margin: 0 10px 10px 0;
display: inline-block;
cursor: pointer;
background: $primary;
background: url('/assets/images/global/upload-video.png')
no-repeat center center / cover;
}
.file-item {
height: 150px;
width: 23.8%;
border-radius: 3px;
margin: 0 10px 10px 0;
display: inline-block;
cursor: pointer;
background: $primary;
background: url('/assets/images/global/upload-doc.png')
no-repeat center center / cover;
}
}
}
}
}
#edit-post {
width: 100%;
max-width: 880px;
margin: 0 auto;
#edit-post-wrapper {
width: 100%;
max-width: 900px;
border-radius: 5px;
position: relative;
textarea:focus {
outline: none;
border-color: $highlight;
}
#edit,
#highlight {
border: 0;
width: 100%;
min-height: 300px;
height: auto;
position: absolute;
top: 0;
left: 0;
overflow: auto;
word-wrap: normal;
white-space: pre-wrap;
line-break: normal;
}
#highlight-content {
word-wrap: normal;
white-space: pre-wrap;
line-break: normal;
}
#edit,
#highlight,
#hightlight * {
font-size: 1.2em;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
line-height: 22pt;
}
#edit {
z-index: 1;
color: transparent;
background: transparent;
caret-color: $highlight;
}
#highlight {
z-index: 0;
pre {
margin: 0;
code {
font-family: $monotype;
padding: 5px;
border-radius: 5px;
line-height: 1.6em;
font-size: 1.25em;
color: $editor-primary;
word-wrap: normal;
white-space: pre-wrap;
line-break: normal;
-webkit-line-break: normal;
-o-line-break: normal;
-moz-line-break: normal;
display: inline-block;
width: 100%;
max-width: 900px;
min-height: 200px;
caret-color: $highlight;
}
}
}
}
}
}
}
@media only screen and (max-width: 800px) {
#post-edit-index {
#post-edit-index-wrapper {
#post-header {
#post-title {
#post-date {
width: 37.6%;
}
}
}
}
}
}
@media only screen and (max-width: 768px) {
#post-edit-index {
#post-edit-index-wrapper {
#post-header {
#post-title {
#post-date {
width: 43.1%;
}
}
#post-meta {
#edit-control {
max-width: 100%;
button {
width: 9.91%;
}
}
}
}
}
}
}
@media only screen and (max-width: 640px) {
#post-edit-index {
#post-edit-index-wrapper {
#post-header {
#post-title {
#post-date {
width: 42%;
}
}
}
}
}
}
@media only screen and (max-width: 480px) {
#post-index {
#post-index-wrapper {
#post-index-header {
#post-index-header-left {
font-size: 1.35em;
width: 30%;
}
#post-index-header-right {
width: 70%;
vertical-align: top;
}
}
#post-index-menu {
a {
font-size: 0.95em;
label {
display: none;
visibility: hidden;
}
}
}
}
}
#post-edit-index {
#post-edit-index-wrapper {
#post-header {
#post-title {
#post-options {
margin: 5px 0 0;
width: 100%;
padding: 0;
}
#post-date {
width: 89.2%;
}
}
#post-meta {
#edit-control {
button {
width: 9.91%;
}
}
}
}
}
}
}
@media only screen and (max-width: 320px) {
#post-index {
#post-index-wrapper {
#post-index-menu {
a {
font-size: 0.95em;
label {
display: none;
visibility: hidden;
}
}
}
}
}
#post-edit-index {
#post-edit-index-wrapper {
#post-header {
#post-title {
#post-title-text {
width: 96.4%;
}
#post-options {
margin: 5px 0 0;
width: 100%;
padding: 0;
}
#post-date {
width: 83.1%;
}
}
#post-meta {
#post-tags {
width: 96.4%;
}
#edit-control {
.content-editor-btn-icon {
svg.icons {
width: 20px;
}
}
.post-sumbit-btn {
svg.icons {
width: 20px;
}
}
button {
width: 10%;
}
}
}
}
}
}
} ;

@ -0,0 +1,383 @@
#settings-actions {
position: fixed;
width: 40%;
margin-top: -85px;
left: 50%;
margin-left: -20%;
#buttons {
width: 185px;
margin: 28px auto;
text-align: center;
background: $white;
padding: 2px;
border-radius: 3px;
button {
color: $white;
border-radius: 3px;
width: 40px;
margin: 0 10px;
svg {
width: 25px;
height: 20px;
fill: $white;
}
}
button[data-render='false'] {
background: $secondary;
svg {
fill: $primary;
}
}
button[data-render='true'] {
background: $highlight;
svg {
fill: $white;
}
}
}
}
#settings-index {
width: 94%;
max-width: 900px;
margin: 0 auto;
overflow: hidden;
#settings-index-wrapper {
padding: 0;
button {
margin-top: 5px;
width: 100%;
height: 33px;
}
#member-settings,
#feature-settings,
#option-settings,
#token-settings,
#backup-settings {
background: $white;
padding: 0;
border-radius: 5px 0;
width: 100%;
margin: 20px auto;
label {
font-family: $basetype;
color: $primary;
font-weight: bold;
}
span {
color: $secondary;
}
input {
width: 95%;
margin: 0 5px 10px 0;
height: 30px;
padding: 10px;
}
input#backup-upload {
visibility: hidden;
display: none;
}
.backup-meta {
background: $primary;
color: $white;
padding: 8px;
border-radius: 3px;
margin: 5px 0 0;
text-align: center;
}
#member-images {
padding: 10px 15px 0;
#member-avatar-drop {
display: inline-block;
margin: 0 0 10px;
img {
width: 100%;
border-radius: 5px;
overflow: hidden;
cursor: pointer;
display: block;
margin-bottom: 2px;
}
input {
visibility: hidden;
display: none;
}
#privacy-toggle {
width: 50%;
}
#render-toggle {
width: 50%;
}
}
#site-background {
margin: 0 0 10px;
img {
width: 92.1%;
height: 292px;
border-radius: 3px;
overflow: hidden;
cursor: pointer;
}
input {
visibility: hidden;
display: none;
}
}
}
#member-meta {
padding: 10px 15px 0;
position: relative;
top: -30px;
}
#features {
padding: 10px 15px 0;
}
}
#mail-settings {
min-height: 240px;
/*
input {
margin: 0 5px 5px 0;
vertical-align: top;
}
*/
a.mail-option {
float: right;
font-family: $monotype;
font-size: 0.9em;
border-radius: 3px;
text-decoration: none;
margin: 0 0 0 5px;
}
a.mail-option[data-enabled='true'] {
color: $highlight;
}
a.mail-option[data-enabled='false'] {
color: $primary;
}
div[data-enabled='false'] {
display: none;
visibility: hidden;
}
#settings-api {
background: $primary;
border-radius: 3px;
padding: 10px;
span {
color: $white !important;
margin: -13px 0 0 5px;
position: relative;
vertical-align: middle;
display: inline-block;
font-weight: bold;
}
button {
color: $white;
border-radius: 3px;
width: 40px;
margin: 0;
svg {
width: 25px;
height: 20px;
fill: $white;
}
}
button[data-enabled='false'] {
background: $secondary;
svg {
fill: $primary;
}
}
button[data-enabled='true'] {
background: $highlight;
svg {
fill: $white;
}
}
}
}
textarea {
background: $primary;
width: 70%;
height: 89.5px;
color: $tertiary;
padding: 10px;
display: inline-block;
margin-bottom: 10px;
}
span#key {
color: $white;
background: $primary;
font-size: 0.9em;
border-radius: 3px;
padding: 5px;
display: block;
width: 95%;
overflow: hidden;
}
#feature-settings {
#feature-api,
#dynamic-api {
background: $white;
border-radius: 3px;
padding: 5px;
button {
color: $white;
border-radius: 3px;
width: 200px;
margin: 0;
height: 200px;
font-size: 1em;
svg.icons {
width: 100px;
height: 90px;
fill: $white;
position: relative;
display: block;
margin: 12px auto;
}
span {
color: $white;
margin: 6px 0 0 5px;
position: relative;
vertical-align: middle;
display: inline-block;
font-weight: bold;
}
}
button[data-enabled='false'] {
background: $secondary;
svg {
fill: $primary;
}
span {
color: $primary;
}
}
button[data-enabled='true'] {
background: $highlight;
svg {
fill: $white;
}
}
}
}
#token-settings {
#keys-tokens {
padding: 10px 15px 0;
#member-api-key,
#form-token {
background: $primary;
border-radius: 3px;
padding: 5px;
color: $white;
}
}
}
#option-settings {
#theme-settings {
a {
width: 95%;
margin: 0 5px 5px 0;
height: 15px;
padding: 10px;
display: inline-block;
}
a[data-enabled='false'] {
background: $white;
color: $primary;
border-radius: 3px;
font-weight: bold;
border-top: 1px $highlight solid;
border-bottom: 1px $highlight solid;
}
a[data-enabled='true'] {
background: $highlight;
color: $primary;
border-radius: 3px;
font-weight: bold;
border-top: 1px $primary solid;
border-bottom: 1px $primary solid;
svg {
fill: $primary;
display: inline-block;
float: right;
}
}
}
}
}
}
// Responsive for Settings
@media only screen and (max-width: 480px) {
#settings-actions {
margin-left: -42%;
#buttons {
width: 150px;
background: none;
button {
margin: 0 5px;
}
}
}
} ;

@ -0,0 +1,251 @@
html,
body {
background: $primary
linear-gradient(
0deg,
rgba($primary, 1) 0%,
rgba(color.adjust($primary, $lightness: 10%), 1) 100%
)
no-repeat;
font: 400 1em $basetype;
height: 100%;
}
a {
font: 300 1em $basetype;
color: $secondary;
text-decoration: underline;
transition: all 0.2s linear;
}
svg.icons {
width: 25px;
fill: $secondary;
}
#notifications {
perspective: 1000px;
position: fixed;
z-index: 2000;
height: 55px;
width: 100%;
display: block;
align-items: center;
justify-content: center;
padding: 0;
margin-top: -55px;
#notify-message {
margin: 0 auto;
// ks-easing( "out-back" );
transition: all 0.6s cubic-bezier(0.83, 0.05, 0.28, 1);
// padding-top -125px
height: 50px;
width: 500px;
display: flex;
align-items: center;
justify-content: center;
opacity: 1;
transform-style: preserve-3d;
transform: rotateX(120deg);
transform-origin: 50% 0;
overflow: hidden;
#notify-good,
#notify-lame,
#notify-working {
display: block;
}
#notify-working-icon {
animation: spin 2s linear infinite;
@keyframes spin {
transform: rotate(360deg);
}
}
.notify-icon {
background: $black;
padding: 8px 5px 5px;
border-radius: 5px 0 0 5px;
height: 30px;
width: 30px;
text-align: center;
border: 2px solid $white;
}
#notify-text {
color: $white;
background: $black;
width: 400px;
height: 28px;
padding: 15px 0 0;
border-radius: 0 5px 5px 0;
border: 2px solid $white;
text-align: center;
overflow: hidden;
position: relative;
#notify-progress {
width: 0;
background: $highlight;
height: 43px;
position: absolute;
top: 0;
}
p {
top: -15px;
display: block;
position: relative;
}
}
.icons {
fill: $white;
}
}
}
.notify-close {
transform-style: preserve-3d;
transform: rotateX(-120deg);
}
.notify-open {
transform-style: preserve-3d;
transform: rotateX(0deg);
}
.blog-container {
width: 100%;
}
.main-container {
margin: 0 auto;
z-index: 10;
position: relative;
height: 100%;
section {
header {
width: 100%;
max-width: 900px;
margin: 10px auto;
background: $white;
height: 50px;
border-radius: 5px;
#wrapper {
padding: 5px;
#left,
#right {
width: 49.7%;
display: inline-block;
vertical-align: top;
min-height: 60px;
#the-logo {
width: 29px;
}
}
#right {
text-align: right;
color: $white;
// word-break: break-all;
#dash-menu {
text-align: right;
a {
button {
border-radius: 50px;
svg {
transition: all 0.2s linear;
width: 40px;
height: 20px;
fill: $white;
}
}
&:hover {
button {
background: $primary;
}
svg {
fill: $secondary;
}
}
}
}
}
}
}
}
} /* Mozilla based browsers */
::selection {
background-color: $highlight;
color: $white;
}
/* Works in Opera */
::-o-selection {
background-color: $highlight;
color: $white;
}
::-ms-selection {
background-color: $highlight;
color: $white;
}
/* Works in Internet Explorer */
::-webkit-selection {
background-color: $highlight;
color: $white;
}
// Responsive
@media only screen and (max-width: 901px) {
.main-container {
padding: 10px;
}
}
@media only screen and (max-width: 800px) {
.main-container {
section {
header {
#wrapper {
#left,
#right {
display: inline-block;
}
}
}
}
}
}
@media only screen and (max-width: 480px) {
.main-container {
section {
header {
#wrapper {
#left {
width: 30%;
}
#right {
width: 70%;
}
}
}
}
}
} ;

@ -0,0 +1,23 @@
$basetype: helvetica, arial, sans-serif;
$monotype: 'Lucida Console', monaco, monospace;
h1,
h2,
h3 {
color: $white;
}
h1 {
font-size: 2em;
font-weight: 400;
}
h2 {
font-size: 1.75em;
font-weight: 400;
}
h3 {
font-size: 1.5em;
font-weight: 300;
}
Loading…
Cancel
Save