Compare commits

..

2 Commits

Author SHA1 Message Date
75332c148d Added protection/permissions. 2016-02-17 12:24:58 +01:00
b54fd85172 Added files module, with for now, a focus on upload files facility.
Contribution from Fabian Murer, as part of an 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-17 12:03:24 +01:00
22 changed files with 7660 additions and 3457 deletions

View File

@@ -1,3 +1,4 @@
@echo off
setlocal
set ROC_CMD=call %~dp0..\..\tools\roc.bat
set ROC_CMS_DIR=%~dp0

View File

@@ -1,38 +1,44 @@
.uploaded-files {
.uploaded-files table {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
}
.uploaded-files th {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
.uploaded-files table th {
padding: 3px 0 3px 5px;
}
.uploaded-files td {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
.uploaded-files table td {
padding: 3px 0 3px 5px;
}
.uploaded-files a.button {
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
}
.uploaded-files a.button:hover {
color: black;
text-decoration: none;
border: solid 1px #06f;
background-color: #cff;
}
.upload-files a.upload-button {
.upload-files .center {
text-align: center;
padding: 10px;
}
.upload-files a.button {
margin: auto;
width: 100px;
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
}
.upload-files a.upload-button:hover {
.upload-files a.button:hover {
color: black;
text-decoration: none;
border: solid 1px #06f;
background-color: #cff;
}
/******************* Drop Zone *******************/
@@ -44,18 +50,3 @@
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;
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
/*
* The MIT License
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
.dropzone, .dropzone * {
box-sizing: border-box;
}
.dropzone {
position: relative;
.dz-preview {
position: relative;
display: inline-block;
width: 120px;
margin: 0.5em;
.dz-progress {
display: block;
height: 15px;
border: 1px solid #aaa;
.dz-upload {
display: block;
height: 100%;
width: 0;
background: green;
}
}
.dz-error-message {
color: red;
display: none;
}
&.dz-error {
.dz-error-message, .dz-error-mark {
display: block;
}
}
&.dz-success {
.dz-success-mark {
display: block;
}
}
.dz-error-mark, .dz-success-mark {
position: absolute;
display: none;
left: 30px;
top: 30px;
width: 54px;
height: 58px;
left: 50%;
margin-left: -(54px/2);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,413 @@
/*
* The MIT License
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
@mixin keyframes($name) {
@-webkit-keyframes #{$name} {
@content;
}
@-moz-keyframes #{$name} {
@content;
}
@keyframes #{$name} {
@content;
}
}
@mixin prefix($map, $vendors: webkit moz ms o) {
@each $prop, $value in $map {
@if $vendors {
@each $vendor in $vendors {
#{"-" + $vendor + "-" + $prop}: #{$value};
}
}
// Dump regular property anyway
#{$prop}: #{$value};
}
}
@include keyframes(passing-through) {
0% {
opacity: 0;
@include prefix((transform: translateY(40px)));
}
30%, 70% {
opacity: 1;
@include prefix((transform: translateY(0px)));
}
100% {
opacity: 0;
@include prefix((transform: translateY(-40px)));
}
}
@include keyframes(slide-in) {
0% {
opacity: 0;
@include prefix((transform: translateY(40px)));
}
30% {
opacity: 1;
@include prefix((transform: translateY(0px)));
}
}
@include keyframes(pulse) {
0% { @include prefix((transform: scale(1))); }
10% { @include prefix((transform: scale(1.1))); }
20% { @include prefix((transform: scale(1))); }
}
.dropzone, .dropzone * {
box-sizing: border-box;
}
.dropzone {
$image-size: 120px;
$image-border-radius: 20px;
&.dz-clickable {
cursor: pointer;
* {
cursor: default;
}
.dz-message {
&, * {
cursor: pointer;
}
}
}
min-height: 150px;
border: 2px solid rgba(0, 0, 0, 0.3);
background: white;
padding: 20px 20px;
&.dz-started {
.dz-message {
display: none;
}
}
&.dz-drag-hover {
border-style: solid;
.dz-message {
opacity: 0.5;
}
}
.dz-message {
text-align: center;
margin: 2em 0;
}
.dz-preview {
position: relative;
display: inline-block;
vertical-align: top;
margin: 16px;
min-height: 100px;
&:hover {
// Making sure that always the hovered preview element is on top
z-index: 1000;
.dz-details {
opacity: 1;
}
}
&.dz-file-preview {
.dz-image {
border-radius: $image-border-radius;
background: #999;
background: linear-gradient(to bottom, #eee, #ddd);
}
.dz-details {
opacity: 1;
}
}
&.dz-image-preview {
background: white;
.dz-details {
@include prefix((transition: opacity 0.2s linear));
}
}
.dz-remove {
font-size: 14px;
text-align: center;
display: block;
cursor: pointer;
border: none;
&:hover {
text-decoration: underline;
}
}
&:hover .dz-details {
opacity: 1;
}
.dz-details {
$background-color: #444;
z-index: 20;
position: absolute;
top: 0;
left: 0;
opacity: 0;
font-size: 13px;
min-width: 100%;
max-width: 100%;
padding: 2em 1em;
text-align: center;
color: rgba(0, 0, 0, 0.9);
$width: 120px;
line-height: 150%;
.dz-size {
margin-bottom: 1em;
font-size: 16px;
}
.dz-filename {
white-space: nowrap;
&:hover {
span {
border: 1px solid rgba(200, 200, 200, 0.8);
background-color: rgba(255, 255, 255, 0.8);
}
}
&:not(:hover) {
span {
border: 1px solid transparent;
}
overflow: hidden;
text-overflow: ellipsis;
}
}
.dz-filename, .dz-size {
span {
background-color: rgba(255, 255, 255, 0.4);
padding: 0 0.4em;
border-radius: 3px;
}
}
}
&:hover {
.dz-image {
// opacity: 0.8;
img {
@include prefix((transform: scale(1.05, 1.05))); // Getting rid of that white bleed-in
@include prefix((filter: blur(8px)), webkit); // Getting rid of that white bleed-in
}
}
}
.dz-image {
border-radius: $image-border-radius;
overflow: hidden;
width: $image-size;
height: $image-size;
position: relative;
display: block;
z-index: 10;
img {
display: block;
}
}
&.dz-success {
.dz-success-mark {
@include prefix((animation: passing-through 3s cubic-bezier(0.770, 0.000, 0.175, 1.000)));
}
}
&.dz-error {
.dz-error-mark {
opacity: 1;
@include prefix((animation: slide-in 3s cubic-bezier(0.770, 0.000, 0.175, 1.000)));
}
}
.dz-success-mark, .dz-error-mark {
$image-height: 54px;
$image-width: 54px;
pointer-events: none;
opacity: 0;
z-index: 500;
position: absolute;
display: block;
top: 50%;
left: 50%;
margin-left: -($image-width/2);
margin-top: -($image-height/2);
svg {
display: block;
width: $image-width;
height: $image-height;
}
}
&.dz-processing .dz-progress {
opacity: 1;
@include prefix((transition: all 0.2s linear));
}
&.dz-complete .dz-progress {
opacity: 0;
@include prefix((transition: opacity 0.4s ease-in));
}
&:not(.dz-processing) {
.dz-progress {
@include prefix((animation: pulse 6s ease infinite));
}
}
.dz-progress {
opacity: 1;
z-index: 1000;
pointer-events: none;
position: absolute;
height: 16px;
left: 50%;
top: 50%;
margin-top: -8px;
width: 80px;
margin-left: -40px;
// border: 2px solid #333;
background: rgba(255, 255, 255, 0.9);
// Fix for chrome bug: https://code.google.com/p/chromium/issues/detail?id=157218
-webkit-transform: scale(1);
border-radius: 8px;
overflow: hidden;
.dz-upload {
background: #333;
background: linear-gradient(to bottom, #666, #444);
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 0;
@include prefix((transition: width 300ms ease-in-out));
}
}
&.dz-error {
.dz-error-message {
display: block;
}
&:hover .dz-error-message {
opacity: 1;
pointer-events: auto;
}
}
.dz-error-message {
$width: $image-size + 20px;
$color: rgb(190, 38, 38);
pointer-events: none;
z-index: 1000;
position: absolute;
display: block;
display: none;
opacity: 0;
@include prefix((transition: opacity 0.3s ease));
border-radius: 8px;
font-size: 13px;
top: $image-size + 10px;
left: -10px;
width: $width;
background: $color;
background: linear-gradient(to bottom, $color, darken($color, 5%));
padding: 0.5em 1.2em;
color: white;
// The triangle pointing up
&:after {
content: '';
position: absolute;
top: -6px;
left: $width / 2 - 6px;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid $color;
}
}
}
}

View File

@@ -1,18 +1,16 @@
.uploaded-files {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
table {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
th {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
th {
padding: 3px 0 3px 5px;
}
td {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
td {
padding: 3px 0 3px 5px;
}
}
a.button{
@@ -20,23 +18,33 @@
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
&:hover {
color: black;
text-decoration: none;
border: solid 1px #06f;
background-color: #cff;
}
}
}
.upload-files {
a.upload-button{
.center {
text-align: center;
padding: 10px;
}
a.button{
margin: auto;
width: 100px;
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
&:hover {
color: black;
text-decoration: none;
border: solid 1px #06f;
background-color: #cff;
}
}
}
@@ -51,23 +59,5 @@
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

@@ -103,7 +103,7 @@ feature -- Storage
end
end
save_uploaded_file (f: CMS_UPLOADED_FILE)
save_uploaded_file (uf: CMS_UPLOADED_FILE)
local
p: PATH
ut: FILE_UTILITIES
@@ -113,9 +113,9 @@ feature -- Storage
finished: BOOLEAN
do
reset_error
create original_name.make_from_string (f.filename)
create original_name.make_from_string (uf.filename)
p := f.location
p := uf.location
if not p.is_absolute then
p := uploads_directory.extended_path (p)
end
@@ -128,8 +128,8 @@ feature -- Storage
finished
loop
if ut.file_path_exists (p) then
f.set_new_location_with_number (n)
p := f.location
uf.set_new_location_with_number (n)
p := uf.location
if p.is_absolute then
else
p := uploads_directory.extended_path (p)
@@ -139,13 +139,15 @@ feature -- Storage
finished := True
end
end
stored := f.move_to (p)
stored := uf.move_to (p)
else
-- move file to path
stored := f.move_to (p)
stored := uf.move_to (p)
end
if not stored then
if stored then
save_metadata_from_uploaded_file (uf, cms_api.user)
else
error_handler.add_custom_error (-1, "uploaded file storage failed", "Issue occurred when saving uploaded file!")
end
end
@@ -156,7 +158,7 @@ feature -- Storage
h_date: HTTP_DATE
retried: BOOLEAN
do
if not retried then
if retried then
-- FIXME: Report error?
if f /= Void and then not f.is_closed then
f.close
@@ -172,7 +174,7 @@ feature -- Storage
end
-- insert username
if u /= Void then
f.put_integer_64 (u.id)
f.put_string (u.id.out)
f.put_new_line
-- f.put_string (utf.utf_32_string_to_utf_8_string_8 (u.name))
-- f.put_new_line
@@ -182,7 +184,7 @@ feature -- Storage
end
-- insert uploaded_time
create h_date.make_now_utc
f.put_integer_64 (h_date.timestamp)
f.put_string (h_date.timestamp.out)
f.put_new_line
-- insert size of file

View File

@@ -43,10 +43,15 @@ feature -- Access
-- List of permission ids, used by this module, and declared.
do
Result := Precursor
Result.force ("admin files")
Result.force ("upload files")
Result.force (admin_files_permission)
Result.force (upload_files_permission)
Result.force (browse_files_permission)
end
admin_files_permission: STRING = "admin files"
upload_files_permission: STRING = "upload files"
browse_files_permission: STRING = "browse files"
feature {CMS_API} -- Module Initialization
initialize (api: CMS_API)
@@ -92,7 +97,7 @@ feature -- Access: router
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)
map_uri_template_agent (a_router, "/" + uploads_location + "remove/{filename}", agent remove_file (?, ?, a_api), a_router.methods_get)
end
uploads_location: STRING = "upload/"
@@ -109,7 +114,7 @@ feature -- Hooks
link: CMS_LOCAL_LINK
do
-- login in demo did somehow not work
if a_response.has_permission ("upload files") then
if a_response.has_permission (upload_files_permission) then
create link.make ("Upload files", uploads_location)
a_menu_system.navigation_menu.extend (link)
end
@@ -133,13 +138,17 @@ feature -- Handler
fn: READABLE_STRING_32
do
check req.is_get_request_method end
create {GENERIC_VIEW_CMS_RESPONSE} r.make (req, res, api)
if not api.has_permission (browse_files_permission) then
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.add_error_message ("You are not allowed to browse CMS files!")
elseif attached {WSF_STRING} req.path_parameter ("filename") as p_filename then
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)
-- add style
r.add_style (r.url ("/module/" + name + "/files/css/files.css", Void), Void)
create body.make_empty
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.
@@ -207,9 +216,13 @@ feature -- Handler
body.append ("</div>%N") -- Overview
end
body.append ("</div>%N")
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.set_main_content ("Missing 'filename' field value!")
end
r.add_to_primary_tabs (create {CMS_LOCAL_LINK}.make ("Uploaded files", uploads_location))
r.set_main_content (body)
r.execute
end
@@ -229,8 +242,9 @@ feature -- Handler
-- add JS for dropzone
r.add_javascript_url (r.url ("/module/" + name + "/files/js/dropzone.js", void))
r.add_style (r.url ("/module/" + name + "/files/js/dropzone.css", void), void)
if r.has_permission ("upload files") then
if api.has_permission (upload_files_permission) then
-- create body
body.append ("<p>Please choose some file(s) to upload.</p>")
@@ -238,7 +252,7 @@ feature -- Handler
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 class=%"center%"><a class=%"button%" href=%""+ r.url (uploads_location, Void) +"%">Upload Files</a></div>")
body.append ("</div>")
if req.is_post_request_method then
process_uploaded_files (req, api, body)
@@ -248,8 +262,11 @@ feature -- Handler
end
-- Build the response.
append_uploaded_file_album_to (req, api, body)
if r.has_permission (browse_files_permission) then
append_uploaded_file_album_to (req, api, body)
else
r.add_warning_message ("You are not allowed to browse files!")
end
r.set_main_content (body)
else
@@ -259,7 +276,9 @@ feature -- Handler
end
process_uploaded_files (req: WSF_REQUEST; api: CMS_API; a_output: STRING)
-- show all newly uploaded files
-- Process http request uploaded files.
require
has_permission: api.has_permission (upload_files_permission)
local
l_uploaded_file: CMS_UPLOADED_FILE
uf: WSF_UPLOADED_FILE
@@ -277,14 +296,12 @@ feature -- Handler
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))
-- store the just uploaded file
l_files_api.save_uploaded_file (l_uploaded_file)
if l_files_api.has_error then
a_output.append (" <span class=%"error%">: upload failed!</span>")
@@ -303,14 +320,14 @@ feature -- Handler
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 ("<div class=%"uploaded-files%">%N")
a_output.append ("<strong>Uploaded files:</strong>%N")
a_output.append ("<table class=%"uploaded-files%">%N")
a_output.append ("<table>%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)
@@ -361,18 +378,7 @@ feature -- Handler
-- 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
a_output.append (file_size_human_string (md.size))
else
a_output.append ("NA")
end
@@ -381,12 +387,12 @@ feature -- Handler
-- 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 ("<a class=%"button%" href=%"" + req.script_url ("/" + l_files_api.file_link (f).location) + "%" download>Download</a>%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 ("<a class=%"button%" href=%"" + req.script_url ("/" + uploads_location + "remove/" + f.filename) + "%">Remove</a>%N")
a_output.append ("</td>%N")
a_output.append ("</tr>%N")
@@ -414,18 +420,21 @@ feature -- Handler
end
end
a_output.append ("</table>%N")
a_output.append ("</div>%N")
end
end
remove (req: WSF_REQUEST; res: WSF_RESPONSE; api: CMS_API)
remove_file (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
if not api.has_permission (admin_files_permission) then
create {FORBIDDEN_ERROR_CMS_RESPONSE} r.make (req, res, api)
r.add_error_message ("You are not allowed to remove file!")
elseif 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)

View File

@@ -1,38 +1,44 @@
.uploaded-files {
.uploaded-files table {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
}
.uploaded-files th {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
.uploaded-files table th {
padding: 3px 0 3px 5px;
}
.uploaded-files td {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
.uploaded-files table td {
padding: 3px 0 3px 5px;
}
.uploaded-files a.button {
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
}
.uploaded-files a.button:hover {
color: black;
text-decoration: none;
border: solid 1px #06f;
background-color: #cff;
}
.upload-files a.upload-button {
.upload-files .center {
text-align: center;
padding: 10px;
}
.upload-files a.button {
margin: auto;
width: 100px;
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
}
.upload-files a.upload-button:hover {
.upload-files a.button:hover {
color: black;
text-decoration: none;
border: solid 1px #06f;
background-color: #cff;
}
/******************* Drop Zone *******************/
@@ -44,18 +50,3 @@
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;
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,78 @@
/*
* The MIT License
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
.dropzone, .dropzone * {
box-sizing: border-box;
}
.dropzone {
position: relative;
.dz-preview {
position: relative;
display: inline-block;
width: 120px;
margin: 0.5em;
.dz-progress {
display: block;
height: 15px;
border: 1px solid #aaa;
.dz-upload {
display: block;
height: 100%;
width: 0;
background: green;
}
}
.dz-error-message {
color: red;
display: none;
}
&.dz-error {
.dz-error-message, .dz-error-mark {
display: block;
}
}
&.dz-success {
.dz-success-mark {
display: block;
}
}
.dz-error-mark, .dz-success-mark {
position: absolute;
display: none;
left: 30px;
top: 30px;
width: 54px;
height: 58px;
left: 50%;
margin-left: -(54px/2);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,413 @@
/*
* The MIT License
* Copyright (c) 2012 Matias Meno <m@tias.me>
*/
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
@mixin keyframes($name) {
@-webkit-keyframes #{$name} {
@content;
}
@-moz-keyframes #{$name} {
@content;
}
@keyframes #{$name} {
@content;
}
}
@mixin prefix($map, $vendors: webkit moz ms o) {
@each $prop, $value in $map {
@if $vendors {
@each $vendor in $vendors {
#{"-" + $vendor + "-" + $prop}: #{$value};
}
}
// Dump regular property anyway
#{$prop}: #{$value};
}
}
@include keyframes(passing-through) {
0% {
opacity: 0;
@include prefix((transform: translateY(40px)));
}
30%, 70% {
opacity: 1;
@include prefix((transform: translateY(0px)));
}
100% {
opacity: 0;
@include prefix((transform: translateY(-40px)));
}
}
@include keyframes(slide-in) {
0% {
opacity: 0;
@include prefix((transform: translateY(40px)));
}
30% {
opacity: 1;
@include prefix((transform: translateY(0px)));
}
}
@include keyframes(pulse) {
0% { @include prefix((transform: scale(1))); }
10% { @include prefix((transform: scale(1.1))); }
20% { @include prefix((transform: scale(1))); }
}
.dropzone, .dropzone * {
box-sizing: border-box;
}
.dropzone {
$image-size: 120px;
$image-border-radius: 20px;
&.dz-clickable {
cursor: pointer;
* {
cursor: default;
}
.dz-message {
&, * {
cursor: pointer;
}
}
}
min-height: 150px;
border: 2px solid rgba(0, 0, 0, 0.3);
background: white;
padding: 20px 20px;
&.dz-started {
.dz-message {
display: none;
}
}
&.dz-drag-hover {
border-style: solid;
.dz-message {
opacity: 0.5;
}
}
.dz-message {
text-align: center;
margin: 2em 0;
}
.dz-preview {
position: relative;
display: inline-block;
vertical-align: top;
margin: 16px;
min-height: 100px;
&:hover {
// Making sure that always the hovered preview element is on top
z-index: 1000;
.dz-details {
opacity: 1;
}
}
&.dz-file-preview {
.dz-image {
border-radius: $image-border-radius;
background: #999;
background: linear-gradient(to bottom, #eee, #ddd);
}
.dz-details {
opacity: 1;
}
}
&.dz-image-preview {
background: white;
.dz-details {
@include prefix((transition: opacity 0.2s linear));
}
}
.dz-remove {
font-size: 14px;
text-align: center;
display: block;
cursor: pointer;
border: none;
&:hover {
text-decoration: underline;
}
}
&:hover .dz-details {
opacity: 1;
}
.dz-details {
$background-color: #444;
z-index: 20;
position: absolute;
top: 0;
left: 0;
opacity: 0;
font-size: 13px;
min-width: 100%;
max-width: 100%;
padding: 2em 1em;
text-align: center;
color: rgba(0, 0, 0, 0.9);
$width: 120px;
line-height: 150%;
.dz-size {
margin-bottom: 1em;
font-size: 16px;
}
.dz-filename {
white-space: nowrap;
&:hover {
span {
border: 1px solid rgba(200, 200, 200, 0.8);
background-color: rgba(255, 255, 255, 0.8);
}
}
&:not(:hover) {
span {
border: 1px solid transparent;
}
overflow: hidden;
text-overflow: ellipsis;
}
}
.dz-filename, .dz-size {
span {
background-color: rgba(255, 255, 255, 0.4);
padding: 0 0.4em;
border-radius: 3px;
}
}
}
&:hover {
.dz-image {
// opacity: 0.8;
img {
@include prefix((transform: scale(1.05, 1.05))); // Getting rid of that white bleed-in
@include prefix((filter: blur(8px)), webkit); // Getting rid of that white bleed-in
}
}
}
.dz-image {
border-radius: $image-border-radius;
overflow: hidden;
width: $image-size;
height: $image-size;
position: relative;
display: block;
z-index: 10;
img {
display: block;
}
}
&.dz-success {
.dz-success-mark {
@include prefix((animation: passing-through 3s cubic-bezier(0.770, 0.000, 0.175, 1.000)));
}
}
&.dz-error {
.dz-error-mark {
opacity: 1;
@include prefix((animation: slide-in 3s cubic-bezier(0.770, 0.000, 0.175, 1.000)));
}
}
.dz-success-mark, .dz-error-mark {
$image-height: 54px;
$image-width: 54px;
pointer-events: none;
opacity: 0;
z-index: 500;
position: absolute;
display: block;
top: 50%;
left: 50%;
margin-left: -($image-width/2);
margin-top: -($image-height/2);
svg {
display: block;
width: $image-width;
height: $image-height;
}
}
&.dz-processing .dz-progress {
opacity: 1;
@include prefix((transition: all 0.2s linear));
}
&.dz-complete .dz-progress {
opacity: 0;
@include prefix((transition: opacity 0.4s ease-in));
}
&:not(.dz-processing) {
.dz-progress {
@include prefix((animation: pulse 6s ease infinite));
}
}
.dz-progress {
opacity: 1;
z-index: 1000;
pointer-events: none;
position: absolute;
height: 16px;
left: 50%;
top: 50%;
margin-top: -8px;
width: 80px;
margin-left: -40px;
// border: 2px solid #333;
background: rgba(255, 255, 255, 0.9);
// Fix for chrome bug: https://code.google.com/p/chromium/issues/detail?id=157218
-webkit-transform: scale(1);
border-radius: 8px;
overflow: hidden;
.dz-upload {
background: #333;
background: linear-gradient(to bottom, #666, #444);
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 0;
@include prefix((transition: width 300ms ease-in-out));
}
}
&.dz-error {
.dz-error-message {
display: block;
}
&:hover .dz-error-message {
opacity: 1;
pointer-events: auto;
}
}
.dz-error-message {
$width: $image-size + 20px;
$color: rgb(190, 38, 38);
pointer-events: none;
z-index: 1000;
position: absolute;
display: block;
display: none;
opacity: 0;
@include prefix((transition: opacity 0.3s ease));
border-radius: 8px;
font-size: 13px;
top: $image-size + 10px;
left: -10px;
width: $width;
background: $color;
background: linear-gradient(to bottom, $color, darken($color, 5%));
padding: 0.5em 1.2em;
color: white;
// The triangle pointing up
&:after {
content: '';
position: absolute;
top: -6px;
left: $width / 2 - 6px;
width: 0;
height: 0;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid $color;
}
}
}
}

View File

@@ -1,18 +1,16 @@
.uploaded-files {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
table {
width: 100%;
border-collapse: collapse;
border: 1px solid black;
th {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
th {
padding: 3px 0 3px 5px;
}
td {
padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
td {
padding: 3px 0 3px 5px;
}
}
a.button{
@@ -20,23 +18,33 @@
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
&:hover {
color: black;
text-decoration: none;
border: solid 1px #06f;
background-color: #cff;
}
}
}
.upload-files {
a.upload-button{
.center {
text-align: center;
padding: 10px;
}
a.button{
margin: auto;
width: 100px;
color: black;
text-decoration: none;
border: solid 1px #999;
background-color: #ddd;
padding: 2px 4px 2px 4px;
&:hover {
color: black;
text-decoration: none;
border: solid 1px #06f;
background-color: #cff;
}
}
}
@@ -51,23 +59,5 @@
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

@@ -9,38 +9,38 @@ deferred class
feature -- Initialisation
load_assets : STRING
-- Loads all assest needed to show the editor
load_assets: STRING
-- Loads all assest needed to show the editor.
deferred
end
feature -- Javascript
javascript_replace_textarea (a_textarea : WSF_FORM_TEXTAREA): STRING
-- Javascript code that replaces a textarea with the editor. The editor instance should be saved in editor_variable
javascript_replace_textarea (a_textarea: WSF_FORM_TEXTAREA): STRING
-- Javascript code that replaces a textarea with the editor. The editor instance should be saved in editor_variable.
deferred
end
javascript_restore_textarea (a_textarea : WSF_FORM_TEXTAREA): STRING
javascript_restore_textarea (a_textarea: WSF_FORM_TEXTAREA): STRING
-- Javascript code that restores a textarea
deferred
end
javascript_textarea_to_editor (a_textarea : WSF_FORM_TEXTAREA): STRING
-- Javascript code to display the textarea as a WYSIWIG editor as soon as the document is loaded
javascript_textarea_to_editor (a_textarea: WSF_FORM_TEXTAREA): STRING
-- Javascript code to display the textarea as a WYSIWIG editor as soon as the document is loaded.
do
Result := javascript_ready (javascript_replace_textarea (a_textarea))
end
javascript_textarea_to_editor_if_selected (a_textarea: WSF_FORM_TEXTAREA; a_select_field : WSF_FORM_SELECT; a_value : STRING) : STRING
-- Javascript code to display the textarea as a WYSIWIG editor if a_select_field has a_value
javascript_textarea_to_editor_if_selected (a_textarea: WSF_FORM_TEXTAREA; a_select_field: WSF_FORM_SELECT; a_value: STRING): STRING
-- Javascript code to display the textarea as a WYSIWIG editor if a_select_field has a_value,
local
initial_replace_code, on_select_replace_code: STRING
do
-- Javascript that replaces the textarea if a_value is selected at load time
-- Javascript that replaces the textarea if a_value is selected at load time
initial_replace_code := javascript_ready (javascript_if_selected (a_select_field, a_value, javascript_replace_textarea (a_textarea)))
-- Javascript code that replaces the textarea as soon as value is selected at a_select_field
-- Javascript code that replaces the textarea as soon as value is selected at a_select_field
on_select_replace_code := javascript_ready(
javascript_init_editor_variable (a_textarea) +
javascript_on_select (a_select_field, a_value,
@@ -55,54 +55,56 @@ feature -- Javascript
Result := initial_replace_code + " " + on_select_replace_code
end
javascript_init_editor_variable (a_textarea : WSF_FORM_TEXTAREA) : STRING
-- Returns the javascript code that initializes a local variable to store the editor instance
javascript_init_editor_variable (a_textarea: WSF_FORM_TEXTAREA): STRING
-- Returns the javascript code that initializes a local variable to store the editor instance.
do
Result := "var " + editor_variable (a_textarea) + "; "
end
javascript_if_selected (a_select_field : WSF_FORM_SELECT; a_value : STRING; a_code : STRING) : STRING
-- Javascript that executes a_code if a_value is selected at a_select_field
javascript_if_selected (a_select_field: WSF_FORM_SELECT; a_value: STRING; a_code: STRING): STRING
-- Javascript that executes a_code if a_value is selected at a_select_field.
do
Result := "if($('#" + field_id (a_select_field) + "').val() == %"" + a_value + "%"){ " + a_code + " }"
end
javascript_ready (a_code : STRING) : STRING
-- Wraps the given javascript code with a ready statement, such that it's executed when the document has loaded
javascript_ready (a_code: STRING): STRING
-- Wraps the given javascript code with a ready statement,
-- such that it's executed when the document has loaded.
do
Result := "$(function() { " + a_code + " });"
end
javascript_on_select (a_select_field : WSF_FORM_SELECT; a_value : STRING; a_then : STRING; a_else : STRING) : STRING
-- Javascript code that executes a_then if at the given select_field the given string value is selected, otherwise it executes a_else
javascript_on_select (a_select_field: WSF_FORM_SELECT; a_value: STRING; a_then: STRING; a_else: STRING): STRING
-- Javascript code that executes `a_then' if at the given `a_select_field'
-- the given string `a_value' is selected, otherwise it executes `a_else'.
do
Result := "$('#" + field_id (a_select_field) + "').change(function(){" +
javascript_if_selected (a_select_field, a_value, a_then) +
"else{" +
a_else +
"}" +
"});"
Result := "$('#" + field_id (a_select_field) + "').change(function(){"
+ javascript_if_selected (a_select_field, a_value, a_then)
+ "else{"
+ a_else
+ "}"
+ "});"
end
feature -- Helper
field_id(a_select_field : WSF_FORM_SELECT) : STRING
-- Returns the id of the given field
field_id (a_select_field: WSF_FORM_SELECT): STRING
-- Id of the given field.
do
if attached a_select_field.css_id as a_id then
if attached a_select_field.css_id as a_id then
Result := a_id
else
Result := a_select_field.name + "-select"
end
end
editor_variable (a_textarea : WSF_FORM_TEXTAREA) : STRING
editor_variable (a_textarea: WSF_FORM_TEXTAREA): STRING
-- Returns the variable name that stores the editor instance of the given textarea
do
Result := "cms_ckeditor_" + a_textarea.name
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -12,7 +12,7 @@ inherit
feature -- Initialisation
load_assets : STRING
load_assets: STRING
-- <Precursor>
do
Result := "<script src=%"//cdn.ckeditor.com/4.4.7/standard/ckeditor.js%"></script>"
@@ -20,23 +20,25 @@ feature -- Initialisation
feature -- Javascript
javascript_replace_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING
javascript_replace_textarea (a_textarea: WSF_FORM_TEXTAREA): STRING
-- <Precursor>
do
-- Replaces the textarea with an editor instance. Save the instance in a variable
-- Replaces the textarea with an editor instance.
-- Save the instance in a variable.
Result := "$(%"textarea[name="+ a_textarea.name +"]%").each(function() {"
Result.append (editor_variable (a_textarea) + " = CKEDITOR.replace(this);")
Result.append ("});")
end
javascript_restore_textarea (a_textarea : WSF_FORM_TEXTAREA) : STRING
javascript_restore_textarea (a_textarea: WSF_FORM_TEXTAREA): STRING
-- <Precursor>
do
-- Replaces the textarea with an editor instance. Save the instance in a variable
-- Replaces the textarea with an editor instance.
-- Save the instance in a variable.
Result := "if (" + editor_variable (a_textarea) + " != undefined) " + editor_variable (a_textarea) + ".destroy();"
end
note
copyright: "2011-2015, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
end

View File

@@ -282,7 +282,9 @@ feature -- Head customization
local
s: STRING_8
do
s := "<link rel=%"stylesheet%" href=%""+ a_href + "%" type=%"text/css%""
create s.make_from_string ("<link rel=%"stylesheet%" href=%"")
s.append (a_href)
s.append ("%" type=%"text/css%"")
if a_media /= Void then
s.append (" media=%""+ a_media + "%"")
end
@@ -290,11 +292,24 @@ feature -- Head customization
add_additional_head_line (s, False)
end
add_style_content (a_style_content: STRING)
-- Add style content `a_style_content' in the head, using <style> tag.
local
s: STRING_8
do
create s.make_from_string ("<style>%N")
s.append (a_style_content)
s.append ("%N</style>")
add_additional_head_line (s, True)
end
add_javascript_url (a_src: STRING)
local
s: STRING_8
do
s := "<script type=%"text/javascript%" src=%"" + a_src + "%"></script>"
create s.make_from_string ("<script type=%"text/javascript%" src=%"")
s.append (a_src)
s.append ("%"></script>")
add_additional_head_line (s, False)
end
@@ -302,7 +317,9 @@ feature -- Head customization
local
s: STRING_8
do
s := "<script type=%"text/javascript%">%N" + a_script + "%N</script>"
create s.make_from_string ("<script type=%"text/javascript%">%N")
s.append (a_script)
s.append ("%N</script>")
add_additional_head_line (s, True)
end

View File

@@ -144,12 +144,17 @@ feature -- Element change
title := s
end
add_additional_head_line (s: READABLE_STRING_8)
do
head_lines.extend (s)
end
add_meta_name_content (a_name: STRING; a_content: STRING)
local
s: STRING_8
do
s := "<meta name=%"" + a_name + "%" content=%"" + a_content + "%" />"
head_lines.extend (s)
add_additional_head_line (s)
end
add_meta_http_equiv (a_http_equiv: STRING; a_content: STRING)
@@ -157,39 +162,56 @@ feature -- Element change
s: STRING_8
do
s := "<meta http-equiv=%"" + a_http_equiv + "%" content=%"" + a_content + "%" />"
head_lines.extend (s)
add_additional_head_line (s)
end
add_style (a_href: STRING; a_media: detachable STRING)
local
s: STRING_8
do
s := "<link rel=%"stylesheet%" href=%""+ a_href + "%" type=%"text/css%""
create s.make_from_string ("<link rel=%"stylesheet%" href=%"")
s.append (a_href)
s.append ("%" type=%"text/css%"")
if a_media /= Void then
s.append (" media=%""+ a_media + "%"")
end
s.append ("/>")
head_lines.extend (s)
add_additional_head_line (s)
end
add_style_content (a_style_content: STRING)
-- Add style content `a_style_content' in the head, using <style> tag.
local
s: STRING_8
do
create s.make_from_string ("<style>%N")
s.append (a_style_content)
s.append ("%N</style>")
add_additional_head_line (s)
end
add_javascript_url (a_src: STRING)
local
s: STRING_8
do
s := "<script type=%"text/javascript%" src=%"" + a_src + "%"></script>"
head_lines.extend (s)
create s.make_from_string ("<script type=%"text/javascript%" src=%"")
s.append (a_src)
s.append ("%"></script>")
add_additional_head_line (s)
end
add_javascript_content (a_script: STRING)
local
s: STRING_8
do
s := "<script type=%"text/javascript%">%N" + a_script + "%N</script>"
head_lines.extend (s)
create s.make_from_string ("<script type=%"text/javascript%">%N")
s.append (a_script)
s.append ("%N</script>")
add_additional_head_line (s)
end
note
copyright: "2011-2014, Jocelyn Fiat, Eiffel Software and others"
copyright: "2011-2016, Jocelyn Fiat, Javier Velilla, Eiffel Software and others"
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
source: "[
Eiffel Software

View File

@@ -30,7 +30,7 @@ set ROC_TOOL_PATH=%~dp0
goto START
:START
echo Calling %ROC_TOOL_PATH%roc.exe %*
rem echo Calling %ROC_TOOL_PATH%roc.exe %*
call %ROC_TOOL_PATH%roc.exe %*
goto END