aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbrettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544>2010-04-13 19:13:36 +0000
committerbrettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544>2010-04-13 19:13:36 +0000
commit444fb4e8a3e5189868b8b12c5fdccc4bef6868fc (patch)
tree2df668e1e079d354006938b2f6a0528b2b22d09b
parentece80595836be086201629824bceeae05892cd55 (diff)
downloadelgg-444fb4e8a3e5189868b8b12c5fdccc4bef6868fc.tar.gz
elgg-444fb4e8a3e5189868b8b12c5fdccc4bef6868fc.tar.bz2
First version of ecml.
git-svn-id: http://code.elgg.org/elgg/trunk@5722 36083f99-b078-4883-b0ff-0f9b5a30f544
-rw-r--r--mod/ecml/README.txt113
-rw-r--r--mod/ecml/ecml_functions.php168
-rw-r--r--mod/ecml/graphics/ecml.pngbin0 -> 341 bytes
-rw-r--r--mod/ecml/languages/en.php41
-rw-r--r--mod/ecml/manifest.xml14
-rw-r--r--mod/ecml/start.php149
-rw-r--r--mod/ecml/views/default/ecml/help.php32
-rw-r--r--mod/ecml/views/default/ecml/input_ext.php14
-rw-r--r--mod/ecml/views/default/ecml/keywords/user_list.php47
9 files changed, 578 insertions, 0 deletions
diff --git a/mod/ecml/README.txt b/mod/ecml/README.txt
new file mode 100644
index 000000000..12b6a1c8b
--- /dev/null
+++ b/mod/ecml/README.txt
@@ -0,0 +1,113 @@
+ECML - Elgg Custom Markup Language
+
+CONTENTS:
+ 1. Overview
+ 2. Using ECML Keywords
+ 2.1 Built-in keywords
+ 2.2 Entities
+ 2.3 Views
+ 3. Custom ECML Keywords
+ 4. Hints and Quirks
+
+
+1. OVERVIEW
+
+ ECML adds support for an extensible keyword system that allows users
+ to quickly add elements and embed media in their content. The ECML
+ control panel can be used to granualarly allow ECML keywords in certain
+ contexts and views.
+
+
+2. USING ECML KEYWORDS
+
+ All ECML keywords are surrounded by two square brackets: [[ and ]].
+ Some keywords, like views and entity lists, take optional parameters.
+
+ Example:
+ [[user_list]] -- Display up to 10 newest users.
+
+ [[user_list: list_type=online]] -- Display up to 10 active users.
+
+ [[user_list: list_type=online, only_with_avatars=true]] -- Display
+ up to 10 active users who have uploaded avatars.
+
+
+2.1 BUILT-IN KEYWORDS
+
+ ECML includes a few built-in keywords to get you started:
+ [[entity]] - Displays a list of users.
+
+ [[view]] - Shows the total members in your site, the currently
+ active members, and other fun stuff.
+
+
+2.2 Entities
+
+ You can generate a list of entities by using the [[entity]] keyword. This
+ keyword takes similar arguments to the elgg_get_entities() function. See
+ documentation in that function for a complete list.
+
+ Additional / changed parameters supported by keywords:
+ * owner: The username owner. (You can still use owner_guid)
+
+ Example: To generate a list of all blog posts by the user named 'admin':
+ [[entities: type=object, subtype=blog, owner=admin]]
+
+ Example: To show newest group created:
+ [[entities: type=object, subtype=group, limit=1]]
+
+
+2.1 Views
+
+ Keywords support outputting arbitrary views with the [[view]] keyword and
+ supports passing arguments as name=value pairs.
+
+ Example: Output a text input field with a default value:
+ [[view: input/text, value=This is a test!]]
+
+ NB: Do NOT quote the name or values when passing them. Also, as of 1.8
+ using commas or = in the name or value is unsupported.
+
+
+3.0 CUSTOM FRONT PAGE KEYWORDS
+
+ Plugins can add their own keywords by replying to the 'get_keywords' hook
+ of type 'sitepages.' Each keyword must be bound to a valid view. Almost
+ all functionality in custom keywords could be implemented using the 'view'
+ keyword, but custom keywords provide a simple way for non-techy users to
+ include ready-made views without the fuss of knowing what they're doing.
+
+ Custom keywords support arguments in the same format as views and entities.
+ These arguments are passed to the custom view via the $vars array. It is
+ the responsibility of the custom view to parse these arguments.
+
+ The below example creates the 'my_plugin_keyword' keyword that displays the
+ view at 'my_plugin/keyword_view.' This is exactly the same as saying
+ [[view: my_plugin/keyword_view]] but much simpler for the user.
+
+ Example:
+ register_plugin_hook('get_keywords', 'sitepages', 'my_plugin_keywords');
+
+ function my_plugin_keywords($hook, $type, $value, $params) {
+ $value['my_plugin_keyword'] = array(
+ 'view' => 'my_plugin/keyword_view',
+ 'description' => 'Provides the awesome My Plugin keyword'
+ );
+
+ return $value;
+ }
+
+
+4. HINTS AND QUIRKS
+
+ * A custom keyword is slightly more complicated to implement, but is
+ much simpler for the end user to use.
+
+ * Custom keywords can contain only alphanumeric and the underscore
+ character.
+
+ * All keywords have limited support for passing arguments but the arguments
+ cannot contain '=' or ','. If you need complicated arguments for a custom
+ keyword, it's better to split the functionality into multiple keywords and
+ views instead of requiring complicated arguments.
+
diff --git a/mod/ecml/ecml_functions.php b/mod/ecml/ecml_functions.php
new file mode 100644
index 000000000..dca4e03f9
--- /dev/null
+++ b/mod/ecml/ecml_functions.php
@@ -0,0 +1,168 @@
+<?php
+/**
+ * Helper functions for ECML.
+ *
+ * @package ECML
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2
+ * @author Curverider Ltd
+ * @copyright Curverider Ltd 2008-2010
+ * @link http://elgg.org/
+ */
+
+
+/**
+ * Parse ECML keywords
+ *
+ * @param array $matches
+ * @return string html
+ */
+function ecml_parse_view_match($matches) {
+ global $CONFIG;
+
+ $view = $CONFIG->ecml_current_view;
+
+ $keyword = trim($matches[1]);
+ $params_string = trim($matches[2]);
+
+ // reject keyword if blacklisted for view or invalid
+ if (!ecml_is_valid_keyword($keyword, $view)) {
+ return $matches[0];
+ }
+
+ switch ($keyword) {
+ case 'entity':
+ $options = ecml_keywords_parse_entity_params($params_string);
+ // must use this lower-level function because I missed refactoring
+ // the list entity functions for relationships.
+ // (which, since you're here, is the only function that runs through all
+ // possible options for elgg_get_entities*() functions...)
+ $entities = elgg_get_entities_from_relationship($options);
+ $content = elgg_view_entity_list($entities, count($entities), $options['offset'],
+ $options['limit'], $options['full_view'], $options['view_type_toggle'], $options['pagination']);
+ break;
+
+ case 'view':
+ // parses this into an acceptable array for $vars.
+ $info = ecml_keywords_parse_view_params($params_string);
+ $content = elgg_view($info['view'], $info['vars']);
+
+ break;
+
+ default:
+ // match against custom keywords with optional args
+ $keyword_info = $CONFIG->ecml_keywords[$keyword];
+ $vars = ecml_keywords_tokenize_params($params_string);
+ $content = elgg_view($keyword_info['view'], $vars);
+ break;
+ }
+
+ // if nothing matched return the original string.
+ if (!$content) {
+ $content = $matches[0];
+ }
+
+ return $content;
+}
+
+/**
+ * Creates an array from a "name=value, name2=value2" string.
+ *
+ * @param $string
+ * @return array
+ */
+function ecml_keywords_tokenize_params($string) {
+ $pairs = array_map('trim', explode(',', $string));
+ $params = array();
+
+ foreach ($pairs as $pair) {
+ list($name, $value) = explode('=', $pair);
+
+ $name = trim($name);
+ $value = trim($value);
+
+ // normalize BOOL values
+ if ($value === 'true') {
+ $value = TRUE;
+ } elseif ($value === 'false') {
+ $value = FALSE;
+ }
+
+ // don't check against value since a falsy/empty value is valid.
+ if ($name) {
+ $params[$name] = $value;
+ }
+ }
+
+ return $params;
+}
+
+/**
+ * Extract the view and vars for view: keyword
+ *
+ * @param $string
+ * @return array views, vars
+ */
+function ecml_keywords_parse_view_params($string) {
+ $vars = ecml_keywords_tokenize_params($string);
+
+ // the first element key is the view
+ $var_keys = array_keys($vars);
+ $view = $var_keys[0];
+
+ $info = array(
+ 'view' => $view,
+ 'vars' => $vars
+ );
+
+ return $info;
+
+}
+
+/**
+ * Returns an options array suitable for using in elgg_get_entities()
+ *
+ * @param string $string "name=value, name2=value2"
+ * @return array
+ */
+function ecml_keywords_parse_entity_params($string) {
+ $params = ecml_keywords_tokenize_params($string);
+
+ // handle some special cases
+ if (isset($params['owner'])) {
+ if ($user = get_user_by_username($params['owner'])) {
+ $params['owner_guid'] = $user->getGUID();
+ }
+ }
+
+ // @todo probably need to add more for
+ // group -> container_guid, etc
+ return $params;
+}
+
+/**
+ * Checks granular permissions if keyword is valid for view
+ *
+ * @param unknown_type $keyword
+ * @param unknown_type $view
+ * @return bool
+ */
+function ecml_is_valid_keyword($keyword, $view = NULL) {
+ global $CONFIG;
+
+ // this isn't even a real keyword.
+ if (!isset($CONFIG->ecml_keywords[$keyword])) {
+ return FALSE;
+ }
+
+ $views = $CONFIG->ecml_permissions['views'];
+ $contexts = $CONFIG->ecml_permissions['contexts'];
+
+ // this is a blacklist, so return TRUE by default.
+ $r = TRUE;
+
+ if (isset($views[$view]) && in_array($keyword, $views[$view])) {
+ $r = FALSE;
+ }
+
+ return $r;
+} \ No newline at end of file
diff --git a/mod/ecml/graphics/ecml.png b/mod/ecml/graphics/ecml.png
new file mode 100644
index 000000000..81de57b56
--- /dev/null
+++ b/mod/ecml/graphics/ecml.png
Binary files differ
diff --git a/mod/ecml/languages/en.php b/mod/ecml/languages/en.php
new file mode 100644
index 000000000..d93c7236a
--- /dev/null
+++ b/mod/ecml/languages/en.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Language definitions for ECML
+ *
+ * @package ecml
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2
+ * @author Curverider Ltd
+ * @copyright Curverider Ltd 2008-2010
+ * @link http://elgg.org/
+ */
+
+$english = array(
+ 'ecml' => 'ECML',
+ 'ecml:help' => 'ECML Help',
+
+ /**
+ * Key words
+ */
+ 'ecml:keywords_title' => 'Keywords',
+ 'ecml:keywords_instructions' =>
+ 'Keywords are replaced with content when viewed. They must be surrounded by
+ two square brackets ([[ and ]]). You can build your own or use the ones listed below.
+ Hover over a keyword to read its description.',
+
+ 'ecml:keywords_instructions_more' =>
+ '
+ <p>You can build your own keywords for views and entities.</p>
+
+ <p>[[entity: type=type, subtype=subtype, owner=username, limit=number]]<br />
+
+ EX: To show 5 blog posts by admin:<br />
+ [[entity: type=object, subtype=blog, owner=admin, limit=5]]</p>
+
+ <p>You can also specify a valid Elgg view:<br />
+ [[view: elgg_view, name=value]]</p>
+
+ <p>Ex: To show a text input with a default value:<br />
+ [[view: input/text, value=This is a default value]]</p>',
+);
+
+add_translation('en', $english); \ No newline at end of file
diff --git a/mod/ecml/manifest.xml b/mod/ecml/manifest.xml
new file mode 100644
index 000000000..266dbcc17
--- /dev/null
+++ b/mod/ecml/manifest.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<plugin_manifest>
+ <field key="name" value="ECML V2" />
+ <field key="author" value="Curverider" />
+ <field key="version" value="1.0" />
+ <field key="description" value="Elgg Custom Markup Language v2 adds support for easy customization of pages and embedding media." />
+ <field key="category" value="service" />
+ <field key="category" value="official" />
+ <field key="provides" value="ecml2" />
+ <field key="website" value="http://www.elgg.org/" />
+ <field key="copyright" value="(C) Curverider 2008-2010" />
+ <field key="licence" value="GNU Public License version 2" />
+ <field key="elgg_version" value="2009030702" />
+</plugin_manifest>
diff --git a/mod/ecml/start.php b/mod/ecml/start.php
new file mode 100644
index 000000000..0e89b4bbc
--- /dev/null
+++ b/mod/ecml/start.php
@@ -0,0 +1,149 @@
+<?php
+/**
+ * Provides the ECML service to plugins.
+ *
+ * @package ECML
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2
+ * @author Curverider Ltd
+ * @copyright Curverider Ltd 2008-2010
+ * @link http://elgg.org/
+ *
+ * @todo
+ * Granular access to keywords based upon view.
+ * Updated docs
+ * Check for SQL injection problems.
+ * Check entity keyword views against fullview. Force to FALSE?
+ *
+ */
+
+/**
+ * Init ECML
+ */
+function ecml_init() {
+ require_once(dirname(__FILE__) . '/ecml_functions.php');
+ global $CONFIG;
+
+ // help page
+ register_page_handler('ecml', 'ecml_help_page_handler');
+
+ // admin access page
+ register_page_handler('ecml_admin', 'ecml_admin_page_handler');
+ register_elgg_event_handler('pagesetup', 'system', 'ecml_pagesetup');
+
+ // parse views for keywords
+ register_plugin_hook('display', 'view', 'ecml_parse_view');
+
+ // show ECML-enabled icon on free-text input areas
+ elgg_extend_view('input/longtext', 'ecml/input_ext');
+ elgg_extend_view('input/plaintext', 'ecml/input_ext');
+ //elgg_extend_view('input/text', 'ecml/input_ext');
+
+ // get register the views we want to parse for ecml
+ // @todo will need to do profiling to see if it would be faster
+ // to foreach through this list and register to specific views or
+ // do the check in a single plugin hook.
+ // Wants array('view_name' => 'Short Description')
+ $CONFIG->ecml_parse_views = trigger_plugin_hook('get_views', 'ecml', NULL, array());
+
+ // provide a few built-in ecml keywords.
+ // @todo could pull this out into an array here to save an API call.
+ register_plugin_hook('get_keywords', 'ecml', 'ecml_keyword_hook');
+
+ // grab the list of keywords and their views from plugins
+ $CONFIG->ecml_keywords = trigger_plugin_hook('get_keywords', 'ecml', NULL, array());
+
+ // grab permissions for specific views/contexts
+ // this is a black list.
+ // it's more efficient to use this as a blacklist
+ // but probably makes more sense from a UI perspective as a whitelist.
+ // uses [views][view_name] = array(keywords, not, allowed)
+ $CONFIG->ecml_permissions = array(
+ 'views' => array()
+ );
+}
+
+/**
+ * Page setup. Adds admin controls to the admin panel for granular permission
+ */
+function ecml_pagesetup(){
+ if (get_context() == 'admin' && isadminloggedin()) {
+ global $CONFIG;
+ add_submenu_item(elgg_echo('ecml'), $CONFIG->wwwroot . 'pg/ecml_admin');
+ }
+}
+
+/**
+ * Display a help page for valid ECML keywords on this page.
+ *
+ * @param array $page
+ */
+function ecml_help_page_handler($page) {
+
+ $content = elgg_view('ecml/help');
+ echo page_draw(elgg_echo('ecml:help'), $content);
+}
+
+/**
+ * Display a help page for valid ECML keywords on this page.
+ *
+ * @param array $page
+ */
+function ecml_admin_page_handler($page) {
+ $content = elgg_view('ecml/admin');
+ echo page_draw(elgg_echo('ecml:admin'), $content);
+}
+
+/**
+ * Parses a registered view / context for supported keywords.
+ *
+ * @param unknown_type $hook
+ * @param unknown_type $entity_type
+ * @param unknown_type $return_value
+ * @param unknown_type $params
+ * @return string
+ */
+function ecml_parse_view($hook, $entity_type, $return_value, $params) {
+ global $CONFIG;
+
+ // give me everything that is not a ], possibly followed by a :, and surrounded by [[ ]]s
+ $keyword_regex = '/\[\[([a-z0-9_]+):?([^\]]+)?\]\]/';
+
+ if (array_key_exists($params['view'], $CONFIG->ecml_parse_views)) {
+ $CONFIG->ecml_current_view = $params['view'];
+
+ $return_value = preg_replace_callback($keyword_regex, 'ecml_parse_view_match', $return_value);
+ }
+
+ return $return_value;
+}
+
+
+/**
+ * Register some default keywords.
+ *
+ * @param unknown_type $hook
+ * @param unknown_type $entity_type
+ * @param unknown_type $return_value
+ * @param unknown_type $params
+ * @return unknown_type
+ */
+function ecml_keyword_hook($hook, $entity_type, $return_value, $params) {
+ $return_value['login_box'] = array(
+ 'view' => 'account/forms/login',
+ 'description' => elgg_echo('ecml:keywords:login_box')
+ );
+
+ $return_value['user_list'] = array(
+ 'view' => 'ecml/keywords/user_list',
+ 'description' => elgg_echo('ecml:keywords:user_list')
+ );
+
+ $return_value['site_stats'] = array(
+ 'view' => 'ecml/keywords/site_stats',
+ 'description' => elgg_echo('ecml:keywords:site_stats')
+ );
+
+ return $return_value;
+}
+
+register_elgg_event_handler('init', 'system', 'ecml_init'); \ No newline at end of file
diff --git a/mod/ecml/views/default/ecml/help.php b/mod/ecml/views/default/ecml/help.php
new file mode 100644
index 000000000..aed6bd2e1
--- /dev/null
+++ b/mod/ecml/views/default/ecml/help.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Lists available keywords
+ *
+ * @package ECML
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2
+ * @author Curverider Ltd
+ * @copyright Curverider Ltd 2008-2010
+ * @link http://elgg.org/
+ */
+
+$keywords = $vars['config']->sitepages_keywords;
+$title = elgg_echo('ecml:keywords_title');
+$instructions = elgg_echo('ecml:keywords_instructions');
+$more_info = elgg_echo('ecml:keywords_instructions_more');
+
+
+
+$keywords_html = '';
+foreach ($keywords as $keyword => $info) {
+ $desc = htmlentities($info['description']);
+ $keywords_html .= "<li><acronym title=\"$desc\">[[$keyword]]</acronym></li>";
+}
+
+echo "
+<h3>$title</h3>
+<p>$instructions</p>
+$more_info
+<ul>
+ $keywords_html
+</ul>
+"; \ No newline at end of file
diff --git a/mod/ecml/views/default/ecml/input_ext.php b/mod/ecml/views/default/ecml/input_ext.php
new file mode 100644
index 000000000..03941fb08
--- /dev/null
+++ b/mod/ecml/views/default/ecml/input_ext.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * Displays an ECML icon on ECML-enabled forms
+ *
+ * @package ECML
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2
+ * @author Curverider Ltd
+ * @copyright Curverider Ltd 2008-2010
+ * @link http://elgg.org/
+ */
+
+$docs_href = "{$vars['url']}pg/ecml";
+?>
+<a href="<?php echo $docs_href; ?>" target="_new"><img src="<?php echo $vars['url']; ?>mod/ecml/graphics/ecml.png" width="50" height="15" alt="ECML" /></a> \ No newline at end of file
diff --git a/mod/ecml/views/default/ecml/keywords/user_list.php b/mod/ecml/views/default/ecml/keywords/user_list.php
new file mode 100644
index 000000000..2f2f09c49
--- /dev/null
+++ b/mod/ecml/views/default/ecml/keywords/user_list.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Lists users
+ *
+ * @package SitePages
+ * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2
+ * @author Curverider Ltd
+ * @copyright Curverider Ltd 2008-2010
+ * @link http://elgg.org/
+ */
+
+$only_with_avatars = (isset($vars['only_with_avatars'])) ? $vars['only_with_avatars'] : TRUE;
+$list_type = (isset($vars['list_type'])) ? $vars['list_type'] : 'newest';
+$limit = (isset($vars['limit'])) ? $vars['limit'] : 10;
+
+$options = array(
+ 'type' => 'user',
+ 'limit' => $limit
+);
+
+if ($only_with_avatars == TRUE) {
+ $options['metadata_name_value_pairs'] = array('name' => 'icontime', 'operand' => '!=', 'value' => 0);
+}
+
+switch ($list_type) {
+ case 'newest':
+ $options['order_by'] = 'e.time_created DESC';
+ break;
+
+ case 'online':
+ // show people with a last action of < 10 minutes.
+ $last_action = time() - 10 * 60;
+ $options['joins'] = array("JOIN {$vars['config']->dbprefix}users_entity ue on ue.guid = e.guid");
+ $options['wheres'] = array("ue.last_action > $last_action");
+ break;
+
+ case 'random':
+ $options['order_by'] = 'RAND()';
+ break;
+
+ default:
+ break;
+}
+
+$users = elgg_get_entities_from_metadata($options);
+
+echo elgg_view_entity_list($users, count($users), 0, $limit, FALSE, FALSE, FALSE); \ No newline at end of file