Added files, and especiall upload files facility.

Main contribution from Fabian Murer, as part of ETH student project.
Supervised, refactorized and merged by Jocelyn Fiat.

Signed-off-by: Fabian Murer <fmurer@student.ethz.ch>
Signed-off-by: Jocelyn Fiat <git@djoce.net>
This commit is contained in:
2016-02-16 21:03:02 +01:00
parent bc07aad01b
commit fc2c72a49b
19 changed files with 4821 additions and 6 deletions

View File

@@ -8,7 +8,7 @@
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" void_safety="all" syntax="transitional">
<option debug="true" warning="true" full_class_checking="false" is_attached_by_default="true" is_obsolete_routine_type="true" void_safety="all" syntax="transitional">
<debug name="dbglog" enabled="true"/>
</option>
<setting name="executable_name" value="demo"/>
@@ -28,6 +28,7 @@
<library name="cms_demo_module" location="modules\demo\cms_demo_module-safe.ecf" readonly="false"/>
<library name="cms_email_service" location="..\..\library\email\email-safe.ecf" readonly="false"/>
<library name="cms_feed_aggregator_module" location="..\..\modules\feed_aggregator\feed_aggregator-safe.ecf" readonly="false"/>
<library name="cms_files_module" location="..\..\modules\files\files-safe.ecf" readonly="false"/>
<library name="cms_google_search_module" location="..\..\modules\google_search\google_search-safe.ecf" readonly="false" use_application_options="true"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf" readonly="false"/>
<library name="cms_node_module" location="..\..\modules\node\node-safe.ecf" readonly="false"/>
@@ -37,12 +38,9 @@
<library name="cms_seo_module" location="..\..\modules\seo\seo-safe.ecf" readonly="false"/>
<library name="cms_session_auth_module" location="..\..\modules\session_auth\cms_session_auth-safe.ecf" readonly="false"/>
<library name="cms_taxnomy_module" location="..\..\modules\taxonomy\taxonomy-safe.ecf" readonly="false"/>
<library name="persistence_sqlite3" location="..\..\library\persistence\sqlite3\sqlite3-safe.ecf" readonly="false">
<option>
<assertions/>
</option>
</library>
<library name="persistence_sqlite3" location="..\..\library\persistence\sqlite3\sqlite3-safe.ecf" readonly="false"/>
<!--
By default, commented, since it depends on specific environment settings.
<library name="persistence_store_odbc" location="..\..\library\persistence\store_odbc\store_odbc-safe.ecf"/>
<library name="persistence_store_mysql" location="..\..\library\persistence\store_mysql\store_mysql-safe.ecf" />
-->

View File

@@ -1,2 +1,3 @@
port=9090
#port=12345
#verbose=true

View File

@@ -16,3 +16,4 @@ set ROC_CMS_DIR=%~dp0
%ROC_CMD% install --module ..\..\modules\seo --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\session_auth --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\taxonomy --dir %ROC_CMS_DIR%
%ROC_CMD% install --module ..\..\modules\files --dir %ROC_CMS_DIR%

View File

@@ -0,0 +1,61 @@
.uploaded-files {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
}
.uploaded-files th {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
.uploaded-files td {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
.uploaded-files a.button {
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
}
.uploaded-files a.button:hover {
color: black;
text-decoration: none;
}
.upload-files a.upload-button {
margin: auto;
width: 100px;
color: black;
text-decoration: none;
}
.upload-files a.upload-button:hover {
color: black;
text-decoration: none;
}
/******************* Drop Zone *******************/
.dropzone {
width: 100%;
border: 2px dashed blue;
border-radius: 3px;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
}
.dropzone .dz-message {
cursor: pointer;
}
.dropzone .dz-preview, .dropzone .dz-file-preview, .dropzone .dz-processing, .dropzone .dz-success,
.dropzone .dz-image-preview, .dropzone .dz-complete {
padding: 10px;
margin: 10px;
width: auto;
display: inline-block;
}
.dropzone .dz-preview {
border: 1px solid black;
border-radius: 5px;
background: #e6e6e6;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,73 @@
.uploaded-files {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
th {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
td {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
a.button{
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
&:hover {
color: black;
text-decoration: none;
}
}
}
.upload-files {
a.upload-button{
margin: auto;
width: 100px;
color: black;
text-decoration: none;
&:hover {
color: black;
text-decoration: none;
}
}
}
/******************* Drop Zone *******************/
.dropzone {
width: 100%;
border: 2px dashed blue;
border-radius: 3px;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
.dz-message {
cursor: pointer;
}
.dz-preview, .dz-file-preview, .dz-processing, .dz-success,
.dz-image-preview, .dz-complete {
padding: 10px;
margin: 10px;
width: auto;
display: inline-block;
}
.dz-preview {
border: 1px solid black;
border-radius: 5px;
background: #e6e6e6;
}
}

View File

@@ -57,6 +57,9 @@ feature -- CMS modules
a_setup.register_module (create {CMS_NODE_MODULE}.make (a_setup))
a_setup.register_module (create {CMS_BLOG_MODULE}.make)
-- Files
a_setup.register_module (create {CMS_FILES_MODULE}.make)
-- Contact
a_setup.register_module (create {CMS_CONTACT_MODULE}.make)
@@ -76,6 +79,7 @@ feature -- CMS modules
a_setup.register_module (create {GOOGLE_CUSTOM_SEARCH_MODULE}.make)
a_setup.register_module (create {CMS_DEBUG_MODULE}.make)
a_setup.register_module (create {CMS_DEMO_MODULE}.make)
end
end

69
modules/files/cms_file.e Normal file
View File

@@ -0,0 +1,69 @@
note
description: "Interface representing any files under `{CMS_API}.files_location' ."
date: "$Date$"
revision: "$Revision$"
class
CMS_FILE
create
make
feature {NONE} -- Initializaion
make (a_relative_path: PATH; a_api: CMS_API)
do
cms_api := a_api
relative_path := a_relative_path
end
cms_api: CMS_API
feature -- Access
filename: STRING_32
-- File name of Current file.
local
p: PATH
do
p := relative_path
if attached p.entry as e then
Result := e.name
else
Result := p.name
end
end
relative_path: PATH
-- Path relative the `CMS_API.files_location'.
owner: detachable CMS_USER
-- Optional owner.
feature -- Status report
is_directory: BOOLEAN
local
d: DIRECTORY
do
create d.make_with_path (cms_api.files_location.extended_path (relative_path))
Result := d.exists
end
is_file: BOOLEAN
local
f: RAW_FILE
do
create f.make_with_path (cms_api.files_location.extended_path (relative_path))
Result := f.exists
end
feature -- Element change
set_owner (u: detachable CMS_USER)
-- Set `owner' to `u'.
do
owner := u
end
end

View File

@@ -0,0 +1,57 @@
note
description: "Metadata associated to a CMS_FILE."
date: "$Date$"
revision: "$Revision$"
class
CMS_FILE_METADATA
create
make,
make_empty
feature {NONE} -- Initialization
make_empty
do
create date.make_now_utc
end
make (f: CMS_FILE)
do
make_empty
end
feature -- Access
user: detachable CMS_USER
date: detachable DATE_TIME
size: INTEGER
file_type: detachable READABLE_STRING_8
feature -- Element change
set_user (u: detachable CMS_USER)
do
user := u
end
set_date (dt: detachable DATE_TIME)
do
date := dt
end
set_size (a_size: INTEGER)
do
size := a_size
end
set_file_type (a_type: detachable READABLE_STRING_8)
do
file_type := a_type
end
end

View File

@@ -0,0 +1,270 @@
note
description: "API to manage files."
date: "$Date$"
revision: "$Revision$"
class
CMS_FILES_API
inherit
CMS_MODULE_API
REFACTORING_HELPER
create
make
feature -- Access : path
uploads_relative_path: PATH
-- Path relative to `{CMS_API}.files_location'.
do
create Result.make_from_string (uploads_directory_name)
end
uploads_directory: PATH
do
Result := cms_api.files_location.extended (uploads_directory_name)
end
uploaded_file_path (f: READABLE_STRING_GENERAL): PATH
do
Result := uploads_directory.extended (f)
end
thumbnail_directory: PATH
do
Result := uploads_directory.extended (thumbnail_directory_name)
end
feature {CMS_FILES_MODULE} -- Access : metadata path
metadata_path (f: READABLE_STRING_GENERAL): PATH
do
Result := metadata_directory.extended (f).appended_with_extension ("cms-metadata")
end
metadata_directory: PATH
do
Result := uploads_directory.extended (metadata_directory_name)
end
feature -- Access : links
file_link (f: CMS_FILE): CMS_LOCAL_LINK
local
s: STRING
do
s := "files"
across
f.relative_path.components as ic
loop
s.append_character ('/')
s.append (percent_encoded (ic.item.name))
end
create Result.make (f.filename, s)
end
feature {NONE} -- Constants
uploads_directory_name: STRING = "uploads"
metadata_directory_name: STRING = ".metadata"
thumbnail_directory_name: STRING = ".thumbnails"
feature -- Factory
new_file (a_relative_path: PATH): CMS_FILE
-- New CMS_FILE for path `a_relative_path' relative to `files' directory.
do
create Result.make (a_relative_path, cms_api)
end
new_uploads_file (p: PATH): CMS_FILE
-- New uploaded path from `p' relative to `uploads_directory'.
do
create Result.make (uploads_relative_path.extended_path (p), cms_api)
end
feature -- Storage
delete_file (fn: READABLE_STRING_GENERAL)
-- Delete file at `fn'.
local
p: PATH
do
error_handler.reset
p := uploaded_file_path (fn)
safe_delete (p)
if not has_error then
p := metadata_path (fn)
safe_delete (p)
end
end
save_uploaded_file (f: CMS_UPLOADED_FILE)
local
p: PATH
ut: FILE_UTILITIES
stored: BOOLEAN
original_name: STRING_32
n: INTEGER_32
finished: BOOLEAN
do
reset_error
create original_name.make_from_string (f.filename)
p := f.location
if not p.is_absolute then
p := uploads_directory.extended_path (p)
end
if ut.file_path_exists (p) then
from
n := 1
until
finished
loop
if ut.file_path_exists (p) then
f.set_new_location_with_number (n)
p := f.location
if p.is_absolute then
else
p := uploads_directory.extended_path (p)
end
n := n + 1
else
finished := True
end
end
stored := f.move_to (p)
else
-- move file to path
stored := f.move_to (p)
end
if not stored then
error_handler.add_custom_error (-1, "uploaded file storage failed", "Issue occurred when saving uploaded file!")
end
end
save_metadata_from_uploaded_file (a_uploaded_file: CMS_UPLOADED_FILE; u: detachable CMS_USER)
local
f: detachable RAW_FILE
h_date: HTTP_DATE
retried: BOOLEAN
do
if not retried then
-- FIXME: Report error?
if f /= Void and then not f.is_closed then
f.close
end
else
-- create a file for metadata
create f.make_with_path (metadata_path (a_uploaded_file.filename))
if f.exists then
f.open_write
else
f.create_read_write
end
-- insert username
if u /= Void then
f.put_integer_64 (u.id)
f.put_new_line
-- f.put_string (utf.utf_32_string_to_utf_8_string_8 (u.name))
-- f.put_new_line
else
f.put_new_line
f.put_new_line
end
-- insert uploaded_time
create h_date.make_now_utc
f.put_integer_64 (h_date.timestamp)
f.put_new_line
-- insert size of file
f.put_string (a_uploaded_file.size.out)
f.put_new_line
-- insert file type
if attached a_uploaded_file.type as type then
f.put_string (type.out)
f.put_new_line
end
f.close
end
rescue
retried := True
retry
end
metadata (a_cms_file: CMS_FILE): detachable CMS_FILE_METADATA
local
f: RAW_FILE
s: READABLE_STRING_8
do
if attached metadata_path (a_cms_file.filename) as p then
create f.make_with_path (p)
if f.exists and then f.is_access_readable then
create Result.make_empty
f.open_read
f.read_line
s := f.last_string
if s.is_integer_64 then
Result.set_user (cms_api.user_api.user_by_id (s.to_integer_64))
else
Result.set_user (cms_api.user_api.user_by_name (s))
end
f.read_line
s := f.last_string
if s.is_integer_64 then
Result.set_date ((create {HTTP_DATE}.make_from_timestamp (s.to_integer_64)).date_time)
end
f.read_line
s := f.last_string
if s.is_integer_32 then
Result.set_size (s.to_integer_32)
else
Result.set_size (-1)
end
if not f.end_of_file then
f.read_line
Result.set_file_type (f.last_string)
end
f.close
end
end
end
safe_delete (p: PATH)
-- Safe remove file at path `p'.
local
f: RAW_FILE
retried: BOOLEAN
do
if retried then
error_handler.add_custom_error (-1, "Can not delete file", {STRING_32} "Can not delete file %"" + p.name + "%"")
else
create f.make_with_path (p)
if f.exists then
f.delete
else
-- Not considered as failure.
end
end
rescue
retried := True
retry
end
end

View File

@@ -0,0 +1,495 @@
note
description: "files module."
date: "$Date$"
revision: "$Revision$"
class
CMS_FILES_MODULE
inherit
CMS_MODULE
rename
module_api as files_api
redefine
install,
initialize,
setup_hooks,
permissions,
files_api
end
CMS_HOOK_MENU_SYSTEM_ALTER
SHARED_EXECUTION_ENVIRONMENT
create
make
feature {NONE} -- Initialization
make
do
name := "files"
version := "1.0"
description := "Service to upload files, and manage them."
package := "file"
end
feature -- Access
name: STRING
permissions: LIST [READABLE_STRING_8]
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("admin files")
Result.force ("upload files")
end
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
-- <Precursor>
do
Precursor (api)
if files_api = Void then
create files_api.make (api)
end
end
feature {CMS_API}-- Module management
install (api: CMS_API)
-- install the module
local
l_files_api: like files_api
d: DIRECTORY
do
create l_files_api.make (api)
create d.make_with_path (l_files_api.uploads_directory)
if not d.exists then
d.recursive_create_dir
end
create d.make_with_path (l_files_api.metadata_directory)
if not d.exists then
d.recursive_create_dir
end
files_api := l_files_api
Precursor (api)
end
feature {CMS_API} -- Access: API
files_api: detachable CMS_FILES_API
-- <Precursor>
feature -- Access: router
setup_router (a_router: WSF_ROUTER; a_api: CMS_API)
-- <Precursor>
do
map_uri_template_agent (a_router, "/" + uploads_location, agent execute_upload (?, ?, a_api), Void) -- Accepts any method GET, HEAD, POST, PUT, DELETE, ...
map_uri_template_agent (a_router, "/" + uploads_location + "{filename}", agent display_uploaded_file_info (?, ?, a_api), a_router.methods_get)
map_uri_template_agent (a_router, "/" + uploads_location + "remove/{filename}", agent remove (?, ?, a_api), a_router.methods_get)
end
uploads_location: STRING = "upload/"
feature -- Hooks
setup_hooks (a_hooks: CMS_HOOK_CORE_MANAGER)
do
a_hooks.subscribe_to_menu_system_alter_hook (Current)
end
menu_system_alter (a_menu_system: CMS_MENU_SYSTEM; a_response: CMS_RESPONSE)
local
link: CMS_LOCAL_LINK
do
-- login in demo did somehow not work
if a_response.has_permission ("upload files") then
create link.make ("Upload files", uploads_location)
a_menu_system.navigation_menu.extend (link)
end
end
feature -- Handler
execute_not_found_handler (uri: READABLE_STRING_8; req: WSF_REQUEST; res: WSF_RESPONSE)
-- `uri' is not found, redirect to default page
do
res.redirect_now_with_content (req.script_url ("/"), uri + ": not found. %N Redirection to" + req.script_url ("/"), "text/html")
end
display_uploaded_file_info (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
-- Display information related to a cms uploaded file.
local
body: STRING_8
r: CMS_RESPONSE
f: CMS_FILE
md: detachable CMS_FILE_METADATA
fn: READABLE_STRING_32
do
check req.is_get_request_method end
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
-- add style
r.add_style (r.url ("/module/" + name + "/files/css/files.css", Void), Void)
create body.make_empty
if attached {WSF_STRING} req.path_parameter ("filename") as p_filename then
fn := p_filename.value
r.set_page_title ({STRING_32} "File %"" + fn + {STRING_32} "%"")
body.append ("<div class=%"uploaded-files%">%N") -- To ease css customization.
if attached files_api as l_files_api then
f := l_files_api.new_uploads_file (create {PATH}.make_from_string (fn))
body.append ("<div class=%"metadata%">")
md := l_files_api.metadata (f)
--| Uploader user
body.append ("<strong>User: </strong>")
if md /= Void and then attached md.user as meta_user then
body.append (api.html_encoded (meta_user.name))
else
body.append ("unknown user")
end
body.append ("<br/>%N")
--| Uploaded date
body.append ("<strong>Upload Time: </strong>")
if md /= Void and then attached md.date as meta_time then
body.append (meta_time.out)
else
body.append ("NA")
end
body.append ("<br/>%N")
--| File size
body.append ("<strong> File Size: </strong>")
if md /= Void and then md.size > 0 then
body.append (file_size_human_string (md.size))
else
body.append ("NA")
end
body.append ("<br/>%N")
--| File type
body.append ("<strong>File Type: </strong>")
if md /= Void and then attached md.file_type as meta_type then
body.append (meta_type)
else
body.append ("NA")
end
body.append ("<br/><br/>%N")
body.append ("<a class=%"button%" href=%"" + req.script_url ("/" + l_files_api.file_link (f).location) + "%">Download</a>%N")
body.append ("<a class=%"button%" href=%"" + req.script_url ("/" + uploads_location + "remove/" + f.filename) + "%">Remove</a>%N")
body.append ("</div>%N") -- metadata
body.append ("<div class=%"overview%">")
if
attached f.relative_path.extension as ext and then
(
ext.is_case_insensitive_equal_general ("png")
or ext.is_case_insensitive_equal_general ("jpg")
or ext.is_case_insensitive_equal_general ("gif")
)
then
body.append ("<img src=%"" + req.script_url ("/" + l_files_api.file_link (f).location) + "%" />")
else
-- add default thumbnail
body.append ("<img src=%"" + req.script_url ("/module/" + name + "/files/img/file-logo.png") + "%" />")
end
body.append ("</div>%N") -- Overview
end
body.append ("</div>%N")
end
r.add_to_primary_tabs (create {CMS_LOCAL_LINK}.make ("Uploaded files", uploads_location))
r.set_main_content (body)
r.execute
end
execute_upload (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
body: STRING_8
r: CMS_RESPONSE
do
if req.is_get_head_request_method or req.is_post_request_method then
create body.make_empty
body.append ("<h1> Upload files </h1>%N")
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
-- set style
r.add_style (r.url ("/module/" + name + "/files/css/files.css", void), void)
-- add JS for dropzone
r.add_javascript_url (r.url ("/module/" + name + "/files/js/dropzone.js", void))
if r.has_permission ("upload files") then
-- create body
body.append ("<p>Please choose some file(s) to upload.</p>")
-- create form to choose files and upload them
body.append ("<div class=%"upload-files%">")
body.append ("<form action=%"" + r.url (uploads_location, Void) + "%" class=%"dropzone%">")
body.append ("</form>%N")
body.append ("<a class=%"button%" href=%""+ r.url (uploads_location, Void) +"%">Upload Files</a>")
body.append ("</div>")
if req.is_post_request_method then
process_uploaded_files (req, api, body)
end
else
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
end
-- Build the response.
append_uploaded_file_album_to (req, api, body)
r.set_main_content (body)
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
end
r.execute
end
process_uploaded_files (req: WSF_REQUEST; api: CMS_API; a_output: STRING)
-- show all newly uploaded files
local
l_uploaded_file: CMS_UPLOADED_FILE
uf: WSF_UPLOADED_FILE
do
if attached files_api as l_files_api then
-- if has uploaded files, then store them
if req.has_uploaded_file then
a_output.append ("<strong>Newly uploaded file(s): </strong>%N")
a_output.append ("<ul class=%"uploaded-files%">")
across
req.uploaded_files as ic
loop
uf := ic.item
create l_uploaded_file.make_with_uploaded_file (l_files_api.uploads_directory, uf)
a_output.append ("<li>")
a_output.append (api.html_encoded (l_uploaded_file.filename))
-- store the just uploaded file
l_files_api.save_uploaded_file (l_uploaded_file)
-- create medadata file
l_uploaded_file.set_size (uf.size)
l_uploaded_file.set_type (uf.content_type)
l_files_api.save_metadata_from_uploaded_file (l_uploaded_file, api.current_user (req))
if l_files_api.has_error then
a_output.append (" <span class=%"error%">: upload failed!</span>")
end
a_output.append ("</li>")
end
a_output.append ("</ul>%N")
end
end
end
append_uploaded_file_album_to (req: WSF_REQUEST; api: CMS_API; a_output: STRING)
local
d: DIRECTORY
f: CMS_FILE
p: PATH
rel: PATH
md: detachable CMS_FILE_METADATA
size: INTEGER
do
if attached files_api as l_files_api then
rel := l_files_api.uploads_relative_path
p := l_files_api.uploads_directory
a_output.append ("<strong>Uploaded files:</strong>%N")
a_output.append ("<table class=%"uploaded-files%">%N")
a_output.append ("<tr><th>Filename</th><th>Uploading Time</th><th>User</th><th>Size</th><th></th><th></th></tr>%N")
create d.make_with_path (p)
if d.exists then
across
d.entries as ic
loop
if ic.item.is_current_symbol then
-- Ignore
elseif ic.item.is_parent_symbol then
-- Ignore for now.
else
f := l_files_api.new_file (rel.extended_path (ic.item))
-- check if f is directory -> yes, then do not show
if not f.is_directory then
a_output.append ("<tr>")
-- add filename
a_output.append ("<td class=%"filename%">")
a_output.append ("<a href=%"" + api.percent_encoded (f.filename) + "%">")
a_output.append (api.html_encoded (f.filename))
a_output.append ("</a>")
a_output.append ("</td>%N")
md := l_files_api.metadata (f)
if md = Void then
a_output.append ("<td class=%"date%"></td>%N")
a_output.append ("<td class=%"user%"></td>%N")
a_output.append ("<td class=%"size%"></td>%N")
else
-- add uploading time
a_output.append ("<td class=%"date%">")
if attached md.date as meta_time then
a_output.append (meta_time.out)
end
a_output.append ("</td>%N")
-- add user
a_output.append ("<td class=%"user%">")
if attached md.user as u then
a_output.append (api.html_encoded (u.name))
end
a_output.append ("</td>%N")
-- add size
a_output.append ("<td class=%"size%">")
if md.size > 0 then
size := md.size
if size >= 1000000 then
size := size // 1000000
a_output.append (size.out + " MB")
else
if size >= 1000 then
size := size // 1000
a_output.append (size.out + " kB")
else
a_output.append (size.out + " bytes")
end
end
else
a_output.append ("NA")
end
a_output.append ("</td>%N")
end
-- add download link
a_output.append ("<td>")
a_output.append ("<button><a class=%"download-button%" href=%"" + req.script_url ("/" + l_files_api.file_link (f).location) + "%" download>Download</a></button>%N")
a_output.append ("</td>%N")
-- add remove button
a_output.append ("<td>")
a_output.append ("<button><a class=%"download-button%" href=%"" + req.script_url ("/" + uploads_location + "remove/" + f.filename) + "%">Remove</a></button>%N")
a_output.append ("</td>%N")
a_output.append ("</tr>%N")
else
if f.relative_path.is_current_symbol or f.relative_path.is_parent_symbol then
-- Ignore "." and ".."
else
-- folder support not yet supported
-- -- add directory identifier
-- a_output.append ("<td>[dir]</td>%N")
-- a_output.append ("<td>")
-- a_output.append ("<a href=%"" + api.percent_encoded (f.filename) + "%">")
-- a_output.append (api.html_encoded (f.filename))
-- a_output.append ("</a>")
-- a_output.append ("</td>%N")
-- a_output.append ("<td></td><td></td><td></td><td></td><td></td>")
end
end
end
end
end
a_output.append ("</table>%N")
end
end
remove (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
local
body: STRING
r: CMS_RESPONSE
err: BOOLEAN
do
if attached files_api as l_files_api then
if attached {WSF_STRING} req.path_parameter ("filename") as p_filename then
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
l_files_api.delete_file (p_filename.value)
err := l_files_api.has_error
l_files_api.reset_error
create body.make_empty
if err then
body.append ("<h3>The file has been removed successfully!</h3>")
else
body.append ("<h3>The file removal failed!</h3>")
end
r.add_to_primary_tabs (create {CMS_LOCAL_LINK}.make ("Uploaded files", uploads_location))
r.set_main_content (body)
else
create {BAD_REQUEST_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.add_error_message ("Missing 'filename' parameter!")
end
else
create {INTERNAL_SERVER_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.set_main_content ("Removal of file failed due to internal server error!")
end
r.execute
end
feature -- Helpers
file_size_human_string (a_size: INTEGER): STRING
local
size: INTEGER
do
size := a_size
if size >= 1000000 then
size := size // 1000000
Result := size.out + " MB"
else
if size >= 1000 then
size := size // 1000
Result := size.out + " kB"
else
Result := size.out + " bytes"
end
end
end
feature -- Mapping helper: uri template agent (analogue to the demo-module)
map_uri_template (a_router: WSF_ROUTER; a_tpl: STRING; h: WSF_URI_TEMPLATE_HANDLER; rqst_methods: detachable WSF_REQUEST_METHODS)
-- Map `h' as handler for `a_tpl', according to `rqst_methods'.
require
a_tpl_attached: a_tpl /= Void
h_attached: h /= Void
do
a_router.map (create {WSF_URI_TEMPLATE_MAPPING}.make (a_tpl, h), rqst_methods)
end
map_uri_template_agent (a_router: WSF_ROUTER; a_tpl: READABLE_STRING_8; proc: PROCEDURE [TUPLE [req: WSF_REQUEST; res: WSF_RESPONSE]]; rqst_methods: detachable WSF_REQUEST_METHODS)
-- Map `proc' as handler for `a_tpl', according to `rqst_methods'.
require
a_tpl_attached: a_tpl /= Void
proc_attached: proc /= Void
do
map_uri_template (a_router, a_tpl, create {WSF_URI_TEMPLATE_AGENT_HANDLER}.make (proc), rqst_methods)
end
end

View File

@@ -0,0 +1,105 @@
note
description: "Summary description for {CMS_UPLOADED_FILE}."
date: "$Date$"
revision: "$Revision$"
class
CMS_UPLOADED_FILE
create
make_with_uploaded_file
feature {NONE} -- Initializaion
make_with_uploaded_file (a_uploads_directory: PATH; uf: WSF_UPLOADED_FILE)
do
uploads_directory := a_uploads_directory
uploaded_file := uf
location := a_uploads_directory.extended (uf.safe_filename)
end
feature -- Access
uploaded_file: WSF_UPLOADED_FILE
uploads_directory: PATH
filename: STRING_32
-- File name of Current file.
local
p: PATH
do
p := location
if attached p.entry as e then
Result := e.name
else
Result := p.name
end
end
location: PATH
-- Absolute path, or relative path to the `CMS_API.files_location'.
owner: detachable CMS_USER
-- Optional owner.
upload_time: detachable DATE_TIME
-- time when the file was uploaded
size: detachable INTEGER_32
-- file size
type: detachable STRING
-- file type
feature -- Element change
set_owner (u: detachable CMS_USER)
-- Set `owner' to `u'.
do
owner := u
end
set_time (a_time: detachable DATE_TIME)
-- Set `upload_time' to `a_time'
do
upload_time := a_time
end
set_size (a_size: detachable INTEGER_32)
-- Set `size' to `a_size'
do
size := a_size
end
set_type (a_type: detachable STRING)
-- Set `type' to `a_type'
do
type := a_type
end
set_new_location_with_number (a_number: INTEGER_32)
-- sets `a_number' after the name. This is done when the file was already uploaded
local
position: INTEGER_32
new_name: STRING_8
do
position := uploaded_file.string_representation.index_of ('.', 1)
create new_name.make_empty
new_name := uploaded_file.string_representation.head (position-1)
new_name.append ("_(" + a_number.out + ")")
new_name.append (uploaded_file.string_representation.substring (position, uploaded_file.string_representation.count))
location := uploads_directory.extended (new_name)
end
feature -- Basic operation
move_to (p: PATH): BOOLEAN
do
Result := uploaded_file.move_to (p.name)
end
end

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="files_module" uuid="795C88E5-9218-4F35-A985-5501340E2D9D" library_target="files_module">
<target name="files_module">
<root all_classes="true" />
<file_rule>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard"/>
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
<library name="cms" location="..\..\cms-safe.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model-safe.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http-safe.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json-safe.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf-safe.ecf"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder-safe.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

22
modules/files/files.ecf Normal file
View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-15-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-15-0 http://www.eiffel.com/developers/xml/configuration-1-15-0.xsd" name="files_module" uuid="795C88E5-9218-4F35-A985-5501340E2D9D" library_target="files_module">
<target name="files_module">
<root all_classes="true"/>
<file_rule>
<exclude>/.svn$</exclude>
<exclude>/CVS$</exclude>
<exclude>/EIFGENs$</exclude>
</file_rule>
<option warning="true" full_class_checking="true" void_safety="none" syntax="standard">
</option>
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
<library name="cms" location="..\..\cms.ecf"/>
<library name="cms_model" location="..\..\library\model\cms_model.ecf"/>
<library name="http" location="$ISE_LIBRARY\contrib\library\network\protocol\http\http.ecf"/>
<library name="json" location="$ISE_LIBRARY\contrib\library\text\parser\json\library\json.ecf"/>
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
<library name="wsf" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\wsf\wsf.ecf"/>
<library name="wsf_encoder" location="$ISE_LIBRARY\contrib\library\web\framework\ewf\text\encoder\encoder.ecf"/>
<cluster name="src" location=".\" recursive="true"/>
</target>
</system>

View File

@@ -0,0 +1,61 @@
.uploaded-files {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
}
.uploaded-files th {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
.uploaded-files td {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
.uploaded-files a.button {
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
}
.uploaded-files a.button:hover {
color: black;
text-decoration: none;
}
.upload-files a.upload-button {
margin: auto;
width: 100px;
color: black;
text-decoration: none;
}
.upload-files a.upload-button:hover {
color: black;
text-decoration: none;
}
/******************* Drop Zone *******************/
.dropzone {
width: 100%;
border: 2px dashed blue;
border-radius: 3px;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
}
.dropzone .dz-message {
cursor: pointer;
}
.dropzone .dz-preview, .dropzone .dz-file-preview, .dropzone .dz-processing, .dropzone .dz-success,
.dropzone .dz-image-preview, .dropzone .dz-complete {
padding: 10px;
margin: 10px;
width: auto;
display: inline-block;
}
.dropzone .dz-preview {
border: 1px solid black;
border-radius: 5px;
background: #e6e6e6;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,73 @@
.uploaded-files {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
th {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
td {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
a.button{
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
&:hover {
color: black;
text-decoration: none;
}
}
}
.upload-files {
a.upload-button{
margin: auto;
width: 100px;
color: black;
text-decoration: none;
&:hover {
color: black;
text-decoration: none;
}
}
}
/******************* Drop Zone *******************/
.dropzone {
width: 100%;
border: 2px dashed blue;
border-radius: 3px;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
.dz-message {
cursor: pointer;
}
.dz-preview, .dz-file-preview, .dz-processing, .dz-success,
.dz-image-preview, .dz-complete {
padding: 10px;
margin: 10px;
width: auto;
display: inline-block;
}
.dz-preview {
border: 1px solid black;
border-radius: 5px;
background: #e6e6e6;
}
}