apostrophe 4.28.0 → 4.28.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.
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(timeout 180 npx mocha:*)",
|
|
5
|
+
"Bash(timeout 600 npx mocha:*)",
|
|
6
|
+
"Bash(npm ls:*)",
|
|
7
|
+
"Bash(timeout 540 npx mocha:*)",
|
|
8
|
+
"Bash(echo:*)",
|
|
9
|
+
"Bash(timeout 10 node:*)",
|
|
10
|
+
"Bash(timeout 300 npx mocha:*)",
|
|
11
|
+
"Bash(timeout 60 npx mocha:*)",
|
|
12
|
+
"Bash(timeout 120 npx mocha:*)"
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 4.28.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f8d1952: Bug fix: the "pretty URLs" feature of @apostrophecms/file is now compatible with locale prefixes.
|
|
8
|
+
|
|
3
9
|
## 4.28.0
|
|
4
10
|
|
|
5
11
|
### Adds
|
|
@@ -8,7 +14,6 @@
|
|
|
8
14
|
- Adds widget graph store, accessible in Admin UI.
|
|
9
15
|
- Support for the new `prettyUrls: true` option for @apostrophecms/file, which enables "pretty URLs" for PDFs and other items in the file library, in exchange for a small performance impact. Edit the slug field to adjust the pretty URL
|
|
10
16
|
|
|
11
|
-
|
|
12
17
|
### Fixes
|
|
13
18
|
|
|
14
19
|
- Fix a bug when rich text link open in new tab checkbox can't be cleared
|
|
@@ -27,7 +32,7 @@
|
|
|
27
32
|
- Improve re-rendering UX while keeping the performance optimization
|
|
28
33
|
- raise the user's widget z-index context only when focused
|
|
29
34
|
- Hide add content buttons on rich text editing, like widget controls
|
|
30
|
-
-
|
|
35
|
+
- Refine in-context focus states for calmer UX
|
|
31
36
|
- Simplifies some in-context UI rendering checks
|
|
32
37
|
- Updated dependencies
|
|
33
38
|
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<a href="https://github.com/apostrophecms/apostrophe">
|
|
3
|
+
<img src="logo.svg" alt="ApostropheCMS logo" width="80" height="80">
|
|
4
|
+
</a>
|
|
5
|
+
|
|
6
|
+
<h1>ApostropheCMS</h1>
|
|
7
|
+
|
|
8
|
+
<p>
|
|
9
|
+
<a aria-label="Join the community on Discord" href="http://chat.apostrophecms.org">
|
|
10
|
+
<img alt="" src="https://img.shields.io/discord/517772094482677790?color=5865f2&label=Join%20the%20Discord&logo=discord&logoColor=fff&labelColor=000&style=for-the-badge&logoWidth=20" />
|
|
11
|
+
</a>
|
|
12
|
+
<a aria-label="License" href="https://github.com/apostrophecms/apostrophe/blob/main/LICENSE.md">
|
|
13
|
+
<img alt="" src="https://img.shields.io/static/v1?style=for-the-badge&labelColor=000000&label=License&message=MIT&color=3DA639" />
|
|
14
|
+
</a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p>
|
|
18
|
+
<strong>Full-stack CMS for developers and content teams</strong><br />
|
|
19
|
+
Build websites with in-context editing and headless flexibility using Node.js and MongoDB.
|
|
20
|
+
<br />
|
|
21
|
+
<a href="https://docs.apostrophecms.org/"><strong>Documentation »</strong></a>
|
|
22
|
+
<br />
|
|
23
|
+
<br />
|
|
24
|
+
<a href="http://demo.apostrophecms.com">Demo</a>
|
|
25
|
+
·
|
|
26
|
+
<a href="https://roadmap.apostrophecms.com/roadmap">Roadmap</a>
|
|
27
|
+
·
|
|
28
|
+
<a href="https://github.com/apostrophecms/apostrophe/issues/new?assignees=&labels=bug,3.0&template=bug_report.md&title=">Report Bug</a>
|
|
29
|
+
</p>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
## About
|
|
33
|
+
|
|
34
|
+
ApostropheCMS is a full-stack content management system built with Node.js and MongoDB. Content creators can edit directly on live pages without switching between admin interfaces, while developers can build with modern JavaScript or use it headlessly with any frontend framework.
|
|
35
|
+
|
|
36
|
+
### Key Features
|
|
37
|
+
|
|
38
|
+
- **🎯 In-Context Editing** - Content creators edit directly on the live page, seeing changes instantly
|
|
39
|
+
- **⚡ Headless-Ready** - Use any frontend framework while keeping the powerful admin experience
|
|
40
|
+
- **🛠️ Developer-First** - Built with Node.js and MongoDB for full-stack JavaScript development
|
|
41
|
+
- **📈 Scales Beautifully** - From small sites to enterprise applications handling millions of pages
|
|
42
|
+
- **🔐 Enterprise Features** - Advanced permissions, workflow management, automated translations, and more
|
|
43
|
+
|
|
44
|
+
## System Requirements
|
|
45
|
+
|
|
46
|
+
| Requirement | Version | Installation Notes |
|
|
47
|
+
|-------------|---------|-------------------|
|
|
48
|
+
| **Node.js** | 20.x+ | Use [NVM](https://github.com/nvm-sh/nvm) for version management |
|
|
49
|
+
| **MongoDB** | 6.0+ | [MongoDB Atlas](https://www.mongodb.com/atlas) (cloud) or local install |
|
|
50
|
+
| **npm** | 10.x+ | Included with Node.js |
|
|
51
|
+
|
|
52
|
+
See our [setup guides](https://docs.apostrophecms.org/guide/development-setup.html) for installation instructions.
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
Get ApostropheCMS running locally in minutes:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Option 1: Install CLI globally (recommended for multiple projects)
|
|
60
|
+
npm install -g @apostrophecms/cli
|
|
61
|
+
apos create my-website
|
|
62
|
+
cd my-website
|
|
63
|
+
npm run dev
|
|
64
|
+
|
|
65
|
+
# Option 2: Use npx for one-time project creation
|
|
66
|
+
npx @apostrophecms/cli create my-website
|
|
67
|
+
cd my-website
|
|
68
|
+
npm run dev
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Your new ApostropheCMS site will be available at `http://localhost:3000` with a powerful admin interface at `/login`.
|
|
72
|
+
|
|
73
|
+
### Prefer to Go Headless?
|
|
74
|
+
|
|
75
|
+
**Get started with Astro integration** - the easiest way to build headless sites while keeping visual editing:
|
|
76
|
+
|
|
77
|
+
- **[Apollo Starter Kit (Astro)](https://apostrophecms.com/starter-kits/apollo-starter-kit-for-astro-cms)** - Production-ready foundation with beautiful design system and rich content features
|
|
78
|
+
- **[Essentials Starter Kit (Astro)](git clone https://github.com/apostrophecms/starter-kit-astro-essentials)** - Minimal, clean foundation for building custom designs from scratch
|
|
79
|
+
|
|
80
|
+
Both starter kits provide headless CMS power with in-context editing, letting content creators edit directly on the live site while you build with modern frontend tools. Our Astro integration handles all the content fetching automatically—no REST API calls to write.
|
|
81
|
+
|
|
82
|
+
**Desire a different frontend framework?** Use our REST APIs with React, Vue, Svelte, or any other framework:
|
|
83
|
+
|
|
84
|
+
- **[REST API Documentation](https://docs.apostrophecms.org/reference/api/pieces.html)** - Complete API reference
|
|
85
|
+
- **[Headless CMS Guide](https://docs.apostrophecms.org/guide/headless-cms.html)** - Integration walkthrough for any framework
|
|
86
|
+
|
|
87
|
+
### Hosting & Deployment
|
|
88
|
+
|
|
89
|
+
Choose [ApostropheCMS hosting](https://apostrophecms.com/hosting) for turnkey solutions with optimized performance and dedicated support, or deploy to [any platform where Node.js runs](https://docs.apostrophecms.org/guide/hosting.html).
|
|
90
|
+
|
|
91
|
+
## Built With Modern Tech
|
|
92
|
+
|
|
93
|
+
- **[Node.js](https://nodejs.org/)** - JavaScript runtime for server-side development
|
|
94
|
+
- **[MongoDB](https://www.mongodb.com/)** - Flexible document database for content storage
|
|
95
|
+
- **ESM Modules** - Native ES6 module support for modern JavaScript
|
|
96
|
+
- **Vite** - Lightning-fast build tool and development server
|
|
97
|
+
- **Modern JavaScript** - ES6+, async/await, and contemporary development patterns
|
|
98
|
+
|
|
99
|
+
## Community & Support
|
|
100
|
+
|
|
101
|
+
**Join other developers and content creators using ApostropheCMS:**
|
|
102
|
+
|
|
103
|
+
- **[Discord](https://discord.com/invite/XkbRNq7)** - Get help, share projects, and connect with other users
|
|
104
|
+
- **[GitHub Discussions](https://github.com/apostrophecms/apostrophe/discussions)** - Feature requests, technical discussions, and community support
|
|
105
|
+
- **[Documentation](https://docs.apostrophecms.org/)** - Comprehensive guides, tutorials, and API references
|
|
106
|
+
|
|
107
|
+
## Contributing
|
|
108
|
+
|
|
109
|
+
We welcome contributions from the community! Whether you're fixing bugs, adding features, or improving documentation, your help makes ApostropheCMS better for everyone.
|
|
110
|
+
|
|
111
|
+
- **[Contribution Guide](https://github.com/apostrophecms/apostrophe/blob/main/CONTRIBUTING.md)** - How to contribute code, documentation, and feedback
|
|
112
|
+
- **[Good First Issues](https://github.com/apostrophecms/apostrophe/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)** - Perfect starting points for new contributors
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
## Pro Features
|
|
116
|
+
|
|
117
|
+
**For teams and organizations requiring additional features:**
|
|
118
|
+
|
|
119
|
+
- **🔐 Advanced User Management** - Granular permissions, user groups, and access controls
|
|
120
|
+
- **🌍 Automated Translation** - AI-powered translation with DeepL, Google Translate, and Azure
|
|
121
|
+
- **📊 Analytics & SEO** - Built-in SEO optimization and content analytics
|
|
122
|
+
- **⚡ Performance Optimization** - Advanced caching, CDN integration, and performance monitoring
|
|
123
|
+
- **🏢 Multisite Management** - Manage multiple sites from a single dashboard with shared resources
|
|
124
|
+
- **💼 Professional Support** - Dedicated support, training, and consultation services
|
|
125
|
+
|
|
126
|
+
[Explore all the pro extensions](https://apostrophecms.com/extensions?autocomplete=&license=assembly&license=pro) and [sign up](https://app.apostrophecms.com/login) for a Pro license in our self-service Apostrophe Workspaces, or [contact us](https://apostrophecms.com/contact-us) to learn about licensing and support options.
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
ApostropheCMS is open source software licensed under the [MIT License](https://github.com/apostrophecms/apostrophe/blob/main/LICENSE.md). This means you're free to use, modify, and distribute it for both personal and commercial projects.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
<div align="center">
|
|
135
|
+
<p>
|
|
136
|
+
<strong>Ready to build something amazing?</strong><br>
|
|
137
|
+
<a href="https://docs.apostrophecms.org/">Get started with our documentation</a> or <a href="https://apostrophecms.com/contact-us">talk to our team</a>
|
|
138
|
+
</p>
|
|
139
|
+
<p>
|
|
140
|
+
<em>Built with ❤️ by the <a href="https://apostrophecms.com">ApostropheCMS team</a></em>
|
|
141
|
+
</p>
|
|
142
|
+
</div>
|
|
@@ -111,8 +111,7 @@ module.exports = {
|
|
|
111
111
|
// (the slug-taken route does that)
|
|
112
112
|
if (self.options.prettyUrls && file.attachment) {
|
|
113
113
|
const { extension } = file.attachment;
|
|
114
|
-
|
|
115
|
-
file._url = `${baseUrl}${self.options.prettyUrlDir}/${file.slug.replace(self.options.slugPrefix || '', '')}.${extension}`;
|
|
114
|
+
file._url = `${req.prefix}${self.options.prettyUrlDir}/${file.slug.replace(self.options.slugPrefix || '', '')}.${extension}`;
|
|
116
115
|
file.attachment._prettyUrl = file._url;
|
|
117
116
|
} else {
|
|
118
117
|
file._url = self.apos.attachment.url(file.attachment);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apostrophe",
|
|
3
|
-
"version": "4.28.
|
|
3
|
+
"version": "4.28.1",
|
|
4
4
|
"description": "The Apostrophe Content Management System.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -119,12 +119,12 @@
|
|
|
119
119
|
"webpack": "^5.72.0",
|
|
120
120
|
"webpack-merge": "^5.7.3",
|
|
121
121
|
"xregexp": "^2.0.0",
|
|
122
|
-
"boring": "^1.1.1",
|
|
123
|
-
"postcss-viewport-to-container-toggle": "^2.3.0",
|
|
124
|
-
"sanitize-html": "^2.17.2",
|
|
125
122
|
"@apostrophecms/emulate-mongo-3-driver": "^1.0.6",
|
|
123
|
+
"express-cache-on-demand": "^1.0.4",
|
|
126
124
|
"broadband": "^1.1.0",
|
|
127
|
-
"
|
|
125
|
+
"sanitize-html": "^2.17.2",
|
|
126
|
+
"postcss-viewport-to-container-toggle": "^2.3.0",
|
|
127
|
+
"boring": "^1.1.1"
|
|
128
128
|
},
|
|
129
129
|
"devDependencies": {
|
|
130
130
|
"eslint": "^9.39.1",
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"../..": {
|
|
8
|
-
"version": "4.
|
|
8
|
+
"version": "4.28.0",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@apostrophecms/emulate-mongo-3-driver": "workspace:^",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"tiny-emitter": "^2.1.0",
|
|
101
101
|
"tough-cookie": "^4.0.0",
|
|
102
102
|
"underscore.string": "^3.3.4",
|
|
103
|
-
"uploadfs": "
|
|
103
|
+
"uploadfs": "workspace:^",
|
|
104
104
|
"void-elements": "^3.1.0",
|
|
105
105
|
"vue": "^3.5.20",
|
|
106
106
|
"vue-advanced-cropper": "^2.8.8",
|
package/test/files.js
CHANGED
|
@@ -133,3 +133,132 @@ describe('Files', function() {
|
|
|
133
133
|
});
|
|
134
134
|
|
|
135
135
|
});
|
|
136
|
+
|
|
137
|
+
describe('Files with i18n locale prefixes', function() {
|
|
138
|
+
|
|
139
|
+
let apos;
|
|
140
|
+
|
|
141
|
+
const mockFiles = [
|
|
142
|
+
{
|
|
143
|
+
type: '@apostrophecms/file',
|
|
144
|
+
slug: 'file-locale-test',
|
|
145
|
+
visibility: 'public',
|
|
146
|
+
attachment: {
|
|
147
|
+
type: 'attachment',
|
|
148
|
+
_id: 'testid-locale',
|
|
149
|
+
name: 'localename',
|
|
150
|
+
extension: 'pdf',
|
|
151
|
+
data: 'I am a fake localized PDF'
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
this.timeout(t.timeout);
|
|
157
|
+
|
|
158
|
+
after(async function() {
|
|
159
|
+
return t.destroy(apos);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
before(async function() {
|
|
163
|
+
this.timeout(t.timeout);
|
|
164
|
+
this.slow(2000);
|
|
165
|
+
|
|
166
|
+
apos = await t.create({
|
|
167
|
+
root: module,
|
|
168
|
+
modules: {
|
|
169
|
+
'@apostrophecms/file': {
|
|
170
|
+
options: {
|
|
171
|
+
prettyUrls: true
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
'@apostrophecms/i18n': {
|
|
175
|
+
options: {
|
|
176
|
+
locales: {
|
|
177
|
+
en: {},
|
|
178
|
+
fr: {
|
|
179
|
+
prefix: '/fr'
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
apos.baseUrl = apos.http.getBase();
|
|
188
|
+
|
|
189
|
+
try {
|
|
190
|
+
await apos.doc.db.deleteMany({ type: '@apostrophecms/file' });
|
|
191
|
+
try {
|
|
192
|
+
fs.mkdirSync(`${__dirname}/public/uploads/attachments`);
|
|
193
|
+
} catch (e) {
|
|
194
|
+
// May already exist
|
|
195
|
+
}
|
|
196
|
+
} catch (e) {
|
|
197
|
+
assert(false);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Insert the file doc in the default (en) locale
|
|
201
|
+
const req = apos.task.getReq();
|
|
202
|
+
for (const file of mockFiles) {
|
|
203
|
+
await apos.file.insert(req, file);
|
|
204
|
+
const {
|
|
205
|
+
_id, name, extension, data
|
|
206
|
+
} = file.attachment;
|
|
207
|
+
fs.writeFileSync(
|
|
208
|
+
`${__dirname}/public/uploads/attachments/${_id}-${name}.${extension}`,
|
|
209
|
+
data
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Localize the file to FR and give it a distinct French slug
|
|
214
|
+
const enFile = await apos.file.find(req, {}).toObject();
|
|
215
|
+
await apos.file.localize(req, enFile, 'fr');
|
|
216
|
+
const frReq = apos.task.getReq({ locale: 'fr' });
|
|
217
|
+
const frDraft = await apos.file.find(frReq.clone({ mode: 'draft' }), {}).toObject();
|
|
218
|
+
frDraft.slug = 'file-test-locale-fr';
|
|
219
|
+
frDraft.title = 'Test Locale FR';
|
|
220
|
+
await apos.file.update(frReq, frDraft);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('should include locale prefix and FR slug in pretty URL for FR locale', async function() {
|
|
224
|
+
const frReq = apos.task.getAnonReq({ locale: 'fr' });
|
|
225
|
+
const files = await apos.file.find(frReq).toArray();
|
|
226
|
+
assert.strictEqual(files.length, 1);
|
|
227
|
+
const file = files[0];
|
|
228
|
+
const attachment = apos.attachment.first(file);
|
|
229
|
+
const url = apos.attachment.url(attachment);
|
|
230
|
+
assert(url);
|
|
231
|
+
// Must use the FR slug (test-locale-fr) and include /fr/ prefix
|
|
232
|
+
assert.strictEqual(
|
|
233
|
+
url,
|
|
234
|
+
`${apos.http.getBase()}/fr/files/test-locale-fr.${attachment.extension}`
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should serve the file at the locale-prefixed pretty URL with FR slug', async function() {
|
|
239
|
+
const frReq = apos.task.getAnonReq({ locale: 'fr' });
|
|
240
|
+
const files = await apos.file.find(frReq).toArray();
|
|
241
|
+
assert.strictEqual(files.length, 1);
|
|
242
|
+
const file = files[0];
|
|
243
|
+
const attachment = apos.attachment.first(file);
|
|
244
|
+
const url = apos.attachment.url(attachment);
|
|
245
|
+
// Verify the locale-prefixed, FR-slug URL actually serves the content
|
|
246
|
+
const body = await apos.http.get(url);
|
|
247
|
+
assert.strictEqual(body, attachment.data);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should use EN slug without locale prefix for default EN locale', async function() {
|
|
251
|
+
const enReq = apos.task.getAnonReq({ locale: 'en' });
|
|
252
|
+
const files = await apos.file.find(enReq).toArray();
|
|
253
|
+
assert.strictEqual(files.length, 1);
|
|
254
|
+
const file = files[0];
|
|
255
|
+
const attachment = apos.attachment.first(file);
|
|
256
|
+
const url = apos.attachment.url(attachment);
|
|
257
|
+
assert(url);
|
|
258
|
+
// EN keeps its original slug (locale-test) with no locale prefix
|
|
259
|
+
assert.strictEqual(
|
|
260
|
+
url,
|
|
261
|
+
`${apos.http.getBase()}/files/locale-test.${attachment.extension}`
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
});
|