Class: Assistant::BlogMediaToolBuilder
- Inherits:
-
Object
- Object
- Assistant::BlogMediaToolBuilder
- Defined in:
- app/services/assistant/blog_media_tool_builder.rb
Defined Under Namespace
Classes: EmbedOpGuardError
Constant Summary collapse
- CRM_POST_URL =
BlogToolBuilder::CRM_POST_URL
Class Method Summary collapse
-
.find_embed_figure(html, uuid) ⇒ Object
Locate the embed wrapper by its data-embedded-asset-uuid in the post solution.
- .save_post_with_embed_op!(post, new_html, change_note:, audit_context:) ⇒ Object
- .serialize_fragment(fragment) ⇒ Object
- .tools(audit_context: {}) ⇒ Object
Class Method Details
.find_embed_figure(html, uuid) ⇒ Object
Locate the embed wrapper by its data-embedded-asset-uuid in the post
solution. Most embeds are , but video embeds are sometimes
rendered as (see
BlogContentValidator#detect_dropped_embeds for the full list). Match
any element carrying the attribute to stay aligned with the validator.
Returns [node, fragment] or [nil, nil].
NOTE: do NOT interpolate uuid into a CSS selector string. embed UUIDs
come straight from tool input, so quotes/CSS metacharacters (e.g. a
value like "]/script>) can turn a stale or malicious value into a
selector parse error — or worse, match the wrong node before we
mutate the fragment. Iterate the candidate nodes and compare the
attribute by string equality instead.
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 |
# File 'app/services/assistant/blog_media_tool_builder.rb', line 535 def (html, uuid) return [nil, nil] if html.nil? || html.to_s.empty? || uuid.to_s.empty? fragment = if defined?(Nokogiri::HTML5) Nokogiri::HTML5.fragment(html.to_s) else Nokogiri::HTML.fragment(html.to_s) end uuid_str = uuid.to_s node = fragment.css('[data-embedded-asset-uuid]').find do |el| el['data-embedded-asset-uuid'] == uuid_str end [node, fragment] end |
.save_post_with_embed_op!(post, new_html, change_note:, audit_context:) ⇒ Object
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 |
# File 'app/services/assistant/blog_media_tool_builder.rb', line 555 def (post, new_html, change_note:, audit_context:) # These atomic embed ops (remove_embed / replace_embed / move_embed) # write post.solution directly, so without this guard a hallucinated # <figure data-embedded-asset-uuid="..."> from replace_embed could # land in the database without going through the same body / link / # asset-reference checks update_blog_post and edit_blog_post rely on. # We pass skip_embed_check: true because the user's intent to drop or # rearrange the targeted UUID is explicit (that's the whole point of # these tools), but every other content rule must still pass. guard_err = Assistant::BlogToolBuilder.guard_content( body_args: { old_html: post.solution.to_s, new_html: new_html.to_s, post_id: post.id, skip_embed_check: true } ) raise EmbedOpGuardError, guard_err if guard_err = Employee.find_by(id: audit_context[:user_id]) post.solution = new_html post.auto_update_revised_at = true if post.respond_to?(:auto_update_revised_at=) post.updater_id = &.id post.revision_change_notes = change_note post.save! post.reload Assistant::BlogToolBuilder.stabilize_post_solution_after_save!(post) Assistant::BlogContentValidator.upsert_link_graph(post) post end |
.serialize_fragment(fragment) ⇒ Object
551 552 553 |
# File 'app/services/assistant/blog_media_tool_builder.rb', line 551 def serialize_fragment(fragment) fragment.to_html end |
.tools(audit_context: {}) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'app/services/assistant/blog_media_tool_builder.rb', line 22 def tools(audit_context: {}) [ build_insert_image_tool, build_insert_video_tool, build_insert_faqs_tool, build_insert_product_tool, (audit_context), (audit_context), (audit_context), (audit_context), ] end |