[CORL-681] Configurable Staff Badges (#2620)

* feat: added configurable staff badge

* fix: removed ambigous styles
This commit is contained in:
Wyatt Johnson
2019-10-04 20:22:31 +00:00
committed by GitHub
parent e7ab0b27f9
commit 918ba10867
32 changed files with 428 additions and 44 deletions
@@ -11,6 +11,7 @@ import GuidelinesConfigContainer from "./GuidelinesConfigContainer";
import LocaleConfigContainer from "./LocaleConfigContainer";
import ReactionConfigContainer from "./ReactionConfigContainer";
import SitewideCommentingConfigContainer from "./SitewideCommentingConfigContainer";
import StaffConfigContainer from "./StaffConfigContainer";
interface Props {
disabled: boolean;
@@ -19,6 +20,7 @@ interface Props {
PropTypesOf<typeof CommentEditingConfigContainer>["settings"] &
PropTypesOf<typeof ClosedStreamMessageConfigContainer>["settings"] &
PropTypesOf<typeof ReactionConfigContainer>["settings"] &
PropTypesOf<typeof StaffConfigContainer>["settings"] &
PropTypesOf<typeof ClosingCommentStreamsConfigContainer>["settings"] &
PropTypesOf<typeof SitewideCommentingConfigContainer>["settings"] &
PropTypesOf<typeof LocaleConfigContainer>["settings"];
@@ -71,6 +73,11 @@ const General: FunctionComponent<Props> = ({
settings={settings}
onInitValues={onInitValues}
/>
<StaffConfigContainer
disabled={disabled}
settings={settings}
onInitValues={onInitValues}
/>
</HorizontalGutter>
);
@@ -53,6 +53,7 @@ const enhanced = withFragmentContainer<Props>({
...ClosingCommentStreamsConfigContainer_settings
...SitewideCommentingConfigContainer_settings
...ReactionConfigContainer_settings
...StaffConfigContainer_settings
}
`,
})(GeneralConfigContainer);
@@ -0,0 +1,3 @@
.textInput {
min-width: 300px;
}
@@ -0,0 +1,78 @@
import { Localized } from "fluent-react/compat";
import React, { FunctionComponent } from "react";
import { Field } from "react-final-form";
import { StaffConfigContainer_settings as SettingsData } from "coral-admin/__generated__/StaffConfigContainer_settings.graphql";
import { required } from "coral-framework/lib/validation";
import {
FieldSet,
Flex,
FormField,
HorizontalGutter,
InputLabel,
Tag,
TextField,
Typography,
} from "coral-ui/components";
import Header from "../../Header";
import SectionContent from "../../SectionContent";
import ValidationMessage from "../../ValidationMessage";
import styles from "./StaffConfig.css";
interface Props {
disabled: boolean;
settings: SettingsData;
}
const StaffConfig: FunctionComponent<Props> = ({ disabled, settings }) => (
<HorizontalGutter size="oneAndAHalf" container={<FieldSet />}>
<Localized id="configure-general-staff-title">
<Header container="legend">Staff member badge</Header>
</Localized>
<SectionContent>
<Localized id="configure-general-staff-explanation">
<Typography variant="bodyShort">
Show a custom badge for staff members of your organization. This badge
appears on the comment stream and in the admin interface.
</Typography>
</Localized>
<HorizontalGutter size="double">
<FormField>
<Field name="staff.label" validate={required}>
{({ input, meta }) => (
<Flex itemGutter="double">
<HorizontalGutter>
<Localized id="configure-general-staff-label">
<InputLabel>Badge text</InputLabel>
</Localized>
<Localized id="configure-general-staff-input">
<TextField
className={styles.textInput}
id={input.name}
type="text"
fullWidth
placeholder="E.g. Staff"
disabled={disabled}
{...input}
/>
</Localized>
<ValidationMessage fullWidth meta={meta} />
</HorizontalGutter>
<HorizontalGutter>
<Localized id="configure-general-staff-preview">
<Typography variant="heading3">Preview</Typography>
</Localized>
{input.value && <Tag>{input.value}</Tag>}
</HorizontalGutter>
</Flex>
)}
</Field>
</FormField>
</HorizontalGutter>
</SectionContent>
</HorizontalGutter>
);
export default StaffConfig;
@@ -0,0 +1,37 @@
import React from "react";
import { graphql } from "react-relay";
import { StaffConfigContainer_settings as SettingsData } from "coral-admin/__generated__/StaffConfigContainer_settings.graphql";
import { withFragmentContainer } from "coral-framework/lib/relay";
import StaffConfig from "./StaffConfig";
interface Props {
settings: SettingsData;
onInitValues: (values: SettingsData) => void;
disabled: boolean;
}
class StaffConfigContainer extends React.Component<Props> {
constructor(props: Props) {
super(props);
props.onInitValues(props.settings);
}
public render() {
const { disabled, settings } = this.props;
return <StaffConfig settings={settings} disabled={disabled} />;
}
}
const enhanced = withFragmentContainer<Props>({
settings: graphql`
fragment StaffConfigContainer_settings on Settings {
staff {
label
}
}
`,
})(StaffConfigContainer);
export default enhanced;
@@ -1063,6 +1063,76 @@ each other's comments.
</div>
</div>
</fieldset>
<fieldset
className="FieldSet-root Box-root HorizontalGutter-root HorizontalGutter-oneAndAHalf"
>
<legend
className="Box-root Typography-root Typography-heading3 Typography-colorTextPrimary Header-root"
>
Staff member badge
</legend>
<div
className="Box-root HorizontalGutter-root SectionContent-sectionContent HorizontalGutter-double"
>
<p
className="Box-root Typography-root Typography-bodyShort Typography-colorTextPrimary"
>
Show a custom badge for staff members of your organization. This badge
appears on the comment stream and in the admin interface.
</p>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-double"
>
<div
className="Box-root HorizontalGutter-root FormField-root HorizontalGutter-half"
>
<div
className="Box-root Flex-root Flex-flex Flex-doubleItemGutter gutter"
>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<label
className="Box-root Typography-root Typography-inputLabel Typography-colorTextPrimary InputLabel-root"
>
Badge text
</label>
<div
className="TextField-root TextField-fullWidth StaffConfig-textInput"
>
<input
className="TextField-input TextField-colorRegular"
disabled={false}
id="staff.label"
name="staff.label"
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
placeholder="E.g. Staff"
type="text"
value="Staff"
/>
</div>
</div>
<div
className="Box-root HorizontalGutter-root HorizontalGutter-full"
>
<h1
className="Box-root Typography-root Typography-heading3 Typography-colorTextPrimary"
>
Preview
</h1>
<span
className="Tag-root Tag-colorGrey Tag-uppercase"
>
Staff
</span>
</div>
</div>
</div>
</div>
</div>
</fieldset>
</div>
</div>
</div>
+3
View File
@@ -50,6 +50,9 @@ export const settings = createFixture<GQLSettings>({
timeout: 604800,
message: "Comments are closed on this story.",
},
staff: {
label: "Staff",
},
reaction: {
label: "Reaction",
labelActive: "reacted",
@@ -253,6 +253,7 @@ export class CommentContainer extends Component<Props, State> {
<UserTagsContainer
className={CLASSES.comment.topBar.userTag}
comment={comment}
settings={settings}
/>
<UserBadgesContainer
className={CLASSES.comment.topBar.userBadge}
@@ -445,6 +446,7 @@ const enhanced = withSetCommentIDMutation(
...ReactionButtonContainer_settings
...ReplyCommentFormContainer_settings
...EditCommentFormContainer_settings
...UserTagsContainer_settings
}
`,
})(CommentContainer)
@@ -1,27 +1,25 @@
import { Localized } from "fluent-react/compat";
import React, { FunctionComponent } from "react";
import { graphql } from "react-relay";
import withFragmentContainer from "coral-framework/lib/relay/withFragmentContainer";
import { UserTagsContainer_comment as CommentData } from "coral-stream/__generated__/UserTagsContainer_comment.graphql";
import { UserTagsContainer_comment } from "coral-stream/__generated__/UserTagsContainer_comment.graphql";
import { UserTagsContainer_settings } from "coral-stream/__generated__/UserTagsContainer_settings.graphql";
import { Tag } from "coral-ui/components";
interface Props {
comment: CommentData;
comment: UserTagsContainer_comment;
settings: UserTagsContainer_settings;
className?: string;
}
const UserTagsContainer: FunctionComponent<Props> = props => {
const { comment } = props;
const UserTagsContainer: FunctionComponent<Props> = ({
settings,
comment,
className,
}) => {
const staffTag = comment.tags.find(t => t.code === "STAFF");
return (
<>
{staffTag && (
<Localized id="comments-staffTag">
<Tag className={props.className}>Staff</Tag>
</Localized>
)}
</>
<>{staffTag && <Tag className={className}>{settings.staff.label}</Tag>}</>
);
};
@@ -33,6 +31,13 @@ const enhanced = withFragmentContainer<Props>({
}
}
`,
settings: graphql`
fragment UserTagsContainer_settings on Settings {
staff {
label
}
}
`,
})(UserTagsContainer);
export default enhanced;
@@ -143,6 +143,13 @@ exports[`hide reply button 1`] = `
"tags": Array [],
}
}
settings={
Object {
"disableCommenting": Object {
"enabled": false,
},
}
}
/>
<Relay(AuthorBadgesContainer)
className="coral coral-userBadge coral-comment-userBadge"
@@ -326,6 +333,13 @@ exports[`renders body only 1`] = `
"tags": Array [],
}
}
settings={
Object {
"disableCommenting": Object {
"enabled": false,
},
}
}
/>
<Relay(AuthorBadgesContainer)
className="coral coral-userBadge coral-comment-userBadge"
@@ -509,6 +523,13 @@ exports[`renders disabled reply when commenting has been disabled 1`] = `
"tags": Array [],
}
}
settings={
Object {
"disableCommenting": Object {
"enabled": true,
},
}
}
/>
<Relay(AuthorBadgesContainer)
className="coral coral-userBadge coral-comment-userBadge"
@@ -692,6 +713,13 @@ exports[`renders disabled reply when story is closed 1`] = `
"tags": Array [],
}
}
settings={
Object {
"disableCommenting": Object {
"enabled": false,
},
}
}
/>
<Relay(AuthorBadgesContainer)
className="coral coral-userBadge coral-comment-userBadge"
@@ -887,6 +915,13 @@ exports[`renders in reply to 1`] = `
"tags": Array [],
}
}
settings={
Object {
"disableCommenting": Object {
"enabled": false,
},
}
}
/>
<Relay(AuthorBadgesContainer)
className="coral coral-userBadge coral-comment-userBadge"
@@ -1074,6 +1109,13 @@ exports[`renders username and body 1`] = `
"tags": Array [],
}
}
settings={
Object {
"disableCommenting": Object {
"enabled": false,
},
}
}
/>
<Relay(AuthorBadgesContainer)
className="coral coral-userBadge coral-comment-userBadge"
@@ -1272,6 +1314,13 @@ exports[`shows conversation link 1`] = `
"tags": Array [],
}
}
settings={
Object {
"disableCommenting": Object {
"enabled": false,
},
}
}
/>
<Relay(AuthorBadgesContainer)
className="coral coral-userBadge coral-comment-userBadge"
@@ -100,6 +100,7 @@ class ConversationThreadContainer extends React.Component<
<UserTagsContainer
className={CLASSES.conversationThread.rootParent.userTag}
comment={rootParent}
settings={settings}
/>
}
/>
@@ -192,6 +193,7 @@ const enhanced = withContext(ctx => ({
fragment ConversationThreadContainer_settings on Settings {
...CommentContainer_settings
...LocalReplyListContainer_settings
...UserTagsContainer_settings
}
`,
comment: graphql`
@@ -71,6 +71,7 @@ const FeaturedCommentContainer: FunctionComponent<Props> = props => {
<UserTagsContainer
className={CLASSES.featuredComment.authorBar.userTag}
comment={comment}
settings={settings}
/>
</Box>
<Box ml={2} clone>
@@ -174,6 +175,7 @@ const enhanced = withSetCommentIDMutation(
settings: graphql`
fragment FeaturedCommentContainer_settings on Settings {
...ReactionButtonContainer_settings
...UserTagsContainer_settings
}
`,
})(FeaturedCommentContainer)
+3
View File
@@ -88,6 +88,9 @@ export const settings = createFixture<GQLSettings>({
},
},
},
staff: {
label: "Staff",
},
reaction: {
icon: "thumb_up",
label: "Respect",
@@ -1085,16 +1085,29 @@ type CommenterAccountFeatures {
changeUsername when true, non-sso user may change username every 14 days
"""
changeUsername: Boolean!
"""
downloadComments when true, user may download their comment history
"""
downloadComments: Boolean!
"""
deleteAccount when true, non-sso user may permanently delete their account
"""
deleteAccount: Boolean!
}
"""
StaffConfiguration specifies the configuration for the staff badges assigned to
users with any role above COMMENTER.
"""
type StaffConfiguration {
"""
label is the string used when displaying the STAFF badge to users.
"""
label: String!
}
"""
Settings stores the global settings for a given Tenant.
"""
@@ -1210,6 +1223,12 @@ type Settings {
"""
reaction: ReactionConfiguration!
"""
staff specifies the configuration for the staff badges assigned to users with
any role above COMMENTER.
"""
staff: StaffConfiguration!
"""
accountFeatures stores the configuration for commenter account features.
"""
@@ -3280,8 +3299,8 @@ input StoryConfigurationInput {
}
"""
ReactionConfigurationInput stores the configuration for reactions used across this
Tenant.
ReactionConfigurationInput stores the configuration for reactions used across
this Tenant.
"""
input ReactionConfigurationInput {
"""
@@ -3318,6 +3337,17 @@ input ReactionConfigurationInput {
color: String
}
"""
StaffConfigurationInput specifies the configuration for the staff badges
assigned to users with any role above COMMENTER.
"""
input StaffConfigurationInput {
"""
label is the string used when displaying the STAFF badge to users.
"""
label: String
}
"""
CommenterAccountFeatures stores the configuration for commenter account features.
"""
@@ -3435,6 +3465,12 @@ input SettingsInput {
"""
reaction: ReactionConfigurationInput
"""
staff specifies the configuration for the staff badges assigned to users with
any role above COMMENTER.
"""
staff: StaffConfigurationInput
"""
accountFeatures specifies the configuration for accounts.
"""
+5 -1
View File
@@ -158,7 +158,10 @@ class Server {
);
// Create the migration manager.
this.migrationManager = new MigrationManager(this.tenantCache);
this.migrationManager = new MigrationManager({
tenantCache: this.tenantCache,
i18n: this.i18n,
});
// Load and upsert the persisted queries.
this.persistedQueryCache = new PersistedQueryCache({ mongo: this.mongo });
@@ -198,6 +201,7 @@ class Server {
// Run migrations if there is already a Tenant installed.
if (await isInstalled(this.tenantCache)) {
await this.migrationManager.executePendingMigrations(this.mongo);
await this.tenantCache.primeAll();
} else {
logger.info("no tenants are installed, skipping running migrations");
}
+1
View File
@@ -0,0 +1 @@
staff-label = Personale
+2
View File
@@ -11,3 +11,5 @@ comment-count =
[one] Comment
*[other] Comments
}</span>
staff-label = Staff
+1
View File
@@ -0,0 +1 @@
staff-label = Miembro del equipo
+1
View File
@@ -0,0 +1 @@
staff-label = Staf
+1
View File
@@ -4,3 +4,4 @@ disableCommentingDefaultMessage = Comentários foram fechados nessa história.
reaction-labelRespect = Respeitar
reaction-labelActiveRespected = Respeitado
reaction-sortLabelMostRespected = Respeitados
staff-label = Staff
+1
View File
@@ -94,6 +94,7 @@ export type Settings = GlobalModerationSettings &
| "wordList"
| "integrations"
| "reaction"
| "staff"
| "editCommentWindowLength"
| "customCSSURL"
| "communityGuidelines"
+10 -1
View File
@@ -1,7 +1,10 @@
import crypto from "crypto";
import { FluentBundle } from "fluent/compat";
import { GQLReactionConfiguration } from "coral-server/graph/tenant/schema/__generated__/types";
import {
GQLReactionConfiguration,
GQLStaffConfiguration,
} from "coral-server/graph/tenant/schema/__generated__/types";
import { translate } from "coral-server/services/i18n";
export const getDefaultReactionConfiguration = (
@@ -19,6 +22,12 @@ export const getDefaultReactionConfiguration = (
icon: "thumb_up",
});
export const getDefaultStaffConfiguration = (
bundle: FluentBundle
): GQLStaffConfiguration => ({
label: translate(bundle, "Staff", "staff-label"),
});
export function generateSSOKey() {
// Generate a new key. We generate a key of minimum length 32 up to 37 bytes,
// as 16 was the minimum length recommended.
+10 -2
View File
@@ -13,7 +13,11 @@ import { Settings } from "coral-server/models/settings";
import { I18n } from "coral-server/services/i18n";
import { tenants as collection } from "coral-server/services/mongodb/collections";
import { generateSSOKey, getDefaultReactionConfiguration } from "./helpers";
import {
generateSSOKey,
getDefaultReactionConfiguration,
getDefaultStaffConfiguration,
} from "./helpers";
/**
* TenantResource references a given resource that should be owned by a specific
@@ -64,6 +68,9 @@ export async function createTenant(
input: CreateTenantInput,
now = new Date()
) {
// Get the tenant's bundle.
const bundle = i18n.getBundle(input.locale);
const defaults: Sub<Tenant, CreateTenantInput> = {
// Create a new ID.
id: uuid.v4(),
@@ -168,7 +175,8 @@ export async function createTenant(
doNotStore: true,
},
},
reaction: getDefaultReactionConfiguration(i18n.getBundle(input.locale)),
reaction: getDefaultReactionConfiguration(bundle),
staff: getDefaultStaffConfiguration(bundle),
stories: {
scraping: {
enabled: true,
+12 -5
View File
@@ -12,6 +12,7 @@ import {
retrieveAllMigrationRecords,
startMigration,
} from "coral-server/models/migration";
import { I18n } from "coral-server/services/i18n";
import TenantCache from "coral-server/services/tenant/cache";
import {
@@ -23,16 +24,21 @@ import Migration from "./migration";
// Extract the id from the filename with this regex.
const fileNamePattern = /^(\d+)_([\S_]+)\.[tj]s$/;
export interface ManagerOptions {
tenantCache: TenantCache;
i18n: I18n;
}
export default class Manager {
private clientID: string;
private migrations: Migration[];
private tenants: TenantCache;
private tenantCache: TenantCache;
private ran: boolean = false;
constructor(tenants: TenantCache) {
constructor({ tenantCache, i18n }: ManagerOptions) {
this.clientID = uuid.v4();
this.migrations = [];
this.tenants = tenants;
this.tenantCache = tenantCache;
const fileNames = fs.readdirSync(path.join(__dirname, "migrations"));
for (const fileName of fileNames) {
@@ -60,9 +66,10 @@ export default class Manager {
throw new Error("fileName format is invalid");
}
const id = parseInt(matches[1], 10);
const name = matches[2];
// Create the migration instance.
const migration = new m.default(id, matches[2]);
const migration = new m.default({ id, name, i18n });
// Insert the migration into the migrations array.
if (!(migration instanceof Migration)) {
@@ -179,7 +186,7 @@ export default class Manager {
if (migration.up) {
// The migration provides an up method, we should run this per Tenant.
for await (const tenant of this.tenants) {
for await (const tenant of this.tenantCache) {
log = log.child({ tenantID: tenant.id }, true);
const migrationStartTime = now();
+12 -3
View File
@@ -2,6 +2,7 @@ import Logger from "bunyan";
import { Db } from "mongodb";
import logger from "coral-server/logger";
import { I18n } from "coral-server/services/i18n";
interface Migration {
indexes?(mongo: Db): Promise<void>;
@@ -10,18 +11,26 @@ interface Migration {
down?(mongo: Db, tenantID: string): Promise<void>;
}
export interface MigrationOptions {
id: number;
name: string;
i18n: I18n;
}
abstract class Migration {
public readonly name: string;
public readonly id: number;
public readonly logger: Logger;
public readonly i18n: I18n;
constructor(version: number, name: string) {
this.id = version;
constructor({ id, name, i18n }: MigrationOptions) {
this.id = id;
this.name = name;
this.i18n = i18n;
this.logger = logger.child(
{
migrationName: this.name,
migrationVersion: this.id,
migrationID: this.id,
},
true
);
@@ -0,0 +1,38 @@
import { Db } from "mongodb";
import { getDefaultStaffConfiguration } from "coral-server/models/tenant";
import Migration from "coral-server/services/migrate/migration";
import collections from "coral-server/services/mongodb/collections";
import { MigrationError } from "../error";
export default class extends Migration {
private async getTenant(mongo: Db, id: string) {
const tenant = await collections.tenants(mongo).findOne({ id });
if (!tenant) {
throw new MigrationError(id, "tenant not found", "tenants", [id]);
}
return tenant;
}
public async test(mongo: Db, id: string) {
const tenant = await this.getTenant(mongo, id);
if (!tenant.staff) {
throw new MigrationError(id, "staff not set", "tenants", [id]);
}
}
public async up(mongo: Db, id: string) {
const tenant = await this.getTenant(mongo, id);
const bundle = this.i18n.getBundle(tenant.locale);
await collections.tenants(mongo).updateOne(
{ id, staff: null },
{
$set: {
staff: getDefaultStaffConfiguration(bundle),
},
}
);
}
}
+11 -12
View File
@@ -114,8 +114,8 @@ comments-userPopover-ignore = Ignorere
comments-userIgnorePopover-ignoreUser = Ignorerer {$username}?
comments-userIgnorePopover-description =
Når du ignorerer en kommentator, vil alle kommentarer,
de skrev på webstedet, være skjult for dig. Du kan fortryde
Når du ignorerer en kommentator, vil alle kommentarer,
de skrev på webstedet, være skjult for dig. Du kan fortryde
dette senere fra Min profil.
comments-userIgnorePopover-ignore = Ignorerer
comments-userIgnorePopover-cancel = Annuller
@@ -136,7 +136,6 @@ comments-rejectedTombstone =
Du har afvist denne kommentar. <TextLink>Gå til moderation for at gennemgå denne beslutning.</TextLink>
comments-featuredTag = Fremhævet
comments-staffTag = Personale
### Featured Comments
comments-featured-gotoConversation = Gå til samtale
@@ -196,7 +195,7 @@ comments-reportPopover-submit = Indsend
comments-reportPopover-thankYou = Mange tak
comments-reportPopover-receivedMessage =
Vi har modtaget din besked.
Rapporteringer fra medlemmer som dig,
Rapporteringer fra medlemmer som dig,
hjælper til at holde en god tone.
comments-reportPopover-dismiss = Luk
@@ -228,8 +227,8 @@ configure-liveUpdates-description =
configure-messageBox-title = Aktivér meddelelsesboks til denne stream
configure-messageBox-description =
Tilføj en besked øverst i kommentarfeltet for dine læsere. Brug dette til at stille et emne,
stille et spørgsmål eller offentliggøre meddelelser vedrørende denne historie.
Tilføj en besked øverst i kommentarfeltet for dine læsere. Brug dette til at stille et emne,
stille et spørgsmål eller offentliggøre meddelelser vedrørende denne historie.
configure-messageBox-preview = Forhåndsvisning
configure-messageBox-selectAnIcon = Vælg et ikon
configure-messageBox-noIcon = Intet ikon
@@ -237,8 +236,8 @@ configure-messageBox-writeAMessage = Skriv en besked
configure-closeStream-title = Luk kommentarer
configure-closeStream-description =
Kommentarerne er i øjeblikket åbne.
Ved at lukke kommentarerne kan der ikke indsendes nye kommentarer,
Kommentarerne er i øjeblikket åbne.
Ved at lukke kommentarerne kan der ikke indsendes nye kommentarer,
og alle tidligere indsendte kommentarer vises stadig.
configure-closeStream-closeStream = Luk kommentarer
@@ -251,8 +250,8 @@ configure-openStream-openStream = Åbn kommentarer
comments-tombstone-ignore = Denne kommentar er skjult, fordi du ignorerede {$username}
suspendInfo-heading = Din konto er midlertidigt suspenderet for at kommentere.
suspendInfo-info =
I overensstemmelse med fællesskabsretningslinjerne for { $organization}
er din konto midlertidigt suspenderet. Mens du er suspenderet,
kan du ikke kommentere, respektere eller rapportere kommentarer.
suspendInfo-info =
I overensstemmelse med fællesskabsretningslinjerne for { $organization}
er din konto midlertidigt suspenderet. Mens du er suspenderet,
kan du ikke kommentere, respektere eller rapportere kommentarer.
Gå igen med til samtalen på { $until }.
+11 -2
View File
@@ -541,10 +541,10 @@ moderate-user-drawer-recent-history-tooltip-button =
.aria-label = Toggle recent comment history tooltip
moderate-user-drawer-recent-history-tooltip-submitted = Submitted
moderate-user-drawer-notes-field =
moderate-user-drawer-notes-field =
.placeholder = Leave a note...
moderate-user-drawer-notes-button = Add note
moderatorNote-left-by = Left by:
moderatorNote-left-by = Left by:
moderatorNote-delete = Delete
## Create Username
@@ -780,6 +780,15 @@ configure-general-reactions-sort-input =
configure-general-reactions-preview = Preview
configure-general-reaction-sortMenu-sortBy = Sort by
configure-general-staff-title = Staff member badge
configure-general-staff-explanation =
Show a custom badge for staff members of your organization. This badge
appears on the comment stream and in the admin interface.
configure-general-staff-label = Badge text
configure-general-staff-input =
.placeholder = E.g. Staff
configure-general-staff-preview = Preview
configure-account-features-title = Commenter account management features
configure-account-features-explanation =
You can enable and disable certain features for your commenters to use
-1
View File
@@ -157,7 +157,6 @@ comments-rejectedTombstone =
You have rejected this comment. <TextLink>Go to Moderate to review this decision.</TextLink>
comments-featuredTag = Featured
comments-staffTag = Staff
### Account Deletion Stream
-1
View File
@@ -135,7 +135,6 @@ comments-rejectedTombstone =
Has rechazado este comentario.<TextLink>Vaya a moderación para revisar esta decisión.</TextLink>
comments-featuredTag = Resaltado
comments-staffTag = Miembro del equipo
### Featured Comments
comments-featured-gotoConversation = Ir a conversación
-1
View File
@@ -135,7 +135,6 @@ comments-rejectedTombstone =
U heeft deze reactie afgewezen. <TextLink>Ga naar modereren om dit besluit te herzien.</TextLink>
comments-featuredTag = Uitgelicht
comments-staffTag = Staf
### Featured Comments
comments-featured-gotoConversation = Ga naar het gesprek
-1
View File
@@ -149,7 +149,6 @@ comments-rejectedTombstone =
Você rejeitou este comentário. <TextLink> Vá para Moderados para rever esta decisão. </TextLink>
comments-featuredTag = Destaques
comments-staffTag = Staff
### Account Deletion Stream