diff --git a/symposion/__init__.py b/symposion/__init__.py index 6046eebc..ea02d4ee 100644 --- a/symposion/__init__.py +++ b/symposion/__init__.py @@ -1 +1 @@ -__version__ = "1.0b1.dev11" +__version__ = "1.0b1.dev13" diff --git a/symposion/proposals/views.py b/symposion/proposals/views.py index e7d65f44..fe7aec96 100644 --- a/symposion/proposals/views.py +++ b/symposion/proposals/views.py @@ -187,6 +187,16 @@ def proposal_edit(request, pk): form = form_class(request.POST, instance=proposal) if form.is_valid(): form.save() + if hasattr(proposal, "reviews"): + for review in proposal.reviews.distinct("user"): + ctx = { + "user": request.user, + "proposal": proposal, + } + send_email( + [review.user.email], "proposal_updated", + context=ctx + ) messages.success(request, "Proposal updated.") return redirect("proposal_detail", proposal.pk) else: diff --git a/symposion/reviews/views.py b/symposion/reviews/views.py index 683b6a1a..9a60c152 100644 --- a/symposion/reviews/views.py +++ b/symposion/reviews/views.py @@ -275,6 +275,9 @@ def review_delete(request, pk): @login_required def review_status(request, section_slug=None, key=None): + if not request.user.has_perm("reviews.can_review_%s" % section_slug): + return access_not_permitted(request) + VOTE_THRESHOLD = settings.SYMPOSION_VOTE_THRESHOLD ctx = { @@ -298,14 +301,18 @@ def review_status(request, section_slug=None, key=None): # proposals with fewer than VOTE_THRESHOLD reviews "too_few": queryset.filter(result__vote_count__lt=VOTE_THRESHOLD).order_by("result__vote_count"), } - + admin = request.user.has_perm("reviews.can_manage_%s" % section_slug) + for status in proposals: + if key and key != status: + continue + proposals[status] = list(proposals_generator(request, proposals[status], check_speaker=not admin)) + if key: ctx.update({ "key": key, - "proposals": proposals_generator(request, proposals[key], check_speaker=not admin), - "proposal_count": proposals[key].count(), + "proposals": proposals[key], }) else: ctx["proposals"] = proposals diff --git a/symposion/static/datatables/js/dataTables.bootstrap.js b/symposion/static/datatables/js/dataTables.bootstrap.js new file mode 100644 index 00000000..b58e5f77 --- /dev/null +++ b/symposion/static/datatables/js/dataTables.bootstrap.js @@ -0,0 +1,138 @@ +$(function() { + /* Default class modification */ + $.extend( $.fn.dataTableExt.oStdClasses, { + "sWrapper": "dataTables_wrapper form-inline" + } ); + + /* API method to get paging information */ + $.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings ) + { + return { + "iStart": oSettings._iDisplayStart, + "iEnd": oSettings.fnDisplayEnd(), + "iLength": oSettings._iDisplayLength, + "iTotal": oSettings.fnRecordsTotal(), + "iFilteredTotal": oSettings.fnRecordsDisplay(), + "iPage": Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ), + "iTotalPages": Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength ) + }; + } + + /* Bootstrap style pagination control */ + $.extend( $.fn.dataTableExt.oPagination, { + "bootstrap": { + "fnInit": function( oSettings, nPaging, fnDraw ) { + var oLang = oSettings.oLanguage.oPaginate; + var fnClickHandler = function ( e ) { + e.preventDefault(); + if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) { + fnDraw( oSettings ); + } + }; + + $(nPaging).addClass('pagination').append( + '
Copied '+len+' row'+plural+' to the clipboard.
', + 1500 + ); + } + } ), + + "pdf": $.extend( {}, TableTools.buttonBase, { + "sAction": "flash_pdf", + "sNewLine": "\n", + "sFileName": "*.pdf", + "sButtonClass": "DTTT_button_pdf", + "sButtonText": "PDF", + "sPdfOrientation": "portrait", + "sPdfSize": "A4", + "sPdfMessage": "", + "fnClick": function( nButton, oConfig, flash ) { + this.fnSetText( flash, + "title:"+ this.fnGetTitle(oConfig) +"\n"+ + "message:"+ oConfig.sPdfMessage +"\n"+ + "colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+ + "orientation:"+ oConfig.sPdfOrientation +"\n"+ + "size:"+ oConfig.sPdfSize +"\n"+ + "--/TableToolsOpts--\n" + + this.fnGetTableData(oConfig) + ); + } + } ), + + "print": $.extend( {}, TableTools.buttonBase, { + "sInfo": "Please use your browser's print function to "+
+ "print this table. Press escape when finished.",
+ "sMessage": null,
+ "bShowAll": true,
+ "sToolTip": "View print view",
+ "sButtonClass": "DTTT_button_print",
+ "sButtonText": "Print",
+ "fnClick": function ( nButton, oConfig ) {
+ this.fnPrint( true, oConfig );
+ }
+ } ),
+
+ "text": $.extend( {}, TableTools.buttonBase ),
+
+ "select": $.extend( {}, TableTools.buttonBase, {
+ "sButtonText": "Select button",
+ "fnSelect": function( nButton, oConfig ) {
+ if ( this.fnGetSelected().length !== 0 ) {
+ $(nButton).removeClass( this.classes.buttons.disabled );
+ } else {
+ $(nButton).addClass( this.classes.buttons.disabled );
+ }
+ },
+ "fnInit": function( nButton, oConfig ) {
+ $(nButton).addClass( this.classes.buttons.disabled );
+ }
+ } ),
+
+ "select_single": $.extend( {}, TableTools.buttonBase, {
+ "sButtonText": "Select button",
+ "fnSelect": function( nButton, oConfig ) {
+ var iSelected = this.fnGetSelected().length;
+ if ( iSelected == 1 ) {
+ $(nButton).removeClass( this.classes.buttons.disabled );
+ } else {
+ $(nButton).addClass( this.classes.buttons.disabled );
+ }
+ },
+ "fnInit": function( nButton, oConfig ) {
+ $(nButton).addClass( this.classes.buttons.disabled );
+ }
+ } ),
+
+ "select_all": $.extend( {}, TableTools.buttonBase, {
+ "sButtonText": "Select all",
+ "fnClick": function( nButton, oConfig ) {
+ this.fnSelectAll();
+ },
+ "fnSelect": function( nButton, oConfig ) {
+ if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) {
+ $(nButton).addClass( this.classes.buttons.disabled );
+ } else {
+ $(nButton).removeClass( this.classes.buttons.disabled );
+ }
+ }
+ } ),
+
+ "select_none": $.extend( {}, TableTools.buttonBase, {
+ "sButtonText": "Deselect all",
+ "fnClick": function( nButton, oConfig ) {
+ this.fnSelectNone();
+ },
+ "fnSelect": function( nButton, oConfig ) {
+ if ( this.fnGetSelected().length !== 0 ) {
+ $(nButton).removeClass( this.classes.buttons.disabled );
+ } else {
+ $(nButton).addClass( this.classes.buttons.disabled );
+ }
+ },
+ "fnInit": function( nButton, oConfig ) {
+ $(nButton).addClass( this.classes.buttons.disabled );
+ }
+ } ),
+
+ "ajax": $.extend( {}, TableTools.buttonBase, {
+ "sAjaxUrl": "/xhr.php",
+ "sButtonText": "Ajax button",
+ "fnClick": function( nButton, oConfig ) {
+ var sData = this.fnGetTableData(oConfig);
+ $.ajax( {
+ "url": oConfig.sAjaxUrl,
+ "data": [
+ { "name": "tableData", "value": sData }
+ ],
+ "success": oConfig.fnAjaxComplete,
+ "dataType": "json",
+ "type": "POST",
+ "cache": false,
+ "error": function () {
+ alert( "Error detected when sending table data to server" );
+ }
+ } );
+ },
+ "fnAjaxComplete": function( json ) {
+ alert( 'Ajax complete' );
+ }
+ } ),
+
+ "div": $.extend( {}, TableTools.buttonBase, {
+ "sAction": "div",
+ "sTag": "div",
+ "sButtonClass": "DTTT_nonbutton",
+ "sButtonText": "Text button"
+ } ),
+
+ "collection": $.extend( {}, TableTools.buttonBase, {
+ "sAction": "collection",
+ "sButtonClass": "DTTT_button_collection",
+ "sButtonText": "Collection",
+ "fnClick": function( nButton, oConfig ) {
+ this._fnCollectionShow(nButton, oConfig);
+ }
+ } )
+};
+/*
+ * on* callback parameters:
+ * 1. node - button element
+ * 2. object - configuration object for this button
+ * 3. object - ZeroClipboard reference (flash button only)
+ * 4. string - Returned string from Flash (flash button only - and only on 'complete')
+ */
+
+
+
+/**
+ * @namespace Classes used by TableTools - allows the styles to be override easily.
+ * Note that when TableTools initialises it will take a copy of the classes object
+ * and will use its internal copy for the remainder of its run time.
+ */
+TableTools.classes = {
+ "container": "DTTT_container",
+ "buttons": {
+ "normal": "DTTT_button",
+ "disabled": "DTTT_disabled"
+ },
+ "collection": {
+ "container": "DTTT_collection",
+ "background": "DTTT_collection_background",
+ "buttons": {
+ "normal": "DTTT_button",
+ "disabled": "DTTT_disabled"
+ }
+ },
+ "select": {
+ "table": "DTTT_selectable",
+ "row": "DTTT_selected"
+ },
+ "print": {
+ "body": "DTTT_Print",
+ "info": "DTTT_print_info",
+ "message": "DTTT_PrintMessage"
+ }
+};
+
+
+/**
+ * @namespace ThemeRoller classes - built in for compatibility with DataTables'
+ * bJQueryUI option.
+ */
+TableTools.classes_themeroller = {
+ "container": "DTTT_container ui-buttonset ui-buttonset-multi",
+ "buttons": {
+ "normal": "DTTT_button ui-button ui-state-default"
+ },
+ "collection": {
+ "container": "DTTT_collection ui-buttonset ui-buttonset-multi"
+ }
+};
+
+
+/**
+ * @namespace TableTools default settings for initialisation
+ */
+TableTools.DEFAULTS = {
+ "sSwfPath": "media/swf/copy_csv_xls_pdf.swf",
+ "sRowSelect": "none",
+ "sSelectedClass": null,
+ "fnPreRowSelect": null,
+ "fnRowSelected": null,
+ "fnRowDeselected": null,
+ "aButtons": [ "copy", "csv", "xls", "pdf", "print" ],
+ "oTags": {
+ "container": "div",
+ "button": "a", // We really want to use buttons here, but Firefox and IE ignore the
+ // click on the Flash element in the button (but not mouse[in|out]).
+ "liner": "span",
+ "collection": {
+ "container": "div",
+ "button": "a",
+ "liner": "span"
+ }
+ }
+};
+
+
+/**
+ * Name of this class
+ * @constant CLASS
+ * @type String
+ * @default TableTools
+ */
+TableTools.prototype.CLASS = "TableTools";
+
+
+/**
+ * TableTools version
+ * @constant VERSION
+ * @type String
+ * @default See code
+ */
+TableTools.VERSION = "2.1.3";
+TableTools.prototype.VERSION = TableTools.VERSION;
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Initialisation
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+/*
+ * Register a new feature with DataTables
+ */
+if ( typeof $.fn.dataTable == "function" &&
+ typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
+ $.fn.dataTableExt.fnVersionCheck('1.9.0') )
+{
+ $.fn.dataTableExt.aoFeatures.push( {
+ "fnInit": function( oDTSettings ) {
+ var oOpts = typeof oDTSettings.oInit.oTableTools != 'undefined' ?
+ oDTSettings.oInit.oTableTools : {};
+
+ var oTT = new TableTools( oDTSettings.oInstance, oOpts );
+ TableTools._aInstances.push( oTT );
+
+ return oTT.dom.container;
+ },
+ "cFeature": "T",
+ "sFeature": "TableTools"
+ } );
+}
+else
+{
+ alert( "Warning: TableTools 2 requires DataTables 1.9.0 or newer - www.datatables.net/download");
+}
+
+$.fn.DataTable.TableTools = TableTools;
+
+})(jQuery, window, document);
diff --git a/symposion/static/tabletools/js/TableTools.min.js b/symposion/static/tabletools/js/TableTools.min.js
new file mode 100644
index 00000000..2440a36d
--- /dev/null
+++ b/symposion/static/tabletools/js/TableTools.min.js
@@ -0,0 +1,76 @@
+// Simple Set Clipboard System
+// Author: Joseph Huckaby
+var ZeroClipboard_TableTools={version:"1.0.4-TableTools2",clients:{},moviePath:"",nextId:1,$:function(a){"string"==typeof a&&(a=document.getElementById(a));a.addClass||(a.hide=function(){this.style.display="none"},a.show=function(){this.style.display=""},a.addClass=function(a){this.removeClass(a);this.className+=" "+a},a.removeClass=function(a){this.className=this.className.replace(RegExp("\\s*"+a+"\\s*")," ").replace(/^\s+/,"").replace(/\s+$/,"")},a.hasClass=function(a){return!!this.className.match(RegExp("\\s*"+
+a+"\\s*"))});return a},setMoviePath:function(a){this.moviePath=a},dispatch:function(a,b,c){(a=this.clients[a])&&a.receiveEvent(b,c)},register:function(a,b){this.clients[a]=b},getDOMObjectPosition:function(a){var b={left:0,top:0,width:a.width?a.width:a.offsetWidth,height:a.height?a.height:a.offsetHeight};""!=a.style.width&&(b.width=a.style.width.replace("px",""));""!=a.style.height&&(b.height=a.style.height.replace("px",""));for(;a;)b.left+=a.offsetLeft,b.top+=a.offsetTop,a=a.offsetParent;return b},
+Client:function(a){this.handlers={};this.id=ZeroClipboard_TableTools.nextId++;this.movieId="ZeroClipboard_TableToolsMovie_"+this.id;ZeroClipboard_TableTools.register(this.id,this);a&&this.glue(a)}};
+ZeroClipboard_TableTools.Client.prototype={id:0,ready:!1,movie:null,clipText:"",fileName:"",action:"copy",handCursorEnabled:!0,cssEffects:!0,handlers:null,sized:!1,glue:function(a,b){this.domElement=ZeroClipboard_TableTools.$(a);var c=99;this.domElement.style.zIndex&&(c=parseInt(this.domElement.style.zIndex)+1);var d=ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);this.div=document.createElement("div");var e=this.div.style;e.position="absolute";e.left="0px";e.top="0px";e.width=d.width+
+"px";e.height=d.height+"px";e.zIndex=c;"undefined"!=typeof b&&""!=b&&(this.div.title=b);0!=d.width&&0!=d.height&&(this.sized=!0);this.domElement&&(this.domElement.appendChild(this.div),this.div.innerHTML=this.getHTML(d.width,d.height))},positionElement:function(){var a=ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement),b=this.div.style;b.position="absolute";b.width=a.width+"px";b.height=a.height+"px";0!=a.width&&0!=a.height&&(this.sized=!0,b=this.div.childNodes[0],b.width=a.width,b.height=
+a.height)},getHTML:function(a,b){var c="",d="id="+this.id+"&width="+a+"&height="+b;if(navigator.userAgent.match(/MSIE/))var e=location.href.match(/^https/i)?"https://":"http://",c=c+('');else c+='';return c},hide:function(){this.div&&(this.div.style.left="-2000px")},show:function(){this.reposition()},destroy:function(){if(this.domElement&&this.div){this.hide();this.div.innerHTML="";var a=document.getElementsByTagName("body")[0];try{a.removeChild(this.div)}catch(b){}this.div=this.domElement=null}},reposition:function(a){a&&((this.domElement=ZeroClipboard_TableTools.$(a))||this.hide());if(this.domElement&&this.div){var a=ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement),
+b=this.div.style;b.left=""+a.left+"px";b.top=""+a.top+"px"}},clearText:function(){this.clipText="";this.ready&&this.movie.clearText()},appendText:function(a){this.clipText+=a;this.ready&&this.movie.appendText(a)},setText:function(a){this.clipText=a;this.ready&&this.movie.setText(a)},setCharSet:function(a){this.charSet=a;this.ready&&this.movie.setCharSet(a)},setBomInc:function(a){this.incBom=a;this.ready&&this.movie.setBomInc(a)},setFileName:function(a){this.fileName=a;this.ready&&this.movie.setFileName(a)},
+setAction:function(a){this.action=a;this.ready&&this.movie.setAction(a)},addEventListener:function(a,b){a=a.toString().toLowerCase().replace(/^on/,"");this.handlers[a]||(this.handlers[a]=[]);this.handlers[a].push(b)},setHandCursor:function(a){this.handCursorEnabled=a;this.ready&&this.movie.setHandCursor(a)},setCSSEffects:function(a){this.cssEffects=!!a},receiveEvent:function(a,b){a=a.toString().toLowerCase().replace(/^on/,"");switch(a){case "load":this.movie=document.getElementById(this.movieId);
+if(!this.movie){var c=this;setTimeout(function(){c.receiveEvent("load",null)},1);return}if(!this.ready&&navigator.userAgent.match(/Firefox/)&&navigator.userAgent.match(/Windows/)){c=this;setTimeout(function(){c.receiveEvent("load",null)},100);this.ready=!0;return}this.ready=!0;this.movie.clearText();this.movie.appendText(this.clipText);this.movie.setFileName(this.fileName);this.movie.setAction(this.action);this.movie.setCharSet(this.charSet);this.movie.setBomInc(this.incBom);this.movie.setHandCursor(this.handCursorEnabled);
+break;case "mouseover":this.domElement&&this.cssEffects&&this.recoverActive&&this.domElement.addClass("active");break;case "mouseout":this.domElement&&this.cssEffects&&(this.recoverActive=!1,this.domElement.hasClass("active")&&(this.domElement.removeClass("active"),this.recoverActive=!0));break;case "mousedown":this.domElement&&this.cssEffects&&this.domElement.addClass("active");break;case "mouseup":this.domElement&&this.cssEffects&&(this.domElement.removeClass("active"),this.recoverActive=!1)}if(this.handlers[a])for(var d=
+0,e=this.handlers[a].length;d Copied "+a+" row"+(1==a?"":"s")+" to the clipboard. Please use your browser's print function to print this table. Press escape when finished.",sMessage:null,bShowAll:!0,sToolTip:"View print view",sButtonClass:"DTTT_button_print",sButtonText:"Print",fnClick:function(a,b){this.fnPrint(!0,b)}}),text:e.extend({},TableTools.buttonBase),select:e.extend({},
+TableTools.buttonBase,{sButtonText:"Select button",fnSelect:function(a){0!==this.fnGetSelected().length?e(a).removeClass(this.classes.buttons.disabled):e(a).addClass(this.classes.buttons.disabled)},fnInit:function(a){e(a).addClass(this.classes.buttons.disabled)}}),select_single:e.extend({},TableTools.buttonBase,{sButtonText:"Select button",fnSelect:function(a){1==this.fnGetSelected().length?e(a).removeClass(this.classes.buttons.disabled):e(a).addClass(this.classes.buttons.disabled)},fnInit:function(a){e(a).addClass(this.classes.buttons.disabled)}}),
+select_all:e.extend({},TableTools.buttonBase,{sButtonText:"Select all",fnClick:function(){this.fnSelectAll()},fnSelect:function(a){this.fnGetSelected().length==this.s.dt.fnRecordsDisplay()?e(a).addClass(this.classes.buttons.disabled):e(a).removeClass(this.classes.buttons.disabled)}}),select_none:e.extend({},TableTools.buttonBase,{sButtonText:"Deselect all",fnClick:function(){this.fnSelectNone()},fnSelect:function(a){0!==this.fnGetSelected().length?e(a).removeClass(this.classes.buttons.disabled):e(a).addClass(this.classes.buttons.disabled)},
+fnInit:function(a){e(a).addClass(this.classes.buttons.disabled)}}),ajax:e.extend({},TableTools.buttonBase,{sAjaxUrl:"/xhr.php",sButtonText:"Ajax button",fnClick:function(a,b){var c=this.fnGetTableData(b);e.ajax({url:b.sAjaxUrl,data:[{name:"tableData",value:c}],success:b.fnAjaxComplete,dataType:"json",type:"POST",cache:!1,error:function(){alert("Error detected when sending table data to server")}})},fnAjaxComplete:function(){alert("Ajax complete")}}),div:e.extend({},TableTools.buttonBase,{sAction:"div",
+sTag:"div",sButtonClass:"DTTT_nonbutton",sButtonText:"Text button"}),collection:e.extend({},TableTools.buttonBase,{sAction:"collection",sButtonClass:"DTTT_button_collection",sButtonText:"Collection",fnClick:function(a,b){this._fnCollectionShow(a,b)}})};TableTools.classes={container:"DTTT_container",buttons:{normal:"DTTT_button",disabled:"DTTT_disabled"},collection:{container:"DTTT_collection",background:"DTTT_collection_background",buttons:{normal:"DTTT_button",disabled:"DTTT_disabled"}},select:{table:"DTTT_selectable",
+row:"DTTT_selected"},print:{body:"DTTT_Print",info:"DTTT_print_info",message:"DTTT_PrintMessage"}};TableTools.classes_themeroller={container:"DTTT_container ui-buttonset ui-buttonset-multi",buttons:{normal:"DTTT_button ui-button ui-state-default"},collection:{container:"DTTT_collection ui-buttonset ui-buttonset-multi"}};TableTools.DEFAULTS={sSwfPath:"media/swf/copy_csv_xls_pdf.swf",sRowSelect:"none",sSelectedClass:null,fnPreRowSelect:null,fnRowSelected:null,fnRowDeselected:null,aButtons:["copy","csv",
+"xls","pdf","print"],oTags:{container:"div",button:"a",liner:"span",collection:{container:"div",button:"a",liner:"span"}}};TableTools.prototype.CLASS="TableTools";TableTools.VERSION="2.1.3";TableTools.prototype.VERSION=TableTools.VERSION;"function"==typeof e.fn.dataTable&&"function"==typeof e.fn.dataTableExt.fnVersionCheck&&e.fn.dataTableExt.fnVersionCheck("1.9.0")?e.fn.dataTableExt.aoFeatures.push({fnInit:function(a){a=new TableTools(a.oInstance,"undefined"!=typeof a.oInit.oTableTools?a.oInit.oTableTools:
+{});TableTools._aInstances.push(a);return a.dom.container},cFeature:"T",sFeature:"TableTools"}):alert("Warning: TableTools 2 requires DataTables 1.9.0 or newer - www.datatables.net/download");e.fn.DataTable.TableTools=TableTools})(jQuery,window,document);
diff --git a/symposion/static/tabletools/js/TableTools.min.js.gz b/symposion/static/tabletools/js/TableTools.min.js.gz
new file mode 100644
index 00000000..16e55c23
Binary files /dev/null and b/symposion/static/tabletools/js/TableTools.min.js.gz differ
diff --git a/symposion/static/tabletools/js/ZeroClipboard.js b/symposion/static/tabletools/js/ZeroClipboard.js
new file mode 100755
index 00000000..de0f6b67
--- /dev/null
+++ b/symposion/static/tabletools/js/ZeroClipboard.js
@@ -0,0 +1,367 @@
+// Simple Set Clipboard System
+// Author: Joseph Huckaby
+
+var ZeroClipboard_TableTools = {
+
+ version: "1.0.4-TableTools2",
+ clients: {}, // registered upload clients on page, indexed by id
+ moviePath: '', // URL to movie
+ nextId: 1, // ID of next movie
+
+ $: function(thingy) {
+ // simple DOM lookup utility function
+ if (typeof(thingy) == 'string') thingy = document.getElementById(thingy);
+ if (!thingy.addClass) {
+ // extend element with a few useful methods
+ thingy.hide = function() { this.style.display = 'none'; };
+ thingy.show = function() { this.style.display = ''; };
+ thingy.addClass = function(name) { this.removeClass(name); this.className += ' ' + name; };
+ thingy.removeClass = function(name) {
+ this.className = this.className.replace( new RegExp("\\s*" + name + "\\s*"), " ").replace(/^\s+/, '').replace(/\s+$/, '');
+ };
+ thingy.hasClass = function(name) {
+ return !!this.className.match( new RegExp("\\s*" + name + "\\s*") );
+ }
+ }
+ return thingy;
+ },
+
+ setMoviePath: function(path) {
+ // set path to ZeroClipboard.swf
+ this.moviePath = path;
+ },
+
+ dispatch: function(id, eventName, args) {
+ // receive event from flash movie, send to client
+ var client = this.clients[id];
+ if (client) {
+ client.receiveEvent(eventName, args);
+ }
+ },
+
+ register: function(id, client) {
+ // register new client to receive events
+ this.clients[id] = client;
+ },
+
+ getDOMObjectPosition: function(obj) {
+ // get absolute coordinates for dom element
+ var info = {
+ left: 0,
+ top: 0,
+ width: obj.width ? obj.width : obj.offsetWidth,
+ height: obj.height ? obj.height : obj.offsetHeight
+ };
+
+ if ( obj.style.width != "" )
+ info.width = obj.style.width.replace("px","");
+
+ if ( obj.style.height != "" )
+ info.height = obj.style.height.replace("px","");
+
+ while (obj) {
+ info.left += obj.offsetLeft;
+ info.top += obj.offsetTop;
+ obj = obj.offsetParent;
+ }
+
+ return info;
+ },
+
+ Client: function(elem) {
+ // constructor for new simple upload client
+ this.handlers = {};
+
+ // unique ID
+ this.id = ZeroClipboard_TableTools.nextId++;
+ this.movieId = 'ZeroClipboard_TableToolsMovie_' + this.id;
+
+ // register client with singleton to receive flash events
+ ZeroClipboard_TableTools.register(this.id, this);
+
+ // create movie
+ if (elem) this.glue(elem);
+ }
+};
+
+ZeroClipboard_TableTools.Client.prototype = {
+
+ id: 0, // unique ID for us
+ ready: false, // whether movie is ready to receive events or not
+ movie: null, // reference to movie object
+ clipText: '', // text to copy to clipboard
+ fileName: '', // default file save name
+ action: 'copy', // action to perform
+ handCursorEnabled: true, // whether to show hand cursor, or default pointer cursor
+ cssEffects: true, // enable CSS mouse effects on dom container
+ handlers: null, // user event handlers
+ sized: false,
+
+ glue: function(elem, title) {
+ // glue to DOM element
+ // elem can be ID or actual DOM element object
+ this.domElement = ZeroClipboard_TableTools.$(elem);
+
+ // float just above object, or zIndex 99 if dom element isn't set
+ var zIndex = 99;
+ if (this.domElement.style.zIndex) {
+ zIndex = parseInt(this.domElement.style.zIndex) + 1;
+ }
+
+ // find X/Y position of domElement
+ var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);
+
+ // create floating DIV above element
+ this.div = document.createElement('div');
+ var style = this.div.style;
+ style.position = 'absolute';
+ style.left = '0px';
+ style.top = '0px';
+ style.width = (box.width) + 'px';
+ style.height = box.height + 'px';
+ style.zIndex = zIndex;
+
+ if ( typeof title != "undefined" && title != "" ) {
+ this.div.title = title;
+ }
+ if ( box.width != 0 && box.height != 0 ) {
+ this.sized = true;
+ }
+
+ // style.backgroundColor = '#f00'; // debug
+ if ( this.domElement ) {
+ this.domElement.appendChild(this.div);
+ this.div.innerHTML = this.getHTML( box.width, box.height );
+ }
+ },
+
+ positionElement: function() {
+ var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);
+ var style = this.div.style;
+
+ style.position = 'absolute';
+ //style.left = (this.domElement.offsetLeft)+'px';
+ //style.top = this.domElement.offsetTop+'px';
+ style.width = box.width + 'px';
+ style.height = box.height + 'px';
+
+ if ( box.width != 0 && box.height != 0 ) {
+ this.sized = true;
+ } else {
+ return;
+ }
+
+ var flash = this.div.childNodes[0];
+ flash.width = box.width;
+ flash.height = box.height;
+ },
+
+ getHTML: function(width, height) {
+ // return HTML for movie
+ var html = '';
+ var flashvars = 'id=' + this.id +
+ '&width=' + width +
+ '&height=' + height;
+
+ if (navigator.userAgent.match(/MSIE/)) {
+ // IE gets an OBJECT tag
+ var protocol = location.href.match(/^https/i) ? 'https://' : 'http://';
+ html += '';
+ }
+ else {
+ // all other browsers get an EMBED tag
+ html += '';
+ }
+ return html;
+ },
+
+ hide: function() {
+ // temporarily hide floater offscreen
+ if (this.div) {
+ this.div.style.left = '-2000px';
+ }
+ },
+
+ show: function() {
+ // show ourselves after a call to hide()
+ this.reposition();
+ },
+
+ destroy: function() {
+ // destroy control and floater
+ if (this.domElement && this.div) {
+ this.hide();
+ this.div.innerHTML = '';
+
+ var body = document.getElementsByTagName('body')[0];
+ try { body.removeChild( this.div ); } catch(e) {;}
+
+ this.domElement = null;
+ this.div = null;
+ }
+ },
+
+ reposition: function(elem) {
+ // reposition our floating div, optionally to new container
+ // warning: container CANNOT change size, only position
+ if (elem) {
+ this.domElement = ZeroClipboard_TableTools.$(elem);
+ if (!this.domElement) this.hide();
+ }
+
+ if (this.domElement && this.div) {
+ var box = ZeroClipboard_TableTools.getDOMObjectPosition(this.domElement);
+ var style = this.div.style;
+ style.left = '' + box.left + 'px';
+ style.top = '' + box.top + 'px';
+ }
+ },
+
+ clearText: function() {
+ // clear the text to be copy / saved
+ this.clipText = '';
+ if (this.ready) this.movie.clearText();
+ },
+
+ appendText: function(newText) {
+ // append text to that which is to be copied / saved
+ this.clipText += newText;
+ if (this.ready) { this.movie.appendText(newText) ;}
+ },
+
+ setText: function(newText) {
+ // set text to be copied to be copied / saved
+ this.clipText = newText;
+ if (this.ready) { this.movie.setText(newText) ;}
+ },
+
+ setCharSet: function(charSet) {
+ // set the character set (UTF16LE or UTF8)
+ this.charSet = charSet;
+ if (this.ready) { this.movie.setCharSet(charSet) ;}
+ },
+
+ setBomInc: function(bomInc) {
+ // set if the BOM should be included or not
+ this.incBom = bomInc;
+ if (this.ready) { this.movie.setBomInc(bomInc) ;}
+ },
+
+ setFileName: function(newText) {
+ // set the file name
+ this.fileName = newText;
+ if (this.ready) this.movie.setFileName(newText);
+ },
+
+ setAction: function(newText) {
+ // set action (save or copy)
+ this.action = newText;
+ if (this.ready) this.movie.setAction(newText);
+ },
+
+ addEventListener: function(eventName, func) {
+ // add user event listener for event
+ // event types: load, queueStart, fileStart, fileComplete, queueComplete, progress, error, cancel
+ eventName = eventName.toString().toLowerCase().replace(/^on/, '');
+ if (!this.handlers[eventName]) this.handlers[eventName] = [];
+ this.handlers[eventName].push(func);
+ },
+
+ setHandCursor: function(enabled) {
+ // enable hand cursor (true), or default arrow cursor (false)
+ this.handCursorEnabled = enabled;
+ if (this.ready) this.movie.setHandCursor(enabled);
+ },
+
+ setCSSEffects: function(enabled) {
+ // enable or disable CSS effects on DOM container
+ this.cssEffects = !!enabled;
+ },
+
+ receiveEvent: function(eventName, args) {
+ // receive event from flash
+ eventName = eventName.toString().toLowerCase().replace(/^on/, '');
+
+ // special behavior for certain events
+ switch (eventName) {
+ case 'load':
+ // movie claims it is ready, but in IE this isn't always the case...
+ // bug fix: Cannot extend EMBED DOM elements in Firefox, must use traditional function
+ this.movie = document.getElementById(this.movieId);
+ if (!this.movie) {
+ var self = this;
+ setTimeout( function() { self.receiveEvent('load', null); }, 1 );
+ return;
+ }
+
+ // firefox on pc needs a "kick" in order to set these in certain cases
+ if (!this.ready && navigator.userAgent.match(/Firefox/) && navigator.userAgent.match(/Windows/)) {
+ var self = this;
+ setTimeout( function() { self.receiveEvent('load', null); }, 100 );
+ this.ready = true;
+ return;
+ }
+
+ this.ready = true;
+ this.movie.clearText();
+ this.movie.appendText( this.clipText );
+ this.movie.setFileName( this.fileName );
+ this.movie.setAction( this.action );
+ this.movie.setCharSet( this.charSet );
+ this.movie.setBomInc( this.incBom );
+ this.movie.setHandCursor( this.handCursorEnabled );
+ break;
+
+ case 'mouseover':
+ if (this.domElement && this.cssEffects) {
+ //this.domElement.addClass('hover');
+ if (this.recoverActive) this.domElement.addClass('active');
+ }
+ break;
+
+ case 'mouseout':
+ if (this.domElement && this.cssEffects) {
+ this.recoverActive = false;
+ if (this.domElement.hasClass('active')) {
+ this.domElement.removeClass('active');
+ this.recoverActive = true;
+ }
+ //this.domElement.removeClass('hover');
+ }
+ break;
+
+ case 'mousedown':
+ if (this.domElement && this.cssEffects) {
+ this.domElement.addClass('active');
+ }
+ break;
+
+ case 'mouseup':
+ if (this.domElement && this.cssEffects) {
+ this.domElement.removeClass('active');
+ this.recoverActive = false;
+ }
+ break;
+ } // switch eventName
+
+ if (this.handlers[eventName]) {
+ for (var idx = 0, len = this.handlers[eventName].length; idx < len; idx++) {
+ var func = this.handlers[eventName][idx];
+
+ if (typeof(func) == 'function') {
+ // actual function reference
+ func(this, args);
+ }
+ else if ((typeof(func) == 'object') && (func.length == 2)) {
+ // PHP style object + method, i.e. [myObject, 'myMethod']
+ func[0][ func[1] ](this, args);
+ }
+ else if (typeof(func) == 'string') {
+ // name of function
+ window[func](this, args);
+ }
+ } // foreach event handler defined
+ } // user defined handler for event
+ }
+
+};
diff --git a/symposion/static/tabletools/swf/copy_csv_xls.swf b/symposion/static/tabletools/swf/copy_csv_xls.swf
new file mode 100644
index 00000000..b8fe47fe
Binary files /dev/null and b/symposion/static/tabletools/swf/copy_csv_xls.swf differ
diff --git a/symposion/static/tabletools/swf/copy_csv_xls_pdf.swf b/symposion/static/tabletools/swf/copy_csv_xls_pdf.swf
new file mode 100644
index 00000000..2aeb65bf
Binary files /dev/null and b/symposion/static/tabletools/swf/copy_csv_xls_pdf.swf differ
diff --git a/symposion/templates/emails/proposal_updated/message.html b/symposion/templates/emails/proposal_updated/message.html
new file mode 100644
index 00000000..f2724074
--- /dev/null
+++ b/symposion/templates/emails/proposal_updated/message.html
@@ -0,0 +1,8 @@
+{% load account_tags %}
+
+ {% user_display user %} has made changes to {{ proposal.title }} which you have previously reviewed.
+
+ {% url review_detail proposal.pk as detail_url %}
+ View the latest version of the proposal online at http://{{ current_site }}{{ detail_url }}
+Print view
Leaving {{ proposal.title }}
+
+
+{% endblock %}
diff --git a/symposion/templates/reviews/base.html b/symposion/templates/reviews/base.html
index 352fb423..4344f06a 100644
--- a/symposion/templates/reviews/base.html
+++ b/symposion/templates/reviews/base.html
@@ -42,15 +42,6 @@
*cursor: hand;
}
- {% comment %}
- table.table thead .sorting { background: url('{{ STATIC_URL }}datatables/images/sort_both.png') no-repeat center right; }
- table.table thead .sorting_asc { background: url('{{ STATIC_URL }}datatables/images/sort_asc.png') no-repeat center right; }
- table.table thead .sorting_desc { background: url('{{ STATIC_URL }}datatables/images/sort_desc.png') no-repeat center right; }
-
- table.table thead .sorting_asc_disabled { background: url('{{ STATIC_URL }}datatables/images/sort_asc_disabled.png') no-repeat center right; }
- table.table thead .sorting_desc_disabled { background: url('{{ STATIC_URL }}datatables/images/sort_desc_disabled.png') no-repeat center right; }
- {% endcomment %}
-
table.dataTable th:active {
outline: none;
}
@@ -107,111 +98,22 @@
{% block extra_script %}
+
+
diff --git a/symposion/templates/reviews/review_stats.html b/symposion/templates/reviews/review_stats.html
index 6451457f..8f4a09cb 100644
--- a/symposion/templates/reviews/review_stats.html
+++ b/symposion/templates/reviews/review_stats.html
@@ -43,35 +43,35 @@