Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/CoreHome/vue/src/AjaxHelper/AjaxHelper.ts')
-rw-r--r--plugins/CoreHome/vue/src/AjaxHelper/AjaxHelper.ts498
1 files changed, 498 insertions, 0 deletions
diff --git a/plugins/CoreHome/vue/src/AjaxHelper/AjaxHelper.ts b/plugins/CoreHome/vue/src/AjaxHelper/AjaxHelper.ts
new file mode 100644
index 0000000000..788f3a8f45
--- /dev/null
+++ b/plugins/CoreHome/vue/src/AjaxHelper/AjaxHelper.ts
@@ -0,0 +1,498 @@
+/*!
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+import PiwikUrl from '../PiwikUrl/PiwikUrl';
+
+window.globalAjaxQueue = [] as GlobalAjaxQueue;
+window.globalAjaxQueue.active = 0;
+
+window.globalAjaxQueue.clean = function globalAjaxQueueClean() {
+ for (let i = this.length; i >= 0; i -= 1) {
+ if (!this[i] || this[i].readyState === 4) {
+ this.splice(i, 1);
+ }
+ }
+};
+
+window.globalAjaxQueue.push = function globalAjaxQueuePush(...args: (XMLHttpRequest|null)[]) {
+ this.active += args.length;
+
+ // cleanup ajax queue
+ this.clean();
+
+ // call original array push
+ return Array.prototype.push.call(this, ...args);
+};
+
+window.globalAjaxQueue.abort = function globalAjaxQueueAbort() {
+ // abort all queued requests if possible
+ this.forEach((x) => x && x.abort && x.abort());
+
+ // remove all elements from array
+ this.splice(0, this.length);
+
+ this.active = 0;
+};
+
+type ParameterValue = string | number | null | undefined | ParameterValue[];
+type Parameters = {[name: string]: ParameterValue | Parameters};
+type AnyFunction = (...params:any[]) => any; // eslint-disable-line
+
+/**
+ * error callback to use by default
+ */
+function defaultErrorCallback(deferred: XMLHttpRequest, status: string): void {
+ // do not display error message if request was aborted
+ if (status === 'abort') {
+ return;
+ }
+
+ const loadingError = $('#loadingError');
+ if (Piwik_Popover.isOpen() && deferred && deferred.status === 500) {
+ if (deferred && deferred.status === 500) {
+ $(document.body).html(piwikHelper.escape(deferred.responseText));
+ }
+ } else {
+ loadingError.show();
+ }
+}
+
+/**
+ * Global ajax helper to handle requests within piwik
+ */
+export default class AjaxHelper {
+ /**
+ * Format of response
+ */
+ format = 'json';
+
+ /**
+ * A timeout for the request which will override any global timeout
+ */
+ timeout = null;
+
+ /**
+ * Callback function to be executed on success
+ */
+ callback: AnyFunction = null;
+
+ /**
+ * Use this.callback if an error is returned
+ */
+ useRegularCallbackInCaseOfError = false;
+
+ /**
+ * Callback function to be executed on error
+ */
+ errorCallback: AnyFunction;
+
+ withToken = false;
+
+ /**
+ * Callback function to be executed on complete (after error or success)
+ */
+ completeCallback: AnyFunction;
+
+ /**
+ * Params to be passed as GET params
+ * @see ajaxHelper.mixinDefaultGetParams
+ */
+ getParams: Parameters = {};
+
+ /**
+ * Base URL used in the AJAX request. Can be set by setUrl.
+ *
+ * It is set to '?' rather than 'index.php?' to increase chances that it works
+ * including for users who have an automatic 301 redirection from index.php? to ?
+ * POST values are missing when there is such 301 redirection. So by by-passing
+ * this 301 redirection, we avoid this issue.
+ *
+ * @see ajaxHelper.setUrl
+ */
+ getUrl = '?';
+
+ /**
+ * Params to be passed as GET params
+ * @see ajaxHelper.mixinDefaultPostParams
+ */
+ postParams: Parameters = {};
+
+ /**
+ * Element to be displayed while loading
+ */
+ loadingElement: HTMLElement|null|JQuery|JQLite|string = null;
+
+ /**
+ * Element to be displayed on error
+ */
+ errorElement: HTMLElement|JQuery|JQLite|string = '#ajaxError';
+
+ /**
+ * Handle for current request
+ */
+ requestHandle: XMLHttpRequest|JQuery.jqXHR|null = null;
+
+ defaultParams = ['idSite', 'period', 'date', 'segment'];
+
+ constructor() {
+ this.errorCallback = defaultErrorCallback;
+ }
+
+ /**
+ * Adds params to the request.
+ * If params are given more then once, the latest given value is used for the request
+ *
+ * @param params
+ * @param type type of given parameters (POST or GET)
+ * @return {void}
+ */
+ addParams(params: Parameters|string, type: string): void {
+ if (typeof params === 'string') {
+ // TODO: add global types for broadcast (multiple uses below)
+ params = window['broadcast'].getValuesFromUrl(params); // eslint-disable-line
+ }
+
+ const arrayParams = ['compareSegments', 'comparePeriods', 'compareDates'];
+ Object.keys(params).forEach((key) => {
+ const value = params[key];
+ if (arrayParams.indexOf(key) !== -1
+ && !value
+ ) {
+ return;
+ }
+
+ if (type.toLowerCase() === 'get') {
+ this.getParams[key] = value;
+ } else if (type.toLowerCase() === 'post') {
+ this.postParams[key] = value;
+ }
+ });
+ }
+
+ withTokenInUrl(): void {
+ this.withToken = true;
+ }
+
+ /**
+ * Sets the base URL to use in the AJAX request.
+ */
+ setUrl(url: string): void {
+ this.addParams(broadcast.getValuesFromUrl(url), 'GET');
+ }
+
+ /**
+ * Gets this helper instance ready to send a bulk request. Each argument to this
+ * function is a single request to use.
+ */
+ setBulkRequests(...urls: string[]): void {
+ const urlsProcessed = urls.map((u) => $.param(u));
+
+ this.addParams({
+ module: 'API',
+ method: 'API.getBulkRequest',
+ urls: urlsProcessed,
+ format: 'json',
+ }, 'post');
+ }
+
+ /**
+ * Set a timeout (in milliseconds) for the request. This will override any global timeout.
+ *
+ * @param timeout Timeout in milliseconds
+ */
+ setTimeout(timeout: number): void {
+ this.timeout = timeout;
+ }
+
+ /**
+ * Sets the callback called after the request finishes
+ *
+ * @param callback Callback function
+ */
+ setCallback(callback: AnyFunction): void {
+ this.callback = callback;
+ }
+
+ /**
+ * Set that the callback passed to setCallback() should be used if an application error (i.e. an
+ * Exception in PHP) is returned.
+ */
+ useCallbackInCaseOfError(): void {
+ this.useRegularCallbackInCaseOfError = true;
+ }
+
+ /**
+ * Set callback to redirect on success handler
+ * &update=1(+x) will be appended to the current url
+ *
+ * @param [params] to modify in redirect url
+ * @return {void}
+ */
+ redirectOnSuccess(params: Parameters): void {
+ this.setCallback(() => {
+ piwikHelper.redirect(params);
+ });
+ }
+
+ /**
+ * Sets the callback called in case of an error within the request
+ */
+ setErrorCallback(callback: AnyFunction): void {
+ this.errorCallback = callback;
+ }
+
+ /**
+ * Sets the complete callback which is called after an error or success callback.
+ */
+ setCompleteCallback(callback: AnyFunction): void {
+ this.completeCallback = callback;
+ }
+
+ /**
+ * Sets the response format for the request
+ *
+ * @param format response format (e.g. json, html, ...)
+ */
+ setFormat(format: string): void {
+ this.format = format;
+ }
+
+ /**
+ * Set the div element to show while request is loading
+ *
+ * @param [element] selector for the loading element
+ */
+ setLoadingElement(element: string|HTMLElement|JQuery): void {
+ this.loadingElement = element || '#ajaxLoadingDiv';
+ }
+
+ /**
+ * Set the div element to show on error
+ *
+ * @param element selector for the error element
+ */
+ setErrorElement(element: HTMLElement|JQuery|string): void {
+ if (!element) {
+ return;
+ }
+ this.errorElement = element;
+ }
+
+ /**
+ * Detect whether are allowed to use the given default parameter or not
+ */
+ private useGETDefaultParameter(parameter: string): boolean {
+ if (parameter && this.defaultParams) {
+ for (let i = 0; i < this.defaultParams.length; i += 1) {
+ if (this.defaultParams[i] === parameter) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Removes a default parameter that is usually send automatically along the request.
+ *
+ * @param parameter A name such as "period", "date", "segment".
+ */
+ removeDefaultParameter(parameter: string): void {
+ if (parameter && this.defaultParams) {
+ for (let i = 0; i < this.defaultParams.length; i += 1) {
+ if (this.defaultParams[i] === parameter) {
+ this.defaultParams.splice(i, 1);
+ }
+ }
+ }
+ }
+
+ /**
+ * Send the request
+ */
+ send(): void {
+ if ($(this.errorElement).length) {
+ $(this.errorElement).hide();
+ }
+
+ if (this.loadingElement) {
+ $(this.loadingElement).fadeIn();
+ }
+
+ this.requestHandle = this.buildAjaxCall();
+ globalAjaxQueue.push(this.requestHandle);
+ }
+
+ /**
+ * Aborts the current request if it is (still) running
+ */
+ abort(): void {
+ if (this.requestHandle && typeof this.requestHandle.abort === 'function') {
+ this.requestHandle.abort();
+ this.requestHandle = null;
+ }
+ }
+
+ /**
+ * Builds and sends the ajax requests
+ */
+ private buildAjaxCall(): JQuery.jqXHR {
+ const self = this;
+ const parameters = this.mixinDefaultGetParams(this.getParams);
+
+ let url = this.getUrl;
+ if (url[url.length - 1] !== '?') {
+ url += '&';
+ }
+
+ // we took care of encoding &segment properly already, so we don't use $.param for it ($.param
+ // URL encodes the values)
+ if (parameters.segment) {
+ url = `${url}segment=${parameters.segment}&`;
+ delete parameters.segment;
+ }
+ if (parameters.date) {
+ url = `${url}date=${decodeURIComponent(parameters.date.toString())}&`;
+ delete parameters.date;
+ }
+ url += $.param(parameters);
+ const ajaxCall = {
+ type: 'POST',
+ async: true,
+ url,
+ dataType: this.format || 'json',
+ complete: this.completeCallback,
+ error: function errorCallback() {
+ globalAjaxQueue.active -= 1;
+
+ if (self.errorCallback) {
+ self.errorCallback.apply(this, arguments); // eslint-disable-line
+ }
+ },
+ success: (response, status, request) => {
+ if (this.loadingElement) {
+ $(this.loadingElement).hide();
+ }
+
+ if (response && response.result === 'error' && !this.useRegularCallbackInCaseOfError) {
+ let placeAt = null;
+ let type = 'toast';
+ if ($(this.errorElement).length && response.message) {
+ $(this.errorElement).show();
+ placeAt = this.errorElement;
+ type = null;
+ }
+
+ if (response.message) {
+ const UI = window['require']('piwik/UI'); // eslint-disable-line
+ const notification = new UI.Notification();
+ notification.show(response.message, {
+ placeat: placeAt,
+ context: 'error',
+ type,
+ id: 'ajaxHelper',
+ });
+ notification.scrollToNotification();
+ }
+ } else if (this.callback) {
+ this.callback(response, status, request);
+ }
+
+ globalAjaxQueue.active -= 1;
+ const { piwik } = window;
+ if (piwik
+ && piwik.ajaxRequestFinished
+ ) {
+ piwik.ajaxRequestFinished();
+ }
+ },
+ data: this.mixinDefaultPostParams(this.postParams),
+ timeout: this.timeout !== null ? this.timeout : undefined,
+ };
+
+ return $.ajax(ajaxCall);
+ }
+
+ private isRequestToApiMethod() {
+ return (this.getParams && this.getParams.module === 'API' && this.getParams.method)
+ || (this.postParams && this.postParams.module === 'API' && this.postParams.method);
+ }
+
+ isWidgetizedRequest(): boolean {
+ return (broadcast.getValueFromUrl('module') === 'Widgetize');
+ }
+
+ private getDefaultPostParams() {
+ if (this.withToken || this.isRequestToApiMethod() || piwik.shouldPropagateTokenAuth) {
+ return {
+ token_auth: piwik.token_auth,
+ // When viewing a widgetized report there won't be any session that can be used, so don't
+ // force session usage
+ force_api_session: broadcast.isWidgetizeRequestWithoutSession() ? 0 : 1,
+ };
+ }
+
+ return {};
+ }
+
+ /**
+ * Mixin the default parameters to send as POST
+ *
+ * @param params parameter object
+ */
+ private mixinDefaultPostParams(params): Parameters {
+ const defaultParams = this.getDefaultPostParams();
+
+ const mergedParams = {
+ ...defaultParams,
+ ...params,
+ };
+
+ return mergedParams;
+ }
+
+ /**
+ * Mixin the default parameters to send as GET
+ *
+ * @param params parameter object
+ */
+ private mixinDefaultGetParams(originalParams): Parameters {
+ const segment = PiwikUrl.getSearchParam('segment');
+
+ const defaultParams = {
+ idSite: piwik.idSite || broadcast.getValueFromUrl('idSite'),
+ period: piwik.period || broadcast.getValueFromUrl('period'),
+ segment,
+ };
+
+ const params = originalParams;
+
+ // never append token_auth to url
+ if (params.token_auth) {
+ params.token_auth = null;
+ delete params.token_auth;
+ }
+
+ Object.keys(defaultParams).forEach((key) => {
+ if (this.useGETDefaultParameter(key)
+ && !params[key]
+ && !this.postParams[key]
+ && defaultParams[key]
+ ) {
+ params[key] = defaultParams[key];
+ }
+ });
+
+ // handle default date & period if not already set
+ if (this.useGETDefaultParameter('date') && !params.date && !this.postParams.date) {
+ params.date = piwik.currentDateString;
+ }
+
+ return params;
+ }
+}