cal-item-procs.tcl

Does not contain a contract.

Location:
/packages/calendar/tcl/cal-item-procs.tcl

Related Files

[ hide source ] | [ make this the default ]

File Contents

# /packages/calendar/tcl/cal-item-procs.tcl

ad_library {

    Utility functions for Calendar Applications

    @author Dirk Gomez (openacs@dirkgomez.de)
    @author Gary Jin (gjin@arsdigita.com)
    @author Ben Adida (ben@openforce.net)
    @creation-date Jan 11, 2001
    @cvs-id $Id: cal-item-procs.tcl,v 1.35.2.9 2023/02/24 12:53:08 antoniop Exp $

}

namespace eval calendar {}
namespace eval calendar::item {}

d_proc -private calendar::item::dates_valid_p {
    {-start_date:required}
    {-end_date:required}
} {
    A sanity check that the start time is before the end time.
} {
    try {
        return [db_string dates_valid_p_select {
            select CASE
              WHEN cast(:start_date as timestamp with time zone)
                   <=
                   cast(:end_date as timestamp with time zone) THEN 1
              ELSE 0
            END from dual
        }]
    } on error {errmsg} {
        # Invalid dates in input, definitely not ok.
        ad_log warning $errmsg
        return 0
    }
}

d_proc -public calendar::item::new {
    {-start_date:required}
    {-end_date:required}
    {-name:required}
    {-description:required}
    {-calendar_id ""}
    {-item_type_id ""}
    {-package_id ""}
    {-location ""}
    {-related_link_url ""}
    {-related_link_text ""}
    {-redirect_to_rel_link_p ""}
    {-cal_uid ""}
    {-ical_vars ""}
} {
    Insert a new calendar item into the database
} {
    if {$package_id eq ""} {
        set package_id [ad_conn package_id]
    }
    if {[dates_valid_p -start_date $start_date -end_date $end_date]} {
        set creation_ip [ad_conn peeraddr]
        set creation_user [ad_conn user_id]

        set activity_id [db_exec_plsql insert_activity {} ]

        #
        # In case we have a cal_uid, save it in the cal_uids table
        # together with the ical_vars.
        #
        if {$cal_uid ne ""} {
            db_dml insert_cal_uid {}
        }

        # Convert from user timezone to system timezone
        if { $start_date ne $end_date } {

            # Convert to server timezone only if it's not an all-day event
            # otherwise, keep the start and end time as 00:00

            set start_date [lc_time_conn_to_system $start_date]
            set end_date [lc_time_conn_to_system $end_date]
        }

        set timespan_id [db_exec_plsql insert_timespan {}]

        # create the cal_item
        # we are leaving the name and description fields in acs_event
        # blank to abide by the definition that an acs_event is an acs_activity
        # with added on temporal information

        # by default, the cal_item permissions
        # are going to be inherited from the calendar permissions
        set cal_item_id [db_exec_plsql cal_item_add {}]

        db_dml set_item_type_id "update cal_items set item_type_id=:item_type_id where cal_item_id=:cal_item_id"

        # removing inherited permissions
        if { $calendar_id ne "" && [calendar::personal_p -calendar_id $calendar_id] } {
            permission::set_not_inherit -object_id $cal_item_id
        }

        ::permission::grant \
            -object_id $cal_item_id \
            -party_id $creation_user \
            -privilege admin

        calendar::do_notifications -mode New -cal_item_id $cal_item_id
        return $cal_item_id

    } else {
        ad_return_complaint 1 [_ calendar.start_time_before_end_time]
        ad_script_abort
    }
}

ad_proc -private calendar::item::all_day_event {start_date_ansi end_date_ansi} {

    Determine, if an event is an all day event depending on the ansi
    start and end dates (e.g. "2018-03-22 00:00:00" and "2018-03-23
    00:00:00").

    The event is a full_day event, when both start_date and end_date
    do not specify a time, which in the datamodel means both dates are
    set at midnight.
} {
    # This previous definition would match any start and end date that
    # were equal (e.g. '2019-02-16 18:00:03' and '2019-02-16
    # 18:00:03'), and also any event with some time specified as long
    # as the date component was different (e.g. '2019-01-01 14:34:02'
    # and '2019-02-16 18:00:03'). When such specific time intervals
    # are given it is hard to argue this would be just an all day
    # event.
    # return [expr {$start_date_ansi eq $end_date_ansi
    #               || [lindex $start_date_ansi 0] ne [lindex $end_date_ansi 0]}]
    return [expr {[lindex $start_date_ansi 1] eq "00:00:00" &&
                  [lindex $end_date_ansi 1] eq "00:00:00"}]
}

d_proc -public calendar::item::get {
    {-cal_item_id:required}
    {-array}
    {-normalize_time_to_utc 0}
} {
    Get the data for a calendar item

} {
    if {[info exists array]} {
        upvar $array row
    }

    db_1row select_item_data {} -column_array row

    if {[calendar::attachments_enabled_p -package_id $row(calendar_package_id)]} {
        set row(n_attachments) [db_string count_attachments {
            select count(*) from attachments where object_id = :cal_item_id
        }]
    }

    if {$normalize_time_to_utc} {
        set row(start_date_ansi) [lc_time_local_to_utc $row(start_date_ansi)]
        set row(end_date_ansi)   [lc_time_local_to_utc $row(end_date_ansi)]
    } else {
        set row(start_date_ansi) [lc_time_system_to_conn $row(start_date_ansi)]
        set row(end_date_ansi)   [lc_time_system_to_conn $row(end_date_ansi)]
    }

    set all_day_event_p [calendar::item::all_day_event \
                             $row(start_date_ansi) $row(end_date_ansi)]
    set row(all_day_event_p) $all_day_event_p
    set row(time_p) [expr {!$all_day_event_p}]

    #ns_log notice "calendar::item::get $row(start_date_ansi) eq $row(end_date_ansi) => $row(time_p)"

    # Localize
    set row(start_time) [lc_time_fmt $row(start_date_ansi) "%X"]

    # Unfortunately, SQL has weekday starting at 1 = Sunday
    set row(start_date)              [lc_time_fmt $row(start_date_ansi) "%Y-%m-%d"]
    set row(end_date)                [lc_time_fmt $row(end_date_ansi) "%Y-%m-%d"]

    set row(day_of_week)             [expr {[lc_time_fmt $row(start_date_ansi) "%w"] + 1}]
    set row(pretty_day_of_week)      [lc_time_fmt $row(start_date_ansi) "%A"]
    set row(day_of_month)            [lc_time_fmt $row(start_date_ansi) "%d"]
    set row(pretty_short_start_date) [lc_time_fmt $row(start_date_ansi) "%x"]
    set row(full_start_date)         [lc_time_fmt $row(start_date_ansi) "%x"]
    set row(full_end_date)           [lc_time_fmt $row(end_date_ansi) "%x"]

    set row(end_time) [lc_time_fmt $row(end_date_ansi) "%X"]

    return [array get row]
}

d_proc -public calendar::item::add_recurrence {
    {-cal_item_id:required}
    {-interval_type:required}
    {-every_n:required}
    {-days_of_week ""}
    {-recur_until ""}
} {
    Adds a recurrence for a calendar item
} {
    db_transaction {
        set recurrence_id [db_exec_plsql create_recurrence {}]

        db_dml update_event {}
        db_exec_plsql insert_instances {}

        # Make sure they're all in the calendar!
        db_dml insert_cal_items {}
    }
    return $recurrence_id
}


d_proc -public calendar::item::edit {
    {-cal_item_id:required}
    {-start_date:required}
    {-end_date:required}
    {-name:required}
    {-description:required}
    {-item_type_id ""}
    {-edit_all_p 0}
    {-edit_past_events_p 1}
    {-calendar_id ""}
    {-location ""}
    {-related_link_url ""}
    {-related_link_text ""}
    {-redirect_to_rel_link_p ""}
    {-cal_uid ""}
    {-ical_vars ""}
} {
    Edit the item
} {
    if {[dates_valid_p -start_date $start_date -end_date $end_date]} {
        if {$edit_all_p} {
            set recurrence_id [db_string select_recurrence_id {}]
            #
            # If the recurrence id is empty (coming from NULL value),
            # then we stop here and just do the normal update
            #
            if {$recurrence_id ne ""} {
                #ns_log notice "recurrence_id $recurrence_id"
                calendar::item::edit_recurrence \
                    -event_id $cal_item_id \
                    -start_date $start_date \
                    -end_date $end_date \
                    -name $name \
                    -description $description \
                    -item_type_id $item_type_id \
                    -calendar_id $calendar_id \
                    -edit_past_events_p $edit_past_events_p

                return
            }
        }

        # Convert from user timezone to system timezone
        if { $start_date ne $end_date } {

            # Convert to server timezone only if it's not an all-day event
            # otherwise, keep the start and end time as 00:00

            set start_date [lc_time_conn_to_system $start_date]
            set end_date   [lc_time_conn_to_system $end_date]
        }

        db_dml update_event {}

        # update the time interval based on the timespan id

        db_1row get_interval_id {}

        db_transaction {
            #
            # If a cal_uid is given, update the attributes in the
            # cal_uid mapping table
            #
            if {$cal_uid ne ""} {
                #
                # We have to determine the activity id for the upsert
                # operation in cal_uids.
                #
                set activity_id [db_string select_activity_id {
                    select activity_id from acs_events where event_id = :cal_item_id
                }]
                #ns_log notice "======= cal_uid_upsert with activity_id $activity_id"
                db_exec_plsql cal_uid_upsert {}
            }

            # call edit procedure
            db_exec_plsql update_interval {}

            # Update the item_type_id and calendar_id
            set colspecs [list]
            lappend colspecs "item_type_id = :item_type_id"
            if { $calendar_id ne "" } {
                lappend colspecs "on_which_calendar = :calendar_id"

                db_dml update_context_id {
                    update acs_objects
                    set    context_id = :calendar_id
                    where  object_id = :cal_item_id
                }
            }

            db_dml update_item_type_id [subst {
                update cal_items
                set    [join $colspecs ", "]
                where  cal_item_id= :cal_item_id
            }]

            calendar::do_notifications -mode Edited -cal_item_id $cal_item_id

            callback calendar::item::after_edit -cal_item_id $cal_item_id
        }
    } else {
        ad_return_complaint 1 [_ calendar.start_time_before_end_time]
        ad_script_abort
    }
}

d_proc -public calendar::item::delete {
    {-cal_item_id:required}
} {
    Delete the calendar item
} {
    callback calendar::item::before_delete \
        -cal_item_id $cal_item_id

    db_exec_plsql delete_cal_item {}
}

d_proc -deprecated calendar::item::assign_permission { cal_item_id
                                     party_id
                                     permission
                                     {revoke ""}
} {
    update the permission of the specific cal_item
    if revoke is set to revoke, then we revoke all permissions

    DEPRECATED: this api is in fact a trivial wrapper for the permission api.

    @see permission::grant
    @see permission::revoke
} {
    if { $revoke ne "revoke" } {
        if { $permission ne "cal_item_read" } {
            permission::grant -object_id $cal_item_id -party_id $party_id -privilege cal_item_read
        }
        permission::grant -object_id $cal_item_id -party_id $party_id -privilege $permission
    } elseif {$revoke eq "revoke"} {
        permission::revoke -object_id $cal_item_id -party_id $party_id -privilege $permission

    }
}

d_proc -public calendar::item::delete_recurrence {
    {-recurrence_id:required}
} {
    delete a recurrence
} {
    db_exec_plsql delete_cal_item_recurrence {}
}


d_proc -public calendar::item::edit_recurrence {
    {-event_id:required}
    {-start_date:required}
    {-end_date:required}
    {-name:required}
    {-description:required}
    {-item_type_id ""}
    {-calendar_id ""}
    {-edit_past_events_p "t"}
} {
    edit a recurrence
} {
    set recurrence_id [db_string select_recurrence_id {}]
    set edit_past_events_p [string map {0 f 1 t} [string is true $edit_past_events_p]]
    db_transaction {
        db_exec_plsql recurrence_timespan_update {}
        # compare this event to the original one we are
        # editing DAVEB 2007-03-15
        calendar::item::get \
            -cal_item_id $event_id \
            -array orig_event

        set colspecs [list]
        foreach col {name description} {
            if {$orig_event($col) ne [set $col]} {
                lappend colspecs "$col = :$col"
            }
        }
        if {[llength $colspecs]} {
            db_dml recurrence_events_update {}
        }
        set colspecs [list]
        lappend colspecs {item_type_id = :item_type_id}
        if { $calendar_id ne "" } {
            lappend colspecs {on_which_calendar = :calendar_id}

            db_dml update_context_id {
            }
        }

        db_dml recurrence_items_update {}
    }
}

d_proc -public -deprecated calendar_item_add_recurrence {
    {-cal_item_id:required}
    {-interval_type:required}
    {-every_n:required}
    {-days_of_week ""}
    {-recur_until ""}
} {
    Adds a recurrence for a calendar item

    @see calendar::item::add_recurrence
} {
    return [calendar::item::add_recurrence \
                -cal_item_id $cal_item_id \
                -interval_type $interval_type \
                -every_n $every_n \
                -days_of_week $days_of_week \
                -recur_until $recur_until
           ]
}

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