Move url related utility methods to @converse/headless

This commit is contained in:
JC Brand 2021-07-05 17:15:48 +02:00
parent bff714f24c
commit b90a435833
10 changed files with 374 additions and 950 deletions

1063
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -115,7 +115,6 @@
"favico.js-slevomat": "^0.3.11",
"jed": "1.1.1",
"lit": "^2.0.0-rc.2",
"urijs": "^1.19.6",
"xss": "^1.0.8"
}
}

View File

@ -3,6 +3,7 @@
* @license Mozilla Public License (MPLv2)
*/
import Storage from '@converse/skeletor/src/storage.js';
import URI from 'urijs';
import _converse from '@converse/headless/shared/_converse';
import advancedFormat from 'dayjs/plugin/advancedFormat';
import dayjs from 'dayjs';
@ -1416,6 +1417,7 @@ Object.assign(converse, {
Model,
Promise,
Strophe,
URI,
dayjs,
html,
log,

View File

@ -48,6 +48,7 @@
"pluggable.js": "3.0.1",
"sizzle": "^2.3.5",
"sprintf-js": "^1.1.2",
"strophe.js": "1.4.2"
"strophe.js": "1.4.2",
"urijs": "^1.19.6"
}
}

103
src/headless/utils/url.js Normal file
View File

@ -0,0 +1,103 @@
import URI from 'urijs';
import log from '@converse/headless/log';
import { api } from '@converse/headless/core';
function checkTLS (uri) {
return (
window.location.protocol === 'http:' ||
(window.location.protocol === 'https:' && uri.protocol().toLowerCase() === 'https')
);
}
export function getURI (url) {
try {
return url instanceof URI ? url : new URI(url);
} catch (error) {
log.debug(error);
return null;
}
}
function checkFileTypes (types, url) {
const uri = getURI(url);
if (uri === null || !checkTLS(uri)) {
return false;
}
const filename = uri.filename().toLowerCase();
return !!types.filter(ext => filename.endsWith(ext)).length;
}
function isDomainAllowed (whitelist, url) {
const uri = getURI(url);
const subdomain = uri.subdomain();
const domain = uri.domain();
const fulldomain = `${subdomain ? `${subdomain}.` : ''}${domain}`;
return whitelist.includes(domain) || whitelist.includes(fulldomain);
}
export function filterQueryParamsFromURL (url) {
const paramsArray = api.settings.get('filter_url_query_params');
if (!paramsArray) return url;
const parsed_uri = getURI(url);
return parsed_uri.removeQuery(paramsArray).toString();
}
export function isAudioDomainAllowed (url) {
const embed_audio = api.settings.get('embed_audio');
if (!Array.isArray(embed_audio)) {
return embed_audio;
}
try {
return isDomainAllowed(embed_audio, url);
} catch (error) {
log.debug(error);
return false;
}
}
export function isVideoDomainAllowed (url) {
const embed_videos = api.settings.get('embed_videos');
if (!Array.isArray(embed_videos)) {
return embed_videos;
}
try {
return isDomainAllowed(embed_videos, url);
} catch (error) {
log.debug(error);
return false;
}
}
export function isImageDomainAllowed (url) {
const show_images_inline = api.settings.get('show_images_inline');
if (!Array.isArray(show_images_inline)) {
return show_images_inline;
}
try {
return isDomainAllowed(show_images_inline, url);
} catch (error) {
log.debug(error);
return false;
}
}
export function isURLWithImageExtension (url) {
return checkFileTypes(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg'], url);
}
export function isAudioURL (url) {
return checkFileTypes(['.ogg', '.mp3', '.m4a'], url);
}
export function isVideoURL (url) {
return checkFileTypes(['.mp4', '.webm'], url);
}
export function isImageURL (url) {
const regex = api.settings.get('image_urls_regex');
return regex?.test(url) || isURLWithImageExtension(url);
}
export function isEncryptedFileURL (url) {
return url.startsWith('aesgcm://');
}

View File

@ -1,5 +1,4 @@
/* global libsignal */
import URI from 'urijs';
import difference from 'lodash-es/difference';
import log from '@converse/headless/log';
import tpl_audio from 'templates/audio.js';
@ -12,7 +11,7 @@ import { __ } from 'i18n';
import { _converse, converse, api } from '@converse/headless/core';
import { html } from 'lit';
import { initStorage } from '@converse/headless/shared/utils.js';
import { isAudioURL, isImageURL, isVideoURL, getURI } from 'utils/html.js';
import { isAudioURL, isImageURL, isVideoURL, getURI } from '@converse/headless/utils/url.js';
import concat from 'lodash-es/concat';
import { until } from 'lit/directives/until.js';
import {
@ -25,7 +24,7 @@ import {
stringToArrayBuffer
} from '@converse/headless/utils/arraybuffer.js';
const { $msg, Strophe, sizzle, u } = converse.env;
const { $msg, Strophe, URI, sizzle, u } = converse.env;
async function encryptMessage (plaintext) {

View File

@ -1,9 +1,6 @@
import { converse } from "@converse/headless/core";
import { getURI } from 'utils/html.js';
import { html } from 'lit';
import { isImageDomainAllowed, } from 'utils/html';
import { getURI, isImageDomainAllowed } from '@converse/headless/utils/url.js';
const u = converse.env.utils;
function isValidURL (url) {
// We don't consider relative URLs as valid
@ -30,7 +27,7 @@ export default (o) => {
<div class="card-body">
${ o.title ? tpl_url_wrapper(o, o => html`<h5 class="card-title">${o.title}</h5>`) : ''}
${ o.description ? html`<p class="card-text"><converse-rich-text text=${o.description}></converse-rich-text></p>` : '' }
${ o.url ? html`<p class="card-text"><a href="${o.url}" target="_blank" rel="noopener">${u.getURI(o.url).domain()}</a></p>` : '' }
${ o.url ? html`<p class="card-text"><a href="${o.url}" target="_blank" rel="noopener">${getURI(o.url).domain()}</a></p>` : '' }
</div>` : '' }
</div>`;
} else {

View File

@ -1,8 +1,11 @@
import URI from 'urijs';
import { AsyncDirective } from 'lit/async-directive.js';
import { converse } from '@converse/headless/core';
import { directive } from 'lit/directive.js';
import { getHyperlinkTemplate, isURLWithImageExtension } from 'utils/html.js';
import { getHyperlinkTemplate } from 'utils/html.js';
import { html } from 'lit';
import { isURLWithImageExtension } from '@converse/headless/utils/url.js';
const { URI } = converse.env;
class ImageDirective extends AsyncDirective {
render (src, href, onLoad, onClick) {

View File

@ -1,10 +1,10 @@
import URI from 'urijs';
import log from '@converse/headless/log';
import tpl_audio from 'templates/audio.js';
import tpl_image from 'templates/image.js';
import tpl_video from 'templates/video.js';
import { _converse, api } from '@converse/headless/core';
import { _converse, api, converse } from '@converse/headless/core';
import { containsDirectives, getDirectiveAndLength, getDirectiveTemplate, isQuoteDirective } from './styling.js';
import { getHyperlinkTemplate } from 'utils/html.js';
import {
convertASCII2Emoji,
getCodePointReferences,
@ -13,7 +13,6 @@ import {
} from '@converse/headless/plugins/emoji/index.js';
import {
filterQueryParamsFromURL,
getHyperlinkTemplate,
isAudioDomainAllowed,
isAudioURL,
isEncryptedFileURL,
@ -21,9 +20,12 @@ import {
isImageURL,
isVideoDomainAllowed,
isVideoURL
} from 'utils/html.js';
} from '@converse/headless/utils/url.js';
import { html } from 'lit';
const { URI } = converse.env;
const isString = s => typeof s === 'string';
// We don't render more than two line-breaks, replace extra line-breaks with
@ -92,8 +94,8 @@ export class RichText extends String {
*/
addHyperlinks (text, offset) {
const objs = [];
const parse_options = { 'start': /\b(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi };
try {
const parse_options = { 'start': /\b(?:([a-z][a-z0-9.+-]*:\/\/)|xmpp:|mailto:|www\.)/gi };
URI.withinString(
text,
(url, start, end) => {

View File

@ -3,7 +3,6 @@
* @license Mozilla Public License (MPLv2)
* @description This is the DOM/HTML utilities module.
*/
import URI from 'urijs';
import isFunction from 'lodash-es/isFunction';
import log from '@converse/headless/log';
import tpl_audio from 'templates/audio.js';
@ -19,7 +18,8 @@ import tpl_form_username from '../templates/form_username.js';
import tpl_hyperlink from 'templates/hyperlink.js';
import tpl_video from 'templates/video.js';
import u from '../headless/utils/core';
import { api, converse } from '@converse/headless/core';
import { converse } from '@converse/headless/core';
import { getURI, isAudioURL, isImageURL, isVideoURL } from '@converse/headless/utils/url.js';
import { render } from 'lit';
const { sizzle } = converse.env;
@ -52,99 +52,6 @@ function slideOutWrapup (el) {
el.style.height = '';
}
export function getURI (url) {
try {
return url instanceof URI ? url : new URI(url);
} catch (error) {
log.debug(error);
return null;
}
}
function checkTLS (uri) {
return (
window.location.protocol === 'http:' ||
(window.location.protocol === 'https:' && uri.protocol().toLowerCase() === 'https')
);
}
function checkFileTypes (types, url) {
const uri = getURI(url);
if (uri === null || !checkTLS(uri)) {
return false;
}
const filename = uri.filename().toLowerCase();
return !!types.filter(ext => filename.endsWith(ext)).length;
}
export function isURLWithImageExtension (url) {
return checkFileTypes(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg'], url);
}
export function isAudioURL (url) {
return checkFileTypes(['.ogg', '.mp3', '.m4a'], url);
}
export function isVideoURL (url) {
return checkFileTypes(['.mp4', '.webm'], url);
}
export function isEncryptedFileURL (url) {
return url.startsWith('aesgcm://');
}
export function isImageURL (url) {
const regex = api.settings.get('image_urls_regex');
return regex?.test(url) || isURLWithImageExtension(url);
}
function isDomainAllowed (whitelist, url) {
const uri = getURI(url);
const subdomain = uri.subdomain();
const domain = uri.domain();
const fulldomain = `${subdomain ? `${subdomain}.` : ''}${domain}`;
return whitelist.includes(domain) || whitelist.includes(fulldomain);
}
export function isAudioDomainAllowed (url) {
const embed_audio = api.settings.get('embed_audio');
if (!Array.isArray(embed_audio)) {
return embed_audio;
}
try {
return isDomainAllowed(embed_audio, url);
} catch (error) {
log.debug(error);
return false;
}
}
export function isVideoDomainAllowed (url) {
const embed_videos = api.settings.get('embed_videos');
if (!Array.isArray(embed_videos)) {
return embed_videos;
}
try {
return isDomainAllowed(embed_videos, url);
} catch (error) {
log.debug(error);
return false;
}
}
export function isImageDomainAllowed (url) {
const show_images_inline = api.settings.get('show_images_inline');
if (!Array.isArray(show_images_inline)) {
return show_images_inline;
}
try {
return isDomainAllowed(show_images_inline, url);
} catch (error) {
log.debug(error);
return false;
}
}
function getFileName (uri) {
try {
return decodeURI(uri.filename());
@ -166,11 +73,11 @@ export function getOOBURLMarkup (url) {
if (uri === null) {
return url;
}
if (u.isVideoURL(uri)) {
if (isVideoURL(uri)) {
return tpl_video(url);
} else if (u.isAudioURL(uri)) {
} else if (isAudioURL(uri)) {
return tpl_audio(url);
} else if (u.isImageURL(uri)) {
} else if (isImageURL(uri)) {
return tpl_file(uri.toString(), getFileName(uri));
} else {
return tpl_file(uri.toString(), getFileName(uri));
@ -340,13 +247,6 @@ export function getHyperlinkTemplate (url) {
return url;
}
export function filterQueryParamsFromURL (url) {
const paramsArray = api.settings.get('filter_url_query_params');
if (!paramsArray) return url;
const parsed_uri = getURI(url);
return parsed_uri.removeQuery(paramsArray).toString();
}
u.slideInAllElements = function (elements, duration = 300) {
return Promise.all(Array.from(elements).map(e => u.slideIn(e, duration)));
};
@ -591,15 +491,6 @@ u.xForm2TemplateResult = function (field, stanza, options) {
}
};
Object.assign(u, {
filterQueryParamsFromURL,
getURI,
isAudioURL,
isImageURL,
isImageDomainAllowed,
isURLWithImageExtension,
isVideoURL,
getOOBURLMarkup,
});
Object.assign(u, { getOOBURLMarkup });
export default u;