<commit_before>###########################################################
#
# Copyright (c) 2005-2008, Southpaw Technology
#                     All Rights Reserved
#
# PROPRIETARY INFORMATION.  This software is proprietary to
# Southpaw Technology, and is not to be reproduced, transmitted,
# or disclosed in any way without written permission.
#
#
#

__all__ = ['TriggerToolWdg', 'TriggerDetailWdg',
'StatusTriggerEditWdg', 'NotificationTriggerEditWdg',
'PythonScriptTriggerEditWdg', 'PythonClassTriggerEditWdg',
'StatusTriggerEditCbk', 'NotificationTriggerEditCbk',
'PythonScriptTriggerEditCbk', 'PythonClassTriggerEditCbk',
'TriggerCreateWdg', 'TriggerCreateCbk',
'TriggerCompleteWdg', 'TriggerCompleteCbk',
'TriggerDateWdg', 'TriggerDateCbk',
'EventTriggerEditWdg'
]

from tactic.ui.common import BaseRefreshWdg

from pyasm.common import jsondumps, jsonloads, Common
from pyasm.biz import Notification, CustomScript, Pipeline, Project
from pyasm.web import DivWdg, WebContainer, Table, HtmlElement
from pyasm.command import Command
from pyasm.search import Search, SearchType, SearchKey
from tactic.ui.panel import TableLayoutWdg

from pyasm.widget import ProdIconButtonWdg, IconWdg, IconButtonWdg, TextWdg, CheckboxWdg, HiddenWdg, SelectWdg, TextAreaWdg, RadioWdg
from tactic.ui.container import ResizableTableWdg
from tactic.ui.container import GearMenuWdg, Menu, MenuItem
from tactic.ui.widget import ActionButtonWdg

import os

class TriggerToolWdg(BaseRefreshWdg):

    def get_display(my):

        search_key = my.kwargs.get("search_key")
        if not search_key:
            web = WebContainer.get_web()
            search_key = web.get_form_value("search_key")
        if search_key:
            current_trigger = SearchKey.get_by_search_key(search_key)
        else:
            current_trigger = None


        # pipeline mode is default
        my.mode = my.kwargs.get("mode")
        if not my.mode:
            my.mode = 'pipeline'

        #my.mode = "pipeline"

        top = DivWdg()
        top.add_class("spt_trigger_top")
        my.set_as_panel(top)

        if my.mode == 'pipeline':
            my.pipeline_code = my.kwargs.get("pipeline_code")
            my.process = my.kwargs.get("process")
            # Is this necessary?
            if my.process:
                top.add_attr("spt_process", my.process)

            top.add_attr("spt_pipeline_code", my.pipeline_code)
            my.title = my.process
            #my.search_type = ''
            my.search_type = my.kwargs.get("search_type")
        else:
            my.pipeline_code = ''
            my.process =''
            my.search_type = my.kwargs.get("search_type")
            top.add_attr("spt_search_type", my.search_type)
            my.title = my.search_type


        table = ResizableTableWdg()
        #table.add_style("width: 100%")
        table.add_color("background", "background")
        table.add_color("color", "color")
        top.add(table)


        table.add_row()
        left = table.add_cell()
        left.add_style("width: 200px")
        left.add_style("min-width: 250px")
        left.add_style("vertical-align: top")
        left.add_style("border: solid 1px %s" % left.get_color("border") )
        left.add_color("color", "color3")
        left.add_color("background", "background3")

        title_div = DivWdg()
        left.add(title_div)
        title_div.add_style("height: 25px")
        title_div.add_style("padding-left: 5px")
        title_div.add_style("padding-top: 8px")
        title_div.add_gradient("background", "background")
        title_div.add_style("min-width: 150px")


        from tactic.ui.app import HelpButtonWdg
        help_button = HelpButtonWdg(alias="project-automation-triggers|project-automation-notifications")
        title_div.add(help_button)
        help_button.add_style("float: right")
        help_button.add_style("margin-top: -8px")
        help_button.add_style("margin-left: -8px")

        add_button = ActionButtonWdg(title='+', size='small', tip='Add a new trigger')
        title_div.add(add_button)
        add_button.add_style("margin-top: -8px")
        add_button.add_style("float: right")
        add_button.add_behavior( {
        'type': 'click_up',
        'kwargs': {
            'mode': my.mode,
            'search_type': my.search_type,
            'pipeline_code': my.pipeline_code,
            'process': my.process
        },
        'cbjs_action': '''
        var top = bvr.src_el.getParent(".spt_trigger_top");
        var content = top.getElement(".spt_trigger_content");

        var class_name = 'tactic.ui.tools.TriggerDetailWdg';
        spt.panel.load(content, class_name, bvr.kwargs);
        '''
        } )

        if my.title:
            title_div.add("<b>Triggers [%s]</b>" % my.title)
        else:
            title_div.add("<b>Triggers</b>")

        left.add("<br clear='all'/>")


        triggers_div = DivWdg()
        left.add(triggers_div)
        triggers_div.add_style("margin: 5px")
        triggers_div.add_style("min-height: 400px")

        # find the triggers
        search = Search("config/trigger")
        if my.mode == 'pipeline':
            search.add_filter("process", my.process)
        else:
            search.add_op('begin')
            search.add_filter("event", "%%|%s" % my.search_type, op='like')
            search.add_filter("search_type", my.search_type)
            search.add_op("or")


        triggers = search.get_sobjects()
        triggers_div.add("<b>Triggers</b><hr/>")
        if triggers:

            cur_trigger = triggers[0]

            for trigger in triggers:
                trigger_div = my.get_trigger_wdg(trigger)
                triggers_div.add(trigger_div)
                trigger_div.add("<br clear='all'/>")
        else:
            triggers_div.add("<i style='opacity: 0.5'>No Triggers Defined</i><br/>")
            triggers_div.add("<br clear='all'/>")


        triggers_div.add("<br/>")

        triggers_div.add("<b>Notifications</b><hr/>")
        search = Search("sthpw/notification")
        if my.mode == 'pipeline':
            search.add_filter("process", my.process)
        else:
            search.add_op('begin')
            search.add_filter("event", "%%|%s" % my.search_type, op='like')
            search.add_filter("search_type", my.search_type)
            search.add_op("or")
        search.add_project_filter()
        triggers = search.get_sobjects()
        for trigger in triggers:
            trigger_div = my.get_trigger_wdg(trigger)
            triggers_div.add(trigger_div)
            trigger_div.add("<br clear='all'/>")

        if not triggers:
            triggers_div.add("<i style='opacity: 0.5'>No Notifications Defined</i><br/>")
            triggers_div.add("<br clear='all'/>")

        """
        triggers_div.add("<b>Site Triggers</b><hr/>")
        search = Search("sthpw/trigger")
        triggers = search.get_sobjects()
        for trigger in triggers:
            trigger_div = my.get_trigger_wdg(trigger)
            triggers_div.add(trigger_div)
            trigger_div.add("<br clear='all'/>")
        if not triggers:
            triggers_div.add("<i>None Defined</i><br/>")
            triggers_div.add("<br clear='all'/>")
        """


        right = table.add_cell()
        right.add_class("spt_trigger_content")
        right.add_style("vertical-align: top")
        right.add_style("padding: 10px 20px 20px 20px")
        right.add_style("min-width: 500px")
        #right.add_style("border: solid 1px %s" % right.get_color("border") )

        right_div = DivWdg()
        right.add(right_div)
        right_div.add_style("width: 500px")
        right_div.add("&nbsp;")

        if current_trigger:
            kwargs = {
                'mode': my.mode,
                'trigger': current_trigger,
                'pipeline_code': my.pipeline_code,
                'process': my.process,
                'search_type': my.search_type,
            }

            right.add( TriggerDetailWdg(**kwargs) )
        else:

            div = DivWdg()
            right.add(div)

            div.add_style("height: 100px")
            div.add_style("width: 300px")
            div.add_color("background", "background3")
            div.add_color("color", "color3")
            div.add_border()
            div.center()
            div.add_style("margin-top: 50px")
            div.add_style("padding-top: 75px")

            div.add_style("text-align: center")
            div.add("<b>No Triggers Selected</b>")



        return top



    def get_trigger_wdg(my, trigger):
        trigger_div = DivWdg()
        trigger_div.add_style("padding: 3px")
        trigger_div.add_class("hand")

        hover = trigger_div.get_color("background", -20)

        trigger_div.add_behavior( {
        'type': 'hover',
        'hover': hover,
        'cbjs_action_over': '''
            bvr.src_el.setStyle("background", bvr.hover);
        ''',
        'cbjs_action_out': '''
            bvr.src_el.setStyle("background", "");
        '''
        } )


        trigger_code = trigger.get_code()
        description = trigger.get_value("description")
        if not description:
            description = '-- no description --'

        title = trigger.get_value("title", no_exception=True)
        if not title:
            title = trigger.get_code()



        trigger_div.add_attr("title", description)

        #checkbox = CheckboxWdg(trigger_code)
        #trigger_div.add(checkbox)
        trigger_div.add(title)

        search_key = SearchKey.get_by_sobject(trigger)

        trigger_div.add_behavior( {
        'type': 'click_up',
        'kwargs': {
            'mode': my.mode,
            'search_type': my.search_type,
            'search_key': search_key,
            'pipeline_code': my.pipeline_code,
            'process': my.process
        },
        'cbjs_action': '''
        var top = bvr.src_el.getParent(".spt_trigger_top");
        var content = top.getElement(".spt_trigger_content");

        var class_name = 'tactic.ui.tools.TriggerDetailWdg';
        spt.panel.load(content, class_name, bvr.kwargs);
        '''
        } )
        return trigger_div



class TriggerDetailWdg(BaseRefreshWdg):

    def get_display(my):
        my.search_key = my.kwargs.get("search_key")
        if my.search_key:
            trigger = Search.get_by_search_key(my.search_key)
        else:
            trigger = my.kwargs.get("trigger")


        # Determine the mode for this widget. 
        my.mode = my.kwargs.get("mode")
        assert my.mode


        # get some info about where this trigger should be defined
        # TODO: is this not embedded in the trigger?
        if my.mode == 'pipeline':
            my.pipeline_code = my.kwargs.get("pipeline_code")
            my.process = my.kwargs.get("process")
            my.pipeline = Pipeline.get_by_code(my.pipeline_code)
            #my.search_type = ""
            my.search_type = my.kwargs.get("search_type")

        else:
            my.pipeline = None
            # search_type is process from schema editor ...
            my.search_type = my.kwargs.get("search_type")
            my.pipeline_code = ""
            my.process = None
            my.pipeline = None


        top = DivWdg()
        top.add_class("spt_trigger_detail_top")

        top.add( my.get_add_trigger_wdg(trigger) )

        return top


    def get_add_trigger_wdg(my, trigger):
        div = DivWdg()
        
        button = ActionButtonWdg(title='Save')
        button.add_style("float: right")
        button.add_style("margin-top: -8px")
        div.add(button)
        button_bvr = {
        'type': 'click_up',
        'cbjs_action': '''

        spt.app_busy.show("Saving Trigger");
        var top = bvr.src_el.getParent(".spt_trigger_top");
        var detail_top = bvr.src_el.getParent(".spt_trigger_detail_top");
        var content = detail_top.getElement(".spt_trigger_add_top");
        
        var values = spt.api.Utility.get_input_values(detail_top, null, false);
        var pipeline_code = top.getAttribute("spt_pipeline_code");
        var process = top.getAttribute("spt_process");
        var search_type = top.getAttribute("spt_search_type");


        values.pipeline_code = pipeline_code;
        values.process = process;

        var event = values.event;
        
        // skip sending thru search_type for checkin type event
        if (!event.test(/^checkin/)) {
            values.search_type = search_type;
        }


        var cbk = content.getAttribute("spt_trigger_add_cbk");
        if (cbk == null || cbk == '') {
            alert("Please select an event and an action");
            spt.app_busy.hide();
            return;
        }

        var server = TacticServerStub.get();
        try{
            var data = server.execute_cmd(cbk, values);
            var search_key = data.info.search_key;
            spt.panel.refresh(top, {search_key: search_key} );
        }
        catch(e){
            alert(spt.exception.handler(e));
        }
        spt.app_busy.hide();
        '''
        } 
        button.add_behavior( button_bvr )


        if not trigger:
            if my.process:
                div.add("<b>Add a new trigger for process [%s]</b><hr/>" % my.process)
            else:
                div.add("<b>Add a new trigger</b><hr/>" )
            event = ''
            code = ''
            description = ''
            search_key = ''
            event = ''
            title = ''

        else:
            event = trigger.get_value("event")
            code = trigger.get_code()
            description = trigger.get_value("description")
            search_key = SearchKey.get_by_sobject(trigger)
            title = trigger.get_value("title", no_exception=True)
            event = trigger.get_value("event")

            if my.process:
                div.add("<b>Edit existing trigger for process [%s]</b><hr/><br/>" % my.process)
            else:
                div.add("<b>Edit existing trigger</b><hr/><br/>")

            div.add("Trigger: %s - %s<br/>" % (trigger.get_code(), trigger.get_value("description") ))



        # determine trigger type
        if not trigger:
            trigger_type = ''
        elif isinstance(trigger, Notification):
            trigger_type = 'notification'
        else:
            class_name = trigger.get_value("class_name")

            script_path = trigger.get_value("script_path")

            # TODO: should use trigger_type in database
            if class_name == 'tactic.command.PipelineTaskStatusTrigger':
                trigger_type = 'task_status'
            elif class_name == 'tactic.command.PipelineTaskCreateTrigger':
                trigger_type = 'task_create'
            elif class_name == 'tactic.command.PipelineTaskDateTrigger':
                trigger_type = 'task_date'

            elif class_name:
                trigger_type = 'python_class'
            elif script_path:
                trigger_type = 'python_script'
            else:
                trigger_type = 'task_status'



        hidden = HiddenWdg("search_key")
        div.add(hidden)
        hidden.set_value( search_key )
        div.add("<br/>")

        table = Table()
        table.add_color("color", "color")
        div.add(table)

        tr = table.add_row()
        td = table.add_cell()
        td.add_style("padding-bottom: 5px")
        td.add("Title: ")
        title_text = TextWdg("title")
        title_text.add_style("width: 200px")
        title_text.set_value(title)
        td = table.add_cell()
        td.add(title_text)
 


        tr = table.add_row()
        td = table.add_cell()
        td.add_style("padding-bottom: 5px")
        td.add("Description: ")
        desc_text = TextAreaWdg("description")
        desc_text.add_style("width: 200px")
        desc_text.add_style("height: 40px")
        desc_text.set_value(description)
        td = table.add_cell()
        td.add(desc_text)
        
        table.add_row()
        td = table.add_cell()
        td.add("Unique Code (optional): ")
        td.add_style("padding-bottom: 5px")
        td = table.add_cell()
        desc_text = TextWdg("code")
        desc_text.add_attr("title", "Unique code for this trigger.  This will be autogenerated if left empty")
        desc_text.add_style("width: 200px")
        desc_text.set_value(code)
        td.add(desc_text)
        td.add("<br/>")
 

        table.add_row()
        tr, td = table.add_row_cell()
        td.add("<b>Event: </b>")
        td.add_style("padding-top", "20px")


        # Higher level triggers
        if my.mode == 'pipeline':
            event_labels = [
                'A new note is added',
                'A task status is changed',
                'A task is assigned',
            ]
            
            #event_labels.append('Files are checked out')

            event_values = [
                'insert|sthpw/note',
                'change|sthpw/task|status',
                'change|sthpw/task|assigned',
            ]
            if my.search_type:
                event_labels.append('Files are checked in')
                event_values.append("checkin|%s"%my.search_type)
        else:
            event_labels = [
                'An item is added',
                'An item is updated',
                'A new note is added',
                'A task status is changed',
                'A task is assigned',
            ]
            event_labels.append('Files are checked in')
            #event_labels.append('Files are checked out')


            event_values = [
                'insert|%s' % my.search_type,
                'update|%s' % my.search_type,
                'insert|sthpw/note',
                'change|sthpw/task|status',
                'change|sthpw/task|assigned',
            ]
            event_values.append('checkin|%s' % my.search_type)
            #event_values.append('checkout|%s' % my.search_type)




        event_labels.append("Custom event")
        event_values.append("__custom__")

        #table.add_row()
        #td = table.add_cell()

        tr, td = table.add_row_cell()
        td.add_color("color", "color")
        td.add_color("background", "background", -5)
        td.add_style("padding: 10px")
        td.add_border()
        td.add("When&nbsp;&nbsp;&nbsp;")

        #td = table.add_cell()

        select = SelectWdg("event")
        td.add(select)
        select.add_class("spt_trigger_event")
        select.set_option("values", event_values)
        select.set_option("labels", event_labels)
        select.add_empty_option("-- Choose event --")

        if event and event not in event_values:
            select.set_value("__custom__")
            custom_event = event
            event = '__custom__'
        else:
            select.set_value(event)
            custom_event = ''

        select.add_behavior( {
        'type': 'change',
        'kwargs': {
            'mode': my.mode,
            'pipeline_code': my.pipeline_code,
            'process': my.process,
            'search_type': my.search_type,
        },
        'cbjs_action': '''
        var top = bvr.src_el.getParent(".spt_trigger_detail_top");
        var content = top.getElement(".spt_trigger_event_top");

        var event = bvr.src_el.value;
        if (!event) {
            spt.info('Please choose an event.');
            return;
        }
        bvr.kwargs.event = event;
        var trigger_wdg = 'tactic.ui.tools.EventTriggerEditWdg';

        spt.panel.load(content, trigger_wdg, bvr.kwargs)
        var action_area = top.getElement('.spt_trigger_add_top')
       
        spt.panel.refresh(action_area, {'event': event});
        '''
        } )



        #tr, td = table.add_row_cell()
        #td.add("Search Type (from pipeline): %s" % my.search_type)

        event_div = DivWdg()
        event_div.add_style("width: 100%")
        event_div.add_class("spt_trigger_event_top")
        event_div.set_parent(td)
        #event_div.add_color("color", "color")
        #event_div.add_color("background", "background", -5)
        #event_div.add_border()
        #event_div.add_style("padding: 5px")
        #event_div.add_style("margin: 10px")

        kwargs = my.kwargs.copy()
        kwargs['trigger'] = trigger
        kwargs['event'] = event
        kwargs['custom_event'] = custom_event

        event_wdg = EventTriggerEditWdg(**kwargs)
        event_wdg.set_parent(event_div)





        # Handle the action
        if my.mode == 'pipeline':
            trigger_labels = [
                'Send a notification',
                'Update another task status',
                'Create another task',
                'Set actual task date',
                'Run python code',
                'Run python trigger',
            ]
            trigger_values = [
                'notification',
                'task_status',
                'task_create',
                'task_date',
                'python_script',
                'python_class',
            ]
        else:
            trigger_labels = [
                'Send a notification',
                #'Create a task for each process',
                #'Add a note',
                'Set task complete',
                #'Delete versions',
                'Run python code',
                'Run python trigger',
            ]
            trigger_values = [
                'notification',
                #'task_create_all',
                #'note_insert',
                'task_complete',
                #'version_delete',
                'python_script',
                'python_class',
            ]


        #trigger_edit = TaskCompleteTestWdg()
        #trigger_values.append(trigger_edit.get_trigger_type())







        tr, td = table.add_row_cell()

        title = DivWdg()
        td.add(title)
        title.add("Action:")
        title.add_style("font-weight: bold")
        title.add_style("padding: 20 0 0 0")

        table.add_row()
        #td = table.add_cell()
        tr, td = table.add_row_cell()
        td.add_color("color", "color")
        td.add_color("background", "background", -5)
        td.add_style("padding: 10px")
        td.add_border()
        if trigger_type != 'notification':
            
            td.add("Do the following: ")
            
            #td = table.add_cell()
            # Action Select
            select = SelectWdg("trigger")
            select.set_option("labels", trigger_labels)
            select.set_option("values", trigger_values)
            if trigger_type:
                select.set_value(trigger_type)

            if isinstance(trigger, Notification):
                select.set_value("notification")
            elif trigger and trigger.get_value("script_path"):
                select.set_value("python_script")


            select.add_empty_option("-- Choose action --")
            td.add(select)
            trigger_sk = ''
            if trigger:
                trigger_sk = trigger.get_search_key()
            select.add_behavior( {
            'type': 'change',
            'kwargs': {
                'pipeline_code': my.pipeline_code,
                'process': my.process,
                'search_key': trigger_sk
            },
            'cbjs_action': '''
            var top = bvr.src_el.getParent(".spt_trigger_detail_top");
            var content = top.getElement(".spt_trigger_add_top");
            var event_el = top.getElement(".spt_trigger_event");

            var event = event_el.value;
            bvr.kwargs['event'] = event;
             
            var trigger_type = bvr.src_el.value;
            var trigger_wdg;
            var trigger_cbk;
            if (trigger_type == "notification") {
                trigger_wdg = 'tactic.ui.tools.NotificationTriggerEditWdg';
                trigger_cbk = 'tactic.ui.tools.NotificationTriggerEditCbk';
            }
            else if (trigger_type == "task_status") {
                trigger_wdg = 'tactic.ui.tools.StatusTriggerEditWdg';
                trigger_cbk = 'tactic.ui.tools.StatusTriggerEditCbk';
            }
            else if (trigger_type == "task_create") {
                trigger_wdg = 'tactic.ui.tools.TriggerCreateWdg';
                trigger_cbk = 'tactic.ui.tools.TriggerCreateCbk';
            }
            else if (trigger_type == "task_complete") {
                trigger_wdg = 'tactic.ui.tools.TriggerCompleteWdg';
                trigger_cbk = 'tactic.ui.tools.TriggerCompleteCbk';
            }
            else if (trigger_type == "task_date") {
                trigger_wdg = 'tactic.ui.tools.TriggerDateWdg';
                trigger_cbk = 'tactic.ui.tools.TriggerDateCbk';
            }
            else if (trigger_type == "python_script") {
                trigger_wdg = 'tactic.ui.tools.PythonScriptTriggerEditWdg';
                trigger_cbk = 'tactic.ui.tools.PythonScriptTriggerEditCbk';
            }
            else if (trigger_type == "python_class") {
                trigger_wdg = 'tactic.ui.tools.PythonClassTriggerEditWdg';
                trigger_cbk = 'tactic.ui.tools.PythonClassTriggerEditCbk';
            }
            else {
                spt.behavior.replace_inner_html(content, "");
                content.setAttribute("spt_trigger_add_cbk", "");
                return;
            }

            spt.panel.load(content, trigger_wdg, bvr.kwargs)
            content.setAttribute("spt_trigger_add_cbk", trigger_cbk);

            '''
            } )

            td.add(HtmlElement.br(2))

        trigger_div = DivWdg()
        td.add(trigger_div)
        trigger_div.add_class("spt_trigger_add_top")
        #trigger_div.add_style("padding: 5px")
        #trigger_div.add_border()
        #trigger_div.add_color("background", "background", -5)
        #trigger_div.add_style("margin: 10px")

        kwargs = my.kwargs.copy()
        kwargs['trigger'] = trigger


        # determine detail widget to display
        if trigger_type == "notification":
            trigger_wdg = NotificationTriggerEditWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.NotificationTriggerEditCbk"

        elif trigger_type == "task_status":
            trigger_wdg = StatusTriggerEditWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.StatusTriggerEditCbk"

        elif trigger_type == "task_create":
            trigger_wdg = TriggerCreateWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.TriggerCreateCbk"

        elif trigger_type == "task_date":
            trigger_wdg = TriggerDateWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.TriggerDateCbk"

        elif trigger_type == "python_script":
            trigger_wdg = PythonScriptTriggerEditWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.PythonScriptTriggerEditCbk"

        elif trigger_type == "python_class":
            trigger_wdg = PythonClassTriggerEditWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.PythonClassTriggerEditCbk"

        else:
            trigger_wdg = DivWdg()
            #trigger_wdg.add("<i>-- No Details --</i>")
            trigger_cbk = ''

        trigger_div.add_attr("spt_trigger_add_cbk", trigger_cbk)
        trigger_div.add_attr("spt_class_name", Common.get_full_class_name(trigger_wdg))
        if trigger:
            trigger_div.add_attr("spt_search_key", trigger.get_search_key())
        trigger_div.add(trigger_wdg)


        div.add("<br/><hr/>")


        # FIXME: this does not delete the python script
        delete_button = ActionButtonWdg(title='Remove', tip='Remove the current trigger')
        div.add(delete_button)
        delete_button.add_style("float: left")
        delete_button.add_behavior( {
        'type': 'click_up',
        'search_key': search_key,
        'cbjs_action': '''
        if (! confirm( "Are you sure you wish to delete this trigger") ) {
            return;
        }
        var server = TacticServerStub.get();
        server.delete_sobject(bvr.search_key);

        var top = bvr.src_el.getParent(".spt_trigger_top");
        spt.panel.refresh(top);
        '''
        } )


        button = ActionButtonWdg(title='Save')
        button.add_style("float: right")
        div.add(button)
       
        button.add_behavior(button_bvr)



        #from tactic.ui.panel import EditWdg
        #edit = EditWdg(search_type='config/trigger')
        #edit.set_sobject(trigger)
        #div.add(edit)


        return div




    def get_trigger_context_menu(my):
        menu = Menu(width=180)
        menu.set_allow_icons(False)
        menu.set_setup_cbfn( 'spt.dg_table.smenu_ctx.setup_cbk' )


        menu_item = MenuItem(type='title', label='Actions')
        menu.add(menu_item)

        menu_item = MenuItem(type='action', label='Delete')
        menu_item.add_behavior( {
            'cbjs_action': '''
            var activator = spt.smenu.get_activator(bvr);
            '''
        } )
        menu.add(menu_item)

        return menu





class BaseTriggerEditWdg(BaseRefreshWdg):

    def get_trigger_type(my):
        pass

    def get_display_class(my):
        return Common.get_full_class_name(my)

    def get_callback_class(my):
        pass

    def get_display(my):
        return "No Display Defined"



class TaskCompleteTestWdg(BaseTriggerEditWdg):

    def get_trigger_type(my):
        return "task_complete"

    #def get_display_class(my):
    #    return 'tactic.ui.tools.TriggerCompleteWdg'

    def get_callback_class(my):
        return 'tactic.ui.tools.TriggerCompleteCbk'

    def get_display(my):
        return "TaskCompleteTestWdg"




class EventTriggerEditWdg(BaseRefreshWdg):
    ''' This class displays options for a chosen event'''
    def get_display(my):
        top = DivWdg()

        process = my.kwargs.get("process")
        pipeline_code = my.kwargs.get("pipeline_code")
        search_type = my.kwargs.get("search_type")

        if search_type:
            search = Search("sthpw/pipeline")
            search.add_filter("search_type", search_type)
            if pipeline_code:
                search.add_filter('code', pipeline_code)
            pipeline = search.get_sobject()
        else:
            pipeline = Pipeline.get_by_code(pipeline_code)

            if not pipeline:
                top.add("<br/>The pipeline [%s] does not exist.  If this pipeline is on your canvas, please save first.<br/>" % pipeline_code)
                return top


        event = my.kwargs.get("event")
        if not event:
            return top



        trigger = my.kwargs.get("trigger")
        if trigger:
            data = trigger.get_value("data", no_exception=True)
            if data:
                try:
                    data = jsonloads(data)
                except:
                    data = {}
                if not isinstance(data, list):
                    data = [data]
            else:
                data = []
        else:
            data = []

        if event == '__custom__':
            top.add("Event name: &nbsp;&nbsp;")
            text = TextWdg("custom_event")
            top.add(text)

            custom_event = my.kwargs.get("custom_event")
            if custom_event:
                text.set_value(custom_event)

            text.add_style("width: 200px")
            top.add_style("padding: 10px")


        #elif event.startswith("change|sthpw/task|status") and process:
        elif event.startswith("change|sthpw/task|status"):

            if process:
                process_obj = pipeline.get_process(process)
                if not process_obj:
                    top.add("<br/>The process item [%s] does not exist. You may need to save your pipeline first.<br/>" % process)
                    return top

                task_pipeline_code = process_obj.get_task_pipeline()
                task_pipeline = Pipeline.get_by_code(task_pipeline_code)
                if not task_pipeline:
                    task_pipeline_code='task'
                    task_pipeline = Pipeline.get_by_code(task_pipeline_code)
                task_statuses = task_pipeline.get_processes()
            elif search_type:
                # get all the processes for this search type
                pipelines = Pipeline.get_by_search_type(search_type)

                task_pipeline_code = "task"
                task_pipeline = Pipeline.get_by_code(task_pipeline_code)
                task_statuses = task_pipeline.get_processes()


                #outputs = pipeline.get_output_processes(process)
                #outputs = [x.get_name() for x in outputs]
                #inputs = pipeline.get_input_processes(process)
                #inputs = [x.get_name() for x in inputs]



            top.add("to: ")

            status = SelectWdg("src_status")
            status.set_option("values", task_statuses)
            
            for item in data:
                src_status = item.get("src_status")
                if src_status:
                    status.set_value(src_status)
                elif task_statuses:
                    status.set_value(task_statuses[-1])
                break

            top.add(status)
            top.add("<br/>")
            top.add_style("padding: 10px")

        elif event.startswith("checkin|") and process:
            if trigger:
                listen_process = trigger.get_value("listen_process", no_exception=True)
            else:
                listen_process = None
            inputs = pipeline.get_input_processes(process)
            inputs = [str(x) for x in inputs]
            
            top.add("In the following process: <br/><br/>")
            process_div = DivWdg()
            top.add(process_div)
            process_div.add_style("margin-left: 10px")

            checkbox = CheckboxWdg("this_process")
            process_div.add(checkbox)
            if not listen_process or listen_process == process:
                checkbox.set_checked()
            process_div.add(" this process")

            process_div.add("&nbsp;"*3 + " or " + "&nbsp;"*3)

            select = SelectWdg("listen_process")
            process_div.add(select)
            select.set_option("values", inputs)
            select.set_option("labels", inputs)
            select.add_empty_option("-- Previous Process --")
            if listen_process and listen_process != process:
                select.set_value(listen_process)

            top.add_style("padding: 10px")


        return top






class StatusTriggerEditWdg(BaseRefreshWdg):

    def get_display(my):
        trigger = my.kwargs.get("trigger")
        if trigger:
            my.data = trigger.get_value("data")
            if not my.data:
                my.data = {}
            else:
                my.data = jsonloads(my.data)
            if isinstance(my.data, dict):<commit_msg>BRANCH 4.1:   updated Trigger Detail Widget and allowed a process dropdown to display in search_type mode   hid the process dropdown in pipeline mode   removed the not-yet-implemented task_complete option<commit_after>###########################################################
#
# Copyright (c) 2005-2008, Southpaw Technology
#                     All Rights Reserved
#
# PROPRIETARY INFORMATION.  This software is proprietary to
# Southpaw Technology, and is not to be reproduced, transmitted,
# or disclosed in any way without written permission.
#
#
#

__all__ = ['TriggerToolWdg', 'TriggerDetailWdg',
'StatusTriggerEditWdg', 'NotificationTriggerEditWdg',
'PythonScriptTriggerEditWdg', 'PythonClassTriggerEditWdg',
'StatusTriggerEditCbk', 'NotificationTriggerEditCbk',
'PythonScriptTriggerEditCbk', 'PythonClassTriggerEditCbk',
'TriggerCreateWdg', 'TriggerCreateCbk',
'TriggerCompleteWdg', 'TriggerCompleteCbk',
'TriggerDateWdg', 'TriggerDateCbk',
'EventTriggerEditWdg'
]

from tactic.ui.common import BaseRefreshWdg

from pyasm.common import jsondumps, jsonloads, Common
from pyasm.biz import Notification, CustomScript, Pipeline, Project
from pyasm.web import DivWdg, WebContainer, Table, HtmlElement, SpanWdg
from pyasm.command import Command
from pyasm.search import Search, SearchType, SearchKey
from tactic.ui.panel import TableLayoutWdg

from pyasm.widget import ProdIconButtonWdg, IconWdg, IconButtonWdg, TextWdg, CheckboxWdg, HiddenWdg, SelectWdg, TextAreaWdg, RadioWdg
from tactic.ui.container import ResizableTableWdg
from tactic.ui.container import GearMenuWdg, Menu, MenuItem
from tactic.ui.widget import ActionButtonWdg

import os

class TriggerToolWdg(BaseRefreshWdg):

    def get_display(my):

        search_key = my.kwargs.get("search_key")
        if not search_key:
            web = WebContainer.get_web()
            search_key = web.get_form_value("search_key")
        if search_key:
            current_trigger = SearchKey.get_by_search_key(search_key)
        else:
            current_trigger = None


        # pipeline mode is default
        my.mode = my.kwargs.get("mode")
        if not my.mode:
            my.mode = 'pipeline'

        #my.mode = "pipeline"

        top = DivWdg()
        top.add_class("spt_trigger_top")
        my.set_as_panel(top)

        if my.mode == 'pipeline':
            my.pipeline_code = my.kwargs.get("pipeline_code")
            my.process = my.kwargs.get("process")
            # Is this necessary?
            if my.process:
                top.add_attr("spt_process", my.process)

            top.add_attr("spt_pipeline_code", my.pipeline_code)
            my.title = my.process
            #my.search_type = ''
            my.search_type = my.kwargs.get("search_type")
        else:
            my.pipeline_code = ''
            my.process =''
            my.search_type = my.kwargs.get("search_type")
            top.add_attr("spt_search_type", my.search_type)
            my.title = my.search_type


        table = ResizableTableWdg()
        #table.add_style("width: 100%")
        table.add_color("background", "background")
        table.add_color("color", "color")
        top.add(table)


        table.add_row()
        left = table.add_cell()
        left.add_style("width: 200px")
        left.add_style("min-width: 250px")
        left.add_style("vertical-align: top")
        left.add_style("border: solid 1px %s" % left.get_color("border") )
        left.add_color("color", "color3")
        left.add_color("background", "background3")

        title_div = DivWdg()
        left.add(title_div)
        title_div.add_style("height: 25px")
        title_div.add_style("padding-left: 5px")
        title_div.add_style("padding-top: 8px")
        title_div.add_gradient("background", "background")
        title_div.add_style("min-width: 150px")


        from tactic.ui.app import HelpButtonWdg
        help_button = HelpButtonWdg(alias="project-automation-triggers|project-automation-notifications")
        title_div.add(help_button)
        help_button.add_style("float: right")
        help_button.add_style("margin-top: -8px")
        help_button.add_style("margin-left: -8px")

        add_button = ActionButtonWdg(title='+', size='small', tip='Add a new trigger')
        title_div.add(add_button)
        add_button.add_style("margin-top: -8px")
        add_button.add_style("float: right")
        add_button.add_behavior( {
        'type': 'click_up',
        'kwargs': {
            'mode': my.mode,
            'search_type': my.search_type,
            'pipeline_code': my.pipeline_code,
            'process': my.process
        },
        'cbjs_action': '''
        var top = bvr.src_el.getParent(".spt_trigger_top");
        var content = top.getElement(".spt_trigger_content");

        var class_name = 'tactic.ui.tools.TriggerDetailWdg';
        spt.panel.load(content, class_name, bvr.kwargs);
        '''
        } )

        if my.title:
            title_div.add("<b>Triggers [%s]</b>" % my.title)
        else:
            title_div.add("<b>Triggers</b>")

        left.add("<br clear='all'/>")


        triggers_div = DivWdg()
        left.add(triggers_div)
        triggers_div.add_style("margin: 5px")
        triggers_div.add_style("min-height: 400px")

        # find the triggers
        search = Search("config/trigger")
        if my.mode == 'pipeline':
            search.add_filter("process", my.process)
        else:
            search.add_op('begin')
            search.add_filter("event", "%%|%s" % my.search_type, op='like')
            search.add_filter("search_type", my.search_type)
            search.add_op("or")


        triggers = search.get_sobjects()
        triggers_div.add("<b>Triggers</b><hr/>")
        if triggers:

            cur_trigger = triggers[0]

            for trigger in triggers:
                trigger_div = my.get_trigger_wdg(trigger)
                triggers_div.add(trigger_div)
                trigger_div.add("<br clear='all'/>")
        else:
            triggers_div.add("<i style='opacity: 0.5'>No Triggers Defined</i><br/>")
            triggers_div.add("<br clear='all'/>")


        triggers_div.add("<br/>")

        triggers_div.add("<b>Notifications</b><hr/>")
        search = Search("sthpw/notification")
        if my.mode == 'pipeline':
            search.add_filter("process", my.process)
        else:
            search.add_op('begin')
            search.add_filter("event", "%%|%s" % my.search_type, op='like')
            search.add_filter("search_type", my.search_type)
            search.add_op("or")
        search.add_project_filter()
        triggers = search.get_sobjects()
        for trigger in triggers:
            trigger_div = my.get_trigger_wdg(trigger)
            triggers_div.add(trigger_div)
            trigger_div.add("<br clear='all'/>")

        if not triggers:
            triggers_div.add("<i style='opacity: 0.5'>No Notifications Defined</i><br/>")
            triggers_div.add("<br clear='all'/>")

        """
        triggers_div.add("<b>Site Triggers</b><hr/>")
        search = Search("sthpw/trigger")
        triggers = search.get_sobjects()
        for trigger in triggers:
            trigger_div = my.get_trigger_wdg(trigger)
            triggers_div.add(trigger_div)
            trigger_div.add("<br clear='all'/>")
        if not triggers:
            triggers_div.add("<i>None Defined</i><br/>")
            triggers_div.add("<br clear='all'/>")
        """


        right = table.add_cell()
        right.add_class("spt_trigger_content")
        right.add_style("vertical-align: top")
        right.add_style("padding: 10px 20px 20px 20px")
        right.add_style("min-width: 500px")
        #right.add_style("border: solid 1px %s" % right.get_color("border") )

        right_div = DivWdg()
        right.add(right_div)
        right_div.add_style("width: 500px")
        right_div.add("&nbsp;")

        if current_trigger:
            kwargs = {
                'mode': my.mode,
                'trigger': current_trigger,
                'pipeline_code': my.pipeline_code,
                'process': my.process,
                'search_type': my.search_type,
            }

            right.add( TriggerDetailWdg(**kwargs) )
        else:

            div = DivWdg()
            right.add(div)

            div.add_style("height: 100px")
            div.add_style("width: 300px")
            div.add_color("background", "background3")
            div.add_color("color", "color3")
            div.add_border()
            div.center()
            div.add_style("margin-top: 50px")
            div.add_style("padding-top: 75px")

            div.add_style("text-align: center")
            div.add("<b>No Triggers Selected</b>")



        return top



    def get_trigger_wdg(my, trigger):
        trigger_div = DivWdg()
        trigger_div.add_style("padding: 3px")
        trigger_div.add_class("hand")

        hover = trigger_div.get_color("background", -20)

        trigger_div.add_behavior( {
        'type': 'hover',
        'hover': hover,
        'cbjs_action_over': '''
            bvr.src_el.setStyle("background", bvr.hover);
        ''',
        'cbjs_action_out': '''
            bvr.src_el.setStyle("background", "");
        '''
        } )


        trigger_code = trigger.get_code()
        description = trigger.get_value("description")
        if not description:
            description = '-- no description --'

        title = trigger.get_value("title", no_exception=True)
        if not title:
            title = trigger.get_code()



        trigger_div.add_attr("title", description)

        #checkbox = CheckboxWdg(trigger_code)
        #trigger_div.add(checkbox)
        trigger_div.add(title)

        search_key = SearchKey.get_by_sobject(trigger)

        trigger_div.add_behavior( {
        'type': 'click_up',
        'kwargs': {
            'mode': my.mode,
            'search_type': my.search_type,
            'search_key': search_key,
            'pipeline_code': my.pipeline_code,
            'process': my.process
        },
        'cbjs_action': '''
        var top = bvr.src_el.getParent(".spt_trigger_top");
        var content = top.getElement(".spt_trigger_content");

        var class_name = 'tactic.ui.tools.TriggerDetailWdg';
        spt.panel.load(content, class_name, bvr.kwargs);
        '''
        } )
        return trigger_div



class TriggerDetailWdg(BaseRefreshWdg):

    def get_display(my):
        my.search_key = my.kwargs.get("search_key")
        if my.search_key:
            trigger = Search.get_by_search_key(my.search_key)
        else:
            trigger = my.kwargs.get("trigger")


        # Determine the mode for this widget. 
        my.mode = my.kwargs.get("mode")
        assert my.mode


        # get some info about where this trigger should be defined
        # TODO: is this not embedded in the trigger?
        if my.mode == 'pipeline':
            my.pipeline_code = my.kwargs.get("pipeline_code")
            my.process = my.kwargs.get("process")
            my.pipeline = Pipeline.get_by_code(my.pipeline_code)
            #my.search_type = ""
            my.search_type = my.kwargs.get("search_type")

        else:
            my.pipeline = None
            # search_type is process from schema editor ...
            my.search_type = my.kwargs.get("search_type")
            my.pipeline_code = ""
            my.process = None
            my.pipeline = None


        top = DivWdg()
        top.add_class("spt_trigger_detail_top")

        top.add( my.get_add_trigger_wdg(trigger) )

        return top


    def get_add_trigger_wdg(my, trigger):
        div = DivWdg()
        
        button = ActionButtonWdg(title='Save')
        button.add_style("float: right")
        button.add_style("margin-top: -8px")
        div.add(button)
        button_bvr = {
        'type': 'click_up',
        'cbjs_action': '''

        spt.app_busy.show("Saving Trigger");
        var top = bvr.src_el.getParent(".spt_trigger_top");
        var detail_top = bvr.src_el.getParent(".spt_trigger_detail_top");
        var content = detail_top.getElement(".spt_trigger_add_top");
        
        var values = spt.api.Utility.get_input_values(detail_top, null, false);
        var pipeline_code = top.getAttribute("spt_pipeline_code");
        var process = top.getAttribute("spt_process");
        var search_type = top.getAttribute("spt_search_type");


        values.pipeline_code = pipeline_code;
        values.process = process;

        var event = values.event;
        
        // skip sending thru search_type for checkin type event
        if (!event.test(/^checkin/)) {
            values.search_type = search_type;
        }


        var cbk = content.getAttribute("spt_trigger_add_cbk");
        if (cbk == null || cbk == '') {
            alert("Please select an event and an action");
            spt.app_busy.hide();
            return;
        }

        var server = TacticServerStub.get();
        try{
            var data = server.execute_cmd(cbk, values);
            var search_key = data.info.search_key;
            spt.panel.refresh(top, {search_key: search_key} );
        }
        catch(e){
            alert(spt.exception.handler(e));
        }
        spt.app_busy.hide();
        '''
        } 
        button.add_behavior( button_bvr )


        if not trigger:
            if my.process:
                div.add("<b>Add a new trigger for process [%s]</b><hr/>" % my.process)
            else:
                div.add("<b>Add a new trigger</b><hr/>" )
            event = ''
            code = ''
            description = ''
            search_key = ''
            event = ''
            title = ''

        else:
            event = trigger.get_value("event")
            code = trigger.get_code()
            description = trigger.get_value("description")
            search_key = SearchKey.get_by_sobject(trigger)
            title = trigger.get_value("title", no_exception=True)
            event = trigger.get_value("event")

            if my.process:
                div.add("<b>Edit existing trigger for process [%s]</b><hr/><br/>" % my.process)
            else:
                div.add("<b>Edit existing trigger</b><hr/><br/>")

            div.add("Trigger: %s - %s<br/>" % (trigger.get_code(), trigger.get_value("description") ))



        # determine trigger type
        if not trigger:
            trigger_type = ''
        elif isinstance(trigger, Notification):
            trigger_type = 'notification'
        else:
            class_name = trigger.get_value("class_name")

            script_path = trigger.get_value("script_path")

            # TODO: should use trigger_type in database
            if class_name == 'tactic.command.PipelineTaskStatusTrigger':
                trigger_type = 'task_status'
            elif class_name == 'tactic.command.PipelineTaskCreateTrigger':
                trigger_type = 'task_create'
            elif class_name == 'tactic.command.PipelineTaskDateTrigger':
                trigger_type = 'task_date'

            elif class_name:
                trigger_type = 'python_class'
            elif script_path:
                trigger_type = 'python_script'
            else:
                trigger_type = 'task_status'



        hidden = HiddenWdg("search_key")
        div.add(hidden)
        hidden.set_value( search_key )
        div.add("<br/>")

        table = Table()
        table.add_color("color", "color")
        div.add(table)

        tr = table.add_row()
        td = table.add_cell()
        td.add_style("padding-bottom: 5px")
        td.add("Title: ")
        title_text = TextWdg("title")
        title_text.add_style("width: 200px")
        title_text.set_value(title)
        td = table.add_cell()
        td.add(title_text)
 


        tr = table.add_row()
        td = table.add_cell()
        td.add_style("padding-bottom: 5px")
        td.add("Description: ")
        desc_text = TextAreaWdg("description")
        desc_text.add_style("width: 200px")
        desc_text.add_style("height: 40px")
        desc_text.set_value(description)
        td = table.add_cell()
        td.add(desc_text)
        
        table.add_row()
        td = table.add_cell()
        td.add("Unique Code (optional): ")
        td.add_style("padding-bottom: 5px")
        td = table.add_cell()
        desc_text = TextWdg("code")
        desc_text.add_attr("title", "Unique code for this trigger.  This will be autogenerated if left empty")
        desc_text.add_style("width: 200px")
        desc_text.set_value(code)
        td.add(desc_text)
        td.add("<br/>")
 

        table.add_row()
        tr, td = table.add_row_cell()
        td.add("<b>Event: </b>")
        td.add_style("padding-top", "20px")


        # Higher level triggers
        if my.mode == 'pipeline':
            event_labels = [
                'A new note is added',
                'A task status is changed',
                'A task is assigned',
            ]
            
            #event_labels.append('Files are checked out')

            event_values = [
                'insert|sthpw/note',
                'change|sthpw/task|status',
                'change|sthpw/task|assigned',
            ]
            if my.search_type:
                event_labels.append('Files are checked in')
                event_values.append("checkin|%s"%my.search_type)
        else:
            event_labels = [
                'An item is added',
                'An item is updated',
                'A new note is added',
                'A task status is changed',
                'A task is assigned',
            ]
            event_labels.append('Files are checked in')
            #event_labels.append('Files are checked out')


            event_values = [
                'insert|%s' % my.search_type,
                'update|%s' % my.search_type,
                'insert|sthpw/note',
                'change|sthpw/task|status',
                'change|sthpw/task|assigned',
            ]
            event_values.append('checkin|%s' % my.search_type)
            #event_values.append('checkout|%s' % my.search_type)




        event_labels.append("Custom event")
        event_values.append("__custom__")

        #table.add_row()
        #td = table.add_cell()

        tr, td = table.add_row_cell()
        td.add_color("color", "color")
        td.add_color("background", "background", -5)
        td.add_style("padding: 10px")
        td.add_border()
        td.add("When&nbsp;&nbsp;&nbsp;")

        #td = table.add_cell()

        select = SelectWdg("event")
        td.add(select)
        select.add_class("spt_trigger_event")
        select.set_option("values", event_values)
        select.set_option("labels", event_labels)
        select.add_empty_option("-- Choose event --")

        if event and event not in event_values:
            select.set_value("__custom__")
            custom_event = event
            event = '__custom__'
        else:
            select.set_value(event)
            custom_event = ''

        select.add_behavior( {
        'type': 'change',
        'kwargs': {
            'mode': my.mode,
            'pipeline_code': my.pipeline_code,
            'process': my.process,
            'search_type': my.search_type,
        },
        'cbjs_action': '''
        var top = bvr.src_el.getParent(".spt_trigger_detail_top");
        var content = top.getElement(".spt_trigger_event_top");

        var event = bvr.src_el.value;
        if (!event) {
            spt.info('Please choose an event.');
            return;
        }
        bvr.kwargs.event = event;
        var trigger_wdg = 'tactic.ui.tools.EventTriggerEditWdg';

        spt.panel.load(content, trigger_wdg, bvr.kwargs)
        var action_area = top.getElement('.spt_trigger_add_top')
       
        spt.panel.refresh(action_area, {'event': event});
        '''
        } )



        #tr, td = table.add_row_cell()
        #td.add("Search Type (from pipeline): %s" % my.search_type)

        event_div = DivWdg()
        event_div.add_style("width: 100%")
        event_div.add_class("spt_trigger_event_top")
        event_div.set_parent(td)
        #event_div.add_color("color", "color")
        #event_div.add_color("background", "background", -5)
        #event_div.add_border()
        #event_div.add_style("padding: 5px")
        #event_div.add_style("margin: 10px")

        kwargs = my.kwargs.copy()
        kwargs['trigger'] = trigger
        kwargs['event'] = event
        kwargs['custom_event'] = custom_event

        event_wdg = EventTriggerEditWdg(**kwargs)
        event_wdg.set_parent(event_div)





        # Handle the action
        if my.mode == 'pipeline':
            trigger_labels = [
                'Send a notification',
                'Update another task status',
                'Create another task',
                'Set actual task date',
                'Run python code',
                'Run python trigger',
            ]
            trigger_values = [
                'notification',
                'task_status',
                'task_create',
                'task_date',
                'python_script',
                'python_class',
            ]
        else:
            trigger_labels = [
                'Send a notification',
                #'Create a task for each process',
                #'Add a note',
                #'Set task complete',
                #'Delete versions',
                'Run python code',
                'Run python trigger',
            ]
            #TODO: enable task_complete
            trigger_values = [
                'notification',
                #'task_create_all',
                #'note_insert',
                #'task_complete',
                #'version_delete',
                'python_script',
                'python_class',
            ]


        #trigger_edit = TaskCompleteTestWdg()
        #trigger_values.append(trigger_edit.get_trigger_type())







        tr, td = table.add_row_cell()

        title = DivWdg()
        td.add(title)
        title.add("Action:")
        title.add_style("font-weight: bold")
        title.add_style("padding: 20 0 0 0")

        table.add_row()
        #td = table.add_cell()
        tr, td = table.add_row_cell()
        td.add_color("color", "color")
        td.add_color("background", "background", -5)
        td.add_style("padding: 10px")
        td.add_border()
        if trigger_type != 'notification':
            
            td.add("Do the following: ")
            
            #td = table.add_cell()
            # Action Select
            select = SelectWdg("trigger")
            select.set_option("labels", trigger_labels)
            select.set_option("values", trigger_values)
            if trigger_type:
                select.set_value(trigger_type)

            if isinstance(trigger, Notification):
                select.set_value("notification")
            elif trigger and trigger.get_value("script_path"):
                select.set_value("python_script")


            select.add_empty_option("-- Choose action --")
            td.add(select)
            trigger_sk = ''
            if trigger:
                trigger_sk = trigger.get_search_key()
            select.add_behavior( {
            'type': 'change',
            'kwargs': {
                'pipeline_code': my.pipeline_code,
                'process': my.process,
                'search_key': trigger_sk
            },
            'cbjs_action': '''
            var top = bvr.src_el.getParent(".spt_trigger_detail_top");
            var content = top.getElement(".spt_trigger_add_top");
            var event_el = top.getElement(".spt_trigger_event");

            var event = event_el.value;
            bvr.kwargs['event'] = event;
             
            var trigger_type = bvr.src_el.value;
            var trigger_wdg;
            var trigger_cbk;
            if (trigger_type == "notification") {
                trigger_wdg = 'tactic.ui.tools.NotificationTriggerEditWdg';
                trigger_cbk = 'tactic.ui.tools.NotificationTriggerEditCbk';
            }
            else if (trigger_type == "task_status") {
                trigger_wdg = 'tactic.ui.tools.StatusTriggerEditWdg';
                trigger_cbk = 'tactic.ui.tools.StatusTriggerEditCbk';
            }
            else if (trigger_type == "task_create") {
                trigger_wdg = 'tactic.ui.tools.TriggerCreateWdg';
                trigger_cbk = 'tactic.ui.tools.TriggerCreateCbk';
            }
            else if (trigger_type == "task_complete") {
                trigger_wdg = 'tactic.ui.tools.TriggerCompleteWdg';
                trigger_cbk = 'tactic.ui.tools.TriggerCompleteCbk';
            }
            else if (trigger_type == "task_date") {
                trigger_wdg = 'tactic.ui.tools.TriggerDateWdg';
                trigger_cbk = 'tactic.ui.tools.TriggerDateCbk';
            }
            else if (trigger_type == "python_script") {
                trigger_wdg = 'tactic.ui.tools.PythonScriptTriggerEditWdg';
                trigger_cbk = 'tactic.ui.tools.PythonScriptTriggerEditCbk';
            }
            else if (trigger_type == "python_class") {
                trigger_wdg = 'tactic.ui.tools.PythonClassTriggerEditWdg';
                trigger_cbk = 'tactic.ui.tools.PythonClassTriggerEditCbk';
            }
            else {
                spt.behavior.replace_inner_html(content, "");
                content.setAttribute("spt_trigger_add_cbk", "");
                return;
            }

            spt.panel.load(content, trigger_wdg, bvr.kwargs)
            content.setAttribute("spt_trigger_add_cbk", trigger_cbk);

            '''
            } )

            td.add(HtmlElement.br(2))

        trigger_div = DivWdg()
        td.add(trigger_div)
        trigger_div.add_class("spt_trigger_add_top")
        #trigger_div.add_style("padding: 5px")
        #trigger_div.add_border()
        #trigger_div.add_color("background", "background", -5)
        #trigger_div.add_style("margin: 10px")

        kwargs = my.kwargs.copy()
        kwargs['trigger'] = trigger


        # determine detail widget to display
        if trigger_type == "notification":
            trigger_wdg = NotificationTriggerEditWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.NotificationTriggerEditCbk"

        elif trigger_type == "task_status":
            trigger_wdg = StatusTriggerEditWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.StatusTriggerEditCbk"

        elif trigger_type == "task_create":
            trigger_wdg = TriggerCreateWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.TriggerCreateCbk"

        elif trigger_type == "task_date":
            trigger_wdg = TriggerDateWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.TriggerDateCbk"

        elif trigger_type == "python_script":
            trigger_wdg = PythonScriptTriggerEditWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.PythonScriptTriggerEditCbk"

        elif trigger_type == "python_class":
            trigger_wdg = PythonClassTriggerEditWdg(**kwargs)
            trigger_cbk = "tactic.ui.tools.PythonClassTriggerEditCbk"

        else:
            trigger_wdg = DivWdg()
            #trigger_wdg.add("<i>-- No Details --</i>")
            trigger_cbk = ''

        trigger_div.add_attr("spt_trigger_add_cbk", trigger_cbk)
        trigger_div.add_attr("spt_class_name", Common.get_full_class_name(trigger_wdg))
        if trigger:
            trigger_div.add_attr("spt_search_key", trigger.get_search_key())
        trigger_div.add(trigger_wdg)


        div.add("<br/><hr/>")


        # FIXME: this does not delete the python script
        delete_button = ActionButtonWdg(title='Remove', tip='Remove the current trigger')
        div.add(delete_button)
        delete_button.add_style("float: left")
        delete_button.add_behavior( {
        'type': 'click_up',
        'search_key': search_key,
        'cbjs_action': '''
        if (! confirm( "Are you sure you wish to delete this trigger") ) {
            return;
        }
        var server = TacticServerStub.get();
        server.delete_sobject(bvr.search_key);

        var top = bvr.src_el.getParent(".spt_trigger_top");
        spt.panel.refresh(top);
        '''
        } )


        button = ActionButtonWdg(title='Save')
        button.add_style("float: right")
        div.add(button)
       
        button.add_behavior(button_bvr)



        #from tactic.ui.panel import EditWdg
        #edit = EditWdg(search_type='config/trigger')
        #edit.set_sobject(trigger)
        #div.add(edit)


        return div




    def get_trigger_context_menu(my):
        menu = Menu(width=180)
        menu.set_allow_icons(False)
        menu.set_setup_cbfn( 'spt.dg_table.smenu_ctx.setup_cbk' )


        menu_item = MenuItem(type='title', label='Actions')
        menu.add(menu_item)

        menu_item = MenuItem(type='action', label='Delete')
        menu_item.add_behavior( {
            'cbjs_action': '''
            var activator = spt.smenu.get_activator(bvr);
            '''
        } )
        menu.add(menu_item)

        return menu





class BaseTriggerEditWdg(BaseRefreshWdg):

    def get_trigger_type(my):
        pass

    def get_display_class(my):
        return Common.get_full_class_name(my)

    def get_callback_class(my):
        pass

    def get_display(my):
        return "No Display Defined"



class TaskCompleteTestWdg(BaseTriggerEditWdg):

    def get_trigger_type(my):
        return "task_complete"

    #def get_display_class(my):
    #    return 'tactic.ui.tools.TriggerCompleteWdg'

    def get_callback_class(my):
        return 'tactic.ui.tools.TriggerCompleteCbk'

    def get_display(my):
        return "TaskCompleteTestWdg"




class EventTriggerEditWdg(BaseRefreshWdg):
    ''' This class displays options for a chosen event'''
    def get_display(my):
        top = DivWdg()

        process = my.kwargs.get("process")
        pipeline_code = my.kwargs.get("pipeline_code")
        search_type = my.kwargs.get("search_type")

        if search_type:
            search = Search("sthpw/pipeline")
            search.add_filter("search_type", search_type)
            if pipeline_code:
                search.add_filter('code', pipeline_code)
            pipeline = search.get_sobject()
        else:
            pipeline = Pipeline.get_by_code(pipeline_code)

            if not pipeline:
                top.add("<br/>The pipeline [%s] does not exist.  If this pipeline is on your canvas, please save first.<br/>" % pipeline_code)
                return top


        event = my.kwargs.get("event")
        if not event:
            return top



        trigger = my.kwargs.get("trigger")
        if trigger:
            data = trigger.get_value("data", no_exception=True)
            if data:
                try:
                    data = jsonloads(data)
                except:
                    data = {}
                if not isinstance(data, list):
                    data = [data]
            else:
                data = []
        else:
            data = []

        if event == '__custom__':
            top.add("Event name: &nbsp;&nbsp;")
            text = TextWdg("custom_event")
            top.add(text)

            custom_event = my.kwargs.get("custom_event")
            if custom_event:
                text.set_value(custom_event)

            text.add_style("width: 200px")
            top.add_style("padding: 10px")


        #elif event.startswith("change|sthpw/task|status") and process:
        elif event.startswith("change|sthpw/task|status"):

            if process:
                process_obj = pipeline.get_process(process)
                if not process_obj:
                    top.add("<br/>The process item [%s] does not exist. You may need to save your pipeline first.<br/>" % process)
                    return top

                task_pipeline_code = process_obj.get_task_pipeline()
                task_pipeline = Pipeline.get_by_code(task_pipeline_code)
                if not task_pipeline:
                    task_pipeline_code='task'
                    task_pipeline = Pipeline.get_by_code(task_pipeline_code)
                task_statuses = task_pipeline.get_processes()
            elif search_type:
                # get all the processes for this search type
                pipelines = Pipeline.get_by_search_type(search_type)

                task_pipeline_code = "task"
                task_pipeline = Pipeline.get_by_code(task_pipeline_code)
                task_statuses = task_pipeline.get_processes()


                #outputs = pipeline.get_output_processes(process)
                #outputs = [x.get_name() for x in outputs]
                #inputs = pipeline.get_input_processes(process)
                #inputs = [x.get_name() for x in inputs]



            top.add("to: ")

            status = SelectWdg("src_status")
            status.set_option("values", task_statuses)
            
            for item in data:
                src_status = item.get("src_status")
                if src_status:
                    status.set_value(src_status)
                elif task_statuses:
                    status.set_value(task_statuses[-1])
                break

            top.add(status)
            top.add("<br/>")
            top.add_style("padding: 10px")

        elif event.startswith("checkin|") and not process:
            # search_type mode does not have a process
            if trigger:
                if isinstance(trigger, Notification):
                    listen_process = trigger.get_value("process", no_exception=True)
                else:
                    listen_process = trigger.get_value("listen_process", no_exception=True)
            
            else:
                listen_process = None

            inputs = pipeline.get_input_processes(process)
            inputs = [str(x) for x in inputs]
            
            """  
            top.add("In the following process: <br/><br/>")
            checkbox = CheckboxWdg("this_process")
            process_div.add(checkbox)
            if not listen_process or listen_process == process:
                checkbox.set_checked()
            process_div.add(" this process")

            process_div.add("&nbsp;"*3 + " or " + "&nbsp;"*3)

            """
            span = SpanWdg('to')
            span.add_style('padding: 8px')

            process_div = DivWdg(span)
            process_div.add_style("margin-left: 6px")
            top.add(process_div)
            
            select = SelectWdg("listen_process")
            process_div.add(select)
            select.set_option("values", inputs)
            select.set_option("labels", inputs)
            select.add_empty_option("-- Select a Process --")
            
            if listen_process:
                select.set_value(listen_process)

            top.add_style("padding: 10px")


        return top






class StatusTriggerEditWdg(BaseRefreshWdg):

    def get_display(my):
        trigger = my.kwargs.get("trigger")
        if trigger:
            my.data = trigger.get_value("data")
            if not my.data:
                my.data = {}
            else:
                my.data = jsonloads(my.data)
            if isinstance(my.data, dict):
{'fraction_non_alphanumeric': 0.11661285045731924, 'fraction_numerical': 0.003613035376452682, 'mean_word_length': 1.9086470831609432, 'pattern_counts': {'":': 12, '<': 127, '<?xml version=': 0, '>': 127, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6940965', 'n_tokens_mistral': 21414, 'n_tokens_neox': 20274, 'n_words': 3681}