This commit is contained in:
Xes
2025-08-14 22:37:50 +02:00
parent fb6d5d5926
commit 3641e93527
9156 changed files with 1813532 additions and 0 deletions

1
main/inc/lib/.htaccess Normal file
View File

@@ -0,0 +1 @@
Options -Indexes

View File

@@ -0,0 +1,441 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Announcement Email.
*
* @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
* @author Julio Montoya <gugli100@gmail.com> Adding session support
*/
class AnnouncementEmail
{
public $session_id = null;
public $logger;
protected $course = null;
protected $announcement = null;
/**
* @param array $courseInfo
* @param int $sessionId
* @param int $announcementId
* @param \Monolog\Logger $logger
*/
public function __construct($courseInfo, $sessionId, $announcementId, $logger = null)
{
if (empty($courseInfo)) {
$courseInfo = api_get_course_info();
}
$this->course = $courseInfo;
$this->session_id = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
if (is_numeric($announcementId)) {
$this->announcement = AnnouncementManager::get_by_id($courseInfo['real_id'], $announcementId);
}
$this->logger = $logger;
}
/**
* Course info.
*
* @param string $key
*
* @return string|null
*/
public function course($key = '')
{
$result = $key ? $this->course[$key] : $this->course;
$result = $key == 'id' ? intval($result) : $result;
return $result;
}
/**
* Announcement info.
*
* @param string $key
*
* @return array
*/
public function announcement($key = '')
{
$result = $key ? $this->announcement[$key] : $this->announcement;
$result = $key == 'id' ? intval($result) : $result;
return $result;
}
/**
* Returns either all course users or all session users depending on whether
* session is turned on or not.
*
* @return array
*/
public function all_users()
{
$courseCode = $this->course('code');
if (empty($this->session_id)) {
$group_id = api_get_group_id();
if (empty($group_id)) {
$userList = CourseManager::get_user_list_from_course_code($courseCode);
} else {
$userList = GroupManager::get_users($group_id);
$new_user_list = [];
foreach ($userList as $user) {
$new_user_list[] = ['user_id' => $user];
}
$userList = $new_user_list;
}
} else {
$userList = CourseManager::get_user_list_from_course_code(
$courseCode,
$this->session_id
);
}
return $userList;
}
/**
* Returns users and groups an announcement item has been sent to.
*
* @return array Array of users and groups to whom the element has been sent
*/
public function sent_to_info()
{
$result = [];
$result['groups'] = [];
$result['users'] = [];
$table = Database::get_course_table(TABLE_ITEM_PROPERTY);
$tool = TOOL_ANNOUNCEMENT;
$id = $this->announcement('id');
$course_id = $this->course('real_id');
$sessionCondition = api_get_session_condition($this->session_id);
$sql = "SELECT to_group_id, to_user_id
FROM $table
WHERE
c_id = $course_id AND
tool = '$tool' AND
ref = $id
$sessionCondition";
$rs = Database::query($sql);
while ($row = Database::fetch_array($rs, 'ASSOC')) {
// if to_user_id <> 0 then it is sent to a specific user
$user_id = $row['to_user_id'];
if (!empty($user_id)) {
$result['users'][] = (int) $user_id;
// If user is set then skip the group
continue;
}
// if to_group_id is null then it is sent to a specific user
// if to_group_id = 0 then it is sent to everybody
$group_id = $row['to_group_id'];
if (!empty($group_id)) {
$result['groups'][] = (int) $group_id;
}
}
return $result;
}
/**
* Returns the list of user info to which an announcement was sent.
* This function returns a list of actual users even when recipient
* are groups.
*
* @return array
*/
public function sent_to()
{
$sent_to = $this->sent_to_info();
$users = $sent_to['users'];
$users = $users ? $users : [];
$groups = $sent_to['groups'];
if ($users) {
$users = UserManager::get_user_list_by_ids($users, true);
}
if (!empty($groups)) {
$groupUsers = GroupManager::get_groups_users($groups);
$groupUsers = UserManager::get_user_list_by_ids($groupUsers, true);
if (!empty($groupUsers)) {
$users = array_merge($users, $groupUsers);
}
}
if (empty($users)) {
if (!empty($this->logger)) {
$this->logger->addInfo('User list is empty. No users found. Trying all_users()');
}
$users = self::all_users();
}
// Clean users just in case
$newListUsers = [];
if (!empty($users)) {
foreach ($users as $user) {
$newListUsers[$user['user_id']] = ['user_id' => $user['user_id']];
}
}
return $newListUsers;
}
/**
* Email subject.
*
* @param bool $directMessage
*
* @return string
*/
public function subject($directMessage = false)
{
if ($directMessage) {
$result = $this->announcement('title');
} else {
$result = $this->course('title').' - '.$this->announcement('title');
}
$result = stripslashes($result);
return $result;
}
/**
* Email message.
*
* @param int $receiverUserId
* @param bool $checkUrls It checks access url of user when multiple_access_urls = true
*
* @return string
*/
public function message($receiverUserId, $checkUrls = false)
{
$content = $this->announcement('content');
$session_id = $this->session_id;
$courseCode = $this->course('code');
$courseId = $this->course('real_id');
$content = AnnouncementManager::parseContent(
$receiverUserId,
$content,
$courseCode,
$session_id
);
$accessConfig = [];
$useMultipleUrl = api_get_configuration_value('multiple_access_urls');
if ($useMultipleUrl && $checkUrls) {
$accessUrls = api_get_access_url_from_user($receiverUserId, $courseId);
if (!empty($accessUrls)) {
$accessConfig['multiple_access_urls'] = true;
$accessConfig['access_url'] = (int) $accessUrls[0];
}
}
// Build the link by hand because api_get_cidreq() doesn't accept course params
$course_param = 'cidReq='.$courseCode.'&id_session='.$session_id.'&gidReq='.api_get_group_id();
$course_name = $this->course('title');
$result = "<div>$content</div>";
// Adding attachment
$attachment = $this->attachment();
if (!empty($attachment)) {
$result .= '<br />';
$result .= Display::url(
$attachment['filename'],
api_get_path(WEB_CODE_PATH, $accessConfig).
'announcements/download.php?file='.basename($attachment['path']).'&'.$course_param
);
$result .= '<br />';
}
$result .= '<hr />';
$userInfo = api_get_user_info();
if (!empty($userInfo)) {
if ('true' === api_get_setting('show_email_addresses')) {
$result .= '<a href="mailto:'.$userInfo['mail'].'">'.$userInfo['complete_name'].'</a><br/>';
} else {
$result .= '<p>'.$userInfo['complete_name'].'</p><br/>';
}
}
$result .= '<a href="'.api_get_path(WEB_CODE_PATH, $accessConfig).'announcements/announcements.php?'.$course_param.'">'.
$course_name.'</a><br/>';
return $result;
}
/**
* Returns the one file that can be attached to an announcement.
*
* @return array
*/
public function attachment()
{
$result = [];
$table = Database::get_course_table(TABLE_ANNOUNCEMENT_ATTACHMENT);
$id = $this->announcement('id');
$course_id = $this->course('real_id');
$sql = "SELECT * FROM $table
WHERE c_id = $course_id AND announcement_id = $id ";
$rs = Database::query($sql);
$course_path = $this->course('directory');
while ($row = Database::fetch_array($rs)) {
$path = api_get_path(SYS_COURSE_PATH).$course_path.'/upload/announcements/'.$row['path'];
$filename = $row['filename'];
$result[] = ['path' => $path, 'filename' => $filename];
}
$result = $result ? reset($result) : [];
return $result;
}
/**
* Send announcement by email to myself.
*/
public function sendAnnouncementEmailToMySelf()
{
$userId = api_get_user_id();
$subject = $this->subject();
$message = $this->message($userId);
MessageManager::send_message_simple(
$userId,
$subject,
$message,
api_get_user_id(),
false,
true
);
}
/**
* Send emails to users.
*
* @param bool $sendToUsersInSession
* @param bool $sendToDrhUsers send a copy of the message to the DRH users
* @param int $senderId related to the main user
* @param bool $directMessage
* @param bool $checkUrls It checks access url of user when multiple_access_urls = true
*
* @return array
*/
public function send($sendToUsersInSession = false, $sendToDrhUsers = false, $senderId = 0, $directMessage = false, $checkUrls = false)
{
$senderId = empty($senderId) ? api_get_user_id() : (int) $senderId;
$subject = $this->subject($directMessage);
$courseId = $this->course('real_id');
// Send email one by one to avoid antispam
$users = $this->sent_to();
$batchSize = 20;
$counter = 1;
$em = Database::getManager();
if (empty($users) && !empty($this->logger)) {
$this->logger->addInfo('User list is empty. No emails will be sent.');
}
$messageSentTo = [];
foreach ($users as $user) {
$message = $this->message($user['user_id'], $checkUrls);
$wasSent = MessageManager::messageWasAlreadySent($senderId, $user['user_id'], $subject, $message);
if ($wasSent === false) {
if (!empty($this->logger)) {
$this->logger->addInfo(
'Announcement: #'.$this->announcement('id').'. Send email to user: #'.$user['user_id']
);
}
$messageSentTo[] = $user['user_id'];
MessageManager::send_message_simple(
$user['user_id'],
$subject,
$message,
$senderId,
$sendToDrhUsers,
true,
[],
true,
[],
$checkUrls,
$courseId
);
} else {
if (!empty($this->logger)) {
$this->logger->addInfo(
'Message "'.$subject.'" was already sent. Announcement: #'.$this->announcement('id').'.
User: #'.$user['user_id']
);
}
}
if (($counter % $batchSize) === 0) {
$em->flush();
$em->clear();
}
$counter++;
}
if ($sendToUsersInSession) {
$sessionList = SessionManager::get_session_by_course($this->course['real_id']);
if (!empty($sessionList)) {
foreach ($sessionList as $sessionInfo) {
$sessionId = $sessionInfo['id'];
$message = $this->message(null);
$userList = CourseManager::get_user_list_from_course_code(
$this->course['code'],
$sessionId
);
if (!empty($userList)) {
foreach ($userList as $user) {
$messageSentTo[] = $user['user_id'];
MessageManager::send_message_simple(
$user['user_id'],
$subject,
$message,
$senderId,
false,
true,
[],
true,
[],
$checkUrls,
$courseId
);
}
}
}
}
}
$this->logMailSent();
$messageSentTo = array_unique($messageSentTo);
return $messageSentTo;
}
/**
* Store that emails where sent.
*/
public function logMailSent()
{
$id = $this->announcement('id');
$courseId = $this->course('real_id');
$table = Database::get_course_table(TABLE_ANNOUNCEMENT);
$sql = "UPDATE $table SET
email_sent = 1
WHERE
c_id = $courseId AND
id = $id AND
session_id = {$this->session_id}
";
Database::query($sql);
}
}

File diff suppressed because it is too large Load Diff

402
main/inc/lib/Compilatio.php Normal file
View File

@@ -0,0 +1,402 @@
<?php
/* For licensing terms, see /license.txt */
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Utils;
/**
* Build the communication with the SOAP server Compilatio.net
* call several methods for the file management in Compilatio.net.
*
* @version: 2.0
*/
class Compilatio
{
/** Identification key for the Compilatio account*/
public $key;
/**
* @var Client
*/
public $client;
/**
* @var string
*/
protected $baseUrl;
/** Webservice connection*/
private $maxFileSize;
private $proxyHost;
private $proxyPort;
/**
* Compilatio constructor.
*
* @throws Exception
*/
public function __construct()
{
$settings = $this->getSettings();
$this->maxFileSize = $settings['max_filesize'];
$this->key = $settings['key'];
$this->baseUrl = $settings['api_url'];
if (!empty($settings['proxy_host'])) {
$this->proxyHost = $settings['proxy_host'];
$this->proxyPort = $settings['proxy_port'];
}
$clientConfig = [
'base_uri' => api_remove_trailing_slash($this->baseUrl).'/',
'headers' => [
'X-Auth-Token' => $this->key,
'Accept' => 'application/json',
],
];
if ($this->proxyPort) {
$clientConfig['proxy'] = $this->proxyHost.':'.$this->proxyPort;
}
$this->client = new Client($clientConfig);
}
/**
* @return string
*/
public function getKey()
{
return $this->key;
}
/**
* @param mixed $key
*
* @return Compilatio
*/
public function setKey($key)
{
$this->key = $key;
return $this;
}
/**
* @return mixed
*/
public function getMaxFileSize()
{
return $this->maxFileSize;
}
/**
* @return mixed
*/
public function getProxyHost()
{
return $this->proxyHost;
}
/**
* @return mixed
*/
public function getProxyPort()
{
return $this->proxyPort;
}
/**
* Method for the file load.
*/
public function sendDoc(
string $title,
string $description,
string $filename,
string $filepath
) {
$user = api_get_user_entity(api_get_user_id());
$postData = [
'folder_id' => '',
'title' => $title,
'filename' => basename($filename),
'indexed' => 'true',
'user_notes' => [
'description' => $description,
],
'authors' => [
[
'firstname' => $user->getFirstname(),
'lastname' => $user->getlastname(),
'email_address' => $user->getEmail(),
],
],
'depositor' => [
'firstname' => $user->getFirstname(),
'lastname' => $user->getlastname(),
'email_address' => $user->getEmail(),
],
];
try {
$responseBody = $this->client
->post(
'private/documents',
[
'multipart' => [
[
'name' => 'postData',
'contents' => json_encode($postData),
],
[
'name' => 'file',
'contents' => Utils::tryFopen($filepath, 'r'),
],
],
]
)
->getBody()
;
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
$body = json_decode((string) $responseBody, true);
return $body['data']['document']['id'];
}
/**
* Method for recover a document's information.
*
* @throws Exception
*/
public function getDoc(string $documentId): array
{
try {
$responseBody = $this->client
->get(
"private/documents/$documentId"
)
->getBody()
;
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
$responseJson = json_decode((string) $responseBody, true);
$dataDocument = $responseJson['data']['document'];
$documentInfo = [
'report_url' => $dataDocument['report_url'],
];
// anasim analyse type is applied for services Magister and Copyright
// anasim-premium analyse type is applied for services Magister+ and Copyright+
$anasim = 'anasim';
if (isset($dataDocument['analyses']['anasim-premium'])) {
$anasim = 'anasim-premium';
if (isset($dataDocument['analyses']['anasim'])) {
if (isset($dataDocument['analyses']['anasim']['creation_launch_date']) && isset($dataDocument['analyses']['anasim-premium']['creation_launch_date'])) {
// if the 2 analyses type exist (which could happen technically but would be exceptional) then we present the most recent one.
if ($dataDocument['analyses']['anasim']['creation_launch_date'] > $dataDocument['analyses']['anasim-premium']['creation_launch_date']) {
$anasim = 'anasim';
}
}
}
}
if (isset($dataDocument['analyses'][$anasim]['state'])) {
$documentInfo['analysis_status'] = $dataDocument['analyses'][$anasim]['state'];
}
if (isset($dataDocument['light_reports'][$anasim]['scores']['global_score_percent'])) {
$documentInfo['report_percent'] = $dataDocument['light_reports'][$anasim]['scores']['global_score_percent'];
}
return $documentInfo;
}
/**
* Method for deleting a Compialtio's account document.
*/
public function deldoc(string $documentId)
{
}
/**
* Method for start the analysis for a document.
*
* @throws Exception
*/
public function startAnalyse(string $compilatioId): string
{
try {
$responseBody = $this->client
->post(
'private/analyses',
[
'json' => [
'doc_id' => $compilatioId,
],
]
)
->getBody()
;
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
$body = json_decode((string) $responseBody, true);
return $body['data']['analysis']['state'];
}
/**
* Method for identify a file extension and the possibility that the document can be managed by Compilatio.
*/
public static function verifiFileType(string $filename): bool
{
$types = ['doc', 'docx', 'rtf', 'xls', 'xlsx', 'ppt', 'pptx', 'odt', 'pdf', 'txt', 'htm', 'html'];
$extension = substr($filename, strrpos($filename, '.') + 1);
$extension = strtolower($extension);
return in_array($extension, $types);
}
/**
* Method for display the PomprseuilmankBar (% de plagiat).
*/
public static function getPomprankBarv31(
int $index,
int $weakThreshold,
int $highThreshold
): string {
$index = round($index);
$class = 'danger';
if ($index < $weakThreshold) {
$class = 'success';
} elseif ($index < $highThreshold) {
$class = 'warning';
}
return Display::bar_progress($index, true, null, $class);
}
/**
* Function for delete a document of the compilatio table if plagiarismTool is Compilatio.
*/
public static function plagiarismDeleteDoc(int $courseId, int $itemId)
{
if (api_get_configuration_value('allow_compilatio_tool') !== false) {
$table = Database::get_course_table(TABLE_PLAGIARISM);
$params = [$courseId, $itemId];
Database::delete($table, ['c_id = ? AND document_id = ?' => $params]);
}
}
public function saveDocument(int $courseId, int $documentId, string $compilatioId)
{
$table = Database::get_course_table(TABLE_PLAGIARISM);
$params = [
'c_id' => $courseId,
'document_id' => $documentId,
'compilatio_id' => $compilatioId,
];
Database::insert($table, $params);
}
public function getCompilatioId(int $documentId, int $courseId): ?string
{
$table = Database::get_course_table(TABLE_PLAGIARISM);
$sql = "SELECT compilatio_id FROM $table
WHERE document_id = $documentId AND c_id= $courseId";
$result = Database::query($sql);
$result = Database::fetch_object($result);
return $result ? (string) $result->compilatio_id : null;
}
public function giveWorkIdState(int $workId): string
{
$courseId = api_get_course_int_id();
$compilatioId = $this->getCompilatioId($workId, $courseId);
$actionCompilatio = '';
// if the compilatio's hash is not a valide hash md5,
// we return à specific status (cf : IsInCompilatio() )
// Not used since implementation of RestAPI but there if needed later
//$actionCompilatio = get_lang('CompilatioDocumentTextNotImage').'<br/>'.
// get_lang('CompilatioDocumentNotCorrupt');
$status = '';
if (!empty($compilatioId)) {
// if compilatio_id is a hash md5, we call the function of the compilatio's
// webservice who return the document's status
$soapRes = $this->getDoc($compilatioId);
$status = $soapRes['analysis_status'] ?? '';
$spinnerIcon = Display::returnFontAwesomeIcon('spinner', null, true, 'fa-spin');
switch ($status) {
case 'finished':
$actionCompilatio .= self::getPomprankBarv31($soapRes['report_percent'], 10, 35)
.PHP_EOL
.Display::url(
get_lang('CompilatioAnalysis'),
$soapRes['report_url'],
['class' => 'btn btn-primary btn-xs', 'target' => '_blank']
);
break;
case 'running':
$actionCompilatio .= "<div style='font-weight:bold;text-align:left'>"
.get_lang('CompilatioAnalysisInProgress')
."</div>";
$actionCompilatio .= "<div style='font-size:80%;font-style:italic;margin-bottom:5px;'>"
.get_lang('CompilatioAnalysisPercentage')
."</div>";
$actionCompilatio .= $spinnerIcon.PHP_EOL.get_lang('CompilatioAnalysisEnding');
break;
case 'waiting':
$actionCompilatio .= $spinnerIcon.PHP_EOL.get_lang('CompilatioWaitingAnalysis');
break;
case 'canceled':
$actionCompilatio .= get_lang('Cancelled');
break;
case 'scheduled':
$actionCompilatio .= $spinnerIcon.PHP_EOL.get_lang('CompilatioAwaitingAnalysis');
break;
}
}
return $workId.'|'.$actionCompilatio.'|'.$status.'|';
}
/**
* @throws Exception
*/
protected function getSettings(): array
{
if (empty(api_get_configuration_value('allow_compilatio_tool')) ||
empty(api_get_configuration_value('compilatio_tool'))
) {
throw new Exception('Compilatio not available');
}
$compilatioTool = api_get_configuration_value('compilatio_tool');
if (!isset($compilatioTool['settings'])) {
throw new Exception('Compilatio config available');
}
$settings = $compilatioTool['settings'];
if (empty($settings['key'])) {
throw new Exception('API key not available');
}
if (empty($settings['api_url'])) {
throw new Exception('Api URL not available');
}
return $settings;
}
}

View File

@@ -0,0 +1,887 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\CourseRelUser;
use Chamilo\CoreBundle\Entity\Session;
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
use Chamilo\CourseBundle\Entity\CChatConnected;
use Chamilo\UserBundle\Entity\User;
use Doctrine\Common\Collections\Criteria;
use Michelf\MarkdownExtra;
/**
* Class CourseChat
* Manage the chat for a course.
*/
class CourseChatUtils
{
private $groupId;
private $courseId;
private $sessionId;
private $userId;
/**
* CourseChat constructor.
*
* @param int $courseId
* @param int $userId
* @param int $sessionId
* @param int $groupId
*/
public function __construct($courseId, $userId, $sessionId = 0, $groupId = 0)
{
$this->courseId = (int) $courseId;
$this->userId = (int) $userId;
$this->sessionId = (int) $sessionId;
$this->groupId = (int) $groupId;
}
/**
* Prepare a message. Clean and insert emojis.
*
* @param string $message The message to prepare
*
* @return string
*/
public static function prepareMessage($message)
{
if (empty($message)) {
return '';
}
Emojione\Emojione::$imagePathPNG = api_get_path(WEB_LIBRARY_PATH).'javascript/emojione/png/';
Emojione\Emojione::$ascii = true;
$message = trim($message);
$message = nl2br($message);
// Security XSS
$message = Security::remove_XSS($message);
//search urls
$message = preg_replace(
'@((https?://)?([-\w]+\.[-\w\.]+)+\w(:\d+)?(/([-\w/_\.]*(\?\S+)?)?)*)@',
'<a href="$1" target="_blank">$1</a>',
$message
);
// add "http://" if not set
$message = preg_replace(
'/<a\s[^>]*href\s*=\s*"((?!https?:\/\/)[^"]*)"[^>]*>/i',
'<a href="http://$1" target="_blank">',
$message
);
// Parsing emojis
$message = Emojione\Emojione::toImage($message);
// Parsing text to understand markdown (code highlight)
$message = MarkdownExtra::defaultTransform($message);
return $message;
}
/**
* Save a chat message in a HTML file.
*
* @param string $message
* @param int $friendId
*
* @return bool
*/
public function saveMessage($message, $friendId = 0)
{
if (empty($message)) {
return false;
}
$friendId = (int) $friendId;
$userInfo = api_get_user_info($this->userId);
$courseInfo = api_get_course_info_by_id($this->courseId);
$isMaster = api_is_course_admin();
$document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
$basepath_chat = '/chat_files';
$group_info = [];
if ($this->groupId) {
$group_info = GroupManager::get_group_properties($this->groupId);
$basepath_chat = $group_info['directory'].'/chat_files';
}
$chat_path = $document_path.$basepath_chat.'/';
if (!is_dir($chat_path)) {
if (is_file($chat_path)) {
@unlink($chat_path);
}
}
$date_now = date('Y-m-d');
$timeNow = date('d/m/y H:i:s');
$basename_chat = 'messages-'.$date_now;
if ($this->groupId && !$friendId) {
$basename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId;
} elseif ($this->sessionId && !$friendId) {
$basename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId;
} elseif ($friendId) {
if ($this->userId < $friendId) {
$basename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId;
} else {
$basename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId;
}
}
$message = self::prepareMessage($message);
$fileTitle = $basename_chat.'.log.html';
$filePath = $basepath_chat.'/'.$fileTitle;
$absoluteFilePath = $chat_path.$fileTitle;
if (!file_exists($absoluteFilePath)) {
$doc_id = add_document(
$courseInfo,
$filePath,
'file',
0,
$fileTitle,
null,
0,
true,
0,
0,
0,
false
);
$documentLogTypes = ['DocumentAdded', 'invisible'];
foreach ($documentLogTypes as $logType) {
api_item_property_update(
$courseInfo,
TOOL_DOCUMENT,
$doc_id,
$logType,
$this->userId,
$group_info,
null,
null,
null,
$this->sessionId
);
}
item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
} else {
$doc_id = DocumentManager::get_document_id($courseInfo, $filePath);
}
$fp = fopen($absoluteFilePath, 'a');
$userPhoto = UserManager::getUserPicture($this->userId, USER_IMAGE_SIZE_MEDIUM, true, $userInfo);
if ($isMaster) {
$fileContent = '
<div class="message-teacher">
<div class="content-message">
<div class="chat-message-block-name">'.$userInfo['complete_name'].'</div>
<div class="chat-message-block-content">'.$message.'</div>
<div class="message-date">'.$timeNow.'</div>
</div>
<div class="icon-message"></div>
<img class="chat-image" src="'.$userPhoto.'">
</div>
';
} else {
$fileContent = '
<div class="message-student">
<img class="chat-image" src="'.$userPhoto.'">
<div class="icon-message"></div>
<div class="content-message">
<div class="chat-message-block-name">'.$userInfo['complete_name'].'</div>
<div class="chat-message-block-content">'.$message.'</div>
<div class="message-date">'.$timeNow.'</div>
</div>
</div>
';
}
fputs($fp, $fileContent);
fclose($fp);
$size = filesize($absoluteFilePath);
update_existing_document($courseInfo, $doc_id, $size);
item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
return true;
}
/**
* Disconnect a user from course chats.
*
* @param int $userId
*/
public static function exitChat($userId)
{
$listCourse = CourseManager::get_courses_list_by_user_id($userId);
foreach ($listCourse as $course) {
Database::getManager()
->createQuery('
DELETE FROM ChamiloCourseBundle:CChatConnected ccc
WHERE ccc.cId = :course AND ccc.userId = :user
')
->execute([
'course' => intval($course['real_id']),
'user' => intval($userId),
]);
}
}
/**
* Disconnect users who are more than 5 seconds inactive.
*/
public function disconnectInactiveUsers()
{
$em = Database::getManager();
$extraCondition = "AND ccc.toGroupId = {$this->groupId}";
if (empty($this->groupId)) {
$extraCondition = "AND ccc.sessionId = {$this->sessionId}";
}
$connectedUsers = $em
->createQuery("
SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc
WHERE ccc.cId = :course $extraCondition
")
->setParameter('course', $this->courseId)
->getResult();
$now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
$cd_count_time_seconds = $now->getTimestamp();
/** @var CChatConnected $connection */
foreach ($connectedUsers as $connection) {
$date_count_time_seconds = $connection->getLastConnection()->getTimestamp();
if (strcmp($now->format('Y-m-d'), $connection->getLastConnection()->format('Y-m-d')) !== 0) {
continue;
}
if (($cd_count_time_seconds - $date_count_time_seconds) <= 5) {
continue;
}
$em
->createQuery('
DELETE FROM ChamiloCourseBundle:CChatConnected ccc
WHERE ccc.cId = :course AND ccc.userId = :user AND ccc.toGroupId = :group
')
->execute([
'course' => $this->courseId,
'user' => $connection->getUserId(),
'group' => $this->groupId,
]);
}
}
/**
* Keep registered to a user as connected.
*/
public function keepUserAsConnected()
{
$em = Database::getManager();
$extraCondition = null;
if ($this->groupId) {
$extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
} else {
$extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
}
$currentTime = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
$connection = $em
->createQuery("
SELECT ccc FROM ChamiloCourseBundle:CChatConnected ccc
WHERE ccc.userId = :user AND ccc.cId = :course $extraCondition
")
->setParameters([
'user' => $this->userId,
'course' => $this->courseId,
])
->getOneOrNullResult();
if ($connection) {
$connection->setLastConnection($currentTime);
$em->merge($connection);
$em->flush();
return;
}
$connection = new CChatConnected();
$connection
->setCId($this->courseId)
->setUserId($this->userId)
->setLastConnection($currentTime)
->setSessionId($this->sessionId)
->setToGroupId($this->groupId);
$em->persist($connection);
$em->flush();
}
/**
* Get the emoji allowed on course chat.
*
* @return array
*/
public static function getEmojiStrategy()
{
return require_once api_get_path(SYS_CODE_PATH).'chat/emoji_strategy.php';
}
/**
* Get the emoji list to include in chat.
*
* @return array
*/
public static function getEmojisToInclude()
{
return [
':bowtie:',
':smile:' |
':laughing:',
':blush:',
':smiley:',
':relaxed:',
':smirk:',
':heart_eyes:',
':kissing_heart:',
':kissing_closed_eyes:',
':flushed:',
':relieved:',
':satisfied:',
':grin:',
':wink:',
':stuck_out_tongue_winking_eye:',
':stuck_out_tongue_closed_eyes:',
':grinning:',
':kissing:',
':kissing_smiling_eyes:',
':stuck_out_tongue:',
':sleeping:',
':worried:',
':frowning:',
':anguished:',
':open_mouth:',
':grimacing:',
':confused:',
':hushed:',
':expressionless:',
':unamused:',
':sweat_smile:',
':sweat:',
':disappointed_relieved:',
':weary:',
':pensive:',
':disappointed:',
':confounded:',
':fearful:',
':cold_sweat:',
':persevere:',
':cry:',
':sob:',
':joy:',
':astonished:',
':scream:',
':neckbeard:',
':tired_face:',
':angry:',
':rage:',
':triumph:',
':sleepy:',
':yum:',
':mask:',
':sunglasses:',
':dizzy_face:',
':imp:',
':smiling_imp:',
':neutral_face:',
':no_mouth:',
':innocent:',
':alien:',
];
}
/**
* Get the chat history file name.
*
* @param bool $absolute Optional. Whether get the base or the absolute file path
* @param int $friendId optional
*
* @return string
*/
public function getFileName($absolute = false, $friendId = 0)
{
$date = date('Y-m-d');
$base = 'messages-'.$date.'.log.html';
if ($this->groupId && !$friendId) {
$base = 'messages-'.$date.'_gid-'.$this->groupId.'.log.html';
} elseif ($this->sessionId && !$friendId) {
$base = 'messages-'.$date.'_sid-'.$this->sessionId.'.log.html';
} elseif ($friendId) {
if ($this->userId < $friendId) {
$base = 'messages-'.$date.'_uid-'.$this->userId.'-'.$friendId.'.log.html';
} else {
$base = 'messages-'.$date.'_uid-'.$friendId.'-'.$this->userId.'.log.html';
}
}
if (!$absolute) {
return $base;
}
$courseInfo = api_get_course_info_by_id($this->courseId);
$document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
$chatPath = $document_path.'/chat_files/';
if ($this->groupId) {
$group_info = GroupManager::get_group_properties($this->groupId);
$chatPath = $document_path.$group_info['directory'].'/chat_files/';
}
return $chatPath.$base;
}
/**
* Get the chat history.
*
* @param bool $reset
* @param int $friendId optional
*
* @return string
*/
public function readMessages($reset = false, $friendId = 0)
{
$courseInfo = api_get_course_info_by_id($this->courseId);
$date_now = date('Y-m-d');
$isMaster = (bool) api_is_course_admin();
$basepath_chat = '/chat_files';
$document_path = api_get_path(SYS_COURSE_PATH).$courseInfo['path'].'/document';
$group_info = [];
if ($this->groupId) {
$group_info = GroupManager::get_group_properties($this->groupId);
$basepath_chat = $group_info['directory'].'/chat_files';
}
$chat_path = $document_path.$basepath_chat.'/';
if (!is_dir($chat_path)) {
if (is_file($chat_path)) {
@unlink($chat_path);
}
if (!api_is_anonymous()) {
@mkdir($chat_path, api_get_permissions_for_new_directories());
// Save chat files document for group into item property
if ($this->groupId) {
$doc_id = add_document(
$courseInfo,
$basepath_chat,
'folder',
0,
'chat_files',
null,
0,
true,
0,
0,
0,
false
);
api_item_property_update(
$courseInfo,
TOOL_DOCUMENT,
$doc_id,
'FolderCreated',
null,
$group_info,
null,
null,
null
);
api_item_property_update(
$courseInfo,
TOOL_DOCUMENT,
$doc_id,
'invisible',
null,
$group_info,
null,
null,
null
);
}
}
}
$filename_chat = 'messages-'.$date_now.'.log.html';
if ($this->groupId && !$friendId) {
$filename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId.'.log.html';
} elseif ($this->sessionId && !$friendId) {
$filename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId.'.log.html';
} elseif ($friendId) {
if ($this->userId < $friendId) {
$filename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId.'.log.html';
} else {
$filename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId.'.log.html';
}
}
if (!file_exists($chat_path.$filename_chat)) {
@fclose(fopen($chat_path.$filename_chat, 'w'));
if (!api_is_anonymous()) {
$doc_id = add_document(
$courseInfo,
$basepath_chat.'/'.$filename_chat,
'file',
0,
$filename_chat,
null,
0,
true,
0,
0,
0,
false
);
if ($doc_id) {
api_item_property_update(
$courseInfo,
TOOL_DOCUMENT,
$doc_id,
'DocumentAdded',
$this->userId,
$group_info,
null,
null,
null,
$this->sessionId
);
api_item_property_update(
$courseInfo,
TOOL_DOCUMENT,
$doc_id,
'invisible',
$this->userId,
$group_info,
null,
null,
null,
$this->sessionId
);
item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
}
}
}
$basename_chat = 'messages-'.$date_now;
if ($this->groupId && !$friendId) {
$basename_chat = 'messages-'.$date_now.'_gid-'.$this->groupId;
} elseif ($this->sessionId && !$friendId) {
$basename_chat = 'messages-'.$date_now.'_sid-'.$this->sessionId;
} elseif ($friendId) {
if ($this->userId < $friendId) {
$basename_chat = 'messages-'.$date_now.'_uid-'.$this->userId.'-'.$friendId;
} else {
$basename_chat = 'messages-'.$date_now.'_uid-'.$friendId.'-'.$this->userId;
}
}
if ($reset && $isMaster) {
$i = 1;
while (file_exists($chat_path.$basename_chat.'-'.$i.'.log.html')) {
$i++;
}
@rename($chat_path.$basename_chat.'.log.html', $chat_path.$basename_chat.'-'.$i.'.log.html');
@fclose(fopen($chat_path.$basename_chat.'.log.html', 'w'));
$doc_id = add_document(
$courseInfo,
$basepath_chat.'/'.$basename_chat.'-'.$i.'.log.html',
'file',
filesize($chat_path.$basename_chat.'-'.$i.'.log.html'),
$basename_chat.'-'.$i.'.log.html',
null,
0,
true,
0,
0,
0,
false
);
api_item_property_update(
$courseInfo,
TOOL_DOCUMENT,
$doc_id,
'DocumentAdded',
$this->userId,
$group_info,
null,
null,
null,
$this->sessionId
);
api_item_property_update(
$courseInfo,
TOOL_DOCUMENT,
$doc_id,
'invisible',
$this->userId,
$group_info,
null,
null,
null,
$this->sessionId
);
item_property_update_on_folder($courseInfo, $basepath_chat, $this->userId);
$doc_id = DocumentManager::get_document_id(
$courseInfo,
$basepath_chat.'/'.$basename_chat.'.log.html'
);
update_existing_document($courseInfo, $doc_id, 0);
}
$remove = 0;
$content = [];
if (file_exists($chat_path.$basename_chat.'.log.html')) {
$content = file($chat_path.$basename_chat.'.log.html');
$nbr_lines = sizeof($content);
$remove = $nbr_lines - 100;
}
if ($remove < 0) {
$remove = 0;
}
array_splice($content, 0, $remove);
if (isset($_GET['origin']) && $_GET['origin'] == 'whoisonline') {
//the caller
$content[0] = get_lang('CallSent').'<br />'.$content[0];
}
$history = '<div id="content-chat">';
foreach ($content as $this_line) {
$history .= $this_line;
}
$history .= '</div>';
if ($isMaster || $GLOBALS['is_session_general_coach']) {
$history .= '
<div id="clear-chat">
<button type="button" id="chat-reset" class="btn btn-danger btn-sm">
'.get_lang('ClearList').'
</button>
</div>
';
}
return $history;
}
/**
* Get the number of users connected in chat.
*
* @return int
*/
public function countUsersOnline()
{
$date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
$date->modify('-5 seconds');
if ($this->groupId) {
$extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
} else {
$extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
}
$number = Database::getManager()
->createQuery("
SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc
WHERE ccc.lastConnection > :date AND ccc.cId = :course $extraCondition
")
->setParameters([
'date' => $date,
'course' => $this->courseId,
])
->getSingleScalarResult();
return (int) $number;
}
/**
* Get the users online data.
*
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*
* @return array
*/
public function listUsersOnline()
{
$subscriptions = $this->getUsersSubscriptions();
$usersInfo = [];
if ($this->groupId) {
/** @var User $groupUser */
foreach ($subscriptions as $groupUser) {
$usersInfo[] = $this->formatUser(
$groupUser,
$groupUser->getStatus()
);
}
} else {
/** @var CourseRelUser|SessionRelCourseRelUser $subscription */
foreach ($subscriptions as $subscription) {
$user = $subscription->getUser();
$usersInfo[] = $this->formatUser(
$user,
$this->sessionId ? $user->getStatus() : $subscription->getStatus()
);
}
}
return $usersInfo;
}
/**
* Format the user data to return it in the user list.
*
* @param int $status
*
* @return array
*/
private function formatUser(User $user, $status)
{
return [
'id' => $user->getId(),
'firstname' => $user->getFirstname(),
'lastname' => $user->getLastname(),
'status' => $status,
'image_url' => UserManager::getUserPicture($user->getId(), USER_IMAGE_SIZE_MEDIUM),
'profile_url' => api_get_path(WEB_CODE_PATH).'social/profile.php?u='.$user->getId(),
'complete_name' => UserManager::formatUserFullName($user),
'username' => $user->getUsername(),
'email' => $user->getEmail(),
'isConnected' => $this->userIsConnected($user->getId()),
];
}
/**
* Get the users subscriptions (SessionRelCourseRelUser array or CourseRelUser array) for chat.
*
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
* @throws \Doctrine\ORM\TransactionRequiredException
*
* @return \Doctrine\Common\Collections\ArrayCollection
*/
private function getUsersSubscriptions()
{
$em = Database::getManager();
if ($this->groupId) {
$students = $em
->createQuery(
'SELECT u FROM ChamiloUserBundle:User u
INNER JOIN ChamiloCourseBundle:CGroupRelUser gru
WITH u.id = gru.userId AND gru.cId = :course
WHERE u.id != :user AND gru.groupId = :group
AND u.active = true'
)
->setParameters(['course' => $this->courseId, 'user' => $this->userId, 'group' => $this->groupId])
->getResult();
$tutors = $em
->createQuery(
'SELECT u FROM ChamiloUserBundle:User u
INNER JOIN ChamiloCourseBundle:CGroupRelTutor grt
WITH u.id = grt.userId AND grt.cId = :course
WHERE u.id != :user AND grt.groupId = :group
AND u.active = true'
)
->setParameters(['course' => $this->courseId, 'user' => $this->userId, 'group' => $this->groupId])
->getResult();
return array_merge($tutors, $students);
}
/** @var Course $course */
$course = $em->find('ChamiloCoreBundle:Course', $this->courseId);
if ($this->sessionId) {
/** @var Session $session */
$session = $em->find('ChamiloCoreBundle:Session', $this->sessionId);
$criteria = Criteria::create()->where(Criteria::expr()->eq('course', $course));
$userIsCoach = api_is_course_session_coach($this->userId, $course->getId(), $session->getId());
if (api_get_configuration_value('course_chat_restrict_to_coach')) {
if ($userIsCoach) {
$criteria->andWhere(
Criteria::expr()->eq('status', Session::STUDENT)
);
} else {
$criteria->andWhere(
Criteria::expr()->eq('status', Session::COACH)
);
}
}
$criteria->orderBy(['status' => Criteria::DESC]);
return $session
->getUserCourseSubscriptions()
->matching($criteria)
->filter(function (SessionRelCourseRelUser $sessionRelCourseRelUser) {
return $sessionRelCourseRelUser->getUser()->isActive();
});
}
return $course
->getUsers()
->filter(function (CourseRelUser $courseRelUser) {
return $courseRelUser->getUser()->isActive();
});
}
/**
* Check if a user is connected in course chat.
*
* @param int $userId
*
* @return int
*/
private function userIsConnected($userId)
{
$date = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
$date->modify('-5 seconds');
if ($this->groupId) {
$extraCondition = 'AND ccc.toGroupId = '.intval($this->groupId);
} else {
$extraCondition = 'AND ccc.sessionId = '.intval($this->sessionId);
}
$number = Database::getManager()
->createQuery("
SELECT COUNT(ccc.userId) FROM ChamiloCourseBundle:CChatConnected ccc
WHERE ccc.lastConnection > :date AND ccc.cId = :course AND ccc.userId = :user $extraCondition
")
->setParameters([
'date' => $date,
'course' => $this->courseId,
'user' => $userId,
])
->getSingleScalarResult();
return (int) $number;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,263 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CourseBundle\Entity\CExerciseCategory;
/**
* Class ExtraFieldValue
* Declaration for the ExtraFieldValue class, managing the values in extra
* fields for any data type.
*/
class ExerciseCategoryManager extends Model
{
public $type = '';
public $columns = [
'id',
'name',
'c_id',
'description',
'created_at',
'updated_at',
];
/**
* Formats the necessary elements for the given datatype.
*
* @assert (-1) === false
*/
public function __construct()
{
parent::__construct();
$this->is_course_model = true;
$this->table = Database::get_course_table('exercise_category');
}
/**
* Gets the number of values stored in the table (all fields together)
* for this type of resource.
*
* @param int $courseId
*
* @return int Number of rows in the table
*/
public function getCourseCount($courseId)
{
$em = Database::getManager();
$query = $em->getRepository('ChamiloCourseBundle:CExerciseCategory')->createQueryBuilder('e');
$query->select('count(e.id)');
$query->where('e.cId = :cId');
$query->setParameter('cId', $courseId);
return $query->getQuery()->getSingleScalarResult();
}
/**
* @param int $courseId
*
* @return array
*/
public function getCategories($courseId)
{
$em = Database::getManager();
$query = $em->getRepository('ChamiloCourseBundle:CExerciseCategory')->createQueryBuilder('e');
$query->where('e.cId = :cId');
$query->setParameter('cId', $courseId);
$query->orderBy('e.position');
return $query->getQuery()->getResult();
}
/**
* @param int $courseId
*
* @return array
*/
public function getCategoriesForSelect($courseId)
{
$categories = $this->getCategories($courseId);
$options = [];
if (!empty($categories)) {
/** @var CExerciseCategory $category */
foreach ($categories as $category) {
$options[$category->getId()] = $category->getName();
}
}
return $options;
}
/**
* @param int $id
*/
public function delete($id)
{
$em = Database::getManager();
$repo = Database::getManager()->getRepository('ChamiloCourseBundle:CExerciseCategory');
$category = $repo->find($id);
if ($category) {
$em->remove($category);
$em->flush();
$courseId = api_get_course_int_id();
$table = Database::get_course_table(TABLE_QUIZ_TEST);
$id = (int) $id;
$sql = "UPDATE $table SET exercise_category_id = 0
WHERE c_id = $courseId AND exercise_category_id = $id";
Database::query($sql);
}
}
/**
* Save values in the *_field_values table.
*
* @param array $params Structured array with the values to save
* @param bool $showQuery Whether to show the insert query (passed to the parent save() method)
*/
public function save($params, $showQuery = false)
{
$em = Database::getManager();
$category = new CExerciseCategory();
$category
->setName($params['name'])
->setCId(api_get_course_int_id())
->setDescription($params['name'])
;
/*
// Update position
$query = $em->getRepository('ChamiloCourseBundle:CExerciseCategory')->createQueryBuilder('e');
$query
->where('e.cId = :cId')
->setParameter('cId', $courseId)
->setMaxResults(1)
->orderBy('e.position', 'DESC');
$last = $query->getQuery()->getOneOrNullResult();
$position = 0;
if (!empty($last)) {
$position = $last->getPosition() + 1;
}
$category->setPosition($position);
*/
$em->persist($category);
$em->flush();
return $category;
}
/**
* @param $token
*
* @return string
*/
public function getJqgridActionLinks($token)
{
//With this function we can add actions to the jgrid (edit, delete, etc)
$editIcon = Display::return_icon('edit.png', get_lang('Edit'), '', ICON_SIZE_SMALL);
$deleteIcon = Display::return_icon('delete.png', get_lang('Delete'), '', ICON_SIZE_SMALL);
$confirmMessage = addslashes(
api_htmlentities(get_lang('ConfirmYourChoice'), ENT_QUOTES)
);
$courseParams = api_get_cidreq();
$editButton = <<<JAVASCRIPT
<a href="?action=edit&{$courseParams}&id=' + options.rowId + '" class="btn btn-link btn-xs">\
$editIcon\
</a>
JAVASCRIPT;
$deleteButton = <<<JAVASCRIPT
<a \
onclick="if (!confirm(\'$confirmMessage\')) {return false;}" \
href="?sec_token=$token&{$courseParams}&id=' + options.rowId + '&action=delete" \
class="btn btn-link btn-xs">\
$deleteIcon\
</a>
JAVASCRIPT;
return "function action_formatter(cellvalue, options, rowObject) {
return '$editButton $deleteButton';
}";
}
/**
* @param string $url
* @param string $action
*
* @return FormValidator
*/
public function return_form($url, $action)
{
$form = new FormValidator('category', 'post', $url);
$id = isset($_GET['id']) ? (int) $_GET['id'] : null;
$form->addElement('hidden', 'id', $id);
// Setting the form elements
$header = get_lang('Add');
$defaults = [];
if ($action === 'edit') {
$header = get_lang('Modify');
// Setting the defaults
$defaults = $this->get($id, false);
}
$form->addElement('header', $header);
$form->addText(
'name',
get_lang('Name')
);
$form->addHtmlEditor('description', get_lang('Description'));
if ($action === 'edit') {
$form->addButtonUpdate(get_lang('Modify'));
} else {
$form->addButtonCreate(get_lang('Add'));
}
/*if (!empty($defaults['created_at'])) {
$defaults['created_at'] = api_convert_and_format_date($defaults['created_at']);
}
if (!empty($defaults['updated_at'])) {
$defaults['updated_at'] = api_convert_and_format_date($defaults['updated_at']);
}*/
$form->setDefaults($defaults);
// Setting the rules
$form->addRule('name', get_lang('ThisFieldIsRequired'), 'required');
return $form;
}
/**
* @return string
*/
public function display()
{
// action links
$content = '<div class="actions">';
$content .= '<a href="'.api_get_path(WEB_CODE_PATH).'exercise/exercise.php?'.api_get_cidreq().'">';
$content .= Display::return_icon(
'back.png',
get_lang('BackTo').' '.get_lang('PlatformAdmin'),
'',
ICON_SIZE_MEDIUM
);
$content .= '</a>';
$content .= '<a href="'.api_get_self().'?action=add&'.api_get_cidreq().'">';
$content .= Display::return_icon(
'add.png',
get_lang('Add'),
'',
ICON_SIZE_MEDIUM
);
$content .= '</a>';
$content .= '</div>';
$content .= Display::grid_html('categories');
return $content;
}
}

View File

@@ -0,0 +1,231 @@
<?php
/* For licensing terms, see /license.txt */
/**
* GamificationUtils class
* Functions to manage the gamification mode.
*
* @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
*/
class GamificationUtils
{
/**
* Get the calculated points on session with gamification mode.
*
* @param int $userId The user ID
* @param int $userStatus The user Status
*
* @return float
*/
public static function getTotalUserPoints($userId, $userStatus)
{
$points = 0;
$sessions = SessionManager::getSessionsFollowedByUser(
$userId,
$userStatus
);
if (empty($sessions)) {
return 0;
}
foreach ($sessions as $session) {
$points += self::getSessionPoints($session['id'], $userId);
}
return round($points / count($sessions), 2);
}
/**
* Get the achieved points for an user in a session.
*
* @param int $sessionId The session ID
* @param int $userId The user ID
*
* @return int The count of points
*/
public static function getSessionPoints($sessionId, $userId)
{
$totalPoints = 0;
$courses = SessionManager::get_course_list_by_session_id($sessionId);
if (empty($courses)) {
return 0;
}
foreach ($courses as $course) {
$learnPathListObject = new LearnpathList(
$userId,
api_get_course_info($course['code']),
$sessionId
);
$learnPaths = $learnPathListObject->get_flat_list();
if (empty($learnPaths)) {
continue;
}
$score = 0;
foreach ($learnPaths as $learnPathId => $learnPathInfo) {
if (empty($learnPathInfo['seriousgame_mode'])) {
continue;
}
$learnPath = new learnpath(
$course['code'],
$learnPathId,
$userId
);
$score += $learnPath->getCalculateScore($sessionId);
}
$totalPoints += round($score / count($learnPaths), 2);
}
return round($totalPoints / count($courses), 2);
}
/**
* Get the calculated progress for an user in a session.
*
* @param int $sessionId The session ID
* @param int $userId The user ID
*
* @return float The progress
*/
public static function getSessionProgress($sessionId, $userId)
{
$courses = SessionManager::get_course_list_by_session_id($sessionId);
$progress = 0;
if (empty($courses)) {
return 0;
}
foreach ($courses as $course) {
$courseProgress = Tracking::get_avg_student_progress(
$userId,
$course['code'],
[],
$sessionId,
false,
true
);
if (false === $courseProgress) {
continue;
}
$progress += $courseProgress;
}
return round($progress / count($courses), 2);
}
/**
* Get the number of stars achieved for an user in a session.
*
* @param int $sessionId The session ID
* @param int $userId The user ID
*
* @return int The number of stars
*/
public static function getSessionStars($sessionId, $userId)
{
$totalStars = 0;
$courses = SessionManager::get_course_list_by_session_id($sessionId);
if (empty($courses)) {
return 0;
}
foreach ($courses as $course) {
$learnPathListObject = new LearnpathList(
$userId,
api_get_course_info($course['code']),
$sessionId
);
$learnPaths = $learnPathListObject->get_flat_list();
if (empty($learnPaths)) {
continue;
}
$stars = 0;
foreach ($learnPaths as $learnPathId => $learnPathInfo) {
if (empty($learnPathInfo['seriousgame_mode'])) {
continue;
}
$learnPath = new learnpath(
$course['code'],
$learnPathId,
$userId
);
$stars += $learnPath->getCalculateStars($sessionId);
}
$totalStars += round($stars / count($learnPaths));
}
return round($totalStars / count($courses));
}
/**
* Get the stars on sessions with gamification mode.
*
* @param int $userId The user ID
* @param int $userStatus The user Status
*
* @return int
*/
public static function getTotalUserStars($userId, $userStatus)
{
$stars = 0;
$sessions = SessionManager::getSessionsFollowedByUser(
$userId,
$userStatus
);
if (empty($sessions)) {
return 0;
}
foreach ($sessions as $session) {
$stars += self::getSessionStars($session['id'], $userId);
}
return round($stars / count($sessions));
}
/**
* Get the total progress on sessions with gamification mode.
*
* @param int $userId The user ID
* @param int $userStatus The user Status
*
* @return float
*/
public static function getTotalUserProgress($userId, $userStatus)
{
$progress = 0;
$sessions = SessionManager::getSessionsFollowedByUser(
$userId,
$userStatus
);
if (empty($sessions)) {
return 0;
}
foreach ($sessions as $session) {
$progress += self::getSessionProgress($session['id'], $userId);
}
return round($progress / count($sessions), 2);
}
}

View File

@@ -0,0 +1,237 @@
<?php
/* For licensing terms, see /license.txt */
use Symfony\Component\Finder\Finder;
/**
* Class MailTemplateManager.
*/
class MailTemplateManager extends Model
{
public $columns = [
'id',
'name',
'template',
'type',
'system',
'url_id',
'default_template',
'created_at',
'updated_at',
'author_id',
];
public function __construct()
{
parent::__construct();
$this->table = 'mail_template';
}
/**
* @return int
*/
public function get_count()
{
$row = Database::select(
'count(*) as count',
$this->table,
['where' => ['url_id = ? ' => api_get_current_access_url_id()]],
'first'
);
return $row['count'];
}
/**
* Displays the title + grid.
*
* @return string html code
*/
public function display()
{
// Action links
$html = '<div class="actions" style="margin-bottom:20px">';
$html .= '<a href="'.api_get_path(WEB_CODE_PATH).'admin">'.
Display::return_icon(
'back.png',
get_lang('Back'),
'',
'32'
)
.'</a>';
$html .= '<a href="'.api_get_self().'?action=add">'.
Display::return_icon(
'add.png',
get_lang('Add'),
'',
'32'
).'</a>';
$html .= '</div>';
$html .= Display::grid_html('mail_template');
return $html;
}
/**
* Returns a Form validator Obj.
*
* @param string $url
* @param string $action
*
* @return FormValidator
*/
public function returnForm($url, $action = 'add')
{
$form = new FormValidator('template', 'post', $url);
// Setting the form elements
$header = get_lang('Add');
if ($action === 'edit') {
$header = get_lang('Modify');
}
$id = isset($_GET['id']) ? (int) $_GET['id'] : '';
$form->addElement('header', '', $header);
$form->addElement('hidden', 'id', $id);
$form->addElement(
'text',
'name',
get_lang('Name'),
['size' => '70', 'id' => 'name']
);
/*$form->addHtmlEditor(
'email_template',
get_lang('Template'),
false,
false,
[
'ToolbarSet' => 'Careers',
'Width' => '100%',
'Height' => '250',
]
);*/
$form->addTextarea(
'email_template',
get_lang('Template')
);
$finder = new Finder();
$files = $finder
->files()
->in(api_get_path(SYS_CODE_PATH).'template/default/mail')
->sort(
function ($a, $b) {
return strcmp($a->getRealpath(), $b->getRealpath());
}
);
$options = [];
/** @var SplFileInfo $file */
foreach ($files as $file) {
$options[$file->getFilename()] = $file->getFilename();
}
$form->addSelect(
'type',
get_lang('Type'),
$options
);
$defaults = $this->get($id);
if ($action === 'edit') {
$form->addLabel(get_lang('CreatedAt'), Display::dateToStringAgoAndLongDate($defaults['created_at']));
$form->addLabel(get_lang('UpdatedAt'), Display::dateToStringAgoAndLongDate($defaults['updated_at']));
$form->addButtonSave(get_lang('Modify'), 'submit');
} else {
$form->addButtonCreate(get_lang('Add'), 'submit');
}
// Setting the defaults
if (!empty($defaults)) {
$defaults['email_template'] = $defaults['template'];
}
$form->setDefaults($defaults);
// Setting the rules
$form->addRule('name', get_lang('ThisFieldIsRequired'), 'required');
return $form;
}
/**
* @param int $id
*
* @return bool
*/
public function setDefault($id)
{
$template = $this->get($id);
if (empty($template)) {
return false;
}
$type = $template['type'];
$urlId = api_get_current_access_url_id();
$sql = "UPDATE {$this->table} SET default_template = 0
WHERE type = '$type' AND url_id = $urlId";
Database::query($sql);
$sql = "UPDATE {$this->table} SET default_template = 1
WHERE id = $id";
Database::query($sql);
return true;
}
/**
* @param int $templateId
* @param array $userInfo
*
* @return string|false
*/
public function parseTemplate($templateId, $userInfo)
{
$templateInfo = $this->get($templateId);
if (!empty($templateInfo)) {
$emailTemplate = nl2br($templateInfo['template']);
$keys = array_keys($userInfo);
foreach ($keys as $key) {
$emailTemplate = str_replace("{{user.$key}}", $userInfo[$key], $emailTemplate);
}
$template = new Template();
$template->twig->setLoader(new \Twig_Loader_String());
$emailBody = $template->twig->render($emailTemplate);
return $emailBody;
}
return false;
}
/**
* Gets a custom mail template by the name of the template it replaces.
*
* @param string $templateType Name of the template file it replaces
*
* @return string
*/
public function getTemplateByType($templateType)
{
if (empty($templateType)) {
return '';
}
$result = Database::select(
'template',
$this->table,
['where' => ['type = ? ' => $templateType, ' AND url_id = ? ' => api_get_current_access_url_id()]],
'first'
);
if (empty($result)) {
return '';
}
return $result['template'];
}
}

File diff suppressed because it is too large Load Diff

101
main/inc/lib/MyStudents.php Normal file
View File

@@ -0,0 +1,101 @@
<?php
/* For licensing terms, see /license.txt */
class MyStudents
{
public static function userCareersTable(int $studentId): string
{
if (!api_get_configuration_value('allow_career_users')) {
return '';
}
$careers = UserManager::getUserCareers($studentId);
if (empty($careers)) {
return '';
}
$title = Display::page_subheader(get_lang('Careers'), null, 'h3', ['class' => 'section-title']);
return $title.self::getCareersTable($careers, $studentId);
}
public static function getCareersTable(array $careers, int $studentId): string
{
if (empty($careers)) {
return '';
}
$webCodePath = api_get_path(WEB_CODE_PATH);
$iconDiagram = Display::return_icon('multiplicate_survey.png', get_lang('Diagram'));
$careerModel = new Career();
$headers = [
get_lang('Career'),
get_lang('Diagram'),
];
$data = array_map(
function (array $careerInfo) use ($careerModel, $webCodePath, $iconDiagram, $studentId) {
$careerId = $careerInfo['id'];
if (api_get_configuration_value('use_career_external_id_as_identifier_in_diagrams')) {
$careerId = $careerModel->getCareerIdFromInternalToExternal($careerId);
}
$url = $webCodePath.'user/career_diagram.php?career_id='.$careerId.'&user_id='.$studentId;
return [
$careerInfo['name'],
Display::url($iconDiagram, $url),
];
},
$careers
);
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$table->setHeaders($headers);
$table->setData($data);
return $table->toHtml();
}
public static function getBlockForSkills(int $studentId, int $courseId, int $sessionId): string
{
$allowAll = api_get_configuration_value('allow_teacher_access_student_skills');
if ($allowAll) {
return Tracking::displayUserSkills($studentId, 0, 0, true);
}
// Default behaviour - Show all skills depending the course and session id
return Tracking::displayUserSkills($studentId, $courseId, $sessionId);
}
public static function getBlockForClasses($studentId): ?string
{
$userGroupManager = new UserGroup();
$userGroups = $userGroupManager->getNameListByUser(
$studentId,
UserGroup::NORMAL_CLASS
);
if (empty($userGroups)) {
return null;
}
$headers = [get_lang('Classes')];
$data = array_map(
function ($class) {
return [$class];
},
$userGroups
);
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$table->setHeaders($headers);
$table->setData($data);
return $table->toHtml();
}
}

View File

@@ -0,0 +1,452 @@
<?php
/* For licensing terms, see /license.txt */
class NotificationEvent extends Model
{
public const ACCOUNT_EXPIRATION = 1;
public const JUSTIFICATION_EXPIRATION = 2;
public const GLOBAL_NOTIFICATION = 3;
public const SPECIFIC_USER = 4;
public $table;
public $columns = [
'id',
'title',
'content',
'link',
'persistent',
'day_diff',
'event_type',
'event_id',
];
public $extraFieldName;
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->table = 'notification_event';
$this->extraFieldName = 'notification_event';
}
public function eventTypeToString($eventTypeId)
{
$list = $this->getEventsForSelect(false);
return $list[$eventTypeId];
}
public function getEventsForSelect($onlyEnabled = true): array
{
$eventTypes = [
self::ACCOUNT_EXPIRATION => get_lang('AccountExpiration'),
self::GLOBAL_NOTIFICATION => get_lang('Global'),
self::SPECIFIC_USER => get_lang('SpecificUsers'),
];
if (!$onlyEnabled || api_get_plugin_setting('justification', 'tool_enable') === 'true') {
$eventTypes[self::JUSTIFICATION_EXPIRATION] = get_lang('JustificationExpiration');
}
return $eventTypes;
}
/**
* @throws Exception
*/
public function getForm(FormValidator $form, $data = []): FormValidator
{
$options = $this->getEventsForSelect();
$form->addSelect('event_type', get_lang('EventType'), $options);
$form->freeze('event_type');
$eventType = $data['event_type'];
switch ($eventType) {
case self::JUSTIFICATION_EXPIRATION:
$list = [];
if (api_get_plugin_setting('justification', 'tool_enable') === 'true'
&& $list = Justification::create()->getList()
) {
$list = array_column($list, 'name', 'id');
}
$form->addSelect('event_id', get_lang('JustificationType'), $list);
$form->freeze('event_id');
break;
default:
break;
}
$form->addText('title', get_lang('Title'));
$form->addTextarea('content', get_lang('Content'));
$form->addText('link', get_lang('Link'), false);
$form->addCheckBox('persistent', get_lang('Persistent'));
$form->addNumeric('day_diff', get_lang('DaysDifference'), false);
switch ($eventType) {
case self::SPECIFIC_USER:
$form->addSelectAjax(
'users',
get_lang('Users'),
$data['users'] ?? [],
[
'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_like',
'multiple' => 'multiple',
]
);
//no break
case self::GLOBAL_NOTIFICATION:
$form->removeElement('day_diff');
break;
}
return $form;
}
/**
* @throws Exception
*/
public function getAddForm(FormValidator $form): FormValidator
{
$options = $this->getEventsForSelect();
$eventType = $form->getSubmitValue('event_type');
$form->addSelect(
'event_type',
get_lang('EventType'),
$options,
['placeholder' => get_lang('SelectAnOption'), 'onchange' => 'document.add.submit()']
);
if (!empty($eventType)) {
$form->freeze('event_type');
$form->addText('title', get_lang('Title'));
$form->addTextarea('content', get_lang('Content'));
$form->addText('link', get_lang('Link'), false);
$form->addCheckBox('persistent', get_lang('Persistent'));
$form->addNumeric('day_diff', get_lang('DaysDifference'), false);
switch ($eventType) {
case self::JUSTIFICATION_EXPIRATION:
$list = [];
if (api_get_plugin_setting('justification', 'tool_enable') === 'true'
&& $list = Justification::create()->getList()
) {
$list = array_column($list, 'name', 'id');
}
$form->addSelect('event_id', get_lang('JustificationType'), $list);
break;
case self::SPECIFIC_USER:
$form->addSelectAjax(
'users',
get_lang('Users'),
[],
[
'url' => api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?a=get_user_like',
'multiple' => 'multiple',
]
);
//no break
case self::GLOBAL_NOTIFICATION:
$form->removeElement('day_diff');
break;
default:
break;
}
$form->addButtonSave(get_lang('Save'));
}
return $form;
}
public function getUserExtraData($userId)
{
$data = UserManager::get_extra_user_data_by_field($userId, $this->extraFieldName);
return $data['notification_event'] ?? '';
}
/**
* @throws Exception
*/
public function getNotificationsByUser(int $userId): array
{
$userInfo = api_get_user_info($userId);
$events = $this->get_all();
$extraFieldData = $this->getUserExtraData($userId);
$notifications = [];
foreach ($events as $event) {
$days = (int) $event['day_diff'];
$checkIsRead = $event['persistent'] == 0;
$eventItemId = $event['event_id'];
switch ($event['event_type']) {
case self::ACCOUNT_EXPIRATION:
if (empty($userInfo['expiration_date'])) {
break;
}
$id = 'id_'.self::ACCOUNT_EXPIRATION.'_event_'.$event['id'].'_'.$userInfo['id'];
$read = false;
if ($checkIsRead) {
$read = $this->isRead($id, $extraFieldData);
}
$showNotification = $this->showNotification($userInfo['expiration_date'], $days);
if ($showNotification && $read === false) {
$notifications[] = [
'id' => $id,
'title' => $event['title'],
'content' => $event['content'],
'event_text' => get_lang('ExpirationDate').': '.api_get_local_time($userInfo['expiration_date']),
'link' => $event['link'],
'persistent' => $event['persistent'],
];
}
break;
case self::JUSTIFICATION_EXPIRATION:
if (api_get_plugin_setting('justification', 'tool_enable') !== 'true') {
break;
}
$plugin = Justification::create();
$userJustificationList = $plugin->getUserJustificationList($userId);
foreach ($userJustificationList as $userJustification) {
if (empty($userJustification['date_validity'])) {
continue;
}
if ($eventItemId != $userJustification['justification_document_id']) {
continue;
}
$showNotification = $this->showNotification($userJustification['date_validity'], $days);
$id = 'id_'.self::JUSTIFICATION_EXPIRATION.'_event_'.$event['id'].'_'.$userJustification['id'];
$fieldData = $plugin->getJustification($userJustification['justification_document_id']);
$read = false;
if ($checkIsRead) {
$read = $this->isRead($id, $extraFieldData);
}
$eventText = $plugin->get_lang('Justification').': '.$fieldData['name'].' <br />';
$eventText .= $plugin->get_lang('JustificationDate').': '.$userJustification['date_validity'];
$url = $event['link'];
if (empty($url)) {
$url = api_get_path(WEB_CODE_PATH).'auth/justification.php#'.$fieldData['code'];
}
if ($showNotification && $read === false) {
$notifications[] = [
'id' => $id,
'title' => $event['title'],
'content' => $event['content'],
'event_text' => $eventText,
'link' => $url,
'persistent' => $event['persistent'],
];
}
}
break;
case self::SPECIFIC_USER:
$assignedUsers = self::getAssignedUsers($event['id']);
$assignedUserIdList = array_keys($assignedUsers);
if (!in_array($userId, $assignedUserIdList)) {
break;
}
//no break
case self::GLOBAL_NOTIFICATION:
$id = "id_{$event['event_type']}_event_{$event['id']}_$userId";
$wasRead = $checkIsRead && $this->isRead($id, $extraFieldData);
if (!$wasRead) {
$notifications[] = [
'id' => $id,
'title' => $event['title'],
'content' => $event['content'],
'event_text' => null,
'link' => $event['link'],
'persistent' => $event['persistent'],
];
}
break;
}
}
return $notifications;
}
public function isRead($id, $extraData): bool
{
$userId = api_get_user_id();
if (empty($extraData)) {
return false;
}
$data = $this->getUserExtraData($userId);
if (empty($data)) {
return false;
}
$data = json_decode($data);
if (in_array($id, $data)) {
return true;
}
return false;
}
public function markAsRead($id): bool
{
if (empty($id)) {
return false;
}
$userId = api_get_user_id();
$data = $this->getUserExtraData($userId);
if (!empty($data)) {
$data = json_decode($data);
} else {
$data = [];
}
$data[] = $id;
$data = json_encode($data);
UserManager::update_extra_field_value($userId, $this->extraFieldName, $data);
return true;
}
/**
* @throws Exception
*/
public function showNotification($date, $dayDiff): bool
{
$today = api_get_utc_datetime();
$expiration = api_get_utc_datetime($date, false, true);
$interval = new DateInterval('P'.$dayDiff.'D');
$diff = $expiration->sub($interval);
if ($diff->format('Y-m-d H:i:s') < $today) {
return true;
}
return false;
}
public function install()
{
$sql = "CREATE TABLE IF NOT EXISTS notification_event (
id INT unsigned NOT NULL auto_increment PRIMARY KEY,
title VARCHAR(255),
content TEXT,
link TEXT,
persistent INT,
day_diff INT,
event_type VARCHAR(255)
)";
Database::query($sql);
}
public function save($params, $show_query = false)
{
$userIdList = [];
if (isset($params['users'])) {
$userIdList = $params['users'];
unset($params['users']);
}
/** @var int|bool $saved */
$saved = parent::save($params, $show_query);
if (false !== $saved && !empty($userIdList)) {
self::assignUserIdList($saved, $userIdList);
}
return $saved;
}
public function update($params, $showQuery = false): bool
{
$userIdList = [];
if (isset($params['users'])) {
$userIdList = $params['users'];
unset($params['users']);
}
$updated = parent::update($params, $showQuery);
self::deleteAssignedUsers($params['id']);
self::assignUserIdList($params['id'], $userIdList);
return $updated;
}
public function get($id)
{
$props = parent::get($id);
$props['users'] = self::getAssignedUsers($id);
return $props;
}
public static function assignUserIdList(int $eventId, array $userIdList)
{
foreach ($userIdList as $userId) {
Database::insert(
'notification_event_rel_user',
[
'event_id' => $eventId,
'user_id' => (int) $userId,
]
);
}
}
public static function getAssignedUsers(int $eventId): array
{
$tblUser = Database::get_main_table(TABLE_MAIN_USER);
$result = Database::select(
'u.id, u.username, u.firstname, u.lastname',
"notification_event_rel_user neru INNER JOIN $tblUser u ON neru.user_id = u.id",
['where' => ['neru.event_id = ?' => $eventId]]
);
$userList = [];
foreach ($result as $userInfo) {
$userList[$userInfo['id']] = api_get_person_name(
$userInfo['firstname'],
$userInfo['lastname'],
null,
null,
null,
$userInfo['username']
);
}
return $userList;
}
public static function deleteAssignedUsers(int $eventId)
{
Database::delete(
'notification_event_rel_user',
['event_id = ?' => $eventId]
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,102 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\Course as CourseEntity;
use Chamilo\CoreBundle\Entity\PortfolioComment;
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
class PortfolioNotifier
{
public static function notifyTeachersAndAuthor(PortfolioComment $comment)
{
$item = $comment->getItem();
$course = $item->getCourse();
$session = $item->getSession();
$messageSubject = sprintf(
get_lang('PortfolioAlertNewCommentSubject'),
$item->getTitle(true)
);
$userIdListToSend = [];
$userIdListToSend[] = $comment->getItem()->getUser()->getId();
$cidreq = api_get_cidreq_params(
$course ? $course->getCode() : '',
$session ? $session->getId() : 0
);
$commentUrl = api_get_path(WEB_CODE_PATH).'portfolio/index.php?'
.($course ? $cidreq.'&' : '')
.http_build_query(['action' => 'view', 'id' => $item->getId()])."#comment-{$comment->getId()}";
if ($course) {
$courseInfo = api_get_course_info($course->getCode());
if (1 !== (int) api_get_course_setting('email_alert_teachers_student_new_comment', $courseInfo)) {
return;
}
$courseTitle = self::getCourseTitle($course, $session);
$userIdListToSend = array_merge(
$userIdListToSend,
self::getTeacherList($course, $session)
);
$messageContent = sprintf(
get_lang('CoursePortfolioAlertNewCommentContent'),
$item->getTitle(),
$courseTitle,
$commentUrl
);
} else {
$messageContent = sprintf(
get_lang('PortfolioAlertNewCommentContent'),
$item->getTitle(),
$commentUrl
);
}
$messageContent .= '<br><br><figure>'
.'<blockquote>'.$comment->getExcerpt().'</blockquote>'
.'<figcaption>'.$comment->getAuthor()->getCompleteName().'</figcaption>'
.'</figure>';
foreach ($userIdListToSend as $userIdToSend) {
MessageManager::send_message_simple(
$userIdToSend,
$messageSubject,
$messageContent,
0,
false,
false,
[],
false
);
}
}
private static function getCourseTitle(CourseEntity $course, ?SessionEntity $session = null): string
{
if ($session) {
return "{$course->getTitle()} ({$session->getName()})";
}
return $course->getTitle();
}
private static function getTeacherList(CourseEntity $course, ?SessionEntity $session = null): array
{
if ($session) {
$teachers = SessionManager::getCoachesByCourseSession(
$session->getId(),
$course->getId()
);
return array_values($teachers);
}
$teachers = CourseManager::get_teacher_list_from_course_code($course->getCode());
return array_keys($teachers);
}
}

View File

@@ -0,0 +1,612 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class ScheduledAnnouncement
* Requires DB change:.
*
* CREATE TABLE scheduled_announcements (id INT AUTO_INCREMENT NOT NULL, subject VARCHAR(255) NOT NULL, message LONGTEXT NOT NULL, date DATETIME DEFAULT NULL, sent TINYINT(1) NOT NULL, session_id INT NOT NULL, c_id INT DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
*
* Config setting:
* $_configuration['allow_scheduled_announcements'] = true;
*
* Setup linux cron file:
* main/cron/scheduled_announcement.php
*
* Requires:
* composer update
*/
class ScheduledAnnouncement extends Model
{
public $table;
public $columns = ['id', 'subject', 'message', 'date', 'sent', 'session_id'];
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->table = 'scheduled_announcements';
}
/**
* @param array $where_conditions
*
* @return array
*/
public function get_all($where_conditions = [])
{
return Database::select(
'*',
$this->table,
['where' => $where_conditions, 'order' => 'subject ASC']
);
}
/**
* @return mixed
*/
public function get_count()
{
$row = Database::select(
'count(*) as count',
$this->table,
[],
'first'
);
return $row['count'];
}
/**
* Displays the title + grid.
*
* @param int $sessionId
*
* @return string
*/
public function getGrid($sessionId)
{
// action links
$action = '<div class="actions" style="margin-bottom:20px">';
$action .= Display::url(
Display::return_icon('back.png', get_lang('Back'), '', ICON_SIZE_MEDIUM),
api_get_path(WEB_CODE_PATH).'session/resume_session.php?id_session='.$sessionId
);
$action .= '<a href="'.api_get_self().'?action=add&session_id='.$sessionId.'">'.
Display::return_icon('add.png', get_lang('Add'), '', ICON_SIZE_MEDIUM).'</a>';
$action .= '<a href="scheduled_announcement.php?action=run&session_id='.$sessionId.'">'.
Display::return_icon('tuning.png', get_lang('SendManuallyPendingAnnouncements'), '', ICON_SIZE_MEDIUM).
'</a>';
$action .= '</div>';
$html = $action;
$html .= '<div id="session-table" class="table-responsive">';
$html .= Display::grid_html('programmed');
$html .= '</div>';
return $html;
}
/**
* Returns a Form validator Obj.
*
* @param int $id
* @param string $url
* @param string $action add, edit
* @param array $sessionInfo
*
* @return FormValidator form validator obj
*/
public function returnSimpleForm($id, $url, $action, $sessionInfo = [])
{
$form = new FormValidator(
'announcement',
'post',
$url
);
$form->addHidden('session_id', $sessionInfo['id']);
$form->addDateTimePicker('date', get_lang('Date'));
$useBaseProgress = api_get_configuration_value('scheduled_announcements_use_base_progress');
if ($useBaseProgress) {
$extraFieldValue = new ExtraFieldValue('scheduled_announcement');
$baseProgress = $extraFieldValue->get_values_by_handler_and_field_variable(
$id,
'use_base_progress'
);
$form->addNumeric('progress',
get_lang('Progress'),
[
'step' => 1,
'min' => 1,
'max' => 100,
'value' => $baseProgress['value'],
],
true
);
}
$form->addText('subject', get_lang('Subject'));
$form->addHtmlEditor('message', get_lang('Message'));
$extraField = new ExtraField('scheduled_announcement');
$extra = $extraField->addElements($form, $id);
$js = $extra['jquery_ready_content'];
$form->addHtml("<script> $(function() { $js }); </script> ");
$this->setTagsInForm($form);
$form->addCheckBox('sent', null, get_lang('MessageSent'));
if ('edit' === $action) {
$form->addButtonUpdate(get_lang('Modify'));
}
return $form;
}
/**
* Returns a Form validator Obj.
*
* @todo the form should be auto generated
*
* @param string $url
* @param string $action add, edit
* @param array
*
* @return FormValidator form validator obj
*/
public function returnForm($url, $action, $sessionInfo = [])
{
// Setting the form elements
$header = get_lang('Add');
if ('edit' === $action) {
$header = get_lang('Modify');
}
$form = new FormValidator(
'announcement',
'post',
$url
);
$form->addHeader($header);
if ('add' === $action) {
$form->addHtml(
Display::return_message(
nl2br(get_lang('ScheduleAnnouncementDescription')),
'normal',
false
)
);
}
$form->addHidden('session_id', $sessionInfo['id']);
$useBaseDate = false;
$startDate = $sessionInfo['access_start_date'];
$endDate = $sessionInfo['access_end_date'];
if (!empty($startDate) || !empty($endDate)) {
$useBaseDate = true;
}
$typeOptions = [
'specific_date' => get_lang('SpecificDate'),
];
if ($useBaseDate) {
$typeOptions['base_date'] = get_lang('BaseDate');
}
$useBaseProgress = api_get_configuration_value('scheduled_announcements_use_base_progress');
if ($useBaseProgress) {
$typeOptions['base_progress'] = get_lang('Progress');
}
$form->addSelect(
'type',
get_lang('Type'),
$typeOptions,
[
'onchange' => "javascript:
if (this.options[this.selectedIndex].value == 'base_date') {
document.getElementById('options').style.display = 'block';
document.getElementById('specific_date').style.display = 'none';
document.getElementById('base_progress').style.display = 'none';
} else if (this.options[this.selectedIndex].value == 'specific_date') {
document.getElementById('options').style.display = 'none';
document.getElementById('specific_date').style.display = 'block';
document.getElementById('base_progress').style.display = 'none';
} else {
document.getElementById('options').style.display = 'block';
document.getElementById('specific_date').style.display = 'none';
document.getElementById('base_progress').style.display = 'block';
}
", ]
);
$form->addHtml('<div id="specific_date">');
$form->addDateTimePicker('date', get_lang('Date'));
$form->addHtml('</div>');
$form->addHtml('<div id="base_progress" style="display:none">');
$form->addNumeric('progress',
get_lang('Progress'),
[
'step' => 1,
'min' => 1,
'max' => 100,
'value' => 100,
],
true
);
$form->addHtml('</div>');
$form->addHtml('<div id="options" style="display:none">');
$startDate = $sessionInfo['access_start_date'];
$endDate = $sessionInfo['access_end_date'];
$form->addText(
'days',
get_lang('Days'),
false
);
$form->addSelect(
'moment_type',
get_lang('AfterOrBefore'),
[
'after' => get_lang('After'),
'before' => get_lang('Before'),
]
);
if (!empty($startDate)) {
$options['start_date'] = get_lang('StartDate').' - '.$startDate;
}
if (!empty($endDate)) {
$options['end_date'] = get_lang('EndDate').' - '.$endDate;
}
if (!empty($options)) {
$form->addSelect('base_date', get_lang('BaseDate'), $options);
}
$form->addHtml('</div>');
$form->addText('subject', get_lang('Subject'));
$form->addHtmlEditor('message', get_lang('Message'));
$extraField = new ExtraField('scheduled_announcement');
$extra = $extraField->addElements($form);
$js = $extra['jquery_ready_content'];
$form->addHtml("<script> $(function() { $js }); </script> ");
$this->setTagsInForm($form);
if ('edit' === $action) {
$form->addButtonUpdate(get_lang('Modify'));
} else {
$form->addButtonCreate(get_lang('Add'));
}
return $form;
}
/**
* @param int $id
*
* @return string
*/
public function getAttachmentToString($id)
{
$file = $this->getAttachment($id);
if (!empty($file) && !empty($file['value'])) {
$url = api_get_path(WEB_UPLOAD_PATH).$file['value'];
return get_lang('Attachment').': '.Display::url(basename($file['value']), $url, ['target' => '_blank']);
}
return '';
}
/**
* @param int $id
*
* @return array
*/
public function getAttachment($id)
{
$extraFieldValue = new ExtraFieldValue('scheduled_announcement');
$attachment = $extraFieldValue->get_values_by_handler_and_field_variable($id, 'attachment');
return $attachment;
}
/**
* @param int $urlId
*
* @return int
*/
public function sendPendingMessages($urlId = 0)
{
if (!$this->allowed()) {
return 0;
}
$messagesSent = 0;
$now = api_get_utc_datetime();
$result = $this->get_all();
$extraFieldValue = new ExtraFieldValue('scheduled_announcement');
// get user extra fields list (only visible to self and filter-able)
$extraField = new ExtraField('user');
$extraFields = $extraField->get_all(['filter = ? AND visible_to_self = ?' => [1, 1]]);
foreach ($result as $result) {
if (empty($result['sent'])) {
if (!empty($result['date']) && $result['date'] < $now) {
$sessionId = $result['session_id'];
$sessionInfo = api_get_session_info($sessionId);
if (empty($sessionInfo)) {
continue;
}
$users = SessionManager::get_users_by_session(
$sessionId,
0,
false,
$urlId
);
$coachId = $sessionInfo['id_coach'];
if (empty($users) || empty($coachId)) {
continue;
}
$coachList = [];
if ($users) {
$sendToCoaches = $extraFieldValue->get_values_by_handler_and_field_variable(
$result['id'],
'send_to_coaches'
);
$courseList = SessionManager::getCoursesInSession($sessionId);
if (!empty($sendToCoaches) && !empty($sendToCoaches['value']) && 1 == $sendToCoaches['value']) {
foreach ($courseList as $courseItemId) {
$coaches = SessionManager::getCoachesByCourseSession(
$sessionId,
$courseItemId
);
$coachList = array_merge($coachList, $coaches);
}
$coachList = array_unique($coachList);
}
$useBaseProgress = api_get_configuration_value('scheduled_announcements_use_base_progress');
if ($useBaseProgress) {
$baseProgress = $extraFieldValue->get_values_by_handler_and_field_variable(
$result['id'],
'use_base_progress'
);
if (empty($baseProgress) || empty($baseProgress['value']) || $baseProgress['value'] < 1) {
$this->update(['id' => $result['id'], 'sent' => 1]);
}
} else {
$this->update(['id' => $result['id'], 'sent' => 1]);
}
$attachments = $this->getAttachmentToString($result['id']);
$subject = $result['subject'];
$courseInfo = [];
if (!empty($courseList)) {
$courseId = current($courseList);
$courseInfo = api_get_course_info_by_id($courseId);
}
$message = '';
foreach ($users as $user) {
// Take original message
$message = $result['message'];
$userInfo = api_get_user_info($user['user_id']);
$userPicture = UserManager::getUserPicture($user['user_id'], USER_IMAGE_SIZE_ORIGINAL);
$progress = '';
if (!empty($sessionInfo) && !empty($courseInfo)) {
$progress = Tracking::get_avg_student_progress(
$user['user_id'],
$courseInfo['code'],
[],
$sessionId
);
}
if ($useBaseProgress) {
$baseProgress = $extraFieldValue->get_values_by_handler_and_field_variable(
$result['id'],
'use_base_progress'
);
if (!empty($baseProgress) && !empty($baseProgress['value']) && $baseProgress['value'] >= 1) {
if ((is_numeric($progress) && $progress > $baseProgress['value']) || !is_numeric($progress)) {
continue;
} else {
$comment = json_decode($baseProgress['comment'], true);
if ($comment !== null && is_array($comment)) {
if (isset($comment['sended']) && is_array($comment['sended'])) {
$userFound = false;
foreach ($comment['sended'] as $item) {
if (isset($item['user']) && $item['user'] === $user['user_id']) {
$userFound = true;
break;
}
}
if ($userFound) {
continue;
} else {
$comment['sended'][] = ['user' => $user['user_id'], 'send_date' => time(), 'progress_user' => $progress, 'progress_mark' => $baseProgress['value']];
$newExtraFieldParams = $baseProgress;
$newExtraFieldParams['comment'] = json_encode($comment);
$extraFieldValue->save($newExtraFieldParams);
}
}
} else {
$comment['sended'][] = ['user' => $user['user_id'], 'send_date' => time(), 'progress_user' => $progress, 'progress_mark' => $baseProgress['value']];
$newExtraFieldParams = $baseProgress;
$newExtraFieldParams['comment'] = json_encode($comment);
$extraFieldValue->save($newExtraFieldParams);
}
}
}
}
if (is_numeric($progress)) {
$progress = $progress.'%';
} else {
$progress = '0%';
}
$startTime = api_get_local_time(
$sessionInfo['access_start_date'],
null,
null,
true
);
$endTime = api_get_local_time(
$sessionInfo['access_end_date'],
null,
null,
true
);
$generalCoach = '';
$generalCoachEmail = '';
if (!empty($coachId)) {
$coachInfo = api_get_user_info($coachId);
if (!empty($coachInfo)) {
$generalCoach = $coachInfo['complete_name'];
$generalCoachEmail = $coachInfo['email'];
}
}
$tags = [
'((session_name))' => $sessionInfo['name'],
'((session_start_date))' => $startTime,
'((general_coach))' => $generalCoach,
'((general_coach_email))' => $generalCoachEmail,
'((session_end_date))' => $endTime,
'((user_username))' => $userInfo['username'],
'((user_complete_name))' => $userInfo['complete_name'],
'((user_firstname))' => $userInfo['firstname'],
'((user_lastname))' => $userInfo['lastname'],
'((user_first_name))' => $userInfo['firstname'],
'((user_last_name))' => $userInfo['lastname'],
'((user_official_code))' => $userInfo['official_code'],
'((user_picture))' => $userPicture,
'((lp_progress))' => $progress,
];
if (!empty($extraFields)) {
$efv = new ExtraFieldValue('user');
foreach ($extraFields as $extraField) {
$valueExtra = $efv->get_values_by_handler_and_field_variable(
$user['user_id'],
$extraField['variable'],
true
);
$tags['(('.strtolower($extraField['variable']).'))'] = $valueExtra['value'];
}
}
$message = str_replace(array_keys($tags), $tags, $message);
$message .= $attachments;
MessageManager::send_message_simple(
$userInfo['user_id'],
$subject,
$message,
$coachId
);
}
$message = get_lang('YouAreReceivingACopyBecauseYouAreACourseCoach').'<br /><br />'.$message;
foreach ($coachList as $courseCoachId) {
MessageManager::send_message_simple(
$courseCoachId,
get_lang('YouAreReceivingACopyBecauseYouAreACourseCoach').'&nbsp;'.$subject,
$message,
$coachId
);
}
}
$messagesSent++;
}
}
}
return $messagesSent;
}
/**
* @return array
*/
public function getTags()
{
$tags = [
'((session_name))',
'((session_start_date))',
'((session_end_date))',
'((general_coach))',
'((general_coach_email))',
'((user_username))',
'((user_complete_name))',
'((user_first_name))',
'((user_last_name))',
'((user_picture))',
'((lp_progress))',
'((user_official_code))',
];
// get user extra fields list (only visible to self and filter-able)
$extraField = new ExtraField('user');
$extraFields = $extraField->get_all(['filter = ? AND visible_to_self = ?' => [1, 1]]);
if (!empty($extraFields)) {
foreach ($extraFields as $extraField) {
$tags[] = '(('.strtolower($extraField['variable']).'))';
}
}
return $tags;
}
/**
* @return bool
*/
public function allowed()
{
return api_get_configuration_value('allow_scheduled_announcements');
}
/**
* @param FormValidator $form
*/
private function setTagsInForm(&$form)
{
$form->addLabel(
get_lang('Tags'),
Display::return_message(
implode('<br />', $this->getTags()),
'normal',
false
)
);
}
}

143
main/inc/lib/SmsPlugin.php Normal file
View File

@@ -0,0 +1,143 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class SmsPlugin.
*
* @author Julio Montoya
*/
class SmsPlugin extends Plugin
{
public const WELCOME_LOGIN_PASSWORD = 0;
public const NEW_FILE_SHARED_COURSE_BY = 1;
public const ACCOUNT_APPROVED_CONNECT = 2;
public const NEW_COURSE_BEEN_CREATED = 3;
public const NEW_USER_SUBSCRIBED_COURSE = 4;
public const NEW_COURSE_SUGGESTED_TEACHER = 5;
public const COURSE_OPENING_REQUEST_CODE_REGISTERED = 6;
public const COURSE_OPENING_REQUEST_CODE_APPROVED = 7;
public const COURSE_OPENING_REQUEST_CODE_REJECTED = 8;
public const COURSE_OPENING_REQUEST_CODE = 9;
public const BEEN_SUBSCRIBED_COURSE = 10;
public const ASSIGNMENT_BEEN_CREATED_COURSE = 11;
public const ACCOUNT_CREATED_UPDATED_LOGIN_PASSWORD = 12;
public const PASSWORD_UPDATED_LOGIN_PASSWORD = 13;
public const REQUESTED_PASSWORD_CHANGE = 14;
public const RECEIVED_NEW_PERSONAL_MESSAGES = 15;
public const NEW_USER_PENDING_APPROVAL = 16;
public const POSTED_FORUM_COURSE = 17;
public const CHECK_EMAIL_CONNECT_MORE_INFO = 18;
public const STUDENT_ANSWERED_TEST = 19;
public const STUDENT_ANSWERED_TEST_OPEN_QUESTION = 20;
public const STUDENT_ANSWERED_TEST_VOICE_QUESTION = 21;
public const ANSWER_OPEN_QUESTION_TEST_REVIEWED = 22;
public const NEW_THREAD_STARTED_FORUM = 23;
public const NEW_ANSWER_POSTED_FORUM = 24;
public const NEW_SYSTEM_ANNOUNCEMENT_ADDED = 25;
public const TEST_NEW_SYSTEM_ANNOUNCEMENT_ADDED = 26;
public const SYSTEM_ANNOUNCEMENT_UPDATE = 27;
public const TEST_SYSTEM_ANNOUNCEMENT_UPDATE = 28;
public const USER_UPLOADED_ASSIGNMENT_COURSE_STUDENT_SUBMITS_PAPER = 29;
public const USER_UPLOADED_ASSIGNMENT_CHECK_STUDENT_SUBMITS_PAPER = 30;
public const USER_UPLOADED_ASSIGNMENT_COURSE = 31;
public const USER_UPLOADED_ASSIGNMENT_CHECK = 32;
public const SUBSCRIBED_SESSION = 33;
public const SUBSCRIBED_SESSION_CSV = 34;
public const USER_SUGGESTED_BE_FRIENDS = 35;
public const USER_ANSWERED_INBOX_MESSAGE = 36;
public const BEEN_INVITED_JOIN_GROUP = 37;
public const MESSAGES_SENT_EDITED_GROUP_EDITED = 38;
public const MESSAGES_SENT_EDITED_GROUP_ADDED = 39;
public const BEEN_INVITED_COMPLETE_SURVEY_COURSE = 40;
public const REMINDER_ASSIGNMENT_COURSE_DUE = 41;
public const USER_DETAILS_MODIFIED = 42;
public const CERTIFICATE_NOTIFICATION = 43;
public $isCoursePlugin = true;
public $isMailPlugin = true;
/**
* getSmsTypeOptions (returns all SMS types).
*
* @return array SMS types
*/
public function getSmsTypeOptions()
{
return [
'MessageWelcomeXLoginXPasswordX',
'MessageXNewFileSharedCourseXByX',
'MessageXAccountApprovedConnectX',
'MessageXNewCourseXBeenCreatedX',
'MessageXNewUserXSubscribedCourseX',
'MessageXNewCourseSuggestedTeacherX',
'MessageXCourseOpeningRequestCodeXRegistered',
'MessageXCourseOpeningRequestCourseCodeXApproved',
'MessageXRequestOpenCourseCodeXReject',
'MessageXCourseOpeningRequestCourseCodeX',
'MessageXBeenSubscribedCourseX',
'MessageXAssignmentBeenCreatedCourseX',
'MessageXAccountCreatedUpdatedLoginXPasswordX',
'MessageXPasswordUpdatedLoginXPasswordX',
'MessageXRequestedPasswordChange',
'MessageXReceivedNewPersonalMessages',
'MessageXNewUserXPendingApproval',
'MessageXXPostedForumXCourseX',
'MessageXXXCheckEmailConnectMoreInfo',
'MessageXXStudentXAnsweredTestX',
'MessageXXStudentXAnsweredTestXOpenQuestion',
'MessageXXStudentXAnsweredTestXVoiceQuestion',
'MessageXXAnswerOpenQuestionTestXReviewed',
'MessageXXNewThreadXStartedForumX',
'MessageXXNewAnswerPostedXForumX',
'MessageXXNewSystemAnnouncementAdded',
'MessageXTestXNewSystemAnnouncementAdded',
'MessageXXSystemAnnouncementUpdate',
'MessageXTestXSystemAnnouncementUpdate',
'MessageXUserXUploadedAssignmentXCourseXStudentSubmitsPaper',
'MessageXUserXUploadedAssignmentXCheckXStudentSubmitsPaper',
'MessageXUserXUploadedAssignmentXCourseX',
'MessageXUserXUploadedAssignmentXCheckX',
'MessageXSubscribedSessionX',
'MessageXSubscribedSessionXCSV',
'MessageXUserXSuggestedBeFriends',
'MessageXUserXAnsweredInboxMessage',
'MessageXBeenInvitedJoinGroupX',
'MessageXMessagesSentEditedGroupXEdited',
'MessageXMessagesSentEditedGroupXAdded',
'MessageXBeenInvitedCompleteSurveyXCourseX',
'MessageXReminderAssignmentXCourseXDue',
'MessageXUserDetailsModified',
];
}
/**
* install (installs the plugin).
*/
public function install()
{
$this->addMobilePhoneNumberField();
}
/**
* addMobilePhoneNumberField (adds a mobile phone number field if it is not
* already created).
*/
private function addMobilePhoneNumberField()
{
$extraField = new ExtraField('user');
$extraFieldInfo = $extraField->get_handler_field_info_by_field_variable('mobile_phone_number');
if (empty($extraFieldInfo)) {
$extraField->save([
'field_type' => 1,
'variable' => 'mobile_phone_number',
'display_text' => $this->get_lang('mobile_phone_number'),
'default_value' => null,
'field_order' => 2,
'visible' => 1,
'changeable' => 1,
'filter' => null,
]);
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class SmsPluginLibraryInterface.
*
* @author Julio Montoya
*/
interface SmsPluginLibraryInterface
{
/**
* getMobilePhoneNumberById (retrieves a user mobile phone number by user id).
*
* @param int $userId
*
* @return int User's mobile phone number
*/
public function getMobilePhoneNumberById($userId);
/**
* @param array $additionalParameters
*
* @return mixed
*/
public function send($additionalParameters);
/**
* @param array $additionalParameters
*
* @return mixed
*/
public function getSms($additionalParameters);
/**
* buildSms (builds an SMS from a template and data).
*
* @param object $plugin ClockworksmsPlugin object
* @param object $tpl Template object
* @param string $templateName Template file name
* @param string $messageKey Text key from lang file
* @param array $parameters Data to fill message variables (if any)
*
* @return object Template object with message property updated
*/
public function buildSms($plugin, $tpl, $templateName, $messageKey, $parameters = null);
}

View File

@@ -0,0 +1,89 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Sortable table which can be used for data available in an array.
*/
class SortableTableFromArray extends SortableTable
{
/**
* The array containing all data for this table.
*/
public $table_data;
public $handlePagination;
/**
* Constructor.
*
* @param array $table_data
* @param int $default_column
* @param int $default_items_per_page
* @param string $tableName
* @param string $get_total_number_function
* @param string $tableId
*/
public function __construct(
$table_data,
$default_column = 1,
$default_items_per_page = 20,
$tableName = 'tablename',
$get_total_number_function = null,
$tableId = ''
) {
parent::__construct(
$tableName,
$get_total_number_function,
null,
$default_column,
$default_items_per_page,
null,
$tableId
);
$this->table_data = $table_data;
$this->handlePagination = false;
}
/**
* Get table data to show on current page.
*
* @see SortableTable#get_table_data
*/
public function get_table_data(
$from = 1,
$per_page = null,
$column = null,
$direction = null,
$sort = true
) {
if ($sort) {
$content = TableSort::sort_table(
$this->table_data,
$this->column,
'ASC' === $this->direction ? SORT_ASC : SORT_DESC
);
} else {
$content = $this->table_data;
}
return array_slice($content, $from, $this->per_page);
}
/**
* Get total number of items.
*
* @see SortableTable#get_total_number_of_items
*/
public function get_total_number_of_items()
{
if (isset($this->total_number_of_items) && !empty($this->total_number_of_items) && $this->total_number_of_items != -1) {
return $this->total_number_of_items;
} else {
if (!empty($this->table_data)) {
return count($this->table_data);
}
return 0;
}
}
}

View File

@@ -0,0 +1,118 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Sortable table which can be used for data available in an array.
*
* Is a variation of SortableTableFromArray because we add 2 new arrays $column_show and $column_order
* $column_show is an array that lets us decide which are going to be the columns to show
* $column_order is an array that lets us decide the ordering of the columns
* i.e: $column_header=array('a','b','c','d','e'); $column_order=array(1,2,5,4,5);
* These means that the 3th column (letter "c") will be sort like the order we use in the 5th column
*/
class SortableTableFromArrayConfig extends SortableTable
{
/**
* The array containing the columns that will be show
* i.e $column_show=array('1','0','0'); we will show only the 1st column.
*/
private $column_show;
/**
* The array containing the real sort column
* $column_order=array('1''4','3','4');
* The 2nd column will be order like the 4th column.
*/
private $column_order;
private $doc_filter;
private $handlePagination = true;
/**
* Constructor.
*
* @param array $data All the information of the table
* @param int $column Default column that will be used in the sort functions
* @param int $itemsPerPage Number of items per pages that we are going to see
* @param string $tableName Name of the table
* @param array $columnShow An array with binary values: 1 = show column, 2 = don't show it
* @param array $columnOrder An array of integers that let us decide how the columns are going to be sort
* @param string $direction ASC/DESC
* @param bool $docFilter special modification to fix the document name order
*/
public function __construct(
$data,
$column = 1,
$itemsPerPage = 20,
$tableName = 'tablename',
$columnShow = [],
$columnOrder = [],
$direction = 'ASC',
$docFilter = false
) {
$this->column_show = $columnShow;
$this->column_order = $columnOrder;
$this->doc_filter = $docFilter;
// if data is empty the pagination is handled with query in database
if (empty($data)) {
$this->handlePagination = false;
}
parent::__construct(
$tableName,
null,
null,
$column,
$itemsPerPage,
$direction
);
$this->table_data = $data;
}
/**
* Get table data to show on current page.
*
* @see SortableTable#get_table_data
*/
public function get_table_data(
$from = 1,
$perPage = null,
$column = null,
$direction = null,
$sort = true
) {
$table = TableSort::sort_table_config(
$this->table_data,
$this->column,
'ASC' === $this->direction ? SORT_ASC : SORT_DESC,
$this->column_show,
$this->column_order,
SORT_REGULAR,
$this->doc_filter
);
if ($this->handlePagination) {
return array_slice($table, $from, $this->per_page);
}
return $table;
}
/**
* Get total number of items.
*
* @see SortableTable#get_total_number_of_items
*/
public function get_total_number_of_items()
{
if (!empty($this->total_number_of_items) && $this->total_number_of_items !== -1) {
return $this->total_number_of_items;
} else {
if (!empty($this->table_data)) {
return count($this->table_data);
}
return 0;
}
}
}

View File

@@ -0,0 +1,214 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CourseBundle\Entity\CItemProperty;
/**
* Class StudentFollowPage.
*/
class StudentFollowPage
{
public const VARIABLE_ACQUISITION = 'acquisition';
public const VARIABLE_INVISIBLE = 'invisible';
public static function getLpSubscription(
array $lpInfo,
int $studentId,
int $courseId,
int $sessionId = 0,
bool $showTeacherName = true
): string {
$em = Database::getManager();
if ($lpInfo['subscribe_users']) {
$itemRepo = $em->getRepository(CItemProperty::class);
$itemProperty = $itemRepo->findByUserSuscribedToItem(
'learnpath',
$lpInfo['iid'],
$studentId,
$courseId,
$sessionId
);
if (null === $itemProperty) {
$userGroups = GroupManager::getAllGroupPerUserSubscription($studentId, $courseId);
foreach ($userGroups as $groupInfo) {
$itemProperty = $itemRepo->findByGroupSuscribedToLp(
'learnpath',
$lpInfo['iid'],
$groupInfo['iid'],
$courseId,
$sessionId
);
if (null !== $itemProperty) {
break;
}
}
}
if (null === $itemProperty) {
return '-';
}
$formattedDate = api_convert_and_format_date($itemProperty->getInsertDate(), DATE_TIME_FORMAT_LONG);
if ($showTeacherName) {
$insertUser = $itemProperty->getInsertUser()->getId() !== $studentId
? $itemProperty->getInsertUser()->getCompleteName()
: '-';
return "$insertUser<br>".Display::tag('small', $formattedDate);
}
return $formattedDate;
}
$subscriptionEvent = Event::findUserSubscriptionToCourse($studentId, $courseId, $sessionId);
if (empty($subscriptionEvent)) {
return '-';
}
$formattedDate = api_convert_and_format_date($subscriptionEvent['default_date'], DATE_TIME_FORMAT_LONG);
if ($showTeacherName) {
$creator = api_get_user_entity($subscriptionEvent['default_user_id']);
return "{$creator->getCompleteName()}<br>".Display::tag('small', $formattedDate);
}
return $formattedDate;
}
public static function getLpAcquisition(
array $lpInfo,
int $studentId,
int $courseId,
int $sessionId = 0,
bool $allowEdit = false
): string {
$lpView = learnpath::findLastView($lpInfo['iid'], $studentId, $courseId, $sessionId, true);
$extraField = new ExtraField('lp_view');
$field = $extraField->get_handler_field_info_by_field_variable(self::VARIABLE_ACQUISITION);
$extraFieldValue = new ExtraFieldValue('lp_view');
$value = $extraFieldValue->get_values_by_handler_and_field_variable($lpView['iid'], self::VARIABLE_ACQUISITION);
$return = '';
if (empty($value)) {
$return .= '-';
} else {
$optionSelected = array_filter(
$field['options'],
function (array $option) use ($value) {
return $option['option_value'] == $value['value'];
}
);
if (empty($optionSelected)) {
$return .= '-';
} else {
$optionSelected = current($optionSelected);
$valueComment = json_decode($value['comment'], true);
$register = api_get_user_entity($valueComment['user']);
$return .= ExtraFieldOption::translateDisplayName($optionSelected['display_text']).'<br>'
.Display::tag('small', $register->getCompleteName()).'<br>'
.Display::tag(
'small',
api_convert_and_format_date($valueComment['datetime'], DATE_TIME_FORMAT_LONG)
).'<br>';
}
}
$editUrl = api_get_path(WEB_AJAX_PATH).'student_follow_page.ajax.php?'
.http_build_query(['lp_view' => $lpView['iid'], 'a' => 'form_adquisition']);
if ($allowEdit) {
$return .= Display::url(
Display::return_icon('edit.png', get_lang('Edit'), [], ICON_SIZE_TINY),
$editUrl,
['class' => 'ajax', 'data-title' => strip_tags($lpInfo['lp_name'])]
);
}
return '<div id="acquisition-'.$lpView['iid'].'">'.$return.'</div>';
}
public static function getLpVisibleScript()
{
$url = api_get_path(WEB_AJAX_PATH).'student_follow_page.ajax.php?'.http_build_query(['a' => 'views_invisible']);
return "<script>$(function () {
var chkbView = $('[name=\"chkb_view[]\"]');
function doRequest(element, state) {
element.prop('disabled', true);
var views = $.makeArray(element).map(function (input) { return input.value; });
return $.post('$url', { 'chkb_view[]': views, 'state': state }, function () { element.prop('disabled', false); });
}
$('[name=\"chkb_category[]\"]').on('change', function () {
var checked = this.checked;
var chkbs = $(this).parents('table').find('td :checkbox').each(function () { this.checked = checked; });
doRequest(chkbs, checked);
}).prop('checked', true);
chkbView.on('change', function () {
doRequest($(this), this.checked);
$(this).parents('table').find('th :checkbox').prop(
'checked',
$.makeArray($(this).parents('table').find('td :checkbox'))
.map(function (input) { return input.checked; })
.reduce(function (acc, cur) { return acc && cur; })
);
}).each(function () {
if (!this.checked) {
$(this).parents('table').find('th :checkbox').prop('checked', false);
}
});
});</script>";
}
public static function getLpVisibleField(array $lpInfo, int $studentId, int $courseId, int $sessionId = 0)
{
$attrs = [];
$isVisible = self::isViewVisible($lpInfo['iid'], $studentId, $courseId, $sessionId);
if (!$isVisible) {
$attrs['checked'] = 'checked';
}
return Display::input(
'checkbox',
'chkb_view[]',
implode('_', [$lpInfo['iid'], $studentId, $courseId, $sessionId]),
$attrs
);
}
public static function isViewVisible(int $lpId, int $studentId, int $courseId, int $sessionId): bool
{
$lpView = learnpath::findLastView($lpId, $studentId, $courseId, $sessionId);
if (empty($lpView)) {
return true;
}
$extraFieldValue = new ExtraFieldValue('lp_view');
$value = $extraFieldValue->get_values_by_handler_and_field_variable($lpView['iid'], self::VARIABLE_INVISIBLE);
return empty($value) || empty($value['value']);
}
}

View File

@@ -0,0 +1,111 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Library for generate a teacher time report.
*
* @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
*/
class TeacherTimeReport
{
/**
* The report data.
*
* @var array
*/
public $data = [];
/**
* Callback for compare sessions names.
*
* @param array $dataA The data A
* @param array $dataB The data B
*
* @return int returns -1 if dataA is less than dataB, 1 if dataA is greater than dataB, and 0 if they are equal
*/
public function compareSessions($dataA, $dataB)
{
return strnatcmp($dataA['session']['name'], $dataB['session']['name']);
}
/**
* Callback for compare courses names.
*
* @param array $dataA The datab A
* @param array $dataB The data B
*
* @return int returns -1 if dataA is less than dataB, 1 if dataA is greater than dataB, and 0 if they are equal
*/
public function compareCourses($dataA, $dataB)
{
return strnatcmp($dataA['course']['name'], $dataB['course']['name']);
}
/**
* Callback for compare coaches names.
*
* @param array $dataA The datab A
* @param array $dataB The data B
*
* @return int returns -1 if dataA is less than dataB, 1 if dataA is greater than dataB, and 0 if they are equal
*/
public function compareCoaches($dataA, $dataB)
{
return strnatcmp($dataA['coach']['complete_name'], $dataB['coach']['complete_name']);
}
/**
* Sort the report data.
*
* @param bool $withFilter Whether sort by sessions and courses
*/
public function sortData($withFilter = false)
{
if ($withFilter) {
uasort($this->data, [$this, 'compareSessions']);
uasort($this->data, [$this, 'compareCourses']);
}
uasort($this->data, [$this, 'compareCoaches']);
}
/**
* @param bool|false $withFilter
*
* @return array
*/
public function prepareDataToExport($withFilter = false)
{
$dataToExport = [];
if ($withFilter) {
$dataToExport[] = [
get_lang('Session'),
get_lang('Course'),
get_lang('Coach'),
get_lang('TotalTime'),
];
} else {
$dataToExport[] = [
get_lang('Coach'),
get_lang('TotalTime'),
];
}
foreach ($this->data as $row) {
$data = [];
if ($withFilter) {
$data[] = $row['session']['name'];
$data[] = $row['course']['name'];
}
$data[] = $row['coach']['complete_name'];
$data[] = $row['total_time'];
$dataToExport[] = $data;
}
return $dataToExport;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class UnserializeApi.
*/
class UnserializeApi
{
/**
* Unserialize content using Brummann\Polyfill\Unserialize.
*
* @param string $type
* @param string $serialized
*
* @return mixed
*/
public static function unserialize($type, $serialized, $ignoreErrors = false)
{
$allowedClasses = [];
switch ($type) {
case 'career':
case 'sequence_graph':
$allowedClasses = [
\Fhaculty\Graph\Graph::class,
\Fhaculty\Graph\Set\VerticesMap::class,
\Fhaculty\Graph\Set\Vertices::class,
\Fhaculty\Graph\Set\Edges::class,
\Fhaculty\Graph\Vertex::class,
\Fhaculty\Graph\Edge\Base::class,
\Fhaculty\Graph\Edge\Directed::class,
\Fhaculty\Graph\Edge\Undirected::class,
];
break;
case 'course':
$allowedClasses = [
\Chamilo\CourseBundle\Component\CourseCopy\Course::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Announcement::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Asset::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Attendance::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\CalendarEvent::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\CourseCopyLearnpath::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\CourseCopyTestCategory::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\CourseDescription::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\CourseSession::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Document::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Forum::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\ForumCategory::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\ForumPost::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\ForumTopic::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Glossary::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\GradeBookBackup::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\LearnPathCategory::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Link::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\LinkCategory::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Quiz::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\QuizQuestion::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\QuizQuestionOption::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\ScormDocument::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Survey::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\SurveyInvitation::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\SurveyQuestion::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Thematic::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\ToolIntro::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Wiki::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\Work::class,
\Chamilo\CourseBundle\Component\CourseCopy\Resources\XapiTool::class,
\Chamilo\CourseBundle\Entity\CLpCategory::class,
stdClass::class,
Category::class,
AttendanceLink::class,
DropboxLink::class,
Evaluation::class,
ExerciseLink::class,
ForumThreadLink::class,
LearnpathLink::class,
LinkFactory::class,
Result::class,
StudentPublicationLink::class,
SurveyLink::class,
];
// no break
case 'lp':
$allowedClasses = array_merge(
$allowedClasses,
[
learnpath::class,
learnpathItem::class,
aicc::class,
aiccBlock::class,
aiccItem::class,
aiccObjective::class,
aiccResource::class,
scorm::class,
scormItem::class,
scormMetadata::class,
scormOrganization::class,
scormResource::class,
Link::class,
LpItem::class,
]
);
break;
case 'not_allowed_classes':
default:
$allowedClasses = false;
}
if ($ignoreErrors) {
return @unserialize(
$serialized,
['allowed_classes' => $allowedClasses]
);
}
return unserialize(
$serialized,
['allowed_classes' => $allowedClasses]
);
}
}

View File

@@ -0,0 +1,96 @@
<?php
/* For licensing terms, see /license.txt */
/**
* VideoChat class.
*
* This class provides methods for video chat management.
*
* @author Angel Fernando Quiroz Campos <angel.quiroz@beeznest.com>
*/
class VideoChat
{
/**
* Get the video chat info by its users.
*
* @param int $user1 User id
* @param int $user2 Other user id
*
* @return array The video chat info. Otherwise return false
*/
public static function getChatRoomByUsers($user1, $user2)
{
$user1 = (int) $user1;
$user2 = (int) $user2;
if (empty($user1) || empty($user2)) {
return false;
}
return Database::select(
'*',
Database::get_main_table(TABLE_MAIN_CHAT_VIDEO),
[
'where' => [
'(from_user = ? AND to_user = ?)' => [$user1, $user2],
'OR (from_user = ? AND to_user = ?)' => [$user2, $user1],
],
],
'first'
);
}
/**
* Create a video chat.
*
* @param int $fromUser The sender user
* @param int $toUser The receiver user
*
* @return int The created video chat id. Otherwise return false
*/
public static function createRoom($fromUser, $toUser)
{
$fromUserInfo = api_get_user_info($fromUser);
$toUserInfo = api_get_user_info($toUser);
$chatName = vsprintf(
get_lang('VideoChatBetweenUserXAndUserY'),
[$fromUserInfo['firstname'], $toUserInfo['firstname']]
);
return Database::insert(
Database::get_main_table(TABLE_MAIN_CHAT_VIDEO),
[
'from_user' => $fromUser,
'to_user' => $toUser,
'room_name' => $chatName,
'datetime' => api_get_utc_datetime(),
]
);
}
/**
* Check if the video chat exists by its room name.
*
* @param string $name The video chat name
*
* @return bool
*/
public static function nameExists($name)
{
$resultData = Database::select(
'COUNT(1) AS count',
Database::get_main_table(TABLE_MAIN_CHAT_VIDEO),
[
'where' => ['room_name = ?' => $name],
],
'first'
);
if ($resultData !== false) {
return $resultData['count'] > 0;
}
return false;
}
}

View File

@@ -0,0 +1,67 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Definition of the Accessurleditcoursestourl class.
*/
/**
* Access_url_edit_courses_to_url class
* Contains several functions dealing with displaying,
* editing,... of a Access_url_edit_courses_to_url_functions.
*
* @version 1.0
*
* @author Toon Keppens <toon@vi-host.net>
* @author Julio Montoya - Cleaning code
* @author Ricardo Rodriguez - Separated the function and code
*/
class Accessurleditcoursestourl
{
/**
* Search for a list of available courses by title or code, based on
* a given string.
*
* @param string String to search for
* @param int Deprecated param
*
* @return xajaxResponse A formatted, xajax answer block
* @assert () === false
*/
public function search_courses($needle, $id)
{
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
$xajax_response = new xajaxResponse();
$return = '';
if (!empty($needle)) {
// xajax send utf8 datas... datas in db can be non-utf8 datas
$charset = api_get_system_encoding();
$needle = api_convert_encoding($needle, $charset, 'utf-8');
$needle = Database::escape_string($needle);
// search courses where username or firstname or lastname begins likes $needle
$sql = 'SELECT id, code, title FROM '.$tbl_course.' u '.
' WHERE (title LIKE "'.$needle.'%" '.
' OR code LIKE "'.$needle.'%" '.
' ) '.
' ORDER BY title, code '.
' LIMIT 11';
$rs = Database::query($sql);
$i = 0;
while ($course = Database::fetch_array($rs)) {
$i++;
if ($i <= 10) {
$return .= '<a href="javascript: void(0);" onclick="javascript: add_course_to_url('.addslashes($course['id']).',\''.addslashes($course['title']).' ('.addslashes($course['code']).')'.'\')">'.$course['title'].' ('.$course['code'].')</a><br />';
} else {
$return .= '...<br />';
}
}
}
$xajax_response->addAssign(
'ajax_list_courses',
'innerHTML',
api_utf8_encode($return)
);
return $xajax_response;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Definition of the Accessurleditsessiontourl class.
*/
require_once 'xajax/xajax.inc.php';
/**
* Accessurleditsessiontourl class
* Contains several functions dealing with displaying,
* editing,... of a Access_url_edit_session_to_url_functions.
*
* @version 1.0
*
* @author Toon Keppens <toon@vi-host.net>
* @author Julio Montoya - Cleaning code
* @author Ricardo Rodriguez - Separated the function and code
*/
class Accessurleditsessionstourl
{
/**
* Search sessions by name, based on a search string.
*
* @param string Search string
* @param int Deprecated param
*
* @return string Xajax response block
* @assert () === false
*/
public function search_sessions($needle, $id)
{
$tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
$xajax_response = new xajaxResponse();
$return = '';
if (!empty($needle)) {
// xajax send utf8 datas... datas in db can be non-utf8 datas
$charset = api_get_system_encoding();
$needle = api_convert_encoding($needle, $charset, 'utf-8');
$needle = Database::escape_string($needle);
// search sessiones where username or firstname or lastname begins likes $needle
$sql = 'SELECT id, name FROM '.$tbl_session.' u
WHERE (name LIKE "'.$needle.'%")
ORDER BY name, id
LIMIT 11';
$rs = Database::query($sql);
$i = 0;
while ($session = Database::fetch_array($rs)) {
$i++;
if ($i <= 10) {
$return .= '<a href="#" onclick="add_user_to_url(\''.addslashes($session['id']).'\',\''.addslashes($session['name']).' ('.addslashes($session['id']).')'.'\')">'.$session['name'].' </a><br />';
} else {
$return .= '...<br />';
}
}
}
$xajax_response->addAssign(
'ajax_list_courses',
'innerHTML',
api_utf8_encode($return)
);
return $xajax_response;
}
}

View File

@@ -0,0 +1,65 @@
<?php
/* For licensing terms, see /license.txt */
/**
* AccessUrlEditUsersToUrl class definition
* Contains several functions dealing with displaying,
* editing,... of a Access_url_edit_users_to_url_functions.
*
* @version 1.0
*
* @author Toon Keppens <toon@vi-host.net>
* @author Julio Montoya - Cleaning code
* @author Ricardo Rodriguez - Separated the function and code
*/
class AccessUrlEditUsersToUrl
{
/**
* Search users by username, firstname or lastname, based on the given
* search string.
*
* @param string Search string
* @param int Deprecated param
*
* @return xajaxResponse Xajax response block
* @assert () === false
*/
public static function search_users($needle, $id)
{
$tbl_user = Database::get_main_table(TABLE_MAIN_USER);
$xajax_response = new xajaxResponse();
$return = '';
if (!empty($needle)) {
// xajax send utf8 datas... datas in db can be non-utf8 datas
$charset = api_get_system_encoding();
$needle = api_convert_encoding($needle, $charset, 'utf-8');
$needle = Database::escape_string($needle);
// search users where username or firstname or lastname begins likes $needle
$order_clause = api_sort_by_first_name() ? ' ORDER BY firstname, lastname, username' : ' ORDER BY lastname, firstname, username';
$sql = 'SELECT u.user_id, username, lastname, firstname FROM '.$tbl_user.' u '.
' WHERE (username LIKE "'.$needle.'%" '.
' OR firstname LIKE "'.$needle.'%" '.
' OR lastname LIKE "'.$needle.'%") '.
$order_clause.
' LIMIT 11';
$rs = Database::query($sql);
$i = 0;
while ($user = Database::fetch_array($rs)) {
$i++;
if ($i <= 10) {
$return .= '<a href="javascript: void(0);" onclick="javascript: add_user_to_url(\''.addslashes($user['user_id']).'\',\''.api_get_person_name(addslashes($user['firstname']), addslashes($user['lastname'])).' ('.addslashes($user['username']).')'.'\')">'.api_get_person_name($user['firstname'], $user['lastname']).' ('.$user['username'].')</a><br />';
} else {
$return .= '...<br />';
}
}
}
$xajax_response->addAssign(
'ajax_list_users',
'innerHTML',
api_utf8_encode($return)
);
return $xajax_response;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,132 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/**
* Class AddCourseToSession.
*/
class AddCourseToSession
{
/**
* Searches a course, given a search string and a type of search box.
*
* @param string $needle Search string
* @param string $type Type of search box ('single' or anything else)
* @param int $id_session
*
* @return xajaxResponse XajaxResponse
* @assert ('abc', 'single') !== null
* @assert ('abc', 'multiple') !== null
*/
public static function search_courses($needle, $type, $id_session)
{
$tbl_session_rel_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
// Session value set in file add_courses_to_session.php
$id_session = (int) $id_session;
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
$course_title = null;
$xajax_response = new xajaxResponse();
$return = '';
if (!empty($needle) && !empty($type)) {
// xajax send utf8 datas... datas in db can be non-utf8 datas
$charset = api_get_system_encoding();
$needle = api_convert_encoding($needle, $charset, 'utf-8');
$needle = Database::escape_string($needle);
$cond_course_code = '';
if (!empty($id_session)) {
$id_session = (int) $id_session;
// check course_code from session_rel_course table
$sql = 'SELECT c_id FROM '.$tbl_session_rel_course.'
WHERE session_id = '.$id_session;
$res = Database::query($sql);
$course_codes = '';
if (Database::num_rows($res) > 0) {
while ($row = Database::fetch_row($res)) {
$course_codes .= '\''.$row[0].'\',';
}
$course_codes = substr($course_codes, 0, (strlen($course_codes) - 1));
$cond_course_code = ' AND course.id NOT IN('.$course_codes.') ';
}
}
if ('single' == $type) {
// search users where username or firstname or lastname begins likes $needle
$sql = 'SELECT
course.id,
course.visual_code,
course.title,
session_rel_course.session_id
FROM '.$tbl_course.' course
LEFT JOIN '.$tbl_session_rel_course.' session_rel_course
ON course.id = session_rel_course.c_id
AND session_rel_course.session_id = '.intval($id_session).'
WHERE
course.visual_code LIKE "'.$needle.'%" OR
course.title LIKE "'.$needle.'%"';
} else {
$sql = 'SELECT course.id, course.visual_code, course.title
FROM '.$tbl_course.' course
WHERE
course.visual_code LIKE "'.$needle.'%" '.$cond_course_code.'
ORDER BY course.code ';
}
if (api_is_multiple_url_enabled()) {
$tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
$access_url_id = api_get_current_access_url_id();
if (-1 != $access_url_id) {
if ('single' == $type) {
$sql = 'SELECT
course.id,
course.visual_code,
course.title,
session_rel_course.session_id
FROM '.$tbl_course.' course
LEFT JOIN '.$tbl_session_rel_course.' session_rel_course
ON course.id = session_rel_course.c_id
AND session_rel_course.session_id = '.intval($id_session).'
INNER JOIN '.$tbl_course_rel_access_url.' url_course
ON (url_course.c_id = course.id)
WHERE
access_url_id = '.$access_url_id.' AND
(course.visual_code LIKE "'.$needle.'%" OR
course.title LIKE "'.$needle.'%" )';
} else {
$sql = 'SELECT course.id, course.visual_code, course.title
FROM '.$tbl_course.' course, '.$tbl_course_rel_access_url.' url_course
WHERE
url_course.c_id = course.id AND
access_url_id = '.$access_url_id.' AND
course.visual_code LIKE "'.$needle.'%" '.$cond_course_code.'
ORDER BY course.code ';
}
}
}
$rs = Database::query($sql);
$course_list = [];
if ('single' == $type) {
while ($course = Database::fetch_array($rs)) {
$course_list[] = $course['id'];
$course_title = str_replace("'", "\'", $course_title);
$return .= '<a href="javascript: void(0);" onclick="javascript: add_course_to_session(\''.$course['id'].'\',\''.$course_title.' ('.$course['visual_code'].')'.'\')">'.$course['title'].' ('.$course['visual_code'].')</a><br />';
}
$xajax_response->addAssign('ajax_list_courses_single', 'innerHTML', api_utf8_encode($return));
} else {
$return .= '<select id="origin" name="NoSessionCoursesList[]" multiple="multiple" size="20" style="width:340px;">';
while ($course = Database::fetch_array($rs)) {
$course_list[] = $course['id'];
$course_title = str_replace("'", "\'", $course_title);
$return .= '<option value="'.$course['id'].'" title="'.htmlspecialchars($course['title'].' ('.$course['visual_code'].')', ENT_QUOTES).'">'.$course['title'].' ('.$course['visual_code'].')</option>';
}
$return .= '</select>';
$xajax_response->addAssign('ajax_list_courses_multiple', 'innerHTML', api_utf8_encode($return));
}
}
Session::write('course_list', $course_list);
return $xajax_response;
}
}

5386
main/inc/lib/agenda.lib.php Normal file

File diff suppressed because it is too large Load Diff

10701
main/inc/lib/api.lib.php Normal file

File diff suppressed because it is too large Load Diff

106
main/inc/lib/app_view.php Normal file
View File

@@ -0,0 +1,106 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class View.
*
* @deprecated use Template class
*/
class View
{
private $data;
private $template;
private $layout;
private $tool_path;
/**
* Constructor, init tool path for rendering.
*
* @deprecated
*
* @param string $toolname tool name (optional)
* @param string $template_path
*/
public function __construct($toolname = '', $template_path = null)
{
if (!empty($toolname)) {
if (isset($template_path)) {
$path = $template_path.$toolname.'/';
} else {
$path = api_get_path(SYS_CODE_PATH).$toolname.'/';
}
if (is_dir($path)) {
$this->tool_path = $path;
} else {
throw new Exception('View::__construct() $path directory does not exist '.$path);
}
}
}
/**
* Set data sent from a controller.
*
* @param array data
*/
public function set_data($data)
{
if (!is_array($data)) {
throw new Exception('View::set_data() $data must to be an array, you have sent a'.gettype($data));
}
$this->data = $data;
}
/**
* Set layout view sent from a controller.
*
* @param string $layout view
*/
public function set_layout($layout)
{
$this->layout = $layout;
}
/**
* Set template view sent from a controller.
*
* @param string $template view
*/
public function set_template($template)
{
$this->template = $template;
}
/**
* Render data to the template and layout views.
*/
public function render()
{
$content = $this->render_template();
$target = $this->tool_path.$this->layout.'.php';
if (file_exists($target)) {
require_once $target;
} else {
throw new Exception('View::render() invalid file path '.$target);
}
}
/**
* It's used into render method for rendering data in the template and layout views.
*
* @return string Rendered template (as HTML, most of the time)
*/
private function render_template()
{
$target = $this->tool_path.$this->template.'.php';
if (file_exists($target)) {
ob_start();
@extract($this->data, EXTR_OVERWRITE); //pass the $this->data array into local scope
require_once $target;
$content = ob_get_clean();
return $content;
} else {
throw new Exception('View::render_template() invalid file path '.$target);
}
}
}

152
main/inc/lib/array.lib.php Normal file
View File

@@ -0,0 +1,152 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This is the array library for Chamilo.
* Include/require it in your code to use its functionality.
*/
/**
* Removes duplicate values from a dimensional array.
*
* @param array $array dimensional array
*
* @return array an array with unique values
*/
function array_unique_dimensional($array)
{
if (!is_array($array)) {
return $array;
}
foreach ($array as &$myvalue) {
$myvalue = serialize($myvalue);
}
$array = array_unique($array);
foreach ($array as &$myvalue) {
$myvalue = UnserializeApi::unserialize('not_allowed_clases', $myvalue);
}
return $array;
}
/**
* Sort multidimensional arrays.
*
* @param array unsorted multidimensional array
* @param string key to be sorted
*
* @return array result array
*
* @author found in http://php.net/manual/en/function.sort.php
*/
function msort($array, $id = 'id', $order = 'desc')
{
if (empty($array)) {
return $array;
}
$temp_array = [];
while (count($array) > 0) {
$lowest_id = 0;
$index = 0;
foreach ($array as $item) {
if ('desc' == $order) {
if (strip_tags($item[$id]) < strip_tags($array[$lowest_id][$id])) {
$lowest_id = $index;
}
} else {
if (isset($item[$id]) && strip_tags($item[$id]) > strip_tags($array[$lowest_id][$id])) {
$lowest_id = $index;
}
}
$index++;
}
$temp_array[] = $array[$lowest_id];
$array = array_merge(
array_slice($array, 0, $lowest_id),
array_slice($array, $lowest_id + 1)
);
}
return $temp_array;
}
/**
* @param $array
*
* @return mixed
*/
function utf8_sort($array)
{
$old_locale = setlocale(LC_ALL, 0);
$code = api_get_language_isocode();
$locale_list = [$code.'.utf8', 'en.utf8', 'en_US.utf8', 'en_GB.utf8'];
$try_sort = false;
foreach ($locale_list as $locale) {
$my_local = setlocale(LC_COLLATE, $locale);
if ($my_local) {
$try_sort = true;
break;
}
}
if ($try_sort) {
uasort($array, 'strcoll');
}
setlocale(LC_COLLATE, $old_locale);
return $array;
}
/**
* @param array $array
* @param string $separator
*
* @return string
*/
function array_to_string($array, $separator = ',')
{
if (empty($array)) {
return '';
}
return implode($separator.' ', $array);
}
/**
* @return array
*/
function array_flatten(array $array)
{
$flatten = [];
array_walk_recursive(
$array,
function ($value) use (&$flatten) {
$flatten[] = $value;
}
);
return $flatten;
}
/**
* Shuffles an array keeping the associations.
*
* @param $array
*
* @return bool
*/
function shuffle_assoc(&$array)
{
$keys = array_keys($array);
shuffle($keys);
$new = [];
foreach ($keys as $key) {
$new[$key] = $array[$key];
}
$array = $new;
return true;
}

File diff suppressed because it is too large Load Diff

399
main/inc/lib/auth.lib.php Normal file
View File

@@ -0,0 +1,399 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class Auth
* Auth can be used to instantiate objects or as a library to manage courses
* This file contains a class used like library provides functions for auth tool.
* It's also used like model to courses_controller (MVC pattern).
*
* @author Christian Fasanando <christian1827@gmail.com>
*/
class Auth
{
/**
* Constructor.
*/
public function __construct()
{
}
/**
* This function get all the courses in the particular user category.
*
* @param bool $hidePrivate
*
* @return array
*/
public function getCoursesInCategory($hidePrivate = true)
{
$user_id = api_get_user_id();
$TABLECOURS = Database::get_main_table(TABLE_MAIN_COURSE);
$TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
$avoidCoursesCondition = CoursesAndSessionsCatalog::getAvoidCourseCondition();
$showCoursesCondition = CoursesAndSessionsCatalog::getCoursesToShowInCatalogueCondition();
$visibilityCondition = CourseManager::getCourseVisibilitySQLCondition('course', true, $hidePrivate);
$sql = "SELECT
course.id as real_id,
course.code, course.visual_code, course.subscribe subscr, course.unsubscribe unsubscr,
course.title title, course.tutor_name tutor, course.directory, course_rel_user.status status,
course_rel_user.sort sort, course_rel_user.user_course_cat user_course_cat
FROM $TABLECOURS course,
$TABLECOURSUSER course_rel_user
WHERE
course.id = course_rel_user.c_id AND
course_rel_user.user_id = '".$user_id."' AND
course_rel_user.relation_type <> ".COURSE_RELATION_TYPE_RRHH."
$avoidCoursesCondition
$showCoursesCondition
$visibilityCondition
ORDER BY course_rel_user.user_course_cat, course_rel_user.sort ASC";
$result = Database::query($sql);
$data = [];
while ($course = Database::fetch_array($result)) {
$data[$course['user_course_cat']][] = $course;
}
return $data;
}
/**
* stores the changes in a course category
* (moving a course to a different course category).
*
* @param int $courseId
* @param int Category id
*
* @return bool True if it success
*/
public function updateCourseCategory($courseId, $newcategory)
{
$courseId = (int) $courseId;
$newcategory = (int) $newcategory;
$current_user = api_get_user_id();
$table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
$max_sort_value = api_max_sort_value($newcategory, $current_user);
$sql = "UPDATE $table SET
user_course_cat='".$newcategory."',
sort='".($max_sort_value + 1)."'
WHERE
c_id ='".$courseId."' AND
user_id='".$current_user."' AND
relation_type<>".COURSE_RELATION_TYPE_RRHH;
$resultQuery = Database::query($sql);
$result = false;
if (Database::affected_rows($resultQuery)) {
$result = true;
}
return $result;
}
/**
* moves the course one place up or down.
*
* @param string Direction (up/down)
* @param string Course code
* @param int Category id
*
* @return bool True if it success
*/
public function move_course($direction, $course2move, $category)
{
$table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
$current_user_id = api_get_user_id();
$all_user_courses = CourseManager::getCoursesByUserCourseCategory($current_user_id);
// we need only the courses of the category we are moving in
$user_courses = [];
foreach ($all_user_courses as $key => $course) {
if ($course['user_course_category'] == $category) {
$user_courses[] = $course;
}
}
$target_course = [];
foreach ($user_courses as $count => $course) {
if ($course2move == $course['code']) {
// source_course is the course where we clicked the up or down icon
$source_course = $course;
// target_course is the course before/after the source_course (depending on the up/down icon)
if ('up' == $direction) {
$target_course = $user_courses[$count - 1];
} else {
$target_course = $user_courses[$count + 1];
}
break;
}
}
$result = false;
if (count($target_course) > 0 && count($source_course) > 0) {
$courseInfo = api_get_course_info($source_course['code']);
$courseId = $courseInfo['real_id'];
$targetCourseInfo = api_get_course_info($target_course['code']);
$targetCourseId = $targetCourseInfo['real_id'];
$sql = "UPDATE $table
SET sort='".$target_course['sort']."'
WHERE
c_id = '".$courseId."' AND
user_id = '".$current_user_id."' AND
relation_type<>".COURSE_RELATION_TYPE_RRHH;
$result1 = Database::query($sql);
$sql = "UPDATE $table SET sort='".$source_course['sort']."'
WHERE
c_id ='".$targetCourseId."' AND
user_id='".$current_user_id."' AND
relation_type<>".COURSE_RELATION_TYPE_RRHH;
$result2 = Database::query($sql);
if (Database::affected_rows($result1) && Database::affected_rows($result2)) {
$result = true;
}
}
return $result;
}
/**
* Moves the course one place up or down.
*
* @param string $direction Direction up/down
* @param string $category2move Category id
*
* @return bool True If it success
*/
public function move_category($direction, $category2move)
{
$userId = api_get_user_id();
$userCategories = CourseManager::get_user_course_categories($userId);
$categories = array_values($userCategories);
$previous = null;
$target_category = [];
foreach ($categories as $key => $category) {
$category_id = $category['id'];
if ($category2move == $category_id) {
// source_course is the course where we clicked the up or down icon
$source_category = $userCategories[$category2move];
// target_course is the course before/after the source_course (depending on the up/down icon)
if ('up' == $direction) {
if (isset($categories[$key - 1])) {
$target_category = $userCategories[$categories[$key - 1]['id']];
}
} else {
if (isset($categories[$key + 1])) {
$target_category = $userCategories[$categories[$key + 1]['id']];
}
}
}
}
$result = false;
if (count($target_category) > 0 && count($source_category) > 0) {
$table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
$sql = "UPDATE $table SET
sort = '".Database::escape_string($target_category['sort'])."'
WHERE id='".intval($source_category['id'])."' AND user_id='".$userId."'";
$resultFirst = Database::query($sql);
$sql = "UPDATE $table SET
sort = '".Database::escape_string($source_category['sort'])."'
WHERE id='".intval($target_category['id'])."' AND user_id='".$userId."'";
$resultSecond = Database::query($sql);
if (Database::affected_rows($resultFirst) && Database::affected_rows($resultSecond)) {
$result = true;
}
}
return $result;
}
/**
* Updates the user course category in the chamilo_user database.
*
* @param string Category title
* @param int Category id
*
* @return bool True if it success
*/
public function store_edit_course_category($title, $category_id)
{
$title = Database::escape_string($title);
$category_id = (int) $category_id;
$result = false;
$table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
$sql = "UPDATE $table
SET title='".api_htmlentities($title, ENT_QUOTES, api_get_system_encoding())."'
WHERE id='".$category_id."'";
$resultQuery = Database::query($sql);
if (Database::affected_rows($resultQuery)) {
$result = true;
}
return $result;
}
/**
* deletes a course category and moves all the courses that were in this category to main category.
*
* @param int Category id
*
* @return bool True if it success
*/
public function delete_course_category($category_id)
{
$current_user_id = api_get_user_id();
$tucc = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
$TABLECOURSUSER = Database::get_main_table(TABLE_MAIN_COURSE_USER);
$category_id = (int) $category_id;
$result = false;
$sql = "DELETE FROM $tucc
WHERE
id='".$category_id."' AND
user_id='".$current_user_id."'";
$resultQuery = Database::query($sql);
if (Database::affected_rows($resultQuery)) {
$result = true;
}
$sql = "UPDATE $TABLECOURSUSER
SET user_course_cat='0'
WHERE
user_course_cat='".$category_id."' AND
user_id='".$current_user_id."' AND
relation_type<>".COURSE_RELATION_TYPE_RRHH." ";
Database::query($sql);
return $result;
}
/**
* @param int $categoryId
*
* @return array|mixed
*/
public function getUserCourseCategory($categoryId)
{
$userId = api_get_user_id();
$tucc = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
$categoryId = (int) $categoryId;
$sql = "SELECT * FROM $tucc
WHERE
id= $categoryId AND
user_id= $userId";
$resultQuery = Database::query($sql);
return Database::fetch_array($resultQuery, 'ASSOC');
}
/**
* unsubscribe the user from a given course.
*
* @param string $course_code
*
* @return bool True if it success
*/
public function remove_user_from_course($course_code, $sessionId = 0)
{
$tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
// protect variables
$current_user_id = api_get_user_id();
$course_code = Database::escape_string($course_code);
$courseInfo = api_get_course_info($course_code);
if (empty($courseInfo) || empty($current_user_id)) {
return false;
}
// Check if course can be unsubscribe.
if ('1' !== $courseInfo['unsubscribe']) {
return false;
}
$courseId = $courseInfo['real_id'];
// we check (once again) if the user is not course administrator
// because the course administrator cannot unsubscribe himself
// (s)he can only delete the course
$sql = "SELECT * FROM $tbl_course_user
WHERE
user_id='".$current_user_id."' AND
c_id ='".$courseId."' AND
status='1' ";
$result_check = Database::query($sql);
$number_of_rows = Database::num_rows($result_check);
$result = true;
if ($number_of_rows > 0) {
$result = false;
}
if ($result) {
CourseManager::unsubscribe_user($current_user_id, $course_code, $sessionId);
}
return $result;
}
/**
* stores the user course category in the chamilo_user database.
*
* @param string Category title
*
* @return bool True if it success
*/
public function store_course_category($category_title)
{
$table = Database::get_main_table(TABLE_USER_COURSE_CATEGORY);
// protect data
$current_user_id = api_get_user_id();
$category_title = Database::escape_string($category_title);
// step 1: we determine the max value of the user defined course categories
$sql = "SELECT sort FROM $table
WHERE user_id='".$current_user_id."'
ORDER BY sort DESC";
$rs_sort = Database::query($sql);
$maxsort = Database::fetch_array($rs_sort);
$nextsort = $maxsort['sort'] + 1;
// step 2: we check if there is already a category with this name,
// if not we store it, else we give an error.
$sql = "SELECT * FROM $table
WHERE
user_id='".$current_user_id."' AND
title='".$category_title."'
ORDER BY sort DESC";
$rs = Database::query($sql);
$result = false;
if (0 == Database::num_rows($rs)) {
$sql = "INSERT INTO $table (user_id, title,sort)
VALUES ('".$current_user_id."', '".api_htmlentities(
$category_title,
ENT_QUOTES,
api_get_system_encoding()
)."', '".$nextsort."')";
$resultQuery = Database::query($sql);
if (Database::affected_rows($resultQuery)) {
$result = true;
}
}
return $result;
}
}

176
main/inc/lib/baker.lib.php Normal file
View File

@@ -0,0 +1,176 @@
<?php
/**
* Php library to Bake the PNG Images.
*/
class PNGImageBaker
{
private $_contents;
private $_size;
private $_chunks;
/**
* Prepares file for handling metadata.
* Verifies that this file is a valid PNG file.
* Unpacks file chunks and reads them into an array.
*
* @param string $contents File content as a string
*/
public function __construct($contents)
{
$this->_contents = $contents;
$png_signature = pack("C8", 137, 80, 78, 71, 13, 10, 26, 10);
// Read 8 bytes of PNG header and verify.
$header = substr($this->_contents, 0, 8);
if ($header != $png_signature) {
echo 'This is not a valid PNG image';
}
$this->_size = strlen($this->_contents);
$this->_chunks = [];
// Skip 8 bytes of IHDR image header.
$position = 8;
do {
$chunk = @unpack('Nsize/a4type', substr($this->_contents, $position, 8));
$this->_chunks[$chunk['type']][] = substr($this->_contents, $position + 8, $chunk['size']);
// Skip 12 bytes chunk overhead.
$position += $chunk['size'] + 12;
} while ($position < $this->_size);
}
/**
* Checks if a key already exists in the chunk of said type.
* We need to avoid writing same keyword into file chunks.
*
* @param string $type chunk type, like iTXt, tEXt, etc
* @param string $check keyword that needs to be checked
*
* @return bool (true|false) True if file is safe to write this keyword, false otherwise
*/
public function checkChunks($type, $check)
{
if (array_key_exists($type, $this->_chunks)) {
foreach (array_keys($this->_chunks[$type]) as $typekey) {
list($key, $data) = explode("\0", $this->_chunks[$type][$typekey]);
if (0 == strcmp($key, $check)) {
echo 'Key "'.$check.'" already exists in "'.$type.'" chunk.';
return false;
}
}
}
return true;
}
/**
* Add a chunk by type with given key and text.
*
* @param string $chunkType chunk type, like iTXt, tEXt, etc
* @param string $key keyword that needs to be added
* @param string $value currently an assertion URL that is added to an image metadata
*
* @return string $result file content with a new chunk as a string
*/
public function addChunk($chunkType, $key, $value)
{
$chunkData = $key."\0".$value;
$crc = pack("N", crc32($chunkType.$chunkData));
$len = pack("N", strlen($chunkData));
$newChunk = $len.$chunkType.$chunkData.$crc;
$result = substr($this->_contents, 0, $this->_size - 12)
.$newChunk
.substr($this->_contents, $this->_size - 12, 12);
return $result;
}
/**
* removes a chunk by type with given key and text.
*
* @param string $chunkType chunk type, like iTXt, tEXt, etc
* @param string $key keyword that needs to be deleted
* @param string $png the png image
*
* @return string $result new File content
*/
public function removeChunks($chunkType, $key, $png)
{
// Read the magic bytes and verify
$retval = substr($png, 0, 8);
$ipos = 8;
if ($retval != "\x89PNG\x0d\x0a\x1a\x0a") {
throw new Exception('Is not a valid PNG image');
}
// Loop through the chunks. Byte 0-3 is length, Byte 4-7 is type
$chunkHeader = substr($png, $ipos, 8);
$ipos = $ipos + 8;
while ($chunkHeader) {
// Extract length and type from binary data
$chunk = @unpack('Nsize/a4type', $chunkHeader);
$skip = false;
if ($chunk['type'] == $chunkType) {
$data = substr($png, $ipos, $chunk['size']);
$sections = explode("\0", $data);
print_r($sections);
if ($sections[0] == $key) {
$skip = true;
}
}
// Extract the data and the CRC
$data = substr($png, $ipos, $chunk['size'] + 4);
$ipos = $ipos + $chunk['size'] + 4;
// Add in the header, data, and CRC
if (!$skip) {
$retval = $retval.$chunkHeader.$data;
}
// Read next chunk header
$chunkHeader = substr($png, $ipos, 8);
$ipos = $ipos + 8;
}
return $retval;
}
/**
* Extracts the baked PNG info by the Key.
*
* @param string $png the png image
* @param string $key keyword that needs to be searched
*
* @return mixed - If there is an error - boolean false is returned
* If there is PNG information that matches the key an array is returned
*/
public function extractBadgeInfo($png, $key = 'openbadges')
{
// Read the magic bytes and verify
$retval = substr($png, 0, 8);
$ipos = 8;
if ("\x89PNG\x0d\x0a\x1a\x0a" != $retval) {
return false;
}
// Loop through the chunks. Byte 0-3 is length, Byte 4-7 is type
$chunkHeader = substr($png, $ipos, 8);
$ipos = $ipos + 8;
while ($chunkHeader) {
// Extract length and type from binary data
$chunk = @unpack('Nsize/a4type', $chunkHeader);
$skip = false;
if ('tEXt' == $chunk['type']) {
$data = substr($png, $ipos, $chunk['size']);
$sections = explode("\0", $data);
if ($sections[0] == $key) {
return $sections;
}
}
// Extract the data and the CRC
$data = substr($png, $ipos, $chunk['size'] + 4);
$ipos = $ipos + $chunk['size'] + 4;
// Read next chunk header
$chunkHeader = substr($png, $ipos, 8);
$ipos = $ipos + 8;
}
}
}

1085
main/inc/lib/banner.lib.php Normal file

File diff suppressed because it is too large Load Diff

3306
main/inc/lib/blog.lib.php Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1570
main/inc/lib/career.lib.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,955 @@
<?php
/* For licensing terms, see /license.txt */
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\QrCode;
/**
* Certificate Class
* Generate certificates based in the gradebook tool.
*/
class Certificate extends Model
{
public $table;
public $columns = [
'id',
'cat_id',
'score_certificate',
'created_at',
'path_certificate',
];
/**
* Certification data.
*/
public $certificate_data = [];
/**
* Student's certification path.
*/
public $certification_user_path = null;
public $certification_web_user_path = null;
public $html_file = null;
public $qr_file = null;
public $user_id;
/** If true every time we enter to the certificate URL
* we would generate a new certificate (good thing because we can edit the
* certificate and all users will have the latest certificate bad because we.
* load the certificate every time */
public $force_certificate_generation = true;
/**
* Constructor.
*
* @param int $certificate_id ID of the certificate
* @param int $userId
* @param bool $sendNotification send message to student
* @param bool $updateCertificateData
*
* If no ID given, take user_id and try to generate one
*/
public function __construct(
$certificate_id = 0,
$userId = 0,
$sendNotification = false,
$updateCertificateData = true
) {
$this->table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
$this->user_id = !empty($userId) ? $userId : api_get_user_id();
if (!empty($certificate_id)) {
$certificate = $this->get($certificate_id);
if (!empty($certificate) && is_array($certificate)) {
$this->certificate_data = $certificate;
$this->user_id = $this->certificate_data['user_id'];
}
}
if ($this->user_id) {
// Need to be called before any operation
$this->check_certificate_path();
// To force certification generation
if ($this->force_certificate_generation) {
$this->generate([], $sendNotification);
}
if (isset($this->certificate_data) && $this->certificate_data) {
if (empty($this->certificate_data['path_certificate'])) {
$this->generate([], $sendNotification);
}
}
}
// Setting the qr and html variables
if (isset($certificate_id) &&
!empty($this->certification_user_path) &&
isset($this->certificate_data['path_certificate'])
) {
$pathinfo = pathinfo($this->certificate_data['path_certificate']);
$this->html_file = $this->certification_user_path.basename($this->certificate_data['path_certificate']);
$this->qr_file = $this->certification_user_path.$pathinfo['filename'].'_qr.png';
} else {
$this->check_certificate_path();
if (api_get_configuration_value('allow_general_certificate')) {
// General certificate
$name = md5($this->user_id).'.html';
$my_path_certificate = $this->certification_user_path.$name;
$path_certificate = '/'.$name;
// Getting QR filename
$file_info = pathinfo($path_certificate);
$content = $this->generateCustomCertificate();
$my_new_content_html = str_replace(
'((certificate_barcode))',
Display::img(
$this->certification_web_user_path.$file_info['filename'].'_qr.png',
'QR'
),
$content
);
$my_new_content_html = mb_convert_encoding(
$my_new_content_html,
'UTF-8',
api_get_system_encoding()
);
$this->html_file = $my_path_certificate;
$result = @file_put_contents($my_path_certificate, $my_new_content_html);
if ($result) {
// Updating the path
self::updateUserCertificateInfo(
0,
$this->user_id,
$path_certificate,
$updateCertificateData
);
$this->certificate_data['path_certificate'] = $path_certificate;
}
}
}
}
/**
* Checks if the certificate user path directory is created.
*/
public function check_certificate_path()
{
$this->certification_user_path = null;
// Setting certification path
$path_info = UserManager::getUserPathById($this->user_id, 'system');
$web_path_info = UserManager::getUserPathById($this->user_id, 'web');
if (!empty($path_info) && isset($path_info)) {
$this->certification_user_path = $path_info.'certificate/';
$this->certification_web_user_path = $web_path_info.'certificate/';
$mode = api_get_permissions_for_new_directories();
if (!is_dir($path_info)) {
mkdir($path_info, $mode, true);
}
if (!is_dir($this->certification_user_path)) {
mkdir($this->certification_user_path, $mode);
}
}
}
/**
* Deletes the current certificate object. This is generally triggered by
* the teacher from the gradebook tool to re-generate the certificate because
* the original version wa flawed.
*
* @param bool $force_delete
*
* @return bool
*/
public function delete($force_delete = false)
{
$delete_db = false;
if (!empty($this->certificate_data)) {
if (!is_null($this->html_file) || $this->html_file != '' || strlen($this->html_file)) {
// Deleting HTML file
if (is_file($this->html_file)) {
@unlink($this->html_file);
if (is_file($this->html_file) === false) {
$delete_db = true;
} else {
$delete_db = false;
}
}
// Deleting QR code PNG image file
if (is_file($this->qr_file)) {
@unlink($this->qr_file);
}
if ($delete_db || $force_delete) {
return parent::delete($this->certificate_data['id']);
}
} else {
return parent::delete($this->certificate_data['id']);
}
}
return false;
}
/**
* Generates an HTML Certificate and fills the path_certificate field in the DB.
*
* @param array $params
* @param bool $sendNotification
*
* @return bool|int
*/
public function generate($params = [], $sendNotification = false)
{
// The user directory should be set
if (empty($this->certification_user_path) &&
$this->force_certificate_generation === false
) {
return false;
}
$params['hide_print_button'] = isset($params['hide_print_button']) ? true : false;
$categoryId = 0;
$my_category = [];
if (isset($this->certificate_data) && isset($this->certificate_data['cat_id'])) {
$categoryId = $this->certificate_data['cat_id'];
$my_category = Category::load($categoryId);
}
if (isset($my_category[0]) && !empty($categoryId) &&
$my_category[0]->is_certificate_available($this->user_id)
) {
/** @var Category $category */
$category = $my_category[0];
$courseInfo = api_get_course_info($category->get_course_code());
$courseId = $courseInfo['real_id'];
$sessionId = $category->get_session_id();
$skill = new Skill();
$skill->addSkillToUser(
$this->user_id,
$category,
$courseId,
$sessionId
);
if (is_dir($this->certification_user_path)) {
if (!empty($this->certificate_data)) {
$new_content_html = GradebookUtils::get_user_certificate_content(
$this->user_id,
$category->get_course_code(),
$category->get_session_id(),
false,
$params['hide_print_button']
);
if ($category->get_id() == $categoryId) {
$name = $this->certificate_data['path_certificate'];
$myPathCertificate = $this->certification_user_path.basename($name);
if (file_exists($myPathCertificate) &&
!empty($name) &&
!is_dir($myPathCertificate) &&
$this->force_certificate_generation == false
) {
// Seems that the file was already generated
return true;
} else {
// Creating new name
$name = md5($this->user_id.$this->certificate_data['cat_id']).'.html';
$myPathCertificate = $this->certification_user_path.$name;
$path_certificate = '/'.$name;
// Getting QR filename
$file_info = pathinfo($path_certificate);
$qr_code_filename = $this->certification_user_path.$file_info['filename'].'_qr.png';
$newContent = str_replace(
'((certificate_barcode))',
Display::img(
$this->certification_web_user_path.$file_info['filename'].'_qr.png',
'QR'
),
$new_content_html['content']
);
$newContent = api_convert_encoding(
$newContent,
'UTF-8',
api_get_system_encoding()
);
$result = @file_put_contents($myPathCertificate, $newContent);
if ($result) {
// Updating the path
$this->updateUserCertificateInfo(
$this->certificate_data['cat_id'],
$this->user_id,
$path_certificate
);
$this->certificate_data['path_certificate'] = $path_certificate;
if ($this->isHtmlFileGenerated()) {
if (!empty($file_info)) {
$text = $this->parseCertificateVariables(
$new_content_html['variables']
);
$this->generateQRImage(
$text,
$qr_code_filename
);
if ($sendNotification) {
$subject = get_lang('NotificationCertificateSubject');
$message = nl2br(get_lang('NotificationCertificateTemplate'));
$score = $this->certificate_data['score_certificate'];
self::sendNotification(
$subject,
$message,
api_get_user_info($this->user_id),
$courseInfo,
[
'score_certificate' => $score,
]
);
}
}
}
}
return $result;
}
}
}
}
} else {
$this->check_certificate_path();
// General certificate
$name = md5($this->user_id).'.html';
$my_path_certificate = $this->certification_user_path.$name;
$path_certificate = '/'.$name;
// Getting QR filename
$file_info = pathinfo($path_certificate);
$content = $this->generateCustomCertificate();
$my_new_content_html = str_replace(
'((certificate_barcode))',
Display::img(
$this->certification_web_user_path.$file_info['filename'].'_qr.png',
'QR'
),
$content
);
$my_new_content_html = mb_convert_encoding(
$my_new_content_html,
'UTF-8',
api_get_system_encoding()
);
$result = @file_put_contents($my_path_certificate, $my_new_content_html);
if ($result) {
// Updating the path
self::updateUserCertificateInfo(
0,
$this->user_id,
$path_certificate
);
$this->certificate_data['path_certificate'] = $path_certificate;
}
return $result;
}
return false;
}
/**
* @return array
*/
public static function notificationTags()
{
$tags = [
'((course_title))',
'((user_first_name))',
'((user_last_name))',
'((author_first_name))',
'((author_last_name))',
'((score))',
'((portal_name))',
'((certificate_link))',
];
return $tags;
}
/**
* @param string $subject
* @param string $message
* @param array $userInfo
* @param array $courseInfo
* @param array $certificateInfo
*
* @return bool
*/
public static function sendNotification(
$subject,
$message,
$userInfo,
$courseInfo,
$certificateInfo
) {
if (empty($userInfo) || empty($courseInfo)) {
return false;
}
$currentUserInfo = api_get_user_info();
$url = api_get_path(WEB_PATH).
'certificates/index.php?id='.$certificateInfo['id'].'&user_id='.$certificateInfo['user_id'];
$link = Display::url($url, $url);
$replace = [
$courseInfo['title'],
$userInfo['firstname'],
$userInfo['lastname'],
$currentUserInfo['firstname'],
$currentUserInfo['lastname'],
$certificateInfo['score_certificate'],
api_get_setting('Institution'),
$link,
];
$message = str_replace(self::notificationTags(), $replace, $message);
MessageManager::send_message(
$userInfo['id'],
$subject,
$message,
[],
[],
0,
0,
0,
0,
$currentUserInfo['id']
);
$plugin = new AppPlugin();
$smsPlugin = $plugin->getSMSPluginLibrary();
if ($smsPlugin) {
$additionalParameters = [
'smsType' => SmsPlugin::CERTIFICATE_NOTIFICATION,
'userId' => $userInfo['id'],
'direct_message' => $message,
];
$smsPlugin->send($additionalParameters);
}
}
/**
* Update user info about certificate.
*
* @param int $categoryId category id
* @param int $user_id user id
* @param string $path_certificate the path name of the certificate
* @param bool $updateCertificateData
*/
public function updateUserCertificateInfo(
$categoryId,
$user_id,
$path_certificate,
$updateCertificateData = true
) {
$categoryId = (int) $categoryId;
$user_id = (int) $user_id;
if ($updateCertificateData &&
!UserManager::is_user_certified($categoryId, $user_id)
) {
$table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
$now = api_get_utc_datetime();
$sql = 'UPDATE '.$table.' SET
path_certificate="'.Database::escape_string($path_certificate).'",
created_at = "'.$now.'"
WHERE cat_id = "'.$categoryId.'" AND user_id="'.$user_id.'" ';
Database::query($sql);
}
}
/**
* Check if the file was generated.
*
* @return bool
*/
public function isHtmlFileGenerated()
{
if (empty($this->certification_user_path)) {
return false;
}
if (!empty($this->certificate_data) &&
isset($this->certificate_data['path_certificate']) &&
!empty($this->certificate_data['path_certificate'])
) {
return true;
}
return false;
}
/**
* Generates a QR code for the certificate. The QR code embeds the text given.
*
* @param string $text Text to be added in the QR code
* @param string $path file path of the image
*
* @return bool
*/
public function generateQRImage($text, $path)
{
if (!empty($text) && !empty($path)) {
$qrCode = new QrCode($text);
//$qrCode->setEncoding('UTF-8');
$qrCode->setSize(120);
$qrCode->setMargin(5);
$qrCode->setWriterByName('png');
$qrCode->setErrorCorrectionLevel(ErrorCorrectionLevel::MEDIUM);
$qrCode->setForegroundColor(['r' => 0, 'g' => 0, 'b' => 0, 'a' => 0]);
$qrCode->setBackgroundColor(['r' => 255, 'g' => 255, 'b' => 255, 'a' => 0]);
$qrCode->setValidateResult(false);
$qrCode->writeFile($path);
return true;
}
return false;
}
/**
* Transforms certificate tags into text values. This function is very static
* (it doesn't allow for much flexibility in terms of what tags are printed).
*
* @param array $array Contains two array entries: first are the headers,
* second is an array of contents
*
* @return string The translated string
*/
public function parseCertificateVariables($array)
{
$headers = $array[0];
$content = $array[1];
$final_content = [];
if (!empty($content)) {
foreach ($content as $key => $value) {
$my_header = str_replace(['((', '))'], '', $headers[$key]);
$final_content[$my_header] = $value;
}
}
/* Certificate tags
*
0 => string '((user_firstname))' (length=18)
1 => string '((user_lastname))' (length=17)
2 => string '((gradebook_institution))' (length=25)
3 => string '((gradebook_sitename))' (length=22)
4 => string '((teacher_firstname))' (length=21)
5 => string '((teacher_lastname))' (length=20)
6 => string '((official_code))' (length=17)
7 => string '((date_certificate))' (length=20)
8 => string '((course_code))' (length=15)
9 => string '((course_title))' (length=16)
10 => string '((gradebook_grade))' (length=19)
11 => string '((certificate_link))' (length=20)
12 => string '((certificate_link_html))' (length=25)
13 => string '((certificate_barcode))' (length=23)
*/
$break_space = " \n\r ";
$text =
$final_content['gradebook_institution'].' - '.
$final_content['gradebook_sitename'].' - '.
get_lang('Certification').$break_space.
get_lang('Student').': '.$final_content['user_firstname'].' '.$final_content['user_lastname'].$break_space.
get_lang('Teacher').': '.$final_content['teacher_firstname'].' '.$final_content['teacher_lastname'].$break_space.
get_lang('Date').': '.$final_content['date_certificate'].$break_space.
get_lang('Score').': '.$final_content['gradebook_grade'].$break_space.
'URL'.': '.$final_content['certificate_link'];
return $text;
}
/**
* Check if the certificate is visible for the current user
* If the global setting allow_public_certificates is set to 'false', no certificate can be printed.
* If the global allow_public_certificates is set to 'true' and the course setting allow_public_certificates
* is set to 0, no certificate *in this course* can be printed (for anonymous users).
* Connected users can always print them.
*
* @return bool
*/
public function isVisible()
{
if (!api_is_anonymous()) {
return true;
}
if (api_get_setting('allow_public_certificates') != 'true') {
// The "non-public" setting is set, so do not print
return false;
}
if (!isset($this->certificate_data, $this->certificate_data['cat_id'])) {
return false;
}
$gradeBook = new Gradebook();
$gradeBookInfo = $gradeBook->get($this->certificate_data['cat_id']);
if (empty($gradeBookInfo['course_code'])) {
return false;
}
$setting = api_get_course_setting(
'allow_public_certificates',
api_get_course_info($gradeBookInfo['course_code'])
);
if ($setting == 0) {
// Printing not allowed
return false;
}
return true;
}
/**
* Check if the certificate is available.
*
* @return bool
*/
public function isAvailable()
{
if (empty($this->certificate_data['path_certificate'])) {
return false;
}
$userCertificate = $this->certification_user_path.basename($this->certificate_data['path_certificate']);
if (!file_exists($userCertificate)) {
return false;
}
return true;
}
/**
* Shows the student's certificate (HTML file).
*/
public function show()
{
$user_certificate = $this->certification_user_path.basename($this->certificate_data['path_certificate']);
if (file_exists($user_certificate)) {
// Needed in order to browsers don't add custom CSS
$certificateContent = '<!DOCTYPE html>';
$certificateContent .= (string) file_get_contents($user_certificate);
// Remove media=screen to be available when printing a document
$certificateContent = str_replace(
' media="screen"',
'',
$certificateContent
);
if ($this->user_id == api_get_user_id() &&
!empty($this->certificate_data) &&
isset($this->certificate_data['id'])
) {
$certificateId = $this->certificate_data['id'];
$extraFieldValue = new ExtraFieldValue('user_certificate');
$value = $extraFieldValue->get_values_by_handler_and_field_variable(
$certificateId,
'downloaded_at'
);
if (empty($value)) {
$params = [
'item_id' => $this->certificate_data['id'],
'extra_downloaded_at' => api_get_utc_datetime(),
];
$extraFieldValue->saveFieldValues($params);
}
}
header('Content-Type: text/html; charset='.api_get_system_encoding());
echo $certificateContent;
return;
}
api_not_allowed(true);
}
/**
* @return string
*/
public function generateCustomCertificate()
{
$myCertificate = GradebookUtils::get_certificate_by_user_id(
0,
$this->user_id
);
if (empty($myCertificate)) {
GradebookUtils::registerUserInfoAboutCertificate(
0,
$this->user_id,
100,
api_get_utc_datetime()
);
}
$userInfo = api_get_user_info($this->user_id);
$extraFieldValue = new ExtraFieldValue('user');
$value = $extraFieldValue->get_values_by_handler_and_field_variable($this->user_id, 'legal_accept');
$termsValidationDate = '';
if (isset($value) && !empty($value['value'])) {
list($id, $id2, $termsValidationDate) = explode(':', $value['value']);
}
$sessions = SessionManager::get_sessions_by_user($this->user_id, false, true);
$totalTimeInLearningPaths = 0;
$sessionsApproved = [];
$coursesApproved = [];
$courseList = [];
if ($sessions) {
foreach ($sessions as $session) {
$allCoursesApproved = [];
foreach ($session['courses'] as $course) {
$courseInfo = api_get_course_info_by_id($course['real_id']);
$courseCode = $courseInfo['code'];
$gradebookCategories = Category::load(
null,
null,
$courseCode,
null,
false,
$session['session_id']
);
if (isset($gradebookCategories[0])) {
/** @var Category $category */
$category = $gradebookCategories[0];
$result = Category::userFinishedCourse(
$this->user_id,
$category,
true
);
// Find time spent in LP
$timeSpent = Tracking::get_time_spent_in_lp(
$this->user_id,
$courseCode,
[],
$session['session_id']
);
if (!isset($courseList[$course['real_id']])) {
$courseList[$course['real_id']]['approved'] = false;
$courseList[$course['real_id']]['time_spent'] = 0;
}
if ($result) {
$courseList[$course['real_id']]['approved'] = true;
$coursesApproved[$course['real_id']] = $courseInfo['title'];
// Find time spent in LP
//$totalTimeInLearningPaths += $timeSpent;
$allCoursesApproved[] = true;
}
$courseList[$course['real_id']]['time_spent'] += $timeSpent;
}
}
if (count($allCoursesApproved) == count($session['courses'])) {
$sessionsApproved[] = $session;
}
}
}
$totalTimeInLearningPaths = 0;
foreach ($courseList as $courseId => $courseData) {
if ($courseData['approved'] === true) {
$totalTimeInLearningPaths += $courseData['time_spent'];
}
}
$skill = new Skill();
// Ofaj
$skills = $skill->getStudentSkills($this->user_id, 2);
$timeInSeconds = Tracking::get_time_spent_on_the_platform(
$this->user_id,
'ever'
);
$time = api_time_to_hms($timeInSeconds);
$tplContent = new Template(null, false, false, false, false, false);
// variables for the default template
$tplContent->assign('complete_name', $userInfo['complete_name']);
$tplContent->assign('time_in_platform', $time);
$tplContent->assign('certificate_generated_date', api_get_local_time($myCertificate['created_at']));
if (!empty($termsValidationDate)) {
$termsValidationDate = api_get_local_time($termsValidationDate);
}
$tplContent->assign('terms_validation_date', $termsValidationDate);
// Ofaj
$tplContent->assign('time_in_platform_in_hours', round($timeInSeconds / 3600, 1));
$tplContent->assign(
'certificate_generated_date_no_time',
api_get_local_time(
$myCertificate['created_at'],
null,
null,
false,
false,
false,
'd-m-Y'
)
);
$tplContent->assign(
'terms_validation_date_no_time',
api_get_local_time(
$termsValidationDate,
null,
null,
false,
false,
false,
'd-m-Y'
)
);
$tplContent->assign('skills', $skills);
$tplContent->assign('sessions', $sessionsApproved);
$tplContent->assign('courses', $coursesApproved);
$tplContent->assign('time_spent_in_lps', api_time_to_hms($totalTimeInLearningPaths));
$tplContent->assign('time_spent_in_lps_in_hours', round($totalTimeInLearningPaths / 3600, 1));
$layoutContent = $tplContent->get_template('gradebook/custom_certificate.tpl');
$content = $tplContent->fetch($layoutContent);
return $content;
}
/**
* Ofaj.
*/
public function generatePdfFromCustomCertificate()
{
$orientation = api_get_configuration_value('certificate_pdf_orientation');
$params['orientation'] = 'landscape';
if (!empty($orientation)) {
$params['orientation'] = $orientation;
}
$params['left'] = 0;
$params['right'] = 0;
$params['top'] = 0;
$params['bottom'] = 0;
$page_format = $params['orientation'] == 'landscape' ? 'A4-L' : 'A4';
$pdf = new PDF($page_format, $params['orientation'], $params);
$pdf->html_to_pdf(
$this->html_file,
get_lang('Certificates'),
null,
false,
false
);
}
/**
* @param int $userId
*
* @return array
*/
public static function getCertificateByUser($userId)
{
$userId = (int) $userId;
if (empty($userId)) {
return [];
}
$table = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
$sql = "SELECT * FROM $table
WHERE user_id= $userId";
$rs = Database::query($sql);
return Database::store_result($rs, 'ASSOC');
}
/**
* @param int $userId
*/
public static function generateUserSkills($userId)
{
$controller = new IndexManager(get_lang('MyCourses'));
$courseAndSessions = $controller->returnCoursesAndSessions($userId, true, null, true, false);
if (isset($courseAndSessions['courses']) && !empty($courseAndSessions['courses'])) {
foreach ($courseAndSessions['courses'] as $course) {
$cats = Category::load(
null,
null,
$course['code'],
null,
null,
null,
false
);
if (isset($cats[0]) && !empty($cats[0])) {
Category::generateUserCertificate(
$cats[0]->get_id(),
$userId
);
}
}
}
if (isset($courseAndSessions['sessions']) && !empty($courseAndSessions['sessions'])) {
foreach ($courseAndSessions['sessions'] as $sessionCategory) {
if (isset($sessionCategory['sessions'])) {
foreach ($sessionCategory['sessions'] as $sessionData) {
if (!empty($sessionData['courses'])) {
$sessionId = $sessionData['session_id'];
foreach ($sessionData['courses'] as $courseData) {
$cats = Category::load(
null,
null,
$courseData['course_code'],
null,
null,
$sessionId,
false
);
if (isset($cats[0]) && !empty($cats[0])) {
Category::generateUserCertificate(
$cats[0]->get_id(),
$userId
);
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,162 @@
<?php
use Symfony\Component\HttpFoundation\Session\Session;
/**
* Chamilo session (i.e. the session that maintains the connection open after usr login).
*
* Usage:
*
*
* use ChamiloSession as Session;
*
* Session::read('name');
*
* Or
*
* Chamilo::session()->...
* session()->...
*
* @license see /license.txt
* @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
*/
/**
* @todo use session symfony component
* @todo replace all $_SESSION calls with this class.
* @todo remove System\Session class
* ChamiloSession class definition
*/
class ChamiloSession extends System\Session
{
public const NAME = 'ch_sid';
/**
* Generate new session instance.
*
* @return ChamiloSession
*/
public static function instance()
{
static $result = null;
if (empty($result)) {
$result = new ChamiloSession();
}
return $result;
}
/**
* Returns the session lifetime.
*
* @return int The session lifetime as defined in the config file, in seconds
*/
public static function session_lifetime()
{
return api_get_configuration_value('session_lifetime');
}
/**
* Starts the Chamilo session.
*
* The default lifetime for session is set here. It is not possible to have it
* as a database setting as it is used before the database connection has been made.
* It is taken from the configuration file, and if it doesn't exist there, it is set
* to 360000 seconds
*
* @author Olivier Brouckaert
*
* @param string variable - the variable name to save into the session
*/
public static function start($already_installed = true)
{
/*
* Prevent Session fixation bug fixes
* See http://support.chamilo.org/issues/3600
* http://php.net/manual/en/session.configuration.php
* @todo use session_set_cookie_params with some custom admin parameters
*/
//session.cookie_lifetime
//the session ID is only accepted from a cookie
ini_set('session.use_only_cookies', 1);
//HTTPS only if possible
//ini_set('session.cookie_secure', 1);
//session ID in the cookie is only readable by the server
ini_set('session.cookie_httponly', 1);
if (api_is_https()) {
ini_set('session.cookie_secure', 1);
}
if (api_get_configuration_value('security_session_cookie_samesite_none')) {
if (PHP_VERSION_ID < 70300) {
$sessionCookieParams = session_get_cookie_params();
session_set_cookie_params($sessionCookieParams['lifetime'], '/; samesite=None',
$sessionCookieParams['domain'], true, $sessionCookieParams['httponly']);
} else {
ini_set('session.cookie_samesite', 'None');
}
}
//Use entropy file
//session.entropy_file
//ini_set('session.entropy_length', 128);
//Do not include the identifier in the URL, and not to read the URL for
// identifiers.
ini_set('session.use_trans_sid', 0);
session_name(self::NAME);
session_start();
$session = self::instance();
if ($already_installed) {
if (!isset($session['checkChamiloURL'])) {
$session['checkChamiloURL'] = api_get_path(WEB_PATH);
} elseif ($session['checkChamiloURL'] != api_get_path(WEB_PATH)) {
self::clear();
}
}
// If the session time has expired, refresh the starttime value,
// so we're starting to count down from a later time
if (self::has('starttime') && $session->is_expired()) {
self::destroy();
} else {
//error_log('Time not expired, extend session for a bit more');
self::write('starttime', time());
}
}
/**
* Session start time: that is the last time the user loaded a page (before this time).
*
* @return int timestamp
*/
public function start_time()
{
return self::read('starttime');
}
/**
* Session end time: when the session expires. This is made of the last page
* load time + a number of seconds.
*
* @return int UNIX timestamp (server's timezone)
*/
public function end_time()
{
$start_time = $this->start_time();
$lifetime = self::session_lifetime();
return $start_time + $lifetime;
}
/**
* Returns whether the session is expired.
*
* @return bool True if the session is expired, false if it is still valid
*/
public function is_expired()
{
return $this->end_time() < time();
}
}

530
main/inc/lib/chat.lib.php Normal file
View File

@@ -0,0 +1,530 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/**
* Class Chat.
*
* @todo ChamiloSession instead of $_SESSION
*/
class Chat extends Model
{
public $columns = [
'id',
'from_user',
'to_user',
'message',
'sent',
'recd',
];
public $window_list = [];
/**
* The contructor sets the chat table name and the window_list attribute.
*/
public function __construct()
{
parent::__construct();
$this->table = Database::get_main_table(TABLE_MAIN_CHAT);
$this->window_list = Session::read('window_list');
Session::write('window_list', $this->window_list);
}
/**
* Get user chat status.
*
* @return int 0 if disconnected, 1 if connected
*/
public function getUserStatus()
{
$status = UserManager::get_extra_user_data_by_field(
api_get_user_id(),
'user_chat_status',
false,
true
);
return $status['user_chat_status'];
}
/**
* Set user chat status.
*
* @param int $status 0 if disconnected, 1 if connected
*/
public function setUserStatus($status)
{
UserManager::update_extra_field_value(
api_get_user_id(),
'user_chat_status',
$status
);
}
/**
* @param int $currentUserId
* @param int $userId
* @param bool $latestMessages
*
* @return array
*/
public function getLatestChat($currentUserId, $userId, $latestMessages)
{
$items = $this->getPreviousMessages(
$currentUserId,
$userId,
0,
$latestMessages
);
return array_reverse($items);
}
/**
* @return string
*/
public function getContacts()
{
$html = SocialManager::listMyFriendsBlock(
api_get_user_id(),
'',
true
);
echo $html;
}
/**
* @param array $chatHistory
* @param int $latestMessages
*
* @return mixed
*/
public function getAllLatestChats($chatHistory, $latestMessages = 5)
{
$currentUserId = api_get_user_id();
if (empty($chatHistory)) {
return [];
}
$chats = [];
foreach ($chatHistory as $userId => $time) {
$total = $this->getCountMessagesExchangeBetweenUsers($userId, $currentUserId);
$start = $total - $latestMessages;
if ($start < 0) {
$start = 0;
}
$items = $this->getMessages($userId, $currentUserId, $start, $latestMessages);
$chats[$userId]['items'] = $items;
$chats[$userId]['window_user_info'] = api_get_user_info($userId);
}
return $chats;
}
/**
* Starts a chat session and returns JSON array of status and chat history.
*
* @return bool (prints output in JSON format)
*/
public function startSession()
{
// ofaj
// $chat = new Chat();
// $chat->setUserStatus(1);
$chatList = Session::read('openChatBoxes');
$chats = $this->getAllLatestChats($chatList);
$return = [
'user_status' => $this->getUserStatus(),
'me' => get_lang('Me'),
'user_id' => api_get_user_id(),
'items' => $chats,
'sec_token' => Security::get_token('chat'),
];
echo json_encode($return);
return true;
}
/**
* @param int $fromUserId
* @param int $toUserId
*
* @return int
*/
public function getCountMessagesExchangeBetweenUsers($fromUserId, $toUserId)
{
$row = Database::select(
'count(*) as count',
$this->table,
[
'where' => [
'(from_user = ? AND to_user = ?) OR (from_user = ? AND to_user = ?) ' => [
$fromUserId,
$toUserId,
$toUserId,
$fromUserId,
],
],
],
'first'
);
return (int) $row['count'];
}
/**
* @param int $fromUserId
* @param int $toUserId
* @param int $visibleMessages
* @param int $previousMessageCount messages to show
*
* @return array
*/
public function getPreviousMessages(
$fromUserId,
$toUserId,
$visibleMessages = 1,
$previousMessageCount = 5,
$orderBy = ''
) {
$toUserId = (int) $toUserId;
$fromUserId = (int) $fromUserId;
$visibleMessages = (int) $visibleMessages;
$previousMessageCount = (int) $previousMessageCount;
$total = $this->getCountMessagesExchangeBetweenUsers($fromUserId, $toUserId);
$show = $total - $visibleMessages;
if ($show < $previousMessageCount) {
$show = $previousMessageCount;
}
$from = $show - $previousMessageCount;
if ($from < 0) {
return [];
}
return $this->getMessages($fromUserId, $toUserId, $from, $previousMessageCount, $orderBy);
}
/**
* @param int $fromUserId
* @param int $toUserId
* @param int $start
* @param int $end
* @param string $orderBy
*
* @return array
*/
public function getMessages($fromUserId, $toUserId, $start, $end, $orderBy = '')
{
$toUserId = (int) $toUserId;
$fromUserId = (int) $fromUserId;
$start = (int) $start;
$end = (int) $end;
if (empty($toUserId) || empty($fromUserId)) {
return [];
}
$orderBy = Database::escape_string($orderBy);
if (empty($orderBy)) {
$orderBy = 'ORDER BY id ASC';
}
$sql = "SELECT * FROM ".$this->table."
WHERE
(
to_user = $toUserId AND
from_user = $fromUserId
)
OR
(
from_user = $toUserId AND
to_user = $fromUserId
)
$orderBy
LIMIT $start, $end
";
$result = Database::query($sql);
$rows = Database::store_result($result);
$fromUserInfo = api_get_user_info($fromUserId, true);
$toUserInfo = api_get_user_info($toUserId, true);
$users = [
$fromUserId => $fromUserInfo,
$toUserId => $toUserInfo,
];
$items = [];
$rows = array_reverse($rows);
foreach ($rows as $chat) {
$fromUserId = $chat['from_user'];
$userInfo = $users[$fromUserId];
$toUserInfo = $users[$toUserId];
$items[$chat['id']] = [
'id' => $chat['id'],
'message' => Security::remove_XSS($chat['message']),
'date' => api_strtotime($chat['sent'], 'UTC'),
'recd' => $chat['recd'],
'from_user_info' => $userInfo,
'to_user_info' => $toUserInfo,
];
$_SESSION['openChatBoxes'][$fromUserId] = api_strtotime($chat['sent'], 'UTC');
}
return $items;
}
/**
* Refreshes the chat windows (usually called every x seconds through AJAX).
*/
public function heartbeat()
{
$chatHistory = Session::read('chatHistory');
$currentUserId = api_get_user_id();
// update current chats
if (!empty($chatHistory) && is_array($chatHistory)) {
foreach ($chatHistory as $fromUserId => &$data) {
$userInfo = api_get_user_info($fromUserId, true);
$count = $this->getCountMessagesExchangeBetweenUsers($fromUserId, $currentUserId);
$chatItems = $this->getLatestChat($fromUserId, $currentUserId, 5);
$data['window_user_info'] = $userInfo;
$data['items'] = $chatItems;
$data['total_messages'] = $count;
}
}
$sql = "SELECT * FROM ".$this->table."
WHERE
to_user = '".$currentUserId."' AND recd = 0
ORDER BY id ASC";
$result = Database::query($sql);
$chatList = [];
while ($chat = Database::fetch_array($result, 'ASSOC')) {
$chatList[$chat['from_user']][] = $chat;
}
foreach ($chatList as $fromUserId => $messages) {
$userInfo = api_get_user_info($fromUserId, true);
$count = $this->getCountMessagesExchangeBetweenUsers($fromUserId, $currentUserId);
$chatItems = $this->getLatestChat($fromUserId, $currentUserId, 5);
// Cleaning tsChatBoxes
unset($_SESSION['tsChatBoxes'][$fromUserId]);
foreach ($messages as $chat) {
$_SESSION['openChatBoxes'][$fromUserId] = api_strtotime($chat['sent'], 'UTC');
}
$chatHistory[$fromUserId] = [
'window_user_info' => $userInfo,
'total_messages' => $count,
'items' => $chatItems,
];
}
Session::write('chatHistory', $chatHistory);
$sql = "UPDATE ".$this->table."
SET recd = 1
WHERE to_user = $currentUserId AND recd = 0";
Database::query($sql);
echo json_encode(['items' => $chatHistory]);
}
/**
* Saves into session the fact that a chat window exists with the given user.
*
* @param int $userId
*/
public function saveWindow($userId)
{
$this->window_list[$userId] = true;
Session::write('window_list', $this->window_list);
}
/**
* Sends a message from one user to another user.
*
* @param int $fromUserId The ID of the user sending the message
* @param int $to_user_id The ID of the user receiving the message
* @param string $message Message
* @param bool $printResult Optional. Whether print the result
* @param bool $sanitize Optional. Whether sanitize the message
*/
public function send(
$fromUserId,
$to_user_id,
$message,
$printResult = true,
$sanitize = true
) {
$relation = SocialManager::get_relation_between_contacts($fromUserId, $to_user_id);
if (!Security::check_token('post', null, 'chat')) {
if ($printResult) {
echo '0';
exit;
}
}
if (USER_RELATION_TYPE_FRIEND == $relation) {
$now = api_get_utc_datetime();
$user_info = api_get_user_info($to_user_id, true);
$this->saveWindow($to_user_id);
$_SESSION['openChatBoxes'][$to_user_id] = api_strtotime($now, 'UTC');
if ($sanitize) {
$messagesan = $this->sanitize($message);
} else {
$messagesan = $message;
}
if (!isset($_SESSION['chatHistory'][$to_user_id])) {
$_SESSION['chatHistory'][$to_user_id] = [];
}
$item = [
's' => '1',
'f' => $fromUserId,
'm' => $messagesan,
'date' => api_strtotime($now, 'UTC'),
'username' => get_lang('Me'),
];
$_SESSION['chatHistory'][$to_user_id]['items'][] = $item;
$_SESSION['chatHistory'][$to_user_id]['user_info']['user_name'] = $user_info['complete_name'];
$_SESSION['chatHistory'][$to_user_id]['user_info']['online'] = $user_info['user_is_online'];
$_SESSION['chatHistory'][$to_user_id]['user_info']['avatar'] = $user_info['avatar_small'];
$_SESSION['chatHistory'][$to_user_id]['user_info']['user_id'] = $user_info['user_id'];
unset($_SESSION['tsChatBoxes'][$to_user_id]);
$params = [];
$params['from_user'] = (int) $fromUserId;
$params['to_user'] = (int) $to_user_id;
$params['message'] = $messagesan;
$params['sent'] = api_get_utc_datetime();
if (!empty($fromUserId) && !empty($to_user_id)) {
$messageId = $this->save($params);
if ($printResult) {
header('Content-Type: application/json');
echo json_encode(['id' => $messageId, 'sec_token' => Security::get_token('chat')]);
exit;
}
}
}
if ($printResult) {
echo '0';
exit;
}
}
/**
* Close a specific chat box (user ID taken from $_POST['chatbox']).
*
* @param int $userId
*/
public function closeWindow($userId)
{
if (empty($userId)) {
return false;
}
$list = Session::read('openChatBoxes');
if (isset($list[$userId])) {
unset($list[$userId]);
Session::write('openChatBoxes', $list);
}
$list = Session::read('chatHistory');
if (isset($list[$userId])) {
unset($list[$userId]);
Session::write('chatHistory', $list);
}
return true;
}
/**
* Close chat - disconnects the user.
*/
public function close()
{
Session::erase('tsChatBoxes');
Session::erase('openChatBoxes');
Session::erase('chatHistory');
Session::erase('window_list');
}
/**
* Filter chat messages to avoid XSS or other JS.
*
* @param string $text Unfiltered message
*
* @return string Filtered message
*/
public function sanitize($text)
{
$text = htmlspecialchars($text, ENT_QUOTES);
$text = str_replace("\n\r", "\n", $text);
$text = str_replace("\r\n", "\n", $text);
$text = str_replace("\n", "<br>", $text);
return $text;
}
/**
* SET Disable Chat.
*
* @param bool $status to disable chat
*/
public static function setDisableChat($status = true)
{
Session::write('disable_chat', $status);
}
/**
* Disable Chat - disable the chat.
*
* @return bool - return true if setDisableChat status is true
*/
public static function disableChat()
{
$status = Session::read('disable_chat');
if (!empty($status)) {
if ($status == true) {
Session::write('disable_chat', null);
return true;
}
}
return false;
}
/**
* @return bool
*/
public function isChatBlockedByExercises()
{
$currentExercises = Session::read('current_exercises');
if (!empty($currentExercises)) {
foreach ($currentExercises as $attempt_status) {
if ($attempt_status == true) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Conditional login
* Used to implement the loading of custom pages
* 2011, Noel Dieschburg <noel@cblue.be>.
*/
class ConditionalLogin
{
/**
* Check conditions based in the $login_conditions see conditional_login.php file.
*
* @param array $user
*/
public static function check_conditions($user)
{
$file = api_get_path(SYS_CODE_PATH).'auth/conditional_login/conditional_login.php';
if (file_exists($file)) {
include_once $file;
if (isset($login_conditions)) {
foreach ($login_conditions as $condition) {
//If condition fails we redirect to the URL defined by the condition
if (isset($condition['conditional_function'])) {
$function = $condition['conditional_function'];
$result = $function($user);
if (false == $result) {
$_SESSION['conditional_login']['uid'] = $user['user_id'];
$_SESSION['conditional_login']['can_login'] = false;
header("Location: ".$condition['url']);
exit;
}
}
}
}
}
}
public static function login()
{
$_SESSION['conditional_login']['can_login'] = true;
LoginRedirection::redirect();
}
}

7516
main/inc/lib/course.lib.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,914 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class CourseCategory.
*/
class CourseCategory
{
/**
* Returns the category fields from the database from an int ID.
*
* @param int $categoryId The category ID
*
* @return array
*/
public static function getCategoryById($categoryId)
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
$categoryId = (int) $categoryId;
$sql = "SELECT * FROM $table WHERE id = $categoryId";
$result = Database::query($sql);
if (Database::num_rows($result)) {
return Database::fetch_array($result, 'ASSOC');
}
return [];
}
/**
* Get category details from a simple category code.
*
* @param string $categoryCode The literal category code
*
* @return array
*/
public static function getCategory($categoryCode)
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
$categoryCode = Database::escape_string($categoryCode);
$sql = "SELECT * FROM $table WHERE code ='$categoryCode'";
$result = Database::query($sql);
if (Database::num_rows($result)) {
$category = Database::fetch_array($result, 'ASSOC');
if ($category) {
// Get access url id
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
$sql = "SELECT * FROM $table WHERE course_category_id = ".$category['id'];
$result = Database::query($sql);
$result = Database::fetch_array($result);
if ($result) {
$category['access_url_id'] = $result['access_url_id'];
}
return $category;
}
}
return [];
}
/**
* @param string $category Optional. Parent category code
*
* @return array
*/
public static function getCategories($category = '')
{
$tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
$category = Database::escape_string($category);
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
$conditions = " INNER JOIN $table a ON (t1.id = a.course_category_id)";
$whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
$allowBaseCategories = api_get_configuration_value('allow_base_course_category');
if ($allowBaseCategories) {
$whereCondition = " AND (a.access_url_id = ".api_get_current_access_url_id()." OR a.access_url_id = 1) ";
}
$parentIdCondition = " AND (t1.parent_id IS NULL OR t1.parent_id = '' )";
if (!empty($category)) {
$parentIdCondition = " AND t1.parent_id = '$category' ";
}
$sql = "SELECT
t1.id,
t1.name,
t1.code,
t1.parent_id,
t1.tree_pos,
t1.children_count,
COUNT(DISTINCT t3.code) AS nbr_courses,
a.access_url_id
FROM $tbl_category t1
$conditions
LEFT JOIN $tbl_category t2
ON t1.code = t2.parent_id
LEFT JOIN $tbl_course t3
ON t3.category_code=t1.code
WHERE
1 = 1
$parentIdCondition
$whereCondition
GROUP BY t1.name,
t1.code,
t1.parent_id,
t1.tree_pos,
t1.children_count
ORDER BY t1.tree_pos";
$result = Database::query($sql);
return Database::store_result($result, 'ASSOC');
}
/**
* Returns a flat list of all course categories in this URL. If the
* allow_base_course_category option is true, then also show the
* course categories of the base URL.
*
* @return array [id, name, code, parent_id, tree_pos, children_count, number_courses]
*/
public static function getAllCategories()
{
$tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
$conditions = " INNER JOIN $table a ON (t1.id = a.course_category_id)";
$whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
$allowBaseCategories = api_get_configuration_value('allow_base_course_category');
if ($allowBaseCategories) {
$whereCondition = " AND (a.access_url_id = ".api_get_current_access_url_id()." OR a.access_url_id = 1) ";
}
$sql = "SELECT
t1.id,
t1.name,
t1.code,
t1.parent_id,
t1.tree_pos,
t1.children_count,
COUNT(DISTINCT t3.code) AS number_courses
FROM $tbl_category t1
$conditions
LEFT JOIN $tbl_course t3
ON t3.category_code=t1.code
WHERE 1=1
$whereCondition
GROUP BY
t1.name,
t1.code,
t1.parent_id,
t1.tree_pos,
t1.children_count
ORDER BY t1.parent_id, t1.tree_pos";
$result = Database::query($sql);
return Database::store_result($result, 'ASSOC');
}
/**
* @param string $code
* @param string $name
* @param string $canHaveCourses
* @param int $parent_id
*
* @return bool
*/
public static function addNode($code, $name, $canHaveCourses, $parent_id)
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
$code = trim($code);
$name = trim($name);
$parent_id = trim($parent_id);
$code = CourseManager::generate_course_code($code);
$sql = "SELECT 1 FROM $table
WHERE code = '".Database::escape_string($code)."'";
$result = Database::query($sql);
if (Database::num_rows($result)) {
return false;
}
$result = Database::query("SELECT MAX(tree_pos) AS maxTreePos FROM $table");
$row = Database::fetch_array($result);
$tree_pos = $row['maxTreePos'] + 1;
$params = [
'name' => html_filter($name),
'code' => $code,
'parent_id' => empty($parent_id) ? null : $parent_id,
'tree_pos' => $tree_pos,
'children_count' => 0,
'auth_course_child' => $canHaveCourses,
'auth_cat_child' => 'TRUE',
];
$categoryId = Database::insert($table, $params);
if ($categoryId) {
self::updateParentCategoryChildrenCount($parent_id, 1);
UrlManager::addCourseCategoryListToUrl(
[$categoryId],
[api_get_current_access_url_id()]
);
return $categoryId;
}
return false;
}
/**
* Recursive function that updates the count of children in the parent.
*
* @param string $categoryId Category ID
* @param int $delta The number to add or delete (1 to add one, -1 to remove one)
*/
public static function updateParentCategoryChildrenCount($categoryId, $delta = 1)
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
$categoryId = Database::escape_string($categoryId);
$delta = (int) $delta;
// First get to the highest level possible in the tree
$result = Database::query("SELECT parent_id FROM $table WHERE code = '$categoryId'");
$row = Database::fetch_array($result);
if ($row !== false and $row['parent_id'] != 0) {
// if a parent was found, enter there to see if he's got one more parent
self::updateParentCategoryChildrenCount($row['parent_id'], $delta);
}
// Now we're at the top, get back down to update each child
$sql = "UPDATE $table SET children_count = (children_count - ".abs($delta).") WHERE code = '$categoryId'";
if ($delta >= 0) {
$sql = "UPDATE $table SET children_count = (children_count + $delta) WHERE code = '$categoryId'";
}
Database::query($sql);
}
/**
* @param string $node
*
* @return bool
*/
public static function deleteNode($node)
{
$category = self::getCategory($node);
if (empty($category)) {
return false;
}
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
$tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
$node = Database::escape_string($node);
$result = Database::query("SELECT parent_id, tree_pos FROM $tbl_category WHERE code='$node'");
if ($row = Database::fetch_array($result)) {
if (!empty($row['parent_id'])) {
Database::query(
"UPDATE $tbl_course SET category_code = '".$row['parent_id']."' WHERE category_code='$node'"
);
Database::query("UPDATE $tbl_category SET parent_id='".$row['parent_id']."' WHERE parent_id='$node'");
} else {
Database::query("UPDATE $tbl_course SET category_code='' WHERE category_code='$node'");
Database::query("UPDATE $tbl_category SET parent_id=NULL WHERE parent_id='$node'");
}
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
$sql = "DELETE FROM $table WHERE course_category_id = ".$category['id'];
Database::query($sql);
Database::query("UPDATE $tbl_category SET tree_pos=tree_pos-1 WHERE tree_pos > '".$row['tree_pos']."'");
Database::query("DELETE FROM $tbl_category WHERE code='$node'");
if (!empty($row['parent_id'])) {
self::updateParentCategoryChildrenCount($row['parent_id'], -1);
}
return true;
}
}
/**
* @param string $code
* @param string $name
* @param string $canHaveCourses
* @param string $old_code
*
* @return bool
*/
public static function editNode(
$code,
$name,
$canHaveCourses,
$old_code,
?string $newParentCode = null,
?string $oldParentCode = null
) {
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
$tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
$code = CourseManager::generate_course_code($code);
$name = html_filter($name);
$code = CourseManager::generate_course_code($code);
// Updating category
Database::update(
$tbl_category,
[
'name' => $name,
'code' => $code,
'auth_course_child' => $canHaveCourses,
],
['code = ?' => $old_code]
);
// Updating children
Database::update(
$tbl_category,
['parent_id' => $code],
['parent_id = ?' => $old_code]
);
// Updating course category
Database::update(
$tbl_course,
['category_code' => $code],
['category_code = ?' => $old_code]
);
Database::update(
$tbl_category,
['parent_id' => $newParentCode ?: null],
['code = ?' => $code]
);
self::updateParentCategoryChildrenCount($oldParentCode, -1);
self::updateParentCategoryChildrenCount($newParentCode, 1);
return true;
}
/**
* Move a node up on display.
*
* @param string $code
* @param int $tree_pos
* @param string $parent_id
*
* @return bool
*/
public static function moveNodeUp($code, $tree_pos, $parent_id)
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
$code = Database::escape_string($code);
$tree_pos = (int) $tree_pos;
$parent_id = Database::escape_string($parent_id);
$parentIdCondition = " AND (parent_id IS NULL OR parent_id = '' )";
if (!empty($parent_id)) {
$parentIdCondition = " AND parent_id = '$parent_id' ";
}
$sql = "SELECT code,tree_pos
FROM $table
WHERE
tree_pos < $tree_pos
$parentIdCondition
ORDER BY tree_pos DESC
LIMIT 0,1";
$result = Database::query($sql);
if (!$row = Database::fetch_array($result)) {
$sql = "SELECT code, tree_pos
FROM $table
WHERE
tree_pos > $tree_pos
$parentIdCondition
ORDER BY tree_pos DESC
LIMIT 0,1";
$result2 = Database::query($sql);
if (!$row = Database::fetch_array($result2)) {
return false;
}
}
$sql = "UPDATE $table
SET tree_pos ='".$row['tree_pos']."'
WHERE code='$code'";
Database::query($sql);
$sql = "UPDATE $table
SET tree_pos = '$tree_pos'
WHERE code= '".$row['code']."'";
Database::query($sql);
return true;
}
public static function getChildren(string $categoryCode, bool $getChildren = true): array
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
$categoryCode = Database::escape_string($categoryCode);
$sql = "SELECT name, code, id FROM $table
WHERE parent_id = '$categoryCode'";
$result = Database::query($sql);
$children = [];
while ($row = Database::fetch_array($result, 'ASSOC')) {
$children[] = $row;
if ($getChildren) {
$subChildren = self::getChildren($row['code']);
$children = array_merge($children, $subChildren);
}
}
return $children;
}
/**
* @param string $categoryCode
*
* @return array
*/
public static function getParents($categoryCode)
{
if (empty($categoryCode)) {
return [];
}
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
$categoryCode = Database::escape_string($categoryCode);
$sql = "SELECT code, parent_id
FROM $table
WHERE code = '$categoryCode'";
$result = Database::query($sql);
$children = [];
while ($row = Database::fetch_array($result, 'ASSOC')) {
$parent = self::getCategory($row['parent_id']);
$children[] = $row;
$subChildren = self::getParents($parent ? $parent['code'] : null);
$children = array_merge($children, $subChildren);
}
return $children;
}
/**
* @param string $categoryCode
*
* @return string|null
*/
public static function getParentsToString($categoryCode)
{
$parents = self::getParents($categoryCode);
if (!empty($parents)) {
$parents = array_reverse($parents);
$categories = [];
foreach ($parents as $category) {
$categories[] = $category['code'];
}
return implode(' > ', $categories).' > ';
}
return null;
}
/**
* @param string $categorySource
*
* @return string
*/
public static function listCategories($categorySource)
{
$categories = self::getCategories($categorySource);
$categorySource = Security::remove_XSS($categorySource);
if (count($categories) > 0) {
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table']);
$column = 0;
$row = 0;
$headers = [
get_lang('Category'),
get_lang('SubCat'),
get_lang('Courses'),
get_lang('Actions'),
];
foreach ($headers as $header) {
$table->setHeaderContents($row, $column, $header);
$column++;
}
$row++;
$mainUrl = api_get_path(WEB_CODE_PATH).'admin/course_category.php?category='.$categorySource;
$ajaxUrl = api_get_path(WEB_AJAX_PATH).'course_category.ajax.php';
$editIcon = Display::return_icon(
'edit.png',
get_lang('EditNode'),
null,
ICON_SIZE_SMALL
);
$exportIcon = Display::return_icon('export_csv.png', get_lang('ExportAsCSV'));
$deleteIcon = Display::return_icon(
'delete.png',
get_lang('DeleteNode'),
null,
ICON_SIZE_SMALL
);
$moveIcon = Display::return_icon(
'up.png',
get_lang('UpInSameLevel'),
null,
ICON_SIZE_SMALL
);
$showCoursesIcon = Display::return_icon(
'course.png',
get_lang('Courses'),
null,
ICON_SIZE_SMALL
);
$urlId = api_get_current_access_url_id();
foreach ($categories as $category) {
$categoryId = $category['id'];
$editUrl = $mainUrl.'&id='.$category['code'].'&action=edit';
$moveUrl = $mainUrl.'&id='.$category['code'].'&action=moveUp&tree_pos='.$category['tree_pos'];
$deleteUrl = $mainUrl.'&id='.$category['code'].'&action=delete';
$exportUrl = $mainUrl.'&id='.$categoryId.'&action=export';
$showCoursesUrl = $ajaxUrl.'?id='.$categoryId.'&a=show_courses';
$actions = [];
if ($urlId == $category['access_url_id']) {
$actions[] = Display::url(
$showCoursesIcon,
$showCoursesUrl,
['onclick' => 'showCourses(this, '.$categoryId.')']
);
$actions[] = Display::url($editIcon, $editUrl);
$actions[] = Display::url($moveIcon, $moveUrl);
$actions[] = Display::url($exportIcon, $exportUrl);
$actions[] = Display::url(
$deleteIcon,
$deleteUrl,
['onclick' => 'javascript: if (!confirm(\''.addslashes(api_htmlentities(sprintf(get_lang('ConfirmYourChoice')), ENT_QUOTES)).'\')) return false;']
);
}
$url = api_get_path(WEB_CODE_PATH).'admin/course_category.php?category='.$category['code'];
$title = Display::url(
Display::return_icon(
'folder_document.gif',
get_lang('OpenNode'),
null,
ICON_SIZE_SMALL
).' '.$category['name'].' ('.$category['code'].')',
$url
);
$countCourses = self::countCoursesInCategory($category['code'], null, false, false);
$content = [
$title,
$category['children_count'],
$countCourses,
implode('', $actions),
];
$column = 0;
foreach ($content as $value) {
$table->setCellContents($row, $column, $value);
$column++;
}
$row++;
}
return $table->toHtml();
}
return Display::return_message(get_lang('NoCategories'), 'warning');
}
/**
* @return array
*/
public static function getCategoriesToDisplayInHomePage()
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
$sql = "SELECT name, code FROM $table
WHERE parent_id IS NULL
ORDER BY tree_pos";
return Database::store_result(Database::query($sql));
}
/**
* @param string $categoryCode
*
* @return array
*/
public static function getCategoriesCanBeAddedInCourse($categoryCode)
{
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
$conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)";
$whereCondition = ' AND a.access_url_id = '.api_get_current_access_url_id();
$tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
$sql = "SELECT code, name
FROM $tbl_category c
$conditions
WHERE (auth_course_child = 'TRUE' OR code = '".Database::escape_string($categoryCode)."')
$whereCondition
ORDER BY tree_pos";
$res = Database::query($sql);
$categoryToAvoid = '';
if (!api_is_platform_admin()) {
$categoryToAvoid = api_get_configuration_value('course_category_code_to_use_as_model');
}
$categories[''] = '-';
while ($cat = Database::fetch_array($res)) {
$categoryCode = $cat['code'];
if (!empty($categoryToAvoid) && $categoryToAvoid == $categoryCode) {
continue;
}
$categories[$categoryCode] = '('.$categoryCode.') '.$cat['name'];
ksort($categories);
}
return $categories;
}
/**
* @param string $category_code
* @param string $keyword
* @param bool $avoidCourses
* @param bool $checkHidePrivate
* @param array $conditions
* @param string $courseLanguageFilter
*
* @return int
*/
public static function countCoursesInCategory(
$category_code = '',
$keyword = '',
$avoidCourses = true,
$checkHidePrivate = true,
$conditions = [],
$courseLanguageFilter = null,
$filterShowInCatalogue = false
) {
return self::getCoursesInCategory(
$category_code,
$keyword,
$avoidCourses,
$checkHidePrivate,
$conditions,
true,
$courseLanguageFilter,
$filterShowInCatalogue
);
}
public static function getCoursesInCategory(
$category_code = '',
$keyword = '',
$avoidCourses = true,
$checkHidePrivate = true,
$conditions = [],
$getCount = false,
$courseLanguageFilter = null,
$filterShowInCatalogue = false
) {
$tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
$categoryCode = Database::escape_string($category_code);
$keyword = Database::escape_string($keyword);
$avoidCoursesCondition = '';
if ($avoidCourses) {
$avoidCoursesCondition = CoursesAndSessionsCatalog::getAvoidCourseCondition();
}
$visibilityCondition = CourseManager::getCourseVisibilitySQLCondition('course', true, $checkHidePrivate);
$showInCatalogueCondition = '';
if ($filterShowInCatalogue) {
$showInCatalogueCondition = CoursesAndSessionsCatalog::getCoursesToShowInCatalogueCondition();
}
$sqlInjectJoins = '';
$courseLanguageWhere = '';
$where = ' AND 1 = 1 ';
$sqlInjectWhere = '';
if (!empty($conditions)) {
$sqlInjectJoins = $conditions['inject_joins'];
$where = $conditions['where'];
$sqlInjectWhere = $conditions['inject_where'];
}
// If have courseLanguageFilter, search for it
if (!empty($courseLanguageFilter)) {
$courseLanguageFilter = Database::escape_string($courseLanguageFilter);
$courseLanguageWhere = "AND course.course_language = '$courseLanguageFilter'";
}
$categoryFilter = '';
if ($categoryCode === 'ALL' || empty($categoryCode)) {
// Nothing to do
} elseif ($categoryCode === 'NONE') {
$categoryFilter = ' AND category_code = "" ';
} else {
$categoryFilter = ' AND category_code = "'.$categoryCode.'" ';
}
$searchFilter = '';
if (!empty($keyword)) {
$searchFilter = ' AND (
code LIKE "%'.$keyword.'%" OR
title LIKE "%'.$keyword.'%" OR
tutor_name LIKE "%'.$keyword.'%"
) ';
}
$urlCondition = ' access_url_id = '.api_get_current_access_url_id().' AND';
$tbl_url_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
$select = " DISTINCT course.id, course.code, course.title, course.category_code, course.directory ";
if ($getCount) {
$select = "count(DISTINCT course.id) as count";
}
$sql = "SELECT $select
FROM $tbl_course as course
INNER JOIN $tbl_url_rel_course as url_rel_course
ON (url_rel_course.c_id = course.id)
$sqlInjectJoins
WHERE
$urlCondition
course.visibility != '0' AND
course.visibility != '4'
$courseLanguageWhere
$categoryFilter
$searchFilter
$avoidCoursesCondition
$showInCatalogueCondition
$visibilityCondition
$where
$sqlInjectWhere
";
$result = Database::query($sql);
if ($getCount) {
$row = Database::fetch_array($result);
return (int) $row['count'];
}
return Database::store_result($result, 'ASSOC');
}
/**
* @param array $list
*
* @return array
*/
public static function getCourseCategoryNotInList($list)
{
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
if (empty($list)) {
$sql = "SELECT * FROM $table
WHERE (parent_id IS NULL) ";
$result = Database::query($sql);
return Database::store_result($result, 'ASSOC');
}
$list = array_map('intval', $list);
$listToString = implode("','", $list);
$sql = "SELECT * FROM $table
WHERE id NOT IN ('$listToString') AND (parent_id IS NULL) ";
$result = Database::query($sql);
return Database::store_result($result, 'ASSOC');
}
/**
* @param string $keyword
*
* @return array|null
*/
public static function searchCategoryByKeyword($keyword)
{
if (empty($keyword)) {
return null;
}
$tableCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
$table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
$conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)";
$whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
$allowBaseCategories = api_get_configuration_value('allow_base_course_category');
if ($allowBaseCategories) {
$whereCondition = " AND (a.access_url_id = ".api_get_current_access_url_id()." OR a.access_url_id = 1) ";
}
$keyword = Database::escape_string($keyword);
$sql = "SELECT c.*, c.name as text
FROM $tableCategory c $conditions
WHERE
(
c.code LIKE '%$keyword%' OR name LIKE '%$keyword%'
) AND auth_course_child = 'TRUE'
$whereCondition ";
$result = Database::query($sql);
return Database::store_result($result, 'ASSOC');
}
/**
* Return the name tool by action.
*
* @param string $action
*
* @return string
*/
public static function getCourseCatalogNameTools($action)
{
$nameTools = get_lang('MyCourses');
if (empty($action)) {
return $nameTools; //should never happen
}
switch ($action) {
case 'subscribe':
case 'subscribe_user_with_password':
case 'display_random_courses':
case 'display_courses':
$nameTools = get_lang('CourseManagement');
break;
case 'display_sessions':
$nameTools = get_lang('Sessions');
break;
default:
// Nothing to do
break;
}
return $nameTools;
}
/**
* Save image for a course category.
*
* @param int $categoryId Course category ID
* @param array $fileData File data from $_FILES
*/
public static function saveImage($categoryId, $fileData)
{
$categoryInfo = self::getCategoryById($categoryId);
if (empty($categoryInfo)) {
return;
}
if (!empty($fileData['error'])) {
return;
}
$extension = getextension($fileData['name']);
$dirName = 'course_category/';
$fileDir = api_get_path(SYS_UPLOAD_PATH).$dirName;
$fileName = "cc_$categoryId.{$extension[0]}";
if (!file_exists($fileDir)) {
mkdir($fileDir, api_get_permissions_for_new_directories(), true);
}
$image = new Image($fileData['tmp_name']);
$image->send_image($fileDir.$fileName);
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
Database::update(
$table,
['image' => $dirName.$fileName],
['id = ?' => $categoryId]
);
}
/**
* @param $categoryId
* @param string $description
*
* @return string
*/
public static function saveDescription($categoryId, $description)
{
$categoryInfo = self::getCategoryById($categoryId);
if (empty($categoryInfo)) {
return false;
}
$table = Database::get_main_table(TABLE_MAIN_CATEGORY);
Database::update(
$table,
['description' => $description],
['id = ?' => $categoryId]
);
return true;
}
}

View File

@@ -0,0 +1,550 @@
<?php
/* For licensing terms, see /license.txt */
/**
* This file contains a class used like library provides functions for
* course description tool. It's also used like model to
* course_description_controller (MVC pattern).
*
* @author Christian Fasanando <christian1827@gmail.com>
*/
/**
* Class CourseDescription course descriptions.
*/
class CourseDescription
{
private $id;
private $course_id;
private $title;
private $content;
private $session_id;
private $description_type;
private $progress;
/**
* Constructor.
*/
public function __construct()
{
}
/**
* Returns an array of objects of type CourseDescription corresponding to
* a specific course, without session ids (session id = 0).
*
* @param int $course_id
*
* @return array Array of CourseDescriptions
*/
public static function get_descriptions($course_id)
{
$course_id = (int) $course_id;
// Get course code
$course_info = api_get_course_info_by_id($course_id);
if (!empty($course_info)) {
$course_id = $course_info['real_id'];
} else {
return [];
}
$table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
$sql = "SELECT * FROM $table
WHERE c_id = $course_id AND session_id = '0'";
$sql_result = Database::query($sql);
$results = [];
while ($row = Database::fetch_array($sql_result)) {
$desc_tmp = new CourseDescription();
$desc_tmp->set_id($row['id']);
$desc_tmp->set_title($row['title']);
$desc_tmp->set_content($row['content']);
$desc_tmp->set_session_id($row['session_id']);
$desc_tmp->set_description_type($row['description_type']);
$desc_tmp->set_progress($row['progress']);
$results[] = $desc_tmp;
}
return $results;
}
/**
* Get all data of course description by session id,
* first you must set session_id property with the object CourseDescription.
*
* @return array
*/
public function get_description_data()
{
$table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
$condition_session = api_get_session_condition(
$this->session_id,
true,
true
);
$course_id = $this->course_id ?: api_get_course_int_id();
if (empty($course_id)) {
return [];
}
$sql = "SELECT * FROM $table
WHERE c_id = $course_id $condition_session
ORDER BY id ";
$rs = Database::query($sql);
$data = [];
while ($description = Database::fetch_array($rs)) {
$data['descriptions'][$description['id']] = $description;
}
return $data;
}
/**
* Get all data by description and session id,
* first you must set session_id property with the object CourseDescription.
*
* @param int $description_type Description type
* @param string $courseId Course code (optional)
* @param int $session_id Session id (optional)
*
* @return array List of fields from the descriptions found of the given type
*/
public function get_data_by_description_type(
$description_type,
$courseId = null,
$session_id = null
) {
$table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
$courseId = (int) $courseId;
if (empty($courseId)) {
$courseId = api_get_course_int_id();
}
if (!isset($session_id)) {
$session_id = $this->session_id;
}
$condition_session = api_get_session_condition($session_id);
$description_type = (int) $description_type;
$sql = "SELECT * FROM $table
WHERE
c_id = $courseId AND
description_type = '$description_type'
$condition_session ";
$rs = Database::query($sql);
$data = [];
if ($description = Database::fetch_array($rs)) {
$data['description_title'] = $description['title'];
$data['description_content'] = $description['content'];
$data['progress'] = $description['progress'];
$data['id'] = $description['id'];
}
return $data;
}
/**
* @param int $id
* @param string $course_code
* @param int $session_id
*
* @return array
*/
public function get_data_by_id($id, $course_code = '', $session_id = null)
{
$table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
$course_id = api_get_course_int_id();
$id = (int) $id;
if (!isset($session_id)) {
$session_id = $this->session_id;
}
$condition_session = api_get_session_condition($session_id);
if (!empty($course_code)) {
$course_info = api_get_course_info($course_code);
$course_id = $course_info['real_id'];
}
$sql = "SELECT * FROM $table
WHERE c_id = $course_id AND id='$id' $condition_session ";
$rs = Database::query($sql);
$data = [];
if ($description = Database::fetch_array($rs)) {
$data['description_type'] = $description['description_type'];
$data['description_title'] = $description['title'];
$data['description_content'] = $description['content'];
$data['progress'] = $description['progress'];
}
return $data;
}
/**
* Get maximum description type by session id,
* first you must set session_id properties with the object CourseDescription.
*
* @return int maximum description time adding one
*/
public function get_max_description_type()
{
$table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
$course_id = api_get_course_int_id();
$sql = "SELECT MAX(description_type) as MAX
FROM $table
WHERE c_id = $course_id AND session_id='".$this->session_id."'";
$rs = Database::query($sql);
$max = Database::fetch_array($rs);
if ($max['MAX'] >= 8) {
$description_type = 8;
} else {
$description_type = $max['MAX'] + 1;
}
if ($description_type < ADD_BLOCK) {
$description_type = ADD_BLOCK;
}
return $description_type;
}
/**
* Insert a description to the course_description table,
* first you must set description_type, title, content, progress and
* session_id properties with the object CourseDescription.
*
* @return int affected rows
*/
public function insert()
{
if (empty($this->course_id)) {
$course_id = api_get_course_int_id();
} else {
$course_id = $this->course_id;
}
$table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
$params = [
'c_id' => $course_id,
'description_type' => $this->description_type,
'title' => $this->title,
'content' => $this->content,
'progress' => intval($this->progress),
'session_id' => $this->session_id,
];
$last_id = Database::insert($table, $params);
if ($last_id > 0) {
$sql = "UPDATE $table SET id = iid WHERE iid = $last_id";
Database::query($sql);
// insert into item_property
api_item_property_update(
api_get_course_info(),
TOOL_COURSE_DESCRIPTION,
$last_id,
'CourseDescriptionAdded',
api_get_user_id()
);
}
return $last_id > 0 ? 1 : 0;
}
/**
* Update a description, first you must set description_type, title, content, progress
* and session_id properties with the object CourseDescription.
*
* @return int affected rows
*/
public function update()
{
$table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
$params = [
'title' => $this->title,
'content' => $this->content,
'progress' => intval($this->progress),
];
Database::update(
$table,
$params,
[
'id = ? AND session_id = ? AND c_id = ?' => [
$this->id,
$this->session_id,
$this->course_id ? $this->course_id : api_get_course_int_id(),
],
]
);
if ($this->id > 0) {
// Insert into item_property
api_item_property_update(
api_get_course_info(),
TOOL_COURSE_DESCRIPTION,
$this->id,
'CourseDescriptionUpdated',
api_get_user_id()
);
}
return 1;
}
/**
* Delete a description, first you must set description_type and session_id
* properties with the object CourseDescription.
*
* @return int affected rows
*/
public function delete()
{
$table = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
$course_id = api_get_course_int_id();
$sql = "DELETE FROM $table
WHERE
c_id = $course_id AND
id = '".intval($this->id)."' AND
session_id = '".intval($this->session_id)."'";
$result = Database::query($sql);
$affected_rows = Database::affected_rows($result);
if ($this->id > 0) {
//insert into item_property
api_item_property_update(
api_get_course_info(),
TOOL_COURSE_DESCRIPTION,
$this->id,
'CourseDescriptionDeleted',
api_get_user_id()
);
}
return $affected_rows;
}
/**
* Get description titles by default.
*
* @return array
*/
public function get_default_description_title()
{
$default_description_titles = [];
$default_description_titles[1] = get_lang('GeneralDescription');
$default_description_titles[2] = get_lang('Objectives');
$default_description_titles[3] = get_lang('Topics');
$default_description_titles[4] = get_lang('Methodology');
$default_description_titles[5] = get_lang('CourseMaterial');
$default_description_titles[6] = get_lang('HumanAndTechnicalResources');
$default_description_titles[7] = get_lang('Assessment');
$default_description_titles[8] = get_lang('Other');
return $default_description_titles;
}
/**
* Get description titles editable by default.
*
* @return array
*/
public function get_default_description_title_editable()
{
$default_description_title_editable = [];
$default_description_title_editable[1] = true;
$default_description_title_editable[2] = true;
$default_description_title_editable[3] = true;
$default_description_title_editable[4] = true;
$default_description_title_editable[5] = true;
$default_description_title_editable[6] = true;
$default_description_title_editable[7] = true;
return $default_description_title_editable;
}
/**
* Get description icons by default.
*
* @return array
*/
public function get_default_description_icon()
{
$default_description_icon = [];
$default_description_icon[1] = 'info.png';
$default_description_icon[2] = 'objective.png';
$default_description_icon[3] = 'topics.png';
$default_description_icon[4] = 'strategy.png';
$default_description_icon[5] = 'laptop.png';
$default_description_icon[6] = 'teacher.png';
$default_description_icon[7] = 'assessment.png';
$default_description_icon[8] = 'wizard.png';
return $default_description_icon;
}
/**
* Get questions by default for help.
*
* @return array
*/
public function get_default_question()
{
$question = [];
$question[1] = get_lang('GeneralDescriptionQuestions');
$question[2] = get_lang('ObjectivesQuestions');
$question[3] = get_lang('TopicsQuestions');
$question[4] = get_lang('MethodologyQuestions');
$question[5] = get_lang('CourseMaterialQuestions');
$question[6] = get_lang('HumanAndTechnicalResourcesQuestions');
$question[7] = get_lang('AssessmentQuestions');
return $question;
}
/**
* Get informations by default for help.
*
* @return array
*/
public function get_default_information()
{
$information = [];
$information[1] = get_lang('GeneralDescriptionInformation');
$information[2] = get_lang('ObjectivesInformation');
$information[3] = get_lang('TopicsInformation');
$information[4] = get_lang('MethodologyInformation');
$information[5] = get_lang('CourseMaterialInformation');
$information[6] = get_lang('HumanAndTechnicalResourcesInformation');
$information[7] = get_lang('AssessmentInformation');
return $information;
}
/**
* Set description id.
*/
public function set_id($id)
{
$this->id = $id;
}
/**
* Set description's course id.
*
* @param int $id Course ID
*/
public function set_course_id($id)
{
$this->course_id = intval($id);
}
/**
* Set description title.
*
* @param string $title
*/
public function set_title($title)
{
$this->title = $title;
}
/**
* Set description content.
*
* @param string $content
*/
public function set_content($content)
{
$this->content = $content;
}
/**
* Set description session id.
*
* @param int $session_id
*/
public function set_session_id($session_id)
{
$this->session_id = $session_id;
}
/**
* Set description type.
*/
public function set_description_type($description_type)
{
$this->description_type = $description_type;
}
/**
* Set progress of a description.
*
* @param string $progress
*/
public function set_progress($progress)
{
$this->progress = $progress;
}
/**
* get description id.
*
* @return int
*/
public function get_id()
{
return $this->id;
}
/**
* get description title.
*
* @return string
*/
public function get_title()
{
return $this->title;
}
/**
* get description content.
*
* @return string
*/
public function get_content()
{
return $this->content;
}
/**
* get session id.
*
* @return int
*/
public function get_session_id()
{
return $this->session_id;
}
/**
* get description type.
*
* @return int
*/
public function get_description_type()
{
return $this->description_type;
}
/**
* get progress of a description.
*
* @return int
*/
public function get_progress()
{
return $this->progress;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,770 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Course request manager.
*
* @author José Manuel Abuin Mosquera <chema@cesga.es>, 2010
* @author Bruno Rubio Gayo <brubio@cesga.es>, 2010
* Centro de Supercomputacion de Galicia (CESGA)
* @author Ivan Tcholakov <ivantcholakov@gmail.com> (technical adaptation for Chamilo 1.8.8), 2010
*/
class CourseRequestManager
{
/**
* Checks whether a given course code has been already occupied.
*
* @param string $wanted_course_code the code to be checked
*
* @return bool
* Returns TRUE if there is created:
* - a course with the same code OR visual_code (visualcode).
* - a course request with the same code as the given one, or
* Othewise returns FALSE.
*/
public static function course_code_exists($wanted_course_code)
{
if ($code_exists = CourseManager::course_code_exists($wanted_course_code)) {
return $code_exists;
}
$table_course_request = Database::get_main_table(TABLE_MAIN_COURSE_REQUEST);
$wanted_course_code = Database::escape_string($wanted_course_code);
$sql = sprintf(
'SELECT COUNT(id) AS number FROM %s WHERE visual_code = "%s"',
$table_course_request,
$wanted_course_code
);
$result = Database::fetch_array(Database::query($sql));
return $result['number'] > 0;
}
/**
* Creates a new course request within the database.
*
* @param string $wanted_code the code for the created in the future course
* @param string $title
* @param string $description
* @param string $category_code
* @param string $course_language
* @param string $objectives
* @param string $target_audience
* @param int $user_id
*
* @return mixed the database id of the newly created course request or FALSE on failure
*/
public static function create_course_request(
$wanted_code,
$title,
$description,
$category_code,
$course_language,
$objectives,
$target_audience,
$user_id,
$exemplary_content
) {
$wanted_code = trim($wanted_code);
$user_id = (int) $user_id;
$exemplary_content = (bool) $exemplary_content ? 1 : 0;
if ('' == $wanted_code) {
return false;
}
if (self::course_code_exists($wanted_code)) {
return false;
}
if ($user_id <= 0) {
return false;
}
$user_info = api_get_user_info($user_id);
if (!is_array($user_info)) {
return false;
}
$tutor_name = api_get_person_name($user_info['firstname'], $user_info['lastname'], null, null, $course_language);
$request_date = api_get_utc_datetime();
$status = COURSE_REQUEST_PENDING;
$info = 0;
$keys = AddCourse::define_course_keys($wanted_code, '');
if (!count($keys)) {
return false;
}
$visual_code = $keys['currentCourseCode'];
$code = $keys['currentCourseId'];
$db_name = isset($keys['currentCourseDbName']) ? $keys['currentCourseDbName'] : null;
$directory = $keys['currentCourseRepository'];
// @todo user entity
$sql = sprintf(
'INSERT INTO %s (
code, user_id, directory, db_name,
course_language, title, description, category_code,
tutor_name, visual_code, request_date,
objetives, target_audience, status, info, exemplary_content)
VALUES (
"%s", "%s", "%s", "%s",
"%s", "%s", "%s", "%s",
"%s", "%s", "%s",
"%s", "%s", "%s", "%s", "%s");',
Database::get_main_table(TABLE_MAIN_COURSE_REQUEST),
Database::escape_string($code),
Database::escape_string($user_id),
Database::escape_string($directory),
Database::escape_string($db_name),
Database::escape_string($course_language),
Database::escape_string($title),
Database::escape_string($description),
Database::escape_string($category_code),
Database::escape_string($tutor_name),
Database::escape_string($visual_code),
Database::escape_string($request_date),
Database::escape_string($objectives),
Database::escape_string($target_audience),
Database::escape_string($status),
Database::escape_string($info),
Database::escape_string($exemplary_content)
);
$result_sql = Database::query($sql);
if (!$result_sql) {
return false;
}
$last_insert_id = Database::insert_id();
// E-mail notifications.
$email_language = $user_info['language'];
$email_language_admin = api_get_setting('platformLanguage');
$email_subject = sprintf(get_lang('CourseRequestEmailSubject', null, $email_language), '['.api_get_setting('siteName').']', $code);
$email_body = get_lang('CourseRequestMailOpening', null, $email_language)."\n\n";
$email_body .= get_lang('CourseName', null, $email_language).': '.$title."\n";
$email_body .= get_lang('Fac', null, $email_language).': '.$category_code."\n";
$email_body .= get_lang('CourseCode', null, $email_language).': '.$code."\n";
$email_body .= get_lang('Professor', null, $email_language).': '.api_get_person_name($user_info['firstname'], $user_info['lastname'], null, null, $email_language)."\n";
$email_body .= get_lang('Email', null, $email_language).': '.$user_info['mail']."\n";
$email_body .= get_lang('Description', null, $email_language).': '.$description."\n";
$email_body .= get_lang('Objectives', null, $email_language).': '.$objectives."\n";
$email_body .= get_lang('TargetAudience', null, $email_language).': '.$target_audience."\n";
$email_body .= get_lang('Ln', null, $email_language).': '.$course_language."\n";
$email_body .= get_lang('FillWithExemplaryContent', null, $email_language).': '.($exemplary_content ? get_lang('Yes', null, $email_language) : get_lang('No', null, $email_language))."\n";
$email_body = nl2br($email_body);
// Sending an e-mail to the platform administrator.
$email_body_admin = $email_body;
$email_body_admin .= "\n".get_lang('CourseRequestPageForApproval', null, $email_language_admin).' '.api_get_path(WEB_CODE_PATH).'admin/course_request_edit.php?id='.$last_insert_id."\n";
$email_body_admin .= "\n".get_lang('CourseRequestLegalNote', null, $email_language_admin)."\n";
$email_body_admin = nl2br($email_body_admin);
$sender_name_teacher = api_get_person_name($user_info['firstname'], $user_info['lastname'], null, PERSON_NAME_EMAIL_ADDRESS);
$sender_email_teacher = $user_info['mail'];
$recipient_name_admin = api_get_person_name(
api_get_setting('administratorName'),
api_get_setting('administratorSurname'),
null,
PERSON_NAME_EMAIL_ADDRESS
);
$recipient_email_admin = api_get_setting('emailAdministrator');
$userInfo = api_get_user_info($user_id);
$additionalParameters = [
'smsType' => SmsPlugin::NEW_COURSE_SUGGESTED_TEACHER,
'userId' => $user_id,
'userUsername' => $userInfo['username'],
];
api_mail_html(
$recipient_name_admin,
$recipient_email_admin,
$email_subject,
$email_body_admin,
$sender_name_teacher,
$sender_email_teacher,
null,
null,
null,
$additionalParameters
);
// Sending an e-mail to the requestor.
$email_body_teacher = get_lang('Dear', null, $email_language).' ';
$email_body_teacher .= api_get_person_name($user_info['firstname'], $user_info['lastname'], null, null, $email_language).",\n\n";
$email_body_teacher .= $email_body;
$email_body_teacher .= "\n".get_lang('SignatureFormula', null, $email_language)."\n";
$email_body_teacher .= api_get_person_name(
api_get_setting('administratorName'),
api_get_setting('administratorSurname'),
null,
null,
$email_language
)."\n";
$email_body_teacher .= get_lang('Manager', null, $email_language).' '.api_get_setting('siteName')."\n";
$email_body_teacher .= get_lang('Phone', null, $email_language).': '.api_get_setting('administratorTelephone')."\n";
$email_body_teacher .= get_lang('Email', null, $email_language).': '.api_get_setting('emailAdministrator', null, $email_language)."\n";
$email_body_teacher .= "\n".get_lang('CourseRequestLegalNote', null, $email_language)."\n";
$email_body_teacher = nl2br($email_body_teacher);
// Swap the sender and the recipient.
$sender_name_admin = $recipient_name_admin;
$sender_email_admin = $recipient_email_admin;
$recipient_name_teacher = $sender_name_teacher;
$recipient_email_teacher = $sender_email_teacher;
$additionalParameters = [
'smsType' => SmsPlugin::COURSE_OPENING_REQUEST_CODE_REGISTERED,
'userId' => $user_info['user_id'],
'courseCode' => $wanted_code,
];
api_mail_html(
$recipient_name_teacher,
$recipient_email_teacher,
$email_subject,
$email_body_teacher,
$sender_name_admin,
$sender_email_admin,
null,
null,
null,
$additionalParameters
);
return $last_insert_id;
}
/**
* Updates a given course request in the database.
*
* @param int $id the id (an integer number) of the corresponding database record
* @param string $wanted_code the code for the created in the future course
* @param string $title
* @param string $description
* @param string $category_code
* @param string $course_language
* @param string $objectives
* @param string $target_audience
* @param int $user_id
*
* @return bool returns TRUE on success or FALSE on failure
*/
public static function update_course_request(
$id,
$wanted_code,
$title,
$description,
$category_code,
$course_language,
$objectives,
$target_audience,
$user_id,
$exemplary_content
) {
$id = (int) $id;
$wanted_code = trim($wanted_code);
$user_id = (int) $user_id;
$exemplary_content = (bool) $exemplary_content ? 1 : 0;
if ('' == $wanted_code) {
return false;
}
if ($user_id <= 0) {
return false;
}
// Retrieve request data
$course_request_info = self::get_course_request_info($id);
if (!is_array($course_request_info)) {
return false;
}
$code = $wanted_code;
$tutor_name = $course_request_info['tutor_name'];
$directory = $course_request_info['directory'];
$visual_code = $course_request_info['visual_code'];
$request_date = $course_request_info['request_date'];
$status = $course_request_info['status'];
$info = $course_request_info['info'];
if ($wanted_code != $course_request_info['code']) {
if (self::course_code_exists($wanted_code)) {
return false;
}
$keys = AddCourse::define_course_keys($wanted_code, '');
if (count($keys)) {
$visual_code = $keys['currentCourseCode'];
$code = $keys['currentCourseId'];
$db_name = $keys['currentCourseDbName'];
$directory = $keys['currentCourseRepository'];
} else {
return false;
}
}
if ($user_id != $course_request_info['code']) {
$user_info = api_get_user_info($user_id);
if (is_array($user_info)) {
$tutor_name = api_get_person_name(
$user_info['firstname'],
$user_info['lastname'],
null,
null,
$course_language
);
} else {
$user_id = $course_request_info['code'];
}
}
if ($course_language != $course_request_info['course_language']) {
$user_info = api_get_user_info($user_id);
if (is_array($user_info)) {
$tutor_name = api_get_person_name(
$user_info['firstname'],
$user_info['lastname'],
null,
null,
$course_language
);
}
}
// @todo use entity
$sql = sprintf(
'UPDATE %s SET
code = "%s", user_id = "%s", directory = "%s", db_name = "%s",
course_language = "%s", title = "%s", description = "%s", category_code = "%s",
tutor_name = "%s", visual_code = "%s", request_date = "%s",
objetives = "%s", target_audience = "%s", status = "%s", info = "%s", exemplary_content = "%s"
WHERE id = '.$id,
Database::get_main_table(TABLE_MAIN_COURSE_REQUEST),
Database::escape_string($code),
intval($user_id),
Database::escape_string($directory),
Database::escape_string($db_name),
Database::escape_string($course_language),
Database::escape_string($title),
Database::escape_string($description),
Database::escape_string($category_code),
Database::escape_string($tutor_name),
Database::escape_string($visual_code),
Database::escape_string($request_date),
Database::escape_string($objectives),
Database::escape_string($target_audience),
Database::escape_string($status),
Database::escape_string($info),
Database::escape_string($exemplary_content)
);
$result_sql = Database::query($sql);
return false !== $result_sql;
}
/**
* Deletes a given course request.
*
* @param int $id the id (an integer number) of the corresponding database record
*
* @return bool returns TRUE on success or FALSE on failure
*/
public static function delete_course_request($id)
{
$id = (int) $id;
$sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_COURSE_REQUEST)."
WHERE id = ".$id;
$result = Database::query($sql);
return false !== $result;
}
/**
* Returns the number of course requests in the course_request table (optionally matching a status).
*
* @param int $status
*
* @return bool
*/
public static function count_course_requests($status = null)
{
$course_table = Database::get_main_table(TABLE_MAIN_COURSE_REQUEST);
if (is_null($status)) {
$sql = "SELECT COUNT(id) AS number FROM ".$course_table;
} else {
$status = (int) $status;
$sql = "SELECT COUNT(id) AS number FROM ".$course_table."
WHERE status = ".$status;
}
$result = Database::fetch_array(Database::query($sql));
if (is_array($result)) {
return $result['number'];
}
return false;
}
/**
* Gets all the information about a course request using its database id as an access key.
*
* @param int $id the id (an integer number) of the corresponding database record
*
* @return string|bool returns the requested data as an array or FALSE on failure
*/
public static function get_course_request_info($id)
{
$id = (int) $id;
$sql = "SELECT *
FROM ".Database::get_main_table(TABLE_MAIN_COURSE_REQUEST)."
WHERE id = ".$id;
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
return Database::fetch_array($result);
}
return false;
}
/**
* Gets the code of a given course request using its database id as an access key.
*
* @param int $id the id (an integer number) of the corresponding database record
*
* @return string|bool returns the requested requested code or FALSE on failure
*/
public static function get_course_request_code($id)
{
$id = (int) $id;
$sql = "SELECT code
FROM ".Database::get_main_table(TABLE_MAIN_COURSE_REQUEST)."
WHERE id = ".$id;
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
$result_array = Database::fetch_array($result, 'NUM');
if (is_array($result_array)) {
return $result_array[0];
}
}
return false;
}
/**
* Accepts a given by its id course request. The requested course gets created immediately after the request acceptance.
*
* @param int $id the id (an integer number) of the corresponding database record
*
* @return string|bool returns the code of the newly created course or FALSE on failure
*/
public static function accept_course_request($id)
{
$id = (int) $id;
// Retrieve request's data
$course_request_info = self::get_course_request_info($id);
if (!is_array($course_request_info)) {
return false;
}
// Make all the checks again before the new course creation.
/*if (CourseManager::course_code_exists($wanted_code)) {
return false;
}*/
$user_id = (int) $course_request_info['user_id'];
if ($user_id <= 0) {
return false;
}
$user_info = api_get_user_info($user_id);
if (!is_array($user_info)) {
return false;
}
// Create the requested course
$params = [];
$params['title'] = $course_request_info['title'];
$params['course_category'] = $course_request_info['category_code'];
$params['course_language'] = $course_request_info['course_language'];
$params['exemplary_content'] = intval($course_request_info['exemplary_content']) > 0;
$params['wanted_code'] = $course_request_info['code'];
$params['user_id'] = $course_request_info['user_id'];
$params['tutor_name'] = api_get_person_name($user_info['firstname'], $user_info['lastname']);
$course_info = CourseManager::create_course($params);
if (!empty($course_info)) {
// Mark the request as accepted.
$sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_COURSE_REQUEST)."
SET status = ".COURSE_REQUEST_ACCEPTED."
WHERE id = ".$id;
Database::query($sql);
// E-mail notification.
// E-mail language: The user language seems to be the best choice
$email_language = $user_info['language'];
$email_subject = sprintf(get_lang('CourseRequestAcceptedEmailSubject', null, $email_language), '['.api_get_setting('siteName').']', $course_info['code']);
$email_body = get_lang('Dear', null, $email_language).' ';
$email_body .= api_get_person_name($user_info['firstname'], $user_info['lastname'], null, null, $email_language).",\n\n";
$email_body .= sprintf(
get_lang(
'CourseRequestAcceptedEmailText',
null,
$email_language
),
$course_info['code'],
$course_info['code'],
api_get_path(WEB_COURSE_PATH).$course_info['directory'].'/'
)."\n";
$email_body .= "\n".get_lang('SignatureFormula', null, $email_language)."\n";
$email_body .= api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'), null, null, $email_language)."\n";
$email_body .= get_lang('Manager', null, $email_language).' '.api_get_setting('siteName')."\n";
$email_body .= get_lang('Phone', null, $email_language).': '.api_get_setting('administratorTelephone')."\n";
$email_body .= get_lang('Email', null, $email_language).': '.api_get_setting('emailAdministrator', null, $email_language)."\n";
$email_body .= "\n".get_lang('CourseRequestLegalNote', null, $email_language)."\n";
$email_body = nl2br($email_body);
$sender_name = api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'), null, PERSON_NAME_EMAIL_ADDRESS);
$sender_email = api_get_setting('emailAdministrator');
$recipient_name = api_get_person_name($user_info['firstname'], $user_info['lastname'], null, PERSON_NAME_EMAIL_ADDRESS);
$recipient_email = $user_info['mail'];
$additionalParameters = [
'smsType' => SmsPlugin::COURSE_OPENING_REQUEST_CODE_APPROVED,
'userId' => $user_id,
'courseCode' => $course_info['code'],
];
api_mail_html(
$recipient_name,
$recipient_email,
$email_subject,
$email_body,
$sender_name,
$sender_email,
null,
null,
null,
$additionalParameters
);
return $course_info['code'];
}
return false;
}
/**
* Rejects a given course request.
*
* @param int $id the id (an integer number) of the corresponding database record
*
* @return bool returns TRUE on success or FALSE on failure
*/
public static function reject_course_request($id)
{
$id = (int) $id;
// Retrieve request's data
$course_request_info = self::get_course_request_info($id);
if (!is_array($course_request_info)) {
return false;
}
$user_id = intval($course_request_info['user_id']);
if ($user_id <= 0) {
return false;
}
$user_info = api_get_user_info($user_id);
if (!is_array($user_info)) {
return false;
}
$code = $course_request_info['code'];
$sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_COURSE_REQUEST)."
SET status = ".COURSE_REQUEST_REJECTED."
WHERE id = ".$id;
if (false === Database::query($sql)) {
return false;
}
// E-mail notification.
// E-mail language: The user language seems to be the best choice.
$email_language = $user_info['language'];
$email_subject = sprintf(get_lang('CourseRequestRejectedEmailSubject', null, $email_language), '['.api_get_setting('siteName').']', $code);
$email_body = get_lang('Dear', null, $email_language).' ';
$email_body .= api_get_person_name($user_info['firstname'], $user_info['lastname'], null, null, $email_language).",\n\n";
$email_body .= sprintf(get_lang('CourseRequestRejectedEmailText', null, $email_language), $code)."\n";
$email_body .= "\n".get_lang('SignatureFormula', null, $email_language)."\n";
$email_body .= api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'), null, null, $email_language)."\n";
$email_body .= get_lang('Manager', null, $email_language).' '.api_get_setting('siteName')."\n";
$email_body .= get_lang('Phone', null, $email_language).': '.api_get_setting('administratorTelephone')."\n";
$email_body .= get_lang('Email', null, $email_language).': '.api_get_setting('emailAdministrator', null, $email_language)."\n";
$email_body .= "\n".get_lang('CourseRequestLegalNote', null, $email_language)."\n";
$email_body = nl2br($email_body);
$sender_name = api_get_person_name(
api_get_setting('administratorName'),
api_get_setting('administratorSurname'),
null,
PERSON_NAME_EMAIL_ADDRESS
);
$sender_email = api_get_setting('emailAdministrator');
$recipient_name = api_get_person_name(
$user_info['firstname'],
$user_info['lastname'],
null,
PERSON_NAME_EMAIL_ADDRESS
);
$recipient_email = $user_info['mail'];
$additionalParameters = [
'smsType' => SmsPlugin::COURSE_OPENING_REQUEST_CODE_REJECTED,
'userId' => $user_id,
'courseCode' => $code,
];
api_mail_html(
$recipient_name,
$recipient_email,
$email_subject,
$email_body,
$sender_name,
$sender_email,
null,
null,
null,
$additionalParameters
);
return true;
}
/**
* Asks the author (through e-mail) for additional information about the given course request.
*
* @param int $id the database primary id of the given request
*
* @return bool returns TRUE on success or FALSE on failure
*/
public static function ask_for_additional_info($id)
{
$id = (int) $id;
// Retrieve request's data
$course_request_info = self::get_course_request_info($id);
if (!is_array($course_request_info)) {
return false;
}
$user_id = intval($course_request_info['user_id']);
if ($user_id <= 0) {
return false;
}
$user_info = api_get_user_info($user_id);
if (!is_array($user_info)) {
return false;
}
$code = $course_request_info['code'];
$info = intval($course_request_info['info']);
// Error is to be returned on a repeated attempt for asking additional information.
if (!empty($info)) {
return false;
}
// E-mail notification.
// E-mail language: The user language seems to be the best choice.
$email_language = $user_info['language'];
$email_subject = sprintf(get_lang('CourseRequestAskInfoEmailSubject', null, $email_language), '['.api_get_setting('siteName').']', $code);
$email_body = get_lang('Dear', null, $email_language).' ';
$email_body .= api_get_person_name($user_info['firstname'], $user_info['lastname'], null, null, $email_language).",\n\n";
$email_body .= sprintf(get_lang('CourseRequestAskInfoEmailText', null, $email_language), $code)."\n";
$email_body .= "\n".get_lang('SignatureFormula', null, $email_language)."\n";
$email_body .= api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'))."\n";
$email_body .= get_lang('Manager', null, $email_language).' '.api_get_setting('siteName')."\n";
$email_body .= get_lang('Phone', null, $email_language).': '.api_get_setting('administratorTelephone')."\n";
$email_body .= get_lang('Email', null, $email_language).': '.api_get_setting('emailAdministrator')."\n";
$email_body .= "\n".get_lang('CourseRequestLegalNote', null, $email_language)."\n";
$email_body = nl2br($email_body);
$sender_name = api_get_person_name(
api_get_setting('administratorName'),
api_get_setting('administratorSurname'),
null,
PERSON_NAME_EMAIL_ADDRESS
);
$sender_email = api_get_setting('emailAdministrator');
$recipient_name = api_get_person_name(
$user_info['firstname'],
$user_info['lastname'],
null,
PERSON_NAME_EMAIL_ADDRESS
);
$recipient_email = $user_info['mail'];
$additionalParameters = [
'smsType' => SmsPlugin::COURSE_OPENING_REQUEST_CODE,
'userId' => $user_id,
'courseCode' => $code,
];
$result = api_mail_html(
$recipient_name,
$recipient_email,
$email_subject,
$email_body,
$sender_name,
$sender_email,
null,
null,
null,
$additionalParameters
);
if (!$result) {
return false;
}
// Marking the fact that additional information about the request has been asked.
$sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_COURSE_REQUEST)."
SET info = 1 WHERE id = ".$id;
$result = false !== Database::query($sql);
return $result;
}
/**
* Checks whether additional information about the given course request has been asked.
*
* @param int $id the database primary id of the given request
*
* @return bool returns TRUE if additional information has been asked or FALSE otherwise
*/
public static function additional_info_asked($id)
{
$id = (int) $id;
$sql = "SELECT id FROM ".Database::get_main_table(TABLE_MAIN_COURSE_REQUEST)."
WHERE (id = ".$id." AND info > 0)";
$result = Database::num_rows(Database::query($sql));
return !empty($result);
}
}

View File

@@ -0,0 +1,103 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Used to implement the loading of custom pages.
*
* @license see /license.txt
* @author 2011, Jean-Karim Bockstael <jeankarim@cblue.be>
* @author Laurent Opprecht <laurent@opprecht.info> for the Univesity of Geneva
*/
class CustomPages
{
public const INDEX_LOGGED = 'index-logged';
public const INDEX_UNLOGGED = 'index-unlogged';
public const LOGGED_OUT = 'loggedout';
public const REGISTRATION_FEEDBACK = 'registration-feedback';
public const REGISTRATION = 'registration';
public const LOST_PASSWORD = 'lostpassword';
/**
* Returns true if custom pages are enabled. False otherwise.
*
* @return bool
*/
public static function enabled()
{
return api_get_setting('use_custom_pages') == 'true';
}
/**
* Returns the path to a custom page.
*
* @param string $name
*
* @return string
*/
public static function path($name = '')
{
return api_get_path(SYS_PATH).'custompages/'.$name;
}
/**
* If enabled display a custom page and exist. Otherwise log error and returns.
*
* @param string $pageName
* @param array $content used to pass data to the custom page
*
* @return bool False if custom pages is not enabled or file could not be found. Void otherwise.
*/
public static function display($pageName, $content = [])
{
if (!self::enabled()) {
return false;
}
$file = self::path($pageName.'.php');
// Only include file if it exists, otherwise do nothing
if (file_exists($file)) {
include $file;
exit; //finish the execution here - do not return
}
return false;
}
/**
* Does not look like this function is being used is being used.
*
* @param int $url_id
*
* @return array
*/
public static function getURLImages($url_id = null)
{
if (is_null($url_id)) {
$url = 'http://'.$_SERVER['HTTP_HOST'].'/';
$url_id = UrlManager::get_url_id($url);
}
$url_images_dir = api_get_path(SYS_PATH).'custompages/url-images/';
$images = [];
for ($img_id = 1; $img_id <= 3; $img_id++) {
if (file_exists($url_images_dir.$url_id.'_url_image_'.$img_id.'.png')) {
$images[] = api_get_path(WEB_PATH).'custompages/url-images/'.$url_id.'_url_image_'.$img_id.'.png';
}
}
return $images;
}
/**
* Check if exists the file for custom page.
*
* @param string $pageName The name of custom page
*
* @return bool
*/
public static function exists($pageName)
{
$fileName = self::path("$pageName.php");
return file_exists($fileName);
}
}

View File

@@ -0,0 +1,552 @@
<?php
/* For licensing terms, see /license.txt */
/**
* DashboardManager can be used to manage dashboard
* author Christian Fasanando <christian1827@gmail.com>.
*
* @package chamilo.dashboard
*/
class DashboardManager
{
/**
* Constructor.
*/
public function __construct()
{
}
/**
* This function allows easy activating and inactivating of dashboard plugins.
*/
public static function handle_dashboard_plugins()
{
$token = Security::get_existing_token();
$tokenCondition = '&sec_token='.$token;
/* We scan the plugin directory. Each folder is a potential plugin. */
$dashboard_pluginpath = api_get_path(SYS_PLUGIN_PATH).'dashboard/';
$possiblePlugins = self::getPossibleDashboardPluginsPath();
$table_cols = ['name', 'version', 'description'];
echo Display::page_subheader(get_lang('DashboardPlugins'));
echo '<form name="plugins" method="post" action="'.api_get_self().'?category='.Security::remove_XSS($_GET['category']).$tokenCondition.'">';
echo '<table class="table table-hover table-striped data_table">';
echo '<tr>';
echo '<th width="50px">'.get_lang('Enabled').'</th>';
echo '<th width="250px">'.get_lang('Name').'</th>';
echo '<th width="100px">'.get_lang('Version').'</th>';
echo '<th>'.get_lang('Description').'</th>';
echo '</tr>';
$disabled_blocks_data = self::get_block_data_without_plugin();
// We display all the possible enabled or disabled plugins
foreach ($possiblePlugins as $testplugin) {
$plugin_info_file = $dashboard_pluginpath.$testplugin."/$testplugin.info";
if (file_exists($plugin_info_file) && is_readable($plugin_info_file)) {
$plugin_info = api_parse_info_file($plugin_info_file);
// change index to lower case
$plugin_info = array_change_key_case($plugin_info);
echo '<tr>';
self::display_dashboard_plugin_checkboxes($testplugin);
for ($i = 0; $i < count($table_cols); $i++) {
if (isset($plugin_info[strtolower($table_cols[$i])])) {
echo '<td>';
echo $plugin_info[$table_cols[$i]];
echo '</td>';
} else {
echo '<td></td>';
}
}
echo '</tr>';
} else {
if ($testplugin != 'css') {
echo Display::tag(
'tr',
Display::tag(
'td',
get_lang('CheckFilePermissions').' '.Security::remove_XSS($plugin_info_file),
['colspan' => '3']
)
);
}
}
}
// display all disabled block data
if (count($disabled_blocks_data) > 0) {
foreach ($disabled_blocks_data as $disabled_block) {
echo '<tr style="background-color:#eee">';
echo '<td><center><input type="checkbox" name="disabled_block" value="true" checked disabled /></center>';
for ($j = 0; $j < count($table_cols); $j++) {
if (isset($disabled_block[strtolower($table_cols[$j])])) {
if ($j == 2) {
echo '<td>';
echo '<font color="#aaa">'.$disabled_block[$table_cols[$j]].'</font><br />';
echo '<font color="red">'.get_lang('ThisPluginHasbeenDeletedFromDashboardPluginDirectory').'</font>';
echo '</td>';
} else {
echo '<td>';
echo '<font color="#aaa">'.$disabled_block[$table_cols[$j]].'</font>';
echo '</td>';
}
} else {
echo '<td>&nbsp;</td>';
}
}
echo '</tr>';
}
}
echo '</table>';
echo '<br />';
echo '<button class="btn btn-default" type="submit" name="submit_dashboard_plugins" value="'.get_lang('EnableDashboardPlugins').'">'.
get_lang('EnableDashboardPlugins').'</button></form>';
}
/**
* display checkboxes for dashboard plugin list.
*
* @param string $plugin_path
*/
public static function display_dashboard_plugin_checkboxes($plugin_path)
{
$tbl_block = Database::get_main_table(TABLE_MAIN_BLOCK);
$sql = "SELECT * FROM $tbl_block
WHERE path = '".Database::escape_string($plugin_path)."' AND active = 1";
$rs = Database::query($sql);
$checked = '';
if (Database::num_rows($rs) > 0) {
$checked = "checked";
}
echo "<td align=\"center\">";
echo '<input type="checkbox" name="'.$plugin_path.'" value="true" '.$checked.'/>';
echo "</td>";
}
/**
* This function allows easy activating and inactivating
* of plugins and save them inside db.
*
* @param array $plugin_paths dashboard plugin paths
* return int affected rows
*/
public static function store_dashboard_plugins($plugin_paths)
{
$tbl_block = Database::get_main_table(TABLE_MAIN_BLOCK);
$affected_rows = 0;
// get all plugins path inside plugin directory
$dashboard_pluginpath = api_get_path(SYS_PLUGIN_PATH).'dashboard/';
$possiblePlugins = self::getPossibleDashboardPluginsPath();
if (count($possiblePlugins) > 0) {
$selected_plugins = array_intersect(array_keys($plugin_paths), $possiblePlugins);
$not_selected_plugins = array_diff($possiblePlugins, array_keys($plugin_paths));
// get blocks id from not selected path
$not_selected_blocks_id = [];
foreach ($not_selected_plugins as $plugin) {
$block_data = self::get_enabled_dashboard_blocks($plugin);
if (!empty($block_data[$plugin])) {
$not_selected_blocks_id[] = $block_data[$plugin]['id'];
}
}
/* clean not selected plugins for extra user data and block data */
// clean from extra user data
$field_variable = 'dashboard';
$extra_user_data = UserManager::get_extra_user_data_by_field_variable($field_variable);
if (!empty($extra_user_data) && count($extra_user_data) > 0) {
foreach ($extra_user_data as $key => $user_data) {
$user_id = $key;
$user_block_data = self::get_user_block_data($user_id);
$user_block_id = array_keys($user_block_data);
// clean disabled block data
foreach ($user_block_id as $block_id) {
if (in_array($block_id, $not_selected_blocks_id)) {
unset($user_block_data[$block_id]);
}
}
// get columns and blocks id for updating extra user data
$columns = [];
$user_blocks_id = [];
foreach ($user_block_data as $data) {
$user_blocks_id[$data['block_id']] = true;
$columns[$data['block_id']] = $data['column'];
}
// update extra user blocks data
self::store_user_blocks($user_id, $user_blocks_id, $columns);
}
}
// clean from block data
if (!empty($not_selected_blocks_id)) {
$sql_check = "SELECT id FROM $tbl_block
WHERE id IN(".implode(',', $not_selected_blocks_id).")";
$rs_check = Database::query($sql_check);
if (Database::num_rows($rs_check) > 0) {
$del = "DELETE FROM $tbl_block WHERE id IN(".implode(',', $not_selected_blocks_id).")";
Database::query($del);
}
}
// store selected plugins
if (!empty($selected_plugins) && count($selected_plugins) > 0) {
foreach ($selected_plugins as $testplugin) {
$selected_path = Database::escape_string($testplugin);
// check if the path already stored inside block table for updating or adding it
$sql = "SELECT path FROM $tbl_block WHERE path = '$selected_path'";
$rs = Database::query($sql);
if (Database::num_rows($rs) > 0) {
// update
$upd = "UPDATE $tbl_block SET active = 1 WHERE path = '$selected_path'";
$result = Database::query($upd);
$affected_rows = Database::affected_rows($result);
} else {
// insert
$plugin_info_file = $dashboard_pluginpath.$testplugin."/$testplugin.info";
$plugin_info = [];
if (file_exists($plugin_info_file)) {
$plugin_info = api_parse_info_file($plugin_info_file);
}
// change keys to lower case
$plugin_info = array_change_key_case($plugin_info);
// setting variables
$plugin_name = $testplugin;
$plugin_description = '';
$plugin_controller = '';
$plugin_path = $testplugin;
if (isset($plugin_info['name'])) {
$plugin_name = Database::escape_string($plugin_info['name']);
}
if (isset($plugin_info['description'])) {
$plugin_description = Database::escape_string($plugin_info['description']);
}
if (isset($plugin_info['controller'])) {
$plugin_controller = Database::escape_string($plugin_info['controller']);
}
$ins = "INSERT INTO $tbl_block(name, description, path, controller)
VALUES ('$plugin_name', '$plugin_description', '$plugin_path', '$plugin_controller')";
$result = Database::query($ins);
$affected_rows = Database::affected_rows($result);
}
}
}
}
return $affected_rows;
}
/**
* Get all plugins path inside dashboard directory.
*
* @return array name plugins directories
*/
public static function getPossibleDashboardPluginsPath()
{
// get all plugins path inside plugin directory
/* We scan the plugin directory. Each folder is a potential plugin. */
$possiblePlugins = [];
$dashboard_pluginpath = api_get_path(SYS_PLUGIN_PATH).'dashboard/';
$handle = @opendir($dashboard_pluginpath);
while (false !== ($file = readdir($handle))) {
if ($file != '.' && $file != '..' && is_dir($dashboard_pluginpath.$file)) {
$possiblePlugins[] = $file;
}
}
@closedir($handle);
return $possiblePlugins;
}
/**
* Get all blocks data without plugin directory.
*
* @return array Block data
*/
public static function get_block_data_without_plugin()
{
$tbl_block = Database::get_main_table(TABLE_MAIN_BLOCK);
$possiblePlugins = self::getPossibleDashboardPluginsPath();
// We check if plugin exists inside directory for updating active field
$sql = "SELECT * FROM $tbl_block";
$rs = Database::query($sql);
if (Database::num_rows($rs) > 0) {
while ($row = Database::fetch_array($rs)) {
if (!in_array($row['path'], $possiblePlugins)) {
$active = 0;
} else {
$active = 1;
}
// update active
$upd = "UPDATE $tbl_block SET active = '$active'
WHERE path = '".$row['path']."'";
Database::query($upd);
}
}
// get disabled block data
$block_data = [];
$sql = "SELECT * FROM $tbl_block WHERE active = 0";
$rs_block = Database::query($sql);
if (Database::num_rows($rs_block) > 0) {
while ($row_block = Database::fetch_array($rs_block)) {
$block_data[] = $row_block;
}
}
return $block_data;
}
/**
* get data about enabled dashboard block (stored insise block table).
*
* @param string $path plugin path
*
* @return array data
*/
public static function get_enabled_dashboard_blocks($path = '')
{
$tbl_block = Database::get_main_table(TABLE_MAIN_BLOCK);
$condition_path = '';
if (!empty($path)) {
$path = Database::escape_string($path);
$condition_path = ' AND path = "'.$path.'" ';
}
$sql = "SELECT * FROM $tbl_block WHERE active = 1 $condition_path ";
$rs = Database::query($sql);
$block_data = [];
if (Database::num_rows($rs) > 0) {
while ($row = Database::fetch_array($rs)) {
$block_data[$row['path']] = $row;
}
}
return $block_data;
}
/**
* display user dashboard list.
*
* @param int User id
*/
public static function display_user_dashboard_list($user_id)
{
$enabled_dashboard_plugins = self::get_enabled_dashboard_blocks();
$user_block_data = self::get_user_block_data($user_id);
$html = '';
if (count($enabled_dashboard_plugins) > 0) {
$html .= '<div style="margin-top:20px">';
$html .= '<div><strong>'.get_lang('SelectBlockForDisplayingInsideBlocksDashboardView').'</strong></div><br />';
$html .= '<form name="dashboard_list" method="post" action="index.php?action=store_user_block">';
$html .= '<table class="table table-hover table-striped data_table">';
$html .= '<tr>';
$html .= '<th width="5%">';
$html .= get_lang('Enabled');
$html .= '</th>';
$html .= '<th width="30%">';
$html .= get_lang('Name');
$html .= '</th>';
$html .= '<th width="40%">';
$html .= get_lang('Description');
$html .= '</th>';
$html .= '<th>';
$html .= get_lang('ColumnPosition');
$html .= '</th>';
$html .= '</tr>';
// We display all enabled plugins and the checkboxes
foreach ($enabled_dashboard_plugins as $block) {
$path = $block['path'];
$controller_class = $block['controller'];
$filename_controller = $path.'.class.php';
$dashboard_plugin_path = api_get_path(SYS_PLUGIN_PATH).'dashboard/'.$path.'/';
require_once $dashboard_plugin_path.$filename_controller;
if (class_exists($controller_class)) {
$obj_block = new $controller_class($user_id);
// check if user is allowed to see the block
if (method_exists($obj_block, 'is_block_visible_for_user')) {
$is_block_visible_for_user = $obj_block->is_block_visible_for_user($user_id);
if (!$is_block_visible_for_user) {
continue;
}
}
$html .= '<tr>';
// checkboxes
$html .= self::display_user_dashboard_list_checkboxes($user_id, $block['id']);
$html .= '<td>'.$block['name'].'</td>';
$html .= '<td>'.$block['description'].'</td>';
$html .= '<td>
<select class="selectpicker form-control" name="columns['.$block['id'].']">
<option value="1" '.(isset($user_block_data[$block['id']]) && $user_block_data[$block['id']]['column'] == 1 ? 'selected' : '').' >1</option>
<option value="2" '.(isset($user_block_data[$block['id']]) && $user_block_data[$block['id']]['column'] == 2 ? 'selected' : '').' >2</option>
</select>
</td>';
$html .= '</tr>';
} else {
$html .= Display::tag('tr', Display::tag('td', get_lang('Error').' '.$controller_class, ['colspan' => '3']));
}
}
$html .= '</table>';
$html .= '<div class="row"><div class="col-md-12">';
$html .= '<button class="btn btn-default" type="submit" name="submit_dashboard_list" value="'.get_lang('EnableDashboardBlock').'"><em class="fa fa-check-square"></em> '.
get_lang('EnableDashboardBlock').'</button></form>';
$html .= '</div></div>';
} else {
$html .= '<div style="margin-top:20px">'.get_lang('ThereAreNoEnabledDashboardPlugins').'</div>';
if (api_is_platform_admin()) {
$html .= '<a class="btn btn-default" href="'.api_get_path(WEB_CODE_PATH).'admin/settings.php?category=Plugins">'.
get_lang('ConfigureDashboardPlugin').'</a>';
}
}
return $html;
}
/**
* display checkboxes for user dashboard list.
*
* @param int User id
* @param int Block id
*/
public static function display_user_dashboard_list_checkboxes($user_id, $block_id)
{
$user_id = intval($user_id);
$user_block_data = self::get_user_block_data($user_id);
$enabled_blocks_id = array_keys($user_block_data);
$checked = '';
if (in_array($block_id, $enabled_blocks_id)) {
$checked = "checked";
}
$html = "<td align=\"center\">";
$html .= '<input type="checkbox" name="enabled_blocks['.$block_id.']" value="true" '.$checked.'/>';
$html .= "</td>";
return $html;
}
/**
* This function store enabled blocks id with its column position (block_id1:colum;block_id2:colum; ...)
* inside extra user fields.
*
* @param int $user_id User id
* @param array $enabled_blocks selected blocks
* @param array $columns columns position
*
* @return bool
*/
public static function store_user_blocks($user_id, $enabled_blocks, $columns)
{
$selected_blocks_id = [];
if (is_array($enabled_blocks) && count($enabled_blocks) > 0) {
$selected_blocks_id = array_keys($enabled_blocks);
}
// build data for storing inside extra user field
$fname = 'dashboard';
$fvalue = [];
foreach ($selected_blocks_id as $block_id) {
$fvalue[] = $block_id.':'.$columns[$block_id];
}
$upd_extra_field = UserManager::update_extra_field_value(
$user_id,
$fname,
$fvalue
);
return $upd_extra_field;
}
/**
* This function get user block data (block id with its number of column) from extra user data.
*
* @param int User id
*
* @return array data (block_id,column)
*/
public static function get_user_block_data($user_id)
{
$user_id = intval($user_id);
$field_variable = 'dashboard';
$extra_user_data = UserManager::get_extra_user_data_by_field($user_id, $field_variable);
if (!isset($extra_user_data[$field_variable])) {
return [];
}
$extra_user_data = explode(';', $extra_user_data[$field_variable]);
$data = [];
foreach ($extra_user_data as $extra) {
$split_extra = explode(':', $extra);
if (!empty($split_extra)) {
$block_id = $split_extra[0];
$column = isset($split_extra[1]) ? $split_extra[1] : null;
$data[$block_id] = ['block_id' => $block_id, 'column' => $column];
}
}
return $data;
}
/**
* This function update extra user blocks data after closing a dashboard block.
*
* @param int $user_id User id
* @param string plugin path
*
* @return bool
*/
public static function close_user_block($user_id, $path)
{
$enabled_dashboard_blocks = self::get_enabled_dashboard_blocks($path);
$user_block_data = self::get_user_block_data($user_id);
foreach ($enabled_dashboard_blocks as $enabled_block) {
unset($user_block_data[$enabled_block['id']]);
}
// get columns and blocks id for updating extra user data
$columns = [];
$user_blocks_id = [];
foreach ($user_block_data as $data) {
$user_blocks_id[$data['block_id']] = true;
$columns[$data['block_id']] = $data['column'];
}
// update extra user blocks data
$upd_extra_field = self::store_user_blocks($user_id, $user_blocks_id, $columns);
return $upd_extra_field;
}
/**
* get links for styles from dashboard plugins.
*
* @return string links
*/
public static function getStyleSheet()
{
return '<link rel="stylesheet" href="'.api_get_path(WEB_PLUGIN_PATH).'dashboard/css/default.css" type="text/css" />'.PHP_EOL;
}
}

View File

@@ -0,0 +1,353 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Database constants definition for Chamilo
* This file is called by database.lib.php.
*
* @todo the table constants have all to start with TABLE_
* This is because of the analogy with the tool constants TOOL_
*/
//See #3910 defines the default prefix for the single course database
// Modified by hubert.borderiou 2011-10-21 Add course category
define('DB_COURSE_PREFIX', 'c_');
// Main database tables
define('TABLE_MAIN_COURSE', 'course');
define('TABLE_MAIN_USER', 'user');
define('TABLE_MAIN_CLASS', 'class_item');
define('TABLE_MAIN_ADMIN', 'admin');
define('TABLE_MAIN_COURSE_CLASS', 'course_rel_class');
define('TABLE_MAIN_COURSE_USER', 'course_rel_user');
define('TABLE_MAIN_COURSE_CATALOGUE_USER', 'course_rel_user_catalogue');
define('TABLE_MAIN_CLASS_USER', 'class_user');
define('TABLE_MAIN_CATEGORY', 'course_category');
define('TABLE_MAIN_COURSE_MODULE', 'course_module');
define('TABLE_MAIN_SYSTEM_ANNOUNCEMENTS', 'sys_announcement');
define('TABLE_MAIN_SYSTEM_ANNOUNCEMENTS_GROUPS', 'announcement_rel_group');
define('TABLE_MAIN_LANGUAGE', 'language');
define('TABLE_MAIN_SETTINGS_OPTIONS', 'settings_options');
define('TABLE_MAIN_SETTINGS_CURRENT', 'settings_current');
define('TABLE_MAIN_SESSION', 'session');
define('TABLE_MAIN_SESSION_CATEGORY', 'session_category');
define('TABLE_MAIN_SESSION_COURSE', 'session_rel_course');
define('TABLE_MAIN_SESSION_USER', 'session_rel_user');
define('TABLE_MAIN_SESSION_CLASS', 'session_rel_class');
define('TABLE_MAIN_SESSION_COURSE_USER', 'session_rel_course_rel_user');
define('TABLE_MAIN_SHARED_SURVEY', 'shared_survey');
define('TABLE_MAIN_SHARED_SURVEY_QUESTION', 'shared_survey_question');
define('TABLE_MAIN_SHARED_SURVEY_QUESTION_OPTION', 'shared_survey_question_option');
define('TABLE_MAIN_TEMPLATES', 'templates');
define('TABLE_MAIN_SYSTEM_TEMPLATE', 'system_template');
define('TABLE_MAIN_OPENID_ASSOCIATION', 'openid_association');
define('TABLE_MAIN_COURSE_REQUEST', 'course_request');
// Gradebook.
define('TABLE_MAIN_GRADEBOOK_CATEGORY', 'gradebook_category');
define('TABLE_MAIN_GRADEBOOK_EVALUATION', 'gradebook_evaluation');
define('TABLE_MAIN_GRADEBOOK_LINKEVAL_LOG', 'gradebook_linkeval_log');
define('TABLE_MAIN_GRADEBOOK_RESULT', 'gradebook_result');
define('TABLE_MAIN_GRADEBOOK_RESULT_LOG', 'gradebook_result_log');
define('TABLE_MAIN_GRADEBOOK_LINK', 'gradebook_link');
define('TABLE_MAIN_GRADEBOOK_SCORE_DISPLAY', 'gradebook_score_display');
define('TABLE_MAIN_GRADEBOOK_CERTIFICATE', 'gradebook_certificate');
define('TABLE_MAIN_GRADEBOOK_SCORE_LOG', 'gradebook_score_log');
define('TABLE_MAIN_GRADEBOOK_RESULT_ATTEMPT', 'gradebook_result_attempt');
define('TABLE_MAIN_GRADEBOOK_COMMENT', 'gradebook_comment');
// Extra fields.
define('TABLE_EXTRA_FIELD', 'extra_field');
define('TABLE_EXTRA_FIELD_OPTIONS', 'extra_field_options');
define('TABLE_EXTRA_FIELD_VALUES', 'extra_field_values');
define('TABLE_MAIN_USER_FIELD', 'user_field');
define('TABLE_MAIN_USER_FIELD_OPTIONS', 'user_field_options');
define('TABLE_MAIN_USER_FIELD_VALUES', 'user_field_values');
/*define('TABLE_MAIN_LP_FIELD', 'lp_field');
define('TABLE_MAIN_LP_FIELD_OPTIONS', 'lp_field_options');
define('TABLE_MAIN_LP_FIELD_VALUES', 'lp_field_values');*/
/*define('TABLE_MAIN_CALENDAR_EVENT_FIELD', 'calendar_event_field');
define('TABLE_MAIN_CALENDAR_EVENT_OPTIONS', 'calendar_event_options');
define('TABLE_MAIN_CALENDAR_EVENT_VALUES', 'calendar_event_values');*/
//User tags
define('TABLE_MAIN_TAG', 'tag');
define('TABLE_MAIN_USER_REL_TAG', 'user_rel_tag');
define('TABLE_MAIN_EXTRA_FIELD_REL_TAG', 'extra_field_rel_tag');
// Search engine
define('TABLE_MAIN_SPECIFIC_FIELD', 'specific_field');
define('TABLE_MAIN_SPECIFIC_FIELD_VALUES', 'specific_field_values');
define('TABLE_MAIN_SEARCH_ENGINE_REF', 'search_engine_ref');
// Access URLs
define('TABLE_MAIN_ACCESS_URL', 'access_url');
define('TABLE_MAIN_ACCESS_URL_REL_USER', 'access_url_rel_user');
define('TABLE_MAIN_ACCESS_URL_REL_COURSE', 'access_url_rel_course');
define('TABLE_MAIN_ACCESS_URL_REL_SESSION', 'access_url_rel_session');
define('TABLE_MAIN_ACCESS_URL_REL_USERGROUP', 'access_url_rel_usergroup');
// This table seems not to be use
define('TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY', 'access_url_rel_course_category');
// Global calendar
define('TABLE_MAIN_SYSTEM_CALENDAR', 'sys_calendar');
// Social networking
define('TABLE_MAIN_USER_REL_USER', 'user_rel_user');
define('TABLE_MAIN_USER_FRIEND_RELATION_TYPE', 'user_friend_relation_type');
define('TABLE_MAIN_COURSE_FIELD', 'course_field');
define('TABLE_MAIN_COURSE_FIELD_OPTIONS', 'course_field_options');
define('TABLE_MAIN_COURSE_FIELD_VALUES', 'course_field_values');
define('TABLE_MAIN_SESSION_FIELD', 'session_field');
define('TABLE_MAIN_SESSION_FIELD_OPTIONS', 'session_field_options');
define('TABLE_MAIN_SESSION_FIELD_VALUES', 'session_field_values');
// Web services
define('TABLE_MAIN_USER_API_KEY', 'user_api_key');
// Term and conditions
define('TABLE_MAIN_LEGAL', 'legal');
// Dashboard blocks plugin
define('TABLE_MAIN_BLOCK', 'block');
// Statistic database tables
define('TABLE_STATISTIC_TRACK_E_LASTACCESS', 'track_e_lastaccess');
// Access to specific tools inside a course
define('TABLE_STATISTIC_TRACK_E_ACCESS', 'track_e_access');
define('TABLE_STATISTIC_TRACK_E_LOGIN', 'track_e_login');
define('TABLE_STATISTIC_TRACK_E_DOWNLOADS', 'track_e_downloads');
define('TABLE_STATISTIC_TRACK_E_LINKS', 'track_e_links');
define('TABLE_STATISTIC_TRACK_E_ONLINE', 'track_e_online');
define('TABLE_STATISTIC_TRACK_E_HOTPOTATOES', 'track_e_hotpotatoes');
// Access date data (in and out dates in course)
define('TABLE_STATISTIC_TRACK_E_COURSE_ACCESS', 'track_e_course_access');
define('TABLE_STATISTIC_TRACK_E_EXERCISES', 'track_e_exercises');
define('TABLE_STATISTIC_TRACK_E_ATTEMPT', 'track_e_attempt');
define('TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING', 'track_e_attempt_recording');
define('TABLE_STATISTIC_TRACK_E_DEFAULT', 'track_e_default');
define('TABLE_STATISTIC_TRACK_E_UPLOADS', 'track_e_uploads');
define('TABLE_STATISTIC_TRACK_E_HOTSPOT', 'track_e_hotspot');
define('TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY', 'track_e_item_property');
define('TABLE_STATISTIC_TRACK_FILTERED_TERMS', 'track_filtered_terms');
// Course catalog stats see #4191
define('TABLE_STATISTIC_TRACK_COURSE_RANKING', 'track_course_ranking');
define('TABLE_MAIN_USER_REL_COURSE_VOTE', 'user_rel_course_vote');
// Course tables
define('TABLE_AGENDA', 'calendar_event');
define('TABLE_AGENDA_REPEAT', 'calendar_event_repeat');
define('TABLE_AGENDA_REPEAT_NOT', 'calendar_event_repeat_not');
define('TABLE_AGENDA_ATTACHMENT', 'calendar_event_attachment');
define('TABLE_ANNOUNCEMENT', 'announcement');
define('TABLE_ANNOUNCEMENT_ATTACHMENT', 'announcement_attachment');
define('TABLE_CHAT_CONNECTED', 'chat_connected');
define('TABLE_COURSE_DESCRIPTION', 'course_description');
define('TABLE_DOCUMENT', 'document');
define('TABLE_ITEM_PROPERTY', 'item_property');
define('TABLE_LINK', 'link');
define('TABLE_LINK_CATEGORY', 'link_category');
define('TABLE_TOOL_LIST', 'tool');
define('TABLE_TOOL_INTRO', 'tool_intro');
define('TABLE_SCORMDOC', 'scormdocument');
define('TABLE_STUDENT_PUBLICATION', 'student_publication');
define('TABLE_STUDENT_PUBLICATION_ASSIGNMENT', 'student_publication_assignment');
define('TABLE_STUDENT_PUBLICATION_REL_DOCUMENT', 'student_publication_rel_document');
define('TABLE_STUDENT_PUBLICATION_REL_USER', 'student_publication_rel_user');
define('TABLE_STUDENT_PUBLICATION_ASSIGNMENT_COMMENT', 'student_publication_comment');
define('TABLE_PLAGIARISM', 'plagiarism_compilatio_docs');
// Course forum tables
define('TABLE_FORUM_CATEGORY', 'forum_category');
define('TABLE_FORUM', 'forum_forum');
define('TABLE_FORUM_THREAD', 'forum_thread');
define('TABLE_FORUM_POST', 'forum_post');
define('TABLE_FORUM_ATTACHMENT', 'forum_attachment');
define('TABLE_FORUM_MAIL_QUEUE', 'forum_mailcue');
define('TABLE_FORUM_THREAD_QUALIFY', 'forum_thread_qualify');
define('TABLE_FORUM_THREAD_QUALIFY_LOG', 'forum_thread_qualify_log');
define('TABLE_FORUM_NOTIFICATION', 'forum_notification');
// Course group tables
define('TABLE_GROUP', 'group_info');
define('TABLE_GROUP_USER', 'group_rel_user');
define('TABLE_GROUP_TUTOR', 'group_rel_tutor');
define('TABLE_GROUP_CATEGORY', 'group_category');
// Course dropbox tables
define('TABLE_DROPBOX_CATEGORY', 'dropbox_category');
define('TABLE_DROPBOX_FEEDBACK', 'dropbox_feedback');
define('TABLE_DROPBOX_POST', 'dropbox_post');
define('TABLE_DROPBOX_FILE', 'dropbox_file');
define('TABLE_DROPBOX_PERSON', 'dropbox_person');
// Course quiz (or test, or exercise) tables
define('TABLE_QUIZ_QUESTION', 'quiz_question');
define('TABLE_QUIZ_TEST', 'quiz');
define('TABLE_QUIZ_ORDER', 'quiz_order');
define('TABLE_QUIZ_ANSWER', 'quiz_answer');
define('TABLE_QUIZ_TEST_QUESTION', 'quiz_rel_question');
define('TABLE_QUIZ_QUESTION_OPTION', 'quiz_question_option');
define('TABLE_QUIZ_QUESTION_CATEGORY', 'quiz_question_category');
define('TABLE_QUIZ_QUESTION_REL_CATEGORY', 'quiz_question_rel_category');
define('TABLE_QUIZ_REL_CATEGORY', 'quiz_rel_category');
define('TABLE_LP_MAIN', 'lp');
define('TABLE_LP_ITEM', 'lp_item');
define('TABLE_LP_VIEW', 'lp_view');
define('TABLE_LP_ITEM_VIEW', 'lp_item_view');
define('TABLE_LP_IV_INTERACTION', 'lp_iv_interaction'); // IV = Item View
define('TABLE_LP_IV_OBJECTIVE', 'lp_iv_objective'); // IV = Item View
define('TABLE_LP_CATEGORY', 'lp_category');
define('TABLE_LP_REL_USERGROUP', 'lp_rel_usergroup');
define('TABLE_LP_CATEGORY_REL_USERGROUP', 'lp_category_rel_usergroup');
// Smartblogs (Kevin Van Den Haute::kevin@develop-it.be)
// Permission tables
define('TABLE_PERMISSION_USER', 'permission_user');
define('TABLE_PERMISSION_TASK', 'permission_task');
define('TABLE_PERMISSION_GROUP', 'permission_group');
// Role tables
define('TABLE_ROLE', 'role');
define('TABLE_ROLE_PERMISSION', 'role_permissions');
define('TABLE_ROLE_USER', 'role_user');
define('TABLE_ROLE_GROUP', 'role_group');
// Blog tables
define('TABLE_BLOGS', 'blog');
define('TABLE_BLOGS_POSTS', 'blog_post');
define('TABLE_BLOGS_COMMENTS', 'blog_comment');
define('TABLE_BLOGS_REL_USER', 'blog_rel_user');
define('TABLE_BLOGS_TASKS', 'blog_task');
define('TABLE_BLOGS_TASKS_REL_USER', 'blog_task_rel_user');
define('TABLE_BLOGS_RATING', 'blog_rating');
define('TABLE_BLOGS_ATTACHMENT', 'blog_attachment');
define('TABLE_BLOGS_TASKS_PERMISSIONS', 'permission_task');
// Course settings table
define('TABLE_COURSE_SETTING', 'course_setting');
// Course online tables
define('TABLE_ONLINE_LINK', 'online_link');
define('TABLE_ONLINE_CONNECTED', 'online_connected');
// User database
define('TABLE_PERSONAL_AGENDA', 'personal_agenda');
define('TABLE_PERSONAL_AGENDA_REPEAT', 'personal_agenda_repeat');
define('TABLE_PERSONAL_AGENDA_REPEAT_NOT', 'personal_agenda_repeat_not');
define('TABLE_USER_COURSE_CATEGORY', 'user_course_category');
// Survey
// @TODO: Are these MAIN tables or course tables?
// @TODO: Probably these constants are obsolete.
define('TABLE_MAIN_SURVEY', 'survey');
define('TABLE_MAIN_SURVEYQUESTION', 'questions');
// Survey
define('TABLE_SURVEY', 'survey');
define('TABLE_SURVEY_QUESTION', 'survey_question');
define('TABLE_SURVEY_QUESTION_OPTION', 'survey_question_option');
define('TABLE_SURVEY_INVITATION', 'survey_invitation');
define('TABLE_SURVEY_ANSWER', 'survey_answer');
define('TABLE_SURVEY_QUESTION_GROUP', 'survey_group');
define('TABLE_SURVEY_REPORT', 'survey_report');
// Wiki tables
define('TABLE_WIKI', 'wiki');
define('TABLE_WIKI_CONF', 'wiki_conf');
define('TABLE_WIKI_DISCUSS', 'wiki_discuss');
define('TABLE_WIKI_MAILCUE', 'wiki_mailcue');
// Glossary
define('TABLE_GLOSSARY', 'glossary');
// Notebook
define('TABLE_NOTEBOOK', 'notebook');
// Message
define('TABLE_MESSAGE', 'message');
define('TABLE_MESSAGE_ATTACHMENT', 'message_attachment');
// Attendance Sheet
define('TABLE_ATTENDANCE', 'attendance');
define('TABLE_ATTENDANCE_CALENDAR', 'attendance_calendar');
define('TABLE_ATTENDANCE_CALENDAR_REL_GROUP', 'attendance_calendar_rel_group');
define('TABLE_ATTENDANCE_SHEET_LOG', 'attendance_sheet_log');
define('TABLE_ATTENDANCE_SHEET', 'attendance_sheet');
define('TABLE_ATTENDANCE_RESULT', 'attendance_result');
// Thematic
define('TABLE_THEMATIC', 'thematic');
define('TABLE_THEMATIC_PLAN', 'thematic_plan');
define('TABLE_THEMATIC_ADVANCE', 'thematic_advance');
// Careers, promotions, Usergroups
define('TABLE_CAREER', 'career');
define('TABLE_PROMOTION', 'promotion');
define('TABLE_USERGROUP', 'usergroup');
define('TABLE_USERGROUP_REL_USER', 'usergroup_rel_user');
define('TABLE_USERGROUP_REL_COURSE', 'usergroup_rel_course');
define('TABLE_USERGROUP_REL_SESSION', 'usergroup_rel_session');
define('TABLE_USERGROUP_REL_USERGROUP', 'usergroup_rel_usergroup');
// Mail notifications
define('TABLE_NOTIFICATION', 'notification');
//Storage api tables
define('TABLE_TRACK_STORED_VALUES', 'track_stored_values');
define('TABLE_TRACK_STORED_VALUES_STACK', 'track_stored_values_stack');
//Event tables
define('TABLE_EVENT_EMAIL_TEMPLATE', 'event_email_template');
define('TABLE_EVENT_TYPE_REL_USER', 'user_rel_event_type');
define('TABLE_EVENT_SENT', 'event_sent');
define('TABLE_MAIN_SKILL', 'skill');
define('TABLE_MAIN_SKILL_REL_SKILL', 'skill_rel_skill');
define('TABLE_MAIN_SKILL_REL_GRADEBOOK', 'skill_rel_gradebook');
define('TABLE_MAIN_SKILL_REL_USER', 'skill_rel_user');
define('TABLE_MAIN_SKILL_PROFILE', 'skill_profile');
define('TABLE_MAIN_SKILL_REL_PROFILE', 'skill_rel_profile');
define('TABLE_MAIN_CHAT', 'chat');
define('TABLE_TIMELINE', 'timeline');
define('TABLE_MAIN_CHAT_VIDEO', 'chat_video');
// Gradebook model
define('TABLE_GRADE_MODEL', 'grade_model');
define('TABLE_GRADE_MODEL_COMPONENTS', 'grade_components');
// Hook tables
define('TABLE_HOOK_OBSERVER', 'hook_observer');
define('TABLE_HOOK_CALL', 'hook_call');
define('TABLE_HOOK_EVENT', 'hook_event');
define('TABLE_TICKET_ASSIGNED_LOG', 'ticket_assigned_log');
define('TABLE_TICKET_CATEGORY', 'ticket_category');
define('TABLE_TICKET_MESSAGE', 'ticket_message');
define('TABLE_TICKET_PRIORITY', 'ticket_priority');
define('TABLE_TICKET_PROJECT', 'ticket_project');
define('TABLE_TICKET_STATUS', 'ticket_status');
define('TABLE_TICKET_TICKET', 'ticket_ticket');
define('TABLE_TICKET_CATEGORY_REL_USER', 'ticket_category_rel_user');
define('TABLE_TICKET_MESSAGE_ATTACHMENTS', 'ticket_message_attachments');
define('TABLE_BRANCH', 'branch_sync');
define('TABLE_BRANCH_TRANSACTION', 'branch_transaction');
define('TABLE_BRANCH_TRANSACTION_STATUS', 'branch_transaction_status');
// main/extra @todo after code is finished remove constants not used
define('TABLE_CAL_DATE', 'cal_dates');
define('TABLE_CAL_HORAIRE', 'cal_horaire');
define('TABLE_CAL_TEMP', 'cal_temp');
define('TABLE_STATISTIC_TRACK_E_EXERCICES_TEMP', 'track_e_exercices_temp');
define('TABLE_USER_INFO_DEF', 'userinfo_def');
define('TABLE_USER_INFO_CONTENT', 'userinfo_content');
define('TABLE_MAIN_USER_CAREER', 'user_career');

View File

@@ -0,0 +1,851 @@
<?php
/* For licensing terms, see /license.txt */
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Proxy\AbstractProxyFactory;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Statement;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Setup;
use Symfony\Component\Debug\ExceptionHandler;
/**
* Class Database.
*/
class Database
{
public static $utcDateTimeClass;
/**
* @var EntityManager
*/
private static $em;
private static $connection;
/**
* Set the DB manager instance.
*
* @param EntityManager $em
*/
public function setManager($em)
{
self::$em = $em;
}
/**
* Set the DB connection instance.
*/
public function setConnection(Connection $connection)
{
self::$connection = $connection;
}
/**
* Get the DB connection instance.
*
* @return Connection
*/
public function getConnection()
{
return self::$connection;
}
/**
* Get the DB manager instance.
*
* @return EntityManager
*/
public static function getManager()
{
return self::$em;
}
/**
* Returns the name of the main database.
*
* @return string
*/
public static function get_main_database()
{
return self::getManager()->getConnection()->getDatabase();
}
/**
* Get main table.
*
* @param string $table
*
* @return string
*/
public static function get_main_table($table)
{
return $table;
}
/**
* Get course table.
*
* @param string $table
*
* @return string
*/
public static function get_course_table($table)
{
return DB_COURSE_PREFIX.$table;
}
/**
* Counts the number of rows in a table.
*
* @param string $table The table of which the rows should be counted
*
* @return int the number of rows in the given table
*
* @deprecated
*/
public static function count_rows($table)
{
$obj = self::fetch_object(self::query("SELECT COUNT(*) AS n FROM $table"));
return $obj->n;
}
/**
* Returns the number of affected rows in the last database operation.
*
* @return int
*/
public static function affected_rows($result)
{
return $result->rowCount();
}
/**
* @return string
*/
public static function getUTCDateTimeTypeClass()
{
return isset(self::$utcDateTimeClass) ? self::$utcDateTimeClass : 'Application\DoctrineExtensions\DBAL\Types\UTCDateTimeType';
}
/**
* Connect to the database sets the entity manager.
*
* @param array $params
* @param string $sysPath
* @param string $entityRootPath
* @param bool $returnConnection
* @param bool $returnManager
*
* @throws \Doctrine\ORM\ORMException
*
* @return
*/
public function connect(
$params = [],
$sysPath = '',
$entityRootPath = '',
$returnConnection = false,
$returnManager = false
) {
$config = self::getDoctrineConfig($entityRootPath);
$params['charset'] = 'utf8';
$entityManager = EntityManager::create($params, $config);
$sysPath = !empty($sysPath) ? $sysPath : api_get_path(SYS_PATH);
// Registering Constraints
/*AnnotationRegistry::registerAutoloadNamespace(
'Symfony\Component',
$sysPath."vendor/"
);*/
AnnotationRegistry::registerLoader(
function ($class) use ($sysPath) {
$file = str_replace("\\", DIRECTORY_SEPARATOR, $class).".php";
$file = str_replace('Symfony/Component/Validator', '', $file);
$file = str_replace('Symfony\Component\Validator', '', $file);
$fileToInclude = $sysPath.'vendor/symfony/validator/'.$file;
if (file_exists($fileToInclude)) {
// file exists makes sure that the loader fails silently
require_once $fileToInclude;
return true;
}
$fileToInclude = $sysPath.'vendor/symfony/validator/Constraints/'.$file;
if (file_exists($fileToInclude)) {
// file exists makes sure that the loader fails silently
require_once $fileToInclude;
return true;
}
}
);
AnnotationRegistry::registerFile(
$sysPath."vendor/symfony/doctrine-bridge/Validator/Constraints/UniqueEntity.php"
);
// Registering gedmo extensions
AnnotationRegistry::registerAutoloadNamespace(
'Gedmo\Mapping\Annotation',
$sysPath."vendor/gedmo/doctrine-extensions/lib"
);
Type::overrideType(
Type::DATETIME,
self::getUTCDateTimeTypeClass()
);
$listener = new \Gedmo\Timestampable\TimestampableListener();
$entityManager->getEventManager()->addEventSubscriber($listener);
$listener = new \Gedmo\Tree\TreeListener();
$entityManager->getEventManager()->addEventSubscriber($listener);
$listener = new \Gedmo\Sortable\SortableListener();
$entityManager->getEventManager()->addEventSubscriber($listener);
$connection = $entityManager->getConnection();
$connection->executeQuery('SET sql_mode = "";');
$connection->executeQuery('SET SESSION sql_mode = ""');
if ($returnConnection) {
return $connection;
}
if ($returnManager) {
return $entityManager;
}
$this->setConnection($connection);
$this->setManager($entityManager);
}
/**
* Escape MySQL wildcards _ and % in LIKE search.
*
* @param string $text The string to escape
*
* @return string The escaped string
*/
public static function escape_sql_wildcards($text)
{
$text = api_preg_replace("/_/", "\_", $text);
$text = api_preg_replace("/%/", "\%", $text);
return $text;
}
/**
* Escapes a string to insert into the database as text.
*
* @param string $string
*
* @return string
*/
public static function escape_string($string)
{
$string = self::getManager()->getConnection()->quote($string);
// The quote method from PDO also adds quotes around the string, which
// is not how the legacy mysql_real_escape_string() was used in
// Chamilo, so we need to remove the quotes around. Using trim will
// remove more than one quote if they are sequenced, generating
// broken queries and SQL injection risks
return substr($string, 1, -1);
}
/**
* Gets the array from a SQL result (as returned by Database::query).
*
* @param string $option Optional: "ASSOC","NUM" or "BOTH"
*
* @return array|mixed
*/
public static function fetch_array($result, $option = 'BOTH')
{
if ($result === false) {
return [];
}
return $result->fetch(self::customOptionToDoctrineOption($option));
}
/**
* Gets an associative array from a SQL result (as returned by Database::query).
*
* @return array
*/
public static function fetch_assoc($result)
{
return $result->fetch(PDO::FETCH_ASSOC);
}
/**
* Gets the next row of the result of the SQL query
* (as returned by Database::query) in an object form.
*
* @return mixed
*/
public static function fetch_object($result)
{
return $result->fetch(PDO::FETCH_OBJ);
}
/**
* Gets the array from a SQL result (as returned by Database::query)
* help achieving database independence.
*
* @return mixed
*/
public static function fetch_row($result)
{
if ($result === false) {
return [];
}
return $result->fetch(PDO::FETCH_NUM);
}
/**
* Frees all the memory associated with the provided result identifier.
*
* @return bool|null Returns TRUE on success or FALSE on failure.
* Notes: Use this method if you are concerned about how much memory is being used for queries that return large result sets.
* Anyway, all associated result memory is automatically freed at the end of the script's execution.
*/
public static function free_result($result)
{
$result->closeCursor();
}
/**
* Gets the ID of the last item inserted into the database.
*
* @return string
*/
public static function insert_id()
{
return self::getManager()->getConnection()->lastInsertId();
}
/**
* Wrapper for rowCount().
*
* @return int
*/
public static function num_rows($result)
{
if ($result === false) {
return 0;
}
return $result->rowCount();
}
/**
* Acts as the relative *_result() function of most DB drivers and fetches a
* specific line and a field.
*
* @param int $row
* @param string $field
*
* @return mixed
*/
public static function result($resource, $row, $field = '')
{
if ($resource->rowCount() > 0) {
$result = $resource->fetchAll(PDO::FETCH_BOTH);
return $result[$row][$field];
}
return false;
}
/**
* Wrapper for executeQuery().
*
* @param string $query
*
* @return Statement
*/
public static function query($query)
{
$connection = self::getManager()->getConnection();
$result = null;
try {
$result = $connection->executeQuery($query);
} catch (Exception $e) {
self::handleError($e);
}
return $result;
}
/**
* Deal with exceptions from the database extension.
*
* @param Exception $e
*/
public static function handleError($e)
{
$debug = api_get_setting('server_type') == 'test';
if ($debug) {
// We use Symfony exception handler for better error information
$handler = new ExceptionHandler();
$handler->handle($e);
exit;
} else {
$msg = $e->getMessage();
if (preg_match('/Serialization failure:/', $msg)) {
//do nothing except from logging
error_log($msg.' - Reported but otherwise ignored');
} else {
error_log($msg);
api_not_allowed(false, get_lang('GeneralError'));
exit;
}
}
}
/**
* Transform an SQL option from Chamilo to PDO syntax.
*
* @param string $option
*
* @return int
*/
public static function customOptionToDoctrineOption($option)
{
switch ($option) {
case 'ASSOC':
return PDO::FETCH_ASSOC;
break;
case 'NUM':
return PDO::FETCH_NUM;
break;
case 'BOTH':
default:
return PDO::FETCH_BOTH;
break;
}
}
/**
* Stores a query result into an array.
*
* @author Olivier Brouckaert
*
* @param Statement $result - the return value of the query
* @param string $option BOTH, ASSOC, or NUM
*
* @return array - the value returned by the query
*/
public static function store_result($result, $option = 'BOTH')
{
return $result->fetchAll(self::customOptionToDoctrineOption($option));
}
/**
* Build an insert query.
*
* @param string $table_name
* @param array $attributes
* @param bool $show_query
*
* @return false|int
*/
public static function insert($table_name, $attributes, $show_query = false)
{
if (empty($attributes) || empty($table_name)) {
return false;
}
$params = array_keys($attributes);
if (!empty($params)) {
$sql = 'INSERT INTO '.$table_name.' ('.implode(',', $params).')
VALUES (:'.implode(', :', $params).')';
$statement = self::getManager()->getConnection()->prepare($sql);
$result = $statement->execute($attributes);
if ($show_query) {
var_dump($sql);
error_log($sql);
}
if ($result) {
return (int) self::getManager()->getConnection()->lastInsertId();
}
}
return false;
}
/**
* Build an update query.
*
* @param string $tableName use Database::get_main_table
* @param array $attributes Values to updates
* Example: $params['name'] = 'Julio'; $params['lastname'] = 'Montoya';
* @param array $whereConditions where conditions i.e array('id = ?' =>'4')
* @param bool $showQuery
*
* @return bool|int
*/
public static function update(
$tableName,
$attributes,
$whereConditions = [],
$showQuery = false
) {
if (!empty($tableName) && !empty($attributes)) {
$updateSql = '';
$count = 1;
foreach ($attributes as $key => $value) {
if ($showQuery) {
echo $key.': '.$value.PHP_EOL;
}
$updateSql .= "$key = :$key ";
if ($count < count($attributes)) {
$updateSql .= ', ';
}
$count++;
}
if (!empty($updateSql)) {
//Parsing and cleaning the where conditions
$whereReturn = self::parse_where_conditions($whereConditions);
$sql = "UPDATE $tableName SET $updateSql $whereReturn ";
$statement = self::getManager()->getConnection()->prepare($sql);
$result = $statement->execute($attributes);
if ($showQuery) {
var_dump($sql);
var_dump($attributes);
var_dump($whereConditions);
}
if ($result) {
return $statement->rowCount();
}
}
}
return false;
}
/**
* Experimental useful database finder.
*
* @todo lot of stuff to do here
* @todo known issues, it doesn't work when using LIKE conditions
*
* @example array('where'=> array('course_code LIKE "?%"'))
* @example array('where'=> array('type = ? AND category = ?' => array('setting', 'Plugins'))
* @example array('where'=> array('name = "Julio" AND lastname = "montoya"'))
*
* @param mixed $columns array (or string if only one column)
* @param string $table_name
* @param array $conditions
* @param string $type_result all|first|count
* @param string $option
* @param bool $debug
*
* @return array
*/
public static function select(
$columns,
$table_name,
$conditions = [],
$type_result = 'all',
$option = 'ASSOC',
$debug = false
) {
if ($type_result === 'count') {
$conditions['LIMIT'] = null;
$conditions['limit'] = null;
}
$conditions = self::parse_conditions($conditions);
//@todo we could do a describe here to check the columns ...
if (is_array($columns)) {
$clean_columns = implode(',', $columns);
} else {
if ($columns === '*') {
$clean_columns = '*';
} else {
$clean_columns = (string) $columns;
}
}
if ($type_result === 'count') {
$clean_columns = ' count(*) count ';
}
$sql = "SELECT $clean_columns FROM $table_name $conditions";
if ($debug) {
var_dump($sql);
}
$result = self::query($sql);
if ($type_result === 'count') {
$row = self::fetch_array($result, $option);
if ($row) {
return (int) $row['count'];
}
return 0;
}
$array = [];
if ($type_result === 'all') {
while ($row = self::fetch_array($result, $option)) {
if (isset($row['iid'])) {
$array[$row['iid']] = $row;
} elseif (isset($row['id'])) {
$array[$row['id']] = $row;
} else {
$array[] = $row;
}
}
} else {
$array = self::fetch_array($result, $option);
}
return $array;
}
/**
* Parses WHERE/ORDER conditions i.e array('where'=>array('id = ?' =>'4'), 'order'=>'id DESC').
*
* @todo known issues, it doesn't work when using
* LIKE conditions example: array('where'=>array('course_code LIKE "?%"'))
*
* @param array $conditions
*
* @return string Partial SQL string to add to longer query
*/
public static function parse_conditions($conditions)
{
if (empty($conditions)) {
return '';
}
$return_value = $where_return = '';
foreach ($conditions as $type_condition => $condition_data) {
if ($condition_data == false) {
continue;
}
$type_condition = strtolower($type_condition);
switch ($type_condition) {
case 'where':
foreach ($condition_data as $condition => $value_array) {
if (is_array($value_array)) {
$clean_values = [];
foreach ($value_array as $item) {
$item = self::escape_string($item);
$clean_values[] = $item;
}
} else {
$value_array = self::escape_string($value_array);
$clean_values = [$value_array];
}
if (!empty($condition) && $clean_values != '') {
$condition = str_replace('%', "'@percentage@'", $condition); //replace "%"
$condition = str_replace("'?'", "%s", $condition);
$condition = str_replace("?", "%s", $condition);
$condition = str_replace("@%s@", "@-@", $condition);
$condition = str_replace("%s", "'%s'", $condition);
$condition = str_replace("@-@", "@%s@", $condition);
// Treat conditions as string
$condition = vsprintf($condition, $clean_values);
$condition = str_replace('@percentage@', '%', $condition); //replace "%"
$where_return .= $condition;
}
}
if (!empty($where_return)) {
$return_value = " WHERE $where_return";
}
break;
case 'order':
$order_array = $condition_data;
if (!empty($order_array)) {
// 'order' => 'id desc, name desc'
$order_array = self::escape_string($order_array);
$new_order_array = explode(',', $order_array);
$temp_value = [];
foreach ($new_order_array as $element) {
$element = explode(' ', $element);
$element = array_filter($element);
$element = array_values($element);
if (!empty($element[1])) {
$element[1] = strtolower($element[1]);
$order = 'DESC';
if (in_array($element[1], ['desc', 'asc'])) {
$order = $element[1];
}
$temp_value[] = ' `'.$element[0].'` '.$order.' ';
} else {
//by default DESC
$temp_value[] = ' `'.$element[0].'` DESC ';
}
}
if (!empty($temp_value)) {
$return_value .= ' ORDER BY '.implode(', ', $temp_value);
}
}
break;
case 'limit':
$limit_array = explode(',', $condition_data);
if (!empty($limit_array)) {
if (count($limit_array) > 1) {
$return_value .= ' LIMIT '.intval($limit_array[0]).' , '.intval($limit_array[1]);
} else {
$return_value .= ' LIMIT '.intval($limit_array[0]);
}
}
break;
}
}
return $return_value;
}
/**
* @param array $conditions
*
* @return string
*/
public static function parse_where_conditions($conditions)
{
return self::parse_conditions(['where' => $conditions]);
}
/**
* Build a delete query.
*
* @param string $table_name
* @param array $where_conditions
* @param bool $show_query
*
* @return int
*/
public static function delete($table_name, $where_conditions, $show_query = false)
{
$where_return = self::parse_where_conditions($where_conditions);
$sql = "DELETE FROM $table_name $where_return ";
if ($show_query) {
echo $sql;
echo '<br />';
}
$result = self::query($sql);
$affected_rows = self::affected_rows($result);
//@todo should return affected_rows for
return $affected_rows;
}
/**
* Get Doctrine configuration.
*
* @param string $path
*
* @return \Doctrine\ORM\Configuration
*/
public static function getDoctrineConfig($path)
{
$isDevMode = true; // Forces doctrine to use ArrayCache instead of apc/xcache/memcache/redis
$isSimpleMode = false; // related to annotations @Entity
$cache = null;
$path = !empty($path) ? $path : api_get_path(SYS_PATH);
$paths = [
//$path.'src/Chamilo/ClassificationBundle/Entity',
//$path.'src/Chamilo/MediaBundle/Entity',
//$path.'src/Chamilo/PageBundle/Entity',
$path.'src/Chamilo/CoreBundle/Entity',
$path.'src/Chamilo/UserBundle/Entity',
$path.'src/Chamilo/CourseBundle/Entity',
$path.'src/Chamilo/TicketBundle/Entity',
$path.'src/Chamilo/SkillBundle/Entity',
$path.'src/Chamilo/PluginBundle/Entity',
//$path.'vendor/sonata-project/user-bundle/Entity',
//$path.'vendor/sonata-project/user-bundle/Model',
//$path.'vendor/friendsofsymfony/user-bundle/FOS/UserBundle/Entity',
];
$proxyDir = $path.'app/cache/';
$config = Setup::createAnnotationMetadataConfiguration(
$paths,
$isDevMode,
$proxyDir,
$cache,
$isSimpleMode
);
$config->setAutoGenerateProxyClasses(AbstractProxyFactory::AUTOGENERATE_FILE_NOT_EXISTS);
$config->setEntityNamespaces(
[
'ChamiloUserBundle' => 'Chamilo\UserBundle\Entity',
'ChamiloCoreBundle' => 'Chamilo\CoreBundle\Entity',
'ChamiloCourseBundle' => 'Chamilo\CourseBundle\Entity',
'ChamiloSkillBundle' => 'Chamilo\SkillBundle\Entity',
'ChamiloTicketBundle' => 'Chamilo\TicketBundle\Entity',
'ChamiloPluginBundle' => 'Chamilo\PluginBundle\Entity',
]
);
return $config;
}
/**
* Check if a given table exists.
*
* @param string $table
*
* @return bool
*/
public static function tableExists($table)
{
return self::getManager()->getConnection()->getSchemaManager()->tablesExist($table);
}
/**
* List the columns of a given table.
*
* @param string $table
*
* @return \Doctrine\DBAL\Schema\Column[]
*/
public static function listTableColumns($table)
{
return self::getManager()->getConnection()->getSchemaManager()->listTableColumns($table);
}
public static function escapeField($field)
{
return self::escape_string(preg_replace("/[^a-zA-Z0-9_.]/", '', $field));
}
public static function clearDatabaseName(string $dbName): string
{
return preg_replace('/[^a-zA-Z0-9_\-]/', '', $dbName);
}
}

View File

@@ -0,0 +1,4 @@
<?php
// This file is needed to avoid the double Database class definition in 1.9.x
// Don't remove.

File diff suppressed because it is too large Load Diff

3122
main/inc/lib/display.lib.php Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Component\Editor\Connector;
use Chamilo\CoreBundle\Component\Editor\Finder;
require_once __DIR__.'/../../global.inc.php';
error_reporting(-1);
/** @var Connector $connector */
$connector = new Connector();
// Check driver list in configuration
$driverList = api_get_configuration_value('editor_driver_list');
if (empty($driverList)) {
$driverList = [
'PersonalDriver',
'CourseDriver',
//'CourseUserDriver',
//'HomeDriver'
];
$block = api_get_configuration_value('block_editor_file_manager_for_students');
$newDriverList = [];
if (($block) && !api_is_allowed_to_edit()) {
foreach ($driverList as $driver) {
if ($driver === 'CourseDriver') {
continue;
}
$newDriverList[] = $driver;
}
$driverList = $newDriverList;
}
}
$connector->setDriverList($driverList);
$operations = $connector->getOperations();
// Run elFinder
$finder = new Finder($operations);
$elFinderConnector = new \elFinderConnector($finder);
$elFinderConnector->run();

View File

@@ -0,0 +1,25 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/../../global.inc.php';
Chat::setDisableChat();
$template = new Template();
$template->assign('course_condition', api_get_cidreq());
$language = 'en';
$platformLanguage = api_get_interface_language();
$iso = api_get_language_isocode($platformLanguage);
$filePart = "vendor/studio-42/elfinder/js/i18n/elfinder.$iso.js";
$file = api_get_path(SYS_PATH).$filePart;
$includeFile = '';
if (file_exists($file)) {
$includeFile = '<script type="text/javascript" src="'.api_get_path(WEB_PATH).$filePart.'"></script>';
$language = $iso;
}
$questionId = isset($_REQUEST['question_id']) ? (int) $_REQUEST['question_id'] : 0;
$template->assign('question_id', $questionId);
$template->assign('elfinder_lang', $language);
$template->assign('elfinder_translation_file', $includeFile);
$template->display('default/javascript/editor/ckeditor/elfinder.tpl');

View File

@@ -0,0 +1,16 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Component\Editor\CkEditor\CkEditor;
require_once __DIR__.'/../../global.inc.php';
$template = new Template();
$editor = new CkEditor();
$templates = $editor->simpleFormatTemplates();
$template->assign('templates', $templates);
header('Content-type: application/x-javascript');
$template->display('default/javascript/editor/ckeditor/templates.tpl');

View File

@@ -0,0 +1,144 @@
<?php
/* For licensing terms, see /license.txt */
/**
* @deprecated
* Class EventEmailTemplate
*/
class EventEmailTemplate extends Model
{
public $table;
public $columns = [
'id',
'message',
'subject',
'event_type_name',
'activated',
];
/**
* Constructor.
*/
public function __construct()
{
$this->table = Database::get_main_table(TABLE_EVENT_EMAIL_TEMPLATE);
}
/**
* @param array $where_conditions
*
* @return array
*/
public function get_all($where_conditions = [])
{
return Database::select(
'*',
$this->table,
['where' => $where_conditions, 'order' => 'name ASC']
);
}
/**
* Displays the title + grid.
*/
public function display()
{
// action links
$content = Display::actions(
[
[
'url' => 'event_type.php',
'content' => Display::return_icon(
'new_document.png',
get_lang('Add'),
[],
ICON_SIZE_MEDIUM
),
],
]
);
$content .= Display::grid_html('event_email_template');
return $content;
}
/**
* @return array
*/
public function get_status_list()
{
return [
EVENT_EMAIL_TEMPLATE_ACTIVE => get_lang('Enabled'),
EVENT_EMAIL_TEMPLATE_INACTIVE => get_lang('Disabled'),
];
}
/**
* Returns a Form validator Obj.
*
* @param string $url
* @param string $action add, edit
*
* @return FormValidator
*/
public function return_form($url, $action)
{
$form = new FormValidator('career', 'post', $url);
// Setting the form elements
$header = get_lang('Add');
if ($action == 'edit') {
$header = get_lang('Modify');
}
$form->addElement('header', $header);
$id = isset($_GET['id']) ? intval($_GET['id']) : '';
$form->addElement('hidden', 'id', $id);
$form->addElement('text', 'name', get_lang('Name'), ['size' => '70']);
$form->addHtmlEditor(
'description',
get_lang('Description'),
false,
false,
[
'ToolbarSet' => 'careers',
'Width' => '100%',
'Height' => '250',
]
);
$status_list = $this->get_status_list();
$form->addElement('select', 'status', get_lang('Status'), $status_list);
if ($action == 'edit') {
$form->addElement('text', 'created_at', get_lang('CreatedAt'));
$form->freeze('created_at');
}
if ($action == 'edit') {
$form->addButtonSave(get_lang('Modify'), 'submit');
} else {
$form->addButtonCreate(get_lang('Add'), 'submit');
}
// Setting the defaults
$defaults = $this->get($id);
if (!empty($defaults['created_at'])) {
$defaults['created_at'] = api_convert_and_format_date($defaults['created_at']);
}
if (!empty($defaults['updated_at'])) {
$defaults['updated_at'] = api_convert_and_format_date($defaults['updated_at']);
}
$form->setDefaults($defaults);
// Setting the rules
$form->addRule('name', get_lang('ThisFieldIsRequired'), 'required');
return $form;
}
public function get_count()
{
$row = Database::select('count(*) as count', $this->table, [], 'first');
return $row['count'];
}
}

2888
main/inc/lib/events.lib.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
<?php
/**
* Class EventsDispatcher
* Entry point for every event in the application.
*
* @deprecated to be removed in 2.x
* Fires the functions linked to the events according to the event's conf.
* Every function got its own filter, it's fired inside the functiones fired
* by this class. The filter config is next to the event config, in conf/events.conf.php
*/
class EventsDispatcher
{
/**
* @param string $event_name
* @param array $event_data
*
* @return bool
*/
public static function events($event_name, $event_data = [])
{
global $event_config;
// get the config for the event passed in parameter ($event_name)
// and execute every actions with the values
foreach ($event_config[$event_name]["actions"] as $func) {
$execute = true;
// if the function doesn't exist, we log
if (!function_exists($func)) {
error_log("EventsDispatcher warning : ".$func." does not exist.");
$execute = false;
}
// check if the event's got a filter
if (function_exists($event_name."_".$func."_filter_func")) {
$filter = $event_name."_".$func."_filter_func";
// if it does, we execute the filter (which changes the data
// in-place and returns true on success or false on error)
$execute = $filter($event_data);
} else {
// if there's no filter
error_log("EventsDispatcher warning : ".$event_name."_".$func."_filter_func does not exist.");
}
if (!$execute) {
// if the filter says we cannot send the mail, we get out of here
return false;
}
// finally, if the filter says yes (or the filter doesn't exist),
// we execute the in-between function that will call the needed
// function
$func($event_name, $event_data);
}
}
}

View File

@@ -0,0 +1,285 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class EventsMail.
*
* @deprecated to be removed in 2.x
* manages the e-mail sending action when a event requires it
*/
class EventsMail
{
/**
* Sends email according to an event.
*
* @param string $event_name the name of the event that was triggered
* @param array $event_data what to put in the mail
*
* Possible key :
* - $event_data["about_user"] (= $user_id)
* - $event_data["prior_lang"]
*
* Warning :
* - $event_data["send_to"] MUST BE an array
*/
public static function send_mail($event_name, $event_data)
{
/**
* Global explanation :
* 1. we get information about the user that fired the event (in $event_data["about_user"])
* 2. we send mail to people that are in the $event_data["send_to"]
* 2b. if a language was specified, we use that one to send the mail,
* else we get the user's language, if there isn't any, we get the english one
* 3. we do the same with the people associated to the event through the admin panel.
*/
global $event_config;
// common variable for every mail sent
$sender_name = api_get_person_name(
api_get_setting('administratorName'),
api_get_setting('administratorSurname'),
null,
PERSON_NAME_EMAIL_ADDRESS
);
$email_admin = api_get_setting('emailAdministrator');
// basic keys
$event_data["sitename"] = api_get_setting('siteName');
$event_data["administrator_name"] = api_get_setting('administratorName');
$event_data["administrator_surname"] = api_get_setting('administratorSurname');
$event_data["administrator_phone"] = api_get_setting('administratorTelephone');
$event_data["administrator_email"] = api_get_setting('emailAdministrator');
$event_data["portal"] = api_get_path(WEB_PATH);
// Fill the array's cells with info regarding the user that fired the event
// (for the keys in the template)
if (isset($event_data["about_user"])) {
$about_user = api_get_user_info($event_data["about_user"]);
$event_data["firstname"] = $about_user["firstname"];
$event_data["lastname"] = $about_user["lastname"];
$event_data["username"] = $about_user["username"];
$event_data["usermail"] = $about_user["mail"];
$event_data["language"] = $about_user["language"];
$event_data["user_id"] = $about_user["user_id"];
}
// First, we send the mail to people we put in the $event_data["send_to"]
if ($event_data["send_to"] != null) {
// the users we precised need to receive the mail
foreach ($event_data["send_to"] as $id) {
// for every member put in the array
// get user's info (to know where to send)
$user_info = api_get_user_info($id);
// get the language the email will be in
if ($event_data["prior_lang"] != null) {
// if $lang is not null, we use that lang
$language = $event_data["prior_lang"];
} else {
// else we use the user's language
$sql = 'SELECT language
FROM '.Database::get_main_table(TABLE_MAIN_USER).' u
WHERE u.user_id = "'.$id.'"
';
$language = Database::store_result(
Database::query($sql),
'ASSOC'
);
$language = $language[0]["language"];
}
// we get the message in the correct language (or in english if doesn't exist)
$result = self::getMessage($event_name, $language);
$message = "";
$subject = "";
self::getCorrectMessage($message, $subject, $language, $result);
// replace the keycodes used in the message
self::formatMessage($message, $subject, $event_config, $event_name, $event_data);
// sending email
$recipient_name = api_get_person_name($user_info['firstname'], $user_info['lastname']);
// checks if there's a file we need to join to the mail
if (isset($values["certificate_pdf_file"])) {
$message = str_replace("\n", "<br />", $message);
api_mail_html(
$recipient_name,
$user_info["mail"],
$subject,
$message,
$sender_name,
$email_admin,
null,
[$values['certificate_pdf_file']]
);
} else {
api_mail_html(
$recipient_name,
$user_info["mail"],
$subject,
$message,
$sender_name,
$email_admin
);
}
// If the mail only need to be send once (we know that thanks to the events.conf), we log it in the table
if ($event_config[$event_name]["sending_mail_once"]) {
$sql = 'INSERT INTO '.Database::get_main_table(TABLE_EVENT_SENT).' (user_from, user_to, event_type_name)
VALUES ('.$event_data["user_id"].', '.$id.' ,"'.Database::escape_string($event_name).'")
';
Database::query($sql);
}
}
}
// Second, we send to people linked to the event
// So, we get everyone
$sql = 'SELECT u.user_id, u.language, u.email, u.firstname, u.lastname
FROM '.Database::get_main_table(TABLE_EVENT_TYPE_REL_USER).' ue
INNER JOIN '.Database::get_main_table(TABLE_MAIN_USER).' u ON u.user_id = ue.user_id
WHERE event_type_name = "'.$event_name.'"';
$result = Database::store_result(Database::query($sql), 'ASSOC');
// for each of the linked users
foreach ($result as $key => $value) {
// we get the language
if ($event_data["prior_lang"] != null) {
// if $lang is not null, we use that lang
$language = $event_data["prior_lang"];
} else {
// else we get the user's lang
$sql = 'SELECT language FROM '.Database::get_main_table(TABLE_MAIN_USER).'
where user_id = '.$value["user_id"].' ';
$result = Database::store_result(Database::query($sql), 'ASSOC');
$language = $result[0]["language"];
}
// we get the message in the correct language (or in english if doesn't exist)
$result = self::getMessage($event_name, $language);
$message = '';
$subject = '';
self::getCorrectMessage($message, $subject, $language, $result);
// replace the keycodes used in the message
self::formatMessage($message, $subject, $event_config, $event_name, $event_data);
// we send the mail
$recipient_name = api_get_person_name($value['firstname'], $value['lastname']);
api_mail_html(
$recipient_name,
$value["email"],
$subject,
$message,
$sender_name,
$email_admin
);
// If the mail only need to be send once (we know that thanks to the events.conf, we log it in the table
if ($event_config[$event_name]["sending_mail_once"]) {
$sql = 'INSERT INTO '.Database::get_main_table(TABLE_EVENT_SENT).'
(user_from, user_to, event_type_name)
VALUES ('.$event_data["user_id"].', '.$value["user_id"].' , "'.Database::escape_string($event_name).'");
';
Database::query($sql);
}
}
}
/**
* Checks if a message in a language exists, if the event is activated
* and if "manage event" is checked in admin panel.
* If yes to three, we can use this class, else we still use api_mail.
*
* @param string $event_name
*
* @return bool
*/
public static function check_if_using_class($event_name)
{
if (api_get_setting('activate_email_template') === 'false') {
return false;
}
$current_language = api_get_interface_language();
$sql = 'SELECT COUNT(*) as total
FROM '.Database::get_main_table(TABLE_EVENT_EMAIL_TEMPLATE).' em
INNER JOIN '.Database::get_main_table(TABLE_MAIN_LANGUAGE).' l
ON em.language_id = l.id
WHERE
em.event_type_name = "'.$event_name.'" and
l.dokeos_folder = "'.$current_language.'" and
em.activated = 1';
$exists = Database::store_result(Database::query($sql), 'ASSOC');
if ($exists[0]["total"]) {
return true;
} else {
return false;
}
}
/**
* Get the record containing the good message and subject.
*
* @param string $event_name
* @param string $language
*
* @return array
*/
private static function getMessage($event_name, $language)
{
$sql = 'SELECT message, subject, l.dokeos_folder
FROM '.Database::get_main_table(TABLE_EVENT_EMAIL_TEMPLATE).' em
INNER JOIN '.Database::get_main_table(TABLE_MAIN_LANGUAGE).' l
ON em.language_id = l.id
WHERE
em.event_type_name = "'.$event_name.'" AND
(l.dokeos_folder = "'.$language.'" OR l.dokeos_folder = "english") AND
em.message <> ""
';
return Database::store_result(Database::query($sql), 'ASSOC');
}
/**
* Get the correct message, meaning in the specified language or in english if previous one doesn't exist.
*
* @param string $message
* @param string $subject
* @param string $language
* @param array $result
*/
private static function getCorrectMessage(&$message, &$subject, $language, $result)
{
foreach ($result as $msg) {
if ($msg["dokeos_folder"] == $language) {
$message = $msg["message"];
$subject = $msg["subject"];
break;
} else {
if ($msg["dokeos_folder"] == "english") {
$message = $msg["message"];
$subject = $msg["subject"];
}
}
}
}
/**
* Replaces the ((key)) by the good values.
*
* @param string $message
* @param string $subject
* @param array $event_config
* @param string $event_name
*/
private static function formatMessage(&$message, &$subject, $event_config, $event_name, &$event_data)
{
foreach ($event_config[$event_name]["available_keyvars"] as $key => $word) {
$message = str_replace('(('.$key.'))', $event_data[$word], $message);
$subject = str_replace('(('.$key.'))', $event_data[$word], $subject);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,420 @@
<?php
/* See license terms in /license.txt */
use Chamilo\CoreBundle\Component\Editor\Connector;
use Chamilo\CoreBundle\Component\Filesystem\Data;
use Ddeboer\DataImport\Writer\CsvWriter;
use Ddeboer\DataImport\Writer\ExcelWriter;
use MediaAlchemyst\Alchemyst;
use MediaAlchemyst\DriversContainer;
use Neutron\TemporaryFilesystem\Manager;
use Neutron\TemporaryFilesystem\TemporaryFilesystem;
use Symfony\Component\Filesystem\Filesystem;
/**
* This is the export library for Chamilo.
* Include/require it in your code to use its functionality.
* Several functions below are adaptations from functions distributed by www.nexen.net.
*/
class Export
{
/**
* Constructor.
*/
public function __construct()
{
}
/**
* Export tabular data to CSV-file.
*
* @param array $data
* @param string $filename
* @param bool $writeOnly Whether to only write on disk or also send for download
* @param string $enclosure
*
* @return mixed csv raw data | false if no data to export | string file path if success in $writeOnly mode
*/
public static function arrayToCsv($data, $filename = 'export', $writeOnly = false, $enclosure = '"')
{
if (empty($data)) {
return false;
}
$enclosure = !empty($enclosure) ? $enclosure : '"';
$filePath = api_get_path(SYS_ARCHIVE_PATH).uniqid('').'.csv';
$stream = fopen($filePath, 'w');
$writer = new CsvWriter(';', $enclosure, $stream, true);
$writer->prepare();
foreach ($data as $item) {
if (empty($item)) {
$writer->writeItem([]);
continue;
}
$item = array_map('trim', $item);
$writer->writeItem($item);
}
$writer->finish();
if (!$writeOnly) {
DocumentManager::file_send_for_download($filePath, true, $filename.'.csv');
exit;
}
return $filePath;
}
/**
* Export tabular data to XLS-file.
*
* @param array $data
* @param string $filename
*/
public static function arrayToXls($data, $filename = 'export', $encoding = 'utf-8')
{
$filePath = api_get_path(SYS_ARCHIVE_PATH).uniqid('').'.xlsx';
$file = new \SplFileObject($filePath, 'w');
$writer = new ExcelWriter($file);
@$writer->prepare();
foreach ($data as $row) {
@$writer->writeItem($row);
}
@$writer->finish();
DocumentManager::file_send_for_download($filePath, true, $filename.'.xlsx');
exit;
}
/**
* Export tabular data to XLS-file included comments.
*
* @param array $data The comment by cell should be added with the prefix [comment] to be added ($txtCellValue.'[comment]'.$txtComment)
*/
public static function arrayToXlsAndComments(
array $data,
string $filename = 'export'
) {
$filePath = api_get_path(SYS_ARCHIVE_PATH).uniqid('').'.xlsx';
$file = new \SplFileObject($filePath, 'w');
$excel = @new PHPExcel();
$type = 'Excel2007';
$sheet = null;
$row = 1;
$prependHeaderRow = false;
if (null !== $sheet && !$excel->sheetNameExists($sheet)) {
$excel->removeSheetByIndex(0);
}
if (null !== $sheet) {
if (!$excel->sheetNameExists($sheet)) {
$excel->createSheet()->setTitle($sheet);
}
$excel->setActiveSheetIndexByName($sheet);
}
foreach ($data as $item) {
$count = count($item);
if ($prependHeaderRow && 1 == $row) {
$headers = array_keys($item);
for ($i = 0; $i < $count; $i++) {
@$excel->getActiveSheet()->setCellValueByColumnAndRow($i, $row, $headers[$i]);
}
$row++;
}
$values = array_values($item);
for ($i = 0; $i < $count; $i++) {
$txtComment = '';
$txtValue = $values[$i];
if (false !== strpos($values[$i], '[comment]')) {
list($txtValue, $txtComment) = explode('[comment]', $values[$i]);
}
@$excel->getActiveSheet()->setCellValueByColumnAndRow($i, $row, $txtValue);
if (!empty($txtComment)) {
$columnLetter = PHPExcel_Cell::stringFromColumnIndex($i);
$coordinate = $columnLetter.$row;
@$excel->getActiveSheet()->getComment($coordinate)->getText()->createTextRun($txtComment);
}
}
$row++;
}
$writer = \PHPExcel_IOFactory::createWriter($excel, $type);
$writer->save($file->getPathname());
DocumentManager::file_send_for_download($filePath, true, $filename.'.xlsx');
exit;
}
/**
* Export tabular data to XLS-file (as html table).
*
* @param array $data
* @param string $filename
*/
public static function export_table_xls_html($data, $filename = 'export', $encoding = 'utf-8')
{
$file = api_get_path(SYS_ARCHIVE_PATH).uniqid('').'.xls';
$handle = fopen($file, 'a+');
$systemEncoding = api_get_system_encoding();
fwrite($handle, '<!DOCTYPE html><html><meta http-equiv="Content-Type" content="text/html" charset="'.$encoding.'" /><body><table>');
foreach ($data as $id => $row) {
foreach ($row as $id2 => $row2) {
$data[$id][$id2] = api_htmlentities($row2);
}
}
foreach ($data as $row) {
$string = implode("</td><td>", $row);
$string = '<tr><td>'.$string.'</td></tr>';
if ($encoding != 'utf-8') {
$string = api_convert_encoding($string, $encoding, $systemEncoding);
}
fwrite($handle, $string."\n");
}
fwrite($handle, '</table></body></html>');
fclose($handle);
DocumentManager::file_send_for_download($file, true, $filename.'.xls');
exit;
}
/**
* Export tabular data to XML-file.
*
* @param array Simple array of data to put in XML
* @param string Name of file to be given to the user
* @param string Name of common tag to place each line in
* @param string Name of the root element. A root element should always be given.
* @param string Encoding in which the data is provided
*/
public static function arrayToXml(
$data,
$filename = 'export',
$item_tagname = 'item',
$wrapper_tagname = null,
$encoding = null
) {
if (empty($encoding)) {
$encoding = api_get_system_encoding();
}
$file = api_get_path(SYS_ARCHIVE_PATH).'/'.uniqid('').'.xml';
$handle = fopen($file, 'a+');
fwrite($handle, '<?xml version="1.0" encoding="'.$encoding.'"?>'."\n");
if (!is_null($wrapper_tagname)) {
fwrite($handle, "\t".'<'.$wrapper_tagname.'>'."\n");
}
foreach ($data as $row) {
fwrite($handle, '<'.$item_tagname.'>'."\n");
foreach ($row as $key => $value) {
fwrite($handle, "\t\t".'<'.$key.'>'.$value.'</'.$key.'>'."\n");
}
fwrite($handle, "\t".'</'.$item_tagname.'>'."\n");
}
if (!is_null($wrapper_tagname)) {
fwrite($handle, '</'.$wrapper_tagname.'>'."\n");
}
fclose($handle);
DocumentManager::file_send_for_download($file, true, $filename.'.xml');
exit;
}
/**
* Export hierarchical tabular data to XML-file.
*
* @param array Hierarchical array of data to put in XML, each element presenting a 'name' and a 'value' property
* @param string Name of file to be given to the user
* @param string Name of common tag to place each line in
* @param string Name of the root element. A root element should always be given.
* @param string Encoding in which the data is provided
*/
public static function export_complex_table_xml(
$data,
$filename = 'export',
$wrapper_tagname = null,
$encoding = 'ISO-8859-1'
) {
$file = api_get_path(SYS_ARCHIVE_PATH).'/'.uniqid('').'.xml';
$handle = fopen($file, 'a+');
fwrite($handle, '<?xml version="1.0" encoding="'.$encoding.'"?>'."\n");
if (!is_null($wrapper_tagname)) {
fwrite($handle, '<'.$wrapper_tagname.'>');
}
$s = self::_export_complex_table_xml_helper($data);
fwrite($handle, $s);
if (!is_null($wrapper_tagname)) {
fwrite($handle, '</'.$wrapper_tagname.'>'."\n");
}
fclose($handle);
DocumentManager::file_send_for_download($file, true, $filename.'.xml');
return false;
}
/**
* Helper for the hierarchical XML exporter.
*
* @param array Hierarhical array composed of elements of type ('name'=>'xyz','value'=>'...')
* @param int Level of recursivity. Allows the XML to be finely presented
*
* @return string The XML string to be inserted into the root element
*/
public static function _export_complex_table_xml_helper($data, $level = 1)
{
if (count($data) < 1) {
return '';
}
$string = '';
foreach ($data as $row) {
$string .= "\n".str_repeat("\t", $level).'<'.$row['name'].'>';
if (is_array($row['value'])) {
$string .= self::_export_complex_table_xml_helper($row['value'], $level + 1)."\n";
$string .= str_repeat("\t", $level).'</'.$row['name'].'>';
} else {
$string .= $row['value'];
$string .= '</'.$row['name'].'>';
}
}
return $string;
}
/**
* @param array $data table to be read with the HTML_table class
*/
public static function export_table_pdf($data, $params = [])
{
$table_html = self::convert_array_to_html($data, $params);
$params['format'] = isset($params['format']) ? $params['format'] : 'A4';
$params['orientation'] = isset($params['orientation']) ? $params['orientation'] : 'P';
$pdf = new PDF($params['format'], $params['orientation'], $params);
$pdf->html_to_pdf_with_template($table_html);
}
/**
* @param string $html
* @param array $params
*/
public static function export_html_to_pdf($html, $params = [])
{
$params['format'] = isset($params['format']) ? $params['format'] : 'A4';
$params['orientation'] = isset($params['orientation']) ? $params['orientation'] : 'P';
$pdf = new PDF($params['format'], $params['orientation'], $params);
$pdf->html_to_pdf_with_template($html);
}
/**
* @param array $data
* @param array $params
*
* @return string
*/
public static function convert_array_to_html($data, $params = [])
{
$headers = $data[0];
unset($data[0]);
$header_attributes = isset($params['header_attributes']) ? $params['header_attributes'] : [];
$table = new HTML_Table(['class' => 'table table-hover table-striped data_table', 'repeat_header' => '1']);
$row = 0;
$column = 0;
foreach ($headers as $header) {
$table->setHeaderContents($row, $column, $header);
$attributes = [];
if (isset($header_attributes) && isset($header_attributes[$column])) {
$attributes = $header_attributes[$column];
}
if (!empty($attributes)) {
$table->updateCellAttributes($row, $column, $attributes);
}
$column++;
}
$row++;
foreach ($data as &$printable_data_row) {
$column = 0;
foreach ($printable_data_row as &$printable_data_cell) {
$table->setCellContents($row, $column, $printable_data_cell);
//$table->updateCellAttributes($row, $column, $atributes);
$column++;
}
$table->updateRowAttributes($row, $row % 2 ? 'class="row_even"' : 'class="row_odd"', true);
$row++;
}
$table_tp_html = $table->toHtml();
return $table_tp_html;
}
/**
* Export HTML content in a ODF document.
*
* @param string $html
* @param string $name
* @param string $format
*
* @return bool
*/
public static function htmlToOdt($html, $name, $format = 'odt')
{
$unoconv = api_get_configuration_value('unoconv.binaries');
if (empty($unoconv)) {
return false;
}
if (!empty($html)) {
$fs = new Filesystem();
$paths = [
'root_sys' => api_get_path(SYS_PATH),
'path.temp' => api_get_path(SYS_ARCHIVE_PATH),
];
$connector = new Connector();
$drivers = new DriversContainer();
$drivers['configuration'] = [
'unoconv.binaries' => $unoconv,
'unoconv.timeout' => 60,
];
$tempFilesystem = TemporaryFilesystem::create();
$manager = new Manager($tempFilesystem, $fs);
$alchemyst = new Alchemyst($drivers, $manager);
$dataFileSystem = new Data($paths, $fs, $connector, $alchemyst);
$content = $dataFileSystem->convertRelativeToAbsoluteUrl($html);
$filePath = $dataFileSystem->putContentInTempFile(
$content,
api_replace_dangerous_char($name),
'html'
);
$try = true;
while ($try) {
try {
$convertedFile = $dataFileSystem->transcode(
$filePath,
$format
);
$try = false;
DocumentManager::file_send_for_download(
$convertedFile,
false,
$name.'.'.$format
);
} catch (Exception $e) {
// error_log($e->getMessage());
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,976 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Entity\ExtraFieldOptions;
/**
* Class ExtraFieldOption
* Handles the extra fields for various objects (users, sessions, courses).
*/
class ExtraFieldOption extends Model
{
public $columns = [
'id',
'field_id',
'option_value',
'display_text',
'option_order',
'priority',
'priority_message',
'tms',
];
public $extraField;
public $fieldId;
/**
* Gets the table for the type of object for which we are using an extra field.
*
* @param string $type Type of object (course, user or session)
*/
public function __construct($type)
{
parent::__construct();
$this->type = $type;
$extraField = new ExtraField($this->type);
$this->extraField = $extraField;
$this->table = Database::get_main_table(TABLE_EXTRA_FIELD_OPTIONS);
$this->tableExtraField = Database::get_main_table(TABLE_EXTRA_FIELD);
}
/**
* @return ExtraField
*/
public function getExtraField()
{
return $this->extraField;
}
/**
* Gets the number of options available for this field.
*
* @param int $fieldId
*
* @return int Number of options
* @assert ('') === false
* @assert (-1) == 0
* @assert (0) == 0
*/
public function get_count_by_field_id($fieldId)
{
if (empty($fieldId)) {
return false;
}
$extraFieldType = $this->getExtraField()->getExtraFieldType();
$fieldId = (int) $fieldId;
$sql = "SELECT count(*) as count
FROM $this->table o
INNER JOIN $this->tableExtraField e
ON o.field_id = e.id
WHERE
o.field_id = $fieldId AND
e.extra_field_type = $extraFieldType ";
$result = Database::query($sql);
$result = Database::fetch_array($result);
return $result['count'];
}
/**
* Returns a list of options for a specific field, separated by ";".
*
* @param int $field_id
* @param bool $add_id_in_array Indicates whether we want the results to be given with their id
* @param string $ordered_by Order by clause (without the "order by") to be added to the SQL query
*
* @return string List of options separated by ;
* @assert (-1, false, null) == ''
*/
public function getFieldOptionsToString($field_id, $add_id_in_array = false, $ordered_by = null)
{
$options = self::get_field_options_by_field($field_id, $add_id_in_array, $ordered_by);
$new_options = [];
if (!empty($options)) {
foreach ($options as $option) {
$new_options[] = $option['option_value'].':'.$option['display_text'];
}
$string = implode(';', $new_options);
return $string;
}
return '';
}
/**
* Delete all the options of a specific field.
*
* @param int $field_id
*
* @assert (-1) === false
*/
public function delete_all_options_by_field_id($field_id)
{
$field_id = (int) $field_id;
$sql = "DELETE FROM {$this->table} WHERE field_id = $field_id";
return Database::query($sql);
}
/**
* Save option.
*
* Example:
* <code>
* <?php
* $fieldOption = new ExtraFieldOption('user');
* $fieldOption->saveOptions([
* 'field_id'=> 1,
* 'option_value'=> 1,
* 'display_text'=> 'extra value option 1',
* 'option_order'=>0
* ]);
* echo "<pre>".var_export($fieldOption,true)."</pre>";
* ?>
* </code>
*
* @param array $params
* @param bool $showQuery
*
* @return int|bool
*/
public function saveOptions($params, $showQuery = false)
{
$optionInfo = $this->get_field_option_by_field_and_option(
$params['field_id'],
$params['option_value']
);
if (false == $optionInfo) {
$optionValue = api_replace_dangerous_char($params['option_value']);
$order = $this->get_max_order($params['field_id']);
$newParams = [
'field_id' => $params['field_id'],
'value' => trim($optionValue),
'display_text' => trim($params['display_text']),
'option_order' => $order,
];
return parent::save($newParams, $showQuery);
}
return false;
}
/**
* Saves an option into the corresponding *_field_options table.
*
* Example:
* <code>
* <?php
* $fieldOption = new ExtraFieldOption('user');
* $fieldOption->save([
* 'field_id'=> 1,
* 'option_value'=> 1,
* 'display_text'=> 'extra value option 1',
* 'option_order=>0'
* ]);
* echo "<pre>".var_export($fieldOption,true)."</pre>";
* ?>
* </code>
*
* @param array $params Parameters to be considered for the insertion
* @param bool $showQuery Whether to show the query (sent to the parent save() method)
*
* @return bool True on success, false on error
* @assert (array('field_id'=>0), false) === false
* @assert (array('field_id'=>1), false) === true
*/
public function save($params, $showQuery = false)
{
$field_id = (int) $params['field_id'];
if (empty($field_id)) {
return false;
}
$parseOptions = in_array(
$params['field_type'],
[
ExtraField::FIELD_TYPE_RADIO,
ExtraField::FIELD_TYPE_SELECT,
ExtraField::FIELD_TYPE_SELECT_MULTIPLE,
ExtraField::FIELD_TYPE_DOUBLE_SELECT,
ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD,
ExtraField::FIELD_TYPE_TRIPLE_SELECT,
]
);
if (empty($params['field_options']) || !$parseOptions) {
return true;
}
switch ($params['field_type']) {
case ExtraField::FIELD_TYPE_DOUBLE_SELECT:
//$params['field_options'] = France:Paris;Bretagne;Marseilles;Lyon|Belgique:Bruxelles;Namur;Liège;Bruges|Peru:Lima;Piura;
case ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD:
//$params['field_options'] = Option 1|Option 2|Option 3
$options_parsed = ExtraField::extra_field_double_select_convert_string_to_array(
$params['field_options']
);
if (empty($options_parsed)) {
break;
}
foreach ($options_parsed as $key => $option) {
$new_params = [
'field_id' => $field_id,
'option_value' => 0,
'display_text' => $option['label'],
'option_order' => 0,
];
// Looking if option already exists:
$option_info = self::get_field_option_by_field_id_and_option_display_text(
$field_id,
$option['label']
);
if (empty($option_info)) {
$sub_id = parent::save($new_params, $showQuery);
} else {
$sub_id = $option_info['id'];
$new_params['id'] = $sub_id;
parent::update($new_params, $showQuery);
}
if (ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD == $params['field_type']) {
continue;
}
foreach ($option['options'] as $sub_option) {
if (empty($sub_option)) {
continue;
}
$new_params = [
'field_id' => $field_id,
'option_value' => $sub_id,
'display_text' => $sub_option,
'option_order' => 0,
];
$option_info = self::getFieldOptionByFieldIdAndOptionDisplayTextAndOptionValue(
$field_id,
$sub_option,
$sub_id
);
if (empty($option_info)) {
parent::save($new_params, $showQuery);
continue;
}
$new_params['id'] = $option_info['id'];
parent::update($new_params, $showQuery);
}
}
break;
case ExtraField::FIELD_TYPE_TRIPLE_SELECT:
//Format: Option1\Option11:Option111;Option112\Option12:Option121|Option2\Option21:Option211
$options = ExtraField::tripleSelectConvertStringToArray($params['field_options']);
if (!$options) {
break;
}
foreach ($options as $level1) {
$level1Params = [
'field_id' => $field_id,
'option_value' => 0,
'display_text' => $level1['label'],
'option_order' => 0,
];
$optionInfo = self::get_field_option_by_field_id_and_option_display_text(
$field_id,
$level1['label']
);
if (empty($optionInfo)) {
$level1Id = parent::save($level1Params);
} else {
$level1Id = $optionInfo['id'];
$level1Params['id'] = $level1Id;
parent::update($level1Params);
}
foreach ($level1['options'] as $level2) {
$level2Params = [
'field_id' => $field_id,
'option_value' => $level1Id,
'display_text' => $level2['label'],
'display_order' => 0,
];
$optionInfo = self::getFieldOptionByFieldIdAndOptionDisplayTextAndOptionValue(
$field_id,
$level2['label'],
$level1Id
);
if (empty($optionInfo)) {
$level2Id = parent::save($level2Params);
} else {
$level2Id = $optionInfo['id'];
$level2Params['id'] = $level2Id;
parent::update($level2Params);
}
foreach ($level2['options'] as $level3) {
foreach ($level3 as $item) {
$level3Params = [
'field_id' => $field_id,
'option_value' => $level2Id,
'display_text' => $item,
'display_order' => 0,
];
$optionInfo = self::getFieldOptionByFieldIdAndOptionDisplayTextAndOptionValue(
$field_id,
$item,
$level2Id
);
if (empty($optionInfo)) {
parent::save($level3Params);
} else {
$level3Params['id'] = $optionInfo['id'];
parent::update($level3Params);
}
}
}
}
}
break;
default:
$list = explode(';', $params['field_options']);
foreach ($list as $option) {
$option_info = $this->get_field_option_by_field_and_option($field_id, $option);
// Use URLify only for new items
$optionValue = api_replace_dangerous_char($option);
$option = trim($option);
if (false != $option_info) {
continue;
}
$order = $this->get_max_order($field_id);
$new_params = [
'field_id' => $field_id,
'option_value' => trim($optionValue),
'display_text' => trim($option),
'option_order' => $order,
];
parent::save($new_params, $showQuery);
}
break;
}
return true;
}
/**
* Save one option item at a time.
*
* @param array $params Parameters specific to the option
* @param bool $show_query Whether to show the query (sent to parent save() method)
* @param bool $insert_repeated Whether to insert even if the option already exists
*
* @return bool True on success, false on failure
* @assert (array('field_id'=>0),false) === false
* @assert (array('field_id'=>0),false) === true
*/
public function save_one_item($params, $show_query = false, $insert_repeated = true)
{
$field_id = intval($params['field_id']);
if (empty($field_id)) {
return false;
}
if (isset($params['option_value'])) {
$params['option_value'] = trim($params['option_value']);
}
if (isset($params['display_text'])) {
$params['display_text'] = trim($params['display_text']);
}
if (empty($params['option_order'])) {
$order = $this->get_max_order($field_id);
$params['option_order'] = $order;
}
if ($insert_repeated) {
parent::save($params, $show_query);
} else {
$check = $this->get_field_option_by_field_and_option(
$field_id,
$params['option_value']
);
if (false == $check) {
parent::save($params, $show_query);
}
}
return true;
}
/**
* Get the complete row of a specific option of a specific field.
*
* @param int $field_id
* @param string $option_value Value of the option
*
* @return mixed The row on success or false on failure
* @assert (0,'') === false
*/
public function get_field_option_by_field_and_option($field_id, $option_value)
{
$field_id = (int) $field_id;
$option_value = Database::escape_string($option_value);
$extraFieldType = $this->getExtraField()->getExtraFieldType();
$sql = "SELECT s.* FROM {$this->table} s
INNER JOIN {$this->tableExtraField} sf
ON (s.field_id = sf.id)
WHERE
field_id = $field_id AND
option_value = '".$option_value."' AND
sf.extra_field_type = $extraFieldType
";
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
return Database::store_result($result, 'ASSOC');
}
return false;
}
/**
* Get the complete row of a specific option's display text of a specific field.
*
* @param int $field_id
* @param string $option_display_text Display value of the option
*
* @return mixed The row on success or false on failure
* @assert (0, '') === false
*/
public function get_field_option_by_field_id_and_option_display_text($field_id, $option_display_text)
{
$field_id = (int) $field_id;
$option_display_text = Database::escape_string($option_display_text);
$extraFieldType = $this->getExtraField()->getExtraFieldType();
$sql = "SELECT s.* FROM {$this->table} s
INNER JOIN {$this->tableExtraField} sf
ON (s.field_id = sf.id)
WHERE
field_id = $field_id AND
s.display_text = '".$option_display_text."' AND
sf.extra_field_type = $extraFieldType
";
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
return Database::fetch_array($result, 'ASSOC');
}
return false;
}
/**
* Get the complete row of a specific option's display text of a specific field.
*
* @param int $field_id
* @param string $option_display_text Display value of the option
* @param string $option_value Value of the option
*
* @return mixed The row on success or false on failure
* @assert (0, '', '') === false
*/
public function getFieldOptionByFieldIdAndOptionDisplayTextAndOptionValue(
$field_id,
$option_display_text,
$option_value
) {
$field_id = (int) $field_id;
$option_display_text = Database::escape_string($option_display_text);
$option_value = Database::escape_string($option_value);
$extraFieldType = $this->getExtraField()->getExtraFieldType();
$sql = "SELECT s.* FROM {$this->table} s
INNER JOIN {$this->tableExtraField} sf
ON (s.field_id = sf.id)
WHERE
field_id = $field_id AND
sf.display_text = '".$option_display_text."' AND
option_value = '$option_value' AND
sf.extra_field_type = ".$extraFieldType."
";
$result = Database::query($sql);
if (Database::num_rows($result) > 0) {
return Database::fetch_array($result, 'ASSOC');
}
return false;
}
/**
* Gets an array of options for a specific field.
*
* Example:
* <code>
* <?php
* $fieldOption = new ExtraFieldOption('user');
* $fieldOption->get_field_options_by_field(1);
* echo "<pre>".var_export($fieldOption,true)."</pre>";
* ?>
* </code>
*
* @param int $field_id The field ID
* @param bool $add_id_in_array Whether to add the row ID in the result
* @param null $ordered_by Extra ordering query bit
*
* @return array The options if they exists. Otherwise return false
*/
public function get_field_options_by_field($field_id, $add_id_in_array = false, $ordered_by = null)
{
$field_id = (int) $field_id;
$orderBy = null;
switch ($ordered_by) {
case 'id':
$orderBy = ['id' => 'ASC'];
break;
case 'field_id':
$orderBy = ['fieldId' => 'ASC'];
break;
case 'option_value':
$orderBy = ['optionValue' => 'ASC'];
break;
case 'display_text':
$orderBy = ['displayText' => 'ASC'];
break;
case 'priority':
$orderBy = ['priority' => 'ASC'];
break;
case 'priority_message':
$orderBy = ['priorityMessage' => 'ASC'];
break;
case 'option_order':
$orderBy = ['optionOrder' => 'ASC'];
break;
}
$result = Database::getManager()
->getRepository('ChamiloCoreBundle:ExtraFieldOptions')
->findBy(['field' => $field_id], $orderBy);
if (!$result) {
return false;
}
$options = [];
/** @var ExtraFieldOptions $row */
foreach ($result as $row) {
$option = [
'id' => $row->getId(),
'field_id' => $row->getField()->getId(),
'option_value' => $row->getValue(),
'display_text' => self::translateDisplayName($row->getDisplayText()),
'priority' => $row->getPriority(),
'priority_message' => $row->getPriorityMessage(),
'option_order' => $row->getOptionOrder(),
];
if ($add_id_in_array) {
$options[$row->getId()] = $option;
continue;
}
$options[] = $option;
}
return $options;
}
/**
* Get options for a specific field as array or in JSON format suited for the double-select format.
*
* @param int $option_value_id Option value ID
* @param bool $to_json Return format (whether it should be formatted to JSON or not)
*
* @return mixed Row/JSON on success
*/
public function get_second_select_field_options_by_field($option_value_id, $to_json = false)
{
$em = Database::getManager();
$option = $em->find('ChamiloCoreBundle:ExtraFieldOptions', $option_value_id);
if (!$option) {
return !$to_json ? [] : '{}';
}
$subOptions = $em
->getRepository('ChamiloCoreBundle:ExtraFieldOptions')
->findSecondaryOptions($option);
$optionsInfo = [];
/** @var ExtraFieldOptions $subOption */
foreach ($subOptions as $subOption) {
$optionsInfo[] = [
'id' => $subOption->getId(),
'field_id' => $subOption->getField()->getId(),
'option_value' => $subOption->getValue(),
'display_text' => $subOption->getDisplayText(),
'priority' => $subOption->getPriority(),
'priority_message' => $subOption->getPriorityMessage(),
'option_order' => $subOption->getOptionOrder(),
];
}
if (!$to_json) {
return $optionsInfo;
}
$json = [];
foreach ($optionsInfo as $optionInfo) {
$json[$optionInfo['id']] = $optionInfo['display_text'];
}
return json_encode($json);
}
/**
* Get options for a specific field as string split by ;.
*
* @param int $field_id
* @param string $ordered_by Extra query bit for reordering
*
* @return string HTML string of options
* @assert (0, '') === null
*/
public function get_field_options_by_field_to_string($field_id, $ordered_by = null)
{
$field = new ExtraField($this->type);
$field_info = $field->get($field_id);
$options = self::get_field_options_by_field($field_id, false, $ordered_by);
$elements = [];
if (!empty($options)) {
switch ($field_info['field_type']) {
case ExtraField::FIELD_TYPE_DOUBLE_SELECT:
$html = ExtraField::extra_field_double_select_convert_array_to_string($options);
break;
case ExtraField::FIELD_TYPE_SELECT_WITH_TEXT_FIELD:
$html = ExtraField::extraFieldSelectWithTextConvertArrayToString($options);
break;
case ExtraField::FIELD_TYPE_TRIPLE_SELECT:
$html = ExtraField::tripleSelectConvertArrayToString($options);
break;
default:
foreach ($options as $option) {
$elements[] = $option['option_value'];
}
$html = implode(';', $elements);
break;
}
return $html;
}
return null;
}
/**
* Get the maximum order value for a specific field.
*
* @param int $field_id
*
* @return int Current max ID + 1 (we start from 0)
* @assert (0, '') === 1
*/
public function get_max_order($field_id)
{
$field_id = (int) $field_id;
$sql = "SELECT MAX(option_order)
FROM {$this->table}
WHERE field_id = $field_id";
$res = Database::query($sql);
$max = 1;
if (Database::num_rows($res) > 0) {
$row = Database::fetch_array($res);
$max = $row[0] + 1;
}
return $max;
}
/**
* Display a form with the options for the field_id given in REQUEST.
*/
public function display()
{
// action links
echo '<div class="actions">';
$field_id = isset($_REQUEST['field_id']) ? intval($_REQUEST['field_id']) : null;
echo '<a href="'.api_get_self().'?action=add&type='.$this->type.'&field_id='.$field_id.'">'.
Display::return_icon('add_user_fields.png', get_lang('Add'), '', ICON_SIZE_MEDIUM).'</a>';
echo '</div>';
echo Display::grid_html('extra_field_options');
}
/**
* @return array
*/
public function getPriorityOptions()
{
return [
'' => get_lang('SelectAnOption'),
1 => get_lang('Success'),
2 => get_lang('Info'),
3 => get_lang('Warning'),
4 => get_lang('Error'),
];
}
/**
* @param $priority
*
* @return string|null
*/
public function getPriorityMessageType($priority)
{
switch ($priority) {
case 1:
return 'success';
case 2:
return 'info';
case 3:
return 'warning';
case 4:
return 'error';
}
return null;
}
/**
* Returns an HTML form for the current field.
*
* @param string URL to send the form to (action=...)
* @param string Type of action to offer through the form (edit, usually)
*
* @return FormValidator
*/
public function return_form($url, $action)
{
$form_name = $this->type.'_field';
$form = new FormValidator($form_name, 'post', $url);
// Setting the form elements
$header = get_lang('Add');
if ($action === 'edit') {
$header = get_lang('Modify');
}
$form->addElement('header', $header);
$id = isset($_GET['id']) ? (int) $_GET['id'] : '';
$form->addElement('hidden', 'id', $id);
$form->addElement('hidden', 'type', $this->type);
$form->addElement('hidden', 'field_id', $this->fieldId);
if ('edit' == $action) {
$translateUrl = api_get_path(WEB_CODE_PATH).'extrafield/translate.php?'.http_build_query([
'extra_field_option' => $id,
]);
$translateButton = Display::toolbarButton(
get_lang('TranslateThisTerm'),
$translateUrl,
'language',
'link'
);
$form->addText(
'display_text',
[get_lang('Name'), $translateButton]
);
} else {
$form->addElement('text', 'display_text', get_lang('Name'));
}
$form->addElement('text', 'option_value', get_lang('Value'));
$form->addElement('text', 'option_order', get_lang('Order'));
$form->addElement('select', 'priority', get_lang('Priority'), $this->getPriorityOptions());
$form->addElement('textarea', 'priority_message', get_lang('PriorityOfMessage'));
$defaults = [];
if ('edit' == $action) {
// Setting the defaults
$defaults = $this->get($id, false);
$form->freeze('option_value');
$form->addButtonUpdate(get_lang('Modify'));
} else {
$form->addButtonCreate(get_lang('Add'));
}
$form->setDefaults($defaults);
// Setting the rules
$form->addRule('display_text', get_lang('ThisFieldIsRequired'), 'required');
$form->addRule('option_value', get_lang('ThisFieldIsRequired'), 'required');
return $form;
}
/**
* @param string $tag
* @param int $field_id
* @param int $limit
*
* @return array
*/
public function searchByField($tag, $field_id, $limit = 10)
{
$field_id = (int) $field_id;
$limit = (int) $limit;
$tag = Database::escape_string($tag);
$sql = "SELECT DISTINCT id, option_display_text
FROM {$this->table}
WHERE
field_id = '".$field_id."' AND
option_value LIKE '%$tag%'
ORDER BY option_value
LIMIT 0, $limit
";
$result = Database::query($sql);
$values = [];
if (Database::num_rows($result)) {
$values = Database::store_result($result, 'ASSOC');
}
return $values;
}
/**
* @param string $tag
* @param int $field_id
* @param int $limit
*
* @return string
*/
public function getSearchOptionsByField($tag, $field_id, $limit = 10)
{
$result = $this->searchByField($tag, $field_id, $limit = 10);
$values = [];
$json = null;
if (!empty($result)) {
foreach ($result as $item) {
$values[] = [
'value' => $item['id'],
'caption' => $item['option_display_text'],
];
}
$json = json_encode($values);
}
return $json;
}
/**
* Gets an element.
*
* @param int $id
* @param bool $translateDisplayText Optional
*
* @return array
*/
public function get($id, $translateDisplayText = true)
{
$info = parent::get($id);
if ($translateDisplayText) {
$info['display_text'] = self::translateDisplayName($info['display_text']);
}
return $info;
}
/**
* Translate the display text for a extra field option.
*
* @param string $defaultDisplayText
*
* @return string
*/
public static function translateDisplayName($defaultDisplayText)
{
$variableLanguage = self::getLanguageVariable($defaultDisplayText);
return isset($GLOBALS[$variableLanguage]) ? $GLOBALS[$variableLanguage] : $defaultDisplayText;
}
/**
* @param $defaultDisplayText
*
* @return mixed|string
*/
public static function getLanguageVariable($defaultDisplayText)
{
$variableLanguage = api_replace_dangerous_char($defaultDisplayText);
$variableLanguage = str_replace('-', '_', $variableLanguage);
$variableLanguage = api_underscore_to_camel_case($variableLanguage);
return $variableLanguage;
}
/**
* @param null $options
*
* @return array
*/
public function get_all($options = null)
{
$result = parent::get_all($options);
foreach ($result as &$row) {
$row['display_text'] = self::translateDisplayName($row['display_text']);
}
return $result;
}
/**
* @param string $variable
*
* @return array|ExtraFieldOptions[]
*/
public function getOptionsByFieldVariable($variable)
{
$extraFieldType = $this->getExtraField()->getExtraFieldType();
$dql = "SELECT o FROM ChamiloCoreBundle:ExtraFieldOptions o
INNER JOIN ChamiloCoreBundle:ExtraField f WITH o.field = f.id
WHERE f.variable = :variable AND f.extraFieldType = :extra_field_type
ORDER BY o.value ASC";
$result = Database::getManager()
->createQuery($dql)
->setParameters(['variable' => $variable, 'extra_field_type' => $extraFieldType])
->getResult();
return $result;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,208 @@
<?php
/* See license terms in /license.txt */
/**
* This is the file display library for Dokeos.
* Include/require it in your code to use its functionality.
*
* @todo move this file to DocumentManager
*
* Define the image to display for each file extension.
* This needs an existing image repository to work.
*
* @author - Hugues Peeters <peeters@ipm.ucl.ac.be>
*
* @param string $file_name (string) - Name of a file
*
* @return string The gif image to chose
*/
function choose_image($file_name)
{
static $type, $image;
/* TABLES INITIALISATION */
if (!$type || !$image) {
$type['word'] = [
'doc',
'dot',
'rtf',
'mcw',
'wps',
'psw',
'docm',
'docx',
'dotm',
'dotx',
];
$type['web'] = [
'htm',
'html',
'htx',
'xml',
'xsl',
'php',
'xhtml',
];
$type['image'] = [
'gif',
'jpg',
'png',
'bmp',
'jpeg',
'tif',
'tiff',
];
$type['image_vect'] = ['svg', 'svgz'];
$type['audio'] = [
'wav',
'mid',
'mp2',
'mp3',
'midi',
'sib',
'amr',
'kar',
'oga',
'au',
'wma',
];
$type['video'] = [
'mp4',
'mov',
'rm',
'pls',
'mpg',
'mpeg',
'm2v',
'm4v',
'flv',
'f4v',
'avi',
'wmv',
'asf',
'3gp',
'ogv',
'ogg',
'ogx',
'webm',
];
$type['excel'] = [
'xls',
'xlt',
'xls',
'xlt',
'pxl',
'xlsx',
'xlsm',
'xlam',
'xlsb',
'xltm',
'xltx',
];
$type['compressed'] = ['zip', 'tar', 'rar', 'gz'];
$type['code'] = [
'js',
'cpp',
'c',
'java',
'phps',
'jsp',
'asp',
'aspx',
'cfm',
];
$type['acrobat'] = ['pdf'];
$type['powerpoint'] = [
'ppt',
'pps',
'pptm',
'pptx',
'potm',
'potx',
'ppam',
'ppsm',
'ppsx',
];
$type['flash'] = ['fla', 'swf'];
$type['text'] = ['txt', 'log'];
$type['oo_writer'] = ['odt', 'ott', 'sxw', 'stw'];
$type['oo_calc'] = ['ods', 'ots', 'sxc', 'stc'];
$type['oo_impress'] = ['odp', 'otp', 'sxi', 'sti'];
$type['oo_draw'] = ['odg', 'otg', 'sxd', 'std'];
$type['epub'] = ['epub'];
$type['java'] = ['class', 'jar'];
$type['freemind'] = ['mm'];
$image['word'] = 'word.png';
$image['web'] = 'file_html.png';
$image['image'] = 'file_image.png';
$image['image_vect'] = 'file_svg.png';
$image['audio'] = 'file_sound.png';
$image['video'] = 'film.png';
$image['excel'] = 'excel.png';
$image['compressed'] = 'file_zip.png';
$image['code'] = 'icons/22/mime_code.png';
$image['acrobat'] = 'file_pdf.png';
$image['powerpoint'] = 'powerpoint.png';
$image['flash'] = 'file_flash.png';
$image['text'] = 'icons/22/mime_text.png';
$image['oo_writer'] = 'file_oo_writer.png';
$image['oo_calc'] = 'file_oo_calc.png';
$image['oo_impress'] = 'file_oo_impress.png';
$image['oo_draw'] = 'file_oo_draw.png';
$image['epub'] = 'file_epub.png';
$image['java'] = 'file_java.png';
$image['freemind'] = 'file_freemind.png';
}
$extension = [];
if (!is_array($file_name)) {
if (preg_match('/\.([[:alnum:]]+)(\?|$)/', $file_name, $extension)) {
$extension[1] = strtolower($extension[1]);
foreach ($type as $generic_type => $extension_list) {
if (in_array($extension[1], $extension_list)) {
return $image[$generic_type];
}
}
}
}
return 'defaut.gif';
}
/**
* Get the icon to display for a folder by its path.
*
* @param string $folderPath
*
* @return string
*/
function chooseFolderIcon($folderPath)
{
if ($folderPath == '/shared_folder') {
return 'folder_users.png';
}
if (strstr($folderPath, 'shared_folder_session_')) {
return 'folder_users.png';
}
switch ($folderPath) {
case '/audio':
return 'folder_audio.png';
case '/flash':
return 'folder_flash.png';
case '/images':
return 'folder_images.png';
case '/video':
return 'folder_video.png';
case '/images/gallery':
return 'folder_gallery.png';
case '/chat_files':
return 'folder_chat.png';
case '/learning_path':
return 'folder_learningpath.png';
}
return 'folder_document.png';
}

View File

@@ -0,0 +1,388 @@
<?php
/* For licensing terms, see /license.txt */
use Symfony\Component\Filesystem\Filesystem;
/**
* This is the file manage library for Chamilo.
* Include/require it in your code to use its functionality.
*/
/**
* Cheks a file or a directory actually exist at this location.
*
* @author Hugues Peeters <peeters@ipm.ucl.ac.be>
*
* @param string $file_path Path of the presume existing file or dir
*
* @return bool TRUE if the file or the directory exists or FALSE otherwise
*/
function check_name_exist($file_path)
{
clearstatcache();
$save_dir = getcwd();
if (!is_dir(dirname($file_path))) {
return false;
}
chdir(dirname($file_path));
$file_name = basename($file_path);
if (file_exists($file_name)) {
chdir($save_dir);
return true;
} else {
chdir($save_dir);
return false;
}
}
/**
* Deletes a file or a directory.
*
* @author - Hugues Peeters
*
* @param $file (String) - the path of file or directory to delete
*
* @return bool - true if the delete succeed, false otherwise
*
* @see - delete() uses check_name_exist() and removeDir() functions
*/
function my_delete($file)
{
if (check_name_exist($file)) {
if (is_file($file)) { // FILE CASE
unlink($file);
return true;
} elseif (is_dir($file)) { // DIRECTORY CASE
removeDir($file);
return true;
}
}
return false; // no file or directory to delete
}
/**
* Removes a directory recursively.
*
* @returns true if OK, otherwise false
*
* @author Amary <MasterNES@aol.com> (from Nexen.net)
* @author Olivier Brouckaert <oli.brouckaert@skynet.be>
*
* @param string $dir directory to remove
*/
function removeDir($dir)
{
if (!@$opendir = opendir($dir)) {
return false;
}
while ($readdir = readdir($opendir)) {
if ($readdir != '..' && $readdir != '.') {
if (is_file($dir.'/'.$readdir)) {
if (!@unlink($dir.'/'.$readdir)) {
return false;
}
} elseif (is_dir($dir.'/'.$readdir)) {
if (!removeDir($dir.'/'.$readdir)) {
return false;
}
}
}
}
closedir($opendir);
if (!@rmdir($dir)) {
return false;
}
return true;
}
/**
* Return true if folder is empty.
*
* @author hubert.borderiou@grenet.fr
*
* @param string $in_folder folder path on disk
*
* @return int 1 if folder is empty, 0 otherwise
*/
function folder_is_empty($in_folder)
{
$folder_is_empty = 0;
if (is_dir($in_folder)) {
$tab_folder_content = scandir($in_folder);
if ((
count($tab_folder_content) == 2 &&
in_array(".", $tab_folder_content) &&
in_array("..", $tab_folder_content)
) ||
(count($tab_folder_content) < 2)
) {
$folder_is_empty = 1;
}
}
return $folder_is_empty;
}
/**
* Renames a file or a directory.
*
* @author Hugues Peeters <peeters@ipm.ucl.ac.be>
*
* @param string $file_path complete path of the file or the directory
* @param string $new_file_name new name for the file or the directory
*
* @return bool true if succeed, false otherwise
*
* @see rename() uses the check_name_exist() and php2phps() functions
*/
function my_rename($file_path, $new_file_name)
{
$save_dir = getcwd();
$path = dirname($file_path);
$old_file_name = basename($file_path);
$new_file_name = api_replace_dangerous_char($new_file_name);
// If no extension, take the old one
if ((strpos($new_file_name, '.') === false) && ($dotpos = strrpos($old_file_name, '.'))) {
$new_file_name .= substr($old_file_name, $dotpos);
}
// Note: still possible: 'xx.yy' -rename-> '.yy' -rename-> 'zz'
// This is useful for folder names, where otherwise '.' would be sticky
// Extension PHP is not allowed, change to PHPS
$new_file_name = php2phps($new_file_name);
if ($new_file_name == $old_file_name) {
return $old_file_name;
}
if (strtolower($new_file_name) != strtolower($old_file_name) && check_name_exist($path.'/'.$new_file_name)) {
return false;
}
// On a Windows server, it would be better not to do the above check
// because it succeeds for some new names resembling the old name.
// But on Unix/Linux the check must be done because rename overwrites.
chdir($path);
$res = rename($old_file_name, $new_file_name) ? $new_file_name : false;
chdir($save_dir);
return $res;
}
/**
* Moves a file or a directory to an other area.
*
* @author Hugues Peeters <peeters@ipm.ucl.ac.be>
*
* @param string $source the path of file or directory to move
* @param string $target the path of the new area
* @param bool $forceMove Whether to force a move or to make a copy (safer but slower) and then delete the original
* @param bool $moveContent In some cases (including migrations), we need to move the *content* and not the folder itself
*
* @return bool true if the move succeed, false otherwise
*
* @see move() uses check_name_exist() and copyDirTo() functions
*/
function move($source, $target, $forceMove = true, $moveContent = false)
{
$target = realpath($target); // remove trailing slash
$source = realpath($source);
if (check_name_exist($source)) {
$file_name = basename($source);
// move onto self illegal: mv a/b/c a/b/c or mv a/b/c a/b
if (strcasecmp($target, dirname($source)) === 0) {
return false;
}
$isWindowsOS = api_is_windows_os();
$canExec = function_exists('exec');
/* File case */
if (is_file($source)) {
if ($forceMove) {
if (!$isWindowsOS && $canExec) {
exec('mv '.$source.' '.$target.'/'.$file_name);
} else {
// Try copying
copy($source, $target.'/'.$file_name);
unlink($source);
}
} else {
copy($source, $target.'/'.$file_name);
unlink($source);
}
return true;
} elseif (is_dir($source)) {
// move dir down will cause loop: mv a/b/ a/b/c/ not legal
if (strncasecmp($target, $source, strlen($source)) == 0) {
return false;
}
/* Directory */
if ($forceMove && !$isWindowsOS && $canExec) {
if ($moveContent) {
$base = basename($source);
$out = [];
$retVal = -1;
exec('mv '.$source.'/* '.$target.'/'.$base, $out, $retVal);
if ($retVal !== 0) {
return false; // mv should return 0 on success
}
exec('rm -rf '.$source);
} else {
$out = [];
$retVal = -1;
exec("mv $source $target", $out, $retVal);
if ($retVal !== 0) {
error_log("Chamilo error fileManage.lib.php: mv $source $target\n");
return false; // mv should return 0 on success
}
}
} else {
$base = basename($source);
return copyDirTo($source, $target.'/'.$base);
}
return true;
}
}
return false;
}
/**
* Moves a directory and its content to an other area.
*
* @author Hugues Peeters <peeters@ipm.ucl.ac.be>
*
* @param string $source the path of the directory to move
* @param string $destination the path of the new area
* @param bool $move Whether we want to remove the source at the end
*
* @return bool false on error
*/
function copyDirTo($source, $destination, $move = true)
{
$fs = new Filesystem();
if (is_dir($source)) {
$fs->mkdir($destination);
if (!is_dir($destination)) {
error_log("Chamilo copyDirTo cannot mkdir $destination\n");
return false; // could not create destination dir
}
$fs->mirror($source, $destination);
if ($move) {
$fs->remove($source);
}
}
return true;
}
/**
* Copy a directory and its directories (not files) to an other area.
*
* @param string $source the path of the directory to move
* @param string $destination the path of the new area
*
* @return bool false on error
*/
function copyDirWithoutFilesTo($source, $destination)
{
$fs = new Filesystem();
if (!is_dir($source)) {
return false;
}
if (!$fs->exists($destination)) {
$fs->mkdir($destination);
}
$dirIterator = new RecursiveDirectoryIterator($source, RecursiveDirectoryIterator::SKIP_DOTS);
$iterator = new RecursiveIteratorIterator($dirIterator, RecursiveIteratorIterator::SELF_FIRST);
/** @var \SplFileInfo $item */
foreach ($iterator as $item) {
if ($item->isFile()) {
continue;
}
$newDir = $destination.'/'.$item->getFilename();
if (!$fs->exists($newDir)) {
$fs->mkdir($destination.'/'.$item->getFilename());
}
}
return true;
}
/**
* Extracting extension of a filename.
*
* @returns array
*
* @param string $filename filename
*/
function getextension($filename)
{
$bouts = explode('.', $filename);
return [array_pop($bouts), implode('.', $bouts)];
}
/**
* Get a list of all PHP (.php) files in a given directory. Includes .tpl files.
*
* @param string $base_path The base path in which to find the corresponding files
* @param bool $includeStatic Include static .html, .htm and .css files
*
* @return array
*/
function getAllPhpFiles($base_path, $includeStatic = false)
{
$list = scandir($base_path);
$files = [];
$extensionsArray = ['.php', '.tpl'];
if ($includeStatic) {
$extensionsArray[] = 'html';
$extensionsArray[] = '.htm';
$extensionsArray[] = '.css';
}
foreach ($list as $item) {
if (substr($item, 0, 1) == '.') {
continue;
}
$special_dirs = [api_get_path(SYS_TEST_PATH), api_get_path(SYS_COURSE_PATH), api_get_path(SYS_LANG_PATH), api_get_path(SYS_ARCHIVE_PATH)];
if (in_array($base_path.$item.'/', $special_dirs)) {
continue;
}
if (is_dir($base_path.$item)) {
$files = array_merge($files, getAllPhpFiles($base_path.$item.'/', $includeStatic));
} else {
//only analyse php files
$sub = substr($item, -4);
if (in_array($sub, $extensionsArray)) {
$files[] = $base_path.$item;
}
}
}
$list = null;
return $files;
}

File diff suppressed because it is too large Load Diff

118
main/inc/lib/fixlinks.js Normal file
View File

@@ -0,0 +1,118 @@
$(function() {
var objects = $(document).find('object');
var pathname = location.pathname;
var coursePath = pathname.substr(0, pathname.indexOf('/courses/'));
var iconPath = location.protocol + '//' + location.host+ coursePath + '/main/img/';
var url = "http://"+location.host + coursePath+"/courses/proxy.php?";
objects.each(function (value, obj) {
var openerId = this.id +'_opener';
var link = '<a id="'+openerId+'" href="#">If video does not work, try clicking here.</a>';
var embed = $("#"+this.id).find('embed').first();
var hasHttp = embed.attr('src').indexOf("http");
if (hasHttp < 0) {
return true;
}
var height = embed.attr('height');
var width = embed.attr('width');
var src = embed.attr('src').replace('https', 'http');
var completeUrl = url + 'width='+embed.attr('width')+
'&height='+height+
'&id='+this.id+
'&flashvars='+encodeURIComponent(embed.attr('flashvars'))+
'&src='+src+
'&width='+width;
var result = $("#"+this.id).find('#'+openerId);
if (result.length == 0) {
$("#" + this.id).append('<br />' + link);
$('#' + openerId).click(function () {
var window = window.open(completeUrl, "Video", "width=" + width + ", " + "height=" + height + "");
window.document.title = 'Video';
});
}
});
var iframes = $(document).find('iframe');
iframes.each(function (value, obj) {
var randLetter = String.fromCharCode(65 + Math.floor(Math.random() * 26));
var uniqid = randLetter + Date.now();
var openerId = uniqid +'_opener';
var link = '<a id="'+openerId+'" class="generated" href="#">Open website.<img width="16px" src="'+iconPath+'link-external.png "/></a>';
var embed = $(this);
var height = embed.attr('height');
var width = embed.attr('width');
var src = embed.attr('src');
var n = src.indexOf("youtube.com");
if (n > 0) {
return true;
}
var completeUrl = url + 'width='+embed.attr('width')+
'&height='+height+
'&type=iframe'+
'&id='+uniqid+
'&src='+src+
'&width='+width;
var result = $(this).find('#'+openerId);
if (result.length == 0) {
if (embed.next().attr('class') != 'generated') {
$(this).prepend(link + '<br />');
$('#' + openerId).click(function () {
width = 1280;
height = 640;
var win = window.open(completeUrl, "Video", "width=" + width + ", " + "height=" + height + "");
win.document.title = 'Video';
});
}
}
});
var anchors = $(document).find('a').not('.generated');
anchors.each(function (value, obj) {
if ($(this).next().attr('class') != 'generated' ) {
var content = $(this).html();
content = content.replace('<br />', '');
content = content.replace('<br>', '');
content = $.trim(content);
if (content == '') {
return true;
}
if ($(this).attr('href')) {
var hasLocalhost = $(this).attr('href').indexOf(location.host);
if (hasLocalhost > 0) {
return true;
}
var hasJs = $(this).attr('href').indexOf('javascript');
if (hasJs >= 0) {
return true;
}
}
if ($(this).attr('class')) {
var hasAccordion = $(this).attr('class').indexOf('accordion-toggle');
if (hasAccordion >= 0) {
return true;
}
}
var src = $(this).attr('href');
src = url+'&type=link&src='+src;
src = src.replace('https', 'http');
$(this).attr('href', src);
$(this).attr('target', '_blank');
var myAnchor = $('<a><img width="16px" src="'+iconPath+'link-external.png "/></a>').attr("href", src).attr('target', '_blank').attr('class', 'generated');
$(this).after(myAnchor);
$(this).after('-');
}
});
});

View File

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

Binary file not shown.

View File

@@ -0,0 +1,86 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Input file with progress element.
*
* Class BigUpload
*/
class BigUpload extends HTML_QuickForm_file
{
/**
* @param string $elementName
* @param string $elementLabel
* @param array $attributes
*/
public function __construct($elementName = null, $elementLabel = null, $attributes = null)
{
parent::__construct($elementName, $elementLabel, $attributes);
}
/**
* @return string
*/
public function toHtml()
{
$origin = $this->getAttribute('data-origin');
$id = $this->getAttribute('id');
$maxSize = getIniMaxFileSizeInBytes();
$errorUploadMessage = get_lang('FileSizeIsTooBig').' '.get_lang('MaxFileSize').' : '.getIniMaxFileSizeInBytes(true);
$html = parent::toHtml();
$html .= '<div id="'.$id.'-bigUploadProgressBarContainer">
<div id="'.$id.'-bigUploadProgressBarFilled"></div>
</div>
<div id="'.$id.'-bigUploadTimeRemaining"></div>
<div id="'.$id.'-bigUploadResponse"></div>';
$js = '<script src="'.api_get_path(WEB_LIBRARY_JS_PATH).'bigupload/js/bigUpload.js"></script>';
$js .= '<script>
var bigUpload = new bigUpload();
var uploadForm, formId, submitButtonId;
$(function() {
uploadForm = $("#'.$id.'").closest("form");
formId = uploadForm.attr("id");
submitButtonId = uploadForm.find("[type=\'submit\']").attr("id");
$("#"+submitButtonId).click(function(e) {
if ($("#'.$id.'").val()) {
e.preventDefault();
setBigUploadSettings();
bigUpload.fire();
}
});
});
function setBigUploadSettings() {
//The id of the file input
bigUpload.settings.inputField = "'.$id.'";
//The id of the form with the file upload.
bigUpload.settings.formId = formId;
//The id of the progress bar
bigUpload.settings.progressBarField = "'.$id.'-bigUploadProgressBarFilled";
//The id of the time remaining field
bigUpload.settings.timeRemainingField = "'.$id.'-bigUploadTimeRemaining";
//The id of the text response field
bigUpload.settings.responseField = "'.$id.'-bigUploadResponse";
//The id of the submit button
bigUpload.settings.submitButton = submitButtonId;
//Color of the background of the progress bar
bigUpload.settings.progressBarColor = "#5bb75b";
//Color of the background of the progress bar when an error is triggered
bigUpload.settings.progressBarColorError = "#da4f49";
//Path to the php script for handling the uploads
bigUpload.settings.scriptPath = "'.api_get_path(WEB_LIBRARY_JS_PATH).'bigupload/inc/bigUpload.php";
//cid Req
bigUpload.settings.cidReq = "'.api_get_cidreq().'";
//Set the origin upload
bigUpload.settings.origin = "'.$origin.'";
//The parameters from the upload form
bigUpload.settings.formParams = uploadForm.serialize();
//Max file size allowed
bigUpload.settings.maxFileSize = "'.$maxSize.'";
// Message error upload filesize
bigUpload.settings.errMessageFileSize = "'.$errorUploadMessage.'";
}
</script>';
return $js.$html;
}
}

View File

@@ -0,0 +1,52 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Input Color element.
*
* Class Color
*/
class Color extends HTML_QuickForm_text
{
/**
* @param string $elementName
* @param string $elementLabel
* @param array $attributes
*/
public function __construct($elementName = null, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
$attributes['type'] = 'color';
$attributes['class'] = 'form-control';
$attributes['cols-size'] = isset($attributes['cols-size']) ? $attributes['cols-size'] : [2, 1, 9];
parent::__construct($elementName, $elementLabel, $attributes);
$this->_appendName = true;
$this->setType('color');
}
/**
* @return string
*/
public function toHtml()
{
return parent::toHtml().<<<JS
<script>
$(function() {
var txtColor = $('#{$this->getAttribute('id')}'),
lblColor = txtColor.parent().next();
lblColor.text(txtColor.val());
txtColor.on('change', function () {
lblColor.text(txtColor.val());
})
});
</script>
JS;
}
}

View File

@@ -0,0 +1,186 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Form element to select a date.
*
* Class DatePicker
*/
class DatePicker extends HTML_QuickForm_text
{
/**
* @param string $elementName
* @param string|array $elementLabel
* @param array $attributes
*/
public function __construct($elementName, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
$attributes['class'] = 'form-control';
parent::__construct($elementName, $elementLabel, $attributes);
$this->_appendName = true;
}
/**
* HTML code to display this datepicker.
*
* @return string
*/
public function toHtml()
{
if ($this->_flagFrozen) {
return $this->getFrozenHtml();
}
$id = $this->getAttribute('id');
$value = $this->getValue();
if (!empty($value)) {
$value = api_format_date($value, DATE_FORMAT_LONG_NO_DAY);
}
return '
<div class="input-group">
<span class="input-group-addon cursor-pointer">
<input '.$this->_getAttrString($this->_attributes).'>
</span>
<p class="form-control disabled" id="'.$id.'_alt_text">'.$value.'</p>
<input class="form-control" type="hidden" id="'.$id.'_alt" value="'.$value.'">
<span class="input-group-btn">
<button class="btn btn-default" type="button"
title="'.sprintf(get_lang('ResetFieldX'), $this->_label).'">
<span class="fa fa-trash text-danger" aria-hidden="true"></span>
<span class="sr-only">'.sprintf(get_lang('ResetFieldX'), $this->_label).'</span>
</button>
</span>
</div>
'.$this->getElementJS();
}
/**
* @param string $value
*/
public function setValue($value)
{
$value = substr($value, 0, 16);
$this->updateAttributes(
[
'value' => $value,
]
);
}
/**
* @param string $layout
*
* @return string
*/
public function getTemplate($layout)
{
$size = $this->calculateSize();
switch ($layout) {
case FormValidator::LAYOUT_INLINE:
return '
<div class="form-group {error_class}">
<label {label-for} >
<!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
{label}
</label>
{element}
</div>';
case FormValidator::LAYOUT_HORIZONTAL:
return '
<div class="form-group {error_class}">
<label {label-for} class="col-sm-'.$size[0].' control-label {extra_label_class}" >
<!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
{label}
</label>
<div class="col-sm-'.$size[1].'">
{icon}
{element}
<!-- BEGIN label_2 -->
<p class="help-block">{label_2}</p>
<!-- END label_2 -->
<!-- BEGIN error -->
<span class="help-inline help-block">{error}</span>
<!-- END error -->
</div>
<div class="col-sm-'.$size[2].'">
<!-- BEGIN label_3 -->
{label_3}
<!-- END label_3 -->
</div>
</div>';
case FormValidator::LAYOUT_BOX_NO_LABEL:
return '{element}';
}
return '<div class="form-group">
<label {label-for}>{label}</label>
{element}
</div>'
;
}
/**
* Get the necessary javascript for this datepicker.
*
* @return string
*/
private function getElementJS()
{
$js = null;
$id = $this->getAttribute('id');
$js .= "<script>
$(function() {
var txtDate = $('#$id'),
inputGroup = txtDate.parents('.input-group'),
txtDateAlt = $('#{$id}_alt'),
txtDateAltText = $('#{$id}_alt_text');
txtDate
.hide()
.datepicker({
defaultDate: '".$this->getValue()."',
dateFormat: 'yy-mm-dd',
altField: '#{$id}_alt',
altFormat: \"".get_lang('DateFormatLongNoDayJS')."\",
showOn: 'both',
buttonImage: '".Display::return_icon('attendance.png', null, [], ICON_SIZE_TINY, true, true)."',
buttonImageOnly: true,
buttonText: '".get_lang('SelectDate')."',
changeMonth: true,
changeYear: true,
yearRange: 'c-60y:c+5y'
})
.on('change', function (e) {
txtDateAltText.text(txtDateAlt.val());
});
txtDateAltText.on('click', function () {
txtDate.datepicker('show');
});
inputGroup
.find('button')
.on('click', function (e) {
e.preventDefault();
$('#$id, #{$id}_alt').val('');
$('#{$id}_alt_text').html('');
});
});
</script>";
return $js;
}
}

View File

@@ -0,0 +1,231 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Form element to select a range of dates (with popup datepicker).
*/
class DateRangePicker extends HTML_QuickForm_text
{
/**
* DateRangePicker constructor.
*
* @param string $elementName
* @param string|array $elementLabel
* @param array $attributes
*/
public function __construct($elementName, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
$attributes['class'] = 'form-control';
parent::__construct($elementName, $elementLabel, $attributes);
$this->_appendName = true;
$this->_type = 'date_range_picker';
}
/**
* @return string
*/
public function toHtml()
{
$js = $this->getElementJS();
$this->removeAttribute('format');
$this->removeAttribute('timepicker');
$this->removeAttribute('validate_format');
return $js.parent::toHtml();
}
/**
* @param string $value
*/
public function setValue($value)
{
$this->updateAttributes(
[
'value' => $value,
]
);
}
/**
* @param array $dateRange
*
* @return array
*/
public function parseDateRange($dateRange)
{
$dateRange = Security::remove_XSS($dateRange);
$dates = explode('/', $dateRange);
$dates = array_map('trim', $dates);
$start = isset($dates[0]) ? $dates[0] : '';
$end = isset($dates[1]) ? $dates[1] : '';
$pattern = 'yyyy-MM-dd HH:mm';
if ('false' === $this->getAttribute('timePicker') &&
false === strpos($this->getAttribute('format'), 'HH:mm')) {
$pattern = 'yyyy-MM-dd';
}
$formatter = new IntlDateFormatter(
'en',
IntlDateFormatter::NONE,
IntlDateFormatter::NONE,
'UTC',
IntlDateFormatter::GREGORIAN,
$pattern
);
$resultStart = $formatter->format($formatter->parse($start));
$resultEnd = $formatter->format($formatter->parse($end));
return [
'start' => $resultStart,
'end' => $resultEnd,
];
}
/**
* @param array $dates result of parseDateRange()
*
* @return bool
*/
public function validateDates($dates, $format = null)
{
if (empty($dates['start']) || empty($dates['end'])) {
return false;
}
$format = $format ? $format : 'Y-m-d H:i';
$d = DateTime::createFromFormat($format, $dates['start']);
$resultStart = $d && $d->format($format) == $dates['start'];
$d = DateTime::createFromFormat($format, $dates['end']);
$resultEnd = $d && $d->format($format) == $dates['end'];
if (!$resultStart || !$resultEnd) {
return false;
}
return true;
}
/**
* @param mixed $value
* @param array $submitValues
* @param array $errors
*
* @return string
*/
public function getSubmitValue($value, &$submitValues, &$errors)
{
/** @var DateRangePicker $element */
$elementName = $this->getName();
$parsedDates = $this->parseDateRange($value);
$validateFormat = $this->getAttribute('validate_format');
if (!$this->validateDates($parsedDates, $validateFormat)) {
$errors[$elementName] = get_lang('CheckDates');
}
$submitValues[$elementName.'_start'] = $parsedDates['start'];
$submitValues[$elementName.'_end'] = $parsedDates['end'];
return $value;
}
/**
* Get the necessary javascript for this datepicker.
*
* @return string
*/
private function getElementJS()
{
$js = null;
$id = $this->getAttribute('id');
$dateRange = $this->getAttribute('value');
$defaultDates = null;
if (!empty($dateRange)) {
$dates = $this->parseDateRange($dateRange);
$defaultDates = "
startDate: '".$dates['start']."',
endDate: '".$dates['end']."', ";
}
$minDate = null;
$minDateValue = Security::remove_XSS($this->getAttribute('minDate'));
if (!empty($minDateValue)) {
$minDate = "
minDate: '{$minDateValue}',
";
}
$maxDate = null;
$maxDateValue = Security::remove_XSS($this->getAttribute('maxDate'));
if (!empty($maxDateValue)) {
$maxDate = "
maxDate: '{$maxDateValue}',
";
}
$format = 'YYYY-MM-DD HH:mm';
$formatValue = Security::remove_XSS($this->getAttribute('format'));
if (!empty($formatValue)) {
$format = $formatValue;
}
$timePicker = 'true';
$timePickerValue = Security::remove_XSS($this->getAttribute('timePicker'));
if (!empty($timePickerValue)) {
$timePicker = 'false';
}
$timeIncrement = FormValidator::getTimepickerIncrement();
// timeFormat: 'hh:mm'
$js .= "<script>
$(function() {
$('#$id').daterangepicker({
timePicker: $timePicker,
timePickerIncrement: $timeIncrement,
timePicker24Hour: true,
$defaultDates
$maxDate
$minDate
ranges: {
'".addslashes(get_lang('Today'))."': [moment(), moment()],
'".addslashes(get_lang('Yesterday'))."': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
'".addslashes(get_lang('ThisMonth'))."': [moment().startOf('month'), moment().endOf('month')],
'".addslashes(get_lang('LastMonth'))."': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')],
'".addslashes(get_lang('ThisWeek'))."': [moment().weekday(1), moment().weekday(5)],
'".addslashes(get_lang('NextWeek'))."': [moment().weekday(8), moment().weekday(12)]
},
//showDropdowns : true,
locale: {
separator: ' / ',
format: '$format',
applyLabel: '".addslashes(get_lang('Ok'))."',
cancelLabel: '".addslashes(get_lang('Cancel'))."',
fromLabel: '".addslashes(get_lang('From'))."',
toLabel: '".addslashes(get_lang('Until'))."',
customRangeLabel: '".addslashes(get_lang('CustomRange'))."',
}
});
$('#$id').on('change', function() {
var myPickedDates = $('#$id').val().split('/');
var {$id}_start = myPickedDates[0].trim();
var {$id}_end = myPickedDates[1].trim();
$('input[name={$id}_start]').val({$id}_start);
$('input[name={$id}_end]').val({$id}_end);
});
});
</script>";
return $js;
}
}

View File

@@ -0,0 +1,191 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Form element to select a date and hour.
*/
class DateTimePicker extends HTML_QuickForm_text
{
/**
* DateTimePicker constructor.
*
* @param string $elementName
* @param string|array $elementLabel
* @param array $attributes
*/
public function __construct($elementName, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
$attributes['class'] = 'form-control';
parent::__construct($elementName, $elementLabel, $attributes);
$this->_appendName = true;
}
/**
* HTML code to display this datepicker.
*
* @return string
*/
public function toHtml()
{
if ($this->_flagFrozen) {
return $this->getFrozenHtml();
}
$id = $this->getAttribute('id');
$value = $this->getValue();
$formattedValue = '';
if (!empty($value)) {
$formattedValue = api_format_date($value, DATE_TIME_FORMAT_LONG_24H);
}
$label = $this->getLabel();
if (is_array($label) && isset($label[0])) {
$label = $label[0];
}
$resetFieldX = sprintf(get_lang('ResetFieldX'), $label);
return '
<div class="input-group" id="date_time_wrapper_'.$id.'">
<span class="input-group-addon cursor-pointer">
<input '.$this->_getAttrString($this->_attributes).'>
</span>
<p class="form-control disabled" id="'.$id.'_alt_text">'.$formattedValue.'</p>
<input class="form-control" type="hidden" id="'.$id.'_alt" value="'.$value.'">
<span class="input-group-btn">
<button class="btn btn-default" type="button"
title="'.$resetFieldX.'">
<span class="fa fa-trash text-danger" aria-hidden="true"></span>
<span class="sr-only">'.$resetFieldX.'</span>
</button>
</span>
</div>
'.$this->getElementJS();
}
/**
* @param string $value
*/
public function setValue($value)
{
$value = substr($value, 0, 16);
$this->updateAttributes(['value' => $value]);
}
/**
* @param string $layout
*
* @return string
*/
public function getTemplate($layout)
{
$size = $this->calculateSize();
switch ($layout) {
case FormValidator::LAYOUT_INLINE:
return '
<div class="form-group {error_class}">
<label {label-for} >
<!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
{label}
</label>
{element}
</div>';
case FormValidator::LAYOUT_HORIZONTAL:
return '
<div class="form-group {error_class}">
<label {label-for} class="col-sm-'.$size[0].' control-label {extra_label_class}" >
<!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
{label}
</label>
<div class="col-sm-'.$size[1].'">
{icon}
{element}
<!-- BEGIN label_2 -->
<p class="help-block">{label_2}</p>
<!-- END label_2 -->
<!-- BEGIN error -->
<span class="help-inline help-block">{error}</span>
<!-- END error -->
</div>
<div class="col-sm-'.$size[2].'">
<!-- BEGIN label_3 -->
{label_3}
<!-- END label_3 -->
</div>
</div>';
case FormValidator::LAYOUT_BOX_NO_LABEL:
return '{element}';
}
}
/**
* Get the necessary javascript for this datepicker.
*
* @return string
*/
private function getElementJS()
{
$timeIncrement = FormValidator::getTimepickerIncrement();
$js = null;
$id = $this->getAttribute('id');
//timeFormat: 'hh:mm'
$js .= "<script>
$(function() {
var txtDateTime = $('#$id'),
inputGroup = txtDateTime.parents('.input-group'),
txtDateTimeAlt = $('#{$id}_alt'),
txtDateTimeAltText = $('#{$id}_alt_text');
txtDateTime
.hide()
.datetimepicker({
defaultDate: '".$this->getValue()."',
dateFormat: 'yy-mm-dd',
controlType: 'select',
oneLine: true,
stepMinute: $timeIncrement,
timeFormat: 'HH:mm',
altField: '#{$id}_alt',
altFormat: \"".get_lang('DateFormatLongNoDayJS')."\",
altTimeFormat: \"".get_lang('TimeFormatNoSecJS')."\",
altSeparator: \" ".get_lang('AtTime')." \",
altFieldTimeOnly: false,
showOn: 'both',
buttonImage: '".Display::return_icon('attendance.png', null, [], ICON_SIZE_TINY, true, true)."',
buttonImageOnly: true,
buttonText: '".get_lang('SelectDate')."',
changeMonth: true,
changeYear: true
})
.on('change', function (e) {
txtDateTimeAltText.text(txtDateTimeAlt.val());
});
txtDateTimeAltText.on('click', function () {
txtDateTime.datepicker('show');
});
inputGroup
.find('button')
.on('click', function (e) {
e.preventDefault();
$('#$id, #{$id}_alt').val('');
$('#{$id}_alt_text').html('');
});
});
</script>";
return $js;
}
}

View File

@@ -0,0 +1,239 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Form element to select a date.
*
* Class DatePicker
*/
class DateTimeRangePicker extends DateRangePicker
{
/**
* HTML code to display this datepicker.
*
* @return string
*/
public function toHtml()
{
if ($this->_flagFrozen) {
return $this->getFrozenHtml();
}
$id = $this->getAttribute('id');
$dateRange = $this->getValue();
$value = '';
if (!empty($dateRange)) {
$dates = $this->parseDateRange($dateRange);
$value = api_format_date($dates['date'], DATE_FORMAT_LONG_NO_DAY);
}
return '
<div class="input-group">
<span class="input-group-addon cursor-pointer">
<input '.$this->_getAttrString($this->_attributes).'>
</span>
<p class="form-control disabled" id="'.$id.'_alt_text">'.$value.'</p>
<input class="form-control" type="hidden" id="'.$id.'_alt" value="'.$value.'">
<span class="input-group-btn">
<button class="btn btn-default" type="button"
title="'.sprintf(get_lang('ResetFieldX'), $this->_label).'">
<span class="fa fa-trash text-danger" aria-hidden="true"></span>
<span class="sr-only">'.sprintf(get_lang('ResetFieldX'), $this->_label).'</span>
</button>
</span>
</div>
'.$this->getElementJS();
}
/**
* @param string $layout
*
* @return string
*/
public function getTemplate($layout)
{
$size = $this->calculateSize();
$id = $this->getAttribute('id');
switch ($layout) {
case FormValidator::LAYOUT_INLINE:
return '
<div class="form-group {error_class}">
<label {label-for} >
<!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
{label}
</label>
{element}
</div>';
break;
case FormValidator::LAYOUT_HORIZONTAL:
return '
<span id="'.$id.'_date_time_wrapper">
<div class="form-group {error_class}">
<label {label-for} class="col-sm-'.$size[0].' control-label" >
<!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
{label}
</label>
<div class="col-sm-'.$size[1].'">
{icon}
{element}
<!-- BEGIN label_2 -->
<p class="help-block">{label_2}</p>
<!-- END label_2 -->
<!-- BEGIN error -->
<span class="help-inline help-block">{error}</span>
<!-- END error -->
</div>
<div class="col-sm-'.$size[2].'">
<!-- BEGIN label_3 -->
{label_3}
<!-- END label_3 -->
</div>
</div>
<div class="form-group {error_class}">
<label class="col-sm-'.$size[0].' control-label" >
<!-- BEGIN required --><span class="form_required">*</span><!-- END required -->
'.get_lang('Hour').'
</label>
<div class="col-sm-'.$size[1].'">
<div class="input-group">
<p id="'.$id.'_time_range">
<input type="text" id="'.$id.'_time_range_start" name="'.$id.'_time_range_start" class="time start" autocomplete="off">
'.get_lang('To').'
<input type="text" id="'.$id.'_time_range_end" name="'.$id.'_time_range_end" class="time end " autocomplete="off">
</p>
</div>
</div>
</div>
</span>
';
break;
case FormValidator::LAYOUT_BOX_NO_LABEL:
return '
<label {label-for}>{label}</label>
<div class="input-group">
{icon}
{element}
</div>';
break;
}
}
/**
* @param array $dateRange
*
* @return array
*/
public function parseDateRange($dateRange)
{
$dateRange = Security::remove_XSS($dateRange);
$dates = explode('@@', $dateRange);
$dates = array_map('trim', $dates);
$start = isset($dates[0]) ? $dates[0] : '';
$end = isset($dates[1]) ? $dates[1] : '';
$date = substr($start, 0, 10);
$start = isset($dates[0]) ? $dates[0] : '';
//$start = substr($start, 11, strlen($start));
//$end = substr($end, 11, strlen($end));
return [
'date' => $date,
'start_time' => $start,
'end_time' => $end,
];
}
/**
* @param string $value
*/
public function setValue($value)
{
$this->updateAttributes(
[
'value' => $value,
]
);
}
/**
* Get the necessary javascript for this datepicker.
*
* @return string
*/
private function getElementJS()
{
$js = null;
$id = $this->getAttribute('id');
$dateRange = $this->getValue();
$defaultDate = '';
$startTime = '';
$endTime = '';
if (!empty($dateRange)) {
$dates = $this->parseDateRange($dateRange);
$defaultDate = $dates['date'];
$startTime = $dates['start_time'];
$endTime = $dates['end_time'];
}
$js .= "<script>
$(function() {
var txtDate = $('#$id'),
inputGroup = txtDate.parents('.input-group'),
txtDateAlt = $('#{$id}_alt'),
txtDateAltText = $('#{$id}_alt_text');
txtDate
.hide()
.datepicker({
defaultDate: '".$defaultDate."',
dateFormat: 'yy-mm-dd',
altField: '#{$id}_alt',
altFormat: \"".get_lang('DateFormatLongNoDayJS')."\",
showOn: 'both',
buttonImage: '".Display::return_icon('attendance.png', null, [], ICON_SIZE_TINY, true, true)."',
buttonImageOnly: true,
buttonText: '".get_lang('SelectDate')."',
changeMonth: true,
changeYear: true,
yearRange: 'c-60y:c+5y'
})
.on('change', function (e) {
txtDateAltText.text(txtDateAlt.val());
});
txtDateAltText.on('click', function () {
txtDate.datepicker('show');
});
inputGroup
.find('button')
.on('click', function (e) {
e.preventDefault();
$('#$id, #{$id}_alt').val('');
$('#{$id}_alt_text').html('');
});
$('#".$id."_time_range .time').timepicker({
'showDuration': true,
'timeFormat': 'H:i:s',
'scrollDefault': 'now',
});
$('#".$id."_time_range_start').timepicker('setTime', new Date('".$startTime."'));
$('#".$id."_time_range_end').timepicker('setTime', new Date('".$endTime."'));
var timeOnlyExampleEl = document.getElementById('".$id."_time_range');
var timeOnlyDatepair = new Datepair(timeOnlyExampleEl);
});
</script>";
return $js;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Float element.
*
* Accepts values like 3.1415 and 3,1415 (its processed and converted to 3.1415)
*
* Class Float
*/
class FloatNumber extends HTML_QuickForm_text
{
/**
* @param string $elementName
* @param string $elementLabel
* @param array $attributes
*/
public function __construct($elementName = null, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
$attributes['type'] = 'float';
$attributes['class'] = 'form-control';
parent::__construct($elementName, $elementLabel, $attributes);
$this->_appendName = true;
$this->setType('float');
}
/**
* @param string $value
*/
public function setValue($value)
{
$value = api_float_val($value);
$this->updateAttributes(
[
'value' => $value,
]
);
}
/**
* @return float
*/
public function getValue()
{
$value = $this->getAttribute('value');
$value = api_float_val($value);
return $value;
}
/**
* @param mixed $value
* @param array $submitValues
* @param array $errors
*/
public function getSubmitValue($value, &$submitValues, &$errors)
{
$value = api_float_val($value);
$elementName = $this->getName();
$submitValues[$elementName] = $value;
return $value;
}
/**
* We check the options and return only the values that _could_ have been
* selected. We also return a scalar value if select is not "multiple".
*/
public function exportValue(&$submitValues, $assoc = false)
{
$value = $this->_findValue($submitValues);
$value = api_float_val($value);
if (!$value) {
$value = '';
}
return $this->_prepareValue($value, $assoc);
}
}

View File

@@ -0,0 +1,119 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\CoreBundle\Component\Editor\CkEditor\CkEditor;
use Chamilo\CoreBundle\Component\HTMLPurifier\Filter\RemoveOnAttributes;
/**
* A html editor field to use with QuickForm.
*/
class HtmlEditor extends HTML_QuickForm_textarea
{
/** @var \Chamilo\CoreBundle\Component\Editor\Editor */
public $editor;
/**
* Full page.
*/
public $fullPage;
/**
* Class Constructor.
*
* @param string $name
* @param string|array $label HTML editor label
* @param array $attributes Attributes for the textarea
* @param array $config optional configuration settings for the online editor
*/
public function __construct(
$name,
$label = null,
$attributes = [],
$config = []
) {
if (empty($name)) {
throw new \Exception('Name is required');
}
parent::__construct($name, $label, $attributes);
$id = $this->getAttribute('id');
$this->_persistantFreeze = true;
$this->_type = 'html_editor';
$editor = new CkEditor();
if ($editor) {
$this->editor = $editor;
$this->editor->setTextareaId($id);
$this->editor->setName($name);
$this->editor->processConfig($config);
}
}
/**
* Return the HTML editor in HTML.
*
* @return string
*/
public function toHtml()
{
if ($this->editor) {
if ($this->editor->getConfigAttribute('fullPage')) {
$value = $this->getValue();
if (strlen(trim($value)) == 0) {
// TODO: To be considered whether here to add
// language and character set declarations.
$value = '<!DOCTYPE html><html><head><title></title></head><body></body></html>';
$this->setValue($value);
}
}
}
if ($this->isFrozen()) {
return $this->getFrozenHtml();
} else {
$styleCss = $this->editor->getConfigAttribute('style');
$style = false;
if ($styleCss) {
$style = true;
}
return $this->buildEditor($style);
}
}
/**
* Returns the html area content in HTML.
*
* @return string
*/
public function getFrozenHtml()
{
return Security::remove_XSS($this->getValue());
}
/**
* @param bool $style
*
* @return string
*/
public function buildEditor($style = false)
{
$result = '';
if ($this->editor) {
$value = $this->getCleanValue();
$this->editor->setName($this->getName());
if ($style === true) {
$result = $this->editor->createHtmlStyle($value);
} else {
$result = $this->editor->createHtml($value);
}
}
return $result;
}
public function getValue(): ?string
{
return RemoveOnAttributes::filter($this->_value);
}
}

View File

@@ -0,0 +1,31 @@
<?php
/* For licensing terms, see /license.txt */
/**
* InternalUrl element (URL without the domain as prefix).
*
* Class InternalUrl
*/
class InternalUrl extends HTML_QuickForm_text
{
/**
* InternalUrl constructor.
*
* @param string $elementName
* @param string $elementLabel
* @param array $attributes
*/
public function __construct($elementName = null, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
$attributes['type'] = 'text';
$attributes['class'] = 'form-control';
parent::__construct($elementName, $elementLabel, $attributes);
$this->setType('text');
}
}

View File

@@ -0,0 +1,28 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Number element.
*
* Class Number
*/
class Number extends HTML_QuickForm_text
{
/**
* @param string $elementName
* @param string $elementLabel
* @param array $attributes
*/
public function __construct($elementName = null, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
$attributes['type'] = 'number';
parent::__construct($elementName, $elementLabel, $attributes);
$this->_appendName = true;
$this->setType('number');
}
}

View File

@@ -0,0 +1,212 @@
<?php
/* For licensing terms, see /license.txt */
/**
* A drop down list with all languages to use with QuickForm.
*/
class SelectAjax extends HTML_QuickForm_select
{
/**
* {@inheritdoc}
*/
public function __construct($elementName, $elementLabel = '', $options = null, $attributes = null)
{
parent::__construct($elementName, $elementLabel, $options, $attributes);
}
/**
* The ajax call must contain an array of id and text.
*
* @return string
*/
public function toHtml()
{
$iso = api_get_language_isocode(api_get_interface_language());
$dropdownParent = $this->getAttribute('dropdownParent');
$dropdownParentCondition = $dropdownParent ? "dropdownParent: '$dropdownParent'," : '';
$formatResult = $this->getAttribute('formatResult');
$formatSelection = $this->getAttribute('formatSelection');
$formatCondition = '';
if (!empty($formatResult)) {
$formatCondition .= ',
templateResult : '.$formatResult;
}
if (!empty($formatSelection)) {
$formatCondition .= ',
templateSelection : '.$formatSelection;
}
$width = 'element';
$givenWidth = '100%';
if (!empty($givenWidth)) {
$width = $givenWidth;
}
//Get the minimumInputLength for select2
$minimumInputLength = $this->getAttribute('minimumInputLength') > 3 ?
$this->getAttribute('minimumInputLength') : 3
;
$plHolder = $this->getAttribute('placeholder');
if (empty($plHolder)) {
$plHolder = preg_replace("/'/", "\\'", get_lang('SelectAnOption'));
}
$id = $this->getAttribute('id');
if (empty($id)) {
$id = $this->getAttribute('name');
$this->setAttribute('id', $id);
}
// URL must return ajax json_encode arrady [items => [['id'=>1, 'text'='content']]
$url = $this->getAttribute('url');
if (!$url) {
$url = $this->getAttribute('url_function');
} else {
$url = "'$url'";
}
$tagsAttr = $this->getAttribute('tags');
$multipleAttr = $this->getAttribute('multiple');
$tags = !empty($tagsAttr) ? (bool) $tagsAttr : false;
$tags = $tags ? 'true' : 'false';
$multiple = !empty($multipleAttr) ? (bool) $multipleAttr : false;
$multiple = $multiple ? 'true' : 'false';
$max = $this->getAttribute('maximumSelectionLength');
$max = !empty($max) ? "maximumSelectionLength: $max, " : '';
// wait XX milliseconds before triggering the request
$delay = ((int) $this->getAttribute('delay')) ?: 1000;
$html = <<<JS
<script>
$(function(){
$('#{$this->getAttribute('id')}').select2({
language: '$iso',
placeholder: '$plHolder',
allowClear: true,
width: '$width',
minimumInputLength: '$minimumInputLength',
tags: $tags,
$dropdownParentCondition
ajax: {
url: $url,
delay: $delay,
dataType: 'json',
data: function(params) {
return {
q: params.term, // search term
page_limit: 10,
};
},
processResults: function (data, page) {
// Parse the results into the format expected by Select2
if (data.items) {
return {
results: data.items
};
}
return {
results: ''
};
}
}
$formatCondition
});
});
</script>
JS;
$this->removeAttribute('formatResult');
$this->removeAttribute('formatSelection');
$this->removeAttribute('minimumInputLength');
$this->removeAttribute('maximumSelectionLength');
$this->removeAttribute('tags');
$this->removeAttribute('placeholder');
$this->removeAttribute('class');
$this->removeAttribute('url');
$this->removeAttribute('url_function');
$this->removeAttribute('dropdownParent');
$this->setAttribute('style', 'width: 100%;');
return parent::toHtml().$html;
}
/**
* We check the options and return only the values that _could_ have been
* selected. We also return a scalar value if select is not "multiple".
*/
public function exportValue(&$submitValues, $assoc = false)
{
$value = $this->_findValue($submitValues);
if (!$value) {
$value = '';
}
return $this->_prepareValue($value, $assoc);
}
public static function templateResultForUsersInCourse(): string
{
return "function (state) {
if (state.loading) {
return state.text;
}
var \$container = \$(
'<div class=\"select2-result-user clearfix\">' +
'<div class=\"select2-result-user__avatar pull-left\">' +
'<img>' +
'</div>' +
'<div class=\"select2-result-user__info pull-left\">' +
'<div class=\"select2-result-user__name\"></div>' +
'<div class=\"select2-result-user__username small\"></div>' +
'</div>' +
'</div>'
);
\$container.find('.select2-result-user__avatar img')
.prop({ 'src': state.avatarUrl, 'alt': state.username })
.css({ 'width': '40px', 'height': '40px' });
\$container.find('.select2-result-user__info').css({ 'paddingLeft': '6px' });
\$container.find('.select2-result-user__name').text(state.completeName);
\$container.find('.select2-result-user__username').text(state.username);
return \$container;
}";
}
public static function templateSelectionForUsersInCourse(): string
{
return "function (state) {
if (!state.id) {
return state.text;
}
if (!state.avatarUrl) {
var avatarUrl = $(state.element).data('avatarurl');
var username = $(state.element).data('username');
state.avatarUrl = avatarUrl;
state.username = username;
state.completeName = state.text;
}
var \$container = \$('<span><img> ' + state.completeName + '</span>');
\$container.find('img')
.prop({ 'src': state.avatarUrl, 'alt': state.username })
.css({ 'width': '20px', 'height': '20px' });
return \$container;
}";
}
}

View File

@@ -0,0 +1,38 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class SelectLanguage
* A dropdownlist with all languages to use with QuickForm.
*/
class SelectLanguage extends HTML_QuickForm_select
{
/**
* Class constructor.
*/
public function __construct(
$elementName = null,
$elementLabel = null,
$options = [],
$attributes = []
) {
parent::__construct($elementName, $elementLabel, $options, $attributes);
$default = isset($attributes['set_custom_default']) ? $attributes['set_custom_default'] : false;
// Get all languages
$languages = api_get_languages();
foreach ($languages['name'] as $index => $name) {
if (!empty($default)) {
$defaultValue = $default;
} else {
$defaultValue = api_get_setting('platformLanguage');
}
if ($languages['folder'][$index] == $defaultValue) {
$this->addOption($name, $languages['folder'][$index], ['selected' => 'selected']);
} else {
$this->addOption($name, $languages['folder'][$index]);
}
}
}
}

View File

@@ -0,0 +1,28 @@
<?php
/* For licensing terms, see /license.txt */
/**
* A dropdownlist with all themes to use with QuickForm.
*/
class SelectTheme extends HTML_QuickForm_select
{
/**
* Class constructor.
*/
public function __construct(
$elementName = null,
$elementLabel = null,
$options = null,
$attributes = null
) {
parent::__construct($elementName, $elementLabel, $options, $attributes);
// Get all languages
$themes = api_get_themes();
$this->_options = [];
$this->_values = [];
$this->addOption('--', ''); // no theme select
foreach ($themes as $themeValue => $themeName) {
$this->addOption($themeName, $themeValue);
}
}
}

View File

@@ -0,0 +1,31 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Url element.
*
* Class Url
*/
class Url extends HTML_QuickForm_text
{
/**
* Url constructor.
*
* @param string $elementName
* @param string $elementLabel
* @param array $attributes
*/
public function __construct($elementName = null, $elementLabel = null, $attributes = null)
{
if (!isset($attributes['id'])) {
$attributes['id'] = $elementName;
}
$attributes['type'] = 'url';
$attributes['class'] = 'form-control';
parent::__construct($elementName, $elementLabel, $attributes);
$this->setType('url');
}
}

View File

@@ -0,0 +1,87 @@
<?php
/* For licensing terms, see /license.txt */
use Chamilo\UserBundle\Entity\User;
/**
* Class UserAvatar
* FormValidator element to add an user avatar wrapping a hidden input with its user ID
* Is necessary set an instance of Chamilo\UserBundle\Entity\User as value. The exported value is the user ID.
*/
class UserAvatar extends HTML_QuickForm_input
{
/** @var User */
private $user = null;
private $imageSize = 'small';
private $subTitle = '';
/**
* UserAvatar constructor.
*
* @param string $name
* @param string $label
* @param array $attributes
*/
public function __construct($name, $label, $attributes = [])
{
if (isset($attributes['image_size'])) {
$this->imageSize = $attributes['image_size'];
unset($attributes['image_size']);
}
if (isset($attributes['sub_title'])) {
$this->subTitle = $attributes['sub_title'];
unset($attributes['sub_title']);
}
parent::__construct($name, $label, $attributes);
$this->setType('hidden');
}
/**
* {@inheritdoc}
*/
public function setValue($value)
{
$this->user = !is_a($value, 'Chamilo\UserBundle\Entity\User')
? UserManager::getManager()->find($value)
: $value;
parent::setValue($this->user->getId());
}
/**
* {@inheritdoc}
*/
public function toHtml()
{
if (!$this->user) {
return '';
}
$userInfo = api_get_user_info($this->user->getId());
$userPicture = isset($userInfo["avatar_{$this->imageSize}"])
? $userInfo["avatar_{$this->imageSize}"]
: $userInfo["avatar"];
if (!$this->subTitle) {
$this->subTitle = $this->user->getUsername();
}
$html = parent::toHtml();
$html .= '
<div class="media">
<div class="media-left">
<img src="'.$userPicture.'" alt="'.UserManager::formatUserFullName($this->user).'">
</div>
<div class="media-body">
<h4 class="media-heading">'.UserManager::formatUserFullName($this->user).'</h4>
'.$this->subTitle.'
</div>
</div>
';
return $html;
}
}

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
<?php
/* For licensing terms, see /license.txt */
/**
* QuickForm rule to compare 2 dates.
*/
class HTML_QuickForm_Rule_CompareDateTimeText extends HTML_QuickForm_Rule_Compare
{
/**
* Validate 2 dates.
*
* @param string $operator The operator to use (default '==')
*
* @return bool True if the 2 given dates match the operator
*/
public function validate($values, $operator = null)
{
$datetime1 = api_strtotime($values[0]);
$datetime2 = api_strtotime($values[1]);
if (strpos($operator, 'allow_empty') !== false) {
$operator = str_replace('allow_empty', '', $operator);
if (!$datetime2 || empty($datetime2)) {
return true;
}
}
$result = parent::validate([$datetime1, $datetime2], $operator);
return $result;
}
}

View File

@@ -0,0 +1,35 @@
<?php
/* For licensing terms, see /license.txt */
/**
* QuickForm rule to check a date.
*/
class HTML_QuickForm_Compare_Fields extends HTML_QuickForm_Rule_Compare
{
/**
* Function to check an array of fields.
*
* @param array of field names
* @param string operator ==, >=, etc
* @param string the value to compare
*
* @return bool True if date is valid
*/
public function validate($values = [], $operator_and_max_value = null)
{
if (is_array($values) && !empty($values) && !empty($operator_and_max_value)) {
$final_value = 0;
foreach ($values as $value) {
$value = (float) $value;
$final_value += $value;
}
$params = explode('@', $operator_and_max_value);
$operator = $params[0];
$max_value = $params[1];
return parent::validate([$final_value, $max_value], $operator);
}
return false;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/* For licensing terms, see /license.txt */
/** @author Bart Mollet, Julio Montoya */
/**
* Class HTML_QuickForm_Rule_Date.
*/
class HTML_QuickForm_Rule_Date extends HTML_QuickForm_Rule
{
/**
* Check a date.
*
* @see HTML_QuickForm_Rule
*
* @param string $date example 2014-04-30
* @param array $options
*
* @return bool True if date is valid
*/
public function validate($date, $options)
{
return api_is_valid_date($date, 'Y-m-d');
}
}

View File

@@ -0,0 +1,23 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class HTML_QuickForm_Rule_DateCompare.
*
* @author Julio Montoya
*/
class HTML_QuickForm_Rule_DateCompare extends HTML_QuickForm_Rule_Compare
{
/**
* Validate 2 dates.
*
* @param array $values array with the 2 dates
* @param $operator
*
* @return bool true if the 2 given dates match the operator
*/
public function validate($values, $operator = null)
{
return api_strtotime($values[0]) < api_strtotime($values[1]);
}
}

View File

@@ -0,0 +1,25 @@
<?php
/* For licensing terms, see /license.txt */
/**
* Class DateTimeRule.
*
* @author Julio Montoya
*/
class DateTimeRule extends HTML_QuickForm_Rule
{
/**
* Check a date.
*
* @param string $date example 2014-04-30 18:00
* @param array $options
*
* @return bool True if date is valid
*
* @see HTML_QuickForm_Rule
*/
public function validate($date, $options)
{
return api_is_valid_date($date, 'Y-m-d H:i');
}
}

View File

@@ -0,0 +1,26 @@
<?php
/* For licensing terms, see /license.txt */
/** @author Julio Montoya */
/**
* Class HTML_QuickForm_Rule_FileName.
*/
class HTML_QuickForm_Rule_FileName extends HTML_QuickForm_Rule
{
/**
* @param $value array Uploaded file info (from $_FILES)
* @param null $options
*
* @return bool
*/
public function validate($value, $options = null)
{
if ((isset($elementValue['error']) && $elementValue['error'] == 0) ||
(!empty($elementValue['tmp_name']) && $elementValue['tmp_name'] != 'none')) {
return is_uploaded_file($elementValue['tmp_name']);
} else {
return false;
}
}
}

Some files were not shown because too many files have changed in this diff Show More