tina4ruby 3.13.149 → 3.13.151

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.
@@ -3,23 +3,23 @@
3
3
 
4
4
  Override: drop your own templates/crud/form.twig to change how inputs render.
5
5
 
6
- fields - one per input: { id, name, label, value, placeholder, required_attr,
6
+ fields - one per input: { id, name, label, value, placeholder, required (bool),
7
7
  and exactly ONE control flag: input (with type) | textarea | checkbox
8
- (checked_attr) | select (options_html) }.
9
- wrap - render the <form> wrapper; form_id_attr/action/form_method set it
8
+ (checked bool) | select (options_html) }.
9
+ wrap - render the <form> wrapper; form_id/action/form_method set it
10
10
  edit - add the hidden primary-key input (edit modal)
11
11
  modal_footer- render the Cancel/Save modal buttons; submit_button - a plain Submit
12
12
 
13
13
  No inline styles or on*= handlers: the modal buttons carry data-crud-action and
14
14
  are wired by the page script's delegated listener (ADR-0088).
15
15
  #}
16
- {% if wrap %}<form{{ form_id_attr|raw }} action="{{ action }}" method="{{ form_method }}" class="needs-validation" data-crud-form="1">{% endif %}
16
+ {% if wrap %}<form{% if form_id %} id="{{ form_id }}"{% endif %} action="{{ action }}" method="{{ form_method }}" class="needs-validation" data-crud-form="1">{% endif %}
17
17
  {% if method_override %}<input type="hidden" name="_method" value="{{ method_override }}">{% endif %}
18
18
  {% if edit %}<input type="hidden" id="{{ mode }}-pk-value" name="{{ pk }}">{% endif %}
19
19
  {% if modal_footer %}<div class="crud-errors" data-crud-errors hidden></div>{% endif %}
20
20
  {% for field in fields %}<div class="mb-3">
21
21
  <label for="{{ field.id }}" class="form-label">{{ field.label }}</label>
22
- {% if field.textarea %}<textarea class="form-control" id="{{ field.id }}" name="{{ field.name }}"{{ field.required_attr|raw }}>{{ field.value }}</textarea>{% endif %}{% if field.checkbox %}<div class="form-check"><input class="form-check-input" type="checkbox" id="{{ field.id }}" name="{{ field.name }}"{{ field.checked_attr|raw }}></div>{% endif %}{% if field.select %}<select class="form-select" id="{{ field.id }}" name="{{ field.name }}"{{ field.required_attr|raw }}>{{ field.options_html|raw }}</select>{% endif %}{% if field.input %}<input type="{{ field.type }}" class="form-control" id="{{ field.id }}" name="{{ field.name }}" value="{{ field.value }}" placeholder="{{ field.placeholder }}"{{ field.required_attr|raw }}>{% endif %}
22
+ {% if field.textarea %}<textarea class="form-control" id="{{ field.id }}" name="{{ field.name }}"{% if field.required %} required{% endif %}>{{ field.value }}</textarea>{% endif %}{% if field.checkbox %}<div class="form-check"><input class="form-check-input" type="checkbox" id="{{ field.id }}" name="{{ field.name }}"{% if field.checked %} checked{% endif %}></div>{% endif %}{% if field.select %}<select class="form-select" id="{{ field.id }}" name="{{ field.name }}"{% if field.required %} required{% endif %}>{{ field.options_html|raw }}</select>{% endif %}{% if field.input %}<input type="{{ field.type }}" class="form-control" id="{{ field.id }}" name="{{ field.name }}" value="{{ field.value }}" placeholder="{{ field.placeholder }}"{% if field.required %} required{% endif %}>{% endif %}
23
23
  </div>
24
24
  {% endfor %}
25
25
  {% if modal_footer %}<div class="modal-footer"><button type="button" class="btn btn-secondary" data-crud-action="close" data-crud-modal="{{ mode }}">Cancel</button><button type="button" class="btn btn-primary" data-crud-action="save" data-crud-mode="{{ mode }}">Save</button></div>{% endif %}{% if submit_button %}<button type="submit" class="btn btn-primary">Submit</button>{% endif %}
@@ -11,7 +11,7 @@
11
11
  empty - true when there are no rows
12
12
  colspan - column count + 1 (for the empty row)
13
13
  editable - inline-edit table (Save/Delete); readonly - view table (Edit/Delete)
14
- table_name, table_id_attr, inline_script
14
+ table_name, table_id, inline_script
15
15
 
16
16
  Alignment: each header/cell carries a tina4-css class (text-start for text,
17
17
  text-end for numbers). Actions is always text-end. No inline styles (ADR-0088).
@@ -20,7 +20,7 @@
20
20
  re-renders <tbody data-crud-body> over fetch() as the user searches/sorts/pages.
21
21
  #}
22
22
  <div class="table-responsive">
23
- <table class="table table-striped table-hover"{{ table_id_attr|raw }}>
23
+ <table class="table table-striped table-hover"{% if table_id %} id="{{ table_id }}"{% endif %}>
24
24
  <thead class="table-dark"><tr>
25
25
  {% for header in headers %}{% if header.sortable %}<th class="{{ header.align }}"><a class="sort-link" href="{{ header.url }}" data-crud-sort="{{ header.col }}" data-crud-dir="{{ header.next_dir }}">{{ header.label }}{{ header.indicator|raw }}</a></th>{% endif %}{% if header.plain %}<th class="{{ header.align }}">{{ header.label }}</th>{% endif %}{% endfor %}<th class="text-end">Actions</th>
26
26
  </tr></thead>
@@ -1,14 +1,15 @@
1
1
  {% extends "errors/base.twig" %}
2
2
  {% block title %}Redirecting…{% endblock %}
3
- {% block meta %}<meta http-equiv="refresh" content="0;url={{ redirect_url }}">{% endblock %}
3
+ {% block meta %}{% set p1 = redirect_url|slice(0,1) %}{% set p2 = redirect_url|slice(0,2) %}{% set p7 = redirect_url|slice(0,7) %}{% set p8 = redirect_url|slice(0,8) %}{% set rel = (p1 == '/') ? ((p2 == '//') ? 'no' : 'yes') : 'no' %}{% set safe_redirect_url = (rel == 'yes' or p7 == 'http://' or p8 == 'https://') ? redirect_url : '/' %}<meta http-equiv="refresh" content="0;url={{ safe_redirect_url }}">{% endblock %}
4
4
  {% block extra_styles %}
5
5
  .spinner { display: inline-block; width: 1.5rem; height: 1.5rem; border: 3px solid var(--border); border-top-color: var(--primary); border-radius: 50%; animation: spin 0.8s linear infinite; margin-bottom: 1rem; }
6
6
  @keyframes spin { to { transform: rotate(360deg); } }
7
7
  {% endblock %}
8
8
  {% block content %}
9
+ {% set p1 = redirect_url|slice(0,1) %}{% set p2 = redirect_url|slice(0,2) %}{% set p7 = redirect_url|slice(0,7) %}{% set p8 = redirect_url|slice(0,8) %}{% set rel = (p1 == '/') ? ((p2 == '//') ? 'no' : 'yes') : 'no' %}{% set safe_redirect_url = (rel == 'yes' or p7 == 'http://' or p8 == 'https://') ? redirect_url : '/' %}
9
10
  <div class="spinner"></div>
10
11
  <div class="error-title">Redirecting…</div>
11
12
  <div class="error-msg">You are being redirected to a new location.</div>
12
- <div class="error-path c-primary">{{ redirect_url }}</div>
13
- <a href="{{ redirect_url }}" class="error-home">Click here if not redirected</a>
13
+ <div class="error-path c-primary">{{ safe_redirect_url }}</div>
14
+ <a href="{{ safe_redirect_url }}" class="error-home">Click here if not redirected</a>
14
15
  {% endblock %}
data/lib/tina4/version.rb CHANGED
@@ -7,5 +7,5 @@
7
7
 
8
8
 
9
9
  module Tina4
10
- VERSION = "3.13.149"
10
+ VERSION = "3.13.151"
11
11
  end
@@ -199,7 +199,7 @@ module Tina4
199
199
  # @param host [String] the host the main socket binds
200
200
  # @return [Array<String>] extra bind addresses (possibly empty)
201
201
  def self.loopback_bind_hosts(host)
202
- normalized = host.to_s.strip.downcase.gsub(/\A\[+|\]+\z/, "")
202
+ normalized = host.to_s.strip.downcase.gsub(/\A\[++|\]++\z/, "")
203
203
  case normalized
204
204
  when "localhost" then ["127.0.0.1", "::1"]
205
205
  when "127.0.0.1", "0.0.0.0" then ["::1"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.149
4
+ version: 3.13.151
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-10 00:00:00.000000000 Z
11
+ date: 2026-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sqlite3
@@ -293,6 +293,7 @@ files:
293
293
  - lib/tina4/public/images/tina4-logo-icon.webp
294
294
  - lib/tina4/public/js/frond.js
295
295
  - lib/tina4/public/js/frond.min.js
296
+ - lib/tina4/public/js/tina4-dev-admin-split.html
296
297
  - lib/tina4/public/js/tina4-dev-admin.min.js
297
298
  - lib/tina4/public/js/tina4.min.js
298
299
  - lib/tina4/public/js/tina4js.min.js
@@ -321,6 +322,7 @@ files:
321
322
  - lib/tina4/realtime/channel_member.rb
322
323
  - lib/tina4/realtime/local_storage.rb
323
324
  - lib/tina4/realtime/message.rb
325
+ - lib/tina4/realtime/s3_client.rb
324
326
  - lib/tina4/realtime/s3_storage.rb
325
327
  - lib/tina4/realtime/storage.rb
326
328
  - lib/tina4/realtime/storage_backend.rb