glanzlicht / MeinWeimar_APP

Official MeinWeimar App

Verified secure showcase
204 Files 2 MB Public size 14 Redacted Dart · PHP · XML · GITIGNORE · YAML · PLIST

Project overview

MeinWeimar_APP is a verified code showcase with 204 visible project entries across 2 top-level modules. The primary detected language is Dart; protected files remain visible in the structure while their contents stay private.

README

MeinWeimar — Flutter App & WordPress Plugin

> Working in Progress · Private portfolio repository · Active development

MeinWeimar is a location-based local platform for residents, students and visitors. This repository intentionally contains only two product parts:

  1. the Flutter consumer app;
  2. the meinweimar-core WordPress plugin that provides the editorial/admin
  3. layer and the REST contract consumed by the app.

The complete public website, WordPress core, databases, private infrastructure and unrelated platform modules are not part of this repository.

MeinWeimar consumer app preview

Why this project is interesting

MeinWeimar combines a mobile/web Flutter client with WordPress used as a structured content and administration backend. The goal is a maintainable product boundary: content, branding, navigation, categories, media and configuration are managed in WordPress and delivered through a versioned REST contract instead of being hardcoded in the Flutter UI.

The project demonstrates work across:

  • Flutter/Dart application architecture for iOS, Android and Web;
  • WordPress plugin architecture with custom post types, taxonomies, admin
  • settings and REST endpoints;

  • localized German/English UI and backend-controlled labels;
  • discovery/list/detail flows for places, services, events, classifieds, jobs
  • and real estate;

  • map, GPS, geocoding, marker and navigation contracts;
  • caching, ETag/config-version handling and offline-safe fallbacks;
  • role-aware account, claim, favorites, moderation and messaging boundaries;
  • automated Flutter tests, PHP/REST contract checks and release builds.

Repository structure

flutter-app/       Flutter consumer application
wordpress-plugin/  meinweimar-core WordPress plugin
screenshots/       Curated portfolio preview only

Current status

This is an active Working in Progress project. The core architecture and many end-to-end flows are implemented and continuously tested. Native device polish, production content, final legal/privacy review and further product modules are still being developed. The repository does not claim to be a finished production release.

Local development

Flutter app

cd flutter-app
flutter pub get
flutter analyze
flutter test
flutter run --dart-define=MW_API_BASE_URL=https://example.test/wp-json/meinweimar/v1

The API base URL and other environment-specific values are supplied at build or run time. No credentials are stored in the repository.

WordPress plugin

Copy wordpress-plugin into a WordPress installation's wp-content/plugins/meinweimar-core directory and activate it in the admin area. The plugin owns the MeinWeimar content types, editorial settings, permissions, REST responses, media/configuration contracts and frontend integration points.

Privacy and repository boundary

This repository is private and is prepared for controlled technical review. Secrets, production database exports, user data, WordPress uploads, private keys and infrastructure configuration must remain outside version control. The public-facing portfolio page uses a separate, redacted presentation layer and does not expose the private repository directly.

For technical reviewers

The most relevant entry points are:

  • flutter-app/lib/core — shared configuration, networking, localization,
  • theme and location contracts;

  • flutter-app/lib/features — consumer-facing product surfaces;
  • flutter-app/lib/repositories — API boundary and data access;
  • wordpress-plugin/includes — plugin services, REST, admin and contracts;
  • wordpress-plugin/assets — admin/frontend integration assets.

MeinWeimar is deliberately presented as an evolving product rather than a finished demo: the repository shows the architectural decisions, the working implementation and the remaining engineering work transparently.

wordpress-plugin/includes/class-detail-renderer.phpPHP
<?php
declare(strict_types=1);
namespace MeinWeimar;
use WP_Post;
final class DetailRenderer
{
public static function place_markup(int $post_id = 0): string
{
return self::resource_markup($post_id, 'places');
}
public static function event_markup(int $post_id = 0): string
{
return self::resource_markup($post_id, 'events');
}
public static function resource_markup(int $post_id = 0, string $route = ''): string
{
$post_type = $route ? ContentTypes::post_type_for_route($route) : null;
$post = $post_type ? self::resolve_post($post_id, $post_type) : self::resolve_resource($post_id);
return $post ? self::markup($post, 'mw_event' === $post->post_type) : '';
}
public static function markup(WP_Post $post, bool $is_event): string
{
Renderer::enqueue_assets();
$item = RestApi::public_item($post);
$address = array_filter([
$item['address']['line1'] ?? '',
$item['address']['line2'] ?? '',
trim(implode(' ', array_filter([
$item['address']['postal_code'] ?? '',
$item['address']['locality'] ?? '',
]))),
$item['address']['region'] ?? '',
]);
$location = implode(', ', $address);
$navigation_url = self::navigation_url((array) ($item['geo'] ?? []), $location);
$place_details = is_array($item['place_details'] ?? null) ? $item['place_details'] : [];
$opening_hours = self::opening_hours_rows((array) ($place_details['opening_hours'] ?? []));
$event = is_array($item['event'] ?? null) ? $item['event'] : [];
$listing = is_array($item['listing'] ?? null) ? $item['listing'] : [];
$resource = ContentTypes::route_for_post_type($post->post_type);
$resource_definition = ContentTypes::definitions()[$resource] ?? [];
$category_taxonomy = ContentTypes::category_taxonomy($post->post_type);
$category_slugs = $category_taxonomy
? array_values(array_filter(array_map('sanitize_title', (array) (($item['terms'] ?? [])[$category_taxonomy] ?? []))))
: [];
$condition_values = FieldSchemas::values_for_post($post, $resource);
ob_start();
?>
<article class="mw-detail" data-mw-detail data-type="<?php echo esc_attr($resource); ?>" data-post-id="<?php echo esc_attr((string) $post->ID); ?>">
<header class="mw-detail__header">
<p class="mw-detail__eyebrow"><?php echo esc_html((string) ($resource_definition['singular'] ?? __('Eintrag', 'meinweimar-core'))); ?></p>
<h1 class="mw-detail__title"><?php echo esc_html($item['title']); ?></h1>
<?php if ($location) : ?><p class="mw-detail__location"><?php echo esc_html($location); ?></p><?php endif; ?>
<?php if (is_user_logged_in()) : ?>
<div class="mw-detail__actions" data-mw-favorite data-object-type="<?php echo esc_attr($resource); ?>" data-object-id="<?php echo esc_attr((string) $post->ID); ?>">
<button type="button" class="mw-detail__favorite" data-mw-favorite-toggle aria-pressed="false"><?php esc_html_e('Speichern', 'meinweimar-core'); ?></button>
<span class="mw-detail__favorite-status" data-mw-favorite-status role="status" aria-live="polite"></span>
</div>
<?php else : ?>
<p class="mw-detail__favorite-login"><a href="<?php echo esc_url(wp_login_url(get_permalink($post->ID) ?: home_url('/'))); ?>"><?php esc_html_e('Anmelden, um Inhalte zu speichern', 'meinweimar-core'); ?></a></p>
<?php endif; ?>
</header>
<div class="mw-detail__layout">
<div class="mw-detail__main">
<?php $primary_media = $item['media'][0] ?? []; ?>
<?php $media_placeholder = !empty($item['media_placeholder']); ?>
<?php if (!empty($primary_media['url']) || !empty($item['image_url'])) : ?>
<figure class="mw-detail__primary-media">
<img class="mw-detail__image" src="<?php echo esc_url((string) ($primary_media['url'] ?? $item['image_url'])); ?>" alt="<?php echo esc_attr((string) ($primary_media['alt'] ?? $item['title'] ?? '')); ?>">
<?php if (($media_placeholder || !empty($primary_media['is_placeholder'])) && ImageSuggestions::supports_target_type($resource)) : ?>
<?php echo self::image_suggestion_markup($resource, $post->ID, (string) $item['title'], true); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
</figure>
<?php else : ?>
<?php if (ImageSuggestions::supports_target_type($resource)) : ?>
<?php echo self::image_suggestion_markup($resource, $post->ID, (string) $item['title'], $media_placeholder); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php endif; ?>
<?php endif; ?>
<div class="mw-detail__content">
<?php echo wp_kses_post((string) ($item['content']['html'] ?? wpautop($post->post_content))); ?>
</div>
<?php if (count((array) ($item['media'] ?? [])) > 1) : ?>
<section class="mw-detail__gallery" aria-label="<?php esc_attr_e('Weitere Bilder', 'meinweimar-core'); ?>">
<?php foreach (array_slice((array) $item['media'], 1) as $media) : ?>
<?php if (empty($media['url'])) { continue; } ?>
<figure class="mw-detail__gallery-item">
<img src="<?php echo esc_url((string) $media['url']); ?>" alt="<?php echo esc_attr((string) ($media['alt'] ?? '')); ?>">
<?php if (!empty($media['caption'])) : ?><figcaption><?php echo esc_html((string) $media['caption']); ?></figcaption><?php endif; ?>
</figure>
<?php endforeach; ?>
</section>
<?php endif; ?>
</div>
<aside class="mw-detail__aside">
<?php if ($location) : ?>
<section class="mw-detail__section"><h2><?php esc_html_e('Adresse', 'meinweimar-core'); ?></h2><p><?php echo esc_html($location); ?></p><?php if ($navigation_url) : ?><p><a class="mw-detail__navigate" href="<?php echo esc_url($navigation_url); ?>" target="_blank" rel="noopener noreferrer"><?php esc_html_e('Navigieren', 'meinweimar-core'); ?></a></p><?php endif; ?></section>
<?php endif; ?>
<?php if (!empty($item['contact']['phone']) || !empty($item['contact']['email']) || !empty($item['contact']['website_url']) || !empty($item['contact']['facebook_url']) || !empty($item['contact']['twitter_url']) || !empty($item['contact']['video_url'])) : ?>
<section class="mw-detail__section">
<h2><?php esc_html_e('Kontakt', 'meinweimar-core'); ?></h2>
<?php if (!empty($item['contact']['phone'])) : ?><p><a href="tel:<?php echo esc_attr($item['contact']['phone']); ?>"><?php echo esc_html($item['contact']['phone']); ?></a></p><?php endif; ?>
<?php if (!empty($item['contact']['email'])) : ?><p><a href="mailto:<?php echo esc_attr($item['contact']['email']); ?>"><?php echo esc_html($item['contact']['email']); ?></a></p><?php endif; ?>
<?php if (!empty($item['contact']['website_url'])) : ?><p><a href="<?php echo esc_url($item['contact']['website_url']); ?>" rel="noopener"><?php esc_html_e('Website öffnen', 'meinweimar-core'); ?></a></p><?php endif; ?>
<?php foreach (['facebook_url' => __('Facebook öffnen', 'meinweimar-core'), 'twitter_url' => __('Twitter / X öffnen', 'meinweimar-core'), 'video_url' => __('Video öffnen', 'meinweimar-core')] as $social_key => $social_label) : ?>
<?php if (!empty($item['contact'][$social_key])) : ?><p><a href="<?php echo esc_url((string) $item['contact'][$social_key]); ?>" rel="noopener"><?php echo esc_html($social_label); ?></a></p><?php endif; ?>
<?php endforeach; ?>
</section>
<?php endif; ?>
<?php if ($opening_hours) : ?>
<section class="mw-detail__section">
<h2><?php esc_html_e('Öffnungszeiten', 'meinweimar-core'); ?></h2>
<?php foreach ($opening_hours as $opening_row) : ?><p><?php echo esc_html($opening_row); ?></p><?php endforeach; ?>
</section>
<?php endif; ?>
<?php if ($place_details && !$is_event) : ?>
<section class="mw-detail__section">
<h2><?php esc_html_e('Weitere Informationen', 'meinweimar-core'); ?></h2>
<?php if (!empty($place_details['featured'])) : ?><p class="mw-detail__status"><?php esc_html_e('Empfohlen', 'meinweimar-core'); ?></p><?php endif; ?>
<?php if (isset($place_details['rating_average']) && null !== $place_details['rating_average'] && '' !== (string) $place_details['rating_average']) : ?>
<p><strong><?php esc_html_e('Bewertung', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(number_format_i18n((float) $place_details['rating_average'], 1)); ?> / 5<?php if (!empty($place_details['rating_count'])) : ?> (<?php echo esc_html(number_format_i18n((int) $place_details['rating_count'])); ?>)<?php endif; ?></p>
<?php endif; ?>
<?php if (!empty($place_details['special_offers'])) : ?><p><strong><?php esc_html_e('Sonderangebote', 'meinweimar-core'); ?>:</strong> <?php echo esc_html((string) $place_details['special_offers']); ?></p><?php endif; ?>
<?php if (!empty($place_details['neighbourhood'])) : ?><p><strong><?php esc_html_e('Umgebung', 'meinweimar-core'); ?>:</strong> <?php echo esc_html((string) $place_details['neighbourhood']); ?></p><?php endif; ?>
<?php if (!empty($place_details['expire_date'])) : ?><p><strong><?php esc_html_e('Ablaufdatum', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(self::format_date((string) $place_details['expire_date'])); ?></p><?php endif; ?>
</section>
<?php endif; ?>
<?php if ($is_event && $event) : ?>
<section class="mw-detail__section">
<h2><?php esc_html_e('Termin', 'meinweimar-core'); ?></h2>
<?php if (!empty($event['occurrences'])) : ?>
<?php foreach ((array) $event['occurrences'] as $occurrence) : ?>
<?php if (!is_array($occurrence) || empty($occurrence['starts_at'])) { continue; } ?>
<p><strong><?php esc_html_e('Termin', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(self::format_datetime((string) $occurrence['starts_at'])); ?><?php if (!empty($occurrence['ends_at'])) : ?>–<?php echo esc_html(self::format_datetime((string) $occurrence['ends_at'])); ?><?php endif; ?><?php if (!empty($occurrence['timezone'])) : ?> (<?php echo esc_html((string) $occurrence['timezone']); ?>)<?php endif; ?></p>
<?php if (!empty($occurrence['status']) && 'scheduled' !== $occurrence['status']) : ?><p class="mw-detail__status"><strong><?php esc_html_e('Status', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(self::event_status_label((string) $occurrence['status'])); ?></p><?php endif; ?>
<?php endforeach; ?>
<?php else : ?>
<?php if (!empty($event['start'])) : ?><p><strong><?php esc_html_e('Beginn', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(self::format_datetime((string) $event['start'])); ?></p><?php endif; ?>
<?php if (!empty($event['end'])) : ?><p><strong><?php esc_html_e('Ende', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(self::format_datetime((string) $event['end'])); ?></p><?php endif; ?>
<?php endif; ?>
<?php if (!empty($event['status'])) : ?><p class="mw-detail__status"><strong><?php esc_html_e('Status', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(self::event_status_label((string) $event['status'])); ?></p><?php endif; ?>
<?php if (!empty($event['organizer_name'])) : ?><p><strong><?php esc_html_e('Veranstalter', 'meinweimar-core'); ?>:</strong> <?php echo esc_html((string) $event['organizer_name']); ?></p><?php endif; ?>
<?php if (!empty($event['organizer_url'])) : ?><p><a href="<?php echo esc_url((string) $event['organizer_url']); ?>" rel="noopener"><?php esc_html_e('Veranstalter-Website öffnen', 'meinweimar-core'); ?></a></p><?php endif; ?>
<?php if (!empty($event['ticket_mode'])) : ?><p><strong><?php esc_html_e('Tickets / Anmeldung', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(self::event_ticket_label((string) $event['ticket_mode'])); ?></p><?php endif; ?>
<?php if (!empty($event['ticket_url'])) : ?><p><a href="<?php echo esc_url((string) $event['ticket_url']); ?>" rel="noopener"><?php esc_html_e('Tickets / Anmeldung öffnen', 'meinweimar-core'); ?></a></p><?php endif; ?>
<?php if (isset($event['ticket_price_from']) && '' !== (string) $event['ticket_price_from']) : ?><p><strong><?php esc_html_e('Ticketpreis ab', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(number_format_i18n((float) $event['ticket_price_from'], 2)); ?> €</p><?php endif; ?>
<?php if (isset($event['ticket_price_to']) && '' !== (string) $event['ticket_price_to']) : ?><p><strong><?php esc_html_e('Ticketpreis bis', 'meinweimar-core'); ?>:</strong> <?php echo esc_html(number_format_i18n((float) $event['ticket_price_to'], 2)); ?> €</p><?php endif; ?>
<?php if (!empty($event['calendar_url'])) : ?><p><a href="<?php echo esc_url((string) $event['calendar_url']); ?>" download><?php esc_html_e('Kalenderdatei speichern', 'meinweimar-core'); ?></a></p><?php endif; ?>
</section>
<?php endif; ?>
<?php if ($listing) : ?>
<section class="mw-detail__section">
<h2><?php esc_html_e('Angebot', 'meinweimar-core'); ?></h2>
<?php if ('real-estate' === $resource && 'no' === (string) ($listing['commission_applicable'] ?? '')) : ?>
<p class="mw-detail__status"><strong><?php esc_html_e('Provisionsfrei', 'meinweimar-core'); ?></strong></p>
<?php endif; ?>
<?php foreach (FieldSchemas::for_route($resource) as $meta_key => $field) : ?>
<?php
$field_group = (string) ($field['api_group'] ?? '');
$field_key = (string) ($field['api_key'] ?? '');
$is_listing_field = 'listing' === $field_group || ('lifecycle' === $field_group && 'expire_date' === $field_key);
if (!$is_listing_field || !array_key_exists($field_key, $listing) || !FieldSchemas::field_applies($resource, $meta_key, $category_slugs, $condition_values)) {
continue;
}
$raw_value = $listing[$field_key];
if (null === $raw_value || '' === (string) $raw_value || in_array((string) $raw_value, ['unknown', 'no'], true)) {
continue;
}
if ('energy_source' === $field_key &&
strtolower(trim((string) $raw_value)) === strtolower(trim((string) ($listing['heating_type'] ?? '')))) {
continue;
}
$display_value = 'date' === ($field['input'] ?? '')
? self::format_date((string) $raw_value)
: self::listing_value_label($field_key, (string) $raw_value);
?>
<p><strong><?php echo esc_html(self::listing_label($field_key)); ?>:</strong> <?php echo esc_html($display_value); ?></p>
<?php endforeach; ?>
</section>
<?php endif; ?>
<?php if (!empty($item['geo']['available'])) : ?>
<section class="mw-detail__section mw-detail__map" data-lat="<?php echo esc_attr((string) $item['geo']['lat']); ?>" data-lng="<?php echo esc_attr((string) $item['geo']['lng']); ?>">
<h2><?php esc_html_e('Karte', 'meinweimar-core'); ?></h2>
<p><?php esc_html_e('Dieser Ort ist für die gemeinsame Kartenansicht verfügbar.', 'meinweimar-core'); ?></p>
</section>
<?php endif; ?>
</aside>
</div>
</article>
<?php
return (string) ob_get_clean();
}
/**
* Build the same external navigation hand-off used by the app.
*
* Google Maps URLs need no API key. On supported devices the URL is
* claimed by the installed Google Maps app; otherwise the browser opens
* the directions page. A configured custom template remains available
* for an explicitly selected system provider.
*/
private static function navigation_url(array $geo, string $address): string
{
if (empty($geo['available']) || !is_numeric($geo['lat']) || !is_numeric($geo['lng'])) {
return '';
}
$latitude = (float) $geo['lat'];
$longitude = (float) $geo['lng'];
if ($latitude < -90 || $latitude > 90 || $longitude < -180 || $longitude > 180) {
return '';
}
$config = (array) (Settings::public_config()['geo'] ?? []);
$provider = (string) ($config['navigation_provider'] ?? 'google_maps');
$template = trim((string) ($config['external_navigation_url_template'] ?? ''));
if ('system' === $provider && '' === $template) {
return '';
}
if ('' !== $template && 'google_maps' !== $provider) {
return strtr($template, [
'{lat}' => (string) $latitude,
'{lng}' => (string) $longitude,
'{coordinates}' => $latitude . ',' . $longitude,
'{destination}' => $latitude . ',' . $longitude,
'{address}' => rawurlencode($address),
]);
}
return add_query_arg([
'api' => '1',
'destination' => $latitude . ',' . $longitude,
'travelmode' => 'driving',
'dir_action' => 'navigate',
], 'https://www.google.com/maps/dir/');
}
/**
* Translate a canonical listing API key for the Website/Elementor view.
* Field membership and ordering come from FieldSchemas; this method only
* supplies human-facing labels and therefore cannot expose foreign CPT
* fields by itself.
*/
private static function listing_label(string $key): string
{
$labels = [
'status' => __('Status', 'meinweimar-core'),
'offer_type' => __('Angebotsart', 'meinweimar-core'),
'provider_type' => __('Anbieter', 'meinweimar-core'),
'price_type' => __('Preisangabe', 'meinweimar-core'),
'price_amount' => __('Betrag', 'meinweimar-core'),
'price_currency' => __('Währung', 'meinweimar-core'),
'price_interval' => __('Preisintervall', 'meinweimar-core'),
'available_from' => __('Verfügbar ab', 'meinweimar-core'),
'price_label' => __('Preis / Vergütung', 'meinweimar-core'),
'company_name' => __('Organisation', 'meinweimar-core'),
'property_kind' => __('Immobilienart', 'meinweimar-core'),
'transaction_type' => __('Vermarktungsart', 'meinweimar-core'),
'commission_applicable' => __('Provision fällig?', 'meinweimar-core'),
'commission_calculation' => __('Berechnungsart', 'meinweimar-core'),
'commission_total' => __('Gesamtprovision', 'meinweimar-core'),
'commission_months' => __('Anzahl Kaltmieten', 'meinweimar-core'),
'commission_buyer_share' => __('Anteil Käufer', 'meinweimar-core'),
'commission_seller_share' => __('Anteil Verkäufer', 'meinweimar-core'),
'commission_vat' => __('Umsatzsteuer', 'meinweimar-core'),
'commission_payer' => __('Zahlungspflichtig', 'meinweimar-core'),
'area' => __('Fläche', 'meinweimar-core'),
'property_rooms' => __('Zimmer', 'meinweimar-core'),
'bedrooms' => __('Schlafzimmer', 'meinweimar-core'),
'bathrooms' => __('Bäder', 'meinweimar-core'),
'floor' => __('Etage', 'meinweimar-core'),
'total_floors' => __('Etagen insgesamt', 'meinweimar-core'),
'year_built' => __('Baujahr', 'meinweimar-core'),
'last_renovated' => __('Letzte Modernisierung', 'meinweimar-core'),
'condition' => __('Zustand', 'meinweimar-core'),
'cold_rent' => __('Kaltmiete', 'meinweimar-core'),
'warm_rent' => __('Warmmiete', 'meinweimar-core'),
'additional_costs' => __('Nebenkosten', 'meinweimar-core'),
'heating_costs_included' => __('Heizkosten in Nebenkosten enthalten', 'meinweimar-core'),
'deposit' => __('Kaution', 'meinweimar-core'),
'house_fee' => __('Hausgeld', 'meinweimar-core'),
'commission_type' => __('Provisionsart', 'meinweimar-core'),
'commission_amount' => __('Provisionsbetrag', 'meinweimar-core'),
'energy_certificate_type' => __('Energieausweis', 'meinweimar-core'),
'energy_value' => __('Endenergiekennwert', 'meinweimar-core'),
'energy_class' => __('Energieeffizienzklasse', 'meinweimar-core'),
'heating_type' => __('Heizungsart', 'meinweimar-core'),
'energy_source' => __('Energieträger', 'meinweimar-core'),
'parking_spaces' => __('Stellplätze', 'meinweimar-core'),
'parking_price' => __('Stellplatzpreis', 'meinweimar-core'),
'brand' => __('Marke', 'meinweimar-core'),
'model' => __('Modell / Typ', 'meinweimar-core'),
'color' => __('Farbe', 'meinweimar-core'),
'size' => __('Größe / Maße', 'meinweimar-core'),
'material' => __('Material', 'meinweimar-core'),
'item_age' => __('Alter', 'meinweimar-core'),
'original_price' => __('Originalpreis', 'meinweimar-core'),
'payment_mode' => __('Zahlungsart', 'meinweimar-core'),
'employment_type' => __('Beschäftigungsart', 'meinweimar-core'),
'work_mode' => __('Arbeitsmodell', 'meinweimar-core'),
'work_hours' => __('Arbeitszeit', 'meinweimar-core'),
'salary_min' => __('Vergütung von', 'meinweimar-core'),
'salary_max' => __('Vergütung bis', 'meinweimar-core'),
'reference_code' => __('Referenznummer', 'meinweimar-core'),
'department' => __('Abteilung', 'meinweimar-core'),
'start_date' => __('Startdatum', 'meinweimar-core'),
'application_url' => __('Bewerbung', 'meinweimar-core'),
'application_email' => __('Bewerbungs-E-Mail', 'meinweimar-core'),
'application_method' => __('Bewerbungsweg', 'meinweimar-core'),
'application_deadline' => __('Bewerbungsfrist', 'meinweimar-core'),
'requirements' => __('Anforderungen', 'meinweimar-core'),
'responsibilities' => __('Aufgaben', 'meinweimar-core'),
'benefits' => __('Vorteile', 'meinweimar-core'),
'languages' => __('Sprachen', 'meinweimar-core'),
'education' => __('Ausbildung', 'meinweimar-core'),
'experience_years' => __('Berufserfahrung', 'meinweimar-core'),
'contact_preference' => __('Kontakt', 'meinweimar-core'),
'expire_date' => __('Ablaufdatum', 'meinweimar-core'),
'amenity_kitchen' => __('Einbauküche', 'meinweimar-core'),
'amenity_balcony' => __('Balkon / Loggia', 'meinweimar-core'),
'amenity_terrace' => __('Terrasse', 'meinweimar-core'),
'amenity_garden' => __('Garten', 'meinweimar-core'),
'amenity_basement' => __('Keller', 'meinweimar-core'),
'amenity_attic' => __('Dachboden', 'meinweimar-core'),
'amenity_elevator' => __('Aufzug', 'meinweimar-core'),
'amenity_guest_toilet' => __('Gäste-WC', 'meinweimar-core'),
'amenity_air_conditioning' => __('Klimaanlage', 'meinweimar-core'),
'amenity_floor_heating' => __('Fußbodenheizung', 'meinweimar-core'),
'amenity_fireplace' => __('Kamin / Ofen', 'meinweimar-core'),
'amenity_furnished' => __('Möbliert', 'meinweimar-core'),
'amenity_barrier_free' => __('Barrierearm / rollstuhlgerecht', 'meinweimar-core'),
'amenity_pets_allowed' => __('Haustiere erlaubt', 'meinweimar-core'),
'amenity_garage' => __('Garage / Tiefgarage', 'meinweimar-core'),
'amenity_carport' => __('Carport', 'meinweimar-core'),
'amenity_e_charging' => __('E-Ladepunkt', 'meinweimar-core'),
];
return $labels[$key] ?? __('Weitere Angaben', 'meinweimar-core');
}
private static function listing_value_label(string $key, string $value): string
{
if (('heating_costs_included' === $key || str_starts_with($key, 'amenity_')) && in_array(strtolower($value), ['1', 'true', 'yes'], true)) {
return __('Ja', 'meinweimar-core');
}
return match ($key) {
'property_kind' => FieldSchemas::property_kind_options()[$value] ?? __('Weitere', 'meinweimar-core'),
'property_rooms' => 'custom' === $value ? __('Eigene Anzahl', 'meinweimar-core') : $value,
'bathrooms', 'bedrooms' => 'more_than_6' === $value ? __('Mehr als 6', 'meinweimar-core') : $value,
'floor' => 'other' === $value ? __('Andere', 'meinweimar-core') : $value,
'offer_type' => match ($value) {
'offer' => __('Angebot', 'meinweimar-core'),
'search' => __('Gesuch', 'meinweimar-core'),
'employment' => __('Stelle / Beschäftigung', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'provider_type' => match ($value) {
'private' => __('Privat', 'meinweimar-core'),
'business' => __('Unternehmen', 'meinweimar-core'),
'organization' => __('Organisation', 'meinweimar-core'),
'broker' => __('Makler / Broker', 'meinweimar-core'),
'employer' => __('Arbeitgeber', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'price_type' => match ($value) {
'fixed' => __('Festpreis', 'meinweimar-core'),
'negotiable' => __('Verhandelbar', 'meinweimar-core'),
'free' => __('Zu verschenken', 'meinweimar-core'),
'on_request' => __('Auf Anfrage', 'meinweimar-core'),
'range' => __('Preisspanne', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'price_currency' => match ($value) {
'EUR' => __('Euro', 'meinweimar-core'),
'USD' => __('US-Dollar', 'meinweimar-core'),
'GBP' => __('Britische Pfund', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'price_interval' => match ($value) {
'once' => __('Einmalig', 'meinweimar-core'),
'hour' => __('Pro Stunde', 'meinweimar-core'),
'month' => __('Pro Monat', 'meinweimar-core'),
'year' => __('Pro Jahr', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'condition' => match ($value) {
'new' => __('Neu', 'meinweimar-core'),
'used' => __('Gebraucht', 'meinweimar-core'),
'defective' => __('Defekt', 'meinweimar-core'),
'first_occupancy' => __('Erstbezug', 'meinweimar-core'),
'renovated' => __('Renoviert', 'meinweimar-core'),
'well_kept' => __('Gepflegt', 'meinweimar-core'),
'needs_renovation' => __('Renovierungsbedürftig', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'delivery_mode' => match ($value) {
'pickup' => __('Abholung', 'meinweimar-core'),
'shipping' => __('Versand', 'meinweimar-core'),
'both' => __('Abholung oder Versand', 'meinweimar-core'),
'digital' => __('Digital', 'meinweimar-core'),
'none' => __('Keine Angabe', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'employment_type' => match ($value) {
'full_time' => __('Vollzeit', 'meinweimar-core'),
'part_time' => __('Teilzeit', 'meinweimar-core'),
'temporary' => __('Befristet', 'meinweimar-core'),
'freelance' => __('Freiberuflich', 'meinweimar-core'),
'apprenticeship' => __('Ausbildung', 'meinweimar-core'),
'internship' => __('Praktikum', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'work_mode' => match ($value) {
'onsite' => __('Vor Ort', 'meinweimar-core'),
'hybrid' => __('Hybrid', 'meinweimar-core'),
'remote' => __('Fernarbeit', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'payment_mode' => match ($value) {
'cash' => __('Barzahlung', 'meinweimar-core'),
'bank_transfer' => __('Überweisung', 'meinweimar-core'),
'secure_payment' => __('Sichere Zahlung', 'meinweimar-core'),
'other' => __('Andere', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'commission_applicable' => 'yes' === $value ? __('Provisionspflichtig', 'meinweimar-core') : __('Provisionsfrei', 'meinweimar-core'),
'commission_calculation' => match ($value) {
'percentage' => __('Prozentsatz', 'meinweimar-core'),
'fixed' => __('Fester Betrag', 'meinweimar-core'),
'monthly_rent' => __('Monatliche Kaltmieten', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'transaction_type' => match ($value) {
'sale' => __('Kaufen', 'meinweimar-core'),
'rent' => __('Mieten', 'meinweimar-core'),
// Legacy transaction values are normalized to the current
// public rent choice by FieldSchemas before rendering.
'lease' => __('Mieten', 'meinweimar-core'),
'temporary' => __('Mieten', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'commission_vat' => 'included' === $value ? __('Enthalten', 'meinweimar-core') : ('excluded' === $value ? __('Nicht enthalten', 'meinweimar-core') : __('Weitere', 'meinweimar-core')),
'commission_payer' => match ($value) {
'buyer' => __('Käufer', 'meinweimar-core'),
'seller' => __('Verkäufer', 'meinweimar-core'),
'both' => __('Beide Parteien', 'meinweimar-core'),
'tenant' => __('Mieter', 'meinweimar-core'),
'landlord' => __('Vermieter', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
'energy_certificate_type' => match ($value) {
'demand' => __('Bedarfsausweis', 'meinweimar-core'),
'consumption' => __('Verbrauchsausweis', 'meinweimar-core'),
'none' => __('Kein Energieausweis', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
},
default => 'yes' === $value ? __('Ja', 'meinweimar-core') : __('Weitere', 'meinweimar-core'),
};
}
private static function opening_hours_rows(array $hours): array
{
$day_labels = [
'mon' => 'Mo', 'tue' => 'Di', 'wed' => 'Mi', 'thu' => 'Do',
'fri' => 'Fr', 'sat' => 'Sa', 'sun' => 'So',
];
$rows = [];
foreach ((array) ($hours['entries'] ?? []) as $entry) {
$days = array_values(array_filter(array_map(static fn ($day): string => $day_labels[(string) $day] ?? (string) $day, (array) ($entry['days'] ?? []))));
$day_label = implode(', ', $days);
if (count($days) >= 2) {
$day_label = $days[0] . '–' . $days[count($days) - 1];
}
if (!empty($entry['closed'])) {
$rows[] = trim($day_label . ': Geschlossen');
continue;
}
$slots = [];
foreach ((array) ($entry['slots'] ?? []) as $slot) {
if (!empty($slot['open']) && !empty($slot['close'])) {
$slots[] = (string) $slot['open'] . '–' . (string) $slot['close'];
}
}
if ($slots) {
$rows[] = trim($day_label . ': ' . implode(', ', $slots));
}
}
if (!$rows && !empty($hours['raw'])) {
$rows[] = (string) $hours['raw'];
}
return $rows;
}
private static function format_date(string $value): string
{
$timestamp = strtotime($value);
if (!$timestamp) {
return $value;
}
return wp_date((string) get_option('date_format'), $timestamp);
}
private static function format_datetime(string $value): string
{
$timestamp = strtotime($value);
if (!$timestamp) {
return $value;
}
$format = trim((string) get_option('date_format')) . ', ' . trim((string) get_option('time_format'));
return wp_date($format ?: 'c', $timestamp);
}
private static function event_status_label(string $status): string
{
return match ($status) {
'scheduled' => __('Geplant', 'meinweimar-core'),
'cancelled' => __('Abgesagt', 'meinweimar-core'),
'postponed' => __('Verschoben', 'meinweimar-core'),
'expired' => __('Beendet', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
};
}
private static function event_ticket_label(string $mode): string
{
return match ($mode) {
'free' => __('Kostenlos', 'meinweimar-core'),
'external' => __('Externe Buchung', 'meinweimar-core'),
'internal' => __('Interne Buchung', 'meinweimar-core'),
'registration' => __('Anmeldung erforderlich', 'meinweimar-core'),
default => __('Weitere', 'meinweimar-core'),
};
}
private static function image_suggestion_markup(string $resource, int $post_id, string $title, bool $placeholder = false): string
{
$logged_in = is_user_logged_in();
$login_return_url = get_permalink($post_id) ?: home_url('/');
$panel_id = wp_unique_id('mw-image-suggestion-panel-');
ob_start();
?>
<section class="mw-image-suggestion" data-image-suggestion data-target-type="<?php echo esc_attr($resource); ?>" data-target-id="<?php echo esc_attr((string) $post_id); ?>">
<div class="mw-image-suggestion__empty">
<span class="mw-image-suggestion__icon" aria-hidden="true">⌁</span>
<h2><?php echo esc_html($placeholder ? __('Platzhalterbild vorhanden', 'meinweimar-core') : __('Kein Bild vorhanden', 'meinweimar-core')); ?></h2>
<p><?php echo esc_html($placeholder ? __('Dieses Bild ist vorläufig. Hilf mit, ein passenderes Bild vorzuschlagen.', 'meinweimar-core') : __('Hilf mit, diesen Eintrag sichtbar zu machen.', 'meinweimar-core')); ?></p>
<button type="button" class="mw-image-suggestion__toggle" data-image-suggestion-toggle aria-expanded="false" aria-controls="<?php echo esc_attr($panel_id); ?>"><?php esc_html_e('Bild vorschlagen', 'meinweimar-core'); ?></button>
</div>
<div id="<?php echo esc_attr($panel_id); ?>" class="mw-image-suggestion__panel" data-image-suggestion-panel hidden>
<?php if (!$logged_in) : ?>
<p><?php esc_html_e('Zum Vorschlagen eines Bildes musst du angemeldet sein.', 'meinweimar-core'); ?></p>
<a class="mw-image-suggestion__login" href="<?php echo esc_url(wp_login_url($login_return_url)); ?>"><?php esc_html_e('Anmelden', 'meinweimar-core'); ?></a>
<?php else : ?>
<form data-image-suggestion-form enctype="multipart/form-data">
<p class="mw-image-suggestion__target"><?php esc_html_e('Vorschlag für', 'meinweimar-core'); ?>: <strong><?php echo esc_html($title); ?></strong></p>
<label class="mw-image-suggestion__file-label">
<span><?php esc_html_e('Bild auswählen', 'meinweimar-core'); ?></span>
<input type="file" name="file" accept="image/jpeg,image/png,image/webp" required data-image-suggestion-file>
</label>
<figure class="mw-image-suggestion__preview" data-image-suggestion-preview hidden>
<img alt="<?php esc_attr_e('Vorschau des ausgewählten Bildes', 'meinweimar-core'); ?>" data-image-suggestion-preview-image>
</figure>
<fieldset>
<legend><?php esc_html_e('Rechte und Bestätigung', 'meinweimar-core'); ?></legend>
<label><input type="checkbox" name="rights_holder_confirmed" value="1" required> <?php esc_html_e('Ich bin Urheber des Bildes oder besitze ausreichende Nutzungsrechte.', 'meinweimar-core'); ?></label>
<label><input type="checkbox" name="lawful_source_confirmed" value="1" required> <?php esc_html_e('Das Bild wurde nicht unrechtmäßig aus dem Internet übernommen.', 'meinweimar-core'); ?></label>
<label><input type="checkbox" name="persons_consent_confirmed" value="1" required> <?php esc_html_e('Abgebildete Personen haben zugestimmt oder sind nicht identifizierbar.', 'meinweimar-core'); ?></label>
<label><input type="checkbox" name="platform_license_confirmed" value="1" required> <?php esc_html_e('Ich räume MeinWeimar die für Anzeige, Speicherung und Verarbeitung erforderlichen Nutzungsrechte ein.', 'meinweimar-core'); ?></label>
</fieldset>
<details>
<summary><?php esc_html_e('Optionale Angaben', 'meinweimar-core'); ?></summary>
<label><?php esc_html_e('Quelle', 'meinweimar-core'); ?><input type="text" name="source" maxlength="1000"></label>
<label><?php esc_html_e('Lizenz', 'meinweimar-core'); ?><input type="text" name="license" maxlength="500"></label>
<label><?php esc_html_e('Fotograf / Urheber', 'meinweimar-core'); ?><input type="text" name="photographer" maxlength="300"></label>
<label><?php esc_html_e('Bildbeschreibung', 'meinweimar-core'); ?><textarea name="description" maxlength="2000" rows="3"></textarea></label>
</details>
<button type="submit" class="mw-image-suggestion__submit"><?php esc_html_e('Bildvorschlag einreichen', 'meinweimar-core'); ?></button>
<p class="mw-image-suggestion__status" data-image-suggestion-status role="status" aria-live="polite"></p>
<div class="mw-image-suggestion__history" data-image-suggestion-history hidden aria-live="polite"></div>
</form>
<?php endif; ?>
</div>
</section>
<?php
return (string) ob_get_clean();
}
private static function resolve_post(int $post_id, string $post_type): ?WP_Post
{
$post = get_post($post_id ?: get_the_ID());
if (!$post instanceof WP_Post || $post->post_type !== $post_type) {
return null;
}
if ('publish' !== $post->post_status && !current_user_can('edit_post', $post->ID)) {
return null;
}
return $post;
}
private static function resolve_resource(int $post_id): ?WP_Post
{
$post = get_post($post_id ?: get_the_ID());
if (!$post instanceof WP_Post || !in_array($post->post_type, array_values(ContentTypes::public_post_types()), true)) {
return null;
}
if ('publish' !== $post->post_status && !current_user_can('edit_post', $post->ID)) {
return null;
}
return $post;
}
}

Architecture

High-level modules detected from the verified public snapshot. Protected implementation details are intentionally omitted.

flutter-appmodule
wordpress-pluginmodule

Technology stack

DartPHPJavaScriptARBLOCKPBXPROJFlutter / Dart
Dart56.1%
PHP29.4%
JavaScript4.0%
ARB2.9%
LOCK1.7%
PBXPROJ1.7%
CSS1.5%
Markdown0.4%