Browse Source

Merge branch '1.10.x' into bootstrap

aragonc 9 years ago
parent
commit
c7a7eeef8f

+ 42 - 31
app/Migrations/Schema/V110/Version20150505142900.php

@@ -22,37 +22,48 @@ class Version20150505142900 extends AbstractMigrationChamilo
     public function up(Schema $schema)
     {
         // Create table for video chat
-        $chatVideoTable = $schema->createTable('chat_video');
-        $chatVideoTable->addColumn(
-            'id',
-            Type::INTEGER,
-            ['unsigned' => true, 'autoincrement' => true, 'notnull' => true]
-        );
-        $chatVideoTable->addColumn(
-            'from_user',
-            Type::INTEGER,
-            ['unsigned' => true, 'notnull' => true]
-        );
-        $chatVideoTable->addColumn(
-            'to_user',
-            Type::INTEGER,
-            ['unsigned' => true, 'notnull' => true]
-        );
-        $chatVideoTable->addColumn(
-            'room_name',
-            Type::STRING,
-            ['length' => 255, 'notnull' => true]
-        );
-        $chatVideoTable->addColumn(
-            'datetime',
-            Type::DATETIME,
-            ['notnull' => true]
-        );
-        $chatVideoTable->setPrimaryKey(['id']);
-        $chatVideoTable->addIndex(['from_user'], 'idx_chat_video_from_user');
-        $chatVideoTable->addIndex(['to_user'], 'idx_chat_video_to_user');
-        $chatVideoTable->addIndex(['from_user', 'to_user'], 'idx_chat_video_users');
-        $chatVideoTable->addIndex(['room_name'], 'idx_chat_video_room_name');
+        if (!$schema->hasTable('chat_video')) {
+            $chatVideoTable = $schema->createTable('chat_video');
+            $chatVideoTable->addColumn(
+                'id',
+                Type::INTEGER,
+                ['unsigned' => true, 'autoincrement' => true, 'notnull' => true]
+            );
+            $chatVideoTable->addColumn(
+                'from_user',
+                Type::INTEGER,
+                ['unsigned' => true, 'notnull' => true]
+            );
+            $chatVideoTable->addColumn(
+                'to_user',
+                Type::INTEGER,
+                ['unsigned' => true, 'notnull' => true]
+            );
+            $chatVideoTable->addColumn(
+                'room_name',
+                Type::STRING,
+                ['length' => 255, 'notnull' => true]
+            );
+            $chatVideoTable->addColumn(
+                'datetime',
+                Type::DATETIME,
+                ['notnull' => true]
+            );
+            $chatVideoTable->setPrimaryKey(['id']);
+            $chatVideoTable->addIndex(
+                ['from_user'],
+                'idx_chat_video_from_user'
+            );
+            $chatVideoTable->addIndex(['to_user'], 'idx_chat_video_to_user');
+            $chatVideoTable->addIndex(
+                ['from_user', 'to_user'],
+                'idx_chat_video_users'
+            );
+            $chatVideoTable->addIndex(
+                ['room_name'],
+                'idx_chat_video_room_name'
+            );
+        }
     }
 
     /**

+ 37 - 35
app/Migrations/Schema/V110/Version20150529164400.php

@@ -17,41 +17,43 @@ class Version20150529164400 extends AbstractMigrationChamilo
      */
     public function up(Schema $schema)
     {
-        $gradebookScoreLog = $schema->createTable('gradebook_score_log');
-        $gradebookScoreLog->addColumn(
-            'id',
-            TableColumnType::INTEGER,
-            ['unsigned' => true, 'autoincrement' => true, 'notnull' => true]
-        );
-        $gradebookScoreLog->addColumn(
-            'category_id',
-            TableColumnType::INTEGER,
-            ['unsigned' => true, 'notnull' => true]
-        );
-        $gradebookScoreLog->addColumn(
-            'user_id',
-            TableColumnType::INTEGER,
-            ['unsigned' => true, 'notnull' => true]
-        );
-        $gradebookScoreLog->addColumn(
-            'score',
-            TableColumnType::FLOAT,
-            ['notnull' => true, 'scale' => 0, 'precision' => 10]
-        );
-        $gradebookScoreLog->addColumn(
-            'registered_at',
-            TableColumnType::DATETIME,
-            ['notnull' => true]
-        );
-        $gradebookScoreLog->setPrimaryKey(['id']);
-        $gradebookScoreLog->addIndex(
-            ['user_id'],
-            'idx_gradebook_score_log_user'
-        );
-        $gradebookScoreLog->addIndex(
-            ['user_id', 'category_id'],
-            'idx_gradebook_score_log_user_category'
-        );
+        if (!$schema->hasTable('gradebook_score_log')) {
+            $gradebookScoreLog = $schema->createTable('gradebook_score_log');
+            $gradebookScoreLog->addColumn(
+                'id',
+                TableColumnType::INTEGER,
+                ['unsigned' => true, 'autoincrement' => true, 'notnull' => true]
+            );
+            $gradebookScoreLog->addColumn(
+                'category_id',
+                TableColumnType::INTEGER,
+                ['unsigned' => true, 'notnull' => true]
+            );
+            $gradebookScoreLog->addColumn(
+                'user_id',
+                TableColumnType::INTEGER,
+                ['unsigned' => true, 'notnull' => true]
+            );
+            $gradebookScoreLog->addColumn(
+                'score',
+                TableColumnType::FLOAT,
+                ['notnull' => true, 'scale' => 0, 'precision' => 10]
+            );
+            $gradebookScoreLog->addColumn(
+                'registered_at',
+                TableColumnType::DATETIME,
+                ['notnull' => true]
+            );
+            $gradebookScoreLog->setPrimaryKey(['id']);
+            $gradebookScoreLog->addIndex(
+                ['user_id'],
+                'idx_gradebook_score_log_user'
+            );
+            $gradebookScoreLog->addIndex(
+                ['user_id', 'category_id'],
+                'idx_gradebook_score_log_user_category'
+            );
+        }
     }
 
     /**

+ 40 - 38
app/Migrations/Schema/V110/Version20150608104600.php

@@ -17,44 +17,46 @@ class Version20150608104600 extends AbstractMigrationChamilo
      */
     public function up(Schema $schema)
     {
-        $extraFieldRelTag = $schema->createTable('extra_field_rel_tag');
-        $extraFieldRelTag->addColumn(
-            'id',
-            TableColumnType::INTEGER,
-            ['unsigned' => true, 'autoincrement' => true, 'notnull' => true]
-        );
-        $extraFieldRelTag->addColumn(
-            'field_id',
-            TableColumnType::INTEGER,
-            ['unsigned' => true, 'notnull' => true]
-        );
-        $extraFieldRelTag->addColumn(
-            'item_id',
-            TableColumnType::INTEGER,
-            ['unsigned' => true, 'notnull' => true]
-        );
-        $extraFieldRelTag->addColumn(
-            'tag_id',
-            TableColumnType::INTEGER,
-            ['unsigned' => true, 'notnull' => true]
-        );
-        $extraFieldRelTag->setPrimaryKey(['id']);
-        $extraFieldRelTag->addIndex(
-            ['field_id'],
-            'idx_frt_field'
-        );
-        $extraFieldRelTag->addIndex(
-            ['item_id'],
-            'idx_frt_item'
-        );
-        $extraFieldRelTag->addIndex(
-            ['tag_id'],
-            'idx_frt_tag'
-        );
-        $extraFieldRelTag->addIndex(
-            ['field_id', 'item_id', 'tag_id'],
-            'idx_frt_field_item_tag'
-        );
+        if (!$schema->hasTable('extra_field_rel_tag')) {
+            $extraFieldRelTag = $schema->createTable('extra_field_rel_tag');
+            $extraFieldRelTag->addColumn(
+                'id',
+                TableColumnType::INTEGER,
+                ['unsigned' => true, 'autoincrement' => true, 'notnull' => true]
+            );
+            $extraFieldRelTag->addColumn(
+                'field_id',
+                TableColumnType::INTEGER,
+                ['unsigned' => true, 'notnull' => true]
+            );
+            $extraFieldRelTag->addColumn(
+                'item_id',
+                TableColumnType::INTEGER,
+                ['unsigned' => true, 'notnull' => true]
+            );
+            $extraFieldRelTag->addColumn(
+                'tag_id',
+                TableColumnType::INTEGER,
+                ['unsigned' => true, 'notnull' => true]
+            );
+            $extraFieldRelTag->setPrimaryKey(['id']);
+            $extraFieldRelTag->addIndex(
+                ['field_id'],
+                'idx_frt_field'
+            );
+            $extraFieldRelTag->addIndex(
+                ['item_id'],
+                'idx_frt_item'
+            );
+            $extraFieldRelTag->addIndex(
+                ['tag_id'],
+                'idx_frt_tag'
+            );
+            $extraFieldRelTag->addIndex(
+                ['field_id', 'item_id', 'tag_id'],
+                'idx_frt_field_item_tag'
+            );
+        }
     }
 
     /**

+ 7 - 0
app/Resources/public/css/base.css

@@ -6330,6 +6330,13 @@ a.sessionView {
 #prev.disabled {
     opacity: 0.2;
 }
+.webcamclip_bg{
+    width:340px;
+    height:260px;
+    border:10px solid;
+    border-radius: 25px;
+    border-color: #00677C;
+}
 /* CSS NEW TOP ******************************************************************************/
 /* CSS Responsive */
 @media (min-width: 1025px) and (max-width: 1200px) {

+ 4 - 0
app/Resources/public/css/scorm.css

@@ -388,6 +388,10 @@ See https://support.chamilo.org/issues/6976
   background:url("../../main/img/scorm/scorm_current.png") no-repeat left center !important;
   color: #009AB8;
 }
+.scorm_item_normal.scorm_highlight.scorm_failed{
+    background:url("../../main/img/scorm/scorm_failed.png") no-repeat left center !important;
+    color: #009AB8;
+}
 .scorm_item_normal.scorm_highlight a{
   color: #009AB8 !important;
 }

+ 1 - 0
composer.json

@@ -76,6 +76,7 @@
         "bower-asset/simplewebrtc": "1.15.*",
         "bower-asset/select2": "4.*",
         "bower-asset/MathJax": "2.5.*",
+        "bower-asset/webcamjs": "1.0.*",
         "clue/graph": "~0.9.0",
         "graphp/graphviz": "~0.2.0",
         "graphp/algorithms": "~0.8.0",

+ 7 - 0
documentation/changelog.html

@@ -54,6 +54,9 @@
 <h3>Release name</h3>
 <p>San Juan (or the "Old San Juan") was the main (old) harbour of Puerto Rico island. A frequent stop-over harbour for Europe's immigrants to the "new world" (be it Latin America or North America). Chamilo 1.10.0 marks a very strong intermediate step between the "old" Chamilo, inheriting over 14 years of code and experiences, and the "new" Chamilo, still maintaining its history of user experiences, but reworking the building bricks in a way that will make new developments possible faster, so that Chamilo can spread to the rest of the world in a rapid but stable way. Chamilo 1.10.0 integrates several new techniques of development that should improve is reliability, speed and flexibility. Welcome to the New World of learning, welcome to Chamilo 1.10.0! (so to speak)</p>
 <h3>Security fixes</h3>
+  <ul>
+    <li>There were no specific security flaws detected during the development of 1.10.0 but standard development procedures and criterias were followed during the development to ensure a very high security level.</li>
+  </ul>
 <h3>Possibly breaking changes</h3>
   <ul>
     <li>Dropped support for PHP 5.3 and inferior (now REQUIRES PHP 5.4 or more)</li>
@@ -962,6 +965,10 @@
   <li>Dropped support for IE8</li>
   <li>Dropped support for PHP 5.3 and inferior</li>
 </ul>
+<h3>Known issues</h3>
+<ul>
+  <li>The hotspot question type is broken due to changes in the underlying Chamilo code. Given the fact this question type is developed in Flash and none of the developers at the time of release had Adobe CS available (proprietary technology that requires compilation) we have been unable to update the corresponding Flash code. Work is under way to provide the same feature using only HTML5, but it is likely to appear in a corrective version for 1.10.0. If you have an urgent need for this feature, please contact an official provider to get this sorted: providers@chamilo.org</li>
+</ul>
 
 <a  name="1.9.10.2"></a>
 

+ 5 - 5
main/admin/career_dashboard.php

@@ -138,24 +138,24 @@ foreach($career_array as $career_id => $data) {
         echo '</tr>';
 
         if (!empty($sessions))
-        foreach($sessions as $session) {
+        foreach ($sessions as $session) {
             $course_list = $session['courses'];
 
             $url = Display::url($session['data']['name'], 'resume_session.php?id_session='.$session['data']['id']);
             echo '<tr>';
                 //Session name
-                echo Display::tag('td',$url);
+                echo Display::tag('td', $url);
                 echo '<td>';
                     //Courses
                     echo '<table>';
-                    foreach($course_list as $course) {
+                    foreach ($course_list as $course) {
                        echo '<tr>';
 
                        $url = Display::url(
                            $course['title'],
-                           api_get_path(WEB_COURSE_PATH).$course['directory'].'/?id_session='.$session['data']['id']
+                           api_get_path(WEB_COURSE_PATH).$course['directory'].'/index.php?id_session='.$session['data']['id']
                        );
-                       echo Display::tag('td',$url);
+                       echo Display::tag('td', $url);
                        echo '</tr>';
                     }
                     echo '</table>';

+ 76 - 24
main/admin/promotions.php

@@ -25,36 +25,76 @@ $check = Security::check_token('request');
 $token = Security::get_token();
 
 if ($action == 'add') {
-    $interbreadcrumb[]=array('url' => 'promotions.php','name' => get_lang('Promotions'));
-    $interbreadcrumb[]=array('url' => '#','name' => get_lang('Add'));
+    $interbreadcrumb[] = array(
+        'url' => 'promotions.php',
+        'name' => get_lang('Promotions'),
+    );
+    $interbreadcrumb[] = array('url' => '#', 'name' => get_lang('Add'));
 } elseif ($action == 'edit') {
-    $interbreadcrumb[]=array('url' => 'promotions.php','name' => get_lang('Promotions'));
-    $interbreadcrumb[]=array('url' => '#','name' => get_lang('Edit'));
+    $interbreadcrumb[] = array(
+        'url' => 'promotions.php',
+        'name' => get_lang('Promotions'),
+    );
+    $interbreadcrumb[] = array('url' => '#', 'name' => get_lang('Edit'));
 } else {
-    $interbreadcrumb[]=array('url' => '#','name' => get_lang('Promotions'));
+    $interbreadcrumb[] = array('url' => '#', 'name' => get_lang('Promotions'));
 }
 
 // The header.
-Display::display_header($tool_name);
+Display::display_header('');
 
 // Tool name
 if (isset($_GET['action']) && $_GET['action'] == 'add') {
     $tool = 'Add';
-    $interbreadcrumb[] = array ('url' => api_get_self(), 'name' => get_lang('Promotion'));
+    $interbreadcrumb[] = array(
+        'url' => api_get_self(),
+        'name' => get_lang('Promotion'),
+    );
 }
 if (isset($_GET['action']) && $_GET['action'] == 'edit') {
     $tool = 'Modify';
-    $interbreadcrumb[] = array ('url' => api_get_self(), 'name' => get_lang('Promotion'));
+    $interbreadcrumb[] = array(
+        'url' => api_get_self(),
+        'name' => get_lang('Promotion'),
+    );
 }
 
-$url            = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_promotions';
+$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_promotions';
 //The order is important you need to check the model.ajax.php the $column variable
-$columns        = array(get_lang('Name'),get_lang('Career'),get_lang('Description'),get_lang('Actions'));
-$column_model   = array(
-    array('name'=>'name',           'index'=>'name',        'width'=>'180',   'align'=>'left'),
-    array('name'=>'career',         'index'=>'career',      'width'=>'100',  'align'=>'left'),
-    array('name'=>'description',    'index'=>'description', 'width'=>'500',  'align'=>'left','sortable'=>'false'),
-    array('name'=>'actions',        'index'=>'actions',     'width'=>'100',  'align'=>'left','formatter'=>'action_formatter','sortable'=>'false'),
+$columns = array(
+    get_lang('Name'),
+    get_lang('Career'),
+    get_lang('Description'),
+    get_lang('Actions'),
+);
+$column_model = array(
+    array(
+        'name' => 'name',
+        'index' => 'name',
+        'width' => '180',
+        'align' => 'left',
+    ),
+    array(
+        'name' => 'career',
+        'index' => 'career',
+        'width' => '100',
+        'align' => 'left',
+    ),
+    array(
+        'name' => 'description',
+        'index' => 'description',
+        'width' => '500',
+        'align' => 'left',
+        'sortable' => 'false',
+    ),
+    array(
+        'name' => 'actions',
+        'index' => 'actions',
+        'width' => '100',
+        'align' => 'left',
+        'formatter' => 'action_formatter',
+        'sortable' => 'false',
+    ),
 );
 $extra_params['autowidth'] = 'true'; //use the width of the parent
 //$extra_params['editurl'] = $url; //use the width of the parent
@@ -65,15 +105,15 @@ $action_links = 'function action_formatter (cellvalue, options, rowObject) {
     return \'<a href="add_sessions_to_promotion.php?id=\'+options.rowId+\'">'.Display::return_icon('session_to_promotion.png',get_lang('SubscribeSessionsToPromotions'),'',ICON_SIZE_SMALL).'</a>'.
     '&nbsp;<a href="?action=edit&id=\'+options.rowId+\'">'.Display::return_icon('edit.png',get_lang('Edit'),'',ICON_SIZE_SMALL).'</a>'.
     '&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"),ENT_QUOTES))."\'".')) return false;"  href="?sec_token='.$token.'&action=copy&id=\'+options.rowId+\'">'.Display::return_icon('copy.png',get_lang('Copy'),'',ICON_SIZE_SMALL).'</a>'.
-    '&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"),ENT_QUOTES))."\'".')) return false;"  href="?sec_token='.$token.'&action=delete&id=\'+options.rowId+\'">'.Display::return_icon('delete.png',get_lang('Delete'),'',ICON_SIZE_SMALL).'</a> \'; 
+    '&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"),ENT_QUOTES))."\'".')) return false;"  href="?sec_token='.$token.'&action=delete&id=\'+options.rowId+\'">'.Display::return_icon('delete.png',get_lang('Delete'),'',ICON_SIZE_SMALL).'</a> \';
 }';
 
 ?>
 <script>
 $(function() {
-    <?php
-         echo Display::grid_js('promotions',  $url,$columns,$column_model,$extra_params,array(), $action_links, true);
-    ?>
+<?php
+     echo Display::grid_js('promotions', $url, $columns, $column_model, $extra_params, array(), $action_links, true);
+?>
 });
 </script>
 <?php
@@ -118,7 +158,7 @@ switch ($action) {
         }
         break;
     case 'edit':
-        //Editing 
+        //Editing
         $url  = api_get_self().'?action='.Security::remove_XSS($_GET['action']).'&id='.intval($_GET['id']);
         $form = $promotion->return_form($url, 'edit');
 
@@ -127,15 +167,23 @@ switch ($action) {
             if ($check) {
                 $values = $form->exportValues();
                 $res    = $promotion->update($values);
-                $promotion->update_all_sessions_status_by_promotion_id($values['id'], $values['status']);  
+                $promotion->update_all_sessions_status_by_promotion_id($values['id'], $values['status']);
                 if ($res) {
                     Display::display_confirmation_message(get_lang('PromotionUpdated'), $values['name']);
                 }
-            }            
+            }
             $promotion->display();
         } else {
             echo '<div class="actions">';
-            echo Display::url(Display::return_icon('back.png',get_lang('Back'),'',ICON_SIZE_MEDIUM), api_get_self());
+            echo Display::url(
+                Display::return_icon(
+                    'back.png',
+                    get_lang('Back'),
+                    '',
+                    ICON_SIZE_MEDIUM
+                ),
+                api_get_self()
+            );
             echo '</div>';
             $form->addElement('hidden', 'sec_token');
             $form->setConstants(array('sec_token' => $token));
@@ -159,7 +207,11 @@ switch ($action) {
         if ($check) {
             $res = $promotion->copy($_GET['id'], null, true);
             if ($res) {
-                Display::display_confirmation_message(get_lang('ItemCopied').' - '.get_lang('ExerciseAndLPsAreInvisibleInTheNewCourse'));
+                Display::display_confirmation_message(
+                    get_lang('ItemCopied').' - '.get_lang(
+                        'ExerciseAndLPsAreInvisibleInTheNewCourse'
+                    )
+                );
             }
         }
         $promotion->display();

+ 2 - 2
main/document/create_draw.php

@@ -40,7 +40,7 @@ if ($_SESSION['draw_dir']=='/'){
     $_SESSION['draw_dir']='';
 }
 
-$dir = isset($dir) ? Security::remove_XSS($dir) : Security::remove_XSS($_POST['dir']);
+$dir = isset($dir) ? Security::remove_XSS($dir) : (isset($_POST['dir']) ? Security::remove_XSS($_POST['dir']) : '/');
 $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
 
 // Please, do not modify this dirname formatting
@@ -80,7 +80,7 @@ if (!empty($groupId)) {
 	}
 }
 
-$interbreadcrumb[] = array ("url" => "./document.php?id=".$parent_id."&".api_get_cidreq(), "name" => get_lang('Documents'));
+$interbreadcrumb[] = array ("url" => "./document.php?".api_get_cidreq(), "name" => get_lang('Documents'));
 
 if (!$is_allowed_in_course) {
 	api_not_allowed(true);

+ 98 - 69
main/document/webcam_clip.php

@@ -8,6 +8,7 @@
  *
  * @author Juan Carlos Raña Trabado herodoto@telefonica.net
  * @since 7/jun/2012
+ * @Updated 04/09/2015 Upgrade to WebCamJS
 */
 require_once '../inc/global.inc.php';
 
@@ -127,78 +128,77 @@ echo '</div>';
 <div align="center">
     <h2><?php echo get_lang('TakeYourPhotos'); ?></h2>
 </div>
-<div align="center" style="padding-left:50px;">
-<table><tr><td valign=top>
-<h3><?php echo get_lang('LocalInputImage'); ?></h3>
-<!-- First, include the JPEGCam JavaScript Library -->
-	<script type="text/javascript" src="<?php echo api_get_path(WEB_LIBRARY_PATH); ?>jpegcam/webcam.js"></script>
-
-	<!-- Configure a few settings -->
-	<script language="JavaScript">
-		var clip_filename='video_clip.jpg';
-		//var clip_filename='<?php //echo date('YmdHis') . '.jpg'; ?>';
-		webcam.set_swf_url ( '<?php echo api_get_path(WEB_LIBRARY_PATH); ?>jpegcam//webcam.swf?blackboard.png' );
-		webcam.set_shutter_sound( true,'<?php echo api_get_path(WEB_LIBRARY_PATH); ?>jpegcam/shutter.mp3' ); // true play shutter click sound
-		webcam.set_quality( 90 ); // JPEG quality (1 - 100)
-		webcam.set_api_url( '<?php echo api_get_path(WEB_LIBRARY_PATH); ?>jpegcam/webcam_receiver.php?webcamname='+escape(clip_filename)+'&webcamdir=<?php echo $webcamdir; ?>&webcamuserid=<?php echo $webcamuserid; ?>' );
-
-	</script>
-
-	<!-- Next, write the movie to the page at 320x240 -->
+<div align="center">
+<table><tr><td valign='top' align='center'>
+<h3 align='center'><?php echo get_lang('LocalInputImage'); ?></h3>
+    <!-- Including New Lib WebCamJS upgrading from JPEGCam -->
+    <script type="text/javascript" src="<?php echo api_get_path(WEB_PATH); ?>web/assets/webcamjs/webcam.js"></script>
+    <!-- Adding a div container for the new live camera with some cool style options-->
+    <div class="webcamclip_bg">
+        <div id="chamilo-camera"></div>
+    </div>
+	<!-- Configure a few settings and attach the camera to the div container -->
 	<script language="JavaScript">
-		document.write( webcam.get_html(320, 240) );
+		Webcam.set({
+            width: 320,
+            height: 240,
+            image_format: 'jpeg',
+            jpeg_quality: 90
+        });
+        Webcam.attach( '#chamilo-camera' );
+        //This line Fix a minor bug that made a conflict with a videochat feature in another module file
+        $('video').addClass('skip');
 	</script>
 
-	<!-- Some buttons for controlling things -->
-	<br/>
-    <form>
-    <br/>
-		<input type=button value="<?php echo get_lang('Snapshot'); ?>" onClick="webcam.freeze()">
-		<input type=button value="<?php echo get_lang('Clean'); ?>" onClick="webcam.reset()">
-        <input type=button value="<?php echo get_lang('Send'); ?>" onClick="do_upload()">
-        &nbsp;&nbsp;||&nbsp;&nbsp;
-        <input type=button value="<?php echo get_lang('Auto'); ?>" onClick="start_video();">
-		<input type=button value="<?php echo get_lang('Stop'); ?>" onClick="stop_video()">
-        <br/>
-        <input type=button value="<?php echo get_lang('Configure'); ?>" onClick="webcam.configure()">
-
-	</form>
-
-	<!-- Code to handle the server response (see webcam_receiver.php) -->
+	<!-- Using now Jquery to do the webcamJS Functions and handle the server response (see webcam_receiver.php now in webcamJS directory) -->
 	<script language="JavaScript">
-		webcam.set_hook( 'onComplete', 'my_completion_handler' );
-
-		function do_upload() {
-			// upload to server
-			if (this.loaded){
-				document.getElementById('upload_results').innerHTML = '<h3><?php echo get_lang('Uploading'); ?></h3>';
-			}
-			webcam.upload();
-		}
-
-		function my_completion_handler(msg) {
-			// extract URL out of PHP output
-			if (msg.match(/(http\:\/\/\S+)/)) {
-				var image_url = RegExp.$1;
-
-				image_url=image_url.replace(/\\/g,'/').replace( /.*\//, '' );// extract basename
-				image_url='<?php echo api_get_path(WEB_COURSE_PATH).$_course['path'].'/document/'.$dir;?>'+image_url+'<?php echo '?'.api_get_cidreq(); ?>';
-
-				// show JPEG image in page
-				document.getElementById('upload_results').innerHTML =
-				'<div style="width: 320px;">' +
-					'<h3><?php echo get_lang('ClipSent'); ?></h3>' +
-					'<img src="' + image_url + '">' +
-					'</div>';
-				// reset camera for another shot
-				webcam.reset();
-			}
-			else alert("PHP Error: " + msg);
-		}
+		$(document).ready(function() {
+           $('#btnCapture').click(function() {
+               Webcam.freeze();
+               return false;
+           });
+           
+           $('#btnClean').click(function() {
+               Webcam.unfreeze();
+               return false;
+           });
+           
+           $('#btnSave').click(function() {
+               snap();
+               return false;
+           });
+           
+           $('#btnAuto').click(function() {
+               start_video();
+               return false;
+           });
+           
+           $('#btnStop').click(function() {
+               stop_video();
+               return false;
+           });
+        });
+        
+        function snap() {
+            Webcam.snap( function(data_uri) {
+                  var clip_filename='video_clip.jpg';
+                  var url = 'webcam_receiver.php?webcamname='+escape(clip_filename)+'&webcamdir=<?php echo $webcamdir; ?>&webcamuserid=<?php echo $webcamuserid; ?>';
+                  Webcam.upload(data_uri, url, function(code, response){
+                      $('#upload_results').html(
+                          '<h3>'+response+'</h3>' +
+                          '<div>'+
+                          '<img src="' + data_uri + '" class="webcamclip_bg">' +
+                          '</div>'+
+                          '</div>'+
+                          '<p hidden="true">'+code+'</p>'
+                      );
+                  });
+              });
+        }
 	</script>
 
      <script language=javascript>
-	   var internaval=null;
+	   var interval=null;
 	   var timeout=null;
 	   var counter=0;
 	   var fps=1000;//one frame per second
@@ -207,18 +207,18 @@ echo '</div>';
 
 	   function stop_video() {
 			interval=window.clearInterval(interval);
+            return false;
 	   }
 
 	   function start_video() {
-		   	webcam.set_stealth( true ); // do not freeze image upon capture
 		 	interval=window.setInterval("clip_send_video()",fps);
 	   }
 
 	   function clip_send_video() {
-		   counter++
+		   counter++;
 		   timeout=setTimeout('stop_video()',maxtime);
 		   if(maxclip>=counter){
-		       webcam.snap();// clip and upload
+		       snap();// clip and upload
 		   }
 		   else {
 			   interval=window.clearInterval(interval);
@@ -227,9 +227,38 @@ echo '</div>';
  </script>
 
 
-	</td><td width=50>&nbsp;</td><td valign=top>
+	</td><td width=50></td><td valign='top' align='center'>
 		<div id="upload_results" style="background-color:#ffffff;"></div>
 	</td></tr></table>
+    
+    <!-- Implementing Button html5 Tags instead Inputs and some cool bootstrap button styles -->
+    <div>
+        <br/>
+            <form>
+                <br/>
+                <button id="btnCapture" class="btn btn-danger">
+                <i class="fa fa-camera"></i>
+                <?php echo get_lang('Snapshot'); ?>
+                </button>
+                <button id="btnClean" class="btn btn-success">
+                <i class="fa fa-refresh"></i>
+                <?php echo get_lang('Clean'); ?>
+                </button>
+                <button id="btnSave" class="btn btn-primary">
+                <i class="fa fa-save"></i>
+                <?php echo get_lang('Save'); ?>
+                </button>
+                &nbsp;&nbsp;||&nbsp;&nbsp;
+                <button id="btnAuto" class="btn btn-default">
+                <?php echo get_lang('Auto'); ?>
+                </button>
+                <button id="btnStop" class="btn btn-default">
+                <?php echo get_lang('Stop'); ?>
+                </button>
+                <br/>
+            </form>
+        <br/>
+   </div>
 </div>
 
 <?php

+ 10 - 18
main/inc/lib/jpegcam/webcam_receiver.php → main/document/webcam_receiver.php

@@ -1,9 +1,11 @@
 <?php
 
-/* JPEGCam Script */
+/* JPEGCam Script *****UPDATED to lib webcamJS 2015-09-04***** */
 /* Receives JPEG webcam submission and saves to local file. */
 /* Make sure your directory has permission to write files as your web server user! */
-require_once '../../../inc/global.inc.php';
+
+//Changes on directory because move the proper script to the new lib upgrade directory
+require_once '../inc/global.inc.php';
 ////Add security from Chamilo
 api_protect_course_script();
 api_block_anonymous_users();
@@ -46,11 +48,11 @@ if($ext!= 'jpg'){
 //Do not use here check Fileinfo method because return: text/plain                //CHECK THIS BEFORE COMMIT
 
 $dirBaseDocuments = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document';
-$saveDir=$dirBaseDocuments.$webcamdir;
+$saveDir = $dirBaseDocuments.$webcamdir;
 $current_session_id = api_get_session_id();
 $groupId = api_get_group_id();
 
-//avoid duplicates
+//Avoid duplicates
 $webcamname_to_save=$webcamname;
 $title_to_save=str_replace('_',' ',$webcamname);
 $webcamname_noex=basename($webcamname, ".jpg");
@@ -62,29 +64,19 @@ if (file_exists($saveDir.'/'.$webcamname_noex.'.'.$ext)){
 		$title_to_save = str_replace('_',' ',$title_to_save);
 }
 
-
 $documentPath = $saveDir.'/'.$webcamname_to_save;
 
-
 //read content
-$content = file_get_contents('php://input');
+//Change to move_uploaded_file() function instead file_get_contents() to adapt the new lib
+$content = move_uploaded_file($_FILES['webcam']['tmp_name'], $documentPath);
 if (!$content) {
-	print "ERROR: Failed to read data\n";
+	print "PHP ERROR: Failed to read data\n";
 	exit();
 }
 
-//add to disk
-$fh = fopen($documentPath, 'w') or die("can't open file");
-fwrite($fh, $content);
-fclose($fh);
-
-
-//
 //add document to database
 	$doc_id = add_document($_course, $webcamdir.'/'.$webcamname_to_save, 'file', filesize($documentPath), $title_to_save);
 	api_item_property_update($_course, TOOL_DOCUMENT, $doc_id, 'DocumentAdded', $_user['user_id'], $groupId, null, null, null, $current_session_id);
 ///
 $url = 'http://' . $_SERVER['HTTP_HOST'] . dirname($_SERVER['REQUEST_URI']) . '/' . $documentPath;
-print "$url\n";
-
-?>
+print get_lang('ClipSent');

+ 34 - 11
main/exercice/answer.class.php

@@ -71,6 +71,7 @@ class Answer
         $objExercise = new Exercise($this->course_id);
         $exerciseId = isset($_REQUEST['exerciseId']) ? $_REQUEST['exerciseId'] : null;
         $objExercise->read($exerciseId);
+
         if ($objExercise->random_answers == '1') {
             $this->readOrderedBy('rand()', '');// randomize answers
         } else {
@@ -198,17 +199,36 @@ class Answer
 			$order = 'ASC';
 		}
 
-		$TBL_ANSWER   = Database::get_course_table(TABLE_QUIZ_ANSWER);
-		$TBL_QUIZ     = Database::get_course_table(TABLE_QUIZ_QUESTION);
+		$TBL_ANSWER = Database::get_course_table(TABLE_QUIZ_ANSWER);
+		$TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_QUESTION);
 		$questionId = intval($this->questionId);
 
 		$sql = "SELECT type FROM $TBL_QUIZ
 		        WHERE c_id = {$this->course_id} AND id = $questionId";
 		$result_question = Database::query($sql);
-		$question_type = Database::fetch_array($result_question);
+		$questionType = Database::fetch_array($result_question);
 
-		$sql = "SELECT answer,correct,comment,ponderation,position, hotspot_coordinates, hotspot_type, destination, id_auto
-                FROM $TBL_ANSWER WHERE c_id = {$this->course_id} AND question_id='".$questionId."'
+        if ($questionType['type'] == DRAGGABLE) {
+            // Random is done by submit.js.tpl
+            $this->read();
+
+            return true;
+        }
+
+		$sql = "SELECT
+		            answer,
+		            correct,
+		            comment,
+		            ponderation,
+		            position,
+		            hotspot_coordinates,
+		            hotspot_type,
+		            destination,
+		            id_auto
+                FROM $TBL_ANSWER
+                WHERE
+                    c_id = {$this->course_id} AND
+                    question_id='".$questionId."'
                 ORDER BY $field $order";
 		$result=Database::query($sql);
 
@@ -216,7 +236,7 @@ class Answer
 		// while a record is found
 		$doubt_data = null;
 		while ($object = Database::fetch_object($result)) {
-		    if ($question_type['type'] == UNIQUE_ANSWER_NO_OPTION && $object->position == 666) {
+		    if ($questionType['type'] == UNIQUE_ANSWER_NO_OPTION && $object->position == 666) {
 		        $doubt_data = $object;
                 continue;
 		    }
@@ -230,7 +250,7 @@ class Answer
             $i++;
 		}
 
-		if ($question_type['type'] == UNIQUE_ANSWER_NO_OPTION && !empty($doubt_data)) {
+		if ($questionType['type'] == UNIQUE_ANSWER_NO_OPTION && !empty($doubt_data)) {
             $this->answer[$i] = $doubt_data->answer;
             $this->correct[$i] = $doubt_data->correct;
             $this->comment[$i] = $doubt_data->comment;
@@ -312,7 +332,7 @@ class Answer
 		$rs = Database::query($sql);
 
 		if (Database::num_rows($rs) > 0) {
-			$row = Database::fetch_array($rs);
+			$row = Database::fetch_array($rs, 'ASSOC');
 
 			return $row;
 		}
@@ -695,7 +715,10 @@ class Answer
             self::getQuestionType() == MULTIPLE_ANSWER_TRUE_FALSE
         ) {
             // Selecting origin options
-            $origin_options = Question::readQuestionOption($this->selectQuestionId(), $this->course['real_id']);
+            $origin_options = Question::readQuestionOption(
+                $this->selectQuestionId(),
+                $this->course['real_id']
+            );
 
             if (!empty($origin_options)) {
                 foreach ($origin_options as $item) {
@@ -750,7 +773,7 @@ class Answer
 
                 $params = [
                     'c_id' => $c_id,
-                    'question_id' =>$newQuestionId,
+                    'question_id' => $newQuestionId,
                     'answer' => $answer,
                     'correct' => $correct,
                     'comment' => $comment,
@@ -763,7 +786,7 @@ class Answer
                 $id = Database::insert($TBL_REPONSES, $params);
 
                 if ($id) {
-                    $sql = "UPDATE $TBL_REPONSES SET id = id_auto WHERE id_auto = $id";
+                    $sql = "UPDATE $TBL_REPONSES SET id = iid, id_auto = iid WHERE iid = $id";
                     Database::query($sql);
                 }
 			}

+ 25 - 12
main/exercice/exercise.class.php

@@ -69,10 +69,10 @@ class Exercise
         $this->expired_time = '0000-00-00 00:00:00';
         $this->propagate_neg = 0;
         $this->review_answers = false;
-        $this->randomByCat = 0;    //
-        $this->text_when_finished = ""; //
+        $this->randomByCat = 0;
+        $this->text_when_finished = '';
         $this->display_category_name = 0;
-        $this->pass_percentage = null;
+        $this->pass_percentage = '';
 
         if (!empty($course_id)) {
             $course_info = api_get_course_info_by_id($course_id);
@@ -1286,7 +1286,12 @@ class Exercise
             );
             $form->addElement('html','</div>');
 
-            $form->addElement('text', 'pass_percentage', array(get_lang('PassPercentage'), null, '%'),  array('id' => 'pass_percentage'));
+            $form->addElement(
+                'text',
+                'pass_percentage',
+                array(get_lang('PassPercentage'), null, '%'),
+                array('id' => 'pass_percentage')
+            );
             $form->addRule('pass_percentage', get_lang('Numeric'), 'numeric');
 
             // add the text_when_finished textbox
@@ -2866,6 +2871,7 @@ class Exercise
                                         exe_id = '$exeId' AND
                                         question_id = '$questionId' AND
                                         position = '$i_answer_id_auto'";
+
                             $res_user_answer = Database::query($sql);
 
                             if (Database::num_rows($res_user_answer) > 0) {
@@ -2879,19 +2885,22 @@ class Exercise
 
                             $user_answer = '';
                             if (!empty($s_user_answer)) {
-                                if ($s_user_answer == $i_answer_correct_answer) {
-                                    $questionScore += $i_answerWeighting;
-                                    $totalScore += $i_answerWeighting;
-                                    if ($answerType == DRAGGABLE) {
+                                if ($answerType == DRAGGABLE) {
+                                    if ($s_user_answer == $i_answer_correct_answer) {
+                                        $questionScore += $i_answerWeighting;
+                                        $totalScore += $i_answerWeighting;
                                         $user_answer = Display::label(get_lang('Correct'), 'success');
                                     } else {
+                                        $user_answer = Display::label(get_lang('Incorrect'), 'danger');
+                                    }
+                                } else {
+                                    if ($s_user_answer == $i_answer_correct_answer) {
+                                        $questionScore += $i_answerWeighting;
+                                        $totalScore += $i_answerWeighting;
+
                                         if (isset($real_list[$i_answer_id])) {
                                             $user_answer = Display::span($real_list[$i_answer_id]);
                                         }
-                                    }
-                                } else {
-                                    if ($answerType == DRAGGABLE) {
-                                        $user_answer = Display::label(get_lang('Incorrect'), 'danger');
                                     } else {
                                         $user_answer = Display::span(
                                             $real_list[$s_user_answer],
@@ -4745,6 +4754,10 @@ class Exercise
         return $new_question_list;
     }
 
+    /**
+     * @param int $exe_id
+     * @return array|mixed
+     */
     public function get_stat_track_exercise_info_by_exe_id($exe_id)
     {
         $track_exercises = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);

+ 16 - 5
main/exercice/exercise_result.php

@@ -33,7 +33,10 @@ if ($debug) {
 if (empty($origin)) {
     $origin = Security::remove_XSS($_REQUEST['origin']);
 }
+
+/** @var Exercise $objExercise */
 if (empty($objExercise)) {
+
     $objExercise = $_SESSION['objExercise'];
 }
 if (empty($remind_list)) {
@@ -59,16 +62,22 @@ if (isset($_SESSION['gradebook'])) {
 	$gradebook=	$_SESSION['gradebook'];
 }
 if (!empty($gradebook) && $gradebook=='view') {
-	$interbreadcrumb[]= array ('url' => '../gradebook/'.$_SESSION['gradebook_dest'], 'name' => get_lang('ToolGradebook'));
+    $interbreadcrumb[] = array(
+        'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
+        'name' => get_lang('ToolGradebook'),
+    );
 }
 
 $nameTools = get_lang('Exercises');
 
-$interbreadcrumb[]= array("url" => "exercise.php?gradebook=$gradebook","name" => get_lang('Exercises'));
+$interbreadcrumb[] = array(
+    "url" => "exercise.php?".api_get_cidreq(),
+    "name" => get_lang('Exercises'),
+);
 
 if ($origin != 'learnpath') {
 	// So we are not in learnpath tool
-	Display::display_header($nameTools,get_lang('Exercise'));
+	Display::display_header($nameTools, get_lang('Exercise'));
 } else {
     Display::display_reduced_header();
 }
@@ -78,8 +87,10 @@ if ($origin != 'learnpath') {
 // I'm in a preview mode as course admin. Display the action menu.
 if (api_is_course_admin() && $origin != 'learnpath') {
 	echo '<div class="actions">';
-	echo '<a href="admin.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'">'.Display::return_icon('back.png', get_lang('GoBackToQuestionList'), array(), 32).'</a>';
-	echo '<a href="exercise_admin.php?'.api_get_cidreq().'&modifyExercise=yes&exerciseId='.$objExercise->id.'">'.Display::return_icon('edit.png', get_lang('ModifyExercise'), array(), 32).'</a>';
+	echo '<a href="admin.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id.'">'.
+        Display::return_icon('back.png', get_lang('GoBackToQuestionList'), array(), 32).'</a>';
+	echo '<a href="exercise_admin.php?'.api_get_cidreq().'&modifyExercise=yes&exerciseId='.$objExercise->id.'">'.
+        Display::return_icon('edit.png', get_lang('ModifyExercise'), array(), 32).'</a>';
 	echo '</div>';
 }
 

+ 1 - 2
main/exercice/exercise_show.php

@@ -776,8 +776,7 @@ if ($origin != 'learnpath') {
 		$url = '../newscorm/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.$learnpath_id.'&lp_item_id='.$learnpath_item_id.'&exeId='.$exeId.'&fb_type='.$feedback_type;
 		$href = ($lp_mode == 'fullscreen')?' window.opener.location.href="'.$url.'" ':' top.location.href="'.$url.'" ';
 		echo '<script type="text/javascript">'.$href.'</script>';
-
-		//Record the results in the learning path, using the SCORM interface (API)
+		// Record the results in the learning path, using the SCORM interface (API)
 		echo "<script>window.parent.API.void_save_asset('$totalScore', '$totalWeighting', 0, 'completed'); </script>";
 		echo '</body></html>';
 	} else {

+ 39 - 9
main/exercice/exercise_submit.php

@@ -62,8 +62,6 @@ $htmlHeadXtra[] = api_get_js('epiclock/renderers/minute/epiclock.minute.js');
 
 $template = new Template();
 
-$htmlHeadXtra[] = $template->fetch('default/exercise/submit.js.tpl');
-
 // General parameters passed via POST/GET
 
 $learnpath_id = isset($_REQUEST['learnpath_id']) ? intval($_REQUEST['learnpath_id']) : 0;
@@ -94,13 +92,15 @@ $exercise_attempt_table = Database :: get_main_table(TABLE_STATISTIC_TRACK_E_ATT
 /*  Teacher takes an exam and want to see a preview,
     we delete the objExercise from the session in order to get the latest
     changes in the exercise */
-if (api_is_allowed_to_edit(null,true) && isset($_GET['preview']) && $_GET['preview'] == 1 ) {
+if (api_is_allowed_to_edit(null, true) && isset($_GET['preview']) && $_GET['preview'] == 1 ) {
     Session::erase('objExercise');
 }
 
 // 1. Loading the $objExercise variable
 
-if (!isset($_SESSION['objExercise']) || $_SESSION['objExercise']->id != $_REQUEST['exerciseId']) {
+if (!isset($_SESSION['objExercise']) ||
+    $_SESSION['objExercise']->id != $_REQUEST['exerciseId']
+) {
     // Construction of Exercise
     $objExercise = new Exercise();
     Session::write('firstTime', true);
@@ -143,6 +143,8 @@ if ($objExercise->review_answers) {
         exit;
     }
 }
+$template->assign('shuffle_answers', $objExercise->random_answers);
+$htmlHeadXtra[] = $template->fetch('default/exercise/submit.js.tpl');
 
 $current_timestamp = time();
 $my_remind_list = array();
@@ -190,9 +192,20 @@ if ($objExercise->selectAttempts() > 0) {
                 if (!empty($exercise_stat_info)) {
                     $max_exe_id = max(array_keys($exercise_stat_info));
                     $last_attempt_info = $exercise_stat_info[$max_exe_id];
-                    $attempt_html .= Display::div(get_lang('Date').': '.api_get_local_time($last_attempt_info['exe_date']), array('id'=>''));
+                    $attempt_html .= Display::div(
+                        get_lang('Date').': '.api_get_local_time($last_attempt_info['exe_date']),
+                        array('id' => '')
+                    );
 
-                    $attempt_html .= Display::return_message(sprintf(get_lang('ReachedMaxAttempts'), $exercise_title, $objExercise->selectAttempts()), 'warning', false);
+                    $attempt_html .= Display::return_message(
+                        sprintf(
+                            get_lang('ReachedMaxAttempts'),
+                            $exercise_title,
+                            $objExercise->selectAttempts()
+                        ),
+                        'warning',
+                        false
+                    );
 
                     if (!empty($last_attempt_info['question_list'])) {
                         foreach($last_attempt_info['question_list'] as $question_data) {
@@ -734,7 +747,16 @@ if (!empty($objExercise->description)) {
             });
          });
         </script>";
-    echo Display::generate_accordion(array(array('title' => get_lang('ExerciseDescriptionLabel'), 'content' => $objExercise->description)), 'jquery', 'description_content');
+    echo Display::generate_accordion(
+        array(
+            array(
+                'title' => get_lang('ExerciseDescriptionLabel'),
+                'content' => $objExercise->description,
+            ),
+        ),
+        'jquery',
+        'description_content'
+    );
 }
 
 if ($origin != 'learnpath') {
@@ -1039,7 +1061,6 @@ if (!empty($error)) {
     }
 
     foreach ($questionList as $questionId) {
-
         // for sequential exercises
         if ($objExercise->type == ONE_PER_PAGE) {
             // if it is not the right question, goes to the next loop iteration
@@ -1088,7 +1109,16 @@ if (!empty($error)) {
         echo '<div id="question_div_'.$questionId.'" class="main-question '.$remind_highlight.'" >';
 
         // Shows the question and its answers
-        ExerciseLib::showQuestion($questionId, false, $origin, $i, true, false, $user_choice, false);
+        ExerciseLib::showQuestion(
+            $questionId,
+            false,
+            $origin,
+            $i,
+            true,
+            false,
+            $user_choice,
+            false
+        );
 
         // Button save and continue
         switch ($objExercise->type) {

+ 5 - 2
main/exercice/question.class.php

@@ -1327,9 +1327,12 @@ abstract class Question
                     $item['c_id'] = $course_id;
                     unset($item['id']);
                     $id = Database::insert($TBL_QUESTION_OPTIONS, $item);
+                    if ($id) {
 
-                    $sql = "UPDATE $TBL_QUESTION_OPTIONS SET id = iid WHERE iid = $id";
-                    Database::query($sql);
+                        $sql = "UPDATE $TBL_QUESTION_OPTIONS SET id = iid
+                                WHERE iid = $id";
+                        Database::query($sql);
+                    }
                 }
             }
 

+ 0 - 1
main/exercice/showinframes.php

@@ -83,7 +83,6 @@ $htmlHeadXtra[] = '
             height  = "95%";
         }
         $("#hotpotatoe").css("height", height);
-        console.log(height);
     });
 </script>';
 

+ 9 - 9
main/inc/lib/exercise.lib.php

@@ -39,6 +39,7 @@ class ExerciseLib
         $exercise_feedback = null,
         $show_answers = false
     ) {
+        $course_id = api_get_course_int_id();
         // Change false to true in the following line to enable answer hinting
         $debug_mark_answer = $show_answers;
 
@@ -86,9 +87,8 @@ class ExerciseLib
 
             // construction of the Answer object (also gets all answers details)
             $objAnswerTmp = new Answer($questionId);
-
             $nbrAnswers = $objAnswerTmp->selectNbrAnswers();
-            $course_id = api_get_course_int_id();
+
             $quiz_question_options = Question::readQuestionOption(
                 $questionId,
                 $course_id
@@ -124,8 +124,6 @@ HTML;
                 for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
                     $answerCorrect = $objAnswerTmp->isCorrect($answerId);
                     $numAnswer = $objAnswerTmp->selectAutoId($answerId);
-
-                    $answer = $objAnswerTmp->selectAnswer($answerId);
                     if ($answerCorrect == 0) {
                         // options (A, B, C, ...) that will be put into the list-box
                         // have the "correct" field set to 0 because they are answer
@@ -631,7 +629,8 @@ HTML;
                         $trackAttempts = Database::get_main_table(
                             TABLE_STATISTIC_TRACK_E_ATTEMPT
                         );
-                        $sqlTrackAttempt = 'SELECT answer FROM ' . $trackAttempts . ' WHERE exe_id=' . $exe_id . ' AND question_id=' . $questionId;
+                        $sqlTrackAttempt = 'SELECT answer FROM ' . $trackAttempts . '
+                                            WHERE exe_id=' . $exe_id . ' AND question_id=' . $questionId;
                         $rsLastAttempt = Database::query($sqlTrackAttempt);
                         $rowLastAttempt = Database::fetch_array($rsLastAttempt);
                         $answer = $rowLastAttempt['answer'];
@@ -789,9 +788,6 @@ HTML;
                             if (isset($user_choice_array_position[$numAnswer]) && $val['id'] == $user_choice_array_position[$numAnswer]) {
                                 $selected = 'selected="selected"';
                             }
-                            /*if (isset($user_choice_array[$matching_correct_answer]) && $val['id'] == $user_choice_array[$matching_correct_answer]['answer']) {
-                                $selected = 'selected="selected"';
-                            }*/
                             $s .= '<option value="' . $val['id'] . '" ' . $selected . '>' . $val['letter'] . '</option>';
 
                         }  // end foreach()
@@ -825,6 +821,11 @@ HTML;
                 } elseif ($answerType == DRAGGABLE) {
                     if ($answerCorrect != 0) {
                         $parsed_answer = $answer;
+                        /*$lines_count = '';
+                        $data = $objAnswerTmp->getAnswerByAutoId($numAnswer);
+                        $data = $objAnswerTmp->getAnswerByAutoId($data['correct']);
+                        $lines_count = $data['answer'];*/
+
                         $windowId = $questionId . '_' . $lines_count;
 
                         $s .= '<li class="touch-items" id="' . $windowId . '">';
@@ -901,7 +902,6 @@ JAVASCRIPT;
                             while (isset($select_items[$lines_count])) {
                                 $s .= Display::tag('b', $select_items[$lines_count]['letter']);
                                 $s .= $select_items[$lines_count]['answer'];
-
                                 $lines_count++;
                             }
                         }

+ 0 - 165
main/inc/lib/jpegcam/LICENSE.txt

@@ -1,165 +0,0 @@
-		   GNU LESSER GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
-  This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
-  0. Additional Definitions. 
-
-  As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
-  "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
-  An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
-  A "Combined Work" is a work produced by combining or linking an
-Application with the Library.  The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
-  The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
-  The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
-  1. Exception to Section 3 of the GNU GPL.
-
-  You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
-  2. Conveying Modified Versions.
-
-  If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
-   a) under this License, provided that you make a good faith effort to
-   ensure that, in the event an Application does not supply the
-   function or data, the facility still operates, and performs
-   whatever part of its purpose remains meaningful, or
-
-   b) under the GNU GPL, with none of the additional permissions of
-   this License applicable to that copy.
-
-  3. Object Code Incorporating Material from Library Header Files.
-
-  The object code form of an Application may incorporate material from
-a header file that is part of the Library.  You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
-   a) Give prominent notice with each copy of the object code that the
-   Library is used in it and that the Library and its use are
-   covered by this License.
-
-   b) Accompany the object code with a copy of the GNU GPL and this license
-   document.
-
-  4. Combined Works.
-
-  You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
-   a) Give prominent notice with each copy of the Combined Work that
-   the Library is used in it and that the Library and its use are
-   covered by this License.
-
-   b) Accompany the Combined Work with a copy of the GNU GPL and this license
-   document.
-
-   c) For a Combined Work that displays copyright notices during
-   execution, include the copyright notice for the Library among
-   these notices, as well as a reference directing the user to the
-   copies of the GNU GPL and this license document.
-
-   d) Do one of the following:
-
-       0) Convey the Minimal Corresponding Source under the terms of this
-       License, and the Corresponding Application Code in a form
-       suitable for, and under terms that permit, the user to
-       recombine or relink the Application with a modified version of
-       the Linked Version to produce a modified Combined Work, in the
-       manner specified by section 6 of the GNU GPL for conveying
-       Corresponding Source.
-
-       1) Use a suitable shared library mechanism for linking with the
-       Library.  A suitable mechanism is one that (a) uses at run time
-       a copy of the Library already present on the user's computer
-       system, and (b) will operate properly with a modified version
-       of the Library that is interface-compatible with the Linked
-       Version. 
-
-   e) Provide Installation Information, but only if you would otherwise
-   be required to provide such information under section 6 of the
-   GNU GPL, and only to the extent that such information is
-   necessary to install and execute a modified version of the
-   Combined Work produced by recombining or relinking the
-   Application with a modified version of the Linked Version. (If
-   you use option 4d0, the Installation Information must accompany
-   the Minimal Corresponding Source and Corresponding Application
-   Code. If you use option 4d1, you must provide the Installation
-   Information in the manner specified by section 6 of the GNU GPL
-   for conveying Corresponding Source.)
-
-  5. Combined Libraries.
-
-  You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
-   a) Accompany the combined library with a copy of the same work based
-   on the Library, uncombined with any other library facilities,
-   conveyed under the terms of this License.
-
-   b) Give prominent notice with the combined library that part of it
-   is a work based on the Library, and explaining where to find the
-   accompanying uncombined form of the same work.
-
-  6. Revised Versions of the GNU Lesser General Public License.
-
-  The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
-  Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
-  If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.

+ 0 - 8
main/inc/lib/jpegcam/index.html

@@ -1,8 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-</head>
-<body>
-<br />
-</body>
-</html>

BIN
main/inc/lib/jpegcam/shutter.mp3


+ 0 - 197
main/inc/lib/jpegcam/webcam.js

@@ -1,197 +0,0 @@
-/* JPEGCam v1.0.9 */
-/* Webcam library for capturing JPEG images and submitting to a server */
-/* Copyright (c) 2008 - 2009 Joseph Huckaby <jhuckaby@goldcartridge.com> */
-/* Licensed under the GNU Lesser Public License */
-/* http://www.gnu.org/licenses/lgpl.html */
-
-/* Usage:
-	<script language="JavaScript">
-		document.write( webcam.get_html(320, 240) );
-		webcam.set_api_url( 'test.php' );
-		webcam.set_hook( 'onComplete', 'my_callback_function' );
-		function my_callback_function(response) {
-			alert("Success! PHP returned: " + response);
-		}
-	</script>
-	<a href="javascript:void(webcam.snap())">Take Snapshot</a>
-*/
-
-// Everything is under a 'webcam' Namespace
-window.webcam = {
-	version: '1.0.9',
-	
-	// globals
-	ie: !!navigator.userAgent.match(/MSIE/),
-	protocol: location.protocol.match(/https/i) ? 'https' : 'http',
-	callback: null, // user callback for completed uploads
-	swf_url: 'webcam.swf', // URI to webcam.swf movie (defaults to cwd)
-	shutter_url: 'shutter.mp3', // URI to shutter.mp3 sound
-	api_url: '', // URL to upload script
-	loaded: false, // true when webcam movie finishes loading
-	quality: 90, // JPEG quality (1 - 100)
-	shutter_sound: true, // shutter sound effect on/off
-	stealth: false, // stealth mode (do not freeze image upon capture)
-	hooks: {
-		onLoad: null,
-		onComplete: null,
-		onError: null
-	}, // callback hook functions
-	
-	set_hook: function(name, callback) {
-		// set callback hook
-		// supported hooks: onLoad, onComplete, onError
-		if (typeof(this.hooks[name]) == 'undefined')
-			return alert("Hook type not supported: " + name);
-		
-		this.hooks[name] = callback;
-	},
-	
-	fire_hook: function(name, value) {
-		// fire hook callback, passing optional value to it
-		if (this.hooks[name]) {
-			if (typeof(this.hooks[name]) == 'function') {
-				// callback is function reference, call directly
-				this.hooks[name](value);
-			}
-			else if (typeof(this.hooks[name]) == 'array') {
-				// callback is PHP-style object instance method
-				this.hooks[name][0][this.hooks[name][1]](value);
-			}
-			else if (window[this.hooks[name]]) {
-				// callback is global function name
-				window[ this.hooks[name] ](value);
-			}
-			return true;
-		}
-		return false; // no hook defined
-	},
-	
-	set_api_url: function(url) {
-		// set location of upload API script
-		this.api_url = url;
-	},
-	
-	set_swf_url: function(url) {
-		// set location of SWF movie (defaults to webcam.swf in cwd)
-		this.swf_url = url;
-	},
-	
-	get_html: function(width, height, server_width, server_height) {
-		// Return HTML for embedding webcam capture movie
-		// Specify pixel width and height (640x480, 320x240, etc.)
-		// Server width and height are optional, and default to movie width/height
-		if (!server_width) server_width = width;
-		if (!server_height) server_height = height;
-		
-		var html = '';
-		var flashvars = 'shutter_enabled=' + (this.shutter_sound ? 1 : 0) + 
-			'&shutter_url=' + escape(this.shutter_url) + 
-			'&width=' + width + 
-			'&height=' + height + 
-			'&server_width=' + server_width + 
-			'&server_height=' + server_height;
-		
-		if (this.ie) {
-			html += '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+this.protocol+'://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+width+'" height="'+height+'" id="webcam_movie" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+this.swf_url+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+flashvars+'"/></object>';
-		}
-		else {
-			html += '<embed id="webcam_movie" src="'+this.swf_url+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+width+'" height="'+height+'" name="webcam_movie" align="middle" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+flashvars+'" />';
-		}
-		
-		this.loaded = false;
-		return html;
-	},
-	
-	get_movie: function() {
-		// get reference to movie object/embed in DOM
-		if (!this.loaded) return alert("ERROR: Movie is not loaded yet");
-		var movie = document.getElementById('webcam_movie');
-		if (!movie) alert("ERROR: Cannot locate movie 'webcam_movie' in DOM");
-		return movie;
-	},
-	
-	set_stealth: function(stealth) {
-		// set or disable stealth mode
-		this.stealth = stealth;
-	},
-	
-	snap: function(url, callback, stealth) {
-		// take snapshot and send to server
-		// specify fully-qualified URL to server API script
-		// and callback function (string or function object)
-		if (callback) this.set_hook('onComplete', callback);
-		if (url) this.set_api_url(url);
-		if (typeof(stealth) != 'undefined') this.set_stealth( stealth );
-		
-		this.get_movie()._snap( this.api_url, this.quality, this.shutter_sound ? 1 : 0, this.stealth ? 1 : 0 );
-	},
-	
-	freeze: function() {
-		// freeze webcam image (capture but do not upload)
-		this.get_movie()._snap('', this.quality, this.shutter_sound ? 1 : 0, 0 );
-	},
-	
-	upload: function(url, callback) {
-		// upload image to server after taking snapshot
-		// specify fully-qualified URL to server API script
-		// and callback function (string or function object)
-		if (callback) this.set_hook('onComplete', callback);
-		if (url) this.set_api_url(url);
-		
-		this.get_movie()._upload( this.api_url );
-	},
-	
-	reset: function() {
-		// reset movie after taking snapshot
-		this.get_movie()._reset();
-	},
-	
-	configure: function(panel) {
-		// open flash configuration panel -- specify tab name:
-		// "camera", "privacy", "default", "localStorage", "microphone", "settingsManager"
-		if (!panel) panel = "camera";
-		this.get_movie()._configure(panel);
-	},
-	
-	set_quality: function(new_quality) {
-		// set the JPEG quality (1 - 100)
-		// default is 90
-		this.quality = new_quality;
-	},
-	
-	set_shutter_sound: function(enabled, url) {
-		// enable or disable the shutter sound effect
-		// defaults to enabled
-		this.shutter_sound = enabled;
-		this.shutter_url = url ? url : 'shutter.mp3';
-	},
-	
-	flash_notify: function(type, msg) {
-		// receive notification from flash about event
-		switch (type) {
-			case 'flashLoadComplete':
-				// movie loaded successfully
-				this.loaded = true;
-				this.fire_hook('onLoad');
-				break;
-
-			case 'error':
-				// HTTP POST error most likely
-				if (!this.fire_hook('onError', msg)) {
-					alert("JPEGCam Flash Error: " + msg);
-				}
-				break;
-
-			case 'success':
-				// upload complete, execute user callback function
-				// and pass raw API script results to function
-				this.fire_hook('onComplete', msg.toString());
-				break;
-
-			default:
-				// catch-all, just in case
-				alert("jpegcam flash_notify: " + type + ": " + msg);
-				break;
-		}
-	}
-};

BIN
main/inc/lib/jpegcam/webcam.swf


+ 5 - 5
main/inc/lib/template.lib.php

@@ -614,7 +614,7 @@ class Template
     public function set_js_files()
     {
         global $disable_js_and_css_files, $htmlHeadXtra;
-        
+
         $isoCode = api_get_language_isocode();
 
         //JS files
@@ -1092,12 +1092,12 @@ class Template
     }
 
     /**
-     * @param $tpl_var
-     * @param null $value
+     * @param string $variable
+     * @param mixed $value
      */
-    public function assign($tpl_var, $value = null)
+    public function assign($variable, $value = '')
     {
-        $this->params[$tpl_var] = $value;
+        $this->params[$variable] = $value;
     }
 
     /**

+ 13 - 4
main/install/install.lib.php

@@ -1657,20 +1657,29 @@ function display_configuration_settings_form(
     }
     $html .= '</div></div>';
 
-
     $html .= '<div class="form-group">
             <label class="col-sm-6 control-label">' . get_lang('AllowSelfReg') . '</label>
             <div class="col-sm-6">';
     if ($installType == 'update') {
-        $html .= '<input type="hidden" name="allowSelfReg" value="'. $allowSelfReg .'" />'. $allowSelfReg ? get_lang('Yes') : get_lang('No');
+        if ($allowSelfReg == 'true') {
+            $label = get_lang('Yes');
+        } elseif ($allowSelfReg == 'false') {
+            $label = get_lang('No');
+        } else {
+            $label = get_lang('AfterApproval');
+        }
+        $html .= '<input type="hidden" name="allowSelfReg" value="'. $allowSelfReg .'" />'. $label;
     } else {
         $html .= '<div class="control-group">';
         $html .= '<label class="checkbox-inline">
-                        <input type="radio" name="allowSelfReg" value="1" id="allowSelfReg1" '. ($allowSelfReg ? 'checked="checked" ' : '') . ' /> '. get_lang('Yes') .'
+                        <input type="radio" name="allowSelfReg" value="1" id="allowSelfReg1" '. ($allowSelfReg == 'true' ? 'checked="checked" ' : '') . ' /> '. get_lang('Yes') .'
                     </label>';
         $html .= '<label class="checkbox-inline">
-                        <input type="radio" name="allowSelfReg" value="0" id="allowSelfReg0" '. ($allowSelfReg ? '' : 'checked="checked" ') .' /> '. get_lang('No') .'
+                        <input type="radio" name="allowSelfReg" value="0" id="allowSelfReg0" '. ($allowSelfReg == 'false' ? '' : 'checked="checked" ') .' /> '. get_lang('No') .'
                     </label>';
+         $html .= '<label class="checkbox-inline">
+                    <input type="radio" name="allowSelfReg" value="0" id="allowSelfReg0" '. ($allowSelfReg == 'approval' ? '' : 'checked="checked" ') .' /> '. get_lang('AfterApproval') .'
+                </label>';
         $html .= '</div>';
     }
     $html .= '</div>';

+ 4 - 1
main/newscorm/learnpath.class.php

@@ -3113,7 +3113,10 @@ class learnpath
         $hide_teacher_icons_lp = api_get_configuration_value('hide_teacher_icons_lp');
 
         if ($is_allowed_to_edit && $hide_teacher_icons_lp == false) {
-            $gradebook = Security :: remove_XSS($_GET['gradebook']);
+            $gradebook = '';
+            if (!empty($_GET['gradebook'])) {
+                $gradebook = Security:: remove_XSS($_GET['gradebook']);
+            }
             if ($this->get_lp_session_id() == api_get_session_id()) {
                 $html .= '<div id="actions_lp" class="actions_lp">';
                 $html .= '<div class="btn-group">';

+ 20 - 3
main/newscorm/lp_view.php

@@ -238,7 +238,7 @@ if (
         $safe_id == strval(intval($safe_id)) &&
         $safe_item_id == strval(intval($safe_item_id))
     ) {
-        $sql = 'SELECT start_date, exe_date, exe_result, exe_weighting
+        $sql = 'SELECT start_date, exe_date, exe_result, exe_weighting, exe_exo_id
                 FROM ' . $TBL_TRACK_EXERCICES . '
                 WHERE exe_id = ' . $safe_exe_id;
         $res = Database::query($sql);
@@ -268,8 +268,25 @@ if (
         if (Database::num_rows($res_last_attempt) && !api_is_invitee()) {
             $row_last_attempt = Database::fetch_row($res_last_attempt);
             $lp_item_view_id = $row_last_attempt[0];
+
+            $exercise = new Exercise(api_get_course_int_id());
+            $exercise->read($row_dates['exe_exo_id']);
+            $status = 'completed';
+
+            if (!empty($exercise->pass_percentage)) {
+                $status = 'failed';
+                $success = ExerciseLib::is_success_exercise_result(
+                    $score,
+                    $max_score,
+                    $exercise->pass_percentage
+                );
+                if ($success) {
+                    $status = 'passed';
+                }
+            }
+
             $sql = "UPDATE $TBL_LP_ITEM_VIEW SET
-                        status = 'completed' ,
+                        status = '$status',
                         score = $score,
                         total_time = $mytime
                     WHERE id='" . $lp_item_view_id . "' AND c_id = $course_id ";
@@ -289,7 +306,7 @@ if (
     if (intval($_GET['fb_type']) > 0) {
         $src = 'blank.php?msg=exerciseFinished';
     } else {
-        $src = api_get_path(WEB_CODE_PATH) . 'exercice/result.php?origin=learnpath&id=' . $safe_exe_id;
+        $src = api_get_path(WEB_CODE_PATH) . 'exercice/result.php?origin=learnpath&id=' . $safe_exe_id.'&'.api_get_cidreq();
 
         if ($debug) {
             error_log('Calling URL: ' . $src);

+ 0 - 8
main/session/session_list.php

@@ -288,14 +288,6 @@ $urlAjaxExtraField = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?1=1';
                     });
                 });
             });
-
-            /*
-             $('.delete-rule').on('click', function(){
-             console.log('deleted');
-             $('.input-elm').each(function(){
-             $(this).find('option:first').attr('selected', 'selected');
-             });
-             });*/
         });
     </script>
     <div class="actions">

+ 232 - 208
main/template/default/exercise/submit.js.tpl

@@ -1,243 +1,267 @@
 <script>
-    var DraggableAnswer = {
-        gallery: null,
-        trash: null,
-        deleteItem: function (item, insertHere) {
-            if (insertHere.find(".exercise-draggable-answer-option").length > 0) {
-                return false;
-            }
 
-            item.fadeOut(function () {
-                var $list = $('<div class="gallery ui-helper-reset"/>').appendTo(insertHere);
+var DraggableAnswer = {
+    gallery: null,
+    trash: null,
+    deleteItem: function (item, insertHere) {
+        if (insertHere.find(".exercise-draggable-answer-option").length > 0) {
+            return false;
+        }
 
-                item.find('a.btn').remove();
+        item.fadeOut(function () {
+            var $list = $('<div class="gallery ui-helper-reset"/>').appendTo(insertHere);
 
-                var droppedId = item.attr('id'),
-                    dropedOnId = insertHere.attr('id'),
-                    originSelectId = 'window_' + droppedId + '_select',
-                    value = dropedOnId.split('_')[2];
+            item.find('a.btn').remove();
 
-                $('#' + originSelectId + ' option')
-                    .filter(function (index) {
-                        return index === parseInt(value);
-                    })
-                    .attr("selected", true);
-
-                var recycleButton = $('<a>')
-                        .attr('href', '#')
-                        .addClass('btn btn-default btn-xs')
-                        .append(
-                            "{{ "Undo" | get_lang }} ",
-                            $('<i>').addClass('fa fa-undo')
-                        )
-                        .on('click', function (e) {
-                            e.preventDefault();
+            var droppedId = item.attr('id'),
+                dropedOnId = insertHere.attr('id'),
+                originSelectId = 'window_' + droppedId + '_select',
+                value = dropedOnId.split('_')[2];
+
+            $('#' + originSelectId + ' option')
+                .filter(function (index) {
+                    return index === parseInt(value);
+                })
+                .attr("selected", true);
+
+            var recycleButton = $('<a>')
+                    .attr('href', '#')
+                    .addClass('btn btn-default btn-xs')
+                    .append(
+                        "{{ "Undo" | get_lang }} ",
+                        $('<i>').addClass('fa fa-undo')
+                    )
+                    .on('click', function (e) {
+                        e.preventDefault();
+
+                        var liParent = $(this).parent();
+
+                        DraggableAnswer.recycleItem(liParent);
+                    });
 
-                            var liParent = $(this).parent();
+            item.append(recycleButton).appendTo($list).fadeIn();
+        });
+    },
+    recycleItem: function (item) {
+        item.fadeOut(function () {
+            item
+                .find('a.btn')
+                .remove()
+                .end()
+                .find("img")
+                .end()
+                .appendTo(DraggableAnswer.gallery)
+                .fadeIn();
+        });
+
+        var droppedId = item.attr('id'),
+            originSelectId = 'window_' + droppedId + '_select';
+
+        $('#' + originSelectId + ' option:first').attr('selected', 'selected');
+    },
+    init: function (gallery, trash) {
+        this.gallery = gallery;
+        this.trash = trash;
+
+        $("li", DraggableAnswer.gallery).draggable({
+            cancel: "a.ui-icon",
+            revert: "invalid",
+            containment: "document",
+            helper: "clone",
+            cursor: "move"
+        });
+
+        DraggableAnswer.trash.droppable({
+            accept: ".exercise-draggable-answer > li",
+            hoverClass: "ui-state-active",
+            drop: function (e, ui) {
+                DraggableAnswer.deleteItem(ui.draggable, $(this));
+            }
+        });
 
-                            DraggableAnswer.recycleItem(liParent);
-                        });
+        DraggableAnswer.gallery.droppable({
+            drop: function (e, ui) {
+                DraggableAnswer.recycleItem(ui.draggable, $(this));
+            }
+        });
+    }
+};
+
+var MatchingDraggable = {
+    colorDestination: '#316B31',
+    curviness: 0,
+    connectorType: 'Straight',
+    initialized: false,
+    init: function (questionId) {
+        var windowQuestionSelector = '.window' + questionId + '_question',
+            countConnections = $(windowQuestionSelector).length,
+            colorArray = [],
+            colorArrayDestination = [];
+
+        if (countConnections > 0) {
+            colorArray = $.xcolor.analogous("#da0", countConnections);
+            colorArrayDestination = $.xcolor.analogous("#51a351", countConnections);
+        } else {
+            colorArray = $.xcolor.analogous("#da0", 10);
+            colorArrayDestination = $.xcolor.analogous("#51a351", 10);
+        }
 
-                item.append(recycleButton).appendTo($list).fadeIn();
-            });
-        },
-        recycleItem: function (item) {
-            item.fadeOut(function () {
-                item
-                    .find('a.btn')
-                    .remove()
-                    .end()
-                    .find("img")
-                    .end()
-                    .appendTo(DraggableAnswer.gallery)
-                    .fadeIn();
-            });
+        jsPlumb.importDefaults({
+            DragOptions: {cursor: 'pointer', zIndex: 2000},
+            PaintStyle: {strokeStyle: '#000'},
+            EndpointStyle: {strokeStyle: '#316b31'},
+            Endpoint: 'Rectangle',
+            Anchors: ['TopCenter', 'TopCenter']
+        });
+
+        var exampleDropOptions = {
+            tolerance: 'touch',
+            hoverClass: 'dropHover',
+            activeClass: 'dragActive'
+        };
+
+        var destinationEndPoint = {
+            endpoint: ["Dot", {radius: 15}],
+            paintStyle: {fillStyle: MatchingDraggable.colorDestination},
+            isSource: false,
+            connectorStyle: {strokeStyle: MatchingDraggable.colorDestination, lineWidth: 8},
+            connector: [
+                MatchingDraggable.connectorType,
+                {curviness: MatchingDraggable.curviness}
+            ],
+            maxConnections: 1000,
+            isTarget: true,
+            dropOptions: exampleDropOptions,
+            beforeDrop: function (params) {
+                jsPlumb.select({source: params.sourceId}).each(function (connection) {
+                    jsPlumb.detach(connection);
+                });
 
-            var droppedId = item.attr('id'),
-                originSelectId = 'window_' + droppedId + '_select';
-
-            $('#' + originSelectId + ' option:first').attr('selected', 'selected');
-        },
-        init: function (gallery, trash) {
-            this.gallery = gallery;
-            this.trash = trash;
-
-            $("li", DraggableAnswer.gallery).draggable({
-                cancel: "a.ui-icon",
-                revert: "invalid",
-                containment: "document",
-                helper: "clone",
-                cursor: "move"
-            });
+                var selectId = params.sourceId + "_select";
+                var value = params.targetId.split("_")[2];
 
-            DraggableAnswer.trash.droppable({
-                accept: ".exercise-draggable-answer > li",
-                hoverClass: "ui-state-active",
-                drop: function (e, ui) {
-                    DraggableAnswer.deleteItem(ui.draggable, $(this));
-                }
-            });
+                $("#" + selectId + " option")
+                    .removeAttr('selected')
+                    .filter(function (index) {
+                        return index === parseInt(value);
+                    })
+                    .attr("selected", true);
 
-            DraggableAnswer.gallery.droppable({
-                drop: function (e, ui) {
-                    DraggableAnswer.recycleItem(ui.draggable, $(this));
-                }
-            });
-        }
-    };
-
-    var MatchingDraggable = {
-        colorDestination: '#316B31',
-        curviness: 0,
-        connectorType: 'Straight',
-        initialized: false,
-        init: function (questionId) {
-            var windowQuestionSelector = '.window' + questionId + '_question',
-                countConnections = $(windowQuestionSelector).length,
-                colorArray = [],
-                colorArrayDestination = [];
-
-            if (countConnections > 0) {
-                colorArray = $.xcolor.analogous("#da0", countConnections);
-                colorArrayDestination = $.xcolor.analogous("#51a351", countConnections);
-            } else {
-                colorArray = $.xcolor.analogous("#da0", 10);
-                colorArrayDestination = $.xcolor.analogous("#51a351", 10);
+                return true;
             }
+        };
 
-            jsPlumb.importDefaults({
-                DragOptions: {cursor: 'pointer', zIndex: 2000},
-                PaintStyle: {strokeStyle: '#000'},
-                EndpointStyle: {strokeStyle: '#316b31'},
-                Endpoint: 'Rectangle',
-                Anchors: ['TopCenter', 'TopCenter']
-            });
+        var count = 0;
+        var sourceDestinationArray = [];
 
-            var exampleDropOptions = {
-                tolerance: 'touch',
-                hoverClass: 'dropHover',
-                activeClass: 'dragActive'
-            };
+        $(windowQuestionSelector).each(function (index) {
+            var windowId = $(this).attr("id");
+            var scope = windowId + "scope";
+            var destinationColor = colorArray[count].getHex();
 
-            var destinationEndPoint = {
-                endpoint: ["Dot", {radius: 15}],
-                paintStyle: {fillStyle: MatchingDraggable.colorDestination},
-                isSource: false,
-                connectorStyle: {strokeStyle: MatchingDraggable.colorDestination, lineWidth: 8},
+            var sourceEndPoint = {
+                endpoint: [
+                    "Dot",
+                    {radius: 15}
+                ],
+                paintStyle: {
+                    fillStyle: destinationColor
+                },
+                isSource: true,
+                connectorStyle: {
+                    strokeStyle: "#8a8888",
+                    lineWidth: 8
+                },
                 connector: [
                     MatchingDraggable.connectorType,
                     {curviness: MatchingDraggable.curviness}
                 ],
-                maxConnections: 1000,
-                isTarget: true,
+                maxConnections: 1,
+                isTarget: false,
                 dropOptions: exampleDropOptions,
-                beforeDrop: function (params) {
-                    jsPlumb.select({source: params.sourceId}).each(function (connection) {
-                        jsPlumb.detach(connection);
-                    });
+                scope: scope
+            };
 
-                    var selectId = params.sourceId + "_select";
-                    var value = params.targetId.split("_")[2];
+            sourceDestinationArray[count + 1] = sourceEndPoint;
 
-                    $("#" + selectId + " option")
-                        .removeAttr('selected')
-                        .filter(function (index) {
-                            return index === parseInt(value);
-                        })
-                        .attr("selected", true);
+            count++;
 
-                    return true;
-                }
-            };
+            jsPlumb.addEndpoint(
+                windowId,
+                {
+                    anchor: ['RightMiddle', 'RightMiddle', 'RightMiddle', 'RightMiddle']
+                },
+                sourceEndPoint
+            );
 
-            var count = 0;
-            var sourceDestinationArray = [];
+            var destinationCount = 0;
 
             $(windowQuestionSelector).each(function (index) {
-                var windowId = $(this).attr("id");
-                var scope = windowId + "scope";
-                var destinationColor = colorArray[count].getHex();
-
-                var sourceEndPoint = {
-                    endpoint: [
-                        "Dot",
-                        {radius: 15}
-                    ],
-                    paintStyle: {
-                        fillStyle: destinationColor
-                    },
-                    isSource: true,
-                    connectorStyle: {
-                        strokeStyle: "#8a8888",
-                        lineWidth: 8
-                    },
-                    connector: [
-                        MatchingDraggable.connectorType,
-                        {curviness: MatchingDraggable.curviness}
-                    ],
-                    maxConnections: 1,
-                    isTarget: false,
-                    dropOptions: exampleDropOptions,
-                    scope: scope
-                };
-
-                sourceDestinationArray[count + 1] = sourceEndPoint;
-
-                count++;
+                var windowDestinationId = $(this).attr("id");
+                destinationEndPoint.scope = scope;
+                destinationEndPoint.paintStyle.fillStyle = colorArrayDestination[destinationCount].getHex();
+                destinationCount++;
 
                 jsPlumb.addEndpoint(
-                    windowId,
+                    windowDestinationId + "_answer",
                     {
-                        anchor: ['RightMiddle', 'RightMiddle', 'RightMiddle', 'RightMiddle']
+                        anchors: ['LeftMiddle', 'LeftMiddle', 'LeftMiddle', 'LeftMiddle']
                     },
-                    sourceEndPoint
+                    destinationEndPoint
                 );
-
-                var destinationCount = 0;
-
-                $(windowQuestionSelector).each(function (index) {
-                    var windowDestinationId = $(this).attr("id");
-                    destinationEndPoint.scope = scope;
-                    destinationEndPoint.paintStyle.fillStyle = colorArrayDestination[destinationCount].getHex();
-                    destinationCount++;
-
-                    jsPlumb.addEndpoint(
-                        windowDestinationId + "_answer",
-                        {
-                            anchors: ['LeftMiddle', 'LeftMiddle', 'LeftMiddle', 'LeftMiddle']
-                        },
-                        destinationEndPoint
-                    );
-                });
             });
+        });
 
-            MatchingDraggable.attachBehaviour();
-        },
-        attachBehaviour: function () {
-            if (!MatchingDraggable.initialized) {
-                MatchingDraggable.initialized = true;
-            }
-        }
-    };
-    
-    jsPlumb.ready(function () {
-        if ($(".drag_question").length > 0) {
-            MatchingDraggable.init();
-
-            $(document).scroll(function () {
-                jsPlumb.repaintEverything();
-            });
-
-            $(window).resize(function () {
-                jsPlumb.repaintEverything();
-            });
+        MatchingDraggable.attachBehaviour();
+    },
+    attachBehaviour: function () {
+        if (!MatchingDraggable.initialized) {
+            MatchingDraggable.initialized = true;
         }
-    });
-
-    $(document).on('ready', function () {
-        DraggableAnswer.init(
-            $(".exercise-draggable-answer"),
-            $(".droppable")
-        );
-    });
+    }
+};
+
+jsPlumb.ready(function () {
+    if ($(".drag_question").length > 0) {
+        MatchingDraggable.init();
+
+        $(document).scroll(function () {
+            jsPlumb.repaintEverything();
+        });
+
+        $(window).resize(function () {
+            jsPlumb.repaintEverything();
+        });
+    }
+});
+
+$(document).on('ready', function () {
+    DraggableAnswer.init(
+        $(".exercise-draggable-answer"),
+        $(".droppable")
+    );
+
+    // if shuffle answers
+    if ('{{ shuffle_answers }}' == '1') {
+        $('.exercise-draggable-answer').each(function(){
+            // get current ul
+            var $ul = $(this);
+            // get array of list items in current ul
+            var $liArr = $ul.children('li');
+            // sort array of list items in current ul randomly
+            $liArr.sort(function(a,b){
+                // Get a random number between 0 and 10
+                var temp = parseInt( Math.random()*100 );
+                // Get 1 or 0, whether temp is odd or even
+                var isOddOrEven = temp%2;
+                // Get +1 or -1, whether temp greater or smaller than 5
+                var isPosOrNeg = temp>5 ? 1 : -1;
+                // Return -1, 0, or +1
+                return( isOddOrEven*isPosOrNeg );
+            })
+            // append list items to ul
+            .appendTo($ul);
+        });
+    }
+});
 </script>

+ 0 - 1
main/template/default/skill/skill_wheel.js.tpl

@@ -276,7 +276,6 @@ function set_skill_style(d, attribute, searched_skill_id) {
 
 /* When you click a skill partition */
 function click_partition(d, path, text, icon, arc, x, y, r, p, vis) {
-    //console.log(d.depth);
     if (debug) {
         console.log('Clicking a partition skill id: '+d.id);
         console.log(d);

+ 6 - 6
main/template/default/social/user_block.tpl

@@ -5,18 +5,18 @@
             {{ user.firstname }}<br>{{ user.lastname }}
         </p>
         <ul class="list-unstyled user-details">
-            <li>
-                <a href="{{ vcard_user_link }}">
-                    <img src="{{ "vcard.png" | icon(22) }}" alt="{{ "UserInfo" | get_lang }}">
-                    {{ "UserInfo" | get_lang }}
-                </a>
-            </li>
             <li>
                 <a href="{{ _p.web }}main/messages/new_message.php">
                     <img src="{{ "instant_message.png" | icon }}" alt="{{ "Email" | get_lang }}">
                     {{ user.email}}
                 </a>
             </li>
+            <li>
+                <a href="{{ vcard_user_link }}">
+                    <img src="{{ "vcard.png" | icon(22) }}" alt="{{ "UserInfo" | get_lang }}" width="22" height="22">
+                    {{ "UserInfo" | get_lang }}
+                </a>
+            </li>
             {% if chat_enabled == 1 %}
                 <li>
                     {% if user.user_is_online_in_chat != 0 %}