--
-- packages/file-storage/sql/postgresql/file-storage-create.sql
--
-- @author Kevin Scaldeferri (kevin@arsdigita.com)
-- @creation-date 6 Nov 2000
-- @cvs-id $Id: file-storage-create.sql,v 1.3 2001/08/28 23:07:55 jowells Exp $
--

--
-- We need to create a root folder in the content repository for 
-- each instance of file storage
--


create table fs_root_folders (
    -- ID for this package instance
    package_id  integer
                constraint fs_root_folder_package_id_fk
                references apm_packages on delete cascade
                constraint fs_root_folder_package_id_pk
                primary key,
    -- the ID of the root folder
    -- JS: I removed the on delete cascade constraint on folder_id!!!!!!!!!!!!!!!!!!!!!
    -- JS:
    -- JS:  1) folder_id points to the root folder id in CR, which cannot (and should not) be deleted
    -- JS:     unless the package instance is deleted (which is done from APM).  Thus there is
    -- JS:     no conceivable scenario when the "on delete cascade" will be triggered to delete 
    -- JS:     the entry in fs_root_folders through the deletion of the root 
    -- JS:     folder id.  This constraint is superfluous.
    -- JS: 
    -- JS:  2) There is a nasty bug in the implementation of File Storage (FS), where if a package
    -- JS:     instance is deleted, the "on delete cascade" on package_id will cause the deletion of 
    -- JS:     the row containing package_id and folder_id when the instance is deleted.  Since the 
    -- JS:     CR items inserted by this FS instance is accessible only through the root folder_id, 
    -- JS:     the CR items are orphaned!  
    -- JS: 
    -- JS:     To clean up CR, we need to impose a trigger on deletion of the row containing the root
    -- JS:     folder_id (i.e., when the package instance is deleted). With "on delete cascade" 
    -- JS:     constaint imposed on folder_id, any trigger on the deletion of a package instance 
    -- JS:     to clean up CR creates a circular reference.  To see this, note cleaning up CR
    -- JS:     requires an inverted tree of the items belonging to the package be created, so 
    -- JS:     that we can clean up from the leaves of the tree up to the root node.
    -- JS:     We then call content_item.delete() or content_folder.delete() to remove the file or 
    -- JS:     folder, as the case may be. However, content_item.delete() (cotent_folder.delete 
    -- JS:     calls content_item.delete) calls on acs_object.delete, relying on the "on delete 
    -- JS:     cascade" constraint imposed on item_id of cr_items to do the actual deletion. Now 
    -- JS:     with "on delete cascade" also imposed on folder_id of fs_root_folders, the "on delete
    -- JS:     cascade" on item_id of cr_items will trigger deletion of the entry in fs_root_folders 
    -- JS:     AS IT IS BEING DELETED BY APM. In oracle, this will trigger the "mutating tables" error.
    -- JS:
    -- JS:     So we simplify our life and drop the "on delete cascade".  We still requie the foreign
    -- JS:     key constraint.  The subtle problem this will cause is that when we clean up CR, we 
    -- JS:     cannot prune the tree up to the root folder, since it will cause a foreign key 
    -- JS:     constraint violation.  We could also drop the foreign key constraint, but we will expose our
    -- JS:     database to carelessness errors.  The solution is to make two triggers: a "before delete"
    -- JS:     trigger on fs_root_folders  that cleans up the CR entries of the package except the
    -- JS:     root folder, and an "after delete" trigger that cleans up the root folder entry
    -- JS:     (since the folder id will have been deleted, so no foreign key reference will be 
    -- JS:     violated by the deletion of the root folder)
    folder_id   integer
                constraint fs_root_folder_folder_id_fk
                references cr_folders
                constraint fs_root_folder_folder_id_un
                unique
);



-- file_storage API
--
-- function get_root_folder (
--        package_id in apm_packages.package_id%TYPE
--    ) return fs_root_folders.folder_id%TYPE;
--
--
-- function new_root_folder (
--        package_id in apm_packages.package_id%TYPE
--    ) return fs_root_folders.folder_id%TYPE;


create function file_storage__get_root_folder (
       --
       -- Returns the root folder corresponding to a particular
       -- package instance.
       --
       integer        -- apm_packages.package_id%TYPE
)
returns integer as '  -- fs_root_folders.folder_id%TYPE 
declare
	get_root_folder__package_id  alias for $1;
        v_folder_id		     fs_root_folders.folder_id%TYPE;
        v_count			     integer;
begin

        select count(*) into v_count 
        from fs_root_folders
        where package_id = get_root_folder__package_id;

        if v_count > 0 then
            select folder_id into v_folder_id 
            from fs_root_folders
            where package_id = get_root_folder__package_id;
        else
            -- must be a new instance.  Gotta create a new root folder
            v_folder_id := file_storage__new_root_folder(get_root_folder__package_id);
        end if;

        return v_folder_id;

end;' language 'plpgsql';



create function file_storage__new_root_folder (
       --
       -- Creates a new root folder
       --
       -- 
       -- A hackish function to get around the fact that we can not run
       -- code automatically when a new package instance is created.
       --
       integer		-- apm_packages.package_id%TYPE
)
returns integer as '	--  fs_root_folders.folder_id%TYPE
declare
	new_root_folder__package_id  alias for $1;
        v_folder_id		    fs_root_folders.folder_id%TYPE;
        v_package_name		    apm_packages.instance_name%TYPE;
        v_package_key		    apm_packages.package_key%TYPE;
begin

        select instance_name, package_key 
        into v_package_name, v_package_key
        from apm_packages
        where package_id = new_root_folder__package_id;

        v_folder_id := content_folder__new (
            v_package_key || ''_'' || new_root_folder__package_id, -- name
            v_package_name || '' Root Folder'',    -- label
            ''Root folder for the file-storage system.  All other folders in file storage are subfolders of this one.'', -- description
	    null				  -- parent_id (default)
        );

        insert into fs_root_folders 
        (package_id, folder_id)
        values 
        (new_root_folder__package_id, v_folder_id);

        -- allow child items to be added
        PERFORM content_folder__register_content_type(
		v_folder_id,	        -- folder_id
		''content_revision'',   -- content_types
		''f''			-- include_subtypes (default)
		);
        PERFORM content_folder__register_content_type(
		v_folder_id,		-- folder_id
		''content_folder'',	-- content_types
		''f''			-- include_subtypes (default)
		);

        -- set up default permissions
        PERFORM acs_permission__grant_permission (
		v_folder_id,                          -- object_id
		acs__magic_object_id(''the_public''), -- grantee_id 
		''read''			      -- privilege
		);

        PERFORM acs_permission__grant_permission (
		v_folder_id,			            -- object_id 
		acs__magic_object_id(''registered_users''), -- grantee_id
		''write''				    -- privilege
		);

	return v_folder_id;

end;' language 'plpgsql';
    

-- JS: BEFORE DELETE TRIGGER to clean up CR entries (except root folder)
create function fs_package_items_delete_trig () returns opaque as '
declare

	v_rec	record;
begin

	
	for v_rec in
	
		-- We want to delete all cr_items entries, starting from the leaves all the way up
		-- the root folder (old.folder_id).
		select item_id,content_type
		from cr_items
		where  tree_sortkey like (select tree_sortkey || ''%''
						            from cr_items
						            where item_id = old.folder_id)
		and item_id != old.folder_id
                order by tree_sortkey desc
	loop


		-- We delete the item. On delete cascade should take care
		-- of deletion of revisions.
		if v_rec.content_type = ''content_revision''
		then
		    raise notice ''Deleting item_id = %'',v_rec.item_id;
		    PERFORM content_item__delete(v_rec.item_id);
		end if;

		-- Instead of doing an if-else, we make sure we are deleting a folder.
		if v_rec.content_type = ''content_folder''
		then
		    raise notice ''Deleting folder_id = %'',v_rec.item_id;
		    PERFORM content_folder__delete(v_rec.item_id);
		end if;

		-- We may have to delete other items here, e.g., symlinks (future feature)

	end loop;

	-- We need to return something for the trigger to be activated
	return old;

end;' language 'plpgsql';

create trigger fs_package_items_delete_trig before delete
on fs_root_folders for each row 
execute procedure fs_package_items_delete_trig ();


-- JS: AFTER DELETE TRIGGER to clean up last CR entry
create function fs_root_folder_delete_trig () returns opaque as '
begin
	PERFORM content_folder__delete(old.folder_id);
	return null;

end;' language 'plpgsql';

create trigger fs_root_folder_delete_trig after delete
on fs_root_folders for each row 
execute procedure fs_root_folder_delete_trig ();

