Index: openacs-4/packages/new-portal/tcl/portal-procs.tcl =================================================================== RCS file: /usr/local/cvsroot/openacs-4/packages/new-portal/tcl/portal-procs.tcl,v diff -u -r1.49 -r1.50 --- openacs-4/packages/new-portal/tcl/portal-procs.tcl 7 Nov 2001 15:29:54 -0000 1.49 +++ openacs-4/packages/new-portal/tcl/portal-procs.tcl 7 Nov 2001 21:50:12 -0000 1.50 @@ -1,7 +1,7 @@ # tcl/portal-procs.tcl ad_library { - Portal. + Portal. @author Arjun Sanyal (arjun@openforce.net) @creation-date Sept 2001 @@ -10,637 +10,375 @@ namespace eval portal { -# helper procs for datasources -ad_proc -public get_datasource_name { ds_id } { - Get the ds name from the id or the null string if not found. + # + # Special Hacks + # - @param ds_id - @return ds_name - @author Arjun Sanyal (arjun@openforce.net) - @creation-date Spetember 2001 -} { - if {[db_0or1row get_ds_name_from_id "select name from portal_datasources where datasource_id = :ds_id"]} { - return $name - } else { - return "" - } -} - -ad_proc -public get_datasource_id { ds_name } { - Get the ds is from the name or the null string if not found. - - @param ds_name - @return ds_id - @creation-date Spetember 2001 -} { - if {[db_0or1row get_ds_id_from_name "select datasource_id from portal_datasources where name = :ds_name"]} { - return $datasource_id - } else { - return "" - } -} - -ad_proc -public make_datasource_available {portal_id ds_id} { - Make the datasource available to the given portal. - - @param portal_id - @param ds_id - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 10/30/2001 -} { -# ad_require_permission $portal_id portal_admin_portal - - set new_p_ds_id [db_nextval acs_object_id_seq] - - db_dml insert_into_portal_datasource_avail_map " - insert into portal_datasource_avail_map - (portal_datasource_id, portal_id, datasource_id) - values - (:new_p_ds_id, :portal_id, :ds_id)" -} - -ad_proc -public make_datasource_unavailable {portal_id ds_id} { - Make the datasource unavailable to the given portal. - - @param portal_id - @param ds_id - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 10/30/2001 -} { - -# ad_require_permission $portal_id portal_admin_portal - - db_dml delete_from_portal_datasource_avail_map " - delete from portal_datasource_avail_map - where portal_id = :portal_id - and datasource_id = :ds_id" -} - -ad_proc -public toggle_datasource_availability {portal_id ds_id} { - Toggle - - @param portal_id - @param ds_id - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 10/30/2001 -} { - ad_require_permission $portal_id portal_admin_portal + # Work around for template::util::url_to_file + ad_proc -private www_path {} { + Returns the path of the www dir of the portal package + } { return "/packages/new-portal/www" } - if { [db_0or1row datasource_avail_check "select 1 - from portal_datasource_avail_map - where portal_id = :portal_id and - datasource_id = :ds_id"] } { - [make_datasource_unavailable $portal_id $ds_id] - } else { - [make_datasource_available $portal_id $ds_id] - } -} + # + # Main portal procs + # + ad_proc -private create {user_id {layout_name "'Simple 2-Column'"}} { + Create a new portal for the passed in user id. + + @return The newly created portal's id + @param user_id + @param layout_name optional + } { - ad_proc -public create {user_id {layout_name "'Simple 2-Column'"}} { - Create a new portal for the passed in user id. - - @return The newly created portal's id - @param user_id - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - # The defualt layout is simple 2 column - db_1row select_layout \ - "select layout_id from - portal_layouts where - name = $layout_name " + # XXX todo permissions should be portal_create_portal + # The defualt layout is simple 2 column + db_1row create_select \ + "select layout_id from + portal_layouts where + name = $layout_name " - # insert the portal and grant user-level permission on it. - return [ db_exec_plsql insert_portal { - begin + # insert the portal and grant user-level permission on it. + return [ db_exec_plsql insert_portal { + begin - :1 := portal.new ( - layout_id => :layout_id - ); + :1 := portal.new ( + layout_id => :layout_id + ); - acs_permission.grant_permission ( - object_id => :1, - grantee_id => :user_id, - privilege => 'portal_read_portal' - ); - - acs_permission.grant_permission ( - object_id => :1, - grantee_id => :user_id, - privilege => 'portal_edit_portal' - ); - end; - }] -} - -ad_proc -public delete { portal_id } { - Destroy the portal - - @param portal_id - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - # remove permissions (this sucks - ben) - db_dml remove_permissions "delete from acs_permissions where object_id= :portal_id" - - return [ db_exec_plsql delete_portal { - begin - portal.delete (portal_id => :portal_id); - end; - }] -} - -ad_proc -public get_name { portal_id } { - Get the name of this portal - - @param portal_id - @return the name of the portal or null - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - # check permissions - ad_require_permission $portal_id portal_read_portal - - if {[portal::exists_p $portal_id]} { - db_1row get_name \ - "select name from portals where portal_id = :portal_id" - } else { - set name "" + acs_permission.grant_permission ( + object_id => :1, + grantee_id => :user_id, + privilege => 'portal_read_portal' + ); + + acs_permission.grant_permission ( + object_id => :1, + grantee_id => :user_id, + privilege => 'portal_edit_portal' + ); + end; + }] } - return $name -} - -ad_proc -public update_name { portal_id new_name } { - Update the name of this - portal - - @param portal_id - @param new_name - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - - # check permissions - ad_require_permission $portal_id portal_read_portal - ad_require_permission $portal_id portal_edit_portal - - db_dml update_name "update portals set name = :new_name where portal_id = :portal_id" - -} - - -ad_proc -public add_element { portal_id ds_name } { - Add an element to a portal given a datasource name. Used for procs - that have no knowledge of regions - - @return the id of the new element - @param portal_id - @param ds_name - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - # Add the new element to the region with the fewest - # number of elements. Go with the first region with 0 elements - # AKS: Maybe support portals chosing their region later - - set min_num 99999 - set min_region 0 - - foreach region [get_regions [get_layout_id $portal_id]] { - db_1row min_num_select \ - "select count(*) as count - from portal_element_map - where portal_id = :portal_id - and region = :region" - - if { $count == 0 } { - set min_region $region - break - } + ad_proc -private delete { portal_id } { + Destroy the portal + @param portal_id + } { + # XXX todo permissions should be portal_delete_portal + # XXX remove permissions (this sucks - ben) + db_dml delete_delete \ + "delete from acs_permissions where object_id= :portal_id" - if { $min_num > $count } { - set min_num $count - set min_region $region - } - + return [ db_exec_plsql delete_portal { + begin + portal.delete (portal_id => :portal_id); + end; + }] } - - if { $min_region == 0 } { - set min_region 1 + + ad_proc -private get_name { portal_id } { + Get the name of this portal + + @param portal_id + @return the name of the portal or null string + } { + ad_require_permission $portal_id portal_read_portal + + if {[portal::exists_p $portal_id]} { + db_1row get_name_select \ + "select name from portals where portal_id = :portal_id" + } else { + set name "" + } + return $name } - return [add_element_to_region $portal_id $ds_name $min_region] -} -ad_proc -public remove_element {element_id} { - Remove an element from a portal -} { - db_transaction { + ad_proc -public render { portal_id } { + Get a portal by id. If it's not found, say so. + + @return Fully rendered portal as an html string + @param portal_id + } { + + ad_require_permission $portal_id portal_read_portal - # Remove map, this PE's parameters will cascade - db_dml remove_map "delete from portal_element_map where element_id= :element_id" - } -} + set edit_p [ad_permission_p $portal_id portal_edit_portal] + set master_template [ad_parameter master_template] + set css_path [ad_parameter css_path] + + # get the portal and layout + db_0or1row render_portal_and_layout_select " + select p.portal_id, p.name, t.filename as layout_template + from portals p, portal_layouts t + where p.layout_id = t.layout_id + and p.portal_id = :portal_id" -column_array portal -ad_proc -public add_element_to_region { portal_id ds_name region } { - Add an element to a portal in a region, given a datasource name + # get the elements of the portal and put them in a list + db_foreach render_element_select " + select element_id, region, sort_key + from portal_element_map + where portal_id = :portal_id + and state != 'hidden' + order by region, sort_key" -column_array entry { + # put the element IDs into buckets by region... + lappend element_ids($entry(region)) $entry(element_id) + } if_no_rows { + set element_ids {} + } - @return the id of the new element - @param portal_id - @param ds_name - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { + set element_list [array get element_ids] - set ds_id [get_datasource_id $ds_name] - - # set up a unique prett_name for the PE - if { [db_0or1row pe_prety_name_unique_check "select 1 - from portal_element_map - where portal_id = :portal_id and - pretty_name = :ds_name"] } { - # XXX sophisticated regsub here - set pretty_name [append ds_name "+1"] - } else { - set pretty_name $ds_name + # set up the template, it includes the layout template, + # which includes the elements + if { [empty_string_p $element_list] } { + # The portal has no elements, show anyway (they can configure) + set template " + @portal.name@" + } else { + set element_src "[www_path]/render-element" + set template " + @portal.name@ + " + } + + # Necessary hack to work around the acs-templating system + set __adp_stub "[get_server_root][www_path]/." + set {master_template} \"master\" + + # Compile and evaluate the template + set code [template::adp_compile -string $template] + set output [template::adp_eval code] + + return $output } - # Bind the DS to the PE by inserting into the map and - # copying the default params. - set new_element_id [db_nextval acs_object_id_seq] + ad_proc -private layout_elements { + element_list + {var_stub "element_ids"} + } { + Split a list up into a bunch of variables for inserting into a + layout template. This seems pretty kludgy (probably because it is), + but a template::multirow isn't really well suited to data of this + shape. It'll setup a set of variables, $var_stub_1 - $var_stub_8 + and $var_stub_i1- $var_stub_i8, each contining the portal_ids that + belong in that region. - db_dml insert_pe_into_map " - insert into portal_element_map - (element_id, - name, - pretty_name, - portal_id, - datasource_id, - theme_id, - region, - sort_key) - values - (:new_element_id, - :ds_name, - :pretty_name, - :portal_id, - :ds_id, - nvl((select max(theme_id) from portal_element_themes), 1), - :region, - nvl((select max(sort_key) + 1 from portal_element_map where region = :region), 1))" + AKS: XXX improve me - db_dml insert_into_params " - insert into portal_element_parameters - (parameter_id, element_id, config_required_p, configured_p, key, value) - select acs_object_id_seq.nextval, - :new_element_id, - config_required_p, - configured_p, - key, - value - from portal_datasource_def_params where datasource_id= :ds_id" - # The caller must now set the necessary params or else! - return $new_element_id -} - -ad_proc -public swap_element {portal_id element_id sort_key region direction} { - Moves a PE in the direction indicated by swapping it with its neighbor - - @param portal_id - @param element_id - @param sort_key of the element to be moved - @param region - @param direction either up or down - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - - if { $direction == "up" } { - # get the sort_key and id of the element above - db_1row move_element_get_prev_sort_key { - select sort_key as other_sort_key, - element_id as other_element_id - from (select sort_key, - element_id - from portal_element_map - where portal_id = :portal_id - and region = :region - and sort_key < :sort_key - order by sort_key desc - ) where rownum = 1 + @param element_id_list An [array get]'d array, keys are regions, \ + values are lists of element_ids. + @param var_stub A name upon which to graft the bits that will be \ + passed to the template. + } { + array set elements $element_list + + foreach idx [list 1 2 3 4 5 6 7 8 9 i1 i2 i3 i4 i5 i6 i7 i8 i9 ] { + upvar [join [list $var_stub "_" $idx] ""] group + if { [info exists elements($idx) ] } { + set group $elements($idx) + } else { + set group {} + } } - } elseif { $direction == "down"} { - # get the sort_key and id of the element below - db_1row move_element_get_next_sort_key { - select sort_key as other_sort_key, - element_id as other_element_id - from (select sort_key, - element_id - from portal_element_map - where portal_id = :portal_id - and region = :region - and sort_key > :sort_key - order by sort_key - ) where rownum = 1 - } - } else { - ad_return_complaint 1 \ - "portal::swap_element: Bad direction: $direction" - ad_script_abort } - # swap the sort keys - db_transaction { + # + # Portal configuration procs + # - # because of the uniqueness constraint on sort_keys we - # need to set a dummy key, then do the swap - - db_1row swap_get_dummy \ - "select acs_object_id_seq.nextval as dummy_sort_key from dual" - - # Set the element to be moved to the dummy key - db_dml swap_sort_keys_1 \ - "update portal_element_map set sort_key = :dummy_sort_key - where element_id = :element_id" + ad_proc -private update_name { portal_id new_name } { + Update the name of this portal - # Set the other_element's sort_key to the right value - db_dml swap_sort_keys_2 \ - "update portal_element_map set sort_key = :sort_key - where element_id = :other_element_id" - - # Set the element to be moved's sort_key to the right value - db_dml swap_sort_keys_3 \ - "update portal_element_map set sort_key = :other_sort_key - where element_id = :element_id" - } on_error { - ad_return_complaint 1 "portal::move_element: transaction failed" + @param portal_id + @param new_name + } { + + ad_require_permission $portal_id portal_read_portal + ad_require_permission $portal_id portal_edit_portal + + db_dml update_name_update \ + "update portals + set name = :new_name + where portal_id = :portal_id" } -} - - -ad_proc -public move_element {portal_id element_id region direction} { - Moves a PE in the direction indicated by swapping it with its neighbor - - @param portal_id - @param element_id - @param region the PEs current region - @param direction up or down - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - ad_require_permission $portal_id portal_read_portal - ad_require_permission $portal_id portal_edit_portal - - if { $direction == "right" } { - set target_region [expr $region + 1] - } elseif { $direction == "left" } { - set target_region [expr $region - 1] - } else { - ad_return_complaint 1 "portal::move_element Bad direction!" - } - # just move the element to the bottom of the region - db_dml move_element \ - "update portal_element_map - set region = :target_region, - sort_key = (select nvl((select max(sort_key) + 1 - from portal_element_map - where portal_id = :portal_id - and region = :target_region), - 1) - from dual) - where element_id = :element_id" -} - -ad_proc -public set_element_param { element_id key value } { - Set an element param - - @return 1 on success - @param element_id - @param key - @param value - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - - db_dml upadate_parms " - update portal_element_parameters set value = :value - where element_id = :element_id and - key = :key" - - return 1 - -} - -ad_proc -public get_element_param { element_id key } { - Get an element param. Returns the value of the param. - - @return the value of the param - @param element_id - @param key - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - - if { [db_0or1row get_parm " - select value - from portal_element_parameters - where element_id = :element_id and - key = :key"] } { - return $value - } else { - ad_return_complaint 1 "portal_get_element_param: Invalid element_id and/or key given." - ad_script_abort - } -} - - -ad_proc -public configure { portal_id } { - Return a portal configuration page. All form targets point to - file_stub-2. + ad_proc -public configure { portal_id } { + Return a portal configuration page. + All form targets point to file_stub-2. - @return A portal configuration page - - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - ad_require_permission $portal_id portal_read_portal - ad_require_permission $portal_id portal_edit_portal - - # Set up some template vars - set master_template [ad_parameter master_template] - - # Set up the form target - set target_stub [lindex [ns_conn urlv] [expr [ns_conn urlc] - 1]] - set action_string [append target_stub "-2"] - - # AKS XXX layout change - # get the layouts - set layout_count 0 - template::multirow create layouts layout_id name \ - description filename resource_dir checked - - db_foreach get_layouts " - select - layout_id, - name, - description, - filename, - resource_dir, - ' ' as checked - from portal_layouts - order by name " { - set resource_dir "$resource_dir" - template::multirow append layouts $layout_id $name \ - $description $filename $resource_dir $checked - incr layout_count - } - - # get the portal. - db_1row select_portal " - select - p.portal_id, - p.name, - t.filename as template, - t.layout_id - from portals p, portal_layouts t - where p.layout_id = t.layout_id and p.portal_id = :portal_id - " -column_array portal - - # AKS: I'm slowly fixing all these kludgy hacks. Your patience is - # requested. Thank you for choosing the new portal package. - - # fake some elements so that the in the template has - # something to do. - foreach region [ portal::get_regions $portal(layout_id) ] { - # pass the portal_id along here instead of the element_id. - lappend fake_element_ids($region) $portal_id - } - - set element_list [array get fake_element_ids] - set element_src "[portal::www_path]/place-element" - - # the sources /www/place-element.tcl - set template " - -
- Change Your Portal's Name: -

- - - -

- -

- - - Configure The Portal's Elements: - - - Undo Your Changes: -

- - <%= [export_form_vars portal_id ] %> - -" - -# -# -# -#

-# Change Layout: -#
-# -# -# -# -# -# -# -# -# -#
-# -# -# -# -#
-# checked> -# @layouts.name@
-# -# -#
-# -#
-# -# @layouts.description@ -# -#
-#
-#

-#
-# -#
-# -#
-#
- + @return A portal configuration page + } { + ad_require_permission $portal_id portal_read_portal + ad_require_permission $portal_id portal_edit_portal + # Set up some template vars, including the form target + set master_template [ad_parameter master_template] + set target_stub [lindex [ns_conn urlv] [expr [ns_conn urlc] - 1]] + set action_string [append target_stub "-2"] + + # XXX todo layout change + # get the layouts + # set layout_count 0 + # template::multirow create layouts layout_id name \ + # description filename resource_dir checked + # + # db_foreach configure_layout_select " + # select + # layout_id, + # name, + # description, + # filename, + # resource_dir, + # ' ' as checked + # from portal_layouts + # order by name " { + # set resource_dir "$resource_dir" + # template::multirow append layouts $layout_id $name \ + # $description $filename $resource_dir $checked + # incr layout_count + # } + # + # get the portal. + + db_1row configure_portal_select " + select + p.portal_id, + p.name, + t.filename as template, + t.layout_id + from portals p, portal_layouts t + where p.layout_id = t.layout_id and p.portal_id = :portal_id + " -column_array portal + + # XXX fixme + # fake some elements so that the in the template has + # something to do. +# foreach region [ portal::get_regions $portal(layout_id) ] { + # # pass the portal_id along here instead of the element_id. + #lappend fake_element_ids($region) $portal_id + #} + + set element_list [array get fake_element_ids] + set element_src "[portal::www_path]/place-element" + + # the sources /www/place-element.tcl + set template " + +
+ Change Your Portal's Name: +

+ + + +

+ +

+ + Configure The Portal's Elements: + + + Undo Your Changes: +

+ + <%= [export_form_vars portal_id ] %> + + " + + # + # + #

+ # Change Layout: + #
+ # + # + # + # + # + # + # + # + # + #
+ # + # + # + # + #
+ # checked> + # @layouts.name@
+ # + # + #
+ # + #
+ # + # @layouts.description@ + # + #
+ #
+ #

+ #
+ # + #
+ # + #
+ #
+ # This hack is to work around the acs-templating system set __adp_stub "[get_server_root][www_path]/." set {master_template} \"master\" - + set code [template::adp_compile -string $template] set output [template::adp_eval code] return $output -} - -ad_proc -public configure_dispatch { portal_id form } { - Dispatches the configuration operation. - We get the target region number from the op. + } - @param portal_id - @param formdata an ns_set with all the formdata - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - - ad_require_permission $portal_id portal_read_portal - ad_require_permission $portal_id portal_edit_portal - - set op [ns_set get $form op] - - switch $op { - "Rename" { - portal::update_name $portal_id [ns_set get $form new_name] - } - "swap" { - portal::swap_element $portal_id \ - [ns_set get $form element_id] \ - [ns_set get $form sort_key] \ - [ns_set get $form region] \ - [ns_set get $form direction] - } - "move" { - portal::move_element $portal_id \ - [ns_set get $form element_id] \ - [ns_set get $form region] \ - [ns_set get $form direction] - } - "Show Here" { - - set region [ns_set get $form region] - set element_id [ns_set get $form element_id] - - db_transaction { - # The new element's sk will be the last in the region - db_dml set_element_region_and_sk \ + ad_proc -public configure_dispatch { portal_id form } { + Dispatches the configuration operation. + We get the target region number from the op. + + @param portal_id + @param formdata an ns_set with all the formdata + } { + + ad_require_permission $portal_id portal_read_portal + ad_require_permission $portal_id portal_edit_portal + + set op [ns_set get $form op] + + switch $op { + "Rename" { + portal::update_name $portal_id [ns_set get $form new_name] + } + "swap" { + portal::swap_element $portal_id \ + [ns_set get $form element_id] \ + [ns_set get $form sort_key] \ + [ns_set get $form region] \ + [ns_set get $form direction] + } + "move" { + portal::move_element $portal_id \ + [ns_set get $form element_id] \ + [ns_set get $form region] \ + [ns_set get $form direction] + } + "Show Here" { + set region [ns_set get $form region] + set element_id [ns_set get $form element_id] + + db_transaction { + # The new element's sk will be the last in the region + db_dml configure_dispatch_show_update \ "update portal_element_map set region = :region, sort_key = (select nvl((select max(sort_key) + 1 @@ -651,486 +389,512 @@ from dual) where element_id = :element_id" - db_dml unhide_element \ - "update portal_element_map - set state = 'full' - where element_id = :element_id" - } - } - "hide" { - - set element_id_list [list] - - # iterate through the set, destructive! - while { [expr [ns_set find $form "element_id"] + 1 ] } { - lappend element_id_list [ns_set get $form "element_id"] - ns_set delkey $form "element_id" + db_dml configure_dispatch_unhide_update \ + "update portal_element_map + set state = 'full' + where element_id = :element_id" + } } - - if {! [empty_string_p $element_id_list] } { - foreach element_id $element_id_list { - db_dml hide_element \ + "hide" { + set element_id_list [list] + + # iterate through the set, destructive! + while { [expr [ns_set find $form "element_id"] + 1 ] } { + lappend element_id_list [ns_set get $form "element_id"] + ns_set delkey $form "element_id" + } + + if {! [empty_string_p $element_id_list] } { + foreach element_id $element_id_list { + db_dml configure_dispatch_hide_update \ "update portal_element_map set state = 'hidden' where element_id = :element_id" - } - } + } + } + } + "revert to default" { + ad_return_complaint 1 \ + "portal::config_dispatch: Not implimented op = $op" + } + "update_layout" { + ad_return_complaint 1 \ + "portal::config_dispatch: Not implimented op = $op" + } + default { + ns_log Error \ + "portal::config_dispatch: Bad op = $op!" + ad_return_complaint 1 \ + "portal::config_dispatch: Bad Op! \n op $op" + } } - "revert to default" { - ad_return_complaint 1 "portal::config_dispatch: Not implimented yet: op = $op" - } - "update_layout" { - ad_return_complaint 1 "portal::config_dispatch: Not implimented yet: op = $op" - } - default { - ns_log Warning \ - "portal::config_dispatch: op = $op, and that's not right!" - ad_return_complaint 1 "portal::config_dispatch: Bad Op! \n op $op" - } } -} -ad_proc -public render { portal_id } { - Get a portal by id. If it's not found, say so. - - @return Fully rendered portal as an html string - @param portal_id - - @author Arjun Sanyal (arjun@openforce.net) - @creation-date 9/28/2001 -} { - ad_require_permission $portal_id portal_read_portal - set edit_p [ad_permission_p $portal_id portal_edit_portal] - set master_template [ad_parameter master_template] - set css_path [ad_parameter css_path] - + # + # Element Procs + # - # put the element IDs into buckets by region... - foreach entry_list [get_elements $portal_id] { - array set entry $entry_list - lappend element_ids($entry(region)) $entry(element_id) - } - - set element_list [array get element_ids] + ad_proc -private add_element { portal_id ds_name } { + Add an element to a portal given a datasource name. Used for procs + that have no knowledge of regions + + @return the id of the new element + @param portal_id + @param ds_name + } { + # Balance the portal by adding the new element to the region + # with the fewest number of elements, the first region w/ 0 elts, + # or, if all else fails, the first region + set min_num 99999 + set min_region 0 + set layout_id [get_layout_id $portal_id] + + db_foreach add_element_get_regions " + select region + from portal_supported_regions + where layout_id = :layout_id" { + lappend region_list $region + } - if { [empty_string_p $element_list] } { + foreach region $region_list { + db_1row add_element_region_count \ + "select count(*) as count + from portal_element_map + where portal_id = :portal_id + and region = :region" + + if { $count == 0 } { + set min_region $region + break + } + + if { $min_num > $count } { + set min_num $count + set min_region $region + } + } + + if { $min_region == 0 } { set min_region 1 } + return [add_element_to_region $portal_id $ds_name $min_region] + } - # The portal has no elements, show anyway (they can configure) - set template " - @portal.name@" - } else { - set element_src "[www_path]/render-element" - set template " - @portal.name@ - " + ad_proc -private remove_element {element_id} { + Remove an element from a portal + } { + db_transaction { + # Remove map, this PE's parameters will cascade + db_dml remove_element_delete \ + "delete from portal_element_map + where element_id= :element_id" + } } + + ad_proc -private add_element_to_region { portal_id ds_name region } { + Add an element to a portal in a region, given a datasource name - db_0or1row select_portal_and_layout " - select - p.portal_id, - p.name, - t.filename as layout_template, - 't' as portal_read_p, - 't' as layout_read_p - from portals p, portal_layouts t - where p.layout_id = t.layout_id - and p.portal_id = :portal_id" -column_array portal + @return the id of the new element + @param portal_id + @param ds_name + } { - if { ! [exists_p $portal_id] } { - ad_return_complaint 1 "That portal (portal_id $portal_id) doesn't exist. This is a bug!." - ad_script_abort - } + set ds_id [get_datasource_id $ds_name] + + # XXX - set up a unique prett_name for the PE + if { [db_0or1row add_element_to_region_select "select 1 + from portal_element_map + where portal_id = :portal_id and + pretty_name = :ds_name"] } { + set pretty_name [append ds_name "+1"] + } else { + set pretty_name $ds_name + } + + # Bind the DS to the PE by inserting into the map + # and copying over the default params. + set new_element_id [db_nextval acs_object_id_seq] - # This hack is to work around the acs-templating system - set __adp_stub "[get_server_root][www_path]/." - set {master_template} \"master\" + db_dml add_element_to_region_map_insert " + insert into portal_element_map + (element_id, + name, + pretty_name, + portal_id, + datasource_id, + theme_id, + region, + sort_key) + values + (:new_element_id, + :ds_name, + :pretty_name, + :portal_id, + :ds_id, + nvl((select max(theme_id) from portal_element_themes), 1), + :region, + nvl((select max(sort_key) + 1 + from portal_element_map + where region = :region), 1))" + + db_dml add_element_to_region_param_insert " + insert into portal_element_parameters + (parameter_id, element_id, config_required_p, configured_p, key, value) + select acs_object_id_seq.nextval, + :new_element_id, + config_required_p, + configured_p, + key, + value + from portal_datasource_def_params where datasource_id= :ds_id" - set code [template::adp_compile -string $template] - set output [template::adp_eval code] + # The caller must now set the necessary params or else! + return $new_element_id + } - return $output + ad_proc -private swap_element {portal_id element_id sort_key region dir} { + Moves a PE in the direction indicated by swapping it with its neighbor + + @param portal_id + @param element_id + @param sort_key of the element to be moved + @param region + @param dir either up or down + } { + + if { $dir == "up" } { + # get the sort_key and id of the element above + db_1row swap_element_get_prev_sort_key { + select sort_key as other_sort_key, + element_id as other_element_id + from (select sort_key, element_id + from portal_element_map + where portal_id = :portal_id + and region = :region + and sort_key < :sort_key + order by sort_key desc + ) where rownum = 1 + } + } elseif { $dir == "down"} { + # get the sort_key and id of the element below + db_1row swap_element_get_next_sort_key { + select sort_key as other_sort_key, + element_id as other_element_id + from (select sort_key, element_id + from portal_element_map + where portal_id = :portal_id + and region = :region + and sort_key > :sort_key + order by sort_key + ) where rownum = 1 + } + } else { + ad_return_complaint 1 \ + "portal::swap_element: Bad direction: $dir" + } -} + db_transaction { + # because of the uniqueness constraint on sort_keys we + # need to set a dummy key, then do the swap + db_1row swap_get_dummy { + select acs_object_id_seq.nextval as dummy_sort_key + from dual + } -ad_proc -public evaluate_element { element_id } { - Get an element. Combine the datasource, template, etc. Return a suitable - chunk of HTML. + # Set the element to be moved to the dummy key + db_dml swap_sort_keys_1 { + update portal_element_map set sort_key = :dummy_sort_key + where element_id = :element_id + } + + # Set the other_element's sort_key to the correct value + db_dml swap_sort_keys_2 { + update portal_element_map set sort_key = :sort_key + where element_id = :other_element_id + } - @return A string containing the fully-rendered content for $element_id. - @param element_id The object-id for the element that you'd like to retrieve. - @author Arjun Sanyal (arjun@openforce.net) - @creation-date October 2001 -} { - - # the caching in here needs to be completely redone. It totally sucks. - # aks - all catching removed - array set element [eval [list get_element_data $element_id]] - - if { ! [info exists element(element_id)] } { - # no permission, probably. Debug? - return + # Set the element to be moved's sort_key to the right value + db_dml swap_sort_keys_3 { + update portal_element_map set sort_key = :other_sort_key + where element_id = :element_id + } + } on_error { + ad_return_complaint 1 "portal::move_element: transaction failed" + } } - # get the datasource and configuration. - array set datasource [eval [list get_datasource $element(datasource_id)] ] - set element(config) [eval [list get_element_parameters $element(element_id) ]] - if { ! [info exists datasource(datasource_id)] } { - # permissions likely didn't match. Debug? - return - } + ad_proc -private move_element {portal_id element_id region direction} { + Moves a PE in the direction indicated by swapping it with its neighbor - # untaint the data-type before passing it through eval, just in case. - if { ! [regexp {^[\w\-]+$} $datasource(data_type)] } { - error "Bad data_type: $datasource(data_type)" - return + @param portal_id + @param element_id + @param region the PEs current region + @param direction up or down + } { + + ad_require_permission $portal_id portal_read_portal + ad_require_permission $portal_id portal_edit_portal + + if { $direction == "right" } { + set target_region [expr $region + 1] + } elseif { $direction == "left" } { + set target_region [expr $region - 1] + } else { + ad_return_complaint 1 "portal::move_element Bad direction!" + } + + # just move the element to the bottom of the region + db_dml move_element_update \ + "update portal_element_map + set region = :target_region, + sort_key = (select nvl((select max(sort_key) + 1 + from portal_element_map + where portal_id = :portal_id + and region = :target_region), + 1) + from dual) + where element_id = :element_id" } + + ad_proc -private set_element_param { element_id key value } { + Set an element param + + @return 1 on success + @param element_id + @param key + @param value + } { + + db_dml set_element_param_upadate " + update portal_element_parameters set value = :value + where element_id = :element_id and + key = :key" + + return 1 + + } + + ad_proc -private get_element_param { element_id key } { + Get an element param. Returns the value of the param. - - # evaulate the datasource. - # it might be good to (optionally) cache this, - # since it can be an expensive step. - set element(content) [ eval { - portal_render_datasource_$datasource(data_type) [array get datasource] $element(config) - } ] + @return the value of the param + @param element_id + @param key + } { - # this is sometimes used when interacting with templates in the - # filesystem. - set element(mime_type) $datasource(mime_type) - regsub -all {/} $element(mime_type) {+} element(mime_type_noslash) + if { [db_0or1row get_element_param_select " + select value + from portal_element_parameters + where element_id = :element_id and + key = :key"] } { + return $value + } else { + ad_return_complaint \ + 1 "get_element_param: Invalid element_id and/or key given." + ad_script_abort + } + } - return [array get element] - -} - -ad_proc -private get_element_data { element_id } { - Fetch element data. - - @param element_id The element's ID. - @return a list-ified array containing the information from portal_elements and portal_templates for $element_id. - @author Arjun Sanyal (arjun@openforce.net) - @creation-date Sept 2001 } { - - # XXX issue here with element config params - - if { ! [db_0or1row select_element_data { - select - pem.element_id, - pem.name, + ad_proc -private evaluate_element { element_id } { + Combine the datasource, template, etc. Return a chunk of HTML. + + @return A string containing the fully-rendered content for $element_id. + @param element_id + } { + + # get the element data and theme + db_1row evaluate_element_element_select " + select pem.element_id, + pem.name, pem.datasource_id, pem.theme_id, pet.description, pet.filename, - 't' as element_read_p, - 't' as template_read_p from portal_element_map pem, portal_element_themes pet where pem.theme_id = pet.theme_id - and pem.element_id = :element_id - } -column_array element_data ] - } { - return -code error "That element doesn't exist." - } - - if { ! [ regexp {^/} $element_data(filename)] } { - # AKS - hack - set element_data(filename) "/packages/new-portal/www/$element_data(filename)" - } - - if { $element_data(element_read_p) } { - if { $element_data(template_read_p) } { - return [array get element_data] - } else { - return -code error "Read permission on template $template_id required." + and pem.element_id = :element_id " -column_array element + + # apply the path hack + set element(filename) "[www_path]/$element(filename)" + + # get the element's params + db_foreach evaluate_element_params_select " + select key, value + from portal_element_parameters + where + element_id = :element_id" { + lappend config($key) $value + } if_no_rows { + array set config {} } - } else { - return -code error "Read permission on element $element_id required." - } -} - -ad_proc -private get_element_parameters { element_id } { - Fetch element parameters. - - @param element_id - @author - @creaton-date -} { - - db_foreach select_element_params " - select key, value - from portal_element_parameters - where - element_id = :element_id - order by key" { - lappend config($key) $value - } if_no_rows { - # this might happen if the passed config_id was null, - # which will happen occasionally. (though not too often, - #since this empty return value will be cached...) - array set config {} - } - - return [array get config] -} - -ad_proc -private get_datasource { datasource_id } { - Fetch datasource data. - - @param datasource_id The element's ID. - @author Ian Baker (ibaker@arsdigita.com) - @creaton-date December 2000 -} { - - if { ! [db_0or1row select_datasource_data { + # shove the listified config into the element array + set element(config) [array get config] + + # get the datasource + db_1row evaluate_element_datasource_select " select - datasource_id, - data_type, - mime_type, - name, - description, - content + datasource_id, + mime_type, + name, + description, + content from portal_datasources - where datasource_id = :datasource_id } -column_array datasource ] - } { - return -code error "That datasource doesn't exist." - } + where datasource_id = :element(datasource_id) + " -column_array datasource -# if { ! $datasource(datasource_read_p) } { -# return -code error "Inadequate permissions on datasource $datasource_id" -# } + # evaulate the datasource. + set element(content) [ eval { + render_datasource_$datasource(data_type) \ + [array get datasource] $element(config) + } ] + + + + if [catch {set output [ eval "$src(content) { $cf }" ] } errmsg ] { + ad_return_complaint 1 "portal::evaluate_element + Error processing datasource '$src(name)': $errmsg" + } - # There's no provision to flush these, but they should update so - # infrequently as to never need flushing (essentially, only when - # the package is upgraded). -# array set datasource [ list portal_data_type data_type $datasource(data_type) ] - - return [array get datasource] -} - -ad_proc -private data_type { type name } { - Get the details about a data or mime type. The idea here is that - the caller will cache the call to this proc with util_memoize. - - @param type Which type to fetch (mime_type or data_type) - @param id The id if the type. - @author Ian Baker (ibaker@arsdigita.com) - @creaton-date December 2000 +ad_proc -private render_datasource_tcl_proc { ds cf } { + Accepts params. } { - if {$type == "data_type"} { - db_1row select_data_type "select pretty_name as data_type_pretty, secure_p, sort_key as data_type_sort_key - from portal_data_types - where name = :name" -column_array type_details - } elseif {$type == "mime_type"} { - db_1row select_mime_type "select pretty_name as mime_type_pretty, sort_key as mime_type_sort_key - from portal_mime_types - where name = :name" -column_array type_details - } else { - error "Invalid type: $type" - return - } - return [ array get type_details ] -} + array set src $ds -ad_proc -private data_types { type } { - Get all the entries in a data_type table, sorted by sort_key. + if [catch {set output [ eval "$src(content) { $cf }" ] } errmsg ] } - @param type Which type to fetch (mime_type or data_type) - @return For data_type, a db_list_of_lists containing name, pretty_name, secure_p, sort_key. For mime_type, name, pretty_name, sort_key. - @author Ian Baker (ibaker@arsdigita.com) - @creaton-date December 2000 -} { - if {$type == "data_type"} { - return [ db_list_of_lists select_all_data_types "select name, pretty_name, secure_p, sort_key - from portal_data_types - order by sort_key" ] - } elseif {$type == "mime_type"} { - return [ db_list_of_lists select_all_mime_types "select name, pretty_name, sort_key - from portal_mime_types - order by sort_key" ] - } else { - error "Invalid type: $type" - } + # in case the data feed code didn't explicitly return. + return $output } -# put a proc here for retrieving stuff from the portal/element map (so -# it can me memoized by index.tcl) -ad_proc -private get_elements { portal_id } { - Get the portal_element_map entries for a portal. - @param portal_id The portal in question's ID. - @return A list of lists. Each sublist is suitable for passing through 'array set', yielding an array with the keys 'element_id', 'region', 'sort_key'. -} { + # this is sometimes used when interacting with templates in the + # filesystem. + set element(mime_type) $datasource(mime_type) + regsub -all {/} $element(mime_type) {+} element(mime_type_noslash) - db_foreach select_p_e_map " - select m.element_id, m.region, m.sort_key - from portal_element_map m - where m.portal_id = :portal_id - and m.state != 'hidden' - order by region, sort_key, element_id" -column_array entry { - lappend entries [array get entry] - } if_no_rows { - set entries {} - } - - return $entries -} + return [array get element] -ad_proc -private get_element_ids_by_ds {portal_id ds_name} { - Get element IDs for a particular portal and a datasource name -} { - set ds_id [get_datasource_id $ds_name] - - return [db_list select_element_ids "select element_id from portal_element_map - where portal_id= :portal_id and datasource_id= :ds_id"] } - ad_proc -private get_layout_id { portal_id } { Get the layout_id of a layout template for a portal. @param portal_id The portal_id. @return A layout_id. - @creation-date 9/28/2001 - @author Arjun Sanyal (arjun@openforce.net) } { - db_1row get_layout_id { + db_1row get_layout_id_select { select layout_id from portals where portal_id = :portal_id } return $layout_id } -ad_proc -private get_regions { layout_id } { - Set the current layout, returning the regions that it supports. - @param layout_id - @return a list containing the name of each region, in no particular order. - @creation-date 9/28/2001 - @author Arjun Sanyal (arjun@openforce.net) -} { - global portal_region_immutable_p - global portal_region_list - if { ! [info exists portal_region_list($layout_id) ] } { - db_foreach get_regions { - select - region, - decode(immutable_p, 't', 1, 'f', 0) as immutable_p - from portal_supported_regions - where layout_id = :layout_id - } { - set portal_region_immutable_p($region) $immutable_p - lappend portal_region_list $region - } - } - - return $portal_region_list -} - -ad_proc -private fake_regions { layout_id } { - Fake a display of regions using simple html tables. - - @param layout_id - @return a list containing the name of each region, in no particular order. - @creation-date 9/28/2001 - @author Arjun Sanyal (arjun@openforce.net) -} { - if { ! [info exists portal_region_list($layout_id) ] } { - db_foreach get_regions { - select - region, - decode(immutable_p, 't', 1, 'f', 0) as immutable_p - from portal_supported_regions - where layout_id = :layout_id - } { - set portal_region_immutable_p($region) $immutable_p - lappend portal_region_list $region - } - } - - return $portal_region_list -} - - -ad_proc -private region_immutable_p { region } { - Check to see if a region in the current layout template is immutable. - - @param region The region - @return 1 if the region is marked immutable, 0 otherwise. - @creation-date 2/13/2001 - @author Ian Baker (ibaker@arsdigita.com) -} { - global portal_region_immutable_p - - if { ! [info exists portal_region_immutable_p($region)] } { - # I'd like to just call it here, but the template datasource that calls - # this won't know what the current layout template is. - return -code error "Region $region doesn't exist, or portal_get_regions hasn't been called" - } - - return $portal_region_immutable_p($region) -} - - -ad_proc -public exists_p { portal_id } { +ad_proc -private exists_p { portal_id } { Check if a portal by that id exists. @return 1 on success, 0 on failure @param a portal_id - @author Arjun Sanyal (arjun@openforce.net) - @creation-date September 2001 } { - if { [db_0or1row select_portal_exists "select 1 from portals where portal_id = :portal_id"]} { + if { [db_0or1row exists_p_select "select 1 from portals where portal_id = :portal_id"]} { return 1 } else { return 0 } } -ad_proc -public layout_elements { - element_list - {var_stub "element_ids"} -} { - Split a list up into a bunch of variables for inserting into a layout - template. This seems pretty kludgy (probably because it is), but a - template::multirow isn't really well suited to data of this shape. - It'll setup a set of variables, $var_stub_1 - $var_stub_8 and $var_stub_i1 - - $var_stub_i8, each contining the portal_ids that belong in that region. - @creation-date 12/11/2000 - @param element_id_list An [array get]'d array, keys are regions, \ - values are lists of element_ids. - @param var_stub A name upon which to graft the bits that will be \ - passed to the template. -} { - array set elements $element_list - - foreach idx [list 1 2 3 4 5 6 7 8 9 i1 i2 i3 i4 i5 i6 i7 i8 i9 ] { - upvar [join [list $var_stub "_" $idx] ""] group - if { [info exists elements($idx) ] } { - set group $elements($idx) - } else { - set group {} - } - } - } -# Work around for template::util::url_to_file -ad_proc -public www_path {} { - Stuff -} { - return "/packages/new-portal/www" -} -ad_proc -public dummy {} { - There's really something wrong with ad_proc -} { - return 1 -} + + # + # Datasource helper procs + # + + ad_proc -private get_datasource_name { ds_id } { + Get the ds name from the id or the null string if not found. + + @param ds_id + @return ds_name + } { + if {[db_0or1row get_datasource_name_select \ + "select name from portal_datasources + where datasource_id = :ds_id"]} { + return $name + } else { + return "" + } + } + + ad_proc -private get_datasource_id { ds_name } { + Get the ds id from the name or the null string if not found. + + @param ds_name + @return ds_id + } { + if {[db_0or1row get_datasource_id_select \ + "select datasource_id from portal_datasources + where name = :ds_name"]} { + return $datasource_id + } else { + return "" + } + } + + ad_proc -private make_datasource_available {portal_id ds_id} { + Make the datasource available to the given portal. + + @param portal_id + @param ds_id + } { + # XXX todo permissions on availabliliy procs + # ad_require_permission $portal_id portal_admin_portal + set new_p_ds_id [db_nextval acs_object_id_seq] + db_dml make_datasource_available_insert " + insert into portal_datasource_avail_map + (portal_datasource_id, portal_id, datasource_id) + values + (:new_p_ds_id, :portal_id, :ds_id)" + } + + ad_proc -private make_datasource_unavailable {portal_id ds_id} { + Make the datasource unavailable to the given portal. + + @param portal_id + @param ds_id + } { + + # ad_require_permission $portal_id portal_admin_portal + db_dml make_datasource_unavailable_delete " + delete from portal_datasource_avail_map + where portal_id = :portal_id + and datasource_id = :ds_id" + } + + ad_proc -private toggle_datasource_availability {portal_id ds_id} { + Toggle + + @param portal_id + @param ds_id + } { + ad_require_permission $portal_id portal_admin_portal + + if { [db_0or1row toggle_datasource_availability_select "select 1 + from portal_datasource_avail_map + where portal_id = :portal_id and + datasource_id = :ds_id"] } { + [make_datasource_unavailable $portal_id $ds_id] + } else { + [make_datasource_available $portal_id $ds_id] + } + } + + }