PropertyWebBuilder has a solid foundation for marketing and social media features with existing infrastructure for:
- Social media link management
- Email templates and delivery systems
- Background job processing
- Analytics tracking
- Media library with image processing
- Multi-tenant content management
- Theme and branding configuration
This document outlines what's available, what gaps exist, and recommendations for building marketing features.
Model: Pwb::WebsiteSocialLinkable concern
Location: /app/models/concerns/pwb/website_social_linkable.rb
Capabilities:
- Stores URLs for 8 social media platforms:
- Facebook, Instagram, LinkedIn, YouTube, Twitter, WhatsApp, Pinterest
- Plus generic
social_mediaJSON field on Website model
- Links stored in
pwb_linkstable withsocial_mediaplacement enum - Memoized caching to avoid N+1 queries
- Admin methods to retrieve and update links
Current Usage:
- Footer template displays social icons (via
social_media_linkhelper) - Page parts registry includes
footer_social_linkslegacy component
Code Example:
# Website model can access:
website.social_media_facebook # => "https://facebook.com/..."
website.social_media_instagram # => "https://instagram.com/..."
website.update_social_media_link("facebook", "https://facebook.com/agencyname")Gaps:
- No sharing widgets for individual properties or blog posts
- No social media feed integration
- No scheduled posting capabilities
- No analytics on social shares
Model: Pwb::EmailTemplate
Location: /app/models/pwb/email_template.rb
Capabilities:
- Website-scoped custom email templates
- Support for multiple template types:
- Enquiry emails (general & property-specific)
- Auto-reply emails to visitors
- Property alerts (new listing & price changes)
- User emails (welcome & password reset)
- Liquid template support with dynamic variable substitution
- HTML and plain text email bodies
- Template preview with sample data
Template Variables Supported:
# General enquiry: visitor_name, visitor_email, visitor_phone, message, website_name
# Property enquiry: + property_title, property_reference, property_url
# Price alerts: subscriber_name, property_title, old_price, new_price, property_url
# User emails: user_name, user_email, reset_urlConfiguration:
- Queue Adapter: Solid Queue (Rails 8 native) for background job processing
- Queue Name:
:mailers - Environment: SMTP-based with support for:
- SendGrid, Mailgun, Amazon SES, Postmark, etc.
- Environment variable configuration:
SMTP_ADDRESS,SMTP_PORT,SMTP_USERNAME,SMTP_PASSWORDSMTP_DOMAIN,SMTP_AUTH
Production Config:
# config/environments/production.rb
config.active_job.queue_adapter = :solid_queue
config.action_mailer.deliver_later_queue_name = :mailers
config.action_mailer.raise_delivery_errors = trueLocation: /app/mailers/pwb/
Available Mailers:
-
EnquiryMailer- Handles contact form and property enquiries- Supports custom Liquid templates OR ERB fallback
- Tracks delivery success/failure on message records
- Methods:
general_enquiry_targeting_agency(contact, message)property_enquiry_targeting_agency(contact, message, property)
-
EmailVerificationMailer- Handles email verification -
ApplicationMailer- Base mailer with helper methods
Key Features:
- Callbacks for tracking delivery
- Error handling with logging
- Multi-locale support
- Website-scoped email rendering
Example Usage:
Pwb::EnquiryMailer.with(
contact: contact_record,
message: message_record
).general_enquiry_targeting_agency.deliver_laterQueue System: Solid Queue (Rails 8)
Base Class: ApplicationJob / Pwb::ApplicationJob
Location: /app/jobs/
Existing Jobs:
UpdateExchangeRatesJob- Currency exchange rate updatesCleanupOrphanedBlobsJob- Media cleanupRefreshPropertiesViewJob- Materialized view updatesNtfyNotificationJob- Push notifications for property listings/inquiries
Available Job Patterns:
- Tenant-aware jobs (scoped to specific website)
- Retry mechanisms
- Error handling
- Scheduled execution support
Example Job:
class Pwb::UpdateExchangeRatesJob < Pwb::ApplicationJob
queue_as :default
def perform
# Job implementation
end
endSolid Queue supports:
- Delayed job scheduling via
perform_later - Cron-like scheduling (can be configured)
- Recurring job patterns
Model: Pwb::Media
Location: /app/models/pwb/media.rb
Capabilities:
- ActiveStorage integration for file storage
- Multi-tenant media folders
- Image variants (thumbnail, small, medium, large)
- Metadata extraction:
- Dimensions (width/height)
- File size, content type
- Checksums for integrity
- Tagging system
- Usage tracking
- Search by filename, title, alt text, description
Supported File Types:
- Images: JPEG, PNG, GIF, WebP, SVG
- Documents: PDF, Word, Excel
- Text: CSV, plain text
Storage Options:
- Cloudflare R2 (production)
- Local disk (development)
- Any S3-compatible service
Methods:
media = Pwb::Media.find(id)
media.url # Original file URL
media.variant_url(:thumb) # Thumbnail (150x150)
media.variant_url(:medium) # 600x600
media.add_tag("marketing") # Tagging
media.record_usage! # Track usageContentPhoto & WebsitePhoto:
Pwb::ContentPhoto- Photos attached to content blocksPwb::WebsitePhoto- Website-level photos (branding)Pwb::PropPhoto- Property images
All use ActiveStorage with image processing capabilities
Model: Pwb::Page
Location: /app/models/pwb/page.rb
Capabilities:
- Multi-locale page support via Mobility gem
- SEO fields:
seo_title,meta_description - Navigation placement:
- Top navigation with sort order
- Footer with sort order
- Visibility control
- Slug-based routing
- Page parts system for flexible layouts
Multi-language Support:
page = Pwb::Page.find(id)
page.raw_html_en # English content
page.raw_html_es # Spanish content
page.raw_html_fr # French content
# Supports all locales configured for websiteFramework: Liquid template-based modular components
Registry: Pwb::PagePartLibrary
Location: /app/views/pwb/page_parts/
Available Page Part Categories:
-
Heroes (3 templates)
- Centered hero with CTA
- Split hero (content + image)
- Hero with property search
-
Features (2 templates)
- 3-column feature grid
- 4-column icon cards with colors
-
Testimonials (2 templates)
- Carousel with slide navigation
- Grid layout
-
Call-to-Action (2 templates)
- Full-width banner CTA
- Split CTA with side image
-
Statistics (1 template)
- Animated number counters
-
Teams (1 template)
- Team member grid with social links
-
Galleries (1 template)
- Image grid with lightbox
-
Pricing (1 template)
- 3-column pricing comparison
-
FAQs (1 template)
- Expandable accordion
-
Content (Various)
- HTML content blocks
- Footer content
- Search component
Page Part Definition Example:
# From PagePartLibrary::DEFINITIONS
'heroes/hero_split' => {
category: :heroes,
label: 'Split Hero',
description: 'Two-column hero with content and image',
fields: %w[title subtitle description cta_text cta_link image image_alt]
}Engine: Liquid 5.3 Use Cases:
- Page part rendering
- Email template rendering
- Dynamic content personalization
Example Template:
{% if property %}
<h1>{{ property.title }}</h1>
<p>Price: {{ property.price | currency }}</p>
{% endif %}Model: Pwb::Website
Key Branding Fields:
# Logo
main_logo_url # Primary logo URL
favicon_url # Favicon
# Company Info
company_display_name # Display name
site_type # residential, commercial, vacation_rental
# Localization
supported_locales # Available languages
default_client_locale # Default language for visitors
default_admin_locale # Admin language
# Colors & Styling
selected_palette # Color scheme name
palette_mode # dynamic, light_only, dark
compiled_palette_css # Generated CSS from palette
raw_css # Custom CSS
style_variables_for_theme # Theme-specific variables
# Google Fonts
google_font_name # Selected fontModel: Pwb::Theme (via active_hash)
Location: /app/themes/
Capabilities:
- Multiple themed layouts
- Theme-specific styling
- Assets per theme
- Responsive design with Tailwind CSS
Website Theming:
website.theme_name # Current theme
website.available_themes # List of available themes
website.style_variables_for_theme # Theme configConcerns:
Pwb::WebsiteStyleable- Style management- Palette mode: dynamic, light, dark
- Custom CSS support
- Color palette compilation
Gem: ahoy_matey v5.0
Models:
Ahoy::Visit- Visitor sessionsAhoy::Event- Tracked events
Visit Data Captured:
# Ahoy::Visit fields:
visit_token # Unique session ID
visitor_token # Unique visitor ID
browser, os, device_type
city, region, country
landing_page, referrer, referring_domain
# UTM Parameters
utm_source, utm_medium, utm_campaign, utm_content, utm_termMulti-tenant Scoping:
website = Pwb::Website.find(1)
website.visits # All visits to this website
visits.where(started_at: 7.days.ago..).count # Recent visitorsGems:
chartkickv5.0 - Beautiful chartsgroupdatev6.0 - Time-based data grouping
Common Analytics Patterns:
# Time-series analytics
visits.group_by_hour(:started_at).count
visits.group_by_day(:started_at).count
# UTM source analysis
visits.group(:utm_source).count
visits.group(:utm_campaign).count
# Geography
visits.group(:country).count
visits.group(:city).countNative Integrations:
analytics_idfield on Websiteanalytics_id_typeenum (Google Analytics, etc.)- Available in serialized website JSON
Location: /app/controllers/api_public/v1/
Available Endpoints:
AuthController- OAuth/token managementPropertiesController- Property listingsPagesController- Page contentLinksController- Navigation linksSiteDetailsController- Website configurationTranslationsController- Multi-language contentWidgetsController- Embeddable widgets
Base Controller: ApiPublic::V1::BaseController
Use Cases:
- Embed property listings on external sites
- Syndicate content via API
- Mobile app data delivery
- Widget integration
Gem: acts_as_tenant v1.0
Automatic Scoping:
# All queries automatically scoped to current_website
Pwb::Page.all # Only pages for current website
Pwb::Link.all # Only links for current website
Pwb::Media.all # Only media for current websiteMulti-tenant Models:
- Website (tenant root)
- User (with memberships for multi-website access)
- Pages, Links, Content
- Media, Images
- Email Templates
- Analytics (visits, events)
Tenant-Aware Jobs:
# Jobs automatically set tenant context
class Pwb::SocialMediaSyncJob < Pwb::ApplicationJob
def perform(website_id)
Pwb::Current.website = Pwb::Website.find(website_id)
# Job logic is tenant-scoped
end
endModels:
Pwb::Contact- Visitor contact informationPwb::Message- Messages from contact forms
Message Fields:
# Pwb::Message
title # Subject/title
content # Message content
origin_email # Visitor email
delivery_email # Where to send responses
delivery_success # Boolean flag
delivery_error # Error message if failed
delivered_at # TimestampUse for Marketing:
- Build email lists from enquiries
- Track lead sources
- Newsletter signup integration
| Feature | Status | Gap Description |
|---|---|---|
| Social Sharing | Not implemented | No share buttons, meta tags, or share counting |
| Email Marketing/Campaigns | Partial | Email templates exist, no campaign/newsletter system |
| Social Media Posting | Not implemented | No integration with Facebook, Instagram, LinkedIn APIs |
| Content Calendar | Not implemented | No scheduling or editorial calendar |
| Property Syndication | Basic | API exists but no feed generation (RSS, data feeds) |
| SEO Optimization | Partial | Meta fields exist, no bulk SEO tools, XML sitemap limited |
| Lead Nurturing | Not implemented | No email sequences or drip campaigns |
| CRM Integration | Minimal | Contact tracking exists but no CRM sync |
| Influencer/Affiliate | Not implemented | No tracking or payment system |
| Video Hosting | Not implemented | No native video support (YouTube only via embeds) |
- No Open Graph/Twitter Card generation
- No A/B testing framework
- No referral tracking
- No UTM parameter generation helper
- No email list segmentation
- No SMS marketing
- No push notification templates (has infrastructure but limited)
✅ Multi-tenant Architecture - Fully supports tenant-scoped marketing features ✅ Email System - Production-ready with custom templates ✅ Background Jobs - Solid Queue ready for async marketing operations ✅ Media Management - Robust image handling with variants ✅ Content Management - Flexible page parts system ✅ Analytics Foundation - Ahoy tracking + chartkick visualization ready ✅ API-Ready - Public APIs for integration and syndication ✅ Branding Controls - Full website customization capabilities ✅ Localization - Multi-language support via Mobility gem
🔨 Sharing Features - Need social sharing widgets and meta tag generation 🔨 Campaign Management - Email/social campaign scheduling system 🔨 Social Media Integration - API integrations for posting and analytics 🔨 SEO Tools - Bulk optimization tools, XML sitemaps, schema markup 🔨 Lead Management - Nurture sequences, segmentation, automation 🔨 Tracking & Attribution - UTM tracking, conversion funnel analytics
- Social Sharing Widget (Share buttons on properties/pages)
- Email Newsletter System (Basic list + send campaign)
- SEO Meta Tags Auto-generation (Using page titles/descriptions)
- Website Analytics Dashboard (Ahoy data visualization)
- Basic UTM Parameter Helper (For campaign tracking)
- Facebook Share API Integration (Scheduled posting)
- Instagram Integration (Image feed, post scheduling)
- LinkedIn Company Updates (Article sharing)
- Social Media Analytics (Engagement metrics)
- Social Feed Display Widget (Embed feeds on site)
- Email Automation (Drip campaigns, sequences)
- Content Calendar (Editorial planning)
- Lead Scoring (Based on visits, engagement)
- Referral System (Tracking referral sources)
- A/B Testing Framework (Email/page variants)
- CRM Sync (Salesforce, HubSpot integrations)
- Advanced Segmentation (RFM analysis, cohorts)
- SMS Marketing (SMS campaigns)
- Video Hosting (Native video management)
- Influencer/Affiliate Program (Commission tracking)
/app/models/pwb/website.rb- Add marketing fields/app/models/pwb/page.rb- Add social share tracking/app/models/pwb/message.rb- Add marketing attributes/app/models/pwb/media.rb- Already has tags, usage tracking
/app/controllers/pwb/marketing/campaigns_controller.rb/app/controllers/pwb/marketing/email_campaigns_controller.rb/app/controllers/pwb/marketing/social_integrations_controller.rb/app/controllers/pwb/marketing/analytics_controller.rb
/app/jobs/pwb/send_email_campaign_job.rb/app/jobs/pwb/sync_social_media_job.rb/app/jobs/pwb/generate_sitemap_job.rb/app/jobs/pwb/calculate_lead_scores_job.rb
/app/views/pwb/page_parts/social_sharing_widget.liquid/app/views/pwb/page_parts/newsletter_signup.liquid/app/views/pwb/admin/marketing/campaigns/(admin interface)
# Marketing features will need:
- create_email_campaigns
- create_campaign_recipients
- create_social_posts
- create_utm_tracking
- create_lead_scores
- create_referral_codes.envvariables for social API keysconfig/marketing.yml- Feature flagsconfig/email_defaults.yml- Default email settings
-
Follow Email Template Pattern (for campaign templates)
# Use Liquid rendering like EmailTemplate does renderer = EmailTemplateRenderer.new( website: website, template_key: "campaign.weekly" )
-
Follow Job Pattern (for async marketing)
# Use TenantAwareJob concern class Pwb::SendCampaignJob < Pwb::ApplicationJob include Pwb::TenantAwareJob end
-
Follow Page Parts Pattern (for marketing components)
{%- liquid assign newsletter_title = newsletter.title assign newsletter_description = newsletter.description -%} -
Follow Multi-tenancy Pattern
# Always scope to current_website Pwb::Current.website = website # Now all queries are scoped
- Email delivery: Use existing ActionMailer + Solid Queue
- Media: Use existing Media model + ActiveStorage
- Analytics: Use existing Ahoy + Chartkick
- Localization: Use existing Mobility gem
- Styling: Use existing Tailwind CSS + theme system
- API: Extend existing API endpoints
-
Email Safety
- Validate all email addresses
- Use AWS SES v2 bounce/complaint handling
- Implement unsubscribe tokens (RFCs 2369, 8058)
-
Social Media API Tokens
- Store in environment variables only
- Rotate tokens regularly
- Rate limit API calls
-
Data Privacy
- GDPR compliance for email lists
- Consent tracking for newsletters
- Right to be forgotten implementation
-
Input Validation
- Sanitize all user input for templates
- Validate URLs in social links
- Check image dimensions/types
# Use existing includes to avoid N+1
Website.includes(:links, :media, :page_contents).find(id)
# Memoization pattern used in WebsiteSocialLinkable
@social_media_links_cache ||= links.where(slug: slugs).index_by(&:slug)- Cache website branding in Redis
- Cache email templates with TTL
- Use fragment caching for marketing components
- Cache analytics queries (regenerate daily)
- Use Solid Queue for bulk operations
- Set job timeouts for long-running tasks
- Implement exponential backoff for retries
PropertyWebBuilder has strong foundational infrastructure for marketing and social media features. The multi-tenant architecture, email system, media handling, and analytics tracking provide a solid base. Development should focus on:
- Social sharing and syndication
- Email campaign management
- Social media API integrations
- SEO optimization tools
- Lead tracking and automation
All of these can be built using existing patterns and infrastructure already in place.