1
0
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:
til-schneider 2015-12-22 20:36:23 +01:00
parent e8e9d8de75
commit b771b39ba5
7 changed files with 153 additions and 50 deletions

1
.gitignore vendored
View File

@ -1,5 +1,6 @@
/.idea/
/dist/
/src/.tmp/
/src/data/
/src/node_modules/
/src/config.php

View File

@ -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) {

View File

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

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

View 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);
}
}

View File

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

View File

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