Получить ID темы при загрузке страницы просмотра темы

В моей теме, когда загружается страница отображения темы, мне нужно получить ID этой темы (и название, если возможно, но ID будет достаточно).

Затем я использую эту информацию в своей теме для выполнения API-запроса, чтобы получить данные о теме (с вызовом эндпоинта вроде forum-name/t/topic-name/id.json…)

Как можно получить ID темы при загрузке страницы отображения темы?

Очевидные методы недостаточны

Очевидное решение — использовать window.location.href или window.location.pathname для получения информации из URL. Проблема в том, что URL страницы отображения темы может иметь разные формы. Часто он выглядит так:
forum-name/t/topic-name/topic-id

Если бы это всегда был такой формат, то использование window.location.pathname сработало бы отлично — я бы получил “t/topic-name/topic-id”.

НО иногда к URL страницы отображения темы добавляется порядковый номер категории темы, поэтому URL выглядит так: forum-name/t/topic-name/topic-id/category-index.

Таким образом, использование window.location.pathname недостаточно, потому что программно я не знаю, будет ли последний параметр ID темы или индексом категории.

Возможно, существует способ решить это с помощью регулярных выражений, но это требует серьёзных навыков работы с ними. Также может быть способ решить задачу с помощью jQuery, анализируя элементы на странице, которые могут содержать ID, — но мне пока не удалось заставить это работать.

Когда/где это происходит? Я не смог найти.

В любом случае, когда эта тема загружается, в ней есть элемент link:

<link rel="canonical" href="https://meta.discourse.org/t/get-topic-id-when-the-topic-show-page-loads/155620" />

Может быть, вы сможете взять его оттуда. :slight_smile:

На моём форуме это чаще всего происходит при клике на тему. Последняя часть URL — это номер темы в категории, если вы перешли на неё из списка категории. Возможно, это настройка в Discourse? На meta-форуме такого не наблюдается, но я не думаю, что делаю что-то особенное, чтобы это происходило на моём форуме.

Однако я не всегда могу полагаться на это, потому что если тема является первой в категории, к ней не добавляется индекс категории. Так что, насколько я понимаю, сейчас нет хорошего способа определить, появится ли тема в индексе категории или нет.

Получение ID из div-элемента возможно, но, к сожалению, это значительно медленнее, чем получение его напрямую из URL или другим прямым способом. Если другого решения нет, не подскажете, какой jQuery-код нужен, чтобы получить значение этого атрибута href?

Попробуйте

api.onPageChange((url, title) => {
    var res = url.match(/\/t\/(.*?)\/(\w+)/);
    if (res && res[2] > 0) {
        console.log(res[2]);
    }
});

Чего вы пытаетесь достичь?

Это может сработать, но не очень эффективно, так как вы делаете дополнительный AJAX-запрос при каждом просмотре страницы темы каждым пользователем — включая анонимов. Это создаст значительную ненужную нагрузку на ваш сервер.

Не совсем. URL темы выглядит следующим образом:

your.site.com/t/topic_title/topic_id/linked_post_number(optional)

В стандартной установке Discourse в URL нет порядкового номера категории.

Мы сможем помочь вам, если вы опишете проблему, которую пытаетесь решить, вместо того чтобы описывать свои решения для неё.

Это работает! Большое спасибо. Я понимаю, что с помощью метода match здесь вы анализируете URL, чтобы получить, предположительно, третье вхождение символа “/”, так как ID всегда появляется после третьего “/” в URL, который имеет формат “/t/name/id/otherstuff”. Не могли бы вы немного объяснить, как именно ваше регулярное выражение это делает? Это было бы очень полезно в моём изучении регулярных выражений.

Спасибо за информацию. То есть именно “linked_post_number” иногда появляется и сбивает мой API-вызов. Вы здесь говорите, что он “необязательный” — есть ли способ гарантировать, что он никогда не будет отображаться?

Когда пользователь заходит на страницу просмотра темы, я хочу:

  1. Программно узнать все теги, связанные с этой темой. Обратите внимание, что некоторые теги скрыты от просмотра пользователем.
  2. Добавить на страницу темы кнопку, которая при нажатии добавляет определённый скрытый тег к теме (если его там ещё нет) и удаляет его (если тег уже есть).

Всё это вполне реализуемо с помощью Admin API и JavaScript/jQuery (при условии, что я смогу получить правильный URL темы для использования в API-вызовах).

Полагаю, единственный другой способ сделать что-то подобное — создать плагин, углубившись в 1. Ember, 2. Rails и 3. исходный код Discourse. Я изучил ключевые посты и документацию Discourse о том, как это сделать, но процесс идёт медленно, так как необходимо действительно понимать эти три компонента. Поэтому пока я сосредоточился на подходе через API.

Мне было бы интересно узнать, есть ли другой способ сделать это, который уменьшил бы нагрузку на сервер.

Я мог бы с уверенностью помочь вам с кодом, если бы решение состояло из одного шага, но здесь задействовано несколько этапов. Если вам нужна помощь в виде консультации по выполнению работы или обучению написанию компонентов темы самостоятельно, пожалуйста, опубликуйте запрос на рынке, и я свяжусь с вами.

Спасибо, но любая помощь на любом этапе будет полезна. Например, кроме использования API, как узнать все теги, связанные с темой?

Полноценный объект темы загружается на стороне клиента при открытии страницы темы. Теперь, если вы хотите использовать его, вам нужно добавить свой код в шаблон для работы с этими данными или «переоткрыть» классы, чтобы использовать загруженные данные (то есть по сути создать свои собственные вычисляемые свойства и применять их в шаблоне). На стороне клиента уже загружено большинство необходимых данных (возможно, даже больше, чем нужно), без дополнительных вызовов API. Также вам понадобятся plugin-outlet для вставки собственного HTML в существующие шаблоны.

Вам поможет изучение кодовой базы и метаданных по упомянутым выше терминам. Я с радостью помогу вам здесь, если вы застрянете, в пределах моих возможностей по времени. Удачи!

Спасибо. Я знаком с некоторыми основными моментами, которые вы упомянули, но один пункт вызывает у меня трудности (и, уверен, именно такие вещи часто ставят в тупик многих):

На странице отображения темы загружается шаблон /templates/components/topic-category.hbs. Именно он отвечает за отображение категории, а также тега под заголовком темы.

В шаблоне topic-category перечисляются topic.tags. Это ключевой элемент информации, необходимый для решения моей задачи.

Вот где я застрял: Как передать информацию topic.tags в JavaScript?

Например, если бы я просто хотел вывести содержимое topic.tags в консоль, как бы я это сделал?


Я знаю, как переопределять шаблоны. Например, в теме я могу создать файл по пути discourse/templates/components/topic-category.hbs, скопировать туда содержимое шаблона и внести желаемые изменения в отображение. (Я использую структуру с отдельными файлами, описанную здесь).

В своей теме я также знаю, как добавлять JavaScript по пути theme/initializers/initializer-file.js.es6.

И я могу заставить их взаимодействовать с помощью jQuery. Например, я мог бы поместить topic.tags в div внутри шаблона, а затем получить доступ к этому div из инициализатора с помощью jQuery, чтобы извлечь его содержимое.

Но это слишком обходной путь. Как можно получить информацию topic.tags напрямую, чтобы я мог её распарсить и обработать?

Я бы вывел всю тему в консоль и нашел бы необходимый ключ. Взаимодействия с jQuery должны выполняться и выполняются внутри компонентов. didInsertElement — это хук компонента, который полезен в данном случае.

Судя по всему, вам нужен вычисляемый параметр (computed property), зависящий от topic.tags, который возвращает результат вашей манипуляции в соответствующем контроллере или компоненте. Его можно использовать напрямую в шаблоне. Также не опускайте руки в этом вопросе. Это займёт время, но определённо окупится способами, о которых вы даже не могли мечтать.

Что вы имеете в виду под «внутри компонентов» — в каком файле мне размещать код jQuery? (в моей теме я могу использовать jQuery в файле theme/intializers/intializer-file.js.es6, но это, кажется, не считается «внутри» компонента)

В каком файле должен находиться код для didInsertElement?

На этом этапе вам действительно стоит ознакомиться с руководством по Ember. Компонент — это пакет функциональности, который можно развернуть и подключить к другим элементам.

Спасибо, ребята, вся эта информация очень полезна. Я изучаю руководства и общую документацию. Также было бы очень полезно узнать название файла, куда нужно вставить (i) код jQuery и (ii) код didInsertElement. Такая конкретная информация станет большой помощью для дальнейшего анализа.

Компоненты = компоненты Ember.

Вы можете использовать reopen для различных классов Ember и внедрять в них свой код. Чтобы лучше понять, как это делается, изучите плагины или темы, которые используют этот подход.

@JQ331 Как вы, вероятно, знаете, я нашёл регулярные выражения в Google и попробовал их здесь! Вы можете вводить различные варианты, чтобы получить наилучшие результаты.

Мне кажется, это область для плагина.

Если теги скрыты от обычного пользователя, как может помочь дополнительный вызов? Сериализатор в любом случае должен скрывать эти теги от обычного пользователя. Если этого не происходит, то вы создаёте уязвимость в безопасности контента.

Если вы идёте на все эти сложности, чтобы обойти ограничения изменений только на стороне фронтенда, я бы предложил вам решиться и реализовать это в виде плагина. Тогда вы сможете выполнить сериализацию за один раз, вместо того чтобы иметь запутанную настройку с дополнительными вызовами.

Это имеет смысл. Я хотел бы узнать больше об использовании сериализатора — хотя сериализатор является одним из самых важных этапов при создании плагина для Discourse, я пока не нашёл документации, в которой были бы приведены базовые примеры его использования. Знаете ли вы о такой документации?

Когда вы говорите, что сериализатор должен скрывать эти теги, не могли бы вы привести какой-нибудь псевдокод, чтобы проиллюстрировать, что вы имеете в виду? (Не обязательно, чтобы он полностью работал — я просто пытаюсь разобраться в концепции).

Вот:

Это как джазовая музыка: лучшие практики обычно уже существуют в ландшафте открытых плагинов с открытым исходным кодом, как и все записи. Не ждите идеального руководства по выполнению всего — вам часто придется вникать в суть (кхм) и учиться на уже существующих решениях.

Найдите плагин из #plugin, который функционально похож на то, что вы хотите сделать, или реализует часть вашей задачи, изучите его код и посмотрите, как он достигает своих целей. То же самое касается исходного кода Discourse, который можно использовать как источник «лучших практик», особенно в контексте Discourse.

Склонируйте существующий плагин локально, внесите несколько изменений и поэкспериментируйте.

Сериализаторы просто фильтруют данные, передаваемые из контроллеров и моделей, и могут выполнять некоторые базовые преобразования.

Вот один пример из исходного кода: discourse/app/serializers/concerns/topic_tags_mixin.rb at 888e68a1637ca784a7bf51a6bbb524dcf7413b13 · discourse/discourse · GitHub

  def tags
    # Вызов метода `pluck` вместе с `includes` вызывает N+1 запросы
    tags = topic.tags.map(&:name)

    if scope.is_staff?
      tags
    else
      tags - scope.hidden_tag_names
    end
  end

Как видно здесь, скрытые теги исключаются для пользователей, не являющихся сотрудниками.