Revision: 543
Author: geof.glass
Date: Wed Jun 20 22:56:48 2012
Log: License with GPL3
Submit fixes that accidentally missed the last SVN commit
http://code.google.com/p/marginalia/source/detail?r=543
Modified:
/moodle/trunk/moodle/blocks/marginalia/MoodleMarginalia.js
/moodle/trunk/moodle/blocks/marginalia/annotate.php
/moodle/trunk/moodle/blocks/marginalia/annotation-styles.php
/moodle/trunk/moodle/blocks/marginalia/annotation_summary_query.php
/moodle/trunk/moodle/blocks/marginalia/block_marginalia.php
/moodle/trunk/moodle/blocks/marginalia/config.php
/moodle/trunk/moodle/blocks/marginalia/keywords.php
/moodle/trunk/moodle/blocks/marginalia/keywords_db.php
/moodle/trunk/moodle/blocks/marginalia/lib.php
/moodle/trunk/moodle/blocks/marginalia/marginalia-config.js
/moodle/trunk/moodle/blocks/marginalia/smartquote.js
/moodle/trunk/moodle/blocks/marginalia/summary-styles.php
/moodle/trunk/moodle/blocks/marginalia/summary.js
/moodle/trunk/moodle/blocks/marginalia/summary.php
/moodle/trunk/moodle/blocks/marginalia/user-preference.php
/moodle/trunk/moodle/blocks/marginalia/version.php
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/MoodleMarginalia.js Wed May 30
14:29:48 2012
+++ /moodle/trunk/moodle/blocks/marginalia/MoodleMarginalia.js Wed Jun 20
22:56:48 2012
@@ -8,12 +8,12 @@
* Canada, the UNDESA Africa i-Parliaments Action Plan, and
* units and individuals within those organizations. Many
* thanks to all of them. See CREDITS.html for details.
- * Copyright (C) 2005-2007 Geoffrey Glass; the United Nations
+ * Copyright (C) 2005-2011 Geoffrey Glass; the United Nations
*
http://www.geof.net/code/annotation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
+ * as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@@ -38,89 +38,192 @@
this.preferences = new Preferences(
new RestPreferenceService( this.annotationPath + '/user-preference.php'
),
prefs );
- this.showAnnotations = prefs[ AN_SHOWANNOTATIONS_PREF ];
+ this.showAnnotations = prefs[ Marginalia.P_SHOWANNOTATIONS ];
this.showAnnotations = this.showAnnotations == 'true';
- this.splash = prefs[ AN_SPLASH_PREF ] == 'true' ? params[ 'splash' ] :
null;
+ this.splash = prefs[ Marginalia.P_SPLASH ] == 'true' ? params[ 'splash'
] : null;
this.useSmartquote = params.useSmartquote;
this.allowAnyUserPatch = params.allowAnyUserPatch;
this.smartquoteIcon = params.smartquoteIcon;
-
- // This is kind of awkward. If the user changes the display user, then
- // visits another page, then uses the browser back button, we need to use
- // the new display user, not the one set when the page was loaded the
first
- // time. So check whether the user dropdown has a value, and match it.
- this.displayUserId = prefs[ AN_USER_PREF ];
- var userCtrl = document.getElementById( 'anuser' );
- if ( userCtrl )
- {
- if ( userCtrl.selectedIndex >= 0 )
- {
- var userId = userCtrl.options[ userCtrl.selectedIndex ].value;
- if ( userId )
- {
- this.displayUserId = userId;
- this.showAnnotations = true;
- }
- else
- this.showAnnotations = false;
- }
- }
+ this.handlers = params.handlers;
+ this.course = params.course;
+ this.smartquoteService = params.smartquoteService;
+ LocalizedAnnotationStrings = params.strings;
+
+ var urlFunc = function( node )
+ {
+ // strip off leading character
+ var id = node.id.substr( 1 );
+
+ // Must first figure out which kind of page is being annotated,
+ // then return the correct URL for an individual *post*, which may
+ // not be the same as the page as a whole (e.g. in the case of the
forum).
+ var x = window.location.href.indexOf( '#' );
+ var base = -1 == x ? window.location.href : window.location.href.substr(
0, x );
+ // console.log( 'match URL ' + base );
+ var matches = base.match( /\/mod\/forum\/discuss.php\?d=([0-9]+)/ );
+ if ( matches )
+ {
+ return '/mod/forum/permalink.php?p=' + node.id.substr( 1 );
+ }
+ else
+ throw "Annotation not supported for this page URL in
MoodleMarginalia.js.";
+ };
+
+ this.selectors = {
+ post: new Selector( '.forumpost', '.forumpost table.forumpost' ),
+ post_id: new Selector( function( root ) { return $( root ).prev( 'a' );
}, null, '@id' ),
+ post_content: new
Selector( '.content .posting', '.content .content .posting' ),
+ post_title: new Selector( '.subject', '.content .subject' ),
+ post_author: new Selector( '.author a', '.content .author a' ),
+ post_authorid: null,
+ post_date: null,
+ post_url: new Selector( function( root) { return $( root ).prev( 'a' );
}, null, urlFunc ),
+ mia_notes: new Selector( '.mia_margin', '.content .posting .mia_margin' )
+ };
+ // URL is blank! Should be a function to construct it from
window.location + '#' + id
+
+ this.sheet = prefs[ Marginalia.P_SHEET ];
}
+/**
+ * Callback on page load to initialize Marginalia.
+ * Looks at the page URL to determine which URL resource this is and
+ * figure out how to set Marginalia up. If annotation is not supported for
+ * this resource nothing happens. Checking here rather than in the PHP
+ * minimizes the number of patches that need to be applied to existing
Moodle
+ * code.
+ */
MoodleMarginalia.prototype.onload = function( )
{
- //initLogging();
+ initLogging();
// Check whether this page should have annotations enabled at all
// The check is here rather in the PHP; that minimizes the number of
patches
// that need to be applied to existing Moodle code.
- var actualUrl = '' + window.location;
- if ( this.loginUserId && actualUrl.match(
/^.*\/mod\/forum\/discuss\.php\?d=(\d+)/ ) )
- {
- var annotationService = new RestAnnotationService( this.annotationPath
+ '/annotate.php', {
- csrfCookie: this.sessionCookie } );
- var keywordService = new RestKeywordService( this.annotationPath
+ '/keywords.php', true);
- keywordService.init( null );
- var moodleMarginalia = this;
- window.marginalia = new Marginalia( annotationService, this.loginUserId,
this.displayUserId == '*' ? '' : this.displayUserId, {
- preferences: this.preferences,
- keywordService: keywordService,
- baseUrl: this.moodleRoot,
- showAccess: true,
- showBlockMarkers: false,
- showActions: false,
- onkeyCreate: true,
- allowAnyUserPatch: this.allowAnyUserPatch ? true : false,
- displayNote: function(m,a,e,p,i) {
moodleMarginalia.displayNote(m,a,e,p,i); },
- editors: {
- link: null,
- 'default': Marginalia.newEditorFunc( YuiAutocompleteNoteEditor )
- },
- onMarginHeight: function( post ) { moodleMarginalia.fixControlMargin(
post ); }
- } );
-
- this.cleanUpPostContent( );
-
- // Display annotations
- var url = this.url;
- if ( this.showAnnotations )
- window.marginalia.showAnnotations( url );
-
- // Fix all control margins
- this.fixAllControlMargins( );
-
- Smartquote.enableSmartquote( this.moodleRoot, marginalia.listPosts( ),
marginalia.skipContent );
-
-// var marginaliaDirect = new MarginaliaDirect( annotationService );
-// marginaliaDirect.init( );
-
- if ( this.showAnnotations && this.splash )
- this.showSplash( );
+ if ( ! this.loginUserId )
+ return;
+
+ // Must first figure out which kind of page is being annotated,
+ // then return the correct URL for an individual *post*, which may
+ // not be the same as the page as a whole (e.g. in the case of the forum).
+ var x = window.location.href.indexOf( '#' );
+ var base = -1 == x ? window.location.href : window.location.href.substr(
0, x );
+ var matches = base.match( /\/mod\/forum\/discuss.php/ );
+ if ( ! matches )
+ matches = base.match( /\/mod\/forum\/post.php/ );
+ if ( matches )
+ {
+ var selectors = {
+ post: new Selector( '.forumpost', '.forumpost table.forumpost' ),
+ post_id: new Selector( function( root ) { return $( root ).prev( 'a' );
}, null, '@id' ),
+ post_content: new
Selector( '.content .posting', '.content .content .posting' ),
+ post_title: new Selector( '.subject', '.content .subject' ),
+ post_author: new Selector( '.author a', '.content .author a' ),
+ post_authorid: null,
+ post_date: null,
+ mia_notes: new Selector( '.mia_margin', '.content .posting .mia_margin'
),
+ post_url: new Selector( function( root) { return $( root ).prev( 'a' );
}, null,
+ function( node ) { return '/mod/forum/permalink.php?p=' +
node.id.substr( 1 ); } )
+ };
+ this.init( selectors );
+ }
+}
+
+MoodleMarginalia.prototype.init = function( selectors )
+{
+ var annotationService = new RestAnnotationService( this.annotationPath
+ '/annotate.php', {
+ noPutDelete: true,
+ csrfCookie: this.sessionCookie } );
+ var keywordService = new RestKeywordService( this.annotationPath
+ '/keywords.php', true);
+ keywordService.init( null );
+ var moodleMarginalia = this;
+ window.marginalia = new Marginalia( annotationService, this.loginUserId,
this.sheet, {
+ preferences: this.preferences,
+ keywordService: keywordService,
+ baseUrl: this.moodleRoot,
+ showBlockMarkers: false,
+ showActions: false,
+ onkeyCreate: true,
+ enableRecentFlag: true,
+ allowAnyUserPatch: this.allowAnyUserPatch ? true : false,
+ displayNote: function(m,a,e,p,i) {
moodleMarginalia.displayNote(m,a,e,p,i); },
+ editors: {
+ link: null,
+ 'default': Marginalia.newEditorFunc( YuiAutocompleteNoteEditor )
+ },
+ onMarginHeight: function( post ) { moodleMarginalia.fixControlMargin(
post ); },
+ selectors: selectors
+ } );
+
+ // Ensure the sheet drop-down reflects the actual sheet to be shown
+ // This relies on preferences being saved correctly. Otherwise, the user
may
+ // pick a different user, visit another page, click back and find that the
+ // sheet control shows the wrong thing.
+ var sheetCtrl = document.getElementById( 'ansheet' );
+ if ( sheetCtrl )
+ {
+ for ( var i = 0; i < sheetCtrl.options.length; ++i )
+ {
+ if ( sheetCtrl.options[ i ].value == this.sheet )
+ {
+ sheetCtrl.selectedIndex = i;
+ break;
+ }
+ }
+ }
+ this.cleanUpPostContent( );
+ // The following is actually part of the initialization routine.
+ // It enables clicking on the margin to create annotations
+ // (clickCreateAnnotation)
+ window.marginalia.listPosts( );
+
+ // Display annotations
+ var url = this.url;
+ if ( this.showAnnotations )
+ window.marginalia.showAnnotations( url );
+
+ // Fix all control margins
+ this.fixAllControlMargins( );
+
+ // Highlight the margin when the mouse is over it - but not one of its
children.
+ // Lets user know s/he can click to create an annotation.
+ var margin = jQuery( '.mia_margin' );
+ margin.mouseover( function( e ) { margin.toggleClass( 'hover', e.target
== margin[0] ); } );
+ margin.mouseleave( function( ) { margin.removeClass( 'hover' ); } );
+
+ // Enable smartquotes and quote logging
+ if ( this.useSmartquote )
+ {
+ this.smartquote = new Smartquote( this.moodleRoot, this.selectors,
this.smartquoteService );
+ this.smartquote.enable( marginalia.listPosts( ), marginalia.skipContent
);
+ }
+
+ if ( this.showAnnotations && this.splash )
+ {
+ var onclose = function() {
+ window.marginalia.preferences.setPreference(
Marginalia.P_SPLASH, 'false', null);
+ };
+ window.marginalia.showTip( this.splash, onclose );
}
};
+MoodleMarginalia.prototype.enablePublishQuotes = function( )
+{
+ this.smartquote = new Smartquote( this.moodleRoot, this.selectors,
this.smartquoteService );
+ this.smartquote.enable( marginalia.listPosts( ), marginalia.skipContent );
+}
+
+MoodleMarginalia.prototype.enableSubscribeQuotes = function( name )
+{
+ // object_type and object_id are needed by extservice, whic doesn't
right now
+ this.quoteSubscriber = new SmartquoteSubscriber( null );
+ this.quoteSubscriber.subscribeMCE( name, null, null );
+}
+
+
MoodleMarginalia.prototype.displayNote = function( marginalia, annotation,
noteElement, params, isEditing )
{
+ var moodleMarginalia = this;
var wwwroot = this.moodleRoot;
buttonParams = this.useSmartquote ?
{
@@ -128,10 +231,9 @@
title: getLocalized( 'annotation quote button' ),
content: this.smartquoteIcon,
onclick: function( ) {
- Smartquote.quoteAnnotation(
+ moodleMarginalia.smartquote.quoteAnnotation(
annotation,
marginalia.loginUserId,
- wwwroot,
Smartquote.postIdFromUrl( annotation.getUrl( ) ) );
}
}
@@ -149,31 +251,9 @@
MoodleMarginalia.prototype.createAnnotation = function( event, postId )
{
- this.hideSplash( );
- delete this.splash;
- window.marginalia.preferences.setPreference( AN_SPLASH_PREF, 'false',
null);
clickCreateAnnotation( event, postId );
};
-MoodleMarginalia.prototype.showSplash = function( )
-{
- var noteMargins = cssQuery( '.hentry .notes div' );
- if ( noteMargins.length > 0 )
- {
- var margin = noteMargins[ 0 ];
- margin.appendChild( domutil.element( 'p', {
- className: 'splash',
- content: this.splash } ) );
- }
-};
-
-MoodleMarginalia.prototype.hideSplash = function( )
-{
- var splash = cssQuery( '.hentry .notes div .splash' );
- if ( splash.length > 0 )
- splash[ 0 ].parentNode.removeChild( splash[ 0 ] );
-};
-
/*
* This fixes the height of create annotation buttons.
@@ -187,10 +267,17 @@
*/
MoodleMarginalia.prototype.fixControlMargin = function( post )
{
- var margin = domutil.childByTagClass( post.getElement(
), 'td', 'control-margin', PostMicro.skipPostContent );
- var button = domutil.childByTagClass( margin, 'button', null );
- button.style.height = '';
- button.style.height = '' + margin.offsetHeight + 'px';
+ var margin = jQuery( 'ol.mia_margin', post.getElement( ) );
+ var postContent = jQuery( '.content .posting', post.getElement( ) );
+ margin.css( 'min-height', postContent.height( ) );
+ postContent.css( 'min-height', margin.height( ) );
+/* margin.height( 'auto' );
+ postContent.height( 'auto' );
+ if ( margin.height( ) < postContent.height( ) )
+ margin.height( postContent.height( ) );
+ if ( postContent.height( ) < margin.height( ) )
+ postContent.height( margin.height( ) );
+ */
};
MoodleMarginalia.prototype.fixAllControlMargins = function( )
@@ -207,9 +294,11 @@
{
if ( child.nodeType == ELEMENT_NODE )
{
- domutil.removeClass( child, PM_POST_CLASS );
- domutil.removeClass( child, PM_CONTENT_CLASS );
- domutil.removeClass( child, AN_NOTES_CLASS );
+// domutil.removeClass( child, PM_POST_CLASS );
+// domutil.removeClass( child, PM_CONTENT_CLASS );
+// domutil.removeClass( child, AN_NOTES_CLASS );
+ // #geof# for now, simply clear all class names
+ child.removeAttribute( 'class' );
child.removeAttribute( 'id' );
if (
child.id )
delete
child.id;
@@ -224,23 +313,40 @@
}
}
-MoodleMarginalia.prototype.changeAnnotationUser = function( userControl,
url )
+MoodleMarginalia.prototype.changeSheet = function( sheetControl, url )
{
var marginalia = window.marginalia;
- var userId = userControl.value;
- this.hideSplash( )
- marginalia.hideAnnotations( );
- if ( null == userId || '' == userId )
- marginalia.preferences.setPreference( AN_SHOWANNOTATIONS_PREF, 'false',
null);
+ var sheet = sheetControl.value;
+
+ // Check to see whether this is a special case with a named handler
+ if ( this.handlers[ sheet ] )
+ this.handlers[ sheet ]( this, marginalia );
+ // This is simply a sheet name: go to that sheet
else
{
- marginalia.displayUserId = userId == '*' ? '' : userId;
- marginalia.showAnnotations( url );
- marginalia.preferences.setPreference( AN_SHOWANNOTATIONS_PREF, 'true',
null);
- marginalia.preferences.setPreference( AN_USER_PREF, userId, null );
- if ( this.splash && ( marginalia.loginUserId == marginalia.displayUserId
|| '' == marginalia.displayUserId ) )
- this.showSplash( );
- this.fixAllControlMargins( );
+ marginalia.hideAnnotations( );
+ if ( null == sheet || '' == sheet )
+ marginalia.preferences.setPreference(
Marginalia.P_SHOWANNOTATIONS, 'false', null);
+ else
+ {
+ marginalia.sheet = sheet;
+ marginalia.showAnnotations( url );
+ marginalia.preferences.setPreference(
Marginalia.P_SHOWANNOTATIONS, 'true', null);
+ marginalia.preferences.setPreference( Marginalia.P_SHEET, sheet, null );
+ this.fixAllControlMargins( );
+ }
}
};
+
+/*
+ * Fetch a localized string
+ * This is a function so that it can be replaced with another source of
strings if desired
+ * (e.g. in a database). The application uses short English-language
strings as keys, so
+ * that if the language source is lacking the key can be returned instead.
+ */
+function getLocalized( s )
+{
+ return LocalizedAnnotationStrings[ s ];
+}
+
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/annotate.php Wed May 30 14:29:48
2012
+++ /moodle/trunk/moodle/blocks/marginalia/annotate.php Wed Jun 20 22:56:48
2012
@@ -1,109 +1,292 @@
<?php // handles annotation actions
-require_once( "../../config.php" );
+/*
+ * blocks/marginalia/annotate.php
+ *
+ * Marginalia has been developed with funding and support from
+ * BC Campus, Simon Fraser University, and the Government of
+ * Canada, the UNDESA Africa i-Parliaments Action Plan, and
+ * units and individuals within those organizations. Many
+ * thanks to all of them. See CREDITS.html for details.
+ * Copyright (C) 2005-2011 Geoffrey Glass; the United Nations
+ *
http://www.geof.net/code/annotation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
+ *
+ * $Id$
+ */
+
+ require_once( "../../config.php" );
require_once( 'config.php' );
require_once( 'marginalia-php/Annotation.php' );
require_once( 'marginalia-php/AnnotationService.php' );
require_once( 'marginalia-php/MarginaliaHelper.php' );
-require_once( 'annotation_globals.php' );
+require_once( 'moodle_marginalia.php' );
require_once( 'annotation_summary_query.php' );
+global $DB;
+
if ( $CFG->forcelogin || ANNOTATION_REQUIRE_USER )
require_login();
-
class moodle_annotation extends Annotation
{
- function is_action_valid( $action )
+ function isActionValid( $action )
{
return null === $action || '' === $action;
}
- function is_access_valid( $access )
- {
- return ! $access || 'public' == $access || 'private' == $access
- || 'author' == $access || 'teacher' == $access
- || 'author teacher' == $access;
+ function isSheetValid( $sheet )
+ {
+ return ! $sheet || 'public' == $sheet || 'private' == $sheet
+ || 'author' == $sheet;
}
}
class moodle_annotation_service extends AnnotationService
{
- function moodle_annotation_service( $userid )
+ var $extService = null;
+
+ /**
+ * Figure out whether annotation is permitted here
+ */
+ function can_annotate( $url )
+ {
+ global $USER, $miagloberror;
+ $miagloberror = "none";
+
+ if ( isguestuser() or ! isloggedin() )
+ {
+ $miagloberror = "not logged in";
+ return false;
+ }
+ $handler = annotation_summary_query::handler_for_url( $url );
+ if ( ! $handler )
+ {
+ $miagloberror = "not on this page " . $url;
+ return false;
+ }
+ $handler->fetch_metadata( );
+ if ( $handler->modulename && $handler->courseid )
+ {
+ $cm = get_coursemodule_from_instance( $handler->modulename,
$handler->modinstanceid, $handler->courseid);
+ if ( $cm )
+ {
+ $modcontext = get_context_instance( CONTEXT_MODULE, $cm->id );
+ if ( ! $handler->capannotate )
+ {
+ $miagloberror = "never on this resource";
+ return false; // annotation of this resource is never permitted
+ }
+ else
+ return has_capability($handler->capannotate, $modcontext);
+ }
+ else
+ {
+ $miagloberror = "no cm";
+ return false;
+ }
+ }
+ else
+ {
+ $miagloberror = "no handler";
+ return false;
+ }
+ }
+
+ function moodle_annotation_service( $userid, $extService=null )
{
global $CFG;
+
+ $this->extService = $extService;
// Note: Cross-site request forgery protection requires cookies, so it
will not be
// activated if $CFG->usesid=true
$csrfprotect = ! empty( $CFG->usesid ) && $CFG->usesid;
+ $cookiename = 'MoodleSession'.$CFG->sessioncookie; // was
MoodleSessionTest.
+ $cookievalue = $_COOKIE[$cookiename];
+ //$_SESSION['SESSION']->session_test;
+
+ $moodlemia = moodle_marginalia::get_instance( );
AnnotationService::AnnotationService(
- annotation_globals::get_host(),
- annotation_globals::get_service_path(),
- annotation_globals::get_install_date(),
+ $moodlemia->get_host(),
+ $moodlemia->get_service_path(),
+ $moodlemia->get_install_date(),
$userid,
array(
'baseUrl' => $CFG->wwwroot,
- 'csrfCookie' => $csrfprotect ?
null : 'MoodleSessionTest'.$CFG->sessioncookie,
- 'csrfCookieValue' => $csrfprotect ? null :
$_SESSION['SESSION']->session_test )
+ 'csrfParam' => $csrfprotect ? null : 'csrf',
+ 'csrfCookieValue' => $csrfprotect ? null : $cookievalue,
+ 'noPutDelete' => true )
);
- $this->tablePrefix = $CFG->prefix;
}
- function doListAnnotations( $url, $username, $block, $all )
- {
+ function doListAnnotations( $url, $sheet, $block, $all, $mark )
+ {
+ global $USER, $DB;
+
+ $moodlemia = moodle_marginalia::get_instance( );
$handler = annotation_summary_query::handler_for_url( $url );
- $user = get_record( 'user', 'username', $username );
- $summary = new annotation_summary_query( $url, $handler, null, $user,
null, false, $all );
- if ( $summary->error ) {
+ $sheet_type = $moodlemia->sheet_type( $sheet );
+ $summary = new annotation_summary_query( array(
+ 'url' => $url
+ ,'sheet_type' => $sheet_type
+ ,'all' => $all ) );
+ if ( $summary->error )
+ {
$this->httpError( 400, 'Bad Request', 'Bad URL 1' );
return null;
}
- elseif ( !isloggedin( ) && ANNOTATION_REQUIRE_USER ) {
+ elseif ( !isloggedin( ) && ANNOTATION_REQUIRE_USER )
+ {
$this->httpError( 403, 'Forbidden', 'Anonymous listing not allowed' );
return null;
}
else
{
- $querysql = $summary->sql( 'section_type, section_name, quote_title,
start_block, start_line, start_word, start_char, end_block, end_line,
end_word, end_char' );
- $annotation_set = get_records_sql( $querysql );
+ $queryparams = array( );
+ $querysql = $summary->sql( $queryparams );
+ //echo "QUERY: $querysql\n";
+ //echo "PARAMS: \n";
+ //foreach ( $queryparams as $key => $value )
+ // echo " $key => $value\n";
$annotations = Array( );
- if ( $annotation_set ) {
- $i = 0;
- foreach ( $annotation_set as $r )
- $annotations[ $i++ ] = annotation_globals::record_to_annotation( $r );
- }
+ $annotations_read = Array( );
+ $annotations_unread = Array( );
+ $i = 0;
+
+ // Prep as much possible now for lastread updates
+ $now = time( ); // lastread time
+
+ // Open the record set
+ /*
+ echo "Query: $querysql<br/>";
+ echo "Params: $queryparams<br/>";
+ foreach ( $queryparams as $p => $v )
+ {
+ echo "Param: $p = $v<br/>";
+ }
+ */
+ $annotation_set = $DB->get_recordset_sql( $querysql, $queryparams );
+ foreach ( $annotation_set as $r )
+ {
+ $annotations[ $i ] = $moodlemia->record_to_annotation( $r );
+ $annotation = $annotations[ $i ];
+ if ( 'read' == $mark )
+ {
+ // Will do a bulk update later
+ if ( $annotation->getLastRead( ) )
+ $annotations_read[ ] = $annotation->id;
+ else
+ $annotations_unread[ ] = $annotation->id;
+ }
+ $i++;
+ }
+ // Close the recordset
+ $annotation_set->close( );
+
+ // Bulk update of lastread
+ if ( 'read' == $mark )
+ {
+ if ( $annotations_read && count( $annotations_read ) )
+ {
+ list( $in_sql, $in_params ) = $DB->get_in_or_equal(
$annotations_read, SQL_PARAMS_NAMED );
+ $query = 'UPDATE {'.AN_READ_TABLE.'}'
+ ."\n SET lastread=:lastread"
+ ."\n WHERE userid=:userid AND annotationid $in_sql";
+ $query_params = array( 'userid' => $USER->id, 'lastread' => (int)$now
);
+ $params = array_merge( $in_params, $query_params );
+ $DB->execute( $query, $params );
+ }
+
+ if ( $annotations_unread && count( $annotations_unread ) )
+ {
+ list( $in_sql, $in_params ) = $DB->get_in_or_equal(
$annotations_unread, SQL_PARAMS_NAMED );
+ $query = 'INSERT INTO {'.AN_READ_TABLE.'}'
+ ."\n (annotationid, userid, firstread, lastread)"
+ ."\n SELECT
a.id, :userid, :now1, :now2"
+ ."\n FROM {".AN_DBTABLE."} a"
+ ."\n WHERE
a.id $in_sql";
+ $query_params = array( 'userid' => $USER->id, 'now1' =>
(int)$now, 'now2' => (int)$now );
+ $params = array_merge( $in_params, $query_params );
+ $DB->execute( $query, $params );
+ }
+ }
+
+ if ( $this->extService )
+ {
+ $extService = $this->extService;
+ $extService->listAnnotations( $url, $sheet, $block, $all, $mark );
+ }
+
$format = $this->getQueryParam( 'format', 'atom' );
- $logurl
= 'annotate.php?format='.$format.($user ? '&user='.$user->id : '').'&url='.$url;
- add_to_log( $summary->handler->courseid, 'annotation', 'list', $logurl
);
+
$moodlemia->moodle_log( 'list', 'annotate.php?format='.$format.'&url='.$url
);
return $annotations;
}
}
- function doGetAnnotation( $id )
- {
- global $CFG;
-
+ function doGetAnnotation( $id, $mark )
+ {
+ global $DB;
+
+ $moodlemia = moodle_marginalia::get_instance( );
+
+ /*
+ * use a.* now instead
// Check whether the range column exists (for backwards compatibility)
- $range = '';
-/* if ( column_type( $this->tablePrefix.'annotation', 'range' ) )
- $range = ', a.range AS range ';
-*/
- // Caller should ensure that id is numeric
- $query = "SELECT
a.id, a.userid, u.username as username, a.url,
+ $range = ( column_type( 'annotation', 'range' ) ) ? ', a.range AS
range ' : '';
+
a.id, a.course, a.userid, a.url,
a.start_block, a.start_xpath, a.start_line, a.start_word,
a.start_char,
a.end_block, a.end_xpath, a.end_line, a.end_word, a.end_char,
- a.note, a.access_perms, a.quote, a.quote_title, a.quote_author_id,
- qu.username as quote_author_username,
+ a.note, a.sheet_type, a.quote, a.quote_title, a.quote_author_id,
+
qu.id as quote_author_userid,
a.link, a.link_title, a.action,
a.created, a.modified $range
- FROM {$this->tablePrefix}".AN_DBTABLE." a
- JOIN {$this->tablePrefix}user u ON
u.id=a.userid
- JOIN {$this->tablePrefix}user qu ON
qu.id=a.quote_author_id
- WHERE
a.id = $id";
- $resultset = get_record_sql( $query );
- if ( $resultset && count( $resultset ) != 0 ) {
- $annotation = annotation_globals::record_to_annotation( $resultset );
+ */
+ // Caller should ensure that id is numeric
+ $query = "SELECT a.*
+ FROM {".AN_DBTABLE."} a
+ JOIN {user} u ON
u.id=a.userid
+ JOIN {user} qu ON
qu.id=a.quote_author_id
+ WHERE
a.id = :id";
+ $r = $DB->get_record_sql( $query, array( 'id' => $id ) );
+ if ( $r ) {
+ $annotation = $moodlemia->record_to_annotation( $r );
+ // Record lastread
+ if ( 'read' == $mark )
+ {
+ $now = time( );
+ $lastread = $DB->get_record_select( AN_READ_TABLE,
+ 'annotationid = :annotationid AND userid = :userid',
+ array( 'annotationid' => $id, 'userid' => $USER->id ) );
+ if ( $lastread )
+ {
+ $lastread->lastread = (int) $now;
+ $DB->update_record( AN_READ_TABLE, $lastread );
+ }
+ else
+ {
+ $lastread = new stdClass;
+ $lastread->annotationid = (int) $id;
+ $lastread->userid = (int) $USER->id;
+ $lastread->lastread = (int) $now;
+ $lastread->firstread = (int) $now;
+ $DB->insert_record( AN_READ_TABLE, $lastread );
+ }
+ }
return $annotation;
}
else
@@ -112,35 +295,77 @@
function doCreateAnnotation( $annotation )
{
- if ( strlen( $annotation->getNote( ) ) > MAX_NOTE_LENGTH )
+ global $USER, $DB;
+
+ if ( ! $this->can_annotate( $annotation->url ) )
+ $this->httpError( 403, 'Forbidden', 'User lacks permission to annotate
this resource.' );
+ elseif ( strlen( $annotation->getNote( ) ) > MAX_NOTE_LENGTH )
$this->httpError( 400, 'Bad Request', 'Note too long' );
elseif ( strlen( $annotation->getQuote( ) ) > MAX_QUOTE_LENGTH )
$this->httpError( 400, 'Bad Request', 'Quote too long' );
else
{
+ $moodlemia = moodle_marginalia::get_instance( );
+
$time = time( );
$annotation->setCreated( $time );
$annotation->setModified( $time );
- $record = annotation_globals::annotation_to_record( $annotation );
+ $record = $moodlemia->annotation_to_record( $annotation );
// Figure out the object type and ID from the url
// Doing this here avoids infecting the caller with
application-specific mumbo-jumbo
// The cost of doing it here is low because annotations are created
one-by one. In essence,
// this is really caching derived fields in the database to make
queries easier. (If only
// MySQL had added views before v5).
- if ( preg_match( '/^.*\/mod\/forum\/permalink\.php\?p=(\d+)/',
$annotation->getUrl( ), $matches ) ) {
- $record->object_type = AN_OTYPE_POST;
- $record->object_id = (int) $matches[ 1 ];
- }
+ $profile = $moodlemia->get_profile( $annotation->getUrl( ) );
+ if ( $profile )
+ {
+ $record->object_type = $profile->get_object_type( $annotation->getUrl(
) );
+ $record->object_id = $profile->get_object_id( $annotation->getUrl( ) );
+ // Find the post author
+ $query = 'SELECT p.userid AS quote_author_id, p.subject AS
quote_title, d.course as course'
+ ." FROM {forum_posts} p "
+ ." JOIN {forum_discussions} d ON p.discussion=
d.id"
+ ." WHERE
p.id=:object_id";
+ $resultset = $DB->get_record_sql( $query, array( 'object_id' =>
$record->object_id ) );
+ if ( $resultset && count ( $resultset ) != 0 ) {
+ $record->quote_author_id = (int)$resultset->quote_author_id;
+ $record->quote_title = $resultset->quote_title;
+ $record->course = $resultset->course;
+ }
+ else {
+ $this->httpError( 400, 'Bad Request', 'No such forum post' );
+ return 0;
+ }
+ }
+ else
+ echo "UNKNOWN URL ".$annotation->getUrl( )."\n";
// must preprocess fields
- $id = insert_record( AN_DBTABLE, $record, true );
+ $id = $DB->insert_record( AN_DBTABLE, $record, true );
if ( $id ) {
+ // Record that this user has read the annotation.
+ // This may be superfluous, as the read flag is not shown for the
current user,
+ // but for consistency it seems like a good idea.
+ $record = new object( );
+ $record->annotationid = $id;
+ $record->userid = $USER->id;
+ $record->firstread = $time;
+ $record->lastread = $time;
+ $DB->insert_record( AN_READ_TABLE, $record, true );
+
+ if ( $this->extService )
+ {
+ $extService = $this->extService;
+ $extService->createAnnotation( $annotation, $record );
+ }
+
+ // Moodle logging
// TODO: fill in queryStr for the log
$urlquerystr = '';
$logurl = 'annotate.php' . ( $urlquerystr ? '?'.$urlquerystr : '' );
- add_to_log( null, 'annotation', 'create', $logurl, "$id" );
+ $moodlemia->moodle_log( 'create', $logurl, "$id" ); // null course
return $id;
}
}
@@ -149,43 +374,75 @@
function doUpdateAnnotation( $annotation )
{
- $urlquerystr = '';
- $annotation->setModified( time( ) );
- $record = annotation_globals::annotation_to_record( $annotation );
- $logurl = 'annotate.php' . ( $urlquerystr ? '?'.$urlquerystr : '' );
- add_to_log( null, 'annotation', 'update', $logurl, "{$annotation->id}" );
- return update_record( AN_DBTABLE, $record );
+ global $USER, $DB;
+
+ if ( ! $this->can_annotate( $annotation->url ) )
+ {
+ $this->httpError( 403, 'Forbidden', 'User lacks permission to annotate
this resource.' );
+ return False;
+ }
+ else
+ {
+ $moodlemia = moodle_marginalia::get_instance( );
+ $urlquerystr = '';
+ $annotation->setModified( time( ) );
+ $record = $moodlemia->annotation_to_record( $annotation );
+
+ $r = $DB->update_record( AN_DBTABLE, $record );
+
+ if ( $this->extService )
+ {
+ $extService = $this->extService;
+ $extService->updateAnnotation( $annotation, $record );
+ }
+
+ // Moodle logging
+ $logurl = 'annotate.php' . ( $urlquerystr ? '?'.$urlquerystr : '' );
+ $moodlemia->moodle_log( 'update', $logurl, "{$annotation->id}" );
+
+ return $r;
+ }
}
function doBulkUpdate( $oldnote, $newnote )
{
- global $CFG, $USER;
-
- $where = "userid='".addslashes($USER->id)."' AND
note='".addslashes($oldnote)."'";
-
- // Count how many replacements will be made
- $query = 'SELECT count(id) AS n FROM '.$CFG->prefix.AN_DBTABLE." WHERE
$where";
- $result = get_record_sql( $query );
- $n = (int)$result->n;
-
- if ( $n ) {
- // Do the replacements
- $query = 'UPDATE '.$CFG->prefix.AN_DBTABLE
- ." set note='".addslashes($newnote)."',"
- ." modified=".time( )
- ." WHERE $where";
- execute_sql( $query, false );
- }
+ return false;
+
+ if ( $this->extService )
+ {
+ $extService = $this->extService;
+ $extService->bulkUpdateAnnotations( $oldnote, $newnote, $n );
+ }
+
header( 'Content-type: text/plain' );
return $n;
}
- function doDeleteAnnotation( $id )
- {
- delete_records( AN_DBTABLE, 'id', $id );
- $logurl = "annotate.php?id=$id";
- add_to_log( null, 'annotation', 'delete', $logurl, "$id" );
- return True;
+ function doDeleteAnnotation( $annotation )
+ {
+ global $USER, $DB, $miagloberror;
+
+ if ( ! $this->can_annotate( $annotation->url ) )
+ {
+ $this->httpError( 403, 'Forbidden', 'User lacks permission to
annotate.' . $miagloberror . ", url=" . $annotation->url . ", id=" .
$annotation->id );
+ return False;
+ }
+ else
+ {
+ $DB->delete_records( AN_DBTABLE, array( 'id' => $annotation->id ) );
+ $DB->delete_records( AN_READ_TABLE, array( 'annotationid' =>
$annotation->id ) );
+
+ if ( $this->extService )
+ {
+ $extService = $this->extService;
+ $extService->deleteAnnotation( $annotation->id );
+ }
+
+ $moodlemia = moodle_marginalia::get_instance( );
+ $logurl = "annotate.php?id=".$annotation->id;
+ $moodlemia->moodle_log( 'delete', $logurl, $annotation->id );
+ return True;
+ }
}
function listBodyParams( )
@@ -194,7 +451,16 @@
}
}
-$service = new moodle_annotation_service( isguest() ? null :
$USER->username );
+// Load up the logger, if available
+$logblock = $DB->get_record('block', array( 'name'=>'marginalia_log' ) );
+$logger = null;
+if ( $logblock )
+{
+ require_once( $CFG->dirroot.'/blocks/marginalia_log/lib.php' );
+ $logger = new marginalia_log();
+}
+
+$service = new moodle_annotation_service( isguestuser() ? null : $USER->id,
+ $logger && $logger->is_active() ? $logger : null );
$service->dispatch( );
-?>
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/annotation-styles.php Wed May 30
14:29:48 2012
+++ /moodle/trunk/moodle/blocks/marginalia/annotation-styles.php Wed Jun 20
22:56:48 2012
@@ -1,15 +1,18 @@
<?php
header( 'Content-type: text/css' );
-/*
-//if (!isset($themename)) {
-// $themename = NULL;
-//}
-
-$nomoodlecookie = true;
-require_once("../config.php");
-//$themeurl = style_sheet_setup(filemtime("annotation-styles.php"), 300,
$themename);
-*/
?>
+
+/* These discussioncontrol definitions are in mod/forum/styles.css, but
there
+ they apply only to the discuss page. We need them for the post page
too. */
+.miacontrols.discussioncontrols {
+ width: 100%;
+ margin: 5px;
+ clear: both;
+}
+.discussioncontrol {
+ width: 33%;
+ float: left;
+}
table td#annotation-controls {
white-space: nowrap;
@@ -20,7 +23,7 @@
font-size: smaller;
}
-.hentry .notes .splash {
+.mia_margin .splash {
font-size: smaller;
}
@@ -33,15 +36,7 @@
font-weight: normal ;
}
-.forumpost {
- position: relative;
-}
-
-.hentry td.content {
- vertical-align: top;
-}
-
-.forumpost .commands button.smartquote {
+.forumpost button.smartquote {
display: inline;
background: none;
border: none;
@@ -53,137 +48,100 @@
cursor: pointer;
}
-.forumpost .commands button.smartquote:hover span {
+.forumpost button.smartquote:hover span {
text-decoration: underline;
color: red;
}
-/* hack button to allow for annotation creation, a result of problems with
Moz positioning */
-/* it has to be a button, otherwise clicking it loses the selection */
-td.control-margin {
- width: 1;
- xheight: auto;
+.mia_margin li.hover,
+.em.mia_annotation.hover,
+.em.mia_annotation.hover ins,
+.em.mia_annotation.hover del {
+ color: red;
}
-td.control-margin div {
- display: none;
+.forumpost .content {
+ position: relative;
}
-.self-annotated td.control-margin {
- vertical-align: top;
- width: 1em;
- padding: 0 ;
-}
-
-.self-annotated td.control-margin div {
- display: block;
- width: 1em;
+.mia_margin {
+ display: none;
height: 100%;
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
}
-/* There's a bug with the button: its height is set to 100%, but when
- * the annotation notes are added they may increase the height of the
table.
- * In that case, the button size does not increase to match.
- */
-.self-annotated td.control-margin button {
- height: 100%;
- width: 1em;
- border: none;
- border-left: #eee 1px dotted;
- border-right: #eee 1px dotted;
- padding: 0;
- padding-left: 1px;
- margin: 0;
- background: none;
- cursor: pointer;
+.mia_annotated .mia_margin {
display: block;
- z-index: 1;
+ width: 16em;
+ margin: 0 0 1.2em 1ex;
+ padding: 1px;
+ border: #f8f8f8 1px solid;
+ list-style-type: none;
+ cursor: pointer;
}
-.self-annotated td.control-margin button span {
- visibility: hidden;
+.mia_margin li {
+ list-style-type: none;
}
-/* the hover class is because of IE cluelessness */
-.self-annotated td.control-margin button:hover span,
-.self-annotated td.control-margin button.hover span {
- visibility: inherit;
+.mia_margin .mia_tip:hover {
+ outline: #aaa 1px dotted;
}
-.self-annotated td.control-margin button:hover,
-.self-annotated td.control-margin button.hover {
- font-weight: bold;
- background: #fdf377; /*should be from the theme, but I'm not sure where
that's set in 1.5 yet */
-}
-
-.hentry .notes li.hover,
-.hentry .entry-content em.annotation.hover,
-.hentry .entry-content em.annotation.hover ins,
-.hentry .entry-content em.annotation.hover del {
- color: red;
-}
-
-/* notes in sidebar */
-.notes a.annotation-summary {
- bottom: .25em ;
- margin: 0 auto;
- text-align: center;
- font-size: 80%;
- width: 100%;
- display: block;
-}
-
-.notes {
- width: 0;
+.mia_annotated .mia_margin.hover {
+ border: #aaa 1px dotted;
}
/* this rigamarole with both changing the column width *and* hiding the
elements within it
* is because IE is a load of steaming horse manure */
-.notes ol,
-.notes a.annotation-summary,
-.notes a.range-mismatch {
+.mia_margin ol,
+.mia_margin a.range-mismatch {
display: none;
}
-.annotated .notes {
- width: 30% ;
+.mia_annotated .mia_margin {
position: relative;
/* unfortunately the background color has been interfering with the
rounded corners
of the default moodle theme, so for now it's disabled */
/*background-color: #f8f8f8; <?PHP echo $THEME->cellcontent2; ?>;*/
}
-.annotated .notes ol,
-.annotated .notes .annotation-summary {
- display: block;
+.mia_margin li {
+ clear: both;
+ position: relative;
+ font-size: 90%;
}
-.notes div {
- position: relative;
- padding: 1px;
+.mia_margin li p {
+/* background-color: #f8f8f8; */
+ z-index: 10;
}
-.notes ol {
- margin: 0;
- padding: 0 ;
- right: 0;
- margin-bottom: 1.2em;
+.mia_margin li.active {
+ color: #d00 ;
}
-.notes ol li {
- clear: both;
+.mia_margin li button {
+ background: none;
}
-.notes ol li.active {
- color: red ;
+.mia_margin .mia_tip .controls {
+ text-align: right;
+ float: none;
}
-.notes ol li button {
- background: none ;
- font-size: 12px ;
+.mia_margin li .controls {
+ visibility: hidden;
}
-.notes ol textarea {
+.mia_margin li:hover .controls,
+.mia_margin li.mia_hover .controls {
+ visibility: visible;
+}
+
+.mia_margin textarea {
vertical-align: top ;
border: none;
font-family: inherit;
@@ -191,38 +149,23 @@
}
/* colors for other users' annotations
-.hentry em.annotation { background-color: #77f3fd ; }
-.hentry .content em.annotation em.annotation { background: #70d4ec; }
-.hentry .content em.annotation em.annotation em.annotation { background:
#66c6d8; }
+.hentry em.mia_annotation { background-color: #77f3fd ; }
+.hentry .content em.mia_annotation em.mia_annotation { background:
#70d4ec; }
+.hentry .content em.mia_annotation em.mia_annotation em.mia_annotation {
background: #66c6d8; }
*/
button#hide-all-annotations,
-body.annotated button#show-all-annotations {
+body.mia_annotated button#show-all-annotations {
display: none ;
}
-body.annotated button#hide-all-annotations {
+body.mia_annotated button#hide-all-annotations {
display: inline;
}
-
-#smartcopy-status {
- position: fixed;
- right: 1em;
- bottom: 1em;
- z-index: 100;
- background: #ffffcc;
- border: #666 1px solid;
- padding: 1ex;
- width: 13em;
- font-size: small;
- opacity: 1;
-}
@media print
{
- .forumpost em.annotation {
+ .forumpost em.mia_annotation {
text-decoration: underline ;
}
}
-
-
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/annotation_summary_query.php Wed
May 30 14:29:48 2012
+++ /moodle/trunk/moodle/blocks/marginalia/annotation_summary_query.php Wed
Jun 20 22:56:48 2012
@@ -1,64 +1,151 @@
<?php
+
+/*
+ * annotation_summary_query.php
+ *
+ * Marginalia has been developed with funding and support from
+ * BC Campus, Simon Fraser University, and the Government of
+ * Canada, the UNDESA Africa i-Parliaments Action Plan, and
+ * units and individuals within those organizations. Many
+ * thanks to all of them. See CREDITS.html for details.
+ * Copyright (C) 2005-2011 Geoffrey Glass; the United Nations
+ *
http://www.geof.net/code/annotation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
+ *
+ * $Id$
+ */
+
+
+// order by value for returning annotations in document order (i.e. same
order
+// the highlights would be shown within the document)
+define( 'AN_SUMMARY_ORDER_DOCUMENT', 'section_type, section_name, a.url,
start_block, start_line, start_word, start_char, end_block, end_line,
end_word, end_char' );
+
+// order by modification time, with most recent first
+define( 'AN_SUMMARY_ORDER_TIME', 'modified DESC');
+
+require_once( $CFG->dirroot.'/blocks/marginalia/config.php' );
+require_once( ANNOTATION_DIR.'/moodle_marginalia.php' );
/**
- * Objects of this class are immutable. Use the for_ methods to generate
- * modified version (e.g. for links to related summaries)
+ * Objects of this class are effectively immutable (the derive method
violates this, but it is called
+ * like a constructor). Use the derive method to generate modified
version (e.g. for links to related summaries)
*/
class annotation_summary_query
{
- var $url;
- var $text;
- var $user;
- var $ofuser;
- var $exatcmatch;
- var $all;
- var $handler; // URL handlers (implements much of this class's behavior)
+ var $moodlemia;
+ var $url = null;
+ var $sheet_type = AN_SHEET_PRIVATE;
+ var $text = null;
+ var $user = null;
+ var $ofuser = null;
+ var $exactmatch = false;
+ var $all = false;
+ var $orderby = AN_SUMMARY_ORDER_DOCUMENT;
+ var $handler = null; // URL handlers (implements much of this class's
behavior)
var $sql; // The result SQL query
var $error; // Any error encountered by the constructor
- function annotation_summary_query( $url, $handler, $text, $user, $ofuser,
$exactmatch, $all )
- {
- $this->url = $url;
- $this->handler = $handler;
- $handler->summary = $this;
- $this->text = $text;
- $this->user = $user;
- $this->ofuser = $ofuser;
- $this->exactmatch = $exactmatch;
- $this->all = $all;
-
- if ( $this->url && ! $this->handler )
- $this->handler = annotation_summary_query::handler_for_url( $url );
+ // Call with internal names in $a
+ // If initializing from a URL, call map_params( $_GET ) first
+ function annotation_summary_query( $a )
+ {
+ $this->moodlemia = moodle_marginalia::get_instance( );
+ if ( $a )
+ $this->from_params( $a );
}
- // useful if you just want to list users
- static function from_url( $url )
- {
- return new annotation_summary_query( $url, null, null, null, null,
false, false );
- }
-
- static function from_params( )
- {
- $url = array_key_exists( 'url', $_GET ) ? $_GET[ 'url' ] : null;
- $text = array_key_exists( 'q', $_GET ) ? $_GET[ 'q' ] : null;
- $username = array_key_exists( 'u', $_GET ) ? $_GET[ 'u' ] : null;
- $ofusername = array_key_exists( 'search-of', $_GET ) ?
$_GET[ 'search-of' ] : null;
- $exactmatch = array_key_exists( 'match', $_GET ) ? 'exact' ==
$_GET[ 'match' ] : false;
- $all = array_key_exists( 'all', $_GET ) ?
- 'yes' == $_GET[ 'all' ] || 'true' == $_GET[ 'all' ] : false;
-
- $user = null;
- if ( $username )
- $user = get_record( 'user', 'username', $username );
-
- $ofuser = null;
- if ( $ofusername )
- $ofuser = get_record( 'user', 'username', $ofusername );
-
- $handler = annotation_summary_query::handler_for_url( $url );
-
- return new annotation_summary_query( $url, $handler, $text, $user,
$ofuser, $exactmatch, $all );
+ // map URL query parameters to internal parameter names and values
+ static function map_params( $a )
+ {
+ $b = array( );
+
+ $b[ 'text' ] = array_key_exists( 'q', $a ) ? $a[ 'q' ] : null;
+ $b[ 'exactmatch' ] = array_key_exists( 'match', $a ) ? 'exact' ==
$a[ 'match' ] : false;
+ $b[ 'all' ] = array_key_exists( 'all', $a ) ? 'yes' == $a[ 'all' ] |
| 'true' == $a[ 'all' ] : false;
+
+ // default sort order is document order, because that's what the
marginalia front-end needs
+ $sort_order = array_key_exists( 'sort', $a ) ? $a[ 'sort' ] : null;
+ if ( 'document' == $sort_order )
+ $b[ 'orderby' ] = AN_SUMMARY_ORDER_DOCUMENT;
+ elseif ( 'time' == $sort_order )
+ $b[ 'orderby' ] = AN_SUMMARY_ORDER_TIME;
+ else
+ $b[ 'orderby' ] = AN_SUMMARY_ORDER_DOCUMENT;
+
+ $sheet = array_key_exists( 'sheet', $a ) ? $a[ 'sheet' ] : null;
+ $b[ 'sheet_type' ] = $sheet ? $this->moodlemia->sheet_type( $sheet ) :
null;
+
+ $b[ 'userid' ] = array_key_exists( 'u', $a ) ? (int)$a[ 'u' ] : null;
+ $b[ 'ofuserid'] = array_key_exists( 'search-of', $a ) ?
(int)$a[ 'search-of' ] : null;
+
+ $b[ 'url' ] = array_key_exists( 'url', $a ) ? $a[ 'url' ] : null;
+ return $b;
+ }
+
+ // Tedious, eh? Maybe there's some nice PHP way to do this.
+ function from_params( $a )
+ {
+ global $DB;
+
+ if ( array_key_exists( 'text', $a ) )
+ $this->text = $a[ 'text' ];
+ if ( array_key_exists( 'exactmatch', $a ) )
+ $this->exactmatch = $a[ 'exactmatch' ];
+ if ( array_key_exists( 'all', $a ) )
+ $this->all = $a[ 'all' ];
+ if ( array_key_exists( 'orderby', $a ) )
+ $this->orderby = $a[ 'orderby' ];
+ if ( array_key_exists( 'sheet_type', $a ) )
+ $this->sheet_type = $a[ 'sheet_type' ];
+
+ if ( array_key_exists( 'user', $a ) )
+ $this->user = $a[ 'user' ];
+ elseif ( array_key_exists( 'userid', $a ) )
+ $this->user = $DB->get_record( 'user', array( 'id' => (int)$a[ 'userid'
] ) );
+
+ if ( array_key_exists( 'ofuser', $a ) )
+ $this->ofuser = $a[ 'ofuser' ];
+ elseif ( array_key_exists( 'ofuserid', $a ) )
+ $this->ofuser = $DB->get_record( 'user', array('id' =>
(int)$a[ 'ofuserid' ] ) );
+
+ if ( array_key_exists( 'url', $a ) )
+ {
+ $this->url = $a[ 'url' ];
+ $this->handler = annotation_summary_query::handler_for_url( $this->url
);
+ }
+ }
+
+ // Derive a version of this summary_query with some parameters changed
+ function derive( $a )
+ {
+ $summary_query = new annotation_summary_query( array(
+ 'text' => $this->text,
+ 'exactmatch' => $this->exactmatch,
+ 'all' => $this->all,
+ 'orderby' => $this->orderby,
+ 'sheet_type' => $this->sheet_type,
+ 'user' => $this->user,
+ 'ofuser' => $this->ofuser,
+ 'url' => $this->url
+ ) );
+
+ if ( $a )
+ $summary_query->from_params( $a );
+ return $summary_query;
}
static function handler_for_url( $url )
@@ -84,49 +171,10 @@
return new post_annotation_url_handler( (int) $matches[ 1 ] );
else
+ {
+ echo "no handler";
return null;
- }
-
- function for_url( $url )
- {
- return new annotation_summary_query( $url,
annotation_summary_query::handler_for_url( $url ),
- $this->text, $this->user, $this->ofuser, $this->exactmatch, $this->all
);
- }
-
- function for_user( $user )
- {
- return new annotation_summary_query( $this->url, $this->handler,
$this->text,
- $user, $this->ofuser, $this->exactmatch, $this->all );
- }
-
- function for_ofuser( $ofuser )
- {
- return new annotation_summary_query( $this->url, $this->handler,
$this->text,
- $this->user, $ofuser, $this->exactmatch, $this->all );
- }
-
- function for_parent( )
- {
- $this->handler->fetch_metadata( );
- if ( $this->handler->parenturl ) {
- return new annotation_summary_query( $this->handler->parenturl,
- annotation_summary_query::handler_for_url( $this->handler->parenturl ),
- $this->text, $this->user, $this->ofuser, $this->exactmatch, $this->all
);
- }
- else
- return null;
- }
-
- function for_text( $text, $exact=false )
- {
- return new annotation_summary_query( $this->url, $this->handler, $text,
- $this->user, $this->ofuser, $exact, $this->all );
- }
-
- function for_match( $exact=false )
- {
- return new annotation_summary_query( $this->url, $this->handler,
$this->text,
- $this->user, $this->ofuser, $exact, $this->all );
+ }
}
function titlehtml( )
@@ -134,11 +182,6 @@
$this->handler->fetch_metadata( );
return $this->handler->titlehtml;
}
-
- function fullname( $user )
- {
- return $user->firstname . ' ' . $user->lastname;
- }
/** Produce a natural language description of a query */
function desc( $titlehtml=null )
@@ -148,11 +191,18 @@
$this->handler->fetch_metadata( );
$a->title = null === $titlehtml ? $this->handler->titlehtml : $titlehtml;
- $a->who = $this->user ? s( $this->fullname( $this->user ) ) :
get_string( 'anyone', ANNOTATION_STRINGS );
- $a->author = $this->ofuser ? s( $this->fullname( $this->ofuser ) ) :
get_string( 'anyone', ANNOTATION_STRINGS );
+ $a->who = $this->user ? s( $this->moodlemia->fullname( $this->user ) ) :
get_string( 'anyone', ANNOTATION_STRINGS );
+ $a->author = $this->ofuser ? s( $this->moodlemia->fullname(
$this->ofuser ) ) : get_string( 'anyone', ANNOTATION_STRINGS );
$a->search = s( $this->text );
$a->match = get_string( $this->exactmatch ? 'matching' : 'containing',
ANNOTATION_STRINGS );
+ if ( AN_SHEET_PUBLIC == $this->sheet_type )
+ $a->sheet = get_string( 'public_sheet', ANNOTATION_STRINGS );
+ elseif ( AN_SHEET_PRIVATE == $this->sheet_type )
+ $a->sheet = get_string( 'private_sheet', ANNOTATION_STRINGS );
+ elseif ( AN_SHEET_AUTHOR == $this->sheet_type )
+ $a->sheet = get_string( 'author_sheet', ANNOTATION_STRINGS );
+
if ( null != $this->text && '' != $this->text )
$s =
$this->ofuser ? 'annotation_desc_authorsearch' : 'annotation_desc_search';
else
@@ -173,7 +223,8 @@
$a->title = null === $titlehtml ? $this->handler->titlehtml : $titlehtml;
// Show link to parent search
- $parent_summary = $this->for_parent( );
+ $this->handler->fetch_metadata( );
+ $parent_summary = $this->handler->parenturl ? $this->derive(
array( 'url' => $this->handler->parenturl ) ) : null;
if ( $parent_summary ) {
$a->title = '<a class="opt-link" href="'.s(
$parent_summary->summary_url( ) )
. '" title="'.get_string( 'unzoom_url_hover', ANNOTATION_STRINGS ).'">'
@@ -183,10 +234,10 @@
// Unzoom from user to anyone
if ( $this->user ) {
- $summary_anyone = $this->for_user( null );
+ $summary_anyone = $this->derive( array( 'user' => null ) );
$a->who = '<a class="opt-link" href="'.s( $summary_anyone->summary_url(
) )
.'" title="'.get_string( 'unzoom_user_hover', ANNOTATION_STRINGS )
- .'"><span class="current">'.s( $this->fullname( $this->user )
).'</span><span class="alt">'
+ .'"><span class="current">'.s( $this->moodlemia->fullname( $this->user
) ).'</span><span class="alt">'
.get_string( 'anyone', ANNOTATION_STRINGS ).'</a></a>';
}
else
@@ -194,10 +245,10 @@
// Unzoom from of user to of anyone
if ( $this->ofuser ) {
- $summary_anyone = $this->for_ofuser( null );
+ $summary_anyone = $this->derive( array( 'ofuser' => null ) );
$a->author = '<a class="opt-link" href="'.s(
$summary_anyone->summary_url( ) )
.'" title="'.get_string( 'unzoom_author_hover', ANNOTATION_STRINGS )
- .'"><span class="current">'.s( $this->fullname( $this->ofuser )
).'</span><span class="alt">'
+ .'"><span class="current">'.s( $this->moodlemia->fullname(
$this->ofuser ) ).'</span><span class="alt">'
.get_string( 'anyone', ANNOTATION_STRINGS ).'</span></a>';
}
else
@@ -206,7 +257,7 @@
$a->search = $this->text;
// Toggle exact match
- $summary_match = $this->for_match( ! $this->exactmatch );
+ $summary_match = $this->derive( array( 'exactmatch' => !
$this->exactmatch ) );
$hover = get_string(
$this->exactmatch ? 'unzoom_match_hover' : 'zoom_match_hover',
ANNOTATION_STRINGS );
$m1 = get_string( $this->exactmatch ? 'matching' : 'containing',
ANNOTATION_STRINGS );
$m2 = get_string( $this->exactmatch ? 'containing' : 'matching',
ANNOTATION_STRINGS );
@@ -225,150 +276,212 @@
return $desc;
}
- /**
- * This takes a list of handlers, each of which corresponds to a
particular type of
- * query (e.g. discussion forum), along with search fields for performing
a search.
- * It returns the SQL query string.
- *
- * $searchAccess can be public, private, or empty. Public annotations
are available to
- * *everyone*, not just course members or Moodle users.
- */
- function sql( $orderby )
- {
- global $CFG, $USER;
-
- // The query is a UNION of separate queries, one for each type of
annotation
- // This is unfortunate: with a common table structure, one for
parent-child
- // URL relationships, another with URL properties (title and owner would
- // suffice), would forgo UNIONs and simplify this code.
-
- // Users can only see their own annotations or the public annotations of
others
- // This is an awfully complex combination of conditions. I'm wondering
if that's
- // a design flaw.
- $accesscond = null;
- $descusers = '';
-
- // this was originally intended to allow more than one handler to
respond to a request.
- // That may still be necessary someday, but perhaps a compound handler
would be the
- // best way to respond to it. I eliminated the handler list because
YAGNI.
- $handler = $this->handler;
-
- // Conditions under which someone else's annotation would be visible to
this user
- $accessvisible = "a.access_perms & ".AN_ACCESS_PUBLIC;
- if ( array_key_exists( 'username', $USER ) ) {
- $accessvisible .= " OR a.userid=".$USER->id
- . " OR a.access_perms & ".AN_ACCESS_AUTHOR." AND
a.quote_author_id=".$USER->id;
- }
-
+ /** Callback used by handlers to get standard query WHERE clause
conditions */
+ function get_sql_conds( &$params )
+ {
+ global $USER;
+
+ $query = '';
+
+ $accessvisible = '';
+ // If not logged in, only the public sheet is visible
+ if ( ! isloggedin( ) )
+ {
+ if ( AN_SHEET_PUBLIC == $this->sheet_type )
+ {
+ $query = 'a.sheet_type = :sheet_type ';
+ $params[ 'sheet_type' ] = AN_SHEET_PUBLIC;
+ }
+ else
+ $query = '1=0'; // automatic fail
+ }
// If the all flag is set, see if this is an admin user with permission
to
// export all annotations.
- if ( $this->all ) {
+ elseif ( $this->all ) {
$sitecontext = get_context_instance( CONTEXT_SYSTEM );
- $all = AN_ADMINVIEWALL && ( has_capability( 'moodle/legacy:admin',
$sitecontext )
- or has_capability( 'moodle/site:doanything', $sitecontext) );
- if ( $all )
- $accessvisible = '1=1';
+ if ( has_capability( 'blocks/marginalia:view_all', $sitecontext ) )
+ $query = '1=1';
+ }
+ // If a sheet is specified, we need to check that the annotation is on
that
+ // sheet and that this user has appropriate access.
+ elseif ( $this->sheet_type )
+ {
+ $query = 'a.sheet_type = :sheet_type';
+ $params[ 'sheet_type' ] = $this->sheet_type;
+ if ( AN_SHEET_PRIVATE == $this->sheet_type )
+ {
+ $query .= ' AND a.userid = :user_id';
+ $params[ 'user_id' ] = $USER->id;
+ }
+ elseif ( AN_SHEET_AUTHOR == $this->sheet_type )
+ {
+ $query .= ' AND (a.userid = :user_id OR a.quote_author_id
= :quote_author_id)';
+ $params[ 'user_id' ] = $USER->id;
+ $params[ 'quote_author_id' ] = $USER->id;
+ }
+ }
+ // If a sheet is *not* specified, we need to be more general
+ else
+ {
+ $query = 'a.sheet_type = :sheet_type OR a.userid = :user_id'
+ .' OR (a.sheet_type = :sheet_author AND a.quote_author_id
= :quote_author_id)';
+ $params[ 'sheet_type' ] = AN_SHEET_PUBLIC;
+ $params[ 'user_id' ] = $USER->id;
+ $params[ 'sheet_author' ] = AN_SHEET_AUTHOR;
+ $params[ 'quote_author_id' ] = $USER->id;
}
- // Filter annotations according to their owners
-
- // Admin only (used especially for research): transcend usual privacy
limitations
- if ( null == $this->user )
- $accesscond = " ($accessvisible) ";
- else {
- if ( ! isloggedin( ) || $USER->id != $this->user->id )
- $accesscond = "($accessvisible)";
- if ( $accesscond )
- $accesscond .= ' AND ';
- $accesscond .= "a.userid=".$this->user->id;
- }
-
-
+
+ $query = '( ' . $query . ' )';
+
+ // Filter by annotation creator
+ if ( $this->user )
+ {
+ $query .= ' AND a.userid = :creator_user_id';
+ $params[ 'creator_user_id' ] = (int)$this->user->id;
+ }
+
// These are the fields to use for a search; specific annotations may
add more fields
$stdsearchfields =
array( 'a.note', 'a.quote', 'u.firstname', 'u.lastname' );
-
- $prefix = $CFG->prefix;
-
- // Do handler-specific stuff
-
- // Check whether the range column exists (for backwards compatibility)
- $range = '';
-
- // These that follow are standard fields, for which no page type
exceptions can apply
- $qstdselect = "SELECT
a.id AS id, a.url AS url, a.userid AS userid"
- . ", a.start_block, a.start_xpath, a.start_line, a.start_word,
a.start_char"
- . ", a.end_block, a.end_xpath, a.end_line, a.end_word, a.end_char"
- . ", a.link AS link, a.link_title AS link_title, a.action AS action"
- . ", a.access_perms AS access_perms, a.created, a.modified $range"
- . ", u.username AS username"
- . ",\n concat(u.firstname, ' ', u.lastname) AS fullname"
- . ",\n concat('$CFG->wwwroot/user/view.php?id=',
u.id) AS note_author_url"
- . ",\n a.note note, a.quote, a.quote_title AS quote_title"
- . ",\n qu.username AS quote_author_username"
- . ",\n concat(qu.firstname, ' ', qu.lastname) AS quote_author_fullname"
- . ",\n concat('$CFG->wwwroot/user/view.php?id=',
qu.id) AS
quote_author_url";
-
- // Standard tables apply to all (but note the outer join of user, which
if gone
- // should not steal the annotation from its owner):
- $qstdfrom = "\nFROM {$prefix}".AN_DBTABLE." a"
- . "\n INNER JOIN {$prefix}user u ON
u.id=a.userid"
- . "\n LEFT OUTER JOIN {$prefix}user qu on
qu.id=a.quote_author_id";
-
- // This search is always limited by access
- $qstdwhere = "\nWHERE ($accesscond)";
// Searching limits also; fields searched are not alone those of the
annotation:
// add to them also those a page of this type might use.
if ( null != $this->text && '' != $this->text ) {
if ( $this->exactmatch )
- $qstdwhere .= "\n AND a.note='".addslashes($this->text)."'";
+ {
+ $query .= "\n AND a.note = :exactmatch";
+ $params[ 'exactmatch' ] = $this->text;
+ }
else {
+ $handler = $this->handler;
$searchcond = '';
$addsearchfields = $handler->get_search_fields( );
$searchcond = '';
$querywords = split( ' ', $this->text );
+ $n = 1;
foreach ( $querywords as $word )
{
- $sword = addslashes( $word );
+ $sword = '%' . str_replace('%', '%%', $word) . '%';
foreach ( $stdsearchfields as $field )
- $searchcond .= ( $searchcond == '' ) ? "$field LIKE '%$sword%'" : "
OR $field LIKE '%$sword%'";
+ {
+ $searchcond .= ( ( $searchcond == '' ) ? '' : ' OR ' ) . "$field
LIKE :match$n";
+ $params[ 'match'.$n ] = $sword;
+ $n += 1;
+ }
foreach ( $addsearchfields as $field )
- $searchcond .= " OR $field LIKE '%$sword%'";
- }
- $qstdwhere .= "\n AND ($searchcond)";
+ {
+ $searchcond .= " OR $field LIKE :match$n";
+ $params[ 'match'.$n ] = $sword;
+ $n += 1;
+ }
+ }
+ $query .= "\n AND ($searchcond)";
}
}
-
- // The handler must construct the query, which might be a single SELECT
or a UNION of multiple SELECTs
- $q = $handler->get_sql( $this, $qstdselect, $qstdfrom, $qstdwhere,
$orderby );
-
- return $q;
+
+ // This search is always limited by permissions and sheet
+ return $query;
+ }
+
+ /** Callback used by handlers to get standard query FROM clause tables */
+ function get_sql_tables( &$params )
+ {
+ global $USER;
+
+ // Standard tables apply to all (but note the outer join of user, which
if gone
+ // should not steal the annotation from its owner):
+ $params[ 'annotation_user_id' ] = (int) $USER->id;
+ return ' {'.AN_DBTABLE."} a"
+ . "\n INNER JOIN {user} u ON
u.id=a.userid"
+ . "\n LEFT OUTER JOIN {user} qu on
qu.id=a.quote_author_id"
+ . "\n LEFT OUTER JOIN {".AN_READ_TABLE."} r ON (r.annotationid=
a.id AND
r.userid= :annotation_user_id )";
+ }
+
+ /** Callback used by handlers to get standard query SELECT clause fields
*/
+ function get_sql_fields( &$params )
+ {
+ global $CFG, $DB;
+
+ // Need range field if present, so instead of querying all fields by name
+ // use a.*.
+ $params[ 'note_author_url_base' ] = $CFG->wwwroot.'/user/view.php?id=';
+ $params[ 'quote_author_url_base' ] = $CFG->wwwroot.'/user/view.php?id=';
+ return ' a.* '
+ . ", r.lastread AS lastread"
+ . ", u.username AS username"
+ . ",\n u.firstname AS firstname, u.lastname AS lastname"
+ // . ",\n concat(u.firstname, ' ', u.lastname) AS fullname"
+ . ",\n ".$DB->sql_concat(':note_author_url_base','
u.id')." AS
note_author_url"
+ . ",\n qu.username AS quote_author_username"
+ . ",\n
qu.id AS quote_author_id"
+ . ",\n qu.firstname as quote_author_firstname, qu.lastname AS
quote_author_lastname"
+ // . ",\n concat(qu.firstname, ' ', qu.lastname) AS
quote_author_fullname"
+ . ",\n ".$DB->sql_concat(':quote_author_url_base', '
qu.id')." AS
quote_author_url";
+ /*
+ return "
a.id AS id, a.url AS url, a.userid AS userid"
+ . ", a.start_block, a.start_xpath, a.start_line, a.start_word,
a.start_char"
+ . ", a.end_block, a.end_xpath, a.end_line, a.end_word, a.end_char"
+ . ", a.link AS link, a.link_title AS link_title, a.action AS action"
+ . ", a.sheet_type AS sheet_type"
+ . ", a.created AS created, a.modified AS modified"
+ . ", r.lastread AS lastread"
+ . ", u.username AS username"
+ . ",\n u.firstname AS firstname, u.lastname AS lastname"
+// . ",\n concat(u.firstname, ' ', u.lastname) AS fullname"
+ . ",\n concat('$CFG->wwwroot/user/view.php?id=',
u.id) AS note_author_url"
+ . ",\n a.note note, a.quote, a.quote_title AS quote_title"
+ . ",\n qu.username AS quote_author_username"
+ . ",\n
qu.id AS quote_author_id"
+ . ",\n qu.firstname as quote_author_firstname, qu.lastname AS
quote_author_lastname"
+// . ",\n concat(qu.firstname, ' ', qu.lastname) AS quote_author_fullname"
+ . ",\n concat('$CFG->wwwroot/user/view.php?id=',
qu.id) AS
quote_author_url";
+ */
+ }
+
+ /** Return a query for performing a search */
+ function sql( &$params )
+ {
+ // The query is a UNION of separate queries, one for each type of
annotation
+ // This is unfortunate: with a common table structure, one for
parent-child
+ // URL relationships, another with URL properties (title and owner would
+ // suffice), would forgo UNIONs and simplify this code.
+
+ // this was originally intended to allow more than one handler to
respond to a request.
+ // That may still be necessary someday, but perhaps a compound handler
would be the
+ // best way to respond to it. I eliminated the handler list because
YAGNI.
+ $handler = $this->handler;
+
+ // The handler must construct the query, which might be a single SELECT
or a UNION of multiple SELECTs
+ return $handler->get_sql( $params, $this );
}
- /** Get query to list users with public annotations on this discussion */
- function list_users_sql( )
- {
- global $CFG;
- return "SELECT
u.id, u.username, u.firstname, u.lastname "
- . "\nFROM {$CFG->prefix}user u "
- . "\nINNER JOIN {$CFG->prefix}".AN_DBTABLE." a ON a.userid=
u.id "
- . $this->handler->get_tables( )
- . "\nWHERE a.access_perms & ".AN_ACCESS_PUBLIC;
+ /** Generate SQL for finding out how many records exist for a query */
+ function count_sql( &$params )
+ {
+ $handler = $this->handler;
+ return $handler->get_count_sql( $params, $this );
}
/** Generate a summary URL corresponding to this query */
- function summary_url( )
+ function summary_url( $first=0 )
{
global $CFG;
+
$s = ANNOTATION_PATH."/summary.php?url=".urlencode($this->url);
+ if ( null != $this->sheet_type )
+ $s .= '&sheet='.urlencode($this->moodlemia->sheet_str(
$this->sheet_type ) );
if ( null != $this->text && '' != $this->text )
$s .= '&q='.urlencode($this->text);
if ( null != $this->user )
- $s .= '&u='.urlencode($this->user->username);
+ $s .= '&u='.urlencode($this->user->id);
if ( null != $this->ofuser )
- $s .= '&search-of='.urlencode($this->ofuser->username);
+ $s .= '&search-of='.urlencode($this->ofuser->id);
if ( $this->exactmatch )
$s .= '&match=exact';
+ if ( $first )
+ $s .= '&first='.$first; // doesn't cast first to int, as it might be a
substitution like {first}
+ if ( AN_SUMMARY_ORDER_TIME == $this->orderby )
+ $s .= '&sort=time';
return $s;
}
@@ -381,7 +494,6 @@
return null;
}
}
-
class annotation_url_handler
{
@@ -391,19 +503,21 @@
// This pulls together the query from the standard portions (which are
passed in)
// and from the handler-specific portions. Some handlers may override
this, e.g. in order
// to construct a UNION.
- function get_sql( $summary, $qstdselect, $qstdfrom, $qstdwhere, $orderby )
- {
- $q = $qstdselect
- . $this->get_fields( )
- . "\n" . $qstdfrom
- . $this->get_tables( )
- . "\n" . $qstdwhere
- . $this->get_conds( $summary );
- if ( $orderby )
- $q .= "\nORDER BY $orderby";
- return $q;
- }
-
+ function get_sql( &$params, $summary )
+ {
+ return 'SELECT' . $summary->get_sql_fields( $params ) .
$this->get_fields( $params )
+ . "\n FROM" . $summary->get_sql_tables( $params ) . $this->get_tables(
$params )
+ . "\n WHERE" . $summary->get_sql_conds( $params ) . $this->get_conds(
$params, $summary )
+ . ( $summary->orderby ? "\nORDER BY $summary->orderby" : '' );
+ }
+
+ function get_count_sql( &$params, $summary )
+ {
+ return 'SELECT count(*)'
+ . "\n FROM" . $summary->get_sql_tables( $params ) . $this->get_tables(
$params )
+ . "\n WHERE" . $summary->get_sql_conds( $params ) . $this->get_conds(
$params, $summary );
+ }
+
function get_search_fields( )
{
return array( );
@@ -439,13 +553,13 @@
* to querying the database. */
function fetch_metadata( )
{
- global $CFG;
+ global $DB;
if ( null != $this->titlehtml )
return;
- $query = "SELECT fullname "
- . " FROM {$CFG->prefix}course WHERE id={$this->courseid}";
- $row = get_record_sql( $query );
+ $query = "SELECT fullname FROM {course} WHERE id = :courseid";
+ $params = array( 'courseid' => $this->courseid );
+ $row = $DB->get_record_sql( $query, $params );
if ( False !== $row )
$this->titlehtml = s( $row->fullname );
else
@@ -454,32 +568,20 @@
$this->parenttitlehtml = null;
}
- // Override the default implementation of getSql. This must construct a
UNION of multiple queries.
-
- function get_sql( $summary, $qstdselect, $qstdfrom, $qstdwhere, $orderby )
- {
- global $CFG;
- $q = '';
-
- // Conditions
- $cond = "\n AND a.object_type=".AN_OTYPE_POST;
- if ( $summary->ofuser )
- $cond .= " AND p.userid=".$summary->ofuser->id;
-
+ // Override the default implementation of get_sql
+ function get_sql( &$params, $summary )
+ {
+ global $CFG, $DB;
+
// First section: discussion posts
- $q = $qstdselect
- . ",\n 'forum' section_type, 'content' row_type"
- . ",\n
f.name section_name"
- . ",\n concat('{$CFG->wwwroot}/mod/forum/view.php?id=',
f.id)
section_url"
- . $qstdfrom
- . "\n INNER JOIN {$CFG->prefix}forum_discussions d ON
d.course=".$this->courseid.' '
- . "\n INNER JOIN {$CFG->prefix}forum_posts p ON p.discussion=
d.id AND
a.object_type=".AN_OTYPE_POST." AND
p.id=a.object_id "
- . "\n INNER JOIN {$CFG->prefix}forum f ON
f.id=d.forum "
- . $qstdwhere
- . $this->get_conds( $summary );
-
- if ( $orderby )
- $q .= "\nORDER BY $orderby";
+ $params[ 'section_url_base' ] = $CFG->wwwroot.'/mod/forum/view.php?id=';
+ $q = "SELECT" . $summary->get_sql_fields( $params )
+ . ",\n 'forum' AS section_type, 'content' AS row_type"
+ . ",\n
f.name AS section_name"
+ . ",\n ".$DB->sql_concat(':section_url_base','
f.id')." AS section_url"
+ . "\n FROM" . $summary->get_sql_tables( $params ) . $this->get_tables(
$params, $summary )
+ . "\n WHERE" . $summary->get_sql_conds( $params ) . $this->get_conds(
$params, $summary )
+ . ( $summary->orderby ? "ORDER BY $summary->orderby" : '' );
// If further types of objects can be annotated, additional SELECT
statements must be added here
// as part of a UNION.
@@ -487,11 +589,22 @@
return $q;
}
- function get_conds( $summary )
- {
- $cond = "\n AND a.object_type=".AN_OTYPE_POST;
+ function get_tables( &$params )
+ {
+ $params[ 'courseid' ] = $this->courseid;
+ return
+ "\n INNER JOIN {forum_discussions} d ON d.course= :courseid "
+ . "\n INNER JOIN {forum_posts} p ON p.discussion=
d.id AND
a.object_type=".AN_OTYPE_POST." AND
p.id=a.object_id "
+ . "\n INNER JOIN {forum} f ON
f.id=d.forum ";
+ }
+
+ function get_conds( &$params, $summary )
+ {
+ $params[ 'object_type' ] = AN_OTYPE_POST;
+ $params[ 'ofuserid' ] = (int) $summary->ofuser->id;
+ $cond = "\n AND a.object_type= :object_type";
if ( $summary->ofuser )
- $cond .= " AND a.quote_author_id=".$summary->ofuser->id;
+ $cond .= " AND a.quote_author_id= :ofuserid";
return $cond;
}
}
@@ -504,11 +617,13 @@
var $parenturl;
var $parenttitlehtml;
var $courseid;
+ var $capannotate;
function forum_annotation_url_handler( $f )
{
$this->f = $f;
$this->titlehtml = null;
+ $this->capannotate = 'mod/forum:replypost';
}
/** Internal function to fetch title etc. setting the following fields:
@@ -516,13 +631,13 @@
* to querying the database. */
function fetch_metadata( )
{
- global $CFG;
+ global $DB;
if ( null != $this->titlehtml )
return;
else {
- $query = "SELECT id, name, course FROM {$CFG->prefix}forum WHERE
id={$this->f}";
- $row = get_record_sql( $query );
+ $query = "SELECT id, name, course FROM {forum} WHERE id= :forumid";
+ $row = $DB->get_record_sql( $query, array( 'forumid' => $this->f ) );
if ( False !== $row )
{
$a->name = s( $row->name );
@@ -534,31 +649,39 @@
$this->titlehtml = get_string( 'unknown_forum', ANNOTATION_STRINGS );
$this->courseid = null;
}
+ $this->modulename = 'forum';
+ $this->modinstanceid = (int) $this->f;
$this->parenturl = '/course/view.php?id='.$this->courseid;
$this->parenttitlehtml = get_string( 'whole_course', ANNOTATION_STRINGS
);
}
}
- function get_fields( )
- {
- global $CFG;
- return ",\n 'discussion' section_type, 'post' row_type"
- . ",\n
d.name section_name"
- . ",\n concat('{$CFG->wwwroot}/mod/forum/discuss.php?d=',
d.id)
section_url";
+ function get_fields( &$params )
+ {
+ global $CFG, $DB;
+ $params[ 'section_url_base' ] =
$CFG->wwwroot.'/mod/forum/discuss.php?d=';
+ return ",\n 'discussion' AS section_type, 'post' AS row_type"
+ . ",\n
d.name AS section_name"
+ . ",\n ".$DB->sql_concat(':section_url_base','
d.id')." AS section_url";
}
- function get_tables( )
+ function get_tables( &$params )
{
global $CFG;
- if ( null == $this->f )
- return "\n LEFT OUTER JOIN {$CFG->prefix}forum_posts p ON
p.id=a.object_id"
- . "\n LEFT OUTER JOIN {$CFG->prefix}forum_discussions d ON
p.discussion=
d.id";
- else
- return "\n JOIN {$CFG->prefix}forum_discussions d ON
d.forum=".addslashes($this->f)
- . "\n JOIN {$CFG->prefix}forum_posts p ON p.discussion=
d.id AND
p.id=a.object_id";
+ $s = '';
+ if ( null == $this->f ) {
+ $s = "\n LEFT OUTER JOIN {forum_posts p ON
p.id=a.object_id"
+ . "\n LEFT OUTER JOIN {forum_discussions d ON p.discussion=
d.id";
+ }
+ else {
+ $params[ 'forumid' ] = $this->f;
+ $s = "\n JOIN {forum_discussions} d ON d.forum= :forumid"
+ . "\n JOIN {forum_posts} p ON p.discussion=
d.id AND
p.id=a.object_id";
+ }
+ return $s;
}
- function get_conds( $summary )
+ function get_conds( &$params, $summary )
{
$cond = "\n AND a.object_type=".AN_OTYPE_POST;
if ( $summary->ofuser )
@@ -581,11 +704,13 @@
var $parenttitlehtml;
var $courseid;
var $forumid;
+ var $capannotate;
function discussion_annotation_url_handler( $d )
{
$this->d = $d;
$this->titlehtml = null;
+ $this->capannotate = 'mod/forum:replypost';
}
/** Internal function to fetch title etc. setting the following fields:
@@ -593,7 +718,7 @@
* to querying the database. */
function fetch_metadata( )
{
- global $CFG;
+ global $DB;
if ( null != $this->titlehtml )
return;
@@ -605,10 +730,10 @@
}
else {
$query = "SELECT
d.id AS id,
d.name AS name, d.course AS course,
d.forum AS forum,
f.name AS forum_name"
- . " FROM {$CFG->prefix}forum_discussions d "
- . " INNER JOIN {$CFG->prefix}forum f ON
f.id=d.forum "
- . " WHERE
d.id={$this->d}";
- $row = get_record_sql( $query );
+ . " FROM {forum_discussions} d "
+ . " INNER JOIN {forum} f ON
f.id=d.forum "
+ . " WHERE
d.id= :discussionid";
+ $row = $DB->get_record_sql( $query, array( 'discussionid' => $this->d )
);
$forumname = 'unknown';
if ( False !== $row ) {
$a->name = s( $row->name );
@@ -616,11 +741,15 @@
$this->courseid = (int) $row->course;
$this->forumid = (int) $row->forum;
$forumname = $row->forum_name;
+ $this->modulename = 'forum';
+ $this->modinstanceid = (int) $row->forum;
}
else {
$this->titlehtml = get_string( 'unknown_discussion',
ANNOTATION_STRINGS );
$this->courseid = null;
$this->forumid = null;
+ $this->modulename = null;
+ $this->modinstanceid = null;
}
$this->parenturl = '/mod/forum/view.php?id='.$this->forumid;
$a->name = s( $forumname );
@@ -628,30 +757,38 @@
}
}
- function get_fields( )
- {
- global $CFG;
- return ",\n 'discussion' section_type, 'post' row_type"
- . ",\n
d.name section_name"
- . ",\n concat('{$CFG->wwwroot}/mod/forum/discuss.php?d=',
d.id)
section_url";
+ function get_fields( &$params )
+ {
+ global $CFG, $DB;
+ $params[ 'section_url_base' ] =
$CFG->wwwroot.'/mod/forum/discuss.php?d=';
+ return ",\n 'discussion' AS section_type, 'post' AS row_type"
+ . ",\n
d.name AS section_name"
+ . ",\n ".$DB->sql_concat(':section_url_base','
d.id')." AS section_url";
}
- function get_tables( )
- {
- global $CFG;
- if ( null == $this->d )
- return "\n LEFT OUTER JOIN {$CFG->prefix}forum_posts p ON
p.id=a.object_id"
- . "\n LEFT OUTER JOIN {$CFG->prefix}forum_discussions d ON
p.discussion=
d.id";
- else
- return "\n JOIN {$CFG->prefix}forum_discussions d ON
d.id=".addslashes($this->d)
- . "\n JOIN {$CFG->prefix}forum_posts p ON p.discussion=
d.id AND
p.id=a.object_id";
+ function get_tables( &$params )
+ {
+ $s = '';
+ if ( null == $this->d ) {
+ $s = "\n LEFT OUTER JOIN {forum_posts} p ON
p.id=a.object_id"
+ . "\n LEFT OUTER JOIN {forum_discussions} d ON p.discussion=
d.id";
+ }
+ else {
+ $params[ 'discussionid' ] = $this->d;
+ $s = "\n JOIN {forum_discussions} d ON
d.id= :discussionid"
+ . "\n JOIN {forum_posts} p ON p.discussion=
d.id AND
p.id=a.object_id";
+ }
+ return $s;
}
- function get_conds( $summary )
- {
- $cond = "\n AND a.object_type=".AN_OTYPE_POST;
- if ( $summary->ofuser )
- $cond .= " AND a.quote_author_id=".$summary->ofuser->id;
+ function get_conds( &$params, $summary )
+ {
+ $params[ 'object_type' ] = AN_OTYPE_POST;
+ $cond = "\n AND a.object_type= :object_type";
+ if ( $summary->ofuser ) {
+ $params[ 'ofuserid' ] = $summary->ofuser->id;
+ $cond .= " AND a.quote_author_id= :ofuserid";
+ }
return $cond;
}
@@ -668,31 +805,35 @@
var $parenturl;
var $parenttitlehtml;
var $courseid;
+ var $capannotate;
function post_annotation_url_handler( $p )
{
$this->annotation_url_handler( );
$this->p = $p;
$this->title = null;
+ $this->capannotate = 'mod/forum:replypost';
}
function fetch_metadata( )
***The diff for this file has been truncated for email.***
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/block_marginalia.php Wed May 30
14:29:48 2012
+++ /moodle/trunk/moodle/blocks/marginalia/block_marginalia.php Wed Jun 20
22:56:48 2012
@@ -1,11 +1,40 @@
<?php
+/*
+ * block_marginalia.php
+ *
+ * Marginalia has been developed with funding and support from
+ * BC Campus, Simon Fraser University, and the Government of
+ * Canada, the UNDESA Africa i-Parliaments Action Plan, and
+ * units and individuals within those organizations. Many
+ * thanks to all of them. See CREDITS.html for details.
+ * Copyright (C) 2005-2011 Geoffrey Glass; the United Nations
+ *
http://www.geof.net/code/annotation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
+ *
+ * $Id$
+ */
+
class block_marginalia extends block_base
{
function init( )
{
$this->title = 'Marginalia Annotation';
//get_string('annotation', 'block_annotation');
- $this->version = 2008121000;
+// $this->version = 2010121800;
+ $this->cron = 60 * 60 * 25.2; // once a day is often enough, but make it
a bit off to prevent sympathetic resonance
}
function get_content( )
@@ -22,5 +51,22 @@
}
return $this->content;
}
+
+ function cron( )
+ {
+ global $CFG;
+
+ // Delete annotations whose users no longer exist
+ // this removes the need to touch admin/user.php
+ // Other code should therefore be careful not to join on non-existent
users
+ $query = "DELETE FROM {$CFG->prefix}marginalia WHERE userid NOT IN
(SELECT id FROM {$CFG->prefix}user)";
+ execute_sql( $query, false );
+ // This will catch all read records for non-existent users and
annotations, though the latter should
+ // already have been deleted with the annotation.
+ $query = "DELETE FROM {$CFG->prefix}marginalia_read WHERE annotationid
NOT IN (SELECT id FROM {$CFG->prefix}marginalia)";
+ execute_sql( $query, false );
+ }
}
+$string['marginalia:view_all'] = 'View all';
+$string['marginalia:fix_notes'] = 'Fix notes';
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/config.php Wed May 30 14:29:48
2012
+++ /moodle/trunk/moodle/blocks/marginalia/config.php Wed Jun 20 22:56:48
2012
@@ -6,9 +6,9 @@
// When this is true, any access to annotations (including fetching the
Atom feed) requires a valid user
// When false, anyone on the Web can retrieve public annotations via an
Atom feed
-define( 'ANNOTATION_REQUIRE_USER', false );
-
-define( 'MAX_NOTE_LENGTH', 250 );
+define( 'ANNOTATION_REQUIRE_USER', true );
+
+define( 'MAX_NOTE_LENGTH', 250 ); // should not exceed 255 due to DB
limitations
define( 'MAX_QUOTE_LENGTH', 1000 );
// Enable tag (keyword) autocomplete
@@ -22,11 +22,6 @@
// but the user has permission to perform administrative updates to it)
define( 'AN_ADMINUPDATE', true );
-// Allow an admin user to view everyone's annotations, regardless of
access level
-// This is useful for research and for retrieving the annotations for
backup
-// without having to query the database directly.
-define( 'AN_ADMINVIEWALL', true );
-
// Show summary column headings at the top of each section, rather than at
// the bottom of the whole table.
define( 'AN_SUMMARYHEADINGSTOP', false );
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/keywords.php Wed May 30 14:29:48
2012
+++ /moodle/trunk/moodle/blocks/marginalia/keywords.php Wed Jun 20 22:56:48
2012
@@ -9,12 +9,12 @@
* Canada, the UNDESA Africa i-Parliaments Action Plan, and
* units and individuals within those organizations. Many
* thanks to all of them. See CREDITS.html for details.
- * Copyright (C) 2005-2007 Geoffrey Glass; the United Nations
+ * Copyright (C) 2005-2011 Geoffrey Glass; the United Nations
*
http://www.geof.net/code/annotation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
+ * as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@@ -35,36 +35,30 @@
require_once( 'marginalia-php/KeywordService.php' );
require_once( 'marginalia-php/MarginaliaHelper.php' );
require_once( 'keywords_db.php' );
-require_once( 'annotation_globals.php' );
+require_once( 'moodle_marginalia.php' );
require_login();
class moodle_keyword_service extends KeywordService
{
- function moodle_keyword_service( $username )
+ function moodle_keyword_service( $userid )
{
global $CFG;
+ $moodlemia = moodle_marginalia::get_instance( );
KeywordService::KeywordService(
- annotation_globals::get_host(),
- annotation_globals::get_keyword_service_path(),
- $username,
+ $moodlemia->get_host(),
+ $moodlemia->get_keyword_service_path(),
+ $userid,
$CFG->wwwroot );
$this->tablePrefix = $CFG->prefix;
}
function doListKeywords( )
{
- global $USER;
-
- $user = get_record( 'user', 'username', $this->currentUserId );
- if ( $user ) {
- $keywords = annotation_keywords_db::list_keywords( $user->id );
- $logurl = 'keywords.php';
- add_to_log( null, 'annotation', 'list', $logurl );
- return $keywords;
- }
- else
- return array( );
+ $keywords = annotation_keywords_db::list_keywords( $this->currentUserId
);
+ $logurl = 'keywords.php';
+// add_to_log( null, 'annotation', 'list', $logurl );
+ return $keywords;
}
/**
@@ -102,7 +96,7 @@
}
if ( AN_USEKEYWORDS ) {
- $service = new moodle_keyword_service( isguest() ? null : $USER->username
);
+ $service = new moodle_keyword_service( isguestuser() ? null : $USER->id );
$service->dispatch( );
}
else {
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/keywords_db.php Sat Dec 13
22:12:12 2008
+++ /moodle/trunk/moodle/blocks/marginalia/keywords_db.php Wed Jun 20
22:56:48 2012
@@ -4,7 +4,7 @@
{
function list_keywords( $userid )
{
- global $CFG;
+ global $CFG, $DB;
// A keyword is a note that occurs more than once
$query =
'SELECT a.note AS name, \'\' AS description'
@@ -12,23 +12,22 @@
. ' JOIN ('
. ' SELECT note, count(*) as m'
. " FROM {$CFG->prefix}".AN_DBTABLE
- . " WHERE userid=$userid"
+ . " WHERE userid= :userid"
. ' GROUP BY note) AS b'
. ' ON a.note = b.note'
. ' AND b.m > 1'
. ' GROUP BY a.note'
. ' ORDER BY a.note';
- $keywordset = get_records_sql( $query );
+ $params = array( 'userid' => $userid );
+ $keywordset = $DB->get_recordset_sql( $query, $params );
$keywords = array( );
- if ( $keywordset ) {
- $i = 0;
- foreach ( $keywordset as $r )
- {
- $keyword = new MarginaliaKeyword( );
- $keyword->name = $r->name;
- $keyword->description = $r->description;
- $keywords[ $i++ ] = $keyword;
- }
+ $i = 0;
+ foreach ( $keywordset as $r )
+ {
+ $keyword = new MarginaliaKeyword( );
+ $keyword->name = $r->name;
+ $keyword->description = $r->description;
+ $keywords[ $i++ ] = $keyword;
}
return $keywords;
}
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/lib.php Wed May 30 14:29:48 2012
+++ /moodle/trunk/moodle/blocks/marginalia/lib.php Wed Jun 20 22:56:48 2012
@@ -1,216 +1,42 @@
<?php
+/*
+ * blocks/marginalia/lib.php
+ *
+ * Code has been moved to moodle_marginalia.php to make it possible to more
+ * finely determine which code is needed for a given page.
+ *
+ * Marginalia has been developed with funding and support from
+ * BC Campus, Simon Fraser University, and the Government of
+ * Canada, the UNDESA Africa i-Parliaments Action Plan, and
+ * units and individuals within those organizations. Many
+ * thanks to all of them. See CREDITS.html for details.
+ * Copyright (C) 2005-2011 Geoffrey Glass; the United Nations
+ *
http://www.geof.net/code/annotation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
+ *
+ * $Id$
+ */
require_once( $CFG->dirroot.'/blocks/marginalia/config.php' );
-require_once( ANNOTATION_DIR.'/annotation_globals.php' );
-
-class moodle_marginalia
-{
- /**
- * Get an annotations preference value; if the preference doesn't exist,
create it
- * so that the Javascript client will have permission to set it later (to
prevent
- * client creation of random preferences, only existing preferences can
be set)
- */
- public static function get_pref( $name, $default )
- {
- $value = get_user_preferences( $name, null );
- if ( null == $value ) {
- $value = $default;
- set_user_preference( $name, $default );
- }
- return $value;
- }
-
- public static function get_userid( )
- {
- global $USER;
- // Get the users whose annotations are to be shown
- $annotationuser = get_user_preferences( AN_USER_PREF, null );
- if ( null == $annotationuser ) {
- $annotationuser = isguest() ? null : $USER->username;
- set_user_preference( AN_USER_PREF, $annotationuser );
- }
- return $annotationuser;
- }
-
- public static function get_show_annotations_pref( )
- {
- return moodle_marginalia::get_pref( AN_SHOWANNOTATIONS_PREF, 'false' );
- }
-
-
- /**
- * Return HTML for insertion in the head of a document to include
Marginalia Javascript
- * and initialize Marginalia. If necessary, also creates relevant user
preferences
- * (necessary for Marginalia to function correctly).
- */
- public static function header_html( )
- {
- global $CFG, $USER;
-
- $anscripts = listMarginaliaJavascript( );
- for ( $i = 0; $i < count( $anscripts ); ++$i )
- require_js( ANNOTATION_PATH.'/marginalia/'.$anscripts[ $i ] );
- require_js( array(
- ANNOTATION_PATH.'/marginalia-config.js',
- ANNOTATION_PATH.'/marginalia-strings.js',
- ANNOTATION_PATH.'/smartquote.js',
- ANNOTATION_PATH.'/MoodleMarginalia.js' ) );
-
- // Bits of YUI
- require_js( array(
- $CFG->wwwroot.'/lib/yui/yahoo-dom-event/yahoo-dom-event.js',
- // $CFG->wwwroot.'/lib/yui/datasource/datasource-min.js',
- $CFG->wwwroot.'/lib/yui/autocomplete/autocomplete-min.js' ) );
-
- $meta = "<link rel='stylesheet' type='text/css'
href='".s($CFG->wwwroot)."/lib/yui/autocomplete/assets/skins/sam/autocomplete.css'/>\n"
- ."<link rel='stylesheet' type='text/css'
href='".s(ANNOTATION_PATH)."/marginalia/marginalia.css'/>\n"
- ."<link rel='stylesheet' type='text/css'
href='".s(ANNOTATION_PATH)."/annotation-styles.php'/>\n";
-/* Hack for attempt to get this working with the block:
- $meta = "<script type='text/javascript'>\n"
- ."
domutil.loadStylesheet( '".s($CFG->wwwroot)."/lib/yui/autocomplete/assets/skins/sam/autocomplete.css');\n"
- ."
domutil.loadStylesheet( '".ANNOTATION_PATH.'/marginalia/marginalia.css'."');\n"
- ."
domutil.loadStylesheet( '".ANNOTATION_PATH.'/annotation-styles.php'."');\n"
- ."</script>\n";
-*/
- return $meta;
- }
-
- /**
- * Generate the content HTML. This contains the Javascript necessary to
- * initialized Marginalia. It also require_js's a number of Javascript
files.
- */
- public static function init_html( $refurl )
- {
- global $CFG, $USER;
-
- // Get all annotation preferences as an associative array and sets them
to defaults
- // in the database if not already present.
- $prefs = array(
- AN_USER_PREF => moodle_marginalia::get_userid( ),
- AN_SHOWANNOTATIONS_PREF => moodle_marginalia::get_pref(
AN_SHOWANNOTATIONS_PREF, 'false' ),
- AN_NOTEEDITMODE_PREF => moodle_marginalia::get_pref(
AN_NOTEEDITMODE_PREF, 'freeform' ),
- AN_SPLASH_PREF => moodle_marginalia::get_pref( AN_SPLASH_PREF, 'true' )
- );
-
- $showannotationspref = $prefs[ AN_SHOWANNOTATIONS_PREF ];
- $annotationuser = $prefs[ AN_USER_PREF ];
- $showsplashpref = $prefs[ AN_SPLASH_PREF ];
-
- // Build a string of initial preference values for passing to Marginalia
- $first = true;
- $sprefs = '';
- foreach ( array_keys( $prefs ) as $name )
- {
- $value = $prefs[ $name ];
- if ( $first )
- $first = false;
- else
- $sprefs .= "\n, ";
- $sprefs .= "'".s( $name )."': '".s( $prefs[ $name ] )."'";
- }
- $sprefs = '{ '.$sprefs.' }';;
-
- $sitecontext = get_context_instance(CONTEXT_SYSTEM);
- $allowAnyUserPatch = AN_ADMINUPDATE && (
- has_capability( 'moodle/legacy:admin', $sitecontext ) or
has_capability( 'moodle/site:doanything', $sitecontext) );
-
- $meta = "<script language='JavaScript' type='text/javascript'>\n"
- ."function myOnload() {\n"
- ." var moodleRoot = '".s($CFG->wwwroot)."';\n"
- ." var annotationPath = '".s(ANNOTATION_PATH)."';\n"
- ." var url = '".s($refurl)."';\n"
- .' var userId = \''.s($USER->username)."';\n"
- .' moodleMarginalia = new MoodleMarginalia( annotationPath, url,
moodleRoot, userId, '.$sprefs.', {'."\n";
- $meta .= ' useSmartquote: '.s(AN_USESMARTQUOTE)
- .",\n".' allowAnyUserPatch: '.($allowAnyUserPatch ? 'true' : 'false' )
- .",\n smartquoteIcon: '".AN_SMARTQUOTEICON."'"
- .",\n sessionCookie: 'MoodleSessionTest".$CFG->sessioncookie."'";
- if ( $showsplashpref == 'true' )
- $meta .= ', splash: \''.get_string('splash',ANNOTATION_STRINGS).'\'';
- $meta .= ' } );'."\n"
- ." moodleMarginalia.onload();\n"
- ."}\n"
- ."addEvent(window,'load',myOnload);\n"
- ."</script>\n";
- return $meta;
- }
-
- function show_help( )
- {
- global $CFG;
-
- helpbutton( 'annotate', get_string( 'annotation_help',
ANNOTATION_STRINGS ), 'block_marginalia' );
- /*
- $helptitle = 'Help with Annotations';
- $linkobject = '<span class="helplink"><img class="iconhelp"
alt="'.$helptitle.'" src="'.$CFG->pixpath .'/help.gif" /></span>';
- echo link_to_popup_window
('/help.php?file=annotate.html&forcelang=', 'popup',
- $linkobject, 400, 500, $helptitle, 'none', true);
- */
- }
-
- function show_user_dropdown( $refurl )
- {
- global $USER;
-
- $summary = annotation_summary_query::from_url( $refurl );
- $userlist = get_records_sql( $summary->list_users_sql( ) );
- $annotationuserid = moodle_marginalia::get_userid( );
- $showannotationspref = moodle_marginalia::get_show_annotations_pref( )
== 'true';
-
- echo "<select name='anuser' id='anuser'
onchange='window.moodleMarginalia.changeAnnotationUser(this,\"$refurl\");'>\n";
- $selected = $showannotationspref ? '' : " selected='selected' ";
- echo " <option $selected
value=''>".get_string('hide_annotations',ANNOTATION_STRINGS)."</option>\n";
- if ( ! isguest() ) {
- $selected = ( $showannotationspref && ( $USER->username ==
$annotationuserid ? "selected='selected' " : '' ) )
- ? " selected='selected' " : '';
- echo " <option $selected"
-
."value='".s($USER->username)."'>".get_string('my_annotations',ANNOTATION_STRINGS)."</option>\n";
- }
- if ( $userlist ) {
- foreach ( $userlist as $user ) {
- if ( $user->username != $USER->username ) {
- $selected = ( $showannotationspref && ( $user->id ==
$annotationuserid ? "selected='selected' ":'' ) )
- ? " selected='selected' " : '';
- echo " <option $selected"
-
."value='".s($user->username)."'>".s($user->firstname.' '.$user->lastname)."</option>\n";
- }
- }
- }
- // Show item for all users
- if ( true ) {
- $selected = ( $showannotationspref && ( '*' ==
$annotationuserid ? "selected='selected' ":'' ) )
- ? " selected='selected' " : '';
- echo " <option $selected
value='*'>".get_string('all_annotations',ANNOTATION_STRINGS)."</option>\n";
- }
- echo "</select>\n";
- }
-
-
- function summary_link_html( $refurl, $userid )
- {
- global $CFG, $course;
- $summaryurl = ANNOTATION_PATH.'/summary.php?user='.urlencode($userid)
- ."&url=".urlencode( $refurl );
- return " <a id='annotation-summary-link' href='".s($summaryurl)."'"
- . " title='".get_string('summary_link_title',ANNOTATION_STRINGS)
- ."'>".get_string('summary_link',ANNOTATION_STRINGS)."</a>\n"
-
- ."<a id='annotation-editkeywords-link'
href='".ANNOTATION_PATH.'/tags.php?course='.$course->id."'"
- . " title='".get_string( 'edit_keywords_link', ANNOTATION_STRINGS )
- ."'>Tags</a>\n";
- }
-
-
- /**
- * Deletes all annotations of a specific user
- * This is here rather than in the annotation code so that not everything
will have to
- * include the annotation code.
- *
- * @param int $userid
- * @return boolean
- */
- function annotations_delete_user( $userid )
- {
- return delete_records( AN_DBTABLE, 'id', $userid );
- }
-}
+require_once( ANNOTATION_DIR.'/moodle_marginalia.php' );
+
+/*
+ * Moodle automatically loads this lib.php file on every page if the
+ * Marginalia block is enabled. It would be really cool if we could
+ * emit the headers here. But... it cannot be. Because we need PAGE->url
+ * to figure out what to emit. So there's no way to avoid a patch point.
+ */
+
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/marginalia-config.js Wed Mar 4
20:59:00 2009
+++ /moodle/trunk/moodle/blocks/marginalia/marginalia-config.js Wed Jun 20
22:56:48 2012
@@ -11,7 +11,7 @@
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
+ * as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/smartquote.js Wed May 30
14:29:48 2012
+++ /moodle/trunk/moodle/blocks/marginalia/smartquote.js Wed Jun 20
22:56:48 2012
@@ -1,236 +1,252 @@
/*
* Smartquote functions used in Moodle
* built on CookieBus
+ *
+ * Marginalia has been developed with funding and support from
+ * BC Campus, Simon Fraser University, and the Government of
+ * Canada, the UNDESA Africa i-Parliaments Action Plan, and
+ * units and individuals within those organizations. Many
+ * thanks to all of them. See CREDITS.html for details.
+ * Copyright (C) 2005-2007 Geoffrey Glass; the United Nations
+ *
http://www.geof.net/code/annotation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
+ *
+ * $Id$
*/
-Smartquote = {
- /**
- * Enable all smartquote buttons on the page
- * Buttons are found in posts with selector button.smartquote
- */
- enableSmartquote: function( wwwroot, postPageInfo, skipContent )
- {
- // Use passed-in value for speed if possible
- if ( ! postPageInfo )
- postPageInfo = PostPageInfo.getPostPageInfo( document );
-
- // Enable smartquote buttons
- var posts = postPageInfo.getAllPosts( );
- for ( var i = 0; i < posts.length; ++i )
- {
- var button = domutil.childByTagClass( posts[ i ].getElement(
), 'button', 'smartquote', skipContent );
- if ( button )
- Smartquote.enableSmartquoteButton( button, posts[ i ], wwwroot,
skipContent );
- }
- },
-
- /**
- * Enable a specific smartquote button
- * Must be a separate function from the loop in enableSmartquote to deal
- * correctly with Javascript dynamic scoping and closures
- */
- enableSmartquoteButton: function( button, post, wwwroot, skipContent )
- {
- var content = post.getContentElement( );
- var postId = Smartquote.postIdFromUrl( post.getUrl( ) );
- var f = function( ) { Smartquote.quotePostMicro( content, skipContent,
wwwroot, postId ); };
- addEvent( button, 'click', f );
- },
-
- /**
- * Calculate a post ID based on its URL
- */
- postIdFromUrl: function( url )
- {
- var matches = url.match( /^.*\/mod\/forum\/permalink\.php\?p=(\d+)/ );
- if ( matches )
- return Number( matches[ 1 ] );
- else
- return 0;
- },
-
- /**
- * Get a quote (selected text) from a postMicro with a given ID
- * Returns the quote as HTML with metadata included. Note, however, that
- * any HTML tags in the selected text are stripped, and whitespace is
- * collapsed.
- */
- getPostMicroQuote: function( content, skipContent, wwwroot, postId )
- {
- // Test for selection support (W3C or IE)
- if ( ( ! window.getSelection || null == window.getSelection().rangeCount
)
- && null == document.selection )
- {
- alert( getLocalized( 'browser support of W3C range required for
smartquote' ) );
- return false;
- }
-
- var textRange0 = getPortableSelectionRange();
- if ( null == textRange0 )
- {
- alert( getLocalized( 'select text to quote' ) );
- return false;
- }
-
- // Strip off leading and trailing whitespace and preprocess so that
- // conversion to WordRange will go smoothly.
- var textRange = TextRange.fromW3C( textRange0 );
-
- // Don't need a skip handler unless we're running on a page with
Marginalia
- textRange = textRange.shrinkwrap( skipContent );
- if ( ! textRange )
- {
- // this happens if the shrinkwrapped range has no non-whitespace text
in it
- alert( getLocalized( 'select text to quote' ) );
- return false;
- }
-
- var quote = getTextRangeContent( textRange, skipContent );
- quote = quote.replace( /(\s|\u00a0)+/g, ' ' );
-
- var postInfo = PostPageInfo.getPostPageInfo( document );
- var post = postInfo.getPostMicro( textRange.startContainer );
- var leadIn = '';
- if ( post )
- {
- leadIn = '<p>' + ( post.getAuthorName( ) ? domutil.htmlEncode(
post.getAuthorName( ) ) : 'Someone' )
- + ( post.getUrl( ) ? ' <a href="' + domutil.htmlEncode( post.getUrl( )
) + '">wrote</a>' : 'wrote' )
- + ",</p>";
- }
- return leadIn + '<blockquote><p>' + domutil.htmlEncode( quote )
+ '</p></blockquote>';
- },
+function Smartquote( wwwroot, selectors, extService )
+{
+ this.wwwroot = wwwroot;
+ this.selectors = selectors;
+ this.extService = extService;
+}
+
+/**
+ * Enable all smartquote buttons on the page
+ * Buttons are found in posts with selector button.smartquote
+ */
+Smartquote.prototype.enable = function( postPageInfo, skipContent, params )
+{
+ // Use passed-in value for speed if possible
+ if ( ! postPageInfo )
+ postPageInfo = PostPageInfo.getPostPageInfo( document, this.selectors );
+
+ // Enable smartquote buttons
+ var posts = postPageInfo.getAllPosts( );
+ for ( var i = 0; i < posts.length; ++i )
+ {
+ var button = domutil.childByTagClass( posts[ i ].getElement(
), 'button', 'smartquote', skipContent );
+ if ( button )
+ this.enableButton( button, posts[ i ], skipContent );
+ }
+}
+
+/**
+ * Enable a specific smartquote button
+ * Must be a separate function from the loop in enableSmartquote to deal
+ * correctly with Javascript dynamic scoping and closures
+ */
+Smartquote.prototype.enableButton = function( button, post, skipContent )
+{
+ var smartquote = this;
+ var content = post.getContentElement( );
+ var postId = Smartquote.postIdFromUrl( post.getUrl( ) );
+ var f = function( ) { smartquote.quotePostMicro( content, skipContent,
postId ); };
+ addEvent( button, 'click', f );
+}
+
+/**
+ * Calculate a post ID based on its URL
+ * must have implementations for each kind of quoteable url
+ */
+Smartquote.postIdFromUrl = function( url )
+{
+ var matches = url.match( /^.*\/mod\/forum\/permalink\.php\?p=(\d+)/ );
+ if ( matches )
+ return Number( matches[ 1 ] );
+ else
+ return 0;
+},
+
+/**
+ * Get a quote (selected text) from a postMicro with a given ID
+ * Returns the quote as HTML with metadata included. Note, however, that
+ * any HTML tags in the selected text are stripped, and whitespace is
+ * collapsed.
+ */
+Smartquote.prototype.getPostMicroQuote = function( content, skipContent,
postId )
+{
+ // Test for selection support (W3C or IE)
+ if ( ( ! window.getSelection || null == window.getSelection().rangeCount )
+ && null == document.selection )
+ {
+ alert( getLocalized( 'browser support of W3C range required for
smartquote' ) );
+ return false;
+ }
+
+ var textRange0 = getPortableSelectionRange();
+ if ( null == textRange0 )
+ {
+ alert( getLocalized( 'select text to quote' ) );
+ return false;
+ }
+
+ // Strip off leading and trailing whitespace and preprocess so that
+ // conversion to WordRange will go smoothly.
+ var textRange = TextRange.fromW3C( textRange0 );
+
+ // Don't need a skip handler unless we're running on a page with
Marginalia
+ textRange = textRange.shrinkwrap( skipContent );
+ if ( ! textRange )
+ {
+ // this happens if the shrinkwrapped range has no non-whitespace text in
it
+ alert( getLocalized( 'select text to quote' ) );
+ return false;
+ }
+
+ var quote = getTextRangeContent( textRange, skipContent );
+ quote = quote.replace( /(\s|\u00a0)+/g, ' ' );
+
+ var postInfo = PostPageInfo.getPostPageInfo( document, this.selectors );
+ var post = postInfo.getPostByElement( textRange.startContainer );
+ var leadIn = '';
+ if ( post )
+ {
+ var url = this.wwwroot + post.getUrl( this.wwwroot );
+ // console.log( 'post url: ' + url );
+ leadIn = '<p>' + ( post.getAuthorName( ) ? domutil.htmlEncode(
post.getAuthorName( ) ) : 'Someone' )
+ + ( post.getUrl( ) ? ' <a href="' + domutil.htmlEncode( url )
+ '">wrote</a>' : 'wrote' )
+ + ",</p>";
+ }
+ return leadIn + '<blockquote><p>' + domutil.htmlEncode( quote )
+ '</p></blockquote>';
+}
- /**
- * Called when a quote button is clicked on a postMicro. Extracts the
- * selected text, builds HTML with metadata, and publishes it on the
- * CookieBus.
- */
- quotePostMicro: function( content, skipContent, wwwroot, postId )
- {
-// console.log( 'quote' );
- var pub = Smartquote.getPostMicroQuote( content, skipContent, wwwroot,
postId );
- var bus = new CookieBus( 'smartquote' );
- if ( bus.getSubscriberCount( ) > 0 )
- {
-// console.log( 'publish: ' + pub );
- bus.publish( pub );
- }
- else if ( wwwroot && postId )
- {
- // The nbsp below inserts an annoying extra space - but that's better
- // than the editor's default behavior of adding any new text to the
previous
- // blockquote. Moodle needs a new editor (this one was discontinued).
- window.location = wwwroot + '/mod/forum/post.php?reply=' + postId
- + '&message=' + encodeURIParameter( pub + ' <p> ' );
- }
- },
-
- quoteAnnotation: function( annotation, loginUserId, wwwroot, postId )
- {
- var quoteAuthor = annotation.getQuoteAuthorName( );
- var url = annotation.getUrl( );
- var quote = annotation.getQuote( );
- var note = annotation.getNote( );
- var noteAuthor = annotation.getUserName( );
-
- if ( url && 0 != url.indexOf( 'http://' ) && 0 !=
url.indexOf( 'https://' ) )
- url = wwwroot + url;
-
- quote = quote.replace( /\s/g, ' ' );
- quote = quote.replace( /\u00a0/g, ' ' );
-
- var pub = '<p>' + ( quoteAuthor ? domutil.htmlEncode( quoteAuthor
) : 'Someone' )
- + ( url ? ' <a href="' + domutil.htmlEncode( url ) + '">wrote,</a>' : '
wrote' )
- + '</p><blockquote><p>' + domutil.htmlEncode( quote )
+ '</p></blockquote>';
- if ( loginUserId == annotation.getUserId( ) )
- {
- if ( annotation.getNote( ) )
- pub += '<p>' + domutil.htmlEncode( note ) + '</p>';
+/**
+ * Called when a quote button is clicked on a postMicro. Extracts the
+ * selected text, builds HTML with metadata, and publishes it on the
+ * CookieBus.
+ */
+Smartquote.prototype.quotePostMicro = function( content, skipContent,
postId )
+{
+ var pub = this.getPostMicroQuote( content, skipContent, postId );
+ var bus = new CookieBus( 'smartquote' );
+ if ( bus.getSubscriberCount( ) > 0 )
+ {
+ bus.publish( pub );
+
+ if ( this.extService )
+ this.extService.createEvent( 'smartquote', 'send', pub, 'forum_post',
postId );
+ }
+ else if ( this.wwwroot && postId )
+ {
+ window.location = this.wwwroot + '/mod/forum/post.php?reply=' + postId
+ + '&message=' + restutil.encodeURIParameter( pub + "<p> ");
+
+ if ( this.extService )
+ this.extService.createEvent( 'smartquote', 'new post',
pub, 'forum_post', postId );
+ }
+}
+
+
+Smartquote.prototype.quoteAnnotation = function( annotation, loginUserId,
postId )
+{
+ var quoteAuthor = annotation.getQuoteAuthorName( );
+ var url = annotation.getUrl( );
+ var quote = annotation.getQuote( );
+ var note = annotation.getNote( );
+ var noteAuthor = annotation.getUserName( );
+
+ quote = quote.replace( /\s/g, ' ' );
+ quote = quote.replace( /\u00a0/g, ' ' );
+
+ var pub = '<p>' + ( quoteAuthor ? domutil.htmlEncode( quoteAuthor
) : 'Someone' )
+ + ( url ? ' <a href="' + domutil.htmlEncode( url ) + '">wrote,</a>' : '
wrote' )
+ + '</p><blockquote><p>' + domutil.htmlEncode( quote )
+ '</p></blockquote>';
+ if ( loginUserId == annotation.getUserId( ) )
+ {
+ if ( annotation.getNote( ) )
+ pub += '<p>' + domutil.htmlEncode( note ) + '</p>';
+ }
+ else
+ {
+ note = note.replace( /\s/g, ' ' );
+ note = note.replace( /\u00a0/g, ' ' );
+ if ( note )
+ {
+ pub += '<p>Via ' + domutil.htmlEncode( noteAuthor ) + ', who noted,</p>'
+ + '<blockquote><p>' + domutil.htmlEncode( note ) + '</p></blockquote>';
}
else
- {
- note = note.replace( /\s/g, ' ' );
- note = note.replace( /\u00a0/g, ' ' );
- if ( note )
- {
- pub += '<p>Via ' + domutil.htmlEncode( noteAuthor ) + ', who
noted,</p>'
- + '<blockquote><p>' + domutil.htmlEncode( note )
+ '</p></blockquote>';
- }
- else
- pub += '<p>(Via an annotation by ' + domutil.htmlEncode( noteAuthor )
+ '.)</p>';
- }
-
- var bus = new CookieBus( 'smartquote' );
- if ( bus.getSubscriberCount( ) > 0 )
- bus.publish( pub );
- else if ( wwwroot && postId )
- {
- // The nbsp below inserts an annoying extra space - but that's better
- // than the editor's default behavior of adding any new text to the
previous
- // blockquote. Moodle needs a new editor (this one was discontinued).
- window.location = wwwroot + '/mod/forum/post.php?reply=' + postId
- + '&message=' + encodeURIParameter( pub + " <p>" );
- }
- },
-
-
- /**
- * Subscribe an HTMLArea control to receive smartquote publish events
- */
- subscribeHtmlArea: function( editor )
- {
- // This code and these tests are very much specific to HTMLArea
- // The test for the range is necessary - otherwise if the user hasn't
- // clicked in the area, everything can blow up.
- var bus = new CookieBus( 'smartquote' );
- bus.subscribe( 2000, function( pub ) {
- var sel = editor._getSelection( );
- var range = editor._createRange( sel );
- // D'oh. Default range is in HTMLDocument, which of course has
- // no parent (best way I could think to test for that). HTMLArea
- // blows up when an insert is attempted then.
- if ( HTMLArea.is_ie )
- {
- if ( 'None' == sel.type && false )
- {
- var textRange = editor._doc.body.createTextRange( );
- textRange.select( );
- }
- }
- else
- {
- if ( ! range.startContainer.parentNode )
- {
- var textRange = editor._doc.createRange( );
- textRange.selectNode( editor._doc.body.lastChild );
- var selection = editor._iframe.contentWindow.getSelection();
- selection.addRange( textRange );
- selection.collapseToEnd( );
- }
- }
- editor.insertHTML( pub.value + ' ' + '<br/>');
-
- // Collapse range to the end of the document
- // Otherwise the editor ends up selecting the first paragraph of the
- // last paste, which will be stomped by subsequent pastes
- // Mozilla only (sorry IE - for now)
- if ( ! HTMLArea.is_ie )
- {
- var textRange = editor._doc.createRange( );
- textRange.selectNode( editor._doc.body.lastChild );
- var selection = editor._iframe.contentWindow.getSelection();
- selection.addRange( textRange );
- selection.collapseToEnd( );
- }
- } );
-
- // Don't forget to unsubscribe if the window is unloaded
- addEvent( window, 'unload', function( ) { bus.unsubscribe( ); } );
-
- return bus;
- }
-};
-
-
-
+ pub += '<p>(Via an annotation by ' + domutil.htmlEncode( noteAuthor )
+ '.)</p>';
+ }
+
+ var bus = new CookieBus( 'smartquote' );
+ if ( bus.getSubscriberCount( ) > 0 )
+ {
+ bus.publish( pub );
+ if ( this.extService )
+ this.extService.createEvent( 'smartquote', 'send', quote, 'annotation',
annotation.getId() );
+ }
+ else if ( this.wwwroot && postId )
+ {
+ bus.publish( pub );
+ window.location = this.wwwroot + '/mod/forum/post.php?reply=' + postId
+ + '&message=' + restutil.encodeURIParameter( pub + "<p> ");
+
+ if ( this.extService )
+ this.extService.createEvent( 'smartquote', 'new post',
quote, 'annotation', annotation.getId() );
+ }
+}
+
+
+function SmartquoteSubscriber( extService )
+{
+ this.extService = extService;
+}
+
+/**
+ * Subscribe a named tiny MCE instance to smartquote events
+ */
+SmartquoteSubscriber.prototype.subscribeMCE = function( name, object_type,
object_id )
+{
+ var subscriber = this;
+ // Thank you oh thank you Moodle 2.0 for moving from HTMLArea to tinyMCE
+ // This code was such a mess of weird exceptions before.
+ var bus = new CookieBus( 'smartquote' );
+ bus.subscribe( 1000, function( pub ) { // should it be mceInsertRawHTML?
+ // Need to check whether the editor exists - if not, return false
+ // so that forward,once quote items won't be used up. (This can happen
+ // if tinyMCE has not yet been initialized in a message reply window
+ // opened by a quote button.)
+ var mce = tinyMCE.get( name );
+ if ( mce )
+ {
+ tinyMCE.execCommand( 'mceFocus', false, name );
+ mce.execCommand( 'mceInsertContent', false, pub.value );
+
+ if ( subscriber.extService )
+ subscriber.extService.createEvent( 'smartquote', 'receive', pub.value,
object_type, object_id );
+ }
+ return true;
+ } );
+
+ // Don't forget to unsubscribe if the window is unloaded
+ addEvent( window, 'unload', function( ) { bus.unsubscribe( ); } );
+
+ return bus;
+}
+
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/summary-styles.php Wed May 30
14:29:48 2012
+++ /moodle/trunk/moodle/blocks/marginalia/summary-styles.php Wed Jun 20
22:56:48 2012
@@ -43,14 +43,22 @@
margin-top: 1.5em;
}
+p#query a .alt {display: none;}
p#query a .alt,
p#query a:hover .current {
- display: none;
+ xdisplay: none;
+ text-decoration: line-through;
}
+p#query a:hover {
+ text-decoration: none;
+}
+
+/*
p#query a:hover .alt {
display: inline;
}
+*/
p.error em.range-error {
color: white;
@@ -84,7 +92,9 @@
table.annotations thead.labels th:before {
font-style: normal;
- content: '\2191 ';
+ font-size: 120%;
+ margin-right: .5ex;
+ content: '\2191';
}
table.annotations thead.labels.top th:before {
@@ -143,6 +153,10 @@
font-size: 80% ;
width: 30%;
}
+
+table.annotations tbody td.modified {
+ font-size: 80%;
+}
table.annotations tbody td.user {
font-size: 80%;
@@ -177,6 +191,7 @@
table.annotations button {
padding-left: .25ex ;
padding-right: .25ex;
+ width: 1em;
background: none;
border: none;
cursor: pointer;
@@ -185,6 +200,21 @@
table.annotations button:hover {
font-weight: bold;
}
+
+.result-pages {
+ list-style-type: none;
+ margin: 2em auto;
+ text-align: center;
+ padding: 1px 0;
+}
+
+.result-pages li {
+ display: inline;
+}
+
+.result-pages + * {
+ clear: both;
+}
/* smartcopy tip */
p#smartcopy-help {
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/summary.js Wed May 30 14:29:48
2012
+++ /moodle/trunk/moodle/blocks/marginalia/summary.js Wed Jun 20 22:56:48
2012
@@ -1,15 +1,17 @@
/*
- * annotation.js
+ * summary.js
*
- * Web Annotation is being developed for Moodle with funding from BC Campus
- * and support from Simon Fraser University and SFU's Applied Communication
- * Technologies Group and the e-Learning Innovation Centre of the
- * Learning Instructional Development Centre at SFU
- * Copyright (C) 2005 Geoffrey Glass
+ * Marginalia has been developed with funding and support from
+ * BC Campus, Simon Fraser University, and the Government of
+ * Canada, the UNDESA Africa i-Parliaments Action Plan, and
+ * units and individuals within those organizations. Many
+ * thanks to all of them. See CREDITS.html for details.
+ * Copyright (C) 2005-2011 Geoffrey Glass; the United Nations
+ *
http://www.geof.net/code/annotation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
+ * as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@@ -23,67 +25,65 @@
*
*/
+function summaryOnLoad( )
+{
+ jQuery('.annotations .note').each( function( index, node ) {
+ domutil.urlize( this );
+ } );
+}
+jQuery(window).load(summaryOnLoad);
+
/*
* Must be called before any other annotation functions
*/
-AN_SUN_SYMBOL = '\u25cb'; //'\u263c';
-AN_MOON_SYMBOL = '\u25c6'; //'\u2641';
-
-function AnnotationSummary( annotationService, wwwroot, loginUserId )
-{
- this.annotationService = annotationService;
+function AnnotationSummary( wwwroot, params )
+{
this.wwwroot = wwwroot;
- this.loginUserId = loginUserId;
+ this.annotationService = params.annotationService;
+ this.loginUserId = params.loginUserId;
+ this.csrfCookie = null;
+ this.useLog = false;
+ this.extService = params.extService;
+
+ for ( var param in params )
+ {
+ switch ( param )
+ {
+ case 'annotationService':
+ this.annotationService = params[ param ];
+ break;
+
+ case 'userid':
+ this.userid = params[ param ];
+ break;
+
+ case 'csrfCookie':
+ this.csrfCookie = params[ param ];
+ break;
+
+ case 'useLog':
+ this.useLog = params[ param ];
+ break;
+
+ default:
+ throw 'Unknown paramater to AnnotationSummary: ' + param;
+ }
+ }
+
+ this.smartquote = new Smartquote( this.wwwroot, null, this.extService );
}
-AnnotationSummary.prototype.deleteAnnotation = function( id )
-{
+AnnotationSummary.prototype.deleteAnnotation = function( id, annotationid )
+{
+ var element = document.getElementById( id );
+ var row = domutil.parentByTagClass( element, 'tr', null );
+ var annotation = this.annotationFromRow( row, {
+ id: annotationid } );
+
var f = function( xmldoc ) {
window.location.reload( );
};
- this.annotationService.deleteAnnotation( id, f );
-}
-
-AnnotationSummary.prototype.shareAnnotation = function( button, id )
-{
- var annotation = new Annotation( );
- annotation.setId( id );
- annotation.resetChanges( );
- annotation.setAccess( button.value );
- this.annotationService.updateAnnotation( annotation, null );
-}
-
-AnnotationSummary.prototype.shareAnnotationPublicPrivate = function(
button, id )
-{
- var annotation = new Annotation( );
- annotation.setId( id );
- annotation.resetChanges( );
-
annotation.id = id;
- var oldAccess = domutil.hasClass( button, 'access-public'
) ? 'public' : 'private';
- annotation.setAccess( 'public' == oldAccess ? 'private' : 'public' );
- this.annotationService.updateAnnotation( annotation, null );
- domutil.removeClass( button, 'access-' + oldAccess );
- while ( button.firstChild )
- button.removeChild( button.firstChild );
- button.appendChild( document.createTextNode( 'public' ==
annotation.access ? AN_SUN_SYMBOL : AN_MOON_SYMBOL ) );
- domutil.addClass( button, 'access-' + annotation.access );
-}
-
-AnnotationSummary.prototype.onSearchAnnotationsChange = function( )
-{
- var searchElement = document.getElementById( 'search-annotations' );
- var accessElement = document.getElementById( 'access' );
- var userElement = document.getElementById( 'user' );
- if ( 'my annotations' == searchElement.value )
- {
- userElement.value = this.loginUserId;
- accessElement.value = '';
- }
- else
- {
- userElement.value = '';
- accessElement.value = 'public';
- }
+ this.annotationService.deleteAnnotation( annotation, f );
}
AnnotationSummary.skipZoom = function( node )
@@ -100,12 +100,14 @@
{
var element = document.getElementById( id );
var row = domutil.parentByTagClass( element, 'tr', null );
- var annotation = this.annotationFromRow( row, userid );
+ var annotation = this.annotationFromRow( row, {
+ userid: userid } );
+
var postId = Smartquote.postIdFromUrl( annotation.getUrl( ) );
- Smartquote.quoteAnnotation( annotation, this.loginUserId, this.wwwroot,
postId );
+ this.smartquote.quoteAnnotation( annotation, this.userid, postId );
}
-AnnotationSummary.prototype.annotationFromRow = function( row, userid )
+AnnotationSummary.prototype.annotationFromRow = function( row, fields )
{
var node = domutil.childByTagClass( row, null, 'quote' );
var quote = domutil.getNodeText( node, AnnotationSummary.skipZoom );
@@ -134,20 +136,16 @@
var url = node ? node.getAttribute( 'href' ) : '';
var annotation = new Annotation( {
- userid: userid,
+ id:
fields.id,
+ userId: fields.userid,
url: url,
quote: quote,
note: note,
userName: userName,
quoteAuthorName: quoteAuthorName
} );
+ annotation.resetChanges( );
return annotation;
}
-function setAnnotationUser( user )
-{
- window.preferenceService.setPreference( 'show_annotations', 'true', null);
- window.preferenceService.setPreference( 'annotation_user', user, null );
-}
-
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/summary.php Wed May 30 14:29:48
2012
+++ /moodle/trunk/moodle/blocks/marginalia/summary.php Wed Jun 20 22:56:48
2012
@@ -1,132 +1,148 @@
<?php
-
- // summary.php
- // Part of Marginalia annotation for Moodle
- // See
www.geof.net/code/annotation/ for full source and documentation.
-
- // Display a summary of all annotations for the current user
+/*
+ * summary.php
+ *
+ * Marginalia has been developed with funding and support from
+ * BC Campus, Simon Fraser University, and the Government of
+ * Canada, the UNDESA Africa i-Parliaments Action Plan, and
+ * units and individuals within those organizations. Many
+ * thanks to all of them. See CREDITS.html for details.
+ * Copyright (C) 2005-2011 Geoffrey Glass; the United Nations
+ *
http://www.geof.net/code/annotation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
+ *
+ * $Id: keywords.php 383 2008-12-14 06:12:12Z geof.glass $
+ */
require_once( "../../config.php" );
+require_once( $CFG->dirroot.'/blocks/marginalia/config.php' );
+require_once( ANNOTATION_DIR.'/marginalia-php/embed.php' );
+require_once( ANNOTATION_DIR.'/annotation_summary_query.php' );
+require_once( ANNOTATION_DIR.'/moodle_marginalia.php' );
+require_once( ANNOTATION_DIR.'/marginalia-php/Keyword.php' );
require_once( "marginalia-php/MarginaliaHelper.php" );
-require_once( 'marginalia-php/Annotation.php' );
+require_once( "keywords_db.php" );
+/*require_once( 'marginalia-php/Annotation.php' );
require_once( 'marginalia-php/Keyword.php' );
require_once( 'config.php' );
-require_once( 'annotation_globals.php' );
+require_once( "lib.php" );
require_once( "annotation_summary_query.php" );
-require_once( "keywords_db.php" );
-
-global $CFG;
-
-if ($CFG->forcelogin) {
- require_login();
-}
+*/
class annotation_summary_page
{
- function show_header( )
- {
- global $CFG, $USER;
-
- $swwwroot = htmlspecialchars( $CFG->wwwroot );
- $navtail = get_string( 'summary_title', ANNOTATION_STRINGS );
- $navmiddle = "";
-
- require_js( ANNOTATION_PATH.'/marginalia/3rd-party.js' );
- require_js( ANNOTATION_PATH.'/marginalia/log.js' );
- require_js( ANNOTATION_PATH.'/marginalia-config.js' );
- require_js( ANNOTATION_PATH.'/marginalia/domutil.js' );
- require_js( ANNOTATION_PATH.'/marginalia/prefs.js' );
- require_js( ANNOTATION_PATH.'/marginalia/rest-prefs.js' );
- require_js( ANNOTATION_PATH.'/marginalia/annotation.js' );
- require_js( ANNOTATION_PATH.'/marginalia/rest-annotate.js' );
- require_js( ANNOTATION_PATH.'/smartquote.js' );
- require_js( ANNOTATION_PATH.'/summary.js' );
-
- $meta = "<link rel='stylesheet' type='text/css' href='".s(
ANNOTATION_PATH )."/summary-styles.php'/>\n";
-
- if ( null != $this->course && $this->course->category) {
- print_header($this->course->shortname.': '.get_string( 'summary_title',
ANNOTATION_STRINGS ), $this->course->fullname,
- '<a
href='.$CFG->wwwroot.'/course/view.php?id='.$this->course->id.'>'.$this->course->shortname.'</a>
-> '.$navtail,
- "", $meta, true, "", navmenu($this->course) );
- }
- elseif ( null != $this->course ) {
- print_header($this->course->shortname.': '.get_string( 'summary_title',
ANNOTATION_STRINGS ), $this->course->fullname,
- $navtail, "", $meta, true, "", navmenu($this->course) );
- }
- else
- print_header(get_string( 'summary_title', ANNOTATION_STRINGS ),
null, "$navtail", "", $meta, true, "", null );
-// echo $tagsHtml;
-
- if( isloggedin() )
- {
- $sannotationpath = s( ANNOTATION_PATH );
- echo "<script language='JavaScript' type='text/javascript'>\n"
- . "var annotationService = new
RestAnnotationService('$sannotationpath/annotate.php', {
csrfCookie: 'MoodleSessionTest' } );\n"
- . "window.annotationSummary = new AnnotationSummary(annotationService"
- .", '$swwwroot'"
- .", '".s($USER->username)."');\n"
- . "window.preferences = new Preferences( new
RestPreferenceService('$sannotationpath/user-preference.php' ) );\n"
- . "</script>\n";
- }
+ var $moodlemia;
+ var $summary;
+ var $first;
+ var $maxrecords = 50;
+ var $logger = null;
+
+ function annotation_summary_page( $first=1 )
+ {
+ $this->moodlemia = moodle_marginalia::get_instance( );
+ $this->first = $first;
+ $this->logger = $this->moodlemia->logger;
+
+ $this->summary = new annotation_summary_query(
annotation_summary_query::map_params( $_GET ) );
}
function show( )
{
+ global $CFG, $DB, $PAGE;
+
+ // Need to set page context
+ // In theory this should be CONTEXT_BLOCK for a block instance, but no
+ // instance exists: Marginalia is using the block plugin type, but
isn't really
+ // a block on the side of the screen. So we're forced to use
CONTEXT_SYSTEM.
+ // Bleargh.
+ //$block = $DB->get_record( 'block_instances', array( 'blockname'
=> 'block_marginalia' ) );
+ $PAGE->set_context( get_context_instance( CONTEXT_SYSTEM ) );
+
+ if ($CFG->forcelogin) {
+ require_login();
+ }
+
$this->errorpage = array_key_exists( 'error', $_GET ) ? $_GET[ 'error'
] : null;
- $summary = annotation_summary_query::from_params( );
-
- if ( null == $summary ) {
+ if ( null == $this->summary ) {
header( 'HTTP/1.1 400 Bad Request' );
echo '<h1>400 Bad Request</h1>';
}
- elseif ( ! MarginaliaHelper::isUrlSafe( $summary->url ) ) {
+ elseif ( ! MarginaliaHelper::isUrlSafe( $this->summary->url ) ) {
header( 'HTTP/1.1 400 Bad Request' );
echo '<h1>400 Bad Request</h1>Bad url parameter';
}
else {
- // Display individual annotations
- // Dunno if the range sorting is working
- $sql = $summary->sql( 'section_type, section_name, a.url, start_block,
start_word, start_char, end_block, end_word, end_char' );
- // echo "SQL: $sql\n"; // uncomment for debugging
- $annotations = get_records_sql( $sql );
-
+ $params = array( );
+ $sql = $this->summary->sql( $params );
+ //echo "SQL: $sql\n"; // uncomment for debugging
+ $annotations = $DB->get_records_sql( $sql, $params, $this->first - 1,
$this->maxrecords );
+
+ $params = array( );
+ $count_sql = $this->summary->count_sql( $params );
+ $annotation_count = $DB->count_records_sql( $count_sql, $params );
+
$format = array_key_exists( 'format', $_GET ) ? $_GET[ 'format'
] : 'html';
if ( 'atom' == $format )
- $this->show_atom( $summary, $annotations );
+ $this->show_atom( $annotations );
else
- $this->show_html( $summary, $annotations );
+ $this->show_html( $annotations, $annotation_count );
}
}
- function show_atom( $summary, $annotations )
+ function show_atom( $annotations )
{
global $CFG;
$annotationobjs = array();
foreach ( $annotations as $annotationrec )
- $annotationobjs[ ] = annotation_globals::record_to_annotation(
$annotationrec );
+ $annotationobjs[ ] = $this->moodlemia->record_to_annotation(
$annotationrec );
MarginaliaHelper::generateAnnotationFeed( $annotationobjs,
- annotation_globals::get_feed_tag_uri( ),
- MarginaliaHelper::getLastModified( $annotationObjs,
annotation_globals::get_install_date( ) ),
- annotation_globals::get_service_path( ),
- annotation_globals::get_host( ),
- $summary->get_feed_url( 'atom' ),
+ $this->moodlemia->get_feed_tag_uri( ),
+ MarginaliaHelper::getLastModified( $annotationobjs,
$this->moodlemia->get_install_date( ) ),
+ $this->moodlemia->get_service_path( ),
+ $this->moodlemia->get_host( ),
+ $this->summary->get_feed_url( 'atom' ),
$CFG->wwwroot );
}
- function show_html( $summary, $annotations )
- {
- global $CFG, $USER;
-
+ function show_html( $annotations, $annotation_count )
+ {
+ global $CFG, $USER, $PAGE, $OUTPUT, $DB;
+
+ $PAGE->set_url( $this->summary->summary_url( ) );
+
+ $blockpath = '/blocks/marginalia'; // was ANNOTATION_PATH
+ $PAGE->requires->css( $blockpath.'/summary-styles.php' );
+
+ // This loads way more than is needed here, but reduces code paths
+ // and hence bugs.
+ $moodlemia = moodle_marginalia::get_instance( );
+ $profile = $moodlemia->get_profile( $this->summary->summary_url( ) );
+ $profile->emit_requires( );
+ $PAGE->requires->js( $blockpath.'/summary.js', true );
+
// Get the course. This can't be passed as a GET parameter because this
URL could be via the
// Atom feed, and the Atom feed is generated exclusively by annotation
code which doesn't know
// that much about Moodle. So the handler has to query it based on a
discussion ID or the like.
$this->course = null;
- $this->courseid = $summary->handler->courseid;
+ $this->courseid = $this->summary->handler->courseid;
if ( null != $this->courseid ) {
- if (! $this->course = get_record( "course", "id", $this->courseid ) )
+ if (! $this->course = $DB->get_record( "course", array( 'id' =>
$this->courseid ) ) )
error( "Course ID is incorrect");
// Ok, now this is probably very wrong. If the user looks for
annotations within a course,
@@ -136,37 +152,73 @@
require_login( $this->course->id );
}
- // Keep for debugging:
- // echo "<h2>Query</h2><pre>".$query->sql( '
a.id' )."</pre>";
-
- // Show header
- $swwwroot = htmlspecialchars( $CFG->wwwroot );
-
- $this->show_header( );
-
+ // #geof#: not quite correct - should fetch the URL from the summary
object
+ $PAGE->set_url( '/mod/forum/summary.php' );
+ if ( null != $this->course )
+ {
+
$PAGE->set_title("$this->course->shortname: ".get_string( 'summary_title',
ANNOTATION_STRINGS ) );
+ $PAGE->set_heading($this->course->fullname);
+ }
+ else
+ {
+ $PAGE->set_title(get_string( 'summary_title', ANNOTATION_STRINGS ) );
+ $PAGE->set_heading( get_string( 'summary_title', ANNOTATION_STRINGS ) );
+ }
+
+ // #geof# Must change to split requires from inline JS:
+ if ( $this->logger && $this->logger->is_active())
+ $this->logger->header_html( );
+
+ if ( null != $this->course && $this->course->category)
+ $PAGE->navbar->add( $this->course->shortname,
$CFG->wwwroot.'/course/view.php?id='.$this->course->id );
+ $PAGE->navbar->add( get_string( 'summary_title', ANNOTATION_STRINGS ) );
+
+ echo $OUTPUT->header( );
+
+ $swwwroot = htmlspecialchars( $CFG->wwwroot );
+ if( isloggedin() )
+ {
+ $profile->emit_body( );
+/* $sannotationpath = s( ANNOTATION_PATH );
+ echo "<script language='JavaScript' type='text/javascript'>\n"
+ . "var annotationService = new
RestAnnotationService('$sannotationpath/annotate.php', "
+ . "{ csrfCookie: 'MoodleSession".$CFG->sessioncookie."',
noPutDelete: true } );\n"
+ . "window.annotationSummary = new AnnotationSummary('$swwwroot', {"
+ ." \n annotationService: annotationService"
+ .",\n userid: ".(int)$USER->id
+ .",\n useLog: ".($this->logger &&
$this->logger->is_active() ? 'true' : 'false' )
+ .",\n csrfCookie: 'MoodleSession".$CFG->sessioncookie."'"
+ ."} );\n"
+ . "window.preferences = new Preferences( new
RestPreferenceService('$sannotationpath/user-preference.php' ) );\n"
+ . "</script>\n";
+*/ }
+
+ // Needed later to determine whether a given annotation is a keyword
$keywords = isloggedin() ? annotation_keywords_db::list_keywords(
$USER->id ) : array( );
$keywordhash = array( );
for ( $i = 0; $i < count( $keywords ); ++$i ) {
$keyword = $keywords[ $i ];
$keywordhash[ $keyword->name ] = true;
}
-
+
// print search header
// * my annotations
// * shared annotations
// * instructor annotations
// * annotations of my work
- $username = $summary->user ? $summary->user->username : '';
- $ofusername = $summary->ofuser ? $summary->ofuser->username : '';
echo "<form id='annotation-search' method='get' action='summary.php'>\n";
echo "<fieldset>\n";
- echo "<label for='search-of'>".get_string( 'prompt_find',
ANNOTATION_STRINGS )."</label>\n";
- echo "<input type='hidden' name='search-of' id='search-of'
value='".s($ofusername)."'/>\n";
- echo "<input type='hidden' name='u' id='u' value='".s($username)."'/>\n";
- echo "<input type='text' id='search-text' name='q'
value='".s($summary->text)."'/>\n";
+ echo "<label for=''>".get_string( 'prompt_find', ANNOTATION_STRINGS
)."</label>\n";
+ if ( $this->summary->ofuser )
+ echo "<input type='hidden' name='search-of' id='search-of'
value='".s($this->moodlemia->fullname($this->summary->ofuser))."'/>\n";
+ if ( $this->summary->user )
+ echo "<input type='hidden' name='u' id='u'
value='".s($this->moodlemia->fullname($this->summary->user))."'/>\n";
+ echo "<input type='text' id='search-text' name='q'
value='".s($this->summary->text)."'/>\n";
echo "<input type='submit' value='".get_string( 'go' )."'/>\n";
- echo "<input type='hidden' name='url' value='".s($summary->url)."'/>\n";
- helpbutton( 'annotation_summary', get_string( 'summary_help',
ANNOTATION_STRINGS ), 'block_marginalia' );
+ echo "<input type='hidden' name='url'
value='".s($this->summary->url)."'/>\n";
+
+// echo $OUTPUT->help_icon( 'annotation_summary', ANNOTATION_STRINGS );
//'block_marginalia', get_string( 'summary_help', ANNOTATION_STRINGS ) );
+
echo "</fieldset>\n";
echo "</form>";
@@ -176,8 +228,11 @@
.get_string( 'summary_range_error', ANNOTATION_STRINGS )."</p>\n";
}
- echo '<p id="query">'.get_string( 'prompt_search_desc',
ANNOTATION_STRINGS )
- .' '.$summary->desc_with_links(null).":</p>\n";
+ $a = new object( );
+ $a->n = $annotations ? count( $annotations ) : 0;
+ $a->m = $annotation_count;
+ echo '<p id="query">'.get_string( 'prompt_search_desc',
ANNOTATION_STRINGS, $a )
+ .' '.$this->summary->desc_with_links(null).":</p>\n";
$cursection = null;
$cursectiontype = null;
@@ -192,6 +247,9 @@
$annotationa[ ] = $annotation;
$ncols = 6;
+
+ if ( AN_SUMMARY_ORDER_TIME == $this->summary->orderby )
+ $ncols += 1;
echo '<table cellspacing="0" class="annotations">'."\n";
for ( $annotationi = 0; $annotationi < count( $annotationa );
++$annotationi ) {
@@ -207,15 +265,15 @@
. "<a href='".s( $annotation->section_url )
."' title='".get_string( 'prompt_section', ANNOTATION_STRINGS, $a
)."'>"
.s( $annotation->section_name ) . "</a>";
- if ( $annotation->section_url != $summary->url ) {
- $tsummary = $summary->for_url( $annotation->section_url );
+ if ( $annotation->section_url != $this->summary->url ) {
+ $tsummary = $this->summary->derive( array( 'url' =>
$annotation->section_url ) );
$turl = $tsummary->summary_url( );
echo "<a class='zoom' title='".get_string( 'zoom_url_hover',
ANNOTATION_STRINGS, $annotation )."' href='".s( $turl
)."'>".AN_FILTERICON_HTML."</a>\n";
}
echo '</th></tr></thead>'."\n";
if ( AN_SUMMARYHEADINGSTOP )
- $this->show_column_headings( 'top' );
+ $this->show_column_headings( $this->summary, 'top' );
echo '<tbody>'."\n";
$cursection = $annotation->section_url;
@@ -247,22 +305,19 @@
echo "<th rowspan='$nrows'>";
$url = MarginaliaHelper::isUrlSafe( $url ) ? $url : '';
$a->row_type = $annotation->row_type;
- $a->author = $annotation->quote_author_fullname;
+ $a->author = $this->moodlemia->fullname2(
$annotation->quote_author_firstname, $annotation->quote_author_lastname );
echo "<a class='url' href='".s($url)."'
title='".get_string( 'prompt_row', ANNOTATION_STRINGS, $a)."'>";
echo s( $annotation->quote_title ) . '</a>';
- echo "<br/>by <span class='quote-author'>".s(
$annotation->quote_author_fullname )."</span>\n";
+ echo "<br/>by <span class='quote-author'>".
+ s( $a->author )."</span>\n";
// Link to filter only annotations by this user
- if ( ! $summary->ofuser || $annotation->quote_author_username !=
$summary->ofuser->username ) {
- $tuser = get_record( 'user', 'username',
$annotation->quote_author_username );
- if ( $tuser ) {
- $tsummary = $summary->for_ofuser( $tuser );
- $turl = $tsummary->summary_url( );
- $a->fullname = $annotation->quote_author_fullname;
- echo "<a class='zoom' title='".get_string( 'zoom_author_hover',
ANNOTATION_STRINGS, $a)
- ."' href='".s( $turl )."'>".AN_FILTERICON_HTML."</a>\n";
- }
+ if ( ! $this->summary->ofuser ||
$annotation->quote_author_username != $this->summary->ofuser->username ) {
+ $tsummary = $this->summary->derive( array( 'ofuserid' =>
$annotation->quote_author_id ) );
+ $turl = $tsummary->summary_url( );
+ $a->fullname = $this->moodlemia->fullname2(
$annotation->quote_author_firstname, $annotation->quote_author_lastname );
+ echo $this->zoom_link( $tsummary->summary_url( ),
get_string( 'zoom_author_hover', ANNOTATION_STRINGS, $a) );
}
echo "</th>\n";
}
@@ -281,16 +336,18 @@
if ( ! $annotation->note )
echo ' ';
else
- p( $annotation->note );
-
- if ( ! $summary->exactmatch && array_key_exists( $annotation->note,
$keywordhash ) ) {
- $tsummary = $summary->for_text( $annotation->note, true );
- echo "<a class='zoom' title='"
- .get_string( 'zoom_match_hover', ANNOTATION_STRINGS )
- ."' href='".s( $tsummary->summary_url( )
)."'>".AN_FILTERICON_HTML."</a>\n";
+ echo s( $annotation->note );
+
+ if ( ! $this->summary->exactmatch && array_key_exists(
$annotation->note, $keywordhash ) ) {
+ $tsummary = $this->summary->derive( array( 'text' =>
$annotation->note, 'exactmatch' => true ) );
+ echo ' '.$this->zoom_link( $tsummary->summary_url( ),
get_string( 'zoom_match_hover', ANNOTATION_STRINGS ) );
}
echo "</td>\n";
+
+ // Show annotation time (if requested)
+ if ( AN_SUMMARY_ORDER_TIME == $this->summary->orderby )
+ echo "<td class='modified'>".s(date('Y-m-d G:i',
$annotation->modified))."</td>\n";
// Show edit controls or the user who created the annotation
echo "<td class='user".( isloggedin() && $annotation->userid ==
$USER->id ? ' isloginuser' : '')."'>\n";
@@ -306,16 +363,12 @@
// Controls for current user
if ( isloggedin() && $annotation->userid == $USER->id ) {
- $AN_SUN_SYMBOL = '○';
- $AN_MOON_SYMBOL = '◆';
- $access_str = AN_ACCESS_PUBLIC &
$annotation->access_perms ? 'public' : 'private';
- echo "<button class='share-button access-$access_str'
onclick='window.annotationSummary.shareAnnotationPublicPrivate(this,$annotation->id);'>"
- .($annotation->access_perms & AN_ACCESS_PUBLIC ? $AN_SUN_SYMBOL :
$AN_MOON_SYMBOL )."</button>";
- echo "<button class='delete-button'
onclick='window.annotationSummary.deleteAnnotation($annotation->id);'>x</button>\n";
+ $delid = s( 'del'.$annotation->id );
+ echo "<button class='delete-button' id='$delid'>x</button>\n";
}
// User name (or "me" for current user)
- $displayusername = s( $annotation->username );
+ $displayusername = s( $this->moodlemia->fullname2(
$annotation->firstname, $annotation->lastname ) );
$hiddenusername = '';
$class = 'user-name';
@@ -330,19 +383,15 @@
echo "<a class='$class'
onclick='setAnnotationUser(\"".s($annotation->userid)."\")'
href='".s($url)."'>"
."$displayusername</a>\n";
else
- echo "<span class='$class'>$displayUserName</span>\n";
+ echo "<span class='$class'>$displayusername</span>\n";
echo $hiddenusername;
// Link to filter only annotations by this user
- if ( ! $summary->user || $annotation->userid !=
$summary->user->username ) {
- $tuser = get_record( 'user', 'username', $annotation->username );
- if ( $tuser ) {
- $tsummary = $summary->for_user( $tuser );
- $turl = $tsummary->summary_url( );
- $a->fullname = $annotation->fullname;
- echo "<a class='zoom' title='".get_string( 'zoom_user_hover',
ANNOTATION_STRINGS, $a)
- ."' href='".s($turl)."'>".AN_FILTERICON_HTML."</a>\n";
- }
+ if ( ! $this->summary->user || $annotation->userid !=
$this->summary->user->username ) {
+ $tsummary = $this->summary->derive( array( 'userid' =>
$annotation->userid) );
+ $turl = $tsummary->summary_url( );
+ $a->fullname = $this->moodlemia->fullname2( $annotation->firstname,
$annotation->lastname );
+ echo $this->zoom_link( $tsummary->summary_url( ),
get_string( 'zoom_user_hover', ANNOTATION_STRINGS, $a) );
}
echo "</td>\n";
@@ -353,10 +402,19 @@
echo "<script type='text/javascript'>\n";
for ( $annotationi = 0; $annotationi < count( $annotationa );
++$annotationi ) {
$annotation = $annotationa[ $annotationi ];
- $sqid = s( 'sq'.$annotation->id );
- $tuserid = s( $annotation->userid );
- echo " addEvent(document.getElementById('$sqid'),'click',function() {"
- ." window.annotationSummary.quote('$sqid','$tuserid'); } );";
+ if ( AN_USESMARTQUOTE )
+ {
+ $sqid = s( 'sq'.$annotation->id );
+ $tuserid = s( $annotation->userid );
+ echo " addEvent(document.getElementById('$sqid'),'click',function()
{"
+ ." window.annotationSummary.quote('$sqid','$tuserid'); } );";
+ }
+ if ( isloggedin() && $annotation->userid == $USER->id )
+ {
+ $delid = s( 'del'.$annotation->id );
+ echo " addEvent(document.getElementById('$delid'),'click',function()
{"
+ ."
window.annotationSummary.deleteAnnotation('$delid',".(int)$annotation->id.");
} );\n";
+ }
}
echo "</script>\n";
@@ -369,41 +427,90 @@
echo "</table>\n";
}
+ marginalia_summary_lib::show_result_pages( $this->first,
$annotation_count, $this->maxrecords,
$this->summary->summary_url('{first}') );
+/*
+ // Show the list of result pages
+ $npages = ceil( $annotation_count / $this->maxrecords );
+ if ( $npages > 1 )
+ {
+ $this_page = 1 + floor( ( $this->first - 1 ) / $this->maxrecords );
+ echo "<ol class='result-pages'>\n";
+ for ( $i = 1; $i <= $npages; ++$i )
+ {
+ if ( $i == $this_page )
+ echo " <li>".$i."</li>\n";
+ else
+ echo " <li><a
href='".s($this->summary->summary_url('{first}')."'>".$i."</a></li>\n";
+ }
+ echo "</ol>\n";
+ }
+*/
//$moodlePath = getMoodlePath( );
+ // Link for sorting by date or document order
+ if ( $this->summary->orderby == AN_SUMMARY_ORDER_DOCUMENT )
+ {
+ $tsummary = $this->summary->derive( array( 'orderby' =>
AN_SUMMARY_ORDER_TIME ) );
+ echo "<p><a
href='".s($tsummary->summary_url())."'>".get_string( 'summary_sort_time',
ANNOTATION_STRINGS )."</a></p>\n";
+ }
+ else
+ {
+ $tsummary = $this->summary->derive( array( 'orderby' =>
AN_SUMMARY_ORDER_DOCUMENT ) );
+ echo "<p><a
href='".s($tsummary->summary_url())."'>".get_string( 'summary_sort_document',
ANNOTATION_STRINGS )."</a></p>\n";
+ }
+
+/* Feed removed because Moodle should require a login for it to be of much
+ use, and the feed reader would then need to authenticate. So it's likely
+ to be more frustrating than useful.
+
// Provide a feed URL. I don't know how to do authentication for the
feed, so for now
// if a login is required I won't include the feature.
if ( ! ANNOTATION_REQUIRE_USER ) {
- $turl = $summary->get_feed_url( 'atom' );
+ $tsummary = $this->summary->derive( array( 'orderby' =>
AN_SUMMARY_ORDER_TIME ) );
+ $turl = $tsummary->get_feed_url( 'atom' );
echo "<p class='feed' title='".get_string( 'atom_feed',
ANNOTATION_STRINGS )
."'><a href='".s($turl)."'><img border='0' alt='"
.get_string( 'atom_feed', ANNOTATION_STRINGS )."' src='".s(
$CFG->wwwroot )."/pix/i/rss.gif'/>"
. '</a> '.get_string( 'atom_feed_desc', ANNOTATION_STRINGS )."</p>\n";
}
-
- print_footer($this->course);
+*/
+ echo "<p><a
href='help.php?component=block_marginalia&topic=annotation_summary'>"
+ .get_string( 'annotation_summary_help_link', ANNOTATION_STRINGS
).'</a></p>';
+
+ $OUTPUT->footer($this->course);
$logurl = $_SERVER[ 'REQUEST_URI' ];
$urlparts = parse_url( $logurl );
$logurl = array_key_exists( 'query', $urlparts ) ? $urlparts[ 'query'
] : null;
- add_to_log(
null, 'annotation', 'summary', 'summary.php'.($logurl?'?'.$logurl:''),
$summary->desc(null) );
+
$this->moodlemia->moodle_log( 'summary', 'summary.php'.($logurl?'?'.$logurl:''),
$this->summary->desc(null) );
+
+ // Marginalia logging
+ if ( $this->logger && $this->logger->is_active() )
+ $this->logger->summarizeAnnotations($this->summary->summary_url(),
$this->summary->desc());
}
function show_column_headings( $className )
{
echo "<thead class='labels $className'>\n"
- ." <th>Source & Author</th>\n"
- ." <th>Highlighted Text</th>\n"
- ." <th>Margin Note</th>\n"
- ." <th>User</th>\n"
+ ." <th>".get_string('summary_source_head',
ANNOTATION_STRINGS)."</th>\n"
+ ." <th>".get_string('summary_quote_head', ANNOTATION_STRINGS)."</th>\n"
+ ." <th>".get_string('summary_note_head', ANNOTATION_STRINGS)."</th>\n";
+ if ( AN_SUMMARY_ORDER_TIME == $this->summary->orderby )
+ echo " <th>".get_string('summary_time_head',
ANNOTATION_STRINGS)."</th>\n";
+ echo " <th>".get_string('summary_user_head',
ANNOTATION_STRINGS)."</th>\n"
."</thead>\n";
}
- function get_summary_link( $text, $title, $summary )
- {
- $turl = $summary->summary_url( );
+ function get_summary_link( $text, $title )
+ {
+ $turl = $this->summary->summary_url( );
return '<a href="'.s( $turl ).'" title="'.s($title).'">'.s( $text
).'</a>';
}
+
+ function zoom_link( $url, $title )
+ {
+ return "<a class='zoom' title='".s($title)."'
href='".s($url)."'>".AN_FILTERICON_HTML."</a>\n";
+ }
}
/*
@@ -439,7 +546,8 @@
echo 'grr';
}
else {
- $summarypage = new annotation_summary_page( );
+ $first = array_key_exists( 'first', $_GET ) ? (int)$_GET[ 'first' ] : 1;
+ $summarypage = new annotation_summary_page( $first );
$summarypage->show( );
}
-?>
+
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/user-preference.php Wed May 30
14:29:48 2012
+++ /moodle/trunk/moodle/blocks/marginalia/user-preference.php Wed Jun 20
22:56:48 2012
@@ -1,42 +1,86 @@
<?php
- require_once("../../config.php");
-
- $url = $_SERVER[ 'REQUEST_URI' ];
-
- $prefname = array_key_exists( 'name', $_GET ) ? $_GET[ 'name' ] : null;
-
- switch ( $_SERVER[ 'REQUEST_METHOD' ] )
- {
- case 'GET':
- $value = get_user_preferences( $prefname, null );
- header( 'Content-type: application/xml' );
- // should be utf-8
- echo "<?xml version='1.0'?>\n";
- echo "<preferences>\n";
- if ( null !== $value )
- echo " <setting url='$url' name='" . htmlspecialchars( $prefname
) . "'>" . htmlspecialchars( $value ) . "</setting>\n";
- else
- echo " <setting url='$url' name='" . htmlspecialchars( $prefname
) . "'/>\n";
- echo "</preferences>";
- break;
-
- case 'POST':
- // Check that it exists first so that malicious users can't fill the
database
- // with meaningless preferences.
- if ( get_user_preferences( $prefname, null ) == null )
- header( 'HTTP/1.1 403 Forbidden' );
- else
- {
- $value = $_POST[ 'value' ];
- set_user_preference( $prefname, $value);
- header( 'HTTP/1.1 204 Preference Set' );
- $prefs = get_user_preferences( $prefname, null );
+/*
+ * user-preference.php
+ * Gets and sets preferences. Used by Marginalia.
+ *
+ * Marginalia has been developed with funding and support from
+ * BC Campus, Simon Fraser University, and the Government of
+ * Canada, the UNDESA Africa i-Parliaments Action Plan, and
+ * units and individuals within those organizations. Many
+ * thanks to all of them. See CREDITS.html for details.
+ * Copyright (C) 2005-2011 Geoffrey Glass; the United Nations
+ *
http://www.geof.net/code/annotation
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
+ *
+ * $Id$
+ */
+
+require_once("../../config.php");
+require_once( 'moodle_marginalia.php' );
+
+global $USER, $DB;
+
+$logblock = $DB->get_record('block', array( 'name' => 'marginalia_log' ) );
+$logger = null;
+if ( $logblock )
+{
+ require_once( $CFG->dirroot.'/blocks/marginalia_log/lib.php' );
+ $logger = new marginalia_log();
+}
+
+$url = $_SERVER[ 'REQUEST_URI' ];
+
+$prefname = array_key_exists( 'name', $_GET ) ? $_GET[ 'name' ] : null;
+
+switch ( $_SERVER[ 'REQUEST_METHOD' ] )
+{
+ case 'GET':
+ $value = get_user_preferences( $prefname, null );
+ header( 'Content-type: application/xml' );
+ // should be utf-8
+ echo "<?xml version='1.0'?>\n";
+ echo "<preferences>\n";
+ if ( null !== $value )
+ echo " <setting url='$url' name='" . htmlspecialchars( $prefname
) . "'>" . htmlspecialchars( $value ) . "</setting>\n";
+ else
+ echo " <setting url='$url' name='" . htmlspecialchars( $prefname
) . "'/>\n";
+ echo "</preferences>";
+ break;
+
+ case 'POST':
+ // Check that it exists first so that malicious users can't fill the
database
+ // with meaningless preferences.
+ if ( get_user_preferences( $prefname, null ) == null )
+ header( 'HTTP/1.1 403 Forbidden' );
+ else
+ {
+ $value = $_POST[ 'value' ];
+ set_user_preference( $prefname, $value);
+ header( 'HTTP/1.1 204 Preference Set' );
+ $prefs = get_user_preferences( $prefname, null );
// echo htmlspecialchars( $prefName ) . '=' . htmlspecialchars( $value
);
- }
- break;
-
- default:
- header( 'HTTP/1.1 400 Bad Request' );
- }
-?>
+
+ // Marginalia logging
+ if ( $logger )
+ $logger->setPreference( $prefname, $value );
+ }
+ break;
+
+ default:
+ header( 'HTTP/1.1 400 Bad Request' );
+}
+
=======================================
--- /moodle/trunk/moodle/blocks/marginalia/version.php Wed May 30 14:29:48
2012
+++ /moodle/trunk/moodle/blocks/marginalia/version.php Wed Jun 20 22:56:48
2012
@@ -1,2 +1,3 @@
<?php
- $version = 2009031600;
+ $plugin->version =
2012041200;
+ $plugin->requires = 2010112400;