Files

530 lines
20 KiB
Lua
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

log("Loading Comprehensive Example Plugin...")
-- ============================================================================
-- PART 1: HOOK CHAINING DEMONSTRATION
-- Multiple hooks that modify HTML content sequentially
-- ============================================================================
-- Hook 1: Add custom CSS (Priority 10 - runs first)
register_hook("post_render", function(filepath, html_content)
log("Hook 1 (Priority 10): Adding custom CSS to " .. filepath)
local custom_css = [[
<style>
.plugin-banner {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px;
text-align: center;
font-family: 'Arial', sans-serif;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.plugin-footer {
background: #2d3748;
color: #cbd5e0;
padding: 20px;
text-align: center;
margin-top: 40px;
font-size: 0.9em;
}
.enhanced-code {
border-left: 4px solid #667eea;
padding-left: 10px;
}
</style>
]]
-- Insert CSS before closing head tag
local modified = html_insert_before(html_content, "</head>", custom_css)
return modified -- Pass to next hook
end, 10)
-- Hook 2: Add banner (Priority 20 - runs second)
register_hook("post_render", function(filepath, html_content)
log("Hook 2 (Priority 20): Adding banner")
local banner = [[
<div class="plugin-banner">
<strong>🚀 Enhanced by Plugin System</strong> |
<span style="opacity: 0.8;">Processing: ]] .. filepath .. [[</span>
</div>
]]
local modified = html_insert_after(html_content, "<body>", banner)
return modified
end, 20)
-- Hook 3: Enhance code blocks (Priority 30 - runs third)
register_hook("post_render", function(filepath, html_content)
log("Hook 3 (Priority 30): Enhancing code blocks")
-- Add class to all code tags
local modified = html_add_class(html_content, "code", "enhanced-code")
modified = html_add_attribute(modified, "pre", "data-enhanced", "true")
return modified
end, 30)
-- Hook 4: Add footer (Priority 40 - runs last)
register_hook("post_render", function(filepath, html_content)
log("Hook 4 (Priority 40): Adding footer")
local footer = [[
<div class="plugin-footer">
<p>🔌 Powered by PyPost Plugin System</p>
<p>Generated: ]] .. os.date("%Y-%m-%d %H:%M:%S") .. [[</p>
<p style="font-size: 0.8em; opacity: 0.7;">
Processed through 4 sequential hooks
</p>
</div>
]]
local modified = html_insert_before(html_content, "</body>", footer)
return modified
end, 40)
-- ============================================================================
-- PART 2: GET ROUTES
-- Demonstrate various GET route patterns
-- ============================================================================
-- Simple text response
add_get_route("/plugin/hello", function(req)
return "<h1>Hello from Plugin!</h1><p>This is a simple GET route.</p>"
end, 50)
-- JSON API endpoint
add_get_route("/plugin/api/info", function(req)
local info = {
plugin_name = "Comprehensive Example",
version = "1.0.0",
features = {"hooks", "routes", "file_ops"},
html_files = #list_html_files(),
markdown_files = #list_markdown_files(),
timestamp = os.date("%Y-%m-%d %H:%M:%S")
}
return 200,
{["Content-Type"] = "application/json"},
table_to_json(info)
end, 50)
-- File listing endpoint
add_get_route("/plugin/files", function(req)
local html_files = list_html_files()
local md_files = list_markdown_files()
local html = [[
<!DOCTYPE html>
<html>
<head>
<title>File Browser</title>
<style>
body { font-family: Arial; max-width: 1000px; margin: 0 auto; padding: 20px; background: #f5f5f5; }
.file-list { background: white; padding: 20px; border-radius: 8px; margin: 20px 0; }
.file-item { padding: 10px; border-bottom: 1px solid #eee; }
.file-item:hover { background: #f9f9f9; }
h2 { color: #667eea; }
</style>
</head>
<body>
<h1>📁 File Browser</h1>
<div class="file-list">
<h2>HTML Files (]] .. #html_files .. [[)</h2>
]]
for i, file in ipairs(html_files) do
html = html .. ' <div class="file-item">📄 ' .. file .. '</div>\n'
end
html = html .. [[
</div>
<div class="file-list">
<h2>Markdown Files (]] .. #md_files .. [[)</h2>
]]
for i, file in ipairs(md_files) do
html = html .. ' <div class="file-item">📝 ' .. file .. '</div>\n'
end
html = html .. [[
</div>
</body>
</html>
]]
return html
end, 50)
-- ============================================================================
-- PART 3: POST ROUTES
-- Handle form submissions and API requests
-- ============================================================================
-- Contact form submission
add_post_route("/plugin/api/contact", function(req)
local data = req.data or {}
log("Contact form received:")
log(" Name: " .. (data.name or "N/A"))
log(" Email: " .. (data.email or "N/A"))
log(" Message: " .. (data.message or "N/A"))
-- Validation
if not data.name or data.name == "" then
return 400,
{["Content-Type"] = "application/json"},
table_to_json({success = false, error = "Name is required"})
end
if not data.message or data.message == "" then
return 400,
{["Content-Type"] = "application/json"},
table_to_json({success = false, error = "Message is required"})
end
-- Save to file (example)
local timestamp = os.date("%Y-%m-%d %H:%M:%S")
local submission = string_join({
"---",
"Name: " .. data.name,
"Email: " .. (data.email or "N/A"),
"Time: " .. timestamp,
"Message:",
data.message,
"---",
""
}, "\n")
-- Note: In production, you'd want better file handling
-- write_file("submissions.txt", submission)
local response = {
success = true,
message = "Thank you for your submission!",
received_at = timestamp
}
return 200,
{["Content-Type"] = "application/json"},
table_to_json(response)
end, 50)
-- File upload/create endpoint
add_post_route("/plugin/api/create-note", function(req)
local data = req.data or {}
local title = data.title or "Untitled"
local content = data.content or ""
if content == "" then
return 400,
{["Content-Type"] = "application/json"},
table_to_json({success = false, error = "Content cannot be empty"})
end
-- Create markdown file
local filename = string_replace(title, " ", "-") .. ".md"
filename = string.lower(filename)
local markdown_content = md_add_header("", 1, title)
markdown_content = md_append_content(markdown_content, content)
markdown_content = md_append_content(markdown_content,
"\n---\n*Created by plugin at " .. os.date("%Y-%m-%d %H:%M:%S") .. "*")
-- Write the file
local success = write_markdown(filename, markdown_content)
if success then
log("Created new markdown file: " .. filename)
return 200,
{["Content-Type"] = "application/json"},
table_to_json({
success = true,
message = "Note created successfully",
filename = filename
})
else
return 500,
{["Content-Type"] = "application/json"},
table_to_json({
success = false,
error = "Failed to create file"
})
end
end, 50)
-- ============================================================================
-- PART 4: DEMONSTRATION DASHBOARD
-- A full-featured page showing all capabilities
-- ============================================================================
add_get_route("/plugin/dashboard", function(req)
local html_count = #list_html_files()
local md_count = #list_markdown_files()
local html = [[
<!DOCTYPE html>
<html>
<head>
<title>Plugin Dashboard</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f0f4f8; }
.header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 20px; margin: 20px 0; }
.card { background: white; padding: 25px; border-radius: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
.card h3 { color: #667eea; margin-bottom: 15px; }
.stat { font-size: 3em; font-weight: bold; color: #667eea; margin: 10px 0; }
.feature-list { list-style: none; padding: 0; }
.feature-list li { padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
.feature-list li:before { content: "✅ "; margin-right: 8px; }
.button { background: #667eea; color: white; padding: 12px 24px; border: none; border-radius: 5px; cursor: pointer; text-decoration: none; display: inline-block; margin: 5px; }
.button:hover { background: #5568d3; }
.form-section { background: white; padding: 25px; border-radius: 10px; margin: 20px 0; box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
input, textarea { width: 100%; padding: 10px; margin: 10px 0; border: 1px solid #ddd; border-radius: 5px; }
.response { margin-top: 15px; padding: 15px; border-radius: 5px; display: none; }
.success { background: #d4edda; color: #155724; display: block; }
.error { background: #f8d7da; color: #721c24; display: block; }
code { background: #f4f4f4; padding: 2px 6px; border-radius: 3px; font-family: 'Courier New', monospace; }
</style>
</head>
<body>
<div class="header">
<h1>🔌 PyPost Plugin Dashboard</h1>
<p>Comprehensive Plugin System Demonstration</p>
</div>
<div class="container">
<!-- Statistics Cards -->
<div class="grid">
<div class="card">
<h3>📄 HTML Files</h3>
<div class="stat">]] .. html_count .. [[</div>
<p>Generated HTML documents</p>
</div>
<div class="card">
<h3>📝 Markdown Files</h3>
<div class="stat">]] .. md_count .. [[</div>
<p>Source markdown files</p>
</div>
<div class="card">
<h3>⏰ Server Time</h3>
<div class="stat" style="font-size: 1.5em;">]] .. os.date("%H:%M") .. [[</div>
<p>]] .. os.date("%Y-%m-%d") .. [[</p>
</div>
</div>
<!-- Features Section -->
<div class="card">
<h3>🚀 Plugin Features</h3>
<ul class="feature-list">
<li><strong>Hook Chaining:</strong> Multiple plugins modify content sequentially</li>
<li><strong>Priority System:</strong> Control execution order (0-100)</li>
<li><strong>GET Routes:</strong> Serve custom pages and APIs</li>
<li><strong>POST Routes:</strong> Handle form submissions</li>
<li><strong>HTML Manipulation:</strong> Find, replace, insert, wrap elements</li>
<li><strong>Markdown Operations:</strong> Add headers, sections, list items</li>
<li><strong>File I/O:</strong> Read, write, list files safely</li>
<li><strong>JSON Support:</strong> Parse and stringify data</li>
<li><strong>Hot Reloading:</strong> Plugins reload automatically on changes</li>
</ul>
</div>
<!-- API Endpoints -->
<div class="card">
<h3>🌐 Available API Endpoints</h3>
<p><strong>GET</strong> <code>/plugin/hello</code> - Simple greeting</p>
<p><strong>GET</strong> <code>/plugin/api/info</code> - JSON plugin info</p>
<p><strong>GET</strong> <code>/plugin/files</code> - File browser</p>
<p><strong>GET</strong> <code>/plugin/dashboard</code> - This page</p>
<p><strong>POST</strong> <code>/plugin/api/contact</code> - Submit contact form</p>
<p><strong>POST</strong> <code>/plugin/api/create-note</code> - Create markdown note</p>
<br>
<a href="/plugin/files" class="button">View Files</a>
<a href="/plugin/api/info" class="button">API Info (JSON)</a>
</div>
<!-- Contact Form Test -->
<div class="form-section">
<h3>📬 Test POST Route - Contact Form</h3>
<form id="contactForm">
<input type="text" name="name" placeholder="Your Name" required>
<input type="email" name="email" placeholder="Your Email">
<textarea name="message" rows="4" placeholder="Your Message" required></textarea>
<button type="submit" class="button">Submit</button>
</form>
<div id="contactResponse" class="response"></div>
</div>
<!-- Create Note Form -->
<div class="form-section">
<h3>📝 Test POST Route - Create Markdown Note</h3>
<form id="noteForm">
<input type="text" name="title" placeholder="Note Title" required>
<textarea name="content" rows="6" placeholder="Note content in markdown..." required></textarea>
<button type="submit" class="button">Create Note</button>
</form>
<div id="noteResponse" class="response"></div>
</div>
<!-- Hook Chain Visualization -->
<div class="card">
<h3>🔗 Hook Chain Processing Order</h3>
<p style="margin-bottom: 15px;">When a markdown file is rendered, these hooks process the HTML sequentially:</p>
<div style="display: flex; align-items: center; justify-content: space-between; padding: 15px; background: #f9f9f9; border-radius: 5px; margin: 10px 0;">
<span>1⃣ Add CSS (Priority 10)</span>
<span>→</span>
<span>2⃣ Add Banner (Priority 20)</span>
<span>→</span>
<span>3⃣ Enhance Code (Priority 30)</span>
<span>→</span>
<span>4⃣ Add Footer (Priority 40)</span>
</div>
<p style="margin-top: 15px; font-size: 0.9em; color: #666;">
Each hook receives the output from the previous hook, ensuring no modifications are lost.
</p>
</div>
<!-- Code Example -->
<div class="card">
<h3>💻 Example Plugin Code</h3>
<pre style="background: #2d3748; color: #e2e8f0; padding: 20px; border-radius: 5px; overflow-x: auto;"><code>-- Register a hook with priority
register_hook("post_render", function(filepath, html)
log("Processing: " .. filepath)
local modified = html_insert_after(html, "&lt;body&gt;",
"&lt;div class='banner'&gt;Hello!&lt;/div&gt;")
return modified -- Chain to next hook
end, 20)
-- Register a POST route
add_post_route("/api/submit", function(req)
local data = req.data
log("Received: " .. data.name)
return 200,
{["Content-Type"] = "application/json"},
table_to_json({success = true})
end, 50)</code></pre>
</div>
</div>
<script>
// Contact form handler
document.getElementById('contactForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
const responseDiv = document.getElementById('contactResponse');
try {
const response = await fetch('/plugin/api/contact', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
responseDiv.className = 'response success';
responseDiv.innerHTML = '<strong>✅ Success!</strong><br>' + result.message;
e.target.reset();
} else {
responseDiv.className = 'response error';
responseDiv.innerHTML = '<strong>❌ Error:</strong><br>' + result.error;
}
} catch (err) {
responseDiv.className = 'response error';
responseDiv.innerHTML = '<strong>❌ Error:</strong><br>' + err.message;
}
});
// Note form handler
document.getElementById('noteForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
const responseDiv = document.getElementById('noteResponse');
try {
const response = await fetch('/plugin/api/create-note', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
const result = await response.json();
if (result.success) {
responseDiv.className = 'response success';
responseDiv.innerHTML = '<strong>✅ Success!</strong><br>' +
result.message + '<br>File: <code>' + result.filename + '</code>';
e.target.reset();
} else {
responseDiv.className = 'response error';
responseDiv.innerHTML = '<strong>❌ Error:</strong><br>' + result.error;
}
} catch (err) {
responseDiv.className = 'response error';
responseDiv.innerHTML = '<strong>❌ Error:</strong><br>' + err.message;
}
});
</script>
</body>
</html>
]]
return html
end, 50)
-- ============================================================================
-- PART 5: UTILITY DEMONSTRATIONS
-- Show string and file utilities
-- ============================================================================
-- String manipulation API
add_get_route("/plugin/api/string-demo", function(req)
local text = "Hello, World! This is a test."
local demo = {
original = text,
split = string_split(text, " "),
replaced = string_replace(text, "World", "PyPost"),
uppercase = string.upper(text),
lowercase = string.lower(text),
match = string_match(text, "w+"),
all_words = string_match_all(text, "%w+")
}
return 200,
{["Content-Type"] = "application/json"},
table_to_json(demo)
end, 50)
-- ============================================================================
-- INITIALIZATION LOG
-- ============================================================================
log("========================================")
log("Comprehensive Plugin Loaded Successfully!")
log("========================================")
log("Registered Hooks:")
log(" - post_render (4 hooks with priorities 10, 20, 30, 40)")
log("Registered GET Routes:")
log(" - /plugin/hello")
log(" - /plugin/api/info")
log(" - /plugin/files")
log(" - /plugin/dashboard")
log(" - /plugin/api/string-demo")
log("Registered POST Routes:")
log(" - /plugin/api/contact")
log(" - /plugin/api/create-note")
log("========================================")
log("Visit /plugin/dashboard to see all features!")
log("========================================")