Search Box with Filter and Large Drop Down Menu
http://tympanus.net/codrops/2010/07/14/ui-elements-search-box/
http://www.web-delicious.com/jquery-plugins/
Search Box with Filter and Large Drop Down Menu
http://tympanus.net/codrops/2010/07/14/ui-elements-search-box/
http://www.web-delicious.com/jquery-plugins/
This entry moved to http://www.project-open.org/documentation/rfe_advanced_financial_project_planning
The package is available from the ]po[ CVS server ("experimental status"):
Package Name: intranet-gtd-dashboard CVSROOT: ":pserver:anonymous@cvs.openacs.org:/home/cvsroot"
It had been created in 2008/2009. The ]po[ RESTful interface was not yet available.
Table "public.im_gtd_tasks"
Column | Type | Modifiers
--------------------+--------------------------+-----------
gtd_task_id | integer | not null
ref_id | integer | not null
owner_id | integer |
created_date | timestamp with time zone |
description | text |
last_modified | date |
gtd_type_id | integer | not null
gtd_task_status_id | integer | not null
Indexes:
"im_gtd_tasks_pkey" PRIMARY KEY, btree (gtd_task_id)
Foreign-key constraints:
"im_gtd_task_type_fk" FOREIGN KEY (gtd_type_id) REFERENCES im_categories(category_id)
"im_gtd_task_type_status_fk" FOREIGN KEY (gtd_task_status_id) REFERENCES im_categories(category_id)
"im_gtd_tasks_owner_id_fkey" FOREIGN KEY (owner_id) REFERENCES users(user_id)
See also:
Check for existing variable
Find message in DB:
select * from lang_messages where message like '%Type%'
OpenACS string localization:
[lang::message::lookup "" intranet-core.Execution_Project "Create an 'Execution Project'"]
OpenACS string localization (short version):
[_ intranet-core.Name]
#intranet-core.Filter_Projects#Localize date to user-locale:
[lc_time_fmt '2009-12-15' %x]
Add message:
SELECT im_lang_add_message('en_US','intranet-audit','Earned_Value','Earned Value');
Localize numbers / beautifying them for output:
/intranet-invoices/www/view.tcl
set rounding_precision 2 set locale [lang::user::locale] set amount_pretty [lc_numeric [im_numeric_add_trailing_zeros [expr $amount+0] $rounding_precision] "" $locale]
set val_hours_output [format "%0.2f" $val_hours]
Localize labels etc.
[lang::message::lookup "" intranet-core.Execution_Project "Create an Execution Project"]
Localize categories
Pls. see below
Create new Category
SELECT im_category_new ('71', 'Potential', 'Intranet Project Status');
Create new Category Hierarchy
SELECT im_category_hierarchy_new (58, 56);
Return id of categories with name CATEGORY_NAME
[im_sub_categories CATEGORY_NAME]
Returns name of category
[im_category_from_id CATEGORY_ID]
Transitive closure – Returns categories including sub-categories
im_sub_categories [ -include_disabled_p include_disabled_p ] category_list
Note: category_list is a list of category id’s (integer)
Category drop-down in ad_form – Default
Currently return an empty entry for ‘All’
...
-form {
{absence_type_id:text(im_category_tree),optional {label "[_ intranet-timesheet2.Absence_Type]"} {value $absence_type_id} {custom {category_type "Intranet Absence Type" translate_p 1} } }
..
}Category drop-down in ad_form – Custom, using VIEW
set absences_types [im_memoize_list select_absences_types "select absence_type_id, absence_type from im_absence_types order by lower(ABSENCE_TYPE)"]
set absences_types [linsert $absences_types 0 "All"]
set absences_types [linsert $absences_types 0 -1]
set absence_type_list [list]
foreach { value text } $absences_types {
regsub -all " " $text "_" category_key
set text [lang::message::lookup "" intranet-core.$category_key $text]
lappend absence_type_list [list $text $value]
}
...
-form {
{absence_type_id:text(select),optional {label "[_ intranet-timesheet2.Absence_Type]"} {options $absence_type_list }} ..
}Plain SQL
SELECT
im_categories.category_id,
im_categories.category AS absence_type
FROM
im_categories
WHERE
im_categories.category_type::text = 'Intranet Absence Type'::text;
Is User logged in?:
set current_user_id [ad_maybe_redirect_for_registration]
Using existing permission procedures:
im_timesheet_task_permissions $current_user_id $project_id view read write admin
sets variables $read $write $admin to true/false
Checking privilege:
if {![im_permission $current_user_id "view_projects_all"]} { ad_return_complaint 1 "[_ intranet-core.lt_You_dont_have_suffici_2]" }
Search for privileges:
SELECT * FROM acs_privileges WHERE pretty_name LIKE '%assign%';
Create a new privilege:
select acs_privilege__create_privilege('add_tickets','Add Tickets','');
select acs_privilege__add_child('admin', 'add_tickets_for_customers');
select im_priv_create('add_tickets', 'P/O Admins');
select im_priv_create('add_tickets', 'Senior Managers');
select im_priv_create('add_tickets', 'Project Managers');
select im_priv_create('add_tickets', 'Employees');
select im_priv_create('add_tickets', 'Customers');
Check if user has privilege
[im_permission $user_id view_costs]
Remove priviliges
delete from acs_permissions where privilege = 'add_view_internal_rates'; delete from acs_privilege_hierarchy where child_privilege = 'add_view_internal_rates'; delete from acs_privileges where privilege = 'add_view_internal_rates';
Parameters
[parameter::get -package_id [apm_package_id_from_key intranet-mail-import] -parameter "ScanMails" -default 60]
Does PAckage exist
set im_survey_installed_p [llength [info proc [db_string get_view_id "select package_id from apm_packages where package_key = 'intranet-simple-survey'" -default 0]]]
OR
set survey_exists_p [llength [info commands im_package_survsimp_id]]
SELECT im_dynfield_attribute_new ( 'im_timesheet_task', 'effort_driven_type_id', 'Fixed Task Type', 'gantt_fixed_task_type', 'integer', 'f', 0, 'f', 'im_timesheet_tasks' );
PERFORM acs_permission__grant_permission(v_dynfield_id, (select group_id from groups where group_name=''Employees''), ''read'');
create or replace function inline_0 ()
returns integer as '
declare
v_dynfield_id integer;
begin
SELECT im_dynfield_attribute_new (
'im_timesheet_task', 'effort_driven_type_id', 'Fixed Task Type', 'gantt_fixed_task_type', 'integer', 'f', 0, 'f', 'im_timesheet_tasks'
);
PERFORM acs_permission__grant_permission(v_dynfield_id, (select group_id from groups where group_name=''Employees''), ''read'');
PERFORM acs_permission__grant_permission(v_dynfield_id, (select group_id from groups where group_name=''Employees''), ''write'');
return 0;
end;' language 'plpgsql';
select inline_0 ();
drop function inline_0 ();
Create View
insert into im_views (view_id, view_name, visible_for, view_type_id) values (80, 'office_list', 'view_offices', 1400);
Create View Columns
insert into im_view_columns (column_id, view_id, group_id, column_name, column_render_tcl, extra_select, extra_where, sort_order, visible_for) values (2221,22,NULL,'Quote', '$quote_date','','',18,'');
Is user member of a project?
[im_biz_object_member_p $user_id $project_id]
Is user employee?
[im_user_is_employee_p $user_id]
Is user employee?
[im_profile::member_p -profile_id [im_employee_group_id] -user_id $user_id]
Is user customer?
[im_profile::member_p -profile_id [im_customer_group_id] -user_id $user_id]
Is user freelancer?
[im_profile::member_p -profile_id [im_freelance_group_id] -user_id $user_id]
Is user customer?
[im_profile::member_p -profile_id [im_customer_group_id] -user_id $user_id]
Is user Project Manager?
[im_biz_object_admin_p $user_id $project_id]
Is user admin?
[im_is_user_site_wide_or_intranet_admin $user_id]
Current user
po34klaus=# select current_user; current_user -------------- po34klaus (1 row)
db_foreach
set column_sql " select * from im_view_columns where view_id=:view_id and group_id is null order by sort_order " db_foreach column_list_sql $column_sql { if {"" == $visible_for || [eval $visible_for]} { lappend column_headers "$column_name" lappend column_vars "$column_render_tcl" } .... }
db_1row
Use: Set variables for a query returning exactly one row.
Sample:
db_1row sender_get_info_1 "
select
pe.first_names as accounting_first_name,
pe.last_name as accounting_last_name
from
persons
where
person_id = $accounting_contact_id
"db_0or1row
Like db_1row but returns an error if more than one row is returned.
db_string
Use: Getting a single string value from the db
Sample:
set view_id [db_string get_view_id "select view_id from im_views where view_name=:view_name" -default 0]
db_dml – performs a dml statement
db_dml delete " delete from im_object_freelance_skill_map where object_id = :object_id and skill_type_id = :skill_type_id and skill_id in ([join $skill_id ","]) "
Create a sequence
create sequence im_inquiries_files_seq start 1;
Get id from sequence
set inquiry_files_id [db_string nextval "select nextval('im_inquiries_files_seq');"]
Check if table exists
<ul> <li>db_table_exists TABLE_NAME </li> <li>[im_table_exists im_freelance_rfqs]</li> </ul>
DB Transactions
db_transaction { db_dml test "nonsense" } on_error { ad_return_error "Error in blah/foo/bar" "The error was: $errmsg" }
String concatenation
Alternative to concat & append – in case var’s are ambigious:
set var_amount "amount.${user_id}_$project_type_id" </p> <strong>Check for empty string</strong> <ul> <li>empty_string_p [VAR]</li> </ul>
Sample:
if { ![empty_string_p $where_clause] } { set where_clause " and $where_clause" }
Compare strings in tcl
if { -1 != [string compare $list_order_by "min(p.price)"] } { ad_return_complaint 1 "here" set sorted_table_rows [qsort $table_rows [lambda {s} { lindex $s 2 }]] } else { set sorted_table_rows $table_rows }
Formating for amounts, etc.
set amount_pretty [im_numeric_add_trailing_zeros [expr $amount+0] 2]
Handling expr error
if {"" != $visible_tcl} { set visible 0 set errmsg "" if [catch { set visible [expr $visible_tcl] } errmsg] { ad_return_complaint 1 "$visible_tcl\n$errmsg" } if {!$visible} { continue } }
Handling db error
if {[catch { db_dml target_languages " select * from im_projects" } errmsg ]} { append errors "..." }
Optional parameters
ad_proc im_costs_base_component { user_id {company_id ""} {project_id ""} } { Returns a HTML table containing a list of costs for a particular company or project. } { ... }
Passing URL parameters
set primary_contact_link "<a href=primary-contact?[export_url_vars company_id limit_to_users_in_group_id]>Add primary contact</a>\n"
Arrays – Create form
<input type='checkbox' name='payed_p.$cost_id'>
Arrays – Handle form
ad_proc im_costs_base_component { ... } { payed_p:array,optional }
Add hidden vars to form
[export_form_vars company_id return_url]
Create list from form var array:
set payed_p_list [array names payed_p] set hours_list [array names hours] foreach hour_rec $hours_list { ns_log NOTICE "hour_rec $hour_rec / value: $hours($hour_rec)" }
if {![im_column_exists im_hours internal_note]} { ad_return_complaint 1 "Internal error in intranet-timesheet2:<br> The field im_hours.internal_note is missing.<br> Please notify your system administrator to upgrade your system to the latest version.<br> " ad_script_abort }
Moved to:
http://www.project-open.org/documentation/date_and_time_operations
/packages/intranet-reporting-finance/www/finance-expenses.tcl
select
...
round((c.paid_amount * im_exchange_rate(c.effective_date::date, c.currency, 'EUR')) :: numeric, 2) as paid_amount_converted,
from ...create or replace function inline_1 () returns integer as ' declare v_menu integer; v_parent_menu integer; v_employees integer; begin select group_id into v_employees from groups where group_name = ''Employees''; select menu_id into v_parent_menu from im_menus where label = ''projects_admin''; v_menu := im_menu__new ( null, -- p_menu_id ''im_menu'', -- object_type now(), -- creation_date null, -- creation_user null, -- creation_ip null, -- context_id ''intranet-cust-kw'', -- package_name ''project_closure_wf'', -- label ''Close Project'', -- name ''/intranet-cust-kw/workflow-close-project?project_id=$project_id'', -- url -10, -- sort_order v_parent_menu, -- parent_menu_id null -- p_visible_tcl ); PERFORM acs_permission__grant_permission(v_menu, v_employees, ''read''); return 0; end;' language 'plpgsql'; select inline_1 (); drop function inline_1();
Get menu items for side menu:
[im_menu_ul_list -no_uls 1 "projects_admin" {}]
Setting Subnavbar:
# Setup the subnavbar set bind_vars [ns_set create] ns_set put $bind_vars project_id $project_id set project_menu_id [db_string parent_menu "select menu_id from im_menus where label='project'" -default 0] set sub_navbar [im_sub_navbar \ -components \ -base_url "/intranet/projects/view?project_id=$project_id" \ $project_menu_id \ $bind_vars "" "pagedesriptionbar" "project_timesheet_task"]
Setting menu label active:
set menu_label "reporting-finance-expenses"
a) Add to [file].tcl
set show_context_help_p 1
b) Add to [file].adp
<property name="show_context_help_p">@show_context_help_p;noquote@</property>
Avoid absolute file paths when using ‘ns_returnredirect’
ns_returnredirect "upload-files.tcl?inq_id=$security_token"ns_returnfile 200 "application/zip" $filepath ns_return 200 text/html 1 set result [ad_parse_template -params $params "/packages/intranet-core/www/related-objects-component"] [ns_conn url] #returns for example: /intranet/projects [im_url_with_query] # returns for example: /intranet/projects/view?project%5fid=34352
SELECT im_component_plugin__new ( null, -- plugin_id 'acs_object', -- object_type now(), -- creation_date null, -- creation_user null, -- creation_ip null, -- context_id 'Home Random Portrait', -- plugin_name 'intranet-core', -- package_name 'right', -- location '/intranet/index', -- page_url null, -- view_name 5, -- sort_order 'im_random_employee_component' -- component_tcl );
select plugin_id
into v_plugin_id
from im_component_plugins pl
where plugin_name = ''Requests for Quote'';
PERFORM im_grant_permission(v_plugin_id, v_cust_id, ''read'');Actions
im_workflow__set_object_status_id im_workflow__assign_to_owner
<%= [eval ad_context_bar $context_bar] %>
<multiple name="user_stuff"> <if @user_stuff.age@ le 21> <tr bgcolor="red"> </if> <else> <tr bgcolor="green"> </else> <td>@user_stuff.user_name@</td> <td>@user_stuff.age@</td> <td>@user_stuff.shoesize@</td> </tr> </multiple>
Callbacks require a callback definition in *-init.tcl:
Example: intranet-core-init.tcl
ad_proc -public -callback im_dynfield_attribute_after_update { {-object_type:required} {-attribute_name:required} } { Callback to be executed after an attribute has been changed } -
Please note the hyphen at the end of the code!
Add the function to your [PACKAGE_NAME]-procs.tcl:
ad_proc -public -callback im_dynfield_attribute_after_update -impl intranet-core { {-object_type:required} {-attribute_name:required} } { << EXPLAIN >> } { << FUNCTION BODY >> }
Place a call to the callback anywhere in your code
callback im_dynfield_attribute_after_update -object_type "im_project" -attribute_name "christmas_card"
template::head::add_css -href "/intranet-sencha/css/ext-all.css" -media "screen" -order 1 template::head::add_javascript -src "/intranet-sencha/js/ext-all-debug-w-comments.js" -order 1
UPDATE
apm_parameter_values
SET
attr_value=0
WHERE
parameter_id in (
select
parameter_id
from
apm_parameters
where
parameter_name = 'SuppressHttpPort' and
package_key = 'acs-tcl'
)
/intranet-helpdesk/www/new.tcl
notification::new \
-type_id [notification::type::get_type_id -short_name ticket_notif] \
-object_id $ticket_id \
-response_id “” \
-notif_subject $ticket_name \
-notif_text $message
One thing we have learned today is that going to AJAX requires also a review of current HTML. Even though most pages show only warnings when HTML is validated, they are still not conform when using script libraries.
In our example we’d like extend the Home Page Project Component to introduce tabs. “open” projects and “potential” projects should be shown in the same component, accessible through tabs. In order to implement tabs we are using the YUI TabView Control.
Here’s the current code to create the main table that holds the table of projects (intranet-component-procs.tcl):
Implementation is done as follows:
This results in:

As we see, Tab Two Content is visible, clicking on the tabs also results in undesired behaviour.
Firebugs shows us the following code:

FF renders based on the definition for each element an additional font tags which then breaks the JS code.
Before a user can create or view a Financial Document, a permission check is performed. As a sample we will have a closer look what is checks are performed when a user wants to create a new quote.
1) Depending on the required permission type (read/write) a list of cost_type_ids is generated:
set create_cost_types [im_cost_type_write_permissions $user_id]
“im_cost_type_write_permissions” performs the following checks:
set can_write [expr [im_permission $user_id add_costs] || [im_permission $user_id add_invoices]]
if {!$can_write} { return [list] }
set result [db_list writable_cost_centers "
select distinct
ct.cost_type_id
from
im_cost_centers cc,
im_cost_types ct,
acs_permissions p,
party_approved_member_map m,
acs_object_context_index c,
acs_privilege_descendant_map h
where
cc.cost_center_id = c.object_id
and ct.write_privilege = h.descendant
and p.object_id = c.ancestor_id
and m.member_id = :user_id
and p.privilege = h.privilege
and p.grantee_id = m.party_id
"]
return $result
Let’s have a closer look at this sql statement and the tables and views involved.
… all Cost Centers …
im_cost_types contains all privileges related to the “Financial Documents”
Our well known acs_permissions, mapping object_id (1st column), grantee_id (2nd column) and privilege (3rd column).
More information here.
Records in acs_permissions are added/removed setting values in /intranet/admin/profiles/
In case a new privilege is set, a record will be added to this table, if a privilege is removed, the respective record will be removed.
If user is a member of more than one profile, his accumulates the permissions of all profile he’s a member of. User/Profile relationship is managed in table “parties”.
Table “party_approved_member_map” maps a party to the fully expanded list of parties represented by that party including the party itself. So if a party is an individual this view will have exactly one mapping hat is from that party to itself. If a view is a group containing three individuals,this view will have four rows for that party, one for each member,and one from the party to itself. As the table name indicates, only approved members are considered. More information on Open ACS parties here
.. pretty obvious too, manages the relationship between related parent/child privileges:
| oid | privilege | descendant |

This table manages the inheritance of privileges. See OpenACS Permissions Tediously Explained – by Vadim Nasardinov for further information.
| object_id | ancestor_id | n_generations |
Based from what we have learned about the tables involved we now investigate further the “where” clause:
...
where
cc.cost_center_id = c.object_id
and ct.write_privilege = h.descendant
and p.object_id = c.ancestor_id
and m.member_id = :user_id
and p.privilege = h.privilege
and p.grantee_id = m.party_id
a) If a permission is granted for the parent “Cost Center”, it should also be granted for its childs:
im_cost_centers.cost_center_id = acs_object_context_index.object_id"
b) The same is true for cost types, a privilege granted on a higher level should be inherited to its childs:
im_cost_types.write_privilege = acs_privilege_descendant_map.descendant
c) … and for parties (user):
acs_permissions.object_id = acs_object_context_index.ancestor_id
d) Get (approved ) parties related to user to look up:
party_approved_member_map.member_id = :user_id
e) Include all sub privileges:
acs_permissions.privilege = acs_privilege_descendant_map.privilege
f) Limit acs_permissions to approved members
acs_permissions.grantee_id = party_approved_member_map.party_id
You might want to take some time to digest that …
2) In a second step we check, if the Financial Document to be shown/created is member of this list:
if {[lsearch -exact $create_cost_types $cost_type_id] == -1} {
ad_return_complaint "Insufficient Privileges" "