diff --git a/src/client/js/app-edit.js b/src/client/js/app-edit.js
index 89bc1cd..54754e6 100644
--- a/src/client/js/app-edit.js
+++ b/src/client/js/app-edit.js
@@ -16,6 +16,8 @@
if (mode == 'edit') {
initEditMode();
+ } else if (mode == 'createArticle') {
+ initCreateArticle();
} else if (mode == 'createUser') {
initCreateUserForm();
}
@@ -36,6 +38,21 @@
editor.on('scroll', onEditorScroll)
}
+ function initCreateArticle() {
+ document.getElementById('createArticleBtn').addEventListener('click', function() {
+ var articleFilename = slimwiki.settings.articleFilename,
+ pageTitle = slimwiki.settings.pageTitle;
+ callRpc('editor', 'createArticle', [ articleFilename, pageTitle ], function(result, error) {
+ if (error) {
+ console.error('Creating article failed:', error);
+ showErrorLogged();
+ } else {
+ location.href = slimwiki.settings.requestPath + '?edit';
+ }
+ })
+ });
+ }
+
function initCreateUserForm() {
document.getElementById('create-user-box').style.display = 'block';
diff --git a/src/client/less/view.less b/src/client/less/view.less
index 01c8559..f3b0757 100644
--- a/src/client/less/view.less
+++ b/src/client/less/view.less
@@ -10,7 +10,7 @@ body {
// Views
-#fatal-error-message {
+#jumbo-message {
margin-top: 150px;
text-align: center;
font-size: 20px;
diff --git a/src/index.php b/src/index.php
index 4c174a9..74f9db7 100644
--- a/src/index.php
+++ b/src/index.php
@@ -37,7 +37,7 @@ function init() {
require_once __DIR__ . '/server/logic/Main.php';
- Main::get()->dispatch($baseUrl, $basePath, $requestPathArray, $uriParts['query']);
+ (new Main())->dispatch($baseUrl, $basePath, $requestPathArray, $uriParts['query']);
}
init();
diff --git a/src/server/i18n/de.php b/src/server/i18n/de.php
index 231f2a3..ee66119 100644
--- a/src/server/i18n/de.php
+++ b/src/server/i18n/de.php
@@ -11,6 +11,9 @@ $i18n = array(
'button.back' => 'Zurück',
'button.edit' => 'Bearbeiten',
'button.createUser' => 'Benutzer erstellen',
+ 'button.createArticle' => 'Artikel anlegen',
+ 'createArticle.text' => 'Diesen Artikel gibt es noch nicht.',
+ 'createArticle.content' => 'Dies ist ihr neuer Artikel.',
'createUser.userName' => 'Benutzername',
'createUser.password' => 'Passwort',
'createUser.showConfig' => 'Konfiguration anzeigen',
diff --git a/src/server/i18n/en.php b/src/server/i18n/en.php
index 6534774..d15b4b6 100644
--- a/src/server/i18n/en.php
+++ b/src/server/i18n/en.php
@@ -11,6 +11,9 @@ $i18n = array(
'button.back' => 'Back',
'button.edit' => 'Edit',
'button.createUser' => 'Create user',
+ 'button.createArticle' => 'Create article',
+ 'createArticle.text' => 'This article does not exist yet.',
+ 'createArticle.content' => 'This is your new article.',
'createUser.userName' => 'User name',
'createUser.password' => 'Password',
'createUser.showConfig' => 'Show config',
diff --git a/src/server/layout/page.php b/src/server/layout/page.php
index d0ac06a..7b86ded 100644
--- a/src/server/layout/page.php
+++ b/src/server/layout/page.php
@@ -45,7 +45,9 @@ $mode = $data['mode'];
$settings = array(
"mode" => $mode
);
- if ($mode == 'edit') {
+ if ($mode == 'edit' || $mode == 'createArticle') {
+ $settings['pageTitle'] = end($data['breadcrumbs'])['name'];
+ $settings['requestPath'] = $data['requestPath'];
$settings['articleFilename'] = $data['articleFilename'];
}
echo json_encode($settings);
@@ -61,14 +63,14 @@ $mode = $data['mode'];
if ($mode != 'view') {
// Show an error message if JavaScript is off or if the browser is not supported.
// NOTE: In view mode we don't show an error. Instead, syntax highlighting will be off for unsupported browsers.
- ?>
+ ?>
diff --git a/src/server/logic/Context.php b/src/server/logic/Context.php
index 8963681..e22f1cd 100644
--- a/src/server/logic/Context.php
+++ b/src/server/logic/Context.php
@@ -75,7 +75,7 @@ class Context {
public function getRenderService() {
if (is_null($this->renderService)) {
require_once __DIR__ . '/RenderService.php';
- $this->renderService = new RenderService();
+ $this->renderService = new RenderService($this);
}
return $this->renderService;
}
diff --git a/src/server/logic/EditorService.php b/src/server/logic/EditorService.php
index 2a41bfe..e7b4db6 100644
--- a/src/server/logic/EditorService.php
+++ b/src/server/logic/EditorService.php
@@ -10,7 +10,7 @@ class EditorService {
}
public function isRpcMethod($methodName) {
- return ($methodName == 'saveArticle' || $methodName == 'createUserConfig');
+ return ($methodName == 'createArticle' || $methodName == 'saveArticle' || $methodName == 'createUserConfig');
}
// Returns one of: 'logged-in', 'no-credentials', 'wrong-credentials'
@@ -62,6 +62,17 @@ class EditorService {
return null;
}
+ public function createArticle($articleFilename, $pageTitle) {
+ if (! $this->str_endswith($articleFilename, '.md')) {
+ $articleFilename .= '.md';
+ }
+
+ $markdownText = $pageTitle . "\n" . str_repeat('=', strlen($pageTitle)) . "\n\n"
+ . $this->context->getI18n()['createArticle.content'];
+
+ return $this->saveArticle($articleFilename, $markdownText);
+ }
+
public function saveArticle($articleFilename, $markdownText) {
$this->assertLoggedIn();
@@ -111,4 +122,11 @@ class EditorService {
return "\$config['user.".strtolower($user)."'] = array('type' => '$type', 'salt' => '$salt', 'hash' => '$hash');";
}
+ function str_endswith($string, $test) {
+ $strlen = strlen($string);
+ $testlen = strlen($test);
+ if ($testlen > $strlen) return false;
+ return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0;
+ }
+
}
diff --git a/src/server/logic/Main.php b/src/server/logic/Main.php
index 3e51764..6cf84cc 100644
--- a/src/server/logic/Main.php
+++ b/src/server/logic/Main.php
@@ -4,22 +4,13 @@ require_once __DIR__ . '/Context.php';
class Main {
- private static $singleton;
-
private $context;
- private function __construct() {
+ public function __construct() {
$this->context = new Context();
}
- public static function get() {
- if (is_null(self::$singleton)) {
- self::$singleton = new self();
- }
- return self::$singleton;
- }
-
// Parameters:
// - $baseUrl: E.g. 'http://localhost/slim-wiki/?edit'
// - $basePath: E.g. '/slim-wiki/'
@@ -96,18 +87,28 @@ class Main {
$articleFilename = $this->getArticleFilename($requestPathArray);
if ($articleFilename == null) {
- header('HTTP/1.0 404 Not Found');
+ header('HTTP/1.0 403 Forbidden');
header('Content-Type:text/html; charset=utf-8');
- echo '
File not found
'; // TODO: Show error page
+ echo '
Forbidden
';
} else {
$config = $this->context->getConfig();
+ $renderService = $this->context->getRenderService();
+
$fatalErrorMessage = null;
- if ($mode == 'edit') {
- $fatalErrorMessage = $this->context->getEditorService()->checkForError($articleFilename);
+ if ($mode == 'view') {
+ if (! $renderService->articleExists($articleFilename)) {
+ $mode = 'noSuchArticle';
+ }
+ } else if ($mode == 'edit') {
+ $editorService = $this->context->getEditorService();
+ $fatalErrorMessage = $editorService->checkForError($articleFilename);
if ($fatalErrorMessage != null) {
$fatalErrorMessage = $this->context->getI18n()['error.editingArticleFailed'] . '
' . $fatalErrorMessage;
$mode = 'error';
+ } else if (! $renderService->articleExists($articleFilename)) {
+ $mode = 'createArticle';
+ $showCreateUserButton = false;
}
}
@@ -121,14 +122,17 @@ class Main {
$data[$key] = $config[$key];
}
- $data['breadcrumbs'] = $this->createBreadcrumbs($requestPathArray, $config['wikiName']);
+ $data['breadcrumbs'] = $this->createBreadcrumbs($requestPathArray);
$data['showCreateUserButton'] = $showCreateUserButton;
$data['requestPath'] = implode('/', $requestPathArray);
$data['articleFilename'] = $articleFilename;
- $articleMarkdown = file_get_contents($this->context->getArticleBaseDir() . $articleFilename);
- $data['articleMarkdown'] = $articleMarkdown;
- $data['articleHtml'] = $this->context->getRenderService()->renderMarkdown($articleMarkdown, $mode == 'edit');
+
+ if ($mode == 'view' || $mode == 'edit') {
+ $articleMarkdown = file_get_contents($this->context->getArticleBaseDir() . $articleFilename);
+ $data['articleMarkdown'] = $articleMarkdown;
+ $data['articleHtml'] = $renderService->renderMarkdown($articleMarkdown, $mode == 'edit');
+ }
$this->renderPage($data);
}
@@ -162,14 +166,13 @@ class Main {
if (! $this->context->isValidArticleFilename($articleFilename)) {
// Attempt to break out of article base directory (e.g. `../../outside.ext`)
return null;
- } else if (file_exists($articleFullFilename) && is_readable($articleFullFilename)) {
- return $articleFilename;
} else {
- return null;
+ return $articleFilename;
}
}
- private function createBreadcrumbs($requestPathArray, $wikiName) {
+ private function createBreadcrumbs($requestPathArray) {
+ $wikiName = $this->context->getConfig()['wikiName'];
$pathCount = count($requestPathArray);
$breadcrumbArray = array(array('name' => $wikiName, 'path' => '', 'active' => ($pathCount == 0)));
diff --git a/src/server/logic/RenderService.php b/src/server/logic/RenderService.php
index 59485e2..2edf901 100644
--- a/src/server/logic/RenderService.php
+++ b/src/server/logic/RenderService.php
@@ -2,6 +2,17 @@
class RenderService {
+ private $context;
+
+
+ public function __construct($context) {
+ $this->context = $context;
+ }
+
+ public function articleExists($articleFilename) {
+ return file_exists($this->context->getArticleBaseDir() . $articleFilename);
+ }
+
public function renderMarkdown($markdownText, $isEditMode) {
require_once __DIR__ . '/../lib/parsedown/Parsedown.php';
$html = Parsedown::instance()->text($markdownText);