Add setting 'allow_career_diagram' see BT#12861

Allow careers to have a diagram based in a CSV file.

Requires DB change:

alter table extra_field_values modify column value longtext null;
pull/2487/head
jmontoyaa 9 years ago
parent e8aba6f257
commit 5fac7f1ff0
  1. 95
      main/admin/career_diagram.php
  2. 7
      main/admin/careers.php
  3. 291
      main/cron/import_csv.php
  4. 3
      main/inc/ajax/sequence.ajax.php
  5. 147
      main/inc/lib/career.lib.php
  6. 8
      main/inc/lib/display.lib.php
  7. 6
      main/inc/lib/extra_field.lib.php
  8. 6
      main/install/configuration.dist.php
  9. 1
      src/Chamilo/CoreBundle/Entity/ExtraField.php

@ -0,0 +1,95 @@
<?php
/* For licensing terms, see /license.txt */
use ChamiloSession as Session;
/**
* @package chamilo.admin
*/
/*
*
* Requires extra_field_values.value to be longtext to save diagram:
*
update extra_field_values set created_at = null where created_at = '0000-00-00 00:00:00';
update extra_field_values set updated_at = null where updated_at = '0000-00-00 00:00:00';
UPDATE extra_field_values SET created_at = NULL WHERE CAST(created_at AS CHAR(20)) = '0000-00-00 00:00:00';
UPDATE extra_field_values SET updated_at = NULL WHERE CAST(updated_at AS CHAR(20)) = '0000-00-00 00:00:00';
alter table extra_field_values modify column value longtext null;
*/
$cidReset = true;
require_once __DIR__.'/../inc/global.inc.php';
if (api_get_configuration_value('allow_career_diagram') == false) {
api_not_allowed(true);
}
$this_section = SECTION_PLATFORM_ADMIN;
api_protect_admin_script();
$htmlHeadXtra[] = api_get_js('jsplumb2.js');
$careerId = isset($_GET['id']) ? $_GET['id'] : 0;
if (empty($careerId)) {
api_not_allowed(true);
}
// setting breadcrumbs
$interbreadcrumb[] = array(
'url' => 'index.php',
'name' => get_lang('PlatformAdmin'),
);
$interbreadcrumb[] = array(
'url' => 'career_dashboard.php',
'name' => get_lang('CareersAndPromotions'),
);
$interbreadcrumb[] = array(
'url' => 'careers.php',
'name' => get_lang('Careers'),
);
$action = isset($_GET['action']) ? $_GET['action'] : null;
$check = Security::check_token('request');
$token = Security::get_token();
if ($action == 'add') {
$interbreadcrumb[] = array('url' => 'careers.php', 'name' => get_lang('Careers'));
$tool_name = get_lang('Add');
} elseif ($action == 'edit') {
$interbreadcrumb[] = array('url' => 'careers.php', 'name' => get_lang('Careers'));
$interbreadcrumb[] = array('url' => '#', 'name' => get_lang('Edit'));
$tool_name = get_lang('Edit');
} else {
$tool_name = get_lang('Careers');
}
$career = new Career();
$careerInfo = $career->get($careerId);
if (empty($careerInfo)) {
api_not_allowed(true);
}
$extraFieldValue = new ExtraFieldValue('career');
$item = $extraFieldValue->get_values_by_handler_and_field_variable(
$careerId,
'career_diagram',
false,
false,
false
);
if (!empty($item) && isset($item['value']) && !empty($item['value'])) {
$graph = unserialize($item['value']);
$html = Display::page_header($careerInfo['name']);
$html .= Career::renderDiagram($graph);
$tpl = new Template('');
$tpl->assign('content', $html);
$tpl->display_one_col_template();
}

@ -81,9 +81,16 @@ $extra_params['autowidth'] = 'true';
//height auto
$extra_params['height'] = 'auto';
$diagramLink = '';
$allow = api_get_configuration_value('allow_career_diagram');
if ($allow) {
$diagramLink = '<a href="'.api_get_path(WEB_CODE_PATH).'admin/career_diagram.php?id=\'+options.rowId+\'">'.get_lang('Diagram').'</a>';
}
//With this function we can add actions to the jgrid (edit, delete, etc)
$action_links = 'function action_formatter(cellvalue, options, rowObject) {
return \'<a href="?action=edit&id=\'+options.rowId+\'">'.Display::return_icon('edit.png', get_lang('Edit'), '', ICON_SIZE_SMALL).'</a>'.
$diagramLink.
'&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"), ENT_QUOTES))."\'".')) return false;" href="?sec_token='.$token.'&action=copy&id=\'+options.rowId+\'">'.Display::return_icon('copy.png', get_lang('Copy'), '', ICON_SIZE_SMALL).'</a>'.
'&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"), ENT_QUOTES))."\'".')) return false;" href="?sec_token='.$token.'&action=delete&id=\'+options.rowId+\'">'.Display::return_icon('delete.png', get_lang('Delete'), '', ICON_SIZE_SMALL).'</a>'.
'\';

@ -4,6 +4,13 @@
use Chamilo\CourseBundle\Entity\CCalendarEvent;
use Chamilo\CourseBundle\Entity\CItemProperty;
use Chamilo\PluginBundle\Entity\StudentFollowUp\CarePost;
use Fhaculty\Graph\Graph;
use Graphp\GraphViz\GraphViz;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\NativeMailerHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\BufferHandler;
if (PHP_SAPI != 'cli') {
die('Run this script through the command line or comment this line in the code');
@ -33,6 +40,8 @@ class ImportCsv
'course' => 'external_course_id',
'user' => 'external_user_id',
'calendar_event' => 'external_calendar_event_id',
'career' => 'external_career_id',
'career_diagram' => 'career_diagram',
);
public $defaultAdminId = 1;
public $defaultSessionVisibility = 1;
@ -137,10 +146,13 @@ class ImportCsv
$method = 'importUnsubsessionsExtidStatic';
}
if ($method == 'importCareersdiagram') {
$method = 'importCareersDiagram';
}
if ($method == 'importSubsessionsextidStatic') {
$method = 'importSubscribeUserToCourseSessionExtStatic';
}
if (method_exists($this, $method)) {
if ((
$method == 'importSubscribeStatic' ||
@ -186,6 +198,8 @@ class ImportCsv
'courseinsert-static',
'unsubscribe-static',
'care',
'careers',
'careersdiagram'
);
foreach ($sections as $section) {
@ -291,6 +305,25 @@ class ImportCsv
'display_text' => 'External calendar event id',
)
);
$extraField = new ExtraField('career');
$extraField->save(
array(
'visible_to_self' => 1,
'field_type' => ExtraField::FIELD_TYPE_TEXT,
'variable' => $this->extraFieldIdNameList['career'],
'display_text' => 'External career id',
)
);
$extraField->save(
array(
'visible_to_self' => 1,
'field_type' => ExtraField::FIELD_TYPE_TEXTAREA,
'variable' => $this->extraFieldIdNameList['career_diagram'],
'display_text' => 'Career diagram',
)
);
}
/**
@ -2192,6 +2225,255 @@ class ImportCsv
return $date;
}
/**
* @param $file
* @param bool $moveFile
* @param array $teacherBackup
* @param array $groupBackup
* @return bool
*/
private function importCareers(
$file,
$moveFile = false,
&$teacherBackup = array(),
&$groupBackup = array()
) {
$data = Import::csv_reader($file);
if (!empty($data)) {
$this->logger->addInfo(count($data)." records found.");
$extraFieldValue = new ExtraFieldValue('career');
$extraFieldName = $this->extraFieldIdNameList['career'];
$externalEventId = null;
$extraField = new ExtraField('career');
$extraFieldInfo = $extraField->get_handler_field_info_by_field_variable(
$extraFieldName
);
if (empty($extraFieldInfo)) {
return false;
}
foreach ($data as $row) {
foreach ($row as $key => $value) {
$key = (string)trim($key);
// Remove utf8 bom
$key = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $key);
$row[$key] = $value;
}
$itemId = $row['CareerId'];
$item = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
$extraFieldName,
$itemId,
false,
false,
false
);
$career = new Career();
if (empty($item)) {
$params = [
'status' => 1,
'name' => $row['CareerName']
];
$careerId = $career->save($params);
if ($careerId) {
$params = [
'item_id' => $careerId,
'extra_'.$extraFieldName => $itemId,
];
$extraFieldValue->saveFieldValues($params);
}
} else {
if (isset($item['item_id'])) {
$params = [
'id' => $item['item_id'],
'name' => $row['CareerName']
];
$career->update($params);
}
}
}
}
}
/**
* @param $file
* @param bool $moveFile
* @param array $teacherBackup
* @param array $groupBackup
*/
private function importCareersDiagram(
$file,
$moveFile = false,
&$teacherBackup = array(),
&$groupBackup = array()
) {
$data = Import::csv_reader($file);
$extraFieldValue = new ExtraFieldValue('career');
$extraFieldName = $this->extraFieldIdNameList['career'];
$externalEventId = null;
$extraField = new ExtraField('career');
$extraFieldInfo = $extraField->get_handler_field_info_by_field_variable(
$extraFieldName
);
$careerDiagramExtraFieldName = $this->extraFieldIdNameList['career_diagram'];
$extraFieldDiagramInfo = $extraField->get_handler_field_info_by_field_variable(
$careerDiagramExtraFieldName
);
if (empty($extraFieldInfo) || empty($extraFieldDiagramInfo)) {
return false;
}
if (!empty($data)) {
$this->logger->addInfo(count($data)." records found.");
$values = [];
foreach ($data as $row) {
foreach ($row as $key => $value) {
$key = (string) trim($key);
// Remove utf8 bom
$key = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $key);
$row[$key] = $value;
}
$values[$row['Column']][] = $row;
}
$careerList = [];
$careerNameList = [];
ksort($values);
$careerChamiloIdList = [];
// 1. First create all items
foreach ($values as $column => $rowList) {
foreach ($rowList as $row) {
$careerId = $row['CareerId'];
$item = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
$extraFieldName,
$careerId,
false,
false,
false
);
$chamiloCareerName = '';
if (empty($item)) {
$this->logger->addInfo("Career not found: $careerId");
continue;
} else {
if (isset($item['item_id'])) {
$careerChamiloId = $item['item_id'];
$career = new Career();
$career = $career->find($careerChamiloId);
$chamiloCareerName = $career['name'];
$careerNameList[$careerId] = $chamiloCareerName;
$careerChamiloIdList[$careerId] = $careerChamiloId;
} else {
continue;
}
}
if (empty($chamiloCareerName)) {
$this->logger->addInfo("Career not found: $careerId");
continue;
}
if (isset($careerList[$careerId])) {
$graph = $careerList[$careerId];
} else {
$graph = new Graph($careerId);
$graph->setAttribute('graphviz.graph.rankdir', 'LR');
$careerList[$careerId] = $graph;
}
$currentCourseId = (int)$row['CourseId'];
$name = $row['CourseName'];
$hasColor = $row['HasColor'];
$notes = $row['Notes'];
if ($graph->hasVertex($currentCourseId)) {
// Avoid double insertion
continue;
} else {
$current = $graph->createVertex($currentCourseId);
$current->setAttribute('graphviz.label', $name);
$current->setAttribute('HasColor', $hasColor);
$current->setAttribute('Notes', $notes);
//$current->setAttribute('graphviz.color', 'blue');
$current->setAttribute('graphviz.shape', 'box');
$current->setGroup($column);
}
}
}
// 2. Create connections
// $column start with 1 (depending in Column row)
foreach ($values as $column => $rowList) {
foreach ($rowList as $row) {
$careerId = $row['CareerId'];
if (isset($careerList[$careerId])) {
$graph = $careerList[$careerId];
} else {
continue;
}
$currentCourseId = (int) $row['CourseId'];
if ($graph->hasVertex($currentCourseId)) {
$current = $graph->getVertex($currentCourseId);
} else {
continue;
}
if (isset($row['DependedOn']) && !empty($row['DependedOn'])) {
$parentList = explode(',', $row['DependedOn']);
foreach ($parentList as $parentId) {
$parentId = (int) $parentId;
echo $parentId.PHP_EOL;
if ($graph->hasVertex($parentId)) {
$parent = $graph->getVertex($parentId);
/*$parent->setAttribute('graphviz.color', 'red');
$parent->setAttribute('graphviz.label', $name);
$parent->setAttribute('graphviz.shape', 'square');*/
$parent->createEdgeTo($current);
}
}
}
}
}
// 2. Transform the graph into a jsplumb graph
$html = '<style>
.window .panel-title {
font-size: 12px;
}
.window {
font-size: 12px;
}
</style>';
/** @var Graph $graph */
foreach ($careerList as $id => $graph) {
if ($id != 631) {
//continue;
}
if (isset($careerChamiloIdList[$id])) {
$params = [
'item_id' => $careerChamiloIdList[$id],
'extra_'.$careerDiagramExtraFieldName => serialize($graph),
'extra_'.$extraFieldName => $id,
];
$extraFieldValue->saveFieldValues($params);
}
}
}
}
/**
* @param string $file
* @param bool $moveFile
@ -2451,15 +2733,8 @@ class ImportCsv
Database::query($sql);
}
}
}
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\NativeMailerHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\BufferHandler;
$logger = new Logger('cron');
$emails = isset($_configuration['cron_notification_mails']) ? $_configuration['cron_notification_mails'] : null;

@ -5,6 +5,7 @@ use Chamilo\CoreBundle\Entity\Sequence;
use Chamilo\CoreBundle\Entity\SequenceResource;
use Fhaculty\Graph\Graph;
use Fhaculty\Graph\Vertex;
use Graphp\GraphViz\GraphViz;
/**
* Responses to AJAX calls
@ -37,7 +38,7 @@ switch ($action) {
if ($sequence->hasGraph()) {
$graph = $sequence->getUnSerializeGraph();
$graph->setAttribute('graphviz.node.fontname', 'arial');
$graphviz = new \Graphp\GraphViz\GraphViz();
$graphviz = new GraphViz();
$graphImage = '';
try {
$graphImage = $graphviz->createImageHtml($graph);

@ -275,4 +275,151 @@ class Career extends Model
return parent::update($params);
}
/**
* @param \Fhaculty\Graph\Graph $graph
*
* @return string
*/
public static function renderDiagram($graph)
{
if (!($graph instanceof \Fhaculty\Graph\Graph)) {
return '';
}
$debug = false;
$maxColumn = 0;
foreach ($graph->getVertices() as $vertex) {
$groupId = (int) $vertex->getGroup();
if ($groupId > $maxColumn) {
$maxColumn = $groupId;
}
}
$width = 80 / $maxColumn;
//var_dump($maxColumn);
//$width = 100;
//$groupWidth = $width + 30;
$defaultSpace = 40;
$group = 0;
/** @var \Fhaculty\Graph\Vertex $vertex */
$counter = 0;
$html = '';
foreach ($graph->getVertices() as $vertex) {
$id = $vertex->getId();
$windowId = "window_$id";
$groupId = $vertex->getGroup();
$groupJsId = "group_$groupId";
if ($group != $vertex->getGroup()) {
if ($group > 0) {
$counter = 0;
$html .= '</div>'.PHP_EOL;
}
$left = ($defaultSpace).'px';
if ($group == 0) {
$left = 0;
}
$html .= PHP_EOL.'<div id="'.$groupJsId.'" style="padding:15px;border-style:solid;float:left; margin-left:'.$left.'; width:'.$width.'%">';
}
if ($debug) {
echo ('->>>>>>>'.$vertex->getAttribute('graphviz.label')).' - '.$vertex->getGroup().PHP_EOL;
}
$group = $vertex->getGroup();
$content = $vertex->getAttribute('Notes');
$content .= '<div class="pull-right">['.$id.']</div>';
if ($debug) {
echo ('entering vertices: ').PHP_EOL;
}
/** @var \Fhaculty\Graph\Vertex $vertexTo */
foreach ($vertex->getVerticesEdgeTo() as $vertexTo) {
$childId = $vertexTo->getId();
if ($id == $childId) {
continue;
}
$childId = "window_$childId";
$childGroupId = $vertexTo->getGroup();
$childJsGroupId = "group_$childGroupId";
if ($debug) {
echo ($vertexTo->getAttribute('graphviz.label')).PHP_EOL;
}
if (($vertexTo->getGroup() - $groupId) == 1) {
$content .= self::createConnection($windowId, $childId, ['Left', 'Right']);
} else {
if ($childGroupId > $groupId) {
$content .= self::createConnection(
$groupJsId,
$childJsGroupId
);
} else {
$anchor = ['Left', 'Right'];
if ($childGroupId == 1) {
$anchor = ['Right', 'Left'];
}
$content .= self::createConnection(
$childJsGroupId,
$groupJsId,
$anchor
);
}
}
}
$counter++;
$color = '';
if ($vertex->getAttribute('HasColor') == 1) {
$color = 'danger';
}
$html .= PHP_EOL.'<div id="'.$windowId.'" class="window" style="float:left; width:100%; " >';
$html .= Display::panel(
$content,
$vertex->getAttribute('graphviz.label'),
null,
$color,
null
//$windowId
);
$html .= '</div>';
}
$html .= '</div>'.PHP_EOL;
return $html;
}
/**
* @param string $source
* @param string $target
* @param array $anchor
* @return string
*/
public static function createConnection($source, $target, $anchor = [])
{
if (empty($anchor)) {
// Default
$anchor = ['Bottom', 'Right'];
}
$anchor = implode('","', $anchor);
$html = '<script> jsPlumb.ready(function() { ';
$html .= 'jsPlumb.connect({
source:"'.$source.'",
target:"'.$target.'",
endpoint:[ "Rectangle", { width:1, height:1 }],
connector: ["Flowchart"],
anchor: ["'.$anchor.'"],
overlays: [
[ "Arrow", { location:0.9 } ],
],
});';
$html .= '});</script>'.PHP_EOL;
return $html;
}
}

@ -2276,18 +2276,22 @@ class Display
* @param string $footer
* @param string $style primary|success|info|warning|danger
* @param string $extra
* @param string $id
*
* @return string
*/
public static function panel($content, $title = '', $footer = '', $style = '', $extra = '')
public static function panel($content, $title = '', $footer = '', $style = '', $extra = '', $id = '')
{
$title = !empty($title) ? '<div class="panel-heading"><h3 class="panel-title">'.$title.'</h3>'.$extra.'</div>' : '';
$footer = !empty($footer) ? '<div class="panel-footer ">'.$footer.'</div>' : '';
$styles = ['primary', 'success', 'info', 'warning', 'danger'];
$style = !in_array($style, $styles) ? 'default' : $style;
if (!empty($id)) {
$id = " id = $id ";
}
return '
<div class="panel panel-'.$style.'">
<div '.$id.' class="panel panel-'.$style.'">
'.$title.'
'.self::contentPanel($content).'
'.$footer.'

@ -129,6 +129,9 @@ class ExtraField extends Model
case 'work':
$this->extraFieldType = EntityExtraField::WORK_FIELD_TYPE;
break;
case 'career':
$this->extraFieldType = EntityExtraField::CAREER_FIELD_TYPE;
break;
}
$this->pageUrl = 'extra_fields.php?type='.$this->type;
@ -158,7 +161,8 @@ class ExtraField extends Model
'calendar_event',
'lp_item',
'skill',
'work'
'work',
'career'
);
}

@ -451,4 +451,8 @@ $_configuration['agenda_legend'] = [
// HTTP headers security section ends here
// Add answered_at field in table survey_invitation
//$_configuration['survey_answered_at_field'] = 'false';
//$_configuration['survey_answered_at_field'] = false;
// Allow career diagram
//$_configuration['allow_career_diagram'] = false;

@ -26,6 +26,7 @@ class ExtraField extends BaseAttribute
const LP_ITEM_FIELD_TYPE = 7;
const SKILL_FIELD_TYPE = 8;
const WORK_FIELD_TYPE = 9;
const CAREER_FIELD_TYPE = 10;
/**
* @var integer

Loading…
Cancel
Save