# GetCourse — кнопка редактирования рассылки (userscript)

## Что делает

В редакторе CMS-страниц GetCourse рядом со ссылками на рассылки (`/notifications/control/mailings/update/id/<id>`) добавляет маленькую оранжевую иконку карандаша. Клик по иконке открывает в новой вкладке **новый** редактор той же рассылки по адресу `/pl/notifications/control/mailings/editor?id=<id>` — без необходимости копировать ID и переходить вручную.

![Кнопка редактирования рассылки в редакторе CMS](https://wiki.kishlaru.ru/uploads/images/gallery/2026-04/scaled-1680-/screenshot-edit-mailing.png)

## Код

```javascript
// ==UserScript==
// @name         GetCourse — кнопка редактирования рассылки
// @namespace    fitnessmama.school
// @version      1.0
// @description  Добавляет кнопку "редактировать" рядом со ссылками на рассылки в редакторе CMS GetCourse
// @match        https://fitnessmama.school/pl/*
// @match        https://*.getcourse.ru/pl/*
// @run-at       document-idle
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const BTN_CLASS = 'gc-edit-mailing-btn';
    const PROCESSED_ATTR = 'data-gc-edit-added';
    const HREF_PATTERN = /\/notifications\/control\/mailings\/update\/id\/(\d+)/;

    const style = document.createElement('style');
    style.textContent = `
        .${BTN_CLASS} {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 18px;
            height: 18px;
            padding: 0;
            margin: 0 0 0 4px;
            border: none;
            border-radius: 4px;
            background: transparent;
            color: #8a96a8;
            cursor: pointer;
            vertical-align: -4px;
            opacity: 0.7;
            transition: background .15s ease, color .15s ease, opacity .15s ease;
            text-decoration: none;
        }
        .${BTN_CLASS}:hover {
            opacity: 1;
            background: #fff4e5;
            color: #e8730c;
            text-decoration: none;
        }
        .${BTN_CLASS}:active { background: #ffe0b2; }
        .${BTN_CLASS} svg { width: 13px; height: 13px; display: block; }
    `;
    document.head.appendChild(style);

    const SVG_NS = 'http://www.w3.org/2000/svg';

    function buildPencil() {
        const s = document.createElementNS(SVG_NS, 'svg');
        s.setAttribute('viewBox', '0 0 24 24');
        s.setAttribute('fill', 'none');
        s.setAttribute('stroke', 'currentColor');
        s.setAttribute('stroke-width', '2');
        s.setAttribute('stroke-linecap', 'round');
        s.setAttribute('stroke-linejoin', 'round');
        const p1 = document.createElementNS(SVG_NS, 'path');
        p1.setAttribute('d', 'M12 20h9');
        const p2 = document.createElementNS(SVG_NS, 'path');
        p2.setAttribute('d', 'M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z');
        s.appendChild(p1);
        s.appendChild(p2);
        return s;
    }

    function makeButton(editUrl) {
        const a = document.createElement('a');
        a.className = BTN_CLASS;
        a.href = editUrl;
        a.target = '_blank';
        a.rel = 'noopener';
        a.title = 'Редактировать рассылку';
        a.setAttribute('aria-label', 'Редактировать рассылку: ' + editUrl);
        a.appendChild(buildPencil());
        return a;
    }

    function process(root) {
        if (!root || root.nodeType !== 1) root = document.body;
        const links = root.querySelectorAll('a[href*="/notifications/control/mailings/update/id/"]');
        links.forEach((a) => {
            if (a.hasAttribute(PROCESSED_ATTR)) return;
            const href = a.getAttribute('href') || '';
            const m = HREF_PATTERN.exec(href);
            if (!m) return;
            const id = m[1];
            const editUrl = '/pl/notifications/control/mailings/editor?id=' + id;
            a.setAttribute(PROCESSED_ATTR, '1');
            const btn = makeButton(editUrl);
            if (a.nextSibling) a.parentNode.insertBefore(btn, a.nextSibling);
            else a.parentNode.appendChild(btn);
        });
    }

    process(document.body);

    const observer = new MutationObserver((mutations) => {
        for (const m of mutations) {
            m.addedNodes.forEach((node) => {
                if (node.nodeType === 1) process(node);
            });
        }
    });
    observer.observe(document.body, { childList: true, subtree: true });
})();
```