Merge pull request #3500 from matrix-org/travis/permalinks
Support local permalinks for unfederated instancespull/21833/head
commit
7d1a04cb12
|
@ -2,6 +2,7 @@
|
|||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017, 2018 New Vector Ltd
|
||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -33,6 +34,7 @@ import url from 'url';
|
|||
|
||||
import EMOJIBASE from 'emojibase-data/en/compact.json';
|
||||
import EMOJIBASE_REGEX from 'emojibase-regex';
|
||||
import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks";
|
||||
|
||||
linkifyMatrix(linkify);
|
||||
|
||||
|
@ -158,30 +160,10 @@ const transformTags = { // custom to matrix
|
|||
if (attribs.href) {
|
||||
attribs.target = '_blank'; // by default
|
||||
|
||||
let m;
|
||||
// FIXME: horrible duplication with linkify-matrix
|
||||
m = attribs.href.match(linkifyMatrix.VECTOR_URL_PATTERN);
|
||||
if (m) {
|
||||
attribs.href = m[1];
|
||||
const transformed = tryTransformPermalinkToLocalHref(attribs.href);
|
||||
if (transformed !== attribs.href || attribs.href.match(linkifyMatrix.VECTOR_URL_PATTERN)) {
|
||||
attribs.href = transformed;
|
||||
delete attribs.target;
|
||||
} else {
|
||||
m = attribs.href.match(linkifyMatrix.MATRIXTO_URL_PATTERN);
|
||||
if (m) {
|
||||
const entity = m[1];
|
||||
switch (entity[0]) {
|
||||
case '@':
|
||||
attribs.href = '#/user/' + entity;
|
||||
break;
|
||||
case '+':
|
||||
attribs.href = '#/group/' + entity;
|
||||
break;
|
||||
case '#':
|
||||
case '!':
|
||||
attribs.href = '#/room/' + entity;
|
||||
break;
|
||||
}
|
||||
delete attribs.target;
|
||||
}
|
||||
}
|
||||
}
|
||||
attribs.rel = 'noopener'; // https://mathiasbynens.github.io/rel-noopener/
|
||||
|
@ -465,10 +447,12 @@ export function bodyToHtml(content, highlights, opts={}) {
|
|||
const match = BIGEMOJI_REGEX.exec(contentBodyTrimmed);
|
||||
emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length &&
|
||||
// Prevent user pills expanding for users with only emoji in
|
||||
// their username
|
||||
// their username. Permalinks (links in pills) can be any URL
|
||||
// now, so we just check for an HTTP-looking thing.
|
||||
(
|
||||
content.formatted_body == undefined ||
|
||||
!content.formatted_body.includes("https://matrix.to/")
|
||||
(!content.formatted_body.includes("http:") &&
|
||||
!content.formatted_body.includes("https:"))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,6 @@ import dis from './dispatcher';
|
|||
import sdk from './index';
|
||||
import {_t, _td} from './languageHandler';
|
||||
import Modal from './Modal';
|
||||
import {MATRIXTO_URL_PATTERN} from "./linkify-matrix";
|
||||
import * as querystring from "querystring";
|
||||
import MultiInviter from './utils/MultiInviter';
|
||||
import { linkifyAndSanitizeHtml } from './HtmlUtils';
|
||||
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
|
||||
|
@ -34,6 +32,7 @@ import Promise from "bluebird";
|
|||
import { getAddressType } from './UserAddress';
|
||||
import { abbreviateUrl } from './utils/UrlUtils';
|
||||
import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/IdentityServerUtils';
|
||||
import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks";
|
||||
|
||||
const singleMxcUpload = async () => {
|
||||
return new Promise((resolve) => {
|
||||
|
@ -441,7 +440,19 @@ export const CommandMap = {
|
|||
const params = args.split(' ');
|
||||
if (params.length < 1) return reject(this.getUsage());
|
||||
|
||||
const matrixToMatches = params[0].match(MATRIXTO_URL_PATTERN);
|
||||
let isPermalink = false;
|
||||
if (params[0].startsWith("http:") || params[0].startsWith("https:")) {
|
||||
// It's at least a URL - try and pull out a hostname to check against the
|
||||
// permalink handler
|
||||
const parsedUrl = new URL(params[0]);
|
||||
const hostname = parsedUrl.host || parsedUrl.hostname; // takes first non-falsey value
|
||||
|
||||
// if we're using a Riot permalink handler, this will catch it before we get much further.
|
||||
// see below where we make assumptions about parsing the URL.
|
||||
if (isPermalinkHost(hostname)) {
|
||||
isPermalink = true;
|
||||
}
|
||||
}
|
||||
if (params[0][0] === '#') {
|
||||
let roomAlias = params[0];
|
||||
if (!roomAlias.includes(':')) {
|
||||
|
@ -469,29 +480,25 @@ export const CommandMap = {
|
|||
auto_join: true,
|
||||
});
|
||||
return success();
|
||||
} else if (matrixToMatches) {
|
||||
let entity = matrixToMatches[1];
|
||||
let eventId = null;
|
||||
let viaServers = [];
|
||||
} else if (isPermalink) {
|
||||
const permalinkParts = parsePermalink(params[0]);
|
||||
|
||||
if (entity[0] !== '!' && entity[0] !== '#') return reject(this.getUsage());
|
||||
|
||||
if (entity.indexOf('?') !== -1) {
|
||||
const parts = entity.split('?');
|
||||
entity = parts[0];
|
||||
|
||||
const parsed = querystring.parse(parts[1]);
|
||||
viaServers = parsed["via"];
|
||||
if (typeof viaServers === 'string') viaServers = [viaServers];
|
||||
// This check technically isn't needed because we already did our
|
||||
// safety checks up above. However, for good measure, let's be sure.
|
||||
if (!permalinkParts) {
|
||||
return reject(this.getUsage());
|
||||
}
|
||||
|
||||
// We quietly support event ID permalinks too
|
||||
if (entity.indexOf('/$') !== -1) {
|
||||
const parts = entity.split("/$");
|
||||
entity = parts[0];
|
||||
eventId = `$${parts[1]}`;
|
||||
// If for some reason someone wanted to join a group or user, we should
|
||||
// stop them now.
|
||||
if (!permalinkParts.roomIdOrAlias) {
|
||||
return reject(this.getUsage());
|
||||
}
|
||||
|
||||
const entity = permalinkParts.roomIdOrAlias;
|
||||
const viaServers = permalinkParts.viaServers;
|
||||
const eventId = permalinkParts.eventId;
|
||||
|
||||
const dispatch = {
|
||||
action: 'view_room',
|
||||
auto_join: true,
|
||||
|
|
|
@ -23,7 +23,7 @@ import QueryMatcher from './QueryMatcher';
|
|||
import {PillCompletion} from './Components';
|
||||
import sdk from '../index';
|
||||
import _sortBy from 'lodash/sortBy';
|
||||
import {makeGroupPermalink} from "../matrix-to";
|
||||
import {makeGroupPermalink} from "../utils/permalinks/Permalinks";
|
||||
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||
import FlairStore from "../stores/FlairStore";
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ import {PillCompletion} from './Components';
|
|||
import {getDisplayAliasForRoom} from '../Rooms';
|
||||
import sdk from '../index';
|
||||
import _sortBy from 'lodash/sortBy';
|
||||
import {makeRoomPermalink} from "../matrix-to";
|
||||
import {makeRoomPermalink} from "../utils/permalinks/Permalinks";
|
||||
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||
|
||||
const ROOM_REGEX = /\B#\S*/g;
|
||||
|
|
|
@ -28,7 +28,7 @@ import _sortBy from 'lodash/sortBy';
|
|||
import MatrixClientPeg from '../MatrixClientPeg';
|
||||
|
||||
import type {MatrixEvent, Room, RoomMember, RoomState} from 'matrix-js-sdk';
|
||||
import {makeUserPermalink} from "../matrix-to";
|
||||
import {makeUserPermalink} from "../utils/permalinks/Permalinks";
|
||||
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||
|
||||
const USER_REGEX = /\B@\S*/g;
|
||||
|
|
|
@ -36,7 +36,7 @@ import classnames from 'classnames';
|
|||
import GroupStore from '../../stores/GroupStore';
|
||||
import FlairStore from '../../stores/FlairStore';
|
||||
import { showGroupAddRoomDialog } from '../../GroupAddressPicker';
|
||||
import {makeGroupPermalink, makeUserPermalink} from "../../matrix-to";
|
||||
import {makeGroupPermalink, makeUserPermalink} from "../../utils/permalinks/Permalinks";
|
||||
import {Group} from "matrix-js-sdk";
|
||||
|
||||
const LONG_DESC_PLACEHOLDER = _td(
|
||||
|
|
|
@ -31,7 +31,7 @@ import Promise from 'bluebird';
|
|||
import classNames from 'classnames';
|
||||
import {Room} from "matrix-js-sdk";
|
||||
import { _t } from '../../languageHandler';
|
||||
import {RoomPermalinkCreator} from '../../matrix-to';
|
||||
import {RoomPermalinkCreator} from '../../utils/permalinks/Permalinks';
|
||||
|
||||
import MatrixClientPeg from '../../MatrixClientPeg';
|
||||
import ContentMessages from '../../ContentMessages';
|
||||
|
|
|
@ -20,7 +20,7 @@ import {Room, User, Group, RoomMember, MatrixEvent} from 'matrix-js-sdk';
|
|||
import sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import QRCode from 'qrcode-react';
|
||||
import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../matrix-to";
|
||||
import {RoomPermalinkCreator, makeGroupPermalink, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
||||
import * as ContextualMenu from "../../structures/ContextualMenu";
|
||||
|
||||
const socials = [
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -22,23 +23,21 @@ import classNames from 'classnames';
|
|||
import { Room, RoomMember, MatrixClient } from 'matrix-js-sdk';
|
||||
import PropTypes from 'prop-types';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
import { MATRIXTO_URL_PATTERN } from '../../../linkify-matrix';
|
||||
import { getDisplayAliasForRoom } from '../../../Rooms';
|
||||
import FlairStore from "../../../stores/FlairStore";
|
||||
|
||||
const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
|
||||
import {getPrimaryPermalinkEntity} from "../../../utils/permalinks/Permalinks";
|
||||
|
||||
// For URLs of matrix.to links in the timeline which have been reformatted by
|
||||
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
|
||||
const REGEX_LOCAL_MATRIXTO = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/;
|
||||
const REGEX_LOCAL_PERMALINK = /^#\/(?:user|room|group)\/(([#!@+])[^/]*)$/;
|
||||
|
||||
const Pill = createReactClass({
|
||||
statics: {
|
||||
isPillUrl: (url) => {
|
||||
return !!REGEX_MATRIXTO.exec(url);
|
||||
return !!getPrimaryPermalinkEntity(url);
|
||||
},
|
||||
isMessagePillUrl: (url) => {
|
||||
return !!REGEX_LOCAL_MATRIXTO.exec(url);
|
||||
return !!REGEX_LOCAL_PERMALINK.exec(url);
|
||||
},
|
||||
roomNotifPos: (text) => {
|
||||
return text.indexOf("@room");
|
||||
|
@ -95,22 +94,21 @@ const Pill = createReactClass({
|
|||
},
|
||||
|
||||
async componentWillReceiveProps(nextProps) {
|
||||
let regex = REGEX_MATRIXTO;
|
||||
if (nextProps.inMessage) {
|
||||
regex = REGEX_LOCAL_MATRIXTO;
|
||||
}
|
||||
|
||||
let matrixToMatch;
|
||||
let resourceId;
|
||||
let prefix;
|
||||
|
||||
if (nextProps.url) {
|
||||
// Default to the empty array if no match for simplicity
|
||||
// resource and prefix will be undefined instead of throwing
|
||||
matrixToMatch = regex.exec(nextProps.url) || [];
|
||||
if (nextProps.inMessage) {
|
||||
// Default to the empty array if no match for simplicity
|
||||
// resource and prefix will be undefined instead of throwing
|
||||
const matrixToMatch = REGEX_LOCAL_PERMALINK.exec(nextProps.url) || [];
|
||||
|
||||
resourceId = matrixToMatch[1]; // The room/user ID
|
||||
prefix = matrixToMatch[2]; // The first character of prefix
|
||||
resourceId = matrixToMatch[1]; // The room/user ID
|
||||
prefix = matrixToMatch[2]; // The first character of prefix
|
||||
} else {
|
||||
resourceId = getPrimaryPermalinkEntity(nextProps.url);
|
||||
prefix = resourceId ? resourceId[0] : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const pillType = this.props.type || {
|
||||
|
|
|
@ -21,7 +21,7 @@ import PropTypes from 'prop-types';
|
|||
import dis from '../../../dispatcher';
|
||||
import {wantsDateSeparator} from '../../../DateUtils';
|
||||
import {MatrixEvent, MatrixClient} from 'matrix-js-sdk';
|
||||
import {makeUserPermalink, RoomPermalinkCreator} from "../../../matrix-to";
|
||||
import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
// This component does no cycle detection, simply because the only way to make such a cycle would be to
|
||||
|
|
|
@ -19,7 +19,7 @@ import PropTypes from 'prop-types';
|
|||
import createReactClass from 'create-react-class';
|
||||
|
||||
import dis from '../../../dispatcher';
|
||||
import { RoomPermalinkCreator } from '../../../matrix-to';
|
||||
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import MatrixClientPeg from '../../../MatrixClientPeg';
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@ import { _t } from '../../../languageHandler';
|
|||
import * as ContextualMenu from '../../structures/ContextualMenu';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import ReplyThread from "../elements/ReplyThread";
|
||||
import {host as matrixtoHost} from '../../../matrix-to';
|
||||
import {pillifyLinks} from '../../../utils/pillify';
|
||||
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
|
||||
import {isPermalinkHost} from "../../../utils/permalinks/Permalinks";
|
||||
|
||||
module.exports = createReactClass({
|
||||
displayName: 'TextualBody',
|
||||
|
@ -248,10 +248,10 @@ module.exports = createReactClass({
|
|||
const url = node.getAttribute("href");
|
||||
const host = url.match(/^https?:\/\/(.*?)(\/|$)/)[1];
|
||||
|
||||
// never preview matrix.to links (if anything we should give a smart
|
||||
// never preview permalinks (if anything we should give a smart
|
||||
// preview of the room/user they point to: nobody needs to be reminded
|
||||
// what the matrix.to site looks like).
|
||||
if (host === matrixtoHost) return false;
|
||||
if (isPermalinkHost(host)) return false;
|
||||
|
||||
if (node.textContent.toLowerCase().trim().startsWith(host.toLowerCase())) {
|
||||
// it's a "foo.pl" style link
|
||||
|
|
|
@ -23,7 +23,7 @@ import sdk from '../../../index';
|
|||
import dis from '../../../dispatcher';
|
||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
import Stickerpicker from './Stickerpicker';
|
||||
import { makeRoomPermalink } from '../../../matrix-to';
|
||||
import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
|
||||
import ContentMessages from '../../../ContentMessages';
|
||||
import classNames from 'classnames';
|
||||
import E2EIcon from './E2EIcon';
|
||||
|
|
|
@ -48,13 +48,11 @@ import Markdown from '../../../Markdown';
|
|||
import MessageComposerStore from '../../../stores/MessageComposerStore';
|
||||
import ContentMessages from '../../../ContentMessages';
|
||||
|
||||
import {MATRIXTO_URL_PATTERN} from '../../../linkify-matrix';
|
||||
|
||||
import EMOJIBASE from 'emojibase-data/en/compact.json';
|
||||
import EMOTICON_REGEX from 'emojibase-regex/emoticon';
|
||||
|
||||
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
|
||||
import {makeUserPermalink} from "../../../matrix-to";
|
||||
import {getPrimaryPermalinkEntity, makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
||||
import ReplyPreview from "./ReplyPreview";
|
||||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
import ReplyThread from "../elements/ReplyThread";
|
||||
|
@ -224,18 +222,15 @@ export default class MessageComposerInput extends React.Component {
|
|||
// special case links
|
||||
if (tag === 'a') {
|
||||
const href = el.getAttribute('href');
|
||||
let m;
|
||||
if (href) {
|
||||
m = href.match(MATRIXTO_URL_PATTERN);
|
||||
}
|
||||
if (m) {
|
||||
const permalinkEntity = getPrimaryPermalinkEntity(href);
|
||||
if (permalinkEntity) {
|
||||
return {
|
||||
object: 'inline',
|
||||
type: 'pill',
|
||||
data: {
|
||||
href,
|
||||
completion: el.innerText,
|
||||
completionId: m[1],
|
||||
completionId: permalinkEntity,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
|
@ -541,7 +536,7 @@ export default class MessageComposerInput extends React.Component {
|
|||
|
||||
const textWithMdPills = this.plainWithMdPills.serialize(editorState);
|
||||
const markdown = new Markdown(textWithMdPills);
|
||||
// HTML deserialize has custom rules to turn matrix.to links into pill objects.
|
||||
// HTML deserialize has custom rules to turn permalinks into pill objects.
|
||||
return this.html.deserialize(markdown.toHTML());
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import { _t } from '../../../languageHandler';
|
|||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import PropTypes from "prop-types";
|
||||
import {RoomPermalinkCreator} from "../../../matrix-to";
|
||||
import {RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks";
|
||||
|
||||
function cancelQuoting() {
|
||||
dis.dispatch({
|
||||
|
|
|
@ -25,7 +25,7 @@ import dis from '../../../dispatcher';
|
|||
import RoomViewStore from '../../../stores/RoomViewStore';
|
||||
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
|
||||
import Stickerpicker from './Stickerpicker';
|
||||
import { makeRoomPermalink } from '../../../matrix-to';
|
||||
import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
|
||||
import ContentMessages from '../../../ContentMessages';
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
|
|
@ -15,11 +15,9 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { MATRIXTO_URL_PATTERN } from '../linkify-matrix';
|
||||
import { walkDOMDepthFirst } from "./dom";
|
||||
import { checkBlockNode } from "../HtmlUtils";
|
||||
|
||||
const REGEX_MATRIXTO = new RegExp(MATRIXTO_URL_PATTERN);
|
||||
import {getPrimaryPermalinkEntity} from "../utils/permalinks/Permalinks";
|
||||
|
||||
function parseAtRoomMentions(text, partCreator) {
|
||||
const ATROOM = "@room";
|
||||
|
@ -41,9 +39,8 @@ function parseAtRoomMentions(text, partCreator) {
|
|||
|
||||
function parseLink(a, partCreator) {
|
||||
const {href} = a;
|
||||
const pillMatch = REGEX_MATRIXTO.exec(href) || [];
|
||||
const resourceId = pillMatch[1]; // The room/user ID
|
||||
const prefix = pillMatch[2]; // The first character of prefix
|
||||
const resourceId = getPrimaryPermalinkEntity(href); // The room/user ID
|
||||
const prefix = resourceId ? resourceId[0] : undefined; // First character of ID
|
||||
switch (prefix) {
|
||||
case "@":
|
||||
return partCreator.userPill(a.textContent, resourceId);
|
||||
|
|
|
@ -16,6 +16,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import Markdown from '../Markdown';
|
||||
import {makeGenericPermalink} from "../utils/permalinks/Permalinks";
|
||||
|
||||
export function mdSerialize(model) {
|
||||
return model.parts.reduce((html, part) => {
|
||||
|
@ -29,7 +30,7 @@ export function mdSerialize(model) {
|
|||
return html + part.text;
|
||||
case "room-pill":
|
||||
case "user-pill":
|
||||
return html + `[${part.text}](https://matrix.to/#/${part.resourceId})`;
|
||||
return html + `[${part.text}](${makeGenericPermalink(part.resourceId)})`;
|
||||
}
|
||||
}, "");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -14,7 +15,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {baseUrl} from "./matrix-to";
|
||||
import {baseUrl} from "./utils/permalinks/SpecPermalinkConstructor";
|
||||
import {tryTransformPermalinkToLocalHref} from "./utils/permalinks/Permalinks";
|
||||
|
||||
function matrixLinkify(linkify) {
|
||||
// Text tokens
|
||||
|
@ -189,13 +191,6 @@ matrixLinkify.MATRIXTO_MD_LINK_PATTERN =
|
|||
'\\[([^\\]]*)\\]\\((?:https?://)?(?:www\\.)?matrix\\.to/#/([#@!+][^\\)]*)\\)';
|
||||
matrixLinkify.MATRIXTO_BASE_URL= baseUrl;
|
||||
|
||||
const matrixToEntityMap = {
|
||||
'@': '#/user/',
|
||||
'#': '#/room/',
|
||||
'!': '#/room/',
|
||||
'+': '#/group/',
|
||||
};
|
||||
|
||||
matrixLinkify.options = {
|
||||
events: function(href, type) {
|
||||
switch (type) {
|
||||
|
@ -225,20 +220,8 @@ matrixLinkify.options = {
|
|||
case 'roomalias':
|
||||
case 'userid':
|
||||
case 'groupid':
|
||||
return matrixLinkify.MATRIXTO_BASE_URL + '/#/' + href;
|
||||
default: {
|
||||
// FIXME: horrible duplication with HtmlUtils' transform tags
|
||||
let m = href.match(matrixLinkify.VECTOR_URL_PATTERN);
|
||||
if (m) {
|
||||
return m[1];
|
||||
}
|
||||
m = href.match(matrixLinkify.MATRIXTO_URL_PATTERN);
|
||||
if (m) {
|
||||
const entity = m[1];
|
||||
if (matrixToEntityMap[entity[0]]) return matrixToEntityMap[entity[0]] + entity;
|
||||
}
|
||||
|
||||
return href;
|
||||
return tryTransformPermalinkToLocalHref(href);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -249,8 +232,8 @@ matrixLinkify.options = {
|
|||
|
||||
target: function(href, type) {
|
||||
if (type === 'url') {
|
||||
if (href.match(matrixLinkify.VECTOR_URL_PATTERN) ||
|
||||
href.match(matrixLinkify.MATRIXTO_URL_PATTERN)) {
|
||||
const transformed = tryTransformPermalinkToLocalHref(href);
|
||||
if (transformed !== href || href.match(matrixLinkify.VECTOR_URL_PATTERN)) {
|
||||
return null;
|
||||
} else {
|
||||
return '_blank';
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for classes that actually produce permalinks (strings).
|
||||
* TODO: Convert this to a real TypeScript interface
|
||||
*/
|
||||
export default class PermalinkConstructor {
|
||||
forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
forGroup(groupId: string): string {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
forUser(userId: string): string {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
forEntity(entityId: string): string {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
isPermalinkHost(host: string): boolean {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
|
||||
parsePermalink(fullUrl: string): PermalinkParts {
|
||||
throw new Error("Not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
// Inspired by/Borrowed with permission from the matrix-bot-sdk:
|
||||
// https://github.com/turt2live/matrix-js-bot-sdk/blob/7c4665c9a25c2c8e0fe4e509f2616505b5b66a1c/src/Permalinks.ts#L1-L6
|
||||
export class PermalinkParts {
|
||||
roomIdOrAlias: string;
|
||||
eventId: string;
|
||||
userId: string;
|
||||
groupId: string;
|
||||
viaServers: string[];
|
||||
|
||||
constructor(roomIdOrAlias: string, eventId: string, userId: string, groupId: string, viaServers: string[]) {
|
||||
this.roomIdOrAlias = roomIdOrAlias;
|
||||
this.eventId = eventId;
|
||||
this.groupId = groupId;
|
||||
this.userId = userId;
|
||||
this.viaServers = viaServers;
|
||||
}
|
||||
|
||||
static forUser(userId: string): PermalinkParts {
|
||||
return new PermalinkParts(null, null, userId, null, null);
|
||||
}
|
||||
|
||||
static forGroup(groupId: string): PermalinkParts {
|
||||
return new PermalinkParts(null, null, null, groupId, null);
|
||||
}
|
||||
|
||||
static forRoom(roomIdOrAlias: string, viaServers: string[]): PermalinkParts {
|
||||
return new PermalinkParts(roomIdOrAlias, null, null, null, viaServers || []);
|
||||
}
|
||||
|
||||
static forEvent(roomId: string, eventId: string, viaServers: string[]): PermalinkParts {
|
||||
return new PermalinkParts(roomId, eventId, null, null, viaServers || []);
|
||||
}
|
||||
}
|
|
@ -14,12 +14,15 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import MatrixClientPeg from "./MatrixClientPeg";
|
||||
import MatrixClientPeg from "../../MatrixClientPeg";
|
||||
import isIp from "is-ip";
|
||||
import utils from 'matrix-js-sdk/lib/utils';
|
||||
import SpecPermalinkConstructor, {baseUrl as matrixtoBaseUrl} from "./SpecPermalinkConstructor";
|
||||
import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor";
|
||||
import RiotPermalinkConstructor from "./RiotPermalinkConstructor";
|
||||
import * as matrixLinkify from "../../linkify-matrix";
|
||||
|
||||
export const host = "matrix.to";
|
||||
export const baseUrl = `https://${host}`;
|
||||
const SdkConfig = require("../../SdkConfig");
|
||||
|
||||
// The maximum number of servers to pick when working out which servers
|
||||
// to add to permalinks. The servers are appended as ?via=example.org
|
||||
|
@ -73,7 +76,7 @@ export class RoomPermalinkCreator {
|
|||
// We support being given a roomId as a fallback in the event the `room` object
|
||||
// doesn't exist or is not healthy for us to rely on. For example, loading a
|
||||
// permalink to a room which the MatrixClient doesn't know about.
|
||||
constructor(room, roomId=null) {
|
||||
constructor(room, roomId = null) {
|
||||
this._room = room;
|
||||
this._roomId = room ? room.roomId : roomId;
|
||||
this._highestPlUserId = null;
|
||||
|
@ -124,15 +127,11 @@ export class RoomPermalinkCreator {
|
|||
}
|
||||
|
||||
forEvent(eventId) {
|
||||
const roomId = this._roomId;
|
||||
const permalinkBase = `${baseUrl}/#/${roomId}/${eventId}`;
|
||||
return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
|
||||
return getPermalinkConstructor().forEvent(this._roomId, eventId, this._serverCandidates);
|
||||
}
|
||||
|
||||
forRoom() {
|
||||
const roomId = this._roomId;
|
||||
const permalinkBase = `${baseUrl}/#/${roomId}`;
|
||||
return `${permalinkBase}${encodeServerCandidates(this._serverCandidates)}`;
|
||||
return getPermalinkConstructor().forRoom(this._roomId, this._serverCandidates);
|
||||
}
|
||||
|
||||
onRoomState(event) {
|
||||
|
@ -182,8 +181,8 @@ export class RoomPermalinkCreator {
|
|||
}
|
||||
const serverName = getServerName(userId);
|
||||
return !isHostnameIpAddress(serverName) &&
|
||||
!isHostInRegex(serverName, this._bannedHostsRegexps) &&
|
||||
isHostInRegex(serverName, this._allowedHostsRegexps);
|
||||
!isHostInRegex(serverName, this._bannedHostsRegexps) &&
|
||||
isHostInRegex(serverName, this._allowedHostsRegexps);
|
||||
});
|
||||
const maxEntry = allowedEntries.reduce((max, entry) => {
|
||||
return (entry[1] > max[1]) ? entry : max;
|
||||
|
@ -221,7 +220,7 @@ export class RoomPermalinkCreator {
|
|||
}
|
||||
|
||||
_updatePopulationMap() {
|
||||
const populationMap: {[server:string]:number} = {};
|
||||
const populationMap: { [server: string]: number } = {};
|
||||
for (const member of this._room.getJoinedMembers()) {
|
||||
const serverName = getServerName(member.userId);
|
||||
if (!populationMap[serverName]) {
|
||||
|
@ -242,9 +241,9 @@ export class RoomPermalinkCreator {
|
|||
.sort((a, b) => this._populationMap[b] - this._populationMap[a])
|
||||
.filter(a => {
|
||||
return !candidates.includes(a) &&
|
||||
!isHostnameIpAddress(a) &&
|
||||
!isHostInRegex(a, this._bannedHostsRegexps) &&
|
||||
isHostInRegex(a, this._allowedHostsRegexps);
|
||||
!isHostnameIpAddress(a) &&
|
||||
!isHostInRegex(a, this._bannedHostsRegexps) &&
|
||||
isHostInRegex(a, this._allowedHostsRegexps);
|
||||
});
|
||||
|
||||
const remainingServers = serversByPopulation.slice(0, MAX_SERVER_CANDIDATES - candidates.length);
|
||||
|
@ -254,25 +253,27 @@ export class RoomPermalinkCreator {
|
|||
}
|
||||
}
|
||||
|
||||
export function makeGenericPermalink(entityId: string): string {
|
||||
return getPermalinkConstructor().forEntity(entityId);
|
||||
}
|
||||
|
||||
export function makeUserPermalink(userId) {
|
||||
return `${baseUrl}/#/${userId}`;
|
||||
return getPermalinkConstructor().forUser(userId);
|
||||
}
|
||||
|
||||
export function makeRoomPermalink(roomId) {
|
||||
const permalinkBase = `${baseUrl}/#/${roomId}`;
|
||||
|
||||
if (!roomId) {
|
||||
throw new Error("can't permalink a falsey roomId");
|
||||
}
|
||||
|
||||
// If the roomId isn't actually a room ID, don't try to list the servers.
|
||||
// Aliases are already routable, and don't need extra information.
|
||||
if (roomId[0] !== '!') return permalinkBase;
|
||||
if (roomId[0] !== '!') return getPermalinkConstructor().forRoom(roomId, []);
|
||||
|
||||
const client = MatrixClientPeg.get();
|
||||
const room = client.getRoom(roomId);
|
||||
if (!room) {
|
||||
return permalinkBase;
|
||||
return getPermalinkConstructor().forRoom(roomId, []);
|
||||
}
|
||||
const permalinkCreator = new RoomPermalinkCreator(room);
|
||||
permalinkCreator.load();
|
||||
|
@ -280,12 +281,96 @@ export function makeRoomPermalink(roomId) {
|
|||
}
|
||||
|
||||
export function makeGroupPermalink(groupId) {
|
||||
return `${baseUrl}/#/${groupId}`;
|
||||
return getPermalinkConstructor().forGroup(groupId);
|
||||
}
|
||||
|
||||
export function encodeServerCandidates(candidates) {
|
||||
if (!candidates || candidates.length === 0) return '';
|
||||
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
|
||||
export function isPermalinkHost(host: string): boolean {
|
||||
// Always check if the permalink is a spec permalink (callers are likely to call
|
||||
// parsePermalink after this function).
|
||||
if (new SpecPermalinkConstructor().isPermalinkHost(host)) return true;
|
||||
return getPermalinkConstructor().isPermalinkHost(host);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a permalink (or possible permalink) into a local URL if possible. If
|
||||
* the given permalink is found to not be a permalink, it'll be returned unaltered.
|
||||
* @param {string} permalink The permalink to try and transform.
|
||||
* @returns {string} The transformed permalink or original URL if unable.
|
||||
*/
|
||||
export function tryTransformPermalinkToLocalHref(permalink: string): string {
|
||||
if (!permalink.startsWith("http:") && !permalink.startsWith("https:")) {
|
||||
return permalink;
|
||||
}
|
||||
|
||||
const m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN);
|
||||
if (m) {
|
||||
return m[1];
|
||||
}
|
||||
|
||||
// A bit of a hack to convert permalinks of unknown origin to Riot links
|
||||
try {
|
||||
const permalinkParts = parsePermalink(permalink);
|
||||
if (permalinkParts) {
|
||||
if (permalinkParts.roomIdOrAlias) {
|
||||
const eventIdPart = permalinkParts.eventId ? `/${permalinkParts.eventId}` : '';
|
||||
permalink = `#/room/${permalinkParts.roomIdOrAlias}${eventIdPart}`;
|
||||
} else if (permalinkParts.groupId) {
|
||||
permalink = `#/group/${permalinkParts.groupId}`;
|
||||
} else if (permalinkParts.userId) {
|
||||
permalink = `#/user/${permalinkParts.userId}`;
|
||||
} // else not a valid permalink for our purposes - do not handle
|
||||
}
|
||||
} catch (e) {
|
||||
// Not an href we need to care about
|
||||
}
|
||||
|
||||
return permalink;
|
||||
}
|
||||
|
||||
export function getPrimaryPermalinkEntity(permalink: string): string {
|
||||
try {
|
||||
let permalinkParts = parsePermalink(permalink);
|
||||
|
||||
// If not a permalink, try the vector patterns.
|
||||
if (!permalinkParts) {
|
||||
const m = permalink.match(matrixLinkify.VECTOR_URL_PATTERN);
|
||||
if (m) {
|
||||
// A bit of a hack, but it gets the job done
|
||||
const handler = new RiotPermalinkConstructor("http://localhost");
|
||||
const entityInfo = m[1].split('#').slice(1).join('#');
|
||||
permalinkParts = handler.parsePermalink(`http://localhost/#${entityInfo}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!permalinkParts) return null; // not processable
|
||||
if (permalinkParts.userId) return permalinkParts.userId;
|
||||
if (permalinkParts.groupId) return permalinkParts.groupId;
|
||||
if (permalinkParts.roomIdOrAlias) return permalinkParts.roomIdOrAlias;
|
||||
} catch (e) {
|
||||
// no entity - not a permalink
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getPermalinkConstructor(): PermalinkConstructor {
|
||||
const riotPrefix = SdkConfig.get()['permalinkPrefix'];
|
||||
if (riotPrefix && riotPrefix !== matrixtoBaseUrl) {
|
||||
return new RiotPermalinkConstructor(riotPrefix);
|
||||
}
|
||||
|
||||
return new SpecPermalinkConstructor();
|
||||
}
|
||||
|
||||
export function parsePermalink(fullUrl: string): PermalinkParts {
|
||||
const riotPrefix = SdkConfig.get()['permalinkPrefix'];
|
||||
if (fullUrl.startsWith(matrixtoBaseUrl)) {
|
||||
return new SpecPermalinkConstructor().parsePermalink(fullUrl);
|
||||
} else if (riotPrefix && fullUrl.startsWith(riotPrefix)) {
|
||||
return new RiotPermalinkConstructor(riotPrefix).parsePermalink(fullUrl);
|
||||
}
|
||||
|
||||
return null; // not a permalink we can handle
|
||||
}
|
||||
|
||||
function getServerName(userId) {
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor";
|
||||
|
||||
/**
|
||||
* Generates permalinks that self-reference the running webapp
|
||||
*/
|
||||
export default class RiotPermalinkConstructor extends PermalinkConstructor {
|
||||
_riotUrl: string;
|
||||
|
||||
constructor(riotUrl: string) {
|
||||
super();
|
||||
this._riotUrl = riotUrl;
|
||||
|
||||
if (!this._riotUrl.startsWith("http:") && !this._riotUrl.startsWith("https:")) {
|
||||
throw new Error("Riot prefix URL does not appear to be an HTTP(S) URL");
|
||||
}
|
||||
}
|
||||
|
||||
forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
|
||||
return `${this._riotUrl}/#/room/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`;
|
||||
}
|
||||
|
||||
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
|
||||
return `${this._riotUrl}/#/room/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`;
|
||||
}
|
||||
|
||||
forUser(userId: string): string {
|
||||
return `${this._riotUrl}/#/user/${userId}`;
|
||||
}
|
||||
|
||||
forGroup(groupId: string): string {
|
||||
return `${this._riotUrl}/#/group/${groupId}`;
|
||||
}
|
||||
|
||||
forEntity(entityId: string): string {
|
||||
if (entityId[0] === '!' || entityId[0] === '#') {
|
||||
return this.forRoom(entityId);
|
||||
} else if (entityId[0] === '@') {
|
||||
return this.forUser(entityId);
|
||||
} else if (entityId[0] === '+') {
|
||||
return this.forGroup(entityId);
|
||||
} else throw new Error("Unrecognized entity");
|
||||
}
|
||||
|
||||
isPermalinkHost(testHost: string): boolean {
|
||||
const parsedUrl = new URL(this._riotUrl);
|
||||
return testHost === (parsedUrl.host || parsedUrl.hostname); // one of the hosts should match
|
||||
}
|
||||
|
||||
encodeServerCandidates(candidates: string[]) {
|
||||
if (!candidates || candidates.length === 0) return '';
|
||||
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
|
||||
}
|
||||
|
||||
// Heavily inspired by/borrowed from the matrix-bot-sdk (with permission):
|
||||
// https://github.com/turt2live/matrix-js-bot-sdk/blob/7c4665c9a25c2c8e0fe4e509f2616505b5b66a1c/src/Permalinks.ts#L33-L61
|
||||
// Adapted for Riot's URL format
|
||||
parsePermalink(fullUrl: string): PermalinkParts {
|
||||
if (!fullUrl || !fullUrl.startsWith(this._riotUrl)) {
|
||||
throw new Error("Does not appear to be a permalink");
|
||||
}
|
||||
|
||||
const parts = fullUrl.substring(`${this._riotUrl}/#/`.length).split("/");
|
||||
if (parts.length < 2) { // we're expecting an entity and an ID of some kind at least
|
||||
throw new Error("URL is missing parts");
|
||||
}
|
||||
|
||||
const entityType = parts[0];
|
||||
const entity = parts[1];
|
||||
if (entityType === 'user') {
|
||||
// Probably a user, no further parsing needed.
|
||||
return PermalinkParts.forUser(entity);
|
||||
} else if (entityType === 'group') {
|
||||
// Probably a group, no further parsing needed.
|
||||
return PermalinkParts.forGroup(entity);
|
||||
} else if (entityType === 'room') {
|
||||
if (parts.length === 2) {
|
||||
return PermalinkParts.forRoom(entity, []);
|
||||
}
|
||||
|
||||
// rejoin the rest because v3 events can have slashes (annoyingly)
|
||||
const eventIdAndQuery = parts.length > 2 ? parts.slice(2).join('/') : "";
|
||||
const secondaryParts = eventIdAndQuery.split("?");
|
||||
|
||||
const eventId = secondaryParts[0];
|
||||
const query = secondaryParts.length > 1 ? secondaryParts[1] : "";
|
||||
|
||||
// TODO: Verify Riot works with via args
|
||||
const via = query.split("via=").filter(p => !!p);
|
||||
|
||||
return PermalinkParts.forEvent(entity, eventId, via);
|
||||
} else {
|
||||
throw new Error("Unknown entity type in permalink");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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 PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor";
|
||||
|
||||
export const host = "matrix.to";
|
||||
export const baseUrl = `https://${host}`;
|
||||
|
||||
/**
|
||||
* Generates matrix.to permalinks
|
||||
*/
|
||||
export default class SpecPermalinkConstructor extends PermalinkConstructor {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
forEvent(roomId: string, eventId: string, serverCandidates: string[]): string {
|
||||
return `${baseUrl}/#/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`;
|
||||
}
|
||||
|
||||
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
|
||||
return `${baseUrl}/#/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`;
|
||||
}
|
||||
|
||||
forUser(userId: string): string {
|
||||
return `${baseUrl}/#/${userId}`;
|
||||
}
|
||||
|
||||
forGroup(groupId: string): string {
|
||||
return `${baseUrl}/#/${groupId}`;
|
||||
}
|
||||
|
||||
forEntity(entityId: string): string {
|
||||
return `${baseUrl}/#/${entityId}`;
|
||||
}
|
||||
|
||||
isPermalinkHost(testHost: string): boolean {
|
||||
return testHost === host;
|
||||
}
|
||||
|
||||
encodeServerCandidates(candidates: string[]) {
|
||||
if (!candidates || candidates.length === 0) return '';
|
||||
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
|
||||
}
|
||||
|
||||
// Heavily inspired by/borrowed from the matrix-bot-sdk (with permission):
|
||||
// https://github.com/turt2live/matrix-js-bot-sdk/blob/7c4665c9a25c2c8e0fe4e509f2616505b5b66a1c/src/Permalinks.ts#L33-L61
|
||||
parsePermalink(fullUrl: string): PermalinkParts {
|
||||
if (!fullUrl || !fullUrl.startsWith(baseUrl)) {
|
||||
throw new Error("Does not appear to be a permalink");
|
||||
}
|
||||
|
||||
const parts = fullUrl.substring(`${baseUrl}/#/`.length).split("/");
|
||||
|
||||
const entity = parts[0];
|
||||
if (entity[0] === '@') {
|
||||
// Probably a user, no further parsing needed.
|
||||
return PermalinkParts.forUser(entity);
|
||||
} else if (entity[0] === '+') {
|
||||
// Probably a group, no further parsing needed.
|
||||
return PermalinkParts.forGroup(entity);
|
||||
} else if (entity[0] === '#' || entity[0] === '!') {
|
||||
if (parts.length === 1) {
|
||||
return PermalinkParts.forRoom(entity, []);
|
||||
}
|
||||
|
||||
// rejoin the rest because v3 events can have slashes (annoyingly)
|
||||
const eventIdAndQuery = parts.length > 1 ? parts.slice(1).join('/') : "";
|
||||
const secondaryParts = eventIdAndQuery.split("?");
|
||||
|
||||
const eventId = secondaryParts[0];
|
||||
const query = secondaryParts.length > 1 ? secondaryParts[1] : "";
|
||||
|
||||
const via = query.split("via=").filter(p => !!p);
|
||||
|
||||
return PermalinkParts.forEvent(entity, eventId, via);
|
||||
} else {
|
||||
throw new Error("Unknown entity type in permalink");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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
|
||||
|
@ -12,14 +14,14 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import expect from 'expect';
|
||||
import peg from '../src/MatrixClientPeg';
|
||||
import peg from '../../../src/MatrixClientPeg';
|
||||
import {
|
||||
makeGroupPermalink,
|
||||
makeRoomPermalink,
|
||||
makeUserPermalink,
|
||||
RoomPermalinkCreator,
|
||||
} from "../src/matrix-to";
|
||||
import * as testUtils from "./test-utils";
|
||||
} from "../../../src/utils/permalinks/Permalinks";
|
||||
import * as testUtils from "../../test-utils";
|
||||
|
||||
function mockRoom(roomId, members, serverACL) {
|
||||
members.forEach(m => m.membership = "join");
|
||||
|
@ -62,7 +64,7 @@ function mockRoom(roomId, members, serverACL) {
|
|||
};
|
||||
}
|
||||
|
||||
describe('matrix-to', function() {
|
||||
describe('Permalinks', function() {
|
||||
let sandbox;
|
||||
|
||||
beforeEach(function() {
|
Loading…
Reference in New Issue