Add realm-level settings to control inline image and url preview.

This gives users more control in case they don't want previews,
especially for the "previews of linked websites" feature.

Fixes: #2640.
This commit is contained in:
Ayush Jain 2017-03-13 19:12:03 +05:30 committed by Tim Abbott
parent a27f8f2f30
commit bddcfb1c96
17 changed files with 296 additions and 8 deletions

View File

@ -381,6 +381,10 @@ function _setup_page() {
realm_restricted_to_domain: page_params.realm_restricted_to_domain, realm_restricted_to_domain: page_params.realm_restricted_to_domain,
realm_invite_required: page_params.realm_invite_required, realm_invite_required: page_params.realm_invite_required,
realm_invite_by_admins_only: page_params.realm_invite_by_admins_only, realm_invite_by_admins_only: page_params.realm_invite_by_admins_only,
realm_inline_image_preview: page_params.realm_inline_image_preview,
server_inline_image_preview: page_params.server_inline_image_preview,
realm_inline_url_embed_preview: page_params.realm_inline_url_embed_preview,
server_inline_url_embed_preview: page_params.server_inline_url_embed_preview,
realm_authentication_methods: page_params.realm_authentication_methods, realm_authentication_methods: page_params.realm_authentication_methods,
realm_create_stream_by_admins_only: page_params.realm_create_stream_by_admins_only, realm_create_stream_by_admins_only: page_params.realm_create_stream_by_admins_only,
realm_name_changes_disabled: page_params.realm_name_changes_disabled, realm_name_changes_disabled: page_params.realm_name_changes_disabled,
@ -623,6 +627,8 @@ function _setup_page() {
var restricted_to_domain_status = $("#admin-realm-restricted-to-domain-status").expectOne(); var restricted_to_domain_status = $("#admin-realm-restricted-to-domain-status").expectOne();
var invite_required_status = $("#admin-realm-invite-required-status").expectOne(); var invite_required_status = $("#admin-realm-invite-required-status").expectOne();
var invite_by_admins_only_status = $("#admin-realm-invite-by-admins-only-status").expectOne(); var invite_by_admins_only_status = $("#admin-realm-invite-by-admins-only-status").expectOne();
var inline_image_preview_status = $("#admin-realm-inline-image-preview-status").expectOne();
var inline_url_embed_preview_status = $("#admin-realm-inline-url-embed-preview-status").expectOne();
var authentication_methods_status = $("#admin-realm-authentication-methods-status").expectOne(); var authentication_methods_status = $("#admin-realm-authentication-methods-status").expectOne();
var create_stream_by_admins_only_status = $("#admin-realm-create-stream-by-admins-only-status").expectOne(); var create_stream_by_admins_only_status = $("#admin-realm-create-stream-by-admins-only-status").expectOne();
var name_changes_disabled_status = $("#admin-realm-name-changes-disabled-status").expectOne(); var name_changes_disabled_status = $("#admin-realm-name-changes-disabled-status").expectOne();
@ -636,6 +642,8 @@ function _setup_page() {
restricted_to_domain_status.hide(); restricted_to_domain_status.hide();
invite_required_status.hide(); invite_required_status.hide();
invite_by_admins_only_status.hide(); invite_by_admins_only_status.hide();
inline_image_preview_status.hide();
inline_url_embed_preview_status.hide();
authentication_methods_status.hide(); authentication_methods_status.hide();
create_stream_by_admins_only_status.hide(); create_stream_by_admins_only_status.hide();
name_changes_disabled_status.hide(); name_changes_disabled_status.hide();
@ -653,6 +661,8 @@ function _setup_page() {
var new_restricted = $("#id_realm_restricted_to_domain").prop("checked"); var new_restricted = $("#id_realm_restricted_to_domain").prop("checked");
var new_invite = $("#id_realm_invite_required").prop("checked"); var new_invite = $("#id_realm_invite_required").prop("checked");
var new_invite_by_admins_only = $("#id_realm_invite_by_admins_only").prop("checked"); var new_invite_by_admins_only = $("#id_realm_invite_by_admins_only").prop("checked");
var new_inline_image_preview = $("#id_realm_inline_image_preview").prop("checked");
var new_inline_url_embed_preview = $("#id_realm_inline_url_embed_preview").prop("checked");
var new_create_stream_by_admins_only = $("#id_realm_create_stream_by_admins_only").prop("checked"); var new_create_stream_by_admins_only = $("#id_realm_create_stream_by_admins_only").prop("checked");
var new_name_changes_disabled = $("#id_realm_name_changes_disabled").prop("checked"); var new_name_changes_disabled = $("#id_realm_name_changes_disabled").prop("checked");
var new_email_changes_disabled = $("#id_realm_email_changes_disabled").prop("checked"); var new_email_changes_disabled = $("#id_realm_email_changes_disabled").prop("checked");
@ -685,6 +695,8 @@ function _setup_page() {
restricted_to_domain: JSON.stringify(new_restricted), restricted_to_domain: JSON.stringify(new_restricted),
invite_required: JSON.stringify(new_invite), invite_required: JSON.stringify(new_invite),
invite_by_admins_only: JSON.stringify(new_invite_by_admins_only), invite_by_admins_only: JSON.stringify(new_invite_by_admins_only),
inline_image_preview: JSON.stringify(new_inline_image_preview),
inline_url_embed_preview: JSON.stringify(new_inline_url_embed_preview),
authentication_methods: JSON.stringify(new_auth_methods), authentication_methods: JSON.stringify(new_auth_methods),
create_stream_by_admins_only: JSON.stringify(new_create_stream_by_admins_only), create_stream_by_admins_only: JSON.stringify(new_create_stream_by_admins_only),
name_changes_disabled: JSON.stringify(new_name_changes_disabled), name_changes_disabled: JSON.stringify(new_name_changes_disabled),
@ -728,6 +740,20 @@ function _setup_page() {
ui_report.success(i18n.t("Any user may now invite new users!"), invite_by_admins_only_status); ui_report.success(i18n.t("Any user may now invite new users!"), invite_by_admins_only_status);
} }
} }
if (response_data.inline_image_preview !== undefined) {
if (response_data.inline_image_preview) {
ui.report_success(i18n.t("Previews of uploaded and linked images will be shown!"), inline_image_preview_status);
} else {
ui.report_success(i18n.t("Previews of uploaded and linked images will not be shown!"), inline_image_preview_status);
}
}
if (response_data.inline_url_embed_preview !== undefined) {
if (response_data.inline_url_embed_preview) {
ui.report_success(i18n.t("Previews for linked websites will be shown!"), inline_url_embed_preview_status);
} else {
ui.report_success(i18n.t("Previews for linked websites will not be shown!"), inline_url_embed_preview_status);
}
}
if (response_data.create_stream_by_admins_only !== undefined) { if (response_data.create_stream_by_admins_only !== undefined) {
if (response_data.create_stream_by_admins_only) { if (response_data.create_stream_by_admins_only) {
ui_report.success(i18n.t("Only administrators may now create new streams!"), create_stream_by_admins_only_status); ui_report.success(i18n.t("Only administrators may now create new streams!"), create_stream_by_admins_only_status);

View File

@ -62,6 +62,10 @@ function dispatch_normal_event(event) {
page_params.realm_invite_required = event.value; page_params.realm_invite_required = event.value;
} else if (event.op === 'update' && event.property === 'invite_by_admins_only') { } else if (event.op === 'update' && event.property === 'invite_by_admins_only') {
page_params.realm_invite_by_admins_only = event.value; page_params.realm_invite_by_admins_only = event.value;
} else if (event.op === 'update' && event.property === 'inline_image_preview') {
page_params.realm_inline_image_preview = event.value;
} else if (event.op === 'update' && event.property === 'inline_url_embed_preview') {
page_params.realm_inline_url_embed_preview = event.value;
} else if (event.op === 'update' && event.property === 'create_stream_by_admins_only') { } else if (event.op === 'update' && event.property === 'create_stream_by_admins_only') {
page_params.realm_create_stream_by_admins_only = event.value; page_params.realm_create_stream_by_admins_only = event.value;
if (!page_params.is_admin) { if (!page_params.is_admin) {

View File

@ -7,6 +7,8 @@
<div class="alert" id="admin-realm-restricted-to-domain-status"></div> <div class="alert" id="admin-realm-restricted-to-domain-status"></div>
<div class="alert" id="admin-realm-invite-required-status"></div> <div class="alert" id="admin-realm-invite-required-status"></div>
<div class="alert" id="admin-realm-invite-by-admins-only-status"></div> <div class="alert" id="admin-realm-invite-by-admins-only-status"></div>
<div class="alert" id="admin-realm-inline-image-preview-status"></div>
<div class="alert" id="admin-realm-inline-url-embed-preview-status"></div>
<div class="alert" id="admin-realm-create-stream-by-admins-only-status"></div> <div class="alert" id="admin-realm-create-stream-by-admins-only-status"></div>
<div class="alert" id="admin-realm-add-emoji-by-admins-only-status"></div> <div class="alert" id="admin-realm-add-emoji-by-admins-only-status"></div>
<div class="alert" id="admin-realm-message-editing-status"></div> <div class="alert" id="admin-realm-message-editing-status"></div>
@ -55,6 +57,30 @@
{{t "Only admins may invite" }} {{t "Only admins may invite" }}
</label> </label>
</div> </div>
{{#if server_inline_image_preview}}
<div class="input-group">
<input type="checkbox" class="inline-block" id="id_realm_inline_image_preview" name="realm_inline_image_preview"
{{#if realm_inline_image_preview}}checked="checked"{{/if}} />
<label for="id_realm_inline_image_preview"
id="id_realm_inline_image_preview_label"
title="{{t 'If checked, image previews will be shown.' }}"
class="inline-block" style="width:initial">
{{t "Show previews of uploaded and linked images" }}
</label>
</div>
{{/if}}
{{#if server_inline_url_embed_preview}}
<div class="input-group">
<input type="checkbox" class="inline-block" id="id_realm_inline_url_embed_preview" name="realm_inline_url_embed_preview"
{{#if realm_inline_url_embed_preview}}checked="checked"{{/if}} />
<label for="id_realm_inline_url_embed_preview"
id="id_realm_inline_url_embed_preview_label"
title="{{t 'If checked, previews of linked websites will be shown.' }}"
class="inline-block">
{{t "Show previews of linked websites" }}
</label>
</div>
{{/if}}
<div class="input-group"> <div class="input-group">
<input type="checkbox" class="inline-block" id="id_realm_create_stream_by_admins_only" <input type="checkbox" class="inline-block" id="id_realm_create_stream_by_admins_only"
name="realm_create_stream_by_admins_only" name="realm_create_stream_by_admins_only"

View File

@ -12,7 +12,8 @@ from django.core import validators
from analytics.lib.counts import COUNT_STATS, do_increment_logging_stat from analytics.lib.counts import COUNT_STATS, do_increment_logging_stat
from zerver.lib.bugdown import ( from zerver.lib.bugdown import (
BugdownRenderingException, BugdownRenderingException,
version as bugdown_version version as bugdown_version,
url_embed_preview_enabled_for_realm
) )
from zerver.lib.cache import ( from zerver.lib.cache import (
to_dict_cache_key, to_dict_cache_key,
@ -479,6 +480,30 @@ def do_set_realm_invite_by_admins_only(realm, invite_by_admins_only):
) )
send_event(event, active_user_ids(realm)) send_event(event, active_user_ids(realm))
def do_set_realm_inline_image_preview(realm, inline_image_preview):
# type: (Realm, bool) -> None
realm.inline_image_preview = inline_image_preview
realm.save(update_fields=['inline_image_preview'])
event = dict(
type="realm",
op="update",
property='inline_image_preview',
value=inline_image_preview,
)
send_event(event, active_user_ids(realm))
def do_set_realm_inline_url_embed_preview(realm, inline_url_embed_preview):
# type: (Realm, bool) -> None
realm.inline_url_embed_preview = inline_url_embed_preview
realm.save(update_fields=['inline_url_embed_preview'])
event = dict(
type="realm",
op="update",
property='inline_url_embed_preview',
value=inline_url_embed_preview,
)
send_event(event, active_user_ids(realm))
def do_set_realm_authentication_methods(realm, authentication_methods): def do_set_realm_authentication_methods(realm, authentication_methods):
# type: (Realm, Dict[str, bool]) -> None # type: (Realm, Dict[str, bool]) -> None
for key, value in list(authentication_methods.items()): for key, value in list(authentication_methods.items()):
@ -924,7 +949,7 @@ def do_send_messages(messages_maybe_none):
event['sender_queue_id'] = message['sender_queue_id'] event['sender_queue_id'] = message['sender_queue_id']
send_event(event, users) send_event(event, users)
if settings.INLINE_URL_EMBED_PREVIEW and links_for_embed: if url_embed_preview_enabled_for_realm(message['message']) and links_for_embed:
event_data = { event_data = {
'message_id': message['message'].id, 'message_id': message['message'].id,
'message_content': message['message'].content, 'message_content': message['message'].content,

View File

@ -63,6 +63,32 @@ if False:
class BugdownRenderingException(Exception): class BugdownRenderingException(Exception):
pass pass
def url_embed_preview_enabled_for_realm(message):
# type: (Message) -> bool
if message is not None:
realm = message.get_realm()
else:
realm = None
if not settings.INLINE_URL_EMBED_PREVIEW:
return False
if realm is None:
return True
return realm.inline_url_embed_preview
def image_preview_enabled_for_realm():
# type: () -> bool
global current_message
if current_message is not None:
realm = current_message.get_realm()
else:
realm = None
if not settings.INLINE_IMAGE_PREVIEW:
return False
if realm is None:
return True
return realm.inline_image_preview
def unescape(s): def unescape(s):
# type: (Text) -> (Text) # type: (Text) -> (Text)
if six.PY2: if six.PY2:
@ -327,7 +353,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
def is_image(self, url): def is_image(self, url):
# type: (Text) -> bool # type: (Text) -> bool
if not settings.INLINE_IMAGE_PREVIEW: if not image_preview_enabled_for_realm():
return False return False
parsed_url = urllib.parse.urlparse(url) parsed_url = urllib.parse.urlparse(url)
# List from http://support.google.com/chromeos/bin/answer.py?hl=en&answer=183093 # List from http://support.google.com/chromeos/bin/answer.py?hl=en&answer=183093
@ -384,7 +410,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
def youtube_id(self, url): def youtube_id(self, url):
# type: (Text) -> Optional[Text] # type: (Text) -> Optional[Text]
if not settings.INLINE_IMAGE_PREVIEW: if not image_preview_enabled_for_realm():
return None return None
# Youtube video id extraction regular expression from http://pastebin.com/KyKAFv1s # Youtube video id extraction regular expression from http://pastebin.com/KyKAFv1s
# If it matches, match.group(2) is the video id. # If it matches, match.group(2) is the video id.
@ -620,7 +646,7 @@ class InlineInterestingLinkProcessor(markdown.treeprocessors.Treeprocessor):
if db_data and db_data['sent_by_bot']: if db_data and db_data['sent_by_bot']:
continue continue
if current_message is None or not settings.INLINE_URL_EMBED_PREVIEW: if current_message is None or not url_embed_preview_enabled_for_realm(current_message):
continue continue
try: try:
extracted_data = link_preview.link_embed_data_from_cache(url) extracted_data = link_preview.link_embed_data_from_cache(url)

View File

@ -97,6 +97,8 @@ def fetch_initial_state_data(user_profile, event_types, queue_id,
state['realm_restricted_to_domain'] = user_profile.realm.restricted_to_domain state['realm_restricted_to_domain'] = user_profile.realm.restricted_to_domain
state['realm_invite_required'] = user_profile.realm.invite_required state['realm_invite_required'] = user_profile.realm.invite_required
state['realm_invite_by_admins_only'] = user_profile.realm.invite_by_admins_only state['realm_invite_by_admins_only'] = user_profile.realm.invite_by_admins_only
state['realm_inline_image_preview'] = user_profile.realm.inline_image_preview
state['realm_inline_url_embed_preview'] = user_profile.realm.inline_url_embed_preview
state['realm_authentication_methods'] = user_profile.realm.authentication_methods_dict() state['realm_authentication_methods'] = user_profile.realm.authentication_methods_dict()
state['realm_create_stream_by_admins_only'] = user_profile.realm.create_stream_by_admins_only state['realm_create_stream_by_admins_only'] = user_profile.realm.create_stream_by_admins_only
state['realm_add_emoji_by_admins_only'] = user_profile.realm.add_emoji_by_admins_only state['realm_add_emoji_by_admins_only'] = user_profile.realm.add_emoji_by_admins_only

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-21 15:56
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('zerver', '0064_sync_uploads_filesize_with_db'),
]
operations = [
migrations.AddField(
model_name='realm',
name='inline_image_preview',
field=models.BooleanField(default=True),
),
]

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-03-21 15:58
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('zerver', '0065_realm_inline_image_preview'),
]
operations = [
migrations.AddField(
model_name='realm',
name='inline_url_embed_preview',
field=models.BooleanField(default=True),
),
]

View File

@ -116,6 +116,8 @@ class Realm(ModelReprMixin, models.Model):
restricted_to_domain = models.BooleanField(default=False) # type: bool restricted_to_domain = models.BooleanField(default=False) # type: bool
invite_required = models.BooleanField(default=True) # type: bool invite_required = models.BooleanField(default=True) # type: bool
invite_by_admins_only = models.BooleanField(default=False) # type: bool invite_by_admins_only = models.BooleanField(default=False) # type: bool
inline_image_preview = models.BooleanField(default=True) # type: bool
inline_url_embed_preview = models.BooleanField(default=True) # type: bool
create_stream_by_admins_only = models.BooleanField(default=False) # type: bool create_stream_by_admins_only = models.BooleanField(default=False) # type: bool
add_emoji_by_admins_only = models.BooleanField(default=False) # type: bool add_emoji_by_admins_only = models.BooleanField(default=False) # type: bool
mandatory_topics = models.BooleanField(default=False) # type: bool mandatory_topics = models.BooleanField(default=False) # type: bool

View File

@ -2,7 +2,7 @@
from __future__ import print_function from __future__ import print_function
from __future__ import absolute_import from __future__ import absolute_import
from django.conf import settings from django.conf import settings
from django.test import TestCase from django.test import TestCase, override_settings
from zerver.lib import bugdown from zerver.lib import bugdown
from zerver.lib.actions import ( from zerver.lib.actions import (
@ -37,6 +37,7 @@ from zerver.models import (
Recipient, Recipient,
) )
import copy
import mock import mock
import os import os
import ujson import ujson
@ -229,6 +230,65 @@ class BugdownTest(TestCase):
self.assertEqual(converted, '<p>Check out the debate: <a href="http://www.youtube.com/watch?v=hx1mjT73xYE" target="_blank" title="http://www.youtube.com/watch?v=hx1mjT73xYE">http://www.youtube.com/watch?v=hx1mjT73xYE</a></p>\n<div class="youtube-video message_inline_image"><a data-id="hx1mjT73xYE" href="http://www.youtube.com/watch?v=hx1mjT73xYE" target="_blank" title="http://www.youtube.com/watch?v=hx1mjT73xYE"><img src="https://i.ytimg.com/vi/hx1mjT73xYE/default.jpg"></a></div>') self.assertEqual(converted, '<p>Check out the debate: <a href="http://www.youtube.com/watch?v=hx1mjT73xYE" target="_blank" title="http://www.youtube.com/watch?v=hx1mjT73xYE">http://www.youtube.com/watch?v=hx1mjT73xYE</a></p>\n<div class="youtube-video message_inline_image"><a data-id="hx1mjT73xYE" href="http://www.youtube.com/watch?v=hx1mjT73xYE" target="_blank" title="http://www.youtube.com/watch?v=hx1mjT73xYE"><img src="https://i.ytimg.com/vi/hx1mjT73xYE/default.jpg"></a></div>')
@override_settings(INLINE_IMAGE_PREVIEW=True)
def test_inline_image_preview(self):
# type: () -> None
with_preview = '<p><a href="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg" target="_blank" title="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg">http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg</a></p>\n<div class="message_inline_image"><a href="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg" target="_blank" title="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg"><img src="https://external-content.zulipcdn.net/389b5d7148a0cbc7475ed564e1b03ceb476bdacb/687474703a2f2f63646e2e77616c6c70617065727361666172692e636f6d2f31332f362f313665566a782e6a706567"></a></div>'
without_preview = '<p><a href="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg" target="_blank" title="http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg">http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg</a></p>'
content = 'http://cdn.wallpapersafari.com/13/6/16eVjx.jpeg'
sender_user_profile = get_user_profile_by_email("othello@zulip.com")
msg = Message(sender=sender_user_profile, sending_client=get_client("test"))
converted = render_markdown(msg, content)
self.assertEqual(converted, with_preview)
realm = msg.get_realm()
setattr(realm, 'inline_image_preview', False)
realm.save()
sender_user_profile = get_user_profile_by_email("othello@zulip.com")
msg = Message(sender=sender_user_profile, sending_client=get_client("test"))
converted = render_markdown(msg, content)
self.assertEqual(converted, without_preview)
@override_settings(INLINE_IMAGE_PREVIEW=False)
def test_image_preview_enabled_for_realm(self):
# type: () -> None
ret = bugdown.image_preview_enabled_for_realm()
self.assertEqual(ret, False)
settings.INLINE_IMAGE_PREVIEW = True
sender_user_profile = get_user_profile_by_email("othello@zulip.com")
bugdown.current_message = copy.deepcopy(Message(sender=sender_user_profile, sending_client=get_client("test")))
realm = bugdown.current_message.get_realm()
ret = bugdown.image_preview_enabled_for_realm()
self.assertEqual(ret, realm.inline_image_preview)
bugdown.current_message = None
ret = bugdown.image_preview_enabled_for_realm()
self.assertEqual(ret, True)
@override_settings(INLINE_URL_EMBED_PREVIEW=False)
def test_url_embed_preview_enabled_for_realm(self):
# type: () -> None
sender_user_profile = get_user_profile_by_email("othello@zulip.com")
message = copy.deepcopy(Message(sender=sender_user_profile, sending_client=get_client("test")))
realm = message.get_realm()
ret = bugdown.url_embed_preview_enabled_for_realm(message)
self.assertEqual(ret, False)
settings.INLINE_URL_EMBED_PREVIEW = True
ret = bugdown.url_embed_preview_enabled_for_realm(message)
self.assertEqual(ret, realm.inline_image_preview)
message = None
ret = bugdown.url_embed_preview_enabled_for_realm(message)
self.assertEqual(ret, True)
def test_inline_dropbox(self): def test_inline_dropbox(self):
# type: () -> None # type: () -> None
msg = 'Look at how hilarious our old office was: https://www.dropbox.com/s/ymdijjcg67hv2ta/IMG_0923.JPG' msg = 'Look at how hilarious our old office was: https://www.dropbox.com/s/ymdijjcg67hv2ta/IMG_0923.JPG'

View File

@ -51,6 +51,8 @@ from zerver.lib.actions import (
do_set_realm_invite_by_admins_only, do_set_realm_invite_by_admins_only,
do_set_name_changes_disabled, do_set_name_changes_disabled,
do_set_email_changes_disabled, do_set_email_changes_disabled,
do_set_realm_inline_image_preview,
do_set_realm_inline_url_embed_preview,
do_set_realm_message_editing, do_set_realm_message_editing,
do_set_realm_default_language, do_set_realm_default_language,
do_set_realm_authentication_methods, do_set_realm_authentication_methods,
@ -711,6 +713,34 @@ class EventsRegisterTest(ZulipTestCase):
error = schema_checker('events[0]', events[0]) error = schema_checker('events[0]', events[0])
self.assert_on_error(error) self.assert_on_error(error)
def test_change_realm_inline_image_preview(self):
# type: () -> None
schema_checker = check_dict([
('type', equals('realm')),
('op', equals('update')),
('property', equals('inline_image_preview')),
('value', check_bool),
])
do_set_realm_inline_image_preview(self.user_profile.realm, inline_image_preview=False)
for inline_image_preview in (True, False):
events = self.do_test(lambda: do_set_realm_inline_image_preview(self.user_profile.realm, inline_image_preview))
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
def test_change_realm_inline_url_embed_preview(self):
# type: () -> None
schema_checker = check_dict([
('type', equals('realm')),
('op', equals('update')),
('property', equals('inline_url_embed_preview')),
('value', check_bool),
])
do_set_realm_inline_url_embed_preview(self.user_profile.realm, inline_url_embed_preview=False)
for inline_url_embed_preview in (True, False):
events = self.do_test(lambda: do_set_realm_inline_url_embed_preview(self.user_profile.realm, inline_url_embed_preview))
error = schema_checker('events[0]', events[0])
self.assert_on_error(error)
def test_change_realm_default_language(self): def test_change_realm_default_language(self):
# type: () -> None # type: () -> None
schema_checker = check_dict([ schema_checker = check_dict([

View File

@ -105,6 +105,8 @@ class HomeTest(ZulipTestCase):
"realm_filters", "realm_filters",
"realm_icon_source", "realm_icon_source",
"realm_icon_url", "realm_icon_url",
"realm_inline_image_preview",
"realm_inline_url_embed_preview",
"realm_invite_by_admins_only", "realm_invite_by_admins_only",
"realm_invite_required", "realm_invite_required",
"realm_message_content_edit_limit_seconds", "realm_message_content_edit_limit_seconds",
@ -117,6 +119,8 @@ class HomeTest(ZulipTestCase):
"referrals", "referrals",
"save_stacktraces", "save_stacktraces",
"server_generation", "server_generation",
"server_inline_image_preview",
"server_inline_url_embed_preview",
"server_uri", "server_uri",
"share_the_love", "share_the_love",
"show_digest_email", "show_digest_email",

View File

@ -256,6 +256,20 @@ class PreviewTestCase(ZulipTestCase):
msg = self._send_message_with_test_org_url(sender_email='prospero@zulip.com') msg = self._send_message_with_test_org_url(sender_email='prospero@zulip.com')
self.assertIn(embedded_link, msg.rendered_content) self.assertIn(embedded_link, msg.rendered_content)
def test_inline_url_embed_preview(self):
# type: () -> None
with_preview = '<p><a href="http://test.org/" target="_blank" title="http://test.org/">http://test.org/</a></p>\n<div class="message_embed"><a class="message_embed_image" href="http://test.org/" style="background-image: url(http://ia.media-imdb.com/images/rock.jpg)" target="_blank"></a><div class="data-container"><div class="message_embed_title"><a href="http://test.org/" target="_blank" title="The Rock">The Rock</a></div><div class="message_embed_description">Description text</div></div></div>'
without_preview = '<p><a href="http://test.org/" target="_blank" title="http://test.org/">http://test.org/</a></p>'
msg = self._send_message_with_test_org_url(sender_email='hamlet@zulip.com')
self.assertEqual(msg.rendered_content, with_preview)
realm = msg.get_realm()
setattr(realm, 'inline_url_embed_preview', False)
realm.save()
msg = self._send_message_with_test_org_url(sender_email='prospero@zulip.com', queue_should_run=False)
self.assertEqual(msg.rendered_content, without_preview)
def test_http_error_get_data(self): def test_http_error_get_data(self):
# type: () -> None # type: () -> None
url = 'http://test.org/' url = 'http://test.org/'

View File

@ -135,6 +135,20 @@ class RealmTest(ZulipTestCase):
realm = update_with_api(invite_by_admins_only=False) realm = update_with_api(invite_by_admins_only=False)
self.assertEqual(realm.invite_by_admins_only, False) self.assertEqual(realm.invite_by_admins_only, False)
# inline_image_preview
set_up_db('inline_image_preview', True)
realm = update_with_api(inline_image_preview=False)
self.assertEqual(realm.inline_image_preview, False)
realm = update_with_api(inline_image_preview=True)
self.assertEqual(realm.inline_image_preview, True)
# inline_url_embed_preview
set_up_db('inline_url_embed_preview', False)
realm = update_with_api(inline_url_embed_preview=True)
self.assertEqual(realm.inline_url_embed_preview, True)
realm = update_with_api(inline_url_embed_preview=False)
self.assertEqual(realm.inline_url_embed_preview, False)
# create_stream_by_admins_only # create_stream_by_admins_only
set_up_db('create_stream_by_admins_only', False) set_up_db('create_stream_by_admins_only', False)
realm = update_with_api(create_stream_by_admins_only=True) realm = update_with_api(create_stream_by_admins_only=True)

View File

@ -206,6 +206,8 @@ def home_real(request):
server_generation = settings.SERVER_GENERATION, server_generation = settings.SERVER_GENERATION,
use_websockets = settings.USE_WEBSOCKETS, use_websockets = settings.USE_WEBSOCKETS,
save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES, save_stacktraces = settings.SAVE_FRONTEND_STACKTRACES,
server_inline_image_preview = settings.INLINE_IMAGE_PREVIEW,
server_inline_url_embed_preview = settings.INLINE_URL_EMBED_PREVIEW,
# realm data. # realm data.
# TODO: Move all of these data to register_ret and pull from there # TODO: Move all of these data to register_ret and pull from there
@ -299,6 +301,8 @@ def home_real(request):
'realm_icon_source', 'realm_icon_source',
'realm_icon_url', 'realm_icon_url',
'realm_invite_by_admins_only', 'realm_invite_by_admins_only',
'realm_inline_image_preview',
'realm_inline_url_embed_preview',
'realm_invite_required', 'realm_invite_required',
'realm_message_content_edit_limit_seconds', 'realm_message_content_edit_limit_seconds',
'realm_name', 'realm_name',

View File

@ -972,6 +972,7 @@ def update_message_backend(request, user_profile,
message, ignored_user_message = access_message(user_profile, message_id) message, ignored_user_message = access_message(user_profile, message_id)
# You only have permission to edit a message if: # You only have permission to edit a message if:
# you change this value also change those two parameters in message_edit.js.
# 1. You sent it, OR: # 1. You sent it, OR:
# 2. This is a topic-only edit for a (no topic) message, OR: # 2. This is a topic-only edit for a (no topic) message, OR:
# 3. This is a topic-only edit and you are an admin. # 3. This is a topic-only edit and you are an admin.
@ -1034,7 +1035,7 @@ def update_message_backend(request, user_profile,
propagate_mode, content, rendered_content) propagate_mode, content, rendered_content)
# Include the number of messages changed in the logs # Include the number of messages changed in the logs
request._log_data['extra'] = "[%s]" % (number_changed,) request._log_data['extra'] = "[%s]" % (number_changed,)
if links_for_embed and getattr(settings, 'INLINE_URL_EMBED_PREVIEW', None): if links_for_embed and bugdown.url_embed_preview_enabled_for_realm(message):
event_data = { event_data = {
'message_id': message.id, 'message_id': message.id,
'message_content': message.content, 'message_content': message.content,

View File

@ -12,6 +12,8 @@ from zerver.lib.actions import (
do_set_realm_invite_by_admins_only, do_set_realm_invite_by_admins_only,
do_set_name_changes_disabled, do_set_name_changes_disabled,
do_set_email_changes_disabled, do_set_email_changes_disabled,
do_set_realm_inline_image_preview,
do_set_realm_inline_url_embed_preview,
do_set_realm_add_emoji_by_admins_only, do_set_realm_add_emoji_by_admins_only,
do_set_realm_invite_required, do_set_realm_invite_required,
do_set_realm_message_editing, do_set_realm_message_editing,
@ -35,6 +37,8 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
invite_by_admins_only=REQ(validator=check_bool, default=None), invite_by_admins_only=REQ(validator=check_bool, default=None),
name_changes_disabled=REQ(validator=check_bool, default=None), name_changes_disabled=REQ(validator=check_bool, default=None),
email_changes_disabled=REQ(validator=check_bool, default=None), email_changes_disabled=REQ(validator=check_bool, default=None),
inline_image_preview=REQ(validator=check_bool, default=None),
inline_url_embed_preview=REQ(validator=check_bool, default=None),
create_stream_by_admins_only=REQ(validator=check_bool, default=None), create_stream_by_admins_only=REQ(validator=check_bool, default=None),
add_emoji_by_admins_only=REQ(validator=check_bool, default=None), add_emoji_by_admins_only=REQ(validator=check_bool, default=None),
allow_message_editing=REQ(validator=check_bool, default=None), allow_message_editing=REQ(validator=check_bool, default=None),
@ -42,7 +46,7 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
default_language=REQ(validator=check_string, default=None), default_language=REQ(validator=check_string, default=None),
waiting_period_threshold=REQ(converter=to_non_negative_int, default=None), waiting_period_threshold=REQ(converter=to_non_negative_int, default=None),
authentication_methods=REQ(validator=check_dict([]), default=None)): authentication_methods=REQ(validator=check_dict([]), default=None)):
# type: (HttpRequest, UserProfile, Optional[str], Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[int], Optional[str], Optional[int], Optional[dict]) -> HttpResponse # type: (HttpRequest, UserProfile, Optional[str], Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[int], Optional[str], Optional[int], Optional[dict]) -> HttpResponse
# Validation for default_language # Validation for default_language
if default_language is not None and default_language not in get_available_language_codes(): if default_language is not None and default_language not in get_available_language_codes():
raise JsonableError(_("Invalid language '%s'" % (default_language,))) raise JsonableError(_("Invalid language '%s'" % (default_language,)))
@ -71,6 +75,12 @@ def update_realm(request, user_profile, name=REQ(validator=check_string, default
if email_changes_disabled is not None and realm.email_changes_disabled != email_changes_disabled: if email_changes_disabled is not None and realm.email_changes_disabled != email_changes_disabled:
do_set_email_changes_disabled(realm, email_changes_disabled) do_set_email_changes_disabled(realm, email_changes_disabled)
data['email_changes_disabled'] = email_changes_disabled data['email_changes_disabled'] = email_changes_disabled
if inline_image_preview is not None and realm.inline_image_preview != inline_image_preview:
do_set_realm_inline_image_preview(realm, inline_image_preview)
data['inline_image_preview'] = inline_image_preview
if inline_url_embed_preview is not None and realm.inline_url_embed_preview != inline_url_embed_preview:
do_set_realm_inline_url_embed_preview(realm, inline_url_embed_preview)
data['inline_url_embed_preview'] = inline_url_embed_preview
if authentication_methods is not None and realm.authentication_methods_dict() != authentication_methods: if authentication_methods is not None and realm.authentication_methods_dict() != authentication_methods:
if True not in list(authentication_methods.values()): if True not in list(authentication_methods.values()):
return json_error(_("At least one authentication method must be enabled."), return json_error(_("At least one authentication method must be enabled."),