diff options
| -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  | 
