Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #2532 from boeserwolf/boeserwolf/get-set-topic
Browse files Browse the repository at this point in the history
Extend slash command '/topic' to display the room topic
  • Loading branch information
dbkr committed Feb 7, 2019
2 parents 7affcf7 + 179f9a1 commit b7fd133
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 52 deletions.
44 changes: 40 additions & 4 deletions src/HtmlUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,21 @@ limitations under the License.

import ReplyThread from "./components/views/elements/ReplyThread";

const React = require('react');
const sanitizeHtml = require('sanitize-html');
const highlight = require('highlight.js');
const linkifyMatrix = require('./linkify-matrix');
import React from 'react';
import sanitizeHtml from 'sanitize-html';
import highlight from 'highlight.js';
import * as linkify from 'linkifyjs';
import linkifyMatrix from './linkify-matrix';
import _linkifyElement from 'linkifyjs/element';
import _linkifyString from 'linkifyjs/string';
import escape from 'lodash/escape';
import emojione from 'emojione';
import classNames from 'classnames';
import MatrixClientPeg from './MatrixClientPeg';
import url from 'url';

linkifyMatrix(linkify);

emojione.imagePathSVG = 'emojione/svg/';
// Store PNG path for displaying many flags at once (for increased performance over SVG)
emojione.imagePathPNG = 'emojione/png/';
Expand Down Expand Up @@ -508,3 +513,34 @@ export function emojifyText(text) {
__html: unicodeToImage(escape(text)),
};
}

/**
* Linkifies the given string. This is a wrapper around 'linkifyjs/string'.
*
* @param {string} str
* @returns {string}
*/
export function linkifyString(str) {
return _linkifyString(str);
}

/**
* Linkifies the given DOM element. This is a wrapper around 'linkifyjs/element'.
*
* @param {object} element DOM element to linkify
* @param {object} [options] Options for linkifyElement. Default: linkifyMatrix.options
* @returns {object}
*/
export function linkifyElement(element, options = linkifyMatrix.options) {
return _linkifyElement(element, options);
}

/**
* Linkify the given string and sanitize the HTML afterwards.
*
* @param {string} dirtyHtml The HTML string to sanitize and linkify
* @returns {string}
*/
export function linkifyAndSanitizeHtml(dirtyHtml) {
return sanitizeHtml(linkifyString(dirtyHtml), sanitizeHtmlParams);
}
38 changes: 24 additions & 14 deletions src/SlashCommands.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import SettingsStore, {SettingLevel} from './settings/SettingsStore';
import {MATRIXTO_URL_PATTERN} from "./linkify-matrix";
import * as querystring from "querystring";
import MultiInviter from './utils/MultiInviter';

import { linkifyAndSanitizeHtml } from './HtmlUtils';

class Command {
constructor({name, args='', description, runFn, hideCompletionAfterSpace=false}) {
Expand Down Expand Up @@ -137,13 +137,26 @@ export const CommandMap = {

topic: new Command({
name: 'topic',
args: '<topic>',
description: _td('Sets the room topic'),
args: '[<topic>]',
description: _td('Gets or sets the room topic'),
runFn: function(roomId, args) {
const cli = MatrixClientPeg.get();
if (args) {
return success(MatrixClientPeg.get().setRoomTopic(roomId, args));
return success(cli.setRoomTopic(roomId, args));
}
return reject(this.getUsage());
const room = cli.getRoom(roomId);
if (!room) return reject('Bad room ID: ' + roomId);

const topicEvents = room.currentState.getStateEvents('m.room.topic', '');
const topic = topicEvents && topicEvents.getContent().topic;
const topicHtml = topic ? linkifyAndSanitizeHtml(topic) : _t('This room has no topic.');

const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
Modal.createTrackedDialog('Slash Commands', 'Topic', InfoDialog, {
title: room.name,
description: <div dangerouslySetInnerHTML={{ __html: topicHtml }} />,
});
return success();
},
}),

Expand Down Expand Up @@ -391,13 +404,12 @@ export const CommandMap = {
ignoredUsers.push(userId); // de-duped internally in the js-sdk
return success(
cli.setIgnoredUsers(ignoredUsers).then(() => {
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
Modal.createTrackedDialog('Slash Commands', 'User ignored', QuestionDialog, {
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
Modal.createTrackedDialog('Slash Commands', 'User ignored', InfoDialog, {
title: _t('Ignored user'),
description: <div>
<p>{ _t('You are now ignoring %(userId)s', {userId}) }</p>
</div>,
hasCancelButton: false,
});
}),
);
Expand All @@ -423,13 +435,12 @@ export const CommandMap = {
if (index !== -1) ignoredUsers.splice(index, 1);
return success(
cli.setIgnoredUsers(ignoredUsers).then(() => {
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
Modal.createTrackedDialog('Slash Commands', 'User unignored', QuestionDialog, {
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
Modal.createTrackedDialog('Slash Commands', 'User unignored', InfoDialog, {
title: _t('Unignored user'),
description: <div>
<p>{ _t('You are no longer ignoring %(userId)s', {userId}) }</p>
</div>,
hasCancelButton: false,
});
}),
);
Expand Down Expand Up @@ -546,8 +557,8 @@ export const CommandMap = {
return cli.setDeviceVerified(userId, deviceId, true);
}).then(() => {
// Tell the user we verified everything
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
Modal.createTrackedDialog('Slash Commands', 'Verified key', QuestionDialog, {
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
Modal.createTrackedDialog('Slash Commands', 'Verified key', InfoDialog, {
title: _t('Verified key'),
description: <div>
<p>
Expand All @@ -558,7 +569,6 @@ export const CommandMap = {
}
</p>
</div>,
hasCancelButton: false,
});
}),
);
Expand Down
9 changes: 2 additions & 7 deletions src/components/structures/RoomDirectory.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ const Modal = require('../../Modal');
const sdk = require('../../index');
const dis = require('../../dispatcher');

const linkify = require('linkifyjs');
const linkifyString = require('linkifyjs/string');
const linkifyMatrix = require('../../linkify-matrix');
const sanitizeHtml = require('sanitize-html');
import { linkifyAndSanitizeHtml } from '../../HtmlUtils';
import Promise from 'bluebird';

import { _t } from '../../languageHandler';
Expand All @@ -37,8 +34,6 @@ import {instanceForInstanceId, protocolNameForInstanceId} from '../../utils/Dire
const MAX_NAME_LENGTH = 80;
const MAX_TOPIC_LENGTH = 160;

linkifyMatrix(linkify);

module.exports = React.createClass({
displayName: 'RoomDirectory',

Expand Down Expand Up @@ -438,7 +433,7 @@ module.exports = React.createClass({
if (topic.length > MAX_TOPIC_LENGTH) {
topic = `${topic.substring(0, MAX_TOPIC_LENGTH)}...`;
}
topic = linkifyString(sanitizeHtml(topic));
topic = linkifyAndSanitizeHtml(topic);

rows.push(
<tr key={ rooms[i].room_id }
Expand Down
64 changes: 64 additions & 0 deletions src/components/views/dialogs/InfoDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd.
Copyright 2019 Bastian Masanek, Noxware IT <matrix@noxware.de>
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 React from 'react';
import PropTypes from 'prop-types';
import sdk from '../../../index';
import { _t } from '../../../languageHandler';

export default React.createClass({
displayName: 'InfoDialog',
propTypes: {
title: PropTypes.string,
description: PropTypes.node,
button: PropTypes.string,
onFinished: PropTypes.func,
},

getDefaultProps: function() {
return {
title: '',
description: '',
};
},

onFinished: function() {
this.props.onFinished();
},

render: function() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return (
<BaseDialog className="mx_InfoDialog" onFinished={this.props.onFinished}
title={this.props.title}
contentId='mx_Dialog_content'
hasCancel={false}
>
<div className="mx_Dialog_content" id="mx_Dialog_content">
{ this.props.description }
</div>
<DialogButtons primaryButton={this.props.button || _t('OK')}
onPrimaryButtonClick={this.onFinished}
hasCancel={false}
>
</DialogButtons>
</BaseDialog>
);
},
});
7 changes: 1 addition & 6 deletions src/components/views/messages/TextualBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import highlight from 'highlight.js';
import * as HtmlUtils from '../../../HtmlUtils';
import * as linkify from 'linkifyjs';
import linkifyElement from 'linkifyjs/element';
import linkifyMatrix from '../../../linkify-matrix';
import sdk from '../../../index';
import ScalarAuthClient from '../../../ScalarAuthClient';
import Modal from '../../../Modal';
Expand All @@ -38,8 +35,6 @@ import PushProcessor from 'matrix-js-sdk/lib/pushprocessor';
import ReplyThread from "../elements/ReplyThread";
import {host as matrixtoHost} from '../../../matrix-to';

linkifyMatrix(linkify);

module.exports = React.createClass({
displayName: 'TextualBody',

Expand Down Expand Up @@ -98,7 +93,7 @@ module.exports = React.createClass({
// are still sent as plaintext URLs. If these are ever pillified in the composer,
// we should be pillify them here by doing the linkifying BEFORE the pillifying.
this.pillifyLinks(this.refs.content.children);
linkifyElement(this.refs.content, linkifyMatrix.options);
HtmlUtils.linkifyElement(this.refs.content);
this.calculateUrlPreview();

if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") {
Expand Down
12 changes: 4 additions & 8 deletions src/components/views/rooms/LinkPreviewWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,15 @@ limitations under the License.

'use strict';

const React = require('react');
import React from 'react';
import PropTypes from 'prop-types';
import { linkifyElement } from '../../../HtmlUtils';

const sdk = require('../../../index');
const MatrixClientPeg = require('../../../MatrixClientPeg');
const ImageUtils = require('../../../ImageUtils');
const Modal = require('../../../Modal');

const linkify = require('linkifyjs');
const linkifyElement = require('linkifyjs/element');
const linkifyMatrix = require('../../../linkify-matrix');
linkifyMatrix(linkify);

module.exports = React.createClass({
displayName: 'LinkPreviewWidget',

Expand Down Expand Up @@ -62,13 +58,13 @@ module.exports = React.createClass({

componentDidMount: function() {
if (this.refs.description) {
linkifyElement(this.refs.description, linkifyMatrix.options);
linkifyElement(this.refs.description);
}
},

componentDidUpdate: function() {
if (this.refs.description) {
linkifyElement(this.refs.description, linkifyMatrix.options);
linkifyElement(this.refs.description);
}
},

Expand Down
8 changes: 2 additions & 6 deletions src/components/views/rooms/RoomDetailRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,11 @@ limitations under the License.
import sdk from '../../../index';
import React from 'react';
import { _t } from '../../../languageHandler';
import * as linkify from 'linkifyjs';
import linkifyElement from 'linkifyjs/element';
import linkifyMatrix from '../../../linkify-matrix';
import { linkifyElement } from '../../../HtmlUtils';
import { ContentRepo } from 'matrix-js-sdk';
import MatrixClientPeg from '../../../MatrixClientPeg';
import PropTypes from 'prop-types';

linkifyMatrix(linkify);

export function getDisplayAliasForRoom(room) {
return room.canonicalAlias || (room.aliases ? room.aliases[0] : "");
}
Expand Down Expand Up @@ -53,7 +49,7 @@ export default React.createClass({

_linkifyTopic: function() {
if (this.refs.topic) {
linkifyElement(this.refs.topic, linkifyMatrix.options);
linkifyElement(this.refs.topic);
}
},

Expand Down
8 changes: 2 additions & 6 deletions src/components/views/rooms/RoomHeader.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,14 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from "../../../Modal";
import RateLimitedFunc from '../../../ratelimitedfunc';

import * as linkify from 'linkifyjs';
import linkifyElement from 'linkifyjs/element';
import linkifyMatrix from '../../../linkify-matrix';
import { linkifyElement } from '../../../HtmlUtils';
import AccessibleButton from '../elements/AccessibleButton';
import ManageIntegsButton from '../elements/ManageIntegsButton';
import {CancelButton} from './SimpleRoomHeader';
import SettingsStore from "../../../settings/SettingsStore";
import RoomHeaderButtons from '../right_panel/RoomHeaderButtons';
import E2EIcon from './E2EIcon';

linkifyMatrix(linkify);

module.exports = React.createClass({
displayName: 'RoomHeader',

Expand Down Expand Up @@ -76,7 +72,7 @@ module.exports = React.createClass({

componentDidUpdate: function() {
if (this.refs.topic) {
linkifyElement(this.refs.topic, linkifyMatrix.options);
linkifyElement(this.refs.topic);
}
},

Expand Down
3 changes: 2 additions & 1 deletion src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@
"Upgrades a room to a new version": "Upgrades a room to a new version",
"Changes your display nickname": "Changes your display nickname",
"Changes colour scheme of current room": "Changes colour scheme of current room",
"Sets the room topic": "Sets the room topic",
"Gets or sets the room topic": "Gets or sets the room topic",
"This room has no topic.": "This room has no topic.",
"Sets the room name": "Sets the room name",
"Invites user with given id to current room": "Invites user with given id to current room",
"Joins room with given alias": "Joins room with given alias",
Expand Down

0 comments on commit b7fd133

Please sign in to comment.