Some background on the implementation of File Uploads:
Files are derived from
DbProjItem which means that they are combined of an
item part and the actual data stored in the
file table:
| field | comments |
|---|
| id | item id |
| name | name of version displayed in Streber |
| org_filename | original filename taken from the browser upload |
| tmp_filename | starting with id and striped from special chars |
| tmp_dir | relative directory on hard drive |
| mimetype | taken from the browser upload |
| status | status like 'new', 'completed', etc. |
| filesize | in bytes |
| version | int of version |
| is_image | DEPRECIATED (done with mime type) |
| is_latest | if several version of an original are uploaded, the one with this flag is the latest version |
| parent_item | id of a task / folder |
| org_file | 0 if original, item-id if later version of an original |
| thumbnail | - reserved - |
| description | long text description of file (version) |
There are two complex things about Files: Upload procedure and Version control.
Upload procedure:
- File Upload Form rendered (e.g. in
task_view.inc.php) and looks like:
<div style="text-align:left;margin-left:3px">Attach<br />
<input type="hidden" name="parent_task" value="{$task->id}">
<input type="hidden" name="MAX_FILE_SIZE" value="'.confGet('FILE_UPLOAD_SIZE_MAX').'" />
<input id="userfiletask" name="userfile" type="file" size="15" accept="*" /><br />
<input style="margin-top:5px;margin-bottom:5px;margin-left:0px" class="button"
type="button" value="Upload"
onclick='document.my_form.go.value="filesUpload";document.my_form.submit();'/>
</div>
- on submit
files.inc.php -> filesUpload() is been called.
- get Project and Parent task
- creates temporary File object by calling...
db/class_file -> File::getUploaded()
- does the actual PHP file upload procedure:
- checks for errors
- get name, filesize, mimetype etc.
- creates a temporary name without special chars
- temporarily moves the file to
_tmp/uploads with php's move_uploaded_file() function
- returns...
- File object if things worked fine
- NULL on failure
- After getting the tempory File-object and a temporyr copy
filesUpload renders a fileEdit form. Here the user can adjust the display name and the comment. Because the file is still not been stored in the database all file attributes are stored in hidden fields.
- On submit
fileEditSubmit is been called:
- restores the File-Object from hidde and form fields
- if File has id=0 it calls File::insert() which
- File::insert() an overwritten version of the original DbProjectItem - function
- it moves the temporarily uploaded file to
_files directory. And there in the project's directory, if necessary creating it on the fly.
- It then inserts the Object to the database and by this finally gets a valid Item id.
- it prepends this Id to the filename and the
tmp_filename file of the Object, which is then been updated once more.
- After inserting the file was successful, fileEditSubmit checks if the uploaded file was a new version of an original file. If so, the original file's
is_latest-field is been cleared.
For a lot of reasons this procedure can fail. Because we want no inconsistent database state, the insertion is delayed as far as possible. The main problem here, is that we do not have the Item Id until the File object is been stored. But we need this Id for the tempoary file name. Some kind of han egg problem.
2. File versions
For uploading newer versions of a file we use the fields:
org_file and
is_latest. With the function File::getLatest() when can check if the requested has
is_latest == 1 and if not get the latest one from the database:
from db/class_file.inc.php
$files= File::getAll(array(
'latest_only' => true,
'org_file' => $org_file,
'project' => $project->id,
'order_by' => 'created DESC',
));
Get latest is used for all file download requests: if you request an image in a
wiki text, Streber always gives you the latest version of it.