mirror of
https://github.com/til-schneider/slim-wiki.git
synced 2025-01-17 20:58:21 +01:00
Added saving articles automatically
This commit is contained in:
parent
e8e9d8de75
commit
b771b39ba5
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
/.idea/
|
||||
/dist/
|
||||
/src/.tmp/
|
||||
/src/data/
|
||||
/src/node_modules/
|
||||
/src/config.php
|
||||
|
@ -1,6 +1,7 @@
|
||||
(function(window, document, console, CodeMirror) {
|
||||
|
||||
var editor,
|
||||
var slimwiki = window.slimwiki,
|
||||
editor,
|
||||
updatePreviewDelay = 1000,
|
||||
updatePreviewTimeout = null,
|
||||
updatePreviewRunning = false,
|
||||
@ -27,16 +28,17 @@
|
||||
previewIsDirty = false;
|
||||
|
||||
updatePreviewRunning = true;
|
||||
var start = new Date().getTime();
|
||||
callRpc('render', 'renderMarkdown', [ editor.getValue() ], function(result, error) {
|
||||
var start = new Date().getTime(),
|
||||
articleFilename = slimwiki.settings.articleFilename;
|
||||
callRpc('editor', 'saveArticle', [ articleFilename, editor.getValue() ], function(result, error) {
|
||||
updatePreviewRunning = false;
|
||||
|
||||
if (error) {
|
||||
console.error('Rendering markdown failed', error);
|
||||
console.error('Saving article failed:', error);
|
||||
} else {
|
||||
document.getElementById('content').innerHTML = result;
|
||||
slimwiki.View.updateSyntaxHighlighting();
|
||||
console.log('Updated preview in ' + (new Date().getTime() - start) + ' ms');
|
||||
console.log('Saved article in ' + (new Date().getTime() - start) + ' ms');
|
||||
}
|
||||
|
||||
if (previewIsDirty) {
|
||||
|
@ -61,6 +61,17 @@
|
||||
</body>
|
||||
|
||||
<?php if ($data['isEditMode']) { ?>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.slimwiki = <?php
|
||||
echo json_encode(array(
|
||||
"settings" => array(
|
||||
"articleFilename" => $data['articleFilename']
|
||||
)
|
||||
));
|
||||
?>;
|
||||
</script>
|
||||
|
||||
<!-- build:js client/edit.js -->
|
||||
<script src="client/libs/CodeMirror/lib/codemirror.js"></script>
|
||||
<script src="client/libs/CodeMirror/addon/mode/overlay.js"></script> <!-- Allow language-in-language -->
|
||||
|
69
src/server/logic/Context.php
Normal file
69
src/server/logic/Context.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
class Context {
|
||||
|
||||
private $config;
|
||||
|
||||
private $articleBaseDir;
|
||||
private $dataBaseDir;
|
||||
|
||||
private $editorService;
|
||||
private $renderService;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
$appBaseDir = realpath(__DIR__ . '/../../');
|
||||
|
||||
$this->articleBaseDir = $appBaseDir . '/articles/';
|
||||
$this->dataBaseDir = $appBaseDir . '/data/';
|
||||
}
|
||||
|
||||
public function getConfig() {
|
||||
if (! is_null($this->config)) {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
$config = array(
|
||||
'wikiName' => 'Slim Wiki',
|
||||
'timezone' => 'Europe/Berlin'
|
||||
);
|
||||
|
||||
if (file_exists(__DIR__ . '/../../config.php')) {
|
||||
include(__DIR__ . '/../../config.php');
|
||||
}
|
||||
|
||||
$this->config = $config;
|
||||
return $config;
|
||||
}
|
||||
|
||||
public function getArticleBaseDir() {
|
||||
return $this->articleBaseDir;
|
||||
}
|
||||
|
||||
public function getDataBaseDir() {
|
||||
return $this->dataBaseDir;
|
||||
}
|
||||
|
||||
public function isValidArticleFilename($articleFilename) {
|
||||
// Don't allow to escape from the article base directory
|
||||
return ! is_string($articleFilename) || ! strpos($articleFilename, '..');
|
||||
}
|
||||
|
||||
public function getEditorService() {
|
||||
if (is_null($this->editorService)) {
|
||||
require_once __DIR__ . '/EditorService.php';
|
||||
$this->editorService = new EditorService($this);
|
||||
}
|
||||
return $this->editorService;
|
||||
}
|
||||
|
||||
public function getRenderService() {
|
||||
if (is_null($this->renderService)) {
|
||||
require_once __DIR__ . '/RenderService.php';
|
||||
$this->renderService = new RenderService();
|
||||
}
|
||||
return $this->renderService;
|
||||
}
|
||||
|
||||
}
|
45
src/server/logic/EditorService.php
Normal file
45
src/server/logic/EditorService.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
class EditorService {
|
||||
|
||||
private $context;
|
||||
|
||||
|
||||
public function __construct($context) {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
public function isRpcMethod($methodName) {
|
||||
return ($methodName == 'saveArticle');
|
||||
}
|
||||
|
||||
public function saveArticle($articleFilename, $markdownText) {
|
||||
if (! $this->context->isValidArticleFilename($articleFilename)) {
|
||||
throw new Exception("Invalid article filename: '$articleFilename'");
|
||||
}
|
||||
|
||||
// Set timezone
|
||||
$this->context->getConfig();
|
||||
|
||||
// Write article file
|
||||
$articleFullFilename = $this->context->getArticleBaseDir() . $articleFilename;
|
||||
$articleDir = dirname($articleFullFilename);
|
||||
if (! file_exists($articleDir)) {
|
||||
mkdir($articleDir, 0777, true);
|
||||
}
|
||||
file_put_contents($articleFullFilename, $markdownText);
|
||||
|
||||
// Write backup file (one per day)
|
||||
$backupFullFilename = $this->context->getDataBaseDir() . 'backup/' . $articleFilename . date('_Y-m-d') . '.gz';
|
||||
$backupDir = dirname($backupFullFilename);
|
||||
if (! file_exists($backupDir)) {
|
||||
mkdir($backupDir, 0777, true);
|
||||
}
|
||||
$fp = gzopen ($backupFullFilename, 'w9');
|
||||
gzwrite ($fp, $markdownText);
|
||||
gzclose($fp);
|
||||
|
||||
return $this->context->getRenderService()->renderMarkdown($markdownText);
|
||||
}
|
||||
|
||||
}
|
@ -1,14 +1,16 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/Context.php';
|
||||
|
||||
class Main {
|
||||
|
||||
private static $singleton;
|
||||
|
||||
private $articleBaseDir;
|
||||
private $context;
|
||||
|
||||
|
||||
private function __construct() {
|
||||
$this->articleBaseDir = realpath(__DIR__ . '/../../articles') . '/';
|
||||
$this->context = new Context();
|
||||
}
|
||||
|
||||
public static function get() {
|
||||
@ -23,12 +25,10 @@ class Main {
|
||||
// - $basePath: E.g. '/slim-wiki/'
|
||||
// - $requestPathArray: E.g. array('myfolder', 'mypage')
|
||||
public function dispatch($baseUrl, $basePath, $requestPathArray) {
|
||||
$config = $this->loadConfig();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$this->handlePost($requestPathArray);
|
||||
} else {
|
||||
$this->handleGet($baseUrl, $basePath, $requestPathArray, $config);
|
||||
$this->handleGet($baseUrl, $basePath, $requestPathArray);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,9 +38,8 @@ class Main {
|
||||
|
||||
$objectName = $requestPathArray[1];
|
||||
$object = null;
|
||||
if ($objectName == 'render') {
|
||||
require_once __DIR__ . '/RenderService.php';
|
||||
$object = RenderService::get();
|
||||
if ($objectName == 'editor') {
|
||||
$object = $this->context->getEditorService();
|
||||
}
|
||||
|
||||
$responseData = array(
|
||||
@ -72,7 +71,7 @@ class Main {
|
||||
}
|
||||
}
|
||||
|
||||
private function handleGet($baseUrl, $basePath, $requestPathArray, $config) {
|
||||
private function handleGet($baseUrl, $basePath, $requestPathArray) {
|
||||
$isEditMode = isset($requestPathArray[0]) && $requestPathArray[0] == 'edit';
|
||||
if ($isEditMode) {
|
||||
array_shift($requestPathArray);
|
||||
@ -84,6 +83,8 @@ class Main {
|
||||
header('Content-Type:text/html; charset=utf-8');
|
||||
echo '<h1>File not found</h1>'; // TODO: Show error page
|
||||
} else {
|
||||
$config = $this->context->getConfig();
|
||||
|
||||
$data = array();
|
||||
$data['baseUrl'] = $baseUrl;
|
||||
$data['basePath'] = $basePath;
|
||||
@ -95,52 +96,40 @@ class Main {
|
||||
|
||||
$data['breadcrumbs'] = $this->createBreadcrumbs($requestPathArray, $config['wikiName']);
|
||||
|
||||
$articleMarkdown = file_get_contents($articleFilename);
|
||||
$data['articleFilename'] = $articleFilename;
|
||||
$articleMarkdown = file_get_contents($this->context->getArticleBaseDir() . $articleFilename);
|
||||
$data['articleMarkdown'] = $articleMarkdown;
|
||||
|
||||
require_once __DIR__ . '/RenderService.php';
|
||||
$data['articleHtml'] = RenderService::get()->renderMarkdown($articleMarkdown);
|
||||
$data['articleHtml'] = $this->context->getRenderService()->renderMarkdown($articleMarkdown);
|
||||
|
||||
$this->renderPage($data);
|
||||
}
|
||||
}
|
||||
|
||||
private function getArticleFilename($requestPathArray) {
|
||||
$articleFilename = $this->articleBaseDir . implode('/', $requestPathArray);
|
||||
$articleBaseDir = $this->context->getArticleBaseDir();
|
||||
$articleFilename = implode('/', $requestPathArray);
|
||||
|
||||
// Support `index.md` for directories
|
||||
if (is_dir($articleFilename)) {
|
||||
if (is_dir($articleBaseDir . $articleFilename)) {
|
||||
$articleFilename = rtrim($articleFilename, '/') . '/index.md';
|
||||
}
|
||||
|
||||
// Make the extension `.md` optional
|
||||
if (! file_exists($articleFilename) && file_exists($articleFilename . '.md')) {
|
||||
if (! file_exists($articleBaseDir . $articleFilename) && file_exists($articleBaseDir . $articleFilename . '.md')) {
|
||||
$articleFilename .= '.md';
|
||||
}
|
||||
|
||||
if ($articleFilename != realpath($articleFilename)) {
|
||||
$articleFullFilename = $articleBaseDir . $articleFilename;
|
||||
if (! $this->context->isValidArticleFilename($articleFilename)) {
|
||||
// Attempt to break out of article base directory (e.g. `../../outside.ext`)
|
||||
return null;
|
||||
} else if (file_exists($articleFilename) && is_readable($articleFilename)) {
|
||||
} else if (file_exists($articleFullFilename) && is_readable($articleFullFilename)) {
|
||||
return $articleFilename;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private function loadConfig() {
|
||||
// Defaults
|
||||
$config = array(
|
||||
'wikiName' => 'Slim Wiki'
|
||||
);
|
||||
|
||||
if (file_exists(__DIR__ . '/../../config.php')) {
|
||||
include(__DIR__ . '/../../config.php');
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function createBreadcrumbs($requestPathArray, $wikiName) {
|
||||
$pathCount = count($requestPathArray);
|
||||
$breadcrumbArray = array(array('name' => $wikiName, 'path' => '', 'active' => ($pathCount == 0)));
|
||||
|
@ -2,20 +2,6 @@
|
||||
|
||||
class RenderService {
|
||||
|
||||
private static $singleton;
|
||||
|
||||
|
||||
public static function get() {
|
||||
if (is_null(self::$singleton)) {
|
||||
self::$singleton = new self();
|
||||
}
|
||||
return self::$singleton;
|
||||
}
|
||||
|
||||
public function isRpcMethod($methodName) {
|
||||
return ($methodName == 'renderMarkdown');
|
||||
}
|
||||
|
||||
public function renderMarkdown($markdownText) {
|
||||
require_once __DIR__ . '/../lib/parsedown/Parsedown.php';
|
||||
return Parsedown::instance()->text($markdownText);
|
||||
|
Loading…
x
Reference in New Issue
Block a user