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
19 changes: 0 additions & 19 deletions app/Http/Controllers/AboutController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
*/
class AboutController extends Controller
{
public const LOGO = '/logo.png';

public function __invoke(ContentRepository $content, SiteFacts $facts): Response
{
$locale = App::getLocale();
Expand All @@ -34,23 +32,6 @@ public function __invoke(ContentRepository $content, SiteFacts $facts): Response
],
'links' => $facts->links(),
'organizationProfiles' => $facts->organizationProfiles(),
'logo' => $this->logo(),
]);
}

/**
* The logo file the brand block offers, or null when `public/` has none.
*
* @return array{src: string, width: int, height: int}|null
*/
private function logo(): ?array
{
$size = @getimagesize(public_path(ltrim(self::LOGO, '/')));

if (! is_array($size)) {
return null;
}

return ['src' => self::LOGO, 'width' => $size[0], 'height' => $size[1]];
}
}
5 changes: 1 addition & 4 deletions resources/data/content/de/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "Marke",
"name": "Der Name wird TablePro geschrieben: ein Wort, mit großem T und großem P.",
"usage": "Du darfst den Namen TablePro und das Logo verwenden, um auf TablePro zu verweisen, etwa in Artikeln, Tutorials, Testberichten, Verzeichnissen und Integrationen. Du darfst das Logo nicht verändern, nicht für ein anderes Produkt verwenden und nicht den Eindruck erwecken, dass TablePro dich empfiehlt. Für alles andere schreib eine E-Mail an <email>{email}</email>.",
"logo": "Logo herunterladen (PNG, {width} × {height})",
"alt": "TablePro-Logo"
"body": "Die <brand>Markenrichtlinien</brand> erklären, wie du den Namen und das Logo von TablePro verwendest, und enthalten die Logodateien zum Herunterladen."
}
}
5 changes: 1 addition & 4 deletions resources/data/content/en/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "Brand",
"name": "The name is written TablePro: one word, with a capital T and a capital P.",
"usage": "You may use the TablePro name and logo to refer to TablePro, for example in articles, tutorials, reviews, listings and integrations. You may not alter the logo, use it for another product, or suggest that TablePro endorses you. For anything else, email <email>{email}</email>.",
"logo": "Download the logo (PNG, {width} × {height})",
"alt": "TablePro logo"
"body": "The <brand>brand guidelines</brand> explain how to use the TablePro name and logo, and have the logo files to download."
}
}
5 changes: 1 addition & 4 deletions resources/data/content/es/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "Marca",
"name": "El nombre se escribe TablePro: una sola palabra, con T y P mayúsculas.",
"usage": "Puedes usar el nombre y el logotipo de TablePro para referirte a TablePro, por ejemplo en artículos, tutoriales, reseñas, directorios e integraciones. No puedes modificar el logotipo, usarlo para otro producto ni dar a entender que TablePro te respalda. Para cualquier otro uso, escribe a <email>{email}</email>.",
"logo": "Descargar el logotipo (PNG, {width} × {height})",
"alt": "Logotipo de TablePro"
"body": "Las <brand>pautas de marca</brand> explican cómo usar el nombre y el logotipo de TablePro, e incluyen los archivos del logotipo para descargar."
}
}
5 changes: 1 addition & 4 deletions resources/data/content/fr/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "Marque",
"name": "Le nom s’écrit TablePro, en un seul mot, avec un T et un P majuscules.",
"usage": "Vous pouvez utiliser le nom et le logo TablePro pour parler de TablePro, par exemple dans des articles, des tutoriels, des tests, des annuaires et des intégrations. Vous ne pouvez pas modifier le logo, l’utiliser pour un autre produit ni laisser entendre que TablePro vous recommande. Pour toute autre utilisation, écrivez à <email>{email}</email>.",
"logo": "Télécharger le logo (PNG, {width} × {height})",
"alt": "Logo de TablePro"
"body": "La <brand>charte de marque</brand> explique comment utiliser le nom et le logo TablePro, et propose les fichiers du logo à télécharger."
}
}
5 changes: 1 addition & 4 deletions resources/data/content/id/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "Merek",
"name": "Nama ini ditulis TablePro: satu kata, dengan huruf T dan P kapital.",
"usage": "Anda boleh memakai nama dan logo TablePro untuk merujuk ke TablePro, misalnya di artikel, tutorial, ulasan, direktori aplikasi, dan integrasi. Anda tidak boleh mengubah logo, memakainya untuk produk lain, atau mengesankan bahwa TablePro mendukung Anda. Untuk keperluan lain, kirim email ke <email>{email}</email>.",
"logo": "Unduh logo (PNG, {width} × {height})",
"alt": "Logo TablePro"
"body": "<brand>Pedoman merek</brand> menjelaskan cara memakai nama dan logo TablePro, beserta file logo untuk diunduh."
}
}
5 changes: 1 addition & 4 deletions resources/data/content/it/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "Marchio",
"name": "Il nome si scrive TablePro, in una sola parola, con la T e la P maiuscole.",
"usage": "Puoi usare il nome e il logo di TablePro per fare riferimento a TablePro, ad esempio in articoli, tutorial, recensioni, directory e integrazioni. Non puoi modificare il logo, usarlo per un altro prodotto o lasciar intendere un sostegno da parte di TablePro. Per qualsiasi altro uso, scrivi a <email>{email}</email>.",
"logo": "Scarica il logo (PNG, {width} × {height})",
"alt": "Logo di TablePro"
"body": "Le <brand>linee guida del marchio</brand> spiegano come usare il nome e il logo di TablePro e includono i file del logo da scaricare."
}
}
5 changes: 1 addition & 4 deletions resources/data/content/ja/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "ブランド",
"name": "名前は TablePro と表記します。1 語で、T と P は大文字です。",
"usage": "TablePro に言及するためであれば、記事、チュートリアル、レビュー、ソフトウェア一覧、連携機能などで TablePro の名前とロゴを使用できます。ロゴの改変、ほかの製品での使用、TablePro が推薦しているかのような表示はできません。それ以外の用途については <email>{email}</email> までご連絡ください。",
"logo": "ロゴをダウンロード(PNG、{width} × {height})",
"alt": "TablePro のロゴ"
"body": "TablePro の名前とロゴの使い方、ロゴファイルのダウンロードは<brand>ブランドガイドライン</brand>をご覧ください。"
}
}
5 changes: 1 addition & 4 deletions resources/data/content/ko/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "브랜드",
"name": "이름은 TablePro로 표기합니다. 한 단어로 쓰며 T와 P는 대문자입니다.",
"usage": "TablePro를 언급하는 용도라면 기사, 튜토리얼, 리뷰, 소프트웨어 목록, 연동 기능 등에서 TablePro 이름과 로고를 사용할 수 있습니다. 로고를 변경하거나 다른 제품에 사용하거나 TablePro가 보증하는 것처럼 표시해서는 안 됩니다. 그 밖의 용도는 <email>{email}</email>로 문의해 주세요.",
"logo": "로고 다운로드(PNG, {width} × {height})",
"alt": "TablePro 로고"
"body": "TablePro 이름과 로고의 사용 방법, 로고 파일 다운로드는 <brand>브랜드 가이드라인</brand>에서 확인하세요."
}
}
5 changes: 1 addition & 4 deletions resources/data/content/pt-BR/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "Marca",
"name": "O nome se escreve TablePro, em uma só palavra, com T e P maiúsculos.",
"usage": "Você pode usar o nome e o logo do TablePro para se referir ao TablePro, por exemplo em artigos, tutoriais, análises, diretórios e integrações. Não é permitido alterar o logo, usá-lo em outro produto ou sugerir que o TablePro endossa você. Para qualquer outro uso, escreva para <email>{email}</email>.",
"logo": "Baixar o logo (PNG, {width} × {height})",
"alt": "Logo do TablePro"
"body": "As <brand>diretrizes de marca</brand> explicam como usar o nome e o logo do TablePro e trazem os arquivos do logo para baixar."
}
}
5 changes: 1 addition & 4 deletions resources/data/content/vi/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "Thương hiệu",
"name": "Tên được viết là TablePro: một từ, chữ T và chữ P viết hoa.",
"usage": "Bạn có thể dùng tên và logo TablePro để nói đến TablePro, chẳng hạn trong bài viết, hướng dẫn, bài đánh giá, danh mục phần mềm và các tích hợp. Bạn không được sửa logo, dùng logo cho một sản phẩm khác, hay ngụ ý rằng TablePro bảo chứng cho bạn. Trường hợp khác, bạn hãy gửi email tới <email>{email}</email>.",
"logo": "Tải về logo (PNG, {width} × {height})",
"alt": "Logo TablePro"
"body": "<brand>Hướng dẫn thương hiệu</brand> giải thích cách dùng tên và logo TablePro, kèm các file logo để tải về."
}
}
5 changes: 1 addition & 4 deletions resources/data/content/zh-Hans/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "品牌",
"name": "名称写作 TablePro:一个词,T 和 P 大写。",
"usage": "您可以在文章、教程、评测、软件目录和集成中使用 TablePro 的名称和徽标来指代 TablePro。请勿修改徽标、将其用于其他产品,或暗示 TablePro 为您背书。其他用途请发送邮件至 <email>{email}</email>。",
"logo": "下载徽标(PNG,{width} × {height})",
"alt": "TablePro 徽标"
"body": "<brand>品牌指南</brand>说明了如何使用 TablePro 的名称和徽标,并提供徽标文件下载。"
}
}
5 changes: 1 addition & 4 deletions resources/data/content/zh-Hant/about.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@
},
"brand": {
"title": "品牌",
"name": "名稱寫作 TablePro:一個詞,T 與 P 大寫。",
"usage": "您可以在文章、教學、評測、軟體目錄和整合中使用 TablePro 的名稱和標誌來指稱 TablePro。請勿修改標誌、將其用於其他產品,或暗示 TablePro 為您背書。其他用途請寄送郵件至 <email>{email}</email>。",
"logo": "下載標誌(PNG,{width} × {height})",
"alt": "TablePro 標誌"
"body": "<brand>品牌指南</brand>說明了如何使用 TablePro 的名稱和標誌,並提供標誌檔案下載。"
}
}
13 changes: 11 additions & 2 deletions resources/js/components/faq/content-links.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ const EXTERNAL: Record<string, { key: ExternalKey; englishOnly: boolean }> = {
/**
* Renderers for every tag FAQ, iPhone-page and about-page copy may use, for `<Trans>`:
* the link tags above, `<account>` (the account app, in the reader's
* language), `<email>` (the support address; its text is the address unless
* the copy gives other words) and `<ui>` (an app label in a click path).
* language), `<brand>` (the brand guidelines, in English only), `<email>`
* (the support address; its text is the address unless the copy gives other
* words) and `<ui>` (an app label in a click path).
*
* A tag whose destination the data does not name renders its words as plain
* text, so a missing URL never becomes a broken link. A link to the English
Expand All @@ -99,6 +100,14 @@ export function useContentTags(links: SiteLinks): Record<string, (text: string)
) : (
text
),
brand: (text) => (
<>
<a href="/brand" hrefLang="en" className={textLinkClasses('inline')}>
{text}
</a>
{locale !== 'en' && ` ${m.common.englishOnly}`}
</>
),
};

for (const [name, path] of Object.entries(INTERNAL)) {
Expand Down
28 changes: 7 additions & 21 deletions resources/js/pages/About.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ interface AboutProps {
repositoryCreated: { date: string; formatted: string } | null;
links: SiteLinks;
organizationProfiles: string[];
/** The logo file under public/, or null when there is none to offer. */
logo: { src: string; width: number; height: number } | null;
}

interface JsonLdInput {
Expand Down Expand Up @@ -57,12 +55,12 @@ function aboutJsonLd({ baseUrl, inLanguage, pageUrl, content, crumbs, descriptio

/**
* `/about` in every locale: who makes TablePro and where, how it is funded,
* where the code is, how to reach the maker, and the brand files.
* where the code is, how to reach the maker, and where the brand guidelines are.
*
* The name, city and country fill `{maker}`, `{city}` and `{country}` from
* the `publisher` prop; no sentence holds them.
*/
export default function About({ content, publisher, repositoryCreated, links, organizationProfiles, logo }: AboutProps) {
export default function About({ content, publisher, repositoryCreated, links, organizationProfiles }: AboutProps) {
const { canonicalBaseUrl } = usePage().props;
const { locale, m, fmt, path } = useI18n();
const tags = useContentTags(links);
Expand All @@ -72,7 +70,6 @@ export default function About({ content, publisher, repositoryCreated, links, or
...(publisher?.city && { city: publisher.city }),
...(publisher?.country && { country: publisher.country }),
...(links.email !== null && { email: links.email }),
...(logo !== null && { width: logo.width, height: logo.height }),
};

const rich = (text: string): ReactNode => <Trans text={text} tags={tags} values={values} />;
Expand Down Expand Up @@ -160,22 +157,11 @@ export default function About({ content, publisher, repositoryCreated, links, or
</div>
</Section>

{logo !== null && (
<Section id="brand" title={content.brand.title} width="text">
<div className="type-body space-y-6 text-foreground">
<div className="space-y-4">
<p>{content.brand.name}</p>
<p>{rich(content.brand.usage)}</p>
</div>
<div className="flex flex-wrap items-center gap-x-6 gap-y-4">
<img src={logo.src} alt={content.brand.alt} width={64} height={64} loading="lazy" decoding="async" className="size-16" />
<a href={logo.src} download className={textLinkClasses('standalone')}>
{fmt(content.brand.logo, values)}
</a>
</div>
</div>
</Section>
)}
<Section id="brand" title={content.brand.title} width="text">
<div className="type-body space-y-4 text-foreground">
<p>{rich(content.brand.body)}</p>
</div>
</Section>
</LandingLayout>
);
}
12 changes: 6 additions & 6 deletions tests/Feature/Landing/AboutPageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function aboutKnownTags(): array
preg_match('/const EXTERNAL: Record<string, \{[^}]*\}> = \{(.*?)\n\};/s', $source, $external);
preg_match_all('/^\s+([a-zA-Z]+):/m', ($internal[1] ?? '') . "\n" . ($external[1] ?? ''), $keys);

return [...$keys[1], 'ui', 'account', 'email'];
return [...$keys[1], 'ui', 'account', 'email', 'brand'];
}

function aboutVisibleText(string $html): string
Expand Down Expand Up @@ -81,7 +81,6 @@ function aboutVisibleText(string $html): string
->where('links.issues', fn(string $url): bool => str_starts_with($url, 'https://github.com/'))
->where('links.discussions', fn(string $url): bool => str_starts_with($url, 'https://github.com/'))
->where('links.sponsorsProgram', 'https://github.com/sponsors/datlechin')
->where('logo', ['src' => '/logo.png', 'width' => 256, 'height' => 256])
->where('seo.robots', 'index, follow'));
})->with(aboutLocales());

Expand All @@ -99,10 +98,10 @@ function aboutVisibleText(string $html): string
preg_match_all('/\{([A-Za-z][A-Za-z0-9_.]*)\}/', $text, $slots);
preg_match_all('/<([a-z][A-Za-z0-9]*)>/', $text, $tags);

expect(array_values(array_diff(array_unique($slots[1]), ['maker', 'city', 'country', 'email', 'width', 'height'])))->toBe([], "content/{$locale}/about.json uses a slot the page does not fill");
expect(array_values(array_diff(array_unique($slots[1]), ['maker', 'city', 'country', 'email'])))->toBe([], "content/{$locale}/about.json uses a slot the page does not fill");
expect(array_values(array_diff(array_unique($tags[1]), aboutKnownTags())))->toBe([], "content/{$locale}/about.json uses a link tag with no destination");
expect($content['policies']['body'])->toMatch('#<security>[^<]+</security>#u');
expect($content['brand']['usage'] ?? null)->toBeString()->toContain('TablePro')->toContain('<email>{email}</email>');
expect($content['brand']['body'])->toMatch('#<brand>[^<]+</brand>#u');
})->with(aboutLocales());

it('types the publisher in facts.json only', function (): void {
Expand Down Expand Up @@ -147,14 +146,15 @@ function aboutVisibleText(string $html): string
}

Assert::assertDoesNotMatchRegularExpression('/\{[a-zA-Z]+\}/', $text, "{$path} leaves a slot unfilled");
expect($html)->toContain('href="/logo.png" download=""');

preg_match('#<section[^>]*id="policies".*?</section>#s', $html, $policies);
preg_match('#<section[^>]*id="brand".*?</section>#s', $html, $brand);
preg_match('#<footer\b.*?</footer>#s', $html, $footer);
preg_match('/[\'"]?englishOnly[\'"]?:\s*([\'"])(.*?)\1/u', File::get(resource_path("js/i18n/messages/{$locale}/common.ts")), $marker);

expect($policies[0] ?? '')->toContain("href=\"{$prefix}/security\"");
expect($brand[0] ?? '')->toContain('href="mailto:hello@tablepro.app"');
expect($brand[0] ?? '')->toContain('href="/brand" hrefLang="en"');
expect(str_contains(aboutVisibleText($brand[0] ?? ''), $marker[2]))->toBe($locale !== Locales::default());
expect(aboutVisibleText($footer[0] ?? ''))->toContain($publisher['name'])
->toContain($publisher['city'][$locale])
->toContain('AGPLv3');
Expand Down