From 793b0e31beb22fe33402c8a9ceebf47a5a950635 Mon Sep 17 00:00:00 2001
From: Kevin Chabowski
Date: Sun, 20 May 2012 13:00:31 +0200
Subject: Plugins can now hook into the Article editor.
* Plugins can display additional fields in the article editor.
* Plugins can perform actions when saving an article and even veto the
saving.
* Plugins can get their ArticleExtradata objects.
---
ratatoeskr/backend.php | 79 +++++++++++++++++-----
ratatoeskr/frontend.php | 15 ++--
ratatoeskr/sys/models.php | 15 ++++
ratatoeskr/sys/plugin_api.php | 55 +++++++++++++--
.../src/systemtemplates/content_write.html | 5 ++
5 files changed, 142 insertions(+), 27 deletions(-)
diff --git a/ratatoeskr/backend.php b/ratatoeskr/backend.php
index 19d3891..a1ab570 100644
--- a/ratatoeskr/backend.php
+++ b/ratatoeskr/backend.php
@@ -12,6 +12,7 @@
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__) . "/sys/plugin_api.php");
require_once(dirname(__FILE__) . "/languages.php");
$admin_grp = NULL;
@@ -116,6 +117,7 @@ $backend_subactions = url_action_subactions(array(
}
}
load_language();
+
/* If we are here, user is not logged in... */
$url_next = array("login");
},
@@ -159,7 +161,7 @@ $backend_subactions = url_action_subactions(array(
"content" => url_action_subactions(array(
"write" => function(&$data, $url_now, &$url_next)
{
- global $ste, $translation, $textprocessors, $ratatoeskr_settings, $languages;
+ global $ste, $translation, $textprocessors, $ratatoeskr_settings, $languages, $articleeditor_plugins;
list($article, $editlang) = array_slice($url_next, 0);
if(!isset($editlang))
@@ -234,14 +236,21 @@ $backend_subactions = url_action_subactions(array(
if(isset($_POST["saveaslang"]))
$editlang = $_POST["saveaslang"];
}
+ else
+ {
+ /* Call articleeditor plugins */
+ $article = empty($article) ? NULL : Article::by_urlname($article);
+ foreach($articleeditor_plugins as $plugin)
+ call_user_func($plugin["fx"], $article, False);
+ }
function fill_article(&$article, $inputs, $editlang)
{
$article->urlname = $inputs["urlname"];
$article->status = $inputs["article_status"];
$article->timestamp = $inputs["date"];
- $article->title [$editlang] = new Translation($inputs["title"], "" );
- $article->text [$editlang] = new Translation($inputs["content"], $inputs["content_txtproc"]);
+ $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"]);
$article->set_tags(maketags($inputs["tags"], $editlang));
$article->set_section($inputs["article_section"]);
@@ -256,15 +265,32 @@ $backend_subactions = url_action_subactions(array(
{
$article = Article::create($inputs["urlname"]);
fill_article($article, $inputs, $editlang);
- try
+
+ /* Calling articleeditor plugins */
+ $call_after_save = array();
+ foreach($articleeditor_plugins as $plugin)
{
- $article->save();
- $ste->vars["article_editurl"] = urlencode($article->urlname) . "/" . urlencode($editlang);
- $ste->vars["success"] = htmlesc($translation["article_save_success"]);
+ $result = call_user_func($plugin["fx"], $article, True);
+ if(is_string($result))
+ $fail_reasons[] = $result;
+ elseif($result !== NULL)
+ $call_after_save[] = $result;
}
- catch(AlreadyExistsError $e)
+
+ if(empty($fail_reasons))
{
- $fail_reasons[] = $translation["article_name_already_in_use"];
+ try
+ {
+ $article->save();
+ foreach($call_after_save as $cb)
+ call_user_func($cb, $article);
+ $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"];
+ }
}
}
}
@@ -272,7 +298,8 @@ $backend_subactions = url_action_subactions(array(
{
try
{
- $article = Article::by_urlname($article);
+ if(!($article instanceof Article))
+ $article = Article::by_urlname($article);
}
catch(DoesNotExistError $e)
{
@@ -282,15 +309,32 @@ $backend_subactions = url_action_subactions(array(
if(empty($fail_reasons) and isset($_POST["save_article"]))
{
fill_article($article, $inputs, $editlang);
- try
+
+ /* Calling articleeditor plugins */
+ $call_after_save = array();
+ foreach($articleeditor_plugins as $plugin)
{
- $article->save();
- $ste->vars["article_editurl"] = urlencode($article->urlname) . "/" . urlencode($editlang);
- $ste->vars["success"] = htmlesc($translation["article_save_success"]);
+ $result = call_user_func($plugin["fx"], $article, True);
+ if(is_string($result))
+ $fail_reasons[] = $result;
+ elseif($result !== NULL)
+ $call_after_save[] = $result;
}
- catch(AlreadyExistsError $e)
+
+ if(empty($fail_reasons))
{
- $fail_reasons[] = $translation["article_name_already_in_use"];
+ try
+ {
+ $article->save();
+ foreach($call_after_save as $cb)
+ call_user_func($cb, $article);
+ $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"];
+ }
}
}
@@ -366,6 +410,9 @@ $backend_subactions = url_action_subactions(array(
$ste->vars[$k_out] = $inputs[$k_in];
}
+ /* Displaying article editor plugins */
+ $ste->vars["displayed_plugins"] = array_map(function($x) { return array("label" => $x["label"], "template" => $x["template"]); }, array_filter($articleeditor_plugins, function($x) { return $x["display"]; }));
+
if(!empty($fail_reasons))
$ste->vars["failed"] = $fail_reasons;
diff --git a/ratatoeskr/frontend.php b/ratatoeskr/frontend.php
index 883f842..0bc083b 100644
--- a/ratatoeskr/frontend.php
+++ b/ratatoeskr/frontend.php
@@ -25,9 +25,10 @@ require_once(dirname(__FILE__) . "/libs/kses.php");
*
* Returns:
* Array with these fields:
- * * id
- * * name
- * * title
+ * * id - The ID of the section.
+ * * name - The name of the section.
+ * * title - The title of the section in the current language
+ * * __obj - The object. Useful for plugins, so they do not need to fetch the object from the database again.
*/
function section_transform_ste($section, $lang)
{
@@ -49,9 +50,10 @@ function section_transform_ste($section, $lang)
*
* Returns:
* Array with these fields:
- * * id
- * * name
- * * title
+ * * id - The ID of the tag.
+ * * name - The name of the tag.
+ * * title - The title in the current language.
+ * * __obj - The object. Useful for plugins, so they do not need to fetch the object from the database again.
*/
function tag_transform_ste($tag, $lang)
{
@@ -93,6 +95,7 @@ function tag_transform_ste($tag, $lang)
* * tags (array(sub-fields: ))
* * languages (array: language name=>url)
* * comments_allowed
+ * * __obj - Useful for plugins, so they do not need to fetch the object from the database again.
*/
function article_transform_ste($article, $lang)
{
diff --git a/ratatoeskr/sys/models.php b/ratatoeskr/sys/models.php
index 9f3a96a..3cda168 100644
--- a/ratatoeskr/sys/models.php
+++ b/ratatoeskr/sys/models.php
@@ -2605,6 +2605,21 @@ WHERE " . implode(" AND ", $subqueries) . " $sorting");
$this->section_obj = $section;
}
+ /*
+ * Function: get_extradata
+ * Get the extradata for this article and the given plugin.
+ *
+ * Parameters:
+ * $plugin_id - The ID of the plugin.
+ *
+ * Returns:
+ * An object.
+ */
+ public function get_extradata($plugin_id)
+ {
+ return new ArticleExtradata($this->id, $plugin_id);
+ }
+
/*
* Function: save
*/
diff --git a/ratatoeskr/sys/plugin_api.php b/ratatoeskr/sys/plugin_api.php
index f6d1c2d..cd3e42b 100644
--- a/ratatoeskr/sys/plugin_api.php
+++ b/ratatoeskr/sys/plugin_api.php
@@ -15,17 +15,18 @@ require_once(dirname(__FILE__) . "/../frontend.php");
/*
* Constant: APIVERSION
- * The current API version (5).
+ * The current API version (6).
*/
-define("APIVERSION", 5);
+define("APIVERSION", 6);
/*
* Array: $api_compat
* Array of API versions, this version is compatible to (including itself).
*/
-$api_compat = array(3, 4, 5);
+$api_compat = array(3, 4, 5, 6);
+
+$url_handlers = array(); /* master URL handler */
-$url_handlers = array();
/*
* Function: register_url_handler
* Register an URL handler. See for more details.
@@ -42,6 +43,8 @@ function register_url_handler($name, $callback)
$pluginpages_handlers = array();
+$articleeditor_plugins = array();
+
/*
* Class: RatatoeskrPlugin
* An abstract class to be extended in order to write your own Plugin.
@@ -93,7 +96,7 @@ abstract class RatatoeskrPlugin
final protected function get_custompub_dir() { return SITE_BASE_PATH . "/ratatoeskr/plugin_extradata/public/" . $this->id; }
final protected function get_custompub_url() { return $GLOBALS["rel_path_to_root"] . "/ratatoeskr/plugin_extradata/public/" . $this->id; }
final protected function get_template_dir() { return "/plugintemplates/" . $this->id; }
-
+
/*
* Function: register_url_handler
* Register a URL handler
@@ -193,6 +196,33 @@ abstract class RatatoeskrPlugin
};
}
+ /*
+ * Function: register_articleeditor_plugin
+ * Register a plugin for the article editor in the backend.
+ *
+ * Parameters:
+ * $label - The label for the plugin.
+ * $fx - A function that will be called during the articleeditor.
+ * This function must accept these parameters:
+ * * $article - An object or NULL, if no Article is edited right now.
+ * * $about_to_save - If True, the article is about to be saved.
+ * If you want to veto the saving, return the rejection reason as a string.
+ * If everything is okay and you need to save additional data, return a callback function that accepts the saved object (that callback should also write data back to the template, if necessary).
+ * If everything is okay and you do not need to save additional data, return NULL.
+ * $template - The name of the template to display in the editor, relative to your template directory. If you do not want to display anything, you can set ths to NULL.
+ */
+ final protected function register_articleeditor_plugin($label, $fx, $template)
+ {
+ global $articleeditor_plugins;
+
+ $articleeditor_plugins[] = array(
+ "label" => $label,
+ "fx" => $fx,
+ "template" => $this->get_template_dir() . "/" . $template,
+ "display" => $template != NULL
+ );
+ }
+
/*
* Function: get_backend_pluginpage_url
* Get the URL to your backend plugin page.
@@ -206,6 +236,21 @@ abstract class RatatoeskrPlugin
return "$rel_path_to_root/backend/pluginpages/p{$this->id}";
}
+ /*
+ * Function: get_article_extradata
+ * Get the object for this plugin and the given article.
+ *
+ * Parameters:
+ * $article - An object.
+ *
+ * Returns:
+ * An object for this plugin and the given article.
+ */
+ final protected function get_article_extradata($article)
+ {
+ return new ArticleExtradata($article->get_id(), $this->id);
+ }
+
/*
* Function: prepare_backend_pluginpage
* Automatically sets the page title and highlights the menu-entry of your backend subpage.
diff --git a/ratatoeskr/templates/src/systemtemplates/content_write.html b/ratatoeskr/templates/src/systemtemplates/content_write.html
index 82c86c4..3d63c61 100644
--- a/ratatoeskr/templates/src/systemtemplates/content_write.html
+++ b/ratatoeskr/templates/src/systemtemplates/content_write.html
@@ -49,6 +49,11 @@