Index: openacs-4/packages/xowiki/tcl/bootstrap-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/bootstrap-procs.tcl,v diff -u -N -r1.3.2.1 -r1.3.2.2 --- openacs-4/packages/xowiki/tcl/bootstrap-procs.tcl 9 Feb 2016 11:06:34 -0000 1.3.2.1 +++ openacs-4/packages/xowiki/tcl/bootstrap-procs.tcl 21 Mar 2016 09:50:34 -0000 1.3.2.2 @@ -25,6 +25,7 @@ -superclass Menu \ -parameter { {autorender false} + {menubar} {containerClass "container"} {navbarClass "navbar navbar-default navbar-static-top"} } @@ -35,18 +36,143 @@ set js [parameter::get_global_value -package_key xowiki -parameter BootstrapJS] foreach url $css {::xo::Page requireCSS $url} foreach url $js {::xo::Page requireJS $url} + #::xo::Page requireJS "/resources/xowiki/dropzone.js" next } + + BootstrapNavbar instproc dropzoneJS {-uploadlink:required} { + + ::html::script -type "text/javascript" { + html::t [subst -nocommands { + + function($) { + 'use strict'; + + var dropZone = document.getElementById('drop-zone'); + var uploadForm = document.getElementById('js-upload-form'); + var progressBar = document.getElementById('dropzone-progress-bar'); + var uploadFileRunning = 0; + + var startUpload = function(files) { + if (typeof files !== "undefined") { + for (var i=0, l=files.length; i URL = $url" + + if {$url ne ""} { + # + # Do actually render the dropzone widget. + # + my dropzone -uploadlink $url + } + } + } } } @@ -112,7 +238,9 @@ ::xowiki::MenuBar instproc render-bootstrap {} { set M [my content] set mb [::xowiki::BootstrapNavbar \ - -id [my get_prop $M id] { + -id [my get_prop $M id] \ + -menubar [self] \ + { foreach {menu_att menu} $M { if {$menu_att eq "id"} continue # Index: openacs-4/packages/xowiki/tcl/folder-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/folder-procs.tcl,v diff -u -N -r1.33.2.3 -r1.33.2.4 --- openacs-4/packages/xowiki/tcl/folder-procs.tcl 9 Feb 2016 11:06:34 -0000 1.33.2.3 +++ openacs-4/packages/xowiki/tcl/folder-procs.tcl 21 Mar 2016 09:50:34 -0000 1.33.2.4 @@ -275,19 +275,19 @@ -parent_id $opt_parent_id \ -nls_language $nls_language -return_url $return_url] } - # set new_page_link [$package_id make_link -with_entities 0 \ + # set new_page_link [$package_id make_link \ # $package_id edit-new \ # {object_type ::xowiki::Page} \ # parent_id return_url autoname template_file] set new_page_link [$package_id make_form_link -form en:page.form \ -parent_id $opt_parent_id \ -return_url $return_url] - set new_file_link [$package_id make_link -with_entities 0 \ + set new_file_link [$package_id make_link \ $package_id edit-new \ {object_type ::xowiki::File} \ parent_id return_url autoname template_file] - set new_form_link [$package_id make_link -with_entities 0 \ + set new_form_link [$package_id make_link \ $package_id edit-new \ {object_type ::xowiki::Form} \ parent_id return_url autoname template_file] Index: openacs-4/packages/xowiki/tcl/form-field-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/form-field-procs.tcl,v diff -u -N -r1.248.2.17 -r1.248.2.18 --- openacs-4/packages/xowiki/tcl/form-field-procs.tcl 15 Mar 2016 10:27:22 -0000 1.248.2.17 +++ openacs-4/packages/xowiki/tcl/form-field-procs.tcl 21 Mar 2016 09:50:35 -0000 1.248.2.18 @@ -694,28 +694,27 @@ } } - FormField instproc pretty_image {-parent_id:required entry_name} { - if {$entry_name eq ""} return - if {[my set value] eq ""} return - my instvar object value + FormField instproc pretty_image {-parent_id:required {-revision_id ""} entry_name} { + if {$entry_name eq "" || ${:value} eq ""} return - array set "" [$object item_ref -default_lang [$object lang] -parent_id $parent_id $entry_name] + array set "" [${:object} item_ref -default_lang [${:object} lang] -parent_id $parent_id $entry_name] - set label [my label] ;# the label is used for alt und title + set label [my label] ;# the label is used for alt and title if {$label eq $(stripped_name)} { + # # The label is apparently the default. For Photo.form instances, # this is always "image". In such cases, use the title of the # parent object as label. - set label [[my object] title] + # + set label [${:object} title] } set l [::xowiki::Link create new -destroy_on_cleanup \ - -page $object -type "image" -lang $(prefix) \ + -page ${:object} -type "image" -lang $(prefix) \ [list -stripped_name $(stripped_name)] [list -label $label] \ -parent_id $(parent_id) -item_id $(item_id)] if {[my istype file]} { - set revision_id [my get_from_value $value revision_id] if {$revision_id ne ""} { $l revision_id $revision_id } @@ -791,9 +790,10 @@ -extend_slot_default validator virus \ -parameter { {size 40} - {viruscheck true} - {sticky false} - {searchable false} + {viruscheck:boolean true} + {sticky:boolean false} + {searchable:boolean false} + {multiple:boolean true} link_label } file instproc check=virus {value} { @@ -813,12 +813,28 @@ file instproc content-type {value} {my set [self proc] $value} file instproc initialize {} { my type file + my set booleanHTMLAttributes {multiple} my set widget_type file(file) next } - file instproc entry_info {value} { - return [list name file:[my name] parent_id [[my object] item_id]] + file instproc entry_info {} { + if {[my multiple]} { + if {[info exists :tmpfile]} { + set list ${:tmpfile} + } else { + set list [my get_from_value ${:value} name] + } + set objName {} + for {set i 0} {$i < [llength $list]} {incr i} { + lappend objName file:${:name}___$i + } + } else { + set objName file:${:name} + } + #my log ENTRY_INFO=[list name $objName parent_id [${:object} item_id]] + return [list name $objName parent_id [[my object] item_id]] } + file instproc get_from_value {value attribute {raw ""}} { # # The value of of a form entry might be: @@ -857,64 +873,111 @@ return [next] } + file instproc store_file { + -file_name + -content_type + -package_id + -parent_id + -object_name + -tmpfile + -publish_date_cmd + -save_flag + } { + + if {$content_type in { application/octetstream application/force-download }} { + set content_type [::xowiki::guesstype $file_name] + } + + set file_object [$package_id get_page_from_name -name $object_name -parent_id $parent_id] + if {$file_object ne ""} { + # + # File entry exists already, create a new revision + # + #my msg "new revision (value $file_name)" + $file_object set import_file $tmpfile + $file_object set mime_type $content_type + $file_object set title $file_name + eval $publish_date_cmd + $file_object save {*}$save_flag + } else { + # + # Create a new file + # + #my msg "new file" + set file_object [::xowiki::File new -destroy_on_cleanup \ + -title $file_name \ + -name $object_name \ + -parent_id $parent_id \ + -mime_type $content_type \ + -package_id [[my object] package_id] \ + -creation_user [::xo::cc user_id] ] + $file_object set import_file $tmpfile + eval $publish_date_cmd + $file_object save_new {*}$save_flag + } + return $file_object + } + + file instproc convert_to_internal {} { my instvar value if {[my no_value_provided]} { [my object] set_property -new 1 [my name] [my get_old_value] return } - #my msg "[my name]: got value '$value'" - regsub -all {\\+} $value {/} value ;# fix IE upload path - set value [::file tail $value] - [my object] set_property -new 1 [my name] $value + #my log "[my name]: got value '$value'" + #[my object] set_property -new 1 [my name] $value set package_id [[my object] package_id] - array set entry_info [my entry_info $value] + array set entry_info [my entry_info] - set content_type [my set content-type] - if {$content_type eq "application/octetstream" - || $content_type eq "application/force-download" - } { - set content_type [::xowiki::guesstype $value] - } - if {[my searchable]} { set publish_date_cmd {;} set save_flag "" } else { set publish_date_cmd {$file_object set publish_date "9999-12-31 23:59:59.0+01"} set save_flag "-use_given_publish_date true" } - #my msg "mime_type of $entry_info(name) = [::xowiki::guesstype $value] // [my set content-type] ==> $content_type" - set file_object [$package_id get_page_from_name -name $entry_info(name) -parent_id $entry_info(parent_id)] - if {$file_object ne ""} { - # file entry exists already, create a new revision - #my msg "new revision (value $value)" - $file_object set import_file [my set tmpfile] - $file_object set mime_type $content_type - $file_object set title $value - eval $publish_date_cmd - $file_object save {*}$save_flag - } else { - # create a new file - #my msg "new file" - set file_object [::xowiki::File new -destroy_on_cleanup \ - -title $value \ - -name $entry_info(name) \ - -parent_id $entry_info(parent_id) \ - -mime_type $content_type \ - -package_id [[my object] package_id] \ - -creation_user [::xo::cc user_id] ] - $file_object set import_file [my set tmpfile] - eval $publish_date_cmd - $file_object save_new {*}$save_flag + + # + # Make sure that we do not mis-interprete spaces in paths or file + # names. + # + if {[llength ${:content-type}] == 1} { + set :tmpfile [list ${:tmpfile}] + set value [list $value] } + + set revision_ids {} + set newValue "" + foreach content_type ${:content-type} \ + object_name $entry_info(name) \ + tmpfile [my set tmpfile] \ + fn $value { + + regsub -all {\\+} $fn {/} fn ;# fix IE upload path + set v [::file tail $fn] + + set file_object [my store_file \ + -file_name $fn \ + -content_type $content_type \ + -package_id $package_id \ + -parent_id $entry_info(parent_id) \ + -object_name $object_name \ + -tmpfile $tmpfile \ + -publish_date_cmd $publish_date_cmd \ + -save_flag $save_flag] + + lappend revision_ids [$file_object revision_id] + lappend newValue $fn + } + # # Update the value with the attribute value pair list containing # the revision_id. TODO: clear revision_id on export. # - set newValue [list name $value revision_id [$file_object revision_id]] + set newValue [list name $newValue revision_id $revision_ids] [my object] set_property -new 1 [my name] $newValue my set value $newValue } @@ -929,15 +992,23 @@ file instproc pretty_value {v} { if {$v ne ""} { my instvar object - array set "" [my entry_info $v] - array set "" [$object item_ref -default_lang [[my object] lang] -parent_id $(parent_id) $(name)] - #my msg "pretty value name '$(stripped_name)'" - set l [::xowiki::Link create new -destroy_on_cleanup \ - -page $object -type "file" -lang $(prefix) \ - [list -stripped_name $(stripped_name)] [list -label [my label]] \ - [list -extra_query_parameter [list [list filename [my get_from_value $v name $v]]]] \ - -parent_id $(parent_id) -item_id $(item_id)] - return [$l render] + array set "" [my entry_info] + + set result "" + foreach object_name $(name) fn [my get_from_value $v name] { + + array set "" [$object item_ref -default_lang [[my object] lang] -parent_id $(parent_id) $object_name] + + #my log "name <$object_name> pretty value name '$(stripped_name)'" + + set l [::xowiki::Link create new -destroy_on_cleanup \ + -page $object -type "file" -lang $(prefix) \ + [list -stripped_name $(stripped_name)] [list -label $fn] \ + [list -extra_query_parameter [list [list filename $fn]]] \ + -parent_id $(parent_id) -item_id $(item_id)] + append result [$l render] + } + return $result } } @@ -946,19 +1017,9 @@ my instvar value set package_id [[my object] package_id] - array set entry_info [my entry_info $value] - set fn [my get_from_value $value name $value] - #my msg "[my name]: [list my get_from_value <$value> name] => '$fn'" - set href [$package_id pretty_link -download 1 -parent_id $entry_info(parent_id) $entry_info(name)] + array set entry_info [my entry_info] + set fns [my get_from_value $value name $value] - if {![my istype image]} { - append href ?filename=[ns_urlencode $fn] - set revision_id [my get_from_value $value revision_id ""] - if {$revision_id ne "" && [string is integer $revision_id]} { - append href &revision_id=$revision_id - } - } - # # The HTML5 handling of "required" would force us to upload in # every form the file again. To implement the sticky option, we @@ -970,29 +1031,46 @@ } next - if {[info exists reset_required]} { - my set required true - } - ::html::t " " set id __old_value_[my name] ::html::div { ::html::input -type hidden -name $id -id $id -value $value } - ::html::span -class file-control -id __a$id { - ::html::a -href $href {::html::t [my label_or_value $fn] } + ::html::div -class file-control -id __a$id { + foreach \ + object_name $entry_info(name) \ + revision_id [my get_from_value $value revision_id ""] \ + fn $fns { + #my msg "[my name]: [list my get_from_value <$value> name] => '$fn'" + set href [$package_id pretty_link -download 1 -parent_id $entry_info(parent_id) $object_name] + + if {![my istype image]} { + append href ?filename=[ns_urlencode $fn] + if {$revision_id ne "" && [string is integer $revision_id]} { + append href &revision_id=$revision_id + } + } + if {[info exists reset_required]} { + my set required true + } + ::html::div { + ::html::a -href $href {::html::t [my label_or_value $fn] } + } + } + # # Show the clear button just when # - there is something to clear, and # - the formfield is not disabled, and # - the form-field is not sticky (default) - + # set disabled [expr {[my exists disabled] && [my disabled] != "false"}] if {$value ne "" && !$disabled && ![my sticky] } { ::html::input -type button -value [_ xowiki.clear] \ -onClick "document.getElementById('$id').value = ''; document.getElementById('__a$id').style.display = 'none';" } } + } ########################################################### @@ -1013,7 +1091,7 @@ set package_id [$object package_id] set parent_id [$object parent_id] if {$v eq ""} {return ""} - array set "" [my entry_info $v] + array set "" [my entry_info] set fn [my get_from_value $v name $v] # # Get the file object of the imported file to obtain is full name and path @@ -1053,9 +1131,15 @@ border border-width position top botton left right } image instproc pretty_value {v} { - array set "" [my entry_info $v] - - return [my pretty_image -parent_id $(parent_id) $(name)] + set html "" + array set "" [my entry_info] + foreach object_name $(name) revision_id [my get_from_value $v revision_id $v] { + append html [my pretty_image \ + -parent_id $(parent_id) \ + -revision_id $revision_id \ + $object_name] + } + return $html } ########################################################### Index: openacs-4/packages/xowiki/tcl/menu-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/menu-procs.tcl,v diff -u -N -r1.8.2.1 -r1.8.2.2 --- openacs-4/packages/xowiki/tcl/menu-procs.tcl 9 Feb 2016 11:06:35 -0000 1.8.2.1 +++ openacs-4/packages/xowiki/tcl/menu-procs.tcl 21 Mar 2016 09:50:35 -0000 1.8.2.2 @@ -128,6 +128,8 @@ Class create ::xowiki::MenuBar -parameter { id + {dropzone:boolean true} + {parent_id ""} } if {[info commands ::dict] ne ""} { @@ -176,6 +178,23 @@ my set Menu($menu) [list text $(text)] } + ::xowiki::MenuBar instproc current_folder {} { + if {${:parent_id} ne ""} { + return ${:parent_id} + } else { + # + # If the current object is the package, use the root folder as + # current_folder; else use the parent of the current object. + # + set object [::xo::cc invoke_object] + if {[$object istype ::xowiki::Package]} { + return [$object folder_id] + } else { + return [$object parent_id] + } + } + } + ::xowiki::MenuBar instproc add_menu_item {-name:required -item:required} { # # The provided items are of the form of attribute-value pairs @@ -238,6 +257,7 @@ # # {clear_menu -menu New} # {entry -name New.Page -label #xowiki.new# -form en:page.form} + my set parent_id $parent_id foreach me $items { array unset "" Index: openacs-4/packages/xowiki/tcl/package-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/package-procs.tcl,v diff -u -N -r1.291.2.15 -r1.291.2.16 --- openacs-4/packages/xowiki/tcl/package-procs.tcl 9 Feb 2016 11:06:35 -0000 1.291.2.15 +++ openacs-4/packages/xowiki/tcl/package-procs.tcl 21 Mar 2016 09:50:35 -0000 1.291.2.16 @@ -451,12 +451,13 @@ } else { set encoded_name [::xowiki::utility urlencode $name] } - + if {$parent_id eq -100} { # In case, we have a cr-toplevel entry, we assume, we can # resolve it at lease against the root folder of the current # package. set folder "" + set encoded_name "" } else { if {$parent_id eq ""} { ns_log notice "pretty_link of $name: you should consider to pass a parent_id to support folders" @@ -996,7 +997,8 @@ # We have no object, but as well no method callable on the # package If the method is "view", allow it to be called on the # root folder object. - if {[my query_parameter m] eq "list"} { + set m [my query_parameter m] + if {$m in {list show-object file-upload}} { my instvar folder_id array set "" [list \ name [$folder_id name] \ Index: openacs-4/packages/xowiki/tcl/xowiki-utility-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/xowiki-utility-procs.tcl,v diff -u -N -r1.37.2.5 -r1.37.2.6 --- openacs-4/packages/xowiki/tcl/xowiki-utility-procs.tcl 28 Feb 2016 10:41:35 -0000 1.37.2.5 +++ openacs-4/packages/xowiki/tcl/xowiki-utility-procs.tcl 21 Mar 2016 09:50:35 -0000 1.37.2.6 @@ -63,14 +63,16 @@ # Ununtu: apt-get install clamav-daemon # ::xotcl::Object create virus - virus proc check {fn} { + virus proc check {fns} { if {[[::xo::cc package_id] get_parameter clamav 1] && [info commands ::util::which] ne ""} { set clamscanCmd [::util::which clamdscan] - if {$clamscanCmd ne "" && [file readable $fn]} { - if {[catch {exec $clamscanCmd $fn 2>@1} result]} { - ns_log warning "[self] virus found:\n$result" - return 1 + foreach fn $fns { + if {$clamscanCmd ne "" && [file readable $fn]} { + if {[catch {exec $clamscanCmd $fn 2>@1} result]} { + ns_log warning "[self] virus found:\n$result" + return 1 + } } } } Index: openacs-4/packages/xowiki/tcl/xowiki-www-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/tcl/xowiki-www-procs.tcl,v diff -u -N -r1.327.2.14 -r1.327.2.15 --- openacs-4/packages/xowiki/tcl/xowiki-www-procs.tcl 8 Mar 2016 20:59:32 -0000 1.327.2.14 +++ openacs-4/packages/xowiki/tcl/xowiki-www-procs.tcl 21 Mar 2016 09:50:35 -0000 1.327.2.15 @@ -740,8 +740,8 @@ {-disable_input_fields 0} {-view true} } { - my instvar page_template doc root package_id #my log "edit [self args]" + my instvar page_template doc root package_id my setCSSDefaults my include_header_info -prefix form_edit @@ -1073,7 +1073,41 @@ } } + # + # externally callable method: file-upload + # + + FormPage instproc www-file-upload {} { + # + # This method is typically called via drop-zone in a POST request, + # where the FormPage is a folder (which is treated as parent object). + # + if {[ns_conn method] ne "POST"} { + error "method should be called via POST" + } + # + # The following code saves the file as xowiki::File. + # TODO: we should support File.form instances as well (optionally). + # + set form [ns_getform] + set f [::xowiki::formfield::file new -name upload -object [self]] + set file_object [$f store_file \ + -file_name [ns_set get $form upload] \ + -content_type [ns_set get $form upload.content-type] \ + -package_id [my package_id] \ + -parent_id [my item_id] \ + -object_name file:[ns_set get $form upload] \ + -tmpfile [ns_set get $form upload.tmpfile] \ + -publish_date_cmd {;} \ + -save_flag ""] + $f destroy + ns_return 200 text/plain ok + } + + + + # # externally callable method: list # Page instproc www-list {} { Index: openacs-4/packages/xowiki/www/resources/xowiki.css =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/xowiki/www/resources/xowiki.css,v diff -u -N -r1.62 -r1.62.2.1 --- openacs-4/packages/xowiki/www/resources/xowiki.css 27 Oct 2014 16:42:07 -0000 1.62 +++ openacs-4/packages/xowiki/www/resources/xowiki.css 21 Mar 2016 09:50:35 -0000 1.62.2.1 @@ -530,11 +530,35 @@ background-color: #F8F8F8; } +/* layout.css Style */ +.upload-drop-zone { + border-width: 2px; + padding: 4px 10px 0px 10px; +} + +.upload-drop-zone .progress { + height: 10px; + margin-bottom: 4px; +} + +/* skin.css Style*/ +.upload-drop-zone { + color: #ccc; + border-style: dashed; + border-color: #ccc; + line-height: 30px; + text-align: center +} +.upload-drop-zone.drop { + color: #222; + border-color: #222; +} + /* * Local Variables: * mode: css * c-basic-offset: 4 * fill-column: 78 * indent-tabs-mode: nil * End: - */ \ No newline at end of file + */