Commit 4ef75593 authored by Yuliya Bozhko's avatar Yuliya Bozhko Committed by Aaron Wells

Generate SVG image preview for page layouts (Bug #1392096)

Change-Id: I32faf161d9ef7c9cd020134a08998ce48acc7060
Signed-off-by: 's avatarYuliya Bozhko <yuliya.bozhko@totaralms.com>
parent b5b899b1
......@@ -1274,11 +1274,8 @@ class PluginImportLeap extends PluginImport {
db_rollback();
return false;
}
else {
$viewobj->updatecustomlayoutpreview($layoutdata);
$layout = (object) array('id' => $layoutresult['layoutid']);
db_commit();
}
$layout = (object) array('id' => $layoutresult['layoutid']);
db_commit();
}
}
......
......@@ -10,9 +10,9 @@
var currentcollayout = $('#customrow_' + numrows).find('#selectcollayoutrow_' + numrows).val();
$(newrow).find('.customrowtitle').html('<strong>' + get_string('rownr', numrows + 1) + '</strong>');
$(newrow).attr('id', 'customrow_' + (numrows + 1));
$(newrow).find('#selectnumcolsrow_' + numrows).attr('value', currentncols);
$(newrow).find('#selectnumcolsrow_' + numrows).val(currentncols);
$(newrow).find('#selectnumcolsrow_' + numrows).attr('id', 'selectnumcolsrow_' + (numrows + 1));
$(newrow).find('#selectcollayoutrow_' + numrows).attr('value', currentcollayout);
$(newrow).find('#selectcollayoutrow_' + numrows).val(currentcollayout);
$(newrow).find('#selectcollayoutrow_' + numrows).attr('id', 'selectcollayoutrow_' + (numrows + 1));
if ((oldremovebutton = $(newrow).find('input')).length != 0) {
oldremovebutton.attr('class', 'removecustomrow_' + (numrows + 1));
......@@ -97,9 +97,7 @@
var id = 'viewlayout_advancedlayoutselect' + unique_timestamp();
$('input', clone).attr('id', id);
$('input', clone).val(layoutid);
$('img', clone).attr('src', config['wwwroot'] + 'thumb.php?type=viewlayout&vl=' + data.data.layoutid);
$('img', clone).attr('alt', data.data.alttext);
$('img', clone).attr('title', data.data.alttext);
$('svg', clone).replaceWith(data.data.layoutpreview);
//insert into appropriate row
var rowcontainer = $('#viewlayout_advancedlayoutselect_row'+numrows);
......@@ -183,7 +181,7 @@
}
function link_thumbs_to_radio_buttons() {
$('div.layoutthumb img').each(function(event) {
$('div.layoutthumb svg').each(function(event) {
$(this).click(function(e) {
$(this).closest('div.layoutoption').children(':radio').attr('checked', 'checked').trigger('click');
$('#viewlayout_layoutselect').val( $(this).closest('div.layoutoption').children(':radio').val() );
......@@ -215,21 +213,7 @@
}
pd['action_updatecustomlayoutpreview_numrows_' + numrows + collayouts] = 1;
sendjsonrequest(config['wwwroot'] + 'view/blocks.json.php', pd, 'POST', function(data) {
var pi = $('<img>').attr({
'src' : data.data.data,
'alt' : data.data.alttext,
'title': data.data.alttext
});
if (data.data.newimage) {
$('#custompreview').html('<p>' + get_string('generatingpreview', 'view') + '</p>');
// delay to allow image to be written to disk
setTimeout(function() {
$('#custompreview').html(pi);
},500);
}
else {
$('#custompreview').html(pi);
}
$('#custompreview').html(data.data);
});
if (typeof formchangemanager !== 'undefined') {
......
......@@ -253,7 +253,6 @@ $string['nrrows'] = array(
'%s row',
'%s rows',
);
$string['generatingpreview'] = 'Generating preview...';
$string['addnewblockhere'] = 'Add new block here';
$string['add'] = 'Add';
......@@ -295,6 +294,7 @@ $string['addarow'] = 'Add a row';
$string['removethisrow'] = 'Remove this row';
$string['columnlayout'] = 'Column layout';
$string['layoutpreview'] = 'Layout preview';
$string['layoutpreviewimage'] = 'Layout preview image';
$string['Help'] = 'Help';
$string['by'] = 'by';
......
......@@ -3851,5 +3851,13 @@ function xmldb_core_upgrade($oldversion=0) {
}
}
if ($oldversion < 2014121200) {
require_once('file.php');
$layoutdir = get_config('dataroot') . 'images/layoutpreviewthumbs';
if (file_exists($layoutdir)) {
rmdirr($layoutdir);
}
}
return $status;
}
......@@ -20,25 +20,18 @@ class LayoutPreviewImage {
private $rows = 1;
private $layout; // contains cols per row data
private $description; // currently not used
private $owner; // currently not used
private $text;
private static $standard_preview_width = 76;
private static $one_row_height = 48;
private static $two_row_height = 23;
private static $three_row_height = 15;
private static $spacer = 3;
public static $destinationfolder = 'images/layoutpreviewthumbs';
/* Constructor.
* @param data containing 'layout' (required) that consists of
* an array of columns per row eg.
* @param data containing 'layout' (required) that consists of an array of columns per row.
* Example:
* array('row1' => '30-30-30',
* 'row2' => '25-25-25-25');
* image manupulation information and place to
* save the resulting image file.
*
* The image manipulation information can be left out and
* the pre-defined defaults will be used.
*/
public function __construct($data = null) {
......@@ -58,109 +51,70 @@ class LayoutPreviewImage {
}
}
/* Generates the preview image (.png) for a custom layout and
* saves it in the specified $destinationfolder within
* the $dataroot directory
/* Generates the preview SVG image.
*
* @return bool true on successful creation and saving of image.
* @return string SVG of the preview image.
*/
public function create_preview() {
global $THEME;
$width = $this->get_preview_width();
$height = $this->get_preview_height();
$im = imagecreate($width,$height); // in pixels
// maximum rows = $maxlayoutrows in View class
$white = imagecolorallocate($im,255,255,255);
$grey1 = imagecolorallocate($im,102,102,102);
$grey2 = imagecolorallocate($im,77,77,77);
$grey3 = $grey1;
$grey4 = $grey2;
$grey5 = $grey1;
$grey6 = $grey2;
$colours = array($grey1, $grey2, $grey3, $grey4, $grey5, $grey6);
$id = uniqid('lid');
$layout = "<svg xmlns=http://www.w3.org/2000/svg role='img' width='{$width}' height='{$height}' aria-labelledby='title{$id} desc{$id}'>";
if (!empty($this->text)) {
$layout .= "<title id='title{$id}' >" . get_string('layoutpreviewimage', 'view') . "</title>";
$layout .= "<desc id='desc{$id}'>" . hsc($this->text) . "</desc>";
}
$x = 0;
$y = 0;
$col_height = $this->get_preview_column_height();
$filename = 'vl-';
$class = true;
foreach ($this->layout as $key => $row) {
$style = 'layout' . (int)$class;
$columns = explode('-', $row);
foreach ($columns as $column) {
$col_width = $this->get_percentage_column_width(count($columns), $column);
imagefilledrectangle($im,$x,$y,$x+$col_width,$y+$col_height,$colours[$key-1]);
$layout .= "<rect x='{$x}' y='{$y}' width='{$col_width}' height='{$col_height}' class='{$style}'/>";
$x += ($col_width + self::$spacer); // increment x val for next col
}
$x = 0;
$y += ($col_height + self::$spacer); // increment y val for next row
$filename .= $row; // build filename
if ($key < count($this->layout)) {
$filename .= '_';
}
}
$filename .= '.png';
$maxsize = get_config('maxuploadsize');
if ($maxsize && filesize($im) > $maxsize) {
return get_string('uploadedfiletoobig');
$class = !$class;
}
$dataroot = get_config('dataroot');
$destination = $dataroot . self::$destinationfolder;
$layout .= '</svg>';
if (!check_dir_exists($destination, true, true)) {
throw new UploadException('Unable to create upload directory for layout preview images');
}
if (self::preview_exists($filename)) {
imagedestroy($im);
return true;
}
if ($madenewimage = imagepng($im, $destination . '/' . $filename) ) {
chmod($destination . '/' . $filename, get_config('filepermissions'));
imagedestroy($im);
return true;
}
imagedestroy($im);
return false;
}
public static function preview_exists($filename) {
return file_exists(self::$destinationfolder . '/' . $filename);
return $layout;
}
private function get_preview_height() {
if ($this->rows == 1) {
return self::$one_row_height;
}
$preview_height = ($this->rows > 2)? $this->rows * self::$three_row_height + ($this->rows-1) * self::$spacer : $this->rows * self::$two_row_height + ($this->rows-1) * self::$spacer;
return $preview_height;
if ($this->rows > 2) {
return $this->rows * self::$three_row_height + ($this->rows - 1) * self::$spacer;
}
return $this->rows * self::$two_row_height + ($this->rows - 1) * self::$spacer;
}
private function get_preview_column_height() {
if ($this->rows == 1) {
return self::$one_row_height;
}
$column_height = ($this->rows > 2)? self::$three_row_height : self::$two_row_height;
return $column_height;
if ($this->rows > 2) {
return self::$three_row_height;
}
return self::$two_row_height;
}
private function get_preview_width() {
return self::$standard_preview_width;
}
private function get_equal_column_widths($numcols) {
return (self::$standard_preview_width - self::$spacer * ($numcols-1)) / $numcols;
}
private function get_percentage_column_width($numcols, $percent) {
return (self::$standard_preview_width - self::$spacer * ($numcols-1)) * $percent/100;
return (self::$standard_preview_width - self::$spacer * ($numcols - 1)) * $percent / 100;
}
}
\ No newline at end of file
}
......@@ -16,7 +16,7 @@ $config = new stdClass();
// See https://wiki.mahara.org/index.php/Developer_Area/Version_Numbering_Policy
// For upgrades on stable branches, increment the version by one. On master, use the date.
$config->version = 2014112700;
$config->version = 2014121200;
$config->series = '15.04';
$config->release = '15.04dev';
$config->minupgradefrom = 2009022600;
......
......@@ -1022,10 +1022,12 @@ class View {
db_commit();
}
/*Return preview image for creation of custom layout
/* Returns preview image for creation of custom layout
*
* @param array
* @return string SVG preview image
*/
public function updatecustomlayoutpreview($values) {
global $THEME;
require_once(get_config('libroot') . 'layoutpreviewimage.php');
$require = array('numrows');
......@@ -1043,7 +1045,6 @@ class View {
}
}
$previewimage = 'vl-';
$alttext = '';
$customlayout = array();
for ($i=0; $i<$numrows; $i++) {
......@@ -1051,44 +1052,32 @@ class View {
$widths = get_field('view_layout_columns', 'widths', 'id', $id);
$hyphenatedwidths = str_replace(',', '-', $widths);
$customlayout[$i+1] = $hyphenatedwidths;
$previewimage .= $hyphenatedwidths;
$alttext .= $hyphenatedwidths;
if ($i != $numrows -1) {
$previewimage .= '_';
if ($i != $numrows - 1) {
$alttext .= ' / ';
}
}
if (LayoutPreviewImage::preview_exists($previewimage)) {
$img = get_config('wwwroot') . 'thumb.php?type=customviewlayout&cvl=' . $previewimage;
$data = array('data' => $img, 'alttext' => $alttext, 'newimage' => 0);
return $data;
}
else {
// generate thumbnail images with GD
$data= array();
$data['layout'] = $customlayout;
$data['description'] = 'test';
$data['owner'] = 1;
// Generate thumbnail images.
$data = array();
$data['layout'] = $customlayout;
$data['text'] = $alttext;
$previewlayoutimage = new LayoutPreviewImage($data);
$newpreviewimage = $previewlayoutimage->create_preview();
$previewlayoutimage = new LayoutPreviewImage($data);
$previewimage = $previewlayoutimage->create_preview();
if ($newpreviewimage) {
$img = get_config('wwwroot') . 'thumb.php?type=customviewlayout&cvl=' . $previewimage;
$data = array('data' => $img, 'alttext' => $alttext, 'newimage' => 1);
return $data;
}
else {
$msg = '<p>' . get_string('previewimagegenerationfailed', 'error') . '</p>';
$data = array('data' => $msg, 'alttext' => $alttext, 'newimage' => 0);
return $data;
}
}
return $previewimage;
}
/*
* Adds custom layout records to database and returns an array
* with layout id and image preview.
*
* @param array
* @return array
*/
public function addcustomlayout($values) {
global $THEME;
require_once(get_config('libroot') . 'layoutpreviewimage.php');
$require = array('numrows');
foreach ($require as $require) {
if (!array_key_exists($require, $values) || empty($values[$require])) {
......@@ -1100,8 +1089,6 @@ class View {
$alttext = '';
$rowscolssql = '';
$rowscols = array();
$resultids = array();
$layoutid = 0;
for ($i=0; $i<$numrows; $i++) {
if (array_key_exists('row'. ($i+1), $values)) {
......@@ -1200,7 +1187,8 @@ class View {
for ($i=0; $i<$numrows; $i++) {
if (array_key_exists(($i+1), $rowscols)) {
$numcols = get_field('view_layout_columns', 'columns', 'id', $rowscols[$i+1]);
$widths = get_field('view_layout_columns', 'widths', 'id', $rowscols[$i+1]);
$structure['layout']['row' . ($i + 1)] = get_string($widths, 'view');
$newrec = insert_record('view_layout_rows_columns', (object) array('viewlayout' => $newlayoutid, 'row' => ($i+1), 'columns' => $rowscols[$i+1]));
if (!$newrec) {
db_rollback();
......@@ -1210,7 +1198,13 @@ class View {
}
db_commit();
$data = array('layoutid' => $newlayoutid, 'newlayout' => 1, 'alttext' => $alttext);
// Generate new custom layout preview.
$structure['text'] = $alttext;
$layoutpreview = new LayoutPreviewImage($structure);
$preview = $layoutpreview->create_preview();
$data = array('layoutid' => $newlayoutid, 'newlayout' => 1, 'layoutpreview' => $preview);
return $data;
}
}
......
......@@ -38,22 +38,15 @@ $output .= '<span class="help" id="basiclayouthelp">'
$output .= '<div id="basiclayoutoptions">';
foreach ($templatedata['basiclayoutoptions'] as $value => $data) {
if (is_array($data)) {
$text = $data['columns'];
$description = (isset($data['description'])) ? $data['description'] : '';
}
else {
$text = $data;
$description = '';
}
$text = Pieform::hsc($text);
$description = (isset($data['description'])) ? $data['description'] : '';
$text = Pieform::hsc($data['columns']);
$output .= '<div class="layoutoption">'
. '<label class="accessible-hidden" for="radiolayout_' . Pieform::hsc($value) . '">' . $text . '</label>'
. '<input type="radio" id="radiolayout_' . Pieform::hsc($value) . '" name="layoutselect"'
. ' value="' . Pieform::hsc($value) . '"'
. (($templatedata['currentlayout'] == $value) ? ' checked="checked"' : '') . '>'
. ($description != '' ? '<div class="radio-description">' . $description . '</div>' : '')
. '<div class="layoutthumb"><img src="' . $wwwroot . 'thumb.php?type=viewlayout&amp;vl=' . Pieform::hsc($value) . '" title="' . $text . '" alt="' . $text . '"></div>'
. '<div class="layoutthumb">' . $data['layout'] . '</div>'
. '</div>';
}
......@@ -79,24 +72,16 @@ for ($row = 0; $row < $templatedata['maxrows']; $row++) {
$output .= '<div id="viewlayout_advancedlayoutselect_row' . ($row + 1) . '">';
foreach ($templatedata['layoutoptions'] as $value => $data) {
if ($data['rows'] == $row+1) {
if (is_array($data)) {
$text = $data['columns'];
$description = (isset($data['description'])) ? $data['description'] : '';
}
else {
$text = $data;
$description = '';
}
$text = Pieform::hsc($text);
$description = (isset($data['description'])) ? $data['description'] : '';
$text = Pieform::hsc($data['columns']);
$output .= '<div class="layoutoption">'
. '<label class="accessible-hidden" for="advancedlayout_' . Pieform::hsc($value) . '">' . $text . '</label>'
. '<input type="radio" name="advancedlayoutselect" id="advancedlayout_' . Pieform::hsc($value) . '"'
. ' value="' . Pieform::hsc($value) . '"'
. (($templatedata['currentlayout'] == $value) ? ' checked="checked"' : '') . '>'
. ($description != '' ? '<div class="radio-description">' . $description . '</div>' : '')
. '<div class="layoutthumb"><img src="' . $wwwroot . 'thumb.php?type=viewlayout&amp;vl=' . Pieform::hsc($value) . '" title="' . $text . '" alt="' . $text . '"></div>'
. '<div class="layoutthumb">' . $data['layout'] . '</div>'
. '</div>';
}
}
......@@ -137,7 +122,7 @@ $output .= '</select>'
foreach ($templatedata['columnlayoutoptions'] as $value => $data) {
$numcols = count(explode('-', $data));
$selectionstring = 'disabled';
if ($value == $templatedata['customlayout']) {
if ($value == $templatedata['customlayoutid']) {
$selectionstring = 'selected="selected"';
}
else if ($numcols == $templatedata['clnumcolumnsdefault']) {
......@@ -163,9 +148,7 @@ $output .= '</div>';
// preview pane
$output .= '<div id="previewcustomlayoutpane" class="fr">'
. '<div id="custompreviewtitle"><strong>' . get_string('layoutpreview', 'view') . '</strong></div>'
. '<div id="custompreview">'
. '<img src="' . $wwwroot . 'thumb.php?type=viewlayout&amp;vl=' . $templatedata['customlayout'] .'" title="' . $templatedata['clwidths'] . '" alt="' . $templatedata['clwidths'] . '">'
. '</div>'
. '<div id="custompreview">' . $templatedata['customlayout'] . '</div>'
. '<div class="cb">'
. '<input type="button" name="submitlayout" id="addlayout" class="button" value="' . get_string('createnewlayout', 'view') . '" onclick="CustomLayoutManager.customlayout_submit_layout()"/>'
. '</div>'
......
......@@ -2817,7 +2817,7 @@ table.templateresults td.right input.submit {
#viewlayout .layoutthumb {
display: block;
}
#viewlayout .layoutthumb img {
#viewlayout .layoutthumb svg {
margin-top: 5px;
background: #FFFFFF;
border: 1px solid #CCCCCC;
......@@ -2906,6 +2906,14 @@ table.templateresults td.right input.submit {
border: 1px solid #CCCCCC;
padding: 5px;
}
#viewlayout .layoutthumb rect.layout1,
#custompreview rect.layout1 {
fill: #666666;
}
#viewlayout .layoutthumb rect.layout0,
#custompreview rect.layout0 {
fill: #4d4d4d;
}
#addlayout {
margin: 0;
}
......
......@@ -15,7 +15,6 @@ define('NOCHECKREQUIREDFIELDS', 1);
require('init.php');
require_once('file.php');
require_once('user.php');
require_once('layoutpreviewimage.php');
$type = param_alpha('type');
......@@ -162,43 +161,6 @@ switch ($type) {
readfile_exit($path);
}
readfile_exit($THEME->get_path('images/no_thumbnail.png'));
case 'viewlayout':
header('Content-type: image/png');
$vl = param_integer('vl');
$rows = get_records_sql_assoc('
SELECT vlrc.row, vlc.widths
FROM {view_layout_rows_columns} vlrc
INNER JOIN {view_layout_columns} vlc ON (vlrc.columns = vlc.id)
WHERE vlrc.viewlayout = ?
ORDER BY vlrc.row ASC',
array($vl));
if ($rows) {
$filename = 'vl-';
foreach ($rows as $key => $row) {
$filename .= str_replace(',', '-', $row->widths);
$filename .= ($key == count($rows))? '.png' : '_';
}
if (($path = get_config('dataroot') . LayoutPreviewImage::$destinationfolder . '/' . $filename)
&& (is_readable($path))) {
readfile_exit($path);
}
// look in theme folder for default layout thumbs, or dataroot folder for custom layout thumbs
else if (($path = $THEME->get_path('images/' . $filename))
&& (is_readable($path))) {
readfile_exit($path);
}
}
readfile_exit($THEME->get_path('images/no_thumbnail.png'));
case 'customviewlayout':
header('Content-type: image/png');
$cvl = param_variable('cvl');
// dataroot folder for custom layout thumbs
if (($path = get_config('dataroot') . LayoutPreviewImage::$destinationfolder . '/' . $cvl . '.png')
&& (is_readable($path))) {
readfile_exit($path);
}
readfile_exit($THEME->get_path('images/no_thumbnail.png'));
}
function readfile_exit($path) {
......
......@@ -60,17 +60,15 @@ $maxrows = 3;
foreach ($layoutrows as $key => $layout) {
$maxrows = (count($layout) > $maxrows)? count($layout) : $maxrows;
$layoutoptions[$key]['rows'] = count($layout);
$layoutoptions[$key]['text'] = '';
for ($r=0; $r<count($layout); $r++) {
// store multi-row column widths for each option - used as img titles in layout.tpl
if ($r==0) {
$layoutoptions[$key]['columns'] = get_string($layoutcolumns[$layout[$r+1]]->widths, 'view');
}
else {
$layoutoptions[$key]['columns'] .= ' / ' . get_string($layoutcolumns[$layout[$r+1]]->widths, 'view');
}
$structure = array();
for ($r = 1; $r <= count($layout); $r++) {
$structure['layout']['row' . $r] = get_string($layoutcolumns[$layout[$r]]->widths, 'view');
}
$structure['text'] = implode(' / ', $structure['layout']);
$l = new LayoutPreviewImage($structure);
$layoutoptions[$key]['layout'] = $l->create_preview();
$layoutoptions[$key]['columns'] = $structure['text'];
}
foreach ($basicoptionids as $id) {
......@@ -97,6 +95,11 @@ $defaultlayout = get_record('view_layout_columns', 'columns', $defaultcustomlayo
$clnumcolumnsdefault = $defaultlayout->columns;
$clwidths = $defaultlayout->widths;
// Ready custom layout preview.
$defaultlayoutpreviewdata['layout']['row1'] = get_string($defaultcustomlayout[1]->widths, 'view');
$defaultlayoutpreviewdata['text'] = get_string($defaultcustomlayout[1]->widths, 'view');
$defaultlayoutpreview = new LayoutPreviewImage($defaultlayoutpreviewdata);
$inlinejavascript = <<<JAVASCRIPT
function get_max_custom_rows() {
......@@ -141,7 +144,8 @@ $templatedata = array(
'clnumcolumnsoptions' => $clnumcolumnsoptions,
'clnumcolumnsdefault' => $clnumcolumnsdefault,
'columnlayoutoptions' => $columnlayoutoptions,
'customlayout' => $defaultlayout->id,
'customlayoutid' => $defaultlayout->id,
'customlayout' => $defaultlayoutpreview->create_preview(),
'clwidths' => $clwidths,
'maxrows' => $maxrows
);
......@@ -160,7 +164,7 @@ $layoutform = pieform($layoutform);
$javascript = array('jquery','js/jquery/jquery-ui/js/jquery-ui-1.10.2.min.js', 'js/customlayout.js','js/jquery/modernizr.custom.js');
$stylesheets[] = '<link rel="stylesheet" type="text/css" href="' . append_version_number(get_config('wwwroot') . 'js/jquery/jquery-ui/css/ui-lightness/jquery-ui-1.10.2.min.css') . '">';
$smarty = smarty($javascript, $stylesheets, array('view' => array('Row', 'removethisrow', 'rownr', 'nrrows', 'generatingpreview')), array('sidebars' => false));
$smarty = smarty($javascript, $stylesheets, array('view' => array('Row', 'removethisrow', 'rownr', 'nrrows')), array('sidebars' => false));
$smarty->assign('INLINEJAVASCRIPT', $inlinejavascript);
$smarty->assign('form', $layoutform);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment