Viewing File: /usr/local/cpanel/base/frontend/jupiter/version_control/index.cmb.js
(function(root) {
define("jquery-chosen", ["jquery"], function() {
return (function() {
/*!
Chosen, a Select Box Enhancer for jQuery and Prototype
by Patrick Filler for Harvest, http://getharvest.com
Version 1.5.1
Full source at https://github.com/harvesthq/chosen
Copyright (c) 2011-2016 Harvest http://getharvest.com
MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
This file is generated by `grunt build`, do not edit it by hand.
*/
(function() {
var $, AbstractChosen, Chosen, SelectParser, _ref,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
SelectParser = (function() {
function SelectParser() {
this.options_index = 0;
this.parsed = [];
}
SelectParser.prototype.add_node = function(child) {
if (child.nodeName.toUpperCase() === "OPTGROUP") {
return this.add_group(child);
} else {
return this.add_option(child);
}
};
SelectParser.prototype.add_group = function(group) {
var group_position, option, _i, _len, _ref, _results;
group_position = this.parsed.length;
this.parsed.push({
array_index: group_position,
group: true,
label: this.escapeExpression(group.label),
title: group.title ? group.title : void 0,
children: 0,
disabled: group.disabled,
classes: group.className
});
_ref = group.childNodes;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
option = _ref[_i];
_results.push(this.add_option(option, group_position, group.disabled));
}
return _results;
};
SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
if (option.nodeName.toUpperCase() === "OPTION") {
if (option.text !== "") {
if (group_position != null) {
this.parsed[group_position].children += 1;
}
this.parsed.push({
array_index: this.parsed.length,
options_index: this.options_index,
value: option.value,
text: option.text,
html: option.innerHTML,
title: option.title ? option.title : void 0,
selected: option.selected,
disabled: group_disabled === true ? group_disabled : option.disabled,
group_array_index: group_position,
group_label: group_position != null ? this.parsed[group_position].label : null,
classes: option.className,
style: option.style.cssText
});
} else {
this.parsed.push({
array_index: this.parsed.length,
options_index: this.options_index,
empty: true
});
}
return this.options_index += 1;
}
};
SelectParser.prototype.escapeExpression = function(text) {
var map, unsafe_chars;
if ((text == null) || text === false) {
return "";
}
if (!/[\&\<\>\"\'\`]/.test(text)) {
return text;
}
map = {
"<": "<",
">": ">",
'"': """,
"'": "'",
"`": "`"
};
unsafe_chars = /&(?!\w+;)|[\<\>\"\'\`]/g;
return text.replace(unsafe_chars, function(chr) {
return map[chr] || "&";
});
};
return SelectParser;
})();
SelectParser.select_to_array = function(select) {
var child, parser, _i, _len, _ref;
parser = new SelectParser();
_ref = select.childNodes;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
parser.add_node(child);
}
return parser.parsed;
};
AbstractChosen = (function() {
function AbstractChosen(form_field, options) {
this.form_field = form_field;
this.options = options != null ? options : {};
if (!AbstractChosen.browser_is_supported()) {
return;
}
this.is_multiple = this.form_field.multiple;
this.set_default_text();
this.set_default_values();
this.setup();
this.set_up_html();
this.register_observers();
this.on_ready();
}
AbstractChosen.prototype.set_default_values = function() {
var _this = this;
this.click_test_action = function(evt) {
return _this.test_active_click(evt);
};
this.activate_action = function(evt) {
return _this.activate_field(evt);
};
this.active_field = false;
this.mouse_on_container = false;
this.results_showing = false;
this.result_highlighted = null;
this.allow_single_deselect = (this.options.allow_single_deselect != null) && (this.form_field.options[0] != null) && this.form_field.options[0].text === "" ? this.options.allow_single_deselect : false;
this.disable_search_threshold = this.options.disable_search_threshold || 0;
this.disable_search = this.options.disable_search || false;
this.enable_split_word_search = this.options.enable_split_word_search != null ? this.options.enable_split_word_search : true;
this.group_search = this.options.group_search != null ? this.options.group_search : true;
this.search_contains = this.options.search_contains || false;
this.single_backstroke_delete = this.options.single_backstroke_delete != null ? this.options.single_backstroke_delete : true;
this.max_selected_options = this.options.max_selected_options || Infinity;
this.inherit_select_classes = this.options.inherit_select_classes || false;
this.display_selected_options = this.options.display_selected_options != null ? this.options.display_selected_options : true;
this.display_disabled_options = this.options.display_disabled_options != null ? this.options.display_disabled_options : true;
this.include_group_label_in_selected = this.options.include_group_label_in_selected || false;
return this.max_shown_results = this.options.max_shown_results || Number.POSITIVE_INFINITY;
};
AbstractChosen.prototype.set_default_text = function() {
if (this.form_field.getAttribute("data-placeholder")) {
this.default_text = this.form_field.getAttribute("data-placeholder");
} else if (this.is_multiple) {
this.default_text = this.options.placeholder_text_multiple || this.options.placeholder_text || AbstractChosen.default_multiple_text;
} else {
this.default_text = this.options.placeholder_text_single || this.options.placeholder_text || AbstractChosen.default_single_text;
}
return this.results_none_found = this.form_field.getAttribute("data-no_results_text") || this.options.no_results_text || AbstractChosen.default_no_result_text;
};
AbstractChosen.prototype.choice_label = function(item) {
if (this.include_group_label_in_selected && (item.group_label != null)) {
return "<b class='group-name'>" + item.group_label + "</b>" + item.html;
} else {
return item.html;
}
};
AbstractChosen.prototype.mouse_enter = function() {
return this.mouse_on_container = true;
};
AbstractChosen.prototype.mouse_leave = function() {
return this.mouse_on_container = false;
};
AbstractChosen.prototype.input_focus = function(evt) {
var _this = this;
if (this.is_multiple) {
if (!this.active_field) {
return setTimeout((function() {
return _this.container_mousedown();
}), 50);
}
} else {
if (!this.active_field) {
return this.activate_field();
}
}
};
AbstractChosen.prototype.input_blur = function(evt) {
var _this = this;
if (!this.mouse_on_container) {
this.active_field = false;
return setTimeout((function() {
return _this.blur_test();
}), 100);
}
};
AbstractChosen.prototype.results_option_build = function(options) {
var content, data, data_content, shown_results, _i, _len, _ref;
content = '';
shown_results = 0;
_ref = this.results_data;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
data = _ref[_i];
data_content = '';
if (data.group) {
data_content = this.result_add_group(data);
} else {
data_content = this.result_add_option(data);
}
if (data_content !== '') {
shown_results++;
content += data_content;
}
if (options != null ? options.first : void 0) {
if (data.selected && this.is_multiple) {
this.choice_build(data);
} else if (data.selected && !this.is_multiple) {
this.single_set_selected_text(this.choice_label(data));
}
}
if (shown_results >= this.max_shown_results) {
break;
}
}
return content;
};
AbstractChosen.prototype.result_add_option = function(option) {
var classes, option_el;
if (!option.search_match) {
return '';
}
if (!this.include_option_in_results(option)) {
return '';
}
classes = [];
if (!option.disabled && !(option.selected && this.is_multiple)) {
classes.push("active-result");
}
if (option.disabled && !(option.selected && this.is_multiple)) {
classes.push("disabled-result");
}
if (option.selected) {
classes.push("result-selected");
}
if (option.group_array_index != null) {
classes.push("group-option");
}
if (option.classes !== "") {
classes.push(option.classes);
}
option_el = document.createElement("li");
option_el.className = classes.join(" ");
option_el.style.cssText = option.style;
option_el.setAttribute("data-option-array-index", option.array_index);
option_el.innerHTML = option.search_text;
if (option.title) {
option_el.title = option.title;
}
return this.outerHTML(option_el);
};
AbstractChosen.prototype.result_add_group = function(group) {
var classes, group_el;
if (!(group.search_match || group.group_match)) {
return '';
}
if (!(group.active_options > 0)) {
return '';
}
classes = [];
classes.push("group-result");
if (group.classes) {
classes.push(group.classes);
}
group_el = document.createElement("li");
group_el.className = classes.join(" ");
group_el.innerHTML = group.search_text;
if (group.title) {
group_el.title = group.title;
}
return this.outerHTML(group_el);
};
AbstractChosen.prototype.results_update_field = function() {
this.set_default_text();
if (!this.is_multiple) {
this.results_reset_cleanup();
}
this.result_clear_highlight();
this.results_build();
if (this.results_showing) {
return this.winnow_results();
}
};
AbstractChosen.prototype.reset_single_select_options = function() {
var result, _i, _len, _ref, _results;
_ref = this.results_data;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
result = _ref[_i];
if (result.selected) {
_results.push(result.selected = false);
} else {
_results.push(void 0);
}
}
return _results;
};
AbstractChosen.prototype.results_toggle = function() {
if (this.results_showing) {
return this.results_hide();
} else {
return this.results_show();
}
};
AbstractChosen.prototype.results_search = function(evt) {
if (this.results_showing) {
return this.winnow_results();
} else {
return this.results_show();
}
};
AbstractChosen.prototype.winnow_results = function() {
var escapedSearchText, option, regex, results, results_group, searchText, startpos, text, zregex, _i, _len, _ref;
this.no_results_clear();
results = 0;
searchText = this.get_search_text();
escapedSearchText = searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
zregex = new RegExp(escapedSearchText, 'i');
regex = this.get_search_regex(escapedSearchText);
_ref = this.results_data;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
option = _ref[_i];
option.search_match = false;
results_group = null;
if (this.include_option_in_results(option)) {
if (option.group) {
option.group_match = false;
option.active_options = 0;
}
if ((option.group_array_index != null) && this.results_data[option.group_array_index]) {
results_group = this.results_data[option.group_array_index];
if (results_group.active_options === 0 && results_group.search_match) {
results += 1;
}
results_group.active_options += 1;
}
option.search_text = option.group ? option.label : option.html;
if (!(option.group && !this.group_search)) {
option.search_match = this.search_string_match(option.search_text, regex);
if (option.search_match && !option.group) {
results += 1;
}
if (option.search_match) {
if (searchText.length) {
startpos = option.search_text.search(zregex);
text = option.search_text.substr(0, startpos + searchText.length) + '</em>' + option.search_text.substr(startpos + searchText.length);
option.search_text = text.substr(0, startpos) + '<em>' + text.substr(startpos);
}
if (results_group != null) {
results_group.group_match = true;
}
} else if ((option.group_array_index != null) && this.results_data[option.group_array_index].search_match) {
option.search_match = true;
}
}
}
}
this.result_clear_highlight();
if (results < 1 && searchText.length) {
this.update_results_content("");
return this.no_results(searchText);
} else {
this.update_results_content(this.results_option_build());
return this.winnow_results_set_highlight();
}
};
AbstractChosen.prototype.get_search_regex = function(escaped_search_string) {
var regex_anchor;
regex_anchor = this.search_contains ? "" : "^";
return new RegExp(regex_anchor + escaped_search_string, 'i');
};
AbstractChosen.prototype.search_string_match = function(search_string, regex) {
var part, parts, _i, _len;
if (regex.test(search_string)) {
return true;
} else if (this.enable_split_word_search && (search_string.indexOf(" ") >= 0 || search_string.indexOf("[") === 0)) {
parts = search_string.replace(/\[|\]/g, "").split(" ");
if (parts.length) {
for (_i = 0, _len = parts.length; _i < _len; _i++) {
part = parts[_i];
if (regex.test(part)) {
return true;
}
}
}
}
};
AbstractChosen.prototype.choices_count = function() {
var option, _i, _len, _ref;
if (this.selected_option_count != null) {
return this.selected_option_count;
}
this.selected_option_count = 0;
_ref = this.form_field.options;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
option = _ref[_i];
if (option.selected) {
this.selected_option_count += 1;
}
}
return this.selected_option_count;
};
AbstractChosen.prototype.choices_click = function(evt) {
evt.preventDefault();
if (!(this.results_showing || this.is_disabled)) {
return this.results_show();
}
};
AbstractChosen.prototype.keyup_checker = function(evt) {
var stroke, _ref;
stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
this.search_field_scale();
switch (stroke) {
case 8:
if (this.is_multiple && this.backstroke_length < 1 && this.choices_count() > 0) {
return this.keydown_backstroke();
} else if (!this.pending_backstroke) {
this.result_clear_highlight();
return this.results_search();
}
break;
case 13:
evt.preventDefault();
if (this.results_showing) {
return this.result_select(evt);
}
break;
case 27:
if (this.results_showing) {
this.results_hide();
}
return true;
case 9:
case 38:
case 40:
case 16:
case 91:
case 17:
case 18:
break;
default:
return this.results_search();
}
};
AbstractChosen.prototype.clipboard_event_checker = function(evt) {
var _this = this;
return setTimeout((function() {
return _this.results_search();
}), 50);
};
AbstractChosen.prototype.container_width = function() {
if (this.options.width != null) {
return this.options.width;
} else {
return "" + this.form_field.offsetWidth + "px";
}
};
AbstractChosen.prototype.include_option_in_results = function(option) {
if (this.is_multiple && (!this.display_selected_options && option.selected)) {
return false;
}
if (!this.display_disabled_options && option.disabled) {
return false;
}
if (option.empty) {
return false;
}
return true;
};
AbstractChosen.prototype.search_results_touchstart = function(evt) {
this.touch_started = true;
return this.search_results_mouseover(evt);
};
AbstractChosen.prototype.search_results_touchmove = function(evt) {
this.touch_started = false;
return this.search_results_mouseout(evt);
};
AbstractChosen.prototype.search_results_touchend = function(evt) {
if (this.touch_started) {
return this.search_results_mouseup(evt);
}
};
AbstractChosen.prototype.outerHTML = function(element) {
var tmp;
if (element.outerHTML) {
return element.outerHTML;
}
tmp = document.createElement("div");
tmp.appendChild(element);
return tmp.innerHTML;
};
AbstractChosen.browser_is_supported = function() {
if (/iP(od|hone)/i.test(window.navigator.userAgent)) {
return false;
}
if (/Android/i.test(window.navigator.userAgent)) {
if (/Mobile/i.test(window.navigator.userAgent)) {
return false;
}
}
if (/IEMobile/i.test(window.navigator.userAgent)) {
return false;
}
if (/Windows Phone/i.test(window.navigator.userAgent)) {
return false;
}
if (/BlackBerry/i.test(window.navigator.userAgent)) {
return false;
}
if (/BB10/i.test(window.navigator.userAgent)) {
return false;
}
if (window.navigator.appName === "Microsoft Internet Explorer") {
return document.documentMode >= 8;
}
return true;
};
AbstractChosen.default_multiple_text = "Select Some Options";
AbstractChosen.default_single_text = "Select an Option";
AbstractChosen.default_no_result_text = "No results match";
return AbstractChosen;
})();
$ = jQuery;
$.fn.extend({
chosen: function(options) {
if (!AbstractChosen.browser_is_supported()) {
return this;
}
return this.each(function(input_field) {
var $this, chosen;
$this = $(this);
chosen = $this.data('chosen');
if (options === 'destroy') {
if (chosen instanceof Chosen) {
chosen.destroy();
}
return;
}
if (!(chosen instanceof Chosen)) {
$this.data('chosen', new Chosen(this, options));
}
});
}
});
Chosen = (function(_super) {
__extends(Chosen, _super);
function Chosen() {
_ref = Chosen.__super__.constructor.apply(this, arguments);
return _ref;
}
Chosen.prototype.setup = function() {
this.form_field_jq = $(this.form_field);
this.current_selectedIndex = this.form_field.selectedIndex;
return this.is_rtl = this.form_field_jq.hasClass("chosen-rtl");
};
Chosen.prototype.set_up_html = function() {
var container_classes, container_props;
container_classes = ["chosen-container"];
container_classes.push("chosen-container-" + (this.is_multiple ? "multi" : "single"));
if (this.inherit_select_classes && this.form_field.className) {
container_classes.push(this.form_field.className);
}
if (this.is_rtl) {
container_classes.push("chosen-rtl");
}
container_props = {
'class': container_classes.join(' '),
'style': "width: " + (this.container_width()) + ";",
'title': this.form_field.title
};
if (this.form_field.id.length) {
container_props.id = this.form_field.id.replace(/[^\w]/g, '_') + "_chosen";
}
this.container = $("<div />", container_props);
if (this.is_multiple) {
this.container.html('<ul class="chosen-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chosen-drop"><ul class="chosen-results"></ul></div>');
} else {
this.container.html('<a class="chosen-single chosen-default"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off" /></div><ul class="chosen-results"></ul></div>');
}
this.form_field_jq.hide().after(this.container);
this.dropdown = this.container.find('div.chosen-drop').first();
this.search_field = this.container.find('input').first();
this.search_results = this.container.find('ul.chosen-results').first();
this.search_field_scale();
this.search_no_results = this.container.find('li.no-results').first();
if (this.is_multiple) {
this.search_choices = this.container.find('ul.chosen-choices').first();
this.search_container = this.container.find('li.search-field').first();
} else {
this.search_container = this.container.find('div.chosen-search').first();
this.selected_item = this.container.find('.chosen-single').first();
}
this.results_build();
this.set_tab_index();
return this.set_label_behavior();
};
Chosen.prototype.on_ready = function() {
return this.form_field_jq.trigger("chosen:ready", {
chosen: this
});
};
Chosen.prototype.register_observers = function() {
var _this = this;
this.container.bind('touchstart.chosen', function(evt) {
_this.container_mousedown(evt);
return evt.preventDefault();
});
this.container.bind('touchend.chosen', function(evt) {
_this.container_mouseup(evt);
return evt.preventDefault();
});
this.container.bind('mousedown.chosen', function(evt) {
_this.container_mousedown(evt);
});
this.container.bind('mouseup.chosen', function(evt) {
_this.container_mouseup(evt);
});
this.container.bind('mouseenter.chosen', function(evt) {
_this.mouse_enter(evt);
});
this.container.bind('mouseleave.chosen', function(evt) {
_this.mouse_leave(evt);
});
this.search_results.bind('mouseup.chosen', function(evt) {
_this.search_results_mouseup(evt);
});
this.search_results.bind('mouseover.chosen', function(evt) {
_this.search_results_mouseover(evt);
});
this.search_results.bind('mouseout.chosen', function(evt) {
_this.search_results_mouseout(evt);
});
this.search_results.bind('mousewheel.chosen DOMMouseScroll.chosen', function(evt) {
_this.search_results_mousewheel(evt);
});
this.search_results.bind('touchstart.chosen', function(evt) {
_this.search_results_touchstart(evt);
});
this.search_results.bind('touchmove.chosen', function(evt) {
_this.search_results_touchmove(evt);
});
this.search_results.bind('touchend.chosen', function(evt) {
_this.search_results_touchend(evt);
});
this.form_field_jq.bind("chosen:updated.chosen", function(evt) {
_this.results_update_field(evt);
});
this.form_field_jq.bind("chosen:activate.chosen", function(evt) {
_this.activate_field(evt);
});
this.form_field_jq.bind("chosen:open.chosen", function(evt) {
_this.container_mousedown(evt);
});
this.form_field_jq.bind("chosen:close.chosen", function(evt) {
_this.input_blur(evt);
});
this.search_field.bind('blur.chosen', function(evt) {
_this.input_blur(evt);
});
this.search_field.bind('keyup.chosen', function(evt) {
_this.keyup_checker(evt);
});
this.search_field.bind('keydown.chosen', function(evt) {
_this.keydown_checker(evt);
});
this.search_field.bind('focus.chosen', function(evt) {
_this.input_focus(evt);
});
this.search_field.bind('cut.chosen', function(evt) {
_this.clipboard_event_checker(evt);
});
this.search_field.bind('paste.chosen', function(evt) {
_this.clipboard_event_checker(evt);
});
if (this.is_multiple) {
return this.search_choices.bind('click.chosen', function(evt) {
_this.choices_click(evt);
});
} else {
return this.container.bind('click.chosen', function(evt) {
evt.preventDefault();
});
}
};
Chosen.prototype.destroy = function() {
$(this.container[0].ownerDocument).unbind("click.chosen", this.click_test_action);
if (this.search_field[0].tabIndex) {
this.form_field_jq[0].tabIndex = this.search_field[0].tabIndex;
}
this.container.remove();
this.form_field_jq.removeData('chosen');
return this.form_field_jq.show();
};
Chosen.prototype.search_field_disabled = function() {
this.is_disabled = this.form_field_jq[0].disabled;
if (this.is_disabled) {
this.container.addClass('chosen-disabled');
this.search_field[0].disabled = true;
if (!this.is_multiple) {
this.selected_item.unbind("focus.chosen", this.activate_action);
}
return this.close_field();
} else {
this.container.removeClass('chosen-disabled');
this.search_field[0].disabled = false;
if (!this.is_multiple) {
return this.selected_item.bind("focus.chosen", this.activate_action);
}
}
};
Chosen.prototype.container_mousedown = function(evt) {
if (!this.is_disabled) {
if (evt && evt.type === "mousedown" && !this.results_showing) {
evt.preventDefault();
}
if (!((evt != null) && ($(evt.target)).hasClass("search-choice-close"))) {
if (!this.active_field) {
if (this.is_multiple) {
this.search_field.val("");
}
$(this.container[0].ownerDocument).bind('click.chosen', this.click_test_action);
this.results_show();
} else if (!this.is_multiple && evt && (($(evt.target)[0] === this.selected_item[0]) || $(evt.target).parents("a.chosen-single").length)) {
evt.preventDefault();
this.results_toggle();
}
return this.activate_field();
}
}
};
Chosen.prototype.container_mouseup = function(evt) {
if (evt.target.nodeName === "ABBR" && !this.is_disabled) {
return this.results_reset(evt);
}
};
Chosen.prototype.search_results_mousewheel = function(evt) {
var delta;
if (evt.originalEvent) {
delta = evt.originalEvent.deltaY || -evt.originalEvent.wheelDelta || evt.originalEvent.detail;
}
if (delta != null) {
evt.preventDefault();
if (evt.type === 'DOMMouseScroll') {
delta = delta * 40;
}
return this.search_results.scrollTop(delta + this.search_results.scrollTop());
}
};
Chosen.prototype.blur_test = function(evt) {
if (!this.active_field && this.container.hasClass("chosen-container-active")) {
return this.close_field();
}
};
Chosen.prototype.close_field = function() {
$(this.container[0].ownerDocument).unbind("click.chosen", this.click_test_action);
this.active_field = false;
this.results_hide();
this.container.removeClass("chosen-container-active");
this.clear_backstroke();
this.show_search_field_default();
return this.search_field_scale();
};
Chosen.prototype.activate_field = function() {
this.container.addClass("chosen-container-active");
this.active_field = true;
this.search_field.val(this.search_field.val());
return this.search_field.focus();
};
Chosen.prototype.test_active_click = function(evt) {
var active_container;
active_container = $(evt.target).closest('.chosen-container');
if (active_container.length && this.container[0] === active_container[0]) {
return this.active_field = true;
} else {
return this.close_field();
}
};
Chosen.prototype.results_build = function() {
this.parsing = true;
this.selected_option_count = null;
this.results_data = SelectParser.select_to_array(this.form_field);
if (this.is_multiple) {
this.search_choices.find("li.search-choice").remove();
} else if (!this.is_multiple) {
this.single_set_selected_text();
if (this.disable_search || this.form_field.options.length <= this.disable_search_threshold) {
this.search_field[0].readOnly = true;
this.container.addClass("chosen-container-single-nosearch");
} else {
this.search_field[0].readOnly = false;
this.container.removeClass("chosen-container-single-nosearch");
}
}
this.update_results_content(this.results_option_build({
first: true
}));
this.search_field_disabled();
this.show_search_field_default();
this.search_field_scale();
return this.parsing = false;
};
Chosen.prototype.result_do_highlight = function(el) {
var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
if (el.length) {
this.result_clear_highlight();
this.result_highlight = el;
this.result_highlight.addClass("highlighted");
maxHeight = parseInt(this.search_results.css("maxHeight"), 10);
visible_top = this.search_results.scrollTop();
visible_bottom = maxHeight + visible_top;
high_top = this.result_highlight.position().top + this.search_results.scrollTop();
high_bottom = high_top + this.result_highlight.outerHeight();
if (high_bottom >= visible_bottom) {
return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
} else if (high_top < visible_top) {
return this.search_results.scrollTop(high_top);
}
}
};
Chosen.prototype.result_clear_highlight = function() {
if (this.result_highlight) {
this.result_highlight.removeClass("highlighted");
}
return this.result_highlight = null;
};
Chosen.prototype.results_show = function() {
if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
this.form_field_jq.trigger("chosen:maxselected", {
chosen: this
});
return false;
}
this.container.addClass("chosen-with-drop");
this.results_showing = true;
this.search_field.focus();
this.search_field.val(this.search_field.val());
this.winnow_results();
return this.form_field_jq.trigger("chosen:showing_dropdown", {
chosen: this
});
};
Chosen.prototype.update_results_content = function(content) {
return this.search_results.html(content);
};
Chosen.prototype.results_hide = function() {
if (this.results_showing) {
this.result_clear_highlight();
this.container.removeClass("chosen-with-drop");
this.form_field_jq.trigger("chosen:hiding_dropdown", {
chosen: this
});
}
return this.results_showing = false;
};
Chosen.prototype.set_tab_index = function(el) {
var ti;
if (this.form_field.tabIndex) {
ti = this.form_field.tabIndex;
this.form_field.tabIndex = -1;
return this.search_field[0].tabIndex = ti;
}
};
Chosen.prototype.set_label_behavior = function() {
var _this = this;
this.form_field_label = this.form_field_jq.parents("label");
if (!this.form_field_label.length && this.form_field.id.length) {
this.form_field_label = $("label[for='" + this.form_field.id + "']");
}
if (this.form_field_label.length > 0) {
return this.form_field_label.bind('click.chosen', function(evt) {
if (_this.is_multiple) {
return _this.container_mousedown(evt);
} else {
return _this.activate_field();
}
});
}
};
Chosen.prototype.show_search_field_default = function() {
if (this.is_multiple && this.choices_count() < 1 && !this.active_field) {
this.search_field.val(this.default_text);
return this.search_field.addClass("default");
} else {
this.search_field.val("");
return this.search_field.removeClass("default");
}
};
Chosen.prototype.search_results_mouseup = function(evt) {
var target;
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
if (target.length) {
this.result_highlight = target;
this.result_select(evt);
return this.search_field.focus();
}
};
Chosen.prototype.search_results_mouseover = function(evt) {
var target;
target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
if (target) {
return this.result_do_highlight(target);
}
};
Chosen.prototype.search_results_mouseout = function(evt) {
if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) {
return this.result_clear_highlight();
}
};
Chosen.prototype.choice_build = function(item) {
var choice, close_link,
_this = this;
choice = $('<li />', {
"class": "search-choice"
}).html("<span>" + (this.choice_label(item)) + "</span>");
if (item.disabled) {
choice.addClass('search-choice-disabled');
} else {
close_link = $('<a />', {
"class": 'search-choice-close',
'data-option-array-index': item.array_index
});
close_link.bind('click.chosen', function(evt) {
return _this.choice_destroy_link_click(evt);
});
choice.append(close_link);
}
return this.search_container.before(choice);
};
Chosen.prototype.choice_destroy_link_click = function(evt) {
evt.preventDefault();
evt.stopPropagation();
if (!this.is_disabled) {
return this.choice_destroy($(evt.target));
}
};
Chosen.prototype.choice_destroy = function(link) {
if (this.result_deselect(link[0].getAttribute("data-option-array-index"))) {
this.show_search_field_default();
if (this.is_multiple && this.choices_count() > 0 && this.search_field.val().length < 1) {
this.results_hide();
}
link.parents('li').first().remove();
return this.search_field_scale();
}
};
Chosen.prototype.results_reset = function() {
this.reset_single_select_options();
this.form_field.options[0].selected = true;
this.single_set_selected_text();
this.show_search_field_default();
this.results_reset_cleanup();
this.form_field_jq.trigger("change");
if (this.active_field) {
return this.results_hide();
}
};
Chosen.prototype.results_reset_cleanup = function() {
this.current_selectedIndex = this.form_field.selectedIndex;
return this.selected_item.find("abbr").remove();
};
Chosen.prototype.result_select = function(evt) {
var high, item;
if (this.result_highlight) {
high = this.result_highlight;
this.result_clear_highlight();
if (this.is_multiple && this.max_selected_options <= this.choices_count()) {
this.form_field_jq.trigger("chosen:maxselected", {
chosen: this
});
return false;
}
if (this.is_multiple) {
high.removeClass("active-result");
} else {
this.reset_single_select_options();
}
high.addClass("result-selected");
item = this.results_data[high[0].getAttribute("data-option-array-index")];
item.selected = true;
this.form_field.options[item.options_index].selected = true;
this.selected_option_count = null;
if (this.is_multiple) {
this.choice_build(item);
} else {
this.single_set_selected_text(this.choice_label(item));
}
if (!((evt.metaKey || evt.ctrlKey) && this.is_multiple)) {
this.results_hide();
}
this.show_search_field_default();
if (this.is_multiple || this.form_field.selectedIndex !== this.current_selectedIndex) {
this.form_field_jq.trigger("change", {
'selected': this.form_field.options[item.options_index].value
});
}
this.current_selectedIndex = this.form_field.selectedIndex;
evt.preventDefault();
return this.search_field_scale();
}
};
Chosen.prototype.single_set_selected_text = function(text) {
if (text == null) {
text = this.default_text;
}
if (text === this.default_text) {
this.selected_item.addClass("chosen-default");
} else {
this.single_deselect_control_build();
this.selected_item.removeClass("chosen-default");
}
return this.selected_item.find("span").html(text);
};
Chosen.prototype.result_deselect = function(pos) {
var result_data;
result_data = this.results_data[pos];
if (!this.form_field.options[result_data.options_index].disabled) {
result_data.selected = false;
this.form_field.options[result_data.options_index].selected = false;
this.selected_option_count = null;
this.result_clear_highlight();
if (this.results_showing) {
this.winnow_results();
}
this.form_field_jq.trigger("change", {
deselected: this.form_field.options[result_data.options_index].value
});
this.search_field_scale();
return true;
} else {
return false;
}
};
Chosen.prototype.single_deselect_control_build = function() {
if (!this.allow_single_deselect) {
return;
}
if (!this.selected_item.find("abbr").length) {
this.selected_item.find("span").first().after("<abbr class=\"search-choice-close\"></abbr>");
}
return this.selected_item.addClass("chosen-single-with-deselect");
};
Chosen.prototype.get_search_text = function() {
return $('<div/>').text($.trim(this.search_field.val())).html();
};
Chosen.prototype.winnow_results_set_highlight = function() {
var do_high, selected_results;
selected_results = !this.is_multiple ? this.search_results.find(".result-selected.active-result") : [];
do_high = selected_results.length ? selected_results.first() : this.search_results.find(".active-result").first();
if (do_high != null) {
return this.result_do_highlight(do_high);
}
};
Chosen.prototype.no_results = function(terms) {
var no_results_html;
no_results_html = $('<li class="no-results">' + this.results_none_found + ' "<span></span>"</li>');
no_results_html.find("span").first().html(terms);
this.search_results.append(no_results_html);
return this.form_field_jq.trigger("chosen:no_results", {
chosen: this
});
};
Chosen.prototype.no_results_clear = function() {
return this.search_results.find(".no-results").remove();
};
Chosen.prototype.keydown_arrow = function() {
var next_sib;
if (this.results_showing && this.result_highlight) {
next_sib = this.result_highlight.nextAll("li.active-result").first();
if (next_sib) {
return this.result_do_highlight(next_sib);
}
} else {
return this.results_show();
}
};
Chosen.prototype.keyup_arrow = function() {
var prev_sibs;
if (!this.results_showing && !this.is_multiple) {
return this.results_show();
} else if (this.result_highlight) {
prev_sibs = this.result_highlight.prevAll("li.active-result");
if (prev_sibs.length) {
return this.result_do_highlight(prev_sibs.first());
} else {
if (this.choices_count() > 0) {
this.results_hide();
}
return this.result_clear_highlight();
}
}
};
Chosen.prototype.keydown_backstroke = function() {
var next_available_destroy;
if (this.pending_backstroke) {
this.choice_destroy(this.pending_backstroke.find("a").first());
return this.clear_backstroke();
} else {
next_available_destroy = this.search_container.siblings("li.search-choice").last();
if (next_available_destroy.length && !next_available_destroy.hasClass("search-choice-disabled")) {
this.pending_backstroke = next_available_destroy;
if (this.single_backstroke_delete) {
return this.keydown_backstroke();
} else {
return this.pending_backstroke.addClass("search-choice-focus");
}
}
}
};
Chosen.prototype.clear_backstroke = function() {
if (this.pending_backstroke) {
this.pending_backstroke.removeClass("search-choice-focus");
}
return this.pending_backstroke = null;
};
Chosen.prototype.keydown_checker = function(evt) {
var stroke, _ref1;
stroke = (_ref1 = evt.which) != null ? _ref1 : evt.keyCode;
this.search_field_scale();
if (stroke !== 8 && this.pending_backstroke) {
this.clear_backstroke();
}
switch (stroke) {
case 8:
this.backstroke_length = this.search_field.val().length;
break;
case 9:
if (this.results_showing && !this.is_multiple) {
this.result_select(evt);
}
this.mouse_on_container = false;
break;
case 13:
if (this.results_showing) {
evt.preventDefault();
}
break;
case 32:
if (this.disable_search) {
evt.preventDefault();
}
break;
case 38:
evt.preventDefault();
this.keyup_arrow();
break;
case 40:
evt.preventDefault();
this.keydown_arrow();
break;
}
};
Chosen.prototype.search_field_scale = function() {
var div, f_width, h, style, style_block, styles, w, _i, _len;
if (this.is_multiple) {
h = 0;
w = 0;
style_block = "position:absolute; left: -1000px; top: -1000px; display:none;";
styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'];
for (_i = 0, _len = styles.length; _i < _len; _i++) {
style = styles[_i];
style_block += style + ":" + this.search_field.css(style) + ";";
}
div = $('<div />', {
'style': style_block
});
div.text(this.search_field.val());
$('body').append(div);
w = div.width() + 25;
div.remove();
f_width = this.container.outerWidth();
if (w > f_width - 10) {
w = f_width - 10;
}
return this.search_field.css({
'width': w + 'px'
});
}
};
return Chosen;
})(AbstractChosen);
}).call(this);
}).apply(root, arguments);
});
}(this));
(function(root) {
define("angular-chosen", ["angular","jquery-chosen"], function() {
return (function() {
/**
* angular-chosen-localytics - Angular Chosen directive is an AngularJS Directive that brings the Chosen jQuery in a Angular way
* @version v1.3.0
* @link http://github.com/leocaseiro/angular-chosen
* @license MIT
*/
(function() {
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
angular.module('localytics.directives', []);
angular.module('localytics.directives').directive('chosen', [
'$timeout', function($timeout) {
var CHOSEN_OPTION_WHITELIST, NG_OPTIONS_REGEXP, isEmpty, snakeCase;
NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
CHOSEN_OPTION_WHITELIST = ['persistentCreateOption', 'createOptionText', 'createOption', 'skipNoResults', 'noResultsText', 'allowSingleDeselect', 'disableSearchThreshold', 'disableSearch', 'enableSplitWordSearch', 'inheritSelectClasses', 'maxSelectedOptions', 'placeholderTextMultiple', 'placeholderTextSingle', 'searchContains', 'singleBackstrokeDelete', 'displayDisabledOptions', 'displaySelectedOptions', 'width', 'includeGroupLabelInSelected', 'maxShownResults'];
snakeCase = function(input) {
return input.replace(/[A-Z]/g, function($1) {
return "_" + ($1.toLowerCase());
});
};
isEmpty = function(value) {
var key;
if (angular.isArray(value)) {
return value.length === 0;
} else if (angular.isObject(value)) {
for (key in value) {
if (value.hasOwnProperty(key)) {
return false;
}
}
}
return true;
};
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
link: function(scope, element, attr, ngModel) {
var chosen, empty, initOrUpdate, match, options, origRender, startLoading, stopLoading, updateMessage, valuesExpr, viewWatch;
scope.disabledValuesHistory = scope.disabledValuesHistory ? scope.disabledValuesHistory : [];
element = $(element);
element.addClass('localytics-chosen');
options = scope.$eval(attr.chosen) || {};
angular.forEach(attr, function(value, key) {
if (indexOf.call(CHOSEN_OPTION_WHITELIST, key) >= 0) {
return attr.$observe(key, function(value) {
options[snakeCase(key)] = String(element.attr(attr.$attr[key])).slice(0, 2) === '{{' ? value : scope.$eval(value);
return updateMessage();
});
}
});
startLoading = function() {
return element.addClass('loading').attr('disabled', true).trigger('chosen:updated');
};
stopLoading = function() {
element.removeClass('loading');
if (angular.isDefined(attr.disabled)) {
element.attr('disabled', attr.disabled);
} else {
element.attr('disabled', false);
}
return element.trigger('chosen:updated');
};
chosen = null;
empty = false;
initOrUpdate = function() {
var defaultText;
if (chosen) {
return element.trigger('chosen:updated');
} else {
$timeout(function() {
chosen = element.chosen(options).data('chosen');
});
if (angular.isObject(chosen)) {
return defaultText = chosen.default_text;
}
}
};
updateMessage = function() {
if (empty) {
element.attr('data-placeholder', chosen.results_none_found).attr('disabled', true);
} else {
element.removeAttr('data-placeholder');
}
return element.trigger('chosen:updated');
};
if (ngModel) {
origRender = ngModel.$render;
ngModel.$render = function() {
origRender();
return initOrUpdate();
};
element.on('chosen:hiding_dropdown', function() {
return scope.$apply(function() {
return ngModel.$setTouched();
});
});
if (attr.multiple) {
viewWatch = function() {
return ngModel.$viewValue;
};
scope.$watch(viewWatch, ngModel.$render, true);
}
} else {
initOrUpdate();
}
attr.$observe('disabled', function() {
return element.trigger('chosen:updated');
});
if (attr.ngOptions && ngModel) {
match = attr.ngOptions.match(NG_OPTIONS_REGEXP);
valuesExpr = match[7];
scope.$watchCollection(valuesExpr, function(newVal, oldVal) {
var timer;
return timer = $timeout(function() {
if (angular.isUndefined(newVal)) {
return startLoading();
} else {
empty = isEmpty(newVal);
stopLoading();
return updateMessage();
}
});
});
return scope.$on('$destroy', function(event) {
if (typeof timer !== "undefined" && timer !== null) {
return $timeout.cancel(timer);
}
});
}
}
};
}
]);
}).call(this);
}).apply(root, arguments);
});
}(this));
/*
* versionControlService.js Copyright 2022 cPanel, L.L.C.
* All rights reserved.
* copyright@cpanel.net http://cpanel.net
* This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define: false, PAGE: false */
define(
'app/services/versionControlService',[
"angular",
"cjt/util/locale",
"cjt/util/parse",
"cjt/io/uapi-request",
"cjt/io/api",
"cjt/io/uapi",
"cjt/services/APIService",
"cjt/filters/qaSafeIDFilter"
],
function(angular, LOCALE, PARSE, UAPIREQUEST) {
"use strict";
var app = angular.module("cpanel.versionControl.service", []);
app.value("PAGE", PAGE);
app.factory("versionControlService", [
"$q",
"APIService",
"$filter",
"PAGE",
"$timeout",
"$rootScope",
function($q, APIService, $filter, PAGE, $timeout, $rootScope) {
var VersionControlService = function() {};
var repos = [];
var fileManagerPath = PAGE.fileManagerURL;
var gitWebPath = PAGE.gitwebURL;
/* Parse Head commit information
* @method parseHeadCommitInformation
* @params {Object} repoInfo Repository Information
* @return {Object} Returns head commit information
*/
function parseHeadCommitInformation(repoInfo) {
// HEAD commit
var commitInfo = {};
commitInfo.lastUpdateSHA = LOCALE.maketext("Not available.");
commitInfo.lastUpdateDate = LOCALE.maketext("Not available.");
commitInfo.commitMessage = LOCALE.maketext("Not available.");
commitInfo.author = LOCALE.maketext("Not available.");
commitInfo.hasHeadInformation = false;
if (typeof repoInfo.last_update !== "undefined" && repoInfo.last_update) {
commitInfo.hasHeadInformation = true;
if (typeof repoInfo.last_update.identifier !== "undefined" &&
repoInfo.last_update.identifier) {
commitInfo.lastUpdateSHA = repoInfo.last_update.identifier;
}
if (typeof repoInfo.last_update.date !== "undefined" &&
repoInfo.last_update.date) {
commitInfo.lastUpdateDate = getHumanReadableTime(repoInfo.last_update.date);
}
if (typeof repoInfo.last_update.author !== "undefined" &&
repoInfo.last_update.author) {
commitInfo.author = repoInfo.last_update.author;
}
if (typeof repoInfo.last_update.message !== "undefined" &&
repoInfo.last_update.message) {
commitInfo.commitMessage = repoInfo.last_update.message;
}
}
return commitInfo;
}
/* Parse Last Deployed Information
* @method parseLastDeployedInformation
* @params {Object} repoInfo Repository Information
* @return {Object} Returns last deployed information
*/
function parseLastDeployedInformation(repoInfo) {
var deployedInfo = {};
// last deployed information
deployedInfo.hasDeploymentInformation = false;
deployedInfo.lastDeployedSHA = LOCALE.maketext("Not available.");
deployedInfo.lastDeployedDate = LOCALE.maketext("Not available.");
deployedInfo.lastDeployedCommitDate = LOCALE.maketext("Not available.");
deployedInfo.lastDeployedCommitMessage = LOCALE.maketext("Not available.");
deployedInfo.lastDeployedAuthor = LOCALE.maketext("Not available.");
if (typeof repoInfo.last_deployment !== "undefined" && repoInfo.last_deployment) {
deployedInfo.hasDeploymentInformation = true;
if (typeof repoInfo.last_deployment.timestamps !== "undefined" &&
typeof repoInfo.last_deployment.timestamps.succeeded !== "undefined" &&
repoInfo.last_deployment.timestamps.succeeded) {
deployedInfo.lastDeployedDate = getHumanReadableTime(repoInfo.last_deployment.timestamps.succeeded);
if (typeof repoInfo.last_deployment.repository_state !== "undefined" &&
repoInfo.last_deployment.repository_state) {
if (typeof repoInfo.last_deployment.repository_state.identifier !== "undefined" &&
repoInfo.last_deployment.repository_state.identifier) {
deployedInfo.lastDeployedSHA = repoInfo.last_deployment.repository_state.identifier;
}
if (typeof repoInfo.last_deployment.repository_state.date !== "undefined" &&
repoInfo.last_deployment.repository_state.date) {
deployedInfo.lastDeployedCommitDate = getHumanReadableTime(repoInfo.last_deployment.repository_state.date);
}
if (typeof repoInfo.last_deployment.repository_state.message !== "undefined" &&
repoInfo.last_deployment.repository_state.message) {
deployedInfo.lastDeployedCommitMessage = repoInfo.last_deployment.repository_state.message;
}
if (typeof repoInfo.last_deployment.repository_state.author !== "undefined" &&
repoInfo.last_deployment.repository_state.author) {
deployedInfo.lastDeployedAuthor = repoInfo.last_deployment.repository_state.author;
}
}
}
}
return deployedInfo;
}
/* Render repository list
* @method refineRepositoryInformation
* @params {Object} repoInfo Repository Information
* @return {Object} Returns a single formatted repository data instance.
*/
function refineRepositoryInformation(repoInfo) {
if (repoInfo && typeof repoInfo !== "undefined") {
// For unique ids
repoInfo.qaSafeSuffix = $filter("qaSafeID")(repoInfo.repository_root);
// File manager url
if (fileManagerPath) {
repoInfo.fileManagerRedirectURL = fileManagerPath + encodeURIComponent(repoInfo.repository_root);
} else {
repoInfo.fileManagerRedirectURL = "";
}
// gitweb url
if (gitWebPath) {
var projectPath = repoInfo.repository_root.replace(/^\/+/g, "") + "/.git";
repoInfo.gitWebURL = gitWebPath + encodeURIComponent(projectPath);
} else {
repoInfo.gitWebURL = "";
}
// has remote
repoInfo.hasRemote = typeof repoInfo.source_repository !== "undefined" && repoInfo.source_repository;
// Clone URL
if (typeof repoInfo.clone_urls !== "undefined" && repoInfo.clone_urls) {
repoInfo.cloneURL = repoInfo.clone_urls.read_write[0] || repoInfo.clone_urls.read_only[0];
}
// Branch information
repoInfo.activeBranch = LOCALE.maketext("Not available.");
repoInfo.hasActiveBranch = false;
if (typeof repoInfo.branch !== "undefined" && repoInfo.branch) {
repoInfo.activeBranch = repoInfo.branch;
repoInfo.hasActiveBranch = true;
}
// Head Commit information
var commitInfo = parseHeadCommitInformation(repoInfo);
repoInfo.lastUpdateSHA = commitInfo.lastUpdateSHA;
repoInfo.lastUpdateDate = commitInfo.lastUpdateDate;
repoInfo.commitMessage = commitInfo.commitMessage;
repoInfo.author = commitInfo.author;
repoInfo.hasHeadInformation = commitInfo.hasHeadInformation;
// Last deployed information
var lastDeployedInfo = parseLastDeployedInformation(repoInfo);
repoInfo.hasDeploymentInformation = lastDeployedInfo.hasDeploymentInformation;
repoInfo.lastDeployedSHA = lastDeployedInfo.lastDeployedSHA;
repoInfo.lastDeployedDate = lastDeployedInfo.lastDeployedDate;
repoInfo.lastDeployedCommitDate = lastDeployedInfo.lastDeployedCommitDate;
repoInfo.lastDeployedCommitMessage = lastDeployedInfo.lastDeployedCommitMessage;
repoInfo.lastDeployedAuthor = lastDeployedInfo.lastDeployedAuthor;
// Is deployable
repoInfo.deployable = PARSE.parsePerlBoolean(repoInfo.deployable);
repoInfo.cloneInProgress = false;
repoInfo.deployInProgress = false;
// Tasks (clone and deploy)
if (typeof repoInfo.tasks !== "undefined" &&
repoInfo.tasks &&
repoInfo.tasks.length > 0) {
for ( var i = 0, len = repoInfo.tasks.length; i < len; i++) {
if (repoInfo.tasks[i].action === "create") {
repoInfo.cloneInProgress = true;
repoInfo.cloneTaskID = repoInfo.tasks[i].id;
}
if (repoInfo.tasks[i].action === "deploy") {
repoInfo.deployInProgress = true;
}
}
}
}
return repoInfo;
}
/* Convert Epoch Time to Human readable
* @method getHumanReadableTime
* @params {Number} epochTime Represents the DateTime in epoch format.
* @return {String} Returns a human readable DateTime string.
*/
function getHumanReadableTime(epochTime) {
if (!epochTime) {
return LOCALE.maketext("Not available.");
} else if (typeof epochTime !== "number") {
epochTime = Number(epochTime);
if ( isNaN(epochTime) ) {
return LOCALE.maketext("Not available.");
}
}
epochTime = Math.floor(epochTime);
return LOCALE.local_datetime(epochTime, "datetime_format_medium");
}
/* Modify Repository List
* @method prepareList
* @params {Array} data List of repositories
* @return {Array} List of Repositories
*/
function prepareList(data) {
var list = [];
if (typeof data !== "undefined" && data !== null ) {
for (var i = 0, len = data.length; i < len; i++) {
var value = refineRepositoryInformation(data[i]);
list.push(value);
}
}
return list;
}
/* Gets the index of specific repository in the list
* @method getRepositoryIndex
* @params {Array} reposList List of Repositories
* @params {String} path Repository path to look for
* @return {Number} Returns the index of the repository
*/
function getRepositoryIndex(reposList, path) {
var index = null;
if (typeof reposList !== undefined && reposList) {
for (var repoIndex = 0, repoLength = reposList.length; repoIndex < repoLength; repoIndex++) {
if (reposList[repoIndex].repository_root === path) {
index = repoIndex;
break;
}
}
}
return index;
}
/* Copies supplied string to the user's clipboard
* @method copyTextToClipboard
* @params {String} text String to be copied to the user's clipboard.
*/
function copyTextToClipboard(text) {
var textArea = document.createElement("textarea");
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand("copy");
} catch (e) {
throw new Error(LOCALE.maketext("You cannot copy the “[_1]” clone [output,acronym,URL,Uniform Resource Locator] to the clipboard.", text));
}
document.body.removeChild(textArea);
}
// get repository list from PAGE data
repos = prepareList(PAGE.repos);
VersionControlService.prototype = new APIService();
angular.extend(VersionControlService.prototype, {
/**
* Gets the list of cached repositories.
* Used for testing purposes
*
* @method getRepositoryList
* @return {Array} Array of cached repositories
*/
getRepositoryList: function() {
return repos;
},
/**
* List repositories
*
* @method listRepositories
* @param {Boolean} forceLoad Forces the API request to take place, rather than pulling the data from the JS vars.
* @param {String} attributeStr Comma seperated list of fields that UI needs
* @return {Promise} When fulfilled, will return the list of repositories.
*/
listRepositories: function(forceLoad, attributeStr) {
if (forceLoad) {
var apiCall = new UAPIREQUEST.Class();
apiCall.initialize("VersionControl", "retrieve");
if (attributeStr) {
apiCall.addArgument("fields", attributeStr);
}
return this.deferred(apiCall).promise
.then(function(response) {
try {
repos = prepareList(response.data);
return $q.resolve(repos);
} catch (err) {
return $q.reject(err);
}
})
.catch(function(error) {
return $q.reject(error);
});
} else {
return $q.resolve(repos);
}
},
/**
* Gets information of a single repository
*
* @method getRepositoryInformation
* @param {String} repositoryPath Repository root path
* @param {String} attributeStr Fields to request
* @return {Promise} When fulfilled, will return repository information.
*/
getRepositoryInformation: function(repositoryPath, attributeStr) {
var apiCall = new UAPIREQUEST.Class();
apiCall.initialize("VersionControl", "retrieve");
if (repositoryPath) {
apiCall.addFilter("repository_root", "eq", repositoryPath);
if (attributeStr) {
apiCall.addArgument("fields", attributeStr);
}
} else {
throw new Error(LOCALE.maketext("Repository path cannot be empty."));
}
var repository = [];
return this.deferred(apiCall).promise
.then(function(response) {
try {
repository = prepareList(response.data);
if (repository && repository.length > 0) {
return $q.resolve(repository[0]);
} else {
return $q.reject(LOCALE.maketext("The system could not find the “[_1]” repository.", repositoryPath));
}
} catch (err) {
return $q.reject(err);
}
})
.catch(function(error) {
return $q.reject(error);
});
},
/**
* Clone to Clipboard
*
* @method cloneToClipboard
* @param {String} cloneUrl The URL to be used to clone repos.
* @return {Boolean} Returns true on success.
*/
cloneToClipboard: function(cloneUrl) {
if (typeof cloneUrl === "string" && cloneUrl !== "") {
// set clipboard to the clone URL
copyTextToClipboard(cloneUrl);
return true;
} else {
// throw dev error
throw new Error(LOCALE.maketext("“[_1]” is not a valid clone [output,acronym,URL,Universal Resource Locator].", cloneUrl));
}
},
/**
* Creates or clones repository
*
* @method createRepository
* @return {Promise} When fulfilled, returns created repository information.
*/
createRepository: function(name, fullPath, cloneURL) {
var apiCall = new UAPIREQUEST.Class();
apiCall.initialize("VersionControl", "create");
apiCall.addArgument("name", name);
apiCall.addArgument("repository_root", fullPath);
apiCall.addArgument("type", "git");
// Creates a new repository when cloneURL is not provided
if (typeof cloneURL !== "undefined" && cloneURL) {
apiCall.addArgument("source_repository", JSON.stringify({
"remote_name": "origin",
"url": cloneURL
}));
}
return this.deferred(apiCall).promise
.then(function(response) {
var processedData = {};
// update the repository list with data returned from create
if (response.data) {
processedData = refineRepositoryInformation(response.data);
repos.push(processedData);
}
return processedData;
})
.catch(function(error) {
return $q.reject(error);
});
},
/**
* Delete repository
*
* @method deleteRepository
* @param {String} repositoryRoot repository root path
* @return {Promise} Returns a promise.
*/
deleteRepository: function(repositoryRoot) {
if (typeof repositoryRoot === "string" && repositoryRoot !== "") {
var apiCall = new UAPIREQUEST.Class();
apiCall.initialize("VersionControl", "delete");
apiCall.addArgument("repository_root", repositoryRoot);
return this.deferred(apiCall).promise
.then(function() {
var index = getRepositoryIndex(repos, repositoryRoot);
if (typeof index === "number" && index >= 0) {
repos.splice(index, 1);
}
return $q.resolve();
})
.catch(function(error) {
return $q.reject(error);
});
}
},
/**
* Updates repository
*
* @method updateRepository
* @param {String} repositoryRoot repository root path
* @param {String} repositoryName name of the repository
* @param {String} branch active branch name
* @return {Promise} Returns a promise.
*/
updateRepository: function(repositoryRoot, repositoryName, branch) {
var apiCall = new UAPIREQUEST.Class();
apiCall.initialize("VersionControl", "update");
apiCall.addArgument("repository_root", repositoryRoot);
if (typeof repositoryName === "string" && repositoryName) {
apiCall.addArgument("name", repositoryName);
}
if (typeof branch === "string" && branch) {
apiCall.addArgument("branch", branch);
}
return this.deferred(apiCall).promise
.then(function(response) {
if (response.data) {
var index = getRepositoryIndex(repos, repositoryRoot);
if (typeof index === "number" && index >= 0) {
repos[index] = refineRepositoryInformation(response.data);
}
}
return response;
})
.catch(function(error) {
return $q.reject(error);
});
},
/**
* Updates changes from remote
*
* @method updateRepository
* @param {String} repositoryRoot repository root path
* @return {Promise} Returns a promise.
*/
updateFromRemote: function(repositoryRoot, branch) {
var apiCall = new UAPIREQUEST.Class();
apiCall.initialize("VersionControl", "update");
apiCall.addArgument("repository_root", repositoryRoot);
apiCall.addArgument("branch", branch || "");
return this.deferred(apiCall).promise
.then(function(response) {
if (response.data) {
var index = getRepositoryIndex(repos, repositoryRoot);
if (typeof index === "number" && index >= 0) {
repos[index] = refineRepositoryInformation(response.data);
}
}
return response;
})
.catch(function(error) {
return $q.reject(error);
});
},
/**
* Deploy repository
*
* @method updateRepository
* @param {String} repositoryRoot repository root path
* @return {Promise} Returns a promise.
*/
deployRepository: function(repositoryRoot) {
var apiCall = new UAPIREQUEST.Class();
apiCall.initialize("VersionControlDeployment", "create");
apiCall.addArgument("repository_root", repositoryRoot);
return this.deferred(apiCall).promise
.then(function(response) {
return response;
})
.catch(function(error) {
return $q.reject(error);
});
}
});
return new VersionControlService();
}
]);
}
);
/*
* services/sseCloneApi.js Copyright 2022 cPanel, L.L.C.
* All rights reserved.
* copyright@cpanel.net http://cpanel.net
* This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define: false */
define('app/services/sseAPIService',[
"angular",
"cjt/core",
"lodash",
], function(
angular,
CJT,
_
) {
"use strict";
var app = angular.module("cpanel.versionControl.sseAPIService", []);
app.factory("sseAPIService", [
"$rootScope",
function(
$rootScope
) {
/**
* Format the data based on the configuration requested.
* Currently only has the option to convert from string to JSON if config.json is true.
*
* @param {Object} config - SSE configuration
* @param {String} data
* @return {Any}
*/
var _formatData = function(config, data) {
if (config && config.json && data) {
data = JSON.parse(data);
}
return data;
};
/**
* Custom event sent when JSON parsing is requested but fails for some reason.
*
* @event sse:<eventName>:error
* @property {Object} data
* @property {String} data.error - Error message from the JSON parser.
* @property {String} data.data - Data passed with the message that could not be parsed.
*/
/**
* Custom event sent.
*
* @event sse:<eventName>
* @property {Object} data
*/
/**
* Send a format error if the parsing fails
*
* @param {String} eventName
* @param {String} exception
* @param {String} data
* @fires sse:<evenName>:error
*/
var _sendFormatErrorEvent = function(eventName, exception, data) {
$rootScope.$broadcast(
"sse:" + eventName + ":error",
{
data: data,
error: exception
}
);
};
/**
* Create a message event handler callback. The callback will annotate the event with
* a eventName field and will generated an angularjs event with the following name:
*
* sse:<eventName>
*
* If a data element is available it will send the data along with the event in the
* data field.
*
*
* @param {String} eventName The name of the event.
* @param {Object} config configuration
* @return {Function}
*/
var makeHandler = function(eventName, config) {
return function(e) {
e.eventName = eventName;
var data = e.data;
try {
data = _formatData(config, data);
} catch (exception) {
_sendFormatErrorEvent(eventName, exception, data);
return;
}
$rootScope.$broadcast("sse:" + eventName, data);
};
};
/**
* Fired when the sse process is done.
*
* @event sse:done
*/
/**
* Fired when sse generated an error.
*
* @event sse:error
*/
/**
* Fired when the sse is running and the page is being unloaded.
*
* @event sse:beforeunload
*/
/**
* Connect the specified SSE url fire an angular event for the requested events.
*
* @param {String} url - url to connect to the sse event source.
* @param {Array[String]} [events] - array of additional events to register.
* @param {Object} [config] - optional configuration options
* @param {Boolean} [config.json] - if true, will parse the e.data as json. otherwise, just returns the data to the caller as is.
* @fires sse:beforeunload, sse:error, sse:done, sse:*
*/
var connect = function connect(url, events, config) {
if (!events) {
events = [];
}
var sseConfig = config || {};
var sse = new EventSource(url);
// Setup known events
if (events) {
events.forEach(function(event) {
sse.addEventListener(event, makeHandler(event, sseConfig));
});
}
// Setup the error event handler
sse.onerror = function(e) {
$rootScope.$broadcast("sse:error", e);
};
// Setup the beforeunload event handler
window.addEventListener("beforeunload", function(e) {
if (sse) {
sse.close();
sse = null;
$rootScope.$broadcast("sse:beforeunload", e);
}
});
return sse;
};
/**
* Fired when the sse polyfill is loaded when needed. It will fire both
* when the polyfill is needed and finished loading and when the polyfill
* is not needed.
*
* @event sse:ready
*/
/**
* Initialize the SSE resources
*
* @param {Function} [ready] Optional callback to call when the sse is ready to run.
* @fires sse:ready
*/
var initialize = function initialize(ready) {
// Microsoft browsers (including Edge)
// don’t support SSE as of November 2017.
// https://developer.microsoft.com/en-us/microsoft-edge/platform/status/serversenteventseventsource/
if (!window.EventSource) {
var script = document.createElement("script");
script.src = CJT.buildFullPath("libraries/eventsource-polyfill/eventsource.js");
script.onload = function() {
if (ready) {
ready();
}
$rootScope.$broadcast("sse:ready");
};
document.body.appendChild(script);
} else {
if (ready) {
ready();
}
$rootScope.$broadcast("sse:ready");
}
};
/**
* Close the sse connection and clean up the sseApi state.
*
* @param {Object} SSE object
*/
var close = function(sse) {
if (sse) {
sse.close();
}
};
return {
initialize: initialize,
connect: connect,
close: close
};
}
]);
});
/*
# version_control/views/listRepositoriesController.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define, PAGE */
define(
'app/views/listRepositoriesController',[
"angular",
"lodash",
"cjt/util/locale",
"cjt/util/table",
"uiBootstrap",
"cjt/filters/wrapFilter",
"cjt/services/cpanel/nvDataService",
"app/services/versionControlService",
"app/services/sseAPIService",
"cjt/services/alertService",
"cjt/directives/alert",
"cjt/directives/alertList",
"cjt/directives/actionButtonDirective"
],
function(angular, _, LOCALE, Table) {
"use strict";
var app = angular.module("cpanel.versionControl");
app.value("PAGE", PAGE);
var controller = app.controller(
"ListRepositoriesController",
["$scope", "$window", "$location", "versionControlService", "sseAPIService", "PAGE", "nvDataService", "alertService", "$timeout",
function($scope, $window, $location, versionControlService, sseAPIService, PAGE, nvDataService, alertService, $timeout) {
var repositories = this;
repositories.isLoading = false;
repositories.list = [];
repositories.cloningList = [];
var sseURL = PAGE.securityToken + "/sse/UserTasks";
var SSEObj;
// SSE events and config
var events = [ "task_processing", "task_complete", "task_failed" ];
var config = { json: true };
repositories.homeDirPath = PAGE.homeDir;
repositories.hasFilemanagerAccess = (PAGE.hasFileManagerAccess === "0") ? false : true;
repositories.hasShellAccess = (PAGE.hasShellAccess === "0") ? false : true;
var table = new Table();
table.setSort("name", "asc");
repositories.meta = table.getMetadata();
repositories.filteredList = table.getList();
repositories.paginationMessage = table.paginationMessage;
repositories.meta.pageSize = parseInt(PAGE.reposListPageSize, 10);
/**
* Search repository by name and path
* @method searchByNameOrFolder
* @param {Object} item Item in repositories list
* @param {String} searchText Text to search for
*/
function searchByNameOrFolder(item, searchText) {
searchText = searchText.toLowerCase();
return item.name.toLowerCase().indexOf(searchText) !== -1 ||
item.repository_root.toLowerCase().indexOf(searchText) !== -1;
}
table.setSearchFunction(searchByNameOrFolder);
/**
* Render repository list
* @method render
*/
repositories.render = function() {
repositories.filteredList = table.update();
};
/**
* Sort repository list
* @method sortList
*/
repositories.sortList = function() {
repositories.render();
};
/**
* Select repository page
* @method selectPage
*/
repositories.selectPage = function() {
repositories.render();
};
/**
* Selects page size
* @method selectPageSize
*/
repositories.selectPageSize = function() {
repositories.render();
if (PAGE.reposListPageSize !== repositories.meta.pageSize) {
nvDataService.setObject({ repos_list_page_size: repositories.meta.pageSize })
.then(function() {
PAGE.reposListPageSize = repositories.meta.pageSize;
})
.catch(function(error) {
alertService.add({
type: "danger",
message: error.message,
closeable: true,
replace: false,
group: "versionControl"
});
});
}
};
/**
* Implements search for repository
* @method searchList
*/
repositories.searchList = function() {
repositories.render();
};
/**
* Initialization of the List Repositories page.
* @method init
*/
repositories.init = function() {
var pageErrors = PAGE.repoErrors;
if (pageErrors.length > 0) {
// Potential cache corruption, attempting to load via JS to display error messages.
pageErrors.forEach(function(error) {
alertService.add({
type: "danger",
message: error,
closeable: true,
replace: false,
group: "versionControl"
});
});
} else {
// Updates the list with the current API cache.
repositories.updateRepositoriesList(true);
}
};
/**
* Extend repository object to add few fields to manage state
* @method extendRepositoryObject
* @param {Object} repoObject repository
* @returns {Object} updated repository object
*/
function extendRepositoryObject(repoObject) {
if (repoObject) {
repoObject.isExpanded = false;
repoObject.detailsLoading = false;
repoObject.delete_requested = false;
repoObject.cloneState = "";
}
return repoObject;
}
/**
* Resets clone state of repository
* @method clearCloning
* @param {Object} repoObject repository
* @returns {Object} updated repository object
*/
function clearCloning(repoObject) {
if (repoObject) {
repoObject.cloneState = "";
repoObject.cloneInProgress = false;
delete repoObject.cloneTaskID;
}
return repoObject;
}
/**
* Calls the API to update the repositories list.
* @method updateRepositoriesList
* @param {Boolean} forceLoad Tells the method to pull the data from the API.
*/
repositories.updateRepositoriesList = function(forceLoad) {
repositories.isLoading = true;
var attributeStr = (forceLoad) ? "name,tasks" : null;
return versionControlService.listRepositories(forceLoad, attributeStr)
.then(function(response) {
repositories.list = _.map(response, function(obj) {
return extendRepositoryObject(obj);
});
for (var i = 0, len = repositories.list.length; i < len; i++) {
if (repositories.list[i].cloneInProgress) {
repositories.cloningList.push(
repositories.list[i]
);
}
}
// Initiate SSE when atleast one repository is being cloned
if (repositories.cloningList && repositories.cloningList.length > 0) {
sseAPIService.initialize();
}
table.load(repositories.list);
repositories.render();
repositories.isLoading = false;
}, function(error) {
alertService.add({
type: "danger",
message: error.message,
closeable: true,
replace: false,
group: "versionControl"
});
repositories.isLoading = false;
});
};
/**
* Handles task_processing.
*
* @method
* @param {sse:task_processing} event - Task processing event.
* @param {Object} data - Data
* @listens sse:task_processing
*/
$scope.$on("sse:task_processing", function(event, data) {
var taskID = data.task_id;
var cloneItem = _.find(repositories.cloningList, function(o) {
return o.cloneTaskID === taskID;
});
if (cloneItem) {
var unfilteredIndex = _.indexOf(repositories.list, cloneItem);
if (unfilteredIndex !== -1) {
cloneItem.cloneState = "processing";
_.extend( repositories.list[unfilteredIndex], cloneItem );
$scope.$apply(repositories.render);
}
}
});
/**
* Handles task_complete.
*
* @method
* @param {sse:task_complete} event - Task complete event.
* @param {Object} data - Data
* @listens sse:task_complete
*/
$scope.$on("sse:task_complete", function(event, data) {
var taskID = data.task_id;
var cloneItem = _.find(repositories.cloningList, function(o) {
return o.cloneTaskID === taskID;
});
if (cloneItem) {
var unfilteredIndex = _.indexOf(repositories.list, cloneItem);
if (unfilteredIndex !== -1) {
cloneItem.cloneState = "complete";
_.extend( repositories.list[unfilteredIndex], cloneItem );
$scope.$apply(repositories.render);
// removing object from clonning list because clone is complete
_.remove(repositories.cloningList, cloneItem);
if (repositories.cloningList.length === 0) {
sseAPIService.close(SSEObj);
}
$timeout(function() {
// Using timeout to visually display success on the row and later change it to a normal row.
return versionControlService.getRepositoryInformation(cloneItem.repository_root, "name,tasks")
.then(function(response) {
var repoDetails = extendRepositoryObject(response);
// updating the repository list with new information so that the row is active
_.extend(repositories.list[unfilteredIndex], clearCloning(repoDetails));
// Remove clone information message
alertService.removeById(taskID, "versionControl");
repositories.render();
}, function(error) {
// Remove clone information message
alertService.removeById(taskID, "versionControl");
// display error
alertService.add({
type: "danger",
message: error.message,
closeable: true,
replace: false,
group: "versionControl"
});
});
}, 5000);
}
}
});
/**
* Handles task_failed.
*
* @method
* @param {sse:task_failed} event - Task failed event.
* @param {Object} data - Data
* @listens sse:task_failed
*/
$scope.$on("sse:task_failed", function(event, data) {
var taskID = data.task_id;
var cloneItem = _.find(repositories.cloningList, function(o) {
return o.cloneTaskID === taskID;
});
if (cloneItem) {
var unfilteredIndex = _.indexOf(repositories.list, cloneItem);
_.remove(repositories.cloningList, cloneItem);
if (repositories.cloningList.length === 0) {
sseAPIService.close(SSEObj);
}
if (unfilteredIndex !== -1) {
repositories.list.splice(unfilteredIndex, 1);
alertService.add({
type: "danger",
message: LOCALE.maketext("Error occurred while cloning repository “[_1]”.", cloneItem.name),
closeable: true,
replace: false,
group: "versionControl"
});
$scope.$apply(repositories.render);
}
}
});
/**
* Handles ready.
*
* @method
* @param {sse:ready} event - Task ready event.
* @listens sse:ready
*/
$scope.$on("sse:ready", function(event) {
SSEObj = sseAPIService.connect(sseURL, events, config);
});
/**
* Handles destroy.
*
* @method
* @listens $destroy
*/
$scope.$on("$destroy", function() {
if (SSEObj) {
sseAPIService.close(SSEObj);
}
});
/**
* Opens repository in gitWeb
* @method redirectToGitWeb
* @param {String} gitWebURL gitWebURL for the repository
* @param {String} repoName Repository name
*/
repositories.redirectToGitWeb = function(gitWebURL, repoName) {
if (gitWebURL) {
$window.open(gitWebURL, repoName + "GitWeb");
} else {
alertService.add({
type: "danger",
message: LOCALE.maketext("Unable to find repository web url"),
closeable: true,
replace: false,
group: "versionControl"
});
}
};
/**
* Opens repository path in file manager
* @method redirectToFileManager
* @param {String} fileManagerURL file Manager url for the repository path
* @param {String} repoName Repository name
*/
repositories.redirectToFileManager = function(fileManagerURL, repoName) {
if (fileManagerURL) {
$window.open(fileManagerURL, repoName + "FileManager");
} else {
alertService.add({
type: "danger",
message: LOCALE.maketext("Unable to redirect to File Manager interface"),
closeable: true,
replace: false,
group: "versionControl"
});
}
};
/**
* Copies the repo's clone link to you machine's clipboard
* @method cloneToClipboard
* @param {String} cloneUrl The URL to be used to clone repos.
*/
repositories.cloneToClipboard = function(cloneUrl) {
try {
var result = versionControlService.cloneToClipboard(cloneUrl);
if (result) {
alertService.add({
type: "success",
message: LOCALE.maketext("The system successfully copied the “[_1]” clone [output,acronym,URL,Uniform Resource Locator] to the clipboard.", cloneUrl),
closeable: true,
replace: false,
autoClose: 10000,
group: "versionControl"
});
}
} catch (error) {
alertService.add({
type: "danger",
message: error,
closeable: true,
replace: false,
group: "versionControl"
});
}
};
/**
* Create Repository View
* @method createRepository
*/
repositories.createRepository = function() {
alertService.clear("", "versionControl");
$location.path("/create");
};
/**
* Deletes a repository
* @method delete
* @param {String} repo The repository to delete.
* @return {Promise} Returns a promise from the VersionControlService.deleteRepository method for success/error handling when the user requests to delete a repository.
*/
repositories.delete = function(repo) {
repo.removing = true;
var successMessage;
if (repositories.hasFilemanagerAccess) {
successMessage = LOCALE.maketext("The system successfully removed the “[_1]” repository from the list of [asis,cPanel]-managed repositories. You can use the [output,url,_2,File Manager,target,_3] interface to delete the repository contents.", repo.name, repo.fileManagerRedirectURL, "file-manager");
} else {
successMessage = LOCALE.maketext("The system successfully removed the “[_1]” repository from the list of [asis,cPanel]-managed repositories.", repo.name);
}
return versionControlService.deleteRepository(repo.repository_root)
.then(function() {
table.remove(repo);
repositories.render();
alertService.add({
type: "success",
message: successMessage,
closeable: true,
replace: false,
autoClose: false,
group: "versionControl"
});
}, function(error) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system could not remove the “[_1]” repository in the “[_2]” directory.", repo.name, repo.repository_root),
closeable: true,
replace: false,
group: "versionControl"
});
repo.removing = false;
repo.delete_requested = false;
});
};
/**
* Delete repository confirmation message.
* @method deleteText
* @param {Object} repo The repository's data representation of the repository that should be deleted.
* @return {String} Returns a dynamic string as a message to the user, that cooresponds to the exact repository within the delete action-scope, to inform/ask the user if they are certain that they would like to permanatly delete a repository from their cPanel instance/account/system.
*/
repositories.deleteText = function(repo) {
return LOCALE.maketext("Are you sure that you want to remove the “[_1]” repository from the list of [asis,cPanel]-managed repositories?", repo.name);
};
/**
* Redirects user to the version control manage page for a specified repo.
* @method manageRepository
* @param {String} repoPath Represents the repository_root of the repo object in the repo list.
*/
repositories.manageRepository = function(repoPath) {
$location.path("/manage/" + encodeURIComponent(repoPath) + "/basic-info");
};
/**
* Get the repository index.
* @method getRepositoryIndex
* @param {String} repositoryRoot Repository root
* @return {Number} return the index
*/
function getRepositoryIndex(repositoryRoot) {
return _.findIndex(repositories.filteredList, function(o) {
return o.repository_root === repositoryRoot;
});
}
/* Gets repository details
* @method repositories.getRepositoryDetails
* @param {Object} repo Repository
* @param {Boolean} expandState Current Expanded state
* @return {Promise} return promise
*/
repositories.getRepositoryDetails = function(repo, expandState) {
repo.detailsLoading = true;
var index;
// When changing from collapse to expand
if (expandState) {
return versionControlService.getRepositoryInformation(repo.repository_root, "name,clone_urls,branch,last_update,source_repository")
.then(function(response) {
index = getRepositoryIndex(repo.repository_root);
if (index !== -1) {
response.detailsLoading = false;
response.isExpanded = true;
// Retrieving back the state of delete
response.delete_requested = repo.delete_requested;
_.assign(repo, response);
}
// Use assign to update the source object in-place
_.assign(repo, response);
}, function(error) {
alertService.add({
type: "danger",
message: error.message,
closeable: true,
replace: false,
group: "versionControl"
});
});
} else {
repo.detailsLoading = false;
repo.isExpanded = false;
}
};
repositories.init();
}
]
);
return controller;
}
);
/*
* version_control/utils/cloneUrlParser.js Copyright(c) 2020 cPanel, L.L.C.
* All rights reserved.
* copyright@cpanel.net http://cpanel.net
* This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* eslint-env amd */
define('app/utils/cloneUrlParser',[],function() {
"use strict";
function parseCloneUrl(cloneUrl) {
var parts = {};
if (!cloneUrl) {
return parts;
}
// Correct IPv6 URLs of the form http(s)://[<ipv6addr]/... are handled properly.
// However, incorrect IPv6 URLs - http(s)://<ipv6addr>/... are not handled well
// because the parser assumes that if there is a : then that represents a port
// number and thus authority is incorrectly parsed. Here we just parse out the
// authority portion and check for more than 2 :'s. If found then the URL is not
// valid
var authority = cloneUrl.match(/^(https?:\/\/)?(?!\[)([^/]+)\/*(?!\])/);
if (authority !== null && authority[2].match(/.*:.*:.*/)) {
return parts;
}
parts.scheme = parseUrlParts(cloneUrl.match(/^\S+:\/\//i));
parts.userInfo = parseUrlParts(cloneUrl.match(/^\S+@/i));
parts.ipv6Authority = parseUrlParts(cloneUrl.match(/^\[\S+\]/i));
if (parts.ipv6Authority) {
parts.ipv6Authority = parts.ipv6Authority.replace(/(\[|\])/gi, "");
}
parts.authority =
parts.ipv6Authority === null
? parseUrlParts(cloneUrl.split(/((:\d+\/)|(\/|:))/i))
: null;
// Parse out the port if it exists.
parts.port = (cloneUrl.match(/^:\d+\//i)) ? parseUrlParts(cloneUrl.match(/^:(\d+)/i), 1) : null;
parts.path = parseUrlParts(cloneUrl.match(/^\S+/i));
parts.unparsed = cloneUrl;
function parseUrlParts(matches, returnIndex) {
returnIndex = returnIndex || 0;
if (matches !== null && matches.length > 0) {
cloneUrl = cloneUrl.replace(matches[0], "");
return matches[returnIndex];
}
return null;
}
return parts;
}
return {
parse: parseCloneUrl,
};
});
/*
* version_control/services/knownHostsService.js Copyright 2022 cPanel, L.L.C.
* All rights reserved.
* copyright@cpanel.net http://cpanel.net
* This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* eslint-env amd */
/* global PAGE: false */
define(
'app/services/knownHostsService',[
"angular",
"cjt/util/locale",
"cjt/util/parse",
"cjt/io/uapi-request",
"cjt/io/uapi",
"cjt/services/APIService",
],
function(angular, LOCALE, PARSE, UAPIREQUEST) {
"use strict";
var app = angular.module("cpanel.versionControl.knownHostsService", ["cjt2.services.api"]);
app.value("PAGE", PAGE); // TODO: Consolidate this higher up
app.factory("knownHostsService", [
"$q",
"APIService",
"$filter",
"PAGE",
"$timeout",
"$rootScope",
function($q, APIService, $filter, PAGE, $timeout, $rootScope) {
var KnownHostsService = function() {};
KnownHostsService.prototype = new APIService();
angular.extend(KnownHostsService.prototype, {
/**
* Checks whether the current keys for a given hostname/port combination
* are in the known_hosts file. This is helpful to use before cloning or
* pulling, since the errors provided by those APIs are not very clear
* when keys are missing or mismatched.
*
* The known_hosts file differentiates by port, so that is why we need to
* provide the port.
*
* @param {String} hostname The hostname to check
* @param {Number|String} port The port to check
* @return {Promise} If resolved, then the keys already exists in
* the known_hosts file. If rejected, then the keys
* don't exist in the known_hosts file. In most cases
* the object provided to the promise callback will
* contain a 'keys' property containing the current
* keys for the server.
*/
verify: function(hostname, port) {
var apiCall = new UAPIREQUEST.Class();
apiCall.initialize("KnownHosts", "verify");
apiCall.addArgument("host_name", hostname);
if (port) {
apiCall.addArgument("port", port);
}
return this.deferred(apiCall).promise.then(
function success(res) {
var data = res && res.data || {};
if (data.status) {
return {
status: "recognized",
};
} else if (data.host.length && data.failure_type) {
return $q.reject({
status: "unrecognized-" + data.failure_type,
keys: data.host,
});
}
return $q.reject({
status: "unrecognized-unknown",
});
},
function failure(error) {
return $q.reject({
status: "unrecognized-unknown",
error: error,
});
}
);
},
/**
* Adds the current keys for an SSH server listening on a given
* hostname/port combination to the user's known_hosts file.
*
* @param {String} hostname The server's hostname
* @param {Number|String} port The server's port
* @return {Promise} If resolved, then the operation was successful.
* Rejections are not caught or manipulated because
* we cannot be sure what the status was before we
* tried to add the host keys, so we just pass the
* API error through as normal.
*/
create: function(hostname, port) {
var apiCall = new UAPIREQUEST.Class();
apiCall.initialize("KnownHosts", "create");
apiCall.addArgument("host_name", hostname);
if (port) {
apiCall.addArgument("port", port);
}
return this.deferred(apiCall).promise.then(function(res) {
return {
status: "recognized",
};
});
}
});
return new KnownHostsService();
}
]);
}
);
/*
* version_control/services/sshKeyVerification.js Copyright 2022 cPanel, L.L.C.
* All rights reserved.
* copyright@cpanel.net http://cpanel.net
* This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* eslint-env amd */
define('app/services/sshKeyVerification',[
"angular",
"lodash",
"cjt/util/locale",
"app/utils/cloneUrlParser",
"ngSanitize",
"uiBootstrap",
"app/services/knownHostsService",
"cjt/directives/actionButtonDirective",
], function(
angular,
_,
LOCALE,
cloneUrlParser
) {
"use strict";
angular
.module("cpanel.versionControl.sshKeyVerificationService", ["cpanel.versionControl.knownHostsService", "ui.bootstrap", "ngSanitize", "cjt2.directives.actionButton", ])
.factory("sshKeyVerification", [
"knownHostsService",
"$uibModal",
function(
knownHostsService,
$uibModal
) {
var _memoizedVerify = _initMemoizedVerify();
var service = {
// A memoized version of knownHostsService.verify
verify: _memoizedVerify,
/**
* Get the hostname and port of an SSH-based clone URL.
*
* @param {String} cloneUrl The URL to parse
* @return {Object|Undefined} An object with hostname and port properties, unless
* it is not an SSH-based URL
*/
getHostnameAndPort: function getHostnameAndPort(cloneUrl) {
var parts = cloneUrlParser.parse(cloneUrl);
if (parts.scheme === "ssh://" || (!parts.scheme && parts.userInfo)) {
return {
hostname: parts.authority || parts.ipv6Authority,
port: parts.port,
};
}
},
/**
* Opens a modal for key verification so that the user can choose
* whether to accept and save the key or not.
*
* @param {Object} props An object of properties that the modal will use.
* @param {String} props.hostname The server's hostname.
* @param {String|Number} [props.port] The server's port (optional).
* @param {String} props.type The key's failure type (unrecognized-new, unrecognized-changed)
* @param {Function} [props.onAccept] A callback function that is called when someone chooses to accept
* the new/changed key and save it to known_hosts. This function is
* called with one argument - a promise that resolves when the changes
* are successfully saved and rejects when it's unsuccessful.
* @return {Modal} An angular-ui-bootstrap modal instance
*/
openModal: function openModal(props) {
var self = this;
self.modal = $uibModal.open({
templateUrl: "views/sshKeyVerification.ptt",
controllerAs: "modal",
controller: KeyVerificationController,
resolve: {
props: function() {
return props;
},
},
});
self.modal.result.finally(function() {
delete self.modal;
});
return self.modal;
},
};
/**
* The constructor for the SSH key verification modal controller.
*
* See service.openModal for documentation on the props argument.
*/
function KeyVerificationController(props) {
_.assign(this, props);
}
KeyVerificationController.$inject = ["props"];
_.assign(KeyVerificationController.prototype, {
/**
* Attempt to add the host to the known_hosts file and continue with
* creation.
*
* @return {Promise} When resolved, we successfully added the host.
* When rejected, something went wrong.
*/
acceptIdentity: function acceptIdentity() {
var self = this;
var acceptPromise = knownHostsService.create( self.hostname, self.port ).then(
function success(data) {
return data.status;
}
).finally(function() {
// We attempted a change to the known_hosts file, so our cached verification results are probably stale
var cacheKey = _memoizedVerifyResolver(self.hostname, self.port);
_memoizedVerify.cache.delete(cacheKey);
_memoizedVerify(self.hostname, self.port);
});
return self.onAccept ? self.onAccept(acceptPromise) : acceptPromise;
},
rejectIdentity: function rejectIdentity() {
service.modal.dismiss();
},
newKeyIntro: function newKeyIntro() {
return LOCALE.maketext("You have not connected this [asis,cPanel] account to the SSH server for “[output,strong,_1].” The system cannot verify the server’s identity.", this.hostname);
},
changedKeyIntro: function changedKeyIntro() {
return LOCALE.maketext("The current identity of the SSH server at “[output,strong,_1]” does not match its identity in your account’s [asis,known_hosts] file.", this.hostname);
},
keyIsNew: function keyIsNew() {
return this.type === "unrecognized-new";
},
keyIsChanged: function keyIsChanged() {
return this.type === "unrecognized-changed";
},
});
/**
* These are some small caching optimizations for the knownHostsService.verify
* method so that we don't make repeated requests for identical hostname/port
* combinations unless we know something has changed.
*/
function _memoizedVerifyResolver(hostname, port) {
port = port ? ":" + port : "";
return hostname + port;
}
function _initMemoizedVerify() {
var memoizedVerify = _.memoize(knownHostsService.verify, _memoizedVerifyResolver);
var memoizedVerifyCache = memoizedVerify.cache;
var boundMemoizedVerify = memoizedVerify.bind(knownHostsService);
boundMemoizedVerify.cache = memoizedVerifyCache;
return boundMemoizedVerify;
}
return service;
}
]);
});
/*
* cpanel - base/frontend/jupiter/_assets/services/directoryLookupService.js
* Copyright(c) 2020 cPanel, L.L.C.
* All rights reserved.
* copyright@cpanel.net http://cpanel.net
* This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define: false */
define(
'app/services/directoryLookupService',[
"angular",
"lodash",
"cjt/core",
"cjt/util/locale",
"cjt/io/api",
"cjt/io/uapi-request",
"cjt/io/uapi",
"cjt/util/parse",
],
function(angular, _, CJT, LOCALE, API, APIREQUEST, APIDRIVER, PARSER) {
"use strict";
var app = angular.module("cpanel.services.directoryLookup", []);
var lastRequestJQXHR = null;
app.factory("directoryLookupService", [
"$q",
"APIService",
function($q, APIService) {
var DirectoryLookupService = function() {};
DirectoryLookupService.prototype = new APIService();
angular.extend(DirectoryLookupService.prototype, {
/**
* Query the directory completion API. Given a path prefix, which may
* include a partial directory name, returns an array of matching
* directories.
* @param {String} match The prefix to match.
* @return {Promise} When fulfilled, will have either provided the list of matching directories or failed.
*/
complete: function(match) {
/* Only allow one promise at a time for this service, and cancel any existing request, since
* the latest request will always supersede the existing one when typing into a text box. */
if (lastRequestJQXHR) {
lastRequestJQXHR.abort();
}
var apiCall = new APIREQUEST.Class();
apiCall.initialize("Fileman", "autocompletedir");
apiCall.addArgument("path", match);
apiCall.addArgument("dirsonly", true);
apiCall.addArgument("skipreserved", true);
apiCall.addArgument("html", 0);
/* If the last character of the path to match is a slash, then the user is probably hoping to see
* a list of all files underneath that directory. The API doesn't understand this unless you
* specify list_all mode, so we need to add that argument. */
if ( "/" === match.charAt(match.length - 1) ) {
apiCall.addArgument("list_all", true);
}
var deferred = this.deferred(apiCall, {
transformAPISuccess: function(response) {
var flattenedResponse = [];
for (var i = 0, l = response.data.length; i < l; i++) {
flattenedResponse.push(response.data[i].file);
}
return flattenedResponse;
},
});
return deferred.promise;
},
/* override sendRequest from APIService to also save our last jqXHR object */
sendRequest: function(apiCall, handlers, deferred) {
apiCall = new APIService.AngularAPICall(apiCall, handlers, deferred);
lastRequestJQXHR = apiCall.jqXHR;
return apiCall.deferred;
},
});
return new DirectoryLookupService();
},
]);
}
);
/*
# version_control/directives/cloneURLValidator.js Copyright(c) 2020 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
define('app/directives/cloneURLValidator',[
"angular",
"app/utils/cloneUrlParser",
"cjt/util/locale",
"cjt/validator/validator-utils",
"cjt/validator/ip-validators",
"cjt/validator/domain-validators",
"cjt/validator/validateDirectiveFactory",
], function(
angular,
cloneUrlParser,
LOCALE,
validationUtils,
IP_VALIDATOR,
DOMAIN_VALIDATOR
) {
"use strict";
var cloneURLValidator = {
/**
* Validate a git-based clone url.
*
* Does not validate local paths like: file:///path/to/repo.git/ and /path/to/repo.git/
*
* @method validCloneUrl
* @param {String} cloneUrl - Check if this is a valid clone url.
* @return {object} result - Validator-Utils result.
*/
validCloneUrl: function(cloneUrl) {
var result = validationUtils.initializeValidationResult();
// Check for blank string.
if (typeof cloneUrl === "undefined" || cloneUrl === null) {
result.isValid = false;
result.add(
"cloneURLValidator",
LOCALE.maketext("You must specify a valid clone URL.")
);
return result;
}
var parts = cloneUrlParser.parse(cloneUrl);
var scheme = parts.scheme;
var userInfo = parts.userInfo;
var ipv6Authority = parts.ipv6Authority;
var authority = parts.authority;
var path = parts.path;
var unparsed = parts.unparsed;
// Check for valid protocols (http:// | https:// | ssh:// | git://)
var protocolPattern = /^(?:git|ssh|https?)(?::\/\/)$/i;
var hasValidProtocol = protocolPattern.test(scheme);
// Check for invalid username and password (user:pass@)
var userAndPassPattern = /^\S+:\S+@/i;
if (userAndPassPattern.test(userInfo)) {
result.isValid = false;
result.add(
"cloneURLValidator",
LOCALE.maketext(
"The clone URL [output,strong,cannot] include a password."
)
);
return result;
}
// Check for valid username (username@)
var emailPattern =
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@/i;
var hasValidUser = emailPattern.test(userInfo);
// Assess if provided scheme and user@ is valid
var preDomainValid = false;
if (hasValidProtocol && hasValidUser) {
// has both
preDomainValid = true;
}
if (hasValidProtocol ^ hasValidUser) {
// has one or the other
// Prevents invalid non-required protocol.
if (!hasValidProtocol && scheme !== null) {
result.isValid = false;
result.add(
"cloneURLValidator",
LOCALE.maketext(
"The provided clone URL [output,strong,must] include a valid protocol."
)
);
return result;
}
preDomainValid = true;
}
if (!preDomainValid) {
result.isValid = false;
result.add(
"cloneURLValidator",
LOCALE.maketext(
"Clone URLs [output,strong,must] include a valid protocol or username."
)
);
return result;
}
// Check for valid ipv6
if (ipv6Authority !== null) {
var validIPV6 = IP_VALIDATOR.methods.ipv6(ipv6Authority);
if (!validIPV6.isValid) {
var errorMsg = combineErrorMessages(validIPV6.messages);
result.isValid = false;
result.add("cloneURLValidator", errorMsg);
return result;
}
}
// Check for valid ipv4 or domain name
if (authority !== null && authority !== "") {
var validIPV4 = IP_VALIDATOR.methods.ipv4(authority);
var validFQDN = DOMAIN_VALIDATOR.methods.fqdn(authority);
if (!validIPV4.isValid && !validFQDN.isValid) {
result.isValid = false;
result.add(
"cloneURLValidator",
LOCALE.maketext(
"The clone URL [output,strong,must] include a valid IP address or a fully-qualified domain name."
)
);
return result;
}
// If there is no valid ipv4, ipv6, or domain name
} else if (ipv6Authority === null) {
result.isValid = false;
result.add(
"cloneURLValidator",
LOCALE.maketext(
"The clone URL [output,strong,must] include a valid IP address or a fully-qualified domain name."
)
);
return result;
}
// Check for ip/domain -> path delimiter: if there is a protocol and path starts with : then throw error
var scpSyntaxPattern = /^:/i;
var hasSCPsyntaxPathStart = scpSyntaxPattern.test(path);
if (hasValidProtocol && hasValidUser && hasSCPsyntaxPathStart) {
result.isValid = false;
result.add(
"cloneURLValidator",
LOCALE.maketext(
"The repository path should [output,strong,not] begin with “:” if it includes the protocol."
)
);
return result;
}
// Check for valid path with .git extension (:|/ + path + .git? + /? )
// This could also indicate a problem with the port number format.
// This ignores any query string vars or page anchors.
var pathAndExtensionPattern = /^(:|\/)\S*(\.git)?\/?/i;
var hasValidPath = pathAndExtensionPattern.test(path);
if (!hasValidPath) {
result.isValid = false;
result.add(
"cloneURLValidator",
LOCALE.maketext(
"The path or port number is [output,strong,not] valid."
)
);
return result;
}
// Check for any left over cloneUrl parts that indicates spaces were used in the URL construction.
if (unparsed !== "") {
result.isValid = false;
result.add(
"cloneURLValidator",
LOCALE.maketext(
"The clone URL [output,strong,cannot] include whitespace characters."
)
);
return result;
}
return result;
/**
* Private method to combine all validation error messages from other validators.
* This method is used to reduce the number of LOCALE.maketext calls via re-useability.
*
* @method combineErrorMessages
* @param {array} msgArr - An array of messages from the validation-utils :: ValidationResult object.
* @return {string} msg - All messages combined into one string.
*/
function combineErrorMessages(msgArr) {
var msg = "";
for (var a = 0, len = msgArr.length; a < len; a++) {
msg +=
a !== len - 1
? msgArr[a].message + " "
: msgArr[a].message;
}
return msg;
}
},
};
var validatorModule = angular.module("cjt2.validate");
validatorModule.run([
"validatorFactory",
function(validatorFactory) {
validatorFactory.generate(cloneURLValidator);
},
]);
return {
methods: cloneURLValidator,
name: "clone-url-validator",
description: "Validation on git-based clone URLs.",
version: 1.0,
};
});
/*
# version_control/views/CreateRepositoriesController.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define, PAGE */
define(
'app/views/createRepositoriesController',[
"angular",
"lodash",
"cjt/util/locale",
"uiBootstrap",
"app/services/versionControlService",
"app/services/sshKeyVerification",
"cjt/services/alertService",
"cjt/directives/alert",
"cjt/directives/alertList",
"cjt/directives/actionButtonDirective",
"cjt/directives/toggleSwitchDirective",
"cjt/directives/toggleLabelInfoDirective",
"app/services/versionControlService",
"cjt/directives/validationContainerDirective",
"cjt/directives/validationItemDirective",
"cjt/validator/ascii-data-validators",
"cjt/validator/path-validators",
"app/services/directoryLookupService",
"app/directives/cloneURLValidator",
"cjt/filters/htmlFilter",
"cjt/decorators/uibTypeaheadDecorator",
],
function(angular, _, LOCALE) {
"use strict";
var app = angular.module("cpanel.versionControl");
app.value("PAGE", PAGE);
var controller = app.controller(
"CreateRepositoriesController",
["$scope", "$location", "versionControlService", "sshKeyVerification", "PAGE", "alertService", "directoryLookupService",
function($scope, $location, versionControlService, sshKeyVerification, PAGE, alertService, directoryLookupService) {
var repository = this;
// home directory path
repository.homeDirPath = PAGE.homeDir + "/";
repository.displaySuccessSummary = false;
// initialize form data
repository.formData = {
repoName: "",
repoPath: "",
clone: true,
cloneURL: "",
createAnother: false
};
repository.ssh = {};
repository.pathExcludeList = "[^'\":\\\\*?<>|@&=%#`$(){};\\[\\]\\s]+";// This is for angular input validation.
var directoryLookupFilter = /[%*{}()=?`$@:|[\]'"<>&#;\s\\]+/g;// This is the same regex for directory lookup service filter.
// Utility function
function _bothAreSameServer(obj1, obj2) {
return obj1
&& obj2
&& obj1.hostname === obj2.hostname
&& obj2.port === obj2.port;
}
/**
* When a user inputs a Clone URL, this method is fired to perform
* an API check against the user's knowns_hosts file.
*
* While some state is updated after the API check, the data mostly
* lays dormant until the createRepository method is called.
*
* All of the relevant state is stored on the repository.ssh object.
*/
repository.checkKnownHosts = function() {
var cloneUrl = repository.formData.cloneURL;
var newServer = cloneUrl && sshKeyVerification.getHostnameAndPort(cloneUrl);
if (!newServer) {
repository.ssh = {};
return;
}
if ( _bothAreSameServer(repository.ssh, newServer) ) {
return;
}
repository.ssh.hostname = newServer.hostname;
repository.ssh.port = newServer.port;
repository.ssh.status = "verifying";
repository.ssh.keys = [];
function _updateScope(data) {
/**
* It's possible to have a race if there are multiple checks in flight
* simultaneously. This check ensures that we only update the scope if
* the finished request was initiated using the current input value.
*/
if ( !_bothAreSameServer(repository.ssh, newServer) ) {
return;
}
repository.ssh.status = data.status;
repository.ssh.keys = data.keys;
return data.status;
}
return repository.ssh.promise = sshKeyVerification.verify(newServer.hostname, newServer.port).then(_updateScope, _updateScope);
};
/**
* Back to List View
* @method backToListView
*/
repository.backToListView = function() {
$location.path("/list");
};
/**
* Reset Form Data
* @method resetFormData
* @param {Object} opts An object of optional values.
* @param {Boolean} opts.isCreateAnother If true, it will only be a partial reset for Create Another.
*/
repository.resetFormData = function(opts) {
repository.formData = {
repoName: "",
repoPath: "",
clone: opts.isCreateAnother ? repository.formData.clone : true,
cloneURL: "",
createAnother: Boolean(opts.isCreateAnother),
};
repository.createRepoForm.$setPristine();
};
/**
* Create Repository
* @method createRepository
* @return {Promise} returns promise.
*/
repository.createRepository = function() {
if (!repository.formData.repoName || !repository.formData.repoPath) {
return;
}
alertService.clear(null, "versionControl");
if (!repository.formData.clone) {
repository.formData.cloneURL = null;
}
if (repository.formData.cloneURL && (repository.ssh.status === "unrecognized-new" || repository.ssh.status === "unrecognized-changed")) {
_showKeyVerificationModal();
return;
} else if (repository.formData.cloneURL && repository.ssh.promise) {
return repository.ssh.promise.then(function(status) {
delete repository.ssh.promise;
return repository.createRepository();
});
} else {
return _createRepository();
}
};
/**
* Opens up the modal for SSH key verification.
*/
function _showKeyVerificationModal() {
alertService.clear(null, "versionControl");
repository.ssh.modal = sshKeyVerification.openModal({
hostname: repository.ssh.hostname,
port: repository.ssh.port,
type: repository.ssh.status,
keys: repository.ssh.keys,
onAccept: _onAcceptKey,
});
/**
* Handle the case where the modal is dismissed, rather than closed.
* Dismissal is when the user clicks the cancel button or if they click
* outside of the modal, causing it to disappear.
*/
repository.ssh.modal.result.catch(function() {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system [output,strong,cannot] clone this repository if you do not trust the host key for “[output,strong,_1]”. To create your repository, select one of the following options:", repository.ssh.hostname),
list: [
LOCALE.maketext("Enter a clone URL that uses the HTTPS or Git protocols instead of SSH."),
LOCALE.maketext("Enter a clone URL for a different, previously-trusted host."),
LOCALE.maketext("Click [output,em,Create] again and choose to trust the remote server."),
],
closeable: true,
replace: true,
group: "versionControl",
id: "known-hosts-verification-cancelled",
});
});
}
function _onAcceptKey(promise) {
return promise.then(
function success(newStatus) {
repository.ssh.status = newStatus;
return _createRepository();
},
function failure(error) {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system failed to add the fingerprints from “[_1]” to the [asis,known_hosts] file: [_2]", repository.ssh.hostname, error),
closeable: true,
replace: true,
group: "versionControl",
id: "known-hosts-verification-failure",
});
}
).finally(function() {
repository.ssh.modal.close();
delete repository.ssh.modal;
});
}
/**
* The common bits for actually creating the repo.
*/
function _createRepository() {
var repositoryPath = repository.homeDirPath + repository.formData.repoPath;
return versionControlService.createRepository(
repository.formData.repoName,
repositoryPath,
repository.formData.cloneURL).then(function(response) {
// Clone Repository Success
if (repository.formData.cloneURL) {
alertService.add({
type: "info",
message: LOCALE.maketext("The system successfully initiated the clone process for the “[_1]” repository.", repository.formData.repoName) + " " + LOCALE.maketext("The system may require more time to clone large remote repositories."),
closeable: true,
replace: false,
group: "versionControl",
id: response.cloneTaskID,
counter: false
});
if (!repository.formData.createAnother) {
repository.backToListView();
} else {
repository.resetFormData({ isCreateAnother: true });
}
} else {
// Create repository Success
alertService.add({
type: "success",
message: LOCALE.maketext("The system successfully created the “[_1]” repository.", repository.formData.repoName),
closeable: true,
replace: false,
autoClose: 10000,
group: "versionControl"
});
if (!repository.formData.createAnother) {
var repoSummary = response;
var cloneURL = repoSummary.cloneURL;
if (typeof cloneURL !== "undefined" && cloneURL) {
repository.displaySuccessSummary = true;
repository.summary = {};
repository.summary.remoteURL = cloneURL;
var parts = repository.summary.remoteURL.split("/");
if (parts && parts.length > 0) {
repository.summary.directoryName = parts[parts.length - 1];
} else {
repository.summary.directoryName = "";
}
repository.summary.readOnly = repoSummary.clone_urls.read_write.length === 0 ? true : false;
} else {
repository.backToListView();
}
} else {
repository.resetFormData({ isCreateAnother: true });
}
}
}, function(error) {
alertService.add({
type: "danger",
message: error,
closeable: true,
replace: false,
group: "versionControl"
});
});
}
/**
* Directory lookup
* @method completeDirectory
* @return {Promise} Returns an array of directory paths.
*/
repository.completeDirectory = function(prefix) {
var directoryLookupPromise = directoryLookupService.complete(prefix);
var outputDirectories = [];
return directoryLookupPromise.then(function(directories) {
for ( var i = 0, len = directories.length; i < len; i++ ) {
var directoryName = directories[i];
if ( directoryName.search(directoryLookupFilter) === -1 ) {
outputDirectories.push(directoryName);
}
}
return outputDirectories;
});
};
/**
* Toggle Status
* @method toggleStatus
* @return {Boolean} Returns true.
*/
repository.toggleStatus = function() {
repository.formData.clone = !repository.formData.clone;
return true;
};
/**
* Autofill Repository path and name based on clone url
* @method autoFillPathAndName
*/
repository.autoFillPathAndName = function() {
if (!repository.formData.repoName && !repository.formData.repoPath) {
if (repository.createRepoForm.repoCloneURL.$valid && repository.formData.cloneURL) {
var cloneUrl = repository.formData.cloneURL;
var repoPathPrefix = "repositories/";
// Removing training slash
cloneUrl = cloneUrl.replace(/\/+$/, "");
// finding last part of the url and replacing .git if present
var repoDirectory = cloneUrl.substr(cloneUrl.lastIndexOf("/") + 1).replace(".git", "");
repoDirectory = repoDirectory.replace(directoryLookupFilter, "_");
var repositoryPath = repoPathPrefix + repoDirectory;
repository.formData.repoPath = repositoryPath;
repository.formData.repoName = repoDirectory;
}
}
};
}
]
);
return controller;
}
);
/*
# version_control/views/manageRepositoriesController.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define, PAGE */
define(
'app/views/manageRepositoriesController',[
"angular",
"lodash",
"cjt/util/locale",
"uiBootstrap",
"app/services/versionControlService",
"app/services/sseAPIService",
"cjt/services/alertService",
"cjt/directives/alert",
"cjt/directives/alertList",
"cjt/directives/actionButtonDirective",
"jquery-chosen",
"angular-chosen",
"cjt/decorators/angularChosenDecorator",
],
function(angular, _, LOCALE) {
"use strict";
var app = angular.module("cpanel.versionControl");
app.value("PAGE", PAGE);
var controller = app.controller(
"ManageRepositoriesController",
["$scope", "$window", "$location", "$timeout", "versionControlService", "sseAPIService", "PAGE", "$routeParams", "alertService", "sshKeyVerification", "$q",
function($scope, $window, $location, $timeout, versionControlService, sseAPIService, PAGE, $routeParams, alertService, sshKeyVerification, $q) {
var repository = this;
// RTL check for chosen
repository.isRTL = false;
var html = document.querySelector("html");
if (html) {
repository.isRTL = html.getAttribute("dir") === "rtl";
}
// Page defaults
repository.isLoading = true;
repository.deployInProgress = false;
repository.deployState = "";
repository.deployedTaskInformation = null;
repository.deployCalloutType = "info";
// SSE events and config
var deploySSEURL = "";
var sseObj;
var events = [ "log_update", "task_complete", "task_failed" ];
var config = { json: true };
var tabs = [
"basic-info",
"deploy"
];
var tabToSelect = 0;
// Get the variables from the URL
var requestedRepoPath = decodeURIComponent($routeParams.repoPath);
var tabName = decodeURIComponent($routeParams.tabname);
selectActiveTab(tabName);
/**
* Selects Active Tab
* @method selectActiveTab
* @param {String} tabName Tab Name
*/
function selectActiveTab(tabName) {
// Selecting tab based on route parameter
if (tabName) {
tabToSelect = tabs.indexOf(tabName);
if ( tabToSelect !== -1) {
$scope.activeTabIndex = tabToSelect;
} else {
$location.path("/list/");
}
} else {
$location.path("/list/");
}
}
retrieveRepositoryInfo(requestedRepoPath);
/**
* Changes active tab
*
* @method changeActiveTab
* @param {String} name name of the tab.
*/
$scope.changeActiveTab = function(name) {
var url = $location.url();
var lastPart = url.split( "/" ).pop().toLowerCase();
if (name) {
$scope.activeTabIndex = tabs.indexOf(name);
// lastpart other than name
if (lastPart !== name) {
$location.path("/manage/" + encodeURIComponent(requestedRepoPath) + "/" + name);
}
}
};
/**
* Checks to see if the user came from the VersionControl List View
*
* @method retrieveRepositoryInfo
* @param {String} requestedRepoPath Represents the path of the repository to be loaded on the page.
*/
function retrieveRepositoryInfo(requestedRepoPath) {
var repoInfo;
return versionControlService.getRepositoryInformation(requestedRepoPath, "name,tasks,clone_urls,branch,last_update,source_repository,last_deployment,deployable")
.then(function(response) {
repoInfo = response;
var branchPromise = _retrieveAvailableBranches(requestedRepoPath);
/**
* If we fail to retrieve the available branches, we will let the UI
* go ahead and display in its mostly broken state, while performing
* this SSH key check in the background for later use with the Try
* Again button.
*/
branchPromise.catch(function() {
var sshServer = sshKeyVerification.getHostnameAndPort(response && response.source_repository && response.source_repository.url);
if (sshServer) {
repository.ssh = {};
repository.ssh.hostname = sshServer.hostname;
repository.ssh.port = sshServer.port;
repository.ssh.promise = sshKeyVerification.verify(sshServer.hostname, sshServer.port);
}
});
return branchPromise;
}, function(error) {
alertService.add({
type: "danger",
message: _.escape(error),
closeable: true,
replace: false,
group: "versionControl"
});
$location.path("/list/");
})
.finally(function() {
setFormData(repoInfo);
repository.isLoading = false;
});
}
function _retrieveAvailableBranches(requestedRepoPath) {
return versionControlService.getRepositoryInformation(requestedRepoPath, "available_branches")
.then(function(response) {
repository.branchList = response && response.available_branches || [];
}, function(error) {
repository.unableToRetrieveAvailableBranches = true;
return $q.reject(error);
});
}
/**
* We don't want to show an alert if there are issues fetching the branches during the
* initial page load, but if it's in response to a user action it's good to have feedback.
*/
function _retrieveAvailableBranchesTryAgain() {
return _retrieveAvailableBranches( repository.repoPath ).catch(function() {
alertService.add({
type: "danger",
id: "retrieve-branches-again-error",
message: _.escape(LOCALE.maketext("The system cannot update information for the repository at “[_1]” because it cannot access the remote repository.", repository.repoPath)),
closeable: true,
replace: true,
group: "versionControl"
});
});
}
/**
* Changes the text for the callout that's used when unableToRetrieveAvailableBranches = true.
*
* @param {Boolean} hasRemote True when the repository was cloned from a remote repo.
*/
$scope.$watch("repository.hasRemote", function(hasRemote) {
if (hasRemote) {
repository._noConnectionText = LOCALE.maketext("The system could not contact the remote repository.");
repository._tryAgainTooltipText = LOCALE.maketext("Attempt to contact the remote repository again.");
} else {
repository._noConnectionText = LOCALE.maketext("The system could not read from the repository.");
repository._tryAgainTooltipText = LOCALE.maketext("Attempt to read from the repository again.");
}
});
/**
* Try to fetch the list of available_branches again.
*
* @return {Promise} Resolves if it successfully retrieves the available branches.
* Rejects otherwise.
*/
repository.tryAgain = function tryAgain() {
alertService.removeById("retrieve-branches-again-error", "versionControl");
if (!repository.ssh.promise) {
return _retrieveAvailableBranchesTryAgain();
}
return repository.ssh.promise.then(
function success() {
// SSH host key verification is not the problem, so just try again
return _retrieveAvailableBranchesTryAgain();
},
function failure(data) {
repository.ssh.status = data.status;
repository.ssh.keys = data.keys;
_showKeyVerificationModal();
}
);
};
/**
* Opens up the modal for SSH key verification.
*/
function _showKeyVerificationModal() {
alertService.clear(null, "versionControl");
repository.ssh.modal = sshKeyVerification.openModal({
hostname: repository.ssh.hostname,
port: repository.ssh.port,
type: repository.ssh.status,
keys: repository.ssh.keys,
onAccept: _onAcceptKey,
});
/**
* Handle the case where the modal is dismissed, rather than closed.
* Dismissal is when the user clicks the cancel button or if they click
* outside of the modal, causing it to disappear.
*/
repository.ssh.modal.result.catch(function() {
alertService.add({
type: "danger",
message: _.escape(LOCALE.maketext("The system [output,strong,cannot] connect to the remote repository if you do not accept the host key for “[output,strong,_1].”", repository.ssh.hostname)),
closeable: true,
replace: true,
group: "versionControl",
id: "known-hosts-verification-cancelled",
});
});
return repository.ssh.modal;
}
function _onAcceptKey(promise) {
return promise.then(
function success(newStatus) {
repository.ssh.status = newStatus;
return _retrieveAvailableBranchesTryAgain();
},
function failure(error) {
alertService.add({
type: "danger",
message: _.escape(LOCALE.maketext("The system failed to add the fingerprints from “[_1]” to the [asis,known_hosts] file: [_2]", repository.ssh.hostname, error)),
closeable: true,
replace: true,
group: "versionControl",
id: "known-hosts-verification-failure",
});
}
).finally(function() {
repository.ssh.modal.close();
delete repository.ssh.modal;
});
}
/**
* Set Manage Form Data
*
* @method setFormData
* @param {object} data Represents the single repository data object.
*/
function setFormData(data) {
repository.name = data.name;
repository.repoPath = data.repository_root;
repository.cloneURL = data.clone_urls.read_write[0];
repository.branch = data.branch;
repository.checkedoutBranch = data.branch;
repository.hasActiveBranch = data.hasActiveBranch;
repository.hasHeadInformation = data.hasHeadInformation;
repository.lastUpdateSHA = data.lastUpdateSHA;
repository.lastUpdateDate = data.lastUpdateDate;
repository.commitMessage = data.commitMessage;
repository.author = data.author;
if (data.available_branches) {
repository.branchList = data.available_branches;
}
repository.hasRemote = data.hasRemote;
repository.remoteInformation = data.source_repository;
repository.gitWebURL = data.gitWebURL;
repository.fileManagerRedirectURL = data.fileManagerRedirectURL;
repository.fullRepoPath = repository.repoPath;
repository.qaSafeSuffix = data.qaSafeSuffix;
repository.deployInProgress = data.deployInProgress;
repository.deployable = data.deployable;
repository.hasDeploymentInformation = data.hasDeploymentInformation;
repository.lastDeployedDate = data.lastDeployedDate;
repository.lastDeployedSHA = data.lastDeployedSHA;
repository.lastDeployedAuthor = data.lastDeployedAuthor;
repository.lastDeployedCommitDate = data.lastDeployedCommitDate;
repository.lastDeployedCommitMessage = data.lastDeployedCommitMessage;
repository.changesAvailableToDeploy = data.lastDeployedSHA !== data.lastUpdateSHA;
repository.deployTasks = getDeployTasks(data.tasks);
if (typeof sseObj === "undefined" && repository.deployInProgress) {
initializeSSE();
}
}
function getDeployTasks(tasks) {
var deployTasks = _.map(tasks, function(task) {
if (task.action === "deploy") {
var timestampInfo = getDeployTimestamp(task.args.log_file);
return {
task_id: task.task_id,
log_file: task.args.log_file,
sse_url: task.sse_url,
timeStamp: timestampInfo,
humanReadableDate: LOCALE.local_datetime(timestampInfo, "datetime_format_medium")
};
}
});
return _.sortBy(deployTasks, [function(o) {
return o.log_file;
}]);
}
/**
* @method getQueuedTaskString
* @param {Number} taskCount TaskCount
* @returns {String} Additional tasks display string
*/
function getQueuedTaskString(taskCount) {
return LOCALE.maketext("[quant,_1,additional task,additional tasks] queued", taskCount);
}
/**
* Gets Deployment timestamp
* @method getDeployTimestamp
* @param {String} logFilePath LogFile path
* @returns {String} deployment timestamp
*/
function getDeployTimestamp(logFilePath) {
var timeStamp;
if (logFilePath) {
var logFileName = logFilePath.split("/").pop();
timeStamp = logFileName.match(/\d+(\.\d+)/g);
}
return timeStamp[0];
}
/**
* Update Repository
* @method updateRepository
* @return {Promise} Returns a promise from the VersionControlService.updateRepository method for success/error handling when the user requests to update a repository.
*/
repository.updateRepository = function() {
var branch = repository.branch === repository.checkedoutBranch ? "" : repository.branch;
return versionControlService.updateRepository(
repository.repoPath,
repository.name,
branch
).then(function(response) {
alertService.add({
type: "success",
message: _.escape(LOCALE.maketext("The system successfully updated the “[_1]” repository.", repository.name)),
closeable: true,
replace: true,
autoClose: 10000,
group: "versionControl"
});
setFormData(response.data);
}, function(error) {
alertService.add({
type: "danger",
message: _.escape(error),
closeable: true,
replace: false,
group: "versionControl"
});
});
};
/**
* Pull from remote repository
* @method pullFromRemote
* @return {Promise} Returns a promise from the VersionControlService.updateRepository method for success/error handling when the user requests to pull from remote repository.
*/
repository.pullFromRemote = function() {
return versionControlService.updateFromRemote(
repository.repoPath,
repository.branch
).then(function(response) {
var data = response.data;
if (repository.lastUpdateSHA === data.lastUpdateSHA) {
alertService.add({
type: "info",
message: _.escape(LOCALE.maketext("The “[_1]” repository is up-to-date.", repository.name)),
closeable: true,
replace: true,
autoClose: 10000,
group: "versionControl"
});
} else {
alertService.add({
type: "success",
message: _.escape(LOCALE.maketext("The system successfully updated the “[_1]” repository.", repository.name)),
closeable: true,
replace: true,
autoClose: 10000,
group: "versionControl"
});
repository.hasHeadInformation = data.hasHeadInformation;
repository.lastUpdateSHA = data.lastUpdateSHA;
repository.lastUpdateDate = data.lastUpdateDate;
repository.commitMessage = data.commitMessage;
repository.author = data.author;
repository.newCommits = true;
$timeout( function() {
repository.newCommits = false;
}, 10000 );
}
}, function(error) {
alertService.add({
type: "danger",
message: _.escape(error),
closeable: true,
replace: false,
group: "versionControl"
});
});
};
/**
* Reset deployment and sse flags
* @method resetSSEState
*/
function resetSSEState() {
repository.deployState = "";
repository.deployCalloutType = "info";
repository.deployedTaskInformation = null;
sseObj = null;
}
/**
* Initialize SSE
* @method initializeSSE
*/
function initializeSSE() {
repository.queuedDeployTasksCount = repository.deployTasks.length - 1;
if (repository.queuedDeployTasksCount) {
repository.queuedTaskString = getQueuedTaskString(repository.queuedDeployTasksCount);
}
repository.firstDeployTask = repository.deployTasks[0];
deploySSEURL = repository.firstDeployTask.sse_url;
repository.deployProgress = LOCALE.maketext("The deployment that you triggered on [_1] is in progress …", repository.firstDeployTask.humanReadableDate);
repository.deployComplete = LOCALE.maketext("The deployment that you triggered on [_1] is complete. Updating last deployment information …", repository.firstDeployTask.humanReadableDate);
repository.deployQueued = LOCALE.maketext("The deployment that you triggered on [_1] is queued …", repository.firstDeployTask.humanReadableDate);
sseAPIService.initialize();
}
/**
* Handles ready.
*
* @method
* @param {sse:ready} event - ready event.
* @listens sse:ready
*/
$scope.$on("sse:ready", function(event) {
deploySSEURL = PAGE.securityToken + deploySSEURL;
sseObj = sseAPIService.connect(deploySSEURL, events, config);
});
/**
* Handles destroy event.
*
* @method
* @listens $destroy
*/
$scope.$on("$destroy", function() {
if (sseObj) {
sseAPIService.close(sseObj);
}
});
/**
* Handles log_update.
*
* @method
* @param {sse:log_update} event - Task log update event.
* @param {String} data - log data
* @listens sse:log_update
*/
$scope.$on("sse:log_update", function(event, data) {
repository.deployState = "processing";
$scope.$apply();
});
/**
* Handles task_complete.
*
* @method
* @param {sse:task_complete} event - Task complete event.
* @param {Object} data - Data
* @listens sse:task_complete
*/
$scope.$on("sse:task_complete", function(event, data) {
var taskData = data;
sseAPIService.close(sseObj);
repository.deployCalloutType = "success";
repository.deployState = "complete";
$scope.$apply();
$timeout(function() {
return versionControlService.getRepositoryInformation(repository.repoPath, "last_deployment,tasks")
.then(function(data) {
repository.lastDeployedDate = data.lastDeployedDate;
repository.lastDeployedSHA = data.lastDeployedSHA;
repository.lastDeployedAuthor = data.lastDeployedAuthor;
repository.lastDeployedCommitDate = data.lastDeployedCommitDate;
repository.lastDeployedCommitMessage = data.lastDeployedCommitMessage;
repository.hasDeploymentInformation = true;
repository.changesAvailableToDeploy = data.lastDeployedSHA !== repository.lastUpdateSHA;
repository.deployTasks = getDeployTasks(data.tasks);
resetSSEState();
if (repository.deployTasks && repository.deployTasks.length > 0) {
repository.deployInProgress = true;
initializeSSE();
} else {
repository.deployInProgress = false;
}
repository.newDeployCommit = true;
$timeout( function() {
repository.newDeployCommit = false;
}, 5000 );
}, function(error) {
// display error
alertService.add({
type: "danger",
message: _.escape(error.message),
closeable: true,
replace: false,
group: "versionControl"
});
});
}, 5000);
});
/**
* Handles task_failed.
*
* @method
* @param {sse:task_failed} event - Task failed event.
* @param {Object} data - Data
* @listens sse:task_failed
*/
$scope.$on("sse:task_failed", function(event, data) {
sseAPIService.close(sseObj);
var deployedTaskInfo = repository.deployedTaskInformation;
var logFileInfo = getLogFileDetails(deployedTaskInfo.log_path);
alertService.add({
type: "danger",
message: LOCALE.maketext("Error occurred while deploying.") +
" " +
LOCALE.maketext("You can view the log file: [output,url,_1,_2,target,_3]", logFileInfo.fileManagerURL, logFileInfo.fileName, "_blank"),
closeable: true,
replace: false,
group: "versionControl"
});
$scope.$apply();
return versionControlService.getRepositoryInformation(repository.repoPath, "tasks")
.then(function(data) {
repository.deployTasks = getDeployTasks(data.tasks);
resetSSEState();
if (repository.deployTasks && repository.deployTasks.length > 0) {
repository.deployInProgress = true;
initializeSSE();
} else {
repository.deployInProgress = false;
}
}, function(error) {
// display error
alertService.add({
type: "danger",
message: _.escape(error.message),
closeable: true,
replace: false,
group: "versionControl"
});
});
});
/**
* Get log file details
* @method getLogFileDetails
*
* @param {Object} logFilePath logfile path
* @return {Object} Log file details
*/
function getLogFileDetails(logFilePath) {
var logFileInfo = {};
if (logFilePath) {
// construct the file manager url for log file
var fileName = logFilePath.split( "/" ).pop();
var dirPath = PAGE.homeDir + "/.cpanel/logs";
var fileManangerURL = PAGE.deprefix + "filemanager/showfile.html?file=" + encodeURIComponent(fileName) + "&dir=" + encodeURIComponent(dirPath);
logFileInfo.fileName = fileName;
logFileInfo.fileManagerURL = fileManangerURL;
}
return logFileInfo;
}
/**
* Deploy repository
* @method deployRepository
* @return {Promise} Returns a promise from the VersionControlService.deployRepository method for success/error handling when the user requests to deploy their repository.
*/
repository.deployRepository = function() {
return versionControlService.deployRepository(
repository.repoPath
).then(function(response) {
var data = response.data || {};
/**
* We have to fake the task object, since the task data returned from the
* VersionControlDeployment::create and VersionControlDeployment::retrieveAPI calls don't match.
*/
repository.deployTasks = getDeployTasks([
{
action: "deploy",
task_id: data.task_id,
sse_url: data.sse_url,
args: {
log_file: data.log_path,
},
}
]);
if (repository.deployTasks && repository.deployTasks.length > 0) {
repository.deployInProgress = true;
initializeSSE();
} else {
repository.deployInProgress = false;
}
}, function(error) {
repository.deployInProgress = false;
alertService.add({
type: "danger",
message: _.escape(error) +
" " +
LOCALE.maketext("For more information, read our [output,url,_1,documentation,target,_2].", "https://go.cpanel.net/GitDeployment", "_blank"),
closeable: true,
replace: false,
group: "versionControl"
});
});
};
/**
* Back to List View
* @method backToListView
*/
repository.backToListView = function() {
$location.path("/list");
};
/**
* Opens repository in gitWeb
* @method redirectToGitWeb
* @param {String} gitWebURL gitWebURL for the repository
* @param {String} repoName Repository name
*/
repository.redirectToGitWeb = function(gitWebURL, repoName) {
if (gitWebURL) {
$window.open(gitWebURL, repoName + "GitWeb");
} else {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system could not find the repository’s [asis,Gitweb] [output,acronym,URL,Universal Resource Locator]."),
closeable: true,
replace: false,
group: "versionControl"
});
}
};
/**
* Opens repository path in file manager
* @method redirectToFileManager
* @param {String} fileManagerURL file Manager url for the repository path
* @param {String} repoName Repository name
*/
repository.redirectToFileManager = function(fileManagerURL, repoName) {
if (fileManagerURL) {
$window.open(fileManagerURL, repoName + "FileManager");
} else {
alertService.add({
type: "danger",
message: LOCALE.maketext("The system could not redirect you to the File Manager interface."),
closeable: true,
replace: false,
group: "versionControl"
});
}
};
/**
* Copies the repo's clone link to your machine's clipboard
* @method cloneToClipboard
* @param {String} cloneUrl The URL to be used to clone repos.
*/
repository.cloneToClipboard = function(cloneUrl) {
try {
var result = versionControlService.cloneToClipboard(cloneUrl);
if (result) {
alertService.add({
type: "success",
message: LOCALE.maketext("The system successfully copied the “[_1]” clone [output,acronym,URL,Uniform Resource Locator] to the clipboard.", cloneUrl),
closeable: true,
replace: false,
autoClose: 10000,
group: "versionControl"
});
}
} catch (error) {
alertService.add({
type: "danger",
message: _.escape(error),
closeable: true,
replace: false,
group: "versionControl"
});
}
};
/**
* Checks if there are available branches or not.
* @method hasAvailableBranches
* @return {Boolean} Returns if there are any branches in the branchList.
*/
repository.hasAvailableBranches = function() {
return Boolean(repository.branchList && repository.branchList.length !== 0);
};
}
]
);
return controller;
}
);
/*
# version_control/index.js Copyright(c) 2020 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global require: false, define: false, PAGE: false */
define(
'app/index',[
"angular",
"cjt/core",
"cjt/modules",
"ngRoute",
"uiBootstrap",
"cjt/services/alertService",
"cjt/directives/alert",
"cjt/directives/alertList",
"cjt/directives/callout",
"jquery-chosen",
"angular-chosen"
],
function(angular) {
"use strict";
return function() {
// First create the application
angular.module("cpanel.versionControl", [
"ngRoute",
"ui.bootstrap",
"cjt2.cpanel",
"cpanel.versionControl.service",
"cpanel.versionControl.sshKeyVerificationService",
"cpanel.services.directoryLookup",
"cpanel.versionControl.sseAPIService",
"localytics.directives",
]);
// Then load the application dependencies
var app = require(
[
"cjt/bootstrap",
"app/views/listRepositoriesController",
"app/views/createRepositoriesController",
"app/views/manageRepositoriesController",
], function(BOOTSTRAP) {
var app = angular.module("cpanel.versionControl");
app.value("PAGE", PAGE);
app.config([
"$routeProvider",
function($routeProvider) {
$routeProvider.when("/list/", {
controller: "ListRepositoriesController",
controllerAs: "repositories",
templateUrl: "views/listRepositoriesView.ptt",
});
$routeProvider.when("/create/", {
controller: "CreateRepositoriesController",
controllerAs: "repository",
templateUrl: "views/createRepositoriesView.ptt",
});
$routeProvider.when("/manage/:repoPath/:tabname?", {
controller: "ManageRepositoriesController",
controllerAs: "repository",
templateUrl: "views/manageRepositoriesView.ptt",
});
$routeProvider.otherwise({
"redirectTo": "/list"
});
}
]);
BOOTSTRAP("#content", "cpanel.versionControl");
});
return app;
};
}
);
Back to Directory
File Manager