diff --git a/LICENSE-3RD-PARTY.txt b/LICENSE-3RD-PARTY.txt index 18925cdd6848629d5f3e36204b6acb08ebbfac1b..3786d1268816c57577001398ae7bb980f561ca34 100644 --- a/LICENSE-3RD-PARTY.txt +++ b/LICENSE-3RD-PARTY.txt @@ -98,6 +98,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - jquery.layout, Copyright (c) 2012 Fabrizio Balliano and Kevin Dalman - jquery.mousewheel, Copyright (c) 2011 Brandon Aaron + - jquery.cookie, Copyright (c) 2013 Klaus Hartl ----------------------------------------------------------------------------- Permission is hereby granted, free of charge, to any person obtaining a copy @@ -149,3 +150,224 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------------- + Creative Commons Attribution 2.5 Generic (CC BY 2.5) + applies to: + - Silk icon set 1.3, Copyright Mark James +----------------------------------------------------------------------------- + +Creative Commons Legal Code + +Attribution 2.5 CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES +NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE +AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS +INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES +REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES +RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE +RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS +AND CONDITIONS. + +1. Definitions + +"Collective Work" means a work, such as a periodical issue, anthology or +encyclopedia, in which the Work in its entirety in unmodified form, +along with a number of other contributions, constituting separate and +independent works in themselves, are assembled into a collective whole. +A work that constitutes a Collective Work will not be considered a +Derivative Work (as defined below) for the purposes of this License. +"Derivative Work" means a work based upon the Work or upon the Work and +other pre-existing works, such as a translation, musical arrangement, +dramatization, fictionalization, motion picture version, sound +recording, art reproduction, abridgment, condensation, or any other form +in which the Work may be recast, transformed, or adapted, except that a +work that constitutes a Collective Work will not be considered a +Derivative Work for the purpose of this License. For the avoidance of +doubt, where the Work is a musical composition or sound recording, the +synchronization of the Work in timed-relation with a moving image +("synching") will be considered a Derivative Work for the purpose of +this License. "Licensor" means the individual or entity that offers the +Work under the terms of this License. "Original Author" means the +individual or entity who created the Work. "Work" means the +copyrightable work of authorship offered under the terms of this +License. "You" means an individual or entity exercising rights under +this License who has not previously violated the terms of this License +with respect to the Work, or who has received express permission from +the Licensor to exercise rights under this License despite a previous +violation. + +2. Fair Use Rights. Nothing in this license is intended to reduce, +limit, or restrict any rights arising from fair use, first sale or other +limitations on the exclusive rights of the copyright owner under +copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + +to reproduce the Work, to incorporate the Work into one or more +Collective Works, and to reproduce the Work as incorporated in the +Collective Works; to create and reproduce Derivative Works; to +distribute copies or phonorecords of, display publicly, perform +publicly, and perform publicly by means of a digital audio transmission +the Work including as incorporated in Collective Works; to distribute +copies or phonorecords of, display publicly, perform publicly, and +perform publicly by means of a digital audio transmission Derivative +Works. + +For the avoidance of doubt, where the work is a musical composition: +Performance Royalties Under Blanket Licenses. Licensor waives the +exclusive right to collect, whether individually or via a performance +rights society (e.g. ASCAP, BMI, SESAC), royalties for the public +performance or public digital performance (e.g. webcast) of the Work. +Mechanical Rights and Statutory Royalties. Licensor waives the exclusive +right to collect, whether individually or via a music rights agency or +designated agent (e.g. Harry Fox Agency), royalties for any phonorecord +You create from the Work ("cover version") and distribute, subject to +the compulsory license created by 17 USC Section 115 of the US Copyright +Act (or the equivalent in other jurisdictions). Webcasting Rights and +Statutory Royalties. For the avoidance of doubt, where the Work is a +sound recording, Licensor waives the exclusive right to collect, whether +individually or via a performance-rights society (e.g. SoundExchange), +royalties for the public digital performance (e.g. webcast) of the Work, +subject to the compulsory license created by 17 USC Section 114 of the +US Copyright Act (or the equivalent in other jurisdictions). + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights +in other media and formats. All rights not expressly granted by Licensor +are hereby reserved. + +4. Restrictions.The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + +You may distribute, publicly display, publicly perform, or publicly +digitally perform the Work only under the terms of this License, and You +must include a copy of, or the Uniform Resource Identifier for, this +License with every copy or phonorecord of the Work You distribute, +publicly display, publicly perform, or publicly digitally perform. You +may not offer or impose any terms on the Work that alter or restrict the +terms of this License or the recipients' exercise of the rights granted +hereunder. You may not sublicense the Work. You must keep intact all +notices that refer to this License and to the disclaimer of warranties. +You may not distribute, publicly display, publicly perform, or publicly +digitally perform the Work with any technological measures that control +access or use of the Work in a manner inconsistent with the terms of +this License Agreement. The above applies to the Work as incorporated in +a Collective Work, but this does not require the Collective Work apart +from the Work itself to be made subject to the terms of this License. If +You create a Collective Work, upon notice from any Licensor You must, to +the extent practicable, remove from the Collective Work any credit as +required by clause 4(b), as requested. If You create a Derivative Work, +upon notice from any Licensor You must, to the extent practicable, +remove from the Derivative Work any credit as required by clause 4(b), +as requested. If you distribute, publicly display, publicly perform, or +publicly digitally perform the Work or any Derivative Works or +Collective Works, You must keep intact all copyright notices for the +Work and provide, reasonable to the medium or means You are utilizing: +(i) the name of the Original Author (or pseudonym, if applicable) if +supplied, and/or (ii) if the Original Author and/or Licensor designate +another party or parties (e.g. a sponsor institute, publishing entity, +journal) for attribution in Licensor's copyright notice, terms of +service or by other reasonable means, the name of such party or parties; +the title of the Work if supplied; to the extent reasonably practicable, +the Uniform Resource Identifier, if any, that Licensor specifies to be +associated with the Work, unless such URI does not refer to the +copyright notice or licensing information for the Work; and in the case +of a Derivative Work, a credit identifying the use of the Work in the +Derivative Work (e.g., "French translation of the Work by Original +Author," or "Screenplay based on original Work by Original Author"). +Such credit may be implemented in any reasonable manner; provided, +however, that in the case of a Derivative Work or Collective Work, at a +minimum such credit will appear where any other comparable authorship +credit appears and in a manner at least as prominent as such other +comparable authorship credit. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE +EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE +LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR +ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES +ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + +This License and the rights granted hereunder will terminate +automatically upon any breach by You of the terms of this License. +Individuals or entities who have received Derivative Works or Collective +Works from You under this License, however, will not have their licenses +terminated provided such individuals or entities remain in full +compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will +survive any termination of this License. Subject to the above terms and +conditions, the license granted here is perpetual (for the duration of +the applicable copyright in the Work). Notwithstanding the above, +Licensor reserves the right to release the Work under different license +terms or to stop distributing the Work at any time; provided, however +that any such election will not serve to withdraw this License (or any +other license that has been, or is required to be, granted under the +terms of this License), and this License will continue in full force and +effect unless terminated as stated above. + +8. Miscellaneous + +Each time You distribute or publicly digitally perform the Work or a +Collective Work, the Licensor offers to the recipient a license to the +Work on the same terms and conditions as the license granted to You +under this License. Each time You distribute or publicly digitally +perform a Derivative Work, Licensor offers to the recipient a license to +the original Work on the same terms and conditions as the license +granted to You under this License. If any provision of this License is +invalid or unenforceable under applicable law, it shall not affect the +validity or enforceability of the remainder of the terms of this +License, and without further action by the parties to this agreement, +such provision shall be reformed to the minimum extent necessary to make +such provision valid and enforceable. No term or provision of this +License shall be deemed waived and no breach consented to unless such +waiver or consent shall be in writing and signed by the party to be +charged with such waiver or consent. This License constitutes the entire +agreement between the parties with respect to the Work licensed here. +There are no understandings, agreements or representations with respect +to the Work not specified here. Licensor shall not be bound by any +additional provisions that may appear in any communication from You. +This License may not be modified without the mutual written agreement of +the Licensor and You. + +Creative Commons is not a party to this License, and makes no warranty +whatsoever in connection with the Work. Creative Commons will not be +liable to You or any party on any legal theory for any damages +whatsoever, including without limitation any general, special, +incidental or consequential damages arising in connection to this +license. Notwithstanding the foregoing two (2) sentences, if Creative +Commons has expressly identified itself as the Licensor hereunder, it +shall have all rights and obligations of Licensor. + +Except for the limited purpose of indicating to the public that the Work +is licensed under the CCPL, neither party will use the trademark +"Creative Commons" or any related trademark or logo of Creative Commons +without the prior written consent of Creative Commons. Any permitted use +will be in compliance with Creative Commons' then-current trademark +usage guidelines, as may be published on its website or otherwise made +available upon request from time to time. + +Creative Commons may be contacted at http://creativecommons.org/. diff --git a/backend/Intro.php b/backend/Intro.php index 89abe3ac9a3ecc253c5681677ba8631f0b963e14..f874ec4a2628b1c039800d9fcc23ea632c507b20 100644 --- a/backend/Intro.php +++ b/backend/Intro.php @@ -149,6 +149,5 @@ class IntroDriver extends ContentDriver { throw new Exception("Access denied"); } - } } diff --git a/backend/Pianos4.php b/backend/Pianos4.php index 0e975c09d8c6456888c5b62874841c19da5149a1..8959cfc41d32014bc3549bfbc35efc080909188e 100644 --- a/backend/Pianos4.php +++ b/backend/Pianos4.php @@ -59,7 +59,8 @@ abstract class Pianos4 extends Application } public function init() - { $this->setName('PiAnoS 4'); + { + $this->setName('PiAnoS 4'); // BEGIN-BUILDSCRIPT-SECTION-1 // Code to check for absence of /install dir goes here in release version @@ -108,7 +109,8 @@ abstract class Pianos4 extends Application abstract protected function createDatabaseInstance(); protected function createDatabase() - { $db = $this->createDatabaseInstance(); + { + $db = $this->createDatabaseInstance(); $db->connect( $this->getSetting('db_host'), $this->getSetting('db_port'), @@ -141,7 +143,7 @@ abstract class Pianos4 extends Application // The version is stored as a configuration parameter (thus, changeable) but is // not really meant to be changed - use this at your own risk. - 'version' => '4.2.1', + 'version' => '4.2.2', // Edition is set by the specific class. Set to NULL for the moment // It may be 'TE' or 'LE'. Note that this is not a configuration setting per se @@ -306,6 +308,7 @@ abstract class Pianos4 extends Application 'jslib_md5' => 'libs/js/md5.js', // no version known (i.e. use latest) 'jslib_jquery_layout' => 'libs/js/jquery.layout.js', // 1.3.0-rc30.74 'jslib_jquery_hashchange' => 'libs/js/jquery.ba-hashchange.js', // 1.3 + 'jslib_jquery_cookie' => 'libs/js/jquery.cookie.js', // 1.4.0 'jslib_microdb' => 'libs/js/udb.js', // no version known (i.e. use latest) 'jslib_ajaxupload' => 'libs/js/ajaxupload.js', // no version known (i.e. use latest) 'jslib_jquery_hotkeys' => 'libs/js/jquery.hotkeys.js', // 0.8 diff --git a/backend/TE/AppDB.php b/backend/TE/AppDB.php index 6d0c4080db7a58e25e8f13fa387e4de470650fa4..a9976aa93a28c1e0cd281d74ccebefc25a01af89 100644 --- a/backend/TE/AppDB.php +++ b/backend/TE/AppDB.php @@ -261,41 +261,71 @@ class AppDB_TE extends AppDB return array('assignments'=>(int)$res[0]['count']); } - // TODO: merge this function and the getUserAssignments in a single one - public function getExerciseAssignments($id) + // TODO: merge this function and the getUserAssignments in a single one, the difference + // being that here we fetch the user data (name, id and groups). And of course that this + // function selects assignments by exercise, whereas getUserAssignments does so by + // user, obviously. + // Otherwise, the SQL code and the post-processing a too similar to be two totally + // different beasts. + public function getExerciseAssignments($id, $extended=false) { $this->checkAdmin(); + $obstypes = array('conclusion' => 'conclusions', 'mark_value' => 'mark_values', 'decision' => 'decisions', 'decision_ex' => 'decisions_ex'); + if ($extended) + { + $view = 'resultvex2'; + $obstypes['mark_degradation'] = 'mark_degradations'; + $obstypes['mark_details'] = 'mark_details'; + $obstypes['comparison_need_better_known'] = 'comparison_need_better_knowns'; + $obstypes['comparison_l3details'] = 'comparison_l3details'; + $obstypes['comparison_qualquant'] = 'comparison_qualquants'; + } + else + { + $view = 'resultvex'; + } + + $innerSelect = ''; + $groupby = ''; + foreach ($obstypes as $k=>$v) + { + $innerSelect .= ",\narray_agg(".$k.") AS ".$v; + $groupby .= ", arv.".$v; + } + $res = $this->query(" SELECT arv.*, object.createdon, object.updatedon, person.name AS person, person.id AS personid, + array_to_string(array_agg(pgroup.name),',') AS pgroups, getAssignmentStatus(arv.id) AS status - FROM person,object, + FROM pgroup,membership,person,object, (SELECT id, ispreview, count(id) AS numresults, array_agg(step) AS ressteps, array_agg(type) AS restypes, - array_agg(updatedon) AS resupdates, - array_agg(conclusion) AS conclusions, - array_agg(mark_value) AS mark_values, - array_agg(decision) AS decisions, - array_agg(decision_ex) AS decisions_ex + array_agg(updatedon) AS resupdates + ".$innerSelect." FROM (SELECT assignment.id, assignment.ispreview, resultvex.* FROM assignment - LEFT OUTER JOIN resultvex ON resultvex.assignment = assignment.id + LEFT OUTER JOIN ".$view." AS resultvex ON resultvex.assignment = assignment.id WHERE assignment.exercise = ".(int)$id." ORDER BY assignment.id,resultvex.stepid,resultvex.typeid) AS rv GROUP BY id,ispreview ) AS arv WHERE person.id = object.creator AND object.id = arv.id + AND pgroup.id = membership.pgroup + AND membership.person = person.id + GROUP BY arv.id, arv.ispreview, arv.numresults, arv.ressteps, arv.restypes, arv.resupdates ".$groupby.", + object.createdon, object.updatedon, person.name, person.id, status ORDER BY person.name,arv.id; "); @@ -303,6 +333,10 @@ class AppDB_TE extends AppDB { $row['progress'] = $this->translateAssignmentStatus((int)$row['status']); unset($row['status']); + $row['user'] = array('name'=>$row['person'], 'id'=>(int)$row['personid'], 'groups'=>$row['pgroups']); + unset($row['person']); + unset($row['personid']); + unset($row['pgroups']); $row['ispreview'] = $row['ispreview'] === 't'; $row['numresults'] = (int)$row['numresults']; if ($row['numresults'] == 0) @@ -314,6 +348,14 @@ class AppDB_TE extends AppDB $row['mark_values'] = NULL; $row['decisions'] = NULL; $row['decisions_ex'] = NULL; + if ($extended) + { + $row['mark_degradations'] = NULL; + $row['mark_details'] = NULL; + $row['comparison_need_better_knowns'] = NULL; + $row['comparison_l3details'] = NULL; + $row['comparison_qualquants'] = NULL; + } } else { @@ -324,6 +366,14 @@ class AppDB_TE extends AppDB $row['mark_values'] = $this->stringSqlArrayToPhp($row['mark_values']); $row['decisions'] = $this->stringSqlArrayToPhp($row['decisions']); $row['decisions_ex'] = $this->stringSqlArrayToPhp($row['decisions_ex']); + if ($extended) + { + $row['mark_degradations'] = $this->stringSqlArrayToPhp($row['mark_degradations']); + $row['mark_details'] = $this->stringSqlArrayToPhp($row['mark_details']); + $row['comparison_need_better_knowns'] = $this->stringSqlArrayToPhp($row['comparison_need_better_knowns']); + $row['comparison_l3details'] = $this->stringSqlArrayToPhp($row['comparison_l3details']); + $row['comparison_qualquants'] = $this->stringSqlArrayToPhp($row['comparison_qualquants']); + } } } @@ -433,7 +483,19 @@ class AppDB_TE extends AppDB $this->commitTransaction(); } - + public function getExerciseNeighbours($id, $window=1) + { + $this->checkAdmin(); + + $res = $this->query("SELECT * FROM exercise WHERE id=".(int)$id.";"); + if (count($res) !== 1) + throw new Exception("Access denied"); + $ex = $res[0]; + $e_exname = pg_escape_string($ex['name']); + $before = $this->query("SELECT * FROM exercise WHERE name < E'".$e_exname."' AND folder = ".(int)$ex['folder']." ORDER BY name DESC LIMIT ".(int)$window.";"); + $after = $this->query("SELECT * FROM exercise WHERE name > E'".$e_exname."' AND folder = ".(int)$ex['folder']." ORDER BY name ASC LIMIT ".(int)$window.";"); + return array($before, $ex, $after); + } // Images management @@ -1258,11 +1320,11 @@ class AppDB_TE extends AppDB // CODE BELOW DOES NOT RUN IN SINGLE USER MODE - // Attach additionnal data relative to the user (the annotation themselves contain the userid + // Attach additional data relative to the user (the annotation themselves contain the userid // but not the name) $data['user'] = array('id'=>(int)$item['userid'], 'name'=>$item['username'], 'groups'=>$item['groupnames']); - // Additionnal data in comparison mode + // Additional data in comparison mode $data['progress'] = array('status'=>$item['status']); if ($rstep > $analysisStepID && $item['status'] >= 2) @@ -1313,7 +1375,7 @@ class AppDB_TE extends AppDB { $this->checkAdmin(); - return $this->getExerciseAssignments($exid); + return $this->getExerciseAssignments($exid, true); $columns = array('object.creator AS uid', 'observationtype.name', 'observation.value'); $from = array('object', 'assignment', 'observationtype', 'observation'); @@ -1423,4 +1485,62 @@ class AppDB_TE extends AppDB throw new Exception("Database integrity error"); return $val === 0; } + + + // Messaging subsystem + + public function getHomeMessages() + { + // TODO: this should be removed and message filtering implemented according to user rights. + $this->checkAdmin(); + + $res = $this->query("SELECT * FROM messagev WHERE type='home' AND visibility IN ('tutors','all') ORDER BY createdon DESC;"); + return $res; + } + + public function getMessageConstants() + { + $this->checkAdmin(); + $r1 = $this->query("SELECT * FROM messagetype ORDER BY id ASC;"); + $r2 = $this->query("SELECT * FROM messagevisibility ORDER BY id ASC;"); + return array($r1, $r2); + } + + public function getAllMessages() + { + $this->checkAdmin(); + $res = $this->query("SELECT * FROM messagev ORDER BY createdon ASC;"); + return $res; + } + + public function getMessage($id) + { + $this->checkAdmin(); + $res = $this->query("SELECT * FROM messagev WHERE id=".(int)$id.";"); + if (count($res) !== 1) + throw new Exception("Access denied"); + return $res[0]; + } + + public function deleteMessage($id) + { + $this->checkAdmin(); + $trtoken = $this->beginTransactionIf(); + $this->query("DELETE FROM message WHERE id=".(int)$id.";"); + $this->query("DELETE FROM object WHERE id=".(int)$id.";"); + $this->commitTransactionIf($trtoken); + } + + public function addMessage($title, $type, $visibility, $value) + { + $this->checkAdmin(); + $res = $this->query("INSERT INTO message(title,type,visibility,value) VALUES (E'".pg_escape_string($title)."', ".(int)$type.", ".(int)$visibility.", E'".pg_escape_string($value)."') RETURNING id;"); + return (int)$res[0]['id']; + } + + public function updateMessage($id, $title, $type, $visibility, $value) + { + $this->checkAdmin(); + $res = $this->query("UPDATE message SET title=E'".pg_escape_string($title)."', type=".(int)$type.", visibility=".(int)$visibility.", value=E'".pg_escape_string($value)."' WHERE id=".(int)$id.";"); + } }; diff --git a/backend/TE/MainFrame.php b/backend/TE/MainFrame.php index 2728b28219f8c9865af3074f8a777dc13c66dc5e..b5ce75ac032cf28d184cf55462f6c7bfc14ec6bd 100644 --- a/backend/TE/MainFrame.php +++ b/backend/TE/MainFrame.php @@ -68,6 +68,7 @@ class MainFrame extends Node $rs->getPage()->addScript(Application::get('jslib_ajaxupload')); $rs->getPage()->addStyleSheet(Application::get('csslib_jquery_layout')); + $rs->getPage()->addScript(Application::get('jslib_jquery_cookie')); $settings = array(); diff --git a/backend/TE/TakeExercise.php b/backend/TE/TakeExercise.php index ce0061a2321fe23d709304b60cafffafe3fa4612..c09533c40ee65bcc8103191d579bce4e9144ff6a 100644 --- a/backend/TE/TakeExercise.php +++ b/backend/TE/TakeExercise.php @@ -188,14 +188,23 @@ class TakeExerciseFrame extends ExerciseFrame $isTutorPreview = $_SESSION['user']['isadmin'] && isset($_GET['preview']) && $_GET['preview']; $isTutorReview = $_SESSION['user']['isadmin'] && !$isTutorPreview; + $db = Application::instance()->getDB(); if ($isTutorReview) { $aid = -$ex['id']; $progress = array('status'=>3, 'step'=>'ANALYSIS'); + if (isset($_GET['step']) && strtolower($_GET['step']) == 'comparison') + $progress['step'] = 'COMPARISON'; + $title = array(NULL, $ex['name'], NULL); + $neighbours = $db->getExerciseNeighbours($ex['id']); + if (count($neighbours[0])) + $title[0] = array($neighbours[0][0]['name'], '?action=take&id='.(int)$neighbours[0][0]['id']); + if (count($neighbours[2])) + $title[2] = array($neighbours[2][0]['name'], '?action=take&id='.(int)$neighbours[2][0]['id']); + $settings['title'] = $title; } else { - $db = Application::instance()->getDB(); $aid = $db->getOrCreateAssignment($ex['id']); $progress = $db->getAssignmentProgress($aid); } diff --git a/css/app.css b/css/app.css index b14ecde433ae94e20207c201c9395e9df6c8c11e..3a930a5ac530f7e3b5834b14cb86735c356c687f 100644 --- a/css/app.css +++ b/css/app.css @@ -81,6 +81,19 @@ a.pianos4_title:active,a.pianos4_title:hover margin-left: 1em; color: #cccccc; } +.pianos4_unsaved_message._hidden +{ + visibility: hidden; +} + +.pianos4_exercise_title_wrap +{ + margin-left: 1em; +} +.pianos4_exercise_title +{ + padding: 0 0.5em; +} .pianos4 .ui-layout-center { @@ -829,3 +842,10 @@ a.pianos4_title:active,a.pianos4_title:hover { background-image: url("icons/help_mouse_right.png"); } + +.pianos4_tutor_userdata +{ + padding-top: 3px; + border-top: 1px solid #c7c7c7; + overflow-y: auto; +} diff --git a/css/icons/backend/page.png b/css/icons/backend/page.png new file mode 100644 index 0000000000000000000000000000000000000000..03ddd799fa0a3aec561c75d4221f195db65d6eb9 Binary files /dev/null and b/css/icons/backend/page.png differ diff --git a/css/icons/backend/page_add.png b/css/icons/backend/page_add.png new file mode 100644 index 0000000000000000000000000000000000000000..d5bfa0719bc3a2ce4fc529403f0acd6b6057c956 Binary files /dev/null and b/css/icons/backend/page_add.png differ diff --git a/css/icons/backend/page_delete.png b/css/icons/backend/page_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..3141467c678d2b53f79deb22086a9cb3a576a08d Binary files /dev/null and b/css/icons/backend/page_delete.png differ diff --git a/css/icons/backend/page_edit.png b/css/icons/backend/page_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..046811ed7a6ef16be1a54bb860e1f22c6dacdacf Binary files /dev/null and b/css/icons/backend/page_edit.png differ diff --git a/database/migrations/4.2.2.sql b/database/migrations/4.2.2.sql new file mode 100644 index 0000000000000000000000000000000000000000..9a5cdded092575b727ba21716199f581d3561290 --- /dev/null +++ b/database/migrations/4.2.2.sql @@ -0,0 +1,95 @@ +CREATE VIEW resultvex2 AS ( + SELECT result.assignment, + result.type AS typeid, + resulttype.name AS type, + result.step AS stepid, + resultstep.name AS step, + result.updatedon, + obs_concl.value AS conclusion, + obs_mark_value.value AS mark_value, + obs_mark_degradation.value AS mark_degradation, + obs_mark_details.value AS mark_details, + obs_decision.value AS decision, + obs_decision_ex.value AS decision_ex, + obs_comparison_need_better_known.value AS comparison_need_better_known, + obs_comparison_l3details.value AS comparison_l3details, + obs_comparison_qualquant.value AS comparison_qualquant + FROM resulttype,resultstep,result + LEFT OUTER JOIN observation AS obs_concl ON obs_concl.assignment = result.assignment + AND obs_concl.restype = result.type + AND obs_concl.resstep = result.step + AND obs_concl.type = (SELECT id FROM observationtype WHERE name = 'conclusion') + LEFT OUTER JOIN observation AS obs_mark_value ON obs_mark_value.assignment = result.assignment + AND obs_mark_value.restype = result.type + AND obs_mark_value.resstep = result.step + AND obs_mark_value.type = (SELECT id FROM observationtype WHERE name = 'mark-value') + LEFT OUTER JOIN observation AS obs_mark_degradation ON obs_mark_degradation.assignment = result.assignment + AND obs_mark_degradation.restype = result.type + AND obs_mark_degradation.resstep = result.step + AND obs_mark_degradation.type = (SELECT id FROM observationtype WHERE name = 'mark-degradation') + LEFT OUTER JOIN observation AS obs_mark_details ON obs_mark_details.assignment = result.assignment + AND obs_mark_details.restype = result.type + AND obs_mark_details.resstep = result.step + AND obs_mark_details.type = (SELECT id FROM observationtype WHERE name = 'mark-details') + LEFT OUTER JOIN observation AS obs_decision ON obs_decision.assignment = result.assignment + AND obs_decision.restype = result.type + AND obs_decision.resstep = result.step + AND obs_decision.type = (SELECT id FROM observationtype WHERE name = 'comparison-decision') + LEFT OUTER JOIN observation AS obs_decision_ex ON obs_decision_ex.assignment = result.assignment + AND obs_decision_ex.restype = result.type + AND obs_decision_ex.resstep = result.step + AND obs_decision_ex.type = (SELECT id FROM observationtype WHERE name = 'comparison-decision-ex') + LEFT OUTER JOIN observation AS obs_comparison_need_better_known ON obs_comparison_need_better_known.assignment = result.assignment + AND obs_comparison_need_better_known.restype = result.type + AND obs_comparison_need_better_known.resstep = result.step + AND obs_comparison_need_better_known.type = (SELECT id FROM observationtype WHERE name = 'comparison-need-better-known') + LEFT OUTER JOIN observation AS obs_comparison_l3details ON obs_comparison_l3details.assignment = result.assignment + AND obs_comparison_l3details.restype = result.type + AND obs_comparison_l3details.resstep = result.step + AND obs_comparison_l3details.type = (SELECT id FROM observationtype WHERE name = 'comparison-l3details') + LEFT OUTER JOIN observation AS obs_comparison_qualquant ON obs_comparison_qualquant.assignment = result.assignment + AND obs_comparison_qualquant.restype = result.type + AND obs_comparison_qualquant.resstep = result.step + AND obs_comparison_qualquant.type = (SELECT id FROM observationtype WHERE name = 'comparison-qualquant') + WHERE result.type = resulttype.id + AND result.step = resultstep.id +); + +CREATE TABLE messagetype ( + id serial UNIQUE NOT NULL, + name text UNIQUE NOT NULL, + PRIMARY KEY (id) +) WITHOUT OIDs; +INSERT INTO messagetype(name) VALUES (E'home'); + +CREATE TABLE messagevisibility ( + id serial UNIQUE NOT NULL, + name text UNIQUE NOT NULL, + PRIMARY KEY (id) +) WITHOUT OIDs; +INSERT INTO messagevisibility(name) VALUES (E'hidden'); +INSERT INTO messagevisibility(name) VALUES (E'tutors'); +INSERT INTO messagevisibility(name) VALUES (E'users'); +INSERT INTO messagevisibility(name) VALUES (E'all'); + +CREATE TABLE message ( + FOREIGN KEY (id) REFERENCES object(id), + id int UNIQUE NOT NULL, + FOREIGN KEY (type) REFERENCES messagetype(id), + type int NOT NULL, + FOREIGN KEY (visibility) REFERENCES messagevisibility(id), + visibility int NOT NULL, + FOREIGN KEY (parent) REFERENCES message(id), + parent int, + title text NOT NULL, + value text NOT NULL, + PRIMARY KEY (id) +) WITHOUT OIDs; +CREATE TRIGGER message_object_Inherit_Trigger + BEFORE INSERT ON message + FOR EACH ROW + EXECUTE PROCEDURE Generic_object_Inherit_Trigger_Hook(); + +CREATE VIEW messagev AS ( + SELECT message.id,messagetype.id AS typeid,messagetype.name AS type,message.visibility AS visibilityid,messagevisibility.name AS visibility,message.title,message.value,message.parent,object.createdon,object.creator AS creatorid,person.name AS creator FROM message,messagevisibility,messagetype,object,person WHERE person.id=object.creator AND object.id=message.id AND message.visibility=messagevisibility.id AND message.type=messagetype.id +); diff --git a/database/pianos4-te.udb b/database/pianos4-te.udb index 96ad8eb8dc9842ebcd2c7eb1394138abea2a24b1..8f9073a785b8723c2b1b06fb7bfce0558e892638 100644 --- a/database/pianos4-te.udb +++ b/database/pianos4-te.udb @@ -149,6 +149,10 @@ view resultvex sqlfile views/resultvex.sql depends result resulttype resultstep observation observationtype +view resultvex2 + sqlfile views/resultvex2.sql + depends result resulttype resultstep observation observationtype + entity annotation id int nn pk result fk result nn pk @@ -196,6 +200,7 @@ entity resulthistory autoid createdon date now nn data text nn + # Search views # ========================== @@ -203,8 +208,36 @@ view ftsv sqlfile views/ftsv.sql depends person pgroup image folder exercise + +# Messaging subsystem +# ========================== +entity message inherits object + type fk messagetype nn + visibility fk messagevisibility nn + parent fk message + title text nn + value text nn + +entity messagetype autoid + name text uq nn + @data + home + +entity messagevisibility autoid + name text uq nn + @data + hidden + tutors + users + all + +view messagev + sql "SELECT message.id,messagetype.id AS typeid,messagetype.name AS type,message.visibility AS visibilityid,messagevisibility.name AS visibility,message.title,message.value,message.parent,object.createdon,object.creator AS creatorid,person.name AS creator FROM message,messagevisibility,messagetype,object,person WHERE person.id=object.creator AND object.id=message.id AND message.visibility=messagevisibility.id AND message.type=messagetype.id" + depends message + + # Plugins # (comment out to disable) # =================== diff --git a/database/views/resultvex2.sql b/database/views/resultvex2.sql new file mode 100644 index 0000000000000000000000000000000000000000..b8994c0c83aa9bab13b4599eae80c33270fb3082 --- /dev/null +++ b/database/views/resultvex2.sql @@ -0,0 +1,54 @@ +SELECT result.assignment, + result.type AS typeid, + resulttype.name AS type, + result.step AS stepid, + resultstep.name AS step, + result.updatedon, + obs_concl.value AS conclusion, + obs_mark_value.value AS mark_value, + obs_mark_degradation.value AS mark_degradation, + obs_mark_details.value AS mark_details, + obs_decision.value AS decision, + obs_decision_ex.value AS decision_ex, + obs_comparison_need_better_known.value AS comparison_need_better_known, + obs_comparison_l3details.value AS comparison_l3details, + obs_comparison_qualquant.value AS comparison_qualquant +FROM resulttype,resultstep,result + LEFT OUTER JOIN observation AS obs_concl ON obs_concl.assignment = result.assignment + AND obs_concl.restype = result.type + AND obs_concl.resstep = result.step + AND obs_concl.type = (SELECT id FROM observationtype WHERE name = 'conclusion') + LEFT OUTER JOIN observation AS obs_mark_value ON obs_mark_value.assignment = result.assignment + AND obs_mark_value.restype = result.type + AND obs_mark_value.resstep = result.step + AND obs_mark_value.type = (SELECT id FROM observationtype WHERE name = 'mark-value') + LEFT OUTER JOIN observation AS obs_mark_degradation ON obs_mark_degradation.assignment = result.assignment + AND obs_mark_degradation.restype = result.type + AND obs_mark_degradation.resstep = result.step + AND obs_mark_degradation.type = (SELECT id FROM observationtype WHERE name = 'mark-degradation') + LEFT OUTER JOIN observation AS obs_mark_details ON obs_mark_details.assignment = result.assignment + AND obs_mark_details.restype = result.type + AND obs_mark_details.resstep = result.step + AND obs_mark_details.type = (SELECT id FROM observationtype WHERE name = 'mark-details') + LEFT OUTER JOIN observation AS obs_decision ON obs_decision.assignment = result.assignment + AND obs_decision.restype = result.type + AND obs_decision.resstep = result.step + AND obs_decision.type = (SELECT id FROM observationtype WHERE name = 'comparison-decision') + LEFT OUTER JOIN observation AS obs_decision_ex ON obs_decision_ex.assignment = result.assignment + AND obs_decision_ex.restype = result.type + AND obs_decision_ex.resstep = result.step + AND obs_decision_ex.type = (SELECT id FROM observationtype WHERE name = 'comparison-decision-ex') + LEFT OUTER JOIN observation AS obs_comparison_need_better_known ON obs_comparison_need_better_known.assignment = result.assignment + AND obs_comparison_need_better_known.restype = result.type + AND obs_comparison_need_better_known.resstep = result.step + AND obs_comparison_need_better_known.type = (SELECT id FROM observationtype WHERE name = 'comparison-need-better-known') + LEFT OUTER JOIN observation AS obs_comparison_l3details ON obs_comparison_l3details.assignment = result.assignment + AND obs_comparison_l3details.restype = result.type + AND obs_comparison_l3details.resstep = result.step + AND obs_comparison_l3details.type = (SELECT id FROM observationtype WHERE name = 'comparison-l3details') + LEFT OUTER JOIN observation AS obs_comparison_qualquant ON obs_comparison_qualquant.assignment = result.assignment + AND obs_comparison_qualquant.restype = result.type + AND obs_comparison_qualquant.resstep = result.step + AND obs_comparison_qualquant.type = (SELECT id FROM observationtype WHERE name = 'comparison-qualquant') +WHERE result.type = resulttype.id + AND result.step = resultstep.id diff --git a/doc/shots/all shots.psd.bz2 b/doc/shots/all shots.psd.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..eb9e28435cd135560b2b1e4801ef9763c39e681f Binary files /dev/null and b/doc/shots/all shots.psd.bz2 differ diff --git a/doc/shots/pianos_tutor_analysis_review.jpg b/doc/shots/pianos_tutor_analysis_review.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c32222e0a3439a8f5868dec9ab60349e317bd3b5 Binary files /dev/null and b/doc/shots/pianos_tutor_analysis_review.jpg differ diff --git a/doc/shots/pianos_tutor_comparison_review.jpg b/doc/shots/pianos_tutor_comparison_review.jpg new file mode 100644 index 0000000000000000000000000000000000000000..da3df7f907424edde2754302e0ae6f1865a44f56 Binary files /dev/null and b/doc/shots/pianos_tutor_comparison_review.jpg differ diff --git a/doc/shots/pianos_tutor_home.jpg b/doc/shots/pianos_tutor_home.jpg new file mode 100644 index 0000000000000000000000000000000000000000..526f0fae19147580c4b0cc227bd6944c340080c9 Binary files /dev/null and b/doc/shots/pianos_tutor_home.jpg differ diff --git a/doc/shots/pianos_tutor_review_tools.jpg b/doc/shots/pianos_tutor_review_tools.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b51122d11aba6440a9cf0a9a9b3614280c477bf2 Binary files /dev/null and b/doc/shots/pianos_tutor_review_tools.jpg differ diff --git a/doc/shots/pianos_user_analysis.jpg b/doc/shots/pianos_user_analysis.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5c9fdff506ca410a2c68ae1ed5caf428cfc08341 Binary files /dev/null and b/doc/shots/pianos_user_analysis.jpg differ diff --git a/doc/shots/pianos_user_analysis_conclusions.jpg b/doc/shots/pianos_user_analysis_conclusions.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4e5113220a17b0b61cbb13e4df31d2a7c7d7cd2f Binary files /dev/null and b/doc/shots/pianos_user_analysis_conclusions.jpg differ diff --git a/doc/shots/pianos_user_comparison.jpg b/doc/shots/pianos_user_comparison.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1511d434c3e4ef5f1e03aaf56b545c3ef48fbd5c Binary files /dev/null and b/doc/shots/pianos_user_comparison.jpg differ diff --git a/doc/shots/pianos_user_home.jpg b/doc/shots/pianos_user_home.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0e38c98ffa5a30223eab9ffb8cacb0ae18fdc6b4 Binary files /dev/null and b/doc/shots/pianos_user_home.jpg differ diff --git a/doc/shots/pianos_user_new_case.jpg b/doc/shots/pianos_user_new_case.jpg new file mode 100644 index 0000000000000000000000000000000000000000..04316013323438a2f01bdca6de03093238e7cd88 Binary files /dev/null and b/doc/shots/pianos_user_new_case.jpg differ diff --git a/doc/source/about.rst b/doc/source/about.rst index d015cabd0f17fa75776d9d1c9a88e9a32864a6a6..9a19f76a0ad5a249b84ca6bef6d20bd6611ef0d1 100644 --- a/doc/source/about.rst +++ b/doc/source/about.rst @@ -1,10 +1,15 @@ About PiAnoS ================================== +.. image:: images/unilogo_bleu.* + :align: right + :alt: Université de Lausanne + :target: http://www.unil.ch/esc + This is the documentation page of the PiAnoS Project, a (fingerprint) examiner training tool created by the School of Criminal Justice, University of Lausanne, Switzerland. PiAnoS stands for "Picture Annotation System". It was initially created by Julien Furrer, Romain Voisard and Christophe Champod. It is now maintained by Thibault Genessay. -The latest version is 4.2.0 and was released on November 21st, 2013. The previous branch (3.x, up to 3.6), based on the Flash interface, is no longer maintained. +The latest version is 4.2.1 and was released on January 6th, 2014. The previous branch (3.x, up to 3.6), based on the Flash interface, is no longer maintained. PiAnoS is free software, released under the GNU Affero GPL license. Please see the :doc:`license` page for more information. diff --git a/doc/source/admin/install/debian.rst b/doc/source/admin/install/debian.rst index 47dded4e2e66bbcc89642d5ee0d10bc4f61314a4..b90d8198f5629e25ec97ca3603b995004dce453e 100644 --- a/doc/source/admin/install/debian.rst +++ b/doc/source/admin/install/debian.rst @@ -96,6 +96,8 @@ Activate it instead of Apache's default. $ sudo chmod 600 settings-local.php +.. _tbs-debian-pg-setting: + Configure the database ------------------------- diff --git a/doc/source/admin/install/troubleshooting.rst b/doc/source/admin/install/troubleshooting.rst index 2a7faa346083ed0a8293c788a992a22c419fa372..a12a6f96ddb9085699c9b6deb93cea78b15ad44a 100644 --- a/doc/source/admin/install/troubleshooting.rst +++ b/doc/source/admin/install/troubleshooting.rst @@ -6,6 +6,11 @@ This chapter is a collection of known problems that can arise when installing Pi All platforms ------------------------------ + * FATAL: unknown configuration parameter "unil.user" + (in French: ERREUR: paramètre de configuration « unil.user » non reconnu) + + This happens when you have forgotten to set the ``custom_variable_classes`` option in ``postgresql.conf``. The detailed procedures explain how to do this on :ref:`Linux ` and on :ref:`Windows `. + Linux ------------------------------ diff --git a/doc/source/admin/install/webinstall.rst b/doc/source/admin/install/webinstall.rst index 0d726cadb13286fd850e37cb4b8c9cbfdd15df9d..f906feba8260a41dbd556168fe257bf9dd1299b2 100644 --- a/doc/source/admin/install/webinstall.rst +++ b/doc/source/admin/install/webinstall.rst @@ -47,7 +47,7 @@ If users cannot log-in (keep seeing the login page even after correct authentica Configuration file -------------------- -The PiAnoS configuration is ``backend/local-settings.php``. It is a mean to override the default configuration options. The wizard only allows to modify a couple values. See ``index.php`` for the complete list of options. +The PiAnoS configuration is ``backend/local-settings.php``. It is a mean to override the default configuration options. The wizard only allows to modify a couple values. See :doc:`../configuration` for more information. Here you will be able to : - generate the configuration file automatically. This requires write access to the file ``local-settings.php``. See :ref:`db-setup` for more info on setting permissions. diff --git a/doc/source/admin/install/win7.rst b/doc/source/admin/install/win7.rst index 0178176e14dbf738c339a40fbee61ff677ff61ef..c9f4e8c6701b821be61a47f35d2ecb0e1bb3f859 100644 --- a/doc/source/admin/install/win7.rst +++ b/doc/source/admin/install/win7.rst @@ -132,7 +132,7 @@ PHP is actually a module for Apache and we need to make them live together. Now that PHP is active, we need to activate some required modules for PiAnoS (e.g. PostgreSQL, imaging libraries, etc.). Even if you used the automated installer (because it was available to you, while it was not when this procedure was written), you need to perform these steps. -You need to perform the following list of changes in your ``C:\Program Files (x86)\PHP\php.ini`` file. Use Ctrl+F to find the key and set the value appropriately. If the line is commented (i.e. begins with a semicolon ``;``) you should first uncomment it and then set the value. I am providing all settings not necessarily in the right order, but these are actually scattered all around the ``php.ini`` file. +You need to perform the following list of changes in your ``C:\Program Files (x86)\PHP\php.ini`` file. Use Ctrl+F to find the key and set the value appropriately. If the line is commented (i.e. begins with a semicolon ``;``) you should first uncomment it and then set the value. These settings are actually scattered all around the ``php.ini`` file. .. code-block:: ini @@ -149,10 +149,11 @@ You need to perform the following list of changes in your ``C:\Program Files (x8 You can now restart the Apache server, and refresh the PHP Information page. If everything is correct, you should have all modules loaded - search for each module in the page to see if it was loaded (if the module does not appear, it was not loaded at all). -If some modules are missing, you need to investigate why. The best method is to turn off all modules, then restart Apache. Then, enable them one by one, restarting Apache each time, to see at where things go wrong. When you have identified the culprit, use the web to find why the module will not load. +If some modules are missing, you need to investigate why. The best method is to turn off all modules, then restart Apache. Then, enable them one by one, restarting Apache each time, to see where things go wrong. When you have identified the culprit, use the web to find why the module will not load. .. note:: We have used a trick in the previous step, namely the ``LoadFile "C:/Program Files (x86)/PHP/libpq.dll"`` configuration directive, for Apache to load the DLLs of PostgreSQL. PHP will simply not load them by default because PHP is broken on Windows. The trick forces Apache to load the DLL earlier than PHP, and then PHP happily uses it. If you let PHP do this by itself, it will fail - silently. Using ``.msi`` installation packages does not prevent the problem to happen, and worse, it will clutter your PATH with stupid values, making the issue even worse. +.. _tbs-win7-pg-setting: Configure the database ------------------------- diff --git a/doc/source/admin/upgrading.rst b/doc/source/admin/upgrading.rst index 990febd0041720ef515a8d7c8e2a7e9a6e979be7..51ffebdccdddf9e1d274e345b84aafff894739c4 100644 --- a/doc/source/admin/upgrading.rst +++ b/doc/source/admin/upgrading.rst @@ -21,7 +21,7 @@ Choosing an upgrade strategy There are mainly 2 upgrade strategies. One is simple and consists in overwriting old files with the newer ones. The other consists in creating another "instance" of PiAnoS with the new version, then switching the link in Apache from the old to the new. -The second method allows easier rollbacks in case of failure and is good for production servers, while the first method is preferred on local systems (or non-risky situations). +The second method allows easier rollbacks in case of failure and is good for production servers, while the first method is preferred on local systems (or non-risky situations). Note that if you have followed the :doc:`install/debian` you are already using symlinks and should use this 2nd method. Simple upgrade @@ -31,6 +31,7 @@ Simple upgrade * Copy the entire directory (containing old files) to a temporary location * Download the new version of PiAnoS * Unpack the new files, overwriting current files + * Remove the ``install/`` directory (the installer should not be run again [#f1]_) * Open ``settings-local.php`` and update the version numbers of ``use_minified_pianos4`` and ``use_minified_pianos4_backend``. E.g. if upgrading from 4.0.0 to 4.1.0, change: .. code:: php @@ -54,4 +55,56 @@ If at any point things go wrong, you can restore both the files and the database Production upgrade --------------------- -.. note:: This part has not been written yet. \ No newline at end of file + * Backup the database and the configuration file + * Download and unpack the new version of PiAnoS + * Install the files in a new directory under ``/var/www/pianos`` + * Copy your ``settings-local.php`` to the ``backend`` directory (use ``-p`` to preserve permission mask and ownership) + * Adjust the Javascript version numbers in this new file (see `Simple upgrade`_ above) + * Remove the ``install/`` directory + * Change the symlink ``root/`` symlink in ``/var/www/`` to point to ``/var/www/pianos/pianos-4.2.1`` + * Connect to PiAnoS. The Login page should show the updated version number + * Log-in as administrator and check that things look normal (are your results still there? can you still review exercises? etc.) + +For example, here is the transcript of an upgrade from 4.0.1 to 4.2.1: + +.. code-block:: bash + + $ whoami + bob + $ cd ~ + $ wget -nc https://ips-labs.unil.ch/pianos/downloads/pianos-latest.tar.gz + $ tar -xvzf pianos-latest.tar.gz + $ cd /var/www/pianos/ + $ sudo mv /home/bob/pianos-4.2.1/ . + $ ls -l + drwxr-xr-x 3 bob bob 4096 Nov 8 02:25 pianos-4.0.1 + drwxr-xr-x 3 bob bob 4096 Dec 25 00:01 pianos-4.2.1 + $ sudo cp -p pianos-4.0.1/backend/settings-local.php pianos-4.2.1/backend/ + $ cd pianos-4.2.1/ + $ sudo emacs backend/settings-local.php + [ ... change version numbers of .js files ... ] + $ rm -rf install/ + $ cd ../.. + $ sudo rm root; ln -s pianos/pianos-4.2.1 root + $ ls -l + -rw-r--r-- 1 root root 177 Nov 8 00:58 index.html + drwxr-xr-x 3 root root 4096 Nov 8 02:25 pianos + lrwxrwxrwx 1 root root 25 Dec 8 00:04 root -> pianos/pianos-4.2.1/ + +As you can see, the old version has not been altered at all. We've simply added a new version, and switched the symlink. Rolling back to the previous version is even simpler: you only have to revert the symlink to ``pianos-4.0.1``. Note however that this only works for releases that use the same database schema. For newer releases this may not be true (note that schema changes are indicated in the :doc:`../download` page). In this case, the only way to do a proper rollback is to both switch the symlink back to the older version AND to restore the backup you have made prior to the upgrade. + +If you want to preserve the entire instance as it was prior to the upgrade, you can also duplicate the database. This can be done by creating an empty DB, and restoring your backup inside. You can take an even shorter route by issuing: + +.. code-block:: bash + + $ sudo su postgres + $ psql + $ CREATE DATABASE pianos421 WITH TEMPLATE pianos OWNER pianos; + $ \q + +And then editing your ``settings-local.php``, replacing ``Application::set('db_name', 'pianos');`` by ``Application::set('db_name', 'pianos421');`` [#f2]_. This way your instance prior to the upgrade is completely kept, and rolling back `really` boils down to changing the symlink back. New data will indeed `not` be part of this "frozen" instance, and the interest of doing a rollback late after upgrade is somewhat limited. + +.. rubric:: Footnotes + +.. [#f1] But you don't really care, since you have a backup. +.. [#f2] This may require you to adjust the connection settings in your ``pg_hba.conf`` file to allow the connection to the new ``pianos421`` database. \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index 20f3cb57e5e1e960a4af967d06c2d9c48e93c40d..a725505b9e0aaa63dfca49d136b54c10ac7a16b8 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -50,7 +50,7 @@ copyright = u'2011-2014, University of Lausanne, Switzerland' # The short X.Y version. version = '4.2' # The full version, including alpha/beta/rc tags. -release = '4.2.1' +release = '4.2.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -248,7 +248,7 @@ texinfo_documents = [ epub_title = u'PiAnoS' epub_author = u'Thibault Genessay' epub_publisher = u'Thibault Genessay' -epub_copyright = u'2011-2013, University of Lausanne, Switzerland' +epub_copyright = u'2011-2014, University of Lausanne, Switzerland' # The language of the text. It defaults to the language option # or en if the language is not set. diff --git a/doc/source/contact.rst b/doc/source/contact.rst index 72e36789be84a76ad938222d7cb1cadef9ffe8fa..cb1f6674de8b21443f129f8ac61cc4a4ee76c354 100644 --- a/doc/source/contact.rst +++ b/doc/source/contact.rst @@ -1,6 +1,11 @@ Contact ================================== +.. image:: images/unilogo_bleu.* + :align: right + :alt: Université de Lausanne + :target: http://www.unil.ch/esc + | Institut de Police Scientifique | École des Sciences Criminelles | Université de Lausanne, Suisse diff --git a/doc/source/credits.rst b/doc/source/credits.rst index 3a682747201728211722400c324cb860d5feed0a..765d71d72b0ab2187e4d152fa6e4546fb81d0ef1 100644 --- a/doc/source/credits.rst +++ b/doc/source/credits.rst @@ -1,6 +1,11 @@ Credits ================================== +.. image:: images/unilogo_bleu.* + :align: right + :alt: Université de Lausanne + :target: http://www.unil.ch/esc + Copyright 2011-2014 University of Lausanne, Switzerland PiAnoS 4 was funded in part by a grant from the `National Institude of Justice `_. @@ -41,7 +46,7 @@ Previous releases (1, 2 and 3.x) were the work of We would like to acknowledge early contributors to the previous versions of PiAnoS, namely: - - the `University of Lausanne `_, through the `Fonds d'Innovation Pédagogique`, awarded [#f1]_ in 2007 (version 1) + - the `University of Lausanne `_, through the `Fonds d'Innovation Pédagogique`, awarded [#f1]_ in 2007 (version 1) - the `Technical Support Working Group `_ and the `National Institute of Justice `_, sponsors of the project `A TOPOLOGICAL MODEL FOR THE EVIDENTIAL VALUE ASSESSMENT OF PARTIAL FINGERPRINTS` [#f2]_ (version 2) - the (now defunct) Forensic Science Service of the United Kingdom (version 3) @@ -60,6 +65,8 @@ PiAnoS was built using many third-party software libraries, programs and tools. - `The PostgreSQL Global Development Group `_ - `Guido van Rossum and the Python Software Foundation `_ - `The Apache Software Foundation `_ + - Art + - `Silk Icons set, by Mark James `_ - Misc. helpful resources - `The StackOverflow community `_ diff --git a/doc/source/download.rst b/doc/source/download.rst index 4539f47883931bc543646a5e5958cc3a26b3c44e..fec1704ad09a91737de6ee566dc8e842e7dd6730 100644 --- a/doc/source/download.rst +++ b/doc/source/download.rst @@ -8,6 +8,8 @@ Latest version `pianos-latest.tar.gz `_ +For new installations, please go to the :doc:`admin/install/index` page. If you are upgrading from a prior version, please refer to the :doc:`admin/upgrading` page. + All releases --------------- diff --git a/doc/source/images/unilogo_bleu.pdf b/doc/source/images/unilogo_bleu.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b0181f888c001504b2cf86d5b5682520fa5dbd3b Binary files /dev/null and b/doc/source/images/unilogo_bleu.pdf differ diff --git a/doc/source/images/unilogo_bleu.png b/doc/source/images/unilogo_bleu.png new file mode 100644 index 0000000000000000000000000000000000000000..e49106004c7624fc6319223e885d965938e023be Binary files /dev/null and b/doc/source/images/unilogo_bleu.png differ diff --git a/doc/source/index.rst b/doc/source/index.rst index ac48444082d432e5cfdf6e3b689c1f35f08ac2da..2258d313ff9e2374e1844dbb950b548d8be30741 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -10,6 +10,11 @@ PiAnoS documentation ================================== +.. image:: images/unilogo_bleu.* + :align: right + :alt: Université de Lausanne + :target: http://www.unil.ch/esc + Welcome! This is the documentation for PiAnoS |release|, last updated |today|. **News**: diff --git a/doc/source/license.rst b/doc/source/license.rst index 0aa512d50728fbec550bce655cd8a605d6294116..0f1177ef8965e36c8172605dbe2769a39c02d2f4 100644 --- a/doc/source/license.rst +++ b/doc/source/license.rst @@ -51,5 +51,16 @@ The terms of the licenses of third-party libraries can be found in a file named | ``_ | Distributed under the BSD License +| **jQuery Cookie Plugin** +| Copyright 2013 Klaus Hartl +| ``_ +| Released under the MIT license +| **FamFamFam Silk Icons** +| Copyright Mark James +| ``_ +| Licensed under a `Creative Commons Attribution 2.5 License `_ +| **Qute Icon set** +| Copyright Arvid Axelsson +| ``_ diff --git a/heidi/Memoire v. 4.2.1 Master.docx b/heidi/Memoire v. 4.2.1 Master.docx new file mode 100644 index 0000000000000000000000000000000000000000..c182abe52c7cf89384107d7772f864f62f66dfc3 Binary files /dev/null and b/heidi/Memoire v. 4.2.1 Master.docx differ diff --git a/js/backend.admin.js b/js/backend.admin.js index 607891d34f42fb4635a0f12a4837a7f5a6d8685f..8b8564e6fe918b73963ed9515f20161d8f507256 100644 --- a/js/backend.admin.js +++ b/js/backend.admin.js @@ -413,6 +413,26 @@ Pianos4Backend.ImageLibraryPanel = function(parent, params) }; } +Pianos4Backend.renderMessage = function(message) +{ + log(message); + var frame = $('
').addClass('ui-widget pianos4_frame'); + var title = $('
').addClass('ui-widget-header ui-corner-top').text(message.title); + var meta = $('
').css('float','right').css('font-size','0.8em').css('font-weight','normal') + .append($('').text('Posted by ')) + .append($('').text(message.creator).css('font-weight','bold')) + .append($('').text(' on ')) + .append($('').text(pgTimestamptzToDate(message.createdon).toLocaleString())); + title.append(meta); + var content = $('
').addClass('ui-widget-content ui-corner-bottom'); + var nl2br = new RegExp(/\n/g); + var lines = message.value.split(nl2br); + for (var k in lines) + content.append($('
').text(lines[k])); + frame.append(title).append(content); + return frame; +} + Pianos4Backend.AdminHome = function(selector, params) { params = params || {}; @@ -434,6 +454,16 @@ Pianos4Backend.AdminHome = function(selector, params) selector .addClass('pianos4_main') .append(div); + + var loading = $('
').append($('').text('Loading messages, please wait ...')).appendTo(div); + + udb.app().getModel().rpc('getHomeMessages', function(messages) { + loading.remove(); + for (var i in messages) (function(msg) { + var msgFrame = Pianos4Backend.renderMessage(msg); + msgFrame.appendTo(div); + })(messages[i]); + }); } } @@ -546,7 +576,7 @@ Pianos4Backend.AdminExercise = function(selector, params) { title: 'User', render: function(data, i, j, cell) { - cell.append($('').attr('href','#person/'+data[i].personid).text(data[i].person)); + cell.append($('').attr('href','#person/'+data[i].user.id).text(data[i].user.name)); }, },{ title: 'Status', @@ -1007,6 +1037,7 @@ Pianos4Backend.AdminFolder = function(selector, params) Pianos4Backend.ImageManager = function(parent, params) { params = params || {}; + if (!params.pageLength || params.pageLength < 0) params.pageLength = 0; this.init = function() { @@ -1017,141 +1048,346 @@ Pianos4Backend.ImageManager = function(parent, params) var self = this; var content = null; var loading = false; + var toolbar = null; + var table = null; + var allImages = null; + var page = null; + var pageLength = parseInt(params.pageLength); var buildUI = function() { + toolbar = $('
'); + + toolbar + .append($('').addClass('_prev').button({disabled:true}).text('<').click(function() { page=Math.max(0,page-1); updateUI(); renderCurrentPage(); })) + .append($('').css('margin','0 0.5em').html('Showing page / (items / )')) + .append($('').addClass('_next').button({disabled:true}).text('>').click(function() { page=Math.min(getNumPages(),page+1); updateUI(); renderCurrentPage(); })); + + var sel = $('').attr('type',f.type).attr('id', id).attr('name',f.name); - if (f.value) - input.attr('value',f.value); - if (f.disabled) - input.prop('disabled',true); - if (f.type == 'checkbox' && f.checked) - input.prop('checked',true); - form.append($('
').append(label).append(input)); - } - else if (f.type == 'select') - { - assert('values' in f); - var id = f.name+'_'+(new Date().getTime()); - var label = $('