AI 對話紀錄  March 22, 2025

【AG-07】CMS Refinements: Dynamic Sections & Video Code Support

【AG-07】CMS Refinements: Dynamic Sections & Video Code Support

Q: I think I will use "pull-section.php". For what you said in "Dynamic vs. Static Content", I think what I want is to replace the heading and content with the corresponding heading/content from the web_content table for EACH of the two test sections "WelcomeMessage" and "VideoTestimonial01" (And you may note from https://admin.nlt-chaplaincy.org/public/index.php that sections are separated by a static button and a separator).

In other words, what I want is:-- WelcomeMessage --

  • 三月籌款開始喇!(Dynamic)
  • 多得好多人撐我哋... (Dynamic)
  • Button (Static)
  • Separator (Static)

-- VideoTestimonial01 --

  • —— 院牧部有咩用?(Dynamic)
  • 作用就大喇!我哋聽吓HA黃醫生佢點講,絕對無做馬嫁!... (Dynamic)
  • Button (Static)
  • Separator (Static)

Do you think our single "pull-section.php" can achieve this?


A: Thank you for clarifying your requirements! I understand that you want to replace the static heading and content in your index.php file with dynamic content from the web_content table for the WelcomeMessage and VideoTestimonial01 sections, while keeping the static button and separator between sections. You’ve confirmed that you’ll use pull-section.php, and I’ve reviewed the live page at https://admin.nlt-chaplaincy.org/public/index.php to understand the structure (noting the static button and separator between sections). Let’s break this down and update pull-section.php to achieve your desired output.


Desired Output

Based on your description, you want the following layout:

  • WelcomeMessage Section:

    • Heading: 三月籌款開始喇! (Dynamic, from web_content.title)

    • Content: 多得好多人撐我哋... (Dynamic, from web_content.content, plus the centered image)

  • Static Elements:

    • Button (Static, e.g., 了解更多)

    • Separator (Static, e.g.,


      )

  • VideoTestimonial01 Section:

    • Heading: —— 院牧部有咩用? (Dynamic, from web_content.title)

    • Content: 作用就大喇!我哋聽吓HA黃醫生佢點講,絕對無做馬嫁!... (Dynamic, from web_content.content)

    • Video Embed: (Dynamic, from web_content.video_embed, e.g., )

  • Static Elements:

    • Button (Static)

    • Separator (Static)

Current index.php Snippet

From your previous snippet, the static content looks like this:

html

<div class="content__entry">
    <div><?php include 'pull-section.php'; ?></div>
    <h1 id="san-yue-chou-kuan-kai-shi-la" class="align-center">三月籌款開始喇!</h1>
    <p class="align-center">多得好多人撐我哋,舊年我哋第一次有盈餘呀!<br><br>所以今年我哋增聘人手,撐返<br>病人、醫護、家屬,做返未完成嘅工作。<br><br>可惜我哋只係夠錢出一年人工,<br>而且仲有好多全新事工需要你嘅支持!</p>
</div>

From the live page (https://admin.nlt-chaplaincy.org/public/index.php), I can infer the static button and separator are likely something like:

html

<div class="content__buttons">
    <a class="btn btn--primary" href="#">了解更多</a>
</div>
<hr class="content__separator">

Goal

  • Replace the static

    and

    with dynamic content from web_content for each section.

  • Keep the static button and separator between sections.

  • Ensure pull-section.php handles both sections (WelcomeMessage and VideoTestimonial01) in a single loop, outputting the dynamic content while preserving the static elements between sections.

    • *

Can pull-section.php Achieve This?

Yes, a single pull-section.php can achieve this! Here’s how:

  • Dynamic Content: pull-section.php will loop through the web_content table to fetch sections for the current-campaign page. We’ll modify it to output the title, content, and additional fields (image for WelcomeMessage, video_embed for VideoTestimonial01) for each section, replacing the static

    and

    .

  • Static Elements: We’ll include the static button and separator in pull-section.php as part of the loop, so they appear between sections automatically.

  • Structure: We’ll wrap each section in a

    , matching your existing HTML structure, and append the static button and separator after each section (except the last one, if desired).

Challenges

  • Static Elements Placement: Since the static button and separator need to appear between sections, we’ll include them in pull-section.php’s loop, ensuring they’re output after each section.

  • Dynamic Replacement: We’ll remove the static

    and

    from index.php and let pull-section.php handle the dynamic content for both sections.

    • *

Updated Setup

Step 1: Update index.php in Publii

To replace the static content with dynamic content, we need to remove the static

and

from the index.php file and let pull-section.php handle the sections, including the static button and separator.

In Publii’s block editor:

  • Remove the static heading (

    三月籌款開始喇!

    ) and paragraph (

    多得好多人撐我哋...

    ).

  • Add a Raw HTML block with the following content to include the dynamic sections and static elements:

html

<?php include 'pull-section.php'; ?>

We’ll let pull-section.php handle the entire structure, including the static button and separator between sections.

Step 2: Update pull-section.php

We’ll modify pull-section.php to:

  • Output each section (WelcomeMessage and VideoTestimonial01) with its dynamic title, content, and additional fields (image or video_embed).

  • Include the static button and separator after each section, except the last one (to avoid an extra separator at the end).

Updated pull-section.php (place in Web/nlhcm/public/):

php

<?php
require_once '../config.php'; // Path from Web/nlhcm/public/ to Web/nlhcm/config.php
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
    echo "Connection failed: " . $conn->connect_error;
} else {
    // Use "current-campaign" as the page identifier, since the file is index.php
    $current_page = 'current-campaign';

    // Fetch all sections for the current page, ordered by position
    $stmt = $conn->prepare("SELECT section, title, content, image, video_embed 
                            FROM web_content 
                            WHERE page = ? AND status = ? 
                            ORDER BY position ASC");
    $status = 'Show';
    $stmt->bind_param("ss", $current_page, $status);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows > 0) {
        $sections = [];
        while ($row = $result->fetch_assoc()) {
            $sections[] = $row;
        }

        // Loop through sections and output each one
        $total_sections = count($sections);
        foreach ($sections as $index => $row) {
            echo '<div class="content__entry">';
            switch ($row['section']) {
                case 'WelcomeMessage':
                    echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                    echo '<div class="welcome-content align-center">' . $row['content'] . '</div>';
                    if ($row['image']) {
                        $image_url = 'https://admin.nlt-chaplaincy.org/images/' . htmlspecialchars($row['image']);
                        echo '<img src="' . $image_url . '" alt="' . htmlspecialchars($row['title']) . '" class="welcome-image">';
                    }
                    break;

                case 'VideoTestimonial01':
                    echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                    echo '<div class="video-content align-center">' . $row['content'] . '</div>';
                    if ($row['video_embed']) {
                        echo '<div class="video-embed">' . $row['video_embed'] . '</div>';
                    }
                    break;

                default:
                    echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                    echo '<div class="generic-content align-center">' . $row['content'] . '</div>';
                    break;
            }
            echo '</div>';

            // Add static button and separator after each section, except the last one
            if ($index < $total_sections - 1) {
                echo '<div class="content__buttons">';
                echo '<a class="btn btn--primary" href="#">了解更多</a>';
                echo '</div>';
                echo '<hr class="content__separator">';
            }
        }
    } else {
        echo '<p>No sections found for this page.</p>';
    }

    $stmt->close();
    $conn->close();
}
?>

What’s Updated in pull-section.php

  • Dynamic Content:

    • Outputs the title as an

      (matching your static heading style).

    • Outputs the content in a

      with align-center class (matching your static paragraph style).

    • For WelcomeMessage, includes the centered image.

    • For VideoTestimonial01, includes the iframe video embed.

  • Static Elements:

    • Adds the static button (了解更多) and separator (
      ) after each section, except the last one (using $index < $total_sections - 1 to avoid an extra separator at the end).
  • Structure:

    • Wraps each section in

      , matching your existing HTML structure.

    • Ensures the static button and separator appear between sections.

CSS (Updated)

Update the CSS in Publii (Settings > Custom CSS) to style the dynamic sections consistently with your theme:

css

.welcome-content, .video-content, .generic-content {
    text-align: center;
    margin: 0 auto;
    max-width: 800px;
}
.welcome-image {
    max-width: 100%;
    height: auto;
    margin: 1em auto;
    display: block;
}
.video-embed {
    position: relative;
    padding-bottom: 56.25%; /* 16:9 aspect ratio */
    height: 0;
    overflow: hidden;
    margin: 1em auto;
    max-width: 800px;
}
.video-embed iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
.content__buttons {
    text-align: center;
    margin: 1em 0;
}
.btn.btn--primary {
    background-color: #007bff;
    color: white;
    padding: 0.5em 1em;
    text-decoration: none;
    border-radius: 5px;
}
.content__separator {
    border: 0;
    border-top: 1px solid #ccc;
    margin: 2em 0;
}
  • What’s Updated:

    • Added styles for .welcome-content, .video-content, and .generic-content to ensure centered text.

    • Added basic styles for the static button (.btn.btn--primary) and separator (.content__separator) to match a typical Publii theme.

Test Data in web_content

Update the test records in AppGini to match your desired content:

id

page

section

title

content

image

video_embed

position

status

date_edited

edited_by

1

current-campaign

WelcomeMessage

三月籌款開始喇!

多得好多人撐我哋,舊年我哋第一次有盈餘呀!

所以今年我哋增聘人手,撐返

病人、醫護、家屬,做返未完成嘅工作。

可惜我哋只係夠錢出一年人工,

而且仲有好多全新事工需要你嘅支持!

a0735fe966d60ff51.jpg

NULL

1

Show

2025-03-22 10:00:00

pastor

2

current-campaign

VideoTestimonial01

—— 院牧部有咩用?

作用就大喇!我哋聽吓HA黃醫生佢點講,絕對無做馬嫁!...

NULL

2

Show

2025-03-22 10:05:00

pastor


Testing the Setup

  1. Publii:

    • Remove the static

      and

      from the page in the block editor.

    • Add a Raw HTML block with <?php include 'pull-section.php'; ?>.

    • Regenerate the site (Publii > Render Website).

    • Rename the generated index.html to index.php in the output folder.

    • Upload to Web/nlhcm/public/ (e.g., index.php, assets/ folder).

  2. QNAP:

  3. AppGini:

    • Add the test records above.

    • Ensure hooks/web_content.php is uploaded (it’s already correct from previous messages).

  4. Test:

    • Visit https://admin.nlt-chaplaincy.org/index.php.

    • Verify the output matches your desired structure:

      • WelcomeMessage:

        • 三月籌款開始喇!

        • 多得好多人撐我哋...
        • Centered image

        • Static button (了解更多)

        • Separator (


          )

      • VideoTestimonial01:

        • —— 院牧部有咩用?

        • 作用就大喇!...
        • Embedded video

        • Static button (了解更多)

        • Separator (


          )


Final Thoughts

  • Single pull-section.php: Yes, a single pull-section.php can achieve your desired output by looping through the sections, outputting dynamic content, and including static elements (button and separator) between sections.

  • Dynamic Replacement: The static

    and

    have been replaced with dynamic content from web_content, and the static button and separator are now part of the loop in pull-section.php.

  • Styling: The CSS ensures the dynamic sections align with your theme’s centered style.

You’re ready to test this setup! Let me know if you encounter any issues or want to adjust the layout further (e.g., styling the button differently).


Q: Wow! Exciting!! I want to use my custom CSS for two sizes of buttons. The big one will be for my manual adding. Please also modify the styles so that the button text won't be underlined. I want the button to look different when on hover and pressed without changing the text effect. You may decide on what looks good!


A: I’m glad you’re excited to move forward with your CMS project! Let’s integrate your custom CSS for the two button sizes (myButtonBig and myButton) into the pull-section.php output, ensuring the static buttons between sections use these styles. We’ll also modify the styles to remove the underline from the button text (which is already handled in your CSS with text-decoration: none), and enhance the hover and pressed states with visual effects that don’t alter the text, focusing on background, border, and shadow changes for a polished look.


Step 1: Integrate Custom Button Classes into pull-section.php

You’ve specified that the big button (myButtonBig) will be used for your manual additions, which I interpret as meaning you’ll manually add these buttons in Publii for other parts of the site. For the static buttons between sections in pull-section.php, we’ll use the smaller button (myButton) to maintain consistency with the dynamic sections.

Updated pull-section.php

Here’s the updated pull-section.php with the static buttons using the myButton class:

php

<?php
require_once '../config.php'; // Path from Web/nlhcm/public/ to Web/nlhcm/config.php
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
    echo "Connection failed: " . $conn->connect_error;
} else {
    // Use "current-campaign" as the page identifier, since the file is index.php
    $current_page = 'current-campaign';

    // Fetch all sections for the current page, ordered by position
    $stmt = $conn->prepare("SELECT section, title, content, image, video_embed 
                            FROM web_content 
                            WHERE page = ? AND status = ? 
                            ORDER BY position ASC");
    $status = 'Show';
    $stmt->bind_param("ss", $current_page, $status);
    $stmt->execute();
    $result = $stmt->get_result();

    if ($result->num_rows > 0) {
        $sections = [];
        while ($row = $result->fetch_assoc()) {
            $sections[] = $row;
        }

        // Loop through sections and output each one
        $total_sections = count($sections);
        foreach ($sections as $index => $row) {
            echo '<div class="content__entry">';
            switch ($row['section']) {
                case 'WelcomeMessage':
                    echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                    echo '<div class="welcome-content align-center">' . $row['content'] . '</div>';
                    if ($row['image']) {
                        $image_url = 'https://admin.nlt-chaplaincy.org/images/' . htmlspecialchars($row['image']);
                        echo '<img src="' . $image_url . '" alt="' . htmlspecialchars($row['title']) . '" class="welcome-image">';
                    }
                    break;

                case 'VideoTestimonial01':
                    echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                    echo '<div class="video-content align-center">' . $row['content'] . '</div>';
                    if ($row['video_embed']) {
                        echo '<div class="video-embed">' . $row['video_embed'] . '</div>';
                    }
                    break;

                default:
                    echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                    echo '<div class="generic-content align-center">' . $row['content'] . '</div>';
                    break;
            }
            echo '</div>';

            // Add static button and separator after each section, except the last one
            if ($index < $total_sections - 1) {
                echo '<div class="content__buttons">';
                echo '<a class="myButton" href="#">了解更多</a>';
                echo '</div>';
                echo '<hr class="content__separator">';
            }
        }
    } else {
        echo '<p>No sections found for this page.</p>';
    }

    $stmt->close();
    $conn->close();
}
?>
  • What’s Updated:

    • Changed the button class from btn btn--primary to myButton to use your custom smaller button style for the static buttons between sections.

    • Kept the rest of the structure (dynamic sections, separator) unchanged.

Note on myButtonBig

Since you mentioned that myButtonBig is for manual additions, I’ve used myButton for the buttons in pull-section.php. If you want to use myButtonBig for these static buttons instead, simply replace myButton with myButtonBig in the echo '了解更多'; line.


Step 2: Modify the Button Styles

Your current CSS for the buttons (myButtonBig and myButton) already has text-decoration: none, which ensures the button text isn’t underlined. We’ll enhance the hover and pressed states with visual effects that don’t affect the text, focusing on background, border, and shadow changes. I’ll also ensure the styles integrate well with the rest of the page.

Updated CSS

Add this updated CSS to Publii (Settings > Custom CSS), combining your button styles with the existing styles for the dynamic sections:

css

/*
 * Custom CSS for NLT Chaplaincy CMS
 */

/* Button Styles */
.myButtonBig {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: Arial;
    font-size: 21px;
    font-weight: bold;
    padding: 24px 42px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButtonBig:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButtonBig:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

.myButton {
    background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
    background-color: #44c767;
    border-radius: 28px;
    border: 1px solid #18ab29;
    display: inline-block;
    cursor: pointer;
    color: #ffffff;
    font-family: Arial;
    font-size: 17px;
    font-weight: bold;
    padding: 16px 31px;
    text-decoration: none;
    text-shadow: 0px 1px 0px #2f6627;
    transition: all 0.3s ease;
}
.myButton:hover {
    background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
    background-color: #5cbf2a;
    border-color: #137c21;
    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.myButton:active {
    position: relative;
    top: 1px;
    background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
    background-color: #3ba052;
    border-color: #0f5c18;
    box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
}

/* Dynamic Section Styles */
.welcome-content, .video-content, .generic-content {
    text-align: center;
    margin: 0 auto;
    max-width: 800px;
}
.welcome-image {
    max-width: 100%;
    height: auto;
    margin: 1em auto;
    display: block;
}
.video-embed {
    position: relative;
    padding-bottom: 56.25%; /* 16:9 aspect ratio */
    height: 0;
    overflow: hidden;
    margin: 1em auto;
    max-width: 800px;
}
.video-embed iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
.content__buttons {
    text-align: center;
    margin: 1em 0;
}
.content__separator {
    border: 0;
    border-top: 1px solid #ccc;
    margin: 2em 0;
}

What’s Updated in the CSS

  • Text Underline: Your original CSS already has text-decoration: none, so the button text isn’t underlined. I’ve kept this to ensure consistency.

  • Hover Effects (for both myButtonBig and myButton):

    • Added a darker border color (border-color: #137c21) to give a subtle outline change.

    • Added a box shadow (box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2)) for a slight lift effect on hover.

    • Kept the background gradient swap (#5cbf2a to #44c767) for a smooth color transition.

    • Added transition: all 0.3s ease to make the hover effects smooth.

  • Pressed (Active) Effects:

    • Kept the top: 1px shift to simulate a press.

    • Added a darker gradient (#3ba052 to #4aa62b) to indicate the press.

    • Changed the border color to a darker shade (#0f5c18) for contrast.

    • Added an inset shadow (box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3)) to give a pressed-in effect.

  • Text Effects: The text (color, shadow, font) remains unchanged on hover and press, as requested, ensuring the focus is on the button’s background, border, and shadow.

  • Dynamic Sections: The styles for .welcome-content, .video-content, etc., remain unchanged to ensure the dynamic sections (e.g., WelcomeMessage, VideoTestimonial01) are styled consistently.

Why These Changes Look Good

  • Hover: The combination of a gradient swap, darker border, and subtle shadow gives the button a “lifted” effect, making it feel interactive without altering the text.

  • Pressed: The darker gradient, inset shadow, and slight downward shift (top: 1px) simulate a physical press, providing clear feedback to the user.

  • Consistency: The effects are applied to both myButtonBig and myButton, ensuring a cohesive look across your site.

    • *

Testing the Updated Setup

  1. Publii:

    • Ensure the page content in the block editor only contains the Raw HTML block with <?php include 'pull-section.php'; ?>.

    • Add the updated CSS to Settings > Custom CSS.

    • Regenerate the site (Publii > Render Website).

    • Rename the generated index.html to index.php.

    • Upload to Web/nlhcm/public/ (e.g., index.php, assets/ folder).

  2. QNAP:

    • Place the updated pull-section.php in Web/nlhcm/public/.

    • Ensure config.php is in Web/nlhcm/.

  3. AppGini:

    • Ensure the test records are in the web_content table (from previous messages):

      id

      page

      section

      title

      content

      image

      video_embed

      position

      status

      date_edited

      edited_by

      1

      current-campaign

      WelcomeMessage

      三月籌款開始喇!

      多得好多人撐我哋,舊年我哋第一次有盈餘呀!

      所以今年我哋增聘人手,撐返

      病人、醫護、家屬,做返未完成嘅工作。

      可惜我哋只係夠錢出一年人工,

      而且仲有好多全新事工需要你嘅支持!

      a0735fe966d60ff51.jpg

      NULL

      1

      Show

      2025-03-22 10:00:00

      pastor

      2

      current-campaign

      VideoTestimonial01

      —— 院牧部有咩用?

      作用就大喇!我哋聽吓HA黃醫生佢點講,絕對無做馬嫁!...

      NULL

      2

      Show

      2025-03-22 10:05:00

      pastor

  4. Test:

    • Visit https://admin.nlt-chaplaincy.org/index.php.

    • Verify:

      • The WelcomeMessage section displays the dynamic heading, content, and image.

      • The VideoTestimonial01 section displays the dynamic heading, content, and video embed.

      • The static button between sections uses the myButton style, with no text underline.

      • On hover, the button shows a gradient swap, darker border, and shadow.

      • On press, the button shifts down, darkens, and shows an inset shadow.


Final Thoughts

  • Custom Buttons: The static buttons between sections now use your myButton class, with myButtonBig available for your manual additions elsewhere.

  • No Text Underline: The text-decoration: none ensures the button text isn’t underlined, as requested.

  • Hover and Pressed Effects: The updated hover and pressed states use background, border, and shadow effects to provide visual feedback without altering the text, creating a clean and interactive look.

You’re all set to test this updated setup! Let me know if you’d like to tweak the button styles further or if you encounter any issues during testing.


<>

Q: Oh thanks! The sections are now showing successfully, except that the YouTube video part is showing the following inside a black box :

An error occurred. Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser.The video embed code in web_content is:The field it is in ("video_embed") is a Text view and a text area is used for the UI.


A: I’m glad to hear that the sections are now displaying successfully on your page! The issue with the YouTube video embed showing the error "An error occurred. Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser" inside a black box indicates a problem with how the YouTube iframe is being rendered. Let’s break this down and fix the issue step-by-step.


Step 1: Analyze the Issue

Observations

  • YouTube Embed Code: The video_embed field in the web_content table contains the following iframe:

    html

    <iframe sandbox width="560" height="315" src="https://www.youtube.com/embed/d1RNS4vlBvY?si=pRsnanOVpEBQ5PDO" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
    • This is a standard YouTube embed code, and the video ID (d1RNS4vlBvY) and parameters (e.g., ?si=pRsnanOVpEBQ5PDO) look correct.

    • The allow attributes and referrerpolicy are also standard for YouTube embeds.

  • Error Message: The error "An error occurred. Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser" typically indicates one of the following:

    1. JavaScript Issue: YouTube embeds require JavaScript to load the video player. If JavaScript is disabled in the browser, this error appears. However, since your site is likely a modern web page, JavaScript is probably enabled.

    2. Sandbox Attribute: The sandbox attribute on the iframe is restricting the video from loading. By default, an iframe with the sandbox attribute enables a very restrictive set of permissions, which can block scripts (including YouTube’s player scripts) unless explicitly allowed.

    3. Content Security Policy (CSP): Your site might have a CSP that blocks the YouTube domain (https://www.youtube.com) or its scripts.

    4. Network or Browser Restrictions: The browser or network might be blocking access to YouTube (e.g., due to ad blockers, privacy extensions, or regional restrictions).

    5. Rendering Issue: The iframe might not be rendering correctly due to how the video_embed field is output in pull-section.php.

How pull-section.php Outputs the Video

In pull-section.php, the VideoTestimonial01 section outputs the video_embed field directly:

php

if ($row['video_embed']) {
    echo '<div class="video-embed">' . $row['video_embed'] . '</div>';
}
  • The video_embed field is stored as a text string in the database, and we’re echoing it directly into the HTML.

  • Since the field contains HTML ()

  • Static Elements:

    • Button (Static)

    • Separator (Static)

  • Current index.php Snippet

    From your previous snippet, the static content looks like this:

    html

    <div class="content__entry">
        <div><?php include 'pull-section.php'; ?></div>
        <h1 id="san-yue-chou-kuan-kai-shi-la" class="align-center">三月籌款開始喇!</h1>
        <p class="align-center">多得好多人撐我哋,舊年我哋第一次有盈餘呀!<br><br>所以今年我哋增聘人手,撐返<br>病人、醫護、家屬,做返未完成嘅工作。<br><br>可惜我哋只係夠錢出一年人工,<br>而且仲有好多全新事工需要你嘅支持!</p>
    </div>

    From the live page (https://admin.nlt-chaplaincy.org/public/index.php), I can infer the static button and separator are likely something like:

    html

    <div class="content__buttons">
        <a class="btn btn--primary" href="#">了解更多</a>
    </div>
    <hr class="content__separator">

    Goal

    • Replace the static

      and

      with dynamic content from web_content for each section.

    • Keep the static button and separator between sections.

    • Ensure pull-section.php handles both sections (WelcomeMessage and VideoTestimonial01) in a single loop, outputting the dynamic content while preserving the static elements between sections.

      • *

    Can pull-section.php Achieve This?

    Yes, a single pull-section.php can achieve this! Here’s how:

    • Dynamic Content: pull-section.php will loop through the web_content table to fetch sections for the current-campaign page. We’ll modify it to output the title, content, and additional fields (image for WelcomeMessage, video_embed for VideoTestimonial01) for each section, replacing the static

      and

      .

    • Static Elements: We’ll include the static button and separator in pull-section.php as part of the loop, so they appear between sections automatically.

    • Structure: We’ll wrap each section in a

      , matching your existing HTML structure, and append the static button and separator after each section (except the last one, if desired).

    Challenges

    • Static Elements Placement: Since the static button and separator need to appear between sections, we’ll include them in pull-section.php’s loop, ensuring they’re output after each section.

    • Dynamic Replacement: We’ll remove the static

      and

      from index.php and let pull-section.php handle the dynamic content for both sections.

      • *

    Updated Setup

    Step 1: Update index.php in Publii

    To replace the static content with dynamic content, we need to remove the static

    and

    from the index.php file and let pull-section.php handle the sections, including the static button and separator.

    In Publii’s block editor:

    • Remove the static heading (

      三月籌款開始喇!

      ) and paragraph (

      多得好多人撐我哋...

      ).

    • Add a Raw HTML block with the following content to include the dynamic sections and static elements:

    html

    <?php include 'pull-section.php'; ?>

    We’ll let pull-section.php handle the entire structure, including the static button and separator between sections.

    Step 2: Update pull-section.php

    We’ll modify pull-section.php to:

    • Output each section (WelcomeMessage and VideoTestimonial01) with its dynamic title, content, and additional fields (image or video_embed).

    • Include the static button and separator after each section, except the last one (to avoid an extra separator at the end).

    Updated pull-section.php (place in Web/nlhcm/public/):

    php

    <?php
    require_once '../config.php'; // Path from Web/nlhcm/public/ to Web/nlhcm/config.php
    $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    if ($conn->connect_error) {
        echo "Connection failed: " . $conn->connect_error;
    } else {
        // Use "current-campaign" as the page identifier, since the file is index.php
        $current_page = 'current-campaign';
    
        // Fetch all sections for the current page, ordered by position
        $stmt = $conn->prepare("SELECT section, title, content, image, video_embed 
                                FROM web_content 
                                WHERE page = ? AND status = ? 
                                ORDER BY position ASC");
        $status = 'Show';
        $stmt->bind_param("ss", $current_page, $status);
        $stmt->execute();
        $result = $stmt->get_result();
    
        if ($result->num_rows > 0) {
            $sections = [];
            while ($row = $result->fetch_assoc()) {
                $sections[] = $row;
            }
    
            // Loop through sections and output each one
            $total_sections = count($sections);
            foreach ($sections as $index => $row) {
                echo '<div class="content__entry">';
                switch ($row['section']) {
                    case 'WelcomeMessage':
                        echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                        echo '<div class="welcome-content align-center">' . $row['content'] . '</div>';
                        if ($row['image']) {
                            $image_url = 'https://admin.nlt-chaplaincy.org/images/' . htmlspecialchars($row['image']);
                            echo '<img src="' . $image_url . '" alt="' . htmlspecialchars($row['title']) . '" class="welcome-image">';
                        }
                        break;
    
                    case 'VideoTestimonial01':
                        echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                        echo '<div class="video-content align-center">' . $row['content'] . '</div>';
                        if ($row['video_embed']) {
                            echo '<div class="video-embed">' . $row['video_embed'] . '</div>';
                        }
                        break;
    
                    default:
                        echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                        echo '<div class="generic-content align-center">' . $row['content'] . '</div>';
                        break;
                }
                echo '</div>';
    
                // Add static button and separator after each section, except the last one
                if ($index < $total_sections - 1) {
                    echo '<div class="content__buttons">';
                    echo '<a class="btn btn--primary" href="#">了解更多</a>';
                    echo '</div>';
                    echo '<hr class="content__separator">';
                }
            }
        } else {
            echo '<p>No sections found for this page.</p>';
        }
    
        $stmt->close();
        $conn->close();
    }
    ?>

    What’s Updated in pull-section.php

    • Dynamic Content:

      • Outputs the title as an

        (matching your static heading style).

      • Outputs the content in a

        with align-center class (matching your static paragraph style).

      • For WelcomeMessage, includes the centered image.

      • For VideoTestimonial01, includes the iframe video embed.

    • Static Elements:

      • Adds the static button (了解更多) and separator (
        ) after each section, except the last one (using $index < $total_sections - 1 to avoid an extra separator at the end).
    • Structure:

      • Wraps each section in

        , matching your existing HTML structure.

      • Ensures the static button and separator appear between sections.

    CSS (Updated)

    Update the CSS in Publii (Settings > Custom CSS) to style the dynamic sections consistently with your theme:

    css

    .welcome-content, .video-content, .generic-content {
        text-align: center;
        margin: 0 auto;
        max-width: 800px;
    }
    .welcome-image {
        max-width: 100%;
        height: auto;
        margin: 1em auto;
        display: block;
    }
    .video-embed {
        position: relative;
        padding-bottom: 56.25%; /* 16:9 aspect ratio */
        height: 0;
        overflow: hidden;
        margin: 1em auto;
        max-width: 800px;
    }
    .video-embed iframe {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
    }
    .content__buttons {
        text-align: center;
        margin: 1em 0;
    }
    .btn.btn--primary {
        background-color: #007bff;
        color: white;
        padding: 0.5em 1em;
        text-decoration: none;
        border-radius: 5px;
    }
    .content__separator {
        border: 0;
        border-top: 1px solid #ccc;
        margin: 2em 0;
    }
    • What’s Updated:

      • Added styles for .welcome-content, .video-content, and .generic-content to ensure centered text.

      • Added basic styles for the static button (.btn.btn--primary) and separator (.content__separator) to match a typical Publii theme.

    Test Data in web_content

    Update the test records in AppGini to match your desired content:

    id

    page

    section

    title

    content

    image

    video_embed

    position

    status

    date_edited

    edited_by

    1

    current-campaign

    WelcomeMessage

    三月籌款開始喇!

    多得好多人撐我哋,舊年我哋第一次有盈餘呀!

    所以今年我哋增聘人手,撐返

    病人、醫護、家屬,做返未完成嘅工作。

    可惜我哋只係夠錢出一年人工,

    而且仲有好多全新事工需要你嘅支持!

    a0735fe966d60ff51.jpg

    NULL

    1

    Show

    2025-03-22 10:00:00

    pastor

    2

    current-campaign

    VideoTestimonial01

    —— 院牧部有咩用?

    作用就大喇!我哋聽吓HA黃醫生佢點講,絕對無做馬嫁!...

    NULL

    2

    Show

    2025-03-22 10:05:00

    pastor


    Testing the Setup

    1. Publii:

      • Remove the static

        and

        from the page in the block editor.

      • Add a Raw HTML block with <?php include 'pull-section.php'; ?>.

      • Regenerate the site (Publii > Render Website).

      • Rename the generated index.html to index.php in the output folder.

      • Upload to Web/nlhcm/public/ (e.g., index.php, assets/ folder).

    2. QNAP:

    3. AppGini:

      • Add the test records above.

      • Ensure hooks/web_content.php is uploaded (it’s already correct from previous messages).

    4. Test:

      • Visit https://admin.nlt-chaplaincy.org/index.php.

      • Verify the output matches your desired structure:

        • WelcomeMessage:

          • 三月籌款開始喇!

          • 多得好多人撐我哋...
          • Centered image

          • Static button (了解更多)

          • Separator (


            )

        • VideoTestimonial01:

          • —— 院牧部有咩用?

          • 作用就大喇!...
          • Embedded video

          • Static button (了解更多)

          • Separator (


            )


    Final Thoughts

    • Single pull-section.php: Yes, a single pull-section.php can achieve your desired output by looping through the sections, outputting dynamic content, and including static elements (button and separator) between sections.

    • Dynamic Replacement: The static

      and

      have been replaced with dynamic content from web_content, and the static button and separator are now part of the loop in pull-section.php.

    • Styling: The CSS ensures the dynamic sections align with your theme’s centered style.

    You’re ready to test this setup! Let me know if you encounter any issues or want to adjust the layout further (e.g., styling the button differently).


    Q: Wow! Exciting!! I want to use my custom CSS for two sizes of buttons. The big one will be for my manual adding. Please also modify the styles so that the button text won't be underlined. I want the button to look different when on hover and pressed without changing the text effect. You may decide on what looks good!


    A: I’m glad you’re excited to move forward with your CMS project! Let’s integrate your custom CSS for the two button sizes (myButtonBig and myButton) into the pull-section.php output, ensuring the static buttons between sections use these styles. We’ll also modify the styles to remove the underline from the button text (which is already handled in your CSS with text-decoration: none), and enhance the hover and pressed states with visual effects that don’t alter the text, focusing on background, border, and shadow changes for a polished look.


    Step 1: Integrate Custom Button Classes into pull-section.php

    You’ve specified that the big button (myButtonBig) will be used for your manual additions, which I interpret as meaning you’ll manually add these buttons in Publii for other parts of the site. For the static buttons between sections in pull-section.php, we’ll use the smaller button (myButton) to maintain consistency with the dynamic sections.

    Updated pull-section.php

    Here’s the updated pull-section.php with the static buttons using the myButton class:

    php

    <?php
    require_once '../config.php'; // Path from Web/nlhcm/public/ to Web/nlhcm/config.php
    $conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    if ($conn->connect_error) {
        echo "Connection failed: " . $conn->connect_error;
    } else {
        // Use "current-campaign" as the page identifier, since the file is index.php
        $current_page = 'current-campaign';
    
        // Fetch all sections for the current page, ordered by position
        $stmt = $conn->prepare("SELECT section, title, content, image, video_embed 
                                FROM web_content 
                                WHERE page = ? AND status = ? 
                                ORDER BY position ASC");
        $status = 'Show';
        $stmt->bind_param("ss", $current_page, $status);
        $stmt->execute();
        $result = $stmt->get_result();
    
        if ($result->num_rows > 0) {
            $sections = [];
            while ($row = $result->fetch_assoc()) {
                $sections[] = $row;
            }
    
            // Loop through sections and output each one
            $total_sections = count($sections);
            foreach ($sections as $index => $row) {
                echo '<div class="content__entry">';
                switch ($row['section']) {
                    case 'WelcomeMessage':
                        echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                        echo '<div class="welcome-content align-center">' . $row['content'] . '</div>';
                        if ($row['image']) {
                            $image_url = 'https://admin.nlt-chaplaincy.org/images/' . htmlspecialchars($row['image']);
                            echo '<img src="' . $image_url . '" alt="' . htmlspecialchars($row['title']) . '" class="welcome-image">';
                        }
                        break;
    
                    case 'VideoTestimonial01':
                        echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                        echo '<div class="video-content align-center">' . $row['content'] . '</div>';
                        if ($row['video_embed']) {
                            echo '<div class="video-embed">' . $row['video_embed'] . '</div>';
                        }
                        break;
    
                    default:
                        echo '<h1 class="align-center">' . htmlspecialchars($row['title']) . '</h1>';
                        echo '<div class="generic-content align-center">' . $row['content'] . '</div>';
                        break;
                }
                echo '</div>';
    
                // Add static button and separator after each section, except the last one
                if ($index < $total_sections - 1) {
                    echo '<div class="content__buttons">';
                    echo '<a class="myButton" href="#">了解更多</a>';
                    echo '</div>';
                    echo '<hr class="content__separator">';
                }
            }
        } else {
            echo '<p>No sections found for this page.</p>';
        }
    
        $stmt->close();
        $conn->close();
    }
    ?>
    • What’s Updated:

      • Changed the button class from btn btn--primary to myButton to use your custom smaller button style for the static buttons between sections.

      • Kept the rest of the structure (dynamic sections, separator) unchanged.

    Note on myButtonBig

    Since you mentioned that myButtonBig is for manual additions, I’ve used myButton for the buttons in pull-section.php. If you want to use myButtonBig for these static buttons instead, simply replace myButton with myButtonBig in the echo '了解更多'; line.


    Step 2: Modify the Button Styles

    Your current CSS for the buttons (myButtonBig and myButton) already has text-decoration: none, which ensures the button text isn’t underlined. We’ll enhance the hover and pressed states with visual effects that don’t affect the text, focusing on background, border, and shadow changes. I’ll also ensure the styles integrate well with the rest of the page.

    Updated CSS

    Add this updated CSS to Publii (Settings > Custom CSS), combining your button styles with the existing styles for the dynamic sections:

    css

    /*
     * Custom CSS for NLT Chaplaincy CMS
     */
    
    /* Button Styles */
    .myButtonBig {
        background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
        background-color: #44c767;
        border-radius: 28px;
        border: 1px solid #18ab29;
        display: inline-block;
        cursor: pointer;
        color: #ffffff;
        font-family: Arial;
        font-size: 21px;
        font-weight: bold;
        padding: 24px 42px;
        text-decoration: none;
        text-shadow: 0px 1px 0px #2f6627;
        transition: all 0.3s ease;
    }
    .myButtonBig:hover {
        background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
        background-color: #5cbf2a;
        border-color: #137c21;
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    }
    .myButtonBig:active {
        position: relative;
        top: 1px;
        background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
        background-color: #3ba052;
        border-color: #0f5c18;
        box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
    }
    
    .myButton {
        background: linear-gradient(to bottom, #44c767 5%, #5cbf2a 100%);
        background-color: #44c767;
        border-radius: 28px;
        border: 1px solid #18ab29;
        display: inline-block;
        cursor: pointer;
        color: #ffffff;
        font-family: Arial;
        font-size: 17px;
        font-weight: bold;
        padding: 16px 31px;
        text-decoration: none;
        text-shadow: 0px 1px 0px #2f6627;
        transition: all 0.3s ease;
    }
    .myButton:hover {
        background: linear-gradient(to bottom, #5cbf2a 5%, #44c767 100%);
        background-color: #5cbf2a;
        border-color: #137c21;
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
    }
    .myButton:active {
        position: relative;
        top: 1px;
        background: linear-gradient(to bottom, #3ba052 5%, #4aa62b 100%);
        background-color: #3ba052;
        border-color: #0f5c18;
        box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3);
    }
    
    /* Dynamic Section Styles */
    .welcome-content, .video-content, .generic-content {
        text-align: center;
        margin: 0 auto;
        max-width: 800px;
    }
    .welcome-image {
        max-width: 100%;
        height: auto;
        margin: 1em auto;
        display: block;
    }
    .video-embed {
        position: relative;
        padding-bottom: 56.25%; /* 16:9 aspect ratio */
        height: 0;
        overflow: hidden;
        margin: 1em auto;
        max-width: 800px;
    }
    .video-embed iframe {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
    }
    .content__buttons {
        text-align: center;
        margin: 1em 0;
    }
    .content__separator {
        border: 0;
        border-top: 1px solid #ccc;
        margin: 2em 0;
    }

    What’s Updated in the CSS

    • Text Underline: Your original CSS already has text-decoration: none, so the button text isn’t underlined. I’ve kept this to ensure consistency.

    • Hover Effects (for both myButtonBig and myButton):

      • Added a darker border color (border-color: #137c21) to give a subtle outline change.

      • Added a box shadow (box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2)) for a slight lift effect on hover.

      • Kept the background gradient swap (#5cbf2a to #44c767) for a smooth color transition.

      • Added transition: all 0.3s ease to make the hover effects smooth.

    • Pressed (Active) Effects:

      • Kept the top: 1px shift to simulate a press.

      • Added a darker gradient (#3ba052 to #4aa62b) to indicate the press.

      • Changed the border color to a darker shade (#0f5c18) for contrast.

      • Added an inset shadow (box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.3)) to give a pressed-in effect.

    • Text Effects: The text (color, shadow, font) remains unchanged on hover and press, as requested, ensuring the focus is on the button’s background, border, and shadow.

    • Dynamic Sections: The styles for .welcome-content, .video-content, etc., remain unchanged to ensure the dynamic sections (e.g., WelcomeMessage, VideoTestimonial01) are styled consistently.

    Why These Changes Look Good

    • Hover: The combination of a gradient swap, darker border, and subtle shadow gives the button a “lifted” effect, making it feel interactive without altering the text.

    • Pressed: The darker gradient, inset shadow, and slight downward shift (top: 1px) simulate a physical press, providing clear feedback to the user.

    • Consistency: The effects are applied to both myButtonBig and myButton, ensuring a cohesive look across your site.

      • *

    Testing the Updated Setup

    1. Publii:

      • Ensure the page content in the block editor only contains the Raw HTML block with <?php include 'pull-section.php'; ?>.

      • Add the updated CSS to Settings > Custom CSS.

      • Regenerate the site (Publii > Render Website).

      • Rename the generated index.html to index.php.

      • Upload to Web/nlhcm/public/ (e.g., index.php, assets/ folder).

    2. QNAP:

      • Place the updated pull-section.php in Web/nlhcm/public/.

      • Ensure config.php is in Web/nlhcm/.

    3. AppGini:

      • Ensure the test records are in the web_content table (from previous messages):

        id

        page

        section

        title

        content

        image

        video_embed

        position

        status

        date_edited

        edited_by

        1

        current-campaign

        WelcomeMessage

        三月籌款開始喇!

        多得好多人撐我哋,舊年我哋第一次有盈餘呀!

        所以今年我哋增聘人手,撐返

        病人、醫護、家屬,做返未完成嘅工作。

        可惜我哋只係夠錢出一年人工,

        而且仲有好多全新事工需要你嘅支持!

        a0735fe966d60ff51.jpg

        NULL

        1

        Show

        2025-03-22 10:00:00

        pastor

        2

        current-campaign

        VideoTestimonial01

        —— 院牧部有咩用?

        作用就大喇!我哋聽吓HA黃醫生佢點講,絕對無做馬嫁!...

        NULL

        2

        Show

        2025-03-22 10:05:00

        pastor

    4. Test:

      • Visit https://admin.nlt-chaplaincy.org/index.php.

      • Verify:

        • The WelcomeMessage section displays the dynamic heading, content, and image.

        • The VideoTestimonial01 section displays the dynamic heading, content, and video embed.

        • The static button between sections uses the myButton style, with no text underline.

        • On hover, the button shows a gradient swap, darker border, and shadow.

        • On press, the button shifts down, darkens, and shows an inset shadow.


    Final Thoughts

    • Custom Buttons: The static buttons between sections now use your myButton class, with myButtonBig available for your manual additions elsewhere.

    • No Text Underline: The text-decoration: none ensures the button text isn’t underlined, as requested.

    • Hover and Pressed Effects: The updated hover and pressed states use background, border, and shadow effects to provide visual feedback without altering the text, creating a clean and interactive look.

    You’re all set to test this updated setup! Let me know if you’d like to tweak the button styles further or if you encounter any issues during testing.


    <>

    Q: Oh thanks! The sections are now showing successfully, except that the YouTube video part is showing the following inside a black box :

    An error occurred. Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser.The video embed code in web_content is:The field it is in ("video_embed") is a Text view and a text area is used for the UI.


    A: I’m glad to hear that the sections are now displaying successfully on your page! The issue with the YouTube video embed showing the error "An error occurred. Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser" inside a black box indicates a problem with how the YouTube iframe is being rendered. Let’s break this down and fix the issue step-by-step.


    Step 1: Analyze the Issue

    Observations

    • YouTube Embed Code: The video_embed field in the web_content table contains the following iframe:

      html

      <iframe sandbox width="560" height="315" src="https://www.youtube.com/embed/d1RNS4vlBvY?si=pRsnanOVpEBQ5PDO" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>
      • This is a standard YouTube embed code, and the video ID (d1RNS4vlBvY) and parameters (e.g., ?si=pRsnanOVpEBQ5PDO) look correct.

      • The allow attributes and referrerpolicy are also standard for YouTube embeds.

    • Error Message: The error "An error occurred. Try watching this video on www.youtube.com, or enable JavaScript if it is disabled in your browser" typically indicates one of the following:

      1. JavaScript Issue: YouTube embeds require JavaScript to load the video player. If JavaScript is disabled in the browser, this error appears. However, since your site is likely a modern web page, JavaScript is probably enabled.

      2. Sandbox Attribute: The sandbox attribute on the iframe is restricting the video from loading. By default, an iframe with the sandbox attribute enables a very restrictive set of permissions, which can block scripts (including YouTube’s player scripts) unless explicitly allowed.

      3. Content Security Policy (CSP): Your site might have a CSP that blocks the YouTube domain (https://www.youtube.com) or its scripts.

      4. Network or Browser Restrictions: The browser or network might be blocking access to YouTube (e.g., due to ad blockers, privacy extensions, or regional restrictions).

      5. Rendering Issue: The iframe might not be rendering correctly due to how the video_embed field is output in pull-section.php.

    How pull-section.php Outputs the Video

    In pull-section.php, the VideoTestimonial01 section outputs the video_embed field directly:

    php

    if ($row['video_embed']) {
        echo '<div class="video-embed">' . $row['video_embed'] . '</div>';
    }
    • The video_embed field is stored as a text string in the database, and we’re echoing it directly into the HTML.

    • Since the field contains HTML (