Krzysztof Kotlarek e0d9232259
FIX: use allowlist and blocklist terminology (#10209)
This is a PR of the renaming whitelist to allowlist and blacklist to the blocklist.
2020-07-27 10:23:54 +10:00

616 lines
15 KiB
JavaScript

import getURL from "discourse-common/lib/get-url";
import I18n from "I18n";
import { get } from "@ember/object";
import { schedule } from "@ember/runloop";
import { createWidget } from "discourse/widgets/widget";
import { iconNode } from "discourse-common/lib/icon-library";
import { avatarImg } from "discourse/widgets/post";
import DiscourseURL from "discourse/lib/url";
import { wantsNewWindow } from "discourse/lib/intercept-click";
import { applySearchAutocomplete } from "discourse/lib/search";
import { ajax } from "discourse/lib/ajax";
import { addExtraUserClasses } from "discourse/helpers/user-avatar";
import { scrollTop } from "discourse/mixins/scroll-top";
import { h } from "virtual-dom";
const _extraHeaderIcons = [];
export function addToHeaderIcons(icon) {
_extraHeaderIcons.push(icon);
}
const dropdown = {
buildClasses(attrs) {
let classes = attrs.classNames || [];
if (attrs.active) {
classes.push("active");
}
return classes;
},
click(e) {
if (wantsNewWindow(e)) {
return;
}
e.preventDefault();
if (!this.attrs.active) {
this.sendWidgetAction(this.attrs.action);
}
}
};
createWidget("header-notifications", {
settings: {
avatarSize: "medium"
},
html(attrs) {
const { user } = attrs;
let avatarAttrs = {
template: user.get("avatar_template"),
username: user.get("username")
};
if (this.siteSettings.enable_names) {
avatarAttrs.name = user.get("name");
}
const contents = [
avatarImg(
this.settings.avatarSize,
addExtraUserClasses(user, avatarAttrs)
)
];
const unreadNotifications = user.get("unread_notifications");
if (!!unreadNotifications) {
contents.push(
this.attach("link", {
action: attrs.action,
className: "badge-notification unread-notifications",
rawLabel: unreadNotifications,
omitSpan: true,
title: "notifications.tooltip.regular",
titleOptions: { count: unreadNotifications }
})
);
}
const unreadHighPriority = user.get("unread_high_priority_notifications");
if (!!unreadHighPriority) {
// highlight the avatar if the first ever PM is not read
if (
!user.get("read_first_notification") &&
!user.get("enforcedSecondFactor")
) {
contents.push(h("span.ring"));
if (!attrs.active && attrs.ringBackdrop) {
contents.push(h("span.ring-backdrop-spotlight"));
contents.push(
h(
"span.ring-backdrop",
{},
h(
"h1.ring-first-notification",
{},
I18n.t("user.first_notification")
)
)
);
}
}
// add the counter for the unread high priority
contents.push(
this.attach("link", {
action: attrs.action,
className: "badge-notification unread-high-priority-notifications",
rawLabel: unreadHighPriority,
omitSpan: true,
title: "notifications.tooltip.high_priority",
titleOptions: { count: unreadHighPriority }
})
);
}
return contents;
}
});
createWidget(
"user-dropdown",
jQuery.extend(
{
tagName: "li.header-dropdown-toggle.current-user",
buildId() {
return "current-user";
},
html(attrs) {
return h(
"a.icon",
{
attributes: {
href: attrs.user.get("path"),
title: attrs.user.get("name"),
"data-auto-route": true
}
},
this.attach("header-notifications", attrs)
);
}
},
dropdown
)
);
createWidget(
"header-dropdown",
jQuery.extend(
{
tagName: "li.header-dropdown-toggle",
html(attrs) {
const title = I18n.t(attrs.title);
const body = [iconNode(attrs.icon)];
if (attrs.contents) {
body.push(attrs.contents.call(this));
}
return h(
"a.icon.btn-flat",
{
attributes: {
href: attrs.href,
"data-auto-route": true,
title,
"aria-label": title,
id: attrs.iconId
}
},
body
);
}
},
dropdown
)
);
createWidget("header-icons", {
tagName: "ul.icons.d-header-icons",
buildAttributes() {
return { role: "navigation" };
},
html(attrs) {
if (this.siteSettings.login_required && !this.currentUser) {
return [];
}
const icons = [];
if (_extraHeaderIcons) {
_extraHeaderIcons.forEach(icon => {
icons.push(this.attach(icon));
});
}
const search = this.attach("header-dropdown", {
title: "search.title",
icon: "search",
iconId: "search-button",
action: "toggleSearchMenu",
active: attrs.searchVisible,
href: getURL("/search"),
classNames: ["search-dropdown"]
});
icons.push(search);
const hamburger = this.attach("header-dropdown", {
title: "hamburger_menu",
icon: "bars",
iconId: "toggle-hamburger-menu",
active: attrs.hamburgerVisible,
action: "toggleHamburger",
href: "",
classNames: ["hamburger-dropdown"],
contents() {
let { currentUser } = this;
if (currentUser && currentUser.reviewable_count) {
return h(
"div.badge-notification.reviewables",
{
attributes: {
title: I18n.t("notifications.reviewable_items")
}
},
this.currentUser.reviewable_count
);
}
}
});
icons.push(hamburger);
if (attrs.user) {
icons.push(
this.attach("user-dropdown", {
active: attrs.userVisible,
action: "toggleUserMenu",
ringBackdrop: attrs.ringBackdrop,
user: attrs.user
})
);
}
return icons;
}
});
createWidget("header-buttons", {
tagName: "span.header-buttons",
html(attrs) {
if (this.currentUser) {
return;
}
const buttons = [];
if (attrs.canSignUp && !attrs.topic) {
buttons.push(
this.attach("button", {
label: "sign_up",
className: "btn-primary btn-small sign-up-button",
action: "showCreateAccount"
})
);
}
buttons.push(
this.attach("button", {
label: "log_in",
className: "btn-primary btn-small login-button",
action: "showLogin",
icon: "user"
})
);
return buttons;
}
});
createWidget("header-cloak", {
tagName: "div.header-cloak",
html() {
return "";
},
click() {},
scheduleRerender() {}
});
const forceContextEnabled = ["category", "user", "private_messages", "tag"];
let additionalPanels = [];
export function attachAdditionalPanel(name, toggle, transformAttrs) {
additionalPanels.push({ name, toggle, transformAttrs });
}
export default createWidget("header", {
tagName: "header.d-header.clearfix",
buildKey: () => `header`,
defaultState() {
let states = {
searchVisible: false,
hamburgerVisible: false,
userVisible: false,
ringBackdrop: true
};
if (this.site.mobileView) {
states.skipSearchContext = true;
}
return states;
},
html(attrs, state) {
let contents = () => {
const headerIcons = this.attach("header-icons", {
hamburgerVisible: state.hamburgerVisible,
userVisible: state.userVisible,
searchVisible: state.searchVisible,
ringBackdrop: state.ringBackdrop,
flagCount: attrs.flagCount,
user: this.currentUser
});
if (attrs.onlyIcons) {
return headerIcons;
}
const panels = [this.attach("header-buttons", attrs), headerIcons];
if (state.searchVisible) {
const contextType = this.searchContextType();
if (state.searchContextType !== contextType) {
state.contextEnabled = undefined;
state.searchContextType = contextType;
}
if (state.contextEnabled === undefined) {
if (forceContextEnabled.includes(contextType)) {
state.contextEnabled = true;
}
}
panels.push(
this.attach("search-menu", { contextEnabled: state.contextEnabled })
);
} else if (state.hamburgerVisible) {
panels.push(this.attach("hamburger-menu"));
} else if (state.userVisible) {
panels.push(this.attach("user-menu"));
}
additionalPanels.map(panel => {
if (this.state[panel.toggle]) {
panels.push(
this.attach(
panel.name,
panel.transformAttrs.call(this, attrs, state)
)
);
}
});
if (this.site.mobileView) {
panels.push(this.attach("header-cloak"));
}
return panels;
};
let contentsAttrs = { contents, minimized: !!attrs.topic };
return h(
"div.wrap",
this.attach("header-contents", $.extend({}, attrs, contentsAttrs))
);
},
updateHighlight() {
if (!this.state.searchVisible) {
const service = this.register.lookup("search-service:main");
service.set("highlightTerm", "");
}
},
closeAll() {
this.state.userVisible = false;
this.state.hamburgerVisible = false;
this.state.searchVisible = false;
this.toggleBodyScrolling(false);
},
linkClickedEvent(attrs) {
let searchContextEnabled = false;
if (attrs) {
searchContextEnabled = attrs.searchContextEnabled;
const { searchLogId, searchResultId, searchResultType } = attrs;
if (searchLogId && searchResultId && searchResultType) {
ajax("/search/click", {
type: "POST",
data: {
search_log_id: searchLogId,
search_result_id: searchResultId,
search_result_type: searchResultType
}
});
}
}
if (!searchContextEnabled) {
this.closeAll();
}
this.updateHighlight();
},
toggleSearchMenu() {
if (this.site.mobileView) {
const searchService = this.register.lookup("search-service:main");
const context = searchService.get("searchContext");
var params = "";
if (context) {
params = `?context=${context.type}&context_id=${context.id}&skip_context=${this.state.skipSearchContext}`;
}
const currentPath = this.register
.lookup("service:router")
.get("_router.currentPath");
if (currentPath === "full-page-search") {
scrollTop();
$(".full-page-search").focus();
return false;
} else {
return DiscourseURL.routeTo("/search" + params);
}
}
this.state.searchVisible = !this.state.searchVisible;
this.updateHighlight();
if (this.state.searchVisible) {
schedule("afterRender", () => {
const $searchInput = $("#search-term");
$searchInput.focus().select();
applySearchAutocomplete(
$searchInput,
this.siteSettings,
this.appEvents,
{
appendSelector: ".menu-panel"
}
);
});
}
},
toggleUserMenu() {
if (this.currentUser.get("read_first_notification")) {
this.state.ringBackdrop = false;
}
this.state.userVisible = !this.state.userVisible;
this.toggleBodyScrolling(this.state.userVisible);
},
toggleHamburger() {
this.state.hamburgerVisible = !this.state.hamburgerVisible;
this.toggleBodyScrolling(this.state.hamburgerVisible);
},
toggleBodyScrolling(bool) {
if (!this.site.mobileView) return;
if (bool) {
document.body.addEventListener("touchmove", this.preventDefault, {
passive: false
});
} else {
document.body.removeEventListener("touchmove", this.preventDefault, {
passive: false
});
}
},
preventDefault(e) {
// prevent all scrollin on menu panels, except on overflow
const height = window.innerHeight ? window.innerHeight : $(window).height();
if (
!$(e.target).parents(".menu-panel").length ||
$(".menu-panel .panel-body-contents").height() <= height
) {
e.preventDefault();
}
},
togglePageSearch() {
const { state } = this;
state.contextEnabled = false;
const currentPath = this.register
.lookup("service:router")
.get("_router.currentPath");
const blocklist = [/^discovery\.categories/];
const allowlist = [/^topic\./];
const check = function(regex) {
return !!currentPath.match(regex);
};
let showSearch = allowlist.any(check) && !blocklist.any(check);
// If we're viewing a topic, only intercept search if there are cloaked posts
if (showSearch && currentPath.match(/^topic\./)) {
const controller = this.register.lookup("controller:topic");
const total = controller.get("model.postStream.stream.length") || 0;
const chunkSize = controller.get("model.chunk_size") || 0;
showSearch =
total > chunkSize &&
$(".topic-post .cooked, .small-action:not(.time-gap)").length < total;
}
if (state.searchVisible) {
this.toggleSearchMenu();
return showSearch;
}
if (showSearch) {
state.contextEnabled = true;
this.toggleSearchMenu();
return false;
}
return true;
},
searchMenuContextChanged(value) {
this.state.contextType = this.register
.lookup("search-service:main")
.get("contextType");
this.state.contextEnabled = value;
},
domClean() {
const { state } = this;
if (state.searchVisible || state.hamburgerVisible || state.userVisible) {
this.closeAll();
}
},
headerDismissFirstNotificationMask() {
// Dismiss notifications
this.store
.findStale(
"notification",
{
recent: true,
silent: this.get("currentUser.enforcedSecondFactor"),
limit: 5
},
{ cacheKey: "recent-notifications" }
)
.refresh();
// Update UI
this.state.ringBackdrop = false;
this.scheduleRerender();
},
headerKeyboardTrigger(msg) {
switch (msg.type) {
case "search":
this.toggleSearchMenu();
break;
case "user":
this.toggleUserMenu();
break;
case "hamburger":
this.toggleHamburger();
break;
case "page-search":
let contextType = this.searchContextType();
if (contextType === "topic") {
this.state.searchContextType = contextType;
}
if (!this.togglePageSearch()) {
msg.event.preventDefault();
msg.event.stopPropagation();
}
break;
}
},
searchContextType() {
const service = this.register.lookup("search-service:main");
if (service) {
const ctx = service.get("searchContext");
if (ctx) {
return get(ctx, "type");
}
}
}
});