Просмотр исходного кода

Adding first draft of the Skills management; adding skill.lib.php, gradebook.lib.php, adding DB changes (not functional yet) see #1791

Julio Montoya 13 лет назад
Родитель
Сommit
0226119323
38 измененных файлов с 1800 добавлено и 257 удалено
  1. 7 0
      index.php
  2. 4 0
      main/admin/index.php
  3. 0 1
      main/admin/promotions.php
  4. 472 0
      main/admin/skills.php
  5. 84 0
      main/admin/skills_gradebook.php
  6. 56 4
      main/css/base.css
  7. 67 0
      main/gradebook/gradebook_add_cat.php
  8. 75 2
      main/gradebook/gradebook_edit_cat.php
  9. 3 3
      main/gradebook/index.php
  10. 48 12
      main/gradebook/lib/be/category.class.php
  11. 28 11
      main/gradebook/lib/fe/catform.class.php
  12. 1 1
      main/gradebook/lib/fe/displaygradebook.php
  13. 5 4
      main/gradebook/lib/fe/gradebooktable.class.php
  14. 7 2
      main/gradebook/lib/gradebook_data_generator.class.php
  15. 1 15
      main/gradebook/lib/gradebook_functions.inc.php
  16. 5 74
      main/inc/ajax/agenda.ajax.php
  17. 35 6
      main/inc/ajax/model.ajax.php
  18. 78 0
      main/inc/ajax/skill.ajax.php
  19. 2 1
      main/inc/ajax/user_manager.ajax.php
  20. 32 6
      main/inc/lib/certificate.lib.php
  21. 8 0
      main/inc/lib/database.constants.inc.php
  22. 15 12
      main/inc/lib/database.lib.php
  23. 9 5
      main/inc/lib/database.mysqli.lib.php
  24. 19 16
      main/inc/lib/document.lib.php
  25. 134 0
      main/inc/lib/gradebook.lib.php
  26. 10 4
      main/inc/lib/javascript/tag/style.css
  27. 8 5
      main/inc/lib/main_api.lib.php
  28. 293 0
      main/inc/lib/skill.lib.php
  29. 125 0
      main/inc/lib/skill.visualizer.lib.php
  30. 11 0
      main/inc/lib/userportal.lib.php
  31. 47 1
      main/install/db_main.sql
  32. 8 0
      main/install/migrate-db-1.8.8-1.9.0-pre.sql
  33. 3 0
      main/lang/english/trad4all.inc.php
  34. 2 12
      main/messages/new_message.php
  35. 91 60
      main/social/home.php
  36. 1 0
      main/social/skills_tree.php
  37. 1 0
      main/template/default/layout/layout_2_col.tpl
  38. 5 0
      user_portal.php

+ 7 - 0
index.php

@@ -186,4 +186,11 @@ $controller->tpl->assign('home_page_block', 		$controller->return_home_page());
 $controller->tpl->assign('notice_block',			$controller->return_notice());
 $controller->tpl->assign('plugin_campushomepage', 	$controller->return_plugin_campushomepage());
 
+if (api_is_platform_admin() || api_is_drh()) {
+    $controller->tpl->assign('skills_block',            $controller->return_skills_links());
+}
+
+
+
+
 $controller->tpl->display_two_col_template();

+ 4 - 0
main/admin/index.php

@@ -115,6 +115,10 @@ if (api_is_platform_admin()) {
 	$items[] = array('url'=>'course_category.php', 			'label' => get_lang('AdminCategories'));
 	$items[] = array('url'=>'subscribe_user2course.php', 	'label' => get_lang('AddUsersToACourse'));
 	$items[] = array('url'=>'course_user_import.php', 		'label' => get_lang('ImportUsersToACourse'));
+    
+    $items[] = array('url'=>'skills.php',                   'label' => get_lang('SkillsTree'));
+    $items[] = array('url'=>'skills_gradebook.php',         'label' => get_lang('SkillsGradebook'));
+    
 
     if (isset($extAuthSource) && isset($extAuthSource['ldap']) && count($extAuthSource['ldap']) > 0) { 
     	$items[] = array('url'=>'ldap_import_students.php', 	'label' => get_lang('ImportLDAPUsersIntoCourse'));

+ 0 - 1
main/admin/promotions.php

@@ -10,7 +10,6 @@ $language_file = array('admin');
 
 $cidReset = true;
 require_once '../inc/global.inc.php';
-require_once api_get_path(LIBRARY_PATH).'formvalidator/FormValidator.class.php';
 require_once api_get_path(LIBRARY_PATH).'promotion.lib.php';
 require_once api_get_path(LIBRARY_PATH).'career.lib.php';
 

+ 472 - 0
main/admin/skills.php

@@ -0,0 +1,472 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ *  @package chamilo.admin
+ */
+
+// Language files that need to be included.
+$language_file = array('admin');
+
+$cidReset = true;
+require_once '../inc/global.inc.php';
+require_once api_get_path(LIBRARY_PATH).'skill.lib.php';
+require_once api_get_path(LIBRARY_PATH).'skill.visualizer.lib.php';
+$this_section = SECTION_PLATFORM_ADMIN;
+
+//api_protect_admin_script();
+
+//Adds the JS needed to use the jqgrid
+$htmlHeadXtra[] = api_get_jquery_ui_js(true);
+$htmlHeadXtra[] = api_get_js('jquery.jsPlumb.all.js');
+
+Display::display_header();
+//Display::display_reduced_header();
+
+$skill = new Skill();
+$skills = $skill->get_all(true);
+$type = 'edit'; //edit
+$skill_visualizer = new SkillVisualizer($skills, $type);
+
+$skill_visualizer->display_html();
+
+$url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?1=1';
+//$url = api_get_path(WEB_AJAX_PATH).'skill.ajax.php?load_user_data=1';
+?>
+
+<script>
+jsPlumb.bind("ready", function() {
+    $("#dialog-form").dialog({
+        autoOpen: false,
+        modal   : true, 
+        width   : 550, 
+        height  : 380
+    });
+    
+    $.getJSON( "<?php echo $url.'&a=get_skills' ?>", {},     
+        function(data) {
+            $.each(data, function(n, parent) {
+                // add a new option with the JSON-specified value and text
+                $("<option />").attr("value", parent.id).text(parent.name).appendTo("#parent_id");
+            });
+        }
+    );
+    
+    $.getJSON( "<?php echo $url.'&a=get_gradebooks' ?>", {},     
+        function(data) {
+            $.each(data, function(n, gradebook) {
+                // add a new option with the JSON-specified value and text
+                $("<option />").attr("value", gradebook.id).text(gradebook.name).appendTo("#gradebook_id");
+            });
+        }
+    );
+    
+    
+    
+    //Filling select
+    $("#add_item_link").click(function() {
+        $("#dialog-form").dialog("open");
+        $("#gradebook_id").addClass('chzn-select');
+        
+        $("#gradebook_id").chosen();
+        $("#parent_id").chosen();
+    });
+
+    var url = '<?php echo $url; ?>';
+    
+    var name = $( "#name" ),
+    description = $( "#description" ),  
+    allFields = $( [] ).add( name ).add( description ), tips = $(".validateTips");
+    
+    
+    $("#dialog-form").dialog({              
+        buttons: {
+            "Add" : function() {                
+                var bValid = true;
+                bValid = bValid && checkLength( name, "name", 1, 255 );
+                var params = $("#add_item").serialize();          
+                      
+                $.ajax({
+                    url: url+'&a=add&'+params,
+                    success:function(data) {
+                             
+                        /*jsPlumb.connect({
+                            source : "block_2", 
+                            target : "block_1",
+                            overlays : overlays            
+                        });*/
+                        
+                        /*
+                        calEvent.title          = $("#name").val();
+                        calEvent.start          = calEvent.start;
+                        calEvent.end            = calEvent.end;
+                        calEvent.allDay         = calEvent.allDay;
+                        calEvent.description    = $("#content").val();                              
+                        calendar.fullCalendar('updateEvent', 
+                                calEvent,
+                                true // make the event "stick"
+                        );*/
+                        
+                        $("#dialog-form").dialog("close");                                      
+                    }                           
+                });
+            },            
+        },              
+        close: function() {     
+            $("#name").attr('value', '');
+            $("#description").attr('value', '');                
+        }
+    });
+    
+ 
+    $(".window").bind('click', function() {
+        var id = $(this).attr('id');
+        id = id.split('_')[1];        
+        //$("#dialog-form").dialog("open");
+    });    
+
+    // chrome fix.
+    document.onselectstart = function () { return false; };             
+
+    // render mode
+    var resetRenderMode = function(desiredMode) {
+        var newMode = jsPlumb.setRenderMode(desiredMode);        
+        jsPlumbDemo.init();
+    };    
+    resetRenderMode(jsPlumb.CANVAS);
+       
+});
+
+;(function() {
+     
+    prepare = function(elId, endpoint) {
+        jsPlumbDemo.initHover(elId);
+        //jsPlumbDemo.initAnimation(elId);        
+        var e = jsPlumb.addEndpoint(elId, endpoint);
+        jsPlumbDemo.initjulio(e);
+        
+        return e;
+    },    
+    
+    window.jsPlumbDemo = {    
+         initjulio :function(e) {             
+              
+        },      
+        initHover :function(elId) {            
+            
+            $("#" + elId).click(function() {
+                var  all = jsPlumb.getConnections({source:elId});
+                for (var i = 0; i < discs.length; i++) {
+                    var d = document.getElementById(discs[i]);
+                    if (d) d.parentNode.removeChild(d);
+                }
+                //var all  = jsPlumb.getConnections({scope:"DEFAULT", source:elId});     
+                   
+                console.log(all);
+            
+                //alert(elId);
+               // jsPlumb.hide(elId);
+            });
+            
+            /*$("#" + elId).hover(
+                function() { $(this).addClass("bigdot-hover"); },
+                function() { $(this).removeClass("bigdot-hover"); }
+            );*/
+        },        
+        init : function() {
+        
+            jsPlumb.Defaults.DragOptions = { cursor: 'pointer', zIndex:2000 };
+            jsPlumb.Defaults.PaintStyle = { strokeStyle:'#666' };
+            jsPlumb.Defaults.EndpointStyle = { width:20, height:16, strokeStyle:'#666' };
+            jsPlumb.Defaults.Endpoint = "Rectangle";
+            jsPlumb.Defaults.Anchors = ["TopCenter", "TopCenter"];
+
+            var connections = [];
+            var updateConnections = function(conn, remove) {
+                if (!remove) connections.push(conn);
+                else {
+                    var idx = -1;
+                    for (var i = 0; i < connections.length; i++) {
+                        if (connections[i] == conn) {
+                            idx = i; break;
+                        }
+                    }
+                    if (idx != -1) connections.splice(idx, 1);
+                }
+                if (connections.length > 0) {
+                    var s = "<span>current connections</span><br/><br/><table><tr><th>scope</th><th>source</th><th>target</th></tr>";
+                    for (var j = 0; j < connections.length; j++) {
+                        s = s + "<tr><td>" + connections[j].scope + "</td>" + "<td>" + connections[j].sourceId + "</td><td>" + connections[j].targetId + "</td></tr>";
+                    }
+                    jsPlumbDemo.showConnectionInfo(s);
+                } else 
+                    jsPlumbDemo.hideConnectionInfo();
+            };              
+
+            jsPlumb.bind("jsPlumbConnection", function(e) {
+                updateConnections(e.connection);
+            });
+            jsPlumb.bind("jsPlumbConnectionDetached", function(e) {
+                updateConnections(e.connection, true);
+            });
+
+            var exampleDropOptions = {
+                tolerance:'touch',
+                hoverClass:'dropHover',
+                activeClass:'dragActive'
+            };
+
+            /**
+                first example endpoint.  it's a 25x21 rectangle (the size is provided in the 'style' arg to the Endpoint), and it's both a source
+                and target.  the 'scope' of this Endpoint is 'exampleConnection', meaning any connection starting from this Endpoint is of type
+                'exampleConnection' and can only be dropped on an Endpoint target that declares 'exampleEndpoint' as its drop scope, and also that
+                only 'exampleConnection' types can be dropped here.
+
+                the connection style for this endpoint is a Bezier curve (we didn't provide one, so we use the default), with a lineWidth of
+                5 pixels, and a gradient.
+
+                note the use of the '$.extend' function to setup generic connection types.  this will save you a lot of typing, and probably
+                errors.
+
+            */
+                // this is the paint style for the connecting lines..
+            var connectorPaintStyle = {
+                lineWidth:5,
+                strokeStyle:"#deea18",
+                joinstyle:"round"
+            };
+            // .. and this is the hover style. 
+           var connectorHoverStyle = {
+                lineWidth:7,
+                strokeStyle:"#2e2aF8"
+            };
+            
+            
+            jsPlumb.Defaults.Overlays = [
+                [ "Arrow", { location:0.9 } ], 
+                [ "Label", { 
+                    location:0.1,
+                    label:function(label) {
+                        return label.connection.labelText || ""; 
+                    },
+                    cssClass:"aLabel",
+         
+                }] 
+            ];
+            
+            //Settings when editing stuff
+            var edit_arrow_color = '#ccc';            
+            var editEndpoint = {  
+                //connectorStyle:connectorPaintStyle,
+                connector:[ "Flowchart", { stub:40 } ],
+                hoverPaintStyle:connectorHoverStyle,
+                connectorHoverStyle:connectorHoverStyle,
+                anchors: ['BottomCenter','TopCenter'],                
+                endpoint:"Rectangle",
+                paintStyle:{ width:25, height:21, fillStyle:edit_arrow_color },
+                isSource:true,
+                scope:'blue rectangle',
+                maxConnections:10,
+                connectorStyle : {
+                    gradient:{stops:[[0, edit_arrow_color], [0.5, edit_arrow_color], [1, edit_arrow_color]]}, //gradient stuff
+                    lineWidth:5,
+                    strokeStyle: edit_arrow_color
+                },
+                isTarget:true,
+                dropOptions : exampleDropOptions
+            };
+                           
+            jsPlumb.setMouseEventsEnabled(true);
+            
+            
+            //Default
+            var default_arrow_color = '#ccc';            
+            var defaultEndpoint = {
+                anchors: ['BottomCenter','TopCenter'],            
+                endpoint:"Rectangle",
+                paintStyle:{ width:1, height:1, fillStyle:default_arrow_color },
+                isSource:false,
+                scope:'blue rectangle',
+                maxConnections:10,
+                connectorStyle : {
+                    gradient:{stops:[[0, default_arrow_color], [0.5, default_arrow_color], [1, default_arrow_color]]},
+                    lineWidth:5,
+                    strokeStyle:default_arrow_color
+                },
+                isTarget:false,          
+                setDraggableByDefault : false,      
+            };
+            
+            
+            // Done end point 
+            var done_arrow_color = '#73982C';   
+            var doneEndpoint = {                
+                endpoint:"Rectangle",
+                paintStyle:{ width:1, height:1, fillStyle:done_arrow_color},
+                isSource:false,
+                scope:'blue rectangle',
+                maxConnections:10,
+                connectorStyle : {
+                    gradient:{stops:[[0, done_arrow_color], [0.5, done_arrow_color], [1, done_arrow_color]]},
+                    lineWidth:5,
+                    strokeStyle:done_arrow_color
+                },
+                isTarget:false,
+                setDraggableByDefault : false,
+                         
+            };
+            
+            
+
+
+            <?php $skill_visualizer->display_js();?>
+            // three ways to do this - an id, a list of ids, or a selector (note the two different types of selectors shown here...anything that is valid jquery will work of course)
+            //jsPlumb.draggable("window1");
+            //jsPlumb.draggable(["window1", "window2"]);
+            //jsPlumb.draggable($("#window1"));
+            var divsWithWindowClass = jsPlumbDemo.getSelector(".window");
+            jsPlumb.draggable(divsWithWindowClass);
+
+            // add the third example using the '.window' class.             
+            //jsPlumb.addEndpoint(divsWithWindowClass, exampleEndpoint3);
+
+            // each library uses different syntax for event stuff, so it is handed off
+            // to the draggableConnectorsDemo-<library>.js files.
+            jsPlumbDemo.attachBehaviour();          
+        }
+    };
+    
+})();
+
+
+
+
+$(document).ready( function() {     
+    //When creating a connection see
+    //http://jsplumb.org/apidocs/files/jsPlumb-1-3-2-all-js.html#bind 
+    jsPlumb.bind("jsPlumbConnection", function(conn) {
+        alert("Connection created " + conn.sourceId + " to " + conn.targetId + " ");
+            //jsPlumb.detach(conn); 
+    });
+    //When double clicking a connection
+    jsPlumb.bind("click", function(conn) {        
+        if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?"))
+            jsPlumb.detach(conn); 
+    });
+    
+     //When double clicking a connection
+    jsPlumb.bind("click", function(endpoint) {
+        if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?"))
+            jsPlumb.detach(conn); 
+    })
+    
+    
+    
+});
+
+;(function() {
+    
+    jsPlumbDemo.showConnectionInfo = function(s) {
+        $("#list").html(s);
+        $("#list").fadeIn({complete:function() { jsPlumb.repaintEverything(); }});
+    };
+    
+    jsPlumbDemo.hideConnectionInfo = function() {
+        $("#list").fadeOut({complete:function() { jsPlumb.repaintEverything(); }});
+    };
+    
+    jsPlumbDemo.getSelector = function(spec) {
+        return $(spec);
+    };
+    
+    jsPlumbDemo.attachBehaviour = function() {
+        $(".hide").click(function() {
+            jsPlumb.toggle($(this).attr("rel"));
+        });
+
+        $(".drag").click(function() {
+            var s = jsPlumb.toggleDraggable($(this).attr("rel"));
+            $(this).html(s ? 'disable dragging' : 'enable dragging');
+            if (!s) $("#" + $(this).attr("rel")).addClass('drag-locked'); else $("#" + $(this).attr("rel")).removeClass('drag-locked');
+            $("#" + $(this).attr("rel")).css("cursor", s ? "pointer" : "default");
+        });
+
+        $(".detach").click(function() {
+            jsPlumb.detachAll($(this).attr("rel"));
+        });
+
+        $("#clear").click(function() { 
+            jsPlumb.detachEverything(); showConnections(); 
+        });
+    };
+
+})();
+
+function checkLength( o, n, min, max ) {
+    if ( o.val().length > max || o.val().length < min ) {
+        o.addClass( "ui-state-error" );
+        updateTips( "Length of " + n + " must be between " +
+            min + " and " + max + "." );
+        return false;
+    } else {
+        return true;
+    }
+}
+</script>
+
+<a class="a_button gray" id="add_item_link" href="#">Add item</a>
+
+<div id="dialog-form" style="display:none;">
+    <div style="width:500px">
+    <form id="add_item" name="form">
+        <div class="row">
+            <div class="label">
+                <label for="name">Name</label>
+            </div>      
+            <div class="formw">
+                <input type="text" name="name" id="name" size="40" />             
+            </div>
+        </div>        
+        <div class="row">
+            <div class="label">
+                <label for="name">Parent</label>
+            </div>      
+            <div class="formw">
+                <select id="parent_id" name="parent_id" />
+                </select>             
+            </div>
+        </div>
+        
+        <div class="row">
+            <div class="label">
+                <label for="name">Gradebook</label>
+            </div>      
+            <div class="formw">
+                <select id="gradebook_id" name="gradebook_id[]" multiple="multiple" />
+                </select>             
+            </div>
+        </div>
+        <div class="row">
+            <div class="label">
+                <label for="name">Description</label>
+            </div>      
+            <div class="formw">
+                <textarea name="description" id="description" cols="40" rows="7"></textarea>
+            </div>
+        </div>  
+    </form>
+    </div>
+</div>
+<?php    
+
+Display::display_footer();
+
+// The header.
+/*
+$tpl = new Template();
+
+$tpl->assign('content', $content);
+$tpl->display_one_col_template();
+
+ *
+ */

+ 84 - 0
main/admin/skills_gradebook.php

@@ -0,0 +1,84 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ *  @package chamilo.admin
+ */
+
+// Language files that need to be included.
+$language_file = array('admin');
+
+$cidReset = true;
+require_once '../inc/global.inc.php';
+require_once api_get_path(LIBRARY_PATH).'skill.lib.php';
+require_once api_get_path(LIBRARY_PATH).'gradebook.lib.php';
+
+$this_section = SECTION_PLATFORM_ADMIN;
+
+api_protect_admin_script();
+//Adds the JS needed to use the jqgrid
+$htmlHeadXtra[] = api_get_jquery_ui_js(true);
+
+Display::display_header();
+
+// setting breadcrumbs
+$interbreadcrumb[]=array('url' => 'index.php','name' => get_lang('PlatformAdmin'));
+$interbreadcrumb[]=array('url' => 'career_dashboard.php','name' => get_lang('CareersAndPromotions'));
+
+
+//jqgrid will use this URL to do the selects
+
+$url            = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_gradebooks';
+
+//The order is important you need to check the the $column variable in the model.ajax.php file 
+$columns        = array(get_lang('Name'), get_lang('Skills'), get_lang('Actions'));
+
+//Column config
+$column_model   = array(
+                        array('name'=>'name',           'index'=>'name',        'width'=>'200',   'align'=>'left'),
+                        array('name'=>'skills',         'index'=>'skills', 'width'=>'300',  'align'=>'left','sortable'=>'false'),
+                        array('name'=>'actions',        'index'=>'actions',     'width'=>'100',  'align'=>'left','formatter'=>'action_formatter','sortable'=>'false')
+                       );            
+//Autowidth             
+$extra_params['autowidth'] = 'true';
+//height auto 
+$extra_params['height'] = 'auto'; 
+
+//With this function we can add actions to the jgrid (edit, delete, etc)
+$action_links = 'function action_formatter(cellvalue, options, rowObject) {
+                         return \'<a href="?action=add_skill&id=\'+options.rowId+\'">'.Display::return_icon('addd.gif', get_lang('AddSkill'),'',22).'</a>'.
+                         '\'; 
+                 }';
+?>
+<script>
+$(function() {
+<?php 
+    // grid definition see the $career->display() function
+    echo Display::grid_js('gradebooks',  $url,$columns,$column_model,$extra_params, array(), $action_links,true);       
+?> 
+});
+</script>
+<?php
+$gradebook = new Gradebook();
+$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'display'; 
+switch($action) {
+    case 'display':
+        $gradebook->display();
+        break;
+    case 'add_skill':
+        $id = isset($_REQUEST['id']) ? $_REQUEST['id'] : null;
+        $gradebook_info = $gradebook->get($id);
+        $url  = api_get_self().'?action='.$action.'&id='.$id;
+        $form =  $gradebook->show_skill_form($id, $url);
+        if ($form->validate()) {
+            $values = $form->exportValues();
+            $res    = $gradebook->update_skills_to_gradebook($values['id'], $values['skill']);            
+            if ($res) {
+                Display::display_confirmation_message(get_lang('ItemAdded'));
+            }
+        }
+        echo Display::tag('h1',$gradebook_info['name']);
+        $form->display();
+        break;
+}
+Display::display_footer();

+ 56 - 4
main/css/base.css

@@ -3240,7 +3240,7 @@ div.admin_section h4 {
 
 /* chosen javascript checkbox select width fix */
 .chzn-select {
-    width: 200px;	
+    min-width: 250px;	
 }
 #lp_item_list {
 	width:90%;
@@ -3785,8 +3785,9 @@ form div.error input, form div.error textarea {
 }
 
 .label_tag {
-  /* padding: 1px 3px 2px; */
-  padding:5px;
+  padding: 1px 3px 2px;
+  margin-right:3px; 
+  /* padding:5px;*/
   background-color: #bfbfbf;
   font-size: 9.75px;
   font-weight: bold;
@@ -3878,4 +3879,55 @@ form div.error input, form div.error textarea {
 .lp_content_type_label {
 	font-style: italic;
 	color:#999;
-}
+}
+
+
+/* Plumb */
+
+.window { 
+    cursor:pointer;
+    box-shadow: 2px 2px 19px #aaa;
+   -o-box-shadow: 2px 2px 19px #aaa;
+   -webkit-box-shadow: 2px 2px 19px #aaa;
+   -moz-box-shadow: 2px 2px 19px #aaa;
+}
+
+.window {
+    background-color: white;
+    border: 0.1em dotted #D4E06B;
+    color: black;
+    font-family: helvetica,sans;
+    font-size: 0.9em;
+    height: 4em;
+    opacity: 0.8;
+    padding-top: 0.9em;
+    position: absolute;
+    text-align: center;
+    width: 100px;
+    z-index: 40;
+}
+
+.done_window {
+    background-color: #73982C;
+}
+
+._jsPlumb_endpoint {
+    z-index: 50;
+}
+
+.drag-locked {
+    border: 1px solid red;
+}
+._jsPlumb_endpoint {
+    z-index: 50;
+}
+._jsPlumb_connector {
+    z-index: 1;
+}
+.dropHover {
+    border: 1px dotted red;
+}
+
+.dragActive {
+    border: 2px dotted orange;
+}

+ 67 - 0
main/gradebook/gradebook_add_cat.php

@@ -20,6 +20,71 @@ require_once 'lib/gradebook_functions.inc.php';
 require_once 'lib/fe/catform.class.php';
 api_block_anonymous_users();
 block_students();
+
+
+
+
+$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/tag/jquery.fcbkcomplete.js" type="text/javascript" language="javascript"></script>';
+$htmlHeadXtra[] = '<link  href="'.api_get_path(WEB_LIBRARY_PATH).'javascript/tag/style.css" rel="stylesheet" type="text/css" />';
+
+$htmlHeadXtra[] = '<script type="text/javascript">
+$(document).ready(function () {
+    $("#skills").fcbkcomplete({
+        json_url: "'.api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=find_skills",
+        cache: false,
+        filter_case: false,
+        filter_hide: true,
+        complete_text:"'.get_lang('StartToType').'",
+        firstselected: true,
+        //onremove: "testme",
+        onselect:"check_skills",
+        filter_selected: true,
+        newel: true
+    });
+    
+    $(".closebutton").click(function() {
+        var skill_id = ($(this).attr("id")).split("_")[1];        
+        if (skill_id) {            
+            $.ajax({ 
+                url: "'.api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=remove_skill", 
+                data: "gradebook_id='.$edit_cat.'&skill_id="+skill_id,
+                success: function(return_value) {                    
+                    if (return_value == 1 ) {
+                            $("#skill_"+skill_id).remove();
+                    }
+                }        
+            });
+        }
+    });
+});
+
+function check_skills() {
+    //selecting only selected users
+    $("#skills option:selected").each(function() {
+        var skill_id = $(this).val();        
+        if (skill_id != "" ) {            
+            $.ajax({ 
+                url: "'.api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=skill_exists", 
+                data: "skill_id="+skill_id,
+                success: function(return_value) {                    
+                if (return_value == 0 ) {
+                        alert("'.get_lang('SkillDoesNotExist').'");                                                
+                        //Deleting select option tag
+                        $("#skills option[value="+skill_id+"]").remove();                        
+                        //Deleting holder
+                        $(".holder li").each(function () {
+                            if ($(this).attr("rel") == skill_id) {
+                                $(this).remove();
+                            }
+                        });                        
+                    }                    
+                },            
+            });                
+        }        
+    });
+}
+</script>';
+
 $get_select_cat = intval($_GET['selectcat']);
 
 $catadd = new Category();
@@ -56,6 +121,8 @@ if ($form->validate()) {
 	}
 	//Always add the gradebook to the course
 	$cat->set_course_code(api_get_course_id());	
+    
+    $cat->set_skills($values['skills']);
 	
 	$cat->set_description($values['description']);
 	$cat->set_user_id($values['hid_user_id']);

+ 75 - 2
main/gradebook/gradebook_edit_cat.php

@@ -10,6 +10,7 @@
 $language_file = 'gradebook';
 
 require_once '../inc/global.inc.php';
+
 require_once 'lib/be.inc.php';
 require_once 'lib/gradebook_functions.inc.php';
 require_once 'lib/fe/catform.class.php';
@@ -18,10 +19,79 @@ api_block_anonymous_users();
 block_students();
 
 $edit_cat = isset($_REQUEST['editcat']) ? $_REQUEST['editcat'] : '';
+
+
+$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/tag/jquery.fcbkcomplete.js" type="text/javascript" language="javascript"></script>';
+$htmlHeadXtra[] = '<link  href="'.api_get_path(WEB_LIBRARY_PATH).'javascript/tag/style.css" rel="stylesheet" type="text/css" />';
+
+$htmlHeadXtra[] = '<script type="text/javascript">
+$(document).ready(function () {
+    $("#skills").fcbkcomplete({
+        json_url: "'.api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=find_skills",
+        cache: false,
+        filter_case: false,
+        filter_hide: true,
+        complete_text:"'.get_lang('StartToType').'",
+        firstselected: true,
+        //onremove: "testme",
+        onselect:"check_skills",
+        filter_selected: true,
+        newel: true
+    });
+    
+    $(".closebutton").click(function() {
+        var skill_id = ($(this).attr("id")).split("_")[1];        
+        if (skill_id) {            
+            $.ajax({ 
+                url: "'.api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=remove_skill", 
+                data: "gradebook_id='.$edit_cat.'&skill_id="+skill_id,
+                success: function(return_value) {                    
+                    if (return_value == 1 ) {
+                            $("#skill_"+skill_id).remove();
+                    }
+                }        
+            });
+        }
+    });
+});
+
+function check_skills() {
+    //selecting only selected users
+    $("#skills option:selected").each(function() {
+        var skill_id = $(this).val();        
+        if (skill_id != "" ) {            
+            $.ajax({ 
+                url: "'.api_get_path(WEB_AJAX_PATH).'skill.ajax.php?a=skill_exists", 
+                data: "skill_id="+skill_id,
+                success: function(return_value) {                    
+                if (return_value == 0 ) {
+                        alert("'.get_lang('SkillDoesNotExist').'");                                                
+                        //Deleting select option tag
+                        $("#skills option[value="+skill_id+"]").remove();                        
+                        //Deleting holder
+                        $(".holder li").each(function () {
+                            if ($(this).attr("rel") == skill_id) {
+                                $(this).remove();
+                            }
+                        });                        
+                    }                    
+                },            
+            });                
+        }        
+    });
+}
+</script>';
+
+
 $catedit  = Category :: load($edit_cat);
-$form = new CatForm(CatForm :: TYPE_EDIT, $catedit[0], 'edit_cat_form');
+$form     = new CatForm(CatForm :: TYPE_EDIT, $catedit[0], 'edit_cat_form');
+
 if ($form->validate()) {
-	$values = $form->exportValues();
+	$values = $form->getSubmitValues();
+    if (isset($values['skills'])) {
+        //$res    = $gradebook->update_skills_to_gradebook($values['hid_id'], $values['skills']);
+    }
+    
 	$cat = new Category();
 	$cat->set_id($values['hid_id']);
 	$cat->set_name($values['name']);
@@ -31,6 +101,9 @@ if ($form->validate()) {
 		$cat->set_course_code($values['course_code']);
 	}
 	$cat->set_description($values['description']);
+	
+	$cat->set_skills($values['skills']);
+    
 	$cat->set_user_id($values['hid_user_id']);
 	$cat->set_parent_id($values['hid_parent_id']);
 	$cat->set_weight($values['weight']);

+ 3 - 3
main/gradebook/index.php

@@ -63,7 +63,7 @@ $filter_warning_msg = true;
 
 ///direct access to one evaluation
 $cats = Category :: load(null, null, $course_code, null, null, $session_id, false); //already init
-
+$first_time = null; 
 if (empty($cats)) {
 	$cats = Category :: load(0, null, $course_code, null, null, $session_id, false);//first time
 	$first_time=1;
@@ -618,7 +618,7 @@ if (isset ($_GET['studentoverview'])) {
         // one) category for this course or for this session.
 
 		//hack for delete a gradebook from inside course
-		$clean_deletecat=Security::remove_XSS($_GET['deletecat']);
+		$clean_deletecat = isset($_GET['deletecat']) ? intval($_GET['deletecat']) : null;
 		if (!empty($clean_deletecat)) {
 			exit;
 		}
@@ -765,7 +765,7 @@ if (api_is_allowed_to_edit(null, true)) {
 		}
 	}
 }
-if ($first_time==1 && api_is_allowed_to_edit(null,true)) {
+if (isset($first_time) && $first_time==1 && api_is_allowed_to_edit(null,true)) {
 	echo '<meta http-equiv="refresh" content="0;url='.api_get_self().'?cidReq='.$course_code.'" />';
 } else {		
 	$cats = Category :: load(null, null, $course_code, null, null, $session_id, false); //already init	

+ 48 - 12
main/gradebook/lib/be/category.class.php

@@ -8,10 +8,14 @@
  * Class
  * @package chamilo.gradebook
  */
+
+require_once api_get_path(LIBRARY_PATH).'skill.lib.php';
+require_once api_get_path(LIBRARY_PATH).'gradebook.lib.php';
+
 class Category implements GradebookItem
 {
 
-// PROPERTIES
+    // PROPERTIES
 
 	private $id;
 	private $name;
@@ -23,15 +27,12 @@ class Category implements GradebookItem
 	private $visible;
 	private $certificate_min_score;
     private $session_id;
-
-
-// CONSTRUCTORS
+    private $skills = array();
 
     function __construct() {
     }
 
-
-// GETTERS AND SETTERS
+    // GETTERS AND SETTERS
 
 	public function get_id() {
 		return $this->id;
@@ -54,8 +55,9 @@ class Category implements GradebookItem
 			return $this->certificate_min_score;
 		} else {
 			return null;
-		}
+		}        
 	}
+    
 	public function get_course_code() {
 		return $this->course_code;
 	}
@@ -114,13 +116,34 @@ class Category implements GradebookItem
 		$this->visible = $visible;
 	}
 	
-	public function get_type()
-	{
+	public function get_type() {
 		return 'category';
 	}
+    
+    public function get_skills($from_db = true) {
+        if ($from_db) {
+            $cat_id = $this->get_id();        
+            
+            $gradebook = new Gradebook();
+            $skills = $gradebook->get_skills_by_gradebook($cat_id);
+        } else {
+            $skills = $this->skills;
+        }
+        return $skills;        
+    }
+    
+    function get_skills_for_select() {
+        $skills = $this->get_skills();
+        $skill_select = array();
+        if (!empty($skills)) {            
+            foreach($skills as $skill) {                
+                $skill_select[$skill['id']] = $skill['name'];
+            }
+        }
+        return $skill_select;
+    }
 
-
-// CRUD FUNCTIONS
+    // CRUD FUNCTIONS
 
 	/**
 	 * Retrieve categories and return them as an array of Category objects
@@ -197,7 +220,8 @@ class Category implements GradebookItem
 			$paramcount ++;
 		}
 		//echo $sql;
-		$result = Database::query($sql);		
+		$result = Database::query($sql);
+        $allcat = array();		
 		if (Database::num_rows($result) > 0) {
 			$allcat = Category::create_category_objects_from_sql_result($result);
 		}
@@ -280,6 +304,10 @@ class Category implements GradebookItem
 			Database::query($sql);
 			$id = Database::insert_id();
 			$this->set_id($id);
+            
+            $gradebook= new Gradebook();
+            $res    = $gradebook->update_skills_to_gradebook($this->id, $this->get_skills(false));
+        
 			return $id;
 		}
 	}
@@ -319,6 +347,10 @@ class Category implements GradebookItem
 			.', visible = '.intval($this->is_visible())
 			.' WHERE id = '.intval($this->id);
 		Database::query($sql);
+        
+        $gradebook= new Gradebook();
+        $res    = $gradebook->update_skills_to_gradebook($this->id, $this->get_skills(false));
+
 	}
 
 	/**
@@ -1097,6 +1129,10 @@ class Category implements GradebookItem
 	public function get_item_type() {
 		return 'C';
 	}
+        
+    public function set_skills($skills) {
+        $this->skills = $skills;
+    }
 
 	public function get_date() {
 		return null;

+ 28 - 11
main/gradebook/lib/fe/catform.class.php

@@ -19,10 +19,11 @@ require_once api_get_path(LIBRARY_PATH).'formvalidator/FormValidator.class.php';
 
 class CatForm extends FormValidator {
 
-    const TYPE_ADD = 1;
-    const TYPE_EDIT = 2;
-    const TYPE_MOVE = 3;
-    const TYPE_SELECT_COURSE = 4;
+    const TYPE_ADD              = 1;
+    const TYPE_EDIT             = 2;
+    const TYPE_MOVE             = 3;
+    const TYPE_SELECT_COURSE    = 4;
+    
     private $category_object;
 
 	/**
@@ -115,11 +116,14 @@ class CatForm extends FormValidator {
 	 * Builds an form to edit a category
 	 */
    	protected function build_editing_form() {
+   	    $skills = $this->category_object->get_skills_for_select();        
+    
    		$this->setDefaults(array(
 			'name' 				=> $this->category_object->get_name(),
     		'description' 		=> $this->category_object->get_description(),
     		'hid_user_id' 		=> $this->category_object->get_user_id(),
     		'hid_parent_id' 	=> $this->category_object->get_parent_id(),
+    		'skills'            => $skills,
    	 		'weight' 			=> $this->category_object->get_weight(),
    	 		'visible' 			=> $this->category_object->is_visible(),
    	 		'certif_min_score'  => $this->category_object->get_certificate_min_score(),
@@ -138,7 +142,7 @@ class CatForm extends FormValidator {
 			//we can't change the root category
 			$this->freeze('name');
 		}
-		$models = api_get_settings_options('grading_model');
+		$models                  = api_get_settings_options('grading_model');
 		$course_grading_model_id = api_get_course_setting('course_grading_model');
 		$grading_model = '';
 		if (!empty($course_grading_model_id)) {
@@ -165,13 +169,26 @@ class CatForm extends FormValidator {
 			//$this->addRule('weight',get_lang('ThisFieldIsRequired'),'required');
 			$this->freeze('weight');			
 		} else {
-			$this->add_textfield('weight', get_lang('TotalWeight'),true,array('value'=>$value,'size'=>'4','maxlength'=>'5'));
+			$this->add_textfield('weight', array(get_lang('TotalWeight'), get_lang('TotalSumOfWeights')), true, array('value'=>$value,'size'=>'4','maxlength'=>'5'));
 			$this->addRule('weight',get_lang('ThisFieldIsRequired'),'required');
 		}
-		
-		
-		$this->addElement('static', null, null, '<i>'.get_lang('TotalSumOfWeights').'</i>');
-		
+        
+        if (api_is_platform_admin() || api_is_drh()) {
+            //the magic should be here
+            
+            $skills = $this->category_object->get_skills();    
+            $this->addElement('select', 'skills', array(get_lang('SkillsAchievedWhenAchievingThisGradebook')), null, array('id'=>'skills', 'multiple'=>'multiple'));
+            $content = '';
+            if (!empty($skills)) {
+                foreach($skills as $skill) {                    
+                    $content .= Display::tag('li', $skill['name'].'<a id="deleteskill_'.$skill['id'].'" class="closebutton" href="#"></a>', array('id'=>'skill_'.$skill['id'], 'class'=>'bit-box')); 
+                }
+            }
+            $this->addElement('html', '<div class="row"><div class="label "></div><div class="formw">'.
+                                Display::tag('ul', $content, array('class'=>'holder holder_simple')).'</div></div>'
+            );
+        }
+        
 		if (isset($this->category_object) && $this->category_object->get_parent_id() == 0) {						
 			$this->add_textfield('certif_min_score', get_lang('CertificateMinScore'),false,array('size'=>'4','maxlength'=>'5'));
 			$this->addRule('certif_min_score', get_lang('ThisFieldIsRequired'), 'required');
@@ -191,7 +208,7 @@ class CatForm extends FormValidator {
 			$this->addElement('hidden','editcat', intval($_GET['editcat']));
 			$this->addElement('style_submit_button', null, get_lang('EditCategory'), 'class="save"');
 		}
-		
+        
 		if (!empty($grading_contents)) {
 			$this->addRule('weight',get_lang('OnlyNumbers'),'numeric');
 			$this->addRule('weight',get_lang('NoDecimals'),'nopunctuation');

+ 1 - 1
main/gradebook/lib/fe/displaygradebook.php

@@ -553,7 +553,7 @@ class DisplayGradebook
             		$modify_icons .= '<a href="../document/document.php?curdirpath=/certificates&'.$my_api_cidreq.'&origin=gradebook&selectcat=' . $catobj->get_id() . '">'.
             							Display::return_icon('certificate.png', get_lang('AttachCertificate'),'','32').'</a>';
             		
-            		$modify_icons .= '<a href="gradebook_edit_all.php?id_session='.intval($_SESSION['id_session']).'&amp;'.$my_api_cidreq.'&selectcat=' . $catobj->get_id() . '">'.Display::return_icon('percentage.png', get_lang('EditAllWeights'),'','32').'</a>';
+            		$modify_icons .= '<a href="gradebook_edit_all.php?id_session='.api_get_session_id().'&amp;'.$my_api_cidreq.'&selectcat=' . $catobj->get_id() . '">'.Display::return_icon('percentage.png', get_lang('EditAllWeights'),'','32').'</a>';
             		
             		$modify_icons .= '<a href="gradebook_scoring_system.php?'.$my_api_cidreq.'&selectcat=' . $catobj->get_id() .'">'.Display::return_icon('ranking.png', get_lang('ScoreEdit'),'','32').'</a>';
             		

+ 5 - 4
main/gradebook/lib/fe/gradebooktable.class.php

@@ -272,10 +272,10 @@ class GradebookTable extends SortableTable {
 		}
 
 		// warning messages
-
+        $view = isset($_GET['view']) ? $_GET['view']: null; 
 		if (api_is_allowed_to_edit()) {
 			
-			if (isset($_GET['selectcat']) && $_GET['selectcat'] > 0 && $_GET['view'] <> 'presence') {
+			if (isset($_GET['selectcat']) && $_GET['selectcat'] > 0 && $view <> 'presence') {
 				$id_cat = intval($_GET['selectcat']);
 				$category = Category :: load($id_cat);
 				$weight_category = intval($this->build_weight($category[0]));
@@ -292,8 +292,9 @@ class GradebookTable extends SortableTable {
 				}
 
 				$content_html = DocumentManager::replace_user_info_into_html(api_get_user_id(), $course_code);
-				
-				$new_content = explode('</head>',$content_html['content']);
+				if (!empty($content_html)) {
+				    $new_content = explode('</head>',$content_html['content']);
+                }
 
 				if (empty($new_content[0])) {
 					$warning_message = get_lang('ThereIsNotACertificateAvailableByDefault');

+ 7 - 2
main/gradebook/lib/gradebook_data_generator.class.php

@@ -73,7 +73,7 @@ class GradebookDataGenerator
 		} elseif ($sorting & self :: GDG_SORT_WEIGHT) {
 			usort($allitems, array('GradebookDataGenerator', 'sort_by_weight'));
 		} elseif ($sorting & self :: GDG_SORT_DATE) {
-			usort($allitems, array('GradebookDataGenerator', 'sort_by_date'));
+			//usort($allitems, array('GradebookDataGenerator', 'sort_by_date'));
 		}
 		if ($sorting & self :: GDG_SORT_DESC) {
 			$allitems = array_reverse($allitems);
@@ -154,7 +154,12 @@ class GradebookDataGenerator
 		if(is_int($item1->get_date())) {
 			$timestamp1 = $item1->get_date();
 		} else {
-			$timestamp1 = api_strtotime($item1->get_date());
+		    $date = $item1->get_date();
+            if (!empty($date)) {
+			 $timestamp1 = api_strtotime($date);
+            } else {
+                $timestamp1 = null;
+            }
 		}
 		
 		if(is_int($item2->get_date())) {

+ 1 - 15
main/gradebook/lib/gradebook_functions.inc.php

@@ -484,21 +484,7 @@ function parse_xml_data($file) {
 	return $users;
 }
 
-/**
-* update user info about certificate
-* @param int The category id
-* @param int The user id
-* @param string the path name of the certificate
-* @return void()
-*/
-function update_user_info_about_certificate ($cat_id,$user_id,$path_certificate) {
-    $table_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
-    if (!UserManager::is_user_certified($cat_id,$user_id)) {
-    	$sql='UPDATE '.$table_certificate.' SET path_certificate="'.Database::escape_string($path_certificate).'"
-    		 WHERE cat_id="'.intval($cat_id).'" AND user_id="'.intval($user_id).'" ';
-    	$rs=Database::query($sql);
-    }
-}
+
 
 /**
 * register user info about certificate

+ 5 - 74
main/inc/ajax/agenda.ajax.php

@@ -6,86 +6,17 @@
 
 require_once '../global.inc.php';
 
-require_once api_get_path(SYS_CODE_PATH).'calendar/agenda.inc.php';
-require_once api_get_path(SYS_CODE_PATH).'calendar/myagenda.inc.php';
-require_once api_get_path(SYS_CODE_PATH).'calendar/agenda.lib.php';
+require_once api_get_path(LIBRARY_PATH).'skill.lib.php';
 
 $action = isset($_REQUEST['a']) ? $_REQUEST['a'] : null;
-$type   = isset($_REQUEST['type']) && in_array($_REQUEST['type'], array('personal', 'course', 'admin')) ? $_REQUEST['type'] : 'personal';
 
-$agenda = new Agenda();
-$agenda->type = $type;
+$skill = new Skill();
 
 switch ($action) {
-	case 'add_event':
-		//For now we only save personal events
-		echo $agenda->add_event($_REQUEST['start'], $_REQUEST['end'], $_REQUEST['all_day'], $_REQUEST['view'], $_REQUEST['title'], $_REQUEST['content']);
+	case 'add':		
+		$skill->add($_REQUEST['name'], $_REQUEST['description'], $_REQUEST['parent']);
 		break;		
-	case 'edit_event':
-		$id_list 	= explode('_', $_REQUEST['id']);		
-		//$type 		= $id_list[0];
-		$id 		= $id_list[1];		
-		$agenda->edit_event($id, $_REQUEST['start'], $_REQUEST['end'], $_REQUEST['all_day'], $_REQUEST['view'], $_REQUEST['title'], $_REQUEST['content']);
-		break;
-	case 'delete_event':
-		$id_list 	= explode('_', $_REQUEST['id']);
-		//$type 		= $id_list[0];
-		$id 		= $id_list[1];		
-		$agenda->delete_event($id);		
-		break;
-	case 'move_event':
-		$day_delta 		= $_REQUEST['day_delta'];
-		$minute_delta 	= $_REQUEST['minute_delta'];
-		//$type 			= $_REQUEST['type'][0];
-		$id 			= explode('_', $_REQUEST['id']);
-		$id				= $id[1];
-		$agenda->move_event($id, $day_delta, $minute_delta);		
-		break;
-	case 'get_events':
-		$start 	= $_REQUEST['start'];
-		$end 	= $_REQUEST['end'];					
-		$events = $agenda->get_events($start, $end, api_get_user_id(), api_get_course_int_id());
-		echo $events;		
-		break;				
-    case 'get_user_agenda':
-    	//Used in the admin user list 
-    	api_protect_admin_script();
-    	 
-        if (api_is_allowed_to_edit(null, true)) {
-            //@todo move this in the agenda class
-            $DaysShort  = api_get_week_days_short();
-            $MonthsLong = api_get_months_long();
-            
-            $user_id = intval($_REQUEST['user_id']);    
-       	    $my_course_list = CourseManager::get_courses_list_by_user_id($user_id, true);
-        	if (!is_array($my_course_list)) {
-        		// this is for the special case if the user has no courses (otherwise you get an error)
-        		$my_course_list = array();
-        	}
-        	$today = getdate();
-        	$year = (!empty($_GET['year'])? (int)$_GET['year'] : NULL);
-        	if ($year == NULL) {
-        		$year = $today['year'];
-        	}
-        	$month = (!empty($_GET['month'])? (int)$_GET['month']:NULL);
-        	if ($month == NULL) {
-        		$month = $today['mon'];
-        	}    
-        	$day = (!empty($_GET['day']) ? (int)$_GET['day']:NULL);
-        	if ($day == NULL) {
-        		$day = $today['mday'];
-        	}
-        	$monthName = $MonthsLong[$month -1];            
-        	    	
-        	$agendaitems = get_myagendaitems($user_id, $my_course_list, $month, $year);        	
-        	$agendaitems = get_global_agenda_items($agendaitems, $day, $month, $year, $week, "month_view");
-        	
-        	if (api_get_setting('allow_personal_agenda') == 'true') {
-        		$agendaitems = get_personal_agenda_items($user_id, $agendaitems, $day, $month, $year, $week, "month_view");
-        	}
-        	display_mymonthcalendar($user_id, $agendaitems, $month, $year, array(), $monthName, false);        	
-        }
-        break;
+	
     default:
         echo '';
 }

+ 35 - 6
main/inc/ajax/model.ajax.php

@@ -20,10 +20,16 @@ if (!in_array($sord, array('asc','desc'))) {
 }
 // get index row - i.e. user click to sort $sord = $_GET['sord']; 
 // get the direction 
-if(!$sidx) $sidx = 1;
+if (!$sidx) $sidx = 1;
  
 //2. Selecting the count FIRST
+//@todo rework this
 switch ($action) {
+    case 'get_gradebooks':
+        require_once $libpath.'gradebook.lib.php';
+        $obj        = new Gradebook();
+        $count      = $obj->get_count();
+        break;
     case 'get_careers':        
         require_once $libpath.'career.lib.php';
         $obj        = new Career();
@@ -66,12 +72,35 @@ if ($_REQUEST['oper'] == 'del') {
     $obj->delete($_REQUEST['id']);
 }
 
-
-
 //4. Querying the DB for the elements
 $columns = array();
 switch ($action) {
-    case 'get_careers':  
+    case 'get_gradebooks': 
+        $columns = array('name', 'skills', 'actions');                
+        if(!in_array($sidx, $columns)) {
+            $sidx = 'name';
+        }
+        $result     = Database::select('*', $obj->table, array('order'=>"$sidx $sord", 'LIMIT'=> "$start , $limit"));
+        $new_result = array();
+        foreach($result as $item) {
+            $skills = $obj->get_skills_by_gradebook($item['id']);
+            
+            if (!empty($item['certif_min_score']) && !empty($item['document_id'])) {
+                $item['name'] .= '* (with_certificate)'; 
+            } else {
+                $item['name'] .= ' (No certificate)';
+            }
+            $skills_string = '';
+            if (!empty($skills)) {
+                foreach($skills as $skill) {
+                    $item['skills'] .= Display::span($skill['name'], array('class' => 'label_tag success'));  
+                }
+            }          
+            $new_result[] = $item;
+        } 
+        $result = $new_result;
+        break;
+    case 'get_careers': 
         $columns = array('name', 'description', 'actions');                
         if(!in_array($sidx, $columns)) {
         	$sidx = 'name';
@@ -84,7 +113,7 @@ switch ($action) {
             }
             $new_result[] = $item;
         } 
-        $result = $new_result;  
+        $result = $new_result;
         
     break;
     case 'get_promotions':        
@@ -129,7 +158,7 @@ switch ($action) {
 //var_dump($result);
 
 //5. Creating an obj to return a json
-if (in_array($action, array('get_careers','get_promotions','get_usergroups'))) { 
+if (in_array($action, array('get_careers','get_promotions','get_usergroups','get_gradebooks'))) { 
     $response = new stdClass();           
     $response->page     = $page; 
     $response->total    = $total_pages; 

+ 78 - 0
main/inc/ajax/skill.ajax.php

@@ -0,0 +1,78 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Responses to AJAX calls
+ */
+
+require_once '../global.inc.php';
+
+require_once api_get_path(LIBRARY_PATH).'skill.lib.php';
+require_once api_get_path(LIBRARY_PATH).'gradebook.lib.php';
+
+$action = isset($_REQUEST['a']) ? $_REQUEST['a'] : null;
+
+$skill = new Skill();
+$gradebook = new Gradebook();
+$skill_gradebook = new SkillRelGradebook();
+
+switch ($action) {
+    case 'add':     
+        $skill->add($_REQUEST);
+        break;      
+    case 'find_skills':
+        $tag    = Database::escape_string($_REQUEST['tag']);
+        $skills = $skill->find('all', array('where' => array('name LIKE %?% '=>$_REQUEST['tag'])));
+        $return_skills = array();    
+        foreach($skills as $skill) {
+            $skill['caption'] = $skill['name'];
+            $skill['value'] =  $skill['id'];
+            $return_skills[] = $skill;
+        }            
+        echo json_encode($return_skills);
+        break;
+    case 'get_gradebooks':
+        $gradebooks = $gradebook_list = $gradebook->get_all();
+        /*$gradebook_list = array();
+        if (!empty($gradebooks)) {
+            foreach($gradebooks as $gradebook) {
+                if ($gradebook['parent_id'] == 0) {
+                    $gradebook['name'] = $gradebook['name'];
+                    $gradebook_list[]  = $gradebook;
+                } else {
+                  //  $gradebook['name'] = $gradebook_list[$gradebook['parent_id']]['name'].' > '.$gradebook['name'];
+                    $gradebook_list[]  = $gradebook;
+                }
+               
+            }
+        }*/
+        echo json_encode($gradebook_list);
+        break;    
+    case 'get_skills':
+        $load_user_data = isset($_REQUEST['load_user_data']) ? $_REQUEST['load_user_data'] : null;
+        $skills = $skill->get_all($load_user_data);                    
+        echo json_encode($skills);
+        break;   
+    case 'skill_exists':
+        $skill_data = $skill->get($_REQUEST['skill_id']);        
+        if (!empty($skill_data)) {
+            echo 1;
+        } else {
+            echo 0;
+        }
+        break;
+    case 'remove_skill':
+        if (!empty($_REQUEST['skill_id']) && !empty($_REQUEST['gradebook_id'])) {
+            
+            $skill_item = $skill_gradebook->get_skill_info($_REQUEST['skill_id'], $_REQUEST['gradebook_id']);
+            if (!empty($skill_item)) {
+                $skill_gradebook->delete($skill_item['id']);
+                echo 1;
+            }  
+        } else {
+            echo 0;
+        }
+        break;    
+    default:
+        echo '';
+}
+exit;

+ 2 - 1
main/inc/ajax/user_manager.ajax.php

@@ -14,8 +14,9 @@ switch ($action) {
         } else {   
             if (UserManager::is_user_id_valid($_GET['user_id'])) {
                 echo 1;
+            } else {
+                echo 0;
             }
-            echo 0;
         }
         break;        
 	case 'search_tags':

+ 32 - 6
main/inc/lib/certificate.lib.php

@@ -1,4 +1,7 @@
 <?php
+
+require_once api_get_path(LIBRARY_PATH).'skill.lib.php';
+
 /**
 * @package chamilo.library
 */
@@ -19,7 +22,8 @@ class Certificate extends Model {
     var $qr_file 	= null;
     var $user_id;
     
-    var $force_certificate_generation = false; //If true every time we enter to the certificate URL we would generate a new certificate
+    //If true every time we enter to the certificate URL we would generate a new certificate
+    var $force_certificate_generation = true; 
     
 	public function __construct($certificate_id = null) {
         $this->table 			=  Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
@@ -118,8 +122,6 @@ class Certificate extends Model {
 	 * */
 	
 	public function generate() {
-		
-		
 		//The user directory should be set
 		if (empty($this->certification_user_path) && $this->force_certificate_generation == false) {
 			return false;
@@ -155,7 +157,7 @@ class Certificate extends Model {
 			if (is_dir($this->certification_user_path)) {
 				if (!empty($this->certificate_data)) {	
 					$new_content_html = get_user_certificate_content($this->user_id, $my_category[0]->get_course_code(), false);
-				
+                    
 					if ($my_category[0]->get_id() == strval(intval($this->certificate_data['cat_id']))) {
 						$name = $this->certificate_data['path_certificate'];
 						$my_path_certificate = $this->certification_user_path.basename($name);
@@ -180,8 +182,15 @@ class Certificate extends Model {
 							$result = @file_put_contents($my_path_certificate, $my_new_content_html);
 							if ($result) {						
 								
-								//@todo move function in this class
-								update_user_info_about_certificate($this->certificate_data['cat_id'], $this->user_id, $path_certificate);
+                                //Updating the path
+								self::update_user_info_about_certificate($this->certificate_data['cat_id'], $this->user_id, $path_certificate);
+                                
+                                //If the gradebook is related to skills we added the skills to the user
+                                
+                                $skill = new Skill();                                
+                                $skill->add_skill_to_user($this->user_id, $this->certificate_data['cat_id']);
+                                
+                                
 								$this->certificate_data['path_certificate'] = $path_certificate;
 								
 								if ($this->html_file_is_generated()) {
@@ -199,6 +208,23 @@ class Certificate extends Model {
 		}
 		return false;
 	}
+
+    /**
+    * update user info about certificate
+    * @param int The category id
+    * @param int The user id
+    * @param string the path name of the certificate
+    * @return void()
+    */
+    function update_user_info_about_certificate ($cat_id,$user_id,$path_certificate) {
+        $table_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
+        if (!UserManager::is_user_certified($cat_id,$user_id)) {
+            $sql='UPDATE '.$table_certificate.' SET path_certificate="'.Database::escape_string($path_certificate).'"
+                 WHERE cat_id="'.intval($cat_id).'" AND user_id="'.intval($user_id).'" ';
+            $rs = Database::query($sql);
+        }
+    }
+
 	
 	/**
 	 * 

+ 8 - 0
main/inc/lib/database.constants.inc.php

@@ -322,3 +322,11 @@ define('TABLE_MAIN_STORED_VALUES_STACK',	'stored_values_stack');
 define('TABLE_MAIN_EVENT_TYPE','event_type');
 define('TABLE_MAIN_EVENT_TYPE_MESSAGE','event_type_message');
 define('TABLE_MAIN_EVENT_TYPE_REL_USER','user_rel_event_type');
+
+define('TABLE_MAIN_SKILL',                  'skill');
+define('TABLE_MAIN_SKILL_REL_SKILL',        'skill_rel_skill');
+define('TABLE_MAIN_SKILL_REL_GRADEBOOK',    'skill_rel_gradebook');
+define('TABLE_MAIN_SKILL_REL_USER',         'skill_rel_user');
+
+
+

+ 15 - 12
main/inc/lib/database.lib.php

@@ -1138,7 +1138,7 @@ class Database {
 
     public static function select($columns = '*' , $table_name,  $conditions = array(), $type_result = 'all', $option = 'ASSOC') {
         $conditions = self::parse_conditions($conditions);
-
+        
         //@todo we could do a describe here to check the columns ...
         $clean_columns = '';
         if (is_array($columns)) {
@@ -1152,7 +1152,7 @@ class Database {
         }
 
         $sql    = "SELECT $clean_columns FROM $table_name $conditions";
-		//echo $sql.'<br />';
+	    //echo $sql.'<br />';
         $result = self::query($sql);
         $array = array();
         //if (self::num_rows($result) > 0 ) {
@@ -1176,7 +1176,7 @@ class Database {
      * @param   array
      * @todo lot of stuff to do here
     */
-    public function parse_conditions($conditions) {
+    public function parse_conditions($conditions) {        
         if (empty($conditions)) {
             return '';
         }
@@ -1187,23 +1187,26 @@ class Database {
             }
             $type_condition = strtolower($type_condition);
             switch($type_condition) {
-                case 'where':                    
-                    foreach ($condition_data as $condition => $value_array) {
+                case 'where':                                       
+                    foreach ($condition_data as $condition => $value_array) {                        
                         if (is_array($value_array)) {
                             $clean_values = array();
                             foreach($value_array as $item) {
                                 $item = Database::escape_string($item);
-                                $clean_values[]= "'$item'";
+                                $clean_values[]= $item;
                             }
                         } else {
                             $value_array = Database::escape_string($value_array);
-                            $clean_values = "'$value_array'";
+                            $clean_values = $value_array;                            
                         }
-                        if (!empty($condition) && !empty($clean_values)) {
-                            //$condition = str_replace('%','@percentage@', $condition);
-                            $condition = str_replace('?','%s', $condition); //we treat everything as string
-                            $condition = vsprintf($condition, $clean_values);
-                            //$condition = str_replace('@percentage@','%', $condition);
+                       
+                        if (!empty($condition) && $clean_values != '') {                            
+                            $condition = str_replace('%',"'@percentage@'", $condition); //replace "%"
+                            $condition = str_replace("'?'","%s", $condition); //we treat everything as string                            
+                            //just in case
+                            $condition = str_replace("?","%s", $condition); //we treat everything as string
+                            $condition = vsprintf($condition, $clean_values);                            
+                            $condition = str_replace('@percentage@','%', $condition); //replace "%"
                             $where_return .= $condition;
                         }
                     }

+ 9 - 5
main/inc/lib/database.mysqli.lib.php

@@ -1132,15 +1132,19 @@ class Database {
                             $clean_values = array();
                             foreach($value_array as $item) {
                                 $item = Database::escape_string($item);
-                                $clean_values[]= "'$item'";
+                                $clean_values[]= $item;
                             }
                         } else {
                             $value_array = Database::escape_string($value_array);
-                            $clean_values = "'$value_array'";
+                            $clean_values = $value_array;
                         }
-                        if (!empty($condition) && !empty($clean_values)) {
-                            $condition = str_replace('?','%s', $condition); //we treat everything as string
-                            $condition = vsprintf($condition, $clean_values);
+                        if (!empty($condition) && $clean_values != '') {
+                            $condition = str_replace('%',"'@percentage@'", $condition); //replace "%"
+                            $condition = str_replace("'?'","%s", $condition); //we treat everything as string                            
+                            //just in case
+                            $condition = str_replace("?","%s", $condition); //we treat everything as string
+                            $condition = vsprintf($condition, $clean_values);                            
+                            $condition = str_replace('@percentage@','%', $condition); //replace "%"
                             $where_return .= $condition;
                         }
                     }

+ 19 - 16
main/inc/lib/document.lib.php

@@ -1338,24 +1338,27 @@ return 'application/octet-stream';
         $tbl_document 	= Database::get_course_table(TABLE_DOCUMENT);
         $course_id 		= $course_info['real_id']; 
         $document_id 	= self::get_default_certificate_id($course_code);
-
-        $sql = "SELECT path FROM $tbl_document WHERE c_id = $course_id AND id = $document_id";
-
-        $rs = Database::query($sql);
-        $new_content = '';
-        $all_user_info = array();
-        if (Database::num_rows($rs)) {
-            $row=Database::fetch_array($rs);
-            $filepath = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
-            if (is_file($filepath)) {
-                $my_content_html = file_get_contents($filepath);
+        if ($document_id) {
+    
+            $sql = "SELECT path FROM $tbl_document WHERE c_id = $course_id AND id = $document_id";
+    
+            $rs = Database::query($sql);
+            $new_content = '';
+            $all_user_info = array();
+            if (Database::num_rows($rs)) {
+                $row=Database::fetch_array($rs);
+                $filepath = api_get_path(SYS_COURSE_PATH).$course_info['path'].'/document'.$row['path'];
+                if (is_file($filepath)) {
+                    $my_content_html = file_get_contents($filepath);
+                }
+                $all_user_info = self::get_all_info_to_certificate($user_id, $course_code, $is_preview);            
+                $info_to_be_replaced_in_content_html=$all_user_info[0];
+                $info_to_replace_in_content_html=$all_user_info[1];
+                $new_content=str_replace($info_to_be_replaced_in_content_html,$info_to_replace_in_content_html, $my_content_html);
             }
-            $all_user_info = self::get_all_info_to_certificate($user_id, $course_code, $is_preview);            
-            $info_to_be_replaced_in_content_html=$all_user_info[0];
-            $info_to_replace_in_content_html=$all_user_info[1];
-            $new_content=str_replace($info_to_be_replaced_in_content_html,$info_to_replace_in_content_html, $my_content_html);
+            return array('content' => $new_content, 'variables' => $all_user_info);
         }
-        return array('content' => $new_content, 'variables' => $all_user_info);
+        return array();
     }
 
     /**

+ 134 - 0
main/inc/lib/gradebook.lib.php

@@ -0,0 +1,134 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+*   This class provides methods for the notebook management.
+*   Include/require it in your code to use its features.
+*   @package chamilo.library
+*/
+/**
+ * Code
+ */
+
+
+    
+    
+class Gradebook extends Model {    
+    var $columns = array('id', 'name','description', 'course_code', 'parent_id');
+    
+    public function __construct() {
+        $this->table                        = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
+        $this->table_skill                  = Database::get_main_table(TABLE_MAIN_SKILL);
+        $this->table_skill_rel_gradebook    = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);                    
+    }
+    
+    public function update_skills_to_gradebook($gradebook_id, $skill_list) {
+        
+        if (!empty($skill_list)) {
+            
+            //Cleaning skills
+            $skill_list = array_map('intval', $skill_list);
+            $skill_list = array_filter($skill_list);            
+            $skill_gradebook = new SkillRelGradebook();
+            $skill_gradebooks_source = $skill_gradebook->get_all(array('where'=>array('gradebook_id = ?' =>$gradebook_id)));
+            $clean_gradebook = array();
+            if (!empty($skill_gradebooks_source)) {
+                foreach($skill_gradebooks_source as $source) {
+                    $clean_gradebook[]= $source['skill_id'];                    
+                }
+            }            
+            if (!empty($clean_gradebook)) {
+                $skill_to_remove = array_diff($clean_gradebook, $skill_list);
+            }            
+                    
+            foreach ($skill_list as $skill_id) {
+                $params = array();     
+                $params['gradebook_id'] = $gradebook_id;
+                $params['skill_id']     = $skill_id; 
+                if (!$skill_gradebook->exists_gradebook_skill($gradebook_id, $skill_id)) {                    
+                    $skill_gradebook->save($params);
+                }
+            }
+            
+            if (!empty($skill_to_remove)) {
+                foreach($skill_to_remove as $remove) {                    
+                    $skill_item = $skill_gradebook->get_skill_info($remove, $gradebook_id);
+                    $skill_gradebook->delete($skill_item['id']);
+                }
+            }    
+            return true;
+        }   
+        return false;     
+    }
+    
+    
+    
+    /**
+     * Returns a Form validator Obj
+     * @todo the form should be auto generated
+     * @param   string  url
+     * @param   string  action add, edit
+     * @return  obj     form validator obj 
+     */
+    public function show_skill_form($gradebook_id, $url) {
+                
+        $form = new FormValidator('gradebook_add_skill', 'POST', $url);
+        // Settting the form elements
+        $header = get_lang('Add');        
+        $form->addElement('header', '', $header);
+        $id = isset($_GET['id']) ? intval($_GET['id']) : '';
+        $form->addElement('hidden', 'id', $id);
+        
+        //$status_list = $this->get_status_list();       
+        $skill = new Skill();
+        $skills = $skill->get_all();
+        $clean_skill_list = array();
+        foreach ($skills as $skill) {
+            $clean_skill_list[$skill['id']] = $skill['name'];
+        }   
+        $form->addElement('select', 'skill', get_lang('Skills'), $clean_skill_list, array('width'=>'450px', 'class'=>'chzn-select','multiple' => 'multiple'));
+        
+        $selected_skills = self::get_skills_by_gradebook($gradebook_id);
+        $clean_selected_skills = array();
+        if (!empty($selected_skills)) {
+            foreach($selected_skills as $skill) {
+                $clean_selected_skills[] = $skill['id'];
+            }
+        }
+        
+        $form->addElement('style_submit_button', 'submit', get_lang('Add'), 'class="save"');
+        
+        $form->setDefaults(array('skill'=>$clean_selected_skills));
+        return $form;                                
+    }
+
+     
+    
+    
+    function get_children() {
+    }
+    
+    function get_skills_by_gradebook($gradebook_id) {
+        $gradebook_id = intval($gradebook_id);
+        $sql = "SELECT skill.id, skill.name FROM {$this->table_skill} skill INNER JOIN {$this->table_skill_rel_gradebook} skill_rel_gradebook
+                    ON skill.id = skill_rel_gradebook.skill_id 
+                 WHERE skill_rel_gradebook.gradebook_id = $gradebook_id";
+        $result = Database::query($sql);         
+        $result = Database::store_result($result,'ASSOC');
+        return $result;
+    }
+    
+    
+    /**
+     * Displays the title + grid
+     */
+    public function display() {
+        // action links
+        echo '<div class="actions" style="margin-bottom:20px">';
+        echo '<a href="career_dashboard.php">'.Display::return_icon('back.png',get_lang('Back'),'','32').'</a>';        
+      //  echo '<a href="'.api_get_self().'?action=add">'.Display::return_icon('new_career.png',get_lang('Add'),'','32').'</a>';                      
+        echo '</div>';   
+        echo Display::grid_html('gradebooks');  
+    }
+    
+    
+}

+ 10 - 4
main/inc/lib/javascript/tag/style.css

@@ -1,12 +1,14 @@
 /* TextboxList sample CSS */
 ul.holder {
 		-moz-border-radius:5px 5px 5px 5px;
-	-web-kit-border-radius:5px 5px 5px 5px;
-	 
+	-web-kit-border-radius:5px 5px 5px 5px;	 
 	margin: 0; border: 1px solid #ccc; overflow: hidden; height: auto !important; height: 1%; padding: 4px 5px 0; }
 *:first-child+html ul.holder { padding-bottom: 2px; } * html ul.holder { padding-bottom: 2px; } /* ie7 and below */
 ul.holder li { float: left; list-style-type: none; margin: 0 5px 4px 0; white-space:nowrap;}
-ul.holder li.bit-box, ul.holder li.bit-input input { font: 11px "Lucida Grande", "Verdana"; }
+ul.holder li.bit-box, ul.holder li.bit-input input { 
+    font: 11px "Lucida Grande", "Verdana";
+    box-shadow: none;
+}
 ul.holder li.bit-box { -moz-border-radius: 6px; -webkit-border-radius: 6px; border-radius: 6px; border: 1px solid #CAD8F3; background: #DEE7F8; padding: 1px 5px 2px; }
 ul.holder li.bit-box-focus { border-color: #598BEC; background: #598BEC; color: #fff; }
 ul.holder li.bit-input input {	 
@@ -55,4 +57,8 @@ ul.holder li.bit-box-focus a.closebutton, ul.holder li.bit-box-focus a.closebutt
 .hidden { display:none;}
 
 #demo ul.holder li.bit-input input { padding: 2px 0 1px; border: 1px solid #999; }
-.ie6fix {height:1px;width:1px; position:absolute;top:0px;left:0px;z-index:1;}
+.ie6fix {height:1px;width:1px; position:absolute;top:0px;left:0px;z-index:1;}
+
+.holder_simple {
+    border:none !important;    
+}

+ 8 - 5
main/inc/lib/main_api.lib.php

@@ -1043,11 +1043,13 @@ function api_get_course_setting($setting_name, $course_code = null) {
     $course_info = api_get_course_info($course_code);    
 	$table 		 = Database::get_course_table(TABLE_COURSE_SETTING);    
     $setting_name = Database::escape_string($setting_name);
-    $sql = "SELECT value FROM $table WHERE c_id = {$course_info['real_id']} AND variable = '$setting_name'";    
-    $res = Database::query($sql);
-    if (Database::num_rows($res) > 0) {
-        $row = Database::fetch_array($res);
-        return $row['value'];
+    if (!empty($course_info['real_id']) && !empty($setting_name)) {
+        $sql = "SELECT value FROM $table WHERE c_id = {$course_info['real_id']} AND variable = '$setting_name'";    
+        $res = Database::query($sql);
+        if (Database::num_rows($res) > 0) {
+            $row = Database::fetch_array($res);
+            return $row['value'];
+        }
     }
     return -1;
 }
@@ -3792,6 +3794,7 @@ function api_get_settings_options($var) {
 	$table_settings_options = Database :: get_main_table(TABLE_MAIN_SETTINGS_OPTIONS);
 	$sql = "SELECT * FROM $table_settings_options WHERE variable = '$var' ORDER BY id";
 	$result = Database::query($sql);
+    $settings_options_array = array();
 	while ($row = Database::fetch_array($result, 'ASSOC')) {
 		//$temp_array = array ('value' => $row['value'], 'display_text' => $row['display_text']);
 		$settings_options_array[] = $row;

+ 293 - 0
main/inc/lib/skill.lib.php

@@ -0,0 +1,293 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+*   This class provides methods for the notebook management.
+*   Include/require it in your code to use its features.
+*   @package chamilo.library
+*/
+/**
+ * Code
+ */
+
+ /**
+ * @package chamilo.library
+ */
+
+define ('SKILL_TYPE_REQUIREMENT',   'required');
+define ('SKILL_TYPE_ACQUIRED',      'acquired');
+define ('SKILL_TYPE_BOTH',          'both');
+
+class SkillRelSkill extends Model {
+    var $columns = array('skill_id', 'parent_id','relation_type', 'level');
+    public function __construct() {
+        $this->table                      = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
+    }
+    
+    /**
+     * Gets an element
+     */
+    public function get_skill_info($id) {
+        if (empty($id)) { return array(); }     
+        $result = Database::select('*',$this->table, array('where'=>array('skill_id = ?'=>intval($id))));
+        return $result;
+    }
+    
+    public function get_skill_parents($skill_id, $add_child_info = true) {
+        $skill_id = intval($skill_id);        
+        $sql = 'SELECT child.* FROM '.$this->table.' child LEFT JOIN '.$this->table.' parent ON child.parent_id = parent.skill_id
+                WHERE child.skill_id = '.$skill_id.' ';
+        $result = Database::query($sql);
+        $skill  = Database::store_result($result,'ASSOC');
+        $skill  = isset($skill[0]) ? $skill[0] : null; 
+                  
+        $parents = array();
+        if (!empty($skill)) {        
+            if ($skill['parent_id'] != null) {
+                $parents = self::get_skill_parents($skill['parent_id']);
+            }        
+            if ($add_child_info) {
+                $parents[] = $skill;
+            }        
+        }
+        return $parents;
+    }
+}
+
+ /**
+ * @package chamilo.library
+ */
+class SkillRelGradebook extends Model {
+    var $columns = array('id', 'gradebook_id','skill_id');
+    
+    public function __construct() {
+        $this->table                      = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
+    }
+    
+    public function exists_gradebook_skill($gradebook_id, $skill_id) {
+        $result = $this->find('all', array('where'=>array('gradebook_id = ? AND skill_id = ?' => array($gradebook_id, $skill_id))));
+        if (!empty($result)) {
+            return true;
+        }
+        return false;    
+    }
+    
+    /**
+     * Gets an element
+     */
+    public function get_skill_info($skill_id, $gradebook_id) {
+        if (empty($skill_id)) { return array(); }     
+        $result = Database::select('*',$this->table, array('where'=>array('skill_id = ? AND gradebook_id = ? '=>array($skill_id, $gradebook_id))),'first');
+        return $result;
+    }
+    
+}
+
+ /**
+ * @package chamilo.library
+ */
+class SkillRelUser extends Model {
+    var $columns = array('id', 'user_id','skill_id','acquired_skill_at','assigned_by');
+    public function __construct() {
+        $this->table = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
+    }
+}
+   
+
+class Skill extends Model {    
+    var $columns = array('id', 'name','description', 'access_url_id');
+    
+    public function __construct() {
+        $this->table                      = Database::get_main_table(TABLE_MAIN_SKILL);
+        $this->table_skill_rel_gradebook  = Database::get_main_table(TABLE_MAIN_SKILL_REL_GRADEBOOK);
+        $this->table_skill_rel_user       = Database::get_main_table(TABLE_MAIN_SKILL_REL_USER);
+        $this->table_course               = Database::get_main_table(TABLE_MAIN_COURSE);
+        $this->table_skill_rel_skill      = Database::get_main_table(TABLE_MAIN_SKILL_REL_SKILL);
+        $this->table_gradebook            = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
+    }
+    
+    public function skill_exists($skill_id) {
+        
+        
+    }
+     
+    function get_all($load_user_data = false) {
+        $sql = "SELECT id, name, description, parent_id, relation_type 
+                    FROM {$this->table} skill INNER JOIN {$this->table_skill_rel_skill} skill_rel_skill
+                    ON skill.id = skill_rel_skill.skill_id ";
+        $result = Database::query($sql);
+        $skills = array();        
+        if (Database::num_rows($result)) {        
+            while ($row = Database::fetch_array($result, 'ASSOC')) {
+                $skill_rel_skill = new SkillRelSkill();
+                $a = $skill_rel_skill->get_skill_parents($row['id']);                              
+                $row['level'] = count($a)-1;
+                $row['gradebooks'] = self::get_gradebooks_by_skill($row['id']);                    
+                $skills[$row['id']] = $row;            
+            }        
+        }
+        
+        if ($load_user_data) {
+            $passed_skills = $this->get_user_skills(api_get_user_id());      
+                  
+            foreach ($skills as &$skill) {
+                $skill['done_by_user'] = 0;
+                if (in_array($skill['id'], $passed_skills)) {
+                    $skill['done_by_user'] = 1;
+                }                
+            }
+        }
+        
+        return $skills;        
+    }
+    
+    function get_gradebooks_by_skill($skill_id) {
+        $skill_id = intval($skill_id);
+        $sql = "SELECT g.* FROM {$this->table_gradebook} g INNER JOIN {$this->table_skill_rel_gradebook} sg
+                    ON g.id = sg.gradebook_id 
+                 WHERE sg.skill_id = $skill_id";
+        $result = Database::query($sql);         
+        $result = Database::store_result($result,'ASSOC');
+        return $result;
+    }
+    
+    public function add($params) {
+        if (!isset($params['parent_id'])) {
+            $params['parent_id'] = 1;
+        }
+        $skill_rel_skill     = new SkillRelSkill();
+        $skill_rel_gradebook = new SkillRelGradebook();
+        
+        //Saving name, description
+        $skill_id = $this->save($params);
+        if ($skill_id) {
+            //Saving skill_rel_skill (parent_id, relation_type)
+            $attributes = array(
+                            'skill_id'      => $skill_id,
+                            'parent_id'     => $params['parent_id'],
+                            'relation_type' => $params['relation_type'],
+                            //'level'         => $params['level'],
+            );            
+            $skill_rel_skill->save($attributes);            
+            
+            if (!empty($params['gradebook_id'])) {
+                foreach ($params['gradebook_id'] as $gradebook_id) {
+                    $attributes = array();
+                    $attributes['gradebook_id'] = $gradebook_id;
+                    $attributes['skill_id']     = $skill_id;                    
+                    $skill_rel_gradebook->save($attributes);
+                }            
+            }                                 
+        }
+    }
+    
+    
+    
+    /**
+    * Return true if the user has the skill
+    * 
+    * @param int $userId User's id
+    * @param int $skillId Skill's id
+    * @param int $checkInParents if true, function will search also in parents of the given skill id
+    * 
+    * @return bool
+    */
+    public function user_has_skill($user_id, $skill_id) {
+        $skills = $this->get_user_skills($user_id);                    
+        foreach($skills as $my_skill_id) {
+            if ($my_skill_id == $skill_id) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    public function add_skill_to_user($user_id, $gradebook_id) {
+        $skill_gradebook = new SkillRelGradebook();
+        $skill_rel_user  = new SkillRelUser();
+        
+        $skill_gradebooks = $skill_gradebook->get_all(array('where'=>array('gradebook_id = ?' =>$gradebook_id)));
+        if (!empty($skill_gradebooks)) {        
+            foreach ($skill_gradebooks as $skill_gradebook) {
+                $user_has_skill = $this->user_has_skill($user_id, $skill_gradebook['skill_id']);            
+                if (!$user_has_skill) {
+                    $params = array(    'user_id'   => $user_id,
+                                        'skill_id'  => $skill_gradebook['skill_id'],
+                                        'acquired_skill_at'  => api_get_utc_datetime(),
+                                   );                                   
+                    $skill_rel_user->save($params);
+                }
+            }
+        }
+    }
+    
+    public function remove_skill_to_user($user_id) {
+    }
+    
+
+    
+    /**
+    * Get user's skills
+    * 
+    * @param int $userId User's id
+
+
+    */
+    public function get_user_skills($user_id, $get_skill_data = false) {
+        $user_id = intval($user_id);        
+        //$sql = 'SELECT skill.*, user.* FROM '.$this->table_skill_rel_user.' user INNER JOIN '.$this->table_skill.' skill
+        
+        $sql = 'SELECT DISTINCT s.id, s.name FROM '.$this->table_skill_rel_user.' u INNER JOIN '.$this->table.' s 
+                ON u.skill_id = s.id
+                WHERE user_id = '.$user_id;
+        $result = Database::query($sql);
+        $skills = Database::store_result($result, 'ASSOC');
+        $clean_skill = array();        
+        if (!empty($skills)) {
+            foreach ($skills as $skill) {
+                if ($get_skill_data) {
+                    $clean_skill[$skill['id']] = $skill;    
+                } else {
+                    $clean_skill[$skill['id']] = $skill['id'];
+                }                
+            }
+        }
+        return $clean_skill;
+    }
+
+        
+    public static function get_skills_tree($user_id = null) {
+        $skills = self::get_all();    
+        $refs = array();        
+        $skills_tree = null;
+        
+        // Create references for all nodes
+        if (!empty($skills)) {
+            foreach($skills as &$skill) {
+                if ($skill['parent'] == null) {
+                    $skill['parent'] = 'root';
+                }
+                
+                $skill['data'] = array('parent' => $skill['parent']); // because except main keys (id, name, children) others keys are not saved while in the space tree
+                
+                if ($user_id) {
+                    $skill['data']['achieved'] = self::user_has_skill($user_id, $skill['id']);
+                }                
+                $refs[$skill['id']] = &$skill;
+            }
+        
+            // Moving node to the children index of their parents
+            foreach($skills as $skillInd => &$skill) {
+                $refs[$skill['parent']]['children'][] = &$skill;                
+            }
+            
+            $skills_tree = array(
+                'name' => get_lang('SkillRootName'),
+                'id' => 'root',
+                'children' => $refs['root']['children'],
+                'data' => array()
+                );
+        }        
+        unset($skills);        
+        return $skills_tree;
+    }    
+}

+ 125 - 0
main/inc/lib/skill.visualizer.lib.php

@@ -0,0 +1,125 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ *  @package chamilo.admin
+ */
+
+ class SkillVisualizer {
+
+    private $offsetX = 100;
+    private $offsetY = 50;
+   
+    private $html    = '';
+    private $type    = 'read';
+    private $js      = '';
+       
+    function __construct($skills, $type = 'read') {
+        $this->skills = $skills;       
+        $this->type = $type; 
+    }
+    
+    function prepare_skill_box($skill, $position, $class) {
+        $block_id = $skill['id'];
+        $this->html .= '<div id="block_'.$block_id.'" class="window '.$class.'" style="top:' . $position['y'] . 'px; left:' . $position['x'] . 'px;">';
+        $gradebook_string = '';
+        if (!empty($skill['gradebooks'])) {
+            foreach($skill['gradebooks'] as $gradebook) {
+                $gradebook_string .= Display::span($gradebook['name'], array('class'=>'label_tag notice','style'=>'width:50px'));    
+            }
+        }        
+        $this->html .= $skill['name'].' '.$gradebook_string;
+        
+        if ($this->type == 'edit' && $skill['parent_id'] != 0) {
+            $this->html .= Display::url(get_lang('Edit'), '#', array('id=>"edit_block_'.$block_id,'class'=>'edit_block'));
+            $this->html .= Display::url(get_lang('Add'), '#', array('id=>"edit_block_'.$block_id,'class'=>'edit_block'));
+            $this->html .= Display::url(get_lang('Delete'), '#', array('id=>"edit_block_'.$block_id,'class'=>'edit_block'));
+            
+        }
+        $this->html .= '</div>';   
+    }
+    
+    /**
+     * Adds a node using jplumb
+     */
+    private function add_item($skill, $position) {
+        $block_id = $skill['id'];
+                        
+       
+        $end_point = 'readEndpoint';
+        //var_dump($skill);
+        $class = 'default_window';
+        
+        if ($this->type == 'edit') {
+            $end_point = 'editEndpoint';
+            $class = 'edit_window';
+        } else {
+            if ($skill['done_by_user'] == 1) {
+                $end_point = 'doneEndpoint';
+                $class = 'done_window';
+            } else {
+                $end_point = 'defaultEndpoint';
+            }
+        } 
+        $this->prepare_skill_box($skill, $position, $class);
+        
+        if ($skill['parent_id'] == 0) {
+            return;
+        }
+
+       //default_arrow_color
+        
+       $this->js .= 'var e'.$block_id.' = prepare("block_' . $block_id.'",  '.$end_point.');'."\n";
+       $this->js .= 'var e'.$skill['parent_id'].' = prepare("block_' . $skill['parent_id'].'",  '.$end_point.');'."\n";;
+                        
+       $this->js .= 'jsPlumb.connect({source: e'.$block_id.', target:e'.$skill['parent_id'].'});'."\n";;
+    }
+    
+    /**
+     * Displays the HTMl part of jplumb
+     */
+    public function display_html() {
+        if (empty($this->skills)) {
+            return '';
+        }
+        $skill_count = sizeof($this->skills);
+        //$corner = 360 / $skill_count;   
+        $count = 0;
+        
+        $brothers = array();
+        
+        
+        //$this->add_item($skill, array('x' => $x + $this->offsetX, 'y' => $y + $this->offsetY));        
+        foreach ($this->skills as $skill) {            
+            if (isset($brothers[$skill['parent_id']])) {
+                $brothers[$skill['parent_id']] +=2;
+            } else {
+                $brothers[$skill['parent_id']] = 1;
+            }            
+            //$x = round($this->offsetX * sin(deg2rad($corner * $count)));
+            //$y = round($this->offsetY * cos(deg2rad($corner * $count)));
+           
+            $x = $brothers[$skill['parent_id']]*100;            
+            $y = $skill['level']*120;                        
+            //$skill['description']  = "{$brothers[$skill['parent_id']]} $x - $y";
+            $this->add_item($skill, array('x' => $this->offsetX + $x, 'y' => $this->offsetY +$y));            
+        }        
+        echo $this->get_html();
+    }
+    
+    /**
+     * Displays the Javascript part of jplumb
+     */
+    public function display_js() {
+        echo $this->get_js();
+    }  
+   
+
+    private function get_html() {
+        return $this->html;
+    }
+   
+    private function get_js() {
+        return $this->js;
+    }
+}

+ 11 - 0
main/inc/lib/userportal.lib.php

@@ -389,6 +389,17 @@ class IndexManager {
 		}
 		return $html;
 	}
+    
+    function return_skills_links() {
+        $content = '<ul class="menulist">';
+        
+        $content .= Display::tag('li', Display::url(get_lang('SkillsTree'), api_get_path(WEB_CODE_PATH).'admin/skills.php'));
+        $content .= Display::tag('li', Display::url(get_lang('MySkills'), api_get_path(WEB_PATH).'social/skills_tree.php'));
+        $content .= '</ul>';
+        
+        $html = self::show_right_block(get_lang("Skills"), $content);
+        return $html;
+    }
 	
 	/**
 	 * Reacts on a failed login:

+ 47 - 1
main/install/db_main.sql

@@ -850,6 +850,7 @@ VALUES
 ('drh_autosubscribe', NULL, 'textfield', 'Platform', '', 'DRHAutosubscribeTitle', 'DRHAutosubscribeComment', NULL, NULL, 0),
 ('sessionadmin_autosubscribe', NULL, 'textfield', 'Platform', '', 'SessionadminAutosubscribeTitle', 'SessionadminAutosubscribeComment', NULL, NULL, 0),
 ('scorm_cumulative_session_time', NULL, 'radio', 'Course', 'true', 'ScormCumulativeSessionTimeTitle', 'ScormCumulativeSessionTimeComment', NULL, NULL, 0),
+('allow_hr_skills_management', NULL, 'radio', 'Gradebook', 'true', 'AllowHRSkillsManagementTitle', 'AllowHRSkillsManagementComment', NULL, NULL, 1),
 ('chamilo_database_version',NULL,'textfield',NULL, '1.9.0.15858','DokeosDatabaseVersion','', NULL, NULL, 0);
 
 /*
@@ -1146,7 +1147,10 @@ VALUES
 ('cas_add_user_activate', 'true', 'Yes'),
 ('cas_add_user_activate', 'false', 'No'),
 ('scorm_cumulative_session_time','true','Yes'),
-('scorm_cumulative_session_time','false','No');
+('scorm_cumulative_session_time','false','No'),
+('allow_hr_skills_management', 'true', 'Yes'),
+('allow_hr_skills_management', 'false', 'No');
+
 /*
 ('use_custom_pages','true','Yes'),
 ('use_custom_pages','false','No'),
@@ -2737,6 +2741,48 @@ CREATE TABLE notification (
 ALTER TABLE notification ADD index mail_notify_sent_index (sent_at);
 ALTER TABLE notification ADD index mail_notify_freq_index (sent_at, send_freq, created_at);
 
+-- Skills management
+
+CREATE TABLE skill (
+  id int NOT NULL AUTO_INCREMENT,
+  name varchar(255) NOT NULL,
+  short_code varchar(100) NOT NULL,
+  description TEXT NOT NULL,
+  access_url_id int NOT NULL,
+  icon varchar(255) NOT NULL,
+  PRIMARY KEY (id)
+);
+
+INSERT INTO skill VALUES('Root');
+
+CREATE TABLE skill_rel_gradebook (
+  id int NOT NULL AUTO_INCREMENT,
+  gradebook_id int NOT NULL,
+  skill_id int NOT NULL,
+  type varchar(10) NOT NULL,
+  PRIMARY KEY (id)
+);
+
+CREATE TABLE skill_rel_skill (
+  skill_id int NOT NULL,
+  parent_id int NOT NULL,
+  relation_type int NOT NULL,
+  level int NOT NULL,
+);
+
+INSERT INTO skill_rel_skill VALUES(1, 0, 0);
+
+CREATE TABLE skill_rel_user (
+  id int NOT NULL AUTO_INCREMENT,
+  user_id int NOT NULL,
+  skill_id int NOT NULL,
+  acquired_skill_at datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
+  assigned_by int NOT NULL, 
+  PRIMARY KEY (id)
+);
+
+
+
 --
 -- Table structure for event alert sending
 -- Pending check and approval

+ 8 - 0
main/install/migrate-db-1.8.8-1.9.0-pre.sql

@@ -122,6 +122,14 @@ CREATE TABLE stored_values_stack (user_id INT NOT NULL, sco_id INT NOT NULL, sta
 ALTER TABLE stored_values_stack ADD KEY (user_id, sco_id, course_id, sv_key, stack_order);
 ALTER TABLE stored_values_stack ADD UNIQUE (user_id, sco_id, course_id, sv_key, stack_order);
 
+INSERT INTO settings_current (variable, subkey, type, category, selected_value, title, comment, scope, subkeytext, access_url_changeable) VALUES ('allow_hr_skills_management', NULL, 'radio', 'Gradebook', 'true', 'AllowHRSkillsManagementTitle', 'AllowHRSkillsManagementComment', NULL, NULL, 1);
+
+INSERT INTO settings_options (variable, value, display_text) VALUES ('allow_hr_skills_management', 'true', 'Yes');
+INSERT INTO settings_options (variable, value, display_text) VALUES ('allow_hr_skills_management', 'false', 'No');
+
+
+
+
 -- xxUSERxx
 
 -- xxCOURSExx

+ 3 - 0
main/lang/english/trad4all.inc.php

@@ -1143,4 +1143,7 @@ $NoJava = "Your browser does not support Java";
 $JavaSun24 = "Your browser has a Java version not supported by this tool.
 To use it you have to install a Java Sun version higher than 24";
 $NoMessageAnywere = "If you do not like see this message again during this session click here";
+
+$SkillsAchievedWhenAchievingThisGradebook = 'Skills Achieved When Achieving This Gradebook';
+
 ?>

+ 2 - 12
main/messages/new_message.php

@@ -21,11 +21,10 @@ require_once api_get_path(LIBRARY_PATH).'mail.lib.inc.php';
 
 api_block_anonymous_users();
 
-if (api_get_setting('allow_message_tool') !='true' ){
+if (api_get_setting('allow_message_tool') !='true') {
 	api_not_allowed();
 }
 
-require_once api_get_path(LIBRARY_PATH).'formvalidator/FormValidator.class.php';
 require_once api_get_path(LIBRARY_PATH).'group_portal_manager.lib.php';
 
 $nameTools = api_xml_http_response_encode(get_lang('Messages'));
@@ -120,16 +119,7 @@ function add_image_form() {
 		}
 	}
 }
-</script>
-
-<style>
-div.row div.label {
-    width:15%;
-}
-div.row div.formw {
-    width:80%;
-}
-</style>';
+</script>';
 $nameTools = get_lang('ComposeMessage');
 /*		FUNCTIONS  */
 

+ 91 - 60
main/social/home.php

@@ -12,6 +12,7 @@ $cidReset = true;
 
 require_once '../inc/global.inc.php';
 require_once api_get_path(LIBRARY_PATH).'group_portal_manager.lib.php';
+require_once api_get_path(LIBRARY_PATH).'skill.lib.php';
 
 $user_id = api_get_user_id();
 $show_full_profile = true;
@@ -77,79 +78,109 @@ echo '<div id="social-content">';
 	//this include the social menu div
 	SocialManager::show_social_menu('home');
 	echo '</div>';
+    
+    
 	echo '<div id="social-content-right">';
-		echo '<div class="social-box-main1">';
-			echo '<div class="social_user_information" style="width:280px">';
-				echo '<div class="social-groups-home-title">'.get_lang('ContactInformation').'</div>';
+		
+        echo '<div class="social_user_information" style="width:280px">';
+            
+            echo '<div class="social-groups-home-title">'.get_lang('ContactInformation').'</div>';
 			
-			   // information current user		       
-               echo '<div><p><strong>'.get_lang('Name').'</strong><br /><span class="social-groups-text4">'.api_get_person_name($user_info['firstname'], $user_info['lastname']).'</span></p></div>
-                        <div><p><strong>'.get_lang('Email').'</strong><br /><span class="social-groups-text4">'.($user_info['email']?$user_info['email']:'').'</span></p></div>
-                        <div class="box_description_group_actions" ><a href="'.api_get_path(WEB_PATH).'main/auth/profile.php">'.Display::return_icon('profile_edit.png', get_lang('EditProfile'), array('hspace'=>'6')).get_lang('EditProfile').'</a></div>';
-                echo '</div>';
+		    // information current user		       
+            echo '<div>
+                    <p><strong>'.get_lang('Name').'</strong><br />
+                    <span class="social-groups-text4">'.api_get_person_name($user_info['firstname'], $user_info['lastname']).'</span></p>
+                  </div>
+                  <div>
+                    <p><strong>'.get_lang('Email').'</strong><br /><span class="social-groups-text4">'.($user_info['email']?$user_info['email']:'').'</span></p>
+                  </div>
+                  <div class="box_description_group_actions">
+                    <a href="'.api_get_path(WEB_PATH).'main/auth/profile.php">
+                        '.Display::return_icon('profile_edit.png', get_lang('EditProfile'), array('hspace'=>'6')).get_lang('EditProfile').'
+                   </a>
+                  </div>';            
+        echo '</div>';
+        
+        $skill = new Skill();
+        $skills =  $skill->get_user_skills(api_get_user_id(), true);
+        
+        echo '<div class="left" style="width:280px">';            
+            echo '<div class="social-groups-home-title">'.get_lang('Skills').'</div>';
+            $lis = '';
+            if (!empty($skills)) {
+                foreach($skills as $skill) {
+                    $lis .= Display::tag('li', Display::span($skill['name'], array('class'=>'label_tag success')));
+                }                
+                echo Display::tag('ul', $lis);
+            }
+            $url = api_get_path(WEB_CODE_PATH).'social/skills_tree.php';            
+            echo Display::url(get_lang('ViewSkillsTree'), $url);                
+        echo '</div>';
+                       
+            
                  
             //Search box
-			echo '<div class="social-box-right">';			
-			echo UserManager::get_search_form('');
-			echo '<br />';
-
-			
-			//Group box by age
-			$results = GroupPortalManager::get_groups_by_age(1,false);
-			
-			$groups_newest = array();
-			if (!empty($results)) {
+			echo '<div class="social-box-right">';	
+					
+    			echo UserManager::get_search_form('');
+    			echo '<br />';
+    			
+    			//Group box by age
+    			$results = GroupPortalManager::get_groups_by_age(1,false);
+    			
+    			$groups_newest = array();
+    			if (!empty($results)) {
+        			foreach ($results as $result) {
+        				$id = $result['id'];    				
+        				$result['description'] = Security::remove_XSS($result['description'], STUDENT, true);
+        				$result['name'] = Security::remove_XSS($result['name'], STUDENT, true);
+        			    if ($result['count'] == 1 ) {
+                            $result['count'] = '1 '.get_lang('Member');
+                        } else {
+                            $result['count'] = $result['count'].' '.get_lang('Members');
+                        }
+        				$group_url = "groups.php?id=$id";
+        				$result['name'] = Display::url(api_ucwords(cut($result['name'],40,true)), $group_url).Display::span('<br />'.$result['count'],array('class'=>'box_description_group_member'));
+        				$picture = GroupPortalManager::get_picture_group($id, $result['picture_uri'],80);
+        				$result['picture_uri'] = '<img class="social-groups-image" src="'.$picture['file'].'" hspace="10" height="44" border="2" align="left" width="44" />';
+        				$actions = '<div class="box_description_group_actions"><a href="groups.php?#tab_browse-2">'.get_lang('SeeMore').'</a></div>';
+        				$groups_newest[]= array(Display::url($result['picture_uri'], $group_url), $result['name'], cut($result['description'],120,true).$actions);
+        			}
+    			}
+    
+    			$results = GroupPortalManager::get_groups_by_popularity(1,false);
+    			
+    			$groups_pop = array();
     			foreach ($results as $result) {
-    				$id = $result['id'];    				
     				$result['description'] = Security::remove_XSS($result['description'], STUDENT, true);
     				$result['name'] = Security::remove_XSS($result['name'], STUDENT, true);
-    			    if ($result['count'] == 1 ) {
-                        $result['count'] = '1 '.get_lang('Member');
-                    } else {
-                        $result['count'] = $result['count'].' '.get_lang('Members');
-                    }
-    				$group_url = "groups.php?id=$id";
+    				$id = $result['id'];
+                    $group_url = "groups.php?id=$id";
+    				
+    				if ($result['count'] == 1 ) {
+    					$result['count'] = '1 '.get_lang('Member');
+    				} else {
+    					$result['count'] = $result['count'].' '.get_lang('Members');
+    				}				
     				$result['name'] = Display::url(api_ucwords(cut($result['name'],40,true)), $group_url).Display::span('<br />'.$result['count'],array('class'=>'box_description_group_member'));
     				$picture = GroupPortalManager::get_picture_group($id, $result['picture_uri'],80);
     				$result['picture_uri'] = '<img class="social-groups-image" src="'.$picture['file'].'" hspace="10" height="44" border="2" align="left" width="44" />';
-    				$actions = '<div class="box_description_group_actions"><a href="groups.php?#tab_browse-2">'.get_lang('SeeMore').'</a></div>';
-    				$groups_newest[]= array(Display::url($result['picture_uri'], $group_url), $result['name'], cut($result['description'],120,true).$actions);
+    				$actions = '<div class="box_description_group_actions" ><a href="groups.php?#tab_browse-3">'.get_lang('SeeMore').'</a></div>';
+    				$groups_pop[]= array(Display::url($result['picture_uri'], $group_url) , $result['name'], cut($result['description'],120,true).$actions);
+    			}
+    
+    			if (count($groups_newest) > 0) {
+    				echo '<div class="social-groups-home-title">'.get_lang('Newest').'</div>';
+    				Display::display_sortable_grid('home_group', array(), $groups_newest, array('hide_navigation'=>true, 'per_page' => 100), array(), false, array(true, true, true,false));				
+    			}
+    
+    			if (count($groups_pop) > 0) {
+    				echo '<div class="social-groups-home-title">'.get_lang('Popular').'</div>';
+    				Display::display_sortable_grid('home_group', array(), $groups_pop, array('hide_navigation'=>true, 'per_page' => 100), array(), false, array(true, true, true,true,true));
     			}
-			}
-
-			$results = GroupPortalManager::get_groups_by_popularity(1,false);
-			
-			$groups_pop = array();
-			foreach ($results as $result) {
-				$result['description'] = Security::remove_XSS($result['description'], STUDENT, true);
-				$result['name'] = Security::remove_XSS($result['name'], STUDENT, true);
-				$id = $result['id'];
-                $group_url = "groups.php?id=$id";
-				
-				if ($result['count'] == 1 ) {
-					$result['count'] = '1 '.get_lang('Member');
-				} else {
-					$result['count'] = $result['count'].' '.get_lang('Members');
-				}				
-				$result['name'] = Display::url(api_ucwords(cut($result['name'],40,true)), $group_url).Display::span('<br />'.$result['count'],array('class'=>'box_description_group_member'));
-				$picture = GroupPortalManager::get_picture_group($id, $result['picture_uri'],80);
-				$result['picture_uri'] = '<img class="social-groups-image" src="'.$picture['file'].'" hspace="10" height="44" border="2" align="left" width="44" />';
-				$actions = '<div class="box_description_group_actions" ><a href="groups.php?#tab_browse-3">'.get_lang('SeeMore').'</a></div>';
-				$groups_pop[]= array(Display::url($result['picture_uri'], $group_url) , $result['name'], cut($result['description'],120,true).$actions);
-			}
-
-			if (count($groups_newest) > 0) {
-				echo '<div class="social-groups-home-title">'.get_lang('Newest').'</div>';
-				Display::display_sortable_grid('home_group', array(), $groups_newest, array('hide_navigation'=>true, 'per_page' => 100), array(), false, array(true, true, true,false));				
-			}
-
-			if (count($groups_pop) > 0) {
-				echo '<div class="social-groups-home-title">'.get_lang('Popular').'</div>';
-				Display::display_sortable_grid('home_group', array(), $groups_pop, array('hide_navigation'=>true, 'per_page' => 100), array(), false, array(true, true, true,true,true));
-			}
 
 			echo '</div>';
-		echo '</div>';
+		
 	echo '</div>';
 echo '</div>';
 

+ 1 - 0
main/social/skills_tree.php

@@ -0,0 +1 @@
+ski

+ 1 - 0
main/template/default/layout/layout_2_col.tpl

@@ -31,6 +31,7 @@
 		{$reservation_block}
 		{$search_block}
 		{$classes_block}
+		{$skills_block}
 	</div>
 {/block}
 

+ 5 - 0
user_portal.php

@@ -236,6 +236,11 @@ $controller->tpl->assign('reservation_block', 			$controller->return_reservation
 $controller->tpl->assign('search_block', 				$controller->return_search_block());
 $controller->tpl->assign('classes_block', 				$controller->return_classes_block());
 
+if (api_is_platform_admin() || api_is_drh()) {
+    $controller->tpl->assign('skills_block',            $controller->return_skills_links());
+}
+
+
 $controller->tpl->display_two_col_template();
 
 // Deleting the session_id.