Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1df6048

Browse files
committed
Merge branch 'master' into nested-repeating-subfields
2 parents 01199b3 + e750779 commit 1df6048

13 files changed

Lines changed: 115 additions & 49 deletions

File tree

README.md

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ scheming.presets = ckanext.scheming:presets.json
100100
scheming.dataset_fallback = false
101101
```
102102

103+
>[!NOTE]
104+
> Schemas for datasets, groups, and organizations can be defined using either **JSON** or **YAML** files. The extension [automatically detects the format based on the file extension](./ckanext/scheming/loader.py) (`.json`, `.yaml`, or `.yml`). While examples often show YAML for datasets and JSON for organizations, you are free to use whichever format you prefer for any entity type.
105+
103106
## Schema Types
104107
With this plugin, you can customize the group, organization, and dataset entities in CKAN. Adding and enabling a schema will modify the forms used to update and create each entity, indicated by the respective `type` property at the root level. Such as `group_type`, `organization_type`, and `dataset_type`. Non-default types are supported properly as is indicated throughout the examples.
105108

@@ -261,10 +264,9 @@ A single `fields` list replaces the `dataset_fields` and `resource_fields` schem
261264
## Field Keys
262265
### `field_name`
263266

264-
The `field_name` value is the name of an existing CKAN dataset or resource
265-
field or a new extra field. Existing dataset
266-
field names include:
267+
The `field_name` value is the name of an existing CKAN dataset or resource field (columns in the `package` or `resource` database tables) or a new extra field.
267268

269+
Existing dataset field names include (but are not limited to):
268270
* `name` - the URI for the dataset
269271
* `title`
270272
* `notes` - the dataset description
@@ -273,9 +275,10 @@ field names include:
273275
* `maintainer`
274276
* `maintainer_email`
275277

276-
New field names should follow the current lowercase_with_underscores
277-
naming convention. Don't name your field `mySpecialField`, use
278-
`my_special_field` instead.
278+
New field names should follow the current lowercase_with_underscores naming convention. Don't name your field `mySpecialField`, use `my_special_field` instead.
279+
280+
>[!NOTE]
281+
> The `ckan_dataset.yaml` file provided in this extension is just an example schema that mirrors standard CKAN behavior. It defines many of these standard fields, but you are free to include or exclude them in your custom schemas.
279282

280283

281284
### `label`
@@ -331,6 +334,30 @@ for each group.
331334
label: Phone Number
332335
```
333336

337+
* `label`: This is the human-readable label for the **entire group** of repeating fields (e.g., "Contacts"). It typically appears as the header for the section.
338+
* `repeating_label`: This is the label used for the **singular item**, often used in the "Add [Label]" button (e.g., "Add Contact").
339+
340+
Both `label` and `repeating_label` support internationalization dictionaries:
341+
342+
```yaml
343+
- field_name: contacts
344+
label:
345+
en: Contacts
346+
es: Contactos
347+
repeating_label:
348+
en: Contact
349+
es: Contacto
350+
repeating_subfields:
351+
352+
- field_name: address
353+
label:
354+
en: Address of the contact point
355+
es: Dirección del punto de contacto
356+
required: true
357+
358+
...
359+
```
360+
334361

335362
### `start_form_page`
336363

@@ -451,7 +478,10 @@ the default value for this field.
451478
### `preset`
452479

453480
A `preset` specifies a set of default values for other field keys. They
454-
allow reuse of definitions for validation and snippets for common field types.
481+
allow reuse of definitions for validation and snippets for common field types.
482+
483+
>[!TIP]
484+
> Presets can be used for all schemas: **datasets, groups, and organizations.**
455485

456486
This extension includes the following presets in [presets.json](ckanext/scheming/presets.json):
457487

@@ -647,7 +677,7 @@ Internally all extra fields are stored as strings. If you are attempting to save
647677

648678
For example if you use a simple "yes/no" question, you will need to let ckanext-scheming know that this data needs to be stored *and retrieved* as a boolean. This is acheieved using [`validators`](#validators) and [`output_validators`](#output_validators) keys.
649679

650-
```
680+
```yaml
651681
- field_name: is_camel_friendly
652682
label: Is this camel friendly?
653683
required: true
@@ -672,8 +702,28 @@ sent to the user.
672702

673703
### `create_validators`
674704

675-
The `create_validators` value if present overrides `validators` during
676-
create only.
705+
The `create_validators` value, if present, overrides `validators` **during creation only**.
706+
When updating an existing record, the standard `validators` list is used instead.
707+
708+
This is useful for:
709+
- Fields that are mandatory when creating a dataset but read-only or optional during updates.
710+
- Setting initial default values or identifiers that shouldn't be validated the same way afterwards.
711+
712+
```yaml
713+
- field_name: source_id
714+
label: Source dataset identifier
715+
form_placeholder: 'INT-12345'
716+
# Creation: must be present and valid
717+
create_validators: scheming_required not_empty unicode_safe
718+
# Update: optional, but if provided it must still be valid
719+
validators: ignore_missing unicode_safe
720+
```
721+
722+
In this example, `source_id` is required when the dataset is created.
723+
On updates the field is optional: if it is omitted the update passes; if it is
724+
provided it still must be a valid string according to the `unicode_safe`
725+
validator. This is useful when introducing a new required field without
726+
breaking updates to existing legacy datasets that do not yet have this value.
677727

678728
### `help_text`
679729

ckanext/scheming/assets/styles/scheming.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,23 @@ a.btn.btn-multiple-remove {
3939
.radio-group label {
4040
font-weight: normal;
4141
}
42+
43+
fieldset.checkboxes label {
44+
font-weight: normal;
45+
display: block;
46+
}
47+
fieldset.checkboxes label:after {
48+
content: none;
49+
}
50+
fieldset.checkboxes label input {
51+
width: auto;
52+
top: 0;
53+
}
54+
55+
.scheming-pages-dynamic-width{
56+
width: attr(data-width, 100)%;
57+
}
58+
59+
.d-none-soft{
60+
display: none;
61+
}

ckanext/scheming/helpers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,11 @@ def date_tz_str_to_datetime(date_str):
313313
tz_split = re.split('([Z+-])', split[1])
314314

315315
date = split[0] + 'T' + tz_split[0]
316-
time_tuple = re.split('[^\d]+', date, maxsplit=5)
316+
time_tuple = re.split(r'[^\d]+', date, maxsplit=5)
317317

318318
# Extract seconds and microseconds
319319
if len(time_tuple) >= 6:
320-
m = re.match('(?P<seconds>\d{2})(\.(?P<microseconds>\d{3,6}))?$',
320+
m = re.match(r'(?P<seconds>\d{2})(\.(?P<microseconds>\d{3,6}))?$',
321321
time_tuple[5])
322322
if not m:
323323
raise ValueError('Unable to parse %s as seconds.microseconds' %
@@ -331,7 +331,7 @@ def date_tz_str_to_datetime(date_str):
331331
# Apply the timezone offset
332332
if len(tz_split) > 1 and not tz_split[1] == 'Z':
333333
tz = tz_split[2]
334-
tz_tuple = re.split('[^\d]+', tz)
334+
tz_tuple = re.split(r'[^\d]+', tz)
335335

336336
if tz_tuple[0] == '':
337337
raise ValueError('Unable to parse timezone')
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{{ h.link_to(data[field.field_name], data[field.field_name], rel=field.display_property, target='_blank') }}
1+
{{ h.link_to(data[field.field_name], data[field.field_name], rel=field.display_property ~ 'noopener noreferrer', target='_blank') }}

ckanext/scheming/templates/scheming/form_snippets/multiple_checkbox.html

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,5 @@
11
{% import 'macros/form.html' as form %}
22

3-
<style>
4-
fieldset.checkboxes label {
5-
font-weight: normal;
6-
display: block;
7-
}
8-
fieldset.checkboxes label:after {
9-
content: none;
10-
}
11-
fieldset.checkboxes label input {
12-
width: auto;
13-
top: 0;
14-
}
15-
</style>
16-
173
{%- call form.input_block(
184
label=h.scheming_language_text(field.label),
195
classes=field.classes if 'classes' in field else ['control-medium'],

ckanext/scheming/templates/scheming/form_snippets/multiple_select.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,10 @@
2222
{%- endif -%}
2323
<select multiple
2424
size="{{ field.get('select_size', field.choices|length) }}"
25-
style="display: block"
2625
id="field-{{ field.field_name }}"
2726
name="{{ field.field_name }}"
2827
{{ form.attributes(dict(
29-
{"class": "form-control"}, **field.get('form_select_attrs', {}))) }}>
28+
{"class": "form-control d-block"}, **field.get('form_select_attrs', {}))) }}>
3029
{%- for val, label in choices -%}
3130
<option id="field-{{ field.field_name }}-{{ val }}"
3231
value="{{ val }}"

ckanext/scheming/templates/scheming/form_snippets/organization_upload.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
{{ form.image_upload(
77
data,
88
errors,
9-
is_upload_enabled=h.uploads_enabled(),
9+
is_upload_enabled=h.uploads_enabled("group") if h.check_ckan_version("2.12") else h.uploads_enabled(),
1010
is_url=is_url,
1111
is_upload=is_upload
12-
)
12+
)
1313
}}
1414

1515
{# image_upload macro doesn't support call #}

ckanext/scheming/templates/scheming/form_snippets/repeating_subfields.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
-%}
3232
{% endfor %}
3333
</div>
34-
<div class="panel-body fields-removed-notice" style="display:none" data-field-name="{{ field.field_name }}">
34+
<div class="panel-body fields-removed-notice d-none-soft" data-field-name="{{ field.field_name }}">
3535
{% block removal_text %}
3636
{% if 'id' in data %}
3737
{{ _('These fields have been removed, click update below to save your changes.') }}
@@ -80,7 +80,7 @@
8080
{%- snippet 'scheming/form_snippets/help_text.html', field=field -%}
8181
</div>
8282

83-
<div name="repeating-template" style="display:none">{{ repeating_panel(field.field_name ~ '-REPEATING-INDEX0', field.field_name ~ '-REPEATING-INDEX1') }}</div>
83+
<div name="repeating-template" class="d-none-soft">{{ repeating_panel(field.field_name ~ '-REPEATING-INDEX0', field.field_name ~ '-REPEATING-INDEX1') }}</div>
8484
</fieldset>
8585
</div>
8686
{% endcall %}

ckanext/scheming/templates/scheming/form_snippets/upload.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
field_url=field.field_name,
88
field_upload=field.upload_field,
99
field_clear=field.upload_clear,
10-
is_upload_enabled=h.uploads_enabled(),
10+
is_upload_enabled=h.uploads_enabled("resource") if h.check_ckan_version("2.12") else h.uploads_enabled(),
1111
is_url=data[field.field_name] and not is_upload,
1212
is_upload=is_upload,
1313
upload_label=h.scheming_language_text(field.upload_label),

ckanext/scheming/templates/scheming/package/snippets/package_form.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
{%- for p in pages -%}
1010
<li class="{{
1111
'first ' if loop.first else ''}}{{
12-
'active ' if loop.index == active_page else '' }}"
13-
style="width:{{ 100 / (loop.length + (0 if form_style == 'edit' else 1)) }}%">
14-
<span class="highlight" style="padding-right:0">{% if loop.index < active_page
12+
'active ' if loop.index == active_page else '' }} scheming-pages-dynamic-width"
13+
data-width="{{- 100 / (loop.length + (0 if form_style == 'edit' else 1)) -}}">
14+
<span class="highlight pr-0">{% if loop.index < active_page
1515
or (form_style == 'edit' and loop.index != active_page)
1616
%}<a href="{{
1717
h.url_for(dataset_type +
@@ -35,7 +35,7 @@
3535
</li>
3636
{%- endfor -%}
3737
{%- if form_style != 'edit' -%}
38-
<li class="last {{ s2 }}" style="width:{{ 100 / (pages | length + 1) }}%">
38+
<li class="last {{ s2 }} scheming-pages-dynamic-width" data-width="{{- 100 / (pages | length + 1) -}}">
3939
{% if s2 != 'complete' %}
4040
<span class="highlight">{{ _('Add data') }}</span>
4141
{% else %}

0 commit comments

Comments
 (0)