Commit 98576155 authored by NGPixel's avatar NGPixel

Integration to Requarks Core

parent 67441908
......@@ -4,15 +4,16 @@
// Licensed under AGPLv3
// ===========================================
global.ROOTPATH = __dirname;
global.PROCNAME = 'AGENT';
global.ROOTPATH = __dirname;
global.CORE_PATH = ROOTPATH + '/../core/';
global.IS_DEBUG = process.env.NODE_ENV === 'development';
// ----------------------------------------
// Load Winston
// ----------------------------------------
var _isDebug = process.env.NODE_ENV === 'development';
global.winston = require('./libs/winston')(_isDebug);
global.winston = require(CORE_PATH + 'core-libs/winston')(IS_DEBUG);
// ----------------------------------------
// Load global modules
......@@ -20,8 +21,8 @@ global.winston = require('./libs/winston')(_isDebug);
winston.info('[AGENT] Background Agent is initializing...');
var appconfig = require('./libs/config')('./config.yml');
global.db = require('./libs/mongo').init(appconfig);
var appconfig = require(CORE_PATH + 'core-libs/config')('./config.yml');
global.db = require(CORE_PATH + 'core-libs/mongodb').init(appconfig);
global.upl = require('./libs/uploads-agent').init(appconfig);
global.git = require('./libs/git').init(appconfig);
global.entries = require('./libs/entries').init(appconfig);
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
//@import './layout/_fonts';
@import './layout/_base';
@import './layout/_mixins';
@import 'core-client/scss/core';
@import 'core-client/scss/components/alert';
@import 'core-client/scss/components/button';
@import 'core-client/scss/components/form';
@import 'core-client/scss/components/grid';
@import 'core-client/scss/components/hero';
@import 'core-client/scss/components/markdown-content';
@import 'core-client/scss/components/modal';
@import 'core-client/scss/components/nav';
@import 'core-client/scss/components/sidebar';
@import 'core-client/scss/components/typography';
@import './libs/bulma/bulma';
@import './libs/twemoji-awesome';
@import './libs/animate.min.css';
@import './libs/jquery-contextmenu';
@import './components/_alerts';
@import './components/_editor';
//@import './components/_editor';
@import './layout/_header';
@import './layout/_footer';
@import './layout/_content';
//@import './layout/_content';
@import './pages/_account';
\ No newline at end of file
//@import './pages/_account';
\ No newline at end of file
#alerts {
position: fixed;
top: 60px;
right: 10px;
width: 350px;
z-index: 10;
text-shadow: 1px 1px 0 rgba(0,0,0,0.1);
.notification {
animation: 0.5s ease slideInRight;
margin-top: 5px;
&.exit {
animation: 0.5s ease fadeOutRight;
}
}
h3 {
font-size: 16px;
font-size: 500;
}
}
\ No newline at end of file
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
[v-cloak] {
display: none;
}
\ No newline at end of file
#root {
padding-top: 52px;
}
#page-type-view > section {
transition: background-color .5s ease;
&.blurred {
background-color: $grey-lighter;
background-color: mc('grey', '50');
}
}
......@@ -36,26 +33,26 @@
.mkcontent {
h1 {
border-bottom: 1px dotted $blue;
border-bottom: 1px dotted mc('blue', '500');
padding-bottom: 4px;
font-weight: 400;
color: desaturate($blue, 20%);
color: desaturate(mc('blue', '500'), 20%);
}
h2 {
border-bottom: 1px dotted $grey-light;
border-bottom: 1px dotted mc('grey', '100');
padding-bottom: 4px;
font-weight: 400;
color: desaturate($purple, 20%);
color: desaturate(mc('purple', '500'), 20%);
}
a.toc-anchor {
font-size: 80%;
color: $blue;
color: mc('blue', '500');
border-bottom: none;
&:visited {
color: $purple !important;
color: mc('purple', '500') !important;
}
}
......@@ -70,7 +67,7 @@
font-style: normal;
font-weight: normal;
text-decoration: inherit;
color: $grey;
color: mc('grey', '500');
font-size: 14px;
position: absolute;
top: 0;
......@@ -81,10 +78,10 @@
pre {
padding: 0;
font-family: $family-monospace;
font-family: $core-font-monospace;
> code {
box-shadow: inset 0 0 5px 0 $grey-light;
box-shadow: inset 0 0 5px 0 mc('grey', '100');
border-radius: 5px;
}
......@@ -106,7 +103,7 @@
}
strong {
color: $grey-dark;
color: mc('grey', '700');
}
.twa {
......@@ -114,18 +111,18 @@
}
table thead th {
background-color: $blue;
background-color: mc('blue', '500');
color: #FFF;
border-color: #FFF;
border-bottom-color: $blue;
border-top-color: $blue;
border-bottom-color: mc('blue', '500');
border-top-color: mc('blue', '500');
&:first-child {
border-left-color: $blue;
border-left-color: mc('blue', '500');
}
&:last-child {
border-right-color: $blue;
border-right-color: mc('blue', '500');
}
}
......@@ -133,58 +130,16 @@
}
.content a:not(.button):visited {
color: $turquoise;
color: mc('teal', '500');
font-weight: 500;
}
code {
font-weight: 500;
color: $purple;
color: mc('purple', '500');
}
p code {
padding: 2px 5px;
border-radius: 4px;
}
.modal {
align-items: flex-start;
}
.modal-background {
animation: 0.4s ease fadeIn;
}
.modal-content {
animation: 0.4s ease slideInDown;
}
.card-header {
background-color: $turquoise;
&.is-warning {
background-color: $orange;
}
&.is-danger {
background-color: $red;
}
&.is-info {
background-color: $purple;
}
}
.card-header-title {
color: #FFF;
font-weight: 400;
font-size: 16px;
padding: 10px 20px;
}
.modal-content .card-footer-item {
font-weight: 500;
}
.modal-content .card-footer-item.featured {
animation: flash 4s ease 0 infinite;
}
\ No newline at end of file
// Roboto
// by Christian Robertson
// Apache 2.0 License
// -> Thin
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-Thin.woff') format('woff');
font-weight: 100;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-ThinItalic.woff') format('woff');
font-weight: 100;
font-style: italic;
}
// -> Light
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-Light.woff') format('woff');
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-LightItalic.woff') format('woff');
font-weight: 300;
font-style: italic;
}
// -> Regular
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-RegularItalic.woff') format('woff');
font-weight: 400;
font-style: italic;
}
// -> Medium
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-Medium.woff') format('woff');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-MediumItalic.woff') format('woff');
font-weight: 500;
font-style: italic;
}
// -> Bold
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-BoldItalic.woff') format('woff');
font-weight: 700;
font-style: italic;
}
// -> Black
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-Black.woff') format('woff');
font-weight: 900;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-BlackItalic.woff') format('woff');
font-weight: 900;
font-style: italic;
}
\ No newline at end of file
.footer {
background-color: mc('light-blue', '600');
color: #FFF;
text-align: center;
padding: 25px;
}
\ No newline at end of file
......@@ -13,7 +13,7 @@
h2.nav-item {
font-size: 150%;
color: $orange;
color: mc('orange', '500');
}
#notifload {
......@@ -26,7 +26,7 @@ h2.nav-item {
&::before {
content: " ";
@include spinner($orange,0.5s,24px);
@include spinner(mc('orange', '500'),0.5s,24px);
}
&.active {
......
.block {
&:not(:last-child) {
margin-bottom: 20px;
}
}
.container {
position: relative;
@include desktop {
margin: 0 auto;
max-width: 960px;
// Modifiers
&.is-fluid {
margin: 0 20px;
max-width: none;
}
}
@include widescreen {
max-width: 1200px;
}
}
.fa {
font-size: 21px;
text-align: center;
vertical-align: top;
}
// Display
$displays: "block" "flex" "inline" "inline-block" "inline-flex";
@each $display in $displays {
.is-#{$display} {
display: #{$display};
}
.is-#{$display}-mobile {
@include mobile {
display: #{$display} !important;
}
}
.is-#{$display}-tablet {
@include tablet {
display: #{$display} !important;
}
}
.is-#{$display}-tablet-only {
@include tablet-only {
display: #{$display} !important;
}
}
.is-#{$display}-touch {
@include touch {
display: #{$display} !important;
}
}
.is-#{$display}-desktop {
@include desktop {
display: #{$display} !important;
}
}
.is-#{$display}-desktop-only {
@include desktop-only {
display: #{$display} !important;
}
}
.is-#{$display}-widescreen {
@include widescreen {
display: #{$display} !important;
}
}
}
// Float
.is-clearfix {
@include clearfix;
}
.is-pulled-left {
float: left;
}
.is-pulled-right {
float: right;
}
// Overflow
.is-clipped {
overflow: hidden !important;
}
// Overlay
.is-overlay {
@include overlay;
}
// Text
.has-text-centered {
text-align: center;
}
.has-text-left {
text-align: left;
}
.has-text-right {
text-align: right;
}
// Visibility
.is-hidden {
display: none !important;
}
.is-hidden-mobile {
@include mobile {
display: none !important;
}
}
.is-hidden-tablet {
@include tablet {
display: none !important;
}
}
.is-hidden-tablet-only {
@include tablet-only {
display: none !important;
}
}
.is-hidden-touch {
@include touch {
display: none !important;
}
}
.is-hidden-desktop {
@include desktop {
display: none !important;
}
}
.is-hidden-desktop-only {
@include desktop-only {
display: none !important;
}
}
.is-hidden-widescreen {
@include widescreen {
display: none !important;
}
}
// Other
.is-disabled {
pointer-events: none;
}
.is-marginless {
margin: 0 !important;
}
.is-unselectable {
@extend .unselectable;
}
// Components
.nav-toggle {
@extend .hamburger;
// Responsiveness
@include tablet {
display: none;
}
}
.nav-item {
align-items: center;
display: flex;
justify-content: center;
padding: 10px;
a {
flex-grow: 1;
}
img {
max-height: 24px;
}
.button + .button {
margin-left: 10px;
}
.tag {
&:first-child {
margin-right: 5px;
}
&:last-child {
margin-left: 5px;
}
}
// Responsiveness
@include mobile {
justify-content: flex-start;
}
}
.nav-item a,
a.nav-item {
color: $text;
&:hover {
color: $link-hover;
}
// Modifiers
&.is-active {
color: $link-active;
}
&.is-tab {
border-bottom: 1px solid transparent;
border-top: 1px solid transparent;
padding-left: 12px;
padding-right: 12px;
&:hover {
border-bottom: 1px solid $link;
border-top: 1px solid transparent;
}
&.is-active {
border-bottom: 3px solid $link;
border-top: 3px solid transparent;
color: $link;
}
}
}
// Containers
.nav-menu {
// Responsiveness
@include mobile {
background-color: $white;
box-shadow: 0 4px 7px rgba($black, 0.1);
left: 0;
display: none;
right: 0;
top: 100%;
position: absolute;
.nav-item {
border-top: 1px solid rgba($border, 0.5);
padding: 10px;
}
&.is-active {
display: block;
}
}
@include tablet-only {
padding-right: 20px;
}
}
.nav-left {
align-items: stretch;
display: flex;
flex-basis: 0;
flex-grow: 1;
justify-content: flex-start;
overflow: hidden;
overflow-x: auto;
white-space: nowrap;
}
.nav-center {
align-items: stretch;
display: flex;
justify-content: center;
margin-left: auto;
margin-right: auto;
}
.nav-right {
// Responsiveness
@include tablet {
align-items: stretch;
display: flex;
flex-basis: 0;
flex-grow: 1;
justify-content: flex-end;
}
}
// Main container
.nav {
align-items: stretch;
background-color: $white;
display: flex;
min-height: $nav-height;
position: relative;
text-align: center;
z-index: 2;
& > .container {
align-items: stretch;
display: flex;
min-height: $nav-height;
width: 100%;
& > .nav-left {
& > .nav-item:first-child:not(.is-tab) {
padding-left: 0;
}
}
& > .nav-right {
& > .nav-item:last-child:not(.is-tab) {
padding-right: 0;
}
}
}
.container > & {
& > .nav-left {
& > .nav-item:first-child:not(.is-tab) {
padding-left: 0;
}
}
& > .nav-right {
& > .nav-item:last-child:not(.is-tab) {
padding-right: 0;
}
}
}
// Modifiers
&.has-shadow {
box-shadow: 0 2px 3px rgba($black, 0.1);
}
// Responsiveness
@include touch {
& > .container,
.container > & {
& > .nav-left {
& > .nav-item.is-brand:first-child {
padding-left: 20px;
}
}
}
}
}
@mixin arrow($color) {
border: 1px solid $color;
border-right: 0;
border-top: 0;
content: " ";
display: block;
height: 7px;
pointer-events: none;
position: absolute;
transform: rotate(-45deg);
width: 7px;
}
@mixin clearfix {
&:after {
clear: both;
content: " ";
display: table;
}
}
@mixin center($size) {
left: 50%;
margin-left: -($size / 2);
margin-top: -($size / 2);
position: absolute;
top: 50%;
}
@mixin fa($size, $dimensions) {
display: inline-block;
font-size: $size;
height: $dimensions;
line-height: $dimensions;
text-align: center;
vertical-align: top;
width: $dimensions;
}
@mixin overlay($offset: 0) {
bottom: $offset;
left: $offset;
position: absolute;
right: $offset;
top: $offset;
}
@mixin placeholder {
$placeholders: ":-moz" ":-webkit-input" "-moz" "-ms-input";
@each $placeholder in $placeholders {
&:#{$placeholder}-placeholder {
@content;
}
}
}
@mixin replace($background, $width, $height) {
background-color: $background;
background-position: center center;
background-repeat: no-repeat;
background-size: $width $height;
display: block;
height: $height;
outline: none;
overflow: hidden;
text-indent: -290486px;
width: $width;
}
@mixin from($device) {
@media screen and (min-width: $device) {
@content;
}
}
@mixin until($device) {
@media screen and (max-width: $device - 1px) {
@content;
}
}
@mixin mobile {
@media screen and (max-width: $tablet - 1px) {
@content;
}
}
@mixin tablet {
@media screen and (min-width: $tablet) {
@content;
}
}
@mixin tablet-only {
@media screen and (min-width: $tablet) and (max-width: $desktop - 1px) {
@content;
}
}
@mixin touch {
@media screen and (max-width: $desktop - 1px) {
@content;
}
}
@mixin desktop {
@media screen and (min-width: $desktop) {
@content;
}
}
@mixin desktop-only {
@media screen and (min-width: $desktop) and (max-width: $widescreen - 1px) {
@content;
}
}
@mixin widescreen {
@media screen and (min-width: $widescreen) {
@content;
}
}
......@@ -29,7 +29,7 @@
font-style: normal;
font-weight: normal;
line-height: 1;
color: $primary;
color: mc('blue', '500');
text-align: center;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
......@@ -57,7 +57,7 @@
font-size: 14px;
list-style-type: none;
background: #fff;
border: 1px solid $primary;
border: 1px solid mc('blue', '500');
border-radius: .2em;
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .25);
box-shadow: 0 2px 5px rgba(0, 0, 0, .25);
......@@ -85,7 +85,7 @@
.context-menu-item.context-menu-hover {
color: #fff;
cursor: pointer;
background-color: $primary;
background-color: mc('blue', '500');
}
.context-menu-item.context-menu-disabled {
......
@import './layout/_base';
@import './layout/_mixins';
@import './libs/animate.min.css';
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
font-size: 14px;
}
a {
color: #FFF;
transition: color 0.4s ease;
text-decoration: none;
&:hover {
color: #FB8C00;
text-decoration: underline;
}
}
#bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
background-color: #000;
> div {
background-size: cover;
background-position: center center;
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
opacity: 0;
visibility: hidden;
transition: opacity 3s ease, visibility 3s;
animation: bg 30s linear infinite;
&:nth-child(1) {
animation-delay: 10s;
}
&:nth-child(2) {
animation-delay: 20s;
}
}
}
#root {
position: fixed;
top: 15vh;
left: 10vw;
z-index: 2;
color: #FFF;
display: flex;
flex-direction: column;
h1 {
font-size: 4rem;
color: #FFF;
padding: 0;
margin: 0;
animation: headerIntro 3s ease;
}
h2 {
font-size: 1.5rem;
font-weight: normal;
color: rgba(255,255,255,0.7);
padding: 0;
margin: 0 0 25px 0;
animation: headerIntro 3s ease;
}
h3 {
font-size: 1.25rem;
font-weight: normal;
color: #FB8C00;
padding: 0;
margin: 0;
animation: shake 1s ease;
> .fa {
margin-right: 7px;
}
}
h4 {
font-size: 0.8rem;
font-weight: normal;
color: rgba(255,255,255,0.7);
padding: 0;
margin: 0 0 15px 0;
animation: fadeIn 3s ease;
}
form {
display: flex;
flex-direction: column;
}
input[type=text], input[type=password] {
width: 350px;
max-width: 80vw;
border: 1px solid rgba(255,255,255,0.3);
border-radius: 3px;
background-color: rgba(0,0,0,0.2);
padding: 0 15px;
height: 40px;
margin: 0 0 10px 0;
color: #FFF;
font-weight: bold;
font-size: 14px;
transition: all 0.4s ease;
&:focus {
outline: none;
border-color: #FB8C00;
}
}
button {
background-color: #FB8C00;
color: #FFF;
border: 1px solid lighten(#FB8C00, 10%);
border-radius: 3px;
height: 40px;
width: 125px;
padding: 0;
font-weight: bold;
margin: 15px 0 0 0;
transition: all 0.4s ease;
cursor: pointer;
&:focus {
outline: none;
border-color: #FFF;
}
&:hover {
background-color: darken(#FB8C00, 10%);
}
}
#social {
margin-top: 25px;
> span {
display: block;
font-weight: bold;
color: rgba(255,255,255,0.7);
}
button {
margin-right: 5px;
width: auto;
padding: 0 15px;
> i {
margin-right: 10px;
font-size: 16px;
}
&.ms {
background-color: #009688;
border-color: lighten(#009688, 10%);
&:focus {
border-color: #FFF;
}
&:hover {
background-color: darken(#009688, 10%);
}
}
&.google {
background-color: #2196F3;
border-color: lighten(#2196F3, 10%);
&:focus {
border-color: #FFF;
}
&:hover {
background-color: darken(#2196F3, 10%);
}
}
&.facebook {
background-color: #673AB7;
border-color: lighten(#673AB7, 10%);
&:focus {
border-color: #FFF;
}
&:hover {
background-color: darken(#673AB7, 10%);
}
}
}
}
}
#copyright {
display: flex;
align-items: center;
justify-content: flex-start;
position: absolute;
left: 10vw;
bottom: 10vh;
z-index: 2;
color: rgba(255,255,255,0.5);
font-weight: bold;
.icon {
font-size: 1.2rem;
margin: 0 8px;
}
a {
opacity: 0.75;
}
}
@include keyframes(bg) {
0% {
@include prefix(transform, scale(1,1));
visibility: visible;
opacity: 0;
},
5% {
opacity: 0.5;
},
33% {
opacity: 0.5;
},
38% {
@include prefix(transform, scale(1.2, 1.2));
opacity: 0;
},
39% {
visibility: hidden;
}
100% {
visibility: hidden;
opacity: 0;
}
}
@include keyframes(headerIntro) {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
\ No newline at end of file
@import 'core-client/scss/core';
@import 'core-client/scss/pages/login';
\ No newline at end of file
......@@ -2,6 +2,7 @@
var express = require('express');
var router = express.Router();
const Promise = require('bluebird');
/**
* Admin
......@@ -11,7 +12,30 @@ router.get('/', (req, res) => {
});
router.get('/profile', (req, res) => {
res.render('pages/account.pug');
res.render('pages/admin/profile', { adminTab: 'profile' });
});
router.get('/stats', (req, res) => {
Promise.all([
db.Entry.count(),
db.UplFile.count(),
db.User.count()
]).spread((totalEntries, totalUploads, totalUsers) => {
return res.render('pages/admin/stats', {
totalEntries, totalUploads, totalUsers,
adminTab: 'stats'
}) || true;
}).catch((err) => {
throw err;
});
});
router.get('/users', (req, res) => {
res.render('pages/admin/users', { adminTab: 'users' });
});
router.get('/settings', (req, res) => {
res.render('pages/admin/settings', { adminTab: 'settings' });
});
module.exports = router;
\ No newline at end of file
.sidebar {
background-color: #FFF;
}
var gulp = require("gulp");
var watch = require('gulp-watch');
var merge = require('merge-stream');
var babel = require("gulp-babel");
var uglify = require('gulp-uglify');
......@@ -18,48 +19,59 @@ var include = require("gulp-include");
* @type {Object}
*/
var paths = {
scriptlibs: [
'./node_modules/socket.io-client/socket.io.js',
'./node_modules/jquery/dist/jquery.min.js',
'./node_modules/vue/dist/vue.min.js',
'./node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
'./node_modules/jquery-simple-upload/simpleUpload.min.js',
'./node_modules/jquery-contextmenu/dist/jquery.contextMenu.min.js',
'./node_modules/sticky-js/dist/sticky.min.js',
'./node_modules/simplemde/dist/simplemde.min.js',
'./node_modules/ace-builds/src-min-noconflict/ace.js',
'./node_modules/ace-builds/src-min-noconflict/ext-modelist.js',
'./node_modules/ace-builds/src-min-noconflict/mode-markdown.js',
'./node_modules/ace-builds/src-min-noconflict/theme-tomorrow_night.js',
'./node_modules/filesize.js/dist/filesize.min.js',
'./node_modules/lodash/lodash.min.js'
],
scriptlibs_acemodes: [
'./node_modules/ace-builds/src-min-noconflict/mode-*.js',
'!./node_modules/ace-builds/src-min-noconflict/mode-markdown.js'
],
scriptapps: [
'./client/js/*.js'
],
scriptapps_watch: [
'./client/js/**/*.js'
],
csslibs: [
'./node_modules/font-awesome/css/font-awesome.min.css',
'./node_modules/highlight.js/styles/default.css',
'./node_modules/simplemde/dist/simplemde.min.css'
],
cssapps: [
'./client/scss/*.scss'
],
cssapps_watch: [
'./client/scss/**/*.scss'
],
scripts: {
combine: [
'./node_modules/socket.io-client/socket.io.js',
'./node_modules/jquery/dist/jquery.min.js',
'./node_modules/vue/dist/vue.min.js',
'./node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
'./node_modules/jquery-simple-upload/simpleUpload.min.js',
'./node_modules/jquery-contextmenu/dist/jquery.contextMenu.min.js',
'./node_modules/sticky-js/dist/sticky.min.js',
'./node_modules/simplemde/dist/simplemde.min.js',
'./node_modules/ace-builds/src-min-noconflict/ace.js',
'./node_modules/ace-builds/src-min-noconflict/ext-modelist.js',
'./node_modules/ace-builds/src-min-noconflict/mode-markdown.js',
'./node_modules/ace-builds/src-min-noconflict/theme-tomorrow_night.js',
'./node_modules/filesize.js/dist/filesize.min.js',
'./node_modules/lodash/lodash.min.js'
],
ace: [
'./node_modules/ace-builds/src-min-noconflict/mode-*.js',
'!./node_modules/ace-builds/src-min-noconflict/mode-markdown.js'
],
compile: [
'./client/js/*.js'
],
watch: [
'./client/js/**/*.js'
]
},
css: {
combine: [
'./node_modules/font-awesome/css/font-awesome.min.css',
'./node_modules/highlight.js/styles/tomorrow.css',
'./node_modules/simplemde/dist/simplemde.min.css'
],
compile: [
'./client/scss/*.scss'
],
includes: [
'../core',
//'./node_modules/requarks-core'
],
watch: [
'./client/scss/**/*.scss',
'../core/core-client/scss/**/*.scss'
]
},
fonts: [
'./node_modules/font-awesome/fonts/*-webfont.*',
'!./node_modules/font-awesome/fonts/*-webfont.svg'
'!./node_modules/font-awesome/fonts/*-webfont.svg',
//'../node_modules/requarks-core/core-client/fonts/**/*'
'../core/core-client/fonts/**/*'
],
deploypackage: [
deploy: [
'./**/*',
'!node_modules', '!node_modules/**',
'!coverage', '!coverage/**',
......@@ -77,7 +89,7 @@ var paths = {
/**
* TASK - Starts server in development mode
*/
gulp.task('server', ['scripts', 'css'/*, 'fonts'*/], function() {
gulp.task('server', ['scripts', 'css', 'fonts'], function() {
nodemon({
script: './server',
ignore: ['assets/', 'client/', 'data/', 'repo/', 'tests/'],
......@@ -98,12 +110,12 @@ gulp.task("scripts-libs", function () {
return merge(
gulp.src(paths.scriptlibs)
gulp.src(paths.scripts.combine)
.pipe(concat('libs.js', {newLine: ';\n'}))
.pipe(uglify({ mangle: false }))
.pipe(gulp.dest("./assets/js")),
gulp.src(paths.scriptlibs_acemodes)
gulp.src(paths.scripts.ace)
.pipe(gulp.dest("./assets/js/ace"))
);
......@@ -115,7 +127,7 @@ gulp.task("scripts-libs", function () {
*/
gulp.task("scripts-app", function () {
return gulp.src(paths.scriptapps)
return gulp.src(paths.scripts.compile)
.pipe(plumber())
.pipe(include({ extensions: "js" }))
.pipe(babel())
......@@ -134,7 +146,7 @@ gulp.task("css", ['css-libs', 'css-app']);
* TASK - Combine css libraries
*/
gulp.task("css-libs", function () {
return gulp.src(paths.csslibs)
return gulp.src(paths.css.combine)
.pipe(plumber())
.pipe(concat('libs.css'))
.pipe(cleanCSS({ keepSpecialComments: 0 }))
......@@ -146,9 +158,9 @@ gulp.task("css-libs", function () {
* TASK - Combine app css
*/
gulp.task("css-app", function () {
return gulp.src(paths.cssapps)
return gulp.src(paths.css.compile)
.pipe(plumber())
.pipe(sass())
.pipe(sass.sync({ includePaths: paths.css.includes }))
.pipe(cleanCSS({ keepSpecialComments: 0 }))
.pipe(plumber.stop())
.pipe(gulp.dest("./assets/css"));
......@@ -166,8 +178,10 @@ gulp.task("fonts", function () {
* TASK - Start dev watchers
*/
gulp.task('watch', function() {
gulp.watch([paths.scriptapps_watch], ['scripts-app']);
gulp.watch([paths.cssapps_watch], ['css-app']);
return merge(
watch(paths.scripts.watch, {base: './'}, function() { return gulp.start('scripts-app'); }),
watch(paths.css.watch, {base: './'}, function() { return gulp.start('css-app'); })
);
});
/**
......@@ -179,11 +193,11 @@ gulp.task('default', ['watch', 'server']);
* TASK - Creates deployment packages
*/
gulp.task('deploy', ['scripts', 'css', 'fonts'], function() {
var zipStream = gulp.src(paths.deploypackage)
var zipStream = gulp.src(paths.deploy)
.pipe(zip('requarks-wiki.zip'))
.pipe(gulp.dest('dist'));
var targzStream = gulp.src(paths.deploypackage)
var targzStream = gulp.src(paths.deploy)
.pipe(tar('requarks-wiki.tar'))
.pipe(gzip())
.pipe(gulp.dest('dist'));
......
"use strict";
const LocalStrategy = require('passport-local').Strategy;
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const WindowsLiveStrategy = require('passport-windowslive').Strategy;
const FacebookStrategy = require('passport-facebook').Strategy;
const _ = require('lodash');
module.exports = function(passport, appconfig) {
// Serialization user methods
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
db.User.findById(id).then((user) => {
if(user) {
done(null, user);
} else {
done(new Error('User not found.'), null);
}
return true;
}).catch((err) => {
done(err, null);
});
});
// Local Account
if(appconfig.auth.local && appconfig.auth.local.enabled) {
passport.use('local',
new LocalStrategy({
usernameField : 'email',
passwordField : 'password'
},
(uEmail, uPassword, done) => {
db.User.findOne({ email: uEmail, provider: 'local' }).then((user) => {
if(user) {
return user.validatePassword(uPassword).then(() => {
return done(null, user) || true;
}).catch((err) => {
return done(err, null);
});
} else {
return done(new Error('Invalid Login'), null);
}
}).catch((err) => {
done(err, null) ;
});
}
));
}
// Google ID
if(appconfig.auth.google && appconfig.auth.google.enabled) {
passport.use('google',
new GoogleStrategy({
clientID: appconfig.auth.google.clientId,
clientSecret: appconfig.auth.google.clientSecret,
callbackURL: appconfig.host + '/login/google/callback'
},
(accessToken, refreshToken, profile, cb) => {
db.User.processProfile(profile).then((user) => {
return cb(null, user) || true;
}).catch((err) => {
return cb(err, null) || true;
});
}
));
}
// Microsoft Accounts
if(appconfig.auth.microsoft && appconfig.auth.microsoft.enabled) {
passport.use('windowslive',
new WindowsLiveStrategy({
clientID: appconfig.auth.microsoft.clientId,
clientSecret: appconfig.auth.microsoft.clientSecret,
callbackURL: appconfig.host + '/login/ms/callback'
},
function(accessToken, refreshToken, profile, cb) {
db.User.processProfile(profile).then((user) => {
return cb(null, user) || true;
}).catch((err) => {
return cb(err, null) || true;
});
}
));
}
// Facebook
if(appconfig.auth.facebook && appconfig.auth.facebook.enabled) {
passport.use('facebook',
new FacebookStrategy({
clientID: appconfig.auth.facebook.clientId,
clientSecret: appconfig.auth.facebook.clientSecret,
callbackURL: appconfig.host + '/login/facebook/callback',
profileFields: ['id', 'displayName', 'email']
},
function(accessToken, refreshToken, profile, cb) {
db.User.processProfile(profile).then((user) => {
return cb(null, user) || true;
}).catch((err) => {
return cb(err, null) || true;
});
}
));
}
// Check for admin access
db.onReady.then(() => {
db.User.count().then((c) => {
if(c < 1) {
winston.info('[' + PROCNAME + '][AUTH] No administrator account found. Creating a new one...');
db.User.hashPassword('admin123').then((pwd) => {
return db.User.create({
provider: 'local',
email: appconfig.admin,
name: "Administrator",
password: pwd,
rights: [{
role: 'admin',
path: '/',
exact: false,
deny: false
}]
});
}).then(() => {
winston.info('[' + PROCNAME + '][AUTH] Administrator account created successfully!');
}).catch((err) => {
winston.error('[' + PROCNAME + '][AUTH] An error occured while creating administrator account:');
winston.error(err);
});
}
});
return true;
});
};
\ No newline at end of file
"use strict";
var fs = require('fs'),
yaml = require('js-yaml'),
_ = require('lodash');
/**
* Load Application Configuration
*
* @param {String} confPath Path to the configuration file
* @return {Object} Application Configuration
*/
module.exports = (confPath) => {
var appconfig = {};
try {
appconfig = yaml.safeLoad(fs.readFileSync(confPath, 'utf8'));
} catch (ex) {
winston.error(ex);
process.exit(1);
}
// Merge with defaults
appconfig = _.defaultsDeep(appconfig, {
title: "Requarks Wiki",
host: "http://localhost",
port: process.env.PORT,
auth: {
local: { enabled: true },
microsoft: { enabled: false },
google: { enabled: false },
facebook: { enabled: false },
},
db: "mongodb://localhost/wiki",
redis: null,
sessionSecret: null,
admin: null
});
// List authentication strategies
appconfig.authStrategies = {
list: _.filter(appconfig.auth, ['enabled', true]),
socialEnabled: (_.chain(appconfig.auth).omit('local').reject({ enabled: false }).value().length > 0)
}
if(appconfig.authStrategies.list.length < 1) {
winston.error(new Error('You must enable at least 1 authentication strategy!'));
process.exit(1);
}
return appconfig;
};
\ No newline at end of file
......@@ -151,7 +151,18 @@ const parseContent = (content) => {
let output = mkdown.render(content);
let cr = cheerio.load(output);
cr('table').addClass('table is-bordered is-striped is-narrow');
//-> Style table headers
//cr('table').addClass('table is-bordered is-striped is-narrow');
//-> Remove links in headers
cr('h1 > a:not(.toc-anchor), h2 > a:not(.toc-anchor), h3 > a:not(.toc-anchor)').each((i, elm) => {
let txtLink = cr(elm).text();
cr(elm).replaceWith(txtLink);
});
output = cr.html();
return output;
......
"use strict";
const modb = require('mongoose'),
fs = require("fs"),
path = require("path"),
_ = require('lodash');
/**
* MongoDB module
*
* @param {Object} appconfig Application config
* @return {Object} MongoDB wrapper instance
*/
module.exports = {
/**
* Initialize DB
*
* @param {Object} appconfig The application config
* @return {Object} DB instance
*/
init(appconfig) {
let self = this;
let dbModelsPath = path.resolve(ROOTPATH, 'models');
modb.Promise = require('bluebird');
// Event handlers
modb.connection.on('error', (err) => {
winston.error('[' + PROCNAME + '] Failed to connect to MongoDB instance.');
});
modb.connection.once('open', function() {
winston.log('[' + PROCNAME + '] Connected to MongoDB instance.');
});
// Store connection handle
self.connection = modb.connection;
self.ObjectId = modb.Types.ObjectId;
// Load DB Models
fs
.readdirSync(dbModelsPath)
.filter(function(file) {
return (file.indexOf(".") !== 0);
})
.forEach(function(file) {
let modelName = _.upperFirst(_.camelCase(_.split(file,'.')[0]));
self[modelName] = require(path.join(dbModelsPath, file));
});
// Connect
self.onReady = modb.connect(appconfig.db);
return self;
}
};
\ No newline at end of file
"use strict";
const _ = require('lodash');
/**
* Rights
*/
module.exports = {
check(req, role) {
let rt = [];
let p = _.chain(req.originalUrl).toLower().trim().value();
// Load User Rights
if(_.isArray(req.user.rights)) {
rt = req.user.rights;
}
// Is admin?
if(_.find(rt, { role: 'admin' })) {
return true;
}
// Check specific role on path
let filteredRights = _.filter(rt, (r) => {
if(r.role === role || (r.role === 'write' && role === 'read')) {
if((!r.exact && _.startsWith(p, r.path)) || (r.exact && p === r.path)) {
return true;
}
}
return false;
});
// Check for deny scenario
let isValid = false;
if(filteredRights.length > 1) {
isValid = !_.chain(filteredRights).sortBy((r) => {
return r.path.length + ((r.deny) ? 0.5 : 0);
}).last().get('deny').value();
} else if(filteredRights.length == 1 && filteredRights[0].deny === false) {
isValid = true;
}
// Deny by default
return isValid;
}
};
\ No newline at end of file
"use strict";
var winston = require('winston');
module.exports = (isDebug) => {
winston.remove(winston.transports.Console);
winston.add(winston.transports.Console, {
level: (isDebug) ? 'debug' : 'info',
prettyPrint: true,
colorize: true,
silent: false,
timestamp: true
});
return winston;
};
\ No newline at end of file
"use strict";
const modb = require('mongoose');
/**
* BruteForce schema
*
* @type {<Mongoose.Schema>}
*/
var bruteForceSchema = modb.Schema({
var bruteForceSchema = Mongoose.Schema({
_id: { type: String, index: 1 },
data: {
count: Number,
......@@ -17,4 +15,4 @@ var bruteForceSchema = modb.Schema({
expires: { type: Date, index: { expires: '1d' } }
});
module.exports = modb.model('Bruteforce', bruteForceSchema);
\ No newline at end of file
module.exports = Mongoose.model('Bruteforce', bruteForceSchema);
\ No newline at end of file
"use strict";
const modb = require('mongoose'),
Promise = require('bluebird'),
const Promise = require('bluebird'),
_ = require('lodash');
/**
......@@ -9,7 +8,7 @@ const modb = require('mongoose'),
*
* @type {<Mongoose.Schema>}
*/
var entrySchema = modb.Schema({
var entrySchema = Mongoose.Schema({
_id: String,
......@@ -51,4 +50,4 @@ entrySchema.index({
name: 'EntriesTextIndex'
});
module.exports = modb.model('Entry', entrySchema);
\ No newline at end of file
module.exports = Mongoose.model('Entry', entrySchema);
\ No newline at end of file
"use strict";
const modb = require('mongoose'),
Promise = require('bluebird'),
const Promise = require('bluebird'),
_ = require('lodash');
/**
......@@ -9,7 +8,7 @@ const modb = require('mongoose'),
*
* @type {<Mongoose.Schema>}
*/
var uplFileSchema = modb.Schema({
var uplFileSchema = Mongoose.Schema({
_id: String,
......@@ -48,4 +47,4 @@ var uplFileSchema = modb.Schema({
timestamps: {}
});
module.exports = modb.model('UplFile', uplFileSchema);
\ No newline at end of file
module.exports = Mongoose.model('UplFile', uplFileSchema);
\ No newline at end of file
"use strict";
const modb = require('mongoose'),
Promise = require('bluebird'),
const Promise = require('bluebird'),
_ = require('lodash');
/**
......@@ -9,7 +8,7 @@ const modb = require('mongoose'),
*
* @type {<Mongoose.Schema>}
*/
var uplFolderSchema = modb.Schema({
var uplFolderSchema = Mongoose.Schema({
_id: String,
......@@ -23,4 +22,4 @@ var uplFolderSchema = modb.Schema({
timestamps: {}
});
module.exports = modb.model('UplFolder', uplFolderSchema);
\ No newline at end of file
module.exports = Mongoose.model('UplFolder', uplFolderSchema);
\ No newline at end of file
"use strict";
const modb = require('mongoose'),
Promise = require('bluebird'),
const Promise = require('bluebird'),
bcrypt = require('bcryptjs-then'),
_ = require('lodash');
......@@ -10,7 +9,7 @@ const modb = require('mongoose'),
*
* @type {<Mongoose.Schema>}
*/
var userSchema = modb.Schema({
var userSchema = Mongoose.Schema({
email: {
type: String,
......@@ -86,4 +85,4 @@ userSchema.methods.validatePassword = function(rawPwd) {
});
};
module.exports = modb.model('User', userSchema);
\ No newline at end of file
module.exports = Mongoose.model('User', userSchema);
\ No newline at end of file
......@@ -81,7 +81,9 @@
"pug": "^2.0.0-beta6",
"read-chunk": "^2.0.0",
"remove-markdown": "^0.1.0",
"requarks-core": "github:requarks/core",
"request": "^2.75.0",
"search-index": "^0.9.6",
"serve-favicon": "^2.3.0",
"sharp": "^0.16.1",
"simplemde": "^1.11.2",
......@@ -111,6 +113,7 @@
"gulp-sass": "^2.3.2",
"gulp-tar": "^1.9.0",
"gulp-uglify": "^2.0.0",
"gulp-watch": "^4.3.11",
"gulp-zip": "^3.2.0",
"istanbul": "^0.4.5",
"jquery": "^3.1.1",
......
......@@ -5,30 +5,30 @@
// Licensed under AGPLv3
// ===========================================
global.ROOTPATH = __dirname;
global.PROCNAME = 'SERVER';
global.ROOTPATH = __dirname;
global.CORE_PATH = ROOTPATH + '/../core/';
global.IS_DEBUG = process.env.NODE_ENV === 'development';
// ----------------------------------------
// Load Winston
// ----------------------------------------
const _isDebug = process.env.NODE_ENV === 'development';
global.winston = require('./libs/winston')(_isDebug);
global.winston = require(CORE_PATH + 'core-libs/winston')(IS_DEBUG);
winston.info('[SERVER] Requarks Wiki is initializing...');
// ----------------------------------------
// Load global modules
// ----------------------------------------
var appconfig = require('./libs/config')('./config.yml');
var appconfig = require(CORE_PATH + 'core-libs/config')('./config.yml');
global.lcdata = require('./libs/local').init(appconfig);
global.db = require('./libs/mongo').init(appconfig);
global.db = require(CORE_PATH + 'core-libs/mongodb').init(appconfig);
global.entries = require('./libs/entries').init(appconfig);
global.git = require('./libs/git').init(appconfig, false);
global.lang = require('i18next');
global.mark = require('./libs/markdown');
global.upl = require('./libs/uploads').init(appconfig);
global.rights = require('./libs/rights');
// ----------------------------------------
// Load modules
......@@ -53,7 +53,7 @@ const session = require('express-session');
const sessionMongoStore = require('connect-mongo')(session);
const socketio = require('socket.io');
var mw = autoload(path.join(ROOTPATH, '/middlewares'));
var mw = autoload(CORE_PATH + '/core-middlewares');
var ctrl = autoload(path.join(ROOTPATH, '/controllers'));
var libInternalAuth = require('./libs/internalAuth');
......@@ -80,10 +80,12 @@ app.use(favicon(path.join(ROOTPATH, 'assets', 'favicon.ico')));
app.use(express.static(path.join(ROOTPATH, 'assets')));
// ----------------------------------------
// Session
// Passport Authentication
// ----------------------------------------
const strategies = require('./libs/auth')(passport, appconfig);
var strategy = require(CORE_PATH + 'core-libs/auth')(passport, appconfig);
global.rights = require(CORE_PATH + 'core-libs/rights');
var sessionStore = new sessionMongoStore({
mongooseConnection: db.connection,
touchAfter: 15
......@@ -165,7 +167,7 @@ app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: _isDebug ? err : {}
error: IS_DEBUG ? err : {}
});
});
......
#header-container
nav.nav.has-shadow.stickyscroll#header
nav.nav.stickyscroll#header
.nav-left
block rootNavLeft
a.nav-item.is-brand(href='/')
a.nav-item(href='/')
img(src='/favicons/android-icon-96x96.png', alt='Wiki')
a.nav-item(href='/')
h1.title Wiki
h1= appconfig.title
.nav-center
block rootNavCenter
.nav-item
......@@ -16,7 +16,7 @@
span
span
span
.nav-right.nav-menu
.nav-right
block rootNavRight
i.nav-item#notifload
......
......@@ -28,7 +28,7 @@ html
block head
body
#root
#root.has-stickynav
include ./common/header.pug
include ./common/alerts.pug
main
......
extends ../../layout.pug
block rootNavCenter
block rootNavRight
i.nav-item#notifload
span.nav-item
a.button.is-dark.is-outlined.btn-edit-discard(href='/')
span.icon
i.fa.fa-home
span Home
block content
#page-type-account
section.hero.is-dark
.hero-body
.container
h1.title Account
h2.subtitle Manage your settings and site configuration
nav.nav.has-shadow
.container
.nav-left
a.nav-item.is-tab(href='/admin/profile', class={'is-active': adminTab === 'profile'}) My Profile
a.nav-item.is-tab(href='/admin/stats', class={'is-active': adminTab === 'stats'}) Stats
a.nav-item.is-tab(href='/admin/users', class={'is-active': adminTab === 'users'}) Users
a.nav-item.is-tab(href='/admin/settings', class={'is-active': adminTab === 'settings'}) Site Settings
a.nav-item.is-tab(href='/logout') Logout
block adminContent
\ No newline at end of file
extends ../layout.pug
extends ./_layout.pug
block rootNavCenter
block rootNavRight
i.nav-item#notifload
span.nav-item
a.button.is-dark.is-outlined.btn-edit-discard(href='/')
span.icon
i.fa.fa-home
span Home
block content
#page-type-account
section.hero.is-dark
.hero-body
.container
h1.title Account
h2.subtitle Manage your settings and site configuration
nav.nav.has-shadow
.container
.nav-left
a.nav-item.is-tab.is-active My Profile
a.nav-item.is-tab Stats
a.nav-item.is-tab Users
a.nav-item.is-tab Site Settings
a.nav-item.is-tab(href='/logout') Logout
section.section
.container
.columns
.column.is-two-thirds
label.label Email
block adminContent
section.section
.container
.columns
.column.is-two-thirds
label.label Email
p.control.has-icon.has-icon-right
input.input(type='email', placeholder='Email', value=user.email, disabled=(user.provider !== 'local'))
i.fa.fa-envelope
if user.provider == 'local'
label.label Password
p.control.has-icon.has-icon-right
input.input(type='email', placeholder='Email', value=user.email, disabled=(user.provider !== 'local'))
i.fa.fa-envelope
if user.provider == 'local'
label.label Password
p.control.has-icon.has-icon-right
input.input(type='password', placeholder='Password', value='********')
i.fa.fa-lock
label.label Verify Password
p.control.has-icon.has-icon-right
input.input(type='password', placeholder='Password', value='********')
i.fa.fa-lock
label.label Display Name
input.input(type='password', placeholder='Password', value='********')
i.fa.fa-lock
label.label Verify Password
p.control.has-icon.has-icon-right
input.input(type='text', placeholder='John Smith', value=user.name)
i.fa.fa-user
.column
label.label Provider
p.control.account-profile-provider
case user.provider
when 'local': i.fa.fa-database
when 'windowslive': i.fa.fa-windows.is-blue
when 'google': i.fa.fa-google.is-blue
when 'facebook': i.fa.fa-facebook.is-purple
default: i.fa.fa-question-circle
= t('auth:providers.' + user.provider)
label.label Member since
p.control= userMoment(user.createdAt).format('LL')
label.label Last Profile Update
p.control= userMoment(user.updatedAt).format('LL')
section.section
.container
p.control
button.button.is-success
| Save Changes
\ No newline at end of file
input.input(type='password', placeholder='Password', value='********')
i.fa.fa-lock
label.label Display Name
p.control.has-icon.has-icon-right
input.input(type='text', placeholder='John Smith', value=user.name)
i.fa.fa-user
.column
label.label Provider
p.control.account-profile-provider
case user.provider
when 'local': i.fa.fa-database
when 'windowslive': i.fa.fa-windows.is-blue
when 'google': i.fa.fa-google.is-blue
when 'facebook': i.fa.fa-facebook.is-purple
default: i.fa.fa-question-circle
= t('auth:providers.' + user.provider)
label.label Member since
p.control= userMoment(user.createdAt).format('LL')
label.label Last Profile Update
p.control= userMoment(user.updatedAt).format('LL')
section.section
.container
p.control
button.button.is-success
| Save Changes
\ No newline at end of file
extends ./_layout.pug
block adminContent
section.section
.container
label.label Coming soon
\ No newline at end of file
extends ./_layout.pug
block adminContent
section.section
.container
label.label Entries
p.control= totalEntries
label.label Uploads
p.control= totalUploads
label.label Users
p.control= totalUsers
\ No newline at end of file
extends ./_layout.pug
block adminContent
section.section
.container
.columns
.column.is-three-quarters
label.label Coming soon
.column
p.control
button.button.is-info
| Create Local User
p.control
button.button.is-info
| Authorize Social User
\ No newline at end of file
......@@ -6,13 +6,11 @@ block rootNavCenter
block rootNavRight
i.nav-item#notifload
span.nav-item
a.button.is-warning.is-outlined.btn-edit-discard
span.icon
i.fa.fa-times
a.button.is-orange.is-outlined.btn-edit-discard
i.fa.fa-times
span Discard
a.button.is-success.btn-edit-save
span.icon
i.fa.fa-check
a.button.is-green.btn-edit-save
i.fa.fa-check
span Save Changes
block content
......
......@@ -10,13 +10,11 @@ block rootNavRight
a.nav-item(href='/' + pageData.meta.path)
| Normal View
span.nav-item
a.button(href='/edit/' + pageData.meta.path)
span.icon
i.fa.fa-edit
a.button.is-orange(href='/edit/' + pageData.meta.path)
i.fa.fa-edit
span Edit
a.button.is-primary.btn-create-prompt
span.icon
i.fa.fa-plus
a.button.is-blue.btn-create-prompt
i.fa.fa-plus
span Create
block content
......
......@@ -15,51 +15,55 @@ block rootNavRight
a.nav-item(href='/source/' + pageData.meta.path)
| Source
span.nav-item
a.button(href='/edit/' + pageData.meta.path)
span.icon
i.fa.fa-edit
a.button.is-orange.is-outlined(href='/edit/' + pageData.meta.path)
i.fa.fa-edit
span Edit
a.button.is-primary.btn-create-prompt
span.icon
i.fa.fa-plus
a.button.is-light-blue.btn-create-prompt
i.fa.fa-plus
span Create
block content
#page-type-view(data-entrypath=pageData.meta.path)
section.section
.container.is-fluid
.columns
.container.is-fluid
.columns.is-gapless
.column.is-narrow.sd-menus.is-hidden-touch
.column.is-narrow.is-hidden-touch.sidebar
.box
aside.menu(style= { 'min-width': '200px' })
p.menu-label
| Navigation
ul.menu-list
li
a(href='/') Home
if pageData.parent
li
a(href='/' + pageData.parent.path)= pageData.parent.title
li
a(href='/admin') Account
.box.stickyscroll(data-margin-top=70)
aside.menu(style= { 'min-width': '200px' })
p.menu-label
| Contents
ul.menu-list
a(href='#root', title='Start') Start
+tocMenu(pageData.tree)
aside
.sidebar-label
i.icon-th-small
span Navigation
ul.sidebar-menu
li
a(href='/')
i.icon-home
span Home
if pageData.parent
li
a(href='/' + pageData.parent.path)
i.icon-reply
span= pageData.parent.title
li
a(href='/admin')
i.icon-head
span Account
aside.stickyscroll(data-margin-top=40)
.sidebar-label
i.icon-th-list
span Contents
ul.sidebar-menu
li: a(href='#root', title='Start') Start
+tocMenu(pageData.tree)
.column
.column
.hero
h1.title#title= pageData.meta.title
if pageData.meta.subtitle
h2.subtitle= pageData.meta.subtitle
.content.mkcontent
!= pageData.html
.content.mkcontent
!= pageData.html
include ../modals/create.pug
include ../modals/move.pug
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment