odac 0.9.0 → 1.0.0
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.
- package/.github/workflows/auto-pr-description.yml +0 -2
- package/.github/workflows/codeql.yml +46 -0
- package/.github/workflows/release.yml +13 -6
- package/.github/workflows/test-coverage.yml +10 -9
- package/.releaserc.js +9 -6
- package/CHANGELOG.md +62 -150
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +8 -8
- package/LICENSE +21 -661
- package/README.md +12 -12
- package/SECURITY.md +4 -4
- package/bin/odac.js +101 -0
- package/{framework/web/candy.js → client/odac.js} +310 -44
- package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
- package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
- package/docs/backend/01-overview/03-development-server.md +11 -11
- package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
- package/docs/backend/03-config/00-configuration-overview.md +6 -6
- package/docs/backend/03-config/01-database-connection.md +1 -1
- package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
- package/docs/backend/03-config/04-environment-variables.md +20 -20
- package/docs/backend/03-config/05-early-hints.md +4 -4
- package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
- package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
- package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
- package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
- package/docs/backend/04-routing/05-advanced-routing.md +3 -3
- package/docs/backend/04-routing/06-error-pages.md +17 -17
- package/docs/backend/04-routing/07-cron-jobs.md +13 -13
- package/docs/backend/04-routing/08-middleware.md +214 -0
- package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
- package/docs/backend/04-routing/09-websocket-examples.md +381 -0
- package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
- package/docs/backend/04-routing/09-websocket.md +298 -0
- package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
- package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
- package/docs/backend/05-controllers/03-controller-classes.md +19 -19
- package/docs/backend/05-forms/01-custom-forms.md +114 -114
- package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
- package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
- package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
- package/docs/backend/07-views/01-the-view-directory.md +1 -1
- package/docs/backend/07-views/02-rendering-a-view.md +22 -22
- package/docs/backend/07-views/03-template-syntax.md +52 -52
- package/docs/backend/07-views/03-variables.md +84 -84
- package/docs/backend/07-views/04-request-data.md +57 -57
- package/docs/backend/07-views/05-conditionals.md +78 -78
- package/docs/backend/07-views/06-loops.md +114 -114
- package/docs/backend/07-views/07-translations.md +66 -66
- package/docs/backend/07-views/08-backend-javascript.md +103 -103
- package/docs/backend/07-views/09-comments.md +71 -71
- package/docs/backend/08-database/01-database-connection.md +8 -8
- package/docs/backend/08-database/02-using-mysql.md +49 -49
- package/docs/backend/09-validation/01-the-validator-service.md +38 -38
- package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
- package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
- package/docs/backend/10-authentication/03-register.md +12 -12
- package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
- package/docs/backend/10-authentication/05-session-management.md +10 -10
- package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
- package/docs/backend/11-mail/01-the-mail-service.md +5 -5
- package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
- package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
- package/docs/frontend/01-overview/01-introduction.md +30 -30
- package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
- package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
- package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
- package/docs/frontend/03-forms/01-form-handling.md +32 -32
- package/docs/frontend/04-api-requests/01-get-post.md +33 -33
- package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
- package/docs/frontend/06-websocket/00-overview.md +76 -0
- package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
- package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
- package/docs/index.json +49 -11
- package/eslint.config.mjs +6 -6
- package/{framework/index.js → index.js} +1 -1
- package/package.json +14 -39
- package/{framework/src → src}/Auth.js +59 -59
- package/{framework/src → src}/Config.js +3 -3
- package/{framework/src → src}/Lang.js +7 -7
- package/{framework/src → src}/Mail.js +5 -5
- package/{framework/src → src}/Mysql.js +42 -42
- package/src/Odac.js +112 -0
- package/{framework/src → src}/Request.js +38 -36
- package/{framework/src → src}/Route/Internal.js +116 -116
- package/src/Route/Middleware.js +75 -0
- package/src/Route.js +621 -0
- package/src/Server.js +22 -0
- package/{framework/src → src}/Stream.js +11 -3
- package/{framework/src → src}/Validator.js +21 -21
- package/{framework/src → src}/Var.js +5 -5
- package/{framework/src → src}/View/EarlyHints.js +1 -1
- package/{framework/src → src}/View/Form.js +69 -69
- package/{framework/src → src}/View.js +78 -81
- package/src/WebSocket.js +403 -0
- package/template/config.json +5 -0
- package/{web → template}/controller/page/about.js +6 -6
- package/{web → template}/controller/page/index.js +9 -9
- package/{web → template}/package.json +4 -5
- package/{web → template}/public/assets/css/style.css +4 -4
- package/{web → template}/public/assets/js/app.js +6 -6
- package/{web → template}/route/www.js +6 -6
- package/{web → template}/skeleton/main.html +1 -1
- package/{web → template}/view/content/about.html +5 -5
- package/{web → template}/view/content/home.html +12 -12
- package/template/view/footer/main.html +11 -0
- package/{web → template}/view/head/main.html +1 -1
- package/{web → template}/view/header/main.html +2 -2
- package/test/core/Candy.test.js +58 -58
- package/test/core/Commands.test.js +7 -7
- package/test/core/Config.test.js +82 -85
- package/test/core/Lang.test.js +2 -2
- package/test/core/Process.test.js +6 -6
- package/test/framework/Route.test.js +56 -37
- package/test/framework/View/EarlyHints.test.js +2 -2
- package/test/framework/WebSocket.test.js +100 -0
- package/test/framework/middleware.test.js +85 -0
- package/test/server/Api.test.js +31 -31
- package/test/server/DNS.test.js +11 -11
- package/test/server/Hub.test.js +497 -0
- package/test/server/Mail.account.test_.js +3 -3
- package/test/server/Mail.init.test_.js +10 -10
- package/test/server/Mail.test_.js +20 -20
- package/test/server/SSL.test_.js +54 -54
- package/test/server/Server.test.js +39 -39
- package/test/server/Service.test_.js +7 -7
- package/test/server/Subdomain.test.js +7 -7
- package/test/server/Web/Firewall.test.js +87 -87
- package/test/server/Web/Proxy.test.js +397 -0
- package/test/server/{Web.test_.js → Web.test.js} +137 -205
- package/test/server/__mocks__/fs.js +2 -2
- package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
- package/test/server/__mocks__/index.js +6 -6
- package/test/server/__mocks__/testFactories.js +1 -1
- package/test/server/__mocks__/testHelpers.js +7 -7
- package/.husky/pre-commit +0 -2
- package/.kiro/steering/code-style.md +0 -56
- package/.kiro/steering/product.md +0 -20
- package/.kiro/steering/structure.md +0 -77
- package/.kiro/steering/tech.md +0 -87
- package/AGENTS.md +0 -84
- package/bin/candy +0 -10
- package/bin/candypack +0 -10
- package/cli/index.js +0 -3
- package/cli/src/Cli.js +0 -348
- package/cli/src/Connector.js +0 -93
- package/cli/src/Monitor.js +0 -416
- package/core/Candy.js +0 -87
- package/core/Commands.js +0 -239
- package/core/Config.js +0 -1094
- package/core/Lang.js +0 -52
- package/core/Log.js +0 -43
- package/core/Process.js +0 -26
- package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
- package/docs/server/01-installation/01-quick-install.md +0 -19
- package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
- package/docs/server/02-get-started/01-core-concepts.md +0 -7
- package/docs/server/02-get-started/02-basic-commands.md +0 -57
- package/docs/server/02-get-started/03-cli-reference.md +0 -276
- package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
- package/docs/server/03-service/01-start-a-new-service.md +0 -57
- package/docs/server/03-service/02-delete-a-service.md +0 -48
- package/docs/server/04-web/01-create-a-website.md +0 -36
- package/docs/server/04-web/02-list-websites.md +0 -9
- package/docs/server/04-web/03-delete-a-website.md +0 -29
- package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
- package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
- package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
- package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
- package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
- package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
- package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
- package/docs/server/07-mail/04-change-account-password.md +0 -23
- package/framework/src/Candy.js +0 -81
- package/framework/src/Route.js +0 -455
- package/framework/src/Server.js +0 -15
- package/locale/de-DE.json +0 -80
- package/locale/en-US.json +0 -79
- package/locale/es-ES.json +0 -80
- package/locale/fr-FR.json +0 -80
- package/locale/pt-BR.json +0 -80
- package/locale/ru-RU.json +0 -80
- package/locale/tr-TR.json +0 -85
- package/locale/zh-CN.json +0 -80
- package/server/index.js +0 -5
- package/server/src/Api.js +0 -88
- package/server/src/DNS.js +0 -940
- package/server/src/Hub.js +0 -535
- package/server/src/Mail.js +0 -571
- package/server/src/SSL.js +0 -180
- package/server/src/Server.js +0 -27
- package/server/src/Service.js +0 -248
- package/server/src/Subdomain.js +0 -64
- package/server/src/Web/Firewall.js +0 -170
- package/server/src/Web/Proxy.js +0 -134
- package/server/src/Web.js +0 -451
- package/server/src/mail/imap.js +0 -1091
- package/server/src/mail/server.js +0 -32
- package/server/src/mail/smtp.js +0 -786
- package/test/server/Client.test.js +0 -338
- package/test/server/__mocks__/http-proxy.js +0 -105
- package/watchdog/index.js +0 -3
- package/watchdog/src/Watchdog.js +0 -156
- package/web/config.json +0 -5
- package/web/view/footer/main.html +0 -11
- /package/{framework/src → src}/Env.js +0 -0
- /package/{framework/src → src}/Route/Cron.js +0 -0
- /package/{framework/src → src}/Token.js +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
## 🔧 Template Syntax Overview
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Odac uses a powerful template engine to create dynamic content in view files. The engine provides a clean, HTML-like syntax for displaying variables, conditionals, loops, translations, and more.
|
|
4
4
|
|
|
5
|
-
> **Note:**
|
|
5
|
+
> **Note:** Odac also supports legacy syntax (`{{ }}`, `{!! !!}`, `{{-- --}}`) for backward compatibility, but the new `<odac>` tag syntax is recommended for all new projects.
|
|
6
6
|
|
|
7
7
|
### Quick Reference
|
|
8
8
|
|
|
@@ -10,17 +10,17 @@ This page provides a quick overview of all available template features. For deta
|
|
|
10
10
|
|
|
11
11
|
### Variables (Controller Data)
|
|
12
12
|
|
|
13
|
-
Display data passed from controllers using `
|
|
13
|
+
Display data passed from controllers using `Odac.set()`:
|
|
14
14
|
|
|
15
15
|
```html
|
|
16
16
|
<!-- HTML-safe output -->
|
|
17
|
-
<
|
|
17
|
+
<odac var="username" />
|
|
18
18
|
|
|
19
19
|
<!-- Raw HTML output -->
|
|
20
|
-
<
|
|
20
|
+
<odac var="htmlContent" raw />
|
|
21
21
|
|
|
22
22
|
<!-- String literals -->
|
|
23
|
-
<
|
|
23
|
+
<odac>Hello World</odac>
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
**[→ Learn more about Variables](./03-variables.md)**
|
|
@@ -32,11 +32,11 @@ Access URL query parameters directly:
|
|
|
32
32
|
```html
|
|
33
33
|
<!-- Get query parameter from URL -->
|
|
34
34
|
<!-- URL: /search?q=laptop -->
|
|
35
|
-
<
|
|
35
|
+
<odac get="q" />
|
|
36
36
|
<!-- Output: laptop -->
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
**Note:** `<
|
|
39
|
+
**Note:** `<odac get>` is for URL parameters. For controller data, use `<odac var>`.
|
|
40
40
|
|
|
41
41
|
**[→ Learn more about Request Data](./04-request-data.md)**
|
|
42
42
|
|
|
@@ -46,13 +46,13 @@ Create multi-language applications:
|
|
|
46
46
|
|
|
47
47
|
```html
|
|
48
48
|
<!-- Basic translation -->
|
|
49
|
-
<
|
|
49
|
+
<odac translate>Welcome</odac>
|
|
50
50
|
|
|
51
51
|
<!-- With placeholders -->
|
|
52
|
-
<
|
|
52
|
+
<odac translate>Hello <odac var="user.name" /></odac>
|
|
53
53
|
|
|
54
54
|
<!-- With HTML preserved -->
|
|
55
|
-
<
|
|
55
|
+
<odac translate raw>Click <a href="/help">here</a></odac>
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
**[→ Learn more about Translations](./07-translations.md)**
|
|
@@ -62,12 +62,12 @@ Create multi-language applications:
|
|
|
62
62
|
Two types of comments for different purposes:
|
|
63
63
|
|
|
64
64
|
```html
|
|
65
|
-
<!--
|
|
65
|
+
<!--odac Backend comment (not rendered) -->
|
|
66
66
|
|
|
67
|
-
<!--
|
|
67
|
+
<!--odac
|
|
68
68
|
Multi-line backend comment
|
|
69
69
|
Won't appear in output
|
|
70
|
-
|
|
70
|
+
odac-->
|
|
71
71
|
|
|
72
72
|
<!-- Regular HTML comment (rendered) -->
|
|
73
73
|
```
|
|
@@ -79,13 +79,13 @@ candy-->
|
|
|
79
79
|
Show or hide content based on conditions:
|
|
80
80
|
|
|
81
81
|
```html
|
|
82
|
-
<
|
|
82
|
+
<odac:if condition="user.isAdmin">
|
|
83
83
|
<p>Admin panel</p>
|
|
84
|
-
<
|
|
84
|
+
<odac:elseif condition="user.isModerator">
|
|
85
85
|
<p>Moderator panel</p>
|
|
86
|
-
<
|
|
86
|
+
<odac:else>
|
|
87
87
|
<p>User panel</p>
|
|
88
|
-
</
|
|
88
|
+
</odac:if>
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
**[→ Learn more about Conditionals](./05-conditionals.md)**
|
|
@@ -96,18 +96,18 @@ Iterate over arrays and objects:
|
|
|
96
96
|
|
|
97
97
|
```html
|
|
98
98
|
<!-- For loop -->
|
|
99
|
-
<
|
|
100
|
-
<div><
|
|
101
|
-
</
|
|
99
|
+
<odac:for in="users" key="index" value="user">
|
|
100
|
+
<div><odac var="user.name" /></div>
|
|
101
|
+
</odac:for>
|
|
102
102
|
|
|
103
103
|
<!-- While loop -->
|
|
104
|
-
<
|
|
105
|
-
<p><
|
|
106
|
-
</
|
|
104
|
+
<odac:while condition="counter < 10">
|
|
105
|
+
<p><odac var="counter" /></p>
|
|
106
|
+
</odac:while>
|
|
107
107
|
|
|
108
108
|
<!-- Loop control -->
|
|
109
|
-
<
|
|
110
|
-
<
|
|
109
|
+
<odac:break />
|
|
110
|
+
<odac:continue />
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
**[→ Learn more about Loops](./06-loops.md)**
|
|
@@ -117,54 +117,54 @@ Iterate over arrays and objects:
|
|
|
117
117
|
Execute JavaScript on the server during template rendering:
|
|
118
118
|
|
|
119
119
|
```html
|
|
120
|
-
<script:
|
|
120
|
+
<script:odac>
|
|
121
121
|
// Runs on SERVER before HTML is sent
|
|
122
122
|
let total = 0;
|
|
123
123
|
for (let item of cart) {
|
|
124
124
|
total += item.price * item.quantity;
|
|
125
125
|
}
|
|
126
|
-
</script:
|
|
126
|
+
</script:odac>
|
|
127
127
|
|
|
128
|
-
<p>Total: $<
|
|
128
|
+
<p>Total: $<odac var="total" /></p>
|
|
129
129
|
```
|
|
130
130
|
|
|
131
131
|
**[→ Learn more about Backend JavaScript](./08-backend-javascript.md)**
|
|
132
132
|
|
|
133
|
-
### Accessing the
|
|
133
|
+
### Accessing the Odac Object
|
|
134
134
|
|
|
135
|
-
Full access to the
|
|
135
|
+
Full access to the Odac object in templates:
|
|
136
136
|
|
|
137
137
|
```html
|
|
138
|
-
<
|
|
139
|
-
<p>User: <
|
|
140
|
-
</
|
|
138
|
+
<odac:if condition="Odac.Auth.check()">
|
|
139
|
+
<p>User: <odac var="Odac.Auth.user().name" /></p>
|
|
140
|
+
</odac:if>
|
|
141
141
|
|
|
142
|
-
<p>URL: <
|
|
142
|
+
<p>URL: <odac var="Odac.Request.url" /></p>
|
|
143
143
|
```
|
|
144
144
|
|
|
145
145
|
### Complete Syntax Reference
|
|
146
146
|
|
|
147
147
|
| Feature | Syntax | Documentation |
|
|
148
148
|
|---------|--------|---------------|
|
|
149
|
-
| Variable (Controller) | `<
|
|
150
|
-
| Raw HTML | `<
|
|
151
|
-
| String | `<
|
|
152
|
-
| Query Parameter | `<
|
|
153
|
-
| Translation | `<
|
|
154
|
-
| Translation Raw | `<
|
|
155
|
-
| If | `<
|
|
156
|
-
| Elseif | `<
|
|
157
|
-
| Else | `<
|
|
158
|
-
| For | `<
|
|
159
|
-
| While | `<
|
|
160
|
-
| Break | `<
|
|
161
|
-
| Continue | `<
|
|
162
|
-
| JavaScript | `<script:
|
|
163
|
-
| Comment | `<!--
|
|
149
|
+
| Variable (Controller) | `<odac var="x" />` | [Variables](./03-variables.md) |
|
|
150
|
+
| Raw HTML | `<odac var="x" raw />` | [Variables](./03-variables.md) |
|
|
151
|
+
| String | `<odac>text</odac>` | [Variables](./03-variables.md) |
|
|
152
|
+
| Query Parameter | `<odac get="key" />` | [Request Data](./04-request-data.md) |
|
|
153
|
+
| Translation | `<odac translate>key</odac>` | [Translations](./07-translations.md) |
|
|
154
|
+
| Translation Raw | `<odac translate raw>key</odac>` | [Translations](./07-translations.md) |
|
|
155
|
+
| If | `<odac:if condition="x">` | [Conditionals](./05-conditionals.md) |
|
|
156
|
+
| Elseif | `<odac:elseif condition="x">` | [Conditionals](./05-conditionals.md) |
|
|
157
|
+
| Else | `<odac:else>` | [Conditionals](./05-conditionals.md) |
|
|
158
|
+
| For | `<odac:for in="x" value="item">` | [Loops](./06-loops.md) |
|
|
159
|
+
| While | `<odac:while condition="x">` | [Loops](./06-loops.md) |
|
|
160
|
+
| Break | `<odac:break />` | [Loops](./06-loops.md) |
|
|
161
|
+
| Continue | `<odac:continue />` | [Loops](./06-loops.md) |
|
|
162
|
+
| JavaScript | `<script:odac>...</script:odac>` | [Backend JavaScript](./08-backend-javascript.md) |
|
|
163
|
+
| Comment | `<!--odac ... odac-->` | [Comments](./09-comments.md) |
|
|
164
164
|
|
|
165
165
|
### Legacy Syntax
|
|
166
166
|
|
|
167
|
-
|
|
167
|
+
Odac also supports legacy syntax for backward compatibility:
|
|
168
168
|
|
|
169
169
|
```html
|
|
170
170
|
<!-- Variable output -->
|
|
@@ -177,5 +177,5 @@ CandyPack also supports legacy syntax for backward compatibility:
|
|
|
177
177
|
{{-- This is a comment --}}
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
-
**Note:** The new `<
|
|
180
|
+
**Note:** The new `<odac>` tag syntax is recommended for all new projects.
|
|
181
181
|
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
## 📦 Variables in Views
|
|
2
2
|
|
|
3
|
-
Variables allow you to display dynamic data in your templates. Data is passed from controllers to views using `
|
|
3
|
+
Variables allow you to display dynamic data in your templates. Data is passed from controllers to views using `Odac.set()` and displayed using the `<odac var>` tag.
|
|
4
4
|
|
|
5
5
|
### Passing Data from Controller
|
|
6
6
|
|
|
7
|
-
Use `
|
|
7
|
+
Use `Odac.set()` in your controller to pass data to views:
|
|
8
8
|
|
|
9
9
|
```javascript
|
|
10
10
|
// Controller: controller/profile.js
|
|
11
|
-
module.exports = async function(
|
|
11
|
+
module.exports = async function(Odac) {
|
|
12
12
|
// Set single variable
|
|
13
|
-
|
|
13
|
+
Odac.set('username', 'John Doe')
|
|
14
14
|
|
|
15
15
|
// Set multiple variables at once
|
|
16
|
-
|
|
16
|
+
Odac.set({
|
|
17
17
|
user: {
|
|
18
18
|
name: 'John Doe',
|
|
19
19
|
email: 'john@example.com',
|
|
@@ -22,7 +22,7 @@ module.exports = async function(Candy) {
|
|
|
22
22
|
pageTitle: 'User Profile'
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
Odac.View.skeleton('main').set('content', 'profile')
|
|
26
26
|
}
|
|
27
27
|
```
|
|
28
28
|
|
|
@@ -31,9 +31,9 @@ module.exports = async function(Candy) {
|
|
|
31
31
|
#### HTML-Safe Output (Recommended)
|
|
32
32
|
|
|
33
33
|
```html
|
|
34
|
-
<
|
|
35
|
-
<
|
|
36
|
-
<
|
|
34
|
+
<odac var="username" />
|
|
35
|
+
<odac var="user.email" />
|
|
36
|
+
<odac var="product.price" />
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
This automatically:
|
|
@@ -43,12 +43,12 @@ This automatically:
|
|
|
43
43
|
**Example:**
|
|
44
44
|
```javascript
|
|
45
45
|
// Controller
|
|
46
|
-
|
|
46
|
+
Odac.set('message', 'Hello\nWorld')
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
```html
|
|
50
50
|
<!-- View -->
|
|
51
|
-
<
|
|
51
|
+
<odac var="message" />
|
|
52
52
|
<!-- Output: Hello<br>World -->
|
|
53
53
|
```
|
|
54
54
|
|
|
@@ -57,8 +57,8 @@ Candy.set('message', 'Hello\nWorld')
|
|
|
57
57
|
When you need to display HTML content without escaping:
|
|
58
58
|
|
|
59
59
|
```html
|
|
60
|
-
<
|
|
61
|
-
<
|
|
60
|
+
<odac var="htmlContent" raw />
|
|
61
|
+
<odac var="user.bio" raw />
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
**Security Warning:** Only use `raw` with trusted content. Never use it with user-generated content to prevent XSS attacks.
|
|
@@ -66,12 +66,12 @@ When you need to display HTML content without escaping:
|
|
|
66
66
|
**Example:**
|
|
67
67
|
```javascript
|
|
68
68
|
// Controller
|
|
69
|
-
|
|
69
|
+
Odac.set('content', '<strong>Bold text</strong>')
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
```html
|
|
73
73
|
<!-- View -->
|
|
74
|
-
<
|
|
74
|
+
<odac var="content" raw />
|
|
75
75
|
<!-- Output: <strong>Bold text</strong> -->
|
|
76
76
|
```
|
|
77
77
|
|
|
@@ -81,7 +81,7 @@ You can access nested object properties using dot notation:
|
|
|
81
81
|
|
|
82
82
|
```javascript
|
|
83
83
|
// Controller
|
|
84
|
-
|
|
84
|
+
Odac.set('user', {
|
|
85
85
|
name: 'John',
|
|
86
86
|
profile: {
|
|
87
87
|
email: 'john@example.com',
|
|
@@ -94,9 +94,9 @@ Candy.set('user', {
|
|
|
94
94
|
|
|
95
95
|
```html
|
|
96
96
|
<!-- View -->
|
|
97
|
-
<p>Name: <
|
|
98
|
-
<p>Email: <
|
|
99
|
-
<p>City: <
|
|
97
|
+
<p>Name: <odac var="user.name" /></p>
|
|
98
|
+
<p>Email: <odac var="user.profile.email" /></p>
|
|
99
|
+
<p>City: <odac var="user.profile.address.city" /></p>
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
### String Literals
|
|
@@ -104,32 +104,32 @@ Candy.set('user', {
|
|
|
104
104
|
Display static text directly:
|
|
105
105
|
|
|
106
106
|
```html
|
|
107
|
-
<
|
|
108
|
-
<
|
|
107
|
+
<odac>Hello World</odac>
|
|
108
|
+
<odac>Welcome to our site</odac>
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
This is useful when you want consistent syntax throughout your templates.
|
|
112
112
|
|
|
113
|
-
### Accessing the
|
|
113
|
+
### Accessing the Odac Object
|
|
114
114
|
|
|
115
|
-
You have full access to the `
|
|
115
|
+
You have full access to the `Odac` object within templates:
|
|
116
116
|
|
|
117
117
|
```html
|
|
118
118
|
<!-- Authentication -->
|
|
119
|
-
<
|
|
120
|
-
<p>User ID: <
|
|
121
|
-
<p>Email: <
|
|
122
|
-
</
|
|
119
|
+
<odac:if condition="Odac.Auth.check()">
|
|
120
|
+
<p>User ID: <odac var="Odac.Auth.user().id" /></p>
|
|
121
|
+
<p>Email: <odac var="Odac.Auth.user().email" /></p>
|
|
122
|
+
</odac:if>
|
|
123
123
|
|
|
124
124
|
<!-- Request Information -->
|
|
125
|
-
<p>Method: <
|
|
126
|
-
<p>URL: <
|
|
127
|
-
<p>IP: <
|
|
125
|
+
<p>Method: <odac var="Odac.Request.method" /></p>
|
|
126
|
+
<p>URL: <odac var="Odac.Request.url" /></p>
|
|
127
|
+
<p>IP: <odac var="Odac.Request.ip" /></p>
|
|
128
128
|
|
|
129
129
|
<!-- Configuration -->
|
|
130
|
-
<
|
|
130
|
+
<odac:if condition="Odac.Config.debug">
|
|
131
131
|
<div class="debug-info">Debug mode enabled</div>
|
|
132
|
-
</
|
|
132
|
+
</odac:if>
|
|
133
133
|
```
|
|
134
134
|
|
|
135
135
|
### Practical Examples
|
|
@@ -138,37 +138,37 @@ You have full access to the `Candy` object within templates:
|
|
|
138
138
|
|
|
139
139
|
```javascript
|
|
140
140
|
// Controller: controller/profile.js
|
|
141
|
-
module.exports = async function(
|
|
141
|
+
module.exports = async function(Odac) {
|
|
142
142
|
// Fetch user from database
|
|
143
|
-
const userId =
|
|
144
|
-
const user = await
|
|
143
|
+
const userId = Odac.Request.get('id')
|
|
144
|
+
const user = await Odac.Mysql.table('users')
|
|
145
145
|
.where('id', userId)
|
|
146
146
|
.first()
|
|
147
147
|
|
|
148
148
|
// Pass to view
|
|
149
|
-
|
|
149
|
+
Odac.set('user', {
|
|
150
150
|
name: user.name,
|
|
151
151
|
email: user.email,
|
|
152
152
|
bio: user.bio,
|
|
153
153
|
isVerified: user.verified
|
|
154
154
|
})
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
Odac.View.skeleton('main').set('content', 'profile')
|
|
157
157
|
}
|
|
158
158
|
```
|
|
159
159
|
|
|
160
160
|
```html
|
|
161
161
|
<!-- View: view/content/profile.html -->
|
|
162
162
|
<div class="profile-card">
|
|
163
|
-
<h2><
|
|
164
|
-
<p><
|
|
163
|
+
<h2><odac var="user.name" /></h2>
|
|
164
|
+
<p><odac var="user.email" /></p>
|
|
165
165
|
|
|
166
|
-
<
|
|
166
|
+
<odac:if condition="user.isVerified">
|
|
167
167
|
<span class="badge">✓ Verified</span>
|
|
168
|
-
</
|
|
168
|
+
</odac:if>
|
|
169
169
|
|
|
170
170
|
<div class="bio">
|
|
171
|
-
<
|
|
171
|
+
<odac var="user.bio" raw />
|
|
172
172
|
</div>
|
|
173
173
|
</div>
|
|
174
174
|
```
|
|
@@ -177,9 +177,9 @@ module.exports = async function(Candy) {
|
|
|
177
177
|
|
|
178
178
|
```javascript
|
|
179
179
|
// Controller: controller/product.js
|
|
180
|
-
module.exports = async function(
|
|
181
|
-
const productId =
|
|
182
|
-
const product = await
|
|
180
|
+
module.exports = async function(Odac) {
|
|
181
|
+
const productId = Odac.Request.get('id')
|
|
182
|
+
const product = await Odac.Mysql.table('products')
|
|
183
183
|
.where('id', productId)
|
|
184
184
|
.first()
|
|
185
185
|
|
|
@@ -187,31 +187,31 @@ module.exports = async function(Candy) {
|
|
|
187
187
|
const hasDiscount = product.discount > 0
|
|
188
188
|
const finalPrice = product.price * (1 - product.discount / 100)
|
|
189
189
|
|
|
190
|
-
|
|
190
|
+
Odac.set({
|
|
191
191
|
product: product,
|
|
192
192
|
hasDiscount: hasDiscount,
|
|
193
193
|
finalPrice: finalPrice
|
|
194
194
|
})
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
Odac.View.skeleton('main').set('content', 'product')
|
|
197
197
|
}
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
```html
|
|
201
201
|
<!-- View: view/content/product.html -->
|
|
202
202
|
<div class="product">
|
|
203
|
-
<h1><
|
|
203
|
+
<h1><odac var="product.name" /></h1>
|
|
204
204
|
|
|
205
|
-
<
|
|
206
|
-
<p class="original-price">$<
|
|
207
|
-
<p class="final-price">$<
|
|
208
|
-
<span class="discount">-<
|
|
209
|
-
<
|
|
210
|
-
<p class="price">$<
|
|
211
|
-
</
|
|
205
|
+
<odac:if condition="hasDiscount">
|
|
206
|
+
<p class="original-price">$<odac var="product.price" /></p>
|
|
207
|
+
<p class="final-price">$<odac var="finalPrice" /></p>
|
|
208
|
+
<span class="discount">-<odac var="product.discount" />%</span>
|
|
209
|
+
<odac:else>
|
|
210
|
+
<p class="price">$<odac var="product.price" /></p>
|
|
211
|
+
</odac:if>
|
|
212
212
|
|
|
213
213
|
<div class="description">
|
|
214
|
-
<
|
|
214
|
+
<odac var="product.description" />
|
|
215
215
|
</div>
|
|
216
216
|
</div>
|
|
217
217
|
```
|
|
@@ -220,38 +220,38 @@ module.exports = async function(Candy) {
|
|
|
220
220
|
|
|
221
221
|
```javascript
|
|
222
222
|
// Controller: controller/products.js
|
|
223
|
-
module.exports = async function(
|
|
224
|
-
const products = await
|
|
223
|
+
module.exports = async function(Odac) {
|
|
224
|
+
const products = await Odac.Mysql.table('products')
|
|
225
225
|
.where('active', true)
|
|
226
226
|
.get()
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
Odac.set({
|
|
229
229
|
products: products,
|
|
230
230
|
totalProducts: products.length
|
|
231
231
|
})
|
|
232
232
|
|
|
233
|
-
|
|
233
|
+
Odac.View.skeleton('main').set('content', 'products')
|
|
234
234
|
}
|
|
235
235
|
```
|
|
236
236
|
|
|
237
237
|
```html
|
|
238
238
|
<!-- View: view/content/products.html -->
|
|
239
|
-
<h1>Products (<
|
|
239
|
+
<h1>Products (<odac var="totalProducts" />)</h1>
|
|
240
240
|
|
|
241
241
|
<div class="products-grid">
|
|
242
|
-
<
|
|
242
|
+
<odac:for in="products" value="product">
|
|
243
243
|
<div class="product-card">
|
|
244
|
-
<h3><
|
|
245
|
-
<p>$<
|
|
244
|
+
<h3><odac var="product.name" /></h3>
|
|
245
|
+
<p>$<odac var="product.price" /></p>
|
|
246
246
|
</div>
|
|
247
|
-
</
|
|
247
|
+
</odac:for>
|
|
248
248
|
</div>
|
|
249
249
|
```
|
|
250
250
|
|
|
251
251
|
### Best Practices
|
|
252
252
|
|
|
253
|
-
1. **Always use
|
|
254
|
-
2. **Set data before rendering**: All `
|
|
253
|
+
1. **Always use Odac.set()**: Pass all data through `Odac.set()` for consistency
|
|
254
|
+
2. **Set data before rendering**: All `Odac.set()` calls should come before `Odac.View.set()`
|
|
255
255
|
3. **Compute in controller**: Do calculations in the controller, not in views
|
|
256
256
|
4. **Use descriptive names**: `pageTitle`, `userProfile` instead of `title`, `data`
|
|
257
257
|
5. **Group related data**: Use objects to organize related data
|
|
@@ -259,10 +259,10 @@ module.exports = async function(Candy) {
|
|
|
259
259
|
**Good:**
|
|
260
260
|
```javascript
|
|
261
261
|
// Controller
|
|
262
|
-
const user = await
|
|
262
|
+
const user = await Odac.Mysql.table('users').first()
|
|
263
263
|
const isAdmin = user.role === 'admin'
|
|
264
264
|
|
|
265
|
-
|
|
265
|
+
Odac.set({
|
|
266
266
|
user: user,
|
|
267
267
|
isAdmin: isAdmin
|
|
268
268
|
})
|
|
@@ -271,9 +271,9 @@ Candy.set({
|
|
|
271
271
|
**Avoid:**
|
|
272
272
|
```html
|
|
273
273
|
<!-- Don't do complex logic in views -->
|
|
274
|
-
<
|
|
274
|
+
<odac:if condition="user.role === 'admin' && user.verified && !user.banned">
|
|
275
275
|
...
|
|
276
|
-
</
|
|
276
|
+
</odac:if>
|
|
277
277
|
```
|
|
278
278
|
|
|
279
279
|
### Error Handling
|
|
@@ -282,38 +282,38 @@ Always handle cases where data might not exist:
|
|
|
282
282
|
|
|
283
283
|
```javascript
|
|
284
284
|
// Controller
|
|
285
|
-
module.exports = async function(
|
|
286
|
-
const productId =
|
|
287
|
-
const product = await
|
|
285
|
+
module.exports = async function(Odac) {
|
|
286
|
+
const productId = Odac.Request.get('id')
|
|
287
|
+
const product = await Odac.Mysql.table('products')
|
|
288
288
|
.where('id', productId)
|
|
289
289
|
.first()
|
|
290
290
|
|
|
291
291
|
if (!product) {
|
|
292
|
-
|
|
292
|
+
Odac.set('error', 'Product not found')
|
|
293
293
|
} else {
|
|
294
|
-
|
|
294
|
+
Odac.set('product', product)
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
-
|
|
297
|
+
Odac.View.skeleton('main').set('content', 'product')
|
|
298
298
|
}
|
|
299
299
|
```
|
|
300
300
|
|
|
301
301
|
```html
|
|
302
302
|
<!-- View -->
|
|
303
|
-
<
|
|
303
|
+
<odac:if condition="error">
|
|
304
304
|
<div class="alert alert-danger">
|
|
305
|
-
<
|
|
305
|
+
<odac var="error" />
|
|
306
306
|
</div>
|
|
307
|
-
<
|
|
307
|
+
<odac:else>
|
|
308
308
|
<div class="product">
|
|
309
|
-
<h1><
|
|
309
|
+
<h1><odac var="product.name" /></h1>
|
|
310
310
|
</div>
|
|
311
|
-
</
|
|
311
|
+
</odac:if>
|
|
312
312
|
```
|
|
313
313
|
|
|
314
314
|
### Legacy Syntax (Backward Compatibility)
|
|
315
315
|
|
|
316
|
-
|
|
316
|
+
Odac also supports legacy syntax:
|
|
317
317
|
|
|
318
318
|
```html
|
|
319
319
|
<!-- HTML-safe output -->
|
|
@@ -325,4 +325,4 @@ CandyPack also supports legacy syntax:
|
|
|
325
325
|
{!! user.bio !!}
|
|
326
326
|
```
|
|
327
327
|
|
|
328
|
-
**Note:** The new `<
|
|
328
|
+
**Note:** The new `<odac>` tag syntax is recommended for all new projects as it provides better IDE support and readability.
|