public static function notification_edit_page($form_id, $notification_id)
    {
        if (!rgempty('gform_notification_id')) {
            $notification_id = rgpost('gform_notification_id');
        }
        $form = RGFormsModel::get_form_meta($form_id);
        $form = apply_filters("gform_form_notification_page_{$form_id}", apply_filters('gform_form_notification_page', $form, $notification_id), $notification_id);
        $notification = !$notification_id ? array() : self::get_notification($form, $notification_id);
        // added second condition to account for new notifications with errors as notification ID will
        // be available in $_POST but the notification has not actually been saved yet
        $is_new_notification = empty($notification_id) || empty($notification);
        $is_valid = true;
        $is_update = false;
        if (rgpost('save')) {
            check_admin_referer('gforms_save_notification', 'gforms_save_notification');
            //clear out notification because it could have legacy data populated
            $notification = array('isActive' => isset($notification['isActive']) ? rgar($notification, 'isActive') : true);
            $is_update = true;
            if ($is_new_notification) {
                $notification_id = uniqid();
                $notification['id'] = $notification_id;
            } else {
                $notification['id'] = $notification_id;
            }
            $notification['name'] = sanitize_text_field(rgpost('gform_notification_name'));
            $notification['event'] = sanitize_text_field(rgpost('gform_notification_event'));
            $notification['to'] = rgpost('gform_notification_to_type') == 'field' ? rgpost('gform_notification_to_field') : rgpost('gform_notification_to_email');
            $to_type = rgpost('gform_notification_to_type');
            if (!in_array($to_type, array('email', 'field', 'routing'))) {
                $to_type = 'email';
            }
            $notification['toType'] = $to_type;
            $notification['bcc'] = rgpost('gform_notification_bcc');
            $notification['subject'] = sanitize_text_field(rgpost('gform_notification_subject'));
            $allowed_tags = wp_kses_allowed_html('post');
            $message = rgpost('gform_notification_message');
            $notification['message'] = wp_kses($message, $allowed_tags);
            $notification['from'] = sanitize_text_field(rgpost('gform_notification_from'));
            $notification['fromName'] = sanitize_text_field(rgpost('gform_notification_from_name'));
            $notification['replyTo'] = rgpost('gform_notification_reply_to');
            $routing = !rgempty('gform_routing_meta') ? GFCommon::json_decode(rgpost('gform_routing_meta'), true) : null;
            if (!empty($routing)) {
                $routing_logic = array('rules' => $routing);
                $routing_logic = GFFormsModel::sanitize_conditional_logic($routing_logic);
                $notification['routing'] = $routing_logic['rules'];
            }
            $notification['routing'] = $routing;
            $conditional_logic = !rgempty('gform_conditional_logic_meta') ? GFCommon::json_decode(rgpost('gform_conditional_logic_meta'), true) : null;
            $notification['conditionalLogic'] = GFFormsModel::sanitize_conditional_logic($conditional_logic);
            $notification['disableAutoformat'] = (bool) rgpost('gform_notification_disable_autoformat');
            if (rgpost('gform_is_default')) {
                $notification['isDefault'] = true;
            }
            $notification = apply_filters('gform_pre_notification_save', apply_filters("gform_pre_notification_save{$form['id']}", $notification, $form, $is_new_notification), $form, $is_new_notification);
            //validating input...
            $is_valid = self::validate_notification();
            if ($is_valid) {
                //input valid, updating...
                //emptying notification email if it is supposed to be disabled
                if ($_POST['gform_notification_to_type'] == 'routing') {
                    $notification['to'] = '';
                } else {
                    $notification['routing'] = null;
                }
                // trim values
                $notification = GFFormsModel::trim_conditional_logic_values_from_element($notification, $form);
                $form['notifications'][$notification_id] = $notification;
                RGFormsModel::save_form_notifications($form_id, $form['notifications']);
            }
        }
        if ($is_update && $is_valid) {
            $url = remove_query_arg('nid');
            GFCommon::add_message(sprintf(esc_html__('Notification saved successfully. %sBack to notifications.%s', 'gravityforms'), '<a href="' . esc_url($url) . '">', '</a>'));
        } else {
            if ($is_update && !$is_valid) {
                GFCommon::add_error_message(esc_html__('Notification could not be updated. Please enter all required information below.', 'gravityforms'));
            }
        }
        // moved page header loading here so the admin messages can be set upon saving and available for the header to print out
        GFFormSettings::page_header(esc_html__('Notifications', 'gravityforms'));
        $notification_ui_settings = self::get_notification_ui_settings($notification);
        $min = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG || isset($_GET['gform_debug']) ? '' : '.min';
        ?>
		<link rel="stylesheet" href="<?php 
        echo GFCommon::get_base_url();
        ?>
/css/admin<?php 
        echo $min;
        ?>
.css?ver=<?php 
        echo GFCommon::$version;
        ?>
" />

		<script type="text/javascript">

		var gform_has_unsaved_changes = false;
		jQuery(document).ready(function () {

			jQuery("#entry_form input, #entry_form textarea, #entry_form select").change(function () {
				gform_has_unsaved_changes = true;
			});

			window.onbeforeunload = function () {
				if (gform_has_unsaved_changes) {
					return "You have unsaved changes.";
				}
			};

			ToggleConditionalLogic(true, 'notification');

			jQuery(document).on('change', '.gfield_routing_value_dropdown', function () {
				SetRoutingValueDropDown(jQuery(this));
			});

		});

		gform.addFilter("gform_merge_tags", "MaybeAddSaveLinkMergeTag");
		function MaybeAddSaveLinkMergeTag(mergeTags, elementId, hideAllFields, excludeFieldTypes, isPrepop, option){
			var event = document.getElementById('gform_notification_event').value;
			if ( event == 'form_saved' || event == 'form_save_email_requested' ) {
				mergeTags["other"].tags.push({ tag: '{save_link}', label: <?php 
        echo json_encode(esc_html__('Save & Continue Link', 'gravityforms'));
        ?>
 });
				mergeTags["other"].tags.push({ tag: '{save_token}', label: <?php 
        echo json_encode(esc_html__('Save & Continue Token', 'gravityforms'));
        ?>
 });
			}

			return mergeTags;
		}

		<?php 
        if (empty($form['notifications'])) {
            $form['notifications'] = array();
        }
        $entry_meta = GFFormsModel::get_entry_meta($form_id);
        $entry_meta = apply_filters('gform_entry_meta_conditional_logic_notifications', $entry_meta, $form, $notification_id);
        ?>

		var form = <?php 
        echo json_encode($form);
        ?>
;
		var current_notification = <?php 
        echo GFCommon::json_encode($notification);
        ?>
;
		var entry_meta = <?php 
        echo GFCommon::json_encode($entry_meta);
        ?>
;


		function SetRoutingValueDropDown(element) {
			//parsing ID to get routing Index
			var index = element.attr("id").replace("routing_value_", '');
			SetRouting(index);
		}

		function CreateRouting(routings) {
			var str = '';
			for (var i = 0; i < routings.length; i++) {

				var isSelected = routings[i].operator == 'is' ? "selected='selected'" : '';
				var isNotSelected = routings[i].operator == 'isnot' ? "selected='selected'" : '';
				var greaterThanSelected = routings[i].operator == '>' ? "selected='selected'" : '';
				var lessThanSelected = routings[i].operator == '<' ? "selected='selected'" : '';
				var containsSelected = routings[i].operator == 'contains' ? "selected='selected'" : '';
				var startsWithSelected = routings[i].operator == 'starts_with' ? "selected='selected'" : '';
				var endsWithSelected = routings[i].operator == 'ends_with' ? "selected='selected'" : '';
				var email = routings[i]["email"] ? routings[i]["email"] : '';

				str += "<div style='width:99%'>" + <?php 
        echo json_encode(esc_html__('Send to', 'gravityforms'));
        ?>
 + " <input type='text' id='routing_email_" + i + "' value='" + email + "' onkeyup='SetRouting(" + i + ");'/>";
				str += " " + <?php 
        echo json_encode(esc_html__('if', 'gravityforms'));
        ?>
 + " " + GetRoutingFields(i, routings[i].fieldId);
				str += "<select id='routing_operator_" + i + "' onchange='SetRouting(" + i + ");' class='gform_routing_operator'>";
				str += "<option value='is' " + isSelected + ">" + <?php 
        echo json_encode(esc_html__('is', 'gravityforms'));
        ?>
 + "</option>";
				str += "<option value='isnot' " + isNotSelected + ">" + <?php 
        echo json_encode(esc_html__('is not', 'gravityforms'));
        ?>
 + "</option>";
				str += "<option value='>' " + greaterThanSelected + ">" + <?php 
        echo json_encode(esc_html__('greater than', 'gravityforms'));
        ?>
 + "</option>";
				str += "<option value='<' " + lessThanSelected + ">" + <?php 
        echo json_encode(esc_html__('less than', 'gravityforms'));
        ?>
 + "</option>";
				str += "<option value='contains' " + containsSelected + ">" + <?php 
        echo json_encode(esc_html__('contains', 'gravityforms'));
        ?>
 + "</option>";
				str += "<option value='starts_with' " + startsWithSelected + ">" + <?php 
        echo json_encode(esc_html__('starts with', 'gravityforms'));
        ?>
 + "</option>";
				str += "<option value='ends_with' " + endsWithSelected + ">" + <?php 
        echo json_encode(esc_html__('ends with', 'gravityforms'));
        ?>
 + "</option>";
				str += "</select>";
				str += GetRoutingValues(i, routings[i].fieldId, routings[i].value);
				str += "<a class='gf_insert_field_choice' title='add another rule' onclick=\"InsertRouting(" + (i + 1) + ");\"><i class='gficon-add'></i></a>";
				if (routings.length > 1)
					str += "<a class='gf_delete_field_choice' title='remove this rule' onclick=\"DeleteRouting(" + i + ");\"><i class='gficon-subtract'></i></a>";

				str += "</div>";
			}

			jQuery("#gform_notification_to_routing_rules").html(str);
		}

		function GetRoutingValues(index, fieldId, selectedValue) {
			var str = GetFieldValues(index, fieldId, selectedValue, 16);

			return str;
		}

		function GetRoutingFields(index, selectedItem) {
			var str = "<select id='routing_field_id_" + index + "' class='gfield_routing_select' onchange='jQuery(\"#routing_value_" + index + "\").replaceWith(GetRoutingValues(" + index + ", jQuery(this).val())); SetRouting(" + index + "); '>";
			str += GetSelectableFields(selectedItem, 16);
			str += "</select>";

			return str;
		}

		//---------------------- generic ---------------
		function GetSelectableFields(selectedFieldId, labelMaxCharacters) {
			var str = "";
			var inputType;
			for (var i = 0; i < form.fields.length; i++) {
				inputType = form.fields[i].inputType ? form.fields[i].inputType : form.fields[i].type;
				//see if this field type can be used for conditionals
				if (IsNotificationConditionalLogicField(form.fields[i])) {
					var selected = form.fields[i].id == selectedFieldId ? "selected='selected'" : "";
					str += "<option value='" + form.fields[i].id + "' " + selected + ">" + form.fields[i].label + "</option>";
				}
			}
			return str;
		}

		function IsNotificationConditionalLogicField(field) {
			//this function is a duplicate of IsConditionalLogicField from form_editor.js
			inputType = field.inputType ? field.inputType : field.type;
			var supported_fields = ['checkbox', 'radio', 'select', 'text', 'website', 'textarea', 'email', 'hidden', 'number', 'phone', 'multiselect', 'post_title',
				'post_tags', 'post_custom_field', 'post_content', 'post_excerpt'];

			var index = jQuery.inArray(inputType, supported_fields);

			return index >= 0;
		}

		function GetFirstSelectableField() {
			var inputType;
			for (var i = 0; i < form.fields.length; i++) {
				inputType = form.fields[i].inputType ? form.fields[i].inputType : form.fields[i].type;
				if (IsNotificationConditionalLogicField(form.fields[i])) {
					return form.fields[i].id;
				}
			}

			return 0;
		}

		function TruncateMiddle(text, maxCharacters) {
			if (!text)
				return "";

			if (text.length <= maxCharacters)
				return text;
			var middle = parseInt(maxCharacters / 2);
			return text.substr(0, middle) + "..." + text.substr(text.length - middle, middle);

		}

		function GetFieldValues(index, fieldId, selectedValue, labelMaxCharacters) {
			if (!fieldId)
				fieldId = GetFirstSelectableField();

			if (!fieldId)
				return "";

			var str = '';
			var field = GetFieldById(fieldId);
			var isAnySelected = false;

			if (!field)
				return "";

			if (field["type"] == 'post_category' && field["displayAllCategories"]) {
				var dropdown_id = 'routing_value_' + index;
				var dropdown = jQuery('#' + dropdown_id + ".gfield_category_dropdown");

				//don't load category drop down if it already exists (to avoid unecessary ajax requests)
				if (dropdown.length > 0) {

					var options = dropdown.html();
					options = options.replace("value=\"" + selectedValue + "\"", "value=\"" + selectedValue + "\" selected=\"selected\"");
					str = "<select id='" + dropdown_id + "' class='gfield_routing_select gfield_category_dropdown gfield_routing_value_dropdown'>" + options + "</select>";
				}
				else {
					//loading categories via AJAX
					jQuery.post(ajaxurl, {   action: "gf_get_notification_post_categories",
							ruleIndex              : index,
							selectedValue          : selectedValue},
						function (dropdown_string) {
							if (dropdown_string) {
								jQuery('#gfield_ajax_placeholder_' + index).replaceWith(dropdown_string.trim());
							}
						}
					);

					//will be replaced by real drop down during the ajax callback
					str = "<select id='gfield_ajax_placeholder_" + index + "' class='gfield_routing_select'><option>" + <?php 
        json_encode(esc_html__('Loading...', 'gravityforms'));
        ?>
 + "</option></select>";
				}
			}
			else if (field.choices) {
				//create a drop down for fields that have choices (i.e. drop down, radio, checkboxes, etc...)
				str = "<select class='gfield_routing_select gfield_routing_value_dropdown' id='routing_value_" + index + "'>";
				for (var i = 0; i < field.choices.length; i++) {
					var choiceValue = field.choices[i].value ? field.choices[i].value : field.choices[i].text;
					var isSelected = choiceValue == selectedValue;
					var selected = isSelected ? "selected='selected'" : '';
					if (isSelected)
						isAnySelected = true;

					str += "<option value='" + choiceValue.replace(/'/g, "&#039;") + "' " + selected + ">" + field.choices[i].text + "</option>";
				}

				if (!isAnySelected && selectedValue) {
					str += "<option value='" + selectedValue.replace(/'/g, "&#039;") + "' selected='selected'>" + selectedValue + "</option>";
				}
				str += "</select>";
			}
			else {
				selectedValue = selectedValue ? selectedValue.replace(/'/g, "&#039;") : "";
				//create a text field for fields that don't have choices (i.e text, textarea, number, email, etc...)
				str = "<input type='text' placeholder='" + <?php 
        echo json_encode(esc_html__('Enter value', 'gravityforms'));
        ?>
 +"' class='gfield_routing_select' id='routing_value_" + index + "' value='" + selectedValue.replace(/'/g, "&#039;") + "' onchange='SetRouting(" + index + ");' onkeyup='SetRouting(" + index + ");'>";
			}
			return str;
		}

		function GetFieldById(fieldId) {
			for (var i = 0; i < form.fields.length; i++) {
				if (form.fields[i].id == fieldId)
					return form.fields[i];
			}
			return null;
		}
		//---------------------------------------------------------------------------------

		function InsertRouting(index) {
			var routings = current_notification.routing;
			routings.splice(index, 0, new ConditionalRule());

			CreateRouting(routings);
			SetRouting(index);
		}

		function SetRouting(ruleIndex) {
			if (!current_notification.routing && ruleIndex == 0)
				current_notification.routing = [new ConditionalRule()];

			current_notification.routing[ruleIndex]["email"] = jQuery("#routing_email_" + ruleIndex).val();
			current_notification.routing[ruleIndex]["fieldId"] = jQuery("#routing_field_id_" + ruleIndex).val();
			current_notification.routing[ruleIndex]["operator"] = jQuery("#routing_operator_" + ruleIndex).val();
			current_notification.routing[ruleIndex]["value"] = jQuery("#routing_value_" + ruleIndex).val();

			var json = jQuery.toJSON(current_notification.routing);
			jQuery('#gform_routing_meta').val(json);
		}

		function DeleteRouting(ruleIndex) {
			current_notification.routing.splice(ruleIndex, 1);
			CreateRouting(current_notification.routing);
		}

		function SetConditionalLogic(isChecked) {
			current_notification.conditionalLogic = isChecked ? new ConditionalLogic() : null;
		}

		function SaveJSMeta() {
			jQuery('#gform_routing_meta').val(jQuery.toJSON(current_notification.routing));
			jQuery('#gform_conditional_logic_meta').val(jQuery.toJSON(current_notification.conditionalLogic));
		}

		<?php 
        GFFormSettings::output_field_scripts();
        ?>

		</script>

		<form method="post" id="gform_notification_form" onsubmit="gform_has_unsaved_changes = false; SaveJSMeta();">

			<?php 
        wp_nonce_field('gforms_save_notification', 'gforms_save_notification');
        ?>
			<?php 
        if (rgar($notification, 'isDefault')) {
            echo '<input type="hidden" id="gform_is_default" name="gform_is_default" value="1"/>';
        }
        ?>
			<input type="hidden" id="gform_routing_meta" name="gform_routing_meta" />
			<input type="hidden" id="gform_conditional_logic_meta" name="gform_conditional_logic_meta" />
			<input type="hidden" id="gform_notification_id" name="gform_notification_id" value="<?php 
        echo esc_attr($notification_id);
        ?>
" />

			<table class="form-table gform_nofification_edit">
				<?php 
        array_map(array('GFFormSettings', 'output'), $notification_ui_settings);
        ?>
			</table>

			<p class="submit">
				<?php 
        $button_label = $is_new_notification ? __('Save Notification', 'gravityforms') : __('Update Notification', 'gravityforms');
        $notification_button = '<input class="button-primary" type="submit" value="' . esc_attr($button_label) . '" name="save"/>';
        echo apply_filters('gform_save_notification_button', $notification_button);
        ?>
			</p>
		</form>

		<?php 
        GFFormSettings::page_footer();
    }
    protected function feed_edit_page($form, $feed_id)
    {
        // Save feed if appropriate
        $feed_id = $this->maybe_save_feed_settings($feed_id, $form['id']);
        $this->_current_feed_id = $feed_id;
        //So that current feed functions work when creating a new feed
        ?>
		<script type="text/javascript">
			<?php 
        GFFormSettings::output_field_scripts();
        ?>
		</script>

		<h3><span><?php 
        echo $this->feed_settings_title();
        ?>
</span></h3>

		<?php 
        $feed = $this->get_feed($feed_id);
        $this->set_settings($feed['meta']);
        GFCommon::display_admin_message();
        $this->render_settings($this->get_feed_settings_fields($form));
    }
    public function feed_edit_page($form, $feed_id)
    {
        $title = '<h3><span>' . $this->feed_settings_title() . '</span></h3>';
        if (!$this->can_create_feed()) {
            echo $title . '<div>' . $this->configure_addon_message() . '</div>';
            return;
        }
        // Save feed if appropriate
        $feed_id = $this->maybe_save_feed_settings($feed_id, $form['id']);
        $this->_current_feed_id = $feed_id;
        // So that current feed functions work when creating a new feed
        ?>
		<script type="text/javascript">
			<?php 
        GFFormSettings::output_field_scripts();
        ?>
		</script>
		<?php 
        echo $title;
        $feed = $this->get_feed($feed_id);
        $this->set_settings($feed['meta']);
        GFCommon::display_admin_message();
        $this->render_settings($this->get_feed_settings_fields($form));
    }