Remove old schedule_events dir (#27095) (#27128)

This commit is contained in:
Melissa Alvarez 2018-12-13 10:28:45 -06:00 committed by GitHub
parent 519f0857c9
commit 0065fc5043
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 0 additions and 1786 deletions

View file

@ -1,6 +0,0 @@
@import 'calendars_list/index';
@import 'new_calendar/index';
@import 'components/events_list/index';
@import 'components/import_events_modal/index';
@import 'components/new_event_modal/index';

View file

@ -1,29 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import ngMock from 'ng_mock';
import expect from 'expect.js';
xdescribe('ML - Calendars List Controller', () => {
beforeEach(() => {
ngMock.module('kibana');
});
it('Initialize Calendars List Controller', (done) => {
ngMock.inject(function ($rootScope, $controller) {
const scope = $rootScope.$new();
expect(() => {
$controller('MlCalendarsList', { $scope: scope });
}).to.not.throwError();
expect(scope.permissions.canCreateCalendar).to.eql(false);
done();
});
});
});

View file

@ -1,20 +0,0 @@
ml-calendars-list{
font-size: $euiFontSizeS;
header {
margin: $euiSize 0;
}
.calendars-list-container {
width: 100%;
margin-right: auto;
margin-left: auto;
padding-left: $euiSize;
padding-right: $euiSize;
}
.actions-col {
// SASSTODO: Proper calcs
width: 150px;
min-width: 150px;
}
}

View file

@ -1 +0,0 @@
@import 'calendars_list';

View file

@ -1,60 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { createPermissionFailureMessage } from 'plugins/ml/privilege/check_privilege';
export function buttonsEnabledChecks(permissions, mlNodesAvailable) {
const NO_ML_NODES_ERROR = 'No ML nodes available';
function newButtonWrapperClass() {
return (permissions.canCreateCalendar && mlNodesAvailable) ? 'button-wrapper' : ['button-wrapper', 'disabled'];
}
function newButtonWrapperTooltip() {
let tooltip = undefined;
if (permissions.canCreateCalendar === false) {
tooltip = createPermissionFailureMessage('canCreateCalendar');
} else if (mlNodesAvailable === false) {
tooltip = NO_ML_NODES_ERROR;
}
return tooltip;
}
function newButtonDisabled() {
return (
(permissions.canCreateCalendar === false) || mlNodesAvailable === false
);
}
function deleteButtonWrapperClass() {
return (permissions.canDeleteCalendar && mlNodesAvailable) ? 'button-wrapper' : ['button-wrapper', 'disabled'];
}
function deleteButtonWrapperTooltip() {
let tooltip = undefined;
if (permissions.canDeleteCalendar === false) {
tooltip = createPermissionFailureMessage('canDeleteCalendar');
} else if (mlNodesAvailable === false) {
tooltip = NO_ML_NODES_ERROR;
}
return tooltip;
}
function deleteButtonDisabled() {
return (permissions.canDeleteCalendar && mlNodesAvailable) === false;
}
return {
newButtonWrapperClass,
newButtonWrapperTooltip,
newButtonDisabled,
deleteButtonWrapperClass,
deleteButtonWrapperTooltip,
deleteButtonDisabled,
};
}

View file

@ -1,136 +0,0 @@
<ml-nav-menu name="settings"></ml-nav-menu>
<ml-calendars-list ng-controller="MlCalendarsList">
<ml-message-bar ></ml-message-bar>
<div class="calendars-list-container">
<div>
<div class="kuiViewContent kuiViewContent--constrainedWidth">
<div class="kuiViewContentItem kuiControlledTable kuiVerticalRhythm">
<div class="kuiToolBar">
<div class="kuiToolBarSearch">
<tool-bar-search-box
ng-disabled="true"
filter="query"
on-filter="onQueryChange">
</tool-bar-search-box>
</div>
<div class="kuiToolBarSection">
<div
ng-class="buttonChecks.newButtonWrapperClass()"
ng-attr-tooltip="{{buttonChecks.newButtonWrapperTooltip()}}"
>
<button class="kuiButton kuiButton--primary"
ng-click="newCalendarClick()"
aria-label="Create new calendar"
tooltip="Create new calendar"
ng-disabled='buttonChecks.newButtonDisabled()'
tooltip-append-to-body="true">
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-plus"></span>
New calendar
</button>
</div>
</div>
<div class="kuiToolBarSection">
<tool-bar-pager-text
start-item="pager.startItem"
end-item="pager.endItem"
total-items="pager.totalItems">
</tool-bar-pager-text>
<tool-bar-pager-buttons
has-previous-page="pager.hasPreviousPage"
has-next-page="pager.hasNextPage"
on-page-next="onPageNext"
on-page-previous="onPagePrevious">
</tool-bar-pager-buttons>
</div>
</div>
<table class="kuiTable">
<thead>
<tr>
<th scope="col" class="kuiTableHeaderCell">
<sortable-column field="{{SORT_FIELD_LABELS.CALENDAR_ID}}" on-sort-change="onSortChange" sort-field="sortField" sort-reverse="sortReverse">
ID
</sortable-column>
</th>
<th scope="col" class="kuiTableHeaderCell">
<sortable-column field="{{SORT_FIELD_LABELS.JOB_IDS}}" on-sort-change="onSortChange" sort-field="sortField" sort-reverse="sortReverse">
Jobs
</sortable-column>
</th>
<th scope="col" class="kuiTableHeaderCell">
<sortable-column field="{{SORT_FIELD_LABELS.EVENT_COUNT}}" on-sort-change="onSortChange" sort-field="sortField" sort-reverse="sortReverse">
Events
</sortable-column>
</th>
<th scope="col" class="kuiTableHeaderCell actions-col"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="calendar in displayCalendars" class="kuiTableRow" ng-class="{ 'kuiTableRow-isBeingDeleted': calendar.isBeingDeleted }">
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
<span title="{{calendar.calendar_id}}">
{{calendar.calendar_id}}
</span>
</div>
</td>
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
<span title="{{calendar.job_ids.join(', ')}}">
{{calendar.job_ids.join(', ')}}
</span>
</div>
</td>
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
<span>
{{calendar.events.length}} {{calendar.events.length === 1 ? 'event' : 'events'}}
</span>
</div>
</td>
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
<div class="kuiMenuButtonGroup kuiMenuButtonGroup--alignRight">
<div
ng-class="buttonChecks.newButtonWrapperClass()"
ng-attr-tooltip="{{buttonChecks.newButtonWrapperTooltip()}}">
<button
class="kuiMenuButton kuiMenuButton--basic kuiMenuButton--iconText"
aria-label="Edit {{calendar.calendar_id}}"
ng-disabled='buttonChecks.newButtonDisabled()'
ng-click="editCalendarClick(calendar.calendar_id)">
<span class="kuiMenuButton__icon kuiIcon fa-pencil"></span>
<span>Edit</span>
</button>
</div>
<div
ng-class="buttonChecks.deleteButtonWrapperClass()"
ng-attr-tooltip="{{buttonChecks.deleteButtonWrapperTooltip()}}">
<button
class="kuiMenuButton kuiMenuButton--danger kuiMenuButton--iconText"
aria-label="Delete {{calendar.calendar_id}}"
ng-disabled='buttonChecks.deleteButtonDisabled()'
ng-click="deleteCalendarClick(calendar.calendar_id)"
href="" >
<span class="kuiMenuButton__icon kuiIcon fa-trash"></span>
<span>Delete</span>
</button>
</div>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</ml-calendars-list>

View file

@ -1,168 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import _ from 'lodash';
import 'ui/pager_control';
import 'ui/pager';
import 'ui/sortable_column';
import uiRoutes from 'ui/routes';
import { checkFullLicense } from 'plugins/ml/license/check_license';
import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege';
import { getMlNodeCount, mlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { buttonsEnabledChecks } from 'plugins/ml/settings/scheduled_events/calendars_list/buttons_enabled_checks';
import { ml } from 'plugins/ml/services/ml_api_service';
import { initPromise } from 'plugins/ml/util/promise';
import template from './calendars_list.html';
import { timefilter } from 'ui/timefilter';
uiRoutes
.when('/settings/calendars_list', {
template,
resolve: {
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
mlNodeCount: getMlNodeCount,
initPromise: initPromise(true)
}
});
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['ui.bootstrap']);
module.controller('MlCalendarsList',
function (
$scope,
$filter,
$route,
$location,
pagerFactory,
mlConfirmModalService) {
$scope.permissions = {
canCreateCalendar: checkPermission('canCreateCalendar'),
canDeleteCalendar: checkPermission('canDeleteCalendar'),
};
timefilter.disableTimeRangeSelector(); // remove time picker from top of page
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page
const mlConfirm = mlConfirmModalService;
let calendars = []; // Complete list of calendars received from the ML endpoint.
$scope.displayCalendars = []; // List of calendars being displayed after filtering and sorting.
$scope.buttonChecks = buttonsEnabledChecks($scope.permissions, mlNodesAvailable());
// Create a pager object to page through long lists of calendars.
const PAGE_SIZE = 20;
const limitTo = $filter('limitTo');
$scope.pager = pagerFactory.create(0, PAGE_SIZE, 1);
// Sort field labels, to identify column used for sorting the list.
const SORT_FIELD_LABELS = {
CALENDAR_ID: 'calendar_id',
JOB_IDS: 'job_ids',
EVENT_COUNT: 'event_count'
};
$scope.SORT_FIELD_LABELS = SORT_FIELD_LABELS;
$scope.sortField = SORT_FIELD_LABELS.CALENDAR_ID;
$scope.sortReverse = false;
$scope.onSortChange = function (field, reverse) {
$scope.sortField = field;
$scope.sortReverse = reverse;
};
// Listen for changes to the search text and build Regexp.
$scope.onQueryChange = function (query) {
if (query) {
$scope.filterRegexp = new RegExp(`(${query})`, 'gi');
} else {
$scope.filterRegexp = undefined;
}
};
$scope.onPageNext = function () {
$scope.pager.nextPage();
};
$scope.onPagePrevious = function () {
$scope.pager.previousPage();
};
$scope.$watchMulti([
'sortField',
'sortReverse',
'filterRegexp',
'pager.currentPage'
], applyTableSettings);
$scope.newCalendarClick = function () {
$location.path('settings/calendars_list/new_calendar');
};
$scope.editCalendarClick = function (id) {
$location.path(`settings/calendars_list/edit_calendar/${id}`);
};
$scope.deleteCalendarClick = function (calendarId) {
mlConfirm.open({
message: `Confirm deletion of ${calendarId}?`,
title: `Delete calendar`
})
.then(() => {
ml.deleteCalendar({ calendarId })
.then(loadCalendars)
.catch((error) => {
console.log(error);
});
})
.catch(() => {});
};
function loadCalendars() {
ml.calendars()
.then((resp) => {
calendars = resp;
$scope.pager = pagerFactory.create(calendars.length, PAGE_SIZE, 1);
applyTableSettings();
});
}
function applyTableSettings() {
// Applies the settings from the table controls (search filter and sorting).
let filteredCalendars = _.filter(calendars, (calendar) => {
// If search text has been entered, look for match in the calendar ID or job IDs.
return $scope.filterRegexp === undefined ||
calendar.calendar_id.match($scope.filterRegexp) ||
calendar.job_ids.join(' ').match($scope.filterRegexp);
});
filteredCalendars = _.sortBy(filteredCalendars, (calendar) => {
switch ($scope.sortField) {
case SORT_FIELD_LABELS.JOB_IDS:
return calendar.job_ids.join();
case SORT_FIELD_LABELS.EVENT_COUNT:
return calendar.events.length;
case SORT_FIELD_LABELS.CALENDAR_ID:
default:
return calendar[$scope.sortField];
}
});
if ($scope.sortReverse === true) {
filteredCalendars = filteredCalendars.reverse();
}
$scope.displayCalendars = limitTo(filteredCalendars, $scope.pager.pageSize, $scope.pager.startIndex);
$scope.pager.setTotalItems(filteredCalendars.length);
}
loadCalendars();
});

View file

@ -1,9 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './calendars_list_controller';

View file

@ -1,19 +0,0 @@
.events-list {
.actions-col {
width: 90px;
min-width: 90px;
}
.asterisk {
color: $euiColorDanger;
font-weight: $euiFontWeightBold;
cursor: default;
}
.footer-cell {
border-top: $euiBorderThin;
padding: $euiSizeS;
font-size: $euiFontSizeS;
font-style: italic;
text-align: right;
}
}

View file

@ -1,131 +0,0 @@
<div class="events-list">
<div
class="kuiVerticalRhythm"
ng-class="{'kuiControlledTable': showControls}">
<div ng-if="showControls" class="kuiToolBar">
<div class="kuiToolBarSection">
<button
class="kuiButton kuiButton--primary"
href=""
aria-label="Create new event"
tooltip="Create new event"
tooltip-append-to-body="true"
ng-disabled="lockControls === true"
ng-click="clickNewEvent()">
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-plus"></span>
New event
</button>
<button
class="kuiButton kuiButton--primary"
href=""
aria-label="Import events"
tooltip="Import events"
tooltip-append-to-body="true"
ng-disabled="lockControls === true"
ng-click="clickImportEvents()">
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-upload"></span>
Import events
</button>
</div>
<div class="kuiToolBarSection">
<tool-bar-pager-text
start-item="pager.startItem"
end-item="pager.endItem"
total-items="pager.totalItems">
</tool-bar-pager-text>
<tool-bar-pager-buttons
has-previous-page="pager.hasPreviousPage"
has-next-page="pager.hasNextPage"
on-page-next="onPageNext"
on-page-previous="onPagePrevious">
</tool-bar-pager-buttons>
</div>
</div>
<table class="kuiTable" ng-show="pageOfEvents.length">
<thead>
<tr>
<th scope="col" class="kuiTableHeaderCell">
<sortable-column field="description" on-sort-change="onSortChange" sort-field="sortField" sort-reverse="sortReverse">
Description
</sortable-column>
</th>
<th scope="col" class="kuiTableHeaderCell">
<sortable-column field="start_time" on-sort-change="onSortChange" sort-field="sortField" sort-reverse="sortReverse">
Start
</sortable-column>
</th>
<th scope="col" class="kuiTableHeaderCell">
<sortable-column field="end_time" on-sort-change="onSortChange" sort-field="sortField" sort-reverse="sortReverse">
End
</sortable-column>
</th>
<th scope="col" class="kuiTableHeaderCell actions-col"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="event in pageOfEvents track by $index" class="kuiTableRow" ng-class="{ 'kuiTableRow-isBeingDeleted': event.isBeingDeleted }">
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
<span>
{{event.description}}
</span>
<span
ng-if="event.asterisk"
class="asterisk"
tooltip='{{asteriskText}}'>*</span>
</div>
</td>
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
<span>
{{formatTime(event.start_time)}}
</span>
</div>
</td>
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
<span>
{{formatTime(event.end_time)}}
</span>
</div>
</td>
<td class="kuiTableRowCell">
<div class="kuiTableRowCell__liner">
<div class="kuiMenuButtonGroup kuiMenuButtonGroup--alignRight">
<button
class="kuiMenuButton kuiMenuButton--danger kuiMenuButton--iconText"
aria-label="Delete {{event.description}}"
ng-disabled="lockControls === true"
ng-click="deleteEvent(event)"
href="">
<span class="kuiMenuButton__icon kuiIcon fa-trash"></span>
<span>Delete</span>
</button>
</div>
</div>
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="4" class="kuiTableHeaderCell footer-cell">
Times are displayed in {{dateFormatTz === 'Browser' ? 'the browser' : dateFormatTz}} timezone
</td>
</tr>
</tfoot>
</table>
</div>
</div>

View file

@ -1,107 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import 'plugins/ml/settings/scheduled_events/components/new_event_modal';
import 'plugins/ml/settings/scheduled_events/components/import_events_modal';
import template from './events_list.html';
import moment from 'moment';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');
module.directive('mlEventsList', function (
$filter,
pagerFactory,
mlNewEventService,
mlImportEventsService,
config) {
return {
restrict: 'AE',
replace: true,
transclude: true,
template,
scope: {
events: '=',
showControls: '=',
lockControls: '=',
asteriskText: '='
},
controller: function ($scope) {
$scope.pageOfEvents = []; // Current page of events displayed in the list.
$scope.dateFormatTz = config.get('dateFormat:tz');
const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
const PAGE_SIZE = 20;
// Create objects for sorting and paging through the events.
const orderBy = $filter('orderBy');
const limitTo = $filter('limitTo');
$scope.pager = pagerFactory.create($scope.events.length, PAGE_SIZE, 1);
$scope.sortField = 'description';
$scope.sortReverse = false;
$scope.onSortChange = function (field, reverse) {
$scope.sortField = field;
$scope.sortReverse = reverse;
};
$scope.onPageNext = function () {
$scope.pager.nextPage();
};
$scope.onPagePrevious = function () {
$scope.pager.previousPage();
};
$scope.$watchMulti([
'events',
'sortField',
'sortReverse',
'pager.currentPage'
], applyTableSettings);
$scope.clickNewEvent = function () {
mlNewEventService.openNewEventWindow()
.then((event) => {
$scope.events.push(event);
applyTableSettings();
})
.catch(() => {});
};
$scope.formatTime = function (timeMs) {
const time = moment(timeMs);
return time.format(TIME_FORMAT);
};
$scope.deleteEvent = function (eventToDelete) {
$scope.events.splice($scope.events.indexOf(eventToDelete), 1);
applyTableSettings();
};
$scope.clickImportEvents = function () {
mlImportEventsService.openImportEventsWindow()
.then((events) => {
$scope.events.push(...events);
applyTableSettings();
})
.catch(() => {});
};
function applyTableSettings() {
// Apply sorting and paging to the complete list of events.
const pageOfEvents = orderBy($scope.events, $scope.sortField, $scope.sortReverse);
$scope.pageOfEvents = limitTo(pageOfEvents, $scope.pager.pageSize, $scope.pager.startIndex);
$scope.pager.setTotalItems($scope.events.length);
}
}
};
});

View file

@ -1,9 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './events_list_directive';

View file

@ -1,35 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import ngMock from 'ng_mock';
import expect from 'expect.js';
const mockModalInstance = { close: function () { }, dismiss: function () { } };
xdescribe('ML - Import Events Modal Controller', () => {
beforeEach(() => {
ngMock.module('kibana');
});
it('Initialize Import Events Modal Controller', (done) => {
ngMock.inject(function ($rootScope, $controller) {
const scope = $rootScope.$new();
expect(() => {
$controller('MlImportEventsModal', {
$scope: scope,
$modalInstance: mockModalInstance,
params: {}
});
}).to.not.throwError();
expect(scope.loadingLock).to.be(false);
done();
});
});
});

View file

@ -1,13 +0,0 @@
.import-events-modal {
font-size: $euiFontSizeS;
padding: $euiSizeL;
cursor: auto;
.no-shadow {
box-shadow: none;
}
.recurring-warning {
color: $euiColorDanger;
}
}

View file

@ -1,91 +0,0 @@
<div class="import-events-modal">
<ml-message-bar ></ml-message-bar>
<h3 class="euiTitle">Import events</h3>
<div class="euiSpacer euiSpacer--s"></div>
<div>
Import events from an ICS file
</div>
<div class="euiSpacer euiSpacer--m"></div>
<div class="import-events-container">
<div class="kuiFormSection no-shadow euiPanel euiPanel--paddingMedium">
<input
type='file'
ng-disabled="(loadingLock === true)"
ml-file-input-on-change="fileNameChanged"
/>
</div>
<div
ng-if="fileLoaded===true && newEvents.length >= 0"
class="kuiFormSection">
<span class="kuiFormLabel">Events to import: {{newEvents.length}}</span>
</div>
<div
ng-if="error===true"
class="kuiFormSection euiCallOut euiCallOut--danger">
<div class="euiCallOutHeader">
<span class="euiCallOutHeader__title">{{errorMessage}}</span>
</div>
</div>
<div
ng-if="loadingLock === true"
class="kuiFormSection">
<ml-loading-indicator
label="Importing events"
is-loading="true"
height="50"
/>
</div>
<div ng-show="fileLoaded===true && newEvents.length >= 0">
<ml-events-list
events='newEvents'
show-controls='false'
asterisk-text='RECURRING_WARNING'
>
</ml-events-list>
<div class="euiSpacer euiSpacer--m"></div>
<div ng-if="showRecurringWarning" class="recurring-warning">
*{{RECURRING_WARNING}}
<div class="euiSpacer euiSpacer--m"></div>
</div>
<div>
<label class='kuiCheckBoxLabel kuiVerticalRhythm'>
<input
ng-model='includePastEvents'
type="checkbox"
class='kuiCheckBox'
ng-click="createEventsList()"/>
<span class='kuiCheckBoxLabel__text'>Include past events</span>
</label>
</div>
</div>
</div>
<div class="clearfix"></div>
<hr class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
<button
ng-click="save()"
ng-disabled="loadingLock === true"
class="kuiButton kuiButton--primary" >
Import
</button>
<button
ng-click="cancel()"
ng-disabled="loadingLock === true"
class="kuiButton kuiButton--primary"
aria-label="Cancel">
Cancel
</button>
</div>

View file

@ -1,164 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
const icalendar = require('icalendar');
import $ from 'jquery';
import moment from 'moment';
import 'plugins/ml/settings/scheduled_events/components/events_list';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');
module.controller('MlImportEventsModal', function (
$scope,
$timeout,
$modalInstance) {
$scope.loadingLock = false;
$scope.allNewEvents = [];
$scope.newEvents = [];
$scope.fileLoaded = false;
$scope.file = undefined;
$scope.includePastEvents = false;
$scope.showRecurringWarning = false;
$scope.RECURRING_WARNING = 'Recurring events not supported. Only the first event will be imported.';
$timeout(() => {
$('.modal-dialog').width(750);
}, 0);
const MAX_FILE_SIZE_MB = 100;
// called when a file is selected using the file browser
$scope.fileNameChanged = function (event) {
$scope.$apply(() => {
if (event.target.files.length) {
reset();
$scope.file = event.target.files[0];
if ($scope.file.size <= (MAX_FILE_SIZE_MB * 1000000)) {
readFile($scope.file)
.then((resp) => {
try {
$scope.allNewEvents = parseICSFile(resp.data);
$scope.createEventsList();
$scope.fileLoaded = true;
$scope.loadingLock = false;
} catch (error) {
$scope.error = true;
$scope.errorMessage = 'Could not parse ICS file';
}
})
.catch((error) => {
console.error(error);
$scope.loadingLock = false;
});
} else {
$scope.fileLoaded = false;
$scope.loadingLock = false;
}
}
});
};
function readFile(file) {
return new Promise((resolve, reject) => {
$scope.loadingLock = true;
if (file && file.size) {
const reader = new FileReader();
reader.readAsText(file);
reader.onload = (() => {
return () => {
$scope.loadingLock = false;
const data = reader.result;
if (data === '') {
reject();
} else {
resolve({ data });
}
};
})(file);
} else {
reject();
}
});
}
function reset() {
$scope.file = undefined;
$scope.fileLoaded = false;
$scope.loadingLock = false;
$scope.newEvents = [];
$scope.error = false;
$scope.errorMessage = '';
}
function parseICSFile(data) {
const cal = icalendar.parse_calendar(data);
return createEvents(cal);
}
function createEvents(ical) {
const events = ical.events();
const mlEvents = [];
events.forEach((e) => {
if (e.element === 'VEVENT') {
const description = e.properties.SUMMARY;
const start = e.properties.DTSTART;
const end = e.properties.DTEND;
const recurring = (e.properties.RRULE !== undefined);
if (description && start && end && description.length && start.length && end.length) {
mlEvents.push({
description: description[0].value,
start_time: start[0].value.valueOf(),
end_time: end[0].value.valueOf(),
asterisk: recurring
});
}
}
});
return mlEvents;
}
// populate the newEvents list
// filtering out past events if the checkbox is ticked
$scope.createEventsList = function () {
if ($scope.includePastEvents) {
$scope.newEvents = [...$scope.allNewEvents];
} else {
const now = moment().valueOf();
$scope.newEvents = $scope.allNewEvents.filter(e => e.start_time > now);
}
$scope.showRecurringWarning = ($scope.newEvents.find(e => e.asterisk) !== undefined);
};
$scope.save = function () {
$modalInstance.close($scope.newEvents);
reset();
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}).directive('mlFileInputOnChange', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
const onChangeHandler = scope.$eval(attrs.mlFileInputOnChange);
element.on('change', onChangeHandler);
element.on('$destroy', () => {
element.off();
});
}
};
});

View file

@ -1,31 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import template from './import_events_modal.html';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');
module.service('mlImportEventsService', function ($modal) {
this.openImportEventsWindow = function () {
return new Promise((resolve, reject) => {
const modal = $modal.open({
template,
controller: 'MlImportEventsModal',
backdrop: 'static',
keyboard: false
});
modal.result
.then(resolve)
.catch(reject);
});
};
});

View file

@ -1,10 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './import_events_service';
import './import_events_modal_controller';

View file

@ -1,35 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import ngMock from 'ng_mock';
import expect from 'expect.js';
const mockModalInstance = { close: function () { }, dismiss: function () { } };
xdescribe('ML - New Event Modal Controller', () => {
beforeEach(() => {
ngMock.module('kibana');
});
it('Initialize New Event Modal Controller', (done) => {
ngMock.inject(function ($rootScope, $controller) {
const scope = $rootScope.$new();
expect(() => {
$controller('MlNewEventModal', {
$scope: scope,
$modalInstance: mockModalInstance,
params: {}
});
}).to.not.throwError();
expect(scope.event.description).to.be('');
done();
});
});
});

View file

@ -1,60 +0,0 @@
.new-event-modal {
font-size: $euiFontSizeS;
padding: $euiSizeL;
cursor: auto;
.date_container {
width: 200px;
display: inline-block;
}
.ml-new-event-contents {
margin-top: $euiSizeXS;
.main-container {
display: flex;
.tabs-container {
// SASSTODO: Proper selectors
ul {
padding: 0px;
li {
a {
border-radius: $euiSizeXS 0px 0px $euiSizeXS;
white-space: nowrap;
}
}
}
}
.calendar-container {
border: 2px solid $euiColorPrimary;
flex-grow: 1;
border-radius: $euiBorderRadius;
// SASSTODO: Proper calcs
.calendar {
width: 235px;
margin: 20px;
float: left;
.calendar-input-container {
margin-right: -33px;
}
.partition {
border-right: 1px solid $euiColorPrimary;
position: absolute;
height: 275px;
left: 270px;
top: 0px;
}
.partition-large {
height: 325px;
}
}
}
.top-tab-selected {
border-radius: 0 $euiSizeXS $euiSizeXS $euiSizeXS;
}
}
}
}

View file

@ -1,10 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './new_event_service';
import './new_event_modal_controller';

View file

@ -1,129 +0,0 @@
<div class="new-event-modal">
<ml-message-bar ></ml-message-bar>
<h3 class="euiTitle">Create new event</h3>
<div class="euiSpacer euiSpacer--s"></div>
<div class="ml-new-event-contents" >
<div class="kuiFormSection" aria-hidden="false">
<label for="id" class="kuiFormLabel">
Description
</label>
<input
type="text"
ng-disabled="isNewCalendar===false"
id="id"
name="id"
ng-model="ui.description"
ng-trim="false"
class="kuiTextInput fullWidth"
autofocus
required />
</div>
<div class="main-container row1">
<div class="tabs-container">
<ul class="nav nav-pills nav-stacked">
<li ng-class="{ active: ui.rangeType === '0' }"><a href="#" ng-click="ui.setRangeType('0', $event)"> Single day</a></li>
<li ng-class="{ active: ui.rangeType === '1' }"><a href="#" ng-click="ui.setRangeType('1', $event)"> Day range</a></li>
<li ng-class="{ active: ui.rangeType === '2' }"><a href="#" ng-click="ui.setRangeType('2', $event)"> Time range</a></li>
</ul>
</div>
<div
class="calendar-container"
ng-class="{ 'top-tab-selected': ui.rangeType === '0' }" >
<div
ng-show="ui.rangeType === RANGE_TYPE.SINGLE_DAY"
class="calendar">
<datepicker
offset-timezone
ng-model="ui.timepicker.start"
show-weeks="false">
</datepicker>
</div>
<div ng-show="ui.rangeType === RANGE_TYPE.DAY_RANGE">
<div class="row">
<div class="col-md-6 calendar">
<label class="kuiFormLabel">
From:
</label>
<datepicker
offset-timezone
ng-model="ui.timepicker.start"
max-date="ui.timepicker.end"
show-weeks="false">
</datepicker>
<div class="partition"></div>
</div>
<div class="col-md-6 calendar">
<label class="kuiFormLabel">
To:
</label>
<datepicker
offset-timezone
ng-model="ui.timepicker.end"
min-date="ui.timepicker.start"
show-weeks="false">
</datepicker>
</div>
</div>
</div>
<div ng-show="ui.rangeType === RANGE_TYPE.TIME_RANGE">
<div class="row">
<div class="col-md-6 calendar">
<div class="calendar-input-container">
<label class="kuiFormLabel">
From:
</label>
<input type="text" class="form-control" input-datetime="YYYY-MM-DD HH:mm:ss" ng-model="ui.timepicker.start">
</div>
<datepicker
offset-timezone
ng-model="ui.timepicker.start"
max-date="ui.timepicker.end"
show-weeks="false">
</datepicker>
<div class="partition partition-large"></div>
</div>
<div class="col-md-6 calendar">
<label class="kuiFormLabel">
From:
</label>
<div class="calendar-input-container">
<input type="text" class="form-control" input-datetime="YYYY-MM-DD HH:mm:ss" ng-model="ui.timepicker.end">
</div>
<datepicker
offset-timezone
ng-model="ui.timepicker.end"
min-date="ui.timepicker.start"
show-weeks="false">
</datepicker>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="clearfix"></div>
<hr class="euiHorizontalRule euiHorizontalRule--full euiHorizontalRule--marginMedium" />
<button
ng-click="save()"
ng-disabled="ui.saveEnabled() === false"
class="kuiButton kuiButton--primary" >
Add
</button>
<button
ng-click="cancel()"
ng-disabled="(saveLock === true)"
class="kuiButton kuiButton--primary"
aria-label="Cancel">
Cancel
</button>
</div>

View file

@ -1,102 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import moment from 'moment';
import $ from 'jquery';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');
module.controller('MlNewEventModal', function (
$scope,
$modalInstance,
$timeout) {
const RANGE_TYPE = {
SINGLE_DAY: '0',
DAY_RANGE: '1',
TIME_RANGE: '2',
};
$scope.RANGE_TYPE = RANGE_TYPE;
const MODAL_WIDTH_SMALL = 425;
const MODAL_WIDTH_LARGE = 700;
$scope.event = {
description: '',
start: '',
end: ''
};
$scope.ui = {
saveEnabled() {
return ($scope.ui.description !== '' && $scope.ui.description !== undefined);
},
description: '',
rangeType: RANGE_TYPE.SINGLE_DAY,
timepicker: {
start: moment().startOf('day'),
end: moment().startOf('day').add(1, 'days')
},
setRangeType(i, event) {
if (event && event.preventDefault) {
event.preventDefault();
}
$scope.ui.rangeType = i;
let width = MODAL_WIDTH_SMALL;
if (i === RANGE_TYPE.SINGLE_DAY) {
width = MODAL_WIDTH_SMALL;
} else if (i === RANGE_TYPE.DAY_RANGE || i === RANGE_TYPE.TIME_RANGE) {
width = MODAL_WIDTH_LARGE;
}
$('.modal-dialog').width(width);
}
};
$timeout(() => {
$scope.ui.setRangeType('0');
$('.ml-new-event-contents #id').focus();
}, 0);
function extractForm() {
let start = null;
let end = null;
const startMoment = moment($scope.ui.timepicker.start);
if ($scope.ui.rangeType === RANGE_TYPE.SINGLE_DAY) {
const endMoment = moment($scope.ui.timepicker.start);
start = startMoment.startOf('day');
end = endMoment.startOf('day').add(1, 'days');
}
else if ($scope.ui.rangeType === RANGE_TYPE.DAY_RANGE) {
const endMoment = moment($scope.ui.timepicker.end);
start = startMoment.startOf('day');
end = endMoment.startOf('day').add(1, 'days');
}
else if ($scope.ui.rangeType === RANGE_TYPE.TIME_RANGE) {
start = startMoment;
end = moment($scope.ui.timepicker.end);
}
return{
description: $scope.ui.description,
start_time: start.valueOf(),
end_time: end.valueOf()
};
}
$scope.save = function () {
const event = extractForm();
$modalInstance.close(event);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
});

View file

@ -1,31 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import template from './new_event_modal.html';
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml');
module.service('mlNewEventService', function ($modal) {
this.openNewEventWindow = function () {
return new Promise((resolve, reject) => {
const modal = $modal.open({
template,
controller: 'MlNewEventModal',
backdrop: 'static',
keyboard: false
});
modal.result
.then(resolve)
.catch(reject);
});
};
});

View file

@ -1,35 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
// Calendar ID requires the same format as a Job ID, therefore isJobIdValid can be used
import { isJobIdValid } from 'plugins/ml/../common/util/job_utils';
import _ from 'lodash';
export function validateCalendarId(calendarId, checks) {
let valid = true;
_.each(checks, item => item.valid = true);
if (calendarId === '' || calendarId === undefined) {
checks.calendarId.valid = false;
} else if (isJobIdValid(calendarId) === false) {
checks.calendarId.valid = false;
let msg = 'Calendar ID can contain lowercase alphanumeric (a-z and 0-9), hyphens or underscores; ';
msg += 'must start and end with an alphanumeric character';
checks.calendarId.message = msg;
}
_.each(checks, (item) => {
if (item.valid === false) {
valid = false;
}
});
return valid;
}

View file

@ -1,10 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './calendars_list';
import './new_calendar';

View file

@ -1,36 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import ngMock from 'ng_mock';
import expect from 'expect.js';
xdescribe('ML - Create Calendar Controller', () => {
beforeEach(() => {
ngMock.module('kibana');
});
it('Initialize Create Calendar Controller', (done) => {
ngMock.inject(function ($rootScope, $controller) {
const scope = $rootScope.$new();
expect(() => {
$controller('MlCreateCalendar', {
$route: {
current: {
params: {}
}
},
$scope: scope
});
}).to.not.throwError();
expect(scope.isNewCalendar).to.eql(true);
done();
});
});
});

View file

@ -1 +0,0 @@
@import 'new_calendar';

View file

@ -1,21 +0,0 @@
ml-new-calendar{
font-size: $euiFontSizeS;
// SASSTODO: Proper selector
h3 {
margin-top: 0px;
}
.new-calendar-container {
width: 100%;
margin-right: auto;
margin-left: auto;
padding-left: $euiSize;
padding-right: $euiSize;
.validation-error {
color: $euiColorDanger;
font-size: $euiFontSizeXS;
}
}
}

View file

@ -1,102 +0,0 @@
<ml-nav-menu name="calendars_list"></ml-nav-menu>
<ml-new-calendar ng-controller="MlCreateCalendar">
<ml-message-bar></ml-message-bar>
<div class="new-calendar-container">
<div class="kuiViewContent kuiViewContent--constrainedWidth">
<h3 class="euiTitle">{{pageTitle}}</h3>
<div class="kuiViewContentItem kuiVerticalRhythm">
<div class="kuiBar">
<div class="kuiBarSection">
<div class="kuiButtonGroup">
<button
class="kuiButton kuiButton--primary"
ng-disabled="saveEnabled() === false"
ng-click="save()" >
Save
</button>
<button
class="kuiButton kuiButton--basic"
ng-click="cancel()"
ng-disabled="saveLock === true"
data-test-subj="btnCancel">
Cancel
</button>
</div>
</div>
</div>
</div>
<div class="kuiViewContentItem kuiVerticalRhythm">
<div class="kuiFormSection" aria-hidden="false">
<label for="id" class="kuiFormLabel">
Calendar ID
</label>
<input
type="text"
ng-disabled="isNewCalendar===false"
id="id"
name="id"
ng-model="calendarId"
ng-trim="true"
class="kuiTextInput fullWidth"
autofocus />
<div ng-hide="validation.checks.calendarId.valid" class="validation-error">
<div class="euiSpacer euiSpacer--s"></div>
{{ ( validation.checks.calendarId.message || "Enter a name for the calendar" ) }}
</div>
</div>
<div class="kuiFormSection" aria-hidden="false">
<label for="description" class="kuiFormLabel">
Description
</label>
<input
type="text"
ng-disabled="isNewCalendar===false"
id="description"
name="description"
ng-model="description"
class="kuiTextInput fullWidth" />
</div>
<div class="kuiFormSection">
<label class="kuiFormLabel">
Jobs
</label>
<ml-item-select
item-ids='jobIds'
all-items='allJobs'
placeholder='"Jobs"'
external-update-function='updateJobsList'
>
</ml-item-select>
</div>
<div class="kuiFormSection">
<label class="kuiFormLabel">
Groups
</label>
<ml-item-select
item-ids='groupIds'
all-items='allGroups'
placeholder='"Groups"'
allow-tagging='true'
tagging-text='"(new group)"'
external-update-function='updateGroupsList'
>
</ml-item-select>
</div>
</div>
<div class="kuiViewContentItem kuiVerticalRhythm">
<label class="kuiFormLabel">
Events
</label>
<ml-events-list
events='events'
show-controls='true'
lock-controls='saveLock'
/>
</div>
</div>
</div>
</ml-calendars-list>

View file

@ -1,163 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import $ from 'jquery';
import _ from 'lodash';
import 'plugins/ml/components/item_select';
import 'plugins/ml/settings/scheduled_events/components/events_list';
import { validateCalendarId } from 'plugins/ml/settings/scheduled_events/components/utils/validate_calendar';
import uiRoutes from 'ui/routes';
import { checkFullLicense } from 'plugins/ml/license/check_license';
import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
import { mlJobService } from 'plugins/ml/services/job_service';
import { mlCalendarService } from 'plugins/ml/services/calendar_service';
import { mlMessageBarService } from 'plugins/ml/components/messagebar/messagebar_service';
import { initPromise } from 'plugins/ml/util/promise';
import { ml } from 'plugins/ml/services/ml_api_service';
import template from './create_calendar.html';
uiRoutes
.when('/settings/calendars_list/new_calendar', {
template,
resolve: {
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
checkMlNodesAvailable,
initPromise: initPromise(true)
}
})
.when('/settings/calendars_list/edit_calendar/:calendarId', {
template,
resolve: {
CheckLicense: checkFullLicense,
privileges: checkGetJobsPrivilege,
checkMlNodesAvailable,
initPromise: initPromise(true)
}
});
import { uiModules } from 'ui/modules';
const module = uiModules.get('apps/ml', ['ui.bootstrap']);
module.controller('MlCreateCalendar',
function (
$scope,
$route,
$location) {
const msgs = mlMessageBarService;
msgs.clear();
const calendarId = $route.current.params.calendarId;
$scope.isNewCalendar = (calendarId === undefined);
$scope.pageTitle = $scope.isNewCalendar ? 'Create new calendar' : `Edit calendar ${calendarId}`;
$scope.calendarId = calendarId || '';
$scope.description = '';
$scope.events = [];
$scope.jobIds = [];
$scope.allJobs = [];
$scope.groupIds = [];
$scope.allGroups = [];
$scope.updateJobsList = {};
$scope.updateGroupsList = {};
$scope.saveLock = false;
$scope.validation = {
checks: {
calendarId: { valid: true },
}
};
mlJobService.loadJobs()
.then(() => {
const jobs = mlJobService.jobs;
$scope.allJobs = jobs.map(j => ({ id: j.job_id }));
$scope.allGroups = mlJobService.getJobGroups().map(g => ({ id: g.id }));
mlCalendarService.loadCalendars(jobs)
.then((resp) => {
const calendars = resp.calendars;
const calGroups = mlCalendarService.getCalendarGroups();
// append calendar groups to the list of all groups and deduplicate
$scope.allGroups.push(...calGroups);
$scope.allGroups = _.uniq($scope.allGroups, 'id');
$scope.allGroups = _.sortBy($scope.allGroups, 'id');
calendars.forEach((calendar) => {
// if we're editing an existing calendar
if (calendarId !== undefined && calendarId === calendar.calendar_id) {
$scope.events = calendar.events || [];
$scope.description = calendar.description || '';
calendar.job_ids.forEach(id => {
if ($scope.allJobs.find((j) => j.id === id)) {
// if the job exists, add it to the job list
$scope.jobIds.push(id);
} else if ($scope.allGroups.find((g) => g.id === id)) {
// otherwise, it could be a group,
// if it exists as a group, add it to the group list
$scope.groupIds.push(id);
}
});
}
});
$scope.updateJobsList.update($scope.jobIds);
$scope.updateGroupsList.update($scope.groupIds);
});
$('.new-calendar-container #id').focus();
});
$scope.save = function () {
msgs.clear();
// Just pass the three event properties used by the endpoint, ignoring UI-specific properties
// such as 'asterisk' which is used to flag recurring events from an ICS file import.
const events = $scope.events.map((event) => {
return {
description: event.description,
start_time: event.start_time,
end_time: event.end_time
};
});
const calendar = {
calendarId: $scope.calendarId,
description: $scope.description,
events,
job_ids: [...$scope.jobIds, ...$scope.groupIds],
};
if (validateCalendarId(calendar.calendarId, $scope.validation.checks)) {
$scope.saveLock = true;
const saveFunc = $scope.isNewCalendar ? (c => ml.addCalendar(c)) : (c => ml.updateCalendar(c));
saveFunc(calendar)
.then(() => {
$location.path('settings/calendars_list');
$scope.saveLock = false;
})
.catch((error) => {
msgs.error('Save calendar failed: ', error);
$scope.saveLock = false;
});
}
};
$scope.cancel = function () {
$location.path('settings/calendars_list');
};
$scope.saveEnabled = function () {
return ($scope.calendarId !== '' && $scope.calendarId !== undefined && $scope.saveLock === false);
};
});

View file

@ -1,9 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import './create_calendar_controller';