Browse Source

Added the cloud file links feature to be able to add links to cloud hostings as if they were documents.

Aquilino Blanco 7 years ago
parent
commit
cbd785478c

+ 45 - 0
app/Migrations/Schema/V111/Version20171127103000.php

@@ -0,0 +1,45 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+namespace Application\Migrations\Schema\V111;
+
+use Application\Migrations\AbstractMigrationChamilo,
+    Doctrine\DBAL\Schema\Schema,
+    Doctrine\DBAL\Types\Type;
+
+/**
+ * Class Version20171127103000
+ *
+ * Adding cloud files' link document type and enabling/disabling option
+ *
+ * @package Application\Migrations\Schema\V111
+ */
+class Version20171127103000 extends AbstractMigrationChamilo
+{
+    /**
+     * @param \Doctrine\DBAL\Schema\Schema $schema
+     */
+    public function up(Schema $schema)
+    {
+        $this->addSql("ALTER TABLE c_document CHANGE filetype filetype SET('file','folder','link');");
+
+        $this->addSql("INSERT INTO `settings_options` (`variable`, `value`, `display_text`) VALUES
+        ('enable_add_file_link', 'true', 'Yes'),
+        ('enable_add_file_link', 'false', 'No');");
+        
+        $this->addSql("INSERT INTO `settings_current` 
+        (`variable`, `subkey`, `type`, `category`, `selected_value`, `title`, `comment`, `scope`, `subkeytext`, `access_url`, `access_url_changeable`, `access_url_locked`)
+        VALUES ('enable_add_file_link', NULL, 'radio', 'Tools', 'false', 'enable_add_file_link_title', 'enable_add_file_link_comment', NULL, NULL, 1, 0, 0);");
+    }
+
+    /**
+     * @param \Doctrine\DBAL\Schema\Schema $schema
+     */
+    public function down(Schema $schema)
+    {
+        $this->addSql("ALTER TABLE c_document CHANGE filetype filetype SET('file','folder');");
+
+        $this->addSql("DELETE FROM `settings_options` WHERE `variable` = 'enable_add_file_link'");
+        $this->addSql("DELETE FROM `settings_current` WHERE `variable` = 'enable_add_file_link'");
+    }
+}

+ 182 - 0
main/document/add_link.php

@@ -0,0 +1,182 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * This script allows to add cloud file links to the document structure
+ * @package chamilo.document
+ */
+/**
+ * Code
+ */
+ 
+// Including the global initialization file
+require_once '../inc/global.inc.php';
+ 
+// Including additional libraries
+require_once '../inc/lib/document.lib.php';
+require_once '../inc/lib/urlUtils.lib.php';
+ 
+if (api_get_setting('enable_add_file_link') == 'false') {
+    api_not_allowed(true);
+}
+ 
+$course_info = api_get_course_info();
+ 
+if (empty($course_info)) {
+    api_not_allowed(true);
+}
+ 
+$document_data  = DocumentManager::get_document_data_by_id($_REQUEST['id'], api_get_course_id(), true);
+if (empty($document_data)) {
+    $document_id  = $parent_id =  0;
+    $path = '/';
+} else {
+    if ($document_data['filetype'] == 'folder') {
+        $document_id    = $document_data['id'];
+        $path           = $document_data['path']."/";
+        $parent_id      = DocumentManager::get_document_id(api_get_course_info(), dirname($path));
+    }
+}
+ 
+$is_allowed_to_edit = api_is_allowed_to_edit(null, true);
+ 
+if (api_get_group_id()) {
+    // If the group id is set, check if the user has the right to be here
+    // Needed for group related stuff
+    require_once api_get_path(LIBRARY_PATH).'groupmanager.lib.php';
+    // Get group info
+    $group_properties = GroupManager::get_group_properties(api_get_group_id());
+
+    if ($is_allowed_to_edit || GroupManager::is_user_in_group($_user['user_id'], api_get_group_id())) { // Only courseadmin or group members allowed
+        $to_group_id = api_get_group_id();
+        $req_gid = '&amp;gidReq='.api_get_group_id();
+        $interbreadcrumb[] = array('url' => '../group/group_space.php?gidReq='.api_get_group_id(), 'name' => get_lang('GroupSpace'));
+    } else {
+        api_not_allowed(true);
+    }
+} elseif ($is_allowed_to_edit || DocumentManager::is_my_shared_folder(api_get_user_id(), $path, api_get_session_id())) {
+    // Admin for "regular" upload, no group documents. And check if is my shared folder
+    $to_group_id = 0;
+    $req_gid = '';
+} else { // No course admin and no group member...
+    api_not_allowed(true);
+}
+ 
+// Group docs can only be uploaded in the group directory
+if ($to_group_id != 0 && $path == '/') {
+    $path = $group_properties['directory'] . "/";
+}
+ 
+// Breadcrumbs
+if ($is_certificate_mode) {
+    $interbreadcrumb[] = array('url' => '../gradebook/'.$_SESSION['gradebook_dest'], 'name' => get_lang('Gradebook'));
+} else {
+    $interbreadcrumb[] = array('url' => './document.php?id='.$document_id.$req_gid, 'name'=> get_lang('Documents'));
+}
+ 
+// Interbreadcrumb for the current directory root path
+if (empty($document_data['parents'])) {
+    // Hack in order to not add the document to the breadcrumb in case it is a link
+    if ($document_data['filetype'] != 'link') {
+        $interbreadcrumb[] = array('url' => '#', 'name' => $document_data['title']);
+    }
+} else {
+    foreach ($document_data['parents'] as $document_sub_data) {
+        // Hack in order to not add the document to the breadcrumb in case it is a link
+        if ($document_data['filetype'] != 'link') {
+            $interbreadcrumb[] = array('url' => $document_sub_data['document_url'], 'name' => $document_sub_data['title']);
+        }
+    }
+}
+ 
+$this_section = SECTION_COURSES;
+ 
+$nameTools = get_lang('LinkAdd');
+
+// Display the header
+Display::display_header($nameTools, 'Doc');
+ 
+/*	Here we do all the work */
+ 
+// Actions
+echo '<div class="actions">';
+// Link back to the documents overview
+if ($is_certificate_mode) {
+    echo '<a href="document.php?id='.$document_id.'&selectcat=' . $selectcat.'">'.Display::return_icon('back.png', get_lang('BackTo').' '.get_lang('CertificateOverview'), '', ICON_SIZE_MEDIUM).'</a>';
+} else {
+    echo '<a href="document.php?id='.$document_id.'">'.Display::return_icon('back.png', get_lang('BackTo').' '.get_lang('DocumentsOverview'), '', ICON_SIZE_MEDIUM).'</a>';
+}
+echo '</div>';
+ 
+// Form to select directory
+$folders = DocumentManager::get_all_document_folders(
+    $_course,
+    $groupIid,
+    $is_allowed_to_edit
+);
+if (!$is_certificate_mode) {
+    echo DocumentManager::build_directory_selector(
+        $folders,
+        $document_id,
+        (isset($group_properties['directory']) ? $group_properties['directory'] : array())
+    );
+}
+ 
+$action = api_get_self().'?'.api_get_cidreq().'&id='.$document_id;
+ 
+// URLs in whitelist
+$urlWL = URLUtils::getFileHostingsWL();
+sort($urlWL);
+$urlWLRegEx = '/(\/\/|\.)('.implode('|', $urlWL).')/i'; //Matches any of the whitelisted urls preceded by // or .
+$urlWLText = "\n\t* ".implode("\n\t* ", $urlWL);
+$urlWLHTML = "<ul><li>".implode("</li><li>", $urlWL)."</li></ul>";
+ 
+$form = new FormValidator('upload', 'POST', $action, '', array('enctype' => 'multipart/form-data'));
+$form->addElement('hidden', 'linkid', $document_id);
+$form->addElement('hidden', 'curdirpath', $path);
+$form->addElement('text', 'name', get_lang('LinkName'), array('id' => 'name_link'));
+$form->addElement('text', 'url', get_lang('Url'), array('id' => 'url_link'));
+$form->addElement('static', 'info', '', '<span class="text-primary" data-toggle="tooltip" title="'.$urlWLHTML.'">'.get_lang('ValidDomainsList').' <span class="glyphicon glyphicon-question-sign"></span></span>');
+$form->addButtonSend(get_lang('AddCloudLink'), 'submitDocument');
+ 
+$form->addRule('name', get_lang('PleaseEnterCloudLinkName'), 'required', null, 'client');
+$form->addRule('name', get_lang('PleaseEnterCloudLinkName'), 'required', null, 'server');
+$form->addRule('url', get_lang('langGiveURL'), 'required', null, 'client');
+$form->addRule('url', get_lang('langGiveURL'), 'required', null, 'server');
+// Well formed url pattern (must have the protocol)
+$urlRegEx = URLUtils::getWellformedUrlRegex();
+$form->addRule('url', get_lang('MalformedUrl'), 'regex', $urlRegEx, 'client');
+$form->addRule('url', get_lang('MalformedUrl'), 'regex', $urlRegEx, 'server');
+$form->addRule('url', get_lang('NotValidDomain').$urlWLText, 'regex', $urlWLRegEx, 'client');
+$form->addRule('url', get_lang('NotValidDomain').$urlWLHTML, 'regex', $urlWLRegEx, 'server');
+ 
+if ($form->validate()) {
+    if (isset($_REQUEST['linkid'])) {
+        $doc_id = DocumentManager::addCloudLink($course_info, $path, $_REQUEST['url'], $_REQUEST['name']);
+        if ($doc_id) {
+            Display::display_confirmation_message(get_lang('CloudLinkAdded') . '<br />', false);
+        } else {
+            if (DocumentManager::cloudLinkExists($course_info, $path, $_REQUEST['url'])) {
+                Display::display_error_message(get_lang('UrlAlreadyExists'), false);
+            } else {
+                Display::display_error_message(get_lang('ErrorAddCloudLink'), false);
+            }
+        }
+    }
+}
+
+// Add tooltip and correctly parse its inner HTML
+echo '<script>
+$(document).ready(function() {
+    $("[data-toggle=\'tooltip\']").tooltip(
+        {
+            content: 
+                function() {
+                    return $(this).attr("title");
+                }
+        }
+    );
+});
+</script>';
+
+echo $form->return_form();
+Display::display_footer();

+ 161 - 78
main/document/document.php

@@ -252,14 +252,19 @@ switch ($action) {
 
                 // Check whether the document is in the database.
                 if (!empty($documentInfo)) {
-                    $deleteDocument = DocumentManager::delete_document(
-                        $courseInfo,
-                        null,
-                        $base_work_dir,
-                        $sessionId,
-                        $_GET['deleteid'],
-                        $groupIid
-                    );
+                    if ($documentInfo['filetype'] != 'link') {
+                        $deleteDocument = DocumentManager::delete_document(
+                            $courseInfo,
+                            null,
+                            $base_work_dir,
+                            $sessionId,
+                            $_GET['deleteid'],
+                            $groupIid
+                        );
+                    } else {
+                        // Cloud Links
+                        $deleteDocument = DocumentManager::deleteCloudLink($_course, $_GET['deleteid']);
+                    }
 
                     if ($deleteDocument) {
                         $certificateId = isset($_GET['delete_certificate_id']) ? $_GET['delete_certificate_id'] : null;
@@ -267,10 +272,24 @@ switch ($action) {
                             api_get_course_id(),
                             $certificateId
                         );
-                        Display::addFlash(Display::return_message(
-                            get_lang('DocDeleted').': '.$documentInfo['title'],
-                            'success'
-                        ));
+                        if ($documentInfo['filetype'] != 'link') {
+                            Display::addFlash(Display::return_message(
+                                get_lang('DocDeleted').': '.$documentInfo['title'],
+                                'success'
+                            ));
+                        } else {
+                            if ($documentInfo['filetype'] != 'link') {
+                                Display::addFlash(Display::return_message(
+                                    get_lang('CloudLinkDeleted').': '.$data['title'],
+                                    'success'
+                                ));
+                            } else {
+                                Display::addFlash(Display::return_message(
+                                    get_lang('CloudLinkDeleteError').': '.$data['title'],
+                                    'error'
+                                ));
+                            }
+                        }
                     } else {
                         Display::addFlash(Display::return_message(get_lang('DocDeleteError'), 'warning'));
                     }
@@ -574,6 +593,11 @@ if (isset($document_id) && empty($action)) {
 
     if ($document_data) {
         $parent_id = $document_data['parent_id'];
+        // Hack in order to clean the document id in case of false positive from links
+        if ($document_data['filetype'] == 'link') {
+            $document_id = null;
+        }
+
         $visibility = DocumentManager::check_visibility_tree(
             $document_id,
             api_get_course_id(),
@@ -582,7 +606,7 @@ if (isset($document_id) && empty($action)) {
             $groupIid
         );
 
-        if (!empty($document_data['filetype']) && $document_data['filetype'] == 'file') {
+        if (!empty($document_data['filetype']) && $document_data['filetype'] == 'file' || $document_data['filetype'] == "link") {
             if ($visibility && api_is_allowed_to_session_edit()) {
                 $url = api_get_path(WEB_COURSE_PATH).
                     $courseInfo['path'].'/document'.$document_data['path'].'?'
@@ -898,10 +922,13 @@ if (empty($document_data['parents'])) {
             'name' => $document_data['title'],
         );
     } else {
-        $interbreadcrumb[] = array(
-            'url' => '#',
-            'name' => $document_data['title']
-        );
+        // Hack in order to not add the document to the breadcrumb in case it is a link
+        if ($document_data['filetype'] != 'link') {
+            $interbreadcrumb[] = array(
+                'url' => '#',
+                'name' => $document_data['title']
+            );
+        }
     }
 } else {
     $counter = 0;
@@ -1110,68 +1137,96 @@ if ($isAllowedToEdit || $group_member_with_upload_rights ||
 
         // Security fix: make sure they can't move files that are not in the document table
         if (!empty($document_to_move)) {
-            $real_path_target = $base_work_dir.$moveTo.'/'.basename($document_to_move['path']);
-            $fileExist = false;
-            if (file_exists($real_path_target)) {
-                $fileExist = true;
-            }
-            if (move($base_work_dir.$document_to_move['path'], $base_work_dir.$moveTo)) {
-                DocumentManager::updateDbInfo(
-                    'update',
-                    $document_to_move['path'],
-                    $moveTo.'/'.basename($document_to_move['path'])
-                );
-
-                //update database item property
-                $doc_id = $_POST['move_file'];
-                if (is_dir($real_path_target)) {
-                    api_item_property_update(
-                        $courseInfo,
-                        TOOL_DOCUMENT,
-                        $doc_id,
-                        'FolderMoved',
-                        api_get_user_id(),
-                        $group_properties,
-                        null,
-                        null,
-                        null,
-                        $sessionId
-                    );
-                    Display::addFlash(Display::return_message(get_lang('DirMv'), 'confirmation'));
-                } elseif (is_file($real_path_target)) {
-                    api_item_property_update(
-                        $courseInfo,
-                        TOOL_DOCUMENT,
-                        $doc_id,
-                        'DocumentMoved',
-                        api_get_user_id(),
-                        $group_properties,
-                        null,
-                        null,
-                        null,
-                        $sessionId
+            if ($document_to_move['filetype'] == 'link') {
+                $real_path_target = $base_work_dir.$_POST['move_to'].'/';
+                if (!DocumentManager::cloudLinkExists($_course, $_POST['move_to'], $document_to_move['comment'])) {
+                    $doc_id = $_POST['move_file'];
+            
+                    DocumentManager::updateDBInfoCloudLink($document_to_move['path'], $_POST['move_to'].'/', $doc_id);
+            
+                    //update database item property
+                    api_item_property_update($_course, TOOL_DOCUMENT, $doc_id, 'FileMoved', api_get_user_id(), $to_group_id, null, null, null, $session_id);
+                    Display::addFlash(
+                        Display::return_message(
+                            get_lang('CloudLinkMoved'),
+                            'success'
+                        )
                     );
+                } else {
                     Display::addFlash(
                         Display::return_message(
-                            get_lang('DocMv'),
-                            'confirmation'
+                            get_lang('UrlAlreadyExists'),
+                            'error'
                         )
                     );
                 }
-
                 // Set the current path
                 $curdirpath = $_POST['move_to'];
                 $curdirpathurl = urlencode($_POST['move_to']);
             } else {
-                if ($fileExist) {
+                $real_path_target = $base_work_dir.$moveTo.'/'.basename($document_to_move['path']);
+                $fileExist = false;
+                if (file_exists($real_path_target)) {
+                    $fileExist = true;
+                }
+                if (move($base_work_dir.$document_to_move['path'], $base_work_dir.$moveTo)) {
+                    DocumentManager::updateDbInfo(
+                        'update',
+                        $document_to_move['path'],
+                        $moveTo.'/'.basename($document_to_move['path'])
+                    );
+
+                    //update database item property
+                    $doc_id = $_POST['move_file'];
                     if (is_dir($real_path_target)) {
-                        $message = Display::return_message(get_lang('DirExists'), 'error');
+                        api_item_property_update(
+                            $courseInfo,
+                            TOOL_DOCUMENT,
+                            $doc_id,
+                            'FolderMoved',
+                            api_get_user_id(),
+                            $group_properties,
+                            null,
+                            null,
+                            null,
+                            $sessionId
+                        );
+                        Display::addFlash(Display::return_message(get_lang('DirMv'), 'confirmation'));
                     } elseif (is_file($real_path_target)) {
-                        $message = Display::return_message(get_lang('FileExists'), 'v');
+                        api_item_property_update(
+                            $courseInfo,
+                            TOOL_DOCUMENT,
+                            $doc_id,
+                            'DocumentMoved',
+                            api_get_user_id(),
+                            $group_properties,
+                            null,
+                            null,
+                            null,
+                            $sessionId
+                        );
+                        Display::addFlash(
+                            Display::return_message(
+                                get_lang('DocMv'),
+                                'confirmation'
+                            )
+                        );
                     }
-                    Display::addFlash($message);
+
+                    // Set the current path
+                    $curdirpath = $_POST['move_to'];
+                    $curdirpathurl = urlencode($_POST['move_to']);
                 } else {
-                    Display::addFlash(Display::return_message(get_lang('Impossible'), 'error'));
+                    if ($fileExist) {
+                        if (is_dir($real_path_target)) {
+                            $message = Display::return_message(get_lang('DirExists'), 'error');
+                        } elseif (is_file($real_path_target)) {
+                            $message = Display::return_message(get_lang('FileExists'), 'v');
+                        }
+                        Display::addFlash($message);
+                    } else {
+                        Display::addFlash(Display::return_message(get_lang('Impossible'), 'error'));
+                    }
                 }
             }
         } else {
@@ -1275,20 +1330,35 @@ if ($isAllowedToEdit ||
                             }
                             $readonlyAlreadyChecked = true;
                         }
-
-                        $deleteDocument = DocumentManager::delete_document(
-                            $courseInfo,
-                            null,
-                            $base_work_dir,
-                            $sessionId,
-                            $documentId,
-                            $groupIid
-                        );
-                        if (!empty($deleteDocument)) {
-                            $messages .= Display::return_message(
-                                get_lang('DocDeleted').': '.$data['title'],
-                                'confirmation'
+                        if ($data['filetype'] != 'link') {
+                            // Files and folders
+                            $deleteDocument = DocumentManager::delete_document(
+                                $courseInfo,
+                                null,
+                                $base_work_dir,
+                                $sessionId,
+                                $documentId,
+                                $groupIid
                             );
+                            if (!empty($deleteDocument)) {
+                                $messages .= Display::return_message(
+                                    get_lang('DocDeleted').': '.$data['title'],
+                                    'confirmation'
+                                );
+                            }
+                        } else {
+                            // Cloud Links
+                            if (DocumentManager::deleteCloudLink($_course, $documentId)) {
+                                $messages .= Display::return_message(
+                                    get_lang('CloudLinkDeleted'),
+                                    'confirmation'
+                                );
+                            } else {
+                                $messages .= Display::return_message(
+                                    get_lang('CloudLinkDeleteError'),
+                                    'error'
+                                );
+                            }
                         }
                         break;
                 }
@@ -1741,6 +1811,14 @@ if ($isAllowedToEdit ||
             api_get_path(WEB_CODE_PATH).'document/document.php?'.api_get_cidreq().'&id='.$document_id.'&createdir=1'
         );
     }
+
+    // "Add cloud link" icon
+    if (api_get_setting('enable_add_file_link') == 'true') {
+        $actionsLeft .= Display::url(
+            Display::return_icon('clouddoc_new.png', get_lang('AddCloudLink'), '', ICON_SIZE_MEDIUM),
+            api_get_path(WEB_CODE_PATH).'document/add_link.php?'.api_get_cidreq().'&id='.$document_id
+        );
+    }
 }
 require 'document_slideshow.inc.php';
 if (!isset($_GET['keyword'])) {
@@ -1845,6 +1923,11 @@ if (isset($documentAndFolders) && is_array($documentAndFolders)) {
                 }
             }
 
+            // Hack in order to avoid the download icon appearing on cloud links
+            if ($document_data['filetype'] == 'link') {
+                $size = 0;
+            }
+
             // Icons (clickable)
             $row[] = DocumentManager::create_document_link(
                 $document_data,

+ 101 - 34
main/document/edit_document.php

@@ -28,6 +28,7 @@ use ChamiloSession as Session;
  */
 
 require_once __DIR__.'/../inc/global.inc.php';
+require_once '../inc/lib/urlUtils.lib.php';
 
 $groupRights = Session::read('group_member_with_upload_rights');
 
@@ -91,6 +92,7 @@ if (isset($_GET['id'])) {
     $dir_original = $dir;
     $doc = basename($file);
     $readonly = $document_data['readonly'];
+    $file_type = $document_data['filetype'];
 }
 
 if (empty($document_data)) {
@@ -213,16 +215,30 @@ if (isset($_POST['comment'])) {
     }
 
     if (!empty($document_id)) {
-        $params = [
-            'comment' => $comment,
-            'title' => $title,
-        ];
-        Database::update(
-            $dbTable,
-            $params,
-            ['c_id = ? AND id = ?' => [$course_id, $document_id]]
-        );
-        Display::addFlash(Display::return_message(get_lang('fileModified')));
+        $linkExists = false;
+        if ($file_type == 'link') {
+            $linkExists = DocumentManager::cloudLinkExists($course_info, $file, $_POST['comment']);
+        }
+        
+        if (!$linkExists || $linkExists == $document_id) {
+            $params = [
+                'comment' => $comment,
+                'title' => $title,
+            ];
+            Database::update(
+                $dbTable,
+                $params,
+                ['c_id = ? AND id = ?' => [$course_id, $document_id]]
+            );
+            
+            if ($file_type != 'link') {
+                Display::addFlash(Display::return_message(get_lang('fileModified')));
+            } else {
+                Display::addFlash(Display::return_message(get_lang('CloudLinkModified')));
+            }
+        } else {
+            Display::addFlash(Display::return_message(get_lang('UrlAlreadyExists'), 'warning'));
+        }
     }
 }
 
@@ -245,7 +261,9 @@ if ($is_allowed_to_edit) {
         if (empty($filename)) {
             Display::addFlash(Display::return_message(get_lang('NoFileName'), 'warning'));
         } else {
-            $file_size = filesize($document_data['absolute_path']);
+            if ($file_type != 'link') {
+                $file_size = filesize($document_data['absolute_path']);
+            }
 
             if ($read_only_flag == 0) {
                 if (!empty($content)) {
@@ -380,9 +398,10 @@ if ($owner_id == api_get_user_id() ||
     $form->addElement('hidden', 'showedit');
     $form->addElement('hidden', 'origin');
     $form->addElement('hidden', 'origin_opt');
+    $key_label_title = ($file_type != 'link' ? 'Title' : 'LinkName');
     $form->addText(
         'title',
-        get_lang('Title'),
+        get_lang($key_label_title),
         true,
         array('cols-size' => [2, 10, 0], 'autofocus')
     );
@@ -412,24 +431,54 @@ if ($owner_id == api_get_user_id() ||
         }
     }
 
-    if (!$group_document && !DocumentManager::is_my_shared_folder(api_get_user_id(), $currentDirPath, $sessionId)) {
-        // Updated on field
-        $display_date = date_to_str_ago($last_edit_date).
-            ' <span class="dropbox_date">'.api_format_date(api_get_local_time($last_edit_date)).'</span>';
-        $form->addElement('static', null, get_lang('UpdatedOn'), $display_date);
+    if ($file_type != 'link') {
+        if (!$group_document && !DocumentManager::is_my_shared_folder(api_get_user_id(), $currentDirPath, $sessionId)) {
+            // Updated on field
+            $display_date = date_to_str_ago($last_edit_date).
+                ' <span class="dropbox_date">'.api_format_date(api_get_local_time($last_edit_date)).'</span>';
+            $form->addElement('static', null, get_lang('UpdatedOn'), $display_date);
+        }
     }
 
-    $form->addElement('textarea', 'comment', get_lang('Comment'), ['cols-size' => [2, 10, 0]]);
+    if ($file_type == 'link') {
+        // URLs in whitelist
+        $urlWL = URLUtils::getFileHostingsWL();
+        sort($urlWL);
+        $urlWLRegEx = '/(\/\/|\.)('.implode('|', $urlWL).')/i'; //Matches any of the whitelisted urls preceded by // or .
+        $urlWLText = "\n\t* ".implode("\n\t* ", $urlWL);
+        $urlWLHTML = "<ul><li>".implode("</li><li>", $urlWL)."</li></ul>";
+        $form->addText('comment', get_lang('Url'));
+        $form->addElement('static', 'info', '', '<span class="text-primary" data-toggle="tooltip" title="'.$urlWLHTML.'">'.get_lang('ValidDomainsList').' <span class="glyphicon glyphicon-question-sign"></span></span>');
+    } else {
+        $form->addElement('textarea', 'comment', get_lang('Comment'), ['cols-size' => [2, 10, 0]]);
+    }
 
-    if ($owner_id == api_get_user_id() || api_is_platform_admin()) {
-        $checked = & $form->addElement('checkbox', 'readonly', null, get_lang('ReadOnly'));
-        if ($readonly == 1) {
-            $checked->setChecked(true);
+    if ($file_type != 'link') {
+        if ($owner_id == api_get_user_id() || api_is_platform_admin()) {
+            $checked = & $form->addElement('checkbox', 'readonly', null, get_lang('ReadOnly'));
+            if ($readonly == 1) {
+                $checked->setChecked(true);
+            }
         }
     }
 
+    if ($file_type == 'link') {
+        $form->addRule('title', get_lang('PleaseEnterCloudLinkName'), 'required', null, 'client');
+        $form->addRule('title', get_lang('PleaseEnterCloudLinkName'), 'required', null, 'server');
+        $form->addRule('comment', get_lang('langGiveURL'), 'required', null, 'client');
+        $form->addRule('comment', get_lang('langGiveURL'), 'required', null, 'server');
+        // Good formed url pattern (must have the protocol)
+        $urlRegEx = URLUtils::getWellformedUrlRegex();
+        $form->addRule('comment', get_lang('MalformedUrl'), 'regex', $urlRegEx, 'client');
+        $form->addRule('comment', get_lang('MalformedUrl'), 'regex', $urlRegEx, 'server');
+        $form->addRule('comment', get_lang('NotValidDomain').$urlWLText,'regex', $urlWLRegEx, 'client');
+        $form->addRule('comment', get_lang('NotValidDomain').$urlWLHTML,'regex', $urlWLRegEx, 'server');
+    }
+
     if ($is_certificate_mode) {
         $form->addButtonUpdate(get_lang('SaveCertificate'));
+    } elseif ($file_type == 'link') {
+        $form->addButtonUpdate(get_lang('SaveLink'));
     } else {
         $form->addButtonUpdate(get_lang('SaveDocument'));
     }
@@ -473,20 +522,38 @@ if ($owner_id == api_get_user_id() ||
     if ($extension == 'svg' && !api_browser_support('svg') && api_get_setting('enabled_support_svg') == 'true') {
         echo Display::return_message(get_lang('BrowserDontSupportsSVG'), 'warning');
     }
-    // HTML-editor
-    echo '<div class="page-create">
-            <div class="row" style="overflow:hidden">
-            <div id="template_col" class="col-md-3">
-                <div class="panel panel-default">
-                <div class="panel-body">
-                    <div id="frmModel" class="items-templates scrollbar-light"></div>
+    if ($file_type != 'link') {
+        // HTML-editor
+        echo '<div class="page-create">
+                <div class="row" style="overflow:hidden">
+                <div id="template_col" class="col-md-3">
+                    <div class="panel panel-default">
+                    <div class="panel-body">
+                        <div id="frmModel" class="items-templates scrollbar-light"></div>
+                    </div>
+                    </div>
                 </div>
+                <div id="doc_form" class="col-md-9">
+                    '.$form->returnForm().'
                 </div>
-            </div>
-            <div id="doc_form" class="col-md-9">
-                '.$form->returnForm().'
-            </div>
-          </div></div>';
+            </div></div>';
+    } else {
+        // Add tooltip and correctly parse its inner HTML
+        echo '<script>
+        $(document).ready(function() {
+            $("[data-toggle=\'tooltip\']").tooltip(
+                {
+                    content: 
+                        function() {
+                            return $(this).attr("title");
+                        }
+                }
+            );
+        });
+        </script>';
+
+        echo $form->return_form();
+    }
 }
 
 Display::display_footer();

BIN
main/img/clouddoc.png


BIN
main/img/clouddoc_new.png


BIN
main/img/icons/22/clouddoc.png


BIN
main/img/icons/22/clouddoc_new.png


BIN
main/img/icons/32/clouddoc_new.png


+ 132 - 6
main/inc/lib/document.lib.php

@@ -5351,7 +5351,7 @@ class DocumentManager
 
         // Get the title or the basename depending on what we're using
         if ($document_data['title'] != '') {
-            $title = $document_data['title'];
+            $title = htmlspecialchars($document_data['title']);
         } else {
             $title = basename($document_data['path']);
         }
@@ -5406,6 +5406,12 @@ class DocumentManager
         $tooltip_title = $title;
 
         $tooltip_title_alt = $tooltip_title;
+
+        if ($filetype == 'link') {
+            $tooltip_title_alt = $title;
+            $url = $document_data['comment'].'" target="_blank';
+        }
+
         if ($path == '/shared_folder') {
             $tooltip_title_alt = get_lang('UserFolders');
         } elseif (strstr($path, 'shared_folder_session_')) {
@@ -5655,6 +5661,9 @@ class DocumentManager
         if ($type == 'file') {
             $icon = choose_image($basename);
             $basename = substr(strrchr($basename, '.'), 1);
+        } elseif ($type == 'link') {
+            $icon = 'clouddoc.png';
+            $basename = 'Cloud link';
         } else {
             if ($path == '/shared_folder') {
                 $icon = 'folder_users.png';
@@ -5992,11 +6001,19 @@ class DocumentManager
         $is_read_only = $document_data['readonly'];
         $path = $document_data['path'];
 
-        $parent_id = self::get_document_id(
-            api_get_course_info(),
-            dirname($path),
-            0
-        );
+        if ($type == 'link') {
+            $parent_id = self::get_document_id(
+                api_get_course_info(),
+                rtrim($path,'/'),
+                0
+            );
+        } else {
+            $parent_id = self::get_document_id(
+                api_get_course_info(),
+                dirname($path),
+                0
+            );
+        }
 
         if (empty($parent_id) && !empty($sessionId)) {
             $parent_id = self::get_document_id(
@@ -6684,4 +6701,113 @@ class DocumentManager
 
         return $result;
     }
+
+    /**
+     * Adds a cloud link to the database
+     *
+     * @author - Aquilino Blanco Cores <aqblanco@gmail.com>
+     * @param array $_course
+     * @param string $path
+     * @param string $url
+     * @return int id of document or 0 if already exists or there was a problem creating it
+     */
+    public static function addCloudLink($_course, $path, $url, $name)
+    {
+        require_once api_get_path(LIBRARY_PATH) . 'fileUpload.lib.php';
+        $file_path = $path;
+        if (!self::cloudLinkExists($_course, $path, $url)) {
+            $doc_id = add_document($_course, $file_path, "link", 0, $name, $url);
+            if ($doc_id) {
+                // Update document item_property
+                api_item_property_update($_course, TOOL_DOCUMENT, $doc_id, 'DocumentAdded', api_get_user_id(), api_get_group_id(), api_get_user_id(), null, null, api_get_session_id());
+            }
+    
+            // If the file is in a folder, we need to update all parent folders
+            item_property_update_on_folder($_course, $file_path, api_get_user_id());
+    
+            return $doc_id;
+        } else {
+            return 0;
+        }
+    }
+ 
+    /**
+     * Deletes a cloud link from the database
+     *
+     * @author - Aquilino Blanco Cores <aqblanco@gmail.com>
+     * @param array $_course
+     * @param string $id
+     * @param string $url
+     * @return boolean true if success / false if an error occurred
+     */
+    public static function deleteCloudLink($_course, $id)
+    {
+ 
+        if (empty($id)) {
+            return false;
+        }
+ 
+        $document_id = $id;
+ 
+        $file_deleted_from_db = false;
+ 
+        if ($document_id) {
+            self::deleteDocumentFromDb($document_id, array(),  0, true);
+            //checking
+            $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
+            $sql = "SELECT * FROM $TABLE_DOCUMENT WHERE id = $document_id";
+            $result = Database::query($sql);
+            $exists = Database::num_rows($result) > 0;
+            $file_deleted_from_db = !$exists ;
+        }
+
+        return $file_deleted_from_db;
+    }
+ 
+    /**
+     * Gets the id of a cloud link with a given path
+     *
+     * @author - Aquilino Blanco Cores <aqblanco@gmail.com>
+     * @param array $_course
+     * @param string $path
+     * @param string $url
+     * @return int id of link / false if no link found
+     * @deprecated
+     */
+    public static function getCloudLinkId($_course, $path, $url)
+    {
+        $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
+        $course_id = $_course['real_id'];
+        $path = Database::escape_string($path);
+
+        if (substr($path, -1) != '/') {
+            // Add final slash to path if not present
+            $path .= '/';
+        }
+            
+        if (!empty($course_id) && !empty($path)) {
+            $sql = "SELECT id FROM $TABLE_DOCUMENT WHERE c_id = $course_id AND path LIKE BINARY '$path' AND comment = '$url' AND filetype = 'link' LIMIT 1";
+            $result = Database::query($sql);
+            if ($result && Database::num_rows($result)) {
+                $row = Database::fetch_array($result);
+                return intval($row[0]);
+            }
+        }
+        return false;
+    }
+ 
+    /**
+     * Checks if a cloud link exists
+     *
+     * @author - Aquilino Blanco Cores <aqblanco@gmail.com>
+     * @param array $_course
+     * @param string $path
+     * @param string $url
+     * @return boolean true if it exists false in other case
+     */
+    public static function cloudLinkExists($_course, $path, $url)
+    {
+        $exists = self::getCloudLinkId($_course, $path, $url);
+        return $exists;
+    }
 }

+ 56 - 0
main/inc/lib/urlUtils.lib.php

@@ -0,0 +1,56 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ * Class URLUtils
+ * This class contains some utilities to work with urls
+ *
+ * @package chamilo.library
+ */
+class URLUtils
+{
+    
+    /**
+     * Construct
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * Gets the wellformed URLs regular expression in order to use it on forms' verifications
+     *
+     * @author Aquilino Blanco Cores <aqblanco@gmail.com>
+     * @return the wellformed URLs regular expressions string
+     */
+    public static function getWellformedUrlRegex()
+    {
+        return '/\(?((http|https|ftp):\/\/)(?:((?:[^\W\s]|\.|-|[:]{1})+)@{1})?((?:www.)?(?:[^\W\s]|\.|-)+[\.][^\W\s]{2,4}|localhost(?=\/)|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?::(\d*))?([\/]?[^\s\?]*[\/]{1})*(?:\/?([^\s\n\?\[\]\{\}\#]*(?:(?=\.)){1}|[^\s\n\?\[\]\{\}\.\#]*)?([\.]{1}[^\s\?\#]*)?)?(?:\?{1}([^\s\n\#\[\]]*))?([\#][^\s\n]*)?\)?/i';
+    }
+    
+    /**
+     * Gets the files hosting sites' whitelist
+     *
+     * @author Aquilino Blanco Cores <aqblanco@gmail.com>
+     * @return array the sites list.
+     */
+    public static function getFileHostingsWL()
+    {
+        return array(
+            "asuswebstorage.com",
+            "dropbox.com",
+            "dropboxusercontent.com",
+            "fileserve.com",
+            "drive.google.com",
+            "icloud.com",
+            "mediafire.com",
+            "mega.nz",
+            "onedrive.live.com",
+            "slideshare.net",
+            "scribd.com",
+            "wetransfer.com",
+            "box.com",
+            "livefilestore.com" // OneDrive
+        );
+    }
+}

+ 8 - 0
main/install/data.sql

@@ -1961,3 +1961,11 @@ INSERT INTO settings_current (variable, type, category, selected_value, title, c
 VALUES ('allow_download_documents_by_api_key', 'radio', 'WebServices', 'false', 'AllowDownloadDocumentsByApiKeyTitle', 'AllowDownloadDocumentsByApiKeyComment', '', NULL, 1);
 INSERT INTO settings_options (variable, value, display_text) VALUES ('allow_download_documents_by_api_key', 'true', 'Yes');
 INSERT INTO settings_options (variable, value, display_text) VALUES ('allow_download_documents_by_api_key', 'false', 'No');
+
+-- Cloud File Links
+INSERT INTO `settings_options` (`variable`, `value`, `display_text`) VALUES
+('enable_add_file_link', 'true', 'Yes'),
+('enable_add_file_link', 'false', 'No');
+
+INSERT INTO `settings_current` (`variable`, `subkey`, `type`, `category`, `selected_value`, `title`, `comment`, `scope`, `subkeytext`, `access_url`, `access_url_changeable`, `access_url_locked`) VALUES
+('enable_add_file_link', NULL, 'radio', 'Tools', 'false', 'enable_add_file_link_title', 'enable_add_file_link_comment', NULL, NULL, 1, 0, 0);

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

@@ -8067,4 +8067,20 @@ $ThisEmailWasSentViaCourseX = "This e-mail was sent via course %s";
 $ThisEmailWasSentViaCourseXInSessionX = "This e-mail was sent via course %s in session %s";
 $Diagram = "Diagram";
 $CareerXDoesntHaveADiagram = "Career %s doesn't have a diagram.";
+
+// Cloud files in documents strings
+$enable_add_file_link_title = 'Enable adding cloud files links';
+$enable_add_file_link_comment = 'Tool for adding cloud files\' links from external storage sites (Dropbox, Google Drive...) at Documents.';
+$UrlAlreadyExists = "It already exists one equal URL in this folder.";
+$ErrorAddLink = "There was an error adding the link.";
+$AddCloudLink = "Add cloud file link";
+$CloudLinkDeleted = "Cloud file link deleted.";
+$CloudLinkMoved = "Cloud file link moved.";
+$CloudLinkDeleteError = "Error deleting cloud file link.";
+$CloudLinkAdded = "Cloud file link added.";
+$PleaseEnterCloudLinkName = "Please, enter a name for the cloud file link.";
+$MalformedUrl = "URL field format not valid. Expected format example: http://dropbox.com/sh/loremipsum/loremipsum?dl=0";
+$CloudLinkModified = "Cloud file link modified.";
+$NotValidDomain = "Not valid domain. It must be one of these:";
+$ValidDomainsList = "Valid domain list";
 ?>

+ 82 - 66
main/lang/galician/trad4all.inc.php

@@ -33,8 +33,8 @@ $CotangentCot = "Cotanxente:\t\t\tcot(x)";
 $HyperbolicCotangentCoth = "Cotanxente hiperbólica:\t\t\tcoth(x)";
 $ArccotangentArccot = "Arcocotanxente:\t\t\tarccota(x)";
 $HyperbolicArccotangentArccoth = "Arcocotanxente hiperbólica:\t\t\tarcconth(x)";
-$HelpCookieUsageValidation = "Para o bo funcionamento deste sitio e a medición do seu uso, esta plataforma usa cookies.</br>
-Se o considera necesario, a sección de axuda do seu navegador  informaralle sobre os procedimentos para configurar as cookies.</br>
+$HelpCookieUsageValidation = "Para o bo funcionamento deste sitio e a medición do seu uso, esta plataforma usa cookies.</br>
+Se o considera necesario, a sección de axuda do seu navegador  informaralle sobre os procedimentos para configurar as cookies.</br>
 Para maior información sobre cookies, pode visitar o sitio <a href=\"http://www.aboutcookies.org\"> About cookies </a>(en inglés) ou cualquera equivalente en español.";
 $YouAcceptCookies = "A través do uso deste sitio web, declara aceptar o uso de cookies";
 $TemplateCertificateComment = "Exemplo de certificado";
@@ -267,12 +267,12 @@ $AreYouSureDeleteTestResultBeforeDateD = "Está seguro que desexa eliminar os re
 $CleanStudentsResultsBeforeDate = "Eliminar todos os resultados antes da data selecionada.";
 $HGlossary = "Axuda do glosario";
 $GlossaryContent = "Esta ferramenta permítelle crear termos de glosario para o seu curso, que poden logo ser usados na ferramenta de documentos";
-$ForumContent = "O foro é unha ferramenta de conversación para o traballo escrito asíncrono. A diferencia do correo-e, un foro é para conversacións públicas, semi-públicas ou de grupos.
-Para usar o foro de Chamilo, os alumnos do curso poden simplemente usar o seu navegador - non requiren de ningun outro tipo de ferramenta.
-Para organizar os foros, premer na ferramenta de foros. As conversas se organizanse segundo a estructura seiguinte: Categoría > Foro > Tema de conversación > Resposta. Para permitir aos alumnos participar nos foros de xeito ordenado e efectivo, é esencial crear primeiro categorías e foros. Logo, pertence aos participantes crear temas de conversación e enviar respostas. Por defecto, se o curso foi creado con contido de exemplo, o foro conten unha única categoría, un foro, un tema de foro e unha resposta. Pode engadir foros á categoría, cambiar o seu titulo ou crear outras categorías dentro das cales podería entón crear novos foros (non confunda categorías e foros, e recorde que unha categoría que non contén foros é inútil e non se mostrará aos alumnos).
-La descripción do foro pode incluir unha lista dos  seus membros, unha definición do seu propósito, unha tarefa, un obxectivo, un tema, etc.
-Os foros de grupos no nson creados pola ferramenta de foros directamente, senón pola ferramenta de grupos, donde pode determinar se os foros serán públicos ou privados, permitiendo ao mesmo tiempo aos membros dos grupos compartir documentos e outros recursos
-Tipos de ensino: Un foro de aprendizaxe non é o mesmo que un foro dos que ve en internet. Dun lado, non é posible que os alumnos modifiquen as súas respostas unha vez que un tema de conversación foi cerrado. Isto é co obxectivo de valorar a súa contribución no foro. Logo, alguns usuarios privilexiados (profesor, titor, asistente) poden correxir directamente ls respostas dentro do foro.
+$ForumContent = "O foro é unha ferramenta de conversación para o traballo escrito asíncrono. A diferencia do correo-e, un foro é para conversacións públicas, semi-públicas ou de grupos.
+Para usar o foro de Chamilo, os alumnos do curso poden simplemente usar o seu navegador - non requiren de ningun outro tipo de ferramenta.
+Para organizar os foros, premer na ferramenta de foros. As conversas se organizanse segundo a estructura seiguinte: Categoría > Foro > Tema de conversación > Resposta. Para permitir aos alumnos participar nos foros de xeito ordenado e efectivo, é esencial crear primeiro categorías e foros. Logo, pertence aos participantes crear temas de conversación e enviar respostas. Por defecto, se o curso foi creado con contido de exemplo, o foro conten unha única categoría, un foro, un tema de foro e unha resposta. Pode engadir foros á categoría, cambiar o seu titulo ou crear outras categorías dentro das cales podería entón crear novos foros (non confunda categorías e foros, e recorde que unha categoría que non contén foros é inútil e non se mostrará aos alumnos).
+La descripción do foro pode incluir unha lista dos  seus membros, unha definición do seu propósito, unha tarefa, un obxectivo, un tema, etc.
+Os foros de grupos no nson creados pola ferramenta de foros directamente, senón pola ferramenta de grupos, donde pode determinar se os foros serán públicos ou privados, permitiendo ao mesmo tiempo aos membros dos grupos compartir documentos e outros recursos
+Tipos de ensino: Un foro de aprendizaxe non é o mesmo que un foro dos que ve en internet. Dun lado, non é posible que os alumnos modifiquen as súas respostas unha vez que un tema de conversación foi cerrado. Isto é co obxectivo de valorar a súa contribución no foro. Logo, alguns usuarios privilexiados (profesor, titor, asistente) poden correxir directamente ls respostas dentro do foro.
 Para facelo, poden seguir o procedemento seguinte: premer na icona de edición (lapis amarelo) e marcalo usando unha funcionalidade de edición (color, subliñado, etc). Finalmente, outros alumnos poden beneficiarse desta corrección visualizando o foro novamente. A mesma idea pode ser aplicada entre alumnos pero require usar a ferramenta de citación para logo indicar os elementos incorrectos (xa que non poden editar directamente a resposta doutro alumno.";
 $HForum = "Axuda do foro";
 $LoginToGoToThisCourse = "Por favor,conéctese para entrar no curso";
@@ -2571,10 +2571,10 @@ $OogieUnknownError = " \tA conversión fallou por unha razón descoñecida. <br
 $OogieBadExtension = "O arquivo non ten unha extensión correcta.";
 $WoogieBadExtension = " Por favor, envíe só documentos de texto. A extensión do arquivo debe ser .doc, .docx ou ben .odt";
 $ShowAudioRecorder = "Mostrar o gravador de audio";
-$SearchFeatureSearchExplanation = "Para procurar na base de datos do curso, por favor, use a seguinte sintaxe:
-    term tag:tag_name -exclude +include \"exact phrase\"
-Por exemplo:
-    car tag:truck -ferrari +ford \"high consumption\".
+$SearchFeatureSearchExplanation = "Para procurar na base de datos do curso, por favor, use a seguinte sintaxe:
+    term tag:tag_name -exclude +include \"exact phrase\"
+Por exemplo:
+    car tag:truck -ferrari +ford \"high consumption\".
 Isto mostraralle tódolos resultados para a palabra 'car' marcados coma 'truck', sen incluir a palabra 'ferrari' pero incluindo a palabra 'ford' e a frase exacta 'high consumption'.";
 $ViewLearningPath = "Ver lección";
 $SearchFeatureDocumentTagsIfIndexing = "Etiquetas a engadir no documento, si está indexado";
@@ -3564,8 +3564,8 @@ $SearchResetKeywords = "Reiniciar palabras chave";
 $SearchKeywords = "Palabas chave";
 $IntroductionTextDeleted = "A introdución foi eliminada";
 $SearchKeywordsHelpTitle = "Axuda da procura de palabras chave";
-$SearchKeywordsHelpComment = "Seleccione palabras chave en un ou máis campos e faga click no botón de procura.
-
+$SearchKeywordsHelpComment = "Seleccione palabras chave en un ou máis campos e faga click no botón de procura.
+
 Para seleccionar máis de unha palabra chave nun cmapo, use Ctrl+click.";
 $Validate = "Validar";
 $SearchCombineSearchWith = "Combinar palabras chave con";
@@ -4157,7 +4157,7 @@ $Changes = "Trocos";
 $MostActiveUsers = "Usuarios máis activos";
 $Contributions = "contribucións";
 $UserContributions = "Contribucións do usuario";
-$WarningDeleteMainPage = "Eliminar a páxina principal do Wiki non é recomendado xa que é o acceso principal ó wiki.
+$WarningDeleteMainPage = "Eliminar a páxina principal do Wiki non é recomendado xa que é o acceso principal ó wiki.
 Si de tódolos xeitos precisa facelo, non esquenza voltar a crear a páxina principal. Ata entón, os outros usuarios non van ser capaces de engadir novas páxinas.";
 $ConvertToLastVersion = "Para establecer esta versión coma a última faga click";
 $CurrentVersion = "Versión actual";
@@ -5297,32 +5297,32 @@ $MoveElement = "Mover";
 $ShowGlossaryInExtraToolsTitle = "Amosar os termos do glosario nas ferramentas:leccións(scorm) e exercicios.";
 $ShowGlossaryInExtraToolsComment = "Desde aquí vostede pode configurar como engadir os termos do glosario en ferramentas como leccións(scorm) e exercicios.";
 $HSurvey = "Axuda: Enquisas";
-$SurveyContent = "A ferramenta Enquisas permitiralle obter a opinión dos usuarios sobre determinados temas; por exemplo, sempre será importante saber a opinión dos alumnos sobre o curso.
-
-Creación dunha Nova Enquisa
-
-Faga clic en Crear \"unha enquisa\" e reencha os campos \"Código da enquisa\" e \"Título da enquisa\". Coa axuda do calendario pode controlar a duración da súa enquisa. Non é necesario mantela durante todo o ano, pode ser suficiente que se vexa tan só durante algúns días do curso. Completar os campos \"Introdución da enquisa\" e \"Agradecementos\" isto é unha boa practica, pois fará que a súa Enquisa sexa máis clara e afectiva. 
-
-Engadindo preguntas a unha enquisa
-
-Unha vez creada a enquisa, deberá crear as preguntas. A ferramenta \"Enquisas\" ten predefinidos diferentes tipos de preguntas: Si/Non, Elección múltiple, Resposta múltiple, Resposta aberta, Porcentaxe.... Entre estes tipos poderá seleccionar os que máis se axusten ás súas necesidades.
-
-Previsualizando a enquisa
-
-Unha vez creadas as preguntas, Vde. ten a opción de previsualizar a enquisa e vela tal como a verán os alumnos. Para iso, faga clic en Vista \"preliminar\" (icona dun documento cunha lupa).
-
-Publicando a enquisa
-
-Se está satisfeito coa súa enquisa e non necesita realizar ningún outro cambio; faga clic en Publicar \"enquisa\" (icona dun sobre cunha frecha verde) para poder enviar a súa enquisa a un grupo de usuarios. Mostraranse dúas listas unha (a da esquerda) cos usuarios do curso e a outra coa lista de usuarios aos que se lles enviará a enquisa. Seleccione os usuarios que desexe que aparezan na nova lista co botón \">>\". Logo, complete os campos \"Asunto do correo\" e \"Texto do correo\".
-Os usuarios seleccionados recibirán un correo electrónico co asunto e texto que introduciu, así como un enlace que terán que pulsar para completar a enquisa. Se desexa introducir esta ligazón nalgún lugar do texto do correo, debe introducir o seguinte: ** ligazón ** (asterisco asterisco  ligazón asterisco asterisco). Esta etiqueta será substituída automaticamente pola ligazón. Se non engade esta ** ligazón **, este será incorporado automaticamente ao final do correo.
-Para rematar, a ferramenta de Enquisas permite enviar un correo-e a todos os usuarios seleccionados se habilita a opción \"Enviar correo\" se non o habilita os usuarios poderán ver a enquisa ao entrar ao sistema na ferramenta \"Enquisas\" a condición de que se atope accesible.
-
-Informes da enquisa
-
-Analizar as enquisas é un proceso tedioso. Os \"Informes\" das enquisas axudaranlle a ver a información por pregunta e por usuario, así como comparar dúas preguntas ou un completo informe de toda a enquisa. Na \"Lista de Enquisas\" faga clic en Informes \"\" (icona dun gráfico circular). 
-
-Administrando as enquisas
-
+$SurveyContent = "A ferramenta Enquisas permitiralle obter a opinión dos usuarios sobre determinados temas; por exemplo, sempre será importante saber a opinión dos alumnos sobre o curso.
+
+Creación dunha Nova Enquisa
+
+Faga clic en Crear \"unha enquisa\" e reencha os campos \"Código da enquisa\" e \"Título da enquisa\". Coa axuda do calendario pode controlar a duración da súa enquisa. Non é necesario mantela durante todo o ano, pode ser suficiente que se vexa tan só durante algúns días do curso. Completar os campos \"Introdución da enquisa\" e \"Agradecementos\" isto é unha boa practica, pois fará que a súa Enquisa sexa máis clara e afectiva. 
+
+Engadindo preguntas a unha enquisa
+
+Unha vez creada a enquisa, deberá crear as preguntas. A ferramenta \"Enquisas\" ten predefinidos diferentes tipos de preguntas: Si/Non, Elección múltiple, Resposta múltiple, Resposta aberta, Porcentaxe.... Entre estes tipos poderá seleccionar os que máis se axusten ás súas necesidades.
+
+Previsualizando a enquisa
+
+Unha vez creadas as preguntas, Vde. ten a opción de previsualizar a enquisa e vela tal como a verán os alumnos. Para iso, faga clic en Vista \"preliminar\" (icona dun documento cunha lupa).
+
+Publicando a enquisa
+
+Se está satisfeito coa súa enquisa e non necesita realizar ningún outro cambio; faga clic en Publicar \"enquisa\" (icona dun sobre cunha frecha verde) para poder enviar a súa enquisa a un grupo de usuarios. Mostraranse dúas listas unha (a da esquerda) cos usuarios do curso e a outra coa lista de usuarios aos que se lles enviará a enquisa. Seleccione os usuarios que desexe que aparezan na nova lista co botón \">>\". Logo, complete os campos \"Asunto do correo\" e \"Texto do correo\".
+Os usuarios seleccionados recibirán un correo electrónico co asunto e texto que introduciu, así como un enlace que terán que pulsar para completar a enquisa. Se desexa introducir esta ligazón nalgún lugar do texto do correo, debe introducir o seguinte: ** ligazón ** (asterisco asterisco  ligazón asterisco asterisco). Esta etiqueta será substituída automaticamente pola ligazón. Se non engade esta ** ligazón **, este será incorporado automaticamente ao final do correo.
+Para rematar, a ferramenta de Enquisas permite enviar un correo-e a todos os usuarios seleccionados se habilita a opción \"Enviar correo\" se non o habilita os usuarios poderán ver a enquisa ao entrar ao sistema na ferramenta \"Enquisas\" a condición de que se atope accesible.
+
+Informes da enquisa
+
+Analizar as enquisas é un proceso tedioso. Os \"Informes\" das enquisas axudaranlle a ver a información por pregunta e por usuario, así como comparar dúas preguntas ou un completo informe de toda a enquisa. Na \"Lista de Enquisas\" faga clic en Informes \"\" (icona dun gráfico circular). 
+
+Administrando as enquisas
+
 Existen as opcións de Editar \"\" e \"Eliminar\" na columna \"Modificar\" da \"Lista de enquisas\"";
 $HBlogs = "Axuda: Blogs";
 $BlogsContent = "As ferramentas do Proxecto dan ó alumno a oportunidade de construir coñecemento colaborativo a través de roles, tarefas e datas de vencemento.";
@@ -5496,17 +5496,17 @@ $StartToType = "Comece a escribir, entón faga click nesta barra para validar a
 $InstallChamilo = "Instalar Chamilo";
 $ChamiloURL = "URL de Chamilo";
 $YouDoNotHaveAnySessionInItsHistory = "Non tes ningunha sesión formativa previa";
-$PortalHomepageDefaultIntroduction = "
-<h2>Noraboa! Instalou satisfactoriamente o seu portal de aprendizaxe!</h2>
-
-<p>Agora pode completar a instalación seguindo tres sinxelos pasos:<br></p>
-
-   1. Configure o seu portal indo a sección de administración e seleccione a entrada Portal -> Opcións de configuración.
-   2. Déalle algo de vida ó seu portal creando usuarios e/ou cursos. Pode facer iso invitando xente a crear as súas contas ou crealas vostede mesmo a través da administración de Usuarios e de Cursos.
-   3. Edite esta páxina a través de Editar páxina principal na sección de administración.
-
-Sempre pode atopar máis información acerca deste software no noso website: http://www.chamilo.org.
-
+$PortalHomepageDefaultIntroduction = "
+<h2>Noraboa! Instalou satisfactoriamente o seu portal de aprendizaxe!</h2>
+
+<p>Agora pode completar a instalación seguindo tres sinxelos pasos:<br></p>
+
+   1. Configure o seu portal indo a sección de administración e seleccione a entrada Portal -> Opcións de configuración.
+   2. Déalle algo de vida ó seu portal creando usuarios e/ou cursos. Pode facer iso invitando xente a crear as súas contas ou crealas vostede mesmo a través da administración de Usuarios e de Cursos.
+   3. Edite esta páxina a través de Editar páxina principal na sección de administración.
+
+Sempre pode atopar máis información acerca deste software no noso website: http://www.chamilo.org.
+
 Disfrute, e non dubide en unirse á comunidade e proporcionarnos feedback a través do foro.";
 $WithTheFollowingSettings = "cos seguintes parámetros:";
 $ThePageHasBeenExportedToDocArea = "A páxina foi exportada á ferramenta documentos";
@@ -5887,7 +5887,7 @@ $SearchProfileMatches = "Buscar perfiles que correspondan.";
 $IsThisWhatYouWereLookingFor = "Corresponde ao que busca?";
 $WhatSkillsAreYouLookingFor = "Que competencias está buscando?";
 $ProfileSearch = "Búsqueda de perfil.";
-$CourseSettingsRegisterDirectLink = "Se un curso é público ou aberto,pode usar a ligazón directa abaixo para invitar a novos usuarios, de xeito que sexan enviados directamente a este curso ao rematar o formulario de rexistro no portal. Se desexa pode engadir o parámetro e=1 a esta ligazón , reemplazando \"1\" polo ID do exercicio, para mandar os usuarios directamente a un exercicio ou exame. O ID do exercicio pode obterse na URL do exercicio cando lle da clic para entrar nel.
+$CourseSettingsRegisterDirectLink = "Se un curso é público ou aberto,pode usar a ligazón directa abaixo para invitar a novos usuarios, de xeito que sexan enviados directamente a este curso ao rematar o formulario de rexistro no portal. Se desexa pode engadir o parámetro e=1 a esta ligazón , reemplazando \"1\" polo ID do exercicio, para mandar os usuarios directamente a un exercicio ou exame. O ID do exercicio pode obterse na URL do exercicio cando lle da clic para entrar nel.
 %s";
 $DirectLink = "Ligazón directa";
 $here = "Aquí";
@@ -5973,14 +5973,14 @@ $ContactInformationHasBeenSent = "A información de contacto foi enviada";
 $EditExtraFieldOptions = "Editar opcións dos campos extras.";
 $ExerciseDescriptionLabel = "Descrición";
 $UserInactivedSinceX = "Usuario inactivo desde %s";
-$ContactInformationDescription = "Querido usuario,
-
-Esta a piques de comezar a usar un dos mellores sistemas abertos de e-learning no mercado. Como moitos outros proxectos de código aberto, este proxecto está mantido por unha gran comunidade de estudiantes, aprofesores, desenvolvedores e creadores de contido ós que les gustaría promover mellor o proxecto.
-
-Coñecendo algo máis de vostede, un dos nosos importantes usuarios que manexa o noso sistema de e-learning, seremos capaces de facerlle saber á xente que o noso software está a ser usado e facendolle saber a vostede cando organizamos eventos que poden ser relevantes para vostede.
-
-Completando este formulario, vostede acepta que a asociación Chamilo ou os seus membros poden mandarlle información por email acerca de importantes eventos ou actualizacións no software de Chamilo ou da comunidade. Isto tamén axuda a crecer á comunidade e a organizar entidades onde a información flúe, con un permantente respeto polo seu tempo e a súa privacidade.
-
+$ContactInformationDescription = "Querido usuario,
+
+Esta a piques de comezar a usar un dos mellores sistemas abertos de e-learning no mercado. Como moitos outros proxectos de código aberto, este proxecto está mantido por unha gran comunidade de estudiantes, aprofesores, desenvolvedores e creadores de contido ós que les gustaría promover mellor o proxecto.
+
+Coñecendo algo máis de vostede, un dos nosos importantes usuarios que manexa o noso sistema de e-learning, seremos capaces de facerlle saber á xente que o noso software está a ser usado e facendolle saber a vostede cando organizamos eventos que poden ser relevantes para vostede.
+
+Completando este formulario, vostede acepta que a asociación Chamilo ou os seus membros poden mandarlle información por email acerca de importantes eventos ou actualizacións no software de Chamilo ou da comunidade. Isto tamén axuda a crecer á comunidade e a organizar entidades onde a información flúe, con un permantente respeto polo seu tempo e a súa privacidade.
+
 Por favor, note que <b>non e obrigatorio</b> completar este formulario. Si vostede desexa permanecer no anonimato, perderemos a oportunidade de ofertarlle os privilexios de ser administrador dun portal rexistrado, pero respetaremos a súa decisión. Simplemente deixe este formulario baleiro e faga click en \"Seguinte\".";
 $CompanyActivity = "Actividade da súa compañía";
 $PleaseAllowUsALittleTimeToSubscribeYouToOneOfOurCourses = "Permítanos un pouco de tempo para subscribirse a un dos nosos cursos. Se considera que nos olvidamos de vostede, póñase en contacto cos administradores da plataforma. Pode atopar os seus datos de contacto ao pé da páxina.";
@@ -6216,8 +6216,8 @@ $Pediaphon = "Usar os servicios de audio de Pediaphon";
 $HelpPediaphon = "Admite textos con varios miles de caracteres, pódense seleccionar varios tipos de voces masculinas e femininas (segundo o idioma). Os arquivos de audio xéranse e gárdanse automaticamente no directorio de Chamilo no que vostede se atopa actualmente.";
 $FirstSelectALanguage = "Primeiro seleccione un idioma";
 $MoveUserStats = "Mover os resultados dos usuarios dende/hacia unha sesión de formación.";
-$CompareUserResultsBetweenCoursesAndCoursesInASession = "Esta ferramenta avanzada permitelle mellorar manualmente o seguimento dos resultados dos usuarios cando cambian dun modelo de cursos a un modelo de sesións de formación. Nunha maioría de casos, non necesitará usala.
-Nesta pantalla, pode comparar os resultados que os usuarios teñen no contexto dun curso e no contexto do mesmo curso dentro dunha sesión de formación. 
+$CompareUserResultsBetweenCoursesAndCoursesInASession = "Esta ferramenta avanzada permitelle mellorar manualmente o seguimento dos resultados dos usuarios cando cambian dun modelo de cursos a un modelo de sesións de formación. Nunha maioría de casos, non necesitará usala.
+Nesta pantalla, pode comparar os resultados que os usuarios teñen no contexto dun curso e no contexto do mesmo curso dentro dunha sesión de formación. 
 Unha vez decidida cal é o mellor contexto para o seguimento (resultados de exercicios e seguimento de leccións), poderá movelo dun curso a unha sesión.";
 $PDFExportWatermarkEnableTitle = "Marcas de auga nas exportacións a PDF.";
 $PDFExportWatermarkEnableComment = "Se activa esta opción poderá cargar unha imaxe ou un texto que serán automaticamente engadidos como marca de auga nos documentos resultantes de todas as exportacións a PDF que realice o sistema.";
@@ -6352,8 +6352,8 @@ $MailNotifyInvitation = "Notificción por correo de novas invitacións recibidas
 $MailNotifyMessage = "Notificación por correo de nova mensaxe persoal";
 $MailNotifyGroupMessage = "Notificación por correo de novas mensaxes recibidas en grupos";
 $SearchEnabledTitle = "Búsqueda de texto completo";
-$SearchEnabledComment = "Esta funcionalidade permite a indexación da maioría dos documentos subidos ao seu portal, e a través disto provee a funcionalidade de búsqueda para os usuarios.
-Esta funcionalidade non indexa documentos que xa foran subidos con anterioridade, polo que é importante(se o desexa) activala ao inicio da implementación.
+$SearchEnabledComment = "Esta funcionalidade permite a indexación da maioría dos documentos subidos ao seu portal, e a través disto provee a funcionalidade de búsqueda para os usuarios.
+Esta funcionalidade non indexa documentos que xa foran subidos con anterioridade, polo que é importante(se o desexa) activala ao inicio da implementación.
 Unha vez activada, unha caixa de búsqueda aparecerá na lista de cursos de cada usuario. Buscar un termo específico trae unha lista de documentos, exercicios e temas de foro correspondentes, filtrados dependendo da súa dispoñibilidade para o usuario.";
 $SpecificSearchFieldsAvailable = "Campos de búsqueda personalizados dispoñibles";
 $XapianModuleInstalled = "Módulo Xapian instalado";
@@ -6377,7 +6377,7 @@ $OnlyLettersAndNumbers = "Só letras (a-z) e números (0-9)";
 $HideCoursesInSessionsTitle = "Esconder cursos na lista de sesións";
 $HideCoursesInSessionsComment = "Cando amose os bloques de sesións na páxina de cursos, esconder a lista de cursos dentro da sesión (só amosalos na páxina específica de sesión).";
 $ShowGroupsToUsersTitle = "Mostrar as clases aos usuarios";
-$ShowGroupsToUsersComment = "Amosar as clases aos usuarios. As clases son unha funcionalidade que permite subscribir/desuscribir grupos de usuarios dentro dunha sesión ou un curso directamente, reducindo o traballo administrativo.
+$ShowGroupsToUsersComment = "Amosar as clases aos usuarios. As clases son unha funcionalidade que permite subscribir/desuscribir grupos de usuarios dentro dunha sesión ou un curso directamente, reducindo o traballo administrativo.
 Cando activa esta opción,os alumnos poden ver de que clase forman parte, a través do seu interface de rede social.";
 $ExerciseWillBeActivatedFromXToY = "O exercicio estará visible do %s ao %s";
 $ExerciseAvailableFromX = "Exercicio dispoñible desde o %s";
@@ -7295,4 +7295,20 @@ $CourseImagesInCoursesListTitle = "Iconas de cursos personalizados";
 $CourseImagesInCoursesListComment = "Usar as imaxes de cursos como iconas nas listas de cursos (no canto da icona verde por defecto)";
 $StudentPublicationSelectionForGradebookTitle = "Tarefas consideradas para avaliación";
 $StudentPublicationSelectionForGradebookComment = "Na ferramenta de tarefas, os estudantes poden subir máis dun arquivo. No caso de haber máis dun arquivo do mesmo estudante para unha soa tarefa, cal destes debe ser considerado para a nota nas avaluacións? Isto depende da súa metodoloxía. Seleccione 'primeiro' para poñer o acento sobre a atención ao detalle (como entregar a tempo e o traballo finalizado a primeira vez). Use 'último' para poner o acento sobre o traballo colaborativo e a adaptabilidade.";
+
+// Cloud files in documents strings
+$enable_add_file_link_title = 'Permitir engadir enlaces a ficheiros na nube';
+$enable_add_file_link_comment = 'Ferramenta para engadir enlaces a ficheiros situados en sitios de almacenamento externo (Dropbox, Google Drive, etc...) en Documentos.';
+$UrlAlreadyExists = "Xa existe unha URL igual neste directorio.";
+$ErrorAddLink = "Produciuse un erro engadindo o enlace.";
+$AddCloudLink = "Engadir enlace a un ficheiro na nube";
+$CloudLinkDeleted = "Enlace a ficheiro na nube eliminado.";
+$CloudLinkMoved = "Enlace a ficheiro na nube movido.";
+$CloudLinkDeleteError = "Erro ao eliminar o enlace ao ficheiro na nube.";
+$CloudLinkAdded = "Enlace a ficheiro na nube engadido.";
+$PleaseEnterCloudLinkName = "Por favor, introduza un nome para o enlace ao ficheiro na nube.";
+$MalformedUrl = "Formato do campo URL non válido. Exemplo do formato esperado: http://dropbox.com/sh/loremipsum/loremipsum?dl=0";
+$CloudLinkModified = "Enlace a ficheiro na nube modificado.";
+$NotValidDomain = "Dominio non válido. Debe ser un dos seguintes:";
+$ValidDomainsList = "Lista de dominios válidos";
 ?>

+ 153 - 137
main/lang/spanish/trad4all.inc.php

@@ -265,11 +265,11 @@ $AreYouSureDeleteTestResultBeforeDateD = "¿Está seguro que desea eliminar los
 $CleanStudentsResultsBeforeDate = "Eliminar todos los resultados antes de la fecha selecionada";
 $HGlossary = "Ayuda del glosario";
 $GlossaryContent = "Esta herramienta le permite crear términos de glosario para su curso, los cuales pueden luego ser usados en la herramienta de documentos";
-$ForumContent = "<p>El foro es una herramienta de conversación para el trabajo escrito asíncrono. A diferencia del e-mail, un foro es para conversaciones públicas, semi-públicas o de grupos.</p>
-<p>Para usar el foro de Chamilo, los alumnos del curso pueden simplemente usar su navegador - no requieren de ningun otro tipo de herramienta</p>
-<p>Para organizar los foros, dar click en la herramienta de foros. Las conversaciones se organizan según la estructura siguiente: Categoría > Foro > Tema de conversación > Respuesta. Para permitir a los alumnos participar en los foros de manera ordenada y efectiva, es esencial crear primero categorías y foros. Luego, pertenece a los participantes crear temas de conversación y enviar respuestas. Por defecto, si el curso se ha creado con contenido de ejemplo, el foro contiene una única categoría, un foro, un tema de foro y una respuesta. Puede añadir foros a la categoría, cambiar su titulo o crear otras categorías dentro de las cuales podría entonces crear nuevos foros (no confunda categorías y foros, y recuerde que una categoría que no contiene foros es inútil y no se mostrará a los alumnos).</p>
-<p>La descripción del foro puede incluir una lista de sus miembros, una definición de su propósito, una tarea, un objetivo, un tema, etc.</p>
-<p>Los foros de grupos no son creados por la herramienta de foros directamente, sino por la herramienta de grupos, donde puede determinar si los foros serán públicos o privados, permitiendo al mismo tiempo a los miembros sus grupos compartir documentos y otros recursos</p>
+$ForumContent = "<p>El foro es una herramienta de conversación para el trabajo escrito asíncrono. A diferencia del e-mail, un foro es para conversaciones públicas, semi-públicas o de grupos.</p>
+<p>Para usar el foro de Chamilo, los alumnos del curso pueden simplemente usar su navegador - no requieren de ningun otro tipo de herramienta</p>
+<p>Para organizar los foros, dar click en la herramienta de foros. Las conversaciones se organizan según la estructura siguiente: Categoría > Foro > Tema de conversación > Respuesta. Para permitir a los alumnos participar en los foros de manera ordenada y efectiva, es esencial crear primero categorías y foros. Luego, pertenece a los participantes crear temas de conversación y enviar respuestas. Por defecto, si el curso se ha creado con contenido de ejemplo, el foro contiene una única categoría, un foro, un tema de foro y una respuesta. Puede añadir foros a la categoría, cambiar su titulo o crear otras categorías dentro de las cuales podría entonces crear nuevos foros (no confunda categorías y foros, y recuerde que una categoría que no contiene foros es inútil y no se mostrará a los alumnos).</p>
+<p>La descripción del foro puede incluir una lista de sus miembros, una definición de su propósito, una tarea, un objetivo, un tema, etc.</p>
+<p>Los foros de grupos no son creados por la herramienta de foros directamente, sino por la herramienta de grupos, donde puede determinar si los foros serán públicos o privados, permitiendo al mismo tiempo a los miembros sus grupos compartir documentos y otros recursos</p>
 <p><b>Tips de enseñanza:</b> Un foro de aprendizaje no es lo mismo que un foro de los que ve en internet. De un lado, no es posible que los alumnos modifiquen sus respuestas una vez que un tema de conversación haya sido cerrado. Esto es con el objetivo de valorar su contribución en el foro. Luego, algunos usuarios privilegiados (profesor, tutor, asistente) pueden corregir directamente las respuestas dentro del foro.<br />Para hacerlo, pueden seguir el procedimiento siguiente:<br />dar click en el icono de edición (lapiz amarillo) y marcarlo usando una funcionalidad de edición (color, subrayado, etc). Finalmente, otros alumnos pueden beneficiar de esta corrección visualizando el foro nuevamente. La misa idea puede ser aplicada entre alumnos pero requiere usar la herramienta de citación para luego indicar los elementos incorrectos (ya que no pueden editar directamente la respuesta de otro alumno)</p>";
 $HForum = "Ayuda del foro";
 $LoginToGoToThisCourse = "Conectarse para entrar al curso";
@@ -330,18 +330,18 @@ $DeleteUsersNotInList = "Desinscribir los alumnos que no están en una sesión e
 $IfSessionExistsUpdate = "Si la sesión existe, actualizarla con los datos del archivo";
 $CreatedByXYOnZ = "Creado/a por <a href=\"%s\">%s</a> el %s";
 $LoginWithExternalAccount = "Ingresar con una cuenta externa";
-$ImportAikenQuizExplanationExample = "Este es el texto de la pregunta 1
-A. Respuesta 1
-B. Respuesta 2
-C. Respuesta 3
-ANSWER: B
-
-Este es el texto de la pregunta 2 (notese la línea blanca arriba)
-A. Respuesta 1
-B. Respuesta 2
-C. Respuesta 3
-D. Respuesta 4
-ANSWER: D
+$ImportAikenQuizExplanationExample = "Este es el texto de la pregunta 1
+A. Respuesta 1
+B. Respuesta 2
+C. Respuesta 3
+ANSWER: B
+
+Este es el texto de la pregunta 2 (notese la línea blanca arriba)
+A. Respuesta 1
+B. Respuesta 2
+C. Respuesta 3
+D. Respuesta 4
+ANSWER: D
 ANSWER_EXPLANATION: Este es un texto opcional de retroalimentación que aparecerá al costado de la respuesta correcta.";
 $ImportAikenQuizExplanation = "El formato Aiken es un simple formato texto (archivo .txt) con varios bloques de preguntas, cada bloque separado por una línea blanca. La primera línea es la pregunta. Las líneas de respuestas tienen un prefijo de letra y punto, y la respuesta correcta sigue, con el prefijo 'ANSWER:'. Ver ejemplo a continuación.";
 $ExerciseAikenErrorNoAnswerOptionGiven = "El archivo importado tiene por lo menos una pregunta sin respuesta (o las respuestas no incluyen la letra de prefijo requerida). Asegúrese de que cada pregunta tengo por lo mínimo una respuesta y que esté prefijada por una letra y un punto o una paréntesis, como sigue: A. Respuesta uno";
@@ -431,16 +431,16 @@ $LatestVersionIs = "La última versión es";
 $YourVersionNotUpToDate = "Su versión está actualizada";
 $Hotpotatoes = "Hotpotatoes";
 $ZeroMeansNoQuestionWillBeSelectedMinusOneMeansThatAllQuestionsWillBeSelected = "-1 = Todas las preguntas serán seleccionadas. 0 = Ninguna pregunta será seleccionada.";
-$EmailNotificationTemplateDescription = "Puede modificar el correo enviado a los usuarios al terminar el ejercicio. Puede usar los siguientes términos: 
-
-{{ student.username }}
-{{ student.firstname }} 
-{{ student.lastname }}
-{{ student.official_code }} 
-{{ exercise.title }} 
-{{ exercise.start_time }}
-{{ exercise.end_time }}
-{{ course.title }}
+$EmailNotificationTemplateDescription = "Puede modificar el correo enviado a los usuarios al terminar el ejercicio. Puede usar los siguientes términos: 
+
+{{ student.username }}
+{{ student.firstname }} 
+{{ student.lastname }}
+{{ student.official_code }} 
+{{ exercise.title }} 
+{{ exercise.start_time }}
+{{ exercise.end_time }}
+{{ course.title }}
 {{ course.code }}";
 $EmailNotificationTemplate = "Plantilla del correo electrónico enviado al usuario al terminar el ejercicio.";
 $ExerciseEndButtonDisconnect = "Desconexión de la plataforma";
@@ -2640,16 +2640,16 @@ $NoPosts = "Sin publicaciones";
 $WithoutAchievedSkills = "Sin competencias logradas";
 $TypeMessage = "Por favor, escriba su mensaje";
 $ConfirmReset = "¿Seguro que quiere borrar todos los mensajes?";
-$MailCronCourseExpirationReminderBody = "Estimado %s,
-
-Ha llegado a nuestra atención que no ha completado el curso %s aunque su fecha de vencimiento haya sido establecida al %s, quedando %s días para terminarlo.
-
-Le recordamos que solo tiene la posibilidad de seguir este curso una vez al año, razón por la cual le invitamos con insistencia a completar su curso en el plazo que queda.
-
-Puede regresar al curso conectándose a la plataforma en esta dirección: %s
-
-Saludos cordiales,
-
+$MailCronCourseExpirationReminderBody = "Estimado %s,
+
+Ha llegado a nuestra atención que no ha completado el curso %s aunque su fecha de vencimiento haya sido establecida al %s, quedando %s días para terminarlo.
+
+Le recordamos que solo tiene la posibilidad de seguir este curso una vez al año, razón por la cual le invitamos con insistencia a completar su curso en el plazo que queda.
+
+Puede regresar al curso conectándose a la plataforma en esta dirección: %s
+
+Saludos cordiales,
+
 El equipo de %s";
 $MailCronCourseExpirationReminderSubject = "Urgente: Recordatorio de vencimiento de curso %s";
 $ExerciseAndLearningPath = "Ejercicios y lecciones";
@@ -5829,7 +5829,7 @@ $Item = "Ítem";
 $ConfigureDashboardPlugin = "Configurar el plugin del Panel de control";
 $EditBlocks = "Editar bloques";
 $Never = "Nunca";
-$YourAccountIsActiveYouCanLoginAndCheckYourCourses = "Estimado usuario <br><br>
+$YourAccountIsActiveYouCanLoginAndCheckYourCourses = "Estimado usuario <br><br>
 Usted no esta activo en la plataforma, por favor inicie sesión nuevamente y revise sus cursos";
 $SessionFields = "Campos de sesión";
 $CopyLabelSuffix = "Copia";
@@ -5887,12 +5887,12 @@ $SearchProfileMatches = "Buscar perfiles que correspondan";
 $IsThisWhatYouWereLookingFor = "Corresponde a lo que busca?";
 $WhatSkillsAreYouLookingFor = "Que competencias está buscando?";
 $ProfileSearch = "Búsqueda de perfil";
-$CourseSettingsRegisterDirectLink = "Si su curso es público o abierto, puede usar el enlace directo abajo para invitar a nuevos usuarios, de tal manera que estén enviados directamente en este curso al finalizar el formulario de registro al portal. Si desea, puede añadir el parámetro e=1 a este enlace, remplazando \"1\" por el ID del ejercicio, para mandar los usuarios directamente a un ejercicio o examen. El ID del ejercicio se puede obtener en la URL del ejercicio cuando le de clic para entrar al mismo.<br />
+$CourseSettingsRegisterDirectLink = "Si su curso es público o abierto, puede usar el enlace directo abajo para invitar a nuevos usuarios, de tal manera que estén enviados directamente en este curso al finalizar el formulario de registro al portal. Si desea, puede añadir el parámetro e=1 a este enlace, remplazando \"1\" por el ID del ejercicio, para mandar los usuarios directamente a un ejercicio o examen. El ID del ejercicio se puede obtener en la URL del ejercicio cuando le de clic para entrar al mismo.<br />
 %s";
 $DirectLink = "Enlace directo";
 $here = "aqui";
 $GoAheadAndBrowseOurCourseCatalogXOnceRegisteredYouWillSeeTheCourseHereX = "Adelante, pulsa %s para acceder al catálogo de cursos e inscribirte en un curso que te interese. Una vez inscrito/a, el curso aparecerá en esta pantalla en lugar de este mensaje.";
-$HelloXAsYouCanSeeYourCourseListIsEmpty = "Hola %s, te damos la bienvenida,<br />
+$HelloXAsYouCanSeeYourCourseListIsEmpty = "Hola %s, te damos la bienvenida,<br />
 Como puedes ver, tu lista de cursos todavía está vacía. Esto es porque todavía no estás inscrito/a en ningún curso.";
 $UnsubscribeUsersAlreadyAddedInCourse = "Desinscribir todos los alumnos ya inscritos";
 $ImportUsers = "Importar usuarios";
@@ -6154,9 +6154,9 @@ $AverageScore = "Puntuación media";
 $LastConnexionDate = "Fecha de la última conexión";
 $ToolVideoconference = "Videoconferencia";
 $BigBlueButtonEnableTitle = "Herramienta de videoconferencia BigBlueButton";
-$BigBlueButtonEnableComment = "Seleccione si desea habilitar la herramienta de videoconferencia BigBlueButton. Una vez activada, se mostrará como una herramienta en la página principal todos los curso. Los profesores podrán lanzar una videoconferencia en cualquier momento, pero los estudiantes sólo podrán unirse a una ya lanzada.
-Si no dispone de un servidor BigBlueButton, pruebe a 
-<a href=\"http://bigbluebutton.org/\" target=\"_blank\">configurar uno</a> o pida ayuda a los <a href=\"http://www.chamilo.org/en/providers\" target=\"_blank\">proveedores oficiales de Chamilo</a>.
+$BigBlueButtonEnableComment = "Seleccione si desea habilitar la herramienta de videoconferencia BigBlueButton. Una vez activada, se mostrará como una herramienta en la página principal todos los curso. Los profesores podrán lanzar una videoconferencia en cualquier momento, pero los estudiantes sólo podrán unirse a una ya lanzada.
+Si no dispone de un servidor BigBlueButton, pruebe a 
+<a href=\"http://bigbluebutton.org/\" target=\"_blank\">configurar uno</a> o pida ayuda a los <a href=\"http://www.chamilo.org/en/providers\" target=\"_blank\">proveedores oficiales de Chamilo</a>.
 BigBlueButton es libre, pero su instalación requiere ciertas habilidades técnicas que no todo el mundo posee. Puede instalarlo por su cuenta o buscar ayuda profesional con el consiguiente costo. En la lógica del software libre, nosotros le ofrecemos las herramientas para hacer más fácil su trabajo y le recomendamos profesionales (los proveedores oficiales de Chamilo) que serán capaces de ayudarle.";
 $BigBlueButtonHostTitle = "Servidor BigBlueButton";
 $BigBlueButtonHostComment = "Este es el nombre del servidor donde su servidor BigBlueButton está ejecutándose. Puede ser localhost, una dirección IP (ej., 192.168.14.54) o un nombre de dominio (por ej., my.video.com).";
@@ -6167,14 +6167,14 @@ $OnlyAccessFromYourGroup = "Sólo accesible desde su grupo";
 $CreateAssignmentPage = "Esto creará una página wiki especial en la que el profesor describe la tarea, la cual se enlazará automáticamente a las páginas wiki donde los estudiantes la realizarán. Tanto las página del docente como la de los estudiantes se crean automáticamente. En este tipo de tareas los estudiantes sólo podrán editar y ver sus páginas, aunque esto puede cambiarlo si lo desea.";
 $UserFolders = "Carpetas de los usuarios";
 $UserFolder = "Carpeta del usuario";
-$HelpUsersFolder = "INFORMACIÓN SOLO VISIBLE POR EL PROFESORADO:\nLa carpeta de los usuarios contiene una carpeta de cada usuario que haya accedido a ella a través de la herramienta documentos, o bien que haya enviado algún fichero al curso a través del editor, salvo desde la herramienta grupos. Si ninguna de las dos circuntancias se ha producido la carpeta del usuario no estará creada. En el caso de los grupos, los archivos que se envíen a través del editor se depositarán en la carpeta de cada grupo, la cual sólo será accesible por los alumnos desde la herramienta grupos.
-
-La carpeta de los usuarios y las carpetas que contiene de cada uno de ellos, se mantendrán por ocultas por defecto, si bien cada alumno podrá ver el contenido de la suya cuando acceda a inspeccionar los archivos del servidor a través del editor. No obstante, si un alumno conoce la dirección de un archivo de la carpeta de otro alumno podrá visualizarlo.
-
-Si se hace visible la carpeta de los usuarios y la carpeta de uno o más alumnos, el resto de los alumnos podrán ver todo su contenido. En este caso, el alumno propietario de la carpeta también podrá desde la herramienta documentos (sólo dentro de su carpeta): crear y editar documentos web, convertir un documento web en una plantilla para uso personal, crear y editar dibujos SVG y PNG, grabar archivos de audio en formato WAV, convertir texto en audio en formato MP3, realizar capturas a través de su webcam, enviar documentos, crear carpetas, mover carpetas y archivos, borrar carpetas y archivos, y descargar copias de seguridad de su carpeta.
-
-Por otra parte, la herramienta documentos se sincroniza con el gestor de archivos del editor web, así que los cambios en la gestión de los documentos realizados en una u otra afectarán a ambas. 
-
+$HelpUsersFolder = "INFORMACIÓN SOLO VISIBLE POR EL PROFESORADO:\nLa carpeta de los usuarios contiene una carpeta de cada usuario que haya accedido a ella a través de la herramienta documentos, o bien que haya enviado algún fichero al curso a través del editor, salvo desde la herramienta grupos. Si ninguna de las dos circuntancias se ha producido la carpeta del usuario no estará creada. En el caso de los grupos, los archivos que se envíen a través del editor se depositarán en la carpeta de cada grupo, la cual sólo será accesible por los alumnos desde la herramienta grupos.
+
+La carpeta de los usuarios y las carpetas que contiene de cada uno de ellos, se mantendrán por ocultas por defecto, si bien cada alumno podrá ver el contenido de la suya cuando acceda a inspeccionar los archivos del servidor a través del editor. No obstante, si un alumno conoce la dirección de un archivo de la carpeta de otro alumno podrá visualizarlo.
+
+Si se hace visible la carpeta de los usuarios y la carpeta de uno o más alumnos, el resto de los alumnos podrán ver todo su contenido. En este caso, el alumno propietario de la carpeta también podrá desde la herramienta documentos (sólo dentro de su carpeta): crear y editar documentos web, convertir un documento web en una plantilla para uso personal, crear y editar dibujos SVG y PNG, grabar archivos de audio en formato WAV, convertir texto en audio en formato MP3, realizar capturas a través de su webcam, enviar documentos, crear carpetas, mover carpetas y archivos, borrar carpetas y archivos, y descargar copias de seguridad de su carpeta.
+
+Por otra parte, la herramienta documentos se sincroniza con el gestor de archivos del editor web, así que los cambios en la gestión de los documentos realizados en una u otra afectarán a ambas. 
+
 Así pues, la carpeta de usuario no sólo es un lugar para depositar los archivos, sino que se convierte en un completo gestor de los documentos que los estudiantes utilizan durante el curso. Además, recuerde que cualquier usuario podrá copiar un archivo, que sea visible, de cualquier carpeta de la herramienta documentos (sea o no la suya) a su portafolios o área personal de documentos de la red social, donde estará disponible para que lo pueda usar en otros cursos.";
 $HelpFolderChat = "INFORMACIÓN SOLO VISIBLE POR EL PROFESORADO:\nEsta carpeta contiene todas las sesiones que se han realizado en el chat. Aunque muchas veces las sesiones en el chat pueden ser triviales, en otras pueden ser dignas de ser tratadas como un documento más de trabajo. Para ello, sin cambiar la visibilidad de esta carpeta, haga visible el archivo y enlácelo donde considere oportuno. No se recomienda hacer visible esta carpeta.";
 $HelpFolderCertificates = "INFORMACIÓN SOLO VISIBLE POR EL PROFESORADO:\nEsta carpeta contiene los distintos modelos de certificados que se han creado para la herramienta Evaluaciones. No se recomienda hacer visible esta carpeta.";
@@ -6188,7 +6188,7 @@ $UploadCorrections = "Subir correcciones";
 $Text2AudioTitle = "Activar servicios de conversión de texto en audio";
 $Text2AudioComment = "Herramienta on-line para convertir texto en voz. Utiliza tecnología y sistemas de síntesis del habla para ofrecer recursos de voz.";
 $ShowUsersFoldersTitle = "Mostrar las carpetas de los usuarios en la herramienta documentos";
-$ShowUsersFoldersComment = "
+$ShowUsersFoldersComment = "
 Esta opción le permitirá mostrar u ocultar a los profesores las carpetas que el sistema genera para cada usuario que visita la herramienta documentos o envía un archivo a través del editor web. Si muestra estas carpetas a los profesores, éstos podrán hacerlas visibles o no a los estudiantes y permitirán a cada estudiante tener un lugar específico en el curso donde, no sólo almacenar documentos, sino donde también podrán crear y modificar páginas web y poder exportarlas a pdf, realizar dibujos, realizar plantillas web personales, enviar archivos, así como crear, mover y eliminar subdirectorios y archivos, y sacar copias de seguridad de sus carpetas. Cada usuario del curso dispondrá de un completo gestor de documentos. Además, recuerde que cualquier usuario podrá copiar un archivo, que sea visible, de cualquier carpeta de la herramienta documentos (sea o no la suya) a su portafolios o área personal de documentos de la red social, donde estará disponible para que lo pueda usar en otros cursos.";
 $ShowDefaultFoldersTitle = "Mostrar en la herramienta documentos las carpetas que contienen los recursos multimedia suministrados por defecto.";
 $ShowDefaultFoldersComment = "Las carpetas de archivos multimedia suministradas por defecto contienen archivos de libre distribución organizados en las categorías de video, audio, imagen y animaciones flash que para utilizar en sus cursos. Aunque las oculte en la herramienta documentos, podrá seguir usándolas en el editor web de la plataforma.";
@@ -6224,8 +6224,8 @@ $Pediaphon = "Usar los servicios de audio de Pediaphon";
 $HelpPediaphon = "Admite textos con varios miles de caracteres, pudiéndose seleccionar varios tipos de voz masculinas y femeninas (según el idioma). Los archivos de audio se generarán y guardarán automáticamente en el directorio de Chamilo en el que Usted actualmente se encuentra.";
 $FirstSelectALanguage = "Primero seleccione un idioma";
 $MoveUserStats = "Mover los resultados de los usuarios desde/hacia una sesión de formación";
-$CompareUserResultsBetweenCoursesAndCoursesInASession = "Esta herramienta avanzada le permite mejorar manualmente el seguimiento de los resultados de los usuarios cuando cambia de un modelo de cursos a un modelo de sesiones de formación. En una mayoría de casos, no necesitará usarla.<br />
-En esta pantalla, puede comparar los resultados que los usuarios tienen en el contexto de un curso y en el contexto del mismo curso dentro de una sesión de formación.<br />
+$CompareUserResultsBetweenCoursesAndCoursesInASession = "Esta herramienta avanzada le permite mejorar manualmente el seguimiento de los resultados de los usuarios cuando cambia de un modelo de cursos a un modelo de sesiones de formación. En una mayoría de casos, no necesitará usarla.<br />
+En esta pantalla, puede comparar los resultados que los usuarios tienen en el contexto de un curso y en el contexto del mismo curso dentro de una sesión de formación.<br />
 Una vez que decidida cuál es el mejor contexto para el seguimiento (resultados de ejercicios y seguimiento de lecciones), podrá moverlo de un curso a una sesión.";
 $PDFExportWatermarkEnableTitle = "Marcas de agua en las exportaciones a PDF";
 $PDFExportWatermarkEnableComment = "Si activa esta opción podrá cargar una imagen o un texto que serán automáticamente añadidos como marca de agua en los documentos resultantes de todas las exportaciones a PDF que realice el sistema.";
@@ -6360,8 +6360,8 @@ $MailNotifyInvitation = "Notificar las invitaciones por correo electrónico";
 $MailNotifyMessage = "Notificar los mensajes por correo electrónico";
 $MailNotifyGroupMessage = "Notificar en los grupos los mensajes por correo electrónico";
 $SearchEnabledTitle = "Búsqueda a texto completo";
-$SearchEnabledComment = "Esta funcionalidad permite la indexación de la mayoría de los documentos subidos a su portal, con lo que permite la búsqueda para los usuarios.<br />
-Esta funcionalidad no indexa los documentos que ya fueron subidos, por lo que es importante (si se quiere) activarla al comienzo de su implementación.<br />
+$SearchEnabledComment = "Esta funcionalidad permite la indexación de la mayoría de los documentos subidos a su portal, con lo que permite la búsqueda para los usuarios.<br />
+Esta funcionalidad no indexa los documentos que ya fueron subidos, por lo que es importante (si se quiere) activarla al comienzo de su implementación.<br />
 Una vez activada, una caja de búsqueda aparecerá en la lista de cursos de cada usuario. Buscar un término específico suministra una lista de documentos, ejercicios o temas de foro correspondientes, filtrados dependiendo de su disponibilidad para el usuario.";
 $SpecificSearchFieldsAvailable = "Campos de búsqueda personalizados disponibles";
 $XapianModuleInstalled = "Módulo Xapian instalado";
@@ -6659,7 +6659,7 @@ $DisableEndDate = "Deshabilitar fecha final";
 $ForumCategories = "Categorías de foro";
 $Copy = "Copiar";
 $ArchiveDirCleanup = "Limpieza de caché y archivos temporales";
-$ArchiveDirCleanupDescr = "Chamilo guarda una copia de los archivos temporales que genera (para los backups, las exportaciones, las copias, etc) dentro del directorio app/cache/. Pasado un tiempo, todo esto puede llegar a ocupar bastante espacio en el disco duro. Si hace clic en el siguiente botón ejecutará una limpieza manual de este directorio. Esta operación debería ser realizada regularmente mediante la utilidad cron de Linux, pero si esto no es posible en su entorno puede utilizar esta página para eliminar todos los archivos temporales cada cierto tiempo.
+$ArchiveDirCleanupDescr = "Chamilo guarda una copia de los archivos temporales que genera (para los backups, las exportaciones, las copias, etc) dentro del directorio app/cache/. Pasado un tiempo, todo esto puede llegar a ocupar bastante espacio en el disco duro. Si hace clic en el siguiente botón ejecutará una limpieza manual de este directorio. Esta operación debería ser realizada regularmente mediante la utilidad cron de Linux, pero si esto no es posible en su entorno puede utilizar esta página para eliminar todos los archivos temporales cada cierto tiempo.
 Esta opción limpia el caché de temas también.";
 $ArchiveDirCleanupProceedButton = "Ejecutar la limpieza";
 $ArchiveDirCleanupSucceeded = "El contenido del directorio app/cache/ ha sido eliminado.";
@@ -7008,69 +7008,69 @@ $ResourceLockedByGradebook = "Esta opción no está disponible porque la activid
 $GradebookLockedAlert = "Esta evaluación ha sido bloqueada y no puede ser desbloqueada. Si necesita realmente desbloquearla, por favor contacte el administrador de la plataforma, explicando su razón (sino podría ser considerado como un intento de fraude).";
 $GradebookEnableLockingTitle = "Activar bloqueo de Evaluaciones por los profesores";
 $GradebookEnableLockingComment = "Una vez activada, esta opción permitirá a los profesores bloquear cualquier evaluación dentro de su curso. Esto prohibirá al profesor cualquier modificación posterior de los resultados de sus alumnos en los recursos usados para esta evaluación: exámenes, lecciones, tareas, etc. El único rol autorizado a desbloquear una evaluación es el administrador. El profesor estará informado de esta posibilidad al intentar desbloquear la evaluación. El bloqueo como el desbloqueo estarán guardados en el registro de actividades importantes del sistema.";
-$LdapDescriptionComment = "<div class=\"alert alert-info\">
-        <ul>
-            <li>Autentificación LDAP: <br>
-                Véase I. a continuación para configurar LDAP<br>
-                Véase II. a continuación para activar la autentificación LDAP
-            </li>
-            <li>Actualizar los atributos de usuario, con los datos de LDAP, después de la autentificación CAS (véase <a href=\"settings.php?category=CAS\">Configuración CAS</a>): <br>
-                Véase I. a continuación para configurar LDAP<br>
-                Para gestionar la autentificación de usuarios por CAS, no se requere la activación de LDAP.
-            </li>
-        </ul>
-    </div>
-    <h4>I. Configuración de LDAP</h4>
-    <h5>Editar el archivo app/config/auth.conf.php </h5>
-    <p>-&gt; Editar los valores del array <code>\$extldap_config</code></p>
-    <ul>
-        <li>base domain string (p. ej.: 'base_dn' =&gt; 'DC=cblue,DC=be')</li>
-        <li>admin distinguished name (p. ej.: 'admin_dn' =&gt;'CN=admin,dc=cblue,dc=be')</li>
-        <li>admin password (p. ej.: 'admin_password' =&gt; '123456') </li>
-        <li>ldap host (p. ej.: 'host' =&gt; array('1.2.3.4', '2.3.4.5', '3.4.5.6'))</li>
-        <li>filter (p. ej.: 'filter' =&gt; '') </li>
-        <li>port (p. ej.: 'port' =&gt; 389) </li>
-        <li>protocol version (2 or 3) (p. ej.: 'protocol_version' =&gt; 3)</li>
-        <li>user_search (p. ej.: 'user_search' =&gt; 'sAMAccountName=%username%') </li>
-        <li>encoding (p. ej.: 'encoding' =&gt; 'UTF-8')</li>
-        <li>update_userinfo (p. ej.: 'update_userinfo' =&gt; true) </li>
-    </ul>
-    <p>-&gt; Para actualizar las correspondencias entre los atributos de usuario y LDAP, editar el array <code>\$extldap_user_correspondance</code></p>
-    <p>Los valores del Array son &lt;chamilo_field&gt; =&gt; &gt;ldap_field&gt;</p><p>
-    </p>
-    <h4>II. Activar la atenticación LDAP</h4>
-    <h5>Editar el archivo app/config/configuration.php </h5>
-    <p>-&gt; Descomentar las líneas:</p>
-    <ul>
-        <li>
-    \$extAuthSource[\"extldap\"][\"login\"] = \$_configuration['root_sys'].\"main/auth/external_login/login.ldap.php\";</li>
-        <li>\$extAuthSource[\"extldap\"][\"newUser\"] = \$_configuration['root_sys'].\"main/auth/external_login/newUser.ldap.php\";</li>
-    </ul>
-    <p>N.B.: Los usuarios de LDAP usan los mismos campos que los usuarios de la plataforma para iniciar sesión.<br>
+$LdapDescriptionComment = "<div class=\"alert alert-info\">
+        <ul>
+            <li>Autentificación LDAP: <br>
+                Véase I. a continuación para configurar LDAP<br>
+                Véase II. a continuación para activar la autentificación LDAP
+            </li>
+            <li>Actualizar los atributos de usuario, con los datos de LDAP, después de la autentificación CAS (véase <a href=\"settings.php?category=CAS\">Configuración CAS</a>): <br>
+                Véase I. a continuación para configurar LDAP<br>
+                Para gestionar la autentificación de usuarios por CAS, no se requere la activación de LDAP.
+            </li>
+        </ul>
+    </div>
+    <h4>I. Configuración de LDAP</h4>
+    <h5>Editar el archivo app/config/auth.conf.php </h5>
+    <p>-&gt; Editar los valores del array <code>\$extldap_config</code></p>
+    <ul>
+        <li>base domain string (p. ej.: 'base_dn' =&gt; 'DC=cblue,DC=be')</li>
+        <li>admin distinguished name (p. ej.: 'admin_dn' =&gt;'CN=admin,dc=cblue,dc=be')</li>
+        <li>admin password (p. ej.: 'admin_password' =&gt; '123456') </li>
+        <li>ldap host (p. ej.: 'host' =&gt; array('1.2.3.4', '2.3.4.5', '3.4.5.6'))</li>
+        <li>filter (p. ej.: 'filter' =&gt; '') </li>
+        <li>port (p. ej.: 'port' =&gt; 389) </li>
+        <li>protocol version (2 or 3) (p. ej.: 'protocol_version' =&gt; 3)</li>
+        <li>user_search (p. ej.: 'user_search' =&gt; 'sAMAccountName=%username%') </li>
+        <li>encoding (p. ej.: 'encoding' =&gt; 'UTF-8')</li>
+        <li>update_userinfo (p. ej.: 'update_userinfo' =&gt; true) </li>
+    </ul>
+    <p>-&gt; Para actualizar las correspondencias entre los atributos de usuario y LDAP, editar el array <code>\$extldap_user_correspondance</code></p>
+    <p>Los valores del Array son &lt;chamilo_field&gt; =&gt; &gt;ldap_field&gt;</p><p>
+    </p>
+    <h4>II. Activar la atenticación LDAP</h4>
+    <h5>Editar el archivo app/config/configuration.php </h5>
+    <p>-&gt; Descomentar las líneas:</p>
+    <ul>
+        <li>
+    \$extAuthSource[\"extldap\"][\"login\"] = \$_configuration['root_sys'].\"main/auth/external_login/login.ldap.php\";</li>
+        <li>\$extAuthSource[\"extldap\"][\"newUser\"] = \$_configuration['root_sys'].\"main/auth/external_login/newUser.ldap.php\";</li>
+    </ul>
+    <p>N.B.: Los usuarios de LDAP usan los mismos campos que los usuarios de la plataforma para iniciar sesión.<br>
     N.B.: La activación LDAP agrega un menú \"Autentificación externa\" [LDAP] en las páginas de \"agregar o modificar\" usuarios.</p>";
 $ShibbolethMainActivateTitle = "<h3>Autenticación Shibboleth</h3>";
-$ShibbolethMainActivateComment = "En primer lugar, tiene que configurar Shibboleth para su servidor web.
-
-Para configurarlo en Chamilo:
-
-editar el archivo  <strong> main/auth/shibboleth/config/aai.class.php</strong>
-
-Modificar valores de \$result con el nombre de los atributos de Shibboleth
-
-    \$result->unique_id = 'mail';
-    \$result->firstname = 'cn';
-    \$result->lastname = 'uid';
-    \$result->email = 'mail';
-    \$result->language = '-';
-    \$result->gender = '-';
-    \$result->address = '-';
-    \$result->staff_category = '-';
-    \$result->home_organization_type = '-';
-    \$result->home_organization = '-';
-    \$result->affiliation = '-';
-    \$result->persistent_id = '-';
-    ...
-
+$ShibbolethMainActivateComment = "En primer lugar, tiene que configurar Shibboleth para su servidor web.
+
+Para configurarlo en Chamilo:
+
+editar el archivo  <strong> main/auth/shibboleth/config/aai.class.php</strong>
+
+Modificar valores de \$result con el nombre de los atributos de Shibboleth
+
+    \$result->unique_id = 'mail';
+    \$result->firstname = 'cn';
+    \$result->lastname = 'uid';
+    \$result->email = 'mail';
+    \$result->language = '-';
+    \$result->gender = '-';
+    \$result->address = '-';
+    \$result->staff_category = '-';
+    \$result->home_organization_type = '-';
+    \$result->home_organization = '-';
+    \$result->affiliation = '-';
+    \$result->persistent_id = '-';
+    ...
+
 Ir a Plug-in para añadir el botón 'Shibboleth Login' en su campus de Chamilo.";
 $LdapDescriptionTitle = "<h3>Autentificacion LDAP</h3>";
 $FacebookMainActivateTitle = "Autenticación con Facebook";
@@ -7499,8 +7499,8 @@ $AreYouSureToSubscribe = "¿Está seguro de suscribirse?";
 $CheckYourEmailAndFollowInstructions = "Revise su correo electrónico y siga las instrucciones.";
 $LinkExpired = "Enlace expirado, por favor vuelva a iniciar el proceso.";
 $ResetPasswordInstructions = "Instrucciones para el procedimiento de cambio de contraseña";
-$ResetPasswordCommentWithUrl = "Ha recibido este mensaje porque Usted (o alguien que intenta hacerse pasar por Ud) ha pedido que su contraseña sea generada nuevamente. Para configurar una nueva contraseña, necesita activarla. Para ello, por favor de clic en el siguiente enlace: %s.
-
+$ResetPasswordCommentWithUrl = "Ha recibido este mensaje porque Usted (o alguien que intenta hacerse pasar por Ud) ha pedido que su contraseña sea generada nuevamente. Para configurar una nueva contraseña, necesita activarla. Para ello, por favor de clic en el siguiente enlace: %s.
+
 Si no ha pedido un cambio de contraseña, puede ignorar este mensaje. No obstante, si vuelve a recibirlo repetidamente, por favor comuníquese con el administrador de su portal.";
 $CronRemindCourseExpirationActivateTitle = "Cron de Recordatorio de Expiración de Curso";
 $CronRemindCourseExpirationActivateComment = "Habilitar el cron de envío de recordatorio de expiración de cursos";
@@ -7509,14 +7509,14 @@ $CronRemindCourseExpirationFrequencyComment = "Número de días antes de la expi
 $CronCourseFinishedActivateText = "Cron de finalización de curso";
 $CronCourseFinishedActivateComment = "Activar el cron de finalización de curso";
 $MailCronCourseFinishedSubject = "Fin del curso %s";
-$MailCronCourseFinishedBody = "Estimado %s,
-
-Gracias por participar en el curso %s. Esperamos que hayas aprendido y disfrutado del curso.
-
-Puedes ver tu rendimiento a lo largo del curso en la sección Mi Avance.
-
-Saludos cordiales,
-
+$MailCronCourseFinishedBody = "Estimado %s,
+
+Gracias por participar en el curso %s. Esperamos que hayas aprendido y disfrutado del curso.
+
+Puedes ver tu rendimiento a lo largo del curso en la sección Mi Avance.
+
+Saludos cordiales,
+
 El equipo de %s";
 $GenerateDefaultContent = "Generar contenido por defecto";
 $ThanksForYourSubscription = "¡Gracias por su suscripción!";
@@ -7738,8 +7738,8 @@ $LegalAccepted = "Acuerdo legal aceptado";
 $LoadTermConditionsSectionTitle = "Cargar la sección de términos y condiciones";
 $LoadTermConditionsSectionDescription = "El acuerdo legal aparecerá durante el login o cuando entre a un curso.";
 $SendTermsSubject = "Su contrato de aprendizaje está listo para firmar.";
-$SendTermsDescriptionToUrlX = "Hola,
-
+$SendTermsDescriptionToUrlX = "Hola,
+
 Su tutor le ha enviado s contrato de aprendizaje. Puede ir a firmarlo siguiendo esta URL: %s";
 $UserXSignedTheAgreement = "El usuario %s ha firmado el acuerdo.";
 $UserXSignedTheAgreementTheY = "El usuario %s ha firmado el acuerdo el %s.";
@@ -7890,7 +7890,7 @@ $EditCourseCategoryToURL = "Editar categorías de cursos de una URL";
 $VisibleToSelf = "Visible para si mismo";
 $VisibleToOthers = "Visible por otros";
 $UpgradeVersion = "Actualizar la versión de Chamilo LMS";
-$CRSTablesIntro = "El script de instalación ha detectado tablas procedentes de versiones anteriores que podrían causar problemas durante el proceso de actualización.
+$CRSTablesIntro = "El script de instalación ha detectado tablas procedentes de versiones anteriores que podrían causar problemas durante el proceso de actualización.
 Por favor, haga clic en el botón de abajo para eliminarlas. Recomendamos seriamente hacer una copia de seguridad completa de estas antes de confirmar este último paso de instalación.";
 $Removing = "Removiendo";
 $CheckForCRSTables = "Comprobar si hay tablas de veriones anteriores";
@@ -8036,12 +8036,12 @@ $SpecificDate = "Envio en fecha concreta";
 $BaseDate = "Envío en base a fecha de inicio/fin de la sesión";
 $AfterOrBefore = "Antes o después";
 $Before = "Antes de";
-$ScheduleAnnouncementDescription = "Este formulario permite programar anuncios/avisos para que sean enviados de manera automática a los alumnos que están realizando un curso en una sesión.
-
-Existe dos tipos de anuncios que se pueden enviar:
-
-Envío en una fecha concreta: En este caso se selecciona un día concreto para hacer el envío.
-
+$ScheduleAnnouncementDescription = "Este formulario permite programar anuncios/avisos para que sean enviados de manera automática a los alumnos que están realizando un curso en una sesión.
+
+Existe dos tipos de anuncios que se pueden enviar:
+
+Envío en una fecha concreta: En este caso se selecciona un día concreto para hacer el envío.
+
 Envío en base a la fecha de inicio/finalización de la sesión: en este caso se ha de indicar el número de días que han de pasar antes de enviar el anuncio. Y esos días pueden estar asociados a antes o después de la fecha de inicio/finalización. Por ejemplo: 3 días después de fecha de inicio.";
 $MandatorySurveyNoAnswered = "Usted tiene pendiente una encuesta obligatoria. Para ingresar al curso, primero deberá completarla";
 $ShowPreviousButton = "Mostrar el botón 'anterior'";
@@ -8081,4 +8081,20 @@ $OralExpressionHelpText = "Cuando lanza la grabación, puede pausar cuando lo de
 $BestScoreInLearningPath = "Mejor nota en las lecciones";
 $BestScoreNotInLearningPath = "Mejor nota fuera de lecciones";
 $StudentXHadEnoughSkillsToGetSkillXToAssignClickHereX = "El alumno %s tiene suficiente competencias secundarias para obtener la competencia %s. Para asignar esta competencia, es posible ir aquí : %s";
+
+// Cloud files in documents strings
+$enable_add_file_link_title = 'Permitir añadir enlaces a ficheros en la nube';
+$enable_add_file_link_comment = 'Herramienta para añadir enlaces a ficheros situados en sitios de almacenamiento externo (Dropbox, Google Drive, etc...) en Documentos.';
+$UrlAlreadyExists = "Ya existe una URL igual en este directorio.";
+$ErrorAddCloudLink = "Se produjo un error añadiendo el enlace al fichero en la nube.";
+$AddCloudLink = "Añadir enlace a un fichero en la nube";
+$CloudLinkDeleted = "Enlace a fichero en la nube eliminado.";
+$CloudLinkMoved = "Enlace a fichero en la nube movido.";
+$CloudLinkDeleteError = "Error al eliminar el enlace al fichero en la nube.";
+$CloudLinkAdded = "Enlace a fichero en la nube añadido.";
+$PleaseEnterCloudLinkName = "Por favor, introduzca un nombre para el enlace al fichero en la nube.";
+$MalformedUrl = "Formato del campo URL no válido. Ejemplo del formato esperado: http://dropbox.com/sh/loremipsum/loremipsum?dl=0";
+$CloudLinkModified = "Enlace a fichero en la nube modificado.";
+$NotValidDomain = "Dominio no válido. Debe de ser uno de los siguientes:";
+$ValidDomainsList = "Lista de dominios válidos";
 ?>