Commit 66136667 authored by Robert Lyon's avatar Robert Lyon Committed by Gerrit Code Review
Browse files

Render a tagged journal block with multiple tags in play (Bug #1317343)



To include and/or exclude certain tags - has two pools of tags; those
that are 'in' and those that are 'out'.

To exclude a tag begin your search with a minus sign.

Change-Id: I32a3b3d3f244d08bd0547d72db25b8189642fc0e
Signed-off-by: Robert Lyon's avatarRobert Lyon <robertl@catalyst.net.nz>
parent c6c17547
<?php
/**
*
* @package mahara
* @subpackage artefact-blog
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
defined('INTERNAL') || die();
function xmldb_blocktype_taggedposts_upgrade($oldversion=0) {
if ($oldversion < 2014121700) {
// if we have existing taggedposts blocks we will need to to update them
if ($taggedblockids = get_column('block_instance', 'id', 'blocktype', 'taggedposts')) {
require_once(get_config('docroot') . 'blocktype/lib.php');
foreach ($taggedblockids as $blockid) {
$bi = new BlockInstance($blockid);
$configdata = $bi->get('configdata');
if (isset($configdata['tagselect']) && !is_array($configdata['tagselect'])) {
$configdata['tagselect'] = array($configdata['tagselect'] => 1);
$bi->set('configdata', $configdata);
$bi->commit();
}
}
}
}
return true;
}
<?php
/**
*
* @package mahara
* @subpackage artefact-internal
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
defined('INTERNAL') || die();
function xmldb_artefact_blog_upgrade($oldversion=0) {
if ($oldversion < 2008101602) {
$table = new XMLDBTable('artefact_blog_blogpost_file_pending');
$table->addFieldInfo('id', XMLDB_TYPE_INTEGER, 10, XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null, null, null);
$table->addFieldInfo('oldextension', XMLDB_TYPE_TEXT, null);
$table->addFieldInfo('filetype', XMLDB_TYPE_TEXT, null);
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
create_table($table);
}
if ($oldversion < 2009033100) {
$bloguploadbase = get_config('dataroot') . 'artefact/blog/uploads/';
if (is_dir($bloguploadbase)) {
if ($basedir = opendir($bloguploadbase)) {
while (false !== ($sessionupload = readdir($basedir))) {
if ($sessionupload != "." && $sessionupload != "..") {
$sessionupload = $bloguploadbase . $sessionupload;
$subdir = opendir($sessionupload);
while (false !== ($uploadfile = readdir($subdir))) {
if ($uploadfile != "." && $uploadfile != "..") {
$uploadfile = $sessionupload . '/' . $uploadfile;
unlink($uploadfile);
}
}
closedir($subdir);
rmdir($sessionupload);
}
}
}
@rmdir($bloguploadbase);
}
}
if ($oldversion < 2009081800) {
$subscription = (object) array('plugin' => 'blog', 'event' => 'createuser', 'callfunction' => 'create_default_blog');
ensure_record_exists('artefact_event_subscription', $subscription, $subscription);
}
if ($oldversion < 2011091400) {
delete_records('artefact_cron', 'plugin', 'blog', 'callfunction', 'clean_post_files');
}
return true;
}
......@@ -12,13 +12,24 @@
defined('INTERNAL') || die();
$string['title'] = 'Tagged journal entries';
$string['description'] = 'Display journal entries with a particular tag (see Content -> Journal)';
$string['blockheading'] = 'Journal entries tagged';
$string['description'] = 'Display journal entries with particular tags (see Content ➞ Journal)';
$string['blockheadingtags'] = array(
0 => 'Journal entries with tag %2$s',
1 => 'Journal entries with tags %2$s'
);
$string['blockheadingtagsomit'] = array(
0 => ' but not tag %2$s',
1 => ' but not tags %2$s'
);
$string['defaulttitledescription'] = 'If you leave this blank, the title of the journal will be used';
$string['postsperpage'] = 'Entries per page';
$string['taglist'] = 'My tags';
$string['notags'] = 'There are no posts tagged "%s"';
$string['taglist'] = 'Display entries tagged with';
$string['taglistdesc'] = 'Type a minus sign before each tag that you want to exclude. These tags are shown with a blue background.';
$string['excludetag'] = 'exclude tag: ';
$string['notags'] = 'There are no journal entries tagged "%s"';
$string['notagsboth'] = 'There are no journal entries tagged "%s" and not "%s"';
$string['notagsavailable'] = 'You have not created any tags';
$string['notagsavailableerror'] = 'No tag selected - You need to add tags to your journal entries before being able to select them here.';
$string['postedin'] = 'in';
$string['postedon'] = 'on';
$string['itemstoshow'] = 'Items to show';
......
<!-- @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later -->
<!-- @copyright For copyright information on Mahara, please see the README file distributed with this software. -->
<h3>Choosing tags</h3>
<p>You can add multiple tags to limit the results as well as indicate which tags you want to ignore.</P>
<p>If you select multiple tags, a blog entry must match <strong>ALL</strong> selected tags in order to be listed.</p>
<p>If you had journal entries tagged like so:</p>
<ol>
<li>cats, domestic, animals</li>
<li>cats, domestic, animals, small</li>
<li>cats, domestic, animals, large</li>
<li>cats, wild, animals</li>
<li>cats, wild, animals, small</li>
<li>cats, wild, animals, large</li>
<li>dogs, wild, animals</li>
<li>dogs, wild, animals, small</li>
<li>dogs, wild, animals, large</li>
</ol>
<p>and you:</p>
<ul>
<li>select the tag "animals", all journal entries would be displayed in the block.</li>
<li>select the tags "cats" and "large", journal entries 3 and 6 would be displayed.</li>
<li>select the tags "animals" and "large", journal entries 3, 6, and 9 would be shown.</li>
</ul>
<h4>Excluding tags:</h4>
<p>You can also exclude tags from the results by putting a minus sign before the tag you want to select.</p>
<p>If you:</p>
<ul>
<li>select the tag "cats" and exclude "domestic" (exclude the tag by typing "-domestic" without the quotation marks), journal entries 4, 5, and 6 would be displayed.</li>
<li>select the tag "wild" and "animals" and exclude "small" and "large" (exclude the tag by typing "-small" "-large" without the quotation marks), journal entries 4 and 7 would be shown.</li>
</ul>
......@@ -46,11 +46,22 @@ class PluginBlocktypeTaggedposts extends SystemBlocktype {
$smarty = smarty_core();
$smarty->assign('view', $view);
$viewownerdisplay = null;
// Display all posts, from all blogs, owned by this user
$configdata['tagselect'] = (!empty($configdata['tagselect'])) ? $configdata['tagselect'] : array();
if (!empty($configdata['tagselect'])) {
$tagselect = $configdata['tagselect'];
$tagsin = $tagsout = array();
foreach ($tagselect as $key => $value) {
if (!empty($value)) {
$tagsin[] = $key;
}
else {
$tagsout[] = $key;
}
}
$tagsout = array_filter($tagsout);
$sqlvalues = array($view);
$sql =
'SELECT a.title, p.title AS parenttitle, a.id, a.parent, a.owner, a.description, a.allowcomments, at.tag, a.ctime
FROM {artefact} a
......@@ -58,13 +69,44 @@ class PluginBlocktypeTaggedposts extends SystemBlocktype {
JOIN {artefact_blog_blogpost} ab ON (ab.blogpost = a.id AND ab.published = 1)
JOIN {artefact_tag} at ON (at.artefact = a.id)
WHERE a.artefacttype = \'blogpost\'
AND a.owner = (SELECT "owner" from {view} WHERE id = ?)
AND at.tag = ?
ORDER BY a.ctime DESC, a.id DESC
LIMIT ?';
$results = get_records_sql_array($sql, array($view, $tagselect, $limit));
AND a.owner = (SELECT "owner" from {view} WHERE id = ?)';
if (!empty($tagsin)) {
foreach ($tagsin as $tagin) {
$sql .= ' AND EXISTS (
SELECT * FROM {artefact_tag} AS at
WHERE a.id = at.artefact
AND at.tag = ?
)';
}
$sqlvalues = array_merge($sqlvalues, $tagsin);
}
if (!empty($tagsout)) {
foreach ($tagsout as $tagout) {
$sql .= ' AND NOT EXISTS (
SELECT * FROM {artefact_tag} AS at
WHERE a.id = at.artefact
AND at.tag = ?
)';
}
$sqlvalues = array_merge($sqlvalues, $tagsout);
}
$sql .= ' ORDER BY a.ctime DESC, a.id DESC';
$results = get_records_sql_array($sql, $sqlvalues);
// We need to filter this down to unique results
if (!empty($results)) {
$used = array();
foreach ($results as $key => $result) {
if (array_search($result->id, $used) === false) {
$used[] = $result->id;
}
else {
unset($results[$key]);
}
}
if (!empty($limit)) {
$results = array_slice($results, 0, $limit);
}
}
$smarty->assign('blockid', $instance->get('id'));
$smarty->assign('editing', $editing);
if ($editing) {
......@@ -73,13 +115,14 @@ class PluginBlocktypeTaggedposts extends SystemBlocktype {
if (!$viewowner || !$blogs = get_records_select_array('artefact', 'artefacttype = \'blog\' AND owner = ?', array($viewowner), 'title ASC', 'id, title')) {
$blogs = array();
}
$smarty->assign('tagselect', $tagselect);
$smarty->assign('tagselect', implode(', ', $tagsin));
$smarty->assign('blogs', $blogs);
}
// if posts are not found with the selected tag, notify the user
if (!$results) {
$smarty->assign('badtag', $tagselect);
$smarty->assign('badtag', implode(', ', array_keys($tagselect,1)));
$smarty->assign('badnotag', implode(', ', array_keys($tagselect,0)));
return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
}
......@@ -128,33 +171,51 @@ class PluginBlocktypeTaggedposts extends SystemBlocktype {
// check if the user viewing the page is the owner of the selected tag
$owner = $results[0]->owner;
if ($USER->id != $owner) {
$viewowner = get_user_for_display($owner);
$smarty->assign('viewowner', $viewowner);
$viewownerdisplay = get_user_for_display($owner);
}
$smarty->assign('tag', $tagselect);
$smarty->assign('tagsin', $tagsin);
$smarty->assign('tagsout', $tagsout);
}
else if (!self::get_tags()) {
// error if block configuration fails
$smarty->assign('configerror', get_string('notagsavailableerror', 'blocktype.blog/taggedposts'));
return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
}
else {
// error if block configuration fails
$smarty->assign('configerror', true);
$smarty->assign('configerror', get_string('configerror', 'blocktype.blog/taggedposts'));
return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
}
// add any needed links to the tags
$tagstr = $tagomitstr = '';
foreach ($tagsin as $key => $tag) {
if ($key > 0) {
$tagstr .= ', ';
}
$tagstr .= ($viewownerdisplay) ? '"' . $tag . '"' : '"<a href="' . get_config('wwwroot') . 'tags.php?tag=' . $tag . '&sort=name&type=text">' . $tag . '</a>"';
}
if (!empty($tagsout)) {
foreach ($tagsout as $key => $tag) {
if ($key > 0) {
$tagomitstr .= ', ';
}
$tagomitstr .= ($viewownerdisplay) ? '"' . $tag . '"' : '"<a href="' . get_config('wwwroot') . 'tags.php?tag=' . $tag . '&sort=name&type=text">' . $tag . '</a>"';
}
}
$blockheading = get_string('blockheadingtags', 'blocktype.blog/taggedposts', count($tagsin), $tagstr);
$blockheading .= (!empty($tagomitstr)) ? get_string('blockheadingtagsomit', 'blocktype.blog/taggedposts', count($tagsout), $tagomitstr) : '';
$blockheading .= ($viewownerdisplay) ? ' ' . get_string('by', 'artefact.blog') . ' <a href="' . profile_url($viewownerdisplay) . '">' . display_name($viewownerdisplay) . '</a>' : '';
$smarty->assign('full', $full);
$smarty->assign('results', $results);
$smarty->assign('blockheading', $blockheading);
return $smarty->fetch('blocktype:taggedposts:taggedposts.tpl');
}
public static function has_instance_config() {
return true;
}
public static function instance_config_form(BlockInstance $instance) {
private static function get_tags() {
global $USER;
$configdata = $instance->get('configdata');
$tags = get_records_sql_array("
return get_records_sql_array("
SELECT at.tag
FROM {artefact_tag} at
JOIN {artefact} a
......@@ -164,19 +225,57 @@ class PluginBlocktypeTaggedposts extends SystemBlocktype {
GROUP BY at.tag
ORDER BY at.tag ASC
", array($USER->id));
}
public static function has_instance_config() {
return true;
}
public static function instance_config_form(BlockInstance $instance) {
global $USER;
$configdata = $instance->get('configdata');
$tags = self::get_tags();
$elements = array();
$options = array();
$tagselect = array();
if (!empty($tags)) {
foreach ($tags as $tag) {
$options[$tag->tag] = $tag->tag;
if (!empty($configdata['tagselect'])) {
foreach ($configdata['tagselect'] as $tag => $option) {
if ($option == '1') {
$tagselect[] = $tag;
}
else {
$tagselect[] = '-' . $tag;
}
}
}
// The javascript to alter the display for the excluded tags
$excludetag = get_string('excludetag', 'blocktype.blog/taggedposts');
$formatSelection = <<<EOF
function (item, container) {
if (item.id[0] == "-") {
container.parent().addClass("tagexcluded");
item.text = '<span class="accessible-hidden">{$excludetag}</span>' + item.text;
}
return item.text;
}
EOF;
$elements['tagselect'] = array(
'type' => 'select',
'type' => 'autocomplete',
'title' => get_string('taglist','blocktype.blog/taggedposts'),
'options' => $options,
'defaultvalue' => !empty($configdata['tagselect']) ? $configdata['tagselect'] : $tags[0]->tag,
'description' => get_string('taglistdesc', 'blocktype.blog/taggedposts'),
'defaultvalue' => $tagselect,
'ajaxurl' => get_config('wwwroot') . 'artefact/blog/blocktype/taggedposts/taggedposts.json.php',
'initfunction' => 'translate_ids_to_tags',
'multiple' => true,
'ajaxextraparams' => array(),
'rules' => array('required' => 'true'),
'required' => true,
'blockconfig' => true,
'help' => true,
'mininputlength' => 0,
'extraparams' => array('formatSelection' => "$formatSelection"),
);
$elements['count'] = array(
'type' => 'text',
......@@ -207,6 +306,39 @@ class PluginBlocktypeTaggedposts extends SystemBlocktype {
}
public static function instance_config_validate($form, $values) {
if (empty($values['tagselect'])) {
// We don't have a tagselect field due to no journal entries having a tag
$form->set_error(null, get_string('notagsavailableerror', 'blocktype.blog/taggedposts'));
}
else {
// Need to fully check that the returned array is empty
$values['tagselect'] = array_filter($values['tagselect']);
if (empty($values['tagselect'])) {
$result['message'] = get_string('required', 'mahara');
$form->set_error('tagselect', $form->i18n('rule', 'required', 'required'), false);
$form->reply(PIEFORM_ERR, $result);
}
}
}
public static function instance_config_save($values) {
$tagselect = $values['tagselect'];
unset($values['tagselect']);
if (!empty($tagselect)) {
foreach ($tagselect as $tag) {
$value = 1;
if (substr($tag, 0, 1) == '-') {
$value = 0;
$tag = substr($tag, 1);
}
$values['tagselect'][$tag] = $value;
}
}
return $values;
}
public static function default_copy_type() {
return 'nocopy';
}
......@@ -220,3 +352,15 @@ class PluginBlocktypeTaggedposts extends SystemBlocktype {
}
}
function translate_ids_to_tags(array $ids) {
$ids = array_diff($ids, array(''));
$results = array();
if (!empty($ids)) {
foreach ($ids as $id) {
$text = (substr($id, 0, 1) == '-') ? substr($id, 1) : $id;
$results[] = (object) array('id' => $id, 'text' => $text);
}
}
return $results;
}
<?php
/**
*
* @package mahara
* @subpackage artefact-blog-taggedposts
* @author Catalyst IT Ltd
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL version 3 or later
* @copyright For copyright information on Mahara, please see the README file distributed with this software.
*
*/
define('INTERNAL', 1);
define('JSON', 1);
require(dirname(dirname(dirname(dirname(dirname(__FILE__))))) . '/init.php');
global $USER;
$request = param_variable('q');
$tagexcluded = '';
if (substr($request, 0, 1) == '-') {
$request = substr($request, 1);
$tagexcluded = '-';
}
$page = param_integer('page');
if ($page < 1) {
$page = 1;
}
$tagsperpage = 5;
$values = array($USER->id);
$sql = "SELECT at.tag FROM {artefact_tag} at
JOIN {artefact} a ON a.id = at.artefact
WHERE a.owner = ?
AND a.artefacttype = 'blogpost'";
if ($request !== '') {
$sql .= " AND at.tag LIKE '%' || ? || '%'";
$values[] = $request;
}
$sql .= " GROUP BY at.tag
ORDER BY at.tag ASC";
$more = true;
$tmptags = array();
$alltags = get_records_sql_array($sql, $values);
while ($alltags !== false && $more && count($tmptags) < $tagsperpage) {
$tags = array_slice($alltags, $tagsperpage * ($page - 1), $tagsperpage);
$more = sizeof($alltags) > $tagsperpage * $page;
foreach ($tags as $tag) {
if (count($tmptags) >= $tagsperpage) {
$more = true;
continue;
}
if (stripos($tag->tag, $request) !== false || $request === '') {
$tmptags[] = (object) array('id' => $tagexcluded . $tag->tag,
'text' => $tag->tag);
}
}
$page++;
}
echo json_encode(array(
'more' => $more,
'results' => $tmptags,
));
\ No newline at end of file
......@@ -12,5 +12,5 @@
defined('INTERNAL') || die();
$config = new StdClass;
$config->version = 2012080600;
$config->release = '1.0.2';
$config->version = 2014121700;
$config->release = '1.0.3';
......@@ -49,6 +49,7 @@
// that you expect will be wide enough to display your entries.
// 'extraparams' => array(key => value, key2 => value2, ...), // Optional additional configuration parameters for
// the select2 ajax library.
// 'inblockconfig' => If the field is a block config field we need to handle the js autocomplete js slightly differently
// ),
defined('INTERNAL') || die();
......@@ -94,7 +95,7 @@ function pieform_element_autocomplete(Pieform $form, $element) {
$extraparams = '';
if (!empty($element['extraparams'])) {
foreach ($element['extraparams'] as $k => $v) {
if (!is_numeric($v)) {
if (!is_numeric($v) && !preg_match('/^function/', $v)) {
if (preg_match('/^\'(.*)\'$/', $v, $match)) {
$v = $match[1];
}
......@@ -117,6 +118,7 @@ function pieform_element_autocomplete(Pieform $form, $element) {
$smarty->assign('sesskey', $USER->get('sesskey'));
$smarty->assign('hint', empty($element['hint']) ? get_string('defaulthint') : $element['hint']);
$smarty->assign('extraparams', $extraparams);
$smarty->assign('inblockconfig', !empty($element['inblockconfig']) ? 'true' : 'false');
if (isset($element['description'])) {
$smarty->assign('describedby', $form->element_descriptors($element));
}
......
......@@ -10,11 +10,9 @@
</div>
{/if}
<p>{str tag='blockheading' section='blocktype.blog/taggedposts'}
{if $viewowner}{$tag} {str tag='by' section='artefact.blog'} <a href="{profile_url($viewowner)}">{$viewowner|display_name}</a>
{else}<a href="{$WWWROOT}tags.php?tag={$tag}&sort=name&type=text">{$tag}</a>{/if}</p>
{if $configerror}{str tag='configerror' section='blocktype.blog/taggedposts'}
<p>{$blockheading|safe}</p>
{if $configerror}{$configerror}
{elseif $badtag && $badnotag}{str tag='notagsboth' section='blocktype.blog/taggedposts' arg1=$badtag arg2=$badnotag}
{elseif $badtag}{str tag='notags' section='blocktype.blog/taggedposts' arg1=$badtag}
{elseif $full}
<div id="blogdescription">
......
......@@ -837,6 +837,9 @@ a.btn-addfile {
display: inline-block;
margin-top: 5px;
}
.tagexcluded {
background: lightblue !important;
}
/******************** Buttons ********************/
/* Default buttons */
/* default state */
......
<input type="hidden" id="{{$id}}" name="{{$name}}" {{if $describedby}}aria-describedby="{{$describedby}}"{{/if}} value="{{$value}}"/>
<script type="text/javascript">
{{if !$inblockconfig}}
addLoadEvent(function () {
{{/if}}
jQuery("#{{$id}}").select2({
initSelection : function(element, callback) {
callback({{$initvalue|safe}});
......@@ -30,6 +32,8 @@ addLoadEvent(function () {
},
{{$extraparams|safe}}
});
{{if !$inblockconfig}}
});
{{/if}}
jQuery("#{{$id}}").prop('disabled', {{$disabled}});
</script>