mirror of https://github.com/zulip/zulip.git
settings: Unite user settings into a single panel.
Previously, there were three different sections for managing active users, deactivated users and invitations. This commit combines users section has into a single tabbed panel. Fixes: #26949. Co-authored-by: shashank-23002 <21bec103@iiitdmj.ac.in>
This commit is contained in:
parent
e4c89771fd
commit
8ab6e71593
|
@ -80,7 +80,7 @@ bots will be deactivated until the user manually
|
|||
|
||||
{tab|via-organization-settings}
|
||||
|
||||
{settings_tab|deactivated-users-admin}
|
||||
{settings_tab|deactivated}
|
||||
|
||||
1. Click the **Reactivate** button to the right of the user account that you
|
||||
want to reactivate.
|
||||
|
|
|
@ -84,7 +84,7 @@ for invitations for the organization owners role.
|
|||
|
||||
{start_tabs}
|
||||
|
||||
{settings_tab|invites-list-admin}
|
||||
{settings_tab|invitations}
|
||||
|
||||
1. From there, you can view pending invitations, **Revoke** email
|
||||
invitations and invitation links, or **Resend** email invitations.
|
||||
|
|
|
@ -15,6 +15,7 @@ async function navigate_to_user_list(page: Page): Promise<void> {
|
|||
|
||||
await page.waitForSelector("#settings_overlay_container.show", {visible: true});
|
||||
await page.click("li[data-section='users']");
|
||||
await page.waitForSelector("#admin-user-list.show", {visible: true});
|
||||
}
|
||||
|
||||
async function user_row(page: Page, name: string): Promise<string> {
|
||||
|
@ -80,7 +81,8 @@ async function test_deactivated_users_section(page: Page): Promise<void> {
|
|||
|
||||
// "Deactivated users" section doesn't render just deactivated users until reloaded.
|
||||
await page.reload();
|
||||
const deactivated_users_section = "li[data-section='deactivated-users-admin']";
|
||||
await page.waitForSelector("#admin-user-list.show", {visible: true});
|
||||
const deactivated_users_section = ".tab-container .ind-tab[data-tab-key='deactivated']";
|
||||
await page.waitForSelector(deactivated_users_section, {visible: true});
|
||||
await page.click(deactivated_users_section);
|
||||
|
||||
|
|
|
@ -80,11 +80,12 @@ function insert_tip_box() {
|
|||
is_admin: current_user.is_admin,
|
||||
});
|
||||
$(".organization-box")
|
||||
.find(".settings-section")
|
||||
.find(".settings-section, .user-settings-section")
|
||||
.not("#emoji-settings")
|
||||
.not("#organization-auth-settings")
|
||||
.not("#admin-bot-list")
|
||||
.not("#admin-invites-list")
|
||||
.not("#admin-user-list")
|
||||
.prepend($(tip_box_html));
|
||||
}
|
||||
|
||||
|
@ -276,12 +277,15 @@ export function build_page() {
|
|||
}
|
||||
}
|
||||
|
||||
export function launch(section) {
|
||||
export function launch(section, user_settings_tab) {
|
||||
settings_sections.reset_sections();
|
||||
|
||||
settings.open_settings_overlay();
|
||||
if (section !== "") {
|
||||
settings_panel_menu.org_settings.set_current_tab(section);
|
||||
}
|
||||
if (section === "users") {
|
||||
settings_panel_menu.org_settings.set_user_settings_tab(user_settings_tab);
|
||||
}
|
||||
settings_toggle.goto("organization");
|
||||
}
|
||||
|
|
|
@ -72,6 +72,24 @@ function is_somebody_else_profile_open() {
|
|||
);
|
||||
}
|
||||
|
||||
function handle_invalid_users_section_url(user_settings_tab) {
|
||||
const valid_user_settings_tab_values = new Set(["active", "deactivated", "invitations"]);
|
||||
if (!valid_user_settings_tab_values.has(user_settings_tab)) {
|
||||
const valid_users_section_url = "#organization/users/active";
|
||||
browser_history.update(valid_users_section_url);
|
||||
return "active";
|
||||
}
|
||||
return user_settings_tab;
|
||||
}
|
||||
|
||||
function get_user_settings_tab(section) {
|
||||
if (section === "users") {
|
||||
const current_user_settings_tab = hash_parser.get_current_nth_hash_section(2);
|
||||
return handle_invalid_users_section_url(current_user_settings_tab);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function set_hash_to_home_view(triggered_by_escape_key = false) {
|
||||
const current_hash = window.location.hash;
|
||||
if (current_hash === "") {
|
||||
|
@ -352,7 +370,10 @@ function do_hashchange_overlay(old_hash) {
|
|||
// hand-typed a hash.
|
||||
blueslip.warn("missing section for organization");
|
||||
}
|
||||
settings_panel_menu.org_settings.activate_section_or_default(section);
|
||||
settings_panel_menu.org_settings.activate_section_or_default(
|
||||
section,
|
||||
get_user_settings_tab(section),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -373,6 +394,7 @@ function do_hashchange_overlay(old_hash) {
|
|||
settings_panel_menu.normal_settings.set_current_tab(section);
|
||||
} else {
|
||||
settings_panel_menu.org_settings.set_current_tab(section);
|
||||
settings_panel_menu.org_settings.set_user_settings_tab(get_user_settings_tab(section));
|
||||
}
|
||||
settings_toggle.goto(base);
|
||||
return;
|
||||
|
@ -440,7 +462,7 @@ function do_hashchange_overlay(old_hash) {
|
|||
if (base === "organization") {
|
||||
settings.build_page();
|
||||
admin.build_page();
|
||||
admin.launch(section);
|
||||
admin.launch(section, get_user_settings_tab(section));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -175,6 +175,10 @@ export function initialize() {
|
|||
show_uploaded_files_section: realm.max_file_upload_size_mib > 0,
|
||||
show_emoji_settings_lock: !settings_data.user_can_add_custom_emoji(),
|
||||
can_create_new_bots: settings_bots.can_create_new_bots(),
|
||||
can_edit_user_panel:
|
||||
current_user.is_admin ||
|
||||
settings_data.user_can_create_multiuse_invite() ||
|
||||
settings_data.user_can_invite_users_by_email(),
|
||||
});
|
||||
$("#settings_overlay_container").append($(rendered_settings_overlay));
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ import $ from "jquery";
|
|||
|
||||
import * as blueslip from "./blueslip";
|
||||
import * as browser_history from "./browser_history";
|
||||
import {$t_html} from "./i18n";
|
||||
import * as components from "./components";
|
||||
import {$t, $t_html} from "./i18n";
|
||||
import * as keydown_util from "./keydown_util";
|
||||
import * as popovers from "./popovers";
|
||||
import * as scroll_util from "./scroll_util";
|
||||
|
@ -50,12 +51,30 @@ export class SettingsPanelMenu {
|
|||
this.hash_prefix = opts.hash_prefix;
|
||||
this.$curr_li = this.$main_elem.children("li").eq(0);
|
||||
this.current_tab = this.$curr_li.data("section");
|
||||
this.current_user_settings_tab = "active";
|
||||
this.org_user_settings_toggler = components.toggle({
|
||||
html_class: "org-user-settings-switcher",
|
||||
child_wants_focus: true,
|
||||
values: [
|
||||
{label: $t({defaultMessage: "Users"}), key: "active"},
|
||||
{
|
||||
label: $t({defaultMessage: "Deactivated users"}),
|
||||
key: "deactivated",
|
||||
},
|
||||
{label: $t({defaultMessage: "Invitations"}), key: "invitations"},
|
||||
],
|
||||
callback: (_name, key) => {
|
||||
browser_history.update(`#organization/users/${key}`);
|
||||
this.set_user_settings_tab(key);
|
||||
$(".user-settings-section").hide();
|
||||
$(`[data-user-settings-section="${CSS.escape(key)}"]`).show();
|
||||
},
|
||||
});
|
||||
|
||||
this.$main_elem.on("click", "li[data-section]", (e) => {
|
||||
const section = $(e.currentTarget).attr("data-section");
|
||||
|
||||
this.activate_section_or_default(section);
|
||||
|
||||
this.activate_section_or_default(section, this.current_user_settings_tab);
|
||||
// You generally want to add logic to activate_section,
|
||||
// not to this click handler.
|
||||
|
||||
|
@ -66,13 +85,26 @@ export class SettingsPanelMenu {
|
|||
show() {
|
||||
this.$main_elem.show();
|
||||
const section = this.current_tab;
|
||||
const user_settings_tab = this.current_user_settings_tab;
|
||||
|
||||
if (two_column_mode()) {
|
||||
// In one column mode want to show the settings list, not the first settings section.
|
||||
this.activate_section_or_default(section);
|
||||
this.activate_section_or_default(section, user_settings_tab);
|
||||
}
|
||||
this.$curr_li.trigger("focus");
|
||||
}
|
||||
|
||||
show_org_user_settings_toggler() {
|
||||
if ($("#admin-user-list").find(".tab-switcher").length === 0) {
|
||||
const toggler_html = this.org_user_settings_toggler.get();
|
||||
$("#admin-user-list .tab-container").html(toggler_html);
|
||||
|
||||
// We need to re-register these handlers since they are
|
||||
// destroyed once the settings modal closes.
|
||||
this.org_user_settings_toggler.register_event_handlers();
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.$main_elem.hide();
|
||||
}
|
||||
|
@ -124,7 +156,11 @@ export class SettingsPanelMenu {
|
|||
this.current_tab = tab;
|
||||
}
|
||||
|
||||
activate_section_or_default(section) {
|
||||
set_user_settings_tab(tab) {
|
||||
this.current_user_settings_tab = tab;
|
||||
}
|
||||
|
||||
activate_section_or_default(section, user_settings_tab) {
|
||||
popovers.hide_all();
|
||||
if (!section) {
|
||||
// No section is given so we display the default.
|
||||
|
@ -153,10 +189,16 @@ export class SettingsPanelMenu {
|
|||
this.$curr_li.addClass("active");
|
||||
this.set_current_tab(section);
|
||||
|
||||
const settings_section_hash = "#" + this.hash_prefix + section;
|
||||
if (section !== "users") {
|
||||
const settings_section_hash = "#" + this.hash_prefix + section;
|
||||
|
||||
// It could be that the hash has already been set.
|
||||
browser_history.update_hash_internally_if_required(settings_section_hash);
|
||||
// It could be that the hash has already been set.
|
||||
browser_history.update_hash_internally_if_required(settings_section_hash);
|
||||
}
|
||||
if (section === "users" && this.org_user_settings_toggler !== undefined) {
|
||||
this.show_org_user_settings_toggler();
|
||||
this.org_user_settings_toggler.goto(user_settings_tab);
|
||||
}
|
||||
|
||||
$(".settings-section").removeClass("show");
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ export function get_group(section) {
|
|||
return "org_bots";
|
||||
|
||||
case "users":
|
||||
case "deactivated-users-admin":
|
||||
return "org_users";
|
||||
|
||||
case "profile":
|
||||
|
@ -70,7 +69,6 @@ export function initialize() {
|
|||
load_func_dict.set("default-channels-list", settings_streams.set_up);
|
||||
load_func_dict.set("linkifier-settings", settings_linkifiers.set_up);
|
||||
load_func_dict.set("playground-settings", settings_playgrounds.set_up);
|
||||
load_func_dict.set("invites-list-admin", settings_invites.set_up);
|
||||
load_func_dict.set("profile-field-settings", settings_profile_fields.set_up);
|
||||
load_func_dict.set("data-exports-admin", settings_exports.set_up);
|
||||
load_func_dict.set(
|
||||
|
|
|
@ -16,6 +16,7 @@ import * as scroll_util from "./scroll_util";
|
|||
import * as settings_bots from "./settings_bots";
|
||||
import * as settings_config from "./settings_config";
|
||||
import * as settings_data from "./settings_data";
|
||||
import * as setting_invites from "./settings_invites";
|
||||
import {current_user} from "./state_data";
|
||||
import * as timerender from "./timerender";
|
||||
import * as user_deactivation_ui from "./user_deactivation_ui";
|
||||
|
@ -329,7 +330,7 @@ section.active.create_table = (active_users) => {
|
|||
},
|
||||
onupdate: reset_scrollbar($users_table),
|
||||
},
|
||||
$parent_container: $("#admin-user-list").expectOne(),
|
||||
$parent_container: $("#admin-active-users-list").expectOne(),
|
||||
init_sort: "full_name_alphabetic",
|
||||
sort_fields: {
|
||||
email: user_sort.sort_email,
|
||||
|
@ -338,7 +339,7 @@ section.active.create_table = (active_users) => {
|
|||
id: user_sort.sort_user_id,
|
||||
...ListWidget.generic_sort_functions("alphabetic", ["full_name"]),
|
||||
},
|
||||
$simplebar_container: $("#admin-user-list .progressive-table-wrapper"),
|
||||
$simplebar_container: $("#admin-active-users-list .progressive-table-wrapper"),
|
||||
});
|
||||
|
||||
loading.destroy_indicator($("#admin_page_users_loading_indicator"));
|
||||
|
@ -561,6 +562,7 @@ export function set_up_humans() {
|
|||
start_data_load();
|
||||
section.active.handle_events();
|
||||
section.deactivated.handle_events();
|
||||
setting_invites.set_up();
|
||||
}
|
||||
|
||||
export function set_up_bots() {
|
||||
|
|
|
@ -385,7 +385,7 @@ select.settings_select {
|
|||
background-size: 14px;
|
||||
}
|
||||
|
||||
#admin-user-list,
|
||||
#admin-active-users-list,
|
||||
#admin-bot-list {
|
||||
.table tr:first-of-type td {
|
||||
border-top: none;
|
||||
|
@ -1582,7 +1582,7 @@ $option_title_width: 180px;
|
|||
}
|
||||
}
|
||||
|
||||
#admin-user-list .last_active {
|
||||
#admin-active-users-list .last_active {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
|
@ -2013,3 +2013,11 @@ $option_title_width: 180px;
|
|||
margin-top: -10px;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-switcher.org-user-settings-switcher {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#admin-user-list .tab-switcher .ind-tab {
|
||||
width: 110px;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<p id="invitation_admin_message">
|
||||
{{#tr}}
|
||||
You can reactivate deactivated users from <z-link>organization settings</z-link>.
|
||||
{{#*inline "z-link"}}<a href="#organization/deactivated-users-admin">{{> @partial-block}}</a>{{/inline}}
|
||||
{{#*inline "z-link"}}<a href="#organization/deactivated">{{> @partial-block}}</a>{{/inline}}
|
||||
{{/tr}}
|
||||
</p>
|
||||
{{else}}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
<div id="admin-active-users-list" class="user-settings-section" data-user-settings-section="active">
|
||||
|
||||
<div class="settings_panel_list_header">
|
||||
<h3>{{t "Users"}}</h3>
|
||||
<div class="alert-notification" id="user-field-status"></div>
|
||||
<div class="user_filters">
|
||||
{{> ../dropdown_widget widget_name=active_user_list_dropdown_widget_name}}
|
||||
<input type="text" class="search filter_text_input" placeholder="{{t 'Filter users' }}" aria-label="{{t 'Filter users' }}"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="progressive-table-wrapper" data-simplebar>
|
||||
<table class="table table-striped wrapped-table">
|
||||
<thead class="table-sticky-headers">
|
||||
<th class="active" data-sort="alphabetic" data-sort-prop="full_name">{{t "Name" }}</th>
|
||||
<th data-sort="email">{{t "Email" }}</th>
|
||||
<th class="user_role" data-sort="role">{{t "Role" }}</th>
|
||||
<th class="last_active" data-sort="last_active">{{t "Last active" }}</th>
|
||||
{{#if is_admin}}
|
||||
<th class="actions">{{t "Actions" }}</th>
|
||||
{{/if}}
|
||||
</thead>
|
||||
<tbody id="admin_users_table" class="admin_user_table"
|
||||
data-empty="{{t 'No users match your filters.' }}"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="admin_page_users_loading_indicator"></div>
|
||||
</div>
|
|
@ -15,8 +15,6 @@
|
|||
|
||||
{{> user_list_admin }}
|
||||
|
||||
{{> deactivated_users_admin }}
|
||||
|
||||
{{> bot_list_admin }}
|
||||
|
||||
{{> default_streams_list_admin }}
|
||||
|
@ -27,8 +25,6 @@
|
|||
|
||||
{{> playground_settings_admin }}
|
||||
|
||||
{{> invites_list_admin }}
|
||||
|
||||
{{> profile_field_settings_admin }}
|
||||
|
||||
{{> data_exports_admin }}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div id="admin-deactivated-users-list" class="settings-section" data-name="deactivated-users-admin">
|
||||
<div id="admin-deactivated-users-list" class="user-settings-section" data-user-settings-section="deactivated">
|
||||
<div class="clear-float"></div>
|
||||
|
||||
<div class="settings_panel_list_header">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div id="admin-invites-list" class="settings-section" data-name="invites-list-admin">
|
||||
<div id="admin-invites-list" class="user-settings-section" data-user-settings-section="invitations">
|
||||
<div class="tip invite-user-settings-tip"></div>
|
||||
{{#unless is_admin }}
|
||||
<div class="tip">{{t "You can only view or manage invitations that you sent." }}</div>
|
||||
|
|
|
@ -1,30 +1,11 @@
|
|||
<div id="admin-user-list" class="settings-section" data-name="users">
|
||||
|
||||
<div class="clear-float"></div>
|
||||
<div class="tab-container"></div>
|
||||
|
||||
<div class="settings_panel_list_header">
|
||||
<h3>{{t "Users"}}</h3>
|
||||
<div class="alert-notification" id="user-field-status"></div>
|
||||
<div class="user_filters">
|
||||
{{> ../dropdown_widget widget_name=active_user_list_dropdown_widget_name}}
|
||||
<input type="text" class="search filter_text_input" placeholder="{{t 'Filter users' }}" aria-label="{{t 'Filter users' }}"/>
|
||||
</div>
|
||||
</div>
|
||||
{{>active_user_list_admin}}
|
||||
|
||||
{{>deactivated_users_admin}}
|
||||
|
||||
{{>invites_list_admin}}
|
||||
|
||||
<div class="progressive-table-wrapper" data-simplebar>
|
||||
<table class="table table-striped wrapped-table">
|
||||
<thead class="table-sticky-headers">
|
||||
<th class="active" data-sort="alphabetic" data-sort-prop="full_name">{{t "Name" }}</th>
|
||||
<th data-sort="email">{{t "Email" }}</th>
|
||||
<th class="user_role" data-sort="role">{{t "Role" }}</th>
|
||||
<th class="last_active" data-sort="last_active">{{t "Last active" }}</th>
|
||||
{{#if is_admin}}
|
||||
<th class="actions">{{t "Actions" }}</th>
|
||||
{{/if}}
|
||||
</thead>
|
||||
<tbody id="admin_users_table" class="admin_user_table"
|
||||
data-empty="{{t 'No users match your filters.' }}"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div id="admin_page_users_loading_indicator"></div>
|
||||
</div>
|
||||
|
|
|
@ -89,14 +89,7 @@
|
|||
<li tabindex="0" data-section="users">
|
||||
<i class="icon fa fa-user" aria-hidden="true"></i>
|
||||
<div class="text">{{t "Users" }}</div>
|
||||
<i class="locked fa fa-lock tippy-zulip-tooltip" {{#if is_admin}}style="display: none;"{{/if}} data-tippy-content="{{t 'Only organization administrators can edit these settings.' }}"></i>
|
||||
</li>
|
||||
{{/unless}}
|
||||
{{#unless is_guest}}
|
||||
<li class="collapse-org-settings {{#unless is_admin}}hide-org-settings{{/unless}}" tabindex="0" data-section="deactivated-users-admin">
|
||||
<i class="icon fa fa-user-times" aria-hidden="true"></i>
|
||||
<div class="text">{{t "Deactivated users" }}</div>
|
||||
<i class="locked fa fa-lock tippy-zulip-tooltip" {{#if is_admin}}style="display: none;"{{/if}} data-tippy-content="{{t 'Only organization administrators can edit these settings.' }}"></i>
|
||||
<i class="locked fa fa-lock tippy-zulip-tooltip" {{#if can_edit_user_panel }}style="display: none;"{{/if}} data-tippy-content="{{t 'Only organization administrators can edit these settings.' }}"></i>
|
||||
</li>
|
||||
{{/unless}}
|
||||
{{#unless is_guest}}
|
||||
|
@ -106,12 +99,6 @@
|
|||
<i class="locked fa fa-lock tippy-zulip-tooltip" {{#if can_create_new_bots}}style="display: none;"{{/if}} data-tippy-content="{{t 'Only organization administrators can edit these settings.' }}"></i>
|
||||
</li>
|
||||
{{/unless}}
|
||||
{{#unless is_guest}}
|
||||
<li tabindex="0" data-section="invites-list-admin">
|
||||
<i class="icon fa fa-user-plus" aria-hidden="true"></i>
|
||||
<div class="text">{{t "Invitations" }}</div>
|
||||
</li>
|
||||
{{/unless}}
|
||||
{{#if is_admin}}
|
||||
<li tabindex="0" data-section="profile-field-settings">
|
||||
<i class="icon fa fa-id-card" aria-hidden="true"></i>
|
||||
|
|
|
@ -361,7 +361,7 @@ run_test("hash_interactions", ({override, override_rewire}) => {
|
|||
[settings, "launch"],
|
||||
]);
|
||||
|
||||
window.location.hash = "#organization/users";
|
||||
window.location.hash = "#organization/users/active";
|
||||
|
||||
helper.clear_events();
|
||||
$window_stub.trigger("hashchange");
|
||||
|
@ -369,7 +369,7 @@ run_test("hash_interactions", ({override, override_rewire}) => {
|
|||
[overlays, "close_for_hash_change"],
|
||||
[settings, "build_page"],
|
||||
[admin, "build_page"],
|
||||
[admin, "launch", ["users"]],
|
||||
[admin, "launch", ["users", "active"]],
|
||||
]);
|
||||
|
||||
window.location.hash = "#organization/user-list-admin";
|
||||
|
@ -384,7 +384,7 @@ run_test("hash_interactions", ({override, override_rewire}) => {
|
|||
[overlays, "close_for_hash_change"],
|
||||
[settings, "build_page"],
|
||||
[admin, "build_page"],
|
||||
[admin, "launch", ["users"]],
|
||||
[admin, "launch", ["users", "active"]],
|
||||
]);
|
||||
|
||||
helper.clear_events();
|
||||
|
|
|
@ -58,11 +58,15 @@ link_mapping = {
|
|||
"Authentication methods",
|
||||
"/#organization/auth-methods",
|
||||
],
|
||||
"users": ["Organization settings", "Users", "/#organization/users"],
|
||||
"deactivated-users-admin": [
|
||||
"users": [
|
||||
"Organization settings",
|
||||
"Users",
|
||||
"/#organization/users/active",
|
||||
],
|
||||
"deactivated": [
|
||||
"Organization settings",
|
||||
"Deactivated users",
|
||||
"/#organization/deactivated-users-admin",
|
||||
"/#organization/users/deactivated",
|
||||
],
|
||||
"bot-list-admin": [
|
||||
"Organization settings",
|
||||
|
@ -89,10 +93,10 @@ link_mapping = {
|
|||
"Custom profile fields",
|
||||
"/#organization/profile-field-settings",
|
||||
],
|
||||
"invites-list-admin": [
|
||||
"invitations": [
|
||||
"Organization settings",
|
||||
"Invitations",
|
||||
"/#organization/invites-list-admin",
|
||||
"/#organization/users/invitations",
|
||||
],
|
||||
"data-exports-admin": [
|
||||
"Organization settings",
|
||||
|
|
Loading…
Reference in New Issue