Commit 8673f20f authored by Gregor Anzelj's avatar Gregor Anzelj Committed by Robert Lyon
Browse files

Support for Resume attachments (Bug #1103942) and


support for Textboxes/Notes attachments (Bug #1117237)

Change-Id: Idf4cc1d2ee0d60e11e1d88ec202eb32b4c0de5c9
Signed-off-by: default avatarGregor Anzelj <gregor.anzelj@gmail.com>
Signed-off-by: Aaron Wells's avatarAaron Wells <aaronw@catalyst.net.nz>
parent 4064ecd3
......@@ -841,7 +841,12 @@ function FileBrowser(idprefix, folderid, config, globalconfig) {
location.href = data.goto;
}
else if (typeof(data.replaceHTML) == 'string') {
formSuccess(form, data);
if (data.returnCode == -1) {
formError(form, data);
}
else {
formSuccess(form, data);
}
self.init();
}
// Recalculate the width of config block
......
......@@ -71,25 +71,52 @@ tr.folderhover a,
font-size: 1.3333em;
}
/* Edit journal post file list */
#editpost_filebrowser_upload_container {
/* Edit note file list */
/* Edit goals and skill file list */
/* Edit resume composite file list */
#editpost_filebrowser_upload_container,
#editnote_filebrowser_upload_container,
#editgoalsandskills_filebrowser_upload_container,
#editcomposite_filebrowser_upload_container {
margin-top: 10px;
}
#editpost_filebrowser_upload_browse,
#editpost_filebrowser_open_upload_browse_container {
#editpost_filebrowser_open_upload_browse_container,
#editnote_filebrowser_upload_browse,
#editnote_filebrowser_open_upload_browse_container,
#editgoalsandskills_filebrowser_upload_browse,
#editgoalsandskills_filebrowser_open_upload_browse_container,
#editcomposite_filebrowser_upload_browse,
#editcomposite_filebrowser_open_upload_browse_container {
padding: 5px;
}
#editpost_filebrowser_agreement {
#editpost_filebrowser_agreement,
#editnote_filebrowser_agreement,
#editgoalsandskills_filebrowser_agreement,
#editcomposite_filebrowser_agreement {
margin-bottom: 10px;
margin-left: 80px;
}
#editpost_filebrowser_agreement label {
#editpost_filebrowser_agreement label,
#editnote_filebrowser_agreement label,
#editgoalsandskills_filebrowser_agreement label,
#editcomposite_filebrowser_agreement label {
margin-left: -80px;
}
/* Edit journal post selected file list */
#editpost_filebrowser_empty_selectlist {
/* Edit note selected file list */
/* Edit goals and skill selected file list */
/* Edit resume composite selected file list */
#editpost_filebrowser_empty_selectlist,
#editnote_filebrowser_empty_selectlist,
#editgoalsandskills_filebrowser_empty_selectlist,
#editcomposite_filebrowser_empty_selectlist {
margin: 5px 0;
}
#editpost_filebrowser_selectlist {
#editpost_filebrowser_selectlist,
#editnote_filebrowser_selectlist,
#editgoalsandskills_filebrowser_selectlist,
#editcomposite_filebrowser_selectlist {
margin: 0 0 10px 0;
}
#instconf_folder_selectlist {
......
......@@ -41,6 +41,10 @@ class PluginBlocktypeTextbox extends PluginBlocktype {
return array('general');
}
public function can_have_attachments() {
return true;
}
public static function render_instance(BlockInstance $instance, $editing=false) {
$configdata = $instance->get('configdata');
......@@ -55,6 +59,24 @@ class PluginBlocktypeTextbox extends PluginBlocktype {
$smarty = smarty_core();
$smarty->assign('text', $text);
$attachments = $artefact->get_attachments();
if ($attachments) {
$artefact->add_to_render_path($options);
require_once(get_config('docroot') . 'artefact/lib.php');
foreach ($attachments as &$attachment) {
$f = artefact_instance_from_id($attachment->id);
$attachment->size = $f->describe_size();
$attachment->iconpath = $f->get_icon(array('id' => $attachment->id, 'viewid' => isset($options['viewid']) ? $options['viewid'] : 0));
$attachment->viewpath = get_config('wwwroot') . 'view/artefact.php?artefact=' . $attachment->id . '&view=' . (isset($viewid) ? $viewid : 0);
$attachment->downloadpath = get_config('wwwroot') . 'artefact/file/download.php?file=' . $attachment->id;
if (isset($viewid)) {
$attachment->downloadpath .= '&view=' . $viewid;
}
}
$smarty->assign('attachments', $attachments);
$smarty->assign('count', count($attachments));
}
if ($artefact->get('allowcomments')) {
$commentcount = ArtefactTypeComment::count_comments(null, array($configdata['artefactid']));
$commentcount = isset($commentcount[$configdata['artefactid']]) ? $commentcount[$configdata['artefactid']]->comments : 0;
......@@ -254,6 +276,7 @@ EOF;
public static function instance_config_form($instance) {
global $USER;
require_once('license.php');
safe_require('artefact', 'file');
$instance->set('artefactplugin', 'internal');
$configdata = $instance->get('configdata');
if (!$height = get_config('blockeditorheight')) {
......@@ -347,6 +370,7 @@ EOF;
. get_string('usecontentfromanothertextbox', 'blocktype.internal/textbox') . '</a>',
),
'artefactid' => self::artefactchooser_element(isset($artefactid) ? $artefactid : null),
'artefactids' => self::filebrowser_element($instance, (isset($configdata['artefactids'])) ? $configdata['artefactids'] : null),
'managenotes' => array(
'type' => 'html',
'class' => 'right hidden',
......@@ -424,9 +448,9 @@ EOF;
throw new AccessDeniedException(get_string('nopublishpermissiononartefact', 'mahara', hsc($artefact->get('title'))));
}
// Stop users from editing html artefacts whose owner is not the same as the
// Stop users from editing textbox artefacts whose owner is not the same as the
// view owner, even if they would normally be allowed to edit the artefact.
// It's too confusing. Html artefacts with other owners *can* be included in
// It's too confusing. Textbox artefacts with other owners *can* be included in
// the view read-only, provided the artefact has the correct republish
// permission.
if ($artefact->get('owner') === $data['owner']
......@@ -446,6 +470,28 @@ EOF;
$artefact->commit();
// Add attachments, if there are any...
$old = $artefact->attachment_id_list();
$new = is_array($values['artefactids']) ? $values['artefactids'] : array();
if (!empty($new) || !empty($old)) {
foreach ($old as $o) {
if (!in_array($o, $new)) {
try {
$artefact->detach($o);
}
catch (ArtefactNotFoundException $e) {}
}
}
foreach ($new as $n) {
if (!in_array($n, $old)) {
try {
$artefact->attach($n);
}
catch (ArtefactNotFoundException $e) {}
}
}
}
$values['artefactid'] = $artefact->get('id');
$instance->save_artefact_instance($artefact);
......@@ -468,6 +514,14 @@ EOF;
return $values;
}
public static function filebrowser_element(&$instance, $default=array()) {
$element = ArtefactTypeFileBase::blockconfig_filebrowser_element($instance, $default);
$element['title'] = get_string('attachments', 'artefact.blog');
$element['name'] = 'artefactids';
$element['config']['selectone'] = false;
return $element;
}
public static function default_copy_type() {
return 'full';
}
......
{$text|clean_html|safe}
{if isset($attachments)}
<table class="cb attachments fullwidth">
<thead class="expandable-head">
<tr class="toggle">
<td colspan="2" class="toggle-padding"><strong>{str tag=attachedfiles section=artefact.blog}</strong>
<img class="fr" src="{theme_url filename='images/icon_attachment.png'}" alt="{str tag=Attachments section=artefact.resume}">
<span class="fr">{$count}&nbsp;</span>
</td>
</tr>
</thead>
<tbody class="expandable-body">
{foreach from=$attachments item=item}
<tr class="{cycle values='r0,r1'}">
{if $icons}<td class="iconcell"><img src="{$item->iconpath}" alt=""></td>{/if}
<td><a href="{$item->viewpath}">{$item->title}</a> ({$item->size}) - <strong><a href="{$item->downloadpath}">{str tag=Download section=artefact.file}</a></strong>
<br>{$item->description}</td>
</tr>
{/foreach}
</tbody>
</table>
{if $artefact->get('tags')}<div class="tags">{str tag=tags}: {list_tags owner=$artefact->get('owner') tags=$artefact->get('tags')}</div>{/if}
{/if}
{if $commentcount || $commentcount === 0}
<div class="comments">
<a href="{$artefacturl}">{str tag=Comments section=artefact.comment} ({$commentcount})</a>
......
......@@ -24,14 +24,19 @@
*/
define('INTERNAL', 1);
define('SECTION_PLUGINTYPE', 'artefact');
define('SECTION_PLUGINNAME', 'internal');
define('SECTION_PAGE', 'editnote');
require(dirname(dirname(dirname(__FILE__))) . '/init.php');
require_once('license.php');
safe_require('artefact', 'internal');
safe_require('artefact', 'file');
define('TITLE', get_string('editnote', 'artefact.internal'));
$artefact = new ArtefactTypeHtml(param_integer('id'));
$note = param_integer('id');
$artefact = new ArtefactTypeHtml($note);
if (!$USER->can_edit_artefact($artefact) || $artefact->get('locked')) {
throw new AccessDeniedException(get_string('accessdenied', 'error'));
}
......@@ -51,11 +56,24 @@ else {
define('MENUITEM', 'content/notes');
}
$folder = param_integer('folder', 0);
$browse = (int) param_variable('browse', 0);
$highlight = null;
if ($file = param_integer('file', 0)) {
$highlight = array($file);
}
$form = array(
'name' => 'editnote',
'method' => 'post',
'plugintype' => 'artefact',
'pluginname' => 'internal',
'name' => 'editnote',
'method' => 'post',
'jsform' => true,
'newiframeonsubmit' => true,
'jssuccesscallback' => 'editnote_callback',
'jserrorcallback' => 'editnote_callback',
'plugintype' => 'artefact',
'pluginname' => 'internal',
'configdirs' => array(get_config('libroot') . 'form/', get_config('docroot') . 'artefact/file/form/'),
'elements' => array(
'title' => array(
'type' => 'text',
......@@ -77,6 +95,28 @@ $form = array(
),
'license' => license_form_el_basic($artefact),
'licensing_advanced' => license_form_el_advanced($artefact),
'filebrowser' => array(
'type' => 'filebrowser',
'title' => get_string('attachments', 'artefact.blog'),
'folder' => $folder,
'highlight' => $highlight,
'browse' => $browse,
'page' => get_config('wwwroot') . 'artefact/internal/editnote.php?id=' . $note . '&browse=1',
'browsehelp' => 'browsemyfiles',
'config' => array(
'upload' => true,
'uploadagreement' => get_config_plugin('artefact', 'file', 'uploadagreement'),
'resizeonuploaduseroption' => get_config_plugin('artefact', 'file', 'resizeonuploaduseroption'),
'resizeonuploaduserdefault' => $USER->get_account_preference('resizeonuploaduserdefault'),
'createfolder' => false,
'edit' => false,
'select' => true,
),
'defaultvalue' => $artefact->attachment_id_list(),
'selectlistcallback' => 'artefact_get_records_by_id',
'selectcallback' => 'add_note_attachment',
'unselectcallback' => 'delete_note_attachment',
),
'allowcomments' => array(
'type' => 'checkbox',
'title' => get_string('allowcomments', 'artefact.comment'),
......@@ -89,7 +129,7 @@ $form = array(
'group' => $group,
'ignore' => !$group,
),
'submit' => array(
'submitnote' => array(
'type' => 'submitcancel',
'value' => array(get_string('save'), get_string('cancel')),
'goto' => $goto,
......@@ -102,13 +142,97 @@ if (!get_config('licensemetadata')) {
}
$form = pieform($form);
$smarty = smarty();
/*
* Javascript specific to this page. Creates the list of files
* attached to the note.
*/
$wwwroot = get_config('wwwroot');
$noimagesmessage = json_encode(get_string('noimageshavebeenattachedtothispost', 'artefact.blog'));
$javascript = <<<EOF
// Override the image button on the tinyMCE editor. Rather than the
// normal image popup, open up a modified popup which allows the user
// to select an image from the list of image files attached to the
// note.
// Get all the files in the attached files list that have been
// recognised as images. This function is called by the the popup
// window, but needs access to the attachment list on this page
function attachedImageList() {
var images = [];
var attachments = editnote_filebrowser.selecteddata;
for (var a in attachments) {
if (attachments[a].artefacttype == 'image' || attachments[a].artefacttype == 'profileicon') {
images.push({
'id': attachments[a].id,
'name': attachments[a].title,
'description': attachments[a].description ? attachments[a].description : ''
});
}
}
return images;
}
function imageSrcFromId(imageid) {
return config.wwwroot + 'artefact/file/download.php?file=' + imageid;
}
function imageIdFromSrc(src) {
var artefactstring = 'download.php?file=';
var ind = src.indexOf(artefactstring);
if (ind != -1) {
return src.substring(ind+artefactstring.length, src.length);
}
return '';
}
var imageList = {};
function noteImageWindow(ui, v) {
var t = tinyMCE.activeEditor;
imageList = attachedImageList();
var template = new Array();
template['file'] = '{$wwwroot}artefact/blog/image_popup.php';
template['width'] = 355;
template['height'] = 275 + (tinyMCE.isMSIE ? 25 : 0);
// Language specific width and height addons
template['width'] += t.getLang('lang_insert_image_delta_width', 0);
template['height'] += t.getLang('lang_insert_image_delta_height', 0);
template['inline'] = true;
t.windowManager.open(template);
}
function editnote_callback(form, data) {
editnote_filebrowser.callback(form, data);
};
EOF;
$smarty = smarty(array(), array(), array(), array(
'tinymcesetup' => "ed.addCommand('mceImage', noteImageWindow);",
'sideblocks' => array(
array(
'name' => 'quota',
'weight' => -10,
'data' => array(),
),
),
));
$smarty->assign('INLINEJAVASCRIPT', $javascript);
$smarty->assign_by_ref('form', $form);
$smarty->assign('PAGEHEADING', $artefact->get('title'));
$smarty->assign('form', $form);
$smarty->display('form.tpl');
function editnote_submit(Pieform $form, array $values) {
global $SESSION, $artefact, $goto;
db_begin();
$artefact->set('title', $values['title']);
$artefact->set('description', $values['description']);
$artefact->set('tags', $values['tags']);
......@@ -123,6 +247,53 @@ function editnote_submit(Pieform $form, array $values) {
$artefact->set('licensorurl', $values['licensorurl']);
}
$artefact->commit();
$SESSION->add_ok_msg(get_string('noteupdated', 'artefact.internal'));
redirect($goto);
// Attachments
$old = $artefact->attachment_id_list();
$new = is_array($values['filebrowser']) ? $values['filebrowser'] : array();
if (!empty($new) || !empty($old)) {
foreach ($old as $o) {
if (!in_array($o, $new)) {
try {
$artefact->detach($o);
}
catch (ArtefactNotFoundException $e) {}
}
}
foreach ($new as $n) {
if (!in_array($n, $old)) {
try {
$artefact->attach($n);
}
catch (ArtefactNotFoundException $e) {}
}
}
}
db_commit();
$result = array(
'error' => false,
'message' => get_string('noteupdated', 'artefact.internal'),
'goto' => $goto,
);
if ($form->submitted_by_js()) {
// Redirect back to the note page from within the iframe
$SESSION->add_ok_msg($result['message']);
$form->json_reply(PIEFORM_OK, $result, false);
}
$form->reply(PIEFORM_OK, $result);
}
function add_note_attachment($attachmentid) {
global $artefact;
if ($artefact) {
$artefact->attach($attachmentid);
}
}
function delete_note_attachment($attachmentid) {
global $artefact;
if ($artefact) {
$artefact->detach($attachmentid);
}
}
<h3>Attach files</h3>
<p>If you wish to attach a file currently stored within your files area to your note, you can browse your repository for the required file(s).</p>
<p>Navigate through your file and folder structure and select the required file(s).</p>
<p>You can also upload new files from your computer. They will be placed in your files area and attached to your note at the same time.</p>
......@@ -703,6 +703,15 @@ class ArtefactTypeIndustry extends ArtefactTypeProfileField {}
/* Artefact type for generic html fragments */
class ArtefactTypeHtml extends ArtefactType {
public function describe_size() {
return $this->count_attachments() . ' ' . get_string('attachments', 'artefact.blog');
}
public function can_have_attachments() {
return true;
}
public static function get_icon($options=null) {
global $THEME;
return $THEME->get_url('images/note.png', false, 'artefact/internal');
......
......@@ -169,6 +169,26 @@ if ($data) {
}
}
// Get the attached files.
$noteids = array();
if ($data) {
$noteids = array_keys($data);
}
$files = ArtefactType::attachments_from_id_list($noteids);
if ($files) {
safe_require('artefact', 'file');
foreach ($files as $file) {
$file->icon = call_static_method(generate_artefact_class_name($file->artefacttype), 'get_icon', array('id' => $file->attachment));
$data[$file->artefact]->files[] = $file;
}
}
// Add Attachments count for each Note
if ($data) {
foreach ($data as $item) {
$item->count = isset($item->files) ? count($item->files) : 0;
}
}
$pagination = build_pagination(array(
'id' => 'notes_pagination',
'url' => $baseurl,
......
......@@ -5,6 +5,7 @@
<tr>
<th>{str tag=Note section=artefact.internal}</th>
<th>{str tag=containedin section=artefact.internal}</th>
<th class="center"><img src="{theme_url filename="images/icon_attachment.png"}" title="{str tag=Attachments section=artefact.resume}" /></th>
<th></th>
</tr>
</thead>
......@@ -20,7 +21,27 @@
{if $n->tags}
<div>{str tag=tags}: {list_tags tags=$n->tags owner=$n->owner}</div>
{/if}
<div id="n{$n->id}_desc" class="hidden detail">{$n->description|clean_html|safe}</div>
<div id="n{$n->id}_desc" class="hidden detail">{$n->description|clean_html|safe}
{if $n->files}
<div id="notefiles_{$n->id}">
<table class="attachments fullwidth">
<col width="5%">
<col width="40%">
<col width="55%">
<tbody>
<tr><th colspan=3>{str tag=attachedfiles section=artefact.blog}</th></tr>
{foreach from=$n->files item=file}
<tr class="{cycle values='r1,r0'}">
<td><img src="{$file->icon}" alt=""></td>
<td class="valign"><a href="{$WWWROOT}artefact/file/download.php?file={$file->attachment}">{$file->title}</a></td>
<td class="valign">{$file->description}</td>
</tr>
{/foreach}
</tbody>
</table>
</div>
</div>
{/if}
</td>
<td>
{foreach from=$n->views item=v}
......@@ -30,6 +51,7 @@
</div>
{/foreach}
</td>
<td align="center">{$n->count}</td>
<td class="right buttonscell btns2">
{if $n->locked}
<span class="s dull">{str tag=Submitted section=view}</span>
......
......@@ -1015,6 +1015,16 @@ abstract class ArtefactType {
return array();
}
public function attachment_id_list_with_item($itemid) {
// If artefact attachment table has 'item' column utilised.
if ($this->can_have_attachments()) {
if ($list = get_column('artefact_attachment', 'attachment', 'artefact', $this->get('id'), 'item', $itemid)) {
return $list;
}
}
return array();
}
public function attachments_from_id_list($artefactids) {
if (empty($artefactids)) {
return array();
......@@ -1080,7 +1090,7 @@ abstract class ArtefactType {
return array_values($list);
}
public function attach($attachmentid) {
public function attach($attachmentid, $itemid=null) {
if (record_exists('artefact_attachment', 'artefact', $this->get('id'), 'attachment', $attachmentid)) {
return;
}
......@@ -1090,6 +1100,7 @@ abstract class ArtefactType {
$data = new StdClass;
$data->artefact = $this->get('id');
$data->attachment = $attachmentid;
$data->item = $itemid;
insert_record('artefact_attachment', $data);
$data = new StdClass;
......
......@@ -52,6 +52,26 @@ if (!$data = get_records_sql_array($sql, array($owner, $type))) {
$data = array();
}
// Add artefact attachments it there are any
$datawithattachments = array();
foreach ($data as $record) {
$sql = 'SELECT a.title, a.id, af.size
FROM {artefact} a
JOIN {artefact_file_files} af ON af.artefact = a.id
JOIN {artefact_attachment} at ON at.attachment = a.id
WHERE at.artefact = ? AND at.item = ?
ORDER BY a.title';
$attachments = get_records_sql_array($sql, array($record->artefact, $record->id));
$record->attachments = $attachments;
if (!is_array($attachments)) {
$record->clipcount = 0;