upgrade
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Options -Indexes
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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').' '.$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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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']);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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> </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;
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
@@ -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');
|
||||
@@ -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');
|
||||
@@ -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'];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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';
|
||||
}
|
||||
@@ -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
@@ -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('-');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}";
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user