Compare commits

...

1 Commits

Author SHA1 Message Date
fc2c72a49b 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>
2016-02-16 21:03:02 +01:00
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;
}
}