From 444fb4e8a3e5189868b8b12c5fdccc4bef6868fc Mon Sep 17 00:00:00 2001 From: brettp Date: Tue, 13 Apr 2010 19:13:36 +0000 Subject: First version of ecml. git-svn-id: http://code.elgg.org/elgg/trunk@5722 36083f99-b078-4883-b0ff-0f9b5a30f544 --- mod/ecml/README.txt | 113 ++++++++++++++ mod/ecml/ecml_functions.php | 168 +++++++++++++++++++++ mod/ecml/graphics/ecml.png | Bin 0 -> 341 bytes mod/ecml/languages/en.php | 41 +++++ mod/ecml/manifest.xml | 14 ++ mod/ecml/start.php | 149 ++++++++++++++++++ mod/ecml/views/default/ecml/help.php | 32 ++++ mod/ecml/views/default/ecml/input_ext.php | 14 ++ mod/ecml/views/default/ecml/keywords/user_list.php | 47 ++++++ 9 files changed, 578 insertions(+) create mode 100644 mod/ecml/README.txt create mode 100644 mod/ecml/ecml_functions.php create mode 100644 mod/ecml/graphics/ecml.png create mode 100644 mod/ecml/languages/en.php create mode 100644 mod/ecml/manifest.xml create mode 100644 mod/ecml/start.php create mode 100644 mod/ecml/views/default/ecml/help.php create mode 100644 mod/ecml/views/default/ecml/input_ext.php create mode 100644 mod/ecml/views/default/ecml/keywords/user_list.php (limited to 'mod/ecml') 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 @@ +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 Binary files /dev/null and b/mod/ecml/graphics/ecml.png 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 @@ + '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' => + ' +

You can build your own keywords for views and entities.

+ +

[[entity: type=type, subtype=subtype, owner=username, limit=number]]
+ + EX: To show 5 blog posts by admin:
+ [[entity: type=object, subtype=blog, owner=admin, limit=5]]

+ +

You can also specify a valid Elgg view:
+ [[view: elgg_view, name=value]]

+ +

Ex: To show a text input with a default value:
+ [[view: input/text, value=This is a default value]]

', +); + +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 @@ + + + + + + + + + + + + + + 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 @@ + '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 @@ +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 .= "
  • [[$keyword]]
  • "; +} + +echo " +

    $title

    +

    $instructions

    +$more_info + +"; \ 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 @@ + +ECML \ 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 @@ + '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 -- cgit v1.2.3