diff --git a/CLAUDE.md b/CLAUDE.md index 4e1551a9..5862d072 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -26,6 +26,12 @@ are updated a few times per year. The process differs by fund: | TUV100 (III Samba Pensionifond) | Hardcoded URL in PHP template | git push → CircleCI | | TKF100 (Täiendav Kogumisfond) | ACF field in WordPress DB | WP REST API call | +Monthly **investment reports** are separate from the legal documents above and work the same way for all +four funds: the `investment_report_file` ACF field on the fund page, set from wp-admin or over the REST API +(`POST /wp-json/wp/v2/pages/{id}` with `{"acf": {"investment_report_file": }}`). The three +pension fund templates fall back to a hardcoded URL while the field is unset. The field groups that carry it +declare `'show_in_rest' => 1` — without that ACF drops the `acf` key from the request and the write no-ops. + ### Required credentials (environment variables — never hardcode) ``` @@ -90,7 +96,7 @@ CircleCI build status: https://app.circleci.com ### Step 4 — Update savings fund ACF fields (TKF100) -ACF REST API is **not enabled** on this site, so this step must be done manually in the WordPress admin: +Do this in the WordPress admin: 1. Go to `https://tuleva.ee/wp-admin/post.php?post=35292&action=edit` 2. Scroll to the ACF document fields diff --git a/src/wp-content/themes/tuleva/helpers/acf/fund-pension.php b/src/wp-content/themes/tuleva/helpers/acf/fund-pension.php new file mode 100644 index 00000000..b3b47a31 --- /dev/null +++ b/src/wp-content/themes/tuleva/helpers/acf/fund-pension.php @@ -0,0 +1,55 @@ + 'group_fund_pension_investment_report', + 'title' => 'Investment Report', + 'fields' => array ( + array ( + 'key' => 'field_fund_pension_investment_report', + 'label' => 'Investment Report', + 'name' => 'investment_report_file', + 'type' => 'file', + 'instructions' => 'Select the latest investment report PDF from media library. When set, this replaces the hardcoded report URL in the template.', + 'required' => 0, + 'return_format' => 'url', + 'library' => 'all', + 'mime_types' => 'pdf', + ), + ), + 'location' => array ( + array ( + array ( + 'param' => 'page_template', + 'operator' => '==', + 'value' => 'page_fund-stocks.php', + ), + ), + array ( + array ( + 'param' => 'page_template', + 'operator' => '==', + 'value' => 'page_fund-bonds.php', + ), + ), + array ( + array ( + 'param' => 'page_template', + 'operator' => '==', + 'value' => 'page_fund-third.php', + ), + ), + ), + 'menu_order' => 10, + 'position' => 'normal', + 'style' => 'default', + 'label_placement' => 'top', + 'instruction_placement' => 'label', + 'active' => 1, + // Required for onboarding-service to set this field over the REST API + // (POST /wp-json/wp/v2/pages/{id} with {"acf": {"investment_report_file": }}). + // Without it ACF strips the "acf" key from the request and the write silently no-ops. + 'show_in_rest' => 1, +)); + +endif; diff --git a/src/wp-content/themes/tuleva/helpers/acf/fund-savings.php b/src/wp-content/themes/tuleva/helpers/acf/fund-savings.php index 64f438ec..fe45c582 100644 --- a/src/wp-content/themes/tuleva/helpers/acf/fund-savings.php +++ b/src/wp-content/themes/tuleva/helpers/acf/fund-savings.php @@ -306,6 +306,10 @@ 'label_placement' => 'top', 'instruction_placement' => 'label', 'active' => 1, + // Required for onboarding-service to set investment_report_file over the REST API + // (POST /wp-json/wp/v2/pages/{id} with {"acf": {...}}). Without it ACF strips the + // "acf" key from the request and the write silently no-ops. + 'show_in_rest' => 1, )); endif; \ No newline at end of file diff --git a/src/wp-content/themes/tuleva/helpers/acf/init.php b/src/wp-content/themes/tuleva/helpers/acf/init.php index 01fe256c..034ab40c 100644 --- a/src/wp-content/themes/tuleva/helpers/acf/init.php +++ b/src/wp-content/themes/tuleva/helpers/acf/init.php @@ -10,6 +10,7 @@ 'helpers/acf/membership.php', // Membership page 'helpers/acf/fund-other.php', // Other fund pages 'helpers/acf/fund-savings.php', // Savings fund page + 'helpers/acf/fund-pension.php', // Pension fund investment report field (TUK75, TUK00, TUV100) 'helpers/acf/fund-choosing.php', // Instructions — Step 1 page fields 'helpers/acf/jobs.php' // Jobs page fields ]; diff --git a/src/wp-content/themes/tuleva/helpers/extras.php b/src/wp-content/themes/tuleva/helpers/extras.php index 647f64e9..b8817860 100644 --- a/src/wp-content/themes/tuleva/helpers/extras.php +++ b/src/wp-content/themes/tuleva/helpers/extras.php @@ -390,37 +390,50 @@ function hyphenate_string($string) { } function generate_report_link($url, $link_text = null) { - preg_match('/\/(\d{4})\/(\d{2})\//', $url, $matches); - $year = intval($matches[1]); - $month = intval($matches[2]); - - $month--; - if ($month === 0) { - $month = 12; - $year--; - } + $is_absolute = (bool) filter_var($url, FILTER_VALIDATE_URL); + // parse_url() returns null for a URL with no path at all ("https://tuleva.ee"), which + // basename() rejects as of PHP 8.1. + $path = ($is_absolute ? parse_url($url, PHP_URL_PATH) : $url) ?? ''; + $filename = basename($path); - $year_str = strval($year); - $month_str = str_pad(strval($month), 2, '0', STR_PAD_LEFT); + if (preg_match('/(\d{4})-(\d{2})/', $filename, $matches)) { + // Report period is encoded directly in the filename (YYYY-MM), e.g. + // "... investeeringute aruanne 2026-05.pdf" — use it as the label. + $date_text = sprintf('%s.%s', $matches[2], $matches[1]); + } elseif (preg_match('/\/(\d{4})\/(\d{2})\//', $url, $matches)) { + // Legacy uploads carry no period in the filename: the report covers the month before the + // upload-folder month (a report is uploaded the month after the period it covers). + $year = intval($matches[1]); + $month = intval($matches[2]); + + $month--; + if ($month === 0) { + $month = 12; + $year--; + } - $default_text = sprintf('%s.%s', $month_str, $year_str); - if ($link_text !== null) { - $link_text = sprintf('%s (%s)', $link_text, $default_text); + $month_str = str_pad(strval($month), 2, '0', STR_PAD_LEFT); + $date_text = sprintf('%s.%s', $month_str, strval($year)); } else { - $link_text = $default_text; + $date_text = null; } - if (filter_var($url, FILTER_VALIDATE_URL)) { - $path = parse_url($url, PHP_URL_PATH); - } else { - $path = $url; + if ($date_text !== null) { + $link_text = $link_text !== null + ? sprintf('%s (%s)', $link_text, $date_text) + : $date_text; + } elseif ($link_text === null) { + $link_text = __('Investment reports', TEXT_DOMAIN); } + // Preserve the original host for absolute URLs: the ACF-supplied media URL may be served from a + // CDN or dedicated media host. Only site-relative paths are resolved against the current site. + $href = $is_absolute ? $url : get_site_url() . $url; + $output = sprintf( - '%s', - get_site_url(), - $path, - $link_text + '%s', + esc_url($href), + esc_html($link_text) ); return $output; diff --git a/src/wp-content/themes/tuleva/templates/components/fund-bonds-details.php b/src/wp-content/themes/tuleva/templates/components/fund-bonds-details.php index c3080812..0d14738f 100644 --- a/src/wp-content/themes/tuleva/templates/components/fund-bonds-details.php +++ b/src/wp-content/themes/tuleva/templates/components/fund-bonds-details.php @@ -80,7 +80,10 @@