Yannick Warnier 10 years ago
parent
commit
40beb58ec0

+ 5 - 0
.gitignore

@@ -37,3 +37,8 @@ nbproject/*
 plugin/bbb/config.vm.php
 
 main/cron/incoming/*
+
+data/*
+!data/index.html
+data/badges/*
+!data/badges/index.html

+ 6 - 0
data/index.html

@@ -0,0 +1,6 @@
+<html>
+<head>
+</head>
+<body>
+</body>
+</html>

+ 1 - 0
main/admin/index.php

@@ -358,6 +358,7 @@ if (api_is_platform_admin()) {
         //$items[] = array('url'=>'skills_profile.php',   'label' => get_lang('SkillsProfile'));
         $items[] = array('url'=>api_get_path(WEB_CODE_PATH).'social/skills_ranking.php',   'label' => get_lang('SkillsRanking'));
         $items[] = array('url'=>'skills_gradebook.php', 'label' => get_lang('SkillsAndGradebooks'));
+        $items[] = array('url'=> api_get_path(WEB_CODE_PATH) . 'admin/skill_badge.php', 'label' => get_lang('Bagdes'));
         $blocks['skills']['items'] = $items;
         $blocks['skills']['extra'] = null;
         $blocks['skills']['search_form'] = null;

+ 29 - 0
main/admin/skill_badge.php

@@ -0,0 +1,29 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Show information about Mozilla OpenBadges
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ * @package chamilo.admin.openbadges
+ */
+$cidReset = true;
+
+require_once '../inc/global.inc.php';
+
+$this_section = SECTION_PLATFORM_ADMIN;
+
+if (!api_is_platform_admin()) {
+    api_not_allowed(true);
+}
+
+$interbreadcrumb = array(
+    array(
+        'url' => api_get_path(WEB_CODE_PATH) . 'admin/index.php',
+        'name' => get_lang('Administration')
+    )
+);
+
+$tpl = new Template(get_lang('Badges'));
+
+$contentTemplate = $tpl->get_template('skill/badge.tpl');
+
+$tpl->display($contentTemplate);

+ 91 - 0
main/admin/skill_badge_create.php

@@ -0,0 +1,91 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Show information about Mozilla OpenBadges
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ * @package chamilo.admin.openbadges
+ */
+$cidReset = true;
+
+require_once '../inc/global.inc.php';
+require_once '../inc/lib/fileUpload.lib.php';
+
+if (!api_is_platform_admin()) {
+    api_not_allowed(true);
+}
+
+$this_section = SECTION_PLATFORM_ADMIN;
+
+$skillId = intval($_GET['id']);
+
+$objSkill = new Skill();
+$skill = $objSkill->get($skillId);
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+    $params = array(
+        'name' => $_POST['name'],
+        'description' => $_POST['description'],
+        'criteria' => $_POST['criteria'],
+        'id' => $skillId
+    );
+
+    if (isset($_FILES['image']) && $_FILES['image']['error'] == 0) {
+        $dirPermissions = api_get_permissions_for_new_directories();
+        $sysPath = api_get_path(SYS_PATH);
+
+        $existsDataDirectory = file_exists($sysPath . 'data/');
+
+        if (!$existsDataDirectory) {
+            $existsDataDirectory = api_create_protected_dir('data', $sysPath);
+        }
+
+        $fileDir = "data/badges/";
+        $fileName = sha1($_POST['name']) . ".png";
+
+        $existsBadgesDirectory = file_exists($sysPath . 'data/badges/');
+
+        if (!$existsBadgesDirectory) {
+            $existsBadgesDirectory = api_create_protected_dir('badges', $sysPath . 'data/');
+        }
+
+        if ($existsBadgesDirectory) {
+            if (!empty($skill['icon'])) {
+                $iconFileAbsoulePath = $sysPath . $skill['icon'];
+                $iconDirAbsolutePath = $sysPath . $fileDir;
+
+                if (Security::check_abs_path($iconFileAbsoulePath, $iconDirAbsolutePath)) {
+                    unlink($sysPath . $skill['icon']);
+                }
+            }
+
+            $imageExtraField = new Image($_FILES['image']['tmp_name']);
+            $imageExtraField->send_image($sysPath . $fileDir . $fileName, -1, 'png');
+
+            $params['icon'] = $fileDir . $fileName;
+        }
+    }
+
+    $objSkill->update($params);
+
+    header('Location: ' . api_get_path(WEB_CODE_PATH) . 'admin/skill_badge_list.php');
+    exit;
+}
+
+$interbreadcrumb = array(
+    array(
+        'url' => api_get_path(WEB_CODE_PATH) . 'admin/index.php',
+        'name' => get_lang('Administration')
+    ),
+    array(
+        'url' => api_get_path(WEB_CODE_PATH) . 'admin/skill_badge.php',
+        'name' => get_lang('Badges')
+    )
+);
+
+$tpl = new Template(get_lang('CreateBadge'));
+$tpl->assign('platformAdminEmail', get_setting('emailAdministrator'));
+$tpl->assign('skill', $skill);
+
+$contentTemplate = $tpl->get_template('skill/badge_create.tpl');
+
+$tpl->display($contentTemplate);

+ 41 - 0
main/admin/skill_badge_issuer.php

@@ -0,0 +1,41 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Show information about Mozilla OpenBadges
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ * @package chamilo.admin.openbadges
+ */
+$cidReset = true;
+
+require_once '../inc/global.inc.php';
+
+if (!api_is_platform_admin()) {
+    api_not_allowed(true);
+}
+
+$backpack = 'https://backpack.openbadges.org/';
+
+if (array_key_exists('openbadges_backpack', $_configuration)) {
+    $backpack = $_configuration['openbadges_backpack'];
+}
+
+$this_section = SECTION_PLATFORM_ADMIN;
+
+$interbreadcrumb = array(
+    array(
+        'url' => api_get_path(WEB_CODE_PATH) . 'admin/index.php',
+        'name' => get_lang('Administration')
+    ),
+    array(
+        'url' => api_get_path(WEB_CODE_PATH) . 'admin/skill_badge.php',
+        'name' => get_lang('Badges')
+    )
+);
+
+$tpl = new Template(get_lang('IssuerDetails'));
+
+$tpl->assign('backpack', $backpack);
+
+$contentTemplate = $tpl->get_template('skill/badge_issuer.tpl');
+
+$tpl->display($contentTemplate);

+ 39 - 0
main/admin/skill_badge_list.php

@@ -0,0 +1,39 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Show information about Mozilla OpenBadges
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ * @package chamilo.admin.openbadges
+ */
+$cidReset = true;
+
+require_once '../inc/global.inc.php';
+require_once '../inc/lib/fileUpload.lib.php';
+
+if (!api_is_platform_admin()) {
+    api_not_allowed(true);
+}
+
+$this_section = SECTION_PLATFORM_ADMIN;
+
+$objSkill = new Skill();
+$skills = $objSkill->get_all();
+
+$interbreadcrumb = array(
+    array(
+        'url' => api_get_path(WEB_CODE_PATH) . 'admin/index.php',
+        'name' => get_lang('Administration')
+    ),
+    array(
+        'url' => api_get_path(WEB_CODE_PATH) . 'admin/skill_badge.php',
+        'name' => get_lang('Badges')
+    )
+);
+
+$tpl = new Template(get_lang('Skills'));
+$tpl->assign('platformAdminEmail', get_setting('emailAdministrator'));
+$tpl->assign('skills', $skills);
+
+$contentTemplate = $tpl->get_template('skill/badge_list.tpl');
+
+$tpl->display($contentTemplate);

+ 49 - 0
main/badge/assertion.php

@@ -0,0 +1,49 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Show information about a new assertion
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ * @package chamilo.badge
+ */
+header('Content-Type: application/json');
+
+require_once '../inc/global.inc.php';
+
+$userId = isset($_GET['user']) ? intval($_GET['user']) : 0;
+$skillId = isset($_GET['skill']) ? intval($_GET['skill']) : 0;
+
+if ($userId === 0 || $skillId === 0) {
+    exit;
+}
+
+$objSkill = new Skill();
+
+if (!$objSkill->user_has_skill($userId, $skillId)) {
+    exit;
+}
+
+$objSkillRelUser = new SkillRelUser();
+$userSkill = $objSkillRelUser->getByUserAndSkill($userId, $skillId);
+
+if ($userSkill == false) {
+    exit;
+}
+
+$user = api_get_user_info($userSkill['user_id']);
+
+$json = array(
+    'uid' => $userSkill['id'],
+    'recipient' => array(
+        'type' => 'email',
+        'hashed' => false,
+        'identity' => $user['email']
+    ),
+    'issuedOn' => strtotime($userSkill['acquired_skill_at']),
+    'badge' => api_get_path(WEB_CODE_PATH) . "badge/class.php?id=$skillId",
+    'verify' => array(
+        'type' => 'hosted',
+        'url' => api_get_path(WEB_CODE_PATH) . "badge/assertion.php?user=$userId&skill=$skillId"
+    )
+);
+
+echo json_encode($json);

+ 25 - 0
main/badge/class.php

@@ -0,0 +1,25 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Show information about the OpenBadge class
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ * @package chamilo.badge
+ */
+header('Content-Type: application/json');
+
+require_once '../inc/global.inc.php';
+
+$skillId = isset($_GET['id']) ? intval($_GET['id']) : 0;
+
+$objSkill = new Skill();
+$skill = $objSkill->get($skillId);
+
+$json = array(
+    'name' => $skill['name'],
+    'description' => $skill['description'],
+    'image' => api_get_path(WEB_PATH) . $skill['icon'],
+    'criteria' => api_get_path(WEB_CODE_PATH) . "badge/criteria.php?id=$skillId",
+    'issuer' => api_get_path(WEB_CODE_PATH) . "badge/issuer.php",
+);
+
+echo json_encode($json);

+ 17 - 0
main/badge/criteria.php

@@ -0,0 +1,17 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Show information about OpenBadge citeria
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ * @package chamilo.badge
+ */
+header('Content-Type: text/plain');
+
+require_once '../inc/global.inc.php';
+
+$skillId = isset($_GET['id']) ? intval($_GET['id']) : 0;
+
+$objSkill = new Skill();
+$skill = $objSkill->get($skillId);
+
+echo $skill['criteria'];

+ 17 - 0
main/badge/issuer.php

@@ -0,0 +1,17 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Show information about the OpenBadge issuer
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ * @package chamilo.badge
+ */
+require_once '../inc/global.inc.php';
+
+header('Content-Type: application/json');
+
+$json = array(
+    'name' => api_get_setting('Institution'),
+    'url' => api_get_path(WEB_PATH)
+);
+
+echo json_encode($json);

+ 50 - 0
main/gradebook/get_badges.php

@@ -0,0 +1,50 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Show the achieved badges by an user
+ * @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
+ * @package chamilo.badge
+ */
+$cidReset = true;
+
+require_once '../inc/global.inc.php';
+
+$userId = isset($_GET['user']) ? intval($_GET['user']) : 0;
+
+if ($userId === 0) {
+    exit;
+}
+
+$objSkillRelUser = new SkillRelUser();
+$userSkills = $objSkillRelUser->get_user_skills($userId);
+
+if (empty($userSkills)) {
+    exit;
+}
+
+$assertions = array();
+
+foreach ($userSkills as $skill) {
+    $skillId = current($skill);
+
+    $assertions[] = api_get_path(WEB_CODE_PATH) . "badge/assertion.php?user=$userId&skill=$skillId";
+}
+
+$backpack = 'https://backpack.openbadges.org/';
+
+if (array_key_exists('openbadges_backpack', $_configuration)) {
+    $backpack = $_configuration['openbadges_backpack'];
+}
+
+$htmlHeadXtra[] = '<script src="' . $backpack . 'issuer.js"></script>';
+
+$tpl = new Template(get_lang('Badges'), false, false);
+
+$tpl->assign(
+    'content',
+    "<script>"
+    . "$(document).on('ready', function (){ OpenBadges.issue_no_modal(" . json_encode($assertions) . "); });"
+    . "</script>"
+);
+
+$tpl->display_one_col_template();

+ 9 - 0
main/gradebook/lib/be/category.class.php

@@ -1666,6 +1666,14 @@ class Category implements GradebookItem
                             'class' => 'btn'
                         )
                     );
+                    $badges = Display::url(
+                        get_lang('DownloadBadges'),
+                        api_get_path(WEB_CODE_PATH) . "gradebook/get_badges.php?user=$user_id",
+                        array(
+                            'target' => '_blank',
+                            'class' => 'btn'
+                        )
+                    );
                     $exportToPDF = Display::url(
                         Display::return_icon(
                             'pdf.png',
@@ -1676,6 +1684,7 @@ class Category implements GradebookItem
                         "$url&action=export"
                     );
                     $html = array(
+                        'badge_link' => $badges,
                         'certificate_link' => $certificates,
                         'pdf_link' => $exportToPDF
                     );

+ 7 - 4
main/gradebook/lib/fe/displaygradebook.php

@@ -453,11 +453,14 @@ class DisplayGradebook
             $total_score = array($item_value_total, $item_total);
             $scorecourse_display = $scoredisplay->display_score($total_score, SCORE_DIV_PERCENT);
             if ((!$catobj->get_id() == '0') && (!isset($_GET['studentoverview'])) && (!isset($_GET['search']))) {
-                $certificateLink = null;
-                if (!empty($certificateLinkInfo) && isset($certificateLinkInfo['certificate_link'])) {
-                    $certificateLink .= '<span style="float:right"> ' . $certificateLinkInfo['certificate_link']."</span>";
+                $aditionalButtons = null;
+                if (!empty($certificateLinkInfo)) {
+                    $aditionalButtons = '<div class="btn-group pull-right">';
+                    $aditionalButtons .= isset($certificateLinkInfo['certificate_link']) ? $certificateLinkInfo['certificate_link'] : '';
+                    $aditionalButtons .= isset($certificateLinkInfo['badge_link']) ? $certificateLinkInfo['badge_link'] : '';
+                    $aditionalButtons .= '</div>';
                 }
-                $scoreinfo .= '<h4>' . get_lang('Total') . ' : ' . $scorecourse_display . $certificateLink. '</h4>';
+                $scoreinfo .= '<h4>' . get_lang('Total') . ' : ' . $scorecourse_display . $aditionalButtons. '</h4>';
 
             }
             Display :: display_normal_message($scoreinfo, false);

+ 27 - 0
main/inc/lib/main_api.lib.php

@@ -7636,3 +7636,30 @@ function api_format_time($time, $originFormat = 'php')
 
     return $formattedTime;
 }
+
+/**
+ * Create a new empty directory with index.html file
+ * @param string $name The new directory name
+ * @param string $parentDirectory Directory parent directory name
+ * @return boolean Return true if the directory was create. Otherwise return false
+ */
+function api_create_protected_dir($name, $parentDirectory)
+{
+    $isCreated = false;
+
+    $fullPath = $parentDirectory . replace_dangerous_char($name);
+
+    if (mkdir($fullPath, api_get_permissions_for_new_directories(), true)) {
+        $fp = fopen($fullPath . '/index.html', 'w');
+
+        if ($fp) {
+            if (fwrite($fp, '<html><head></head><body></body></html>')) {
+                $isCreated = true;
+            }
+        }
+
+        fclose($fp);
+    }
+
+    return $isCreated;
+}

+ 20 - 2
main/inc/lib/skill.lib.php

@@ -371,12 +371,30 @@ class SkillRelUser extends Model
         );
         return $result;
     }
+
+    /**
+     * Get the relation data between user and skill
+     * @param int $userId The user id
+     * @param int $skillId The skill id
+     * @return array The relation data. Otherwise return false
+     */
+    public function getByUserAndSkill($userId, $skillId)
+    {
+        $where = array(
+            'user_id = ? AND skill_id = ?' => array($userId, $skillId)
+        );
+
+        return Database::select('*', $this->table, array(
+            'where' => $where
+        ), 'first');
+    }
+
 }
 
 
 class Skill extends Model
 {
-    public $columns = array('id', 'name', 'description', 'access_url_id', 'short_code');
+    public $columns = array('id', 'name', 'description', 'access_url_id', 'short_code', 'icon', 'criteria');
     public $required = array('name');
 
     /** Array of colours by depth, for the coffee wheel. Each depth has 4 col */
@@ -445,7 +463,7 @@ class Skill extends Model
             }
         }
 
-        $sql = "SELECT s.id, s.name, s.description, ss.parent_id, ss.relation_type
+        $sql = "SELECT s.id, s.name, s.description, ss.parent_id, ss.relation_type, s.icon
                 FROM {$this->table} s INNER JOIN {$this->table_skill_rel_skill} ss ON (s.id = ss.skill_id) $id_condition
                 ORDER BY ss.id, ss.parent_id";
 

+ 2 - 0
main/install/configuration.dist.php

@@ -286,6 +286,8 @@ $_configuration['system_stable']     = NEW_VERSION_STABLE;
 //$_configuration['exercise_max_fckeditors_in_page'] = 0;
 // Default upload option
 //$_configuration['document_if_file_exists_option'] = 'rename'; // overwrite
+// Which OpenBadges backpack send the badges
+//$_configuration['openbadges_backpack'] = 'https://backpack.openbadges.org/';
 // Custom name_order_conventions
 //$_configuration['name_order_conventions'] = array(
 //  'french' => array('format' => 'title last_name first_name',  'sort_by' => 'last_name')

+ 29 - 0
main/template/default/skill/badge.tpl

@@ -0,0 +1,29 @@
+{% extends "default/layout/main.tpl" %}
+
+{% block body %}
+    <div class="span12">
+        <h1 class="page-header">{{ 'Badges' | get_lang }}</h1>
+        <ul class="nav nav-tabs">
+            <li class="active">
+                <a href="{{ _p.web_main }}admin/skill_badge.php">{{ 'Home' | get_lang }}</a>
+            </li>
+            <li>
+                <a href="{{ _p.web_main }}admin/skill_badge_issuer.php">{{ 'IssuerDetails' | get_lang }}</a>
+            </li>
+            <li>
+                <a href="{{ _p.web_main }}admin/skill_badge_list.php">{{ 'Skills' | get_lang }}</a>
+            </li>
+        </ul>
+        <div class="tab-content">
+            <div class="tab-pane active">
+                <div class="hero-unit">
+                    <h1>Introducing Open Badges</h1>
+                    <p class="lead">A new online standard to recognize and verify learning</p>
+                    <p class="lead">
+                        <a href="http://openbadges.org/">http://openbadges.org/</a>
+                    </p>
+                </div>
+            </div>
+        </div>
+    </div>
+{% endblock %}

+ 109 - 0
main/template/default/skill/badge_create.tpl

@@ -0,0 +1,109 @@
+{% extends "default/layout/main.tpl" %}
+
+{% block body %}
+    <script>
+        (function () {
+            var designer = null;
+
+            $(document).on('ready', function () {
+                $('#btn-open-designer').on('click', function (e) {
+                    e.preventDefault();
+
+                    var designerUrl = 'https://www.openbadges.me/designer.html?origin={{ _p.web }}';
+                    designerUrl = designerUrl + '&email={{ platformAdminEmail }}';
+                    designerUrl = designerUrl + '&close=true';
+                    designerUrl = designerUrl + '&hidePublish=true';
+
+                    var windowOptions = 'width=1200,height=680,location=0,menubar=0,status=0,toolbar=0';
+                    designer = window.open(designerUrl, '', windowOptions);
+                });
+
+                $('#image').on('change', function () {
+                    var self = this;
+
+                    if (self.files.length > 0) {
+                        var image = self.files[0];
+
+                        if (!image.type.match(/image.*/)) {
+                            return;
+                        }
+
+                        var fileReader = new FileReader();
+                        fileReader.onload = function (e) {
+                            $('#badge-preview').attr('src', e.target.result);
+                            $('#badge-container').removeClass('hide');
+                        };
+                        fileReader.readAsDataURL(image);
+                    }
+                });
+            });
+        })();
+    </script>
+    <div class="span12">
+        <h1 class="page-header">{{ 'Badges' | get_lang }}</h1>
+        <ul class="nav nav-tabs">
+            <li>
+                <a href="{{ _p.web_main }}admin/skill_badge.php">{{ 'Home' | get_lang }}</a>
+            </li>
+            <li>
+                <a href="{{ _p.web_main }}admin/skill_badge_issuer.php">{{ 'IssuerDetails' | get_lang }}</a>
+            </li>
+            <li>
+                <a href="{{ _p.web_main }}admin/skill_badge_list.php">{{ 'Skills' | get_lang }}</a>
+            </li>
+            <li class="active">
+                <a href="#">{{ 'Edit' | get_lang }}</a>
+            </li>
+        </ul>
+        <div class="tab-content">
+            <div class="tab-pane active">
+                <div class="row">
+                    <div class="span3">
+                        <p>Design a new badge. Download from the design tool. And Upload in your platform.</p>
+                        <p>
+                            <button id="btn-open-designer" class="btn btn-info btn-large btn-block" type="button">{{ 'DesignNewBadge' | get_lang }}</button>
+                        </p>
+                        <hr>
+                        <div class="well well-small {{ skill.icon ? '' : 'hide' }}" id="badge-container">
+                            <img id="badge-preview" alt="{{ 'BadgePreview' | get_lang }}" src="{{ skill.icon ? [_p.web, skill.icon] | join('') : '' }}">
+                        </div>
+                    </div>
+                    <div class="span9">
+                        <form action="{{ _p.web_self_query_vars }}" class="form-horizontal" method="post" enctype="multipart/form-data">
+                            <fieldset>
+                                <legend>{{ 'SkillInfo' | get_lang }}</legend>
+                                <div class="control-group">
+                                    <label class="control-label" for="name">{{ 'Name' | get_lang }}</label>
+                                    <div class="controls">
+                                        <input type="text" name="name" id="name" class="input-xxlarge" value="{{ skill.name }}">
+                                    </div>
+                                </div>
+                                <div class="control-group">
+                                    <label class="control-label" for="name">{{ 'Description' | get_lang }}</label>
+                                    <div class="controls">
+                                        <textarea name="description" id="description" class="input-xxlarge" rows="4">{{ skill.description }}</textarea>
+                                    </div>
+                                </div>
+                                <div class="control-group">
+                                    <label class="control-label" for="image">{{ 'Image' | get_lang }}</label>
+                                    <div class="controls">
+                                        <input type="file" name="image" id="image" class="input-xxlarge" accept="image/*">
+                                    </div>
+                                </div>
+                                <div class="control-group">
+                                    <label class="control-label" for="criteria">{{ 'Criteria' | get_lang }}</label>
+                                    <div class="controls">
+                                        <textarea name="criteria" id="criteria" class="input-xxlarge" rows="10">{{ skill.criteria }}</textarea>
+                                    </div>
+                                </div>
+                            </fieldset>
+                            <div class="form-actions">
+                                <button type="submit" class="btn btn-primary">{{ 'Create'| get_lang }}</button>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+{% endblock %}

+ 49 - 0
main/template/default/skill/badge_issuer.tpl

@@ -0,0 +1,49 @@
+{% extends "default/layout/main.tpl" %}
+
+{% block body %}
+    <div class="span12">
+        <h1 class="page-header">{{ 'Badges' | get_lang }}</h1>
+        <ul class="nav nav-tabs">
+            <li>
+                <a href="{{ _p.web_main }}admin/skill_badge.php">{{ 'Home' | get_lang }}</a>
+            </li>
+            <li class="active">
+                <a href="{{ _p.web_main }}admin/skill_badge_issuer.php">{{ 'IssuerDetails' | get_lang }}</a>
+            </li>
+            <li>
+                <a href="{{ _p.web_main }}admin/skill_badge_list.php">{{ 'Skills' | get_lang }}</a>
+            </li>
+        </ul>
+        <div class="tab-content">
+            <div class="tab-pane active">
+                <form action="{{ _p.web_self }}" class="form-horizontal">
+                    <fieldset>
+                        <legend>{{ 'IssuerDetails' | get_lang }}</legend>
+                        <div class="control-group">
+                            <label class="control-label">{{ 'Name' | get_lang }}</label>
+                            <div class="controls">
+                                <span class="uneditable-input input-xxlarge">{{ _s.institution }}</span>
+                            </div>
+                        </div>
+                        <div class="control-group">
+                            <label class="control-label">{{ 'URL' | get_lang }}</label>
+                            <div class="controls">
+                                <span class="uneditable-input input-xxlarge">{{ _p.web }}</span>
+                            </div>
+                        </div>
+                    </fieldset>
+                    <fieldset>
+                        <legend>{{ 'BackpackDetails' | get_lang }}</legend>
+                        <div class="control-group">
+                            <label class="control-label">{{ 'URL' | get_lang }}</label>
+                            <div class="controls">
+                                <span class="uneditable-input input-xxlarge">{{ backpack }}</span>
+                                <p class="help-block">{{ 'TheBadgesWillBeSentToThatBackpack' | get_lang }}</p>
+                            </div>
+                        </div>
+                    </fieldset>
+                </form>
+            </div>
+        </div>
+    </div>
+{% endblock %}

+ 56 - 0
main/template/default/skill/badge_list.tpl

@@ -0,0 +1,56 @@
+{% extends "default/layout/main.tpl" %}
+
+{% block body %}
+    <div class="span12">
+        <h1 class="page-header">{{ 'Badges' | get_lang }}</h1>
+        <ul class="nav nav-tabs">
+            <li>
+                <a href="{{ _p.web_main }}admin/skill_badge.php">{{ 'Home' | get_lang }}</a>
+            </li>
+            <li>
+                <a href="{{ _p.web_main }}admin/skill_badge_issuer.php">{{ 'IssuerDetails' | get_lang }}</a>
+            </li>
+            <li class="active">
+                <a href="{{ _p.web_main }}admin/skill_badge_list.php">{{ 'Skills' | get_lang }}</a>
+            </li>
+        </ul>
+        <div class="tab-content">
+            <div class="tab-pane active">
+                <table class="table table-bordered table-striped">
+                    <thead>
+                        <tr>
+                            <th>{{ 'Name' | get_lang }}</th>
+                            <th>{{ 'Description' | get_lang }}</th>
+                            <th>{{ 'Actions' | get_lang }}</th>
+                        </tr>
+                    </thead>
+                    <tfoot>
+                        <tr>
+                            <th>{{ 'Name' | get_lang }}</th>
+                            <th>{{ 'Description' | get_lang }}</th>
+                            <th>{{ 'Actions' | get_lang }}</th>
+                        </tr>
+                    </tfoot>
+                    <tbody>
+                        {% for skill in skills %}
+                            <tr>
+                                <td>
+                                    {% if skill.icon %}
+                                        <img src="{{ [_p.web, skill.icon] | join('') }}" width="50" alt="{{ skill.name }}">
+                                    {% endif %}
+                                    {{ skill.name }}
+                                </td>
+                                <td>{{ skill.description }}</td>
+                                <td>
+                                    <a href="{{ _p.web_main }}admin/skill_badge_create.php?id={{ skill.id }}" title="{{ 'Edit' | get_lang }}">
+                                        <img src="{{ _p.web_img }}icons/22/edit.png" alt="{{ 'Edit' | get_lang }}">
+                                    </a>
+                                </td>
+                            </tr>
+                        {% endfor %}
+                    </tbody>
+                </table>
+            </div>
+        </div>
+    </div>
+{% endblock %}