Expert guidance for building and extending a WordPress plugin with dynamic surveys, conditional logic, templates, and participant management
This skill has safety concerns that you should review before use. Some patterns were detected that may pose a risk.Safety score: 60/100.
KillerSkills scans all public content for safety. Use caution before installing or executing flagged content.
Expert guidance for building and extending the FlowQ WordPress dynamic survey plugin with advanced features including conditional logic, templates, participant management, and analytics.
This skill provides comprehensive knowledge of the FlowQ WordPress survey plugin architecture, database schema, template system, and admin interface. Use it when working on:
```
flowq/
├── admin/ # Admin interface (survey management, settings)
├── includes/ # Core classes (managers, handlers, migrations)
├── public/ # Frontend (survey display, participant forms)
├── assets/ # CSS/JS resources
└── survey-plugin.php # Main plugin file
```
Custom tables:
**Current Implementation:**
**Conditional Logic:**
**When extending:**
1. Read `includes/class-question-manager.php` to understand CRUD operations
2. Check `admin/class-question-admin.php` for AJAX handlers
3. Review `public/class-frontend.php` for survey navigation logic
**Template Architecture:**
**Template Structure:**
```php
// Template styles (JSON stored in database)
{
"primary_color": "#3b82f6",
"background_color": "#f8fafc",
"text_color": "#1e293b",
"input_bg_color": "#ffffff",
"input_border_color": "#cbd5e1",
"input_text_color": "#1e293b",
"button_style": "solid",
"border_radius": "8px",
"font_family": "system-ui"
}
```
**Extending Templates:**
1. Add new template to `flowq_templates` table
2. Create SVG preview in `assets/images/templates/`
3. Update `flowq_Template_Handler::generate_dynamic_css()` if adding new style properties
4. Test with both light and dark themes
**Settings Storage:**
**Available Settings:**
**Adding New Settings:**
1. Register option in `admin/class-settings-admin.php`
2. Add UI field in `admin/templates/general-settings.php`
3. Update save handler in `save_general_settings()`
4. Use `wp_kses_post()` for HTML content, `absint()` for booleans
5. Apply setting in participant form (`public/templates/participant-form.php`)
**Implementation Pattern:**
```php
// Check if email exists for survey (when duplicates disabled)
$setting = get_option('flowq_allow_duplicate_emails', 0);
if (!$setting) {
if ($participant_manager->email_exists_for_survey($email, $survey_id)) {
wp_send_json_error([
'message' => 'You have already submitted this survey with this email address'
]);
}
}
```
**Key Method:**
**Survey Cards (2-column grid):**
```php
// In all-surveys.php
<div class="flowq-surveys-grid" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px;">
<?php foreach ($surveys as $survey): ?>
<div class="survey-card">
<div class="survey-card-details">
<span class="label">Form Header:</span>
<span class="value"><?php echo esc_html($survey['form_header']); ?></span>
</div>
<div class="survey-stats">
<div class="stat-item">
<span class="stat-value"><?php echo $survey['question_count']; ?></span>
<span class="stat-label">Questions</span>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
```
**Preventing N+1 Queries:**
```php
// In class-survey-manager.php
public function get_surveys($include_question_count = false) {
if ($include_question_count) {
$query = "SELECT s.*, COUNT(q.id) as question_count
FROM {$this->table} s
LEFT JOIN {$wpdb->prefix}flowq_questions q ON s.id = q.survey_id
GROUP BY s.id";
// Execute with LEFT JOIN
}
}
```
**Using wp_editor():**
```php
// In add-survey.php
wp_editor(
$survey['form_subtitle'],
'form_subtitle',
[
'textarea_name' => 'form_subtitle',
'teeny' => true,
'media_buttons' => false,
'textarea_rows' => 4,
'tinymce' => [
'toolbar1' => 'bold,italic,underline,link,unlink,bullist,numlist,undo,redo'
]
]
);
```
**Sanitization:**
**Page Selection Pattern:**
```php
<!-- In add-survey.php -->
<select name="thank_you_page_slug" id="thank_you_page_slug">
<option value="">Select a page...</option>
<?php
$pages = get_pages(['post_status' => 'publish']);
foreach ($pages as $page):
$is_thank_you = stripos($page->post_title, 'thank you') !== false
|| stripos($page->post_title, 'thankyou') !== false;
$star = $is_thank_you ? '⭐ ' : '';
?>
<option value="<?php echo esc_attr($page->post_name); ?>"
data-page-id="<?php echo $page->ID; ?>"
<?php selected($survey['thank_you_page_slug'], $page->post_name); ?>>
<?php echo $star . esc_html($page->post_title); ?>
</option>
<?php endforeach; ?>
</select>
<script>
jQuery('#thank_you_page_slug').on('change', function() {
var selectedOption = jQuery(this).find(':selected');
var pageId = selectedOption.data('page-id');
if (pageId) {
var editUrl = '<?php echo admin_url('post.php'); ?>?post=' + pageId + '&action=edit';
jQuery('#edit-page-btn').attr('href', editUrl).show();
} else {
jQuery('#edit-page-btn').hide();
}
});
</script>
```
**Two-Stage vs Single-Stage:**
```php
<?php
$two_stage = get_option('flowq_two_stage_form', 1);
$show_address = get_option('flowq_field_address', 1);
$show_zipcode = get_option('flowq_field_zipcode', 1);
$show_phone = get_option('flowq_field_phone', 1);
if ($two_stage && $show_phone):
// Stage 1: name, email, address, zipcode
// Stage 2: phone
else:
// All fields in single form
endif;
?>
```
**Privacy Policy Display:**
```php
<div class="privacy-policy-section">
<div class="privacy-policy-content">
<?php echo wp_kses_post(get_option('flowq_privacy_policy')); ?>
</div>
<label>
<input type="checkbox" name="privacy_accepted" required>
I accept the privacy policy
</label>
</div>
```
**Dynamic CSS Generation:**
```php
// In class-template-handler.php
public function generate_dynamic_css() {
$styles = $this->get_active_template_styles();
echo "<style>
.flowq-question-container {
background-color: {$styles['background_color']};
color: {$styles['text_color']};
border-radius: {$styles['border_radius']};
}
.flowq-input {
background-color: {$styles['input_bg_color']};
border-color: {$styles['input_border_color']};
color: {$styles['input_text_color']};
}
.flowq-button {
background: {$this->get_button_background($styles)};
}
</style>";
}
```
**Centralized AJAX in class-ajax-handler.php:**
```php
public function handle_ajax_action() {
check_ajax_referer('flowq_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(['message' => 'Unauthorized']);
}
// Process request
wp_send_json_success(['data' => $result]);
}
```
1. **Single Question Type**: Only 'single_choice' supported (hardcoded)
2. **No Custom Question Ordering**: Questions ordered by ID only
3. **Global Templates**: Templates apply to all surveys (not per-survey)
4. **Phone Optional Not Implemented**: Setting exists but not yet functional
1. Modify `class-question-manager.php:62` to remove hardcoded type
2. Add question type dropdown in `admin/templates/questions.php`
3. Update database schema to support type-specific fields
4. Extend `flowq_Frontend::get_next_question_id()` for type-specific logic
5. Add frontend rendering in `public/templates/question-display.php`
1. Insert template in `flowq_templates` table
2. Create SVG preview in `assets/images/templates/[name].svg`
3. Test with Dark Mode patterns (dark backgrounds, white text on hover)
4. Update `flowq_Template_Handler` if new style properties needed
1. Add UI field in `admin/templates/general-settings.php`
2. Register save logic in `flowq_Settings_Admin::save_general_settings()`
3. Apply setting in participant form or survey logic
4. Document in CLAUDE.md under "General Settings System"
1. Check `flowq_phone_optional` setting value
2. Modify phone field validation in participant form JavaScript
3. Update `flowq_Participant_Manager::create_participant()` to allow null phone
4. Add conditional "Skip" button in Stage 2 form
When making changes, verify:
**Core Admin Files:**
**Core Includes:**
**Frontend Files:**
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/wordpress-dynamic-survey-builder/raw