Issue 81: Allow users to create downloads that point offsite

This commit is contained in:
Nathan Adams 2015-10-16 22:09:57 -05:00
parent 4421b6d4a1
commit 75986bb272
6 changed files with 134 additions and 9 deletions

View File

@ -44,6 +44,15 @@ class IDF_Form_Upload extends Pluf_Form
'size' => 67, 'size' => 67,
), ),
)); ));
$this->fields['ext_file'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('External File'),
'initial' => '',
'widget_attrs' => array(
'maxlength' => 200,
'size' => 67,
),
));
$this->fields['changelog'] = new Pluf_Form_Field_Varchar( $this->fields['changelog'] = new Pluf_Form_Field_Varchar(
array('required' => false, array('required' => false,
'label' => __('Description'), 'label' => __('Description'),
@ -55,7 +64,7 @@ class IDF_Form_Upload extends Pluf_Form
), ),
)); ));
$this->fields['file'] = new Pluf_Form_Field_File( $this->fields['file'] = new Pluf_Form_Field_File(
array('required' => true, array('required' => false,
'label' => __('File'), 'label' => __('File'),
'initial' => '', 'initial' => '',
'max_size' => Pluf::f('max_upload_size', 2097152), 'max_size' => Pluf::f('max_upload_size', 2097152),
@ -80,6 +89,9 @@ class IDF_Form_Upload extends Pluf_Form
public function clean_file() public function clean_file()
{ {
// FIXME: we do the same in IDF_Form_WikiResourceCreate and a couple of other places as well // FIXME: we do the same in IDF_Form_WikiResourceCreate and a couple of other places as well
if (empty($this->cleaned_data['file'])) {
return $this->cleaned_data['file'];
}
$extra = strtolower(implode('|', explode(' ', Pluf::f('idf_extra_upload_ext')))); $extra = strtolower(implode('|', explode(' ', Pluf::f('idf_extra_upload_ext'))));
if (strlen($extra)) $extra .= '|'; 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'])) { 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'])) {
@ -121,6 +133,9 @@ class IDF_Form_Upload extends Pluf_Form
throw new Pluf_Form_Invalid(__('You provided an invalid label.')); throw new Pluf_Form_Invalid(__('You provided an invalid label.'));
} }
} }
if (empty($this->cleaned_data["file"]) && empty($this->cleaned_data["ext_file"])) {
throw new Pluf_Form_Invalid(__("Must upload a file or specify an external file"));
}
return $this->cleaned_data; return $this->cleaned_data;
} }
@ -162,14 +177,21 @@ class IDF_Form_Upload extends Pluf_Form
$tags[] = IDF_Tag::add($name, $this->project, $class); $tags[] = IDF_Tag::add($name, $this->project, $class);
} }
} }
// Create the upload // Create the upload
$upload = new IDF_Upload(); $upload = new IDF_Upload();
$upload->project = $this->project; $upload->project = $this->project;
$upload->submitter = $this->user; $upload->submitter = $this->user;
$upload->summary = trim($this->cleaned_data['summary']); $upload->summary = trim($this->cleaned_data['summary']);
$upload->changelog = trim($this->cleaned_data['changelog']); $upload->changelog = trim($this->cleaned_data['changelog']);
if (!empty($this->cleaned_data["file"])) {
$upload->file = $this->cleaned_data['file']; $upload->file = $this->cleaned_data['file'];
$upload->filesize = filesize(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->cleaned_data['file']); $upload->filesize = filesize(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->cleaned_data['file']);
} else {
$upload->file = end(explode("/", $this->cleaned_data["ext_file"]));
$upload->ext_file = $this->cleaned_data['ext_file'];
}
$upload->downloads = 0; $upload->downloads = 0;
$upload->create(); $upload->create();
foreach ($tags as $tag) { foreach ($tags as $tag) {

View File

@ -0,0 +1,29 @@
<?php
function IDF_Migrations_32ExternalFile_up()
{
$table = Pluf::factory('IDF_Upload')->getSqlTable();
$sql = array();
$sql["MySQL"] = "ALTER TABLE " . $table . " ADD COLUMN `ext_file` VARCHAR(250) NULL AFTER `modif_dtime`;";
$db = Pluf::db();
$engine = Pluf::f('db_engine');
$db->execute($sql[$engine]);
}
function IDF_Migrations_32ExternalFile_down()
{
$table = Pluf::factory('IDF_Upload')->getSqlTable();
$sql = array();
$sql["MySQL"] = "ALTER TABLE " . $table . " DROP COLUMN `ext_file`;";
$db = Pluf::db();
$engine = Pluf::f('db_engine');
$db->execute($sql[$engine]);
}

View File

@ -56,6 +56,13 @@ class IDF_Upload extends Pluf_Model
'size' => 250, 'size' => 250,
'verbose' => __('summary'), 'verbose' => __('summary'),
), ),
'ext_file' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 250,
'verbose' => __('External File URL'),
),
'changelog' => 'changelog' =>
array( array(
'type' => 'Pluf_DB_Field_Text', 'type' => 'Pluf_DB_Field_Text',
@ -140,6 +147,54 @@ class IDF_Upload extends Pluf_Model
return $this->file; return $this->file;
} }
/**
* Get the file size of any remote resource (using get_headers()),
* either in bytes or - default - as human-readable formatted string.
*
* @author Stephan Schmitz <eyecatchup@gmail.com>
* @license MIT <http://eyecatchup.mit-license.org/>
* @url <https://gist.github.com/eyecatchup/f26300ffd7e50a92bc4d>
*
* @param string $url Takes the remote object's URL.
* @param boolean $formatSize Whether to return size in bytes or formatted.
* @param boolean $useHead Whether to use HEAD requests. If false, uses GET.
* @return string Returns human-readable formatted size
* or size in bytes (default: formatted).
*
* <code>
* //example
* echo getRemoteFilesize('https://github.com/eyecatchup/SEOstats/archive/master.zip');
* </code>
*/
private function getRemoteFilesize($url, $formatSize = true, $useHead = true)
{
if (false !== $useHead) {
stream_context_set_default(array('http' => array('method' => 'HEAD')));
}
$head = array_change_key_case(get_headers($url, 1));
// content-length of download (in bytes), read from Content-Length: field
$clen = isset($head['content-length']) ? $head['content-length'] : 0;
// cannot retrieve file size, return "-1"
if (!$clen) {
return -1;
}
if (!$formatSize) {
return $clen; // return size in bytes
}
$size = $clen;
switch ($clen) {
case $clen < 1024:
$size = $clen .' B'; break;
case $clen < 1048576:
$size = round($clen / 1024, 2) .' KiB'; break;
case $clen < 1073741824:
$size = round($clen / 1048576, 2) . ' MiB'; break;
case $clen < 1099511627776:
$size = round($clen / 1073741824, 2) . ' GiB'; break;
}
return $size; // return formatted size
}
function _toIndex() function _toIndex()
{ {
return ''; return '';
@ -150,7 +205,11 @@ class IDF_Upload extends Pluf_Model
if ($this->id == '') { if ($this->id == '') {
$this->creation_dtime = gmdate('Y-m-d H:i:s'); $this->creation_dtime = gmdate('Y-m-d H:i:s');
$this->modif_dtime = gmdate('Y-m-d H:i:s'); $this->modif_dtime = gmdate('Y-m-d H:i:s');
if ($this->ext_file == "") {
$this->md5 = md5_file ($this->getFullPath()); $this->md5 = md5_file ($this->getFullPath());
} else {
$this->filesize = $this->getRemoteFilesize($this->ext_file, false);
}
} }
} }

View File

@ -204,11 +204,15 @@ class IDF_Views_Download
$prj->inOr404($upload); $prj->inOr404($upload);
$upload->downloads += 1; $upload->downloads += 1;
$upload->update(); $upload->update();
if ($upload->ext_file == "") {
$path = $upload->getFullPath(); $path = $upload->getFullPath();
$mime = IDF_FileUtil::getMimeType($path); $mime = IDF_FileUtil::getMimeType($path);
$render = new Pluf_HTTP_Response_File($path, $mime[0]); $render = new Pluf_HTTP_Response_File($path, $mime[0]);
$render->headers['Content-MD5'] = $upload->md5; $render->headers['Content-MD5'] = $upload->md5;
$render->headers['Content-Disposition'] = 'attachment; filename="'.$upload->file.'"'; $render->headers['Content-Disposition'] = 'attachment; filename="'.$upload->file.'"';
} else {
$render = new Pluf_HTTP_Response_Redirect($upload->ext_file);
}
return $render; return $render;
} }

View File

@ -18,6 +18,12 @@
{$form.f.summary|unsafe} {$form.f.summary|unsafe}
</td> </td>
</tr> </tr>
<tr>
<th><strong>{$form.f.ext_file.labelTag}:</strong></th>
<td>{if $form.f.ext_file.errors}{$form.f.ext_file.fieldErrors}{/if}
{$form.f.ext_file|unsafe}
</td>
</tr>
<tr> <tr>
<th>{$form.f.changelog.labelTag}:</th> <th>{$form.f.changelog.labelTag}:</th>
<td>{if $form.f.changelog.errors}{$form.f.changelog.fieldErrors}{/if} <td>{if $form.f.changelog.errors}{$form.f.changelog.fieldErrors}{/if}

View File

@ -4,9 +4,14 @@
<div class="download-file"> <div class="download-file">
{if $deprecated}<p class="smaller">{blocktrans}<strong>Attention!</strong> This file is marked as deprecated, download it only if you are sure you need this specific version.{/blocktrans}</p>{/if} {if $deprecated}<p class="smaller">{blocktrans}<strong>Attention!</strong> This file is marked as deprecated, download it only if you are sure you need this specific version.{/blocktrans}</p>{/if}
<a href="{url 'IDF_Views_Download::download', array($project.shortname, $file.file)}">{$file}</a> - {$file.filesize|size} <a href="{url 'IDF_Views_Download::download', array($project.shortname, $file.file)}">{$file}</a> - {$file.filesize|size}
{if !$file.ext_file}
<br /> <br />
<span class="helptext">{trans 'md5:'} {$file.md5}</span> <span class="helptext">{trans 'md5:'} {$file.md5}</span>
{/if}
</div> </div>
{if $file.changelog} {if $file.changelog}
<h2 class="changes">{trans 'Changes'}</h2> <h2 class="changes">{trans 'Changes'}</h2>