import { checkBrowserCompatibility } from '../../view/helpers';
import { Events, ApiMethods, callApiMethod, parseJson, _sessionList } from './components';
import { PromptLogin } from './promptlogin';
import { Resources } from '../../resources/resources';
let PmaStartUrl = "http://127.0.0.1:54001/";
export
/**
* The Context class glues component instances together. It provides API method implementations and simplifies the interaction with PMA.core by automatically managing authentication and sessionID handling, via the authentication provider classes.
* @memberof PMA.UI.Components
* @alias Context
* @class
* @param {Object} options
* @param {string} options.caller
* @tutorial 03-gallery
* @tutorial 04-tree
* @tutorial 05-annotations
*/
class Context {
constructor(options) {
if (!checkBrowserCompatibility()) {
return;
}
if (!options || typeof options.caller !== "string") {
throw "Caller parameter not supplied";
}
this.options = options;
// create event listeners object and add one array for each event type
this.listeners = {};
for (var ev in Events) {
// if (Events.hasOwnProperty(ev)) {
if (Object.prototype.hasOwnProperty.call(Events, ev)) {
this.listeners[Events[ev]] = [];
}
}
// list of components that can authenticate against a PMA.core server
this.authenticationProviders = [];
}
/**
* Gets the caller value
* @return {string}
*/
getCaller() {
return this.options.caller;
}
/**
* Adds an authentication provider to the list of available authentication methods
* @param {AutoLogin|PromptLogin|SessionLogin} provider
*/
registerAuthenticationProvider(provider) {
if (typeof provider.authenticate !== "function") {
console.error("Invalid authentication provider");
}
else {
this.authenticationProviders.push(provider);
}
}
/**
* Removes an authentication provider from the list of available authentication methods
* @param {AutoLogin|PromptLogin|SessionLogin} provider - The provider to remove
* @param {bool} clearCache - Clears the session ids cache
*/
removeAuthenticationProvider(provider, clearCache) {
if (typeof provider.authenticate !== "function") {
console.error("Invalid authentication provider");
}
else {
for (var i = 0; i < this.authenticationProviders.length; i++) {
if (this.authenticationProviders[i] === provider) {
this.authenticationProviders.splice(i, 1);
// Clear all cache
if (clearCache !== false) {
_sessionList.clear();
}
break;
}
}
}
}
/**
* Gets the list of available authentication methods
* @returns [{PMA.UI.Authentication.AutoLogin|PMA.UI.Authentication.PromptLogin|PMA.UI.Authentication.SessionLogin}] providers
*/
getAuthenticationProviders() {
return this.authenticationProviders;
}
/**
* PMA.core authentication response
* @typedef {Object} Context~authenticationResponse
* @property {string} SessionId - The session ID
* @property {string} Username - The username
* @property {string} Email - The user's email
* @property {string} FirstName - The user's first name
* @property {string} LastName - The user's last name
*/
/**
* A function called after successfully pinging a list of servers
* @callback Context~pingServersDoneCallback
* @param {String[]} servers - A sorted list of server url's from fastest to slowest
* @param {Object[]} detailInfo - An array of detailed information from pinging the servers (not sorted)
* @param {string} detailInfo.serverUrl - The server url
* @param {bool} detailInfo.success - Whether the server responded to any pinging
* @param {Number[]} detailInfo.times - An array of all the times the server took to respond (in miliseconds)
* @param {Number} detailInfo.avgTime - The average time the server took to respond (in miliseconds)
* @param {Number} detailInfo.attempts - The number of attempted pings to the server
*/
/**
* Pings a list of servers to find the fastests
* @param {string[]} servers - An array of server url to ping
* @param {Context~pingServersDoneCallback} done - The done callback to run
* @param {Number} [maxAttempts=5] - The number of attempts for each server
* */
pingServers(servers, done, maxAttempts) {
if (maxAttempts <= 1 || !maxAttempts || maxAttempts === undefined) {
maxAttempts = 6;
}
var instances = servers.map(function (s) { return { serverUrl: s, success: false, times: [], avgTime: null, attempts: 0 }; });
var cb = function (startTime, instanceIndex, success) {
instances[instanceIndex].attempts++;
if (success && instances[instanceIndex].attempts > 1) {
// We ignore first attempt for warm up
var time = performance.now() - startTime;
instances[instanceIndex].times.push(time);
instances[instanceIndex].avgTime = instances[instanceIndex].avgTime != null ?
((instances[instanceIndex].avgTime * instances[instanceIndex].times.length) + time) / (instances[instanceIndex].times.length + 1) :
time;
instances[instanceIndex].success = true;
}
if (instances[instanceIndex].attempts >= maxAttempts) {
instanceIndex++;
}
if (instanceIndex >= instances.length) {
// done testing
if (typeof done === "function") {
done(instances.sort(function (a, b) {
if (a.avgTime != null && b.avgTime != null) {
return a.avgTime - b.avgTime;
}
else if (a.avgTime != null) {
return -10000000;
}
else {
return 10000000;
}
}).map(function (s) { return s.serverUrl; }), instances);
}
}
else {
// Run next test
var nowTime = performance.now();
callApiMethod({
serverUrl: instances[instanceIndex].serverUrl,
method: ApiMethods.GetVersionInfo,
success: cb.bind(this, nowTime, instanceIndex, true),
failure: cb.bind(this, nowTime, instanceIndex, false)
});
}
};
//Start first test
var nowTime = performance.now();
callApiMethod({
serverUrl: instances[0].serverUrl,
method: ApiMethods.GetVersionInfo,
success: cb.bind(this, nowTime, 0, true),
failure: cb.bind(this, nowTime, 0, false)
});
}
/**
* Connection information
* @typedef {Object} Context~connection
* @property {Number} roundTrip - The ping time in milliseconds
* @property {Number} downloadSpeed - The download speed in bytes/second
*/
/**
* Calculates the ping time and download speed between the client and a PMA.core instance
* @param {string} serverUrl - The server url to connect to
* @returns {Context~connection}
**/
async evaluateConnection(serverUrl) {
if (!serverUrl.endsWith('/')) {
serverUrl += '/';
}
const maxAttempts = 10;
const dataSize = 50 * 1024 * 1024;
let pingTime = 0;
let i = 0;
do {
let start = performance.now();
try {
await fetch(`${serverUrl}api/json/GetVersionInfo`);
}
catch {
console.error("connection failed");
return {
roundTrip: null,
downloadSpeed: null
};
}
let end = performance.now();
pingTime += end - start;
i++;
} while (i < maxAttempts)
pingTime /= maxAttempts;
let start = performance.now();
try {
const resp = await fetch(`${serverUrl}speedtest`, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
"body": `size=${dataSize}`,
"method": "POST",
// "mode": 'no-cors'
});
const reader = resp.body.getReader();
var finalDone = false;
while (!finalDone) {
const { done, value } = await reader.read();
finalDone = done;
}
}
catch {
console.error("download failed");
return {
roundTrip: pingTime,
downloadSpeed: null
};
}
let end = performance.now();
const downloadTime = end - start;
const transferSpeed = dataSize / (downloadTime / 1000);
return {
roundTrip: pingTime,
downloadSpeed: transferSpeed
};
}
/**
* Gets the user information associated with a server
* @param {string} serverUrl - The URL of the PMA.core for which to fetch user information
* @returns {Context~authenticationResponse} If no authentication has taken place for the particular server, null is returned, otherwise an object.
*/
getUserInfo(serverUrl) {
for (var url in _sessionList.get()) {
// if (_sessionList.get().hasOwnProperty(url) && url === serverUrl && _sessionList.get()[url]) {
if (Object.prototype.hasOwnProperty.call(_sessionList.get(), url) && url === serverUrl && _sessionList.get()[url]) {
return _sessionList.get()[url];
}
}
return null;
}
/**
* Called when a session ID was successfully obtained
* @callback Context~getSessionCallback
* @param {string} sessionID
*/
/**
* Finds a session ID for the requested server, either by scanning the already cached session ids or by invoking one by one the available authentication providers, until a valid session ID is found
* @param {string} serverUrl - The URL of the PMA.core for which to fetch a session ID
* @param {Context~getSessionCallback} success
* @param {function} [failure]
*/
getSession(serverUrl, success, failure) {
// scan cached session IDs
for (var url in _sessionList.get()) {
// if (_sessionList.get().hasOwnProperty(url) && url === serverUrl && _sessionList.get()[url]) {
if (Object.prototype.hasOwnProperty.call(_sessionList.get(), url) && url === serverUrl && _sessionList.get()[url]) {
success(_sessionList.get()[url].SessionId);
return;
}
}
// no session ID found in cached list
// fire all providers until one succeeds or all failed
authenticateWithProvider.call(this, serverUrl, success, failure);
}
getImageInfo(serverUrl, pathOrUid, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.GetImageInfo,
data: {
pathOrUid: pathOrUid
},
httpMethod: "GET",
success: success,
failure: failure
});
}
getFiles(serverUrl, path, success, failure) {
console.warn("Context.getFiles is deprecated please use Context.getSlides instead");
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.GetFiles,
data: {
path: path
},
httpMethod: "GET",
success: success,
failure: failure
});
}
/**
* Gets all slides in a specified path
* @param {Object} options - Parameters to pass to the GetSlides request
* @param {string} options.serverUrl - The server url to get slides from
* @param {string} options.path - The path to get slides from
* @param {PMA.UI.Components.GetSlidesScope} options.scope - The search scope to use
* @param {function} [options.success] - Called upon success
* @param {function} [options.failure] - Called upon failure
**/
getSlides(options) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: options.serverUrl,
method: ApiMethods.GetFiles,
data: {
path: options.path,
scope: options.scope ? options.scope : 0
},
httpMethod: "GET",
success: options.success,
failure: options.failure
});
}
getDirectories(serverUrl, path, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.GetDirectories,
data: {
path: path
},
httpMethod: "GET",
success: success,
failure: failure
});
}
createDirectory(serverUrl, path, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.CreateDirectory,
data: {
path: path
},
httpMethod: "POST",
contentType: "application/json",
success: success,
failure: failure
});
}
deleteDirectory(serverUrl, path, deleteContents, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.DeleteDirectory,
data: {
path: path,
deleteContents: deleteContents
},
httpMethod: "POST",
contentType: "application/json",
success: success,
failure: failure
});
}
renameDirectory(serverUrl, path, newName, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.RenameDirectory,
data: {
path: path,
newName: newName
},
httpMethod: "POST",
contentType: "application/json",
success: success,
failure: failure
});
}
deleteSlide(serverUrl, path, deleteAsFile, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.DeleteSlide,
data: {
path: path,
deleteAsFile: deleteAsFile
},
httpMethod: "POST",
contentType: "application/json",
success: success,
failure: failure
});
}
getAnnotations(serverUrl, path, currentUserOnly, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.GetAnnotations,
data: {
pathOrUid: path,
currentUserOnly: currentUserOnly
},
httpMethod: "GET",
success: success,
failure: failure
});
}
addAnnotation(serverUrl, path, classification, layerID, notes, geometry, color, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.AddAnnotation,
data: {
pathOrUid: path,
classification: classification,
layerID: layerID,
notes: notes,
geometry: geometry,
color: color
},
httpMethod: "POST",
contentType: "application/json",
success: success,
failure: failure
});
}
updateAnnotation(serverUrl, path, layerID, annotationID, notes, geometry, color, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.UpdateAnnotation,
data: {
pathOrUid: path,
layerID: layerID,
annotationID: annotationID,
notes: notes,
geometry: geometry,
color: color
},
httpMethod: "POST",
contentType: "application/json",
success: success,
failure: failure
});
}
saveAnnotations(serverUrl, path, added, updated, deleted, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.SaveAnnotations,
data: {
pathOrUid: path,
added: added,
updated: updated,
deleted: deleted
},
httpMethod: "POST",
contentType: "application/json",
success: success,
failure: failure
});
}
deleteAnnotation(serverUrl, path, layerID, annotationID, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.DeleteAnnotation,
data: {
pathOrUid: path,
layerID: layerID,
annotationID: annotationID
},
httpMethod: "POST",
contentType: "application/json",
success: success,
failure: failure
});
}
getFormDefinitions(serverUrl, formIDs, rootDirectoryAlias, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.GetFormDefinitions,
data: {
formIDs: formIDs instanceof Array ? formIDs.join(",") : formIDs,
rootDirectoryAlias: rootDirectoryAlias
},
httpMethod: "GET",
success: success,
failure: failure
});
}
getFormSubmissions(serverUrl, pathOrUids, formIDs, currentUserOnly, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.GetFormSubmissions,
data: {
pathOrUids: pathOrUids instanceof Array ? pathOrUids : [],
formIDs: formIDs instanceof Array ? formIDs : [],
currentUserOnly: currentUserOnly
},
httpMethod: "POST",
contentType: "application/json",
success: success,
failure: failure
});
}
saveFormDefinition(serverUrl, definition, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.SaveFormDefinition,
apiPath: "admin",
contentType: "application/json",
data: {
definition: definition
},
httpMethod: "POST",
success: success,
failure: failure
});
}
deleteFormDefinition(serverUrl, formID, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.DeleteFormDefinition,
apiPath: "admin",
data: {
formID: formID
},
httpMethod: "GET",
success: success,
failure: failure
});
}
getVersionInfo(serverUrl, success, failure) {
callApiMethod({
serverUrl: serverUrl,
method: ApiMethods.GetVersionInfo,
success: function (http) {
if (typeof success === "function") {
var response = parseJson(http.responseText);
success(response);
}
},
failure: function (http) {
if (typeof failure === "function") {
if (http.responseText && http.responseText.length !== 0) {
var response = parseJson(http.responseText);
failure(response);
}
else {
failure({ Message: Resources.translate("Get Version Info failed") });
}
}
}
});
}
/**
* Gets the events log from the server
* @param {Object} options - Parameters to pass to the GetEvents request
* @param {string} options.serverUrl - The server url to get events log
* @param {number} options.page - The page to fetch
* @param {number} options.pageSize - The page size to fetch
* @param {function} [options.success] - Called upon success
* @param {function} [options.failure] - Called upon failure
**/
getEvents(options, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: options.serverUrl,
method: ApiMethods.GetEvents,
apiPath: "admin",
data: {
page: options.page,
pageSize: options.pageSize
},
httpMethod: "GET",
success: success,
failure: failure
});
}
deAuthenticate(serverUrl, success, failure) {
var sessionId = null;
// if (_sessionList.get().hasOwnProperty(serverUrl) && _sessionList.get()[serverUrl]) {
if (Object.prototype.hasOwnProperty.call(_sessionList.get(), serverUrl) && _sessionList.get()[serverUrl]) {
sessionId = _sessionList.get()[serverUrl].SessionId;
if (!sessionId) {
// nothing to do, no session ID cached, call success
if (typeof success === "function") {
success();
return;
}
}
}
else {
var cbFn = function (sId) {
sessionId = sId;
};
// there is no cached sessionId for this server check for a sessionLogin provider
for (var i = 0; i < this.authenticationProviders.length; i++) {
if (this.authenticationProviders[i] instanceof PMA.UI.Authentication.SessionLogin) { // eslint-disable-line no-undef
// check that this sessionLogin provider can handle the requested serverUrl
if (this.authenticationProviders[i].authenticate(serverUrl, cbFn, null)) {
break;
}
continue;
}
}
}
if (sessionId == null) {
// cannot handle this serverUrl just return
return;
}
_sessionList.set(serverUrl, null);
callApiMethod({
serverUrl: serverUrl,
method: ApiMethods.DeAuthenticate,
data: { sessionID: sessionId },
success: function () {
if (typeof success === "function") {
success();
}
},
failure: function (http) {
if (typeof failure === "function") {
if (http.responseText && http.responseText.length !== 0) {
var response = parseJson(http.responseText);
failure(response);
}
else {
failure();
}
}
}
});
}
queryFilename(serverUrl, path, pattern, success, failure) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: serverUrl,
method: ApiMethods.QueryFilename,
apiPath: "query",
data: {
path: path,
pattern: pattern
},
httpMethod: "GET",
success: success,
failure: failure
});
}
/**
* Gets all distinct values for a field in a form
* @param {Object} options - Parameters to pass to the "distinct values" request
* @param {string} options.serverUrl - The server url to use
* @param {string} options.formId - The Form Id to use
* @param {string} options.fieldId - The Field Id to get distinct values for
* @param {function} [options.success] - Called upon success
* @param {function} [options.failure] - Called upon failure
**/
distinctValues(options) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: options.serverUrl,
method: ApiMethods.DistinctValues,
apiPath: "query",
data: {
formID: options.formId,
fieldID: options.fieldId
},
httpMethod: "GET",
success: options.success,
failure: options.failure
});
}
/**
* Gets information for all slides specified
* @param {Object} options - Parameters to pass to the GetSlides request
* @param {string} options.serverUrl - The server url to get slides from
* @param {string[]} options.images - An array of image paths or uids to fetch information for
* @param {function} [options.success] - Called upon success
* @param {function} [options.failure] - Called upon failure
**/
getImagesInfo(options) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: options.serverUrl,
method: ApiMethods.GetImagesInfo,
apiPath: "api",
data: {
pathOrUids: options.images
},
contentType: "application/json",
httpMethod: "POST",
success: options.success,
failure: options.failure
});
}
/**
* Gets slides that satisfy the specified expressions
* @param {Object} options - Parameters to pass to the "distinct values" request
* @param {string} options.serverUrl - The server url to use
* @param {Object[]} options.expressions - The Expressions to use
* @param {Number} options.expressions.FormID - The form Id for this expression
* @param {Number} options.expressions.FieldID - The field Id for this expression
* @param {Number} options.expressions.Operator - The Operator for this expression ( Equals = 0, LessThan = 1, LessThanOrEquals = 2, GreaterThan = 3, GreaterThanOrEquals = 4)
* @param {Number} options.expressions.Value - The value to compare for this expression
* @param {function} [options.success] - Called upon success
* @param {function} [options.failure] - Called upon failure
**/
metadata(options) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: options.serverUrl,
method: ApiMethods.Metadata,
apiPath: "query",
data: {
expressions: options.expressions
},
httpMethod: "POST",
contentType: "application/json",
success: options.success,
failure: options.failure
});
}
/**
* Checks if a PMA.start server is available
* @param {function} success - The function to call when check succeded
* @param {function} failure - The function to call when check failed
**/
checkPmaStartServer(success, failure) {
callApiMethod({
method: ApiMethods.GetVersionInfo,
httpMethod: "GET",
serverUrl: PmaStartUrl,
success: function () {
if (typeof callback === "function") {
success.call(this, true);
}
},
failure: function () {
if (typeof callback === "function") {
failure.call(this, false);
}
}
});
}
/**
* Returns the url to PMA.start CORS allow page
* @return {string}
**/
getPmaStartCorsUrl() {
var url = PmaStartUrl + "home/allowdomain/?host=" + document.location.origin;
return url;
}
/**
* Gets slides that satisfy the specified expressions
* @param {Object} options - Parameters to pass to the "distinct values" request
* @param {string} options.serverUrl - The server url to use
* @param {string} options.name - The script name to run
* @param {object} [options.params] - Optional script parameters to pass
* @param {function} [options.success] - Called upon success
* @param {function} [options.failure] - Called upon failure
**/
runScript(options) {
callApiMethodWithAuthentication.call(
this, {
attemptCount: 0,
serverUrl: options.serverUrl,
method: ApiMethods.RunScripts,
apiPath: "scripts",
webapi: true,
data: Object.assign({
name: options.name,
}, options.params),
httpMethod: "GET",
success: options.success,
failure: options.failure
});
}
// registers an event listener
listen(eventName, callback) {
// if (!this.listeners.hasOwnProperty(eventName)) {
if (!Object.prototype.hasOwnProperty.call(this.listeners, eventName)) {
console.error(eventName + " is not a valid event");
}
this.listeners[eventName].push(callback);
}
fireEvent(eventName, eventArgs) {
// if (!this.listeners.hasOwnProperty(eventName)) {
if (!Object.prototype.hasOwnProperty.call(this.listeners, eventName)) {
console.error(eventName + " does not exist");
return;
}
for (var i = 0, max = this.listeners[eventName].length; i < max; i++) {
this.listeners[eventName][i](eventArgs);
}
}
/**
* A function called for progress on an upload
* @callback Context~uploadProgressCallback
* @param {String} name - The file name uploaded
* @param {Number} progress - The progress percentage
*/
/**
* Uploads a file to the pma.core server
* @param {Object} options
* @param {string} options.serverUrl - The server url to use
* @param {string} options.targetPath - The virtual path to upload to
* @param {File} options.file - The file object to upload
* @param {Context~uploadProgressCallback} options.progress - A progress callback
* @returns A promise to get the result of the upload
*/
uploadFile(options) {
let that = this;
return new Promise(function (resolve, reject) {
callApiMethodWithAuthentication.call(
that, {
attemptCount: 0,
serverUrl: options.serverUrl,
method: ApiMethods.Upload,
apiPath: "transfer",
webapi: true,
data: {
"path": options.targetPath,
"files": [{
"isMain": true,
"length": options.file.size,
"path": options.file.name
}]
},
httpMethod: "POST",
contentType: "application/json",
success: function (sessionId, uploadContent) {
let method = "PUT";
if (!uploadContent.Urls) {
uploadContent.Urls = [];
}
if (uploadContent.Urls.length === 0) {
method = "POST";
uploadContent.Urls.push(`${options.serverUrl}transfer/Upload/${uploadContent.Id}?sessionId=${sessionId}&path=${encodeURIComponent(options.file.name)}`);
}
var fd = new FormData();
fd.append("file", options.file);
var xhr = new XMLHttpRequest();
xhr.open(method, uploadContent.Urls[0], true);
xhr.upload.onprogress = function (e) {
let progress = Math.ceil((e.loaded / e.total) * 100);
if (typeof options.progress === "function") {
options.progress(options.file.name, progress);
}
};
xhr.onload = function () {
resolve(true);
};
xhr.onerror = function () {
reject();
}
if (method === "POST") {
xhr.send(fd);
}
else {
xhr.send(options.file);
}
},
failure: function (f) { reject(f); }
});
});
}
/**
* A function called for progress on an upload
* @callback Context~slideUploadProgressCallback
* @param {ImageUpload~Slide} slide - The slide uploaded
* @param {Number} progress - The progress percentage
*/
/**
* A function called for result of an upload
* @callback Context~slideUploadResultCallback
* @param {ImageUpload~Slide} slide - The slide uploaded
*/
/**
* Uploads a file to the pma.core server
* @param {Object} options
* @param {string} options.serverUrl - The server url to use
* @param {string} options.targetPath - The virtual path to upload to
* @param {ImageUpload~Slide} options.slide - The file object to upload
* @param {Context~slideUploadProgressCallback} options.progress - A callback called on progress
* @param {Context~slideUploadResultCallback} options.success - A callback called on success
* @param {Context~slideUploadResultCallback} options.error - A callback called on error
* @returns A promise to get the result of the upload
*/
uploadSlide(options) {
let that = this;
return new Promise(function (resolve) {
var completed = 0;
var totalSize = options.slide.files.map(f => (f && f.size) ? f.size : 0).reduce((accumulator, current) => accumulator + current, 0);
var uploadedSize = [];
callApiMethodWithAuthentication.call(
that, {
attemptCount: 0,
serverUrl: options.serverUrl,
method: ApiMethods.Upload,
apiPath: "transfer",
webapi: true,
data: {
"path": options.targetPath,
"files": options.slide.files.map((x, i) => {
return {
"isMain": i === 0,
"length": x.size,
"path": (i === 0 ? "" : (!options.targetChildPath ? "" : (Array.isArray(options.targetChildPath) ? options.targetChildPath[i] : options.targetChildPath) + "/")) + x.name
}
})
},
httpMethod: "POST",
contentType: "application/json",
success: function (sessionId, uploadContent) {
let method = "PUT";
let isUploadingAzure = false;
if (uploadContent.UploadType === 2) {
isUploadingAzure = true;
}
if (!uploadContent.Urls) {
uploadContent.Urls = [];
}
if (uploadContent.Urls.length === 0) {
method = "POST";
options.slide.files.map((x, i) => uploadContent.Urls.push(`${options.serverUrl}transfer/Upload/${uploadContent.Id}?sessionId=${sessionId}&path=${encodeURIComponent((i === 0 ? "" : (!options.targetChildPath ? "" : (Array.isArray(options.targetChildPath) ? options.targetChildPath[i] : options.targetChildPath) + "/")) + x.name)}`))
}
for (var i = 0; i < options.slide.files.length; i++) {
(function (index) {
var fd = new FormData();
var f = options.slide.files[index];
fd.append("file", f, f.name);
var xhr = new XMLHttpRequest();
xhr.open(method, uploadContent.Urls[index], true);
uploadedSize[index] = 0;
if (isUploadingAzure) {
xhr.setRequestHeader("x-ms-blob-type", "BlockBlob");
}
xhr.upload.onprogress = function (e) {
uploadedSize[index] = e.loaded;
let totalUploadedSize = uploadedSize.reduce((accumulator, current) => accumulator + current, 0);
let progress = Math.floor((totalUploadedSize / totalSize) * 100);
if (progress !== options.slide.uploadProgress && typeof options.progress === "function") {
options.progress(options.slide, progress);
}
};
xhr.onload = function () {
uploadedSize[index] = f.size;
completed++;
if (completed >= options.slide.files.length) {
fetch(`${options.serverUrl}transfer/Upload/${uploadContent.Id}?sessionId=${sessionId}`);
if (typeof options.success === "function") {
options.success(options.slide);
}
resolve(true);
}
};
xhr.onerror = function (evt) {
if (typeof options.error === "function") {
options.error(options.slide, evt.Message ? evt.Message : "");
}
resolve();
}
if (method === "POST") {
xhr.send(fd);
}
else {
xhr.send(f);
}
})(i);
}
},
failure: function (f) {
if (typeof options.error === "function") {
options.error(options.slide, f.Message ? f.Message : "");
}
resolve(f);
}
});
});
}
}
// private methods
// finds the first authentication provider that handles the requested server
// and calls it's authenticate method
function authenticateWithProvider(serverUrl, success, failure) {
// search all authentication providers apart from PromptLogin
var i = 0;
for (i = 0; i < this.authenticationProviders.length; i++) {
if (this.authenticationProviders[i] instanceof PromptLogin) {
continue;
}
if (this.authenticationProviders[i].authenticate(serverUrl, success, failure)) {
return;
}
}
// if none of the previous providers were able to provide a session, search for a PromptLogin and attempt to authenticate with it
for (i = 0; i < this.authenticationProviders.length; i++) {
if (this.authenticationProviders[i] instanceof PromptLogin) {
if (this.authenticationProviders[i].authenticate(serverUrl, success, failure)) {
return;
}
break;
}
}
// no provider found, call failure
if (typeof failure === "function") {
failure({ Message: Resources.translate("Authentication failed at server {serverUrl}.", { serverUrl: serverUrl }) });
}
}
/**
* calls an API method that requires a session ID. This method will first attempt to acquire
* a session ID (possibly a cached one) and then call the requested method.
* If the method call fails because the call was unauthorized (so possible the session ID was not good),
* the method will be called again for a second time, this time forcing an authentication before the
* actual call.
* @param {object} options - The parameters to pass to the ajax request
* @param {number} options.attemptCount - Current attempt count
* @param {string} options.serverUrl - The URL of the server to send the request to
* @param {string} options.method - The API method to call
* @param {object} options.data - The data to send
* @param {string} options.httpMethod - The HTTP method to use
* @param {string} options.contentType - The content type of the request
* @param {function} options.success - Called upon success
* @param {function} options.failure - Called upon failure
* @param {string} [options.apiPath="api"] - The API path to append to the server URL
* @param {boolean} [options.webapi=false] - Whether the api call is a webapi call
* @fires PMA.UI.Components.Events.SessionIdLoginFailed
* @ignore
*/
function callApiMethodWithAuthentication(options) {
var _this = this;
if (!options.apiPath) {
options.apiPath = "api";
}
_this.getSession(options.serverUrl,
function (sessionId) {
// we have a session ID, try to call the actual method
options.data.sessionID = sessionId;
callApiMethod({
serverUrl: options.serverUrl,
method: options.method,
data: options.data,
contentType: options.contentType,
httpMethod: options.httpMethod,
apiPath: options.apiPath,
webapi: options.webapi,
success: function (http) {
// the call succeeded, parse data and call success
if (typeof options.success === "function") {
var response = parseJson(http.responseText);
options.success(options.data.sessionID, response);
}
},
failure: function (http) {
// failed, clean up the session ID that was possibly cached for this server
_sessionList.set(options.serverUrl, null);
// if it's the first attempt, try again, otherwise fail for good
if (http.status == 0 && options.attemptCount === 0) {
options.attemptCount = 1;
callApiMethodWithAuthentication.call(_this, options);
}
else {
if (typeof options.failure === "function") {
if (http.responseText && http.responseText.length !== 0) {
try {
var response = parseJson(http.responseText);
options.failure(response);
_this.fireEvent(Events.SessionIdLoginFailed, { serverUrl: options.serverUrl });
}
catch (ex) {
options.failure(http.responseText);
_this.fireEvent(Events.SessionIdLoginFailed, { serverUrl: options.serverUrl });
}
}
else {
options.failure({ Message: Resources.translate("Authentication failed") });
_this.fireEvent(Events.SessionIdLoginFailed, { serverUrl: options.serverUrl });
}
}
}
}
});
},
function (error) {
if (!error.Message && error.Reason) {
error.Message = error.Reason;
}
// session acquisition failed in the first place, so fail
if (typeof options.failure === "function") {
options.failure(error);
}
_this.fireEvent(Events.SessionIdLoginFailed, { serverUrl: options.serverUrl });
});
}
// legacy alias because of typo
Context.prototype.GetImagesInfo = Context.prototype.getImagesInfo;