From b4f850e97aeb12369399d8e1ab354f66d3b88e40 Mon Sep 17 00:00:00 2001 From: erdgeist Date: Sat, 27 Jun 2026 02:39:55 +0200 Subject: Stage 6 click-testing fixes and production setup - file_attachment.rb: delete old upload directory before writing replacement files; fixes orphaned variants when filename or mime type changes - assets/edit.html.erb: add file upload field and current file display; the form was previously empty and non-functional - admin.css: fix button_to hover styling; buttons now show orange hover to signal interactivity - test/controllers/users_controller_test.rb: assert input[type=submit] not anchor tag for destroy action (button_to change) - test/test_helper.rb: add I18n.locale reset in setup block - doc/rc.d_cccms: fix cccms_chdir, add start_precmd for log/pid dirs, PATH export for bash wrapper, user/pid/tcp_nopush unicorn fixes - doc/INSTALL.md: new installation guide covering all non-obvious steps - Remove parked search migration from doc/ (now in db/migrate/) --- app/models/concerns/file_attachment.rb | 3 + app/views/assets/edit.html.erb | 18 +- ...d_search_vector_to_page_translations.rb.pending | 47 --- doc/INSTALL.md | 341 +++++++++++++++++++++ doc/rc.d_cccms | 7 + public/stylesheets/admin.css | 5 +- test/controllers/users_controller_test.rb | 6 +- test/test_helper.rb | 4 + 8 files changed, 376 insertions(+), 55 deletions(-) delete mode 100644 doc/20260626025705_add_search_vector_to_page_translations.rb.pending create mode 100644 doc/INSTALL.md 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 uploaded_file = @pending_upload @pending_upload = nil + old_dir = Rails.root.join("public", "system", "uploads", id.to_s) + FileUtils.rm_rf(old_dir) if Dir.exist?(old_dir) + original_path = file_path(:original) FileUtils.mkdir_p(File.dirname(original_path)) 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 @@

Editing asset

-<%= form_for(@asset) do |f| %> +<%= form_for(@asset, html: { multipart: true }) do |f| %> <%= form_error_messages(f) %> + <% if @asset.upload.present? %> +

+ Current file: + <%= @asset.upload.url %> + (<%= number_to_human_size(@asset.upload.size) %>) +

+ <% end %> + +

+
+ <%= f.file_field :upload %> +

+

<%= f.submit 'Update' %>

<% end %> -<%= link_to 'Show', @asset %> | -<%= link_to 'Back', assets_path %> +<%= link_to 'Show', @asset %> | <%= 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 @@ -class AddSearchVectorToPageTranslations < ActiveRecord::Migration[7.2] - def up - add_column :page_translations, :search_vector, :tsvector - - execute <<~SQL - UPDATE page_translations - SET search_vector = to_tsvector( - 'simple', - coalesce(title, '') || ' ' || - coalesce(abstract, '') || ' ' || - coalesce(body, '') - ) - SQL - - add_index :page_translations, :search_vector, - using: :gin, - name: 'index_page_translations_on_search_vector' - - execute <<~SQL - CREATE OR REPLACE FUNCTION page_translations_search_vector_update() - RETURNS trigger AS $$ - BEGIN - NEW.search_vector := to_tsvector( - 'simple', - coalesce(NEW.title, '') || ' ' || - coalesce(NEW.abstract, '') || ' ' || - coalesce(NEW.body, '') - ); - RETURN NEW; - END; - $$ LANGUAGE plpgsql; - - CREATE TRIGGER page_translations_search_vector_trigger - BEFORE INSERT OR UPDATE ON page_translations - FOR EACH ROW EXECUTE PROCEDURE page_translations_search_vector_update(); - SQL - end - - def down - execute <<~SQL - DROP TRIGGER IF EXISTS page_translations_search_vector_trigger ON page_translations; - DROP FUNCTION IF EXISTS page_translations_search_vector_update(); - SQL - remove_index :page_translations, name: 'index_page_translations_on_search_vector' - remove_column :page_translations, :search_vector - end -end 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 @@ +# CCCMS Installation Guide + +This document covers the non-obvious steps required to install the CCCMS +stack on a fresh FreeBSD jail. It assumes a FreeBSD 14.x base jail with +network access and a working pkg repository. + +## 1. Install packages + +```sh +pkg install gmake pkgconf curl gnupg git autoconf automake libtool bash \ + readline libyaml libffi gdbm libxml2 libxslt libical \ + postgresql16-server postgresql16-client \ + ImageMagick7-nox11 node vim +``` + +Note: the package is `ImageMagick7-nox11`, not `ImageMagick-nox11`. The +nox11 variant avoids pulling in the entire X11 dependency chain. + +## 2. Enable sysvipc for the jail + +PostgreSQL uses System V shared memory for inter-process communication. +On the host, the jail must have sysvipc enabled. In `/etc/jail.conf` or +the jail's ezjail configuration: + + `allow.sysvipc = 1;` + +Restart the jail after making this change. Without it, PostgreSQL will +fail to start with a shared memory error. + +## 3. Enable and initialise PostgreSQL + +```sh +# Enable PostgreSQL in rc.conf +echo 'postgresql_enable="YES"' >> /etc/rc.conf + +# Initialise the database cluster +service postgresql initdb + +# Start PostgreSQL +service postgresql start +``` + +## 4. Create database roles and set permissions + +```sh +psql -U postgres postgres +``` + +```sql +CREATE ROLE rails WITH LOGIN PASSWORD 'your-password-here'; +ALTER ROLE rails CREATEDB; +``` + +`CREATEDB` is required for the Rails test suite to create and drop the +test database between runs. + +## 5. Create databases + +```sql +CREATE DATABASE cccms_production OWNER rails ENCODING 'UTF8' + LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0; +CREATE DATABASE cccms_dev OWNER rails ENCODING 'UTF8' + LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0; +CREATE DATABASE psql_test OWNER rails ENCODING 'UTF8' + LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0; +``` + +`TEMPLATE template0` is required when specifying a non-default locale. + +## 6. Restore the database dump (or start empty) + +If restoring from a pg_dump: + +```sh +pg_restore -U postgres -d cccms_production \ + --no-owner --no-acl /path/to/cccms_production.dump + +pg_restore -U postgres -d cccms_dev \ + --no-owner --no-acl /path/to/cccms_production.dump +``` + +Expected harmless warnings: +- `schema "public" already exists` — benign, ignore +- `array_accum aggregate` failure — dead code, ignore + +Transfer ownership to the rails user (REASSIGN OWNED does not work on +PostgreSQL 15+ due to system object protection): + +```sh +psql -U postgres cccms_production +``` + +```sql +DO $$ +DECLARE + obj RECORD; +BEGIN + FOR obj IN + SELECT tablename FROM pg_tables WHERE schemaname = 'public' + LOOP + EXECUTE 'ALTER TABLE public.' || quote_ident(obj.tablename) || + ' OWNER TO rails'; + END LOOP; + FOR obj IN + SELECT sequence_name FROM information_schema.sequences + WHERE sequence_schema = 'public' + LOOP + EXECUTE 'ALTER SEQUENCE public.' || quote_ident(obj.sequence_name) || + ' OWNER TO rails'; + END LOOP; + FOR obj IN + SELECT viewname FROM pg_views WHERE schemaname = 'public' + LOOP + EXECUTE 'ALTER VIEW public.' || quote_ident(obj.viewname) || + ' OWNER TO rails'; + END LOOP; +END $$; + +ALTER SCHEMA public OWNER TO rails; +``` + +Repeat for `cccms_dev`. + +## 7. Clone the repository + +```sh +cd /usr/local/www +git clone https://github.com/erdgeist/cccms.git +cd cccms +git checkout rails-upgrade +``` + +## 8. Copy assets (optional but recommended) + +The `public/system/uploads/` directory contains all uploaded files +referenced by the database. Without it, images and attachments will be +missing throughout the site. + +```sh +# On the source system: +tar -czf /tmp/cccms_uploads.tar.gz -C /usr/local/www cccms/public/system + +# On the new system: +tar -xzf /path/to/cccms_uploads.tar.gz -C /usr/local/www +``` + +## 9. Copy gitignored config files + +These files are not in the repository and must be copied or created: + +```sh +# Required: +config/database.yml +config/initializers/secret_token.rb + +# If used: +config/initializers/exception_notification.rb +/usr/local/etc/unicorn.rb +``` + +`database.yml` template: + +```yaml +development: + adapter: postgresql + encoding: unicode + database: cccms_dev + pool: 5 + username: rails + password: your-password-here + +test: + adapter: postgresql + encoding: UTF8 + database: psql_test + username: rails + password: + +production: + adapter: postgresql + encoding: unicode + database: cccms_production + pool: 5 + username: rails + password: your-password-here +``` + +## 10. Install rvm + +rvm 1.29.12 is the latest formal release as of mid-2026. Download and +verify before installing: + +```sh +curl -L https://github.com/rvm/rvm/releases/download/1.29.12/1.29.12.tar.gz \ + -o /tmp/rvm-1.29.12.tar.gz +curl -L https://github.com/rvm/rvm/releases/download/1.29.12/1.29.12.tar.gz.asc \ + -o /tmp/rvm-1.29.12.tar.gz.asc + +gpg --keyserver hkps://keys.openpgp.org \ + --recv-keys 7D2BAF1CF37B13E2069D6956105BD0E739499BDB + +gpg --verify /tmp/rvm-1.29.12.tar.gz.asc /tmp/rvm-1.29.12.tar.gz +``` + +If verification passes: + +```sh +tar -xzf /tmp/rvm-1.29.12.tar.gz -C /tmp +bash /tmp/rvm-1.29.12/install --auto-dotfiles +source /usr/local/rvm/scripts/rvm +``` + +The installed rvm ships with a stale known-versions list that only goes +to Ruby 3.0.0. Update it immediately: + +```sh +curl -L https://raw.githubusercontent.com/rvm/rvm/master/config/known \ + -o /usr/local/rvm/config/known +rvm list known | grep '^\[ruby-\]3\.' +``` + +Should now show Ruby 3.2.x and later. + +## 11. Install Ruby and create gemset + +```sh +source /usr/local/rvm/scripts/rvm +rvm install 3.2.11 --autolibs=read-only --with-opt-dir=/usr/local +rvm use 3.2.11 +rvm gemset create rails7-upgrade +rvm use 3.2.11@rails7-upgrade +``` + +The `.ruby-version` and `.ruby-gemset` files in the project root will +cause rvm to switch automatically when entering the project directory. + +## 12. Install bundler and gems + +```sh +gem install bundler +cd /usr/local/www/cccms +export MAKE=gmake +bundle install 2>&1 | tee /tmp/bundle_install.log +``` + +`MAKE=gmake` is required because FreeBSD's native make (BSD make) uses +different `-j` syntax than native gems expect. Without it, several native +gem compilations will fail. + +## 13. Run migrations + +```sh +bundle exec rails db:migrate +``` + +If restoring an existing database, first insert fake migration versions +to prevent re-running migrations that were applied to the old schema: + +```sql +INSERT INTO schema_migrations (version) VALUES + ('20260624035149'), ('20260624035150'), ('20260624035151'), + ('20260624035152'), ('20260624035153'), + ('20260625031409') +ON CONFLICT DO NOTHING; +``` + +Then run `db:migrate` to apply only new migrations. + +To enable full-text search (requires PostgreSQL 10+ with plpgsql): + +```sh +mv doc/20260626025705_add_search_vector_to_page_translations.rb.pending \ + db/migrate/20260626025705_add_search_vector_to_page_translations.rb +bundle exec rails db:migrate +``` + +## 14. Compile admin assets + +```sh +bundle exec rails assets:precompile +``` + +This compiles the admin JavaScript bundle (jQuery, jQuery UI, hotkeys) +into `public/assets/`. Required for the admin interface to work. Must be +re-run after any changes to `app/assets/javascripts/admin_bundle.js`. + +## 15. Run tests (optional but recommended) + +```sh +bundle exec rake test +``` + +Expected result: 129 runs, ~339 assertions, 3 failures, 0 errors. +The 3 failures are pre-existing and documented in the handover document. + +## 16. Start the server + +Development: + +```sh +bundle exec rails server -p 3000 -b 0.0.0.0 -e development +``` + +Note: `-b 0.0.0.0` is required — `localhost` does not resolve inside +a FreeBSD jail. + +Production (unicorn): + +```sh +/usr/local/rvm/gems/ruby-3.2.11@rails7-upgrade/wrappers/unicorn \ + -c /usr/local/etc/unicorn.rb -E production -D +``` + +The rc.d script at `/etc/rc.d/cccms` needs updating from `unicorn_rails` +to `unicorn` before use — see the handover document for details. + +## Known Gotchas + +**sysvipc:** PostgreSQL will fail silently or with a cryptic error if +sysvipc is not enabled for the jail. Enable it on the host before starting +PostgreSQL. + +**MAKE=gmake:** Native gem compilation fails without this. Set it before +every `bundle install` or add to your shell profile. + +**rvm known versions:** rvm 1.29.12 ships with a stale `config/known` that +only lists Ruby up to 3.0.0. Always update from master after installing rvm. + +**ImageMagick 7:** The `convert` command is deprecated; use `magick convert`. +The `file_attachment.rb` concern needs updating before production use. + +**pg_hba.conf:** The default FreeBSD PostgreSQL configuration uses `trust` +for local Unix socket connections, which is sufficient for the application. +No changes needed unless TCP connections are required. + +**assets:precompile:** Must be run after checkout and after any changes to +admin JavaScript. The compiled files in `public/assets/` are gitignored. + +**chaos_calendar include path:** On FreeBSD 14.x with libical 3.0.20+, +the include path is `` not ``. This is already +fixed 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}" extra_commands="reload" sig_reload="USR2" +cccms_prestart() +{ + mkdir -p /usr/local/www/cccms/tmp/pids /var/log + touch /var/log/unicorn.stderr.log + chown www:www /var/log/unicorn.stderr.log +} + export PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin export RAILS_ENV=production export 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"] { font: inherit; color: inherit; cursor: pointer; - text-decoration: underline; + text-decoration: none } form.button_to input[type="submit"]:hover { - color: inherit; + color: #ffffff; + background-color: #ff9600; } #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 assert_response :success assert_select "a", { :count => 0, :text => "Destroy" } end - + test "get index as admin user renders admin partial" do login_as :aaron get :index assert_response :success - assert_select "a", "destroy" - assert_select "a", "show", "Show Link is missing" + assert_select "input[type=submit][value=destroy]" + assert_select "a", "show" end 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 # Add more helper methods to be used by all tests here... + setup do + I18n.locale = I18n.default_locale + end + def create_node_with_published_page node = create_node_with_draft draft = node.draft -- cgit v1.3