summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/models/concerns/file_attachment.rb3
-rw-r--r--app/views/assets/edit.html.erb18
-rw-r--r--doc/20260626025705_add_search_vector_to_page_translations.rb.pending47
-rw-r--r--doc/INSTALL.md341
-rw-r--r--doc/rc.d_cccms7
-rw-r--r--public/stylesheets/admin.css5
-rw-r--r--test/controllers/users_controller_test.rb6
-rw-r--r--test/test_helper.rb4
8 files changed, 376 insertions, 55 deletions
diff --git a/app/models/concerns/file_attachment.rb b/app/models/concerns/file_attachment.rb
index 5483de5..e9acda6 100644
--- a/app/models/concerns/file_attachment.rb
+++ b/app/models/concerns/file_attachment.rb
@@ -60,6 +60,9 @@ module FileAttachment
60 uploaded_file = @pending_upload 60 uploaded_file = @pending_upload
61 @pending_upload = nil 61 @pending_upload = nil
62 62
63 old_dir = Rails.root.join("public", "system", "uploads", id.to_s)
64 FileUtils.rm_rf(old_dir) if Dir.exist?(old_dir)
65
63 original_path = file_path(:original) 66 original_path = file_path(:original)
64 FileUtils.mkdir_p(File.dirname(original_path)) 67 FileUtils.mkdir_p(File.dirname(original_path))
65 FileUtils.cp(uploaded_file.tempfile.path, original_path) 68 FileUtils.cp(uploaded_file.tempfile.path, original_path)
diff --git a/app/views/assets/edit.html.erb b/app/views/assets/edit.html.erb
index e65d600..f198600 100644
--- a/app/views/assets/edit.html.erb
+++ b/app/views/assets/edit.html.erb
@@ -1,12 +1,24 @@
1<h1>Editing asset</h1> 1<h1>Editing asset</h1>
2 2
3<%= form_for(@asset) do |f| %> 3<%= form_for(@asset, html: { multipart: true }) do |f| %>
4 <%= form_error_messages(f) %> 4 <%= form_error_messages(f) %>
5 5
6 <% if @asset.upload.present? %>
7 <p>
8 <strong>Current file:</strong>
9 <%= @asset.upload.url %>
10 (<%= number_to_human_size(@asset.upload.size) %>)
11 </p>
12 <% end %>
13
14 <p>
15 <label>Replace file:</label><br>
16 <%= f.file_field :upload %>
17 </p>
18
6 <p> 19 <p>
7 <%= f.submit 'Update' %> 20 <%= f.submit 'Update' %>
8 </p> 21 </p>
9<% end %> 22<% end %>
10 23
11<%= link_to 'Show', @asset %> | 24<%= link_to 'Show', @asset %> | <%= link_to 'Back', assets_path %>
12<%= link_to 'Back', assets_path %>
diff --git a/doc/20260626025705_add_search_vector_to_page_translations.rb.pending b/doc/20260626025705_add_search_vector_to_page_translations.rb.pending
deleted file mode 100644
index 0747637..0000000
--- a/doc/20260626025705_add_search_vector_to_page_translations.rb.pending
+++ /dev/null
@@ -1,47 +0,0 @@
1class AddSearchVectorToPageTranslations < ActiveRecord::Migration[7.2]
2 def up
3 add_column :page_translations, :search_vector, :tsvector
4
5 execute <<~SQL
6 UPDATE page_translations
7 SET search_vector = to_tsvector(
8 'simple',
9 coalesce(title, '') || ' ' ||
10 coalesce(abstract, '') || ' ' ||
11 coalesce(body, '')
12 )
13 SQL
14
15 add_index :page_translations, :search_vector,
16 using: :gin,
17 name: 'index_page_translations_on_search_vector'
18
19 execute <<~SQL
20 CREATE OR REPLACE FUNCTION page_translations_search_vector_update()
21 RETURNS trigger AS $$
22 BEGIN
23 NEW.search_vector := to_tsvector(
24 'simple',
25 coalesce(NEW.title, '') || ' ' ||
26 coalesce(NEW.abstract, '') || ' ' ||
27 coalesce(NEW.body, '')
28 );
29 RETURN NEW;
30 END;
31 $$ LANGUAGE plpgsql;
32
33 CREATE TRIGGER page_translations_search_vector_trigger
34 BEFORE INSERT OR UPDATE ON page_translations
35 FOR EACH ROW EXECUTE PROCEDURE page_translations_search_vector_update();
36 SQL
37 end
38
39 def down
40 execute <<~SQL
41 DROP TRIGGER IF EXISTS page_translations_search_vector_trigger ON page_translations;
42 DROP FUNCTION IF EXISTS page_translations_search_vector_update();
43 SQL
44 remove_index :page_translations, name: 'index_page_translations_on_search_vector'
45 remove_column :page_translations, :search_vector
46 end
47end
diff --git a/doc/INSTALL.md b/doc/INSTALL.md
new file mode 100644
index 0000000..8056f7c
--- /dev/null
+++ b/doc/INSTALL.md
@@ -0,0 +1,341 @@
1# CCCMS Installation Guide
2
3This document covers the non-obvious steps required to install the CCCMS
4stack on a fresh FreeBSD jail. It assumes a FreeBSD 14.x base jail with
5network access and a working pkg repository.
6
7## 1. Install packages
8
9```sh
10pkg install gmake pkgconf curl gnupg git autoconf automake libtool bash \
11 readline libyaml libffi gdbm libxml2 libxslt libical \
12 postgresql16-server postgresql16-client \
13 ImageMagick7-nox11 node vim
14```
15
16Note: the package is `ImageMagick7-nox11`, not `ImageMagick-nox11`. The
17nox11 variant avoids pulling in the entire X11 dependency chain.
18
19## 2. Enable sysvipc for the jail
20
21PostgreSQL uses System V shared memory for inter-process communication.
22On the host, the jail must have sysvipc enabled. In `/etc/jail.conf` or
23the jail's ezjail configuration:
24
25 `allow.sysvipc = 1;`
26
27Restart the jail after making this change. Without it, PostgreSQL will
28fail to start with a shared memory error.
29
30## 3. Enable and initialise PostgreSQL
31
32```sh
33# Enable PostgreSQL in rc.conf
34echo 'postgresql_enable="YES"' >> /etc/rc.conf
35
36# Initialise the database cluster
37service postgresql initdb
38
39# Start PostgreSQL
40service postgresql start
41```
42
43## 4. Create database roles and set permissions
44
45```sh
46psql -U postgres postgres
47```
48
49```sql
50CREATE ROLE rails WITH LOGIN PASSWORD 'your-password-here';
51ALTER ROLE rails CREATEDB;
52```
53
54`CREATEDB` is required for the Rails test suite to create and drop the
55test database between runs.
56
57## 5. Create databases
58
59```sql
60CREATE DATABASE cccms_production OWNER rails ENCODING 'UTF8'
61 LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0;
62CREATE DATABASE cccms_dev OWNER rails ENCODING 'UTF8'
63 LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0;
64CREATE DATABASE psql_test OWNER rails ENCODING 'UTF8'
65 LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0;
66```
67
68`TEMPLATE template0` is required when specifying a non-default locale.
69
70## 6. Restore the database dump (or start empty)
71
72If restoring from a pg_dump:
73
74```sh
75pg_restore -U postgres -d cccms_production \
76 --no-owner --no-acl /path/to/cccms_production.dump
77
78pg_restore -U postgres -d cccms_dev \
79 --no-owner --no-acl /path/to/cccms_production.dump
80```
81
82Expected harmless warnings:
83- `schema "public" already exists` — benign, ignore
84- `array_accum aggregate` failure — dead code, ignore
85
86Transfer ownership to the rails user (REASSIGN OWNED does not work on
87PostgreSQL 15+ due to system object protection):
88
89```sh
90psql -U postgres cccms_production
91```
92
93```sql
94DO $$
95DECLARE
96 obj RECORD;
97BEGIN
98 FOR obj IN
99 SELECT tablename FROM pg_tables WHERE schemaname = 'public'
100 LOOP
101 EXECUTE 'ALTER TABLE public.' || quote_ident(obj.tablename) ||
102 ' OWNER TO rails';
103 END LOOP;
104 FOR obj IN
105 SELECT sequence_name FROM information_schema.sequences
106 WHERE sequence_schema = 'public'
107 LOOP
108 EXECUTE 'ALTER SEQUENCE public.' || quote_ident(obj.sequence_name) ||
109 ' OWNER TO rails';
110 END LOOP;
111 FOR obj IN
112 SELECT viewname FROM pg_views WHERE schemaname = 'public'
113 LOOP
114 EXECUTE 'ALTER VIEW public.' || quote_ident(obj.viewname) ||
115 ' OWNER TO rails';
116 END LOOP;
117END $$;
118
119ALTER SCHEMA public OWNER TO rails;
120```
121
122Repeat for `cccms_dev`.
123
124## 7. Clone the repository
125
126```sh
127cd /usr/local/www
128git clone https://github.com/erdgeist/cccms.git
129cd cccms
130git checkout rails-upgrade
131```
132
133## 8. Copy assets (optional but recommended)
134
135The `public/system/uploads/` directory contains all uploaded files
136referenced by the database. Without it, images and attachments will be
137missing throughout the site.
138
139```sh
140# On the source system:
141tar -czf /tmp/cccms_uploads.tar.gz -C /usr/local/www cccms/public/system
142
143# On the new system:
144tar -xzf /path/to/cccms_uploads.tar.gz -C /usr/local/www
145```
146
147## 9. Copy gitignored config files
148
149These files are not in the repository and must be copied or created:
150
151```sh
152# Required:
153config/database.yml
154config/initializers/secret_token.rb
155
156# If used:
157config/initializers/exception_notification.rb
158/usr/local/etc/unicorn.rb
159```
160
161`database.yml` template:
162
163```yaml
164development:
165 adapter: postgresql
166 encoding: unicode
167 database: cccms_dev
168 pool: 5
169 username: rails
170 password: your-password-here
171
172test:
173 adapter: postgresql
174 encoding: UTF8
175 database: psql_test
176 username: rails
177 password:
178
179production:
180 adapter: postgresql
181 encoding: unicode
182 database: cccms_production
183 pool: 5
184 username: rails
185 password: your-password-here
186```
187
188## 10. Install rvm
189
190rvm 1.29.12 is the latest formal release as of mid-2026. Download and
191verify before installing:
192
193```sh
194curl -L https://github.com/rvm/rvm/releases/download/1.29.12/1.29.12.tar.gz \
195 -o /tmp/rvm-1.29.12.tar.gz
196curl -L https://github.com/rvm/rvm/releases/download/1.29.12/1.29.12.tar.gz.asc \
197 -o /tmp/rvm-1.29.12.tar.gz.asc
198
199gpg --keyserver hkps://keys.openpgp.org \
200 --recv-keys 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
201
202gpg --verify /tmp/rvm-1.29.12.tar.gz.asc /tmp/rvm-1.29.12.tar.gz
203```
204
205If verification passes:
206
207```sh
208tar -xzf /tmp/rvm-1.29.12.tar.gz -C /tmp
209bash /tmp/rvm-1.29.12/install --auto-dotfiles
210source /usr/local/rvm/scripts/rvm
211```
212
213The installed rvm ships with a stale known-versions list that only goes
214to Ruby 3.0.0. Update it immediately:
215
216```sh
217curl -L https://raw.githubusercontent.com/rvm/rvm/master/config/known \
218 -o /usr/local/rvm/config/known
219rvm list known | grep '^\[ruby-\]3\.'
220```
221
222Should now show Ruby 3.2.x and later.
223
224## 11. Install Ruby and create gemset
225
226```sh
227source /usr/local/rvm/scripts/rvm
228rvm install 3.2.11 --autolibs=read-only --with-opt-dir=/usr/local
229rvm use 3.2.11
230rvm gemset create rails7-upgrade
231rvm use 3.2.11@rails7-upgrade
232```
233
234The `.ruby-version` and `.ruby-gemset` files in the project root will
235cause rvm to switch automatically when entering the project directory.
236
237## 12. Install bundler and gems
238
239```sh
240gem install bundler
241cd /usr/local/www/cccms
242export MAKE=gmake
243bundle install 2>&1 | tee /tmp/bundle_install.log
244```
245
246`MAKE=gmake` is required because FreeBSD's native make (BSD make) uses
247different `-j` syntax than native gems expect. Without it, several native
248gem compilations will fail.
249
250## 13. Run migrations
251
252```sh
253bundle exec rails db:migrate
254```
255
256If restoring an existing database, first insert fake migration versions
257to prevent re-running migrations that were applied to the old schema:
258
259```sql
260INSERT INTO schema_migrations (version) VALUES
261 ('20260624035149'), ('20260624035150'), ('20260624035151'),
262 ('20260624035152'), ('20260624035153'),
263 ('20260625031409')
264ON CONFLICT DO NOTHING;
265```
266
267Then run `db:migrate` to apply only new migrations.
268
269To enable full-text search (requires PostgreSQL 10+ with plpgsql):
270
271```sh
272mv doc/20260626025705_add_search_vector_to_page_translations.rb.pending \
273 db/migrate/20260626025705_add_search_vector_to_page_translations.rb
274bundle exec rails db:migrate
275```
276
277## 14. Compile admin assets
278
279```sh
280bundle exec rails assets:precompile
281```
282
283This compiles the admin JavaScript bundle (jQuery, jQuery UI, hotkeys)
284into `public/assets/`. Required for the admin interface to work. Must be
285re-run after any changes to `app/assets/javascripts/admin_bundle.js`.
286
287## 15. Run tests (optional but recommended)
288
289```sh
290bundle exec rake test
291```
292
293Expected result: 129 runs, ~339 assertions, 3 failures, 0 errors.
294The 3 failures are pre-existing and documented in the handover document.
295
296## 16. Start the server
297
298Development:
299
300```sh
301bundle exec rails server -p 3000 -b 0.0.0.0 -e development
302```
303
304Note: `-b 0.0.0.0` is required — `localhost` does not resolve inside
305a FreeBSD jail.
306
307Production (unicorn):
308
309```sh
310/usr/local/rvm/gems/ruby-3.2.11@rails7-upgrade/wrappers/unicorn \
311 -c /usr/local/etc/unicorn.rb -E production -D
312```
313
314The rc.d script at `/etc/rc.d/cccms` needs updating from `unicorn_rails`
315to `unicorn` before use — see the handover document for details.
316
317## Known Gotchas
318
319**sysvipc:** PostgreSQL will fail silently or with a cryptic error if
320sysvipc is not enabled for the jail. Enable it on the host before starting
321PostgreSQL.
322
323**MAKE=gmake:** Native gem compilation fails without this. Set it before
324every `bundle install` or add to your shell profile.
325
326**rvm known versions:** rvm 1.29.12 ships with a stale `config/known` that
327only lists Ruby up to 3.0.0. Always update from master after installing rvm.
328
329**ImageMagick 7:** The `convert` command is deprecated; use `magick convert`.
330The `file_attachment.rb` concern needs updating before production use.
331
332**pg_hba.conf:** The default FreeBSD PostgreSQL configuration uses `trust`
333for local Unix socket connections, which is sufficient for the application.
334No changes needed unless TCP connections are required.
335
336**assets:precompile:** Must be run after checkout and after any changes to
337admin JavaScript. The compiled files in `public/assets/` are gitignored.
338
339**chaos_calendar include path:** On FreeBSD 14.x with libical 3.0.20+,
340the include path is `<libical/ical.h>` not `<ical.h>`. This is already
341fixed in the `erdgeist-ruby1.9` branch.
diff --git a/doc/rc.d_cccms b/doc/rc.d_cccms
index 62e8bde..8404bc7 100644
--- a/doc/rc.d_cccms
+++ b/doc/rc.d_cccms
@@ -25,6 +25,13 @@ required_dirs="${cccms_dir}"
25extra_commands="reload" 25extra_commands="reload"
26sig_reload="USR2" 26sig_reload="USR2"
27 27
28cccms_prestart()
29{
30 mkdir -p /usr/local/www/cccms/tmp/pids /var/log
31 touch /var/log/unicorn.stderr.log
32 chown www:www /var/log/unicorn.stderr.log
33}
34
28export PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin 35export PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
29export RAILS_ENV=production 36export RAILS_ENV=production
30export HOME=/root 37export HOME=/root
diff --git a/public/stylesheets/admin.css b/public/stylesheets/admin.css
index 1a29eff..4b08356 100644
--- a/public/stylesheets/admin.css
+++ b/public/stylesheets/admin.css
@@ -131,11 +131,12 @@ form.button_to input[type="submit"] {
131 font: inherit; 131 font: inherit;
132 color: inherit; 132 color: inherit;
133 cursor: pointer; 133 cursor: pointer;
134 text-decoration: underline; 134 text-decoration: none
135} 135}
136 136
137form.button_to input[type="submit"]:hover { 137form.button_to input[type="submit"]:hover {
138 color: inherit; 138 color: #ffffff;
139 background-color: #ff9600;
139} 140}
140 141
141#admin_wizard { 142#admin_wizard {
diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb
index 3ace95c..d37b428 100644
--- a/test/controllers/users_controller_test.rb
+++ b/test/controllers/users_controller_test.rb
@@ -8,13 +8,13 @@ class UsersControllerTest < ActionController::TestCase
8 assert_response :success 8 assert_response :success
9 assert_select "a", { :count => 0, :text => "Destroy" } 9 assert_select "a", { :count => 0, :text => "Destroy" }
10 end 10 end
11 11
12 test "get index as admin user renders admin partial" do 12 test "get index as admin user renders admin partial" do
13 login_as :aaron 13 login_as :aaron
14 get :index 14 get :index
15 assert_response :success 15 assert_response :success
16 assert_select "a", "destroy" 16 assert_select "input[type=submit][value=destroy]"
17 assert_select "a", "show", "Show Link is missing" 17 assert_select "a", "show"
18 end 18 end
19 19
20 test "get new when logged in as admin" do 20 test "get new when logged in as admin" do
diff --git a/test/test_helper.rb b/test/test_helper.rb
index e7b15da..d438bb5 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -47,6 +47,10 @@ class ActiveSupport::TestCase
47 47
48 # Add more helper methods to be used by all tests here... 48 # Add more helper methods to be used by all tests here...
49 49
50 setup do
51 I18n.locale = I18n.default_locale
52 end
53
50 def create_node_with_published_page 54 def create_node_with_published_page
51 node = create_node_with_draft 55 node = create_node_with_draft
52 draft = node.draft 56 draft = node.draft