• Publicity: Public Only All

file-storage-procs.tcl

Tcl library for the file-storage system (v.4)

Location:
packages/file-storage/tcl/file-storage-procs.tcl
Created:
6 November 2000
Author:
Kevin Scaldeferri <kevin@arsdigita.com>
CVS Identification:
$Id: file-storage-procs.tcl,v 1.90.2.42 2024/05/31 13:57:38 antoniop Exp $

Procedures in this file

Detailed information

fs::add_created_file (public, deprecated)

 fs::add_created_file [ -name name ] -parent_id parent_id \
    -package_id package_id [ -item_id item_id ] \
    [ -mime_type mime_type ] [ -creation_user creation_user ] \
    [ -creation_ip creation_ip ] [ -title title ] \
    [ -description description ] [ -content_body content_body ]
Deprecated. Invoking this procedure generates a warning.

Create a new file storage item or add a new revision if an item with the same name and parent folder already exists DEPRECATED: this proc was superseded by fs::add_file

Switches:
-name (optional)
-parent_id (required)
-package_id (required)
-item_id (optional)
-mime_type (optional)
-creation_user (optional)
-creation_ip (optional)
-title (optional)
-description (optional)
-content_body (optional)
Returns:
revision_id
See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 ad_conn ad_conn (public) ad_log_deprecated ad_log_deprecated (public) callback callback (public) db_exec_plsql db_exec_plsql (public) db_string db_string (public) fs::add_created_file fs::add_created_file fs::add_created_file->ad_conn fs::add_created_file->ad_log_deprecated fs::add_created_file->callback fs::add_created_file->db_exec_plsql fs::add_created_file->db_string

Testcases:
No testcase defined.

fs::add_created_version (public, deprecated)

 fs::add_created_version -name name -content_body content_body \
    -mime_type mime_type -item_id item_id \
    [ -creation_user creation_user ] [ -creation_ip creation_ip ] \
    [ -title title ] [ -description description ] \
    [ -suppress_notify_p suppress_notify_p ] \
    [ -storage_type storage_type ] [ -package_id package_id ] \
    [ -storage_type storage_type ]
Deprecated. Invoking this procedure generates a warning.

Create a new version of a file storage item using the content passed in content_body DEPRECATED: this proc has been superseded by fs::add_version

Switches:
-name (required)
-content_body (required)
-mime_type (required)
-item_id (required)
-creation_user (optional)
-creation_ip (optional)
-title (optional)
-description (optional)
-suppress_notify_p (optional, defaults to "f")
-storage_type (optional)
-package_id (optional)
-storage_type (optional)
Returns:
revision_id
See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 fs::add_created_file fs::add_created_file (public, deprecated) fs::add_created_version fs::add_created_version fs::add_created_file->fs::add_created_version ad_conn ad_conn (public) fs::add_created_version->ad_conn ad_log_deprecated ad_log_deprecated (public) fs::add_created_version->ad_log_deprecated content::revision::new content::revision::new (public) fs::add_created_version->content::revision::new db_string db_string (public) fs::add_created_version->db_string fs::do_notifications fs::do_notifications (public) fs::add_created_version->fs::do_notifications

Testcases:
No testcase defined.

fs::add_file (public)

 fs::add_file -name name -parent_id parent_id -package_id package_id \
    [ -item_id item_id ] [ -creation_user creation_user ] \
    [ -creation_ip creation_ip ] [ -title title ] \
    [ -description description ] [ -tmp_filename tmp_filename ] \
    [ -mime_type mime_type ] [ -no_callback ] [ -no_notification ]

Create a new file storage item or add a new revision if an item with the same name and parent folder already exists

Switches:
-name (required)
-parent_id (required)
-package_id (required)
-item_id (optional)
-creation_user (optional)
-creation_ip (optional)
-title (optional)
-description (optional)
-tmp_filename (optional)
-mime_type (optional)
-no_callback (optional, boolean)
-no_notification (optional, boolean)
Returns:
revision_id

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::add_file fs::add_file test_fs_add_delete_copy->fs::add_file test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) test_fs_add_file_to_folder->fs::add_file test_fs_publish_file fs_publish_file (test file-storage) test_fs_publish_file->fs::add_file ad_conn ad_conn (public) fs::add_file->ad_conn callback callback (public) fs::add_file->callback content::item::new content::item::new (public) fs::add_file->content::item::new content::type::content_type_p content::type::content_type_p (public) fs::add_file->content::type::content_type_p cr_check_mime_type cr_check_mime_type (public) fs::add_file->cr_check_mime_type fs::impl::fs_object::put fs::impl::fs_object::put (private) fs::impl::fs_object::put->fs::add_file packages/acs-templating/www/scripts/xinha/file-selector.tcl packages/acs-templating/ www/scripts/xinha/file-selector.tcl packages/acs-templating/www/scripts/xinha/file-selector.tcl->fs::add_file packages/attachments/www/file-add-2.tcl packages/attachments/ www/file-add-2.tcl packages/attachments/www/file-add-2.tcl->fs::add_file packages/file-storage/www/file-add.tcl packages/file-storage/ www/file-add.tcl packages/file-storage/www/file-add.tcl->fs::add_file packages/file-storage/www/folder-add.tcl packages/file-storage/ www/folder-add.tcl packages/file-storage/www/folder-add.tcl->fs::add_file

Testcases:
fs_publish_file, fs_add_delete_copy, fs_add_file_to_folder

fs::add_version (public)

 fs::add_version -item_id item_id [ -name name ] \
    [ -package_id package_id ] [ -mime_type mime_type ] \
    [ -tmp_filename tmp_filename ] [ -content_body content_body ] \
    [ -creation_user creation_user ] [ -creation_ip creation_ip ] \
    [ -title title ] [ -description description ] \
    [ -suppress_notify_p suppress_notify_p ] \
    [ -storage_type storage_type ] [ -no_callback ]

Create a new version of a file storage item.

Switches:
-item_id (required)
-name (optional)
-package_id (optional)
-mime_type (optional)
-tmp_filename (optional)
absolute path to a file on the filesystem. when specified, the new revision data will come from this file.
-content_body (optional)
Text content for the new revision. When 'tmp_filename' is missing, the new revision data will come from here.
-creation_user (optional)
-creation_ip (optional)
-title (optional)
-description (optional)
-suppress_notify_p (optional, defaults to "f")
-storage_type (optional)
-no_callback (optional, boolean)
Returns:
revision_id

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::add_version fs::add_version test_fs_add_delete_copy->fs::add_version test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) test_fs_add_file_to_folder->fs::add_version ad_conn ad_conn (public) fs::add_version->ad_conn ad_file ad_file (public) fs::add_version->ad_file callback callback (public) fs::add_version->callback content::item::set_live_revision content::item::set_live_revision (public) fs::add_version->content::item::set_live_revision content::revision::new content::revision::new (public) fs::add_version->content::revision::new fs::add_file fs::add_file (public) fs::add_file->fs::add_version fs::impl::fs_object::put fs::impl::fs_object::put (private) fs::impl::fs_object::put->fs::add_version packages/file-storage/www/file-add.tcl packages/file-storage/ www/file-add.tcl packages/file-storage/www/file-add.tcl->fs::add_version packages/file-storage/www/folder-zip-add.tcl packages/file-storage/ www/folder-zip-add.tcl packages/file-storage/www/folder-zip-add.tcl->fs::add_version

Testcases:
fs_add_delete_copy, fs_add_file_to_folder

fs::delete_file (public)

 fs::delete_file -item_id item_id [ -parent_id parent_id ] \
    [ -no_callback ]

Deletes a file and all its revisions. Note that we do not perform filesystem operations here. A trigger on cr_revisions informs the content repository about the deletion and periodic cleanup of files to be deleted is performed in a scheduled procedure.

Switches:
-item_id (required)
-parent_id (optional)
-no_callback (optional, boolean)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::delete_file fs::delete_file test_fs_add_delete_copy->fs::delete_file test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) test_fs_add_file_to_folder->fs::delete_file ad_conn ad_conn (public) fs::delete_file->ad_conn callback callback (public) fs::delete_file->callback db_exec_plsql db_exec_plsql (public) fs::delete_file->db_exec_plsql db_list db_list (public) fs::delete_file->db_list fs::do_notifications fs::do_notifications (public) fs::delete_file->fs::do_notifications fs::delete_version fs::delete_version (public) fs::delete_version->fs::delete_file packages/attachments/www/detach.tcl packages/attachments/ www/detach.tcl packages/attachments/www/detach.tcl->fs::delete_file packages/file-storage/www/delete.tcl packages/file-storage/ www/delete.tcl packages/file-storage/www/delete.tcl->fs::delete_file packages/file-storage/www/move.tcl packages/file-storage/ www/move.tcl packages/file-storage/www/move.tcl->fs::delete_file packages/file-storage/www/simple-delete.tcl packages/file-storage/ www/simple-delete.tcl packages/file-storage/www/simple-delete.tcl->fs::delete_file

Testcases:
fs_add_delete_copy, fs_add_file_to_folder

fs::delete_folder (public)

 fs::delete_folder -folder_id folder_id [ -cascade_p cascade_p ] \
    [ -parent_id parent_id ] [ -no_callback ] [ -no_notifications ]

Deletes a folder and all contents. Note that we do not perform filesystem operations here. A trigger on cr_revisions informs the content repository about the deletion and periodic cleanup of files to be deleted is performed in a scheduled procedure.

Switches:
-folder_id (required)
-cascade_p (optional, defaults to "t")
-parent_id (optional)
-no_callback (optional, boolean)
-no_notifications (optional, boolean)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_create_folder_using_api fs_create_folder_using_api (test file-storage) fs::delete_folder fs::delete_folder test_fs_create_folder_using_api->fs::delete_folder ad_conn ad_conn (public) fs::delete_folder->ad_conn callback callback (public) fs::delete_folder->callback db_exec_plsql db_exec_plsql (public) fs::delete_folder->db_exec_plsql fs::do_notifications fs::do_notifications (public) fs::delete_folder->fs::do_notifications fs::get_object_name fs::get_object_name (public) fs::delete_folder->fs::get_object_name fs::install::before_uninstantiate fs::install::before_uninstantiate (private) fs::install::before_uninstantiate->fs::delete_folder packages/file-storage/www/delete.tcl packages/file-storage/ www/delete.tcl packages/file-storage/www/delete.tcl->fs::delete_folder packages/file-storage/www/folder-delete.tcl packages/file-storage/ www/folder-delete.tcl packages/file-storage/www/folder-delete.tcl->fs::delete_folder packages/file-storage/www/move.tcl packages/file-storage/ www/move.tcl packages/file-storage/www/move.tcl->fs::delete_folder

Testcases:
fs_create_folder_using_api

fs::delete_version (public)

 fs::delete_version -item_id item_id -version_id version_id

Deletes a revision. If it was the last revision, it deletes the file as well. Note that we do not perform filesystem operations here. A trigger on cr_revisions informs the content repository about the deletion and periodic cleanup of files to be deleted is performed in a scheduled procedure.

Switches:
-item_id (required)
-version_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::delete_version fs::delete_version test_fs_add_delete_copy->fs::delete_version db_exec_plsql db_exec_plsql (public) fs::delete_version->db_exec_plsql fs::delete_file fs::delete_file (public) fs::delete_version->fs::delete_file packages/file-storage/www/version-delete.tcl packages/file-storage/ www/version-delete.tcl packages/file-storage/www/version-delete.tcl->fs::delete_version

Testcases:
fs_add_delete_copy

fs::do_notifications (public)

 fs::do_notifications -folder_id folder_id -filename filename \
    -item_id item_id [ -package_id package_id ] -action action

Send notifications for file-storage operations. Note that not all possible operations are implemented, e.g. move, copy etc. See documentation.

Switches:
-folder_id (required)
-filename (required)
-item_id (required)
-package_id (optional)
-action (required)
The kind of operation. One of: new_file, new_version, new_url, delete_file, delete_url, delete_folder

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) fs::do_notifications fs::do_notifications test_fs_add_file_to_folder->fs::do_notifications _ _ (public) fs::do_notifications->_ acs_object::get_element acs_object::get_element (public) fs::do_notifications->acs_object::get_element ad_html_text_convert ad_html_text_convert (public) fs::do_notifications->ad_html_text_convert ad_url ad_url (public) fs::do_notifications->ad_url db_0or1row db_0or1row (public) fs::do_notifications->db_0or1row fs::add_created_file fs::add_created_file (public, deprecated) fs::add_created_file->fs::do_notifications fs::add_created_version fs::add_created_version (public, deprecated) fs::add_created_version->fs::do_notifications fs::add_file fs::add_file (public) fs::add_file->fs::do_notifications fs::add_version fs::add_version (public) fs::add_version->fs::do_notifications fs::delete_file fs::delete_file (public) fs::delete_file->fs::do_notifications

Testcases:
fs_add_file_to_folder

fs::file_copy (public)

 fs::file_copy -file_id file_id -target_folder_id target_folder_id \
    [ -postfix postfix ] [ -symlink ]

copy file to target folder

Switches:
-file_id (required)
Item_id of the file to be copied
-target_folder_id (required)
Folder ID of the folder to which the file is copied to
-postfix (optional)
Postfix will be added with "_" to the new filename (not title). Very useful if you want to avoid unique name constraints on cr_items.
-symlink (optional, boolean)
Defines if, instead of a full item, we should just add a symlink.

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::file_copy fs::file_copy test_fs_add_delete_copy->fs::file_copy ad_conn ad_conn (public) fs::file_copy->ad_conn ad_file ad_file (public) fs::file_copy->ad_file content::item::get_id_by_name content::item::get_id_by_name (public) fs::file_copy->content::item::get_id_by_name content::item::new content::item::new (public) fs::file_copy->content::item::new content::item::set_live_revision content::item::set_live_revision (public) fs::file_copy->content::item::set_live_revision

Testcases:
fs_add_delete_copy

fs::folder_p (public)

 fs::folder_p -object_id object_id

Is this object a folder?

Switches:
-object_id (required)
Returns:
true if object_id is a folder

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_create_folder_using_api fs_create_folder_using_api (test file-storage) fs::folder_p fs::folder_p test_fs_create_folder_using_api->fs::folder_p db_string db_string (public) fs::folder_p->db_string

Testcases:
fs_create_folder_using_api

fs::get_archive_command (public, deprecated)

 fs::get_archive_command [ -in_file in_file ] [ -out_file out_file ]
Deprecated. Invoking this procedure generates a warning.

return the archive command after replacing {in_file} and {out_file} with their respective values.

Switches:
-in_file (optional)
-out_file (optional)

Partial Call Graph (max 5 caller/called nodes):
%3 ad_conn ad_conn (public) ad_log_deprecated ad_log_deprecated (public) parameter::get parameter::get (public) fs::get_archive_command fs::get_archive_command fs::get_archive_command->ad_conn fs::get_archive_command->ad_log_deprecated fs::get_archive_command->parameter::get

Testcases:
No testcase defined.

fs::get_archive_extension (public, deprecated)

 fs::get_archive_extension
Deprecated. Invoking this procedure generates a warning.

return the archive extension that should be added to the output file of an archive command DEPRECATED: this is a trivial wrapper over the parameter api

See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 ad_log_deprecated ad_log_deprecated (public) parameter::get parameter::get (public) fs::get_archive_extension fs::get_archive_extension fs::get_archive_extension->ad_log_deprecated fs::get_archive_extension->parameter::get

Testcases:
No testcase defined.

fs::get_file_package_id (public)

 fs::get_file_package_id -file_id file_id

Returns the package_id for a passed-in file_id. This is useful when using symlinks to files whose real root_folder_id is not the root_folder_id of the package the user is in.

Switches:
-file_id (required)
Returns:
package_id
Author:
Stan Kaufman <skaufman@epimetrics.com>
Created:
2005-09-07

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_publish_file fs_publish_file (test file-storage) fs::get_file_package_id fs::get_file_package_id test_fs_publish_file->fs::get_file_package_id db_string db_string (public) fs::get_file_package_id->db_string packages/file-storage/www/download-zip.tcl packages/file-storage/ www/download-zip.tcl packages/file-storage/www/download-zip.tcl->fs::get_file_package_id

Testcases:
fs_publish_file

fs::get_file_system_safe_object_name (public)

 fs::get_file_system_safe_object_name -object_id object_id

get the name of a file storage object and make it safe for writing to the filesystem

Switches:
-object_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::get_file_system_safe_object_name fs::get_file_system_safe_object_name test_fs_add_delete_copy->fs::get_file_system_safe_object_name ad_sanitize_filename ad_sanitize_filename (public) fs::get_file_system_safe_object_name->ad_sanitize_filename fs::get_object_name fs::get_object_name (public) fs::get_file_system_safe_object_name->fs::get_object_name packages/file-storage/www/download-zip.tcl packages/file-storage/ www/download-zip.tcl packages/file-storage/www/download-zip.tcl->fs::get_file_system_safe_object_name

Testcases:
fs_add_delete_copy

fs::get_folder (public)

 fs::get_folder -name name -parent_id parent_id

Retrieve the folder_id of a folder given its name and parent folder.

Switches:
-name (required)
Internal name of the folder, must be unique under a given parent_id
-parent_id (required)
The parent folder to look under
Returns:
folder_id of the folder, or null if no folder was found by that name

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_create_folder_using_api fs_create_folder_using_api (test file-storage) fs::get_folder fs::get_folder test_fs_create_folder_using_api->fs::get_folder db_string db_string (public) fs::get_folder->db_string packages/file-storage/lib/recent.tcl packages/file-storage/ lib/recent.tcl packages/file-storage/lib/recent.tcl->fs::get_folder

Testcases:
fs_create_folder_using_api

fs::get_folder_contents (public, deprecated)

 fs::get_folder_contents [ -folder_id folder_id ] [ -user_id user_id ] \
    [ -n_past_days n_past_days ]
Deprecated. Invoking this procedure generates a warning.

WARNING: This proc is not scalable because it does too many permission checks. DRB: Not so true now that permissions are fast. However, it is now only used to clone files in dotLRN and for the somewhat brain-damaged syllabus package. At minimum the permission checks returned by the code can be removed. Most of the other fields as well. Oh well ... REMOVE WHEN SYLLABUS IS REWRITTEN TO FIND ITS FILE INTELLIGENTLY Retrieve the contents of the specified folder in the form of a list of ns_sets, one for each row returned. The keys for each row are as follows: object_id, name, live_revision, type, last_modified, new_p, content_size, file_upload_name write_p, delete_p, admin_p, DEPRECATED: this proc has been evidently the subject of controversy over the years. To this day (2023-03-17) no package seems to be using it. One can either query the database directly or use other existing apis to retrieve the folder children and then fetch the needed metadata via other means.

Switches:
-folder_id (optional)
The folder for which to retrieve contents
-user_id (optional)
The viewer of the contents (to make sure they have permission)
-n_past_days (optional, defaults to "99999")
Mark files that are newer than the past N days as new
See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 acs_magic_object acs_magic_object (public) ad_conn ad_conn (public) ad_log_deprecated ad_log_deprecated (public) db_list_of_ns_sets db_list_of_ns_sets (public) fs::get_root_folder fs::get_root_folder (public) fs::get_folder_contents fs::get_folder_contents fs::get_folder_contents->acs_magic_object fs::get_folder_contents->ad_conn fs::get_folder_contents->ad_log_deprecated fs::get_folder_contents->db_list_of_ns_sets fs::get_folder_contents->fs::get_root_folder

Testcases:
No testcase defined.

fs::get_folder_contents_count (public)

 fs::get_folder_contents_count [ -folder_id folder_id ] \
    [ -user_id user_id ]

Retrieve the count of contents of the specified folder.

Switches:
-folder_id (optional)
The folder for which to retrieve contents
-user_id (optional)
DEPRECATED since commit 2002-02-22 by Yonatan Feldman (yon@milliped.com) this parameter doesn't have any effect. It was used to count only items where user had read permission, but was considered unscalable.

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) fs::get_folder_contents_count fs::get_folder_contents_count test_fs_add_file_to_folder->fs::get_folder_contents_count ad_conn ad_conn (public) fs::get_folder_contents_count->ad_conn db_string db_string (public) fs::get_folder_contents_count->db_string fs::get_root_folder fs::get_root_folder (public) fs::get_folder_contents_count->fs::get_root_folder packages/attachments/www/attach.tcl packages/attachments/ www/attach.tcl packages/attachments/www/attach.tcl->fs::get_folder_contents_count packages/file-storage/www/folder-delete.tcl packages/file-storage/ www/folder-delete.tcl packages/file-storage/www/folder-delete.tcl->fs::get_folder_contents_count packages/file-storage/www/index.tcl packages/file-storage/ www/index.tcl packages/file-storage/www/index.tcl->fs::get_folder_contents_count

Testcases:
fs_add_file_to_folder

fs::get_folder_objects (public)

 fs::get_folder_objects -folder_id folder_id -user_id user_id

Return a list the object_ids contained by a file storage folder.

Switches:
-folder_id (required)
The folder for which to retrieve contents
-user_id (required)
The viewer of the contents (to make sure they have permission)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_publish_file fs_publish_file (test file-storage) fs::get_folder_objects fs::get_folder_objects test_fs_publish_file->fs::get_folder_objects db_list db_list (public) fs::get_folder_objects->db_list

Testcases:
fs_publish_file

fs::get_folder_package_and_root (public)

 fs::get_folder_package_and_root folder_id

Returns a two-element Tcl list containing the package_id and root_folder_id for the passed-in folder_id.

Parameters:
folder_id (required)
Author:
Andrew Grumet <aegrumet@alum.mit.edu>
Created:
15 March 2004

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) fs::get_folder_package_and_root fs::get_folder_package_and_root test_fs_add_file_to_folder->fs::get_folder_package_and_root db_1row db_1row (public) fs::get_folder_package_and_root->db_1row fs::add_created_version fs::add_created_version (public, deprecated) fs::add_created_version->fs::get_folder_package_and_root fs::add_version fs::add_version (public) fs::add_version->fs::get_folder_package_and_root fs::delete_file fs::delete_file (public) fs::delete_file->fs::get_folder_package_and_root fs::do_notifications fs::do_notifications (public) fs::do_notifications->fs::get_folder_package_and_root fs::notification::get_url fs::notification::get_url (private) fs::notification::get_url->fs::get_folder_package_and_root

Testcases:
fs_add_file_to_folder

fs::get_item_id (public)

 fs::get_item_id -name name [ -folder_id folder_id ]

Get the item_id of a file

Switches:
-name (required)
-folder_id (optional)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) fs::get_item_id fs::get_item_id test_fs_add_file_to_folder->fs::get_item_id ad_conn ad_conn (public) fs::get_item_id->ad_conn content::item::get_id content::item::get_id (public) fs::get_item_id->content::item::get_id fs_get_root_folder fs_get_root_folder (public) fs::get_item_id->fs_get_root_folder packages/acs-templating/www/scripts/xinha/file-selector.tcl packages/acs-templating/ www/scripts/xinha/file-selector.tcl packages/acs-templating/www/scripts/xinha/file-selector.tcl->fs::get_item_id packages/file-storage/www/file-add.tcl packages/file-storage/ www/file-add.tcl packages/file-storage/www/file-add.tcl->fs::get_item_id packages/file-storage/www/folder-add.tcl packages/file-storage/ www/folder-add.tcl packages/file-storage/www/folder-add.tcl->fs::get_item_id packages/file-storage/www/folder-zip-add.tcl packages/file-storage/ www/folder-zip-add.tcl packages/file-storage/www/folder-zip-add.tcl->fs::get_item_id packages/xowiki/www/xinha/file-selector.tcl packages/xowiki/ www/xinha/file-selector.tcl packages/xowiki/www/xinha/file-selector.tcl->fs::get_item_id

Testcases:
fs_add_file_to_folder

fs::get_object_info (public)

 fs::get_object_info -file_id file_id [ -revision_id revision_id ]

Returns an array containing the fs object info.

Switches:
-file_id (required)
Id of the file
-revision_id (optional)
Id of the revision
Returns:
Error:
Author:
Deds Castillo <deds@i-manila.com.ph>
Created:
2004-07-03

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::get_object_info fs::get_object_info test_fs_add_delete_copy->fs::get_object_info content::item::get_live_revision content::item::get_live_revision (public) fs::get_object_info->content::item::get_live_revision content::revision::get_cr_file_path content::revision::get_cr_file_path (public) fs::get_object_info->content::revision::get_cr_file_path db_1row db_1row (public) fs::get_object_info->db_1row

Testcases:
fs_add_delete_copy

fs::get_object_name (public)

 fs::get_object_name -object_id object_id

Select the name of this object.

Switches:
-object_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::get_object_name fs::get_object_name test_fs_add_delete_copy->fs::get_object_name test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) test_fs_add_file_to_folder->fs::get_object_name db_string db_string (public) fs::get_object_name->db_string fs::delete_file fs::delete_file (public) fs::delete_file->fs::get_object_name fs::delete_folder fs::delete_folder (public) fs::delete_folder->fs::get_object_name fs::get_file_system_safe_object_name fs::get_file_system_safe_object_name (public) fs::get_file_system_safe_object_name->fs::get_object_name fs::publish_folder_to_file_system fs::publish_folder_to_file_system (public) fs::publish_folder_to_file_system->fs::get_object_name packages/acs-templating/www/scripts/xinha/file-selector.tcl packages/acs-templating/ www/scripts/xinha/file-selector.tcl packages/acs-templating/www/scripts/xinha/file-selector.tcl->fs::get_object_name

Testcases:
fs_add_delete_copy, fs_add_file_to_folder

fs::get_object_prettyname (public)

 fs::get_object_prettyname -object_id object_id

Select a pretty name for this object. If title is empty, returns name.

Switches:
-object_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::get_object_prettyname fs::get_object_prettyname test_fs_add_delete_copy->fs::get_object_prettyname db_string db_string (public) fs::get_object_prettyname->db_string

Testcases:
fs_add_delete_copy

fs::get_parent (public)

 fs::get_parent -item_id item_id

Get the parent of a given item.

Switches:
-item_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) fs::get_parent fs::get_parent test_fs_add_file_to_folder->fs::get_parent db_string db_string (public) fs::get_parent->db_string fs::add_created_version fs::add_created_version (public, deprecated) fs::add_created_version->fs::get_parent fs::delete_file fs::delete_file (public) fs::delete_file->fs::get_parent fs::delete_folder fs::delete_folder (public) fs::delete_folder->fs::get_parent packages/acs-templating/www/scripts/xinha/file-selector.tcl packages/acs-templating/ www/scripts/xinha/file-selector.tcl packages/acs-templating/www/scripts/xinha/file-selector.tcl->fs::get_parent packages/file-storage/www/download-zip.tcl packages/file-storage/ www/download-zip.tcl packages/file-storage/www/download-zip.tcl->fs::get_parent

Testcases:
fs_add_file_to_folder

fs::get_root_folder (public)

 fs::get_root_folder [ -package_id package_id ]

Get the root folder of a package instance.

Switches:
-package_id (optional)
Package instance of the root folder to retrieve
Returns:
folder_id of the root folder retrieved

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) fs::get_root_folder fs::get_root_folder test_fs_add_file_to_folder->fs::get_root_folder test_fs_create_folder_using_api fs_create_folder_using_api (test file-storage) test_fs_create_folder_using_api->fs::get_root_folder ad_conn ad_conn (public) fs::get_root_folder->ad_conn db_exec_plsql db_exec_plsql (public) fs::get_root_folder->db_exec_plsql callback::pm::project_new::impl::file_storage callback::pm::project_new::impl::file_storage (private) callback::pm::project_new::impl::file_storage->fs::get_root_folder fs::after_mount fs::after_mount (private) fs::after_mount->fs::get_root_folder fs::before_unmount fs::before_unmount (private) fs::before_unmount->fs::get_root_folder fs::get_folder_contents fs::get_folder_contents (public, deprecated) fs::get_folder_contents->fs::get_root_folder fs::get_folder_contents_count fs::get_folder_contents_count (public) fs::get_folder_contents_count->fs::get_root_folder

Testcases:
fs_create_folder_using_api, fs_add_file_to_folder

fs::item_editable_info (public, deprecated)

 fs::item_editable_info -item_id item_id
Deprecated. Invoking this procedure generates a warning.

Returns an array containing elements editable_p, mime_type, file_extension if an fs item is editable through the browser, editable_p is set to 1 DEPRECATED: it is unclear what editable is supposed to mean. As of 2023-03-16 file-storage does not offer inline editing and no package, including file-storage itself, appears to be using this api.

Switches:
-item_id (required)
Returns:
Error:
Author:
Deds Castillo <deds@i-manila.com.ph>
Created:
2004-07-03
See Also:
  • nothing

Partial Call Graph (max 5 caller/called nodes):
%3 fs::item_editable_p fs::item_editable_p (public, deprecated) fs::item_editable_info fs::item_editable_info fs::item_editable_p->fs::item_editable_info ad_log_deprecated ad_log_deprecated (public) fs::item_editable_info->ad_log_deprecated content::item::get content::item::get (public) fs::item_editable_info->content::item::get db_string db_string (public) fs::item_editable_info->db_string

Testcases:
No testcase defined.

fs::item_editable_p (public, deprecated)

 fs::item_editable_p -item_id item_id
Deprecated. Invoking this procedure generates a warning.

returns 1 if item is editable via browser DEPRECATED: it is unclear what editable is supposed to mean. As of 2023-03-16 file-storage does not offer inline editing and no package, including file-storage itself, appears to be using this api.

Switches:
-item_id (required)
Returns:
Error:
Author:
Deds Castillo <deds@i-manila.com.ph>
Created:
2004-07-03
See Also:
  • nothing

Partial Call Graph (max 5 caller/called nodes):
%3 ad_log_deprecated ad_log_deprecated (public) fs::item_editable_info fs::item_editable_info (public, deprecated) fs::item_editable_p fs::item_editable_p fs::item_editable_p->ad_log_deprecated fs::item_editable_p->fs::item_editable_info

Testcases:
No testcase defined.

fs::max_upload_size (public)

 fs::max_upload_size [ -package_id package_id ]
Switches:
-package_id (optional)
id of the file-storage package instance. Will default to the connection package_id if not specified. Returns the maximum upload size for this file-storage instance. If the value from the parameter is empty, invalid, or bigger than the server-wide upload limit, the latter will take over.
Returns:
numeric value in bytes

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) fs::max_upload_size fs::max_upload_size test_fs_add_file_to_folder->fs::max_upload_size fs::unit_conv fs::unit_conv (private) fs::max_upload_size->fs::unit_conv parameter::get parameter::get (public) fs::max_upload_size->parameter::get packages/attachments/www/file-add-2.tcl packages/attachments/ www/file-add-2.tcl packages/attachments/www/file-add-2.tcl->fs::max_upload_size packages/file-storage/www/admin/upload-size-limit.tcl packages/file-storage/ www/admin/upload-size-limit.tcl packages/file-storage/www/admin/upload-size-limit.tcl->fs::max_upload_size packages/file-storage/www/file-add.tcl packages/file-storage/ www/file-add.tcl packages/file-storage/www/file-add.tcl->fs::max_upload_size packages/file-storage/www/folder-zip-add.tcl packages/file-storage/ www/folder-zip-add.tcl packages/file-storage/www/folder-zip-add.tcl->fs::max_upload_size

Testcases:
fs_add_file_to_folder

fs::new_folder (public)

 fs::new_folder -name name -pretty_name pretty_name \
    -parent_id parent_id [ -creation_user creation_user ] \
    [ -creation_ip creation_ip ] [ -description description ] \
    [ -package_id package_id ] [ -no_callback ]

Create a new folder.

Switches:
-name (required)
Internal name of the folder, must be unique under a given parent_id
-pretty_name (required)
What we show to users of the system
-parent_id (required)
Where we create this folder
-creation_user (optional)
Who created this folder
-creation_ip (optional)
What is the IP address of the creation_user
-description (optional)
of the folder. Not used in the current FS UI but might be used elsewhere.
-package_id (optional)
Package_id of the package for which to create the new folder. Preferably a file storage package_id
-no_callback (optional, boolean)
defines if the callback should be called. Defaults to yes
Returns:
folder_id of the newly created folder

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::new_folder fs::new_folder test_fs_add_delete_copy->fs::new_folder test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) test_fs_add_file_to_folder->fs::new_folder test_fs_create_folder_using_api fs_create_folder_using_api (test file-storage) test_fs_create_folder_using_api->fs::new_folder test_fs_publish_file fs_publish_file (test file-storage) test_fs_publish_file->fs::new_folder acs_object::package_id acs_object::package_id (public) fs::new_folder->acs_object::package_id ad_conn ad_conn (public) fs::new_folder->ad_conn apm_package_key_from_id apm_package_key_from_id (public) fs::new_folder->apm_package_key_from_id callback callback (public) fs::new_folder->callback content::folder::new content::folder::new (public) fs::new_folder->content::folder::new callback::pm::project_new::impl::file_storage callback::pm::project_new::impl::file_storage (private) callback::pm::project_new::impl::file_storage->fs::new_folder fs::impl::fs_object::mkcol fs::impl::fs_object::mkcol (private) fs::impl::fs_object::mkcol->fs::new_folder install::xml::action::file-storage-folder install::xml::action::file-storage-folder (private) install::xml::action::file-storage-folder->fs::new_folder packages/file-storage/www/folder-create.tcl packages/file-storage/ www/folder-create.tcl packages/file-storage/www/folder-create.tcl->fs::new_folder

Testcases:
fs_publish_file, fs_add_delete_copy, fs_create_folder_using_api, fs_add_file_to_folder

fs::new_root_folder (public)

 fs::new_root_folder [ -package_id package_id ] \
    [ -pretty_name pretty_name ] [ -description description ] \
    [ -name name ]

Create a root folder for a package instance.

Switches:
-package_id (optional)
Package instance associated with this root folder
-pretty_name (optional)
-description (optional)
-name (optional)
Returns:
folder_id of the new root folder

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::new_root_folder fs::new_root_folder test_fs_add_delete_copy->fs::new_root_folder ad_conn ad_conn (public) fs::new_root_folder->ad_conn apm_instance_name_from_id apm_instance_name_from_id (public) fs::new_root_folder->apm_instance_name_from_id db_exec_plsql db_exec_plsql (public) fs::new_root_folder->db_exec_plsql fs::install::after_instantiate fs::install::after_instantiate (private) fs::install::after_instantiate->fs::new_root_folder packages/attachments/www/admin/new-root-folder-map-2.tcl packages/attachments/ www/admin/new-root-folder-map-2.tcl packages/attachments/www/admin/new-root-folder-map-2.tcl->fs::new_root_folder

Testcases:
fs_add_delete_copy

fs::object_p (public)

 fs::object_p -object_id object_id

is this a file storage object

Switches:
-object_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::object_p fs::object_p test_fs_add_delete_copy->fs::object_p db_string db_string (public) fs::object_p->db_string

Testcases:
fs_add_delete_copy

fs::publish_folder_to_file_system (public)

 fs::publish_folder_to_file_system -folder_id folder_id [ -path path ] \
    [ -folder_name folder_name ] [ -user_id user_id ]

publish the contents of a file storage folder to the filesystem

Switches:
-folder_id (required)
-path (optional)
-folder_name (optional)
-user_id (optional)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_publish_file fs_publish_file (test file-storage) fs::publish_folder_to_file_system fs::publish_folder_to_file_system test_fs_publish_file->fs::publish_folder_to_file_system ad_file ad_file (public) fs::publish_folder_to_file_system->ad_file ad_sanitize_filename ad_sanitize_filename (public) fs::publish_folder_to_file_system->ad_sanitize_filename ad_tmpnam ad_tmpnam (public) fs::publish_folder_to_file_system->ad_tmpnam db_foreach db_foreach (public) fs::publish_folder_to_file_system->db_foreach fs::get_object_name fs::get_object_name (public) fs::publish_folder_to_file_system->fs::get_object_name fs::publish_object_to_file_system fs::publish_object_to_file_system (public) fs::publish_object_to_file_system->fs::publish_folder_to_file_system

Testcases:
fs_publish_file

fs::publish_object_to_file_system (public)

 fs::publish_object_to_file_system -object_id object_id [ -path path ] \
    [ -file_name file_name ] [ -user_id user_id ]

publish a file storage object to the filesystem

Switches:
-object_id (required)
-path (optional)
-file_name (optional)
-user_id (optional)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_publish_file fs_publish_file (test file-storage) fs::publish_object_to_file_system fs::publish_object_to_file_system test_fs_publish_file->fs::publish_object_to_file_system ad_mktmpdir ad_mktmpdir (public) fs::publish_object_to_file_system->ad_mktmpdir content::symlink::resolve content::symlink::resolve (public) fs::publish_object_to_file_system->content::symlink::resolve db_1row db_1row (public) fs::publish_object_to_file_system->db_1row fs::publish_url_to_file_system fs::publish_url_to_file_system (public) fs::publish_object_to_file_system->fs::publish_url_to_file_system fs::publish_versioned_object_to_file_system fs::publish_versioned_object_to_file_system (public) fs::publish_object_to_file_system->fs::publish_versioned_object_to_file_system fs::publish_folder_to_file_system fs::publish_folder_to_file_system (public) fs::publish_folder_to_file_system->fs::publish_object_to_file_system packages/file-storage/www/download-zip.tcl packages/file-storage/ www/download-zip.tcl packages/file-storage/www/download-zip.tcl->fs::publish_object_to_file_system

Testcases:
fs_publish_file

fs::publish_url_to_file_system (public)

 fs::publish_url_to_file_system -object_id object_id [ -path path ] \
    [ -file_name file_name ]

publish a URL object to the filesystem as a Windows shortcut (which at least KDE also knows how to handle)

Switches:
-object_id (required)
-path (optional)
-file_name (optional)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_publish_file fs_publish_file (test file-storage) fs::publish_url_to_file_system fs::publish_url_to_file_system test_fs_publish_file->fs::publish_url_to_file_system ad_file ad_file (public) fs::publish_url_to_file_system->ad_file ad_mktmpdir ad_mktmpdir (public) fs::publish_url_to_file_system->ad_mktmpdir ad_sanitize_filename ad_sanitize_filename (public) fs::publish_url_to_file_system->ad_sanitize_filename db_1row db_1row (public) fs::publish_url_to_file_system->db_1row fs::publish_object_to_file_system fs::publish_object_to_file_system (public) fs::publish_object_to_file_system->fs::publish_url_to_file_system

Testcases:
fs_publish_file

fs::publish_versioned_object_to_file_system (public)

 fs::publish_versioned_object_to_file_system -object_id object_id \
    [ -path path ] [ -file_name file_name ]

publish an object to the filesystem

Switches:
-object_id (required)
-path (optional)
-file_name (optional)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_publish_file fs_publish_file (test file-storage) fs::publish_versioned_object_to_file_system fs::publish_versioned_object_to_file_system test_fs_publish_file->fs::publish_versioned_object_to_file_system ad_file ad_file (public) fs::publish_versioned_object_to_file_system->ad_file ad_mktmpdir ad_mktmpdir (public) fs::publish_versioned_object_to_file_system->ad_mktmpdir ad_sanitize_filename ad_sanitize_filename (public) fs::publish_versioned_object_to_file_system->ad_sanitize_filename content::item::get content::item::get (public) fs::publish_versioned_object_to_file_system->content::item::get content::revision::export_to_filesystem content::revision::export_to_filesystem (public) fs::publish_versioned_object_to_file_system->content::revision::export_to_filesystem fs::publish_object_to_file_system fs::publish_object_to_file_system (public) fs::publish_object_to_file_system->fs::publish_versioned_object_to_file_system

Testcases:
fs_publish_file

fs::remove_special_file_system_characters (public, deprecated)

 fs::remove_special_file_system_characters -string string
Deprecated. Invoking this procedure generates a warning.

Remove unsafe filesystem characters. Useful if you want to use $string as the name of an object to write to disk.

Switches:
-string (required)
See Also:

Partial Call Graph (max 5 caller/called nodes):
%3 _ _ (public) ad_log_deprecated ad_log_deprecated (public) fs::remove_special_file_system_characters fs::remove_special_file_system_characters fs::remove_special_file_system_characters->_ fs::remove_special_file_system_characters->ad_log_deprecated

Testcases:
No testcase defined.

fs::rename_folder (public)

 fs::rename_folder -folder_id folder_id -name name [ -no_callback ]

rename the given folder

Switches:
-folder_id (required)
-name (required)
-no_callback (optional, boolean)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_create_folder_using_api fs_create_folder_using_api (test file-storage) fs::rename_folder fs::rename_folder test_fs_create_folder_using_api->fs::rename_folder ad_conn ad_conn (public) fs::rename_folder->ad_conn callback callback (public) fs::rename_folder->callback db_exec_plsql db_exec_plsql (public) fs::rename_folder->db_exec_plsql packages/file-storage/www/folder-create.tcl packages/file-storage/ www/folder-create.tcl packages/file-storage/www/folder-create.tcl->fs::rename_folder

Testcases:
fs_create_folder_using_api

fs::set_folder_description (public)

 fs::set_folder_description -folder_id folder_id \
    [ -description description ]

sets the description for the given folder in cr_folders. Perhaps this should be a CR proc?

Switches:
-folder_id (required)
-description (optional)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs::set_folder_description fs::set_folder_description test_fs_add_delete_copy->fs::set_folder_description db_dml db_dml (public) fs::set_folder_description->db_dml packages/file-storage/www/folder-create.tcl packages/file-storage/ www/folder-create.tcl packages/file-storage/www/folder-create.tcl->fs::set_folder_description

Testcases:
fs_add_delete_copy

fs::webdav_url (public)

 fs::webdav_url -item_id item_id [ -root_folder_id root_folder_id ] \
    [ -package_id package_id ]

Provide URL for webdav access to file or folder

Switches:
-item_id (required)
folder_id or item_id of file-storage folder or file
-root_folder_id (optional)
root folder to resolve URL from
-package_id (optional)
Returns:
fully qualified URL for WebDAV access or empty string if item is not WebDAV enabled

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_publish_file fs_publish_file (test file-storage) fs::webdav_url fs::webdav_url test_fs_publish_file->fs::webdav_url ad_conn ad_conn (public) fs::webdav_url->ad_conn ad_url ad_url (public) fs::webdav_url->ad_url apm_package_url_from_id apm_package_url_from_id (public) fs::webdav_url->apm_package_url_from_id content::item::get_virtual_path content::item::get_virtual_path (public) fs::webdav_url->content::item::get_virtual_path fs::get_root_folder fs::get_root_folder (public) fs::webdav_url->fs::get_root_folder packages/file-storage/www/index.tcl packages/file-storage/ www/index.tcl packages/file-storage/www/index.tcl->fs::webdav_url

Testcases:
fs_publish_file

fs_context_bar_list (public)

 fs_context_bar_list [ -root_folder_id root_folder_id ] \
    [ -final final ] [ -folder_url folder_url ] [ -file_url file_url ] \
    [ -extra_vars extra_vars ] item_id

Constructs the list to be fed to ad_context_bar appropriate for item_id. If -final is specified, that string will be the last item in the context bar. Otherwise, the name corresponding to item_id will be used.

Switches:
-root_folder_id (optional)
-final (optional)
-folder_url (optional, defaults to "index")
-file_url (optional, defaults to "file")
-extra_vars (optional)
Parameters:
item_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_attachments_url_api attachments_url_api (test attachments) fs_context_bar_list fs_context_bar_list test_attachments_url_api->fs_context_bar_list test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) test_fs_add_file_to_folder->fs_context_bar_list test_fs_create_folder fs_create_folder (test file-storage) test_fs_create_folder->fs_context_bar_list test_fs_edit_folder fs_edit_folder (test file-storage) test_fs_edit_folder->fs_context_bar_list db_exec_plsql db_exec_plsql (public) fs_context_bar_list->db_exec_plsql db_list_of_lists db_list_of_lists (public) fs_context_bar_list->db_list_of_lists db_string db_string (public) fs_context_bar_list->db_string fs_get_root_folder fs_get_root_folder (public) fs_context_bar_list->fs_get_root_folder attachments::context_bar attachments::context_bar (public) attachments::context_bar->fs_context_bar_list packages/file-storage/www/admin/rss-subscr-ae.tcl packages/file-storage/ www/admin/rss-subscr-ae.tcl packages/file-storage/www/admin/rss-subscr-ae.tcl->fs_context_bar_list packages/file-storage/www/admin/rss-subscrs.tcl packages/file-storage/ www/admin/rss-subscrs.tcl packages/file-storage/www/admin/rss-subscrs.tcl->fs_context_bar_list packages/file-storage/www/file-add.tcl packages/file-storage/ www/file-add.tcl packages/file-storage/www/file-add.tcl->fs_context_bar_list packages/file-storage/www/file-edit.tcl packages/file-storage/ www/file-edit.tcl packages/file-storage/www/file-edit.tcl->fs_context_bar_list

Testcases:
attachments_url_api, fs_create_folder, fs_edit_folder, fs_add_file_to_folder

fs_file_p (public)

 fs_file_p file_id

Returns 1 if the file_id corresponds to a file in the file-storage system. Returns 0 otherwise.

Parameters:
file_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs_file_p fs_file_p test_fs_add_delete_copy->fs_file_p db_0or1row db_0or1row (public) fs_file_p->db_0or1row packages/file-storage/www/file-edit.tcl packages/file-storage/ www/file-edit.tcl packages/file-storage/www/file-edit.tcl->fs_file_p packages/file-storage/www/file.tcl packages/file-storage/ www/file.tcl packages/file-storage/www/file.tcl->fs_file_p packages/file-storage/www/permissions.tcl packages/file-storage/ www/permissions.tcl packages/file-storage/www/permissions.tcl->fs_file_p

Testcases:
fs_add_delete_copy

fs_folder_p (public)

 fs_folder_p folder_id

Returns 1 if the folder_id corresponds to a folder in the file-storage system. Returns 0 otherwise.

Parameters:
folder_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs_folder_p fs_folder_p test_fs_add_delete_copy->fs_folder_p db_0or1row db_0or1row (public) fs_folder_p->db_0or1row packages/acs-templating/www/scripts/xinha/file-selector.tcl packages/acs-templating/ www/scripts/xinha/file-selector.tcl packages/acs-templating/www/scripts/xinha/file-selector.tcl->fs_folder_p packages/attachments/www/file-add-2.tcl packages/attachments/ www/file-add-2.tcl packages/attachments/www/file-add-2.tcl->fs_folder_p packages/attachments/www/file-add.tcl packages/attachments/ www/file-add.tcl packages/attachments/www/file-add.tcl->fs_folder_p packages/attachments/www/simple-add-2.tcl packages/attachments/ www/simple-add-2.tcl packages/attachments/www/simple-add-2.tcl->fs_folder_p packages/attachments/www/simple-add.tcl packages/attachments/ www/simple-add.tcl packages/attachments/www/simple-add.tcl->fs_folder_p

Testcases:
fs_add_delete_copy

fs_get_folder_name (public)

 fs_get_folder_name folder_id

Returns the name of a folder.

Parameters:
folder_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_create_folder_using_api fs_create_folder_using_api (test file-storage) fs_get_folder_name fs_get_folder_name test_fs_create_folder_using_api->fs_get_folder_name db_string db_string (public) fs_get_folder_name->db_string fs::do_notifications fs::do_notifications (public) fs::do_notifications->fs_get_folder_name packages/file-storage/www/admin/rss-subscr-ae.tcl packages/file-storage/ www/admin/rss-subscr-ae.tcl packages/file-storage/www/admin/rss-subscr-ae.tcl->fs_get_folder_name packages/file-storage/www/admin/rss-subscrs.tcl packages/file-storage/ www/admin/rss-subscrs.tcl packages/file-storage/www/admin/rss-subscrs.tcl->fs_get_folder_name packages/file-storage/www/folder-delete.tcl packages/file-storage/ www/folder-delete.tcl packages/file-storage/www/folder-delete.tcl->fs_get_folder_name packages/file-storage/www/index.tcl packages/file-storage/ www/index.tcl packages/file-storage/www/index.tcl->fs_get_folder_name

Testcases:
fs_create_folder_using_api

fs_get_root_folder (public)

 fs_get_root_folder [ -package_id package_id ]

Returns the root folder for the file storage system.

Switches:
-package_id (optional)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_file_to_folder fs_add_file_to_folder (test file-storage) fs_get_root_folder fs_get_root_folder test_fs_add_file_to_folder->fs_get_root_folder test_fs_create_folder fs_create_folder (test file-storage) test_fs_create_folder->fs_get_root_folder test_fs_edit_folder fs_edit_folder (test file-storage) test_fs_edit_folder->fs_get_root_folder ad_conn ad_conn (public) fs_get_root_folder->ad_conn fs::get_root_folder fs::get_root_folder (public) fs_get_root_folder->fs::get_root_folder fs::get_item_id fs::get_item_id (public) fs::get_item_id->fs_get_root_folder fs_context_bar_list fs_context_bar_list (public) fs_context_bar_list->fs_get_root_folder packages/acs-templating/www/scripts/xinha/file-selector.tcl packages/acs-templating/ www/scripts/xinha/file-selector.tcl packages/acs-templating/www/scripts/xinha/file-selector.tcl->fs_get_root_folder packages/file-storage/www/admin/rss-subscr-ae.tcl packages/file-storage/ www/admin/rss-subscr-ae.tcl packages/file-storage/www/admin/rss-subscr-ae.tcl->fs_get_root_folder packages/file-storage/www/admin/rss-subscrs.tcl packages/file-storage/ www/admin/rss-subscrs.tcl packages/file-storage/www/admin/rss-subscrs.tcl->fs_get_root_folder

Testcases:
fs_create_folder, fs_edit_folder, fs_add_file_to_folder

fs_version_p (public)

 fs_version_p version_id

Returns 1 if the version_id corresponds to a version in the file-storage system. Returns 0 otherwise.

Parameters:
version_id (required)

Partial Call Graph (max 5 caller/called nodes):
%3 test_fs_add_delete_copy fs_add_delete_copy (test file-storage) fs_version_p fs_version_p test_fs_add_delete_copy->fs_version_p db_0or1row db_0or1row (public) fs_version_p->db_0or1row packages/file-storage/www/version-delete.tcl packages/file-storage/ www/version-delete.tcl packages/file-storage/www/version-delete.tcl->fs_version_p

Testcases:
fs_add_delete_copy
[ hide source ] | [ make this the default ]

Content File Source

ad_library {
    Tcl library for the file-storage system (v.4)
    @author Kevin Scaldeferri (kevin@arsdigita.com)
    @creation-date 6 November 2000
    @cvs-id $Id: file-storage-procs.tcl,v 1.90.2.42 2024/05/31 13:57:38 antoniop Exp $
}

d_proc fs_get_root_folder {
    {-package_id ""}
} {
    Returns the root folder for the file storage system.
} {
    if {$package_id eq ""} {
        set package_id [ad_conn package_id]
    }

    return [fs::get_root_folder -package_id $package_id]
}

d_proc fs_get_folder_name {
    folder_id
} {
    Returns the name of a folder.
} {
    return [db_string folder_name {}]
}

#
# Type-checking procs
#
# These might be cleaner if we were subtyping the CR object types
# We aren't checking if the objects are actually below the file-storage
# root, although we probably should.
#
# Note that we aren't using things like the content_folder.is_folder
# method because that will return 't' for things elsewhere in the
# content repository that have subclassed the default types.
#

d_proc fs_folder_p {
    folder_id
} {
    Returns 1 if the folder_id corresponds to a folder in the file-storage
    system.  Returns 0 otherwise.
} {
    return [db_0or1row is_folder {
        select 1 from acs_objects
        where object_id = :folder_id
        and object_type = 'content_folder'}]
}

d_proc fs_file_p {
    file_id
} {
    Returns 1 if the file_id corresponds to a file in the file-storage
    system.  Returns 0 otherwise.
} {
    return [db_0or1row is_file {
        select 1 from acs_objects
        where object_id = :file_id
        and object_type = 'content_item'}]
}

d_proc fs_version_p {
    version_id
} {
    Returns 1 if the version_id corresponds to a version in the file-storage
    system.  Returns 0 otherwise.
} {
    return [db_0or1row is_version {
        select 1 from acs_objects
        where object_id = :version_id
        and object_type = 'file_storage_object'}]
}

#
# Permission procs
#

d_proc -private fs_children_have_permission_p {
    {-user_id ""}
    item_id
    privilege
} {
    This procedure, given a content item and a privilege, checks to see if
    there are any children of the item on which the user does not have that
    privilege.

    @return 0 if there is any child item on which the user does not
    have the privilege.  It returns 1 if the user has the privilege on
    every child item.
} {
    if {$user_id eq ""} {
        set user_id [ad_conn user_id]
    }

    # Check that no item or revision over the whole cr_item
    # descendants hierarchy does not have the required permissison.
    set all_children_have_privilege_p [db_string all_children_have_privilege {
        with recursive children(item_id) as (
            select cast(:item_id as integer) as item_id
            union all
            select i.item_id
            from cr_items i,
                 children c
            where i.parent_id = c.item_id
        )
        select not exists (select 1 from children
                            where acs_permission.permission_p(item_id, :user_id, :privilege) = 'f')
           and not exists (select 1 from cr_revisions
                            where item_id in (select item_id from children)
                              and acs_permission.permission_p(revision_id, :user_id, :privilege) = 'f')
          from dual
    }]
    return [expr {$all_children_have_privilege_p ? 1 : 0}]
}


#
# Display procs
#

d_proc fs_context_bar_list {
    {-root_folder_id ""}
    {-final ""}
    {-folder_url "index"}
    {-file_url "file"}
    {-extra_vars ""}
    item_id
} {
    Constructs the list to be fed to ad_context_bar appropriate for
    item_id.  If -final is specified, that string will be the last
    item in the context bar.  Otherwise, the name corresponding to
    item_id will be used.
} {
    if {$root_folder_id eq ""} {
        set root_folder_id [fs_get_root_folder]
    }

    if {$final eq ""
        && !($item_id == $root_folder_id)
    } {
        # don't get title for last element if we are in the
        # root folder
        set start_id [db_string parent_id {
            select parent_id from cr_items where item_id = :item_id
        }]
        set final [db_exec_plsql title {}]
    } else {
        set start_id $item_id
    }

    set extra_vars [concat &$extra_vars]

    set context_bar [db_list_of_lists context_bar {}]
    if {$item_id != $root_folder_id} {
        lappend context_bar $final
    }
    return $context_bar
}

namespace eval fs {}

d_proc -private fs::after_mount {
    -package_id:required
    -node_id:required
} {
    Create root folder for package instance
    via Tcl callback.
} {
    set folder_id [fs::get_root_folder -package_id $package_id]

    if {[apm_package_installed_p oacs-dav]} {
        oacs_dav::register_folder -enabled_p "t" $folder_id $node_id
    }
}

d_proc -private fs::before_unmount {
    -package_id:required
    -node_id:required
} {
    Unregister the root WebDAV folder mapping before
    unmounting a file storage package instance.
} {
    set folder_id [fs::get_root_folder -package_id $package_id]

    if {[apm_package_installed_p oacs-dav]} {
        oacs_dav::unregister_folder $folder_id $node_id
    }
}

d_proc -public fs::new_root_folder {
    {-package_id ""}
    {-pretty_name ""}
    {-description ""}
    {-name ""}
} {
    Create a root folder for a package instance.

    @param package_id Package instance associated with this root folder

    @return folder_id of the new root folder
} {

    if {$package_id eq ""} {
        set package_id [ad_conn package_id]
    }

    if {$pretty_name eq ""} {
        set pretty_name [apm_instance_name_from_id $package_id]
    }

    if {$name eq ""} {
        set name "file-storage_${package_id}"
    }

    return [db_exec_plsql new_root_folder {}]

}


d_proc -public fs::get_root_folder {
    {-package_id ""}
} {
    Get the root folder of a package instance.

    @param package_id Package instance of the root folder to retrieve

    @return folder_id of the root folder retrieved
} {
    if {$package_id eq ""} {
        set package_id [ad_conn package_id]
    }

    return [db_exec_plsql get_root_folder {}]
}

d_proc -public fs::get_parent {
    -item_id:required
} {
    Get the parent of a given item.
} {
    return [db_string get_parent_id {}]
}


d_proc -public fs::new_folder {
    {-name:required}
    {-pretty_name:required}
    {-parent_id:required}
    {-creation_user ""}
    {-creation_ip ""}
    {-description ""}
    {-package_id ""}
    -no_callback:boolean
} {
    Create a new folder.

    @param name Internal name of the folder, must be unique under a given
    parent_id
    @param pretty_name What we show to users of the system
    @param parent_id Where we create this folder
    @param creation_user Who created this folder
    @param creation_ip What is the IP address of the creation_user
    @param description of the folder. Not used in the current FS UI but might be used elsewhere.
    @param package_id Package_id of the package for which to create the new folder.
           Preferably a file storage package_id
    @param no_callback defines if the callback should be called. Defaults to yes
    @return folder_id of the newly created folder
} {
    if {$creation_user eq ""} {
        set creation_user [ad_conn user_id]
    }

    if {$creation_ip eq ""} {
        set creation_ip [ns_conn peeraddr]
    }

    # If the package_id is empty, try the package_id from the parent_object
    if {$package_id eq ""} {
        set package_id [acs_object::package_id -object_id $parent_id]

        # If the package_id from the parent_id exists, make sure it is a file-storage package_id
        if {$package_id ne ""} {
            if {[apm_package_key_from_id $package_id] ne "file-storage"} {
                set package_id ""
            }
        }
    }

    set folder_id [content::folder::new \
                       -name $name \
                       -label $pretty_name \
                       -parent_id $parent_id \
                       -creation_user $creation_user \
                       -creation_ip $creation_ip \
                       -description $description \
                       -package_id $package_id]
    permission::grant -party_id $creation_user -object_id $folder_id -privilege "admin"

    if {!$no_callback_p} {
        callback fs::folder_new -package_id $package_id -folder_id $folder_id
    }

    return $folder_id
}

d_proc -public fs::rename_folder {
    {-folder_id:required}
    {-name:required}
    -no_callback:boolean
} {
    rename the given folder
} {
    db_exec_plsql rename_folder {}
    if {!$no_callback_p} {
        callback fs::folder_edit \
            -package_id [ad_conn package_id] \
            -folder_id $folder_id
    }
}

d_proc -public fs::set_folder_description {
    {-folder_id:required}
    {-description ""}
} {
    sets the description for the given folder in cr_folders. Perhaps this should be a CR proc?
} {
    db_dml set_folder_description {}
}

d_proc -public fs::object_p {
    {-object_id:required}
} {
    is this a file storage object
} {
    if {![string is integer -strict $object_id]} {
        return 0
    }
    return [db_string select_object_p {}]
}

d_proc -public fs::get_object_name {
    {-object_id:required}
} {
    Select the name of this object.
} {
    return [db_string select_object_name {} -default $object_id]
}

d_proc -public fs::get_object_prettyname {
    {-object_id:required}
} {
    Select a pretty name for this object. If title is empty, returns name.
} {
    return [db_string select_object_prettyname {} -default $object_id]
}

d_proc -public fs::get_file_system_safe_object_name {
    {-object_id:required}
} {
    get the name of a file storage object and make it safe for writing to
    the filesystem
} {
    return [ad_sanitize_filename \
                -collapse_spaces \
                -tolower \
                [get_object_name -object_id $object_id]]
}

d_proc -deprecated -public fs::remove_special_file_system_characters {
    {-string:required}
} {
    Remove unsafe filesystem characters. Useful if you want to use $string
    as the name of an object to write to disk.

    @see ad_sanitize_filename
} {
    regsub -all -- {[<>:\"|/@\#%&+\\]} $string {_} string
    return [string trim $string]
}

d_proc -public fs::folder_p {
    {-object_id:required}
} {
    Is this object a folder?

    @return true if object_id is a folder
} {
    return [db_string select_folder_p {} -default 0]
}

d_proc -public fs::get_folder {
    {-name:required}
    {-parent_id:required}
} {
    Retrieve the folder_id of a folder given its name and parent folder.

    @param name Internal name of the folder, must be unique under a given
    parent_id
    @param parent_id The parent folder to look under

    @return folder_id of the folder, or null if no folder was found by that
    name
} {
    return [db_string select_folder {} -default ""]
}

d_proc -public fs::get_folder_objects {
    -folder_id:required
    -user_id:required
} {
    Return a list the object_ids contained by a file storage folder.

    @param folder_id The folder for which to retrieve contents
    @param user_id The viewer of the contents (to make sure they have
                                               permission)

} {
    return [db_list select_folder_contents {
        select item_id
          from cr_items
         where parent_id = :folder_id
           and acs_permission.permission_p(item_id, :user_id, 'read') = 't'
    }]
}

d_proc -deprecated fs::get_folder_contents {
    {-folder_id ""}
    {-user_id ""}
    {-n_past_days "99999"}
} {
    WARNING: This proc is not scalable because it does too many permission checks.

    DRB: Not so true now that permissions are fast.  However, it is now only used
    to clone files in dotLRN and for the somewhat brain-damaged syllabus package.
    At minimum the permission checks returned by the code can be removed.  Most of
    the other fields as well.   Oh well ...

    REMOVE WHEN SYLLABUS IS REWRITTEN TO FIND ITS FILE INTELLIGENTLY

    Retrieve the contents of the specified folder in the form of a list
    of ns_sets, one for each row returned. The keys for each row are as
    follows:

    object_id, name, live_revision, type,
    last_modified, new_p, content_size, file_upload_name
    write_p, delete_p, admin_p,

    DEPRECATED: this proc has been evidently the subject of
    controversy over the years. To this day (2023-03-17) no package
    seems to be using it. One can either query the database directly
    or use other existing apis to retrieve the folder children and
    then fetch the needed metadata via other means.

    @see fs::get_folder_objects

    @param folder_id The folder for which to retrieve contents
    @param user_id The viewer of the contents (to make sure they have
                                               permission)
    @param n_past_days Mark files that are newer than the past N days as new
} {
    if {$folder_id eq ""} {
        set folder_id [get_root_folder -package_id [ad_conn package_id]]
    }

    if {$user_id eq ""} {
        set user_id [acs_magic_object the_public]
    }

    set list_of_ns_sets [db_list_of_ns_sets select_folder_contents [subst {
           select fs_objects.object_id,
           fs_objects.name,
           fs_objects.title,
           fs_objects.live_revision,
           fs_objects.type,
           to_char(fs_objects.last_modified, 'YYYY-MM-DD HH24:MI:SS') as last_modified_ansi,
           fs_objects.content_size,
           fs_objects.url,
           fs_objects.key,
           fs_objects.sort_key,
           fs_objects.file_upload_name,
           fs_objects.title,
           fs_objects.last_modified >= (current_timestamp - cast('$n_past_days days' as interval)) as new_p,
           acs_permission.permission_p(fs_objects.object_id, :user_id, 'admin') as admin_p,
           acs_permission.permission_p(fs_objects.object_id, :user_id, 'delete') as delete_p,
           acs_permission.permission_p(fs_objects.object_id, :user_id, 'write') as write_p
           from fs_objects
           where fs_objects.parent_id = :folder_id
           and acs_permission.permission_p(fs_objects.object_id, :user_id, 'read') = 't'
           order by fs_objects.sort_key, fs_objects.name
    }]]

    foreach set $list_of_ns_sets {
        # in plain Tcl:
        # set last_modified_ansi [lc_time_system_to_conn $last_modified_ansi]
        ns_set put $set last_modified_ansi [lc_time_system_to_conn [ns_set get $set last_modified_ansi]]

        # in plain Tcl:
        # set last_modified [lc_time_fmt $last_modified_ansi "%x %X"]
        ns_set put $set last_modified [lc_time_fmt [ns_set get $set last_modified_ansi] "%x %X"]

        # set content_size_pretty [lc_numeric $content_size]
        ns_set put $set content_size_pretty [lc_numeric [ns_set get $set content_size]]
    }

    return $list_of_ns_sets
}

d_proc -public fs::get_folder_contents_count {
    {-folder_id ""}
    {-user_id ""}
} {
    Retrieve the count of contents of the specified folder.

    @param folder_id The folder for which to retrieve contents

    @param user_id DEPRECATED since commit 2002-02-22 by Yonatan
                   Feldman (yon@milliped.com) this parameter doesn't
                   have any effect. It was used to count only items
                   where user had read permission, but was considered
                   unscalable.
} {
    if {$folder_id eq ""} {
        set folder_id [get_root_folder -package_id [ad_conn package_id]]
    }

    if {$user_id ne ""} {
        ns_log warning "fs::get_folder_contents_count:" \
            "specified -user_id doesn't have any effect on proc result"
    }

    return [db_string select_folder_contents_count {}]
}

d_proc -public fs::publish_object_to_file_system {
    {-object_id:required}
    {-path ""}
    {-file_name ""}
    {-user_id ""}
} {
    publish a file storage object to the filesystem
} {
    if {$path eq ""} {
        set path [ad_mktmpdir]
    }

    db_1row select_object_info {}

    switch -- $type {
        folder {
            set result [publish_folder_to_file_system \
                            -folder_id $object_id \
                            -path $path \
                            -folder_name $name \
                            -user_id $user_id]
        }
        url {
            set result [publish_url_to_file_system \
                            -object_id $object_id \
                            -path $path \
                            -file_name $file_name]
        }
        symlink {
            set linked_object_id [content::symlink::resolve -item_id $object_id]
            set result [publish_versioned_object_to_file_system \
                            -object_id $linked_object_id \
                            -path $path \
                            -file_name $file_name]
        }
        default {
            set result [publish_versioned_object_to_file_system \
                            -object_id $object_id \
                            -path $path \
                            -file_name $file_name]
        }
    }
    return $result
}

d_proc -public fs::publish_folder_to_file_system {
    {-folder_id:required}
    {-path ""}
    {-folder_name ""}
    {-user_id ""}
} {
    publish the contents of a file storage folder to the filesystem
} {
    if {$path eq ""} {
        set path [ad_tmpnam]
    }

    if {$folder_name eq ""} {
        set folder_name [get_object_name -object_id $folder_id]
    }
    set folder_name [ad_sanitize_filename \
                         -collapse_spaces \
                         -tolower \
                         $folder_name]

    set dir [ad_file join $path $folder_name]
    # set dir [ad_file join $path "download"]
    file mkdir $dir

    db_foreach get_folder_contents {
        select object_id,
               name
          from fs_objects
         where parent_id = :folder_id
           and acs_permission.permission_p(object_id, :user_id, 'read') = 't'
        order by sort_key,
                 name
    } {
        set file_name [ad_sanitize_filename \
                           -collapse_spaces \
                           -tolower \
                           $name]
        publish_object_to_file_system \
            -object_id $object_id \
            -path $dir \
            -file_name $file_name \
            -user_id $user_id
    }

    return $dir
}

d_proc -public fs::publish_url_to_file_system {
    {-object_id:required}
    {-path ""}
    {-file_name ""}
} {
    publish a URL object to the filesystem as a Windows shortcut
    (which at least KDE also knows how to handle)
} {
    if {$path eq ""} {
        set path [ad_mktmpdir]
    }

    db_1row select_object_metadata {}

    if {$file_name eq ""} {
        set file_name $name
    }
    set file_name "${file_name}.url"
    set file_name [ad_sanitize_filename \
                       -collapse_spaces \
                       -tolower \
                       $file_name]

    set fp [open [ad_file join $path $file_name] w]
    puts $fp {[InternetShortcut]}
    puts $fp URL=$url
    close $fp

    return [ad_file join $path $file_name]
}

d_proc -public fs::publish_versioned_object_to_file_system {
    {-object_id:required}
    {-path ""}
    {-file_name ""}
} {
    publish an object to the filesystem
} {
    if {$path eq ""} {
        set path [ad_mktmpdir]
    }

    db_1row select_object_metadata {}

    # After upgrade change title and filename...
    set like_filesystem_p [parameter::get -parameter BehaveLikeFilesystemP -default 1]

    if { $like_filesystem_p } {
        set file_name $title
        if {$file_name eq ""} {
            if {![info exists upload_file_name]} {
                set file_name "unnamedfile"
            } else {
                set file_name $file_upload_name
            }
        } elseif {[content::item::get -item_id $object_id -array_name item_info]} {
            # We make sure that the file_name contains the file
            # extension at the end so that the users default
            # application for that file type can be used

            set mime_type $item_info(mime_type)
            set file_extension [db_string get_extension {
                select file_extension from cr_mime_types where mime_type = :mime_type
            }]

            if { ![regexp "\.$file_extension$" $file_name match] } {
                set file_name "$file_name.$file_extension"
            }
        }
    } else {
        set file_name $file_upload_name
    }

    set file_name [ad_sanitize_filename \
                       -collapse_spaces \
                       -tolower \
                       $file_name]

    set full_filename [ad_file join $path $file_name]
    ::content::revision::export_to_filesystem \
        -storage_type $storage_type \
        -revision_id $live_revision \
        -filename $full_filename

    return $full_filename
}

d_proc -deprecated fs::get_archive_command {
    {-in_file ""}
    {-out_file ""}
} {
    return the archive command after replacing {in_file} and {out_file} with
    their respective values.
} {
    if {[ad_conn package_key] ne "file-storage"} {
        error "fs::get_archive_command must be called inside the file-storage"
    }

    set cmd [parameter::get -parameter ArchiveCommand -default "tar cf - {in_file} | gzip > {out_file}"]

    regsub -all -- {(\W)} $in_file {\\\1} in_file
    regsub -all -- {\\/} $in_file {/} in_file
    regsub -all -- {\\\.} $in_file {.} in_file

    regsub -all -- {(\W)} $out_file {\\\1} out_file
    regsub -all -- {\\/} $out_file {/} out_file
    regsub -all -- {\\\.} $out_file {.} out_file

    regsub -all -- {{in_file}} $cmd $in_file cmd
    regsub -all -- {{out_file}} $cmd $out_file cmd

    return $cmd
}

ad_proc -deprecated fs::get_archive_extension {} {
    return the archive extension that should be added to the output file of
    an archive command

    DEPRECATED: this is a trivial wrapper over the parameter api

    @see parameter::get
} {
    return [parameter::get -parameter ArchiveExtension -default "txt"]
}

d_proc -public fs::get_item_id {
    -name:required
    {-folder_id ""}
} {
    Get the item_id of a file
} {
    if {$folder_id eq ""} {
        set folder_id [fs_get_root_folder -package_id [ad_conn package_id]]
    }
    return [content::item::get_id \
                -item_path      $name \
                -root_folder_id $folder_id \
                -resolve_index "f"]
}

d_proc -public fs::add_file {
    -name:required
    -parent_id:required
    -package_id:required
    {-item_id ""}
    {-creation_user ""}
    {-creation_ip ""}
    {-title ""}
    {-description ""}
    {-tmp_filename ""}
    {-mime_type ""}
    -no_callback:boolean
    -no_notification:boolean
} {
    Create a new file storage item or add a new revision if
    an item with the same name and parent folder already exists

    @return revision_id
} {

    if {[parameter::get -parameter "StoreFilesInDatabaseP" -package_id $package_id]} {
        set indbp "t"
        set storage_type "lob"
    } else {
        set indbp "f"
        set storage_type "file"
    }

    # This check also happens in content repository, but as something
    # similar was already here and mimetype coming from this was used
    # afterwards, we kept this behavior.
    set mime_type [cr_check_mime_type \
                       -filename  $name \
                       -mime_type $mime_type \
                       -file      $tmp_filename]

    # we have to do this here because we create the object before
    # calling cr_import_content

    if {[content::type::content_type_p -mime_type $mime_type -content_type "image"]} {
        set content_type image
    } else {
        set content_type file_storage_object
    }

    if {$item_id eq ""} {
        set item_id [db_nextval acs_object_id_seq]
    }

    db_transaction {
        if {![db_string item_exists {}]} {

            set item_id [content::item::new \
                             -item_id $item_id \
                             -parent_id $parent_id \
                             -creation_user "$creation_user" \
                             -creation_ip "$creation_ip" \
                             -package_id "$package_id" \
                             -name $name \
                             -storage_type "$storage_type" \
                             -content_type "file_storage_object" \
                             -mime_type "text/plain"
                            ]

            if {$creation_user ne ""} {
                permission::grant -party_id $creation_user -object_id $item_id -privilege admin
            }

            # Deal with notifications. Usually, send out the notification
            # But suppress it if the parameter is given
            if {$no_notification_p} {
                set do_notify_here_p "f"
            } else {
                set do_notify_here_p "t"
            }
        } else {
            # th: fixed to set old item_id if item already exists and no new item needed to be created
            db_1row get_old_item ""
            set do_notify_here_p "f"
        }
        if {$no_callback_p} {
            set revision_id [fs::add_version \
                                 -name $name \
                                 -tmp_filename $tmp_filename \
                                 -package_id $package_id \
                                 -item_id $item_id \
                                 -creation_user $creation_user \
                                 -creation_ip $creation_ip \
                                 -title $title \
                                 -description $description \
                                 -suppress_notify_p $do_notify_here_p \
                                 -storage_type $storage_type \
                                 -mime_type $mime_type \
                                 -no_callback
                            ]
        } else {
            set revision_id [fs::add_version \
                                 -name $name \
                                 -tmp_filename $tmp_filename \
                                 -package_id $package_id \
                                 -item_id $item_id \
                                 -creation_user $creation_user \
                                 -creation_ip $creation_ip \
                                 -title $title \
                                 -description $description \
                                 -suppress_notify_p $do_notify_here_p \
                                 -storage_type $storage_type \
                                 -mime_type $mime_type
                            ]
        }

        if {[string is true $do_notify_here_p]} {
            fs::do_notifications \
                -folder_id $parent_id \
                -filename $title \
                -item_id $revision_id \
                -action "new_file" \
                -package_id $package_id

            if {!$no_callback_p} {
                callback fs::file_new \
                    -package_id [ad_conn package_id] \
                    -file_id $item_id
            }
        }
    }
    return $revision_id
}

d_proc -deprecated fs::add_created_file {
    {-name ""}
    -parent_id:required
    -package_id:required
    {-item_id ""}
    {-mime_type ""}
    {-creation_user ""}
    {-creation_ip ""}
    {-title ""}
    {-description ""}
    {-content_body ""}
} {
    Create a new file storage item or add a new revision if
    an item with the same name and parent folder already exists

    DEPRECATED: this proc was superseded by fs::add_file

    @see fs::add_file

    @return revision_id
} {
    if {[parameter::get -parameter "StoreFilesInDatabaseP" -package_id $package_id]} {
        set indbp "t"
        set storage_type "lob"
    } else {
        set indbp "f"
        set storage_type "file"
    }
    if {$item_id ne ""} {
        set storage_type [db_string get_storage_type {
            select storage_type from cr_items where item_id=:item_id
        }]
    }
    if {$mime_type eq "" } {
        set mime_type "text/html"
    }
    if { $name eq "" } {
        set name $title
    }

    set content_type "file_storage_object"

    db_transaction {
        if {$item_id eq "" || ![db_string item_exists {}]} {
            set item_id [db_exec_plsql create_item ""]
            if {$creation_user ne ""} {
                permission::grant -party_id $creation_user -object_id $item_id -privilege admin
            }
            set do_notify_here_p "t"
        } else {
            set do_notify_here_p "f"
        }

        set revision_id [fs::add_created_version \
            -name $title \
            -item_id $item_id \
            -creation_user $creation_user \
            -creation_ip [ad_conn peeradd] \
            -title $title \
            -description $description \
            -package_id $package_id \
            -content_body $content_body \
            -mime_type $mime_type \
            -storage_type $storage_type]


        if {[string is true $do_notify_here_p]} {
            fs::do_notifications \
                -folder_id $parent_id \
                -filename $title \
                -item_id $revision_id \
                -action "new_file" \
                -package_id $package_id
        }

        if {!$no_callback_p} {
            callback fs::file_new \
                -package_id [ad_conn package_id] \
                -file_id $item_id
        }
    }
    return $revision_id
}

d_proc -deprecated fs::add_created_version {
    -name:required
    -content_body:required
    -mime_type:required
    -item_id:required
    {-creation_user ""}
    {-creation_ip ""}
    {-title ""}
    {-description ""}
    {-suppress_notify_p "f" }
    {-storage_type ""}
    {-package_id ""}
    {-storage_type ""}
} {
    Create a new version of a file storage item using the content passed in content_body

    DEPRECATED: this proc has been superseded by fs::add_version

    @see fs::add_version

    @return revision_id
} {
    if {$package_id eq ""} {
        set package_id [ad_conn package_id]
    }
    if {$storage_type eq ""} {
        set storage_type [db_string get_storage_type {
            select storage_type from cr_items where item_id = :item_id
        }]
    }
    if {$creation_user eq ""} {
        set creation_user [ad_conn user_id]
    }
    if {$creation_ip eq ""} {
        set creation_ip [ns_conn peeraddr]
    }

    set revision_id [content::revision::new \
                         -item_id $item_id \
                         -title $title \
                         -description $description \
                         -content $content_body \
                         -mime_type $mime_type \
                         -creation_user $creation_user \
                         -creation_ip $creation_ip \
                         -package_id $package_id \
                         -is_live "t" \
                         -storage_type $storage_type]

    set parent_id [fs::get_parent -item_id $item_id]

    if {[string is false $suppress_notify_p]} {
        fs::do_notifications \
            -folder_id $parent_id \
            -filename $title \
            -item_id $revision_id \
            -action "new_version" \
            -package_id $package_id
    }

    #
    # It is safe to rebuild RSS repeatedly, assuming it's not too
    # expensive.
    #
    set folder_info [fs::get_folder_package_and_root $parent_id]
    set db_package_id [lindex $folder_info 0]
    if { [parameter::get -package_id $db_package_id -parameter ExposeRssP -default 0] } {
        fs::rss::build_feeds $parent_id
    }

    return $revision_id
}


d_proc fs::add_version {
    -item_id:required
    {-name ""}
    {-package_id ""}
    {-mime_type ""}
    -tmp_filename
    -content_body
    {-creation_user ""}
    {-creation_ip ""}
    {-title ""}
    {-description ""}
    {-suppress_notify_p "f"}
    {-storage_type ""}
    -no_callback:boolean
} {
    Create a new version of a file storage item.

    @param tmp_filename absolute path to a file on the
                        filesystem. when specified, the new revision
                        data will come from this file.
    @param content_body Text content for the new revision. When
                        'tmp_filename' is missing, the new revision
                        data will come from here.

    @return revision_id
} {
    if {![info exists content_body] && ![info exists tmp_filename]} {
        error "No data supplied for the new version."
    }

    #
    # Obtain optional information for the new version from the
    # existing item.
    #
    db_1row get_item_info {
        select coalesce(:storage_type, i.storage_type) as storage_type,
               coalesce(:name, i.name) as name,
               coalesce(:package_id, o.package_id) as package_id,
               coalesce(:title, r.title) as title,
               coalesce(:description, r.description) as description,
               coalesce(:mime_type, r.mime_type) as mime_type,
               i.parent_id
        from cr_items i
             -- we may not have a live revision here yet
             left join cr_revisions r
               on r.revision_id = i.live_revision,
             acs_objects o
        where i.item_id = :item_id
          and o.object_id = i.item_id
    }

    #
    # Obtain other possibly missing information from the connection
    # context.
    #
    if {$package_id eq ""} {
        set package_id [ad_conn package_id]
    }
    if {$creation_user eq ""} {
        set creation_user [ad_conn user_id]
    }
    if {$creation_ip eq ""} {
        set creation_ip [ns_conn peeraddr]
    }

    if {[info exists tmp_filename]} {
        #
        # The new revision will come from a file.
        #

        # This check also happens in content repository, but as something
        # similar was already here and mimetype coming from this was used
        # afterwards, we kept this behavior.
        set mime_type [cr_check_mime_type \
                           -filename  $name \
                           -mime_type $mime_type \
                           -file      $tmp_filename]

        set tmp_size [ad_file size $tmp_filename]
        set revision_id [cr_import_content \
                             -item_id $item_id \
                             -storage_type $storage_type \
                             -creation_user $creation_user \
                             -creation_ip $creation_ip \
                             -other_type "file_storage_object" \
                             -image_type "file_storage_object" \
                             -title $title \
                             -description $description \
                             -package_id $package_id \
                             $parent_id \
                             $tmp_filename \
                             $tmp_size \
                             $mime_type \
                             $name]

        content::item::set_live_revision -revision_id $revision_id
    } else {
        #
        # The new revision will come from text content.
        #

        set revision_id [content::revision::new \
                             -item_id $item_id \
                             -title $title \
                             -description $description \
                             -content $content_body \
                             -mime_type $mime_type \
                             -creation_user $creation_user \
                             -creation_ip $creation_ip \
                             -package_id $package_id \
                             -is_live "t" \
                             -storage_type $storage_type]
    }

    # apisano - This is what we had before (postgres code):
    # begin
    # perform acs_object__update_last_modified
    # (:parent_id,:creation_user,:creation_ip);
    # perform
    # acs_object__update_last_modified(:item_id,:creation_user,:creation_ip);
    # return null;
    # end;
    # Could be refactored with the recursive query below, which will
    # not go over the context hierarchy, but over the "filesystem"
    # hierarchy, which makes more sense, and update modification
    # metadata for the whole tree.
    # However, I wonder if there is really need for all of this... If
    # there is, one should probably have this logic at the content
    # repository level, rather than here.
    db_dml update_last_modified {
        with recursive fs_hierarchy as (
            select object_id, parent_id
              from fs_objects
             where object_id = :item_id

            union

            select p.object_id, p.parent_id
              from fs_objects p,
                   fs_hierarchy c
             where p.object_id = c.parent_id
        )
        update acs_objects set
          modifying_user = :creation_user,
          modifying_ip   = :creation_ip,
          last_modified  = current_timestamp
        where object_id in (select object_id from fs_hierarchy)
    }

    if {[string is false $suppress_notify_p]} {
        fs::do_notifications \
            -folder_id $parent_id \
            -filename $title \
            -item_id $revision_id \
            -action "new_version" \
            -package_id $package_id
    }

    #It's safe to rebuild RSS repeatedly, assuming it's not too expensive.
    set folder_info [fs::get_folder_package_and_root $parent_id]
    set db_package_id [lindex $folder_info 0]
    if { [parameter::get -package_id $db_package_id -parameter ExposeRssP -default 0] } {
        fs::rss::build_feeds $parent_id
    }

    if {!$no_callback_p} {
        callback fs::file_revision_new \
            -package_id [ad_conn package_id] \
            -file_id $item_id \
            -parent_id $parent_id
    }

    return $revision_id
}

# modified 2006/08/11 (nfl) delete all symlinks
d_proc fs::delete_file {
    -item_id:required
    {-parent_id ""}
    -no_callback:boolean
} {
    Deletes a file and all its revisions.

    Note that we do not perform filesystem operations here. A trigger
    on cr_revisions informs the content repository about the deletion
    and periodic cleanup of files to be deleted is performed in a
    scheduled procedure.
} {

    set version_name [get_object_name -object_id $item_id]

    if {$parent_id eq ""} {
        set parent_id [fs::get_parent -item_id $item_id]
    }

    set folder_info [fs::get_folder_package_and_root $parent_id]
    set package_id [lindex $folder_info 0]

    # check if there were symlinks, if yes, delete them
    set all_symlinks [db_list get_all_symlinks {}]
    foreach symlink_id $all_symlinks {
        fs::delete_file -item_id $symlink_id
    }

    if {!$no_callback_p} {
        callback fs::file_delete \
            -package_id [ad_conn package_id] \
            -file_id $item_id
    }

    fs::do_notifications \
        -folder_id $parent_id \
        -filename $version_name \
        -item_id $item_id \
        -action "delete_file"

    db_exec_plsql delete_file {}
}

d_proc fs::delete_folder {
    -folder_id:required
    {-cascade_p "t"}
    {-parent_id ""}
    -no_callback:boolean
    -no_notifications:boolean
} {
    Deletes a folder and all contents.

    Note that we do not perform filesystem operations here. A trigger
    on cr_revisions informs the content repository about the deletion
    and periodic cleanup of files to be deleted is performed in a
    scheduled procedure.
} {
    if {!$no_callback_p} {
        callback fs::folder_delete \
            -package_id [ad_conn package_id] \
            -folder_id $folder_id
    }

    if {$parent_id eq ""} {
        set parent_id [fs::get_parent -item_id $folder_id]
    }

    set version_name [get_object_name -object_id $folder_id]

    if { !$no_notifications_p } {
        fs::do_notifications \
            -folder_id $parent_id \
            -filename $version_name \
            -item_id $folder_id \
            -action "delete_folder"
    }

    db_exec_plsql delete_folder {}
}

d_proc fs::delete_version {
    -item_id:required
    -version_id:required
} {
    Deletes a revision. If it was the last revision, it deletes
    the file as well.

    Note that we do not perform filesystem operations here. A trigger
    on cr_revisions informs the content repository about the deletion
    and periodic cleanup of files to be deleted is performed in a
    scheduled procedure.
} {
    set parent_id [db_exec_plsql delete_version {}]

    if {$parent_id > 0} {
        delete_file -item_id $item_id -parent_id $parent_id
    }
    return $parent_id
}

ad_proc -private fs::webdav_p {} {
    Returns if webDAV is enabled.

    @return boolean
} {
    return [expr {
                  [parameter::get -parameter "UseWebDavP" -default 0] &&
                  [apm_package_installed_p oacs-dav]
              }]
}

d_proc fs::webdav_url {
    -item_id:required
    {-root_folder_id ""}
    {-package_id ""}
} {
    Provide URL for webdav access to file or folder

    @param item_id folder_id or item_id of file-storage folder or file
    @param root_folder_id root folder to resolve URL from

    @return fully qualified URL for WebDAV access or empty string if
            item is not WebDAV enabled
} {

    if {![fs::webdav_p]} {
        return ""
    }
    if {$package_id eq ""} {
        set package_id [ad_conn package_id]
    }

    if {$root_folder_id eq ""} {
        set root_folder_id [fs::get_root_folder -package_id $package_id]
    }

    if {"t" eq [oacs_dav::folder_enabled -folder_id $root_folder_id]} {
        if {$root_folder_id eq $item_id} {
            set url_stub ""
        } else {
            set url_stub [content::item::get_virtual_path -root_folder_id $root_folder_id -item_id $item_id]
        }
        set package_url [apm_package_url_from_id $package_id]

        set webdav_prefix [oacs_dav::uri_prefix]

        if { [security::RestrictLoginToSSLP] } {
            return "[security::get_secure_location]${webdav_prefix}${package_url}${url_stub}"
        } else {
            return "[ad_url]${webdav_prefix}${package_url}${url_stub}"
        }

    } else {

        return ""

    }
}

d_proc -public fs::do_notifications {
    {-folder_id:required}
    {-filename:required}
    {-item_id:required}
    {-package_id ""}
    {-action:required}
} {
    Send notifications for file-storage operations.

    Note that not all possible operations are implemented, e.g. move, copy etc. See documentation.

    @param action The kind of operation. One of: new_file, new_version,
                  new_url, delete_file, delete_url, delete_folder
} {
    set package_and_root [fs::get_folder_package_and_root $folder_id]
    set root_folder [lindex $package_and_root 1]
    if {$package_id eq ""} {
        set package_id [lindex $package_and_root 0]
    }

    switch $action {
        "new_file" {
            set action_type "[_ file-storage.New_File_Uploaded]"
        }
        "new_url" {
            set action_type "[_ file-storage.New_URL_Uploaded]"
        }
        "new_version" {
            set action_type "[_ file-storage.lt_New_version_of_file_u]"
        }
        "delete_file" {
            set action_type "[_ file-storage.File_deleted]"
        }
        "delete_url" {
            set action_type "[_ file-storage.URL_deleted]"
        }
        "delete_folder" {
            set action_type "[_ file-storage.Folder_deleted]"
        }
        default {
            error "Unknown file-storage notification action: $action"
        }
    }

    set url "[ad_url]"
    set new_content ""
    set creation_user [acs_object::get_element \
                           -object_id $item_id \
                           -element creation_user]
    set owner [person::name -person_id $creation_user]

    if {$action in {"new_file" "new_url" "new_version"}} {

        if {$action eq "new_version"} {
            set sql "select description as description from cr_revisions
                           where cr_revisions.revision_id = :item_id"
        } else {
            set sql "select description as description from cr_revisions
                           where cr_revisions.item_id = :item_id"
        }

        db_0or1row description $sql

    }
    set root_folder_package_id [db_string get_package_id {
        select package_id from fs_root_folders
        where folder_id = :root_folder
    }]
    set path1 [site_node::get_url_from_object_id \
                   -object_id $root_folder_package_id]

    # Set email message body - "text only" for now
    set text_version ""
    append text_version "[_ file-storage.lt_Notification_for_File]\n"
    set folder_name [fs_get_folder_name $folder_id]
    append text_version "[_ file-storage.lt_File-Storage_folder_f]\n"

    if {$action eq "new_version"} {
        append text_version "[_ file-storage.lt_New_Version_Uploaded_]\n"
    } else {
        append text_version "[_ file-storage.lt_Name_of_the_action_ty]\n"
    }
    if {[info exists owner]} {
        append text_version "[_ file-storage.Uploaded_by_ownern]\n"
    }
    if {[info exists description]} {
        append text_version "[_ file-storage.lt_Version_Notes_descrip]\n"
    }

    set url_version "$url$path1?folder_id=$folder_id"
    append text_version "[_ file-storage.lt_View_folder_contents_]\n"

    set html_version [ad_html_text_convert -from text/plain -to text/html -- $text_version]
    append html_version "<br><br>"
    # Do the notification for the file-storage

    notification::new \
        -type_id [notification::type::get_type_id \
                      -short_name fs_fs_notif] \
        -object_id $folder_id \
        -notif_subject "[_ file-storage.lt_File_Storage_Notifica]" \
        -notif_text $text_version \
        -notif_html $html_version

    # walk through all folders up to the root folder
    while {$folder_id != $root_folder} {
        set parent_id [db_string parent_id {
            select parent_id from cr_items where item_id = :folder_id
        }]
        notification::new \
            -type_id [notification::type::get_type_id \
                          -short_name fs_fs_notif] \
            -object_id $parent_id \
            -notif_subject "[_ file-storage.lt_File_Storage_Notifica]" \
            -notif_text $new_content \
            -notif_html $html_version
        set folder_id $parent_id
    }
}

d_proc -deprecated fs::item_editable_info {
    -item_id:required
} {
    Returns an array containing elements editable_p, mime_type, file_extension
    if an fs item is editable through the browser, editable_p is set to 1

    DEPRECATED: it is unclear what editable is supposed to mean. As of
    2023-03-16 file-storage does not offer inline editing and no
    package, including file-storage itself, appears to be using this
    api.

    @see nothing

    @author Deds Castillo (deds@i-manila.com.ph)
    @creation-date 2004-07-03

    @param item_id

    @return

    @error
} {
    # ideally, this would get values from parameters
    # hardcoding it for now
    set editable_mime_types [list "text/html" "text/plain"]

    content::item::get -item_id $item_id -array_name item_info
    set mime_info(mime_type) [set mime_type $item_info(mime_type)]
    set mime_info(file_extension) [db_string get_extension {
        select file_extension from cr_mime_types where mime_type = :mime_type
    }]

    if {[string tolower $mime_info(mime_type)] in $editable_mime_types} {
        set mime_info(editable_p) 1
    } else {
        set mime_info(editable_p) 0
    }
    return [array get mime_info]
}

d_proc -deprecated fs::item_editable_p {
    -item_id:required
} {
    returns 1 if item is editable via browser

    DEPRECATED: it is unclear what editable is supposed to mean. As of
    2023-03-16 file-storage does not offer inline editing and no
    package, including file-storage itself, appears to be using this
    api.

    @see nothing

    @author Deds Castillo (deds@i-manila.com.ph)
    @creation-date 2004-07-03

    @param item_id

    @return

    @error
} {
    array set item_editable_info [fs::item_editable_info -item_id $item_id]

    return $item_editable_info(editable_p)
}

d_proc -public fs::get_object_info {
    -file_id:required
    -revision_id
} {
    Returns an array containing the fs object info.

    @author Deds Castillo (deds@i-manila.com.ph)
    @creation-date 2004-07-03

    @param file_id Id of the file
    @param revision_id Id of the revision

    @return

    @error
} {
    if {![info exists revision_id] || $revision_id eq ""} {
        set revision_id [content::item::get_live_revision -item_id $file_id]
    }

    db_1row file_info {
        select r.item_id as file_id, r.revision_id,
               r.mime_type, r.title, r.description,
               r.content_length as content_size,
               i.name, o.last_modified, i.parent_id,
               i.storage_type, i.storage_area_key
        from cr_revisions r, cr_items i, acs_objects o
        where r.revision_id = :revision_id
        and r.item_id = i.item_id
        and i.item_id = :file_id
        and i.content_type = 'file_storage_object'
        and r.revision_id = o.object_id
    } -column_array file_object_info

    # GN: this query was probably never defined in CVS
    #set content [db_exec_plsql get_content {}]

    if {$file_object_info(storage_type) eq "file"} {
        set file_object_info(cr_file_path) [content::revision::get_cr_file_path \
                                                -revision_id $revision_id]
    }

    return [array get file_object_info]
}

ad_proc -public fs::get_folder_package_and_root folder_id {

    Returns a two-element Tcl list containing the package_id
    and root_folder_id for the passed-in folder_id.

    @author Andrew Grumet (aegrumet@alum.mit.edu)
    @creation-date 15 March 2004

} {

    db_1row select_package_and_root {}

    return [list $package_id $root_folder_id]
}

d_proc -public fs::get_file_package_id {
    -file_id:required
} {
    Returns the package_id for a passed-in file_id. This is useful when
    using symlinks to files whose real root_folder_id is not the root_folder_id
    of the package the user is in.

    @author Stan Kaufman (skaufman@epimetrics.com)
    @creation-date 2005-09-07

    @param file_id

    @return package_id

} {
    return [db_string select_package_id {
        with recursive hierarchy as
        (
         select package_id, context_id
         from acs_objects
         where object_id = :file_id

         union

         select o.package_id, o.context_id
         from acs_objects o, hierarchy h
         where object_id = h.context_id
           and h.package_id is null
         )
        select package_id from hierarchy
        where package_id is not null
    } -default ""]
}

namespace eval fs::notification {}

d_proc -private fs::notification::get_url {
    object_id:required
} {
    This proc implements the GetURL operation of the NotificationType
    Service Contract and should not be invoked directly.

    @return a full URL to the object_id. Handles folders.

    @param object_id

    @author Stan Kaufman (skaufman@epimetrics.com)
    @creation-date 2005-02-28
} {
    set folder_id $object_id
    set package_id [lindex [fs::get_folder_package_and_root $folder_id] 0]
    set fs_package_url [lindex [site_node::get_url_from_object_id -object_id $package_id] 0]
    return "[ad_url]${fs_package_url}index?folder_id=$folder_id"
}

d_proc -public fs::file_copy {
    {-file_id:required}
    {-target_folder_id:required}
    {-postfix ""}
    -symlink:boolean
} {
    copy file to target folder

    @param file_id Item_id of the file to be copied
    @param target_folder_id Folder ID of the folder to which the file is copied to
    @param postfix Postfix will be added with "_" to the new filename (not title).
           Very useful if you want to avoid unique name constraints on cr_items.
    @param symlink Defines if, instead of a full item, we should just add a symlink.
} {
    db_1row file_data {}

    if {$postfix ne ""} {
        set name [lang::util::localize "[ad_file rootname $name]_$postfix[ad_file extension $name]"]
    }

    if {$symlink_p} {
        return [content::symlink::new \
                    -name $name \
                    -label $title \
                    -target_id $file_id \
                    -parent_id $target_folder_id]
    } else {
        set user_id [ad_conn user_id]
        set creation_ip [ad_conn peeraddr]
        #set file_path "[cr_fs_path][cr_create_content_file_path $file_id $file_rev_id]"
        set file_path [content::revision::get_cr_file_path -revision_id $file_rev_id]

        # We need to check if the file already exists with the same name in the target folder
        # If yes, just add a new revision.

        set new_file_id [content::item::get_id_by_name -name $name -parent_id $target_folder_id]
        if {$new_file_id eq ""} {
            set new_file_id [content::item::new \
                                 -name $name \
                                 -parent_id $target_folder_id \
                                 -context_id $target_folder_id \
                                 -item_subtype "content_item" \
                                 -content_type "file_storage_object" \
                                 -storage_type "file"]
        }

        # Now create the revision
        set new_file_rev_id [content::revision::copy \
                                 -revision_id $file_rev_id \
                                 -target_item_id $new_file_id \
                                 -creation_user $user_id \
                                 -creation_ip $creation_ip]

        #set new_path [cr_create_content_file_path $new_file_id $new_file_rev_id]
        cr_create_content_file $new_file_id $new_file_rev_id $file_path

        if {$postfix ne ""} {
            # set postfixed new title
            db_dml update_title {}
        }

        content::item::set_live_revision -revision_id $new_file_rev_id

        return $new_file_id
    }
}

d_proc -public fs::file_copy {
    {-file_id:required}
    {-target_folder_id:required}
    {-postfix ""}
    -symlink:boolean
} {
    copy file to target folder

    @param file_id Item_id of the file to be copied
    @param target_folder_id Folder ID of the folder to which the file is copied to
    @param postfix Postfix will be added with "_" to the new filename (not title).
           Very useful if you want to avoid unique name constraints on cr_items.
    @param symlink Defines if, instead of a full item, we should just add a symlink.
} {
    db_1row file_data {}

    if {$postfix ne ""} {
        set name [lang::util::localize "[ad_file rootname $name]_$postfix[ad_file extension $name]"]
    }

    if {$symlink_p} {
        return [content::symlink::new \
                    -name $name \
                    -label $title \
                    -target_id $file_id \
                    -parent_id $target_folder_id]
    } else {
        set user_id [ad_conn user_id]
        set creation_ip [ad_conn peeraddr]
        #set file_path "[cr_fs_path][cr_create_content_file_path $file_id $file_rev_id]"
        set file_path [content::revision::get_cr_file_path -revision_id $file_rev_id]

        # We need to check if the file already exists with the same name in the target folder
        # If yes, just add a new revision.

        set new_file_id [content::item::get_id_by_name -name $name -parent_id $target_folder_id]
        if {$new_file_id eq ""} {
            set new_file_id [content::item::new \
                                 -name $name \
                                 -parent_id $target_folder_id \
                                 -context_id $target_folder_id \
                                 -item_subtype "content_item" \
                                 -content_type "file_storage_object" \
                                 -storage_type "file"]
        }

        # Now create the revision
        set new_file_rev_id [content::revision::copy \
                                 -revision_id $file_rev_id \
                                 -target_item_id $new_file_id \
                                 -creation_user $user_id \
                                 -creation_ip $creation_ip]

        #set new_path [cr_create_content_file_path $new_file_id $new_file_rev_id]
        cr_create_content_file $new_file_id $new_file_rev_id $file_path

        if {$postfix ne ""} {
            # set postfixed new title
            db_dml update_title {}
        }

        content::item::set_live_revision -revision_id $new_file_rev_id

        return $new_file_id
    }
}

d_proc -private fs::category_links {
    {-object_id:required}
    {-folder_id:required}
    {-selected_category_id ""}
    {-fs_url ""}
    {-joinwith ", "}
} {
    @param object_id the file storage object_id whose category list we creating
    @param folder_id the folder the category link should shearch on
    @param selected_category_id the category that has been selected and for
           which a link to return to the folder without that category limitation should exist
    @param fs_url is the file storage url for which these links will be created -
           defaults to the current package_url
    @param joinwith allows you to join the link list with something other than the default ", "

    @return a list of category_links to filter the supplied folder for a given category
} {
    if { $fs_url eq "" } {
        set fs_url [ad_conn package_url]
    }
    set selected_found_p 0
    set categories [list]
    foreach category_id [category::get_mapped_categories $object_id] {
        set name [category::get_name $category_id]
        if { $category_id eq $selected_category_id } {
            set selected_found_p 1
            set href [export_vars -base $fs_url -url {folder_id}]
            lappend categories "[ns_quotehtml $name] <a href=\"[ns_quotehtml $href]\">(x)</a>"
        } else {
            set href [export_vars -base $fs_url -url {folder_id category_id}]
            lappend categories "<a href=\"[ns_quotehtml $href]\">[ns_quotehtml $name]</a>"
        }
    }
    if { [string is false $selected_found_p] && $selected_category_id ne "" } {
        # we need to show the link to remove this category file at the
        # top of the folder
        set href [export_vars -base $fs_url -url {folder_id}]
        set name [category::get_name $selected_category_id]
        lappend categories "[ns_quotehtml $name] <a href=\"[ns_quotehtml $href]\">(x)</a>"
    }
    return [join $categories $joinwith]
}

ad_proc -private fs::unit_conv {value} {

    Convert units to value. This should done more generic, ... we have
    in NaviServer c-level support for this which should be used if
    available in the future.

} {
    if {[regexp {^([0-9.]+)\s*(MB|KB)} $value . number unit]} {
        set value [expr {int($number * ($unit eq "KB" ? 1024 : 1024*1024))}]
    }
    return $value
}

d_proc -public fs::max_upload_size {
    {-package_id ""}
} {
    @param package_id id of the file-storage package instance. Will
           default to the connection package_id if not specified.

    Returns the maximum upload size for this file-storage instance. If
    the value from the parameter is empty, invalid, or bigger than
    the server-wide upload limit, the latter will take over.

    @return numeric value in bytes
} {
    set max_bytes_param [fs::unit_conv [parameter::get \
                                            -package_id $package_id \
                                            -parameter "MaximumFileSize"]]
    if {![string is double -strict $max_bytes_param]} {
        set max_bytes_param Inf
    }

    set driver [expr {[ns_conn isconnected] ?
                      [ns_conn driver] :
                      [lindex [ns_driver names] 0]}]
    set section [ns_driversection -driver $driver]
    set max_bytes_conf [fs::unit_conv  [ns_config $section maxinput]]
    return [expr {min($max_bytes_param,$max_bytes_conf)}]
}

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