Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/api/refer-community-project/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export async function POST(request: NextRequest) {
project_url: body.project_url || '',
github_url: body.github_url || '',
tags: Array.isArray(body.tags) ? body.tags : [],
language: typeof body.language === 'string' ? body.language : '',
submitter_name: body.submitter_name,
submitter_email: body.submitter_email,
verification_code: body.verification_code || '',
Expand Down
1 change: 1 addition & 0 deletions app/api/refer-local-group/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export async function POST(request: NextRequest) {
location: body.location || '',
url: body.url,
description: body.description,
language: typeof body.language === 'string' ? body.language : '',
submitter_name: body.submitter_name,
submitter_email: body.submitter_email,
verification_code: body.verification_code || '',
Expand Down
1 change: 1 addition & 0 deletions app/api/submit-project/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export async function POST(request: NextRequest) {
url: (body.url as string) || '',
repo_urls: Array.isArray(body.repo_urls) ? body.repo_urls : [],
tags: Array.isArray(body.tags) ? body.tags : [],
language: typeof body.language === 'string' ? body.language : '',
submitter_name: body.submitter_name,
submitter_email: body.submitter_email,
verification_code: body.verification_code || '',
Expand Down
10 changes: 1 addition & 9 deletions components/LanguageSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,11 @@
import { useLocale } from 'next-intl'
import { useRouter, usePathname } from '@/src/i18n/navigation'
import { locales, type Locale } from '@/src/i18n/routing'
import { localeLabels } from '@/src/i18n/locale-labels'
import { GlobeAltIcon } from '@heroicons/react/24/outline'
import { useState, useRef, useEffect, useTransition } from 'react'
import clsx from 'clsx'

const localeLabels: Record<Locale, string> = {
en: 'English',
it: 'Italiano',
es: 'Espanol',
fr: 'Francais',
pt: 'Portugues',
de: 'Deutsch',
}

export default function LanguageSwitcher() {
const locale = useLocale()
const router = useRouter()
Expand Down
26 changes: 25 additions & 1 deletion components/sections/ReferCommunityProjectModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
'use client'

import { useState, useRef, useCallback } from 'react'
import { useTranslations } from 'next-intl'
import { useTranslations, useLocale } from 'next-intl'
import { locales, type Locale } from '@/src/i18n/routing'
import { localeLabels } from '@/src/i18n/locale-labels'

interface ReferCommunityProjectModalProps {
buttonLabel: string
Expand All @@ -11,6 +13,8 @@ type Status = 'idle' | 'sending_code' | 'awaiting_code' | 'submitting' | 'succes

export default function ReferCommunityProjectModal({ buttonLabel }: ReferCommunityProjectModalProps) {
const t = useTranslations('communityProjects')
const tc = useTranslations('common')
const currentLocale = useLocale() as Locale
const dialogRef = useRef<HTMLDialogElement>(null)
const openedAtRef = useRef<number>(0)
const [formData, setFormData] = useState<{ fields: Record<string, string>; tags: string[] }>({ fields: {}, tags: [] })
Expand Down Expand Up @@ -50,6 +54,7 @@ export default function ReferCommunityProjectModal({ buttonLabel }: ReferCommuni
description: data.get('description') as string,
project_url: data.get('project_url') as string,
github_url: data.get('github_url') as string,
language: data.get('language') as string,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
submitter_name: data.get('submitter_name') as string,
submitter_email: data.get('submitter_email') as string,
website: data.get('website') as string,
Expand Down Expand Up @@ -290,6 +295,25 @@ export default function ReferCommunityProjectModal({ buttonLabel }: ReferCommuni
/>
</div>

<div>
<label htmlFor="cp_language" className="block text-sm font-medium text-gray-700">
{tc('submissionLanguage')} <span className="text-red-500">*</span>
</label>
<select
id="cp_language"
name="language"
defaultValue={formData.fields.language || currentLocale}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-cdcf-gold focus:ring-1 focus:ring-cdcf-gold focus:outline-none"
>
{locales.map((loc) => (
<option key={loc} value={loc}>
{localeLabels[loc]}
</option>
))}
</select>
<p className="mt-1 text-xs text-gray-500">{tc('submissionLanguageHint')}</p>
</div>

<div>
<label htmlFor="cp_project_name" className="block text-sm font-medium text-gray-700">
{t('fieldProjectName')} <span className="text-red-500">*</span>
Expand Down
27 changes: 26 additions & 1 deletion components/sections/ReferLocalGroupModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
'use client'

import { useState, useRef, useCallback } from 'react'
import { useTranslations } from 'next-intl'
import { useTranslations, useLocale } from 'next-intl'
import { locales, type Locale } from '@/src/i18n/routing'
import { localeLabels } from '@/src/i18n/locale-labels'

interface ReferLocalGroupModalProps {
buttonLabel: string
Expand All @@ -11,6 +13,8 @@ type Status = 'idle' | 'sending_code' | 'awaiting_code' | 'submitting' | 'succes

export default function ReferLocalGroupModal({ buttonLabel }: ReferLocalGroupModalProps) {
const t = useTranslations('community')
const tc = useTranslations('common')
const currentLocale = useLocale() as Locale
const dialogRef = useRef<HTMLDialogElement>(null)
const openedAtRef = useRef<number>(0)
const [formData, setFormData] = useState<Record<string, string>>({})
Expand Down Expand Up @@ -45,6 +49,7 @@ export default function ReferLocalGroupModal({ buttonLabel }: ReferLocalGroupMod
location: data.get('location') as string,
url: data.get('url') as string,
description: data.get('description') as string,
language: data.get('language') as string,
submitter_name: data.get('submitter_name') as string,
submitter_email: data.get('submitter_email') as string,
website: data.get('website') as string,
Expand All @@ -57,6 +62,7 @@ export default function ReferLocalGroupModal({ buttonLabel }: ReferLocalGroupMod
location: payload.location,
url: payload.url,
description: payload.description,
language: payload.language,
submitter_name: payload.submitter_name,
submitter_email: payload.submitter_email,
website: payload.website,
Expand Down Expand Up @@ -285,6 +291,25 @@ export default function ReferLocalGroupModal({ buttonLabel }: ReferLocalGroupMod
/>
</div>

<div>
<label htmlFor="lg_language" className="block text-sm font-medium text-gray-700">
{tc('submissionLanguage')} <span className="text-red-500">*</span>
</label>
<select
id="lg_language"
name="language"
defaultValue={formData.language || currentLocale}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-cdcf-gold focus:ring-1 focus:ring-cdcf-gold focus:outline-none"
>
{locales.map((loc) => (
<option key={loc} value={loc}>
{localeLabels[loc]}
</option>
))}
</select>
<p className="mt-1 text-xs text-gray-500">{tc('submissionLanguageHint')}</p>
</div>

<div>
<label htmlFor="group_name" className="block text-sm font-medium text-gray-700">
{t('fieldGroupName')} <span className="text-red-500">*</span>
Expand Down
26 changes: 25 additions & 1 deletion components/sections/SubmitProjectModal.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
'use client'

import { useState, useRef, useCallback } from 'react'
import { useTranslations } from 'next-intl'
import { useTranslations, useLocale } from 'next-intl'
import { locales, type Locale } from '@/src/i18n/routing'
import { localeLabels } from '@/src/i18n/locale-labels'

interface SubmitProjectModalProps {
buttonLabel: string
Expand All @@ -11,6 +13,8 @@ type Status = 'idle' | 'sending_code' | 'awaiting_code' | 'submitting' | 'succes

export default function SubmitProjectModal({ buttonLabel }: SubmitProjectModalProps) {
const t = useTranslations('projects')
const tc = useTranslations('common')
const currentLocale = useLocale() as Locale
const dialogRef = useRef<HTMLDialogElement>(null)
const openedAtRef = useRef<number>(0)
const [formData, setFormData] = useState<{ fields: Record<string, string>; repoUrls: string[]; tags: string[] }>({
Expand Down Expand Up @@ -67,6 +71,7 @@ export default function SubmitProjectModal({ buttonLabel }: SubmitProjectModalPr
category: data.get('category') as string,
description: data.get('description') as string,
url: data.get('url') as string,
language: data.get('language') as string,
submitter_name: data.get('submitter_name') as string,
submitter_email: data.get('submitter_email') as string,
website: data.get('website') as string,
Expand Down Expand Up @@ -308,6 +313,25 @@ export default function SubmitProjectModal({ buttonLabel }: SubmitProjectModalPr
/>
</div>

<div>
<label htmlFor="sp_language" className="block text-sm font-medium text-gray-700">
{tc('submissionLanguage')} <span className="text-red-500">*</span>
</label>
<select
id="sp_language"
name="language"
defaultValue={formData.fields.language || currentLocale}
className="mt-1 block w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:border-cdcf-gold focus:ring-1 focus:ring-cdcf-gold focus:outline-none"
>
{locales.map((loc) => (
<option key={loc} value={loc}>
{localeLabels[loc]}
</option>
))}
</select>
<p className="mt-1 text-xs text-gray-500">{tc('submissionLanguageHint')}</p>
</div>

<div>
<label htmlFor="project_name" className="block text-sm font-medium text-gray-700">
{t('fieldProjectName')} <span className="text-red-500">*</span>
Expand Down
4 changes: 3 additions & 1 deletion messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@
"error": "Etwas ist schiefgelaufen",
"notFound": "Seite nicht gefunden",
"search": "Suchen",
"language": "Sprache"
"language": "Sprache",
"submissionLanguage": "Inhaltssprache",
"submissionLanguageHint": "Verfassen Sie Ihre Einreichung in einer der 6 unterst\u00fctzten Sprachen. Wir \u00fcbersetzen sie automatisch in die anderen 5."
},
"hero": {
"defaultTagline": "Catholic Digital Commons Foundation",
Expand Down
4 changes: 3 additions & 1 deletion messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@
"error": "Something went wrong",
"notFound": "Page not found",
"search": "Search",
"language": "Language"
"language": "Language",
"submissionLanguage": "Content language",
"submissionLanguageHint": "Write your submission in any of the 6 supported languages. We'll auto-translate to the other 5."
},
"hero": {
"defaultTagline": "Catholic Digital Commons Foundation",
Expand Down
4 changes: 3 additions & 1 deletion messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@
"error": "Algo salió mal",
"notFound": "Página no encontrada",
"search": "Buscar",
"language": "Idioma"
"language": "Idioma",
"submissionLanguage": "Idioma del contenido",
"submissionLanguageHint": "Escribe tu propuesta en cualquiera de los 6 idiomas admitidos. La traduciremos autom\u00e1ticamente a los otros 5."
},
"hero": {
"defaultTagline": "Catholic Digital Commons Foundation",
Expand Down
4 changes: 3 additions & 1 deletion messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@
"error": "Une erreur est survenue",
"notFound": "Page introuvable",
"search": "Rechercher",
"language": "Langue"
"language": "Langue",
"submissionLanguage": "Langue du contenu",
"submissionLanguageHint": "R\u00e9digez votre soumission dans l'une des 6 langues prises en charge. Nous la traduirons automatiquement vers les 5 autres."
},
"hero": {
"defaultTagline": "Catholic Digital Commons Foundation",
Expand Down
4 changes: 3 additions & 1 deletion messages/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@
"error": "Qualcosa è andato storto",
"notFound": "Pagina non trovata",
"search": "Cerca",
"language": "Lingua"
"language": "Lingua",
"submissionLanguage": "Lingua del contenuto",
"submissionLanguageHint": "Scrivi la tua candidatura in una qualsiasi delle 6 lingue supportate. La tradurremo automaticamente nelle altre 5."
},
"hero": {
"defaultTagline": "Catholic Digital Commons Foundation",
Expand Down
4 changes: 3 additions & 1 deletion messages/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@
"error": "Algo deu errado",
"notFound": "Página não encontrada",
"search": "Pesquisar",
"language": "Idioma"
"language": "Idioma",
"submissionLanguage": "Idioma do conte\u00fado",
"submissionLanguageHint": "Escreva sua submiss\u00e3o em qualquer um dos 6 idiomas suportados. Traduziremos automaticamente para os outros 5."
},
"hero": {
"defaultTagline": "Catholic Digital Commons Foundation",
Expand Down
10 changes: 10 additions & 0 deletions src/i18n/locale-labels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { Locale } from './routing'

export const localeLabels: Record<Locale, string> = {
en: 'English',
it: 'Italiano',
es: 'Español',
fr: 'Français',
pt: 'Português',
de: 'Deutsch',
}
3 changes: 3 additions & 0 deletions wordpress/themes/cdcf-headless/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,7 @@
'description' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_textarea_field'],
'url' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'esc_url_raw'],
'location' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => ''],
'language' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => ''],
'submitter_name' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
'submitter_email' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_email'],
'verification_code' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
Expand Down Expand Up @@ -871,6 +872,7 @@
'project_url' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'esc_url_raw', 'default' => ''],
'github_url' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'esc_url_raw', 'default' => ''],
'tags' => ['required' => false, 'type' => 'array', 'default' => []],
'language' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => ''],
'submitter_name' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
'submitter_email' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_email'],
'verification_code' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
Expand Down Expand Up @@ -924,6 +926,7 @@
'url' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'esc_url_raw', 'default' => ''],
'repo_urls' => ['required' => false, 'type' => 'array', 'default' => []],
'tags' => ['required' => false, 'type' => 'array', 'default' => []],
'language' => ['required' => false, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'default' => ''],
'submitter_name' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
'submitter_email' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_email'],
'verification_code' => ['required' => true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
}

function cdcf_rest_refer_community_project(WP_REST_Request $request) {
// Resolve + validate the submission content language up front so we
// refuse tampered values before any side effects.
$language = cdcf_validate_submission_language($request['language']);
if (is_wp_error($language)) {
return $language;
}

// Rate limiting via transients: 3 submissions per hour per IP (defense-in-depth).
$ip = sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? 'unknown');
$transient_key = 'cdcf_refer_cp_' . md5($ip);
Expand Down Expand Up @@ -86,9 +93,11 @@ function cdcf_rest_refer_community_project(WP_REST_Request $request) {
return new WP_Error('insert_failed', 'Failed to create referral.', ['status' => 500]);
}

// Assign English language so Polylang can link translations later.
// Tag the post with its submission content language so Polylang
// can link translations later. Defaults to 'en' for legacy callers
// that don't send the field; see cdcf_validate_submission_language.
if (function_exists('pll_set_post_language')) {
pll_set_post_language($post_id, 'en');
pll_set_post_language($post_id, $language);
}

// Set ACF fields if ACF is active.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
}

function cdcf_rest_refer_local_group(WP_REST_Request $request) {
// Resolve + validate the submission content language up front so we
// refuse tampered values before any side effects.
$language = cdcf_validate_submission_language($request['language']);
if (is_wp_error($language)) {
return $language;
}

// Rate limiting via transients: 3 submissions per hour per IP (defense-in-depth).
$ip = sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? 'unknown');
$transient_key = 'cdcf_refer_' . md5($ip);
Expand Down Expand Up @@ -85,9 +92,11 @@ function cdcf_rest_refer_local_group(WP_REST_Request $request) {
return new WP_Error('insert_failed', 'Failed to create referral.', ['status' => 500]);
}

// Assign English language so Polylang can link translations later.
// Tag the post with its submission content language so Polylang
// can link translations later. Defaults to 'en' for legacy callers
// that don't send the field; see cdcf_validate_submission_language.
if (function_exists('pll_set_post_language')) {
pll_set_post_language($post_id, 'en');
pll_set_post_language($post_id, $language);
}

// Set ACF fields if ACF is active.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
}

function cdcf_rest_submit_project(WP_REST_Request $request) {
// Resolve + validate the submission content language up front so we
// refuse tampered values before any side effects.
$language = cdcf_validate_submission_language($request['language']);
if (is_wp_error($language)) {
return $language;
}

// Rate limiting via transients: 3 submissions per hour per IP (defense-in-depth).
$ip = sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? 'unknown');
$transient_key = 'cdcf_projsub_' . md5($ip);
Expand Down Expand Up @@ -88,9 +95,11 @@ function cdcf_rest_submit_project(WP_REST_Request $request) {
return new WP_Error('insert_failed', 'Failed to create project submission.', ['status' => 500]);
}

// Assign English language so Polylang can link translations later.
// Tag the post with its submission content language so Polylang
// can link translations later. Defaults to 'en' for legacy callers
// that don't send the field; see cdcf_validate_submission_language.
if (function_exists('pll_set_post_language')) {
pll_set_post_language($post_id, 'en');
pll_set_post_language($post_id, $language);
}

// Sanitise repo URLs.
Expand Down
Loading