{"id":36371792,"url":"https://github.com/in2code-de/sitescore","last_synced_at":"2026-01-11T14:01:46.489Z","repository":{"id":330121056,"uuid":"1119622609","full_name":"in2code-de/sitescore","owner":"in2code-de","description":"Sitescore provides an at-a-glance SEO and content quality dashboard directly in the TYPO3 page module","archived":false,"fork":false,"pushed_at":"2026-01-01T19:28:13.000Z","size":1613,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-03T01:26:20.220Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/in2code-de.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-19T15:13:48.000Z","updated_at":"2026-01-02T01:02:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/in2code-de/sitescore","commit_stats":null,"previous_names":["in2code-de/sitescore"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/in2code-de/sitescore","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/in2code-de%2Fsitescore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/in2code-de%2Fsitescore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/in2code-de%2Fsitescore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/in2code-de%2Fsitescore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/in2code-de","download_url":"https://codeload.github.com/in2code-de/sitescore/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/in2code-de%2Fsitescore/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28306985,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-11T11:18:18.743Z","status":"ssl_error","status_checked_at":"2026-01-11T11:07:56.842Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-11T14:01:05.396Z","updated_at":"2026-01-11T14:01:46.446Z","avatar_url":"https://github.com/in2code-de.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sitescore - AI-powered SEO/GEO and Content Quality Dashboard for TYPO3\n\n## Table of Contents\n\n- [Introduction](#introduction)\n- [Screenshots](#screenshots)\n- [Google Gemini API](#google-gemini-api)\n- [Installation](#installation)\n- [Usage](#usage)\n  - [In the Page Module](#in-the-page-module)\n  - [CLI Commands](#cli-commands)\n- [Custom LLM Integration](#custom-llm-integration-like-chatgpt-claude-copilot-mistral-etc)\n- [Changelog](#changelog)\n- [Contribution with DDEV](#contribution-with-ddev)\n\n## Introduction\n\nSitescore provides an at-a-glance SEO/GEO and content quality dashboard directly in the TYPO3 page module. \nIt displays key metrics as visual score gauges inspired by Google Pagespeed, helping editors identify and fix content \nissues without leaving the backend.\n\nThe extension analyzes pages using AI (Google Gemini by default) and provides scores for 5 categories:\n\n- **GEO \u0026 SEO**: Generative Engine Optimization and Search Engine Optimization\n- **Performance**: Page structure, image optimization, CSS/JS inclusion\n- **Semantics**: HTML5 semantics, heading hierarchy, ARIA labels\n- **Keyword Optimization**: Keyword usage based on `pages.tx_sitescore_keyword` field\n- **Accessibility**: WCAG compliance, keyboard navigation, screen reader support\n\nEach analysis includes actionable suggestions to improve your content quality and SEO rankings.\n\n## Screenshots\n\n![Sitescore in Page Module](Documentation/Images/screenshot-pagemodule-short.png)\n\n*Sitescore dashboard integrated in TYPO3 page module*\n\n![Sitescore Dark Mode](Documentation/Images/screenshot-pagemodule-list-darkmode.png)\n\n*Analysis results with suggestions (dark mode)*\n\n![Sitescore TYPO3 Dashboard](Documentation/Images/screenshot-dashboard.png)\n\n*Add sitescore wizards to your dashboard*\n\n## Google Gemini API\n\n- To use the extension, you need a **Google Gemini API** key. You can register for one at https://aistudio.google.com/app/api-keys.\n- Alternatively, you can implement your own LLM provider (see [Custom LLM Integration](#custom-llm-integration-like-chatgpt-claude-copilot-mistral-etc) below).\n\n## Installation\n\n### With composer\n\n```\ncomposer req in2code/sitescore\n```\n\n### Main configuration\n\nAfter installation, configure the extension in Extension Manager or via environment variables:\n\n| Title    | Default value | Description                                                                                                                                           |\n|----------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|\n| apiKey   | -             | Google Gemini API key. You can leave this empty and use the `GOOGLE_API_KEY` environment variable instead (recommended for CI/CD pipelines)           |\n\n**Recommended:** Use environment variables instead of saving the API key in Extension Manager:\n\n```\nGOOGLE_API_KEY=your_api_key_from_google\n```\n\n## Usage\n\n### In the Page Module\n\n1. Navigate to the **Page** module in TYPO3 backend\n2. Select a page in the page tree\n3. You'll see the **Sitescore Analysis** section at the top\n4. Click **\"Analyze Page\"** to perform a new analysis\n5. View the scores and suggestions by expanding the results\n\n### CLI Commands\n\n#### Analyze a single page or page tree\n\nAnalyzes pages automatically with a scheduler command\n\n```\n# Analyze single page\n./vendor/bin/typo3 sitescore:analyzePage 123\n\n# Analyze page tree recursively (page + all subpages with 99 recursion level)\n./vendor/bin/typo3 sitescore:analyzePage 123 99\n```\n\n#### Clear all analysis data\n\nThis removes all stored analysis results from the database. Useful for fresh starts or testing.\n\n```\n# Truncate all analysis records!\n./vendor/bin/typo3 sitescore:truncateAnalysis\n```\n\n## Custom LLM Integration (like ChatGPT, Claude, Copilot, Mistral, etc...)\n\nSitescore uses a factory pattern to allow custom LLM providers. By default, it uses Google Gemini, \nbut you can easily integrate other AI services (OpenAI, Claude, local models, etc.).\n\n### Implementing a Custom LLM Repository\n\n1. Create a custom repository class implementing `LlmRepositoryInterface` - see example for Mistral AI::\n\n```\n\u003c?php\n\ndeclare(strict_types=1);\n\nnamespace In2code\\SitescoreTest\\Domain\\Repository\\Llm;\n\nuse In2code\\Sitescore\\Domain\\Repository\\Llm\\AbstractRepository;\nuse In2code\\Sitescore\\Domain\\Repository\\Llm\\RepositoryInterface;\nuse In2code\\Sitescore\\Exception\\ApiException;\nuse In2code\\Sitescore\\Exception\\ConfigurationException;\nuse TYPO3\\CMS\\Core\\Http\\RequestFactory;\n\nclass MistralRepository extends AbstractRepository implements RepositoryInterface\n{\n    private string $apiKey = '';\n    private string $apiUrl = 'https://api.mistral.ai/v1/chat/completions';\n\n    public function __construct(\n        protected RequestFactory $requestFactory,\n    ) {\n        // Get API key from environment variable or extension configuration\n        $this-\u003eapiKey = getenv('MISTRAL_API_KEY') ?: '';\n    }\n\n    public function checkApiKey(): void\n    {\n        if ($this-\u003eapiKey === '') {\n            throw new ConfigurationException('Mistral API key not configured', 1735200000);\n        }\n    }\n\n    public function getApiUrl(): string\n    {\n        return $this-\u003eapiUrl;\n    }\n\n    public function getOptions(string $html, string $pageTitle, string $keyword): array\n    {\n        $prompt = $this-\u003egetPrompt($html, $pageTitle, $keyword);\n\n        return [\n            'headers' =\u003e [\n                'Authorization' =\u003e 'Bearer ' . $this-\u003eapiKey,\n                'Content-Type' =\u003e 'application/json',\n            ],\n            'body' =\u003e json_encode([\n                'model' =\u003e 'mistral-large-latest',\n                'messages' =\u003e [\n                    [\n                        'role' =\u003e 'user',\n                        'content' =\u003e $prompt,\n                    ],\n                ],\n                'temperature' =\u003e 0.1,\n                'max_tokens' =\u003e 2048,\n            ]),\n        ];\n    }\n\n    public function analyzePageContent(string $html, string $pageTitle, string $keyword): array\n    {\n        $this-\u003echeckApiKey();\n        return $this-\u003egenerateAnalysis($html, $pageTitle, $keyword);\n    }\n\n    protected function generateAnalysis(string $html, string $pageTitle, string $keyword): array\n    {\n        $response = $this-\u003erequestFactory-\u003erequest(\n            $this-\u003egetApiUrl(),\n            'POST',\n            $this-\u003egetOptions($html, $pageTitle, $keyword)\n        );\n\n        if ($response-\u003egetStatusCode() !== 200) {\n            throw new ApiException(\n                'Failed to analyze page with Mistral: ' . $response-\u003egetBody()-\u003egetContents(),\n                1735200001\n            );\n        }\n\n        $responseData = json_decode($response-\u003egetBody()-\u003egetContents(), true);\n        return $this-\u003eparseResponse($responseData);\n    }\n\n    protected function parseResponse(array $responseData): array\n    {\n        if (isset($responseData['choices'][0]['message']['content']) === false) {\n            throw new ApiException('Invalid Mistral API response structure', 1735200002);\n        }\n\n        $text = $responseData['choices'][0]['message']['content'];\n\n        // Extract JSON from markdown code blocks if present\n        if (preg_match('/```json\\s*(\\{.*?\\})\\s*```/s', $text, $matches)) {\n            $text = $matches[1];\n        } elseif (preg_match('/```\\s*(\\{.*?\\})\\s*```/s', $text, $matches)) {\n            $text = $matches[1];\n        }\n\n        $data = json_decode($text, true);\n        if ($data === false || isset($data['scores']) === false || isset($data['suggestions']) === false) {\n            throw new ApiException('Could not parse Mistral response as JSON', 1735200003);\n        }\n\n        return $data;\n    }\n}\n```\n\n2. Register your custom repository in `ext_localconf.php`:\n\n```\n\u003c?php\ndefined('TYPO3') || die();\n\n// Register custom LLM repository\n$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['sitescore']['llmRepositoryClass']\n    = \\Vendor\\MyExtension\\Repository\\MyCustomLlmRepository::class;\n```\n\n3. Clear TYPO3 caches:\n\n```\n./vendor/bin/typo3 cache:flush\n```\n\nYour custom LLM provider will now be used for all content analysis instead of Google Gemini.\n\n## Changelog\n\n| Version | Date       | State   | Description                                                                                                                 |\n|---------|------------|---------|-----------------------------------------------------------------------------------------------------------------------------|\n| 2.0.0   | 2025-01-06 | Feature | Add dashboard widgets of sitescore, prevent counting of tags with LLM (wrong results), support non-translated page analyzes |\n| 1.0.1   | 2025-01-01 | Task    | Small documentation update                                                                                                  |\n| 1.0.0   | 2025-12-30 | Task    | Initial release with Google Gemini integration and 5 analysis categories                                                    |\n\n## Contribution with DDEV\n\nThis repository provides a [DDEV](https://ddev.readthedocs.io)-backed development environment. \nIf DDEV is installed, simply run the following commands to quickly set up a local environment:\n\n```\n# Start DDEV\nddev start\n\n# Import a dummy Database and use a default configuration\nddev initialize\n```\n\n**URLs:**\n- Web: https://sitescore.ddev.site\n- Backend: https://sitescore.ddev.site/typo3\n\n**Backend Login:**\n```\nUsername: admin\nPassword: admin\n```\n\n**Installation requirements:**\n\n1. Install DDEV: https://ddev.readthedocs.io/en/stable/#installation\n2. Install git-lfs: https://git-lfs.github.com/\n3. (Optional) Create `.ddev/.env` with your Google API key:\n\n```\nGOOGLE_API_KEY=your_api_key_from_google\n```\n\n**Development commands:**\n\n```\n# Install dependencies\nddev composer install\n\n# Flush TYPO3 cache\nddev exec .Build/bin/typo3 cache:flush\n\n# Analyze a page\nddev exec .Build/bin/typo3 sitescore:analyzePage 1\n\n# Access database\nddev mysql\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fin2code-de%2Fsitescore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fin2code-de%2Fsitescore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fin2code-de%2Fsitescore/lists"}