folder-zip-add.tcl

Page to upload and decompress a zip file into the file storage.

Location:
/packages/file-storage/www/folder-zip-add.tcl
Author:
Kevin Scaldeferri <kevin@arsdigita.com>
Created:
6 Nov 2000
CVS ID:
$Id: folder-zip-add.tcl,v 1.11.2.12 2023/10/22 16:30:01 gustafn Exp $

Related Files

[ hide source ] | [ make this the default ]

File Contents

ad_page_contract {

    Page to upload and decompress a zip file into the file storage.

    @author Kevin Scaldeferri (kevin@arsdigita.com)
    @creation-date 6 Nov 2000
    @cvs-id $Id: folder-zip-add.tcl,v 1.11.2.12 2023/10/22 16:30:01 gustafn Exp $

} {

    file_id:naturalnum,optional,notnull
    {folder_id:naturalnum,notnull ""}
    upload_file:trim,optional,notnull
    {return_url:localurl ""}
    upload_file.tmpfile:tmpfile,optional
    {title ""}
    {lock_title_p:boolean,notnull 0}

} -properties {

    folder_id:onevalue
    context:onevalue
    title:onevalue
    lock_title_p:onevalue

} -validate {

    file_id_or_folder_id {
        #
        # Get parent folder_id from file_id, if such exists and folder_id is
        # empty, and complain if the resultant folder is not valid.
        #
        if {[info exists file_id] && $file_id ne "" && $folder_id eq ""} {
            set folder_id [db_string get_folder_id {
                select parent_id as folder_id from cr_items where item_id=:file_id
            } -default ""]
        }
        if {$folder_id eq "" || ![fs_folder_p $folder_id]} {
            ad_complain "The specified parent folder is not valid."
        }
    }

    upload_file_tmpfile -requires {upload_file} {
        #
        # Check if the upload file looks like a zip file
        #
        set n_bytes [ad_file size ${upload_file.tmpfile}]
        if {$n_bytes < 5} {
            #
            # A zip file has at least 4 bytes.
            #
            set ok 0
        } else {
            #
            # Check the signature of the zip file.
            #
            set ok [util::file_content_check -type zip -file ${upload_file.tmpfile}]
        }
        if { !$ok} {
            ad_complain "The uploaded file does not look like a zip file."
        }
    }

    max_size -requires {upload_file} {
        #
        # Check if the file is larger than fs::max_upload_size.
        #
        set n_bytes [ad_file size ${upload_file.tmpfile}]
        set max_bytes [fs::max_upload_size]
        if { $n_bytes > $max_bytes } {
            ad_complain "Your file is larger than the maximum file size allowed on this system ([lc_content_size_pretty -size $max_bytes])"
        }
    }
}

set user_id     [ad_conn user_id]
set package_id  [ad_conn package_id]
set creation_ip [ad_conn peeraddr]

# Check for write permission on the folder.
permission::require_permission \
    -object_id $folder_id \
    -party_id $user_id \
    -privilege "write"

if {![ad_form_new_p -key file_id]} {
    #
    # Check for write permission on the file if we are editing existing data,
    # adding a file revision in this case, and set the context bar accordingly.
    #
    permission::require_permission \
        -object_id $file_id \
        -party_id $user_id \
        -privilege "write"
    set context [fs_context_bar_list -final "[_ file-storage.Add_Revision]" $folder_id]
} else {
    set context [fs_context_bar_list -final "[_ file-storage.Add_File]" $folder_id]
}

# Add file_id and upload_file to the form.
ad_form -name file_add -html { enctype multipart/form-data } -export { folder_id lock_title_p } -form {
    file_id:key
    {upload_file:file {label \#file-storage.Upload_a_file\#} {html "size 30"}}
}

# Add return_url to the form if is not empty.
if {$return_url ne ""} {
    ad_form -extend -name file_add -form {
        {return_url:text(hidden) {value $return_url}}
    }
}

# 'Lock' title if lock_title_p.
if {$lock_title_p} {
    ad_form -extend -name file_add -form {
        {title:text(hidden) {value $title}}
    }
} else {
    ad_form -extend -name file_add -form {
        {title:text {label \#file-storage.Title\#} {html {size 30}} }
    }
}

# Add an explanation about the purpose of the form.
if {[ad_form_new_p -key file_id]} {
    ad_form -extend -name file_add -form {
        {unpack_message:text(inform) {label "[_ file-storage.Important]"} {value "[_ file-storage.Use_this_form_to_upload_a_ZIP]"}}
    }
}

# Rest of the form.

# Folder names cannot contain slashes
ad_form -extend -name file_add -form {} -validate {
    {title
        {[string first "/" $title] == -1}
        "#acs-templating.Invalid_filename#"
    }
}

ad_form -extend -name file_add -form {} -new_data {
    #
    # new_data block, which unzips the file and uploads its contents to the file
    # storage, creating the necessary folders.
    #
    # Start defining the title if it does not exist already.
    #
    if {$title eq ""} {
        set title [file rootname [list [template::util::file::get_property filename $upload_file]]]
    }

    #
    # Create a new folder to hold the zip contents, if it does not exist already.
    #
    set parent_folder_id $folder_id
    set folder_id [content::item::get_id_by_name -name $title -parent_id $parent_folder_id]
    if {$folder_id eq ""} {
        set folder_id [content::folder::new -name $title -parent_id $parent_folder_id -label $title]
    }

    #
    # Uncompress the file.
    #
    set unzip_binary [string trim [parameter::get -parameter UnzipBinary]]
    if {$unzip_binary ne ""} {
        ns_log warning "package parameter UnzipBinary of file-storage is ignored, using systemwide util::unzip"
    }

    set unzip_binary [util::which unzip]
    if { $unzip_binary ne "" } {
        #
        # Create temp directory to unzip.
        #
        set unzip_path [ad_mktmpdir]

        #
        # Unzip.
        #
        util::unzip -source ${upload_file.tmpfile} -destination $unzip_path

        #
        # Get two lists of the files to upload, with and without their full path.
        #
        set upload_files [list]
        set upload_tmpfiles [list]
        foreach file [ad_find_all_files $unzip_path] {
            lappend upload_files [regsub "^$unzip_path\/" $file {}]
            lappend upload_tmpfiles $file
        }
    } else {
        #
        # No unzip available, just upload the whole zip file.
        #
        set upload_files    [list [template::util::file::get_property filename $upload_file]]
        set upload_tmpfiles [list [template::util::file::get_property tmp_filename $upload_file]]
    }
    set number_upload_files [llength $upload_files]

    #
    # Something is quite broken if there are no files to upload.
    #
    if {$number_upload_files == 0} {
        ad_return_complaint 1 "<li>You have to upload a file"
        ad_script_abort
    }

    #
    # Upload the files.
    #
    set i 0
    foreach upload_file $upload_files tmpfile $upload_tmpfiles {
        #
        # Upload a file.
        #
        set this_file_id $file_id
        set p_f_id $folder_id
        set file_paths [file split [ad_file dirname $upload_file]]
        if {"." ne $file_paths && [llength $file_paths] > 0} {
            #
            # Make sure every folder exists, or create it otherwise.
            #
            set path ""
            foreach p $file_paths {
                append path /${p}
                if {![info exists paths($path)]} {
                    set f_id [content::item::get_id -item_path $path -root_folder_id $p_f_id]
                    if {$f_id eq ""} {
                        set p_f_id [content::folder::new -parent_id $p_f_id -name $p -label $p]
                        set paths($path$p_f_id
                    }
                } else {
                    set p_f_id $paths($path)
                }
            }
            set upload_file [ad_file tail $upload_file]
        }
        set this_title $upload_file
        set this_folder_id $p_f_id

        #
        # If the user choose upload from the folder view, and a file with the
        # same name already exists, we create a new revision.
        #
        # Check for permission in the existing file in order to do so.
        #
        set existing_item_id [fs::get_item_id -name $upload_file -folder_id $this_folder_id]
        if {$existing_item_id ne ""} {
            set this_file_id $existing_item_id
            permission::require_permission \
                -object_id $this_file_id \
                -party_id $user_id \
                -privilege write
        }

        #
        # Add the file.
        #
        set rev_id [fs::add_file \
            -name $upload_file \
            -item_id $this_file_id \
            -parent_id $this_folder_id \
            -tmp_filename $tmpfile \
            -creation_user $user_id \
            -creation_ip $creation_ip \
            -title $this_title \
            -package_id $package_id]

        #
        # Increment file_id to the next value of acs_object_id_seq.
        #
        incr i
        if {$i < $number_upload_files} {
            set file_id [db_nextval "acs_object_id_seq"]
        }

        #
        # Cleanup of the temporary file.
        #
        file delete -- $tmpfile
    }

    #
    # Cleanup of zip file and tmp directory.
    #
    file delete -- $upload_file.tmpfile
    if {$unzip_path ne ""} {
        file delete -force -- $unzip_path
    }

} -edit_data {
    #
    # edit_data block, which just adds a revision of a file.
    #
    fs::add_version \
        -name [template::util::file::get_property filename $upload_file] \
        -tmp_filename [template::util::file::get_property tmp_filename $upload_file] \
        -item_id $file_id \
        -creation_user $user_id \
        -creation_ip $creation_ip \
        -title $title \
        -package_id $package_id

} -after_submit {
    #
    # Code to be executed after new_data or edit_data, just redirecting to
    # return_url.
    #
    if {$return_url ne ""} {
        ad_returnredirect $return_url
    } else {
        ad_returnredirect [export_vars -base ./ {folder_id}]
    }
    ad_script_abort
}

ad_return_template

# Local variables:
#    mode: tcl
#    tcl-indent-level: 4
#    indent-tabs-mode: nil
# End: