@mytechtoday/augment-extensions 0.7.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (483) hide show
  1. package/AGENTS.md +265 -232
  2. package/README.md +956 -771
  3. package/augment-extensions/coding-standards/bash/README.md +196 -196
  4. package/augment-extensions/coding-standards/bash/module.json +163 -163
  5. package/augment-extensions/coding-standards/bash/rules/naming-conventions.md +336 -336
  6. package/augment-extensions/coding-standards/bash/rules/universal-standards.md +289 -289
  7. package/augment-extensions/coding-standards/css/README.md +40 -40
  8. package/augment-extensions/coding-standards/css/examples/css-examples.css +550 -550
  9. package/augment-extensions/coding-standards/css/module.json +44 -44
  10. package/augment-extensions/coding-standards/css/rules/css-modern-features.md +448 -448
  11. package/augment-extensions/coding-standards/css/rules/css-standards.md +492 -492
  12. package/augment-extensions/coding-standards/html/README.md +40 -40
  13. package/augment-extensions/coding-standards/html/examples/html-examples.html +267 -267
  14. package/augment-extensions/coding-standards/html/examples/responsive-layout.html +505 -505
  15. package/augment-extensions/coding-standards/html/module.json +44 -44
  16. package/augment-extensions/coding-standards/html/rules/html-standards.md +349 -349
  17. package/augment-extensions/coding-standards/html-css-js/README.md +194 -194
  18. package/augment-extensions/coding-standards/html-css-js/examples/async-examples.js +487 -487
  19. package/augment-extensions/coding-standards/html-css-js/examples/css-examples.css +550 -550
  20. package/augment-extensions/coding-standards/html-css-js/examples/dom-examples.js +667 -667
  21. package/augment-extensions/coding-standards/html-css-js/examples/html-examples.html +267 -267
  22. package/augment-extensions/coding-standards/html-css-js/examples/javascript-examples.js +612 -612
  23. package/augment-extensions/coding-standards/html-css-js/examples/responsive-layout.html +505 -505
  24. package/augment-extensions/coding-standards/html-css-js/module.json +48 -48
  25. package/augment-extensions/coding-standards/html-css-js/rules/async-patterns.md +515 -515
  26. package/augment-extensions/coding-standards/html-css-js/rules/css-modern-features.md +448 -448
  27. package/augment-extensions/coding-standards/html-css-js/rules/css-standards.md +492 -492
  28. package/augment-extensions/coding-standards/html-css-js/rules/dom-manipulation.md +439 -439
  29. package/augment-extensions/coding-standards/html-css-js/rules/html-standards.md +349 -349
  30. package/augment-extensions/coding-standards/html-css-js/rules/javascript-standards.md +486 -486
  31. package/augment-extensions/coding-standards/html-css-js/rules/performance.md +463 -463
  32. package/augment-extensions/coding-standards/html-css-js/rules/tooling.md +543 -543
  33. package/augment-extensions/coding-standards/js/README.md +46 -46
  34. package/augment-extensions/coding-standards/js/examples/async-examples.js +487 -487
  35. package/augment-extensions/coding-standards/js/examples/dom-examples.js +667 -667
  36. package/augment-extensions/coding-standards/js/examples/javascript-examples.js +612 -612
  37. package/augment-extensions/coding-standards/js/module.json +49 -49
  38. package/augment-extensions/coding-standards/js/rules/async-patterns.md +515 -515
  39. package/augment-extensions/coding-standards/js/rules/dom-manipulation.md +439 -439
  40. package/augment-extensions/coding-standards/js/rules/javascript-standards.md +486 -486
  41. package/augment-extensions/coding-standards/js/rules/performance.md +463 -463
  42. package/augment-extensions/coding-standards/js/rules/tooling.md +543 -543
  43. package/augment-extensions/coding-standards/php/README.md +248 -248
  44. package/augment-extensions/coding-standards/php/examples/api-endpoint-example.php +204 -204
  45. package/augment-extensions/coding-standards/php/examples/cli-command-example.php +206 -206
  46. package/augment-extensions/coding-standards/php/examples/legacy-refactoring-example.php +234 -234
  47. package/augment-extensions/coding-standards/php/examples/web-application-example.php +211 -211
  48. package/augment-extensions/coding-standards/php/examples/woocommerce-extension-example.php +215 -215
  49. package/augment-extensions/coding-standards/php/examples/wordpress-plugin-example.php +189 -189
  50. package/augment-extensions/coding-standards/php/module.json +166 -166
  51. package/augment-extensions/coding-standards/php/rules/api-development.md +480 -480
  52. package/augment-extensions/coding-standards/php/rules/category-configuration.md +332 -332
  53. package/augment-extensions/coding-standards/php/rules/cli-tools.md +472 -472
  54. package/augment-extensions/coding-standards/php/rules/cms-integration.md +561 -561
  55. package/augment-extensions/coding-standards/php/rules/code-quality.md +402 -402
  56. package/augment-extensions/coding-standards/php/rules/documentation.md +425 -425
  57. package/augment-extensions/coding-standards/php/rules/ecommerce.md +627 -627
  58. package/augment-extensions/coding-standards/php/rules/error-handling.md +336 -336
  59. package/augment-extensions/coding-standards/php/rules/legacy-migration.md +677 -677
  60. package/augment-extensions/coding-standards/php/rules/naming-conventions.md +279 -279
  61. package/augment-extensions/coding-standards/php/rules/performance.md +392 -392
  62. package/augment-extensions/coding-standards/php/rules/psr-standards.md +186 -186
  63. package/augment-extensions/coding-standards/php/rules/security.md +358 -358
  64. package/augment-extensions/coding-standards/php/rules/testing.md +403 -403
  65. package/augment-extensions/coding-standards/php/rules/type-declarations.md +331 -331
  66. package/augment-extensions/coding-standards/php/rules/web-applications.md +426 -426
  67. package/augment-extensions/coding-standards/powershell/README.md +154 -154
  68. package/augment-extensions/coding-standards/powershell/examples/admin-example.ps1 +272 -272
  69. package/augment-extensions/coding-standards/powershell/examples/automation-example.ps1 +173 -173
  70. package/augment-extensions/coding-standards/powershell/examples/cloud-example.ps1 +243 -243
  71. package/augment-extensions/coding-standards/powershell/examples/cross-platform-example.ps1 +297 -297
  72. package/augment-extensions/coding-standards/powershell/examples/dsc-example.ps1 +224 -224
  73. package/augment-extensions/coding-standards/powershell/examples/legacy-migration-example.ps1 +340 -340
  74. package/augment-extensions/coding-standards/powershell/examples/module-example.psm1 +255 -255
  75. package/augment-extensions/coding-standards/powershell/module.json +165 -165
  76. package/augment-extensions/coding-standards/powershell/rules/administrative-tools.md +439 -439
  77. package/augment-extensions/coding-standards/powershell/rules/automation-scripts.md +240 -240
  78. package/augment-extensions/coding-standards/powershell/rules/cloud-orchestration.md +384 -384
  79. package/augment-extensions/coding-standards/powershell/rules/configuration-schema.md +383 -383
  80. package/augment-extensions/coding-standards/powershell/rules/cross-platform-scripts.md +482 -482
  81. package/augment-extensions/coding-standards/powershell/rules/dsc-configurations.md +296 -296
  82. package/augment-extensions/coding-standards/powershell/rules/error-handling.md +314 -314
  83. package/augment-extensions/coding-standards/powershell/rules/legacy-migrations.md +466 -466
  84. package/augment-extensions/coding-standards/powershell/rules/modules-functions.md +244 -244
  85. package/augment-extensions/coding-standards/powershell/rules/naming-conventions.md +266 -266
  86. package/augment-extensions/coding-standards/powershell/rules/performance-optimization.md +209 -209
  87. package/augment-extensions/coding-standards/powershell/rules/security-practices.md +314 -314
  88. package/augment-extensions/coding-standards/powershell/rules/testing-guidelines.md +268 -268
  89. package/augment-extensions/coding-standards/powershell/rules/universal-standards.md +197 -197
  90. package/augment-extensions/coding-standards/python/README.md +48 -48
  91. package/augment-extensions/coding-standards/python/examples/best-practices.py +373 -373
  92. package/augment-extensions/coding-standards/python/module.json +30 -30
  93. package/augment-extensions/coding-standards/python/rules/async-patterns.md +884 -884
  94. package/augment-extensions/coding-standards/python/rules/best-practices.md +232 -232
  95. package/augment-extensions/coding-standards/python/rules/code-organization.md +220 -220
  96. package/augment-extensions/coding-standards/python/rules/documentation.md +831 -831
  97. package/augment-extensions/coding-standards/python/rules/error-handling.md +1008 -1008
  98. package/augment-extensions/coding-standards/python/rules/naming-conventions.md +172 -172
  99. package/augment-extensions/coding-standards/python/rules/testing.md +409 -409
  100. package/augment-extensions/coding-standards/python/rules/tooling.md +446 -446
  101. package/augment-extensions/coding-standards/python/rules/type-hints.md +253 -253
  102. package/augment-extensions/coding-standards/react/README.md +45 -45
  103. package/augment-extensions/coding-standards/react/module.json +27 -27
  104. package/augment-extensions/coding-standards/react/rules/component-patterns.md +214 -214
  105. package/augment-extensions/coding-standards/react/rules/hooks-best-practices.md +235 -235
  106. package/augment-extensions/coding-standards/react/rules/performance.md +300 -300
  107. package/augment-extensions/coding-standards/react/rules/state-management.md +265 -265
  108. package/augment-extensions/coding-standards/react/rules/typescript-react.md +271 -271
  109. package/augment-extensions/coding-standards/typescript/README.md +45 -45
  110. package/augment-extensions/coding-standards/typescript/module.json +27 -27
  111. package/augment-extensions/coding-standards/typescript/rules/naming-conventions.md +225 -225
  112. package/augment-extensions/collections/html-css-js/README.md +82 -82
  113. package/augment-extensions/collections/html-css-js/collection.json +41 -41
  114. package/augment-extensions/domain-rules/api-design/README.md +41 -41
  115. package/augment-extensions/domain-rules/api-design/module.json +27 -27
  116. package/augment-extensions/domain-rules/api-design/rules/authentication.md +263 -263
  117. package/augment-extensions/domain-rules/api-design/rules/documentation.md +395 -395
  118. package/augment-extensions/domain-rules/api-design/rules/error-handling.md +290 -290
  119. package/augment-extensions/domain-rules/api-design/rules/graphql-api.md +313 -313
  120. package/augment-extensions/domain-rules/api-design/rules/rest-api.md +214 -214
  121. package/augment-extensions/domain-rules/api-design/rules/versioning.md +268 -268
  122. package/augment-extensions/domain-rules/database/README.md +161 -161
  123. package/augment-extensions/domain-rules/database/examples/flat-database-example.md +793 -793
  124. package/augment-extensions/domain-rules/database/examples/hybrid-database-example.md +1132 -1132
  125. package/augment-extensions/domain-rules/database/examples/nosql-document-example.md +868 -868
  126. package/augment-extensions/domain-rules/database/examples/nosql-graph-example.md +805 -805
  127. package/augment-extensions/domain-rules/database/examples/relational-schema-example.md +621 -621
  128. package/augment-extensions/domain-rules/database/examples/vector-database-example.md +965 -965
  129. package/augment-extensions/domain-rules/database/module.json +28 -28
  130. package/augment-extensions/domain-rules/database/rules/flat-databases.md +624 -624
  131. package/augment-extensions/domain-rules/database/rules/nosql-databases.md +588 -588
  132. package/augment-extensions/domain-rules/database/rules/nosql-document-stores.md +856 -856
  133. package/augment-extensions/domain-rules/database/rules/nosql-graph-databases.md +778 -778
  134. package/augment-extensions/domain-rules/database/rules/nosql-key-value-stores.md +963 -963
  135. package/augment-extensions/domain-rules/database/rules/performance-optimization.md +1076 -1076
  136. package/augment-extensions/domain-rules/database/rules/relational-databases.md +697 -697
  137. package/augment-extensions/domain-rules/database/rules/relational-indexing.md +671 -671
  138. package/augment-extensions/domain-rules/database/rules/relational-query-optimization.md +607 -607
  139. package/augment-extensions/domain-rules/database/rules/relational-schema-design.md +907 -907
  140. package/augment-extensions/domain-rules/database/rules/relational-transactions.md +783 -783
  141. package/augment-extensions/domain-rules/database/rules/security-standards.md +980 -980
  142. package/augment-extensions/domain-rules/database/rules/universal-best-practices.md +485 -485
  143. package/augment-extensions/domain-rules/database/rules/vector-databases.md +521 -521
  144. package/augment-extensions/domain-rules/database/rules/vector-embeddings.md +858 -858
  145. package/augment-extensions/domain-rules/database/rules/vector-indexing.md +934 -934
  146. package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/README.md +23 -23
  147. package/augment-extensions/domain-rules/design/color/themes/catppuccin-latte/module.json +26 -26
  148. package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/README.md +23 -23
  149. package/augment-extensions/domain-rules/design/color/themes/catppuccin-mocha/module.json +26 -26
  150. package/augment-extensions/domain-rules/design/color/themes/dracula/README.md +23 -23
  151. package/augment-extensions/domain-rules/design/color/themes/dracula/module.json +26 -26
  152. package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/README.md +23 -23
  153. package/augment-extensions/domain-rules/design/color/themes/gruvbox-dark/module.json +26 -26
  154. package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/README.md +23 -23
  155. package/augment-extensions/domain-rules/design/color/themes/gruvbox-light/module.json +26 -26
  156. package/augment-extensions/domain-rules/design/color/themes/high-contrast/README.md +27 -27
  157. package/augment-extensions/domain-rules/design/color/themes/high-contrast/module.json +26 -26
  158. package/augment-extensions/domain-rules/design/color/themes/monokai/README.md +23 -23
  159. package/augment-extensions/domain-rules/design/color/themes/monokai/module.json +26 -26
  160. package/augment-extensions/domain-rules/design/color/themes/nord/README.md +23 -23
  161. package/augment-extensions/domain-rules/design/color/themes/nord/module.json +26 -26
  162. package/augment-extensions/domain-rules/design/color/themes/one-dark/README.md +23 -23
  163. package/augment-extensions/domain-rules/design/color/themes/one-dark/module.json +26 -26
  164. package/augment-extensions/domain-rules/design/color/themes/one-light/README.md +23 -23
  165. package/augment-extensions/domain-rules/design/color/themes/one-light/module.json +26 -26
  166. package/augment-extensions/domain-rules/design/color/themes/solarized-dark/README.md +23 -23
  167. package/augment-extensions/domain-rules/design/color/themes/solarized-dark/module.json +26 -26
  168. package/augment-extensions/domain-rules/design/color/themes/solarized-light/README.md +23 -23
  169. package/augment-extensions/domain-rules/design/color/themes/solarized-light/module.json +26 -26
  170. package/augment-extensions/domain-rules/design/color/themes/tokyo-night/README.md +23 -23
  171. package/augment-extensions/domain-rules/design/color/themes/tokyo-night/module.json +26 -26
  172. package/augment-extensions/domain-rules/mcp/README.md +150 -150
  173. package/augment-extensions/domain-rules/mcp/examples/compressed-example.md +522 -522
  174. package/augment-extensions/domain-rules/mcp/examples/graph-augmented-example.md +520 -520
  175. package/augment-extensions/domain-rules/mcp/examples/hybrid-example.md +570 -570
  176. package/augment-extensions/domain-rules/mcp/examples/state-based-example.md +427 -427
  177. package/augment-extensions/domain-rules/mcp/examples/token-based-example.md +435 -435
  178. package/augment-extensions/domain-rules/mcp/examples/vector-based-example.md +502 -502
  179. package/augment-extensions/domain-rules/mcp/module.json +49 -49
  180. package/augment-extensions/domain-rules/mcp/rules/compressed-mcp.md +595 -595
  181. package/augment-extensions/domain-rules/mcp/rules/configuration.md +345 -345
  182. package/augment-extensions/domain-rules/mcp/rules/graph-augmented-mcp.md +687 -687
  183. package/augment-extensions/domain-rules/mcp/rules/hybrid-mcp.md +636 -636
  184. package/augment-extensions/domain-rules/mcp/rules/state-based-mcp.md +484 -484
  185. package/augment-extensions/domain-rules/mcp/rules/testing-validation.md +360 -360
  186. package/augment-extensions/domain-rules/mcp/rules/token-based-mcp.md +393 -393
  187. package/augment-extensions/domain-rules/mcp/rules/universal-rules.md +194 -194
  188. package/augment-extensions/domain-rules/mcp/rules/vector-based-mcp.md +625 -625
  189. package/augment-extensions/domain-rules/security/README.md +41 -41
  190. package/augment-extensions/domain-rules/security/module.json +28 -28
  191. package/augment-extensions/domain-rules/security/rules/authentication-security.md +361 -361
  192. package/augment-extensions/domain-rules/security/rules/encryption.md +208 -208
  193. package/augment-extensions/domain-rules/security/rules/input-validation.md +294 -294
  194. package/augment-extensions/domain-rules/security/rules/owasp-top-10.md +339 -339
  195. package/augment-extensions/domain-rules/security/rules/secure-coding.md +293 -293
  196. package/augment-extensions/domain-rules/security/rules/web-security.md +268 -268
  197. package/augment-extensions/domain-rules/seo-sales-marketing/ANNOUNCEMENT.md +143 -0
  198. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/README.md +140 -136
  199. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/SCHEMA-VALIDATION-REPORT.md +216 -216
  200. package/augment-extensions/domain-rules/seo-sales-marketing/TEST-VALIDATION.md +129 -0
  201. package/augment-extensions/domain-rules/seo-sales-marketing/USAGE-GUIDES.md +254 -0
  202. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/examples/brand-kit-example.yaml +292 -292
  203. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/examples/campaign-brief-example.yaml +389 -389
  204. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/examples/content-calendar-example.yaml +643 -643
  205. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/examples/email-newsletter-example.md +376 -376
  206. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/examples/landing-page-example.md +934 -934
  207. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/examples/ppc-ad-copy-example.md +301 -301
  208. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/examples/seo-blog-post-example.md +347 -347
  209. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/examples/social-media-campaign-example.md +606 -606
  210. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/module.json +50 -50
  211. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/affiliate-influencer-marketing.md +593 -593
  212. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/asset-management.md +418 -418
  213. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/brand-consistency.md +210 -210
  214. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/content-marketing.md +337 -337
  215. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/conversion-optimization.md +455 -455
  216. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/direct-sales.md +499 -499
  217. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/email-marketing.md +439 -439
  218. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/legal-compliance.md +227 -227
  219. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/ppc-advertising.md +569 -569
  220. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/seo-optimization.md +470 -470
  221. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/social-media-marketing.md +414 -414
  222. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/rules/universal-marketing.md +177 -177
  223. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/schemas/asset-inventory.schema.json +247 -247
  224. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/schemas/brand-kit.schema.json +326 -326
  225. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/schemas/campaign-brief.schema.json +342 -342
  226. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/schemas/color-palette.schema.json +223 -223
  227. package/augment-extensions/domain-rules/{marketing-standards/seo-sales-marketing → seo-sales-marketing}/schemas/content-template.schema.json +383 -383
  228. package/augment-extensions/domain-rules/wordpress/README.md +163 -163
  229. package/augment-extensions/domain-rules/wordpress/module.json +32 -32
  230. package/augment-extensions/domain-rules/wordpress/rules/coding-standards.md +617 -617
  231. package/augment-extensions/domain-rules/wordpress/rules/directory-structure.md +270 -270
  232. package/augment-extensions/domain-rules/wordpress/rules/file-patterns.md +423 -423
  233. package/augment-extensions/domain-rules/wordpress/rules/gutenberg-blocks.md +493 -493
  234. package/augment-extensions/domain-rules/wordpress/rules/performance.md +568 -568
  235. package/augment-extensions/domain-rules/wordpress/rules/plugin-development.md +510 -510
  236. package/augment-extensions/domain-rules/wordpress/rules/project-detection.md +251 -251
  237. package/augment-extensions/domain-rules/wordpress/rules/rest-api.md +501 -501
  238. package/augment-extensions/domain-rules/wordpress/rules/security.md +564 -564
  239. package/augment-extensions/domain-rules/wordpress/rules/theme-development.md +388 -388
  240. package/augment-extensions/domain-rules/wordpress/rules/woocommerce.md +441 -441
  241. package/augment-extensions/domain-rules/wordpress-plugin/README.md +139 -139
  242. package/augment-extensions/domain-rules/wordpress-plugin/examples/ajax-plugin.md +1599 -1599
  243. package/augment-extensions/domain-rules/wordpress-plugin/examples/custom-post-type-plugin.md +1727 -1727
  244. package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block-plugin.md +428 -428
  245. package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block.md +422 -422
  246. package/augment-extensions/domain-rules/wordpress-plugin/examples/mvc-plugin.md +1623 -1623
  247. package/augment-extensions/domain-rules/wordpress-plugin/examples/object-oriented-plugin.md +1343 -1343
  248. package/augment-extensions/domain-rules/wordpress-plugin/examples/rest-endpoint.md +734 -734
  249. package/augment-extensions/domain-rules/wordpress-plugin/examples/settings-page-plugin.md +1350 -1350
  250. package/augment-extensions/domain-rules/wordpress-plugin/examples/simple-procedural-plugin.md +503 -503
  251. package/augment-extensions/domain-rules/wordpress-plugin/examples/singleton-plugin.md +971 -971
  252. package/augment-extensions/domain-rules/wordpress-plugin/module.json +53 -53
  253. package/augment-extensions/domain-rules/wordpress-plugin/rules/activation-hooks.md +770 -770
  254. package/augment-extensions/domain-rules/wordpress-plugin/rules/admin-interface.md +874 -874
  255. package/augment-extensions/domain-rules/wordpress-plugin/rules/ajax-handlers.md +629 -629
  256. package/augment-extensions/domain-rules/wordpress-plugin/rules/asset-management.md +559 -559
  257. package/augment-extensions/domain-rules/wordpress-plugin/rules/context-providers.md +709 -709
  258. package/augment-extensions/domain-rules/wordpress-plugin/rules/cron-jobs.md +736 -736
  259. package/augment-extensions/domain-rules/wordpress-plugin/rules/database-management.md +1057 -1057
  260. package/augment-extensions/domain-rules/wordpress-plugin/rules/documentation-standards.md +463 -463
  261. package/augment-extensions/domain-rules/wordpress-plugin/rules/frontend-functionality.md +478 -478
  262. package/augment-extensions/domain-rules/wordpress-plugin/rules/gutenberg-blocks.md +818 -818
  263. package/augment-extensions/domain-rules/wordpress-plugin/rules/internationalization.md +416 -416
  264. package/augment-extensions/domain-rules/wordpress-plugin/rules/migration.md +667 -667
  265. package/augment-extensions/domain-rules/wordpress-plugin/rules/performance-optimization.md +878 -878
  266. package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-architecture.md +693 -693
  267. package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-structure.md +352 -352
  268. package/augment-extensions/domain-rules/wordpress-plugin/rules/rest-api.md +818 -818
  269. package/augment-extensions/domain-rules/wordpress-plugin/rules/scaffolding-workflow.md +624 -624
  270. package/augment-extensions/domain-rules/wordpress-plugin/rules/security-best-practices.md +866 -866
  271. package/augment-extensions/domain-rules/wordpress-plugin/rules/testing-patterns.md +1165 -1165
  272. package/augment-extensions/domain-rules/wordpress-plugin/rules/testing.md +414 -414
  273. package/augment-extensions/domain-rules/wordpress-plugin/rules/vscode-integration.md +751 -751
  274. package/augment-extensions/domain-rules/wordpress-plugin/rules/woocommerce-integration.md +949 -949
  275. package/augment-extensions/domain-rules/wordpress-plugin/rules/wordpress-org-submission.md +458 -458
  276. package/augment-extensions/examples/design-patterns/README.md +37 -37
  277. package/augment-extensions/examples/design-patterns/examples/behavioral-patterns.md +370 -370
  278. package/augment-extensions/examples/design-patterns/examples/creational-patterns.md +250 -250
  279. package/augment-extensions/examples/design-patterns/examples/structural-patterns.md +264 -264
  280. package/augment-extensions/examples/design-patterns/module.json +27 -27
  281. package/augment-extensions/examples/gutenberg-block-plugin/README.md +101 -101
  282. package/augment-extensions/examples/gutenberg-block-plugin/examples/testimonial-block.md +428 -428
  283. package/augment-extensions/examples/gutenberg-block-plugin/module.json +40 -40
  284. package/augment-extensions/examples/rest-api-plugin/README.md +98 -98
  285. package/augment-extensions/examples/rest-api-plugin/examples/task-manager-api.md +1299 -1299
  286. package/augment-extensions/examples/rest-api-plugin/module.json +40 -40
  287. package/augment-extensions/examples/woocommerce-extension/README.md +98 -98
  288. package/augment-extensions/examples/woocommerce-extension/examples/product-customizer.md +763 -763
  289. package/augment-extensions/examples/woocommerce-extension/module.json +40 -40
  290. package/augment-extensions/workflows/beads/README.md +135 -135
  291. package/augment-extensions/workflows/beads/examples/complete-workflow-example.md +278 -278
  292. package/augment-extensions/workflows/beads/module.json +55 -55
  293. package/augment-extensions/workflows/beads/rules/best-practices.md +398 -398
  294. package/augment-extensions/workflows/beads/rules/file-format.md +327 -327
  295. package/augment-extensions/workflows/beads/rules/manual-setup.md +315 -315
  296. package/augment-extensions/workflows/beads/rules/workflow.md +326 -326
  297. package/augment-extensions/workflows/beads-integration/IMPLEMENTATION-STATUS.md +145 -145
  298. package/augment-extensions/workflows/beads-integration/README.md +143 -143
  299. package/augment-extensions/workflows/beads-integration/config/defaults.json +32 -32
  300. package/augment-extensions/workflows/beads-integration/config/schema.json +140 -140
  301. package/augment-extensions/workflows/beads-integration/examples/basic-task-generation.md +293 -293
  302. package/augment-extensions/workflows/beads-integration/module.json +75 -75
  303. package/augment-extensions/workflows/beads-integration/rules/core-rules.md +219 -219
  304. package/augment-extensions/workflows/beads-integration/rules/effectiveness-standards.md +256 -256
  305. package/augment-extensions/workflows/beads-integration/rules/task-generation.md +607 -607
  306. package/augment-extensions/workflows/database/README.md +195 -195
  307. package/augment-extensions/workflows/database/ai-prompt-testing.md +295 -295
  308. package/augment-extensions/workflows/database/examples/migration-example.md +498 -498
  309. package/augment-extensions/workflows/database/examples/optimization-example.md +496 -496
  310. package/augment-extensions/workflows/database/examples/schema-design-example.md +444 -444
  311. package/augment-extensions/workflows/database/module.json +42 -42
  312. package/augment-extensions/workflows/database/rules/data-migration.md +249 -249
  313. package/augment-extensions/workflows/database/rules/documentation-standards.md +339 -339
  314. package/augment-extensions/workflows/database/rules/migration-workflow.md +352 -352
  315. package/augment-extensions/workflows/database/rules/optimization-workflow.md +435 -435
  316. package/augment-extensions/workflows/database/rules/schema-design-workflow.md +535 -535
  317. package/augment-extensions/workflows/database/rules/testing-patterns.md +305 -305
  318. package/augment-extensions/workflows/database/rules/workflow.md +458 -458
  319. package/augment-extensions/workflows/wordpress-plugin/README.md +232 -232
  320. package/augment-extensions/workflows/wordpress-plugin/ai-prompts.md +839 -839
  321. package/augment-extensions/workflows/wordpress-plugin/bead-decomposition-patterns.md +854 -854
  322. package/augment-extensions/workflows/wordpress-plugin/examples/complete-plugin-example.md +540 -540
  323. package/augment-extensions/workflows/wordpress-plugin/examples/custom-post-type-example.md +1083 -1083
  324. package/augment-extensions/workflows/wordpress-plugin/examples/feature-addition-workflow.md +669 -669
  325. package/augment-extensions/workflows/wordpress-plugin/examples/plugin-creation-workflow.md +597 -597
  326. package/augment-extensions/workflows/wordpress-plugin/examples/secure-form-handler-example.md +925 -925
  327. package/augment-extensions/workflows/wordpress-plugin/examples/security-audit-workflow.md +752 -752
  328. package/augment-extensions/workflows/wordpress-plugin/examples/wordpress-org-submission-workflow.md +773 -773
  329. package/augment-extensions/workflows/wordpress-plugin/module.json +49 -49
  330. package/augment-extensions/workflows/wordpress-plugin/rules/best-practices.md +942 -942
  331. package/augment-extensions/workflows/wordpress-plugin/rules/development-workflow.md +702 -702
  332. package/augment-extensions/workflows/wordpress-plugin/rules/submission-workflow.md +728 -728
  333. package/augment-extensions/workflows/wordpress-plugin/rules/testing-workflow.md +775 -775
  334. package/augment-extensions/writing-standards/screenplay/README.md +339 -300
  335. package/augment-extensions/writing-standards/screenplay/_templates/README.md +121 -121
  336. package/augment-extensions/writing-standards/screenplay/_templates/genre-template.md +153 -153
  337. package/augment-extensions/writing-standards/screenplay/_templates/style-template.md +243 -243
  338. package/augment-extensions/writing-standards/screenplay/_templates/theme-template.md +213 -213
  339. package/augment-extensions/writing-standards/screenplay/examples/aaa-hollywood-scene.fountain +164 -164
  340. package/augment-extensions/writing-standards/screenplay/examples/beat-sheet-example.yaml +95 -95
  341. package/augment-extensions/writing-standards/screenplay/examples/character-profile-example.yaml +116 -116
  342. package/augment-extensions/writing-standards/screenplay/examples/commercial-30sec.fountain +151 -151
  343. package/augment-extensions/writing-standards/screenplay/examples/independent-monologue.fountain +67 -67
  344. package/augment-extensions/writing-standards/screenplay/examples/news-segment.fountain +142 -142
  345. package/augment-extensions/writing-standards/screenplay/examples/plot-outline-example.yaml +184 -184
  346. package/augment-extensions/writing-standards/screenplay/examples/tv-episode-teaser.fountain +204 -204
  347. package/augment-extensions/writing-standards/screenplay/genres/README.md +181 -181
  348. package/augment-extensions/writing-standards/screenplay/genres/examples/.gitkeep +2 -2
  349. package/augment-extensions/writing-standards/screenplay/genres/module.json +70 -70
  350. package/augment-extensions/writing-standards/screenplay/genres/rules/.gitkeep +2 -2
  351. package/augment-extensions/writing-standards/screenplay/genres/rules/action.md +399 -399
  352. package/augment-extensions/writing-standards/screenplay/genres/rules/adventure.md +407 -407
  353. package/augment-extensions/writing-standards/screenplay/genres/rules/animation.md +293 -293
  354. package/augment-extensions/writing-standards/screenplay/genres/rules/biographical.md +293 -293
  355. package/augment-extensions/writing-standards/screenplay/genres/rules/comedy.md +401 -401
  356. package/augment-extensions/writing-standards/screenplay/genres/rules/documentary.md +293 -293
  357. package/augment-extensions/writing-standards/screenplay/genres/rules/drama.md +409 -409
  358. package/augment-extensions/writing-standards/screenplay/genres/rules/fantasy.md +293 -293
  359. package/augment-extensions/writing-standards/screenplay/genres/rules/historical.md +293 -293
  360. package/augment-extensions/writing-standards/screenplay/genres/rules/horror.md +268 -268
  361. package/augment-extensions/writing-standards/screenplay/genres/rules/musical.md +294 -294
  362. package/augment-extensions/writing-standards/screenplay/genres/rules/mystery.md +293 -293
  363. package/augment-extensions/writing-standards/screenplay/genres/rules/noir.md +294 -294
  364. package/augment-extensions/writing-standards/screenplay/genres/rules/romance.md +293 -293
  365. package/augment-extensions/writing-standards/screenplay/genres/rules/sci-fi.md +289 -289
  366. package/augment-extensions/writing-standards/screenplay/genres/rules/superhero.md +293 -293
  367. package/augment-extensions/writing-standards/screenplay/genres/rules/thriller.md +294 -294
  368. package/augment-extensions/writing-standards/screenplay/genres/rules/western.md +293 -293
  369. package/augment-extensions/writing-standards/screenplay/module.json +124 -124
  370. package/augment-extensions/writing-standards/screenplay/rules/aaa-hollywood-films.md +339 -339
  371. package/augment-extensions/writing-standards/screenplay/rules/ai-integration-testing.md +329 -329
  372. package/augment-extensions/writing-standards/screenplay/rules/character-development.md +169 -169
  373. package/augment-extensions/writing-standards/screenplay/rules/commercials.md +437 -437
  374. package/augment-extensions/writing-standards/screenplay/rules/dialogue-writing.md +263 -263
  375. package/augment-extensions/writing-standards/screenplay/rules/diversity-inclusion.md +261 -261
  376. package/augment-extensions/writing-standards/screenplay/rules/examples-guide.md +315 -315
  377. package/augment-extensions/writing-standards/screenplay/rules/file-organization.md +213 -0
  378. package/augment-extensions/writing-standards/screenplay/rules/formatting-validation.md +413 -413
  379. package/augment-extensions/writing-standards/screenplay/rules/fountain-format.md +372 -372
  380. package/augment-extensions/writing-standards/screenplay/rules/independent-films.md +374 -374
  381. package/augment-extensions/writing-standards/screenplay/rules/live-tv-productions.md +443 -443
  382. package/augment-extensions/writing-standards/screenplay/rules/narrative-structures.md +207 -207
  383. package/augment-extensions/writing-standards/screenplay/rules/news-broadcasts.md +444 -444
  384. package/augment-extensions/writing-standards/screenplay/rules/pacing-timing.md +331 -331
  385. package/augment-extensions/writing-standards/screenplay/rules/quality-review-checklist.md +334 -334
  386. package/augment-extensions/writing-standards/screenplay/rules/quick-reference.md +299 -299
  387. package/augment-extensions/writing-standards/screenplay/rules/screen-continuity.md +263 -263
  388. package/augment-extensions/writing-standards/screenplay/rules/streaming-content.md +412 -412
  389. package/augment-extensions/writing-standards/screenplay/rules/trope-management.md +370 -370
  390. package/augment-extensions/writing-standards/screenplay/rules/tv-series.md +374 -374
  391. package/augment-extensions/writing-standards/screenplay/rules/universal-formatting.md +339 -339
  392. package/augment-extensions/writing-standards/screenplay/rules/vscode-integration.md +277 -277
  393. package/augment-extensions/writing-standards/screenplay/rules/web-content.md +393 -393
  394. package/augment-extensions/writing-standards/screenplay/schemas/beat-sheet.json +332 -332
  395. package/augment-extensions/writing-standards/screenplay/schemas/character-profile.json +247 -247
  396. package/augment-extensions/writing-standards/screenplay/schemas/feature-selection.json +200 -200
  397. package/augment-extensions/writing-standards/screenplay/schemas/plot-outline.json +233 -233
  398. package/augment-extensions/writing-standards/screenplay/schemas/screenplay-config.json +245 -245
  399. package/augment-extensions/writing-standards/screenplay/schemas/trope-inventory.json +221 -221
  400. package/augment-extensions/writing-standards/screenplay/styles/README.md +159 -159
  401. package/augment-extensions/writing-standards/screenplay/styles/examples/.gitkeep +2 -2
  402. package/augment-extensions/writing-standards/screenplay/styles/examples/style-applications.md +1449 -1449
  403. package/augment-extensions/writing-standards/screenplay/styles/module.json +64 -64
  404. package/augment-extensions/writing-standards/screenplay/styles/rules/.gitkeep +2 -2
  405. package/augment-extensions/writing-standards/screenplay/styles/rules/dialogue-centric.md +520 -520
  406. package/augment-extensions/writing-standards/screenplay/styles/rules/ensemble.md +499 -499
  407. package/augment-extensions/writing-standards/screenplay/styles/rules/epic.md +497 -497
  408. package/augment-extensions/writing-standards/screenplay/styles/rules/experimental.md +492 -492
  409. package/augment-extensions/writing-standards/screenplay/styles/rules/flashback.md +509 -509
  410. package/augment-extensions/writing-standards/screenplay/styles/rules/linear.md +490 -490
  411. package/augment-extensions/writing-standards/screenplay/styles/rules/minimalist.md +499 -499
  412. package/augment-extensions/writing-standards/screenplay/styles/rules/non-linear.md +501 -501
  413. package/augment-extensions/writing-standards/screenplay/styles/rules/poetic.md +499 -499
  414. package/augment-extensions/writing-standards/screenplay/styles/rules/realistic.md +498 -498
  415. package/augment-extensions/writing-standards/screenplay/styles/rules/satirical.md +499 -499
  416. package/augment-extensions/writing-standards/screenplay/styles/rules/surreal.md +508 -508
  417. package/augment-extensions/writing-standards/screenplay/styles/rules/voice-over.md +500 -500
  418. package/augment-extensions/writing-standards/screenplay/themes/README.md +158 -158
  419. package/augment-extensions/writing-standards/screenplay/themes/examples/.gitkeep +2 -2
  420. package/augment-extensions/writing-standards/screenplay/themes/examples/common-mistakes-and-fixes.md +643 -643
  421. package/augment-extensions/writing-standards/screenplay/themes/examples/complete-scene-example.md +311 -311
  422. package/augment-extensions/writing-standards/screenplay/themes/examples/individual-theme-examples.md +562 -562
  423. package/augment-extensions/writing-standards/screenplay/themes/examples/multi-theme-weaving.md +538 -538
  424. package/augment-extensions/writing-standards/screenplay/themes/examples/theme-application-guide.md +432 -432
  425. package/augment-extensions/writing-standards/screenplay/themes/examples/theme-integration-across-acts.md +637 -637
  426. package/augment-extensions/writing-standards/screenplay/themes/module.json +66 -66
  427. package/augment-extensions/writing-standards/screenplay/themes/rules/.gitkeep +2 -2
  428. package/augment-extensions/writing-standards/screenplay/themes/rules/ambition.md +458 -458
  429. package/augment-extensions/writing-standards/screenplay/themes/rules/betrayal.md +490 -490
  430. package/augment-extensions/writing-standards/screenplay/themes/rules/environment.md +458 -458
  431. package/augment-extensions/writing-standards/screenplay/themes/rules/fate.md +459 -459
  432. package/augment-extensions/writing-standards/screenplay/themes/rules/friendship.md +491 -491
  433. package/augment-extensions/writing-standards/screenplay/themes/rules/growth.md +491 -491
  434. package/augment-extensions/writing-standards/screenplay/themes/rules/identity.md +490 -490
  435. package/augment-extensions/writing-standards/screenplay/themes/rules/isolation.md +464 -464
  436. package/augment-extensions/writing-standards/screenplay/themes/rules/justice.md +461 -461
  437. package/augment-extensions/writing-standards/screenplay/themes/rules/love.md +489 -489
  438. package/augment-extensions/writing-standards/screenplay/themes/rules/power.md +494 -494
  439. package/augment-extensions/writing-standards/screenplay/themes/rules/redemption.md +483 -483
  440. package/augment-extensions/writing-standards/screenplay/themes/rules/revenge.md +489 -489
  441. package/augment-extensions/writing-standards/screenplay/themes/rules/survival.md +496 -496
  442. package/augment-extensions/writing-standards/screenplay/themes/rules/technology.md +463 -463
  443. package/augment-extensions/writing-standards/screenplay/utils/__tests__/file-organization.test.ts +169 -0
  444. package/augment-extensions/writing-standards/screenplay/utils/file-organization.ts +165 -0
  445. package/cli/MODULES.md +302 -302
  446. package/cli/dist/cli.js +109 -22
  447. package/cli/dist/cli.js.map +1 -1
  448. package/cli/dist/commands/gui.d.ts.map +1 -1
  449. package/cli/dist/commands/gui.js +54 -6
  450. package/cli/dist/commands/gui.js.map +1 -1
  451. package/cli/dist/commands/init.d.ts.map +1 -1
  452. package/cli/dist/commands/init.js +76 -23
  453. package/cli/dist/commands/init.js.map +1 -1
  454. package/cli/dist/commands/self-remove.d.ts.map +1 -1
  455. package/cli/dist/commands/self-remove.js +48 -74
  456. package/cli/dist/commands/self-remove.js.map +1 -1
  457. package/cli/dist/commands/show.d.ts +11 -0
  458. package/cli/dist/commands/show.d.ts.map +1 -1
  459. package/cli/dist/commands/show.js +120 -0
  460. package/cli/dist/commands/show.js.map +1 -1
  461. package/cli/dist/commands/showCompleted.d.ts +21 -0
  462. package/cli/dist/commands/showCompleted.d.ts.map +1 -0
  463. package/cli/dist/commands/showCompleted.js +225 -0
  464. package/cli/dist/commands/showCompleted.js.map +1 -0
  465. package/cli/dist/commands/skill.js +88 -88
  466. package/cli/dist/commands/update.d.ts +2 -0
  467. package/cli/dist/commands/update.d.ts.map +1 -1
  468. package/cli/dist/commands/update.js +67 -1
  469. package/cli/dist/commands/update.js.map +1 -1
  470. package/cli/dist/utils/beadsCompletedChecker.d.ts +72 -0
  471. package/cli/dist/utils/beadsCompletedChecker.d.ts.map +1 -0
  472. package/cli/dist/utils/beadsCompletedChecker.js +198 -0
  473. package/cli/dist/utils/beadsCompletedChecker.js.map +1 -0
  474. package/cli/dist/utils/catalog-sync.js +13 -13
  475. package/cli/dist/utils/extractCommandHelp.d.ts +51 -0
  476. package/cli/dist/utils/extractCommandHelp.d.ts.map +1 -0
  477. package/cli/dist/utils/extractCommandHelp.js +250 -0
  478. package/cli/dist/utils/extractCommandHelp.js.map +1 -0
  479. package/cli/dist/utils/install-rules.js +55 -55
  480. package/cli/dist/utils/mcp-integration.js +44 -44
  481. package/cli/dist/utils/rule-install-hooks.js +8 -8
  482. package/modules.md +667 -630
  483. package/package.json +85 -85
@@ -1,925 +1,925 @@
1
- # Secure Form Handler Example
2
-
3
- This example demonstrates implementing a secure form handler in WordPress with comprehensive security measures including nonce verification, capability checks, input sanitization, and output escaping.
4
-
5
- ## Scenario
6
-
7
- Creating a secure contact form handler that demonstrates all WordPress security best practices.
8
-
9
- ## Complete Implementation
10
-
11
- ### Form Display
12
-
13
- **File**: `public/partials/contact-form.php`
14
-
15
- ```php
16
- <?php
17
- /**
18
- * Contact form template.
19
- *
20
- * Security features:
21
- * - Nonce field for CSRF protection
22
- * - Escaped output for XSS prevention
23
- * - Honeypot field for spam prevention
24
- */
25
-
26
- // Get current values (for form repopulation after validation errors)
27
- $name = isset($_POST['contact_name']) ? sanitize_text_field($_POST['contact_name']) : '';
28
- $email = isset($_POST['contact_email']) ? sanitize_email($_POST['contact_email']) : '';
29
- $subject = isset($_POST['contact_subject']) ? sanitize_text_field($_POST['contact_subject']) : '';
30
- $message = isset($_POST['contact_message']) ? sanitize_textarea_field($_POST['contact_message']) : '';
31
-
32
- // Get error messages
33
- $errors = get_transient('contact_form_errors_' . session_id());
34
- $success = get_transient('contact_form_success_' . session_id());
35
-
36
- // Clear transients
37
- delete_transient('contact_form_errors_' . session_id());
38
- delete_transient('contact_form_success_' . session_id());
39
- ?>
40
-
41
- <div class="contact-form-wrapper">
42
- <?php if ($success) : ?>
43
- <div class="contact-form-success">
44
- <p><?php echo esc_html($success); ?></p>
45
- </div>
46
- <?php endif; ?>
47
-
48
- <?php if ($errors && is_array($errors)) : ?>
49
- <div class="contact-form-errors">
50
- <ul>
51
- <?php foreach ($errors as $error) : ?>
52
- <li><?php echo esc_html($error); ?></li>
53
- <?php endforeach; ?>
54
- </ul>
55
- </div>
56
- <?php endif; ?>
57
-
58
- <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="contact-form">
59
- <!-- Nonce field for CSRF protection -->
60
- <?php wp_nonce_field('contact_form_submit', 'contact_form_nonce'); ?>
61
-
62
- <!-- Action for WordPress admin-post.php handler -->
63
- <input type="hidden" name="action" value="submit_contact_form" />
64
-
65
- <!-- Honeypot field (hidden from users, catches bots) -->
66
- <input type="text" name="contact_website" value="" style="display:none;" tabindex="-1" autocomplete="off" />
67
-
68
- <div class="form-field">
69
- <label for="contact_name">
70
- <?php _e('Name', 'text-domain'); ?> <span class="required">*</span>
71
- </label>
72
- <input type="text"
73
- id="contact_name"
74
- name="contact_name"
75
- value="<?php echo esc_attr($name); ?>"
76
- required
77
- maxlength="100" />
78
- </div>
79
-
80
- <div class="form-field">
81
- <label for="contact_email">
82
- <?php _e('Email', 'text-domain'); ?> <span class="required">*</span>
83
- </label>
84
- <input type="email"
85
- id="contact_email"
86
- name="contact_email"
87
- value="<?php echo esc_attr($email); ?>"
88
- required
89
- maxlength="100" />
90
- </div>
91
-
92
- <div class="form-field">
93
- <label for="contact_subject">
94
- <?php _e('Subject', 'text-domain'); ?> <span class="required">*</span>
95
- </label>
96
- <input type="text"
97
- id="contact_subject"
98
- name="contact_subject"
99
- value="<?php echo esc_attr($subject); ?>"
100
- required
101
- maxlength="200" />
102
- </div>
103
-
104
- <div class="form-field">
105
- <label for="contact_message">
106
- <?php _e('Message', 'text-domain'); ?> <span class="required">*</span>
107
- </label>
108
- <textarea id="contact_message"
109
- name="contact_message"
110
- required
111
- rows="6"
112
- maxlength="2000"><?php echo esc_textarea($message); ?></textarea>
113
- </div>
114
-
115
- <div class="form-field">
116
- <button type="submit" class="submit-button">
117
- <?php _e('Send Message', 'text-domain'); ?>
118
- </button>
119
- </div>
120
- </form>
121
- </div>
122
- ```
123
-
124
- ### Form Handler Class
125
-
126
- **File**: `includes/class-contact-form-handler.php`
127
-
128
- ```php
129
- <?php
130
- /**
131
- * Secure contact form handler.
132
- *
133
- * Security features:
134
- * - Nonce verification (CSRF protection)
135
- * - Capability checks (authorization)
136
- * - Input sanitization (data cleaning)
137
- * - Input validation (data verification)
138
- * - Rate limiting (spam prevention)
139
- * - Honeypot field (bot detection)
140
- * - SQL injection prevention (prepared statements)
141
- * - XSS prevention (output escaping)
142
- */
143
- class Contact_Form_Handler {
144
-
145
- /**
146
- * Rate limit: max submissions per IP per hour.
147
- */
148
- const RATE_LIMIT = 3;
149
-
150
- /**
151
- * Initialize the form handler.
152
- */
153
- public function __construct() {
154
- // Register form submission handler
155
- add_action('admin_post_submit_contact_form', array($this, 'handle_submission'));
156
- add_action('admin_post_nopriv_submit_contact_form', array($this, 'handle_submission'));
157
- }
158
-
159
- /**
160
- * Handle form submission.
161
- */
162
- public function handle_submission() {
163
- // Start session for error/success messages
164
- if (!session_id()) {
165
- session_start();
166
- }
167
-
168
- $errors = array();
169
-
170
- // 1. CSRF Protection: Verify nonce
171
- if (!isset($_POST['contact_form_nonce']) ||
172
- !wp_verify_nonce($_POST['contact_form_nonce'], 'contact_form_submit')) {
173
- $errors[] = __('Security check failed. Please try again.', 'text-domain');
174
- $this->redirect_with_errors($errors);
175
- return;
176
- }
177
-
178
- // 2. Bot Detection: Check honeypot field
179
- if (!empty($_POST['contact_website'])) {
180
- // Bot detected - silently fail
181
- $this->redirect_with_success(__('Thank you for your message!', 'text-domain'));
182
- return;
183
- }
184
-
185
- // 3. Rate Limiting: Check submission rate
186
- if (!$this->check_rate_limit()) {
187
- $errors[] = __('Too many submissions. Please try again later.', 'text-domain');
188
- $this->redirect_with_errors($errors);
189
- return;
190
- }
191
-
192
- // 4. Input Sanitization: Clean all inputs
193
- $name = isset($_POST['contact_name']) ? sanitize_text_field($_POST['contact_name']) : '';
194
- $email = isset($_POST['contact_email']) ? sanitize_email($_POST['contact_email']) : '';
195
- $subject = isset($_POST['contact_subject']) ? sanitize_text_field($_POST['contact_subject']) : '';
196
- $message = isset($_POST['contact_message']) ? sanitize_textarea_field($_POST['contact_message']) : '';
197
-
198
- // 5. Input Validation: Verify data
199
- $errors = $this->validate_input($name, $email, $subject, $message);
200
-
201
- if (!empty($errors)) {
202
- $this->redirect_with_errors($errors);
203
- return;
204
- }
205
-
206
- // 6. Save to Database: Use prepared statements
207
- $saved = $this->save_to_database($name, $email, $subject, $message);
208
-
209
- if (!$saved) {
210
- $errors[] = __('Failed to save message. Please try again.', 'text-domain');
211
- $this->redirect_with_errors($errors);
212
- return;
213
- }
214
-
215
- // 7. Send Email Notification
216
- $this->send_email_notification($name, $email, $subject, $message);
217
-
218
- // 8. Log Submission
219
- $this->log_submission($email);
220
-
221
- // 9. Success
222
- $this->redirect_with_success(__('Thank you for your message! We will get back to you soon.', 'text-domain'));
223
- }
224
-
225
- /**
226
- * Validate form input.
227
- *
228
- * @return array Array of error messages (empty if valid)
229
- */
230
- private function validate_input($name, $email, $subject, $message) {
231
- $errors = array();
232
-
233
- // Validate name
234
- if (empty($name)) {
235
- $errors[] = __('Name is required.', 'text-domain');
236
- } elseif (strlen($name) < 2) {
237
- $errors[] = __('Name must be at least 2 characters.', 'text-domain');
238
- } elseif (strlen($name) > 100) {
239
- $errors[] = __('Name must be less than 100 characters.', 'text-domain');
240
- }
241
-
242
- // Validate email
243
- if (empty($email)) {
244
- $errors[] = __('Email is required.', 'text-domain');
245
- } elseif (!is_email($email)) {
246
- $errors[] = __('Please enter a valid email address.', 'text-domain');
247
- }
248
-
249
- // Validate subject
250
- if (empty($subject)) {
251
- $errors[] = __('Subject is required.', 'text-domain');
252
- } elseif (strlen($subject) < 3) {
253
- $errors[] = __('Subject must be at least 3 characters.', 'text-domain');
254
- } elseif (strlen($subject) > 200) {
255
- $errors[] = __('Subject must be less than 200 characters.', 'text-domain');
256
- }
257
-
258
- // Validate message
259
- if (empty($message)) {
260
- $errors[] = __('Message is required.', 'text-domain');
261
- } elseif (strlen($message) < 10) {
262
- $errors[] = __('Message must be at least 10 characters.', 'text-domain');
263
- } elseif (strlen($message) > 2000) {
264
- $errors[] = __('Message must be less than 2000 characters.', 'text-domain');
265
- }
266
-
267
- return $errors;
268
- }
269
-
270
- /**
271
- * Check rate limit for submissions.
272
- *
273
- * @return bool True if within rate limit, false otherwise
274
- */
275
- private function check_rate_limit() {
276
- $ip_address = $this->get_client_ip();
277
- $transient_key = 'contact_form_rate_' . md5($ip_address);
278
-
279
- $submissions = get_transient($transient_key);
280
-
281
- if ($submissions === false) {
282
- // First submission in this hour
283
- set_transient($transient_key, 1, HOUR_IN_SECONDS);
284
- return true;
285
- }
286
-
287
- if ($submissions >= self::RATE_LIMIT) {
288
- // Rate limit exceeded
289
- return false;
290
- }
291
-
292
- // Increment submission count
293
- set_transient($transient_key, $submissions + 1, HOUR_IN_SECONDS);
294
- return true;
295
- }
296
-
297
- /**
298
- * Get client IP address.
299
- *
300
- * @return string IP address
301
- */
302
- private function get_client_ip() {
303
- $ip_address = '';
304
-
305
- if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
306
- $ip_address = $_SERVER['HTTP_CLIENT_IP'];
307
- } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
308
- $ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
309
- } else {
310
- $ip_address = $_SERVER['REMOTE_ADDR'];
311
- }
312
-
313
- // Sanitize IP address
314
- return filter_var($ip_address, FILTER_VALIDATE_IP) ? $ip_address : '0.0.0.0';
315
- }
316
-
317
- /**
318
- * Save submission to database using prepared statements.
319
- *
320
- * @return bool True on success, false on failure
321
- */
322
- private function save_to_database($name, $email, $subject, $message) {
323
- global $wpdb;
324
-
325
- $table_name = $wpdb->prefix . 'contact_messages';
326
-
327
- // Use prepared statement to prevent SQL injection
328
- $result = $wpdb->insert(
329
- $table_name,
330
- array(
331
- 'name' => $name,
332
- 'email' => $email,
333
- 'subject' => $subject,
334
- 'message' => $message,
335
- 'ip_address' => $this->get_client_ip(),
336
- 'submitted_at' => current_time('mysql'),
337
- 'status' => 'unread',
338
- ),
339
- array(
340
- '%s', // name
341
- '%s', // email
342
- '%s', // subject
343
- '%s', // message
344
- '%s', // ip_address
345
- '%s', // submitted_at
346
- '%s', // status
347
- )
348
- );
349
-
350
- return $result !== false;
351
- }
352
-
353
- /**
354
- * Send email notification to admin.
355
- */
356
- private function send_email_notification($name, $email, $subject, $message) {
357
- $admin_email = get_option('admin_email');
358
-
359
- // Prepare email
360
- $to = $admin_email;
361
- $email_subject = sprintf(
362
- __('[%s] New Contact Form Submission: %s', 'text-domain'),
363
- get_bloginfo('name'),
364
- $subject
365
- );
366
-
367
- // Build email body with escaped content
368
- $email_body = sprintf(
369
- __("New contact form submission:\n\nName: %s\nEmail: %s\nSubject: %s\n\nMessage:\n%s", 'text-domain'),
370
- $name,
371
- $email,
372
- $subject,
373
- $message
374
- );
375
-
376
- // Set headers
377
- $headers = array(
378
- 'Content-Type: text/plain; charset=UTF-8',
379
- 'Reply-To: ' . $email,
380
- );
381
-
382
- // Send email
383
- wp_mail($to, $email_subject, $email_body, $headers);
384
- }
385
-
386
- /**
387
- * Log submission for security monitoring.
388
- */
389
- private function log_submission($email) {
390
- // Log to WordPress debug log if enabled
391
- if (defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
392
- error_log(sprintf(
393
- 'Contact form submission from %s (IP: %s)',
394
- $email,
395
- $this->get_client_ip()
396
- ));
397
- }
398
- }
399
-
400
- /**
401
- * Redirect back to form with errors.
402
- */
403
- private function redirect_with_errors($errors) {
404
- set_transient('contact_form_errors_' . session_id(), $errors, 60);
405
-
406
- $redirect_url = wp_get_referer();
407
- if (!$redirect_url) {
408
- $redirect_url = home_url();
409
- }
410
-
411
- wp_safe_redirect($redirect_url);
412
- exit;
413
- }
414
-
415
- /**
416
- * Redirect back to form with success message.
417
- */
418
- private function redirect_with_success($message) {
419
- set_transient('contact_form_success_' . session_id(), $message, 60);
420
-
421
- $redirect_url = wp_get_referer();
422
- if (!$redirect_url) {
423
- $redirect_url = home_url();
424
- }
425
-
426
- wp_safe_redirect($redirect_url);
427
- exit;
428
- }
429
- }
430
- ```
431
-
432
- ### AJAX Form Handler (Alternative)
433
-
434
- **File**: `includes/class-contact-form-ajax-handler.php`
435
-
436
- ```php
437
- <?php
438
- /**
439
- * Secure AJAX contact form handler.
440
- *
441
- * Additional security for AJAX:
442
- * - check_ajax_referer() for nonce verification
443
- * - wp_send_json_error() and wp_send_json_success() for responses
444
- */
445
- class Contact_Form_Ajax_Handler {
446
-
447
- /**
448
- * Initialize the AJAX handler.
449
- */
450
- public function __construct() {
451
- // Register AJAX handlers
452
- add_action('wp_ajax_submit_contact_form', array($this, 'handle_ajax_submission'));
453
- add_action('wp_ajax_nopriv_submit_contact_form', array($this, 'handle_ajax_submission'));
454
-
455
- // Enqueue scripts with localized nonce
456
- add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
457
- }
458
-
459
- /**
460
- * Enqueue scripts with localized data.
461
- */
462
- public function enqueue_scripts() {
463
- wp_enqueue_script(
464
- 'contact-form-ajax',
465
- plugin_dir_url(__FILE__) . '../public/js/contact-form-ajax.js',
466
- array('jquery'),
467
- '1.0.0',
468
- true
469
- );
470
-
471
- // Localize script with AJAX URL and nonce
472
- wp_localize_script('contact-form-ajax', 'contactFormAjax', array(
473
- 'ajax_url' => admin_url('admin-ajax.php'),
474
- 'nonce' => wp_create_nonce('contact_form_ajax_nonce'),
475
- ));
476
- }
477
-
478
- /**
479
- * Handle AJAX form submission.
480
- */
481
- public function handle_ajax_submission() {
482
- // 1. CSRF Protection: Verify AJAX nonce
483
- if (!check_ajax_referer('contact_form_ajax_nonce', 'nonce', false)) {
484
- wp_send_json_error(array(
485
- 'message' => __('Security check failed. Please refresh the page and try again.', 'text-domain'),
486
- ));
487
- }
488
-
489
- // 2. Bot Detection: Check honeypot
490
- if (!empty($_POST['contact_website'])) {
491
- // Bot detected - send fake success
492
- wp_send_json_success(array(
493
- 'message' => __('Thank you for your message!', 'text-domain'),
494
- ));
495
- }
496
-
497
- // 3. Rate Limiting
498
- if (!$this->check_rate_limit()) {
499
- wp_send_json_error(array(
500
- 'message' => __('Too many submissions. Please try again later.', 'text-domain'),
501
- ));
502
- }
503
-
504
- // 4. Input Sanitization
505
- $name = isset($_POST['contact_name']) ? sanitize_text_field($_POST['contact_name']) : '';
506
- $email = isset($_POST['contact_email']) ? sanitize_email($_POST['contact_email']) : '';
507
- $subject = isset($_POST['contact_subject']) ? sanitize_text_field($_POST['contact_subject']) : '';
508
- $message = isset($_POST['contact_message']) ? sanitize_textarea_field($_POST['contact_message']) : '';
509
-
510
- // 5. Input Validation
511
- $errors = $this->validate_input($name, $email, $subject, $message);
512
-
513
- if (!empty($errors)) {
514
- wp_send_json_error(array(
515
- 'message' => implode(' ', $errors),
516
- 'errors' => $errors,
517
- ));
518
- }
519
-
520
- // 6. Save to Database
521
- $saved = $this->save_to_database($name, $email, $subject, $message);
522
-
523
- if (!$saved) {
524
- wp_send_json_error(array(
525
- 'message' => __('Failed to save message. Please try again.', 'text-domain'),
526
- ));
527
- }
528
-
529
- // 7. Send Email
530
- $this->send_email_notification($name, $email, $subject, $message);
531
-
532
- // 8. Log Submission
533
- $this->log_submission($email);
534
-
535
- // 9. Success Response
536
- wp_send_json_success(array(
537
- 'message' => __('Thank you for your message! We will get back to you soon.', 'text-domain'),
538
- ));
539
- }
540
-
541
- // ... (same helper methods as Contact_Form_Handler)
542
- }
543
- ```
544
-
545
- ### JavaScript for AJAX Form
546
-
547
- **File**: `public/js/contact-form-ajax.js`
548
-
549
- ```javascript
550
- (function($) {
551
- 'use strict';
552
-
553
- $(document).ready(function() {
554
- $('#contact-form-ajax').on('submit', function(e) {
555
- e.preventDefault();
556
-
557
- var $form = $(this);
558
- var $submitButton = $form.find('button[type="submit"]');
559
- var $messageContainer = $('#form-messages');
560
-
561
- // Disable submit button
562
- $submitButton.prop('disabled', true).text('Sending...');
563
-
564
- // Clear previous messages
565
- $messageContainer.html('').removeClass('success error');
566
-
567
- // Prepare form data
568
- var formData = {
569
- action: 'submit_contact_form',
570
- nonce: contactFormAjax.nonce,
571
- contact_name: $form.find('#contact_name').val(),
572
- contact_email: $form.find('#contact_email').val(),
573
- contact_subject: $form.find('#contact_subject').val(),
574
- contact_message: $form.find('#contact_message').val(),
575
- contact_website: $form.find('[name="contact_website"]').val() // Honeypot
576
- };
577
-
578
- // Send AJAX request
579
- $.ajax({
580
- url: contactFormAjax.ajax_url,
581
- type: 'POST',
582
- data: formData,
583
- dataType: 'json',
584
- success: function(response) {
585
- if (response.success) {
586
- // Success
587
- $messageContainer
588
- .html('<p>' + escapeHtml(response.data.message) + '</p>')
589
- .addClass('success');
590
-
591
- // Reset form
592
- $form[0].reset();
593
- } else {
594
- // Error
595
- $messageContainer
596
- .html('<p>' + escapeHtml(response.data.message) + '</p>')
597
- .addClass('error');
598
- }
599
- },
600
- error: function(xhr, status, error) {
601
- // AJAX error
602
- $messageContainer
603
- .html('<p>An error occurred. Please try again.</p>')
604
- .addClass('error');
605
- },
606
- complete: function() {
607
- // Re-enable submit button
608
- $submitButton.prop('disabled', false).text('Send Message');
609
- }
610
- });
611
- });
612
- });
613
-
614
- /**
615
- * Escape HTML to prevent XSS in JavaScript.
616
- */
617
- function escapeHtml(text) {
618
- var map = {
619
- '&': '&amp;',
620
- '<': '&lt;',
621
- '>': '&gt;',
622
- '"': '&quot;',
623
- "'": '&#039;'
624
- };
625
- return text.replace(/[&<>"']/g, function(m) { return map[m]; });
626
- }
627
-
628
- })(jQuery);
629
- ```
630
-
631
- ### Database Table Creation
632
-
633
- **File**: `includes/class-contact-form-database.php`
634
-
635
- ```php
636
- <?php
637
- /**
638
- * Database table management for contact form.
639
- */
640
- class Contact_Form_Database {
641
-
642
- /**
643
- * Create the contact messages table.
644
- */
645
- public static function create_table() {
646
- global $wpdb;
647
-
648
- $table_name = $wpdb->prefix . 'contact_messages';
649
- $charset_collate = $wpdb->get_charset_collate();
650
-
651
- $sql = "CREATE TABLE $table_name (
652
- id bigint(20) NOT NULL AUTO_INCREMENT,
653
- name varchar(100) NOT NULL,
654
- email varchar(100) NOT NULL,
655
- subject varchar(200) NOT NULL,
656
- message text NOT NULL,
657
- ip_address varchar(45) NOT NULL,
658
- submitted_at datetime NOT NULL,
659
- status varchar(20) NOT NULL DEFAULT 'unread',
660
- PRIMARY KEY (id),
661
- KEY email (email),
662
- KEY status (status),
663
- KEY submitted_at (submitted_at)
664
- ) $charset_collate;";
665
-
666
- require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
667
- dbDelta($sql);
668
- }
669
- }
670
- ```
671
-
672
- ### Admin Page to View Submissions
673
-
674
- **File**: `admin/class-contact-messages-admin.php`
675
-
676
- ```php
677
- <?php
678
- /**
679
- * Admin page to view contact form submissions.
680
- *
681
- * Security features:
682
- * - Capability check (manage_options)
683
- * - Nonce verification for actions
684
- * - Prepared statements for queries
685
- * - Output escaping
686
- */
687
- class Contact_Messages_Admin {
688
-
689
- /**
690
- * Initialize the admin page.
691
- */
692
- public function __construct() {
693
- add_action('admin_menu', array($this, 'add_admin_menu'));
694
- add_action('admin_post_delete_contact_message', array($this, 'delete_message'));
695
- }
696
-
697
- /**
698
- * Add admin menu item.
699
- */
700
- public function add_admin_menu() {
701
- add_submenu_page(
702
- 'tools.php',
703
- __('Contact Messages', 'text-domain'),
704
- __('Contact Messages', 'text-domain'),
705
- 'manage_options',
706
- 'contact-messages',
707
- array($this, 'render_admin_page')
708
- );
709
- }
710
-
711
- /**
712
- * Render the admin page.
713
- */
714
- public function render_admin_page() {
715
- // Check user capability
716
- if (!current_user_can('manage_options')) {
717
- wp_die(__('You do not have sufficient permissions to access this page.', 'text-domain'));
718
- }
719
-
720
- global $wpdb;
721
- $table_name = $wpdb->prefix . 'contact_messages';
722
-
723
- // Get messages with prepared statement
724
- $messages = $wpdb->get_results(
725
- $wpdb->prepare(
726
- "SELECT * FROM $table_name ORDER BY submitted_at DESC LIMIT %d",
727
- 100
728
- )
729
- );
730
-
731
- ?>
732
- <div class="wrap">
733
- <h1><?php _e('Contact Messages', 'text-domain'); ?></h1>
734
-
735
- <?php if (empty($messages)) : ?>
736
- <p><?php _e('No messages found.', 'text-domain'); ?></p>
737
- <?php else : ?>
738
- <table class="wp-list-table widefat fixed striped">
739
- <thead>
740
- <tr>
741
- <th><?php _e('Date', 'text-domain'); ?></th>
742
- <th><?php _e('Name', 'text-domain'); ?></th>
743
- <th><?php _e('Email', 'text-domain'); ?></th>
744
- <th><?php _e('Subject', 'text-domain'); ?></th>
745
- <th><?php _e('Message', 'text-domain'); ?></th>
746
- <th><?php _e('Status', 'text-domain'); ?></th>
747
- <th><?php _e('Actions', 'text-domain'); ?></th>
748
- </tr>
749
- </thead>
750
- <tbody>
751
- <?php foreach ($messages as $message) : ?>
752
- <tr>
753
- <td><?php echo esc_html($message->submitted_at); ?></td>
754
- <td><?php echo esc_html($message->name); ?></td>
755
- <td><a href="mailto:<?php echo esc_attr($message->email); ?>"><?php echo esc_html($message->email); ?></a></td>
756
- <td><?php echo esc_html($message->subject); ?></td>
757
- <td><?php echo esc_html(wp_trim_words($message->message, 10)); ?></td>
758
- <td><?php echo esc_html($message->status); ?></td>
759
- <td>
760
- <a href="<?php echo esc_url(wp_nonce_url(
761
- admin_url('admin-post.php?action=delete_contact_message&id=' . $message->id),
762
- 'delete_message_' . $message->id
763
- )); ?>"
764
- onclick="return confirm('<?php _e('Are you sure you want to delete this message?', 'text-domain'); ?>');">
765
- <?php _e('Delete', 'text-domain'); ?>
766
- </a>
767
- </td>
768
- </tr>
769
- <?php endforeach; ?>
770
- </tbody>
771
- </table>
772
- <?php endif; ?>
773
- </div>
774
- <?php
775
- }
776
-
777
- /**
778
- * Delete a message.
779
- */
780
- public function delete_message() {
781
- // Check user capability
782
- if (!current_user_can('manage_options')) {
783
- wp_die(__('You do not have sufficient permissions to perform this action.', 'text-domain'));
784
- }
785
-
786
- // Get message ID
787
- $message_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
788
-
789
- if (!$message_id) {
790
- wp_die(__('Invalid message ID.', 'text-domain'));
791
- }
792
-
793
- // Verify nonce
794
- if (!isset($_GET['_wpnonce']) ||
795
- !wp_verify_nonce($_GET['_wpnonce'], 'delete_message_' . $message_id)) {
796
- wp_die(__('Security check failed.', 'text-domain'));
797
- }
798
-
799
- // Delete message with prepared statement
800
- global $wpdb;
801
- $table_name = $wpdb->prefix . 'contact_messages';
802
-
803
- $deleted = $wpdb->delete(
804
- $table_name,
805
- array('id' => $message_id),
806
- array('%d')
807
- );
808
-
809
- if ($deleted) {
810
- wp_safe_redirect(admin_url('tools.php?page=contact-messages&deleted=1'));
811
- } else {
812
- wp_die(__('Failed to delete message.', 'text-domain'));
813
- }
814
-
815
- exit;
816
- }
817
- }
818
- ```
819
-
820
- ## Security Checklist
821
-
822
- ### ✅ CSRF Protection (Cross-Site Request Forgery)
823
-
824
- - [x] **Nonce field in form**: `wp_nonce_field('contact_form_submit', 'contact_form_nonce')`
825
- - [x] **Nonce verification in handler**: `wp_verify_nonce($_POST['contact_form_nonce'], 'contact_form_submit')`
826
- - [x] **AJAX nonce**: `check_ajax_referer('contact_form_ajax_nonce', 'nonce', false)`
827
- - [x] **Admin action nonce**: `wp_nonce_url()` for delete links
828
-
829
- ### ✅ XSS Prevention (Cross-Site Scripting)
830
-
831
- - [x] **Output escaping**: `esc_html()`, `esc_attr()`, `esc_url()`, `esc_textarea()`
832
- - [x] **JavaScript escaping**: Custom `escapeHtml()` function
833
- - [x] **Email content**: Plain text email (no HTML)
834
-
835
- ### ✅ SQL Injection Prevention
836
-
837
- - [x] **Prepared statements**: `$wpdb->insert()` with format array
838
- - [x] **Prepared queries**: `$wpdb->prepare()` with placeholders
839
- - [x] **Type casting**: `intval()` for IDs
840
-
841
- ### ✅ Authorization
842
-
843
- - [x] **Capability checks**: `current_user_can('manage_options')`
844
- - [x] **Admin page protection**: Check capability before rendering
845
- - [x] **Admin action protection**: Check capability before deleting
846
-
847
- ### ✅ Input Validation
848
-
849
- - [x] **Required fields**: Check for empty values
850
- - [x] **Length validation**: Min/max character limits
851
- - [x] **Email validation**: `is_email()` function
852
- - [x] **Type validation**: Ensure correct data types
853
-
854
- ### ✅ Input Sanitization
855
-
856
- - [x] **Text fields**: `sanitize_text_field()`
857
- - [x] **Email**: `sanitize_email()`
858
- - [x] **Textarea**: `sanitize_textarea_field()`
859
- - [x] **IP address**: `filter_var($ip, FILTER_VALIDATE_IP)`
860
-
861
- ### ✅ Spam Prevention
862
-
863
- - [x] **Rate limiting**: Max 3 submissions per hour per IP
864
- - [x] **Honeypot field**: Hidden field to catch bots
865
- - [x] **IP logging**: Track submissions by IP address
866
-
867
- ### ✅ Additional Security
868
-
869
- - [x] **Session management**: Proper session handling
870
- - [x] **Transients for messages**: Temporary storage for errors/success
871
- - [x] **Safe redirects**: `wp_safe_redirect()`
872
- - [x] **Error logging**: Log submissions for monitoring
873
-
874
- ## Testing Checklist
875
-
876
- ### Functional Testing
877
-
878
- - [ ] Form displays correctly
879
- - [ ] Form submits successfully with valid data
880
- - [ ] Success message displays after submission
881
- - [ ] Email notification sent to admin
882
- - [ ] Data saved to database correctly
883
- - [ ] AJAX form works without page reload
884
-
885
- ### Security Testing
886
-
887
- - [ ] Form submission fails without nonce
888
- - [ ] Form submission fails with invalid nonce
889
- - [ ] Form submission fails with expired nonce
890
- - [ ] Rate limiting blocks excessive submissions
891
- - [ ] Honeypot catches bot submissions
892
- - [ ] Admin page requires manage_options capability
893
- - [ ] Delete action requires nonce verification
894
- - [ ] SQL injection attempts are blocked
895
- - [ ] XSS attempts are escaped
896
-
897
- ### Validation Testing
898
-
899
- - [ ] Empty fields show error messages
900
- - [ ] Invalid email shows error message
901
- - [ ] Too short inputs show error messages
902
- - [ ] Too long inputs show error messages
903
- - [ ] Multiple errors display correctly
904
-
905
- ### Edge Cases
906
-
907
- - [ ] Form works with special characters in input
908
- - [ ] Form works with Unicode characters
909
- - [ ] Form works with very long messages
910
- - [ ] Form handles database errors gracefully
911
- - [ ] Form handles email sending failures gracefully
912
-
913
- ## Key Takeaways
914
-
915
- 1. **Defense in Depth**: Multiple layers of security (nonces, capability checks, sanitization, escaping)
916
- 2. **Never Trust User Input**: Always sanitize and validate
917
- 3. **Always Escape Output**: Prevent XSS vulnerabilities
918
- 4. **Use Prepared Statements**: Prevent SQL injection
919
- 5. **Rate Limiting**: Prevent spam and abuse
920
- 6. **Honeypot Fields**: Simple bot detection
921
- 7. **Proper Error Handling**: Don't expose sensitive information
922
- 8. **Logging**: Monitor for suspicious activity
923
- 9. **WordPress Functions**: Use built-in security functions
924
- 10. **Testing**: Comprehensive security testing is essential
925
-
1
+ # Secure Form Handler Example
2
+
3
+ This example demonstrates implementing a secure form handler in WordPress with comprehensive security measures including nonce verification, capability checks, input sanitization, and output escaping.
4
+
5
+ ## Scenario
6
+
7
+ Creating a secure contact form handler that demonstrates all WordPress security best practices.
8
+
9
+ ## Complete Implementation
10
+
11
+ ### Form Display
12
+
13
+ **File**: `public/partials/contact-form.php`
14
+
15
+ ```php
16
+ <?php
17
+ /**
18
+ * Contact form template.
19
+ *
20
+ * Security features:
21
+ * - Nonce field for CSRF protection
22
+ * - Escaped output for XSS prevention
23
+ * - Honeypot field for spam prevention
24
+ */
25
+
26
+ // Get current values (for form repopulation after validation errors)
27
+ $name = isset($_POST['contact_name']) ? sanitize_text_field($_POST['contact_name']) : '';
28
+ $email = isset($_POST['contact_email']) ? sanitize_email($_POST['contact_email']) : '';
29
+ $subject = isset($_POST['contact_subject']) ? sanitize_text_field($_POST['contact_subject']) : '';
30
+ $message = isset($_POST['contact_message']) ? sanitize_textarea_field($_POST['contact_message']) : '';
31
+
32
+ // Get error messages
33
+ $errors = get_transient('contact_form_errors_' . session_id());
34
+ $success = get_transient('contact_form_success_' . session_id());
35
+
36
+ // Clear transients
37
+ delete_transient('contact_form_errors_' . session_id());
38
+ delete_transient('contact_form_success_' . session_id());
39
+ ?>
40
+
41
+ <div class="contact-form-wrapper">
42
+ <?php if ($success) : ?>
43
+ <div class="contact-form-success">
44
+ <p><?php echo esc_html($success); ?></p>
45
+ </div>
46
+ <?php endif; ?>
47
+
48
+ <?php if ($errors && is_array($errors)) : ?>
49
+ <div class="contact-form-errors">
50
+ <ul>
51
+ <?php foreach ($errors as $error) : ?>
52
+ <li><?php echo esc_html($error); ?></li>
53
+ <?php endforeach; ?>
54
+ </ul>
55
+ </div>
56
+ <?php endif; ?>
57
+
58
+ <form method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>" class="contact-form">
59
+ <!-- Nonce field for CSRF protection -->
60
+ <?php wp_nonce_field('contact_form_submit', 'contact_form_nonce'); ?>
61
+
62
+ <!-- Action for WordPress admin-post.php handler -->
63
+ <input type="hidden" name="action" value="submit_contact_form" />
64
+
65
+ <!-- Honeypot field (hidden from users, catches bots) -->
66
+ <input type="text" name="contact_website" value="" style="display:none;" tabindex="-1" autocomplete="off" />
67
+
68
+ <div class="form-field">
69
+ <label for="contact_name">
70
+ <?php _e('Name', 'text-domain'); ?> <span class="required">*</span>
71
+ </label>
72
+ <input type="text"
73
+ id="contact_name"
74
+ name="contact_name"
75
+ value="<?php echo esc_attr($name); ?>"
76
+ required
77
+ maxlength="100" />
78
+ </div>
79
+
80
+ <div class="form-field">
81
+ <label for="contact_email">
82
+ <?php _e('Email', 'text-domain'); ?> <span class="required">*</span>
83
+ </label>
84
+ <input type="email"
85
+ id="contact_email"
86
+ name="contact_email"
87
+ value="<?php echo esc_attr($email); ?>"
88
+ required
89
+ maxlength="100" />
90
+ </div>
91
+
92
+ <div class="form-field">
93
+ <label for="contact_subject">
94
+ <?php _e('Subject', 'text-domain'); ?> <span class="required">*</span>
95
+ </label>
96
+ <input type="text"
97
+ id="contact_subject"
98
+ name="contact_subject"
99
+ value="<?php echo esc_attr($subject); ?>"
100
+ required
101
+ maxlength="200" />
102
+ </div>
103
+
104
+ <div class="form-field">
105
+ <label for="contact_message">
106
+ <?php _e('Message', 'text-domain'); ?> <span class="required">*</span>
107
+ </label>
108
+ <textarea id="contact_message"
109
+ name="contact_message"
110
+ required
111
+ rows="6"
112
+ maxlength="2000"><?php echo esc_textarea($message); ?></textarea>
113
+ </div>
114
+
115
+ <div class="form-field">
116
+ <button type="submit" class="submit-button">
117
+ <?php _e('Send Message', 'text-domain'); ?>
118
+ </button>
119
+ </div>
120
+ </form>
121
+ </div>
122
+ ```
123
+
124
+ ### Form Handler Class
125
+
126
+ **File**: `includes/class-contact-form-handler.php`
127
+
128
+ ```php
129
+ <?php
130
+ /**
131
+ * Secure contact form handler.
132
+ *
133
+ * Security features:
134
+ * - Nonce verification (CSRF protection)
135
+ * - Capability checks (authorization)
136
+ * - Input sanitization (data cleaning)
137
+ * - Input validation (data verification)
138
+ * - Rate limiting (spam prevention)
139
+ * - Honeypot field (bot detection)
140
+ * - SQL injection prevention (prepared statements)
141
+ * - XSS prevention (output escaping)
142
+ */
143
+ class Contact_Form_Handler {
144
+
145
+ /**
146
+ * Rate limit: max submissions per IP per hour.
147
+ */
148
+ const RATE_LIMIT = 3;
149
+
150
+ /**
151
+ * Initialize the form handler.
152
+ */
153
+ public function __construct() {
154
+ // Register form submission handler
155
+ add_action('admin_post_submit_contact_form', array($this, 'handle_submission'));
156
+ add_action('admin_post_nopriv_submit_contact_form', array($this, 'handle_submission'));
157
+ }
158
+
159
+ /**
160
+ * Handle form submission.
161
+ */
162
+ public function handle_submission() {
163
+ // Start session for error/success messages
164
+ if (!session_id()) {
165
+ session_start();
166
+ }
167
+
168
+ $errors = array();
169
+
170
+ // 1. CSRF Protection: Verify nonce
171
+ if (!isset($_POST['contact_form_nonce']) ||
172
+ !wp_verify_nonce($_POST['contact_form_nonce'], 'contact_form_submit')) {
173
+ $errors[] = __('Security check failed. Please try again.', 'text-domain');
174
+ $this->redirect_with_errors($errors);
175
+ return;
176
+ }
177
+
178
+ // 2. Bot Detection: Check honeypot field
179
+ if (!empty($_POST['contact_website'])) {
180
+ // Bot detected - silently fail
181
+ $this->redirect_with_success(__('Thank you for your message!', 'text-domain'));
182
+ return;
183
+ }
184
+
185
+ // 3. Rate Limiting: Check submission rate
186
+ if (!$this->check_rate_limit()) {
187
+ $errors[] = __('Too many submissions. Please try again later.', 'text-domain');
188
+ $this->redirect_with_errors($errors);
189
+ return;
190
+ }
191
+
192
+ // 4. Input Sanitization: Clean all inputs
193
+ $name = isset($_POST['contact_name']) ? sanitize_text_field($_POST['contact_name']) : '';
194
+ $email = isset($_POST['contact_email']) ? sanitize_email($_POST['contact_email']) : '';
195
+ $subject = isset($_POST['contact_subject']) ? sanitize_text_field($_POST['contact_subject']) : '';
196
+ $message = isset($_POST['contact_message']) ? sanitize_textarea_field($_POST['contact_message']) : '';
197
+
198
+ // 5. Input Validation: Verify data
199
+ $errors = $this->validate_input($name, $email, $subject, $message);
200
+
201
+ if (!empty($errors)) {
202
+ $this->redirect_with_errors($errors);
203
+ return;
204
+ }
205
+
206
+ // 6. Save to Database: Use prepared statements
207
+ $saved = $this->save_to_database($name, $email, $subject, $message);
208
+
209
+ if (!$saved) {
210
+ $errors[] = __('Failed to save message. Please try again.', 'text-domain');
211
+ $this->redirect_with_errors($errors);
212
+ return;
213
+ }
214
+
215
+ // 7. Send Email Notification
216
+ $this->send_email_notification($name, $email, $subject, $message);
217
+
218
+ // 8. Log Submission
219
+ $this->log_submission($email);
220
+
221
+ // 9. Success
222
+ $this->redirect_with_success(__('Thank you for your message! We will get back to you soon.', 'text-domain'));
223
+ }
224
+
225
+ /**
226
+ * Validate form input.
227
+ *
228
+ * @return array Array of error messages (empty if valid)
229
+ */
230
+ private function validate_input($name, $email, $subject, $message) {
231
+ $errors = array();
232
+
233
+ // Validate name
234
+ if (empty($name)) {
235
+ $errors[] = __('Name is required.', 'text-domain');
236
+ } elseif (strlen($name) < 2) {
237
+ $errors[] = __('Name must be at least 2 characters.', 'text-domain');
238
+ } elseif (strlen($name) > 100) {
239
+ $errors[] = __('Name must be less than 100 characters.', 'text-domain');
240
+ }
241
+
242
+ // Validate email
243
+ if (empty($email)) {
244
+ $errors[] = __('Email is required.', 'text-domain');
245
+ } elseif (!is_email($email)) {
246
+ $errors[] = __('Please enter a valid email address.', 'text-domain');
247
+ }
248
+
249
+ // Validate subject
250
+ if (empty($subject)) {
251
+ $errors[] = __('Subject is required.', 'text-domain');
252
+ } elseif (strlen($subject) < 3) {
253
+ $errors[] = __('Subject must be at least 3 characters.', 'text-domain');
254
+ } elseif (strlen($subject) > 200) {
255
+ $errors[] = __('Subject must be less than 200 characters.', 'text-domain');
256
+ }
257
+
258
+ // Validate message
259
+ if (empty($message)) {
260
+ $errors[] = __('Message is required.', 'text-domain');
261
+ } elseif (strlen($message) < 10) {
262
+ $errors[] = __('Message must be at least 10 characters.', 'text-domain');
263
+ } elseif (strlen($message) > 2000) {
264
+ $errors[] = __('Message must be less than 2000 characters.', 'text-domain');
265
+ }
266
+
267
+ return $errors;
268
+ }
269
+
270
+ /**
271
+ * Check rate limit for submissions.
272
+ *
273
+ * @return bool True if within rate limit, false otherwise
274
+ */
275
+ private function check_rate_limit() {
276
+ $ip_address = $this->get_client_ip();
277
+ $transient_key = 'contact_form_rate_' . md5($ip_address);
278
+
279
+ $submissions = get_transient($transient_key);
280
+
281
+ if ($submissions === false) {
282
+ // First submission in this hour
283
+ set_transient($transient_key, 1, HOUR_IN_SECONDS);
284
+ return true;
285
+ }
286
+
287
+ if ($submissions >= self::RATE_LIMIT) {
288
+ // Rate limit exceeded
289
+ return false;
290
+ }
291
+
292
+ // Increment submission count
293
+ set_transient($transient_key, $submissions + 1, HOUR_IN_SECONDS);
294
+ return true;
295
+ }
296
+
297
+ /**
298
+ * Get client IP address.
299
+ *
300
+ * @return string IP address
301
+ */
302
+ private function get_client_ip() {
303
+ $ip_address = '';
304
+
305
+ if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
306
+ $ip_address = $_SERVER['HTTP_CLIENT_IP'];
307
+ } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
308
+ $ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
309
+ } else {
310
+ $ip_address = $_SERVER['REMOTE_ADDR'];
311
+ }
312
+
313
+ // Sanitize IP address
314
+ return filter_var($ip_address, FILTER_VALIDATE_IP) ? $ip_address : '0.0.0.0';
315
+ }
316
+
317
+ /**
318
+ * Save submission to database using prepared statements.
319
+ *
320
+ * @return bool True on success, false on failure
321
+ */
322
+ private function save_to_database($name, $email, $subject, $message) {
323
+ global $wpdb;
324
+
325
+ $table_name = $wpdb->prefix . 'contact_messages';
326
+
327
+ // Use prepared statement to prevent SQL injection
328
+ $result = $wpdb->insert(
329
+ $table_name,
330
+ array(
331
+ 'name' => $name,
332
+ 'email' => $email,
333
+ 'subject' => $subject,
334
+ 'message' => $message,
335
+ 'ip_address' => $this->get_client_ip(),
336
+ 'submitted_at' => current_time('mysql'),
337
+ 'status' => 'unread',
338
+ ),
339
+ array(
340
+ '%s', // name
341
+ '%s', // email
342
+ '%s', // subject
343
+ '%s', // message
344
+ '%s', // ip_address
345
+ '%s', // submitted_at
346
+ '%s', // status
347
+ )
348
+ );
349
+
350
+ return $result !== false;
351
+ }
352
+
353
+ /**
354
+ * Send email notification to admin.
355
+ */
356
+ private function send_email_notification($name, $email, $subject, $message) {
357
+ $admin_email = get_option('admin_email');
358
+
359
+ // Prepare email
360
+ $to = $admin_email;
361
+ $email_subject = sprintf(
362
+ __('[%s] New Contact Form Submission: %s', 'text-domain'),
363
+ get_bloginfo('name'),
364
+ $subject
365
+ );
366
+
367
+ // Build email body with escaped content
368
+ $email_body = sprintf(
369
+ __("New contact form submission:\n\nName: %s\nEmail: %s\nSubject: %s\n\nMessage:\n%s", 'text-domain'),
370
+ $name,
371
+ $email,
372
+ $subject,
373
+ $message
374
+ );
375
+
376
+ // Set headers
377
+ $headers = array(
378
+ 'Content-Type: text/plain; charset=UTF-8',
379
+ 'Reply-To: ' . $email,
380
+ );
381
+
382
+ // Send email
383
+ wp_mail($to, $email_subject, $email_body, $headers);
384
+ }
385
+
386
+ /**
387
+ * Log submission for security monitoring.
388
+ */
389
+ private function log_submission($email) {
390
+ // Log to WordPress debug log if enabled
391
+ if (defined('WP_DEBUG') && WP_DEBUG && defined('WP_DEBUG_LOG') && WP_DEBUG_LOG) {
392
+ error_log(sprintf(
393
+ 'Contact form submission from %s (IP: %s)',
394
+ $email,
395
+ $this->get_client_ip()
396
+ ));
397
+ }
398
+ }
399
+
400
+ /**
401
+ * Redirect back to form with errors.
402
+ */
403
+ private function redirect_with_errors($errors) {
404
+ set_transient('contact_form_errors_' . session_id(), $errors, 60);
405
+
406
+ $redirect_url = wp_get_referer();
407
+ if (!$redirect_url) {
408
+ $redirect_url = home_url();
409
+ }
410
+
411
+ wp_safe_redirect($redirect_url);
412
+ exit;
413
+ }
414
+
415
+ /**
416
+ * Redirect back to form with success message.
417
+ */
418
+ private function redirect_with_success($message) {
419
+ set_transient('contact_form_success_' . session_id(), $message, 60);
420
+
421
+ $redirect_url = wp_get_referer();
422
+ if (!$redirect_url) {
423
+ $redirect_url = home_url();
424
+ }
425
+
426
+ wp_safe_redirect($redirect_url);
427
+ exit;
428
+ }
429
+ }
430
+ ```
431
+
432
+ ### AJAX Form Handler (Alternative)
433
+
434
+ **File**: `includes/class-contact-form-ajax-handler.php`
435
+
436
+ ```php
437
+ <?php
438
+ /**
439
+ * Secure AJAX contact form handler.
440
+ *
441
+ * Additional security for AJAX:
442
+ * - check_ajax_referer() for nonce verification
443
+ * - wp_send_json_error() and wp_send_json_success() for responses
444
+ */
445
+ class Contact_Form_Ajax_Handler {
446
+
447
+ /**
448
+ * Initialize the AJAX handler.
449
+ */
450
+ public function __construct() {
451
+ // Register AJAX handlers
452
+ add_action('wp_ajax_submit_contact_form', array($this, 'handle_ajax_submission'));
453
+ add_action('wp_ajax_nopriv_submit_contact_form', array($this, 'handle_ajax_submission'));
454
+
455
+ // Enqueue scripts with localized nonce
456
+ add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
457
+ }
458
+
459
+ /**
460
+ * Enqueue scripts with localized data.
461
+ */
462
+ public function enqueue_scripts() {
463
+ wp_enqueue_script(
464
+ 'contact-form-ajax',
465
+ plugin_dir_url(__FILE__) . '../public/js/contact-form-ajax.js',
466
+ array('jquery'),
467
+ '1.0.0',
468
+ true
469
+ );
470
+
471
+ // Localize script with AJAX URL and nonce
472
+ wp_localize_script('contact-form-ajax', 'contactFormAjax', array(
473
+ 'ajax_url' => admin_url('admin-ajax.php'),
474
+ 'nonce' => wp_create_nonce('contact_form_ajax_nonce'),
475
+ ));
476
+ }
477
+
478
+ /**
479
+ * Handle AJAX form submission.
480
+ */
481
+ public function handle_ajax_submission() {
482
+ // 1. CSRF Protection: Verify AJAX nonce
483
+ if (!check_ajax_referer('contact_form_ajax_nonce', 'nonce', false)) {
484
+ wp_send_json_error(array(
485
+ 'message' => __('Security check failed. Please refresh the page and try again.', 'text-domain'),
486
+ ));
487
+ }
488
+
489
+ // 2. Bot Detection: Check honeypot
490
+ if (!empty($_POST['contact_website'])) {
491
+ // Bot detected - send fake success
492
+ wp_send_json_success(array(
493
+ 'message' => __('Thank you for your message!', 'text-domain'),
494
+ ));
495
+ }
496
+
497
+ // 3. Rate Limiting
498
+ if (!$this->check_rate_limit()) {
499
+ wp_send_json_error(array(
500
+ 'message' => __('Too many submissions. Please try again later.', 'text-domain'),
501
+ ));
502
+ }
503
+
504
+ // 4. Input Sanitization
505
+ $name = isset($_POST['contact_name']) ? sanitize_text_field($_POST['contact_name']) : '';
506
+ $email = isset($_POST['contact_email']) ? sanitize_email($_POST['contact_email']) : '';
507
+ $subject = isset($_POST['contact_subject']) ? sanitize_text_field($_POST['contact_subject']) : '';
508
+ $message = isset($_POST['contact_message']) ? sanitize_textarea_field($_POST['contact_message']) : '';
509
+
510
+ // 5. Input Validation
511
+ $errors = $this->validate_input($name, $email, $subject, $message);
512
+
513
+ if (!empty($errors)) {
514
+ wp_send_json_error(array(
515
+ 'message' => implode(' ', $errors),
516
+ 'errors' => $errors,
517
+ ));
518
+ }
519
+
520
+ // 6. Save to Database
521
+ $saved = $this->save_to_database($name, $email, $subject, $message);
522
+
523
+ if (!$saved) {
524
+ wp_send_json_error(array(
525
+ 'message' => __('Failed to save message. Please try again.', 'text-domain'),
526
+ ));
527
+ }
528
+
529
+ // 7. Send Email
530
+ $this->send_email_notification($name, $email, $subject, $message);
531
+
532
+ // 8. Log Submission
533
+ $this->log_submission($email);
534
+
535
+ // 9. Success Response
536
+ wp_send_json_success(array(
537
+ 'message' => __('Thank you for your message! We will get back to you soon.', 'text-domain'),
538
+ ));
539
+ }
540
+
541
+ // ... (same helper methods as Contact_Form_Handler)
542
+ }
543
+ ```
544
+
545
+ ### JavaScript for AJAX Form
546
+
547
+ **File**: `public/js/contact-form-ajax.js`
548
+
549
+ ```javascript
550
+ (function($) {
551
+ 'use strict';
552
+
553
+ $(document).ready(function() {
554
+ $('#contact-form-ajax').on('submit', function(e) {
555
+ e.preventDefault();
556
+
557
+ var $form = $(this);
558
+ var $submitButton = $form.find('button[type="submit"]');
559
+ var $messageContainer = $('#form-messages');
560
+
561
+ // Disable submit button
562
+ $submitButton.prop('disabled', true).text('Sending...');
563
+
564
+ // Clear previous messages
565
+ $messageContainer.html('').removeClass('success error');
566
+
567
+ // Prepare form data
568
+ var formData = {
569
+ action: 'submit_contact_form',
570
+ nonce: contactFormAjax.nonce,
571
+ contact_name: $form.find('#contact_name').val(),
572
+ contact_email: $form.find('#contact_email').val(),
573
+ contact_subject: $form.find('#contact_subject').val(),
574
+ contact_message: $form.find('#contact_message').val(),
575
+ contact_website: $form.find('[name="contact_website"]').val() // Honeypot
576
+ };
577
+
578
+ // Send AJAX request
579
+ $.ajax({
580
+ url: contactFormAjax.ajax_url,
581
+ type: 'POST',
582
+ data: formData,
583
+ dataType: 'json',
584
+ success: function(response) {
585
+ if (response.success) {
586
+ // Success
587
+ $messageContainer
588
+ .html('<p>' + escapeHtml(response.data.message) + '</p>')
589
+ .addClass('success');
590
+
591
+ // Reset form
592
+ $form[0].reset();
593
+ } else {
594
+ // Error
595
+ $messageContainer
596
+ .html('<p>' + escapeHtml(response.data.message) + '</p>')
597
+ .addClass('error');
598
+ }
599
+ },
600
+ error: function(xhr, status, error) {
601
+ // AJAX error
602
+ $messageContainer
603
+ .html('<p>An error occurred. Please try again.</p>')
604
+ .addClass('error');
605
+ },
606
+ complete: function() {
607
+ // Re-enable submit button
608
+ $submitButton.prop('disabled', false).text('Send Message');
609
+ }
610
+ });
611
+ });
612
+ });
613
+
614
+ /**
615
+ * Escape HTML to prevent XSS in JavaScript.
616
+ */
617
+ function escapeHtml(text) {
618
+ var map = {
619
+ '&': '&amp;',
620
+ '<': '&lt;',
621
+ '>': '&gt;',
622
+ '"': '&quot;',
623
+ "'": '&#039;'
624
+ };
625
+ return text.replace(/[&<>"']/g, function(m) { return map[m]; });
626
+ }
627
+
628
+ })(jQuery);
629
+ ```
630
+
631
+ ### Database Table Creation
632
+
633
+ **File**: `includes/class-contact-form-database.php`
634
+
635
+ ```php
636
+ <?php
637
+ /**
638
+ * Database table management for contact form.
639
+ */
640
+ class Contact_Form_Database {
641
+
642
+ /**
643
+ * Create the contact messages table.
644
+ */
645
+ public static function create_table() {
646
+ global $wpdb;
647
+
648
+ $table_name = $wpdb->prefix . 'contact_messages';
649
+ $charset_collate = $wpdb->get_charset_collate();
650
+
651
+ $sql = "CREATE TABLE $table_name (
652
+ id bigint(20) NOT NULL AUTO_INCREMENT,
653
+ name varchar(100) NOT NULL,
654
+ email varchar(100) NOT NULL,
655
+ subject varchar(200) NOT NULL,
656
+ message text NOT NULL,
657
+ ip_address varchar(45) NOT NULL,
658
+ submitted_at datetime NOT NULL,
659
+ status varchar(20) NOT NULL DEFAULT 'unread',
660
+ PRIMARY KEY (id),
661
+ KEY email (email),
662
+ KEY status (status),
663
+ KEY submitted_at (submitted_at)
664
+ ) $charset_collate;";
665
+
666
+ require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
667
+ dbDelta($sql);
668
+ }
669
+ }
670
+ ```
671
+
672
+ ### Admin Page to View Submissions
673
+
674
+ **File**: `admin/class-contact-messages-admin.php`
675
+
676
+ ```php
677
+ <?php
678
+ /**
679
+ * Admin page to view contact form submissions.
680
+ *
681
+ * Security features:
682
+ * - Capability check (manage_options)
683
+ * - Nonce verification for actions
684
+ * - Prepared statements for queries
685
+ * - Output escaping
686
+ */
687
+ class Contact_Messages_Admin {
688
+
689
+ /**
690
+ * Initialize the admin page.
691
+ */
692
+ public function __construct() {
693
+ add_action('admin_menu', array($this, 'add_admin_menu'));
694
+ add_action('admin_post_delete_contact_message', array($this, 'delete_message'));
695
+ }
696
+
697
+ /**
698
+ * Add admin menu item.
699
+ */
700
+ public function add_admin_menu() {
701
+ add_submenu_page(
702
+ 'tools.php',
703
+ __('Contact Messages', 'text-domain'),
704
+ __('Contact Messages', 'text-domain'),
705
+ 'manage_options',
706
+ 'contact-messages',
707
+ array($this, 'render_admin_page')
708
+ );
709
+ }
710
+
711
+ /**
712
+ * Render the admin page.
713
+ */
714
+ public function render_admin_page() {
715
+ // Check user capability
716
+ if (!current_user_can('manage_options')) {
717
+ wp_die(__('You do not have sufficient permissions to access this page.', 'text-domain'));
718
+ }
719
+
720
+ global $wpdb;
721
+ $table_name = $wpdb->prefix . 'contact_messages';
722
+
723
+ // Get messages with prepared statement
724
+ $messages = $wpdb->get_results(
725
+ $wpdb->prepare(
726
+ "SELECT * FROM $table_name ORDER BY submitted_at DESC LIMIT %d",
727
+ 100
728
+ )
729
+ );
730
+
731
+ ?>
732
+ <div class="wrap">
733
+ <h1><?php _e('Contact Messages', 'text-domain'); ?></h1>
734
+
735
+ <?php if (empty($messages)) : ?>
736
+ <p><?php _e('No messages found.', 'text-domain'); ?></p>
737
+ <?php else : ?>
738
+ <table class="wp-list-table widefat fixed striped">
739
+ <thead>
740
+ <tr>
741
+ <th><?php _e('Date', 'text-domain'); ?></th>
742
+ <th><?php _e('Name', 'text-domain'); ?></th>
743
+ <th><?php _e('Email', 'text-domain'); ?></th>
744
+ <th><?php _e('Subject', 'text-domain'); ?></th>
745
+ <th><?php _e('Message', 'text-domain'); ?></th>
746
+ <th><?php _e('Status', 'text-domain'); ?></th>
747
+ <th><?php _e('Actions', 'text-domain'); ?></th>
748
+ </tr>
749
+ </thead>
750
+ <tbody>
751
+ <?php foreach ($messages as $message) : ?>
752
+ <tr>
753
+ <td><?php echo esc_html($message->submitted_at); ?></td>
754
+ <td><?php echo esc_html($message->name); ?></td>
755
+ <td><a href="mailto:<?php echo esc_attr($message->email); ?>"><?php echo esc_html($message->email); ?></a></td>
756
+ <td><?php echo esc_html($message->subject); ?></td>
757
+ <td><?php echo esc_html(wp_trim_words($message->message, 10)); ?></td>
758
+ <td><?php echo esc_html($message->status); ?></td>
759
+ <td>
760
+ <a href="<?php echo esc_url(wp_nonce_url(
761
+ admin_url('admin-post.php?action=delete_contact_message&id=' . $message->id),
762
+ 'delete_message_' . $message->id
763
+ )); ?>"
764
+ onclick="return confirm('<?php _e('Are you sure you want to delete this message?', 'text-domain'); ?>');">
765
+ <?php _e('Delete', 'text-domain'); ?>
766
+ </a>
767
+ </td>
768
+ </tr>
769
+ <?php endforeach; ?>
770
+ </tbody>
771
+ </table>
772
+ <?php endif; ?>
773
+ </div>
774
+ <?php
775
+ }
776
+
777
+ /**
778
+ * Delete a message.
779
+ */
780
+ public function delete_message() {
781
+ // Check user capability
782
+ if (!current_user_can('manage_options')) {
783
+ wp_die(__('You do not have sufficient permissions to perform this action.', 'text-domain'));
784
+ }
785
+
786
+ // Get message ID
787
+ $message_id = isset($_GET['id']) ? intval($_GET['id']) : 0;
788
+
789
+ if (!$message_id) {
790
+ wp_die(__('Invalid message ID.', 'text-domain'));
791
+ }
792
+
793
+ // Verify nonce
794
+ if (!isset($_GET['_wpnonce']) ||
795
+ !wp_verify_nonce($_GET['_wpnonce'], 'delete_message_' . $message_id)) {
796
+ wp_die(__('Security check failed.', 'text-domain'));
797
+ }
798
+
799
+ // Delete message with prepared statement
800
+ global $wpdb;
801
+ $table_name = $wpdb->prefix . 'contact_messages';
802
+
803
+ $deleted = $wpdb->delete(
804
+ $table_name,
805
+ array('id' => $message_id),
806
+ array('%d')
807
+ );
808
+
809
+ if ($deleted) {
810
+ wp_safe_redirect(admin_url('tools.php?page=contact-messages&deleted=1'));
811
+ } else {
812
+ wp_die(__('Failed to delete message.', 'text-domain'));
813
+ }
814
+
815
+ exit;
816
+ }
817
+ }
818
+ ```
819
+
820
+ ## Security Checklist
821
+
822
+ ### ✅ CSRF Protection (Cross-Site Request Forgery)
823
+
824
+ - [x] **Nonce field in form**: `wp_nonce_field('contact_form_submit', 'contact_form_nonce')`
825
+ - [x] **Nonce verification in handler**: `wp_verify_nonce($_POST['contact_form_nonce'], 'contact_form_submit')`
826
+ - [x] **AJAX nonce**: `check_ajax_referer('contact_form_ajax_nonce', 'nonce', false)`
827
+ - [x] **Admin action nonce**: `wp_nonce_url()` for delete links
828
+
829
+ ### ✅ XSS Prevention (Cross-Site Scripting)
830
+
831
+ - [x] **Output escaping**: `esc_html()`, `esc_attr()`, `esc_url()`, `esc_textarea()`
832
+ - [x] **JavaScript escaping**: Custom `escapeHtml()` function
833
+ - [x] **Email content**: Plain text email (no HTML)
834
+
835
+ ### ✅ SQL Injection Prevention
836
+
837
+ - [x] **Prepared statements**: `$wpdb->insert()` with format array
838
+ - [x] **Prepared queries**: `$wpdb->prepare()` with placeholders
839
+ - [x] **Type casting**: `intval()` for IDs
840
+
841
+ ### ✅ Authorization
842
+
843
+ - [x] **Capability checks**: `current_user_can('manage_options')`
844
+ - [x] **Admin page protection**: Check capability before rendering
845
+ - [x] **Admin action protection**: Check capability before deleting
846
+
847
+ ### ✅ Input Validation
848
+
849
+ - [x] **Required fields**: Check for empty values
850
+ - [x] **Length validation**: Min/max character limits
851
+ - [x] **Email validation**: `is_email()` function
852
+ - [x] **Type validation**: Ensure correct data types
853
+
854
+ ### ✅ Input Sanitization
855
+
856
+ - [x] **Text fields**: `sanitize_text_field()`
857
+ - [x] **Email**: `sanitize_email()`
858
+ - [x] **Textarea**: `sanitize_textarea_field()`
859
+ - [x] **IP address**: `filter_var($ip, FILTER_VALIDATE_IP)`
860
+
861
+ ### ✅ Spam Prevention
862
+
863
+ - [x] **Rate limiting**: Max 3 submissions per hour per IP
864
+ - [x] **Honeypot field**: Hidden field to catch bots
865
+ - [x] **IP logging**: Track submissions by IP address
866
+
867
+ ### ✅ Additional Security
868
+
869
+ - [x] **Session management**: Proper session handling
870
+ - [x] **Transients for messages**: Temporary storage for errors/success
871
+ - [x] **Safe redirects**: `wp_safe_redirect()`
872
+ - [x] **Error logging**: Log submissions for monitoring
873
+
874
+ ## Testing Checklist
875
+
876
+ ### Functional Testing
877
+
878
+ - [ ] Form displays correctly
879
+ - [ ] Form submits successfully with valid data
880
+ - [ ] Success message displays after submission
881
+ - [ ] Email notification sent to admin
882
+ - [ ] Data saved to database correctly
883
+ - [ ] AJAX form works without page reload
884
+
885
+ ### Security Testing
886
+
887
+ - [ ] Form submission fails without nonce
888
+ - [ ] Form submission fails with invalid nonce
889
+ - [ ] Form submission fails with expired nonce
890
+ - [ ] Rate limiting blocks excessive submissions
891
+ - [ ] Honeypot catches bot submissions
892
+ - [ ] Admin page requires manage_options capability
893
+ - [ ] Delete action requires nonce verification
894
+ - [ ] SQL injection attempts are blocked
895
+ - [ ] XSS attempts are escaped
896
+
897
+ ### Validation Testing
898
+
899
+ - [ ] Empty fields show error messages
900
+ - [ ] Invalid email shows error message
901
+ - [ ] Too short inputs show error messages
902
+ - [ ] Too long inputs show error messages
903
+ - [ ] Multiple errors display correctly
904
+
905
+ ### Edge Cases
906
+
907
+ - [ ] Form works with special characters in input
908
+ - [ ] Form works with Unicode characters
909
+ - [ ] Form works with very long messages
910
+ - [ ] Form handles database errors gracefully
911
+ - [ ] Form handles email sending failures gracefully
912
+
913
+ ## Key Takeaways
914
+
915
+ 1. **Defense in Depth**: Multiple layers of security (nonces, capability checks, sanitization, escaping)
916
+ 2. **Never Trust User Input**: Always sanitize and validate
917
+ 3. **Always Escape Output**: Prevent XSS vulnerabilities
918
+ 4. **Use Prepared Statements**: Prevent SQL injection
919
+ 5. **Rate Limiting**: Prevent spam and abuse
920
+ 6. **Honeypot Fields**: Simple bot detection
921
+ 7. **Proper Error Handling**: Don't expose sensitive information
922
+ 8. **Logging**: Monitor for suspicious activity
923
+ 9. **WordPress Functions**: Use built-in security functions
924
+ 10. **Testing**: Comprehensive security testing is essential
925
+