Add `Ui/Message/Share/Dialog` as reusable share dialog module (#4108)
authorMatthias Schmidt <gravatronics@live.com>
Fri, 9 Apr 2021 11:54:30 +0000 (13:54 +0200)
committerGitHub <noreply@github.com>
Fri, 9 Apr 2021 11:54:30 +0000 (13:54 +0200)
* Add `Ui/Message/Share/Dialog` as reusable share dialog module

Close  #4026

* Apply suggestions from code review

* Apply suggestions from code review

12 files changed:
com.woltlab.wcf/templates/article.tpl
com.woltlab.wcf/templates/cms.tpl
com.woltlab.wcf/templates/headIncludeJavaScript.tpl
ts/WoltLabSuite/Core/BootstrapFrontend.ts
ts/WoltLabSuite/Core/Ui/Message/Share/Dialog.ts [new file with mode: 0644]
ts/WoltLabSuite/Core/Ui/Message/Share/Providers.ts [new file with mode: 0644]
wcfsetup/install/files/js/WoltLabSuite/Core/BootstrapFrontend.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Share/Dialog.js [new file with mode: 0644]
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Share/Providers.js [new file with mode: 0644]
wcfsetup/install/files/lib/data/page/content/PageContent.class.php
wcfsetup/install/lang/de.xml
wcfsetup/install/lang/en.xml

index 536662ae5b3a2848b17859c2ceb69726ef5245c6..93f0acb7c00808b9ba145dcbd6019efe1803ed2c 100644 (file)
                                </div>
                        {/if}
                        
-                       
                        <div class="col-xs-12 col-md-6 col-md{if !(MODULE_LIKE && ARTICLE_ENABLE_LIKE && $__wcf->session->getPermission('user.like.canViewLike'))} col-md-offset-6{/if}">
                                <ul class="articleLikeButtons buttonGroup buttonList smallButtons">
+                                       <li>
+                                               <a href="{$articleContent->getLink()}" class="button shareButton jsOnly" data-link-title="{$articleContent->getTitle()}">
+                                                       <span class="icon icon16 fa-share-alt"></span> <span>{lang}wcf.message.share{/lang}</span>
+                                               </a>
+                                       </li>
                                        {if $__wcf->session->getPermission('user.profile.canReportContent')}
                                                <li class="jsReportArticle jsOnly" data-object-id="{@$articleContent->articleID}"><a href="#" title="{lang}wcf.moderation.report.reportContent{/lang}" class="button jsTooltip"><span class="icon icon16 fa-exclamation-triangle"></span> <span class="invisible">{lang}wcf.moderation.report.reportContent{/lang}</span></a></li>
                                        {/if}
        {/if}
 </div>
 
-{if ENABLE_SHARE_BUTTONS}
-       {capture assign='footerBoxes'}
-               <section class="box boxFullWidth jsOnly">
-                       <h2 class="boxTitle">{lang}wcf.message.share{/lang}</h2>
-                       
-                       <div class="boxContent">
-                               {include file='shareButtons'}
-                       </div>
-               </section>
-       {/capture}
-{/if}
-
 <footer class="contentFooter">
        {hascontent}
                <nav class="contentFooterNavigation">
index 17f33755a5991f7bb19639219663072eead3af2d..eb29f7c6a66f27dbe9d5978906f797da186dcf45 100644 (file)
 {/capture}
 
 {capture assign='contentHeaderNavigation'}
+       {if $page->showShareButtons()}
+               <li>
+                       <a href="{$content->getLink()}" class="button shareButton jsOnly" data-link-title="{$content->getTitle()}">
+                               <span class="icon icon16 fa-share-alt"></span> <span>{lang}wcf.message.share{/lang}</span>
+                       </a>
+               </li>
+       {/if}
+       
        {if $page->isMultilingual && $__wcf->user->userID && $page->getPageLanguages()|count > 1}
                <li class="dropdown">
                        <a class="dropdownToggle boxFlag box24 button">
        {/hascontent}
 </footer>
 
-{if $page->showShareButtons()}
-       {capture assign='footerBoxes'}
-               <section class="box boxFullWidth jsOnly">
-                       <h2 class="boxTitle">{lang}wcf.message.share{/lang}</h2>
-                       
-                       <div class="boxContent">
-                               {include file='shareButtons'}
-                       </div>
-               </section>
-       {/capture}
-{/if}
-
 {include file='footer'}
index 9b70fc5a07883202dff4d5b24db538cf047baffd..64560c01dc550aae1e36e6c0e7dcf785f1373e82 100644 (file)
@@ -133,7 +133,21 @@ window.addEventListener('pageshow', function(event) {
                        'wcf.date.datePicker.hour': '{jslang}wcf.date.datePicker.hour{/jslang}',
                        'wcf.date.datePicker.minute': '{jslang}wcf.date.datePicker.minute{/jslang}',
                        'wcf.global.form.password.button.hide': '{jslang}wcf.global.form.password.button.hide{/jslang}',
-                       'wcf.global.form.password.button.show': '{jslang}wcf.global.form.password.button.show{/jslang}'
+                       'wcf.global.form.password.button.show': '{jslang}wcf.global.form.password.button.show{/jslang}',
+                       'wcf.message.share': '{jslang}wcf.message.share{/jslang}',
+                       'wcf.message.share.facebook': '{jslang}wcf.message.share.facebook{/jslang}',
+                       'wcf.message.share.twitter': '{jslang}wcf.message.share.twitter{/jslang}',
+                       'wcf.message.share.reddit': '{jslang}wcf.message.share.reddit{/jslang}',
+                       'wcf.message.share.whatsApp': '{jslang}wcf.message.share.whatsApp{/jslang}',
+                       'wcf.message.share.linkedIn': '{jslang}wcf.message.share.linkedIn{/jslang}',
+                       'wcf.message.share.pinterest': '{jslang}wcf.message.share.pinterest{/jslang}',
+                       'wcf.message.share.xing': '{jslang}wcf.message.share.xing{/jslang}',
+                       'wcf.message.share.permalink': '{jslang}wcf.message.share.permalink{/jslang}',
+                       'wcf.message.share.permalink.bbcode': '{jslang}wcf.message.share.permalink.bbcode{/jslang}',
+                       'wcf.message.share.permalink.html': '{jslang}wcf.message.share.permalink.html{/jslang}',
+                       'wcf.message.share.socialMedia': '{jslang}wcf.message.share.socialMedia{/jslang}',
+                       'wcf.message.share.copy': '{jslang}wcf.message.share.copy{/jslang}',
+                       'wcf.message.share.copy.success': '{jslang}wcf.message.share.copy.success{/jslang}'
                        {if MODULE_LIKE}
                                ,'wcf.like.button.like': '{jslang}wcf.like.button.like{/jslang}',
                                'wcf.like.button.dislike': '{jslang}wcf.like.button.dislike{/jslang}',
@@ -153,6 +167,9 @@ window.addEventListener('pageshow', function(event) {
                        },
                        enableUserPopover: {if $__wcf->getSession()->getPermission('user.profile.canViewUserProfile')}true{else}false{/if},
                        executeCronjobs: {if $executeCronjobs}true{else}false{/if},
+                       {if ENABLE_SHARE_BUTTONS}
+                           shareButtonProviders: [{implode from="\n"|explode:SHARE_BUTTONS_PROVIDERS item=shareButtonProvider}'{$shareButtonProvider}'{/implode}],
+                       {/if}
                        styleChanger: {if $__wcf->getStyleHandler()->showStyleChanger()}true{else}false{/if}
                });
                
index 658626f7e12242b0df34bef73d61be94200412bb..d58f13ce660b18c5a49f725d91956677c529433a 100644 (file)
@@ -15,6 +15,8 @@ import * as UiUserIgnore from "./Ui/User/Ignore";
 import * as UiPageHeaderMenu from "./Ui/Page/Header/Menu";
 import * as UiMessageUserConsent from "./Ui/Message/UserConsent";
 import * as Ajax from "./Ajax";
+import * as UiMessageShareDialog from "./Ui/Message/Share/Dialog";
+import * as UiMessageShareProviders from "./Ui/Message/Share/Providers";
 
 interface BoostrapOptions {
   backgroundQueue: {
@@ -23,6 +25,7 @@ interface BoostrapOptions {
   };
   enableUserPopover: boolean;
   executeCronjobs: boolean;
+  shareButtonProviders?: string[];
   styleChanger: boolean;
 }
 
@@ -85,4 +88,7 @@ export function setup(options: BoostrapOptions): void {
   }
 
   UiMessageUserConsent.init();
+
+  UiMessageShareProviders.enableShareProviders(options.shareButtonProviders || []);
+  UiMessageShareDialog.setup();
 }
diff --git a/ts/WoltLabSuite/Core/Ui/Message/Share/Dialog.ts b/ts/WoltLabSuite/Core/Ui/Message/Share/Dialog.ts
new file mode 100644 (file)
index 0000000..16e2f77
--- /dev/null
@@ -0,0 +1,170 @@
+/**
+ * Shows the share dialog when clicking on the share button of a message.
+ *
+ * @author  Matthias Schmidt
+ * @copyright  2001-2021 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Message/Share/Dialog
+ */
+
+import UiDialog from "../../Dialog";
+import DomUtil from "../../../Dom/Util";
+import * as DomTraverse from "../../../Dom/Traverse";
+import * as Language from "../../../Language";
+import * as Clipboard from "../../../Clipboard";
+import * as UiNotification from "../../Notification";
+import * as StringUtil from "../../../StringUtil";
+import DomChangeListener from "../../../Dom/Change/Listener";
+import * as UiMessageShare from "../Share";
+import * as UiMessageShareProviders from "./Providers";
+
+const shareButtons = new WeakSet<HTMLElement>();
+
+/**
+ * Copies the contents of one of the share dialog's input elements to the clipboard.
+ */
+async function copy(event: Event): Promise<void> {
+  event.preventDefault();
+
+  const target = event.currentTarget as HTMLElement;
+  const input = DomTraverse.prevBySel(target, 'input[type="text"]') as HTMLInputElement;
+
+  await Clipboard.copyTextToClipboard(input.value);
+
+  UiNotification.show(Language.get("wcf.message.share.copy.success"));
+}
+
+/**
+ * Returns all of the dialog elements shown in the dialog.
+ */
+function getDialogElements(shareButton: HTMLElement): string {
+  let dialogOptions = "";
+
+  let permalink = "";
+  if (shareButton instanceof HTMLAnchorElement && shareButton.href) {
+    permalink = shareButton.href;
+  }
+
+  if (permalink) {
+    dialogOptions += getDialogElement("wcf.message.share.permalink", permalink);
+  }
+  if (shareButton.dataset.bbcode) {
+    dialogOptions += getDialogElement("wcf.message.share.permalink.bbcode", shareButton.dataset.bbcode);
+  }
+  if (permalink && shareButton.dataset.linkTitle) {
+    if (!shareButton.dataset.bbcode) {
+      dialogOptions += getDialogElement(
+        "wcf.message.share.permalink.bbcode",
+        `[url='${permalink}']${shareButton.dataset.linkTitle}[/url]`,
+      );
+    }
+
+    dialogOptions += getDialogElement(
+      "wcf.message.share.permalink.html",
+      `<a href="${StringUtil.escapeHTML(permalink)}">${StringUtil.escapeHTML(shareButton.dataset.linkTitle)}</a>`,
+    );
+  }
+
+  return dialogOptions;
+}
+
+/**
+ * Returns a dialog element with the given label and input field value.
+ */
+function getDialogElement(label: string, value: string): string {
+  return `
+    <dl>
+      <dt>${Language.get(label)}</dt>
+      <dd>
+        <div class="inputAddon">
+          <input type="text" class="long" readonly value="${StringUtil.escapeHTML(value)}">
+          <a href="#" class="inputSuffix button jsTooltip shareDialogCopyButton" title="${Language.get(
+            "wcf.message.share.copy",
+          )}"><span class="icon icon16 fa-files-o pointer"></span></a>
+        </div>
+      </dd>
+    </dl>
+  `;
+}
+
+function getProviderButtons(): string {
+  const providerButtons = Array.from(UiMessageShareProviders.getEnabledProviders())
+    .map((provider) => {
+      const label = Language.get(provider.label);
+
+      return `
+      <li>
+        <a href="#" role="button" class="button ${provider.cssClass}" title="${label}" aria-label="${label}">
+          <span class="icon icon24 ${provider.iconClassName}"></span>
+          <span>${label}</span>
+        </a>
+      </li>
+    `;
+    })
+    .join("\n");
+
+  if (providerButtons) {
+    return `<ul class="inlineList">${providerButtons}</ul>`;
+  }
+
+  return "";
+}
+
+/**
+ * Opens the share dialog after clicking on the share button.
+ */
+function openDialog(event: MouseEvent): void {
+  event.preventDefault();
+
+  const target = event.currentTarget as HTMLElement;
+  const dialogId = `shareContentDialog_${DomUtil.identify(target)}`;
+  if (!UiDialog.getDialog(dialogId)) {
+    const providerButtons = getProviderButtons();
+    let providerElement = "";
+    if (providerButtons) {
+      providerElement = `
+        <dl class="messageShareButtons jsMessageShareButtons">
+          <dt>${Language.get("wcf.message.share.socialMedia")}</dt>
+          <dd>${providerButtons}</dd>
+        </dl>
+      `;
+    }
+
+    const dialogContent = `
+      <div class="shareContentDialog">
+        ${getDialogElements(target)}
+        ${providerElement}
+      </div>
+    `;
+
+    const dialogData = UiDialog.openStatic(dialogId, dialogContent, {
+      title: Language.get("wcf.message.share"),
+    });
+
+    dialogData.content.style.maxWidth = "600px";
+    dialogData.dialog
+      .querySelectorAll(".shareDialogCopyButton")
+      .forEach((el) => el.addEventListener("click", (ev) => copy(ev)));
+
+    if (providerButtons) {
+      UiMessageShare.init();
+    }
+  } else {
+    UiDialog.openStatic(dialogId, null);
+  }
+}
+
+function registerButtons(): void {
+  document.querySelectorAll(".shareButton").forEach((shareButton: HTMLElement) => {
+    if (!shareButtons.has(shareButton)) {
+      shareButton.addEventListener("click", (ev) => openDialog(ev));
+
+      shareButtons.add(shareButton);
+    }
+  });
+}
+
+export function setup(): void {
+  registerButtons();
+  DomChangeListener.add("WoltLabSuite/Core/Ui/Message/Share/Dialog", () => registerButtons());
+}
diff --git a/ts/WoltLabSuite/Core/Ui/Message/Share/Providers.ts b/ts/WoltLabSuite/Core/Ui/Message/Share/Providers.ts
new file mode 100644 (file)
index 0000000..0a4a1ae
--- /dev/null
@@ -0,0 +1,98 @@
+/**
+ * Manages the share providers shown in the share dialogs.
+ *
+ * @author  Matthias Schmidt
+ * @copyright  2001-2021 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Message/Share/Providers
+ */
+
+export interface ShareProvider {
+  cssClass: string;
+  iconClassName: string;
+  label: string;
+}
+
+const enabledProviders = new Set<ShareProvider>();
+const providers = new Map<string, ShareProvider>([
+  [
+    "Facebook",
+    {
+      cssClass: "jsShareFacebook",
+      iconClassName: "fa-facebook-official",
+      label: "wcf.message.share.facebook",
+    },
+  ],
+  [
+    "Twitter",
+    {
+      cssClass: "jsShareTwitter",
+      iconClassName: "fa-twitter",
+      label: "wcf.message.share.twitter",
+    },
+  ],
+  [
+    "Reddit",
+    {
+      cssClass: "jsShareReddit",
+      iconClassName: "fa-reddit",
+      label: "wcf.message.share.reddit",
+    },
+  ],
+  [
+    "WhatsApp",
+    {
+      cssClass: "jsShareWhatsApp",
+      iconClassName: "fa-whatsapp",
+      label: "wcf.message.share.whatsApp",
+    },
+  ],
+  [
+    "LinkedIn",
+    {
+      cssClass: "jsShareLinkedIn",
+      iconClassName: "fa-linkedin",
+      label: "wcf.message.share.linkedIn",
+    },
+  ],
+  [
+    "Pinterest",
+    {
+      cssClass: "jsSharePinterest",
+      iconClassName: "fa-pinterest-p",
+      label: "wcf.message.share.pinterest",
+    },
+  ],
+  [
+    "XING",
+    {
+      cssClass: "jsShareXing",
+      iconClassName: "fa-xing",
+      label: "wcf.message.share.xing",
+    },
+  ],
+]);
+
+export function addShareProvider(providerName: string, provider: ShareProvider): void {
+  if (providers.has(providerName)) {
+    throw new Error(`A share provider with name "${providerName}" already exists.`);
+  }
+
+  providers.set(providerName, provider);
+}
+
+export function enableShareProviders(providerNames: string[]): void {
+  providerNames.forEach((providerName) => {
+    if (providers.has(providerName)) {
+      enabledProviders.add(providers.get(providerName)!);
+    }
+  });
+}
+
+export function getProviders(): ReadonlyMap<string, ShareProvider> {
+  return providers;
+}
+
+export function getEnabledProviders(): ReadonlySet<ShareProvider> {
+  return enabledProviders;
+}
index 381c737dc7718fa392a20e3955920c9988fa034a..b5623a30e2bf3ba7b3e6b353af7e2180767924dd 100644 (file)
@@ -6,7 +6,7 @@
  * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  * @module  WoltLabSuite/Core/BootstrapFrontend
  */
-define(["require", "exports", "tslib", "./BackgroundQueue", "./Bootstrap", "./Controller/Style/Changer", "./Controller/Popover", "./Ui/User/Ignore", "./Ui/Page/Header/Menu", "./Ui/Message/UserConsent", "./Ajax"], function (require, exports, tslib_1, BackgroundQueue, Bootstrap, ControllerStyleChanger, ControllerPopover, UiUserIgnore, UiPageHeaderMenu, UiMessageUserConsent, Ajax) {
+define(["require", "exports", "tslib", "./BackgroundQueue", "./Bootstrap", "./Controller/Style/Changer", "./Controller/Popover", "./Ui/User/Ignore", "./Ui/Page/Header/Menu", "./Ui/Message/UserConsent", "./Ajax", "./Ui/Message/Share/Dialog", "./Ui/Message/Share/Providers"], function (require, exports, tslib_1, BackgroundQueue, Bootstrap, ControllerStyleChanger, ControllerPopover, UiUserIgnore, UiPageHeaderMenu, UiMessageUserConsent, Ajax, UiMessageShareDialog, UiMessageShareProviders) {
     "use strict";
     Object.defineProperty(exports, "__esModule", { value: true });
     exports.setup = void 0;
@@ -18,6 +18,8 @@ define(["require", "exports", "tslib", "./BackgroundQueue", "./Bootstrap", "./Co
     UiPageHeaderMenu = tslib_1.__importStar(UiPageHeaderMenu);
     UiMessageUserConsent = tslib_1.__importStar(UiMessageUserConsent);
     Ajax = tslib_1.__importStar(Ajax);
+    UiMessageShareDialog = tslib_1.__importStar(UiMessageShareDialog);
+    UiMessageShareProviders = tslib_1.__importStar(UiMessageShareProviders);
     /**
      * Initializes user profile popover.
      */
@@ -68,6 +70,8 @@ define(["require", "exports", "tslib", "./BackgroundQueue", "./Bootstrap", "./Co
             UiUserIgnore.init();
         }
         UiMessageUserConsent.init();
+        UiMessageShareProviders.enableShareProviders(options.shareButtonProviders || []);
+        UiMessageShareDialog.setup();
     }
     exports.setup = setup;
 });
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Share/Dialog.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Share/Dialog.js
new file mode 100644 (file)
index 0000000..d70c839
--- /dev/null
@@ -0,0 +1,144 @@
+/**
+ * Shows the share dialog when clicking on the share button of a message.
+ *
+ * @author  Matthias Schmidt
+ * @copyright  2001-2021 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Message/Share/Dialog
+ */
+define(["require", "exports", "tslib", "../../Dialog", "../../../Dom/Util", "../../../Dom/Traverse", "../../../Language", "../../../Clipboard", "../../Notification", "../../../StringUtil", "../../../Dom/Change/Listener", "../Share", "./Providers"], function (require, exports, tslib_1, Dialog_1, Util_1, DomTraverse, Language, Clipboard, UiNotification, StringUtil, Listener_1, UiMessageShare, UiMessageShareProviders) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.setup = void 0;
+    Dialog_1 = tslib_1.__importDefault(Dialog_1);
+    Util_1 = tslib_1.__importDefault(Util_1);
+    DomTraverse = tslib_1.__importStar(DomTraverse);
+    Language = tslib_1.__importStar(Language);
+    Clipboard = tslib_1.__importStar(Clipboard);
+    UiNotification = tslib_1.__importStar(UiNotification);
+    StringUtil = tslib_1.__importStar(StringUtil);
+    Listener_1 = tslib_1.__importDefault(Listener_1);
+    UiMessageShare = tslib_1.__importStar(UiMessageShare);
+    UiMessageShareProviders = tslib_1.__importStar(UiMessageShareProviders);
+    const shareButtons = new WeakSet();
+    /**
+     * Copies the contents of one of the share dialog's input elements to the clipboard.
+     */
+    async function copy(event) {
+        event.preventDefault();
+        const target = event.currentTarget;
+        const input = DomTraverse.prevBySel(target, 'input[type="text"]');
+        await Clipboard.copyTextToClipboard(input.value);
+        UiNotification.show(Language.get("wcf.message.share.copy.success"));
+    }
+    /**
+     * Returns all of the dialog elements shown in the dialog.
+     */
+    function getDialogElements(shareButton) {
+        let dialogOptions = "";
+        let permalink = "";
+        if (shareButton instanceof HTMLAnchorElement && shareButton.href) {
+            permalink = shareButton.href;
+        }
+        if (permalink) {
+            dialogOptions += getDialogElement("wcf.message.share.permalink", permalink);
+        }
+        if (shareButton.dataset.bbcode) {
+            dialogOptions += getDialogElement("wcf.message.share.permalink.bbcode", shareButton.dataset.bbcode);
+        }
+        if (permalink && shareButton.dataset.linkTitle) {
+            if (!shareButton.dataset.bbcode) {
+                dialogOptions += getDialogElement("wcf.message.share.permalink.bbcode", `[url='${permalink}']${shareButton.dataset.linkTitle}[/url]`);
+            }
+            dialogOptions += getDialogElement("wcf.message.share.permalink.html", `<a href="${StringUtil.escapeHTML(permalink)}">${StringUtil.escapeHTML(shareButton.dataset.linkTitle)}</a>`);
+        }
+        return dialogOptions;
+    }
+    /**
+     * Returns a dialog element with the given label and input field value.
+     */
+    function getDialogElement(label, value) {
+        return `
+    <dl>
+      <dt>${Language.get(label)}</dt>
+      <dd>
+        <div class="inputAddon">
+          <input type="text" class="long" readonly value="${StringUtil.escapeHTML(value)}">
+          <a href="#" class="inputSuffix button jsTooltip shareDialogCopyButton" title="${Language.get("wcf.message.share.copy")}"><span class="icon icon16 fa-files-o pointer"></span></a>
+        </div>
+      </dd>
+    </dl>
+  `;
+    }
+    function getProviderButtons() {
+        const providerButtons = Array.from(UiMessageShareProviders.getEnabledProviders())
+            .map((provider) => {
+            const label = Language.get(provider.label);
+            return `
+      <li>
+        <a href="#" role="button" class="button ${provider.cssClass}" title="${label}" aria-label="${label}">
+          <span class="icon icon24 ${provider.iconClassName}"></span>
+          <span>${label}</span>
+        </a>
+      </li>
+    `;
+        })
+            .join("\n");
+        if (providerButtons) {
+            return `<ul class="inlineList">${providerButtons}</ul>`;
+        }
+        return "";
+    }
+    /**
+     * Opens the share dialog after clicking on the share button.
+     */
+    function openDialog(event) {
+        event.preventDefault();
+        const target = event.currentTarget;
+        const dialogId = `shareContentDialog_${Util_1.default.identify(target)}`;
+        if (!Dialog_1.default.getDialog(dialogId)) {
+            const providerButtons = getProviderButtons();
+            let providerElement = "";
+            if (providerButtons) {
+                providerElement = `
+        <dl class="messageShareButtons jsMessageShareButtons">
+          <dt>${Language.get("wcf.message.share.socialMedia")}</dt>
+          <dd>${providerButtons}</dd>
+        </dl>
+      `;
+            }
+            const dialogContent = `
+      <div class="shareContentDialog">
+        ${getDialogElements(target)}
+        ${providerElement}
+      </div>
+    `;
+            const dialogData = Dialog_1.default.openStatic(dialogId, dialogContent, {
+                title: Language.get("wcf.message.share"),
+            });
+            dialogData.content.style.maxWidth = "600px";
+            dialogData.dialog
+                .querySelectorAll(".shareDialogCopyButton")
+                .forEach((el) => el.addEventListener("click", (ev) => copy(ev)));
+            if (providerButtons) {
+                UiMessageShare.init();
+            }
+        }
+        else {
+            Dialog_1.default.openStatic(dialogId, null);
+        }
+    }
+    function registerButtons() {
+        document.querySelectorAll(".shareButton").forEach((shareButton) => {
+            if (!shareButtons.has(shareButton)) {
+                shareButton.addEventListener("click", (ev) => openDialog(ev));
+                shareButtons.add(shareButton);
+            }
+        });
+    }
+    function setup() {
+        registerButtons();
+        Listener_1.default.add("WoltLabSuite/Core/Ui/Message/Share/Dialog", () => registerButtons());
+    }
+    exports.setup = setup;
+});
diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Share/Providers.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Share/Providers.js
new file mode 100644 (file)
index 0000000..bad89cf
--- /dev/null
@@ -0,0 +1,95 @@
+/**
+ * Manages the share providers shown in the share dialogs.
+ *
+ * @author  Matthias Schmidt
+ * @copyright  2001-2021 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Message/Share/Providers
+ */
+define(["require", "exports"], function (require, exports) {
+    "use strict";
+    Object.defineProperty(exports, "__esModule", { value: true });
+    exports.getEnabledProviders = exports.getProviders = exports.enableShareProviders = exports.addShareProvider = void 0;
+    const enabledProviders = new Set();
+    const providers = new Map([
+        [
+            "Facebook",
+            {
+                cssClass: "jsShareFacebook",
+                iconClassName: "fa-facebook-official",
+                label: "wcf.message.share.facebook",
+            },
+        ],
+        [
+            "Twitter",
+            {
+                cssClass: "jsShareTwitter",
+                iconClassName: "fa-twitter",
+                label: "wcf.message.share.twitter",
+            },
+        ],
+        [
+            "Reddit",
+            {
+                cssClass: "jsShareReddit",
+                iconClassName: "fa-reddit",
+                label: "wcf.message.share.reddit",
+            },
+        ],
+        [
+            "WhatsApp",
+            {
+                cssClass: "jsShareWhatsApp",
+                iconClassName: "fa-whatsapp",
+                label: "wcf.message.share.whatsApp",
+            },
+        ],
+        [
+            "LinkedIn",
+            {
+                cssClass: "jsShareLinkedIn",
+                iconClassName: "fa-linkedin",
+                label: "wcf.message.share.linkedIn",
+            },
+        ],
+        [
+            "Pinterest",
+            {
+                cssClass: "jsSharePinterest",
+                iconClassName: "fa-pinterest-p",
+                label: "wcf.message.share.pinterest",
+            },
+        ],
+        [
+            "XING",
+            {
+                cssClass: "jsShareXing",
+                iconClassName: "fa-xing",
+                label: "wcf.message.share.xing",
+            },
+        ],
+    ]);
+    function addShareProvider(providerName, provider) {
+        if (providers.has(providerName)) {
+            throw new Error(`A share provider with name "${providerName}" already exists.`);
+        }
+        providers.set(providerName, provider);
+    }
+    exports.addShareProvider = addShareProvider;
+    function enableShareProviders(providerNames) {
+        providerNames.forEach((providerName) => {
+            if (providers.has(providerName)) {
+                enabledProviders.add(providers.get(providerName));
+            }
+        });
+    }
+    exports.enableShareProviders = enableShareProviders;
+    function getProviders() {
+        return providers;
+    }
+    exports.getProviders = getProviders;
+    function getEnabledProviders() {
+        return enabledProviders;
+    }
+    exports.getEnabledProviders = getEnabledProviders;
+});
index d79ff58ca01df5d0b0e19e3f321abd41dadd3771..c9d4e14968aa94e55f9a922f2d682af6f9d01739 100644 (file)
@@ -3,7 +3,7 @@
 namespace wcf\data\page\content;
 
 use wcf\data\DatabaseObject;
-use wcf\data\ILinkableObject;
+use wcf\data\ITitledLinkObject;
 use wcf\system\html\output\HtmlOutputProcessor;
 use wcf\system\html\simple\HtmlSimpleParser;
 use wcf\system\message\embedded\object\MessageEmbeddedObjectManager;
@@ -28,7 +28,7 @@ use wcf\system\WCF;
  * @property-read   string $customURL      custom url of the page in the associated language
  * @property-read   int $hasEmbeddedObjects is `1` if the page content contains embedded objects, otherwise `0`
  */
-class PageContent extends DatabaseObject implements ILinkableObject
+class PageContent extends DatabaseObject implements ITitledLinkObject
 {
     /**
      * @inheritDoc
@@ -138,4 +138,12 @@ class PageContent extends DatabaseObject implements ILinkableObject
     {
         return LinkHandler::getInstance()->getCmsLink($this->pageID, $this->languageID);
     }
+
+    /**
+     * @inheritDoc
+     */
+    public function getTitle()
+    {
+        return $this->title;
+    }
 }
index d5781d4afb7896941e80c6e59ecac6e25935e456..216b280fe7d6868625f5817159c4112d4d3cacb6 100644 (file)
@@ -4188,6 +4188,9 @@ Dateianhänge:
                <item name="wcf.message.share.linkedIn"><![CDATA[LinkedIn]]></item>
                <item name="wcf.message.share.pinterest"><![CDATA[Pinterest]]></item>
                <item name="wcf.message.share.xing"><![CDATA[XING]]></item>
+               <item name="wcf.message.share.socialMedia"><![CDATA[Social Media]]></item>
+               <item name="wcf.message.share.copy"><![CDATA[Code kopieren]]></item>
+               <item name="wcf.message.share.copy.success"><![CDATA[Der Code wurde erfolgreich kopiert.]]></item>
                <item name="wcf.message.smilies"><![CDATA[Smileys]]></item>
                <item name="wcf.message.button.extendedReply"><![CDATA[Erweiterte Antwort]]></item>
                <item name="wcf.message.button.extendedEdit"><![CDATA[Erweiterte Bearbeitung]]></item>
index 718052e023a9869bbe9fa3feb1803deecfa8dec0..2c9055d426f07641ebc9723286ce0a48fb1bf7a7 100644 (file)
@@ -4135,6 +4135,9 @@ Attachments:
                <item name="wcf.message.share.linkedIn"><![CDATA[LinkedIn]]></item>
                <item name="wcf.message.share.pinterest"><![CDATA[Pinterest]]></item>
                <item name="wcf.message.share.xing"><![CDATA[XING]]></item>
+               <item name="wcf.message.share.socialMedia"><![CDATA[Social Media]]></item>
+               <item name="wcf.message.share.copy"><![CDATA[Copy Code]]></item>
+               <item name="wcf.message.share.copy.success"><![CDATA[The code has been successfully copied.]]></item>
                <item name="wcf.message.smilies"><![CDATA[Smilies]]></item>
                <item name="wcf.message.button.extendedReply"><![CDATA[More Options]]></item>
                <item name="wcf.message.button.extendedEdit"><![CDATA[More Options]]></item>