diff options
author | brettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544> | 2010-04-13 19:13:36 +0000 |
---|---|---|
committer | brettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544> | 2010-04-13 19:13:36 +0000 |
commit | 444fb4e8a3e5189868b8b12c5fdccc4bef6868fc (patch) | |
tree | 2df668e1e079d354006938b2f6a0528b2b22d09b /mod | |
parent | ece80595836be086201629824bceeae05892cd55 (diff) | |
download | elgg-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
Diffstat (limited to 'mod')
-rw-r--r-- | mod/ecml/README.txt | 113 | ||||
-rw-r--r-- | mod/ecml/ecml_functions.php | 168 | ||||
-rw-r--r-- | mod/ecml/graphics/ecml.png | bin | 0 -> 341 bytes | |||
-rw-r--r-- | mod/ecml/languages/en.php | 41 | ||||
-rw-r--r-- | mod/ecml/manifest.xml | 14 | ||||
-rw-r--r-- | mod/ecml/start.php | 149 | ||||
-rw-r--r-- | mod/ecml/views/default/ecml/help.php | 32 | ||||
-rw-r--r-- | mod/ecml/views/default/ecml/input_ext.php | 14 | ||||
-rw-r--r-- | mod/ecml/views/default/ecml/keywords/user_list.php | 47 |
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 Binary files differnew file mode 100644 index 000000000..81de57b56 --- /dev/null +++ b/mod/ecml/graphics/ecml.png 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 |