Move the orig_file_ext field from resource to resourcerev where

it actually belongs.  Add an option to download a specific resource
revision as attachment in the view.  Fix a bug that occurred when
displaying an old revision of a resource.  Prepare for proper
deletion of the original file and the connected resource in case
a revision is deleted; mark any previous revision as head in this
case. Left-align the summary label in the resource list view.
This commit is contained in:
Thomas Keller 2011-12-01 00:30:50 +01:00
parent 58ccb93f2d
commit d6eb7532fd
7 changed files with 296 additions and 20 deletions

View File

@ -133,7 +133,6 @@ class IDF_Form_WikiResourceCreate extends Pluf_Form
$resource->summary = trim($this->cleaned_data['summary']); $resource->summary = trim($this->cleaned_data['summary']);
$resource->title = trim($this->cleaned_data['title']); $resource->title = trim($this->cleaned_data['title']);
$resource->mime_type = $mimeType; $resource->mime_type = $mimeType;
$resource->orig_file_ext = $extension;
$resource->create(); $resource->create();
// add the first revision // add the first revision
@ -142,6 +141,7 @@ class IDF_Form_WikiResourceCreate extends Pluf_Form
$rev->submitter = $this->user; $rev->submitter = $this->user;
$rev->summary = __('Initial resource creation'); $rev->summary = __('Initial resource creation');
$rev->filesize = filesize($tempFile); $rev->filesize = filesize($tempFile);
$rev->fileext = $extension;
$rev->create(); $rev->create();
$finalFile = $rev->getFilePath(); $finalFile = $rev->getFilePath();

View File

@ -0,0 +1,161 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2011 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Update a documentation page.
*
* This add a corresponding revision.
*
*/
class IDF_Form_WikiResourceUpdate extends Pluf_Form
{
public $user = null;
public $project = null;
public $page = null;
public $show_full = false;
public function initFields($extra=array())
{
$this->resource = $extra['resource'];
$this->user = $extra['user'];
$this->project = $extra['project'];
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Description'),
'help_text' => __('This one line description is displayed in the list of resources.'),
'initial' => $this->resource->summary,
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
$this->fields['file'] = new Pluf_Form_Field_File(
array('required' => true,
'label' => __('File'),
'initial' => '',
'max_size' => Pluf::f('max_upload_size', 2097152),
'move_function_params' => array('upload_path' => $this->getTempUploadPath(),
'upload_path_create' => true,
'upload_overwrite' => true),
));
$this->fields['comment'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'label' => __('Comment'),
'help_text' => __('One line to describe the changes you made.'),
'initial' => '',
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
}
public function clean_file()
{
// FIXME: we do the same in IDF_Form_Upload and a couple of other places as well
$extra = strtolower(implode('|', explode(' ', Pluf::f('idf_extra_upload_ext'))));
if (strlen($extra)) $extra .= '|';
if (!preg_match('/\.('.$extra.'png|jpg|jpeg|gif|bmp|psd|tif|aiff|asf|avi|bz2|css|doc|eps|gz|jar|mdtext|mid|mov|mp3|mpg|ogg|pdf|ppt|ps|qt|ra|ram|rm|rtf|sdd|sdw|sit|sxi|sxw|swf|tgz|txt|wav|xls|xml|war|wmv|zip)$/i', $this->cleaned_data['file'])) {
@unlink($this->getTempUploadPath().$this->cleaned_data['file']);
throw new Pluf_Form_Invalid(__('For security reasons, you cannot upload a file with this extension.'));
}
list($mimeType, , $extension) = IDF_FileUtil::getMimeType($this->getTempUploadPath().$this->cleaned_data['file']);
if ($this->resource->mime_type != $mimeType) {
throw new Pluf_Form_Invalid(sprintf(
__('The mime type of the uploaded file "%s" does not match the mime type of this resource "%s"'),
$mimeType, $this->resource->mime_type
));
}
$this->cleaned_data['fileext'] = $extension;
if (md5_file($this->getTempUploadPath().$this->cleaned_data['file']) ===
md5_file($this->resource->get_current_revision()->getFilePath())) {
throw new Pluf_Form_Invalid(__('The current version of the resource and the uploaded file are equal.'));
}
return $this->cleaned_data['file'];
}
/**
* If we have uploaded a file, but the form failed remove it.
*
*/
function failed()
{
if (!empty($this->cleaned_data['file'])
and file_exists($this->getTempUploadPath().$this->cleaned_data['file'])) {
@unlink($this->getTempUploadPath().$this->cleaned_data['file']);
}
}
/**
* Save the model in the database.
*
* @param bool Commit in the database or not. If not, the object
* is returned but not saved in the database.
* @return Object Model with data set from the form.
*/
function save($commit=true)
{
if (!$this->isValid()) {
throw new Exception(__('Cannot save the model from an invalid form.'));
}
$tempFile = $this->getTempUploadPath().$this->cleaned_data['file'];
$this->resource->summary = trim($this->cleaned_data['summary']);
$this->resource->update();
// add the new revision
$rev = new IDF_Wiki_ResourceRevision();
$rev->wikiresource = $this->resource;
$rev->submitter = $this->user;
$rev->summary = $this->cleaned_data['comment'];
$rev->filesize = filesize($tempFile);
$rev->fileext = $this->cleaned_data['fileext'];
$rev->create();
$finalFile = $rev->getFilePath();
if (!is_dir(dirname($finalFile))) {
@unlink($tempFile);
$rev->delete();
throw new Exception('resource path does not exist');
}
if (!@rename($tempFile, $finalFile)) {
@unlink($tempFile);
$rev->delete();
throw new Exception('could not move resource to final location');
}
return $this->resource;
}
private function getTempUploadPath()
{
return Pluf::f('upload_path').'/'.$this->project->shortname.'/wiki/temp/';
}
}

View File

@ -95,7 +95,7 @@ class IDF_Views_Wiki
$pag->action = array('IDF_Views_Wiki::listResources', array($prj->shortname)); $pag->action = array('IDF_Views_Wiki::listResources', array($prj->shortname));
$pag->edit_action = array('IDF_Views_Wiki::viewResource', 'shortname', 'title'); $pag->edit_action = array('IDF_Views_Wiki::viewResource', 'shortname', 'title');
$pag->forced_where = new Pluf_SQL('project=%s', array($prj->id)); $pag->forced_where = new Pluf_SQL('project=%s', array($prj->id));
$pag->extra_classes = array('right', 'a-c', 'a-c', 'a-c'); $pag->extra_classes = array('right', 'a-c', 'left', 'a-c');
$list_display = array( $list_display = array(
'title' => __('Resource Title'), 'title' => __('Resource Title'),
'mime_type' => __('MIME type'), 'mime_type' => __('MIME type'),
@ -347,7 +347,7 @@ class IDF_Views_Wiki
if (isset($request->GET['rev']) and preg_match('/^[0-9]+$/', $request->GET['rev'])) { if (isset($request->GET['rev']) and preg_match('/^[0-9]+$/', $request->GET['rev'])) {
$revision = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_ResourceRevision', $revision = Pluf_Shortcuts_GetObjectOr404('IDF_Wiki_ResourceRevision',
$request->GET['rev']); $request->GET['rev']);
if ($oldrev->wikiresource != $resource->id or $oldrev->is_head == true) { if ($revision->wikiresource != $resource->id or $revision->is_head == true) {
return new Pluf_HTTP_Response_NotFound($request); return new Pluf_HTTP_Response_NotFound($request);
} }
} }
@ -382,7 +382,12 @@ class IDF_Views_Wiki
return new Pluf_HTTP_Response_NotFound($request); return new Pluf_HTTP_Response_NotFound($request);
} }
return new Pluf_HTTP_Response_File($rev->getFilePath(), $res->mime_type); $response = new Pluf_HTTP_Response_File($rev->getFilePath(), $res->mime_type);
if (isset($request->GET['attachment']) && $request->GET['attachment']) {
$response->headers['Content-Disposition'] =
'attachment; filename="'.$res->title.'.'.$rev->fileext.'"';
}
return $response;
} }
/** /**
@ -425,7 +430,7 @@ class IDF_Views_Wiki
} }
/** /**
* View a documentation page. * Update a documentation page.
*/ */
public $updatePage_precond = array('IDF_Precondition::accessWiki', public $updatePage_precond = array('IDF_Precondition::accessWiki',
'Pluf_Precondition::loginRequired'); 'Pluf_Precondition::loginRequired');
@ -475,6 +480,54 @@ class IDF_Views_Wiki
$request); $request);
} }
/**
* Update a documentation resource.
*/
public $updateResource_precond = array('IDF_Precondition::accessWiki',
'Pluf_Precondition::loginRequired');
public function updateResource($request, $match)
{
$prj = $request->project;
// Find the page
$sql = new Pluf_SQL('project=%s AND title=%s',
array($prj->id, $match[2]));
$resources = Pluf::factory('IDF_Wiki_Resource')->getList(array('filter'=>$sql->gen()));
if ($resources->count() != 1) {
return new Pluf_HTTP_Response_NotFound($request);
}
$resource = $resources[0];
$title = sprintf(__('Update %s'), $resource->title);
$revision = $resource->get_current_revision();
$params = array('project' => $prj,
'user' => $request->user,
'resource' => $resource);
if ($request->method == 'POST') {
$form = new IDF_Form_WikiResourceUpdate(array_merge($request->POST, $request->FILES),
$params);
if ($form->isValid()) {
$page = $form->save();
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::viewResource',
array($prj->shortname, $resource->title));
$request->user->setMessage(sprintf(__('The resource <a href="%s">%s</a> has been updated.'),
$url, Pluf_esc($resource->title)));
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Wiki::listResources',
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
} else {
$form = new IDF_Form_WikiResourceUpdate(null, $params);
}
return Pluf_Shortcuts_RenderToResponse('idf/wiki/updateResource.html',
array(
'page_title' => $title,
'resource' => $resource,
'rev' => $revision,
'form' => $form,
),
$request);
}
/** /**
* Delete a Wiki page. * Delete a Wiki page.
*/ */

View File

@ -72,14 +72,6 @@ class IDF_Wiki_Resource extends Pluf_Model
'verbose' => __('MIME media type'), 'verbose' => __('MIME media type'),
'help_text' => __('The MIME media type of the resource.'), 'help_text' => __('The MIME media type of the resource.'),
), ),
'orig_file_ext' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 10,
'verbose' => __('Original file extension'),
'help_text' => __('The original file extension of the uploaded resource.'),
),
'summary' => 'summary' =>
array( array(
'type' => 'Pluf_DB_Field_Varchar', 'type' => 'Pluf_DB_Field_Varchar',

View File

@ -70,6 +70,14 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model
'default' => 0, 'default' => 0,
'verbose' => __('file size in bytes'), 'verbose' => __('file size in bytes'),
), ),
'fileext' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 10,
'verbose' => __('File extension'),
'help_text' => __('The file extension of the uploaded resource.'),
),
'submitter' => 'submitter' =>
array( array(
'type' => 'Pluf_DB_Field_Foreignkey', 'type' => 'Pluf_DB_Field_Foreignkey',
@ -105,6 +113,25 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model
return ''; return '';
} }
function preDelete()
{
// if we kill off a head revision, ensure that we either mark a previous
// revision as head or kill off the resource record as well
if ($this->is_head) {
$sql = new Pluf_SQL('wikiresource=%s and id!=%s', array($this->wikiresource, $this->id));
$revs = Pluf::factory('IDF_Wiki_ResourceRevision')->getList(array('filter'=>$sql->gen(), 'order'=>'id DESC'));
if ($revs->count() > 0) {
$previous = $revs[0];
$previous->is_head = true;
$previous->update();
} else {
$this->get_wikiresource()->delete();
}
}
@unlink($this->getFilePath());
}
function preSave($create=false) function preSave($create=false)
{ {
if ($this->id == '') { if ($this->id == '') {
@ -135,7 +162,7 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model
function getFilePath() function getFilePath()
{ {
return sprintf(Pluf::f('upload_path').'/'.$this->get_wikiresource()->get_project()->shortname.'/wiki/res/%d/%d.%s', return sprintf(Pluf::f('upload_path').'/'.$this->get_wikiresource()->get_project()->shortname.'/wiki/res/%d/%d.%s',
$this->get_wikiresource()->id, $this->id, $this->get_wikiresource()->orig_file_ext); $this->get_wikiresource()->id, $this->id, $this->fileext);
} }
function getFileURL() function getFileURL()
@ -145,11 +172,6 @@ class IDF_Wiki_ResourceRevision extends Pluf_Model
array($prj->shortname, $this->id)); array($prj->shortname, $this->id));
} }
function preDelete()
{
@unlink($this->getFilePath());
}
/** /**
* Returns the page revisions which contain references to this resource revision * Returns the page revisions which contain references to this resource revision
*/ */

View File

@ -0,0 +1,46 @@
{extends "idf/wiki/base.html"}
{block docclass}yui-t2{/block}
{block body}
{if $form.errors}
<div class="px-message-error">
<p>{trans 'The form contains some errors. Please correct them to update the page.'}</p>
{if $form.get_top_errors}
{$form.render_top_errors|unsafe}
{/if}
</div>
{/if}
<form method="post" enctype="multipart/form-data" action="." >
<table class="form" summary="">
<tr>
<th><strong>{$form.f.summary.labelTag}:</strong></th>
<td>{if $form.f.summary.errors}{$form.f.summary.fieldErrors}{/if}
{$form.f.summary|unsafe}<br />
<span class="helptext">{$form.f.summary.help_text}</span>
</td>
</tr>
<tr>
<th><strong>{$form.f.file.labelTag}:</strong></th>
<td>{if $form.f.file.errors}{$form.f.file.fieldErrors}{/if}
{$form.f.file|unsafe}
</td>
</tr>
<tr>
<th><strong>{$form.f.comment.labelTag}:</strong></th>
<td>{if $form.f.comment.errors}{$form.f.comment.fieldErrors}{/if}
{$form.f.comment|unsafe}<br />
<span class="helptext">{$form.f.comment.help_text}</span>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>{* float left is a fix for Firefox < 3.5 *}
<span style="float: left;"> <input type="submit" value="{trans 'Update Resource'}" name="submit" /> | <a href="{url 'IDF_Views_Wiki::viewResource', array($project.shortname, $resource.title)}">{trans 'Cancel'}</a></span>{if $isOwner or $isAdmin or $isMember}
{aurl 'url', 'IDF_Views_Wiki::deleteResource', array($project.shortname, $resource.id)}
<span class="dellink"><a href="{$url}" title="{trans 'Delete this resource'}"><img src="{media '/idf/img/trash.png'}" style="vertical-align: text-bottom;" alt="{trans 'Trash'}" /></a> <a href="{$url}" title="{trans 'Delete this resource'}">{trans 'Delete this resource'}</a></span>{/if}
</td>
</tr>
</table>
</form>
{/block}

View File

@ -8,7 +8,7 @@
{block body} {block body}
{if !$rev.is_head} {if !$rev.is_head}
{ashowuser 'submitter', $oldrev.get_submitter(), $request}{aurl 'url', 'IDF_Views_Wiki::viewResource', array($project.shortname, $resource.title)} {ashowuser 'submitter', $rev.get_submitter(), $request}{aurl 'url', 'IDF_Views_Wiki::viewResource', array($project.shortname, $resource.title)}
<div class="old-rev"> <div class="old-rev">
<p>{blocktrans}You are looking at an old revision of the resource <p>{blocktrans}You are looking at an old revision of the resource
<a href="{$url}">{$resource.title}</a>. This revision was created <a href="{$url}">{$resource.title}</a>. This revision was created
@ -21,9 +21,11 @@ by {$submitter}.{/blocktrans}</p>
<p class="preview">{$rev.render()|unsafe}</p> <p class="preview">{$rev.render()|unsafe}</p>
{aurl 'url', 'IDF_Views_Wiki::rawResource', array($project.shortname, $rev.id), array('attachment' => 1)}
<ul> <ul>
<li>{trans 'File size'}: {$rev.filesize|size}</li> <li>{trans 'File size'}: {$rev.filesize|size}</li>
<li>{trans 'MIME type'}: {$resource.mime_type}</li> <li>{trans 'MIME type'}: {$resource.mime_type}</li>
<li><a href="{$url}">{trans 'Download this file'}</a></li>
</ul> </ul>
{if ($isOwner or $isAdmin) and !$rev.is_head}{aurl 'url', 'IDF_Views_Wiki::deleteResourceRev', array($project.shortname, $rev.id)} {if ($isOwner or $isAdmin) and !$rev.is_head}{aurl 'url', 'IDF_Views_Wiki::deleteResourceRev', array($project.shortname, $rev.id)}
<p class="delp"><a href="{$url}" title="{trans 'Delete this revision'}"><img src="{media '/idf/img/trash.png'}" style="vertical-align: text-bottom;" alt="{trans 'Trash'}" /></a> <a href="{$url}">{trans 'Delete this revision'}</a></p> <p class="delp"><a href="{$url}" title="{trans 'Delete this revision'}"><img src="{media '/idf/img/trash.png'}" style="vertical-align: text-bottom;" alt="{trans 'Trash'}" /></a> <a href="{$url}">{trans 'Delete this revision'}</a></p>