diff options
-rw-r--r-- | ratatoeskr/backend/main.php | 240 | ||||
-rw-r--r-- | ratatoeskr/cms_style/layout.css | 32 | ||||
-rw-r--r-- | ratatoeskr/templates/src/systemtemplates/content_write.html | 159 | ||||
-rw-r--r-- | ratatoeskr/translations/en.php | 12 |
4 files changed, 423 insertions, 20 deletions
diff --git a/ratatoeskr/backend/main.php b/ratatoeskr/backend/main.php index 9767cb4..737ffab 100644 --- a/ratatoeskr/backend/main.php +++ b/ratatoeskr/backend/main.php @@ -11,16 +11,44 @@ require_once(dirname(__FILE__) . "/../sys/models.php"); require_once(dirname(__FILE__) . "/../sys/pwhash.php"); +require_once(dirname(__FILE__) . "/../sys/textprocessors.php"); +require_once(dirname(__FILE__) . "/../languages.php"); $admin_grp = Group::by_name("admins"); +/* Mass creation of tags. */ +function maketags($tagnames, $lang) +{ + $rv = array(); + foreach($tagnames as $tagname) + { + if(empty($tagname)) + continue; + try + { + $tag = Tag::by_name($tagname); + } + catch(DoesNotExistError $e) + { + $tag = Tag::create($tagname); + $tag->title[$lang] = new Translation($tagname, ""); + } + $rv[] = $tag; + } + return $rv; +} + $backend_subactions = url_action_subactions(array( "_index" => url_action_alias(array("login")), "index" => url_action_alias(array("login")), - /* _prelude guarantees that the user is logged in properly, so we do not have to care about that later. */ + /* _prelude guarantees that the user is logged in properly, so we do not have to care about that later, and sets some STE vars. */ "_prelude" => function(&$data, $url_now, &$url_next) { - global $ratatoeskr_settings, $admin_grp, $ste; + global $ratatoeskr_settings, $admin_grp, $ste, $languages; + + $ste->vars["all_languages"] = array(); + foreach($languages as $code => $data) + $ste->vars["all_languages"][$code] = $data["language"]; /* Check authentification */ if(isset($_SESSION["ratatoeskr_uid"])) @@ -40,7 +68,8 @@ $backend_subactions = url_action_subactions(array( if($url_next[0] == "login") $url_next = array("content", "write"); $data["user"] = $user; - $ste->vars["user"] = array("name" => $user->username); + $ste->vars["user"] = array("name" => $user->username, "lang" => $user->language); + return; /* Authentification successful, continue */ } else @@ -67,18 +96,20 @@ $backend_subactions = url_action_subactions(array( throw new Exception(); if(!$user->member_of($admin_grp)) throw new Exception(); + + /* Login successful. */ $_SESSION["ratatoeskr_uid"] = $user->get_id(); $_SESSION["ratatoeskr_pwhash"] = $user->pwhash; + $data["user"] = $user; + $ste->vars["user"] = array("name" => $user->username, "lang" => $user->language); } catch(Exception $e) { $ste->vars["login_failed"] = True; } - /* Login successful. */ - $data["user"] = $user; - $ste->vars["user"] = array("name" => $user->username); - throw new Redirect(array("content", "write")); + if(isset($data["user"])) + throw new Redirect(array("content", "write")); } echo $ste->exectemplate("systemtemplates/backend_login.html"); @@ -93,21 +124,212 @@ $backend_subactions = url_action_subactions(array( "content" => url_action_subactions(array( "write" => function(&$data, $url_now, &$url_next) { - global $ste, $translation; + global $ste, $translation, $textprocessors, $ratatoeskr_settings, $languages; + + list($article, $editlang) = array_slice($url_next, 0); + if(!isset($editlang)) + $editlang = $data["user"]->language; + if(isset($article)) + $ste->vars["article_editurl"] = urlencode($article) . "/" . urlencode($editlang); + else + $ste->vars["article_editurl"] = ""; - $article = array_slice($url_next, 0); $url_next = array(); + $default_section = Section::by_id($ratatoeskr_settings["default_section"]); + $ste->vars["section"] = "content"; $ste->vars["submenu"] = "newarticle"; + $ste->vars["textprocessors"] = array(); + foreach($textprocessors as $txtproc => $properties) + if($properties[1]) + $ste->vars["textprocessors"][] = $txtproc; + + $ste->vars["sections"] = array(); + foreach(Section::all() as $section) + $ste->vars["sections"][] = $section->name; + $ste->vars["article_section"] = $default_section->name; + + /* Check Form */ + $fail_reasons = array(); + + $inputs = array( + "date" => time(), + "article_status" => ARTICLE_STATUS_LIVE + ); + + if(isset($_POST["save_article"])) + { + if(!preg_match('/^[a-zA-Z0-9-_]+$/', @$_POST["urlname"])) + $fail_reasons[] = $translation["invalid_urlname"]; + else + $inputs["urlname"] = $_POST["urlname"]; + if((@$_POST["article_status"] < 0) or (@$_POST["article_status"] > 3)) + $fail_reasons[] = $translation["invalid_article_status"]; + else + $inputs["article_status"] = (int) $_POST["article_status"]; + if(!isset($textprocessors[@$_POST["content_txtproc"]])) + $fail_reasons[] = $translation["unknown_txtproc"]; + else + $inputs["content_txtproc"] = $_POST["content_txtproc"]; + if(!isset($textprocessors[@$_POST["excerpt_txtproc"]])) + $fail_reasons[] = $translation["unknown_txtproc"]; + else + $inputs["excerpt_txtproc"] = $_POST["excerpt_txtproc"]; + if(!empty($_POST["date"])) + { + if(($time_tmp = strptime(@$_POST["date"], "%Y-%m-%d %H:%M:%S")) === False) + $fail_reasons[] = $translation["invalid_date"]; + else + $inputs["date"] = @mktime($time_tmp["tm_sec"], $time_tmp["tm_min"], $time_tmp["tm_hour"], $time_tmp["tm_mon"] + 1, $time_tmp["tm_mday"], $time_tmp["tm_year"] + 1900); + } + else + $inputs["date"] = time(); + $inputs["allow_comments"] = !(empty($_POST["allow_comments"]) or $_POST["allow_comments"] != "yes"); + + try + { + $inputs["section"] = Section::by_name($_POST["section"]); + } + catch(DoesNotExistError $e) + { + $fail_reasons[] = $translation["unknown_section"]; + } + + $inputs["title"] = $_POST["title"]; + $inputs["content"] = $_POST["content"]; + $inputs["excerpt"] = $_POST["excerpt"]; + $inputs["tags"] = array_filter(array_map("trim", explode(",", $_POST["tags"])), function($t) { return !empty($t); }); + if(isset($_POST["saveaslang"])) + $editlang = $_POST["saveaslang"]; + } + + function fill_article(&$article, $inputs, $editlang) + { + $article->urlname = $inputs["urlname"]; + $article->status = $inputs["article_status"]; + $article->timestamp = $inputs["date"]; + $article->section = $inputs["section"]; + $article->tags = maketags($inputs["tags"], $editlang); + $article->title [$editlang] = new Translation($inputs["title"], "" ); + $article->text [$editlang] = new Translation($inputs["content"], $inputs["content_txtproc"]); + $article->excerpt[$editlang] = new Translation($inputs["excerpt"], $inputs["excerpt_txtproc"]); + } + if(empty($article)) { /* New Article */ $ste->vars["pagetitle"] = $translation["new_article"]; + + if(empty($fail_reasons) and isset($_POST["save_article"])) + { + $article = Article::create($inputs["urlname"]); + fill_article($article, $inputs, $editlang); + try + { + $article->save(); + $ste->vars["article_editurl"] = urlencode($article->urlname) . "/" . urlencode($editlang); + $ste->vars["success"] = htmlesc($translation["article_save_success"]); + } + catch(AlreadyExistsError $e) + { + $fail_reasons[] = $translation["article_name_already_in_use"]; + } + } + } + else + { + try + { + $article = Article::by_urlname($article); + } + catch(DoesNotExistError $e) + { + throw new NotFoundError(); + } + + if(empty($fail_reasons) and isset($_POST["save_article"])) + { + fill_article($article, $inputs, $editlang); + try + { + $article->save(); + $ste->vars["article_editurl"] = urlencode($article->urlname) . "/" . urlencode($editlang); + $ste->vars["success"] = htmlesc($translation["article_save_success"]); + } + catch(AlreadyExistsError $e) + { + $fail_reasons[] = $translation["article_name_already_in_use"]; + } + } + + foreach(array( + "urlname" => "urlname", + "section" => "article_section", + "status" => "article_status", + "timestamp" => "date", + "allow_comments" => "allow_comments" + ) as $prop => $k_out) + { + if(!isset($inputs[$k_out])) + $inputs[$k_out] = $article->$prop; + } + if(!isset($inputs["title"])) + $inputs["title"] = $article->title[$editlang]->text; + if(!isset($inputs["content"])) + { + $translation_obj = $article->text[$editlang]; + $inputs["content"] = $translation_obj->text; + $inputs["content_txtproc"] = $translation_obj->texttype; + } + if(!isset($inputs["excerpt"])) + { + $translation_obj = $article->excerpt[$editlang]; + $inputs["excerpt"] = $translation_obj->text; + $inputs["excerpt_txtproc"] = $translation_obj->texttype; + } + if(!isset($inputs["tags"])) + $inputs["tags"] = array_map(function($tag) use ($editlang) { return $tag->name; }, $article->tags); + $ste->vars["morelangs"] = array(); + $ste->vars["pagetitle"] = $article->title[$editlang]->text; + foreach($article->title as $lang => $_) + { + if($lang != $editlang) + $ste->vars["morelangs"][] = array("url" => urlencode($article->urlname) . "/$lang", "full" => "($lang) " . $languages[$lang]["language"]); + } + } + + /* Push data back to template */ + if(isset($inputs["tags"])) + $ste->vars["tags"] = implode(", ", $inputs["tags"]); + if(isset($inputs["article_section"])) + $ste->section["article_section"] = $inputs["article_section"]->name; + $ste->vars["editlang"] = $editlang; + foreach(array( + "urlname" => "urlname", + "article_status" => "article_status", + "title" => "title", + "content_txtproc" => "content_txtproc", + "content" => "content", + "excerpt_txtproc" => "excerpt_txtproc", + "excerpt" => "excerpt", + "date" => "date", + "allow_comments" => "allow_comments" + ) as $k_in => $k_out) + { + if(isset($inputs[$k_in])) + $ste->vars[$k_out] = $inputs[$k_in]; } + if(!empty($fail_reasons)) + $ste->vars["failed"] = $fail_reasons; + echo $ste->exectemplate("systemtemplates/content_write.html"); + }, + "tags" => function(&$data, $url_now, &$url_next) + { + } )) )); diff --git a/ratatoeskr/cms_style/layout.css b/ratatoeskr/cms_style/layout.css index 42ea1bc..f206da6 100644 --- a/ratatoeskr/cms_style/layout.css +++ b/ratatoeskr/cms_style/layout.css @@ -111,7 +111,7 @@ h1 { min-width: 300px; } -code, code pre { +code, code pre, code * { font-family: monospace; background: #eee; } @@ -150,7 +150,33 @@ h2 { clear: both; } -form.fullwidthinputs input[type="text"], form.fullwidthinputs input[type="password"], form.fullwidthinputs select, form.fullwidthinputs textarea { +.fullwidth { width: 100%; - margin:0mm; +} + +div.error { + border: 1px solid #f00; + background: #fcc; + color: #f00; + padding: 1em; + margin: 3mm 0mm 3mm; + text-align: center; +} + +div.notice { + border: 1px solid #33f; + background: #ccf; + color: #33f; + padding: 1em; + margin: 3mm 0mm 3mm; + text-align: center; +} + +div.success { + border: 1px solid #0a0; + background: #cfc; + color: #0a0; + padding: 1em; + margin: 3mm 0mm 3mm; + text-align: center; } diff --git a/ratatoeskr/templates/src/systemtemplates/content_write.html b/ratatoeskr/templates/src/systemtemplates/content_write.html index 962f116..81a4d9b 100644 --- a/ratatoeskr/templates/src/systemtemplates/content_write.html +++ b/ratatoeskr/templates/src/systemtemplates/content_write.html @@ -1,20 +1,165 @@ <ste:load name="master.html" /> +<ste:comment>Create textprocessor options. The default will be tags content.</ste:comment> +<ste:mktag name="textprocessor_options"> + <ste:set var="txtproc_default"><ste:tagcontent /></ste:set> + <ste:foreach array="textprocessors" value="txtproc"> + <option value="<ste:escape>$txtproc</ste:escape>"?{~{$txtproc|eq|$txtproc_default}| selected="selected"|}><ste:escape>$txtproc</ste:escape></option> + </ste:foreach> +</ste:mktag> <ste:block name="content"> - <form action="$rel_path_to_root/content/write?{$article_editurl|/$article_editurl|}" method="POST" accept_charset="utf-8" class="fullwidthinputs"> + <form action="$rel_path_to_root/backend/content/write/$article_editurl" method="POST" accept_charset="utf-8"> + <ste:if>$failed + <ste:then> + <div class="error"> + <p><strong><ste:get_translation for="article_edit_error" /></strong></p> + <ul> + <ste:foreach array="failed" value="v"><li>$v</li></ste:foreach> + </ul> + </div> + </ste:then> + </ste:if> + <ste:if>$success + <ste:then> + <div class="success">$success</div> + </ste:then> + </ste:if> <div class="triplecolumns"> <div class="column_left"> <h2>Markdown Cheat Sheet</h2> + + <h3>Emphasis / Strong</h3> + <p> + <code>*<em>emphasis</em>*</code><br /> + <code>_<em>emphasis</em>_</code><br /> + <code>**<strong>strong</strong>**</code><br /> + <code>__<strong>strong</strong>__</code> + </p> + + <h3>Paragraphs and manual line breaks.</h3> + <p> + <code>First Paragraph<br /> +<br /> +Second Paragraph with<br /> +long<br /> +text<br /> +<br /> +To enforce a line break <br /> +end a line with two whitespaces.</code> + </p> + + <h3>Headers</h3> + <p> + <code>Header 1<br /> +========<br /> +<br /> +Header 2<br /> +--------<br /> +<br /> +# Header 1<br /> +<br /> +## Header 2<br /> +<br /> +...<br /> +<br /> +###### Header 6<br /></code> + </p> + + <h3>Linking</h3> + <p> + <code>[Linktext](http://url/to/resource "Optional title")</code> + </p> + <p> + <code>[Linktext][id]. Somewhere else:<br /> + <br /> + [id]: http://url/to/resource "Optional title"</code> + </p> + + <h3>Images</h3> + <p> + <code>![alt text](/path/to/image "Optional title")</code> + </p> + + <h3>Ordered / Unordered Lists</h3> + <p> + <code>* Item A<br /> +* Item B<br /> + <br /> + With a second paragraph.<br /> + <br /> +* Item C<br /> + * Item C1<br /> + * Item C2<br /></code> + </p> + <p> + <code>1. First element<br /> +2. Second Element +</code> + </p> + + <h3>Learn More</h3> + <p> + <a href="http://daringfireball.net/projects/markdown/syntax">Complete Syntax</a><br /> + <a href="http://daringfireball.net/projects/markdown/dingus">Test Markdown</a> + </p> </div> + <div class="column_right"> - <h2>Settings / Metadata</h2> + <h2><ste:get_translation for="settings_meta" /></h2> - <p>Unique URL Title: <input type="text" name="urltitle" value="<ste:escape>$urltitle</ste:escape>" /></p> - <p></p> + <p><ste:get_translation for="urlname" />: <input type="text" name="urlname" value="<ste:escape>$urlname</ste:escape>" class="fullwidth" /></p> + <p> + <ste:get_translation for="article_section" />: + <select name="section" class="fullwidth"> + <ste:foreach array="sections" value="section_name"> + <option name="<ste:escape>$section_name</ste:escape>"?{~{$section_name|eq|$article_section}| selected="selected"|}><ste:escape>$section_name</ste:escape></option> + </ste:foreach> + </select> + </p> + <p><ste:get_translation for="tags_cs" />: <input type="text" name="tags" value="<ste:escape>$tags</ste:escape>" class="fullwidth" /></p> + <p><ste:get_translation for="date_time" />:<br />(YYYY-MM-DD HH:MM:SS) <input type="text" name="date"?{$date| value="<ste:date timestamp='$date'>%Y-%m-%d %H:%M:%S</ste:date>"|} class="fullwidth" /></p> + <p> + <ste:get_translation for="article_status" />: + <ste:set var="article_status">?{$article_status|$article_status|1}</ste:set> + <select name="article_status" class="fullwidth"> + <option value="0"?{~{$article_status|eq|0}| selected="selected"|}><ste:get_translation for="article_status_hidden" /></option> + <option value="1"?{~{$article_status|eq|1}| selected="selected"|}><ste:get_translation for="article_status_live" /></option> + <option value="2"?{~{$article_status|eq|2}| selected="selected"|}><ste:get_translation for="article_status_sticky" /></option> + </select> + </p> + <p><ste:get_translation for="allow_comments" />: <input type="checkbox" name="allow_comments" value="yes" ?{$allow_comments|checked="checked" |}/></p> </div> + <div class="column_main"> - <p><ste:get_translation for="articleedit_title" />: <input type="text" name="title" value="<ste:escape>$title</ste:escape>" /></p> - <p><ste:get_translation for="articleedit_content" />: <textarea name="content" cols="80" rows="20"><ste:escape>$content</ste:escape></textarea></p> - <p><ste:get_translation for="articleedit_excerpt" />: <textarea name="excerpt" cols="80" rows="10"><ste:escape>$excerpt</ste:escape></textarea></p> + <ste:if>$morelangs + <ste:then> + <h2><ste:get_translation for="article_other_languages" /></h2> + <p> + <ste:foreach array="morelangs" value="lang" counter="i"> + ?{~{$i|eq|0}|| - }<a href="$rel_path_to_root/backend/content/write/$lang[url]"><ste:escape>$lang[full]</ste:escape></a> + </ste:foreach> + </p> + </ste:then></ste:if> + <p> + <ste:get_translation for="articleedit_title" />: + <input type="text" name="title" value="<ste:escape>$title</ste:escape>" class="fullwidth" /> + </p> + <p> + <ste:get_translation for="articleedit_content" />: <select name="content_txtproc"><ste:textprocessor_options>$content_txtproc</ste:textprocessor_options></select> + <textarea name="content" cols="80" rows="20" class="fullwidth"><ste:escape>$content</ste:escape></textarea> + </p> + <p> + <ste:get_translation for="articleedit_excerpt" />: <select name="excerpt_txtproc"><ste:textprocessor_options>$excerpt_txtproc</ste:textprocessor_options></select> + <textarea name="excerpt" cols="80" rows="10" class="fullwidth"><ste:escape>$excerpt</ste:escape></textarea> + </p> + <p style="text-align: center;"> + <ste:get_translation for="save_texts_as_lang" />: <select name="saveaslang"> + <ste:set var="default_langsel">?{$editlang|$editlang|$user[lang]}</ste:set> + <ste:foreach array="all_languages" key="code" value="name"> + <option value="$code"?{~{$default_langsel|eq|$code}| selected="selected"|}>($code) $name</option> + </ste:foreach> + </select><br /> + <input type="submit" name="save_article" /> + </p> </div> </div> <div class="triplecolumns_stop"></div> diff --git a/ratatoeskr/translations/en.php b/ratatoeskr/translations/en.php index c80e92d..31d0944 100644 --- a/ratatoeskr/translations/en.php +++ b/ratatoeskr/translations/en.php @@ -50,7 +50,17 @@ $translation = array( "article_status" => "Article Status", "article_status_hidden" => "Hidden", "article_status_live" => "Live", - "article_status_sticky" => "Sticky" + "article_status_sticky" => "Sticky", + "article_section" => "Section", + "invalid_urlname" => "Invalid URL name (Can contain a-z A-Z 0-9 - and _)", + "invalid_article_status" => "Invalid article status", + "unknown_txtproc" => "Unknown Textprocessor", + "invalid_date" => "Invalid date", + "article_edit_error" => "Could not edit article.", + "unknown_section" => "Unknown section", + "allow_comments" => "Allow comments?", + "article_name_already_in_use" => "Article name is already in use.", + "article_save_success" => "Article successfully saved." ); ?> |