diff --git a/hubzilla/addon/scn01/config.json.template b/hubzilla/addon/scn01/config.json.template
new file mode 100644
index 0000000..4964c98
--- /dev/null
+++ b/hubzilla/addon/scn01/config.json.template
@@ -0,0 +1,5 @@
+{
+ "_note": "Copy to config.json. Do not commit config.json — it contains secrets and installation-specific values. Access-state (associations/groups) is NOT duplicated here — scn01 reads addon/vs01/config.json as the single source of truth for that.",
+ "receiver_url": "REPLACE — orchestrator receiver endpoint, e.g. https://orchestrator1.internal.diagnostics.kane-il.us/receive-scn",
+ "node_token": "REPLACE — shared secret for node-to-orchestrator authentication"
+}
diff --git a/hubzilla/addon/scn01/scenarios.json b/hubzilla/addon/scn01/scenarios.json
new file mode 100644
index 0000000..3eec99d
--- /dev/null
+++ b/hubzilla/addon/scn01/scenarios.json
@@ -0,0 +1,30 @@
+{
+ "_note": "Sanitized scenario library for scn01. Each entry is an operator/participant-sanitized account, tagged with a single open-vocabulary category. Edited by operator/expert participants over time. Used to populate the browse carousel on /scn01/{association_slug}.",
+ "scenarios": [
+ {
+ "id": "scn-001",
+ "category": "Porch Pirates",
+ "text": "The HOA has not done anything about packages being stolen from porches. Multiple residents have reported missing deliveries, but no notice, camera policy, or response has come from the board."
+ },
+ {
+ "id": "scn-002",
+ "category": "Noise Complaint",
+ "text": "The same residents across several units have been playing loud music late at night, seemingly on purpose. Complaints have not resulted in any visible action."
+ },
+ {
+ "id": "scn-003",
+ "category": "Towing",
+ "text": "A guest's car was towed overnight from a space marked for guest parking. No warning was posted and no notice was given before the tow."
+ },
+ {
+ "id": "scn-004",
+ "category": "Excessive Maintenance Charges",
+ "text": "The lawn is being mowed three times a week, which feels excessive. The frequency was not discussed with homeowners and the cost implications are unclear."
+ },
+ {
+ "id": "scn-005",
+ "category": "Surprise Charges",
+ "text": "A one-time charge appeared on the account for removing a tree that had been dead for years. There was no advance notice that this charge was coming or why it was billed now."
+ }
+ ]
+}
diff --git a/hubzilla/addon/scn01/scn01.php b/hubzilla/addon/scn01/scn01.php
index b89e764..300ab51 100644
--- a/hubzilla/addon/scn01/scn01.php
+++ b/hubzilla/addon/scn01/scn01.php
@@ -2,14 +2,17 @@
/**
* Name: SCN01 Scenarios
- * Description: Public civic diagnostic — browse and submit diagnostic scenarios.
- * Version: 0.2.0
+ * Description: Public civic diagnostic — browse and submit diagnostic scenarios in plain language.
+ * Version: 0.3.0
* MinVersion: 11.0
* MaxVersion: 12.0
*/
use Zotlabs\Extend\Widget;
+require_once 'addon/scn01/scn01_renderer.php';
+require_once 'addon/scn01/scn01_spool.php';
+
function scn01_module() {}
function scn01_load() {
@@ -101,7 +104,7 @@ function scn01_access_state($association_slug = '') {
intval($prof_gid),
dbesc($observer)
);
- if ($r) return 'participant';
+ if ($r) return 'professional';
}
return 'public';
@@ -127,7 +130,7 @@ function scn01_access_wall($association_slug = '') {
}
// ---------------------------------------------------------------------------
-// CONTENT
+// CONTENT ROUTER
// ---------------------------------------------------------------------------
function scn01_content() {
@@ -140,37 +143,40 @@ function scn01_content() {
$association_slug = argv(1) ?? '';
+ // Index — no association selected
+ if (!$association_slug) {
+ return scn01_render_index();
+ }
+
+ $raw = @file_get_contents('addon/vs01/config.json');
+ $cfg = $raw ? json_decode($raw, true) : [];
+ if (json_last_error() !== JSON_ERROR_NONE || !isset($cfg['associations'][$association_slug])) {
+ return scn01_render_not_found();
+ }
+
$access = scn01_access_state($association_slug);
- // scn01 is public — access wall only gates submission, not reading
+ // POST — submission handler
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if ($access === 'public') {
return scn01_access_wall($association_slug);
}
- // TODO: handle POST submission
- return scn01_access_wall($association_slug);
+ if (!scn01_verify_csrf()) {
+ return '
Browse examples of situations other homeowners have experienced. When something looks familiar, select it and describe your own situation in your own words.
';
+ $out .= 'Scenarios are public. ';
+ $out .= 'Complete the SASE process ';
+ $out .= 'to submit your own account for this association.';
+ $out .= '
+ Your account for ' . $assoc_name . ' has been submitted. Records cannot be edited —
+ if you want to add more later, submit a new record.
+
+ Return to ' . $assoc_name . '
+
+
+
';
+ }
+
+ $out = '
+ Submission failed. Please try again or contact the operator.
+ ' . scn01_h($result) . '
+