Example #1
0
def getContent(req, ids):

    reload(su)

    language = lang(req)
    user = users.getUserFromRequest(req)
    username = user.getName()
    #access = acl.AccessData(user=user)
    access = acl.AccessData(req=req)

    if "schedule" in users.getHideMenusForUser(user):
        req.setStatus(httpstatus.HTTP_FORBIDDEN)
        return req.getTAL("web/edit/edit.html", {}, macro="access_error")

    errors = []
    error = ''

    if "action" in req.params.keys():
        action = req.params.get("action")
        if action.startswith("get_fields_for_"):
            schedule_func = req.params.get(
                "action").replace("get_fields_for_", "")
            fields = su.fc_dict[schedule_func].getMetafields(lang(req))
            fieldDicts = su.fc_dict[schedule_func].getMetafieldDicts(lang(req))

            field_errors = []
            for i, field in enumerate(fields):
                field_errors.append(False)

            d = {
                'fields': fields,
                'fieldDicts': fieldDicts,
                'currentField': fields[0],
                'field_errors': field_errors,
                'currentFunction': schedule_func,
                'explain_func': su.fc_dict[schedule_func].getExplanation(language)
            }

            req.writeTAL("web/edit/modules/schedule.html", d,
                         macro="schedule_func_show_fields_and_explanation")
            return ""

        elif action.startswith("load_schedule_"):
            schedule_id = action.replace("load_schedule_", "")
            try:
                schedule = tree.getNode(schedule_id)
            except:
                errors.append("edit_schedule_no_such_node_error")
                return "no such schedule error"

            if not schedule.type == "schedule" and access and access.hasWriteAccess(schedule):
                return "schedule access error"

            schedule_func = schedule.get("function")
            if schedule_func:
                fields = su.fc_dict[schedule_func].getMetafields(lang(req))
                fieldDicts = su.fc_dict[
                    schedule_func].getMetafieldDicts(lang(req))
                d['explain_func'] = su.fc_dict[
                    schedule_func].getExplanation(language)
            else:
                fields = []
                fieldDicts = []
                d['explain_func'] = ""

            field_errors = []
            for i, field in enumerate(fields):
                field_errors.append(False)

            has_evaluation_errors = False
            for i, dfield in enumerate(fieldDicts):
                field_name = dfield['field_name']
                field_value = schedule.get(field_name)
                dfield['value'] = field_value
                field_validator_func = dfield['field_validator_func']
                if field_validator_func and not field_validator_func(field_value):
                    dfield['evaluation_error'] = True
                    has_evaluation_errors = True
                else:
                    dfield['evaluation_error'] = False

            if has_evaluation_errors:
                error = "\n<br/>\n".join([error, t(language,
                                                   'edit_schedule_field_validation_error')])

            d = {
                'fields': fields,
                'fieldDicts': fieldDicts,
                'currentField': fields[0],
                'field_errors': field_errors,
                'currentFunction': schedule_func,
            }

            req.writeTAL("web/edit/modules/schedule.html", d,
                         macro="schedule_func_show_fields_and_explanation")
            return ""

        elif action == "delete_node_from_schedule":

            node_id = req.params.get('node_id', None)

            if node_id:
                pass
            else:
                errors.append("edit_schedule_unexpected_no_such_node")

            schedule_id = req.params.get('schedule_id', None)
            if schedule_id:
                try:
                    schedule = tree.getNode(schedule_id)
                except:
                    errors.append(
                        "edit_schedule_unexpected_no_such_schedule_node")
            else:
                errors.append("edit_schedule_unexpected_no_such_schedule_node")

            delete_errors = su.deleteNodeIDsFromSchedule(
                [node_id], schedule_id, access=access)

            if not delete_errors:
                msg = "user '%s' removed node %s from schedule '%s' (%s)" % (
                    username, node_id, schedule.name, schedule_id)
                logging.getLogger("backend").info(msg)
            else:
                error_msg = ", ".join([t(language, e) for e in delete_errors])
                msg = "user '%s' tried to remove node %s from schedule '%s' (%s): %s" % (
                    username, node_id, schedule.name, schedule_id, error_msg)
                logging.getLogger("backend").error(msg)

            errors += delete_errors

            s = {}
            s['errors'] = errors
            s['delete_errors'] = delete_errors

            s['delete_table_rows'] = ['sid_%s' % schedule_id]

            res_msg = req.params.get(
                "jsoncallback") + "(%s)" % json.dumps(s, indent=4)
            req.write(res_msg)
            return ""

        elif action == "delete_schedule":
            schedule_id = req.params.get('schedule_id', None)
            if schedule_id:
                try:
                    schedule = tree.getNode(schedule_id)
                except:
                    errors.append(
                        "edit_schedule_unexpected_no_such_schedule_node")
            else:
                errors.append("edit_schedule_unexpected_no_such_schedule_node")

            delete_errors = su.deleteSchedule(schedule_id, access=access)

            if not delete_errors:
                msg = "user '%s' removed schedule %s (%s)" % (
                    username, schedule.name, schedule_id)
                logging.getLogger("backend").info(msg)
            else:
                error_msg = ", ".join([t(language, e) for e in delete_errors])
                msg = "user '%s' tried to remove schedule '%s' (%s): %s" % (
                    username, schedule.name, schedule_id, error_msg)
                logging.getLogger("backend").error(msg)

            errors += delete_errors

            s = {}
            s['errors'] = errors
            s['delete_errors'] = delete_errors
            s['delete_table_rows'] = ['sid_%s' % schedule_id]

            res_msg = req.params.get(
                "jsoncallback") + "(%s)" % json.dumps(s, indent=4)
            req.write(res_msg)
            return ""

        elif action == "load_table_nid2schedules":

            nid2schedules, schedule2nids, nid2active_schedules = su.getSchedulesForIds(
                ids, access=access, language=language)

            nid2schedules_attrs = {}
            for nid in nid2schedules:
                nid2schedules_attrs[nid] = [
                    [s.id, s.name, s.get("single_trigger")] for s in nid2schedules[nid]['schedule_list']]

            d = {}
            d['nid2schedules'] = nid2schedules
            d['nid2active_schedules'] = nid2active_schedules
            d['errors'] = ['testerror1', 'testerror1']
            d['date'] = datetime.now().isoformat()
            d['isActive'] = su.isActive

            req.writeTAL(
                "web/edit/modules/schedule.html", d, macro="table_nid2schedules")
            return ""

        elif action == "load_table_schedule2nids":

            nid2schedules, schedule2nids, nid2active_schedules = su.getSchedulesForIds(
                ids, access=access, language=language)

            nid2schedules_attrs = {}
            for nid in nid2schedules:
                nid2schedules_attrs[nid] = [
                    [s.id, s.name, s.get("single_trigger")] for s in nid2schedules[nid]['schedule_list']]

            d = {}
            d['nid2schedules'] = nid2schedules
            d['schedule2nids'] = schedule2nids
            d['nid2active_schedules'] = nid2active_schedules
            d['errors'] = ['testerror1', 'testerror1']
            d['date'] = datetime.now().isoformat()
            d['isActive'] = su.isActive

            req.writeTAL(
                "web/edit/modules/schedule.html", d, macro="table_schedule2nids")
            return ""

    nid2schedules, schedule2nids, nid2active_schedules = su.getSchedulesForIds(
        ids, access=access, language=language)

    datetime_str = req.params.get("datetime", "").strip()
    datetime_error = False
    has_evaluation_errors = False

    if datetime_str:
        patter = "^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$"
        if not re.match(patter, datetime_str):
            error = t(
                language, "edit_schedule_input_datetime_format_error") % datetime_str
            datetime_error = True
        else:
            try:
                parser_test_result = datetime.strptime(
                    datetime_str, "%Y-%m-%dT%H:%M")  # "%Y-%m-%dT%H:%M:%S.%f"
            except:
                parser_test_result = None
                error = t(
                    language, "edit_schedule_input_datetime_format_error") % datetime_str
                datetime_error = True
            if parser_test_result and datetime_str <= datetime.now().isoformat():
                error = t(
                    language, "edit_schedule_input_datetime_already_past_error") % datetime_str
                datetime_error = True
    elif "submit" in req.params:
        error = t(
            language, "edit_schedule_input_datetime_format_error") % datetime_str
        datetime_error = True

    schedule_id = req.params.get("schedule_id", "")
    if schedule_id:
        schedule = tree.getNode(schedule_id)
        if access and not access.hasWriteAccess(schedule):
            errors.append("edit_schedule_no_access_error")
            schedule = None
            current_function = req.params.get('schedule_function', '')
        else:
            current_function = schedule.get("function")
    else:
        schedule = None
        current_function = req.params.get('schedule_function', '')

    if "submit" in req.params or "submit_run_now" in req.params:
        current_function = req.params.get('schedule_function', '')
        if current_function in su.fc_dict:
            field_descriptors = su.fc_dict[
                current_function].getFieldDescriptors()
            field_errors = []
        else:
            # should not happen
            msg = "-> unexpected error: 'non-existant schedule function' requested in module %s: :s" % (
                str(__file__), str(inspect.currentframe().f_lineno))
            print msg
            logging.getLogger("backend").error(msg)

    d = {
        "id": req.params.get("id")
    }

    # nodes list is used to display icons in upper part of page
    # additional nodes should not / will not be shown there
    nodes = []
    for nid in ids:
        node = tree.getNode(nid)
        nodes.append(node)

    d['nodes'] = nodes

    if current_function in su.fc_dict:
        fields = su.fc_dict[current_function].getMetafields(lang(req))
        fieldDicts = su.fc_dict[current_function].getMetafieldDicts(lang(req))
        d['explain_func'] = su.fc_dict[
            current_function].getExplanation(language)
    else:
        fields = []
        fieldDicts = []
        d['explain_func'] = ""

    if not "submit_run_now" in req.params:
        has_evaluation_errors = datetime_error

    if schedule and not "submit" in req.params and not "submit_run_now" in req.params:
        for i, dfield in enumerate(fieldDicts):
            field_name = dfield['field_name']
            field_value = schedule.get(field_name)
            dfield['value'] = field_value
            field_validator_func = dfield['field_validator_func']
            if field_validator_func and not field_validator_func(field_value):
                dfield['evaluation_error'] = True
                has_evaluation_errors = True
            else:
                dfield['evaluation_error'] = False
    else:
        for i, dfield in enumerate(fieldDicts):
            field_name = dfield['field_name']
            # m_* classes from metadata/ are not multilingual for schedules
            # their getEditorHTML methods are used to display schedule node
            # attributes
            field_value = req.params.get(
                getDefaultLanguage() + '__' + field_name, '')
            dfield['value'] = field_value
            field_validator_func = dfield['field_validator_func']
            if field_validator_func and not field_validator_func(field_value):
                dfield['evaluation_error'] = True
                has_evaluation_errors = True
            else:
                dfield['evaluation_error'] = False

    additional_nodes_str = req.params.get("additional_nodes", "")
    additional_nodes_error = False
    additional_nodes_bad_ids = []

    try:
        additional_nodes_id_list = [
            str(int(nid.strip())) for nid in additional_nodes_str.strip().split(";") if nid.strip()]
    except:
        has_evaluation_errors = True
        additional_nodes_error = True
        additional_nodes_id_list = []
        errors.append(
            "edit_schedule_additional_nodes_list_not_semikolon_separated_list_of_integers")

    if not additional_nodes_error:
        collections_root = tree.getRoot("collections")
        for nid in additional_nodes_id_list:
            n = None
            try:
                n = tree.getNode(nid)
            except tree.NoSuchNodeError as e:
                has_evaluation_errors = True
                additional_nodes_error = True
                if nid not in additional_nodes_bad_ids:
                    additional_nodes_bad_ids.append(nid)
            if n and (not isDescendantOf(n, collections_root) or not access.hasWriteAccess(n)):
                # to do? discussion: override collections rectriction for
                # admins ?
                has_evaluation_errors = True
                additional_nodes_error = True
                if nid not in additional_nodes_bad_ids:
                    additional_nodes_bad_ids.append(nid)

    if additional_nodes_bad_ids:
        has_evaluation_errors = True
        additional_nodes_error = True
        errors.append(t(language, "edit_schedule_additional_nodes_bad_ids") +
                      (";".join(additional_nodes_bad_ids)))

    if has_evaluation_errors:
        errors.append('edit_schedule_field_validation_error')
        error = "\n<br/>\n".join(map(lambda x:
                                     t(language, x), [error] + errors))

    d['fields'] = fields
    d['fieldDicts'] = fieldDicts
    d['field_errors'] = [False] * len(fields)
    d['currentField'] = None
    d['currentFunction'] = current_function

    d['error'] = error

    d['fc_dict'] = su.filter_access(su.fc_dict, access)

    d['input_datetime'] = datetime_str
    d['input_datetime_error'] = datetime_error

    d['nid2schedules'] = nid2schedules
    d['schedule2nids'] = schedule2nids
    d['nid2active_schedules'] = nid2active_schedules

    d['loaded_schedule'] = schedule

    if schedule:
        d['loaded_schedule_id'] = str(schedule.id)
    else:
        d['loaded_schedule_id'] = None

    d['mklink'] = su.mklink

    d['language'] = language
    d['t'] = t
    d['isActive'] = su.isActive

    d['result'] = ''

    d['created_new_schedule'] = False

    d['additional_nodes'] = additional_nodes_str
    d['additional_nodes_error'] = additional_nodes_error

    d['submitbutton_run_now_label'] = t(
        language, 'edit_schedule_submit_run_now_button')
    d['edit_schedule_submit_run_now_button_confirm'] = t(
        language, 'edit_schedule_submit_run_now_button_confirm')
    d['edit_schedule_delete_schedule_confirm'] = t(
        language, 'edit_schedule_delete_schedule_confirm')

    if has_evaluation_errors and not ('submit_run_now' in req.params):
        return req.getTAL("web/edit/modules/schedule.html", d, macro="schedule_popup")

    new_schedule = None
    if (not schedule and "submit" in req.params) or "submit_run_now" in req.params:

        new_schedule_name = user.name + \
            "_created_at_" + datetime.now().isoformat()
        new_schedule = tree.Node(new_schedule_name, 'schedule')

        username = user.getName()
        new_schedule.setAccess("write", "{user %s}" % username)

        if not "submit_run_now" in req.params:
            schedules = tree.getRoot("schedules")
            schedules.addChild(new_schedule)

            msg = "user '%s' created new schedule '%s' (%s), trigger='%s', function='%s', nodelist='%s'" % (
                username, new_schedule.name, str(new_schedule.id), datetime_str, d['currentFunction'], new_schedule.get('nodelist'))
            logging.getLogger("backend").info(msg)

            d['result'] = t(
                language, 'edit_schedule_result_new_schedule_created')
            d['created_new_schedule'] = True
        else:
            msg = "user '%s' created temporary schedule '%s' (%s), trigger='%s', function='%s', nodelist='%s'" % (
                username, new_schedule.name, str(new_schedule.id), datetime_str, d['currentFunction'], new_schedule.get('nodelist'))
            logging.getLogger("backend").info(msg)

            d['result'] = t(
                language, 'edit_schedule_result_temporary_schedule_created')
            d['created_temporary_schedule'] = True

    elif (schedule) and ("submit" in req.params):
        new_schedule = schedule
        msg = "user '%s' is editing schedule '%s' (%s), trigger='%s', function='%s', nodelist='%s'" % (
            username, new_schedule.name, str(new_schedule.id), datetime_str, d['currentFunction'], new_schedule.get('nodelist'))
        logging.getLogger("backend").info(msg)
        new_schedule.set("system.edited", datetime.now().isoformat())

        d['result'] = t(
            language, 'edit_schedule_result_existing_schedule_edited')

    if new_schedule:
        for i, dfield in enumerate(fieldDicts):
            field_name = dfield['field_name']
            field_value = dfield['value']
            new_schedule.set(field_name, field_value)

        ids_plus_additional_nodes = ids
        for nid in additional_nodes_id_list:
            if nid not in ids_plus_additional_nodes:
                ids_plus_additional_nodes.append(nid)

        new_schedule.set('function', d['currentFunction'])
        new_schedule.set('nodelist', ",".join(ids_plus_additional_nodes))
        new_schedule.set('single_trigger', datetime_str)

        if datetime.now().isoformat() < datetime_str:
            new_schedule.set('single_trigger_status', '')

        if "submit_run_now" in req.params:
            new_schedule.set("single_trigger", datetime.now().isoformat())
            has_fired, has_error, TT = su.handle_single_trigger(
                new_schedule, datetime.now().isoformat(), su.OUT)
            if has_error:
                _error1 = d['error']
                _error2 = "<br/>\n".join(map(lambda x:
                                             (t(language, str(x))), TT))
                _error = "<br/>\n".join([_error1, _error2])
                d['error'] = _error

    return req.getTAL("web/edit/modules/schedule.html", d, macro="schedule_popup")
Example #2
0
def scheduler_thread():
    global count
    import utils.scheduleutils as scheduleutils
    if not time:
        return
    ensureSchedulesRoot()
    TRIGGER_COUNT = 0

    while True:

        count += 1
        TT = []
        HAS_FIRED = HAS_ERROR = False
        atime0 = atime = time.time()
        now_obj = datetime.now()
        now_str = now_obj.isoformat()  # example: '2012-05-29T13:15:17.557000'

        if athana.ATHANA_STARTED:

            if count % scheduleutils.TRIGGER_INTERVAL == 0:

                TRIGGER_COUNT += 1

                sched_root = tree.getRoot("schedules")
                try:
                    tree.remove_from_nodecaches(sched_root)
                    msg = "flushed schedules"
                    TT.append([msg, time.time() - atime])
                    atime = time.time()
                except TypeError:
                    msg = "scheduler thread failed to remove schedules root from cache"
                    OUT(msg, logger='backend', level='warning')

                sched_list = [
                    c for c in sched_root.getChildren() if c.type == 'schedule'
                ]
                # to do: sort?

                msg = "%d schedule(s) found" % len(sched_list)
                TT.append([msg, time.time() - atime])
                atime = time.time()
                SCHEDULES_IMPORT_ERROR = False

                try:
                    reload(scheduleutils)
                    msg = "reloaded module utils.scheduleutils"
                    TT.append([msg, time.time() - atime])
                    atime = time.time()
                except:
                    msg = "Error reloading module 'scheduleutils': %s %s" % (
                        str(sys.exc_info()[0]), str(sys.exc_info()[1]))
                    TT.append([msg, time.time() - atime])
                    atime = time.time()
                    OUT(msg,
                        logger='backend',
                        print_stdout=True,
                        level='error')
                    SCHEDULES_IMPORT_ERROR = True

                if not SCHEDULES_IMPORT_ERROR:
                    interval_seconds = scheduleutils.SLEEP_INTERVAL * scheduleutils.TRIGGER_INTERVAL
                    OUT("scheduler_thread (interval: %s sec.): %s" %
                        (str(interval_seconds), now_str))
                    now_obj = datetime.now()
                    now_str = now_obj.isoformat()

                    for s in sched_list:

                        has_fired, has_error, tt = scheduleutils.handle_single_trigger(
                            s, now_str, OUT)
                        TT = TT + tt
                        atime = time.time()
                        if has_fired:
                            HAS_FIRED = True
                        if has_error:
                            HAS_ERROR = True
                        try:
                            has_fired, has_error, tt = scheduleutils.handle_cron_dict(
                                s, now_obj, OUT)
                        except:
                            has_fired, has_error, tt = False, True, []
                        TT = TT + tt
                        atime = time.time()
                        if has_fired:
                            HAS_FIRED = True
                        if has_error:
                            HAS_ERROR = True
                    sys.stdout.flush()

        else:
            msg = 'scheduler: check no: %d: athana not yet started' % (count)
            OUT(msg, print_stdout=False)

        def OUT_TT(TT):
            msg = "scheduler: timetable (scheduleutils.DEBUG = '%s')\n|" % (
                scheduleutils.DEBUG) + '-' * 60 + '\n'
            msg += '| check no %d at %s:\n' % (TRIGGER_COUNT, now_str)
            for s, t in TT:
                msg = msg + '| %.3f: %s\n' % (t, str(s))
            msg += '| duration: %.3f sec.\n' % (time.time() - atime0)
            msg += '|' + '-' * 60

            OUT(msg)

        if scheduleutils.DEBUG:
            if TT:
                OUT_TT(TT)
        else:
            if (HAS_FIRED or HAS_ERROR) and TT:
                OUT_TT(TT)

        correction = int((time.time() - atime0) / scheduleutils.SLEEP_INTERVAL)
        count += correction

        time.sleep(scheduleutils.SLEEP_INTERVAL)
Example #3
0
def scheduler_thread():
    global count
    import utils.scheduleutils as scheduleutils
    if not time:
        return
    ensureSchedulesRoot()
    TRIGGER_COUNT = 0

    while True:

        count += 1
        TT = []
        HAS_FIRED = HAS_ERROR = False
        atime0 = atime = time.time()
        now_obj = datetime.now()
        now_str = now_obj.isoformat()  # example: '2012-05-29T13:15:17.557000'

        if athana.ATHANA_STARTED:

            if count % scheduleutils.TRIGGER_INTERVAL == 0:

                TRIGGER_COUNT += 1

                sched_root = tree.getRoot("schedules")
                try:
                    tree.remove_from_nodecaches(sched_root)
                    msg = "flushed schedules"
                    TT.append([msg, time.time() - atime])
                    atime = time.time()
                except TypeError:
                    msg = "scheduler thread failed to remove schedules root from cache"
                    OUT(msg, logger='backend', level='warning')

                sched_list = [c for c in sched_root.getChildren() if c.type == 'schedule']
                # to do: sort?

                msg = "%d schedule(s) found" % len(sched_list)
                TT.append([msg, time.time() - atime])
                atime = time.time()
                SCHEDULES_IMPORT_ERROR = False

                try:
                    reload(scheduleutils)
                    msg = "reloaded module utils.scheduleutils"
                    TT.append([msg, time.time() - atime])
                    atime = time.time()
                except:
                    msg = "Error reloading module 'scheduleutils': %s %s" % (str(sys.exc_info()[0]), str(sys.exc_info()[1]))
                    TT.append([msg, time.time() - atime])
                    atime = time.time()
                    OUT(msg, logger='backend', print_stdout=True, level='error')
                    SCHEDULES_IMPORT_ERROR = True

                if not SCHEDULES_IMPORT_ERROR:
                    interval_seconds = scheduleutils.SLEEP_INTERVAL * scheduleutils.TRIGGER_INTERVAL
                    OUT("scheduler_thread (interval: %s sec.): %s" % (str(interval_seconds), now_str))
                    now_obj = datetime.now()
                    now_str = now_obj.isoformat()

                    for s in sched_list:

                        has_fired, has_error, tt = scheduleutils.handle_single_trigger(s, now_str, OUT)
                        TT = TT + tt
                        atime = time.time()
                        if has_fired:
                            HAS_FIRED = True
                        if has_error:
                            HAS_ERROR = True
                        try:
                            has_fired, has_error, tt = scheduleutils.handle_cron_dict(s, now_obj, OUT)
                        except:
                            has_fired, has_error, tt = False, True, []
                        TT = TT + tt
                        atime = time.time()
                        if has_fired:
                            HAS_FIRED = True
                        if has_error:
                            HAS_ERROR = True
                    sys.stdout.flush()

        else:
            msg = 'scheduler: check no: %d: athana not yet started' % (count)
            OUT(msg, print_stdout=False)

        def OUT_TT(TT):
            msg = "scheduler: timetable (scheduleutils.DEBUG = '%s')\n|" % (scheduleutils.DEBUG) + '-' * 60 + '\n'
            msg += '| check no %d at %s:\n' % (TRIGGER_COUNT, now_str)
            for s, t in TT:
                msg = msg + '| %.3f: %s\n' % (t, str(s))
            msg += '| duration: %.3f sec.\n' % (time.time() - atime0)
            msg += '|' + '-' * 60

            OUT(msg)

        if scheduleutils.DEBUG:
            if TT:
                OUT_TT(TT)
        else:
            if (HAS_FIRED or HAS_ERROR) and TT:
                OUT_TT(TT)

        correction = int((time.time() - atime0) / scheduleutils.SLEEP_INTERVAL)
        count += correction

        time.sleep(scheduleutils.SLEEP_INTERVAL)
Example #4
0
def getContent(req, ids):

    reload(su)

    language = lang(req)
    user = users.getUserFromRequest(req)
    username = user.getName()
    #access = acl.AccessData(user=user)
    access = acl.AccessData(req=req)

    if "schedule" in users.getHideMenusForUser(user):
        req.setStatus(httpstatus.HTTP_FORBIDDEN)
        return req.getTAL("web/edit/edit.html", {}, macro="access_error")

    errors = []
    error = ''

    if "action" in req.params.keys():
        action = req.params.get("action")
        if action.startswith("get_fields_for_"):
            schedule_func = req.params.get("action").replace(
                "get_fields_for_", "")
            fields = su.fc_dict[schedule_func].getMetafields(lang(req))
            fieldDicts = su.fc_dict[schedule_func].getMetafieldDicts(lang(req))

            field_errors = []
            for i, field in enumerate(fields):
                field_errors.append(False)

            d = {
                'fields': fields,
                'fieldDicts': fieldDicts,
                'currentField': fields[0],
                'field_errors': field_errors,
                'currentFunction': schedule_func,
                'explain_func':
                su.fc_dict[schedule_func].getExplanation(language)
            }

            req.writeTAL("web/edit/modules/schedule.html",
                         d,
                         macro="schedule_func_show_fields_and_explanation")
            return ""

        elif action.startswith("load_schedule_"):
            schedule_id = action.replace("load_schedule_", "")
            try:
                schedule = tree.getNode(schedule_id)
            except:
                errors.append("edit_schedule_no_such_node_error")
                return "no such schedule error"

            if not schedule.type == "schedule" and access and access.hasWriteAccess(
                    schedule):
                return "schedule access error"

            schedule_func = schedule.get("function")
            if schedule_func:
                fields = su.fc_dict[schedule_func].getMetafields(lang(req))
                fieldDicts = su.fc_dict[schedule_func].getMetafieldDicts(
                    lang(req))
                d['explain_func'] = su.fc_dict[schedule_func].getExplanation(
                    language)
            else:
                fields = []
                fieldDicts = []
                d['explain_func'] = ""

            field_errors = []
            for i, field in enumerate(fields):
                field_errors.append(False)

            has_evaluation_errors = False
            for i, dfield in enumerate(fieldDicts):
                field_name = dfield['field_name']
                field_value = schedule.get(field_name)
                dfield['value'] = field_value
                field_validator_func = dfield['field_validator_func']
                if field_validator_func and not field_validator_func(
                        field_value):
                    dfield['evaluation_error'] = True
                    has_evaluation_errors = True
                else:
                    dfield['evaluation_error'] = False

            if has_evaluation_errors:
                error = "\n<br/>\n".join([
                    error,
                    t(language, 'edit_schedule_field_validation_error')
                ])

            d = {
                'fields': fields,
                'fieldDicts': fieldDicts,
                'currentField': fields[0],
                'field_errors': field_errors,
                'currentFunction': schedule_func,
            }

            req.writeTAL("web/edit/modules/schedule.html",
                         d,
                         macro="schedule_func_show_fields_and_explanation")
            return ""

        elif action == "delete_node_from_schedule":

            node_id = req.params.get('node_id', None)

            if node_id:
                pass
            else:
                errors.append("edit_schedule_unexpected_no_such_node")

            schedule_id = req.params.get('schedule_id', None)
            if schedule_id:
                try:
                    schedule = tree.getNode(schedule_id)
                except:
                    errors.append(
                        "edit_schedule_unexpected_no_such_schedule_node")
            else:
                errors.append("edit_schedule_unexpected_no_such_schedule_node")

            delete_errors = su.deleteNodeIDsFromSchedule([node_id],
                                                         schedule_id,
                                                         access=access)

            if not delete_errors:
                msg = "user '%s' removed node %s from schedule '%s' (%s)" % (
                    username, node_id, schedule.name, schedule_id)
                logging.getLogger("backend").info(msg)
            else:
                error_msg = ", ".join([t(language, e) for e in delete_errors])
                msg = "user '%s' tried to remove node %s from schedule '%s' (%s): %s" % (
                    username, node_id, schedule.name, schedule_id, error_msg)
                logging.getLogger("backend").error(msg)

            errors += delete_errors

            s = {}
            s['errors'] = errors
            s['delete_errors'] = delete_errors

            s['delete_table_rows'] = ['sid_%s' % schedule_id]

            res_msg = req.params.get("jsoncallback") + "(%s)" % json.dumps(
                s, indent=4)
            req.write(res_msg)
            return ""

        elif action == "delete_schedule":
            schedule_id = req.params.get('schedule_id', None)
            if schedule_id:
                try:
                    schedule = tree.getNode(schedule_id)
                except:
                    errors.append(
                        "edit_schedule_unexpected_no_such_schedule_node")
            else:
                errors.append("edit_schedule_unexpected_no_such_schedule_node")

            delete_errors = su.deleteSchedule(schedule_id, access=access)

            if not delete_errors:
                msg = "user '%s' removed schedule %s (%s)" % (
                    username, schedule.name, schedule_id)
                logging.getLogger("backend").info(msg)
            else:
                error_msg = ", ".join([t(language, e) for e in delete_errors])
                msg = "user '%s' tried to remove schedule '%s' (%s): %s" % (
                    username, schedule.name, schedule_id, error_msg)
                logging.getLogger("backend").error(msg)

            errors += delete_errors

            s = {}
            s['errors'] = errors
            s['delete_errors'] = delete_errors
            s['delete_table_rows'] = ['sid_%s' % schedule_id]

            res_msg = req.params.get("jsoncallback") + "(%s)" % json.dumps(
                s, indent=4)
            req.write(res_msg)
            return ""

        elif action == "load_table_nid2schedules":

            nid2schedules, schedule2nids, nid2active_schedules = su.getSchedulesForIds(
                ids, access=access, language=language)

            nid2schedules_attrs = {}
            for nid in nid2schedules:
                nid2schedules_attrs[nid] = [[
                    s.id, s.name, s.get("single_trigger")
                ] for s in nid2schedules[nid]['schedule_list']]

            d = {}
            d['nid2schedules'] = nid2schedules
            d['nid2active_schedules'] = nid2active_schedules
            d['errors'] = ['testerror1', 'testerror1']
            d['date'] = datetime.now().isoformat()
            d['isActive'] = su.isActive

            req.writeTAL("web/edit/modules/schedule.html",
                         d,
                         macro="table_nid2schedules")
            return ""

        elif action == "load_table_schedule2nids":

            nid2schedules, schedule2nids, nid2active_schedules = su.getSchedulesForIds(
                ids, access=access, language=language)

            nid2schedules_attrs = {}
            for nid in nid2schedules:
                nid2schedules_attrs[nid] = [[
                    s.id, s.name, s.get("single_trigger")
                ] for s in nid2schedules[nid]['schedule_list']]

            d = {}
            d['nid2schedules'] = nid2schedules
            d['schedule2nids'] = schedule2nids
            d['nid2active_schedules'] = nid2active_schedules
            d['errors'] = ['testerror1', 'testerror1']
            d['date'] = datetime.now().isoformat()
            d['isActive'] = su.isActive

            req.writeTAL("web/edit/modules/schedule.html",
                         d,
                         macro="table_schedule2nids")
            return ""

    nid2schedules, schedule2nids, nid2active_schedules = su.getSchedulesForIds(
        ids, access=access, language=language)

    datetime_str = req.params.get("datetime", "").strip()
    datetime_error = False
    has_evaluation_errors = False

    if datetime_str:
        patter = "^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$"
        if not re.match(patter, datetime_str):
            error = t(
                language,
                "edit_schedule_input_datetime_format_error") % datetime_str
            datetime_error = True
        else:
            try:
                parser_test_result = datetime.strptime(
                    datetime_str, "%Y-%m-%dT%H:%M")  # "%Y-%m-%dT%H:%M:%S.%f"
            except:
                parser_test_result = None
                error = t(
                    language,
                    "edit_schedule_input_datetime_format_error") % datetime_str
                datetime_error = True
            if parser_test_result and datetime_str <= datetime.now().isoformat(
            ):
                error = t(language,
                          "edit_schedule_input_datetime_already_past_error"
                          ) % datetime_str
                datetime_error = True
    elif "submit" in req.params:
        error = t(language,
                  "edit_schedule_input_datetime_format_error") % datetime_str
        datetime_error = True

    schedule_id = req.params.get("schedule_id", "")
    if schedule_id:
        schedule = tree.getNode(schedule_id)
        if access and not access.hasWriteAccess(schedule):
            errors.append("edit_schedule_no_access_error")
            schedule = None
            current_function = req.params.get('schedule_function', '')
        else:
            current_function = schedule.get("function")
    else:
        schedule = None
        current_function = req.params.get('schedule_function', '')

    if "submit" in req.params or "submit_run_now" in req.params:
        current_function = req.params.get('schedule_function', '')
        if current_function in su.fc_dict:
            field_descriptors = su.fc_dict[
                current_function].getFieldDescriptors()
            field_errors = []
        else:
            # should not happen
            msg = "-> unexpected error: 'non-existant schedule function' requested in module %s: :s" % (
                str(__file__), str(inspect.currentframe().f_lineno))
            print msg
            logging.getLogger("backend").error(msg)

    d = {"id": req.params.get("id")}

    # nodes list is used to display icons in upper part of page
    # additional nodes should not / will not be shown there
    nodes = []
    for nid in ids:
        node = tree.getNode(nid)
        nodes.append(node)

    d['nodes'] = nodes

    if current_function in su.fc_dict:
        fields = su.fc_dict[current_function].getMetafields(lang(req))
        fieldDicts = su.fc_dict[current_function].getMetafieldDicts(lang(req))
        d['explain_func'] = su.fc_dict[current_function].getExplanation(
            language)
    else:
        fields = []
        fieldDicts = []
        d['explain_func'] = ""

    if not "submit_run_now" in req.params:
        has_evaluation_errors = datetime_error

    if schedule and not "submit" in req.params and not "submit_run_now" in req.params:
        for i, dfield in enumerate(fieldDicts):
            field_name = dfield['field_name']
            field_value = schedule.get(field_name)
            dfield['value'] = field_value
            field_validator_func = dfield['field_validator_func']
            if field_validator_func and not field_validator_func(field_value):
                dfield['evaluation_error'] = True
                has_evaluation_errors = True
            else:
                dfield['evaluation_error'] = False
    else:
        for i, dfield in enumerate(fieldDicts):
            field_name = dfield['field_name']
            # m_* classes from metadata/ are not multilingual for schedules
            # their getEditorHTML methods are used to display schedule node
            # attributes
            field_value = req.params.get(
                getDefaultLanguage() + '__' + field_name, '')
            dfield['value'] = field_value
            field_validator_func = dfield['field_validator_func']
            if field_validator_func and not field_validator_func(field_value):
                dfield['evaluation_error'] = True
                has_evaluation_errors = True
            else:
                dfield['evaluation_error'] = False

    additional_nodes_str = req.params.get("additional_nodes", "")
    additional_nodes_error = False
    additional_nodes_bad_ids = []

    try:
        additional_nodes_id_list = [
            str(int(nid.strip()))
            for nid in additional_nodes_str.strip().split(";") if nid.strip()
        ]
    except:
        has_evaluation_errors = True
        additional_nodes_error = True
        additional_nodes_id_list = []
        errors.append(
            "edit_schedule_additional_nodes_list_not_semikolon_separated_list_of_integers"
        )

    if not additional_nodes_error:
        collections_root = tree.getRoot("collections")
        for nid in additional_nodes_id_list:
            n = None
            try:
                n = tree.getNode(nid)
            except tree.NoSuchNodeError as e:
                has_evaluation_errors = True
                additional_nodes_error = True
                if nid not in additional_nodes_bad_ids:
                    additional_nodes_bad_ids.append(nid)
            if n and (not isDescendantOf(n, collections_root)
                      or not access.hasWriteAccess(n)):
                # to do? discussion: override collections rectriction for
                # admins ?
                has_evaluation_errors = True
                additional_nodes_error = True
                if nid not in additional_nodes_bad_ids:
                    additional_nodes_bad_ids.append(nid)

    if additional_nodes_bad_ids:
        has_evaluation_errors = True
        additional_nodes_error = True
        errors.append(
            t(language, "edit_schedule_additional_nodes_bad_ids") +
            (";".join(additional_nodes_bad_ids)))

    if has_evaluation_errors:
        errors.append('edit_schedule_field_validation_error')
        error = "\n<br/>\n".join(
            map(lambda x: t(language, x), [error] + errors))

    d['fields'] = fields
    d['fieldDicts'] = fieldDicts
    d['field_errors'] = [False] * len(fields)
    d['currentField'] = None
    d['currentFunction'] = current_function

    d['error'] = error

    d['fc_dict'] = su.filter_access(su.fc_dict, access)

    d['input_datetime'] = datetime_str
    d['input_datetime_error'] = datetime_error

    d['nid2schedules'] = nid2schedules
    d['schedule2nids'] = schedule2nids
    d['nid2active_schedules'] = nid2active_schedules

    d['loaded_schedule'] = schedule

    if schedule:
        d['loaded_schedule_id'] = str(schedule.id)
    else:
        d['loaded_schedule_id'] = None

    d['mklink'] = su.mklink

    d['language'] = language
    d['t'] = t
    d['isActive'] = su.isActive

    d['result'] = ''

    d['created_new_schedule'] = False

    d['additional_nodes'] = additional_nodes_str
    d['additional_nodes_error'] = additional_nodes_error

    d['submitbutton_run_now_label'] = t(language,
                                        'edit_schedule_submit_run_now_button')
    d['edit_schedule_submit_run_now_button_confirm'] = t(
        language, 'edit_schedule_submit_run_now_button_confirm')
    d['edit_schedule_delete_schedule_confirm'] = t(
        language, 'edit_schedule_delete_schedule_confirm')

    if has_evaluation_errors and not ('submit_run_now' in req.params):
        return req.getTAL("web/edit/modules/schedule.html",
                          d,
                          macro="schedule_popup")

    new_schedule = None
    if (not schedule
            and "submit" in req.params) or "submit_run_now" in req.params:

        new_schedule_name = user.name + \
            "_created_at_" + datetime.now().isoformat()
        new_schedule = tree.Node(new_schedule_name, 'schedule')

        username = user.getName()
        new_schedule.setAccess("write", "{user %s}" % username)

        if not "submit_run_now" in req.params:
            schedules = tree.getRoot("schedules")
            schedules.addChild(new_schedule)

            msg = "user '%s' created new schedule '%s' (%s), trigger='%s', function='%s', nodelist='%s'" % (
                username, new_schedule.name, str(
                    new_schedule.id), datetime_str, d['currentFunction'],
                new_schedule.get('nodelist'))
            logging.getLogger("backend").info(msg)

            d['result'] = t(language,
                            'edit_schedule_result_new_schedule_created')
            d['created_new_schedule'] = True
        else:
            msg = "user '%s' created temporary schedule '%s' (%s), trigger='%s', function='%s', nodelist='%s'" % (
                username, new_schedule.name, str(
                    new_schedule.id), datetime_str, d['currentFunction'],
                new_schedule.get('nodelist'))
            logging.getLogger("backend").info(msg)

            d['result'] = t(language,
                            'edit_schedule_result_temporary_schedule_created')
            d['created_temporary_schedule'] = True

    elif (schedule) and ("submit" in req.params):
        new_schedule = schedule
        msg = "user '%s' is editing schedule '%s' (%s), trigger='%s', function='%s', nodelist='%s'" % (
            username, new_schedule.name, str(new_schedule.id), datetime_str,
            d['currentFunction'], new_schedule.get('nodelist'))
        logging.getLogger("backend").info(msg)
        new_schedule.set("system.edited", datetime.now().isoformat())

        d['result'] = t(language,
                        'edit_schedule_result_existing_schedule_edited')

    if new_schedule:
        for i, dfield in enumerate(fieldDicts):
            field_name = dfield['field_name']
            field_value = dfield['value']
            new_schedule.set(field_name, field_value)

        ids_plus_additional_nodes = ids
        for nid in additional_nodes_id_list:
            if nid not in ids_plus_additional_nodes:
                ids_plus_additional_nodes.append(nid)

        new_schedule.set('function', d['currentFunction'])
        new_schedule.set('nodelist', ",".join(ids_plus_additional_nodes))
        new_schedule.set('single_trigger', datetime_str)

        if datetime.now().isoformat() < datetime_str:
            new_schedule.set('single_trigger_status', '')

        if "submit_run_now" in req.params:
            new_schedule.set("single_trigger", datetime.now().isoformat())
            has_fired, has_error, TT = su.handle_single_trigger(
                new_schedule,
                datetime.now().isoformat(), su.OUT)
            if has_error:
                _error1 = d['error']
                _error2 = "<br/>\n".join(
                    map(lambda x: (t(language, str(x))), TT))
                _error = "<br/>\n".join([_error1, _error2])
                d['error'] = _error

    return req.getTAL("web/edit/modules/schedule.html",
                      d,
                      macro="schedule_popup")