forked from EVOgeek/Vmeda.Online
528 lines
16 KiB
TypeScript
528 lines
16 KiB
TypeScript
// (C) Copyright 2015 Moodle Pty Ltd.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
import { Injectable } from '@angular/core';
|
|
|
|
import { CoreLang } from '@services/lang';
|
|
import { CoreTextUtils } from '@services/utils/text';
|
|
import { CoreConstants } from '@/core/constants';
|
|
import { makeSingleton } from '@singletons';
|
|
import { CoreUrl } from '@singletons/url';
|
|
|
|
/*
|
|
* "Utils" service with helper functions for URLs.
|
|
*/
|
|
@Injectable({ providedIn: 'root' })
|
|
export class CoreUrlUtilsProvider {
|
|
|
|
/**
|
|
* Add or remove 'www' from a URL. The url needs to have http or https protocol.
|
|
*
|
|
* @param url URL to modify.
|
|
* @return Modified URL.
|
|
*/
|
|
addOrRemoveWWW(url: string): string {
|
|
if (url) {
|
|
if (url.match(/http(s)?:\/\/www\./)) {
|
|
// Already has www. Remove it.
|
|
url = url.replace('www.', '');
|
|
} else {
|
|
url = url.replace('https://', 'https://www.');
|
|
url = url.replace('http://', 'http://www.');
|
|
}
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Add params to a URL.
|
|
*
|
|
* @param url URL to add the params to.
|
|
* @param params Object with the params to add.
|
|
* @param anchor Anchor text if needed.
|
|
* @param boolToNumber Whether to convert bools to 1 or 0.
|
|
* @return URL with params.
|
|
*/
|
|
addParamsToUrl(url: string, params?: CoreUrlParams, anchor?: string, boolToNumber?: boolean): string {
|
|
let separator = url.indexOf('?') != -1 ? '&' : '?';
|
|
|
|
for (const key in params) {
|
|
let value = params[key];
|
|
|
|
if (boolToNumber && typeof value == 'boolean') {
|
|
// Convert booleans to 1 or 0.
|
|
value = value ? '1' : '0';
|
|
}
|
|
|
|
// Ignore objects.
|
|
if (typeof value != 'object') {
|
|
url += separator + key + '=' + value;
|
|
separator = '&';
|
|
}
|
|
}
|
|
|
|
if (anchor) {
|
|
url += '#' + anchor;
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Given a URL and a text, return an HTML link.
|
|
*
|
|
* @param url URL.
|
|
* @param text Text of the link.
|
|
* @return Link.
|
|
*/
|
|
buildLink(url: string, text: string): string {
|
|
return '<a href="' + url + '">' + text + '</a>';
|
|
}
|
|
|
|
/**
|
|
* Check whether we can use tokenpluginfile.php endpoint for a certain URL.
|
|
*
|
|
* @param url URL to check.
|
|
* @param siteUrl The URL of the site the URL belongs to.
|
|
* @param accessKey User access key for tokenpluginfile.
|
|
* @return Whether tokenpluginfile.php can be used.
|
|
*/
|
|
canUseTokenPluginFile(url: string, siteUrl: string, accessKey?: string): boolean {
|
|
// Do not use tokenpluginfile if site doesn't use slash params, the URL doesn't work.
|
|
// Also, only use it for "core" pluginfile endpoints. Some plugins can implement their own endpoint (like customcert).
|
|
return !!accessKey && !url.match(/[&?]file=/) && (
|
|
url.indexOf(CoreTextUtils.instance.concatenatePaths(siteUrl, 'pluginfile.php')) === 0 ||
|
|
url.indexOf(CoreTextUtils.instance.concatenatePaths(siteUrl, 'webservice/pluginfile.php')) === 0);
|
|
}
|
|
|
|
/**
|
|
* Extracts the parameters from a URL and stores them in an object.
|
|
*
|
|
* @param url URL to treat.
|
|
* @return Object with the params.
|
|
*/
|
|
extractUrlParams(url: string): CoreUrlParams {
|
|
const regex = /[?&]+([^=&]+)=?([^&]*)?/gi;
|
|
const subParamsPlaceholder = '@@@SUBPARAMS@@@';
|
|
const params: CoreUrlParams = {};
|
|
const urlAndHash = url.split('#');
|
|
const questionMarkSplit = urlAndHash[0].split('?');
|
|
let subParams: string;
|
|
|
|
if (questionMarkSplit.length > 2) {
|
|
// There is more than one question mark in the URL. This can happen if any of the params is a URL with params.
|
|
// We only want to treat the first level of params, so we'll remove this second list of params and restore it later.
|
|
questionMarkSplit.splice(0, 2);
|
|
|
|
subParams = '?' + questionMarkSplit.join('?');
|
|
urlAndHash[0] = urlAndHash[0].replace(subParams, subParamsPlaceholder);
|
|
}
|
|
|
|
urlAndHash[0].replace(regex, (match: string, key: string, value: string): string => {
|
|
params[key] = typeof value != 'undefined' ? CoreTextUtils.instance.decodeURIComponent(value) : '';
|
|
|
|
if (subParams) {
|
|
params[key] = params[key].replace(subParamsPlaceholder, subParams);
|
|
}
|
|
|
|
return match;
|
|
});
|
|
|
|
if (urlAndHash.length > 1) {
|
|
// Remove the URL from the array.
|
|
urlAndHash.shift();
|
|
|
|
// Add the hash as a param with a special name. Use a join in case there is more than one #.
|
|
params.urlHash = urlAndHash.join('#');
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
/**
|
|
* Generic function for adding the wstoken to Moodle urls and for pointing to the correct script.
|
|
* For download remote files from Moodle we need to use the special /webservice/pluginfile passing
|
|
* the ws token as a get parameter.
|
|
*
|
|
* @param url The url to be fixed.
|
|
* @param token Token to use.
|
|
* @param siteUrl The URL of the site the URL belongs to.
|
|
* @param accessKey User access key for tokenpluginfile.
|
|
* @return Fixed URL.
|
|
*/
|
|
fixPluginfileURL(url: string, token: string, siteUrl: string, accessKey?: string): string {
|
|
if (!url) {
|
|
return '';
|
|
}
|
|
|
|
url = url.replace(/&/g, '&');
|
|
|
|
const canUseTokenPluginFile = accessKey && this.canUseTokenPluginFile(url, siteUrl, accessKey);
|
|
|
|
// First check if we need to fix this url or is already fixed.
|
|
if (!canUseTokenPluginFile && url.indexOf('token=') != -1) {
|
|
return url;
|
|
}
|
|
|
|
// Check if is a valid URL (contains the pluginfile endpoint) and belongs to the site.
|
|
if (!this.isPluginFileUrl(url) || url.indexOf(CoreTextUtils.instance.addEndingSlash(siteUrl)) !== 0) {
|
|
return url;
|
|
}
|
|
|
|
if (canUseTokenPluginFile) {
|
|
// Use tokenpluginfile.php.
|
|
url = url.replace(/(\/webservice)?\/pluginfile\.php/, '/tokenpluginfile.php/' + accessKey);
|
|
} else {
|
|
// Use pluginfile.php. Some webservices returns directly the correct download url, others not.
|
|
if (url.indexOf(CoreTextUtils.instance.concatenatePaths(siteUrl, 'pluginfile.php')) === 0) {
|
|
url = url.replace('/pluginfile', '/webservice/pluginfile');
|
|
}
|
|
|
|
url = this.addParamsToUrl(url, { token });
|
|
}
|
|
|
|
return this.addParamsToUrl(url, { offline: '1' }); // Always send offline=1 (it's for external repositories).
|
|
}
|
|
|
|
/**
|
|
* Formats a URL, trim, lowercase, etc...
|
|
*
|
|
* @param url The url to be formatted.
|
|
* @return Fromatted url.
|
|
*/
|
|
formatURL(url: string): string {
|
|
url = url.trim();
|
|
|
|
// Check if the URL starts by http or https.
|
|
if (! /^http(s)?:\/\/.*/i.test(url)) {
|
|
// Test first allways https.
|
|
url = 'https://' + url;
|
|
}
|
|
|
|
// http always in lowercase.
|
|
url = url.replace(/^http/i, 'http');
|
|
url = url.replace(/^https/i, 'https');
|
|
|
|
// Replace last slash.
|
|
url = url.replace(/\/$/, '');
|
|
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Returns the URL to the documentation of the app, based on Moodle version and current language.
|
|
*
|
|
* @param release Moodle release.
|
|
* @param page Docs page to go to.
|
|
* @return Promise resolved with the Moodle docs URL.
|
|
*/
|
|
async getDocsUrl(release?: string, page: string = 'Mobile_app'): Promise<string> {
|
|
let docsUrl = 'https://docs.moodle.org/en/' + page;
|
|
|
|
if (typeof release != 'undefined') {
|
|
const version = release.substr(0, 3).replace('.', '');
|
|
// Check is a valid number.
|
|
if (Number(version) >= 24) {
|
|
// Append release number.
|
|
docsUrl = docsUrl.replace('https://docs.moodle.org/', 'https://docs.moodle.org/' + version + '/');
|
|
}
|
|
}
|
|
|
|
try {
|
|
const lang = await CoreLang.instance.getCurrentLanguage();
|
|
|
|
return docsUrl.replace('/en/', '/' + lang + '/');
|
|
} catch (error) {
|
|
return docsUrl;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the Youtube Embed Video URL or null if not found.
|
|
*
|
|
* @param url URL
|
|
* @return Youtube Embed Video URL or null if not found.
|
|
*/
|
|
getYoutubeEmbedUrl(url?: string): string | void {
|
|
if (!url) {
|
|
return;
|
|
}
|
|
|
|
let videoId = '';
|
|
const params: CoreUrlParams = {};
|
|
|
|
url = CoreTextUtils.instance.decodeHTML(url);
|
|
|
|
// Get the video ID.
|
|
let match = url.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/);
|
|
|
|
if (match && match[2].length === 11) {
|
|
videoId = match[2];
|
|
}
|
|
|
|
// No videoId, do not continue.
|
|
if (!videoId) {
|
|
return;
|
|
}
|
|
|
|
// Now get the playlist (if any).
|
|
match = url.match(/[?&]list=([^#&?]+)/);
|
|
|
|
if (match && match[1]) {
|
|
params.list = match[1];
|
|
}
|
|
|
|
// Now get the start time (if any).
|
|
match = url.match(/[?&]start=(\d+)/);
|
|
|
|
if (match && match[1]) {
|
|
params.start = parseInt(match[1], 10).toString();
|
|
} else {
|
|
// No start param, but it could have a time param.
|
|
match = url.match(/[?&]t=(\d+h)?(\d+m)?(\d+s)?/);
|
|
if (match) {
|
|
const start = (match[1] ? parseInt(match[1], 10) * 3600 : 0) +
|
|
(match[2] ? parseInt(match[2], 10) * 60 : 0) +
|
|
(match[3] ? parseInt(match[3], 10) : 0);
|
|
params.start = start.toString();
|
|
}
|
|
}
|
|
|
|
return this.addParamsToUrl('https://www.youtube.com/embed/' + videoId, params);
|
|
}
|
|
|
|
/**
|
|
* Given a URL, returns what's after the last '/' without params.
|
|
* Example:
|
|
* http://mysite.com/a/course.html?id=1 -> course.html
|
|
*
|
|
* @param url URL to treat.
|
|
* @return Last file without params.
|
|
*/
|
|
getLastFileWithoutParams(url: string): string {
|
|
let filename = url.substr(url.lastIndexOf('/') + 1);
|
|
if (filename.indexOf('?') != -1) {
|
|
filename = filename.substr(0, filename.indexOf('?'));
|
|
}
|
|
|
|
return filename;
|
|
}
|
|
|
|
/**
|
|
* Get the protocol from a URL.
|
|
* E.g. http://www.google.com returns 'http'.
|
|
*
|
|
* @param url URL to treat.
|
|
* @return Protocol, undefined if no protocol found.
|
|
*/
|
|
getUrlProtocol(url: string): string | void {
|
|
if (!url) {
|
|
return;
|
|
}
|
|
|
|
const matches = url.match(/^([^/:.?]*):\/\//);
|
|
if (matches && matches[1]) {
|
|
return matches[1];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the scheme from a URL. Please notice that, if a URL has protocol, it will return the protocol.
|
|
* E.g. javascript:doSomething() returns 'javascript'.
|
|
*
|
|
* @param url URL to treat.
|
|
* @return Scheme, undefined if no scheme found.
|
|
*/
|
|
getUrlScheme(url: string): string | void {
|
|
if (!url) {
|
|
return;
|
|
}
|
|
|
|
const matches = url.match(/^([a-z][a-z0-9+\-.]*):/);
|
|
if (matches && matches[1]) {
|
|
return matches[1];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Gets a username from a URL like: user@mysite.com.
|
|
*
|
|
* @param url URL to treat.
|
|
* @return Username. Undefined if no username found.
|
|
*/
|
|
getUsernameFromUrl(url: string): string | void {
|
|
if (url.indexOf('@') > -1) {
|
|
// Get URL without protocol.
|
|
const withoutProtocol = url.replace(/^[^?@/]*:\/\//, '');
|
|
const matches = withoutProtocol.match(/[^@]*/);
|
|
|
|
// Make sure that @ is at the start of the URL, not in a param at the end.
|
|
if (matches && matches.length && !matches[0].match(/[/|?]/)) {
|
|
return matches[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns if a URL has any protocol (not a relative URL).
|
|
*
|
|
* @param url The url to test against the pattern.
|
|
* @return Whether the url is absolute.
|
|
*/
|
|
isAbsoluteURL(url: string): boolean {
|
|
return /^[^:]{2,}:\/\//i.test(url) || /^(tel:|mailto:|geo:)/.test(url);
|
|
}
|
|
|
|
/**
|
|
* Returns if a URL is downloadable: plugin file OR theme/image.php OR gravatar.
|
|
*
|
|
* @param url The URL to test.
|
|
* @return Whether the URL is downloadable.
|
|
*/
|
|
isDownloadableUrl(url: string): boolean {
|
|
return this.isPluginFileUrl(url) || this.isThemeImageUrl(url) || this.isGravatarUrl(url);
|
|
}
|
|
|
|
/**
|
|
* Returns if a URL is a gravatar URL.
|
|
*
|
|
* @param url The URL to test.
|
|
* @return Whether the URL is a gravatar URL.
|
|
*/
|
|
isGravatarUrl(url: string): boolean {
|
|
return url?.indexOf('gravatar.com/avatar') !== -1;
|
|
}
|
|
|
|
/**
|
|
* Check if a URL uses http or https protocol.
|
|
*
|
|
* @param url The url to test.
|
|
* @return Whether the url uses http or https protocol.
|
|
*/
|
|
isHttpURL(url: string): boolean {
|
|
return /^https?:\/\/.+/i.test(url);
|
|
}
|
|
|
|
/**
|
|
* Check whether an URL belongs to a local file.
|
|
*
|
|
* @param url URL to check.
|
|
* @return Whether the URL belongs to a local file.
|
|
*/
|
|
isLocalFileUrl(url: string): boolean {
|
|
const urlParts = CoreUrl.parse(url);
|
|
|
|
return this.isLocalFileUrlScheme(urlParts?.protocol || '');
|
|
}
|
|
|
|
/**
|
|
* Check whether a URL scheme belongs to a local file.
|
|
*
|
|
* @param scheme Scheme to check.
|
|
* @return Whether the scheme belongs to a local file.
|
|
*/
|
|
isLocalFileUrlScheme(scheme: string): boolean {
|
|
if (!scheme) {
|
|
return false;
|
|
}
|
|
scheme = scheme.toLowerCase();
|
|
|
|
return scheme == 'cdvfile' ||
|
|
scheme == 'file' ||
|
|
scheme == 'filesystem' ||
|
|
scheme == CoreConstants.CONFIG.ioswebviewscheme;
|
|
}
|
|
|
|
/**
|
|
* Returns if a URL is a pluginfile URL.
|
|
*
|
|
* @param url The URL to test.
|
|
* @return Whether the URL is a pluginfile URL.
|
|
*/
|
|
isPluginFileUrl(url: string): boolean {
|
|
return url?.indexOf('/pluginfile.php') !== -1;
|
|
}
|
|
|
|
/**
|
|
* Returns if a URL is a theme image URL.
|
|
*
|
|
* @param url The URL to test.
|
|
* @return Whether the URL is a theme image URL.
|
|
*/
|
|
isThemeImageUrl(url: string): boolean {
|
|
return url?.indexOf('/theme/image.php') !== -1;
|
|
}
|
|
|
|
/**
|
|
* Remove protocol and www from a URL.
|
|
*
|
|
* @param url URL to treat.
|
|
* @return Treated URL.
|
|
*/
|
|
removeProtocolAndWWW(url: string): string {
|
|
// Remove protocol.
|
|
url = url.replace(/.*?:\/\//g, '');
|
|
// Remove www.
|
|
url = url.replace(/^www./, '');
|
|
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Remove the parameters from a URL, returning the URL without them.
|
|
*
|
|
* @param url URL to treat.
|
|
* @return URL without params.
|
|
*/
|
|
removeUrlParams(url: string): string {
|
|
const matches = url.match(/^[^?]+/);
|
|
|
|
return matches ? matches[0] : '';
|
|
}
|
|
|
|
/**
|
|
* Modifies a pluginfile URL to use the default pluginfile script instead of the webservice one.
|
|
*
|
|
* @param url The url to be fixed.
|
|
* @param siteUrl The URL of the site the URL belongs to.
|
|
* @return Modified URL.
|
|
*/
|
|
unfixPluginfileURL(url: string, siteUrl?: string): string {
|
|
if (!url) {
|
|
return '';
|
|
}
|
|
|
|
url = url.replace(/&/g, '&');
|
|
|
|
// It site URL is supplied, check if the URL belongs to the site.
|
|
if (siteUrl && url.indexOf(CoreTextUtils.instance.addEndingSlash(siteUrl)) !== 0) {
|
|
return url;
|
|
}
|
|
|
|
// Not a pluginfile URL. Treat webservice/pluginfile case.
|
|
url = url.replace(/\/webservice\/pluginfile\.php\//, '/pluginfile.php/');
|
|
|
|
// Make sure the URL doesn't contain the token.
|
|
url.replace(/([?&])token=[^&]*&?/, '$1');
|
|
|
|
return url;
|
|
}
|
|
|
|
}
|
|
|
|
export class CoreUrlUtils extends makeSingleton(CoreUrlUtilsProvider) {}
|
|
|
|
export type CoreUrlParams = {[key: string]: string};
|