زر "عرض المشاركة الكاملة" لا يعمل في التثبيتات الفرعية

لقد نقلت تثبيت Discourse مؤخرًا إلى مجلد فرعي. بعد القيام بذلك، توقف زر “عرض المشاركة الكاملة” عن العمل - تنقر لتوسيع المحتوى، لكنه لا يقوم بتحميل المشاركة الكاملة.

لم يتغير شيء في إعدادات WP Discourse الخاصة بي.

https://tecnoblog.net/comunidade/t/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix/157441

عند الوصول إلى عنوان URL المضمن مباشرة في المتصفح، فإنه يُرجع خطأ 404:

https://tecnoblog.net/comunidade/posts/483289/expand-embed

إعجاب واحد (1)

هذا غير ذي صلة، هذا المسار يستجيب فقط بنوع محتوى application/json. يُرجع https://tecnoblog.net/comunidade/posts/483289/expand-embed.json

"\"\u003cdiv\u003e\u003cdiv\u003e\u003c/div\u003e\u003c/div\u003e\\n\u003chr\u003e\\n\u003csmall\u003eEste é um tópico de discussão auxiliar para a entrada original em \u003ca href='https://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix'\u003ehttps://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix\u003c/a\u003e\u003c/small\u003e\\n\""

من المفترض أن يكون \u003cdiv\u003e\u003cdiv\u003e\u003c/div\u003e\u003c/div\u003e هو المحتوى.

هل قمت بتغيير رابط المدونة أيضًا بأي حال من الأحوال؟

عرض onebox يبدو غريبًا بالنسبة لي أيضًا، كنت أتوقع أن يحتوي على محتوى مقتطع ومخزن مؤقتًا بدلاً من ذلك، لذا أفترض أن body.present? هو false في الشرط أعلاه.

هل يمكنك الدخول إلى وحدة تحكم Rails والتحقق مما إذا كان TopicEmbed.where(topic_id: 157441).pick(:embed_url) يعرض لك رابط محتوى المدونة الصحيح؟

هل يمكنك ملاحظة أي أخطاء ذات صلة على https://tecnoblog.net/comunidade/logs؟

إعجابَين (2)

أوه، حسنًا!

إنه يُرجع رابط المنشور:

discourse(prod) => TopicEmbed.where(topic_id: 157441).pick(:embed_url)
=> “``https://tecnoblog.net/noticias/paramount-oferece-us-108-bilhoes-em-dinheiro-para-tomar-warner-da-netflix”

لا أعتقد أن هناك أي أخطاء ذات صلة في السجل.

لا! كان عنوان URL للمدونة دائمًا هو `tecnoblog.net`

ومن الجدير بالذكر أيضًا أن عنوان IP الخاص بالخادم يتم تجاوزه في جدار حماية CF:

إعجابَين (2)

لقد اضطررت إلى تصحيح هذه المشكلات بهذه الطريقة عدة مرات وهي معقدة، لذا تحلَّ بالصبر معي.

قم بتشغيل البرنامج النصي التالي وشارك المخرجات هنا

# استبدل بمعرف الموضوع أو عنوان URL الذي تقوم بتصحيحه
topic_id = 386983

# 1. التحقق مما إذا كان TopicEmbed موجودًا ومحتواه
te = TopicEmbed.find_by(topic_id: topic_id)
puts "TopicEmbed موجود: #{te.present?}"
puts "رابط التضمين: #{te&.embed_url}"
puts "ذاكرة التخزين المؤقت للمحتوى موجودة: #{te&.embed_content_cache.present?}"
puts "طول ذاكرة التخزين المؤقت: #{te&.embed_content_cache&.length || 0}"
puts "محتوى SHA1: #{te&.content_sha1}"

# 2. التحقق من المحتوى المخزن مؤقتًا الفعلي (أول 500 حرف)
puts "\n--- معاينة المحتوى المخزن مؤقتًا ---"
puts te&.embed_content_cache&.truncate(500)

# 3. محاولة الجلب من عنوان URL البعيد
if te&.embed_url.present?
  puts "\n--- محاولة الجلب عن بعد ---"
  begin
    response = TopicEmbed.find_remote(te.embed_url)
    puts "نجاح الجلب عن بعد: #{response.present?}"
    puts "الجسم البعيد موجود: #{response&.body.present?}"
    puts "طول الجسم البعيد: #{response&.body&.length || 0}"
    puts "العنوان البعيد: #{response&.title}"
    puts "الجسم البعيد: #{response&.body&.truncate(500)}"
  rescue => e
    puts "فشل الجلب عن بعد: #{e.message}"
  end
end

# 4. التحقق مما سيعيده expanded_for
if te.present?
  puts "\n--- اختبار expanded_for ---"
  post = Post.find(te.post_id)

  # مسح ذاكرة التخزين المؤقت لإجبار الجلب الجديد
  Discourse.cache.delete("embed-topic:#{topic_id}")

  begin
    expanded = TopicEmbed.expanded_for(post)
    puts "المحتوى الموسع موجود: #{expanded.present?}"
    puts "طول المحتوى الموسع: #{expanded&.length || 0}"
  rescue => e
    puts "فشل expanded_for: #{e.message}"
  end
end

# 5. التحقق من الإعدادات ذات الصلة
puts "\n--- إعدادات الموقع ---"
puts "embed_truncate: #{SiteSetting.embed_truncate}"
puts "allowed_embed_selectors: #{SiteSetting.allowed_embed_selectors}"
puts "blocked_embed_selectors: #{SiteSetting.blocked_embed_selectors}"

سيُظهر هذا سبب فشل https://tecnoblog.net/comunidade/t/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/157462?u=falco

4 إعجابات
discourse(prod)> # Replace with the topic ID or URL you’re debugging
discourse(prod)> topic_id = 386983
discourse(prod)>
discourse(prod)> # 1. Check if TopicEmbed exists and its content
discourse(prod)> te = TopicEmbed.find_by(topic_id: topic_id)
discourse(prod)> puts “TopicEmbed exists: #{te.present?}”
discourse(prod)> puts “Embed URL: #{te&.embed_url}”
discourse(prod)> puts “Content cache present: #{te&.embed_content_cache.present?}”
discourse(prod)> puts “Content cache length: #{te&.embed_content_cache&.length || 0}”
discourse(prod)> puts “Content SHA1: #{te&.content_sha1}”
discourse(prod)>
discourse(prod)> # 2. Check the actual cached content (first 500 chars)
discourse(prod)> puts “\n— Cached content preview —”
discourse(prod)> puts te&.embed_content_cache&.truncate(500)
discourse(prod)>
discourse(prod)> # 3. Try fetching from the remote URL
discourse(prod)* if te&.embed_url.present?
discourse(prod)*   puts “\n— Attempting remote fetch —”
discourse(prod)*   begin
discourse(prod)*     response = TopicEmbed.find_remote(te.embed_url)
discourse(prod)*     puts “Remote fetch success: #{response.present?}”
discourse(prod)*     puts “Remote body present: #{response&.body.present?}”
discourse(prod)*     puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)*     puts “Remote title: #{response&.title}”
discourse(prod)*     puts “Remote body: #{response&.body&.truncate(500)}”
discourse(prod)*   rescue => e
discourse(prod)*     puts “Remote fetch FAILED: #{e.message}”
discourse(prod)*   end
discourse(prod)* end
discourse(prod)>
discourse(prod)> # 4. Check what expanded_for would return
discourse(prod)* if te.present?
discourse(prod)*   puts “\n— Testing expanded_for —”
discourse(prod)*   post = Post.find(te.post_id)
discourse(prod)*
discourse(prod)*   # Clear cache to force fresh fetch
discourse(prod)*   Discourse.cache.delete(“embed-topic:#{topic_id}”)
discourse(prod)*
discourse(prod)*   begin
discourse(prod)*     expanded = TopicEmbed.expanded_for(post)
discourse(prod)*     puts “Expanded content present: #{expanded.present?}”
discourse(prod)*     puts “Expanded content length: #{expanded&.length || 0}”
discourse(prod)*   rescue => e
discourse(prod)*     puts “expanded_for FAILED: #{e.message}”
discourse(prod)*   end
discourse(prod)* end
discourse(prod)>
discourse(prod)> # 5. Check relevant settings
discourse(prod)> puts “\n— Site Settings —”
discourse(prod)> puts “embed_truncate: #{SiteSetting.embed_truncate}”
discourse(prod)> puts “allowed_embed_selectors: #{SiteSetting.allowed_embed_selectors}”
discourse(prod)> puts “blocked_embed_selectors: #{SiteSetting.blocked_embed_selectors}”
TopicEmbed exists: false
Embed URL:
Content cache present: false
Content cache length: 0
Content SHA1:

— Cached content preview —

— Site Settings —
embed_truncate: true
allowed_embed_selectors:
blocked_embed_selectors:
=> nil
discourse(prod)>

:thinking:

إعجاب واحد (1)

هل أنت متأكد من أن هذا هو مُعرّف الموضوع الصحيح؟ https://tecnoblog.net/comunidade/t/-/386983 يؤدي إلى صفحة 404.

إعجاب واحد (1)

أوه هذا هو. الموضوع الذي أشرت إليه هو في الواقع 157462.

خطئي!

إليك النتائج لمعرّف الموضوع الصحيح

TopicEmbed exists: true
Embed URL: https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento
Content cache present: true
Content cache length: 22
Content SHA1:

— Cached content preview —

<div><div></div></div>

— Attempting remote fetch —
Remote fetch success: true
Remote body present: true
Remote body length: 22
Remote title:
Remote body: 

— Testing expanded_for —
Expanded content present: true
Expanded content length: 309

— Site Settings —
embed_truncate: true
allowed_embed_selectors:
blocked_embed_selectors:
=> nil

هل نجح تجاوز Cloudflare الخاص بك؟ يبدو أن نص https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento هو 22 حرفًا فقط، ولا يحتوي على وسم عنوان.

نعم! تم تجاوز جميع الطلبات من خادم discourse:

Screenshot 2025-12-12 at 14.44.21

ما لاحظته هو أن عنوان URL المضمن لا يحتوي على شرطة مائلة لاحقة في النهاية. يجب أن تحتوي جميع عناوين URL على الشرطة المائلة اللاحقة.

Screenshot 2025-12-12 at 14.45.51

لذا ربما لا يتبع discourse إعادة التوجيه؟

ولكن أيضًا، لماذا يتم حفظ عنوان URL بدون الشرطة المائلة اللاحقة؟

إعجاب واحد (1)

هذا سهل الاختبار، جرب

url = "https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/"
response = TopicEmbed.find_remote(url)
puts "Remote fetch success: #{response.present?}"
puts "Remote body present: #{response&.body.present?}"
puts "Remote body length: #{response&.body&.length || 0}"
puts "Remote title: #{response&.title}"
puts "Remote body: #{response&.body&.truncate(500)}"

أعتقد أنها تعمل:

discourse(prod)> url = “https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/”
discourse(prod)> response = TopicEmbed.find_remote(url)
discourse(prod)> puts “Remote fetch success: #{response.present?}”
discourse(prod)> puts “Remote body present: #{response&.body.present?}”
discourse(prod)> puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)> puts “Remote title: #{response&.title}”
discourse(prod)> puts “Remote body: #{response&.body&.truncate(500)}”
Remote fetch success: true
Remote body present: true
Remote body length: 3776
Remote title: Governo renova app da CNH para baratear obtenção do documento • Tecnoblog
Remote body: 

<figure><img src="https://files.tecnoblog.net/wp-content/uploads/2025/12/cnh-brasil-app-1060x596.jpg">

	<figcaption>Aplicativo CNH do Brasil (imagem: Emerson Alecrim/Tecnoblog)</figcaption></figure>

</div>

<details>
    Resumo
    <div><ul>
<li>App CNH do Brasil substitui CDT e passa a oferecer recursos para obtenção da CNH, em especial, aulas teóricas gratuitas;</li>
<li>Aulas práticas continuam obrigatórias, mas a carga horária mínima foi reduzida de ...
=> nil


وهنا بدون الشرطة المائلة في النهاية:

discourse(prod)> url = “https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento”
discourse(prod)> response = TopicEmbed.find_remote(url)
discourse(prod)> puts “Remote fetch success: #{response.present?}”
discourse(prod)> puts “Remote body present: #{response&.body.present?}”
discourse(prod)> puts “Remote body length: #{response&.body&.length || 0}”
discourse(prod)> puts “Remote title: #{response&.title}”
discourse(prod)> puts “Remote body: #{response&.body&.truncate(500)}”
Remote fetch success: true
Remote body present: true
Remote body length: 22
Remote title:
Remote body: 
=> nil

يحدث نفس الخطأ في المشاركات القديمة، حيث تغير رابط المشاركة (slug).

على سبيل المثال، في هذه المشاركة، كان الرابط المستخدم هو:

https://tecnoblog.net/486925/o-que-e-pirataria-digital/

الآن، تغير إلى:

https://tecnoblog.net/responde/o-que-e-pirataria-digital/

إعجاب واحد (1)

هذه هي المشكلة الرئيسية على ما يبدو. عند استخدام تضمين تعليقات Discourse على موقع ويب آخر عبر Javascript فإنك تتحكم في ذلك عبر معلمة، ومن السهل جدًا إصلاح ذلك.

أنا لست على دراية بكيفية تحديد WP-Discourse لهذا، يجب أن يستخدم المنشور القانوني (canonical)، ولكني لست متأكدًا من ذلك. أي أفكار يا @angus؟

هل هناك طريقة لإجبار Discourse على تحديث جميع عناوين URL المضمنة من فئة ما، وتتبعها إلى الوجهة النهائية؟

أعتزم الترحيل إلى تضمين Discourse (ذلك التضمين الكامل الذي كنت تختبره) عندما يكون جاهزًا للإنتاج. ولكن إذا لم تتطابق عناوين URL المضمنة، فمن المحتمل أن يؤدي ذلك إلى إنشاء مواضيع جديدة لكل مشاركة وفقدان التعليقات…

إعجاب واحد (1)

قم بتشغيل

te = TopicEmbed.find_by(topic_id: 157462)
te.embed_url = te.embed_url + "/"
te.save

هل هذا يصلح https://tecnoblog.net/comunidade/t/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/157462 ؟

يعمل!

ولكن هل هناك حل لحالات كهذه؟

اقترح Gemini هذا الكود:

# الإعداد
CATEGORY_SLUG = 'tb'
category = Category.find_by(slug: CATEGORY_SLUG)

unless category
  puts "خطأ: الفئة '#{CATEGORY_SLUG}' غير موجودة."
  exit
end

puts "بدء المسح الكامل لروابط URL في الفئة '#{category.name}'..."
puts "قد يستغرق هذا بعض الوقت اعتمادًا على عدد المواضيع واستجابة موقعك..."

count_updated = 0
count_errors = 0
count_ok = 0

Topic.where(category_id: category.id).find_each do |topic|
  current_url = topic.custom_fields["embed_url"]

  # تخطي إذا لم يكن هناك embed_url
  next unless current_url.present?

  begin
    # إجراء طلب GET مع متابعة عمليات إعادة التوجيه
    response = Faraday.get(current_url)
    final_url = response.env.url.to_s

    # إذا كان الطلب ناجحًا (200 OK)
    if response.status == 200
      # التحقق مما إذا كان الرابط النهائي مختلفًا عن الرابط المحفوظ في قاعدة البيانات
      # المقارنة تتجاهل الاختلافات الطفيفة إذا لزم الأمر، ولكن هنا نقارن السلسلة النصية بالضبط
      if final_url != current_url
        puts "\n[تحديث] الموضوع ##{topic.id}:"
        puts "   من:   #{current_url}"
        puts "   إلى: #{final_url}"

        topic.custom_fields["embed_url"] = final_url
        topic.save_custom_fields(true)
        count_updated += 1
      else
        # print "." # قم بإلغاء التعليق لرؤية التقدم البصري (نقاط)
        count_ok += 1
      end
    else
      puts "\n[خطأ HTTP #{response.status}] الموضوع ##{topic.id} - الرابط: #{current_url}"
      count_errors += 1
    end

  rescue Faraday::ConnectionFailed, Faraday::TimeoutError => e
    puts "\n[فشل الاتصال] الموضوع ##{topic.id} - الرابط: #{current_url} - #{e.message}"
    count_errors += 1
  rescue StandardError => e
    puts "\n[خطأ عام] الموضوع ##{topic.id} - #{e.message}"
    count_errors += 1
  end

  # اختياري: وقفة قصيرة لتجنب إرهاق خادم ووردبريس الخاص بك
  # sleep 0.1
end

puts "\n\nملخص نهائي:"
puts "------------------------------------------------"
puts "المواضيع التي تم التحقق منها (موافق): #{count_ok}"
puts "المواضيع التي تم تحديثها:      #{count_updated}"
puts "الأخطاء التي تم العثور عليها:        #{count_errors}"
puts "------------------------------------------------"

أخيرًا بعض التقدم :sweat_smile:

برنامج نصي كهذا فكرة رائعة، فقط قم بعمل نسخة احتياطية قبل تشغيله.

حتى نسخة احتياطية من هذا الجدول الصغير ستكون رائعة.

إعجاب واحد (1)

حسناً! سأحاول تشغيله لاحقاً، عندما ينتهي الفريق من نوبته.

إعجاب واحد (1)

مرحباً يا رفاق، أرى أن الشرطة المائلة اللاحقة قد أثرت مجدداً :slight_smile:

[الشرطات المائلة اللاحقة هي] القضية الرئيسية التي تظهر. عند استخدام تضمين تعليقات Discourse على موقع ويب آخر عبر Javascript فإنك تتحكم في ذلك عبر مُعلَمة، ومن السهل جداً إصلاح ذلك.

فقط للإشارة إلى أن جميع تضمينات الموضوعات في Discourse تزيل الشرطات المائلة اللاحقة من embed_url؛ انظر TopicEmbed.normalize_url. نتيجة لحالة منفصلة تتضمن تقاطع التضمينات عبر جافاسكريبت وتضمينات WP Discourse، قمنا بتوحيد هذه المعالجة عبر كلتا طريقتي التضمين. انظر Apply TopicEmbed url normalisation to embed urls inserted in the PostCreator by angusmcleod · Pull Request #30641 · discourse/discourse · GitHub

@Thiago_Mobilon أثناء هذه الخطوة، هل قمت أيضاً بتحديث Discourse الخاص بك؟ قد نكون نشهد تطبيق توحيد تسوية embed_url على تضمينات WP Discourse كنتيجة لتحديث Discourse الخاص بك، والذي حدث في نفس الوقت الذي تم فيه الانتقال إلى تثبيت المجلد الفرعي. ما هو إصدار Discourse الذي تستخدمه حالياً؟ (وما هو الإصدار الذي كنت تستخدمه قبل الانتقال، إذا كنت تعرف ذلك؟)

مجرد ملاحظة جانبية أنه عندما أقوم بتشغيل هذين الأمرين محلياً على أحدث إصدار من Discourse، أحصل على نفس النتيجة، وهي محتوى HTML للمقالة

# مع الشرطة المائلة اللاحقة
TopicEmbed.find_remote("https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento/")

# بدون الشرطة المائلة اللاحقة
TopicEmbed.find_remote("https://tecnoblog.net/noticias/governo-renova-app-da-cnh-para-baratear-obtencao-do-documento")

# ينتج نفس النتيجة

هل قمت بتغيير شيء ما في جانب ووردبريس؟

إعجابَين (2)

مرحباً أنجوس!

لا، هذه مشاكل مختلفة. بدأت الشرطة المائلة اللاحقة عندما انتقلنا إلى المجلد الفرعي، ولكن هناك أيضًا عناوين URL قديمة من سنوات مضت لها جزء مختلف الآن.

اضطررت إلى إعادة بناء التثبيت، لذا نعم، أعتقد أن هذا المعيار الجديد قد يكون السبب.

اقتراحي لحل هذه المشكلة: ألا يمكن لـ Discourse تتبع عملية إعادة توجيه واحدة أو اثنتين على الأقل لاسترداد البيانات؟ سيؤدي ذلك إلى حل مشكلة الشرطة المائلة اللاحقة وأيضًا تدعيم الموقع ضد التغييرات المحتملة في عناوين URL في المستقبل.

بالإضافة إلى ذلك، سيكون أكثر أمانًا، حيث لن تكون هناك حاجة لتشغيل نصوص برمجية لتحديث المواضيع القديمة، والتي قد تسبب بعض الضرر في قاعدة البيانات.