Web Panel & Public Site

Installing MediaDesk, the reader website, and every admin menu area.
Version: 1.2.0
Stack: Laravel 13 · Flutter 3.44 · MySQL

Item: MediaDesk — Multilingual AI-Powered Newsroom ERP

Start Here: Who Does What

Read this first. MediaDesk has two kinds of work: a short one-time server setup that needs someone comfortable with hosting (a server administrator or developer), and everything else, which an editor or site owner does in the admin panel with no code. The table tells you who to send each task to before you start.

TaskWhoWhere
Upload files, create the database, run composer install and npm run build, run the web installerServer admin / developerQuick Start → Install
Point the domain at public/, HTTPS certificate, PHP upload limits, folder permissionsServer admin / developerServer Requirements
Cron entry (scheduler) and queue worker (Supervisor)Server admin / developerCron, Queue & Workers
Upgrading to a new version (upload, migrations, cache clear)Server admin / developerUpgrading
CDN rules, Redis, headless Chrome for e-paper PDFs (all optional)Server admin / developerCommon Setup
Building and publishing the Android / iOS app (Flutter SDK, Firebase project, store accounts)DeveloperMobile App
Logo, favicon, brand colour, front-page design, home page sections, menusAdmin panelBranding Without Code
Site name, languages, time zone, currency, contact details, social linksAdmin panelSettings
Mail, AI keys, payment gateways, push keys, storage, social accounts (paste credentials, press Test connection)Admin panelSettings → Integrations
Articles, categories, authors, staff accounts and roles, ads, memberships, newslettersAdmin panelAdmin Panel
Checking that cron, queue, storage and keys workAdmin panelSystem → System health (Troubleshooting FAQ)

Rule of thumb: if a step mentions a terminal command, .env, cron or Supervisor, it belongs to your server administrator. Once they have finished the required server items once, day-to-day running needs only the admin panel.

Third-party accounts you create yourself. Hosting is the only hard requirement. Every other service (mail delivery, payment gateways, AI providers, cloud storage, CDN) is optional and uses your own account and keys. The mobile app depends on Google Firebase (firebase_core, firebase_messaging are compiled into it): for app push notifications you must create your own Firebase project and accept Google’s Firebase / Google APIs Terms of Service. No Firebase project, key or config file from the author is included. See Mobile App → Firebase disclosure.

Minimum Setup to Go Live

When is setup “complete enough”? When every Required row below is done. Everything marked Optional adds a feature and can be switched on later, while the site is live. A full production checklist is in Quick Start → Go-Live Checklist.

ItemStatusWithout itWho
Hosting with PHP 8.3+, MySQL 8 / MariaDB 10.6+, HTTPS, domain pointing at public/RequiredNothing runsServer admin
Installer finished; Super Admin with a strong passwordRequiredEvery page redirects to /installServer admin
php artisan storage:link and writable storage/, bootstrap/cache/RequiredUploaded images do not show; errors on saveServer admin
Cron entry running schedule:run every minuteRequiredView counts, scheduled posts, renewals, digests and the finance ledger stopServer admin
Queue worker (or QUEUE_CONNECTION=sync on a small site)Required for newsletters and pushCampaigns and push wait forever in the queueServer admin
Mail (SMTP or a mail API) with a test e-mail receivedRequiredNo password resets, newsletter confirmations or invoicesAdmin panel
Site name, logo, favicon, default language, time zone, currencyRequiredThe site shows the MediaDesk defaultsAdmin panel
APP_DEMO=false, sample staff accounts removed (only after a command-line db:seed)RequiredKnown demo logins exist on your siteServer admin
At least one category and one published articleRequiredEmpty home page; the app shows nothingAdmin panel
Payment gateway (Stripe, PayPal, local gateways) + webhookOptionalPaid memberships, ad invoices and donations use bank transfer / manual onlyAdmin panel (webhook URL in the gateway’s dashboard)
AI key (Anthropic, Gemini or OpenAI)OptionalThe AI copilot says “not configured”; everything else worksAdmin panel
Web push (VAPID keys)OptionalNo browser notification promptAdmin panel
Firebase (FCM) for the mobile appOptional (needs your own Firebase project)The app works, without push notificationsDeveloper + admin panel
Mobile app built and publishedOptionalReaders use the website / PWADeveloper
Cloud storage (S3/R2/B2), CDN, RedisOptionalMedia stays on the server disk; fine for most sitesServer admin + admin panel
Social auto-posting, Google sign-in, captcha, analytics IDsOptionalThose features stay hiddenAdmin panel
Headless Chrome (e-paper PDFs, best-quality social cards)OptionalNo e-paper PDF; social cards use GDServer admin

Quick test: open System → System health. When Database, Writable storage/*, public/storage link, Scheduler and Mail show Ok and Queue is not sync (or you accept sync), you are ready to go live. Warn on AI, push or payment keys only means an optional feature is off.

Branding Without Code: Logo, Colours, Front Page

Everything in this section is done in the admin panel by a Super Admin (or a role with the general settings and appearance permissions). No file editing, no build step. Changes appear on the public site right after saving; the visual theme uses draft → preview → publish so readers never see half-finished colours.

1. Logo, dark-mode logo, favicon and share image
  1. Open Settings → General settings

    Left menu Settings, then General settings (callout 1).

  2. Upload the logos

    Light Logo (2) is used on light backgrounds, Dark Logo (3) in dark mode. Use a transparent PNG about 600 × 120 px. Press Change, pick an image from the media library or upload a new one.

  3. Favicon and share image

    Favicon (4): a square PNG (browser tab icon). The installable-app (PWA) icon is a separate field, Settings → Public site → App icon (PNG 512 × 512). Default social share image (5): 1200 × 630 px, shown when a page without its own image is shared on Facebook, X or WhatsApp.

  4. Save

    Press Save Change (6), then reload the public site. If the old logo still shows, see “My change does not show”.

Settings → General settings with the logo, dark logo, favicon and share image fields numbered 1 to 6
Settings → General settings: (1) menu, (2) light logo, (3) dark logo, (4) favicon, (5) share image, (6) Save

Different logo per language (for example a Bangla wordmark)? Settings → Public site → Site shell → Logo and favicon per language. Empty fields fall back to the General settings logo.

2. Brand colour and front-page design (skin)
  1. Open Settings → Public site

    Default skin (callout 1) chooses the front-page design new visitors see: Modern, Classic, Magazine, Night, Compact, Local, Alo, Times, Wire, Broadcast or Star (screenshots in Reader Site & Skins). Readers can still switch skins themselves. Default theme sets the colour mode on the website and in the mobile app for readers who have not picked one: Light (the default), Dark or System, which follows the device.

  2. Brand colour

    Scroll to Branding → Primary colour (2). Pick a colour or type a hex code such as #c8102e. It replaces the accent colour (links, buttons, highlights) on every skin in light mode, the phone browser bar colour and the header of e-mails and newsletters. Dark mode and the Night skin keep their own accent so text stays readable. A note under the field warns when white text on your colour would be hard to read (contrast below 4.5:1).

  3. Site name and tagline

    In the same page, Per language tabs hold the site name, tagline, footer text and address for each language.

  4. Save

    Press Save at the bottom of the page.

Settings → Public site with Default skin and Primary colour numbered
Settings → Public site: (1) default skin = front-page design, (2) primary brand colour
3. Background, text colours, corners, header and footer style
  1. Open Site → Visual theme

    Callout 1. Set page background, card surface, main text, muted text and border colours (2) with the colour pickers; corner radius, content width and spacing; and the Header variant (3: default, centered, compact) and footer variant.

  2. Save draft, preview, publish

    Save theme draft (4) stores your choice without changing the live site. Preview saved draft (5) opens the site in a new tab with the draft applied only for you. Publish saved draft (6) makes it live for everyone. To go back to the defaults, clear the fields, save and publish.

Site → Visual theme with colours, header variant and the draft, preview and publish buttons numbered
Site → Visual theme: (2) colours, (3) header style, (4) save draft, (5) preview, (6) publish
4. Front-page sections and order
  1. Open Site → Home layout

    The table lists the home page blocks top to bottom (callout 3): live TV box, lead story, featured tiles, latest, editor’s picks, most read, category sections, videos, podcasts, newsletter box and more.

  2. Re-order, hide, rename

    Use the ↑ / ↓ arrows to move a block. Open a block to switch it off (Active), change its title per language, change how many stories it shows or, for category blocks, which category and zone it uses.

  3. Add a section for a category

    + Category block (2) adds, for example, a “Sports” section that shows the latest Sports stories. + Content-type block adds a strip of videos, podcasts, fact checks and similar. Changes are live immediately.

  4. Menus

    Site → Navigation builds the header menu, the “more” dropdown, the footer and the mobile bottom menu, with labels per language.

Site → Home layout with the add category block button and the block list numbered
Site → Home layout: (2) add a category section, (3) blocks in page order

Fonts per language: Site → Fonts. Sidebar widgets: Site → Sidebar widgets. Interface wording (button labels, footer texts): Settings → Site phrases. The mobile app’s name and icon are set at build time by the developer (Mobile App → Rebranding); the app picks up articles, categories and menu automatically.

What You Get

MediaDesk is one Laravel 13 application that gives a newsroom three things at once: a public reader website (11 selectable skins, 4 languages, PWA), an admin “ERP” for the whole editorial and commercial workflow (190 admin screens, 813 routes), and a REST API (51 endpoints under /api/v1) that the included Flutter app for Android and iOS uses. This page covers the web side: installing it, the public site, and every admin menu area. The phone app has its own page: Mobile App.

Reader site

Home, categories, articles, live blog, podcasts, e-paper, shorts, web stories, classifieds, membership.

Open →
Newsroom desk

Story board, deadlines, news inbox with RSS/API feeds, tasks, revisions, AI copilot.

Open →
Automation & marketing

Rules engine, social packages, newsletters, web push and FCM push.

Open →
Money

Ads, sponsorship, memberships, donations, classifieds, finance ledger and P&L.

Open →

For a step-by-step install see the Quick Start; for mail, storage, queue, AI keys, payment gateways, push and OAuth see Common Setup.

Screenshots in this guide were captured on a demo database (sample articles, “Demo” advertisers and invoices, sample weather/stock/prayer values). Names, numbers and the Bangla-first language setting are demo data, not features of your install.

Server Requirements

The installer checks every item below and will not continue until they pass.

RequirementMinimum
PHP8.3 or newer (composer.json requires ^8.3)
DatabaseMySQL 8 or MariaDB 10.6+. The installer connects with pdo_mysql only; sqlite is not supported by the wizard.
PHP extensions (checked)curl, fileinfo, exif, mbstring, intl, pdo_mysql, openssl, json, xml, ctype, tokenizer, gd
Writable folders (checked)storage, storage/app, storage/framework, storage/logs, bootstrap/cache, and the .env file (or its folder)
Web serverApache or nginx with URL rewriting; document root = public/
Node.js 20+Required once to build the front-end assets (npm ci && npm run build); public/build is not shipped. Vite 6 + Tailwind CSS 4.
Composer 2Required to install the PHP dependencies (composer install); vendor/ is not shipped.
OptionalRedis (cache/session/queue, phpredis); ffmpeg for video/audio tools; Google Chrome / Chromium for e-paper PDFs and social cards; Supervisor for the queue worker.
CronOne entry, every minute (see Cron, Queue & Workers)

Point the domain at the public folder, not the project root. Serving the project root exposes .env.

Run the Installer

Step-by-step guides: VPS Installation (your own server with SSH) or cPanel Installation (shared hosting, no SSH needed).

Upload the project, run composer install --no-dev --optimize-autoloader and npm ci && npm run build (the package does not include vendor/ or public/build), copy .env.example to .env, create an empty MySQL database with a user that has access to that database only, point the domain at public/, then open https://your-domain.com/install. The wizard is a real feature of the app (routes/install.php); once it finishes it writes the lock file storage/installed and the /install URLs answer 404 for good.

  1. Welcome — requirements

    Shows PHP version, each required extension and each writable path with pass/fail. Continue is only possible when everything is green.

  2. Database & site

    App name, app URL, DB host, port, name, user, password. The installer tests the connection and refuses any database that already contains tables (nothing is changed and .env is not written), then writes .env (APP_ENV=production, APP_DEBUG=false, APP_URL, DB_*, and a fresh APP_KEY if none exists).

  3. Administrator

    Your name, e-mail and password (min 8, confirmed). This becomes the Super Admin.

  4. Run

    Checks again that the database is empty, runs migrate --force (never migrate:fresh or db:wipe; no table is ever dropped), seeds base data (route list, currencies, languages, flag icons, settings, permissions, roles), creates the Super Admin, runs cms:sync-permissions, tries storage:link, then writes the lock file storage/installed and APP_INSTALL=true. A “Done” page links to /login.

The wizard installs base data only: no articles, no demo advertisers. Your site starts empty. See Demo mode if you want sample content for evaluation.

The installer lock

The installer is closed for good as soon as either of these exists: the file storage/installed (written by the wizard) or APP_INSTALL=true in .env (for command-line installs; the lock file is then written automatically on the next request). The lock does not depend on the database: losing APP_INSTALL from .env, replacing .env or a database outage never reopens the wizard. If the database cannot be reached the site shows an error page until the DB_* values in .env are correct, so the installer can never overwrite your database credentials or your content.

To run the installer again on purpose (for example on a new, empty database), delete storage/installed and set APP_INSTALL=false. It still refuses a database that contains tables. If a run is interrupted, the database is no longer empty: empty it (or create a new one) and start the wizard again. When moving a site to a new server, copy storage/ including storage/installed.

Prefer the command line? cp .env.example .env, set APP_URL and the DB_* values (use APP_ENV=local for local development over http), then composer install, php artisan key:generate, php artisan migrate --force, php artisan db:seed --force, php artisan cms:sync-permissions, php artisan storage:link, npm ci && npm run build, and finally set APP_INSTALL=true in .env to lock the installer. Note that db:seed also runs UserSeeder, which creates three sample staff accounts ([email protected], [email protected], [email protected], password 12345678; defaults in config/demo.php, overridable with DEMO_SUPERADMIN_EMAIL, DEMO_ADMIN_EMAIL, DEMO_USER_EMAIL, DEMO_PASSWORD) — change or delete them before going live. The wizard does not create them.

After Installing

  1. Sign in

    Open /login with the administrator you created.

  2. Run the doctor

    php artisan mediadesk:doctor checks PHP, folders, database, cache/queue/mail and optional tools and prints how to fix each problem. Platform → System health shows the same kind of checks in the browser.

  3. Set the site identity

    Settings → Public site: default language, default skin, site name, tagline and description per language. Settings → General settings: logos, favicon, social image. See Settings.

  4. Set your time zone and currency

    Settings → Public site → Time zone sets the public time zone (dates, “today”, scheduled digests and the e-paper build); the default comes from SITE_TIMEZONE in .env. Finance → Currencies sets the base currency for ads, invoices, finance and reader support, with exchange rates for other currencies; the default comes from APP_CURRENCY (ISO 4217, e.g. USD). Membership prices are entered per currency on each plan. The database always stores UTC.

  5. Add credentials

    Settings → Integrations holds mail, AI keys, storage, push, payment gateways and social OAuth. They are stored encrypted in the database. See Integrations Hub.

  6. Start the scheduler and a queue worker

    Without them newsletters, push, social posts, renewals, e-paper builds and the finance ledger do not run. See next section.

  7. Make it indexable (only when you go live)

    Search engines are blocked unless APP_ENV=production or SEO_INDEXABLE=true.

Cron, Queue & Workers

MediaDesk needs both a cron entry and a queue worker for production use. This is the single most common reason “nothing sends”.

* * * * * cd /var/www/mediadesk && php artisan schedule:run >> /dev/null 2>&1

The scheduler (routes/console.php) runs these entries; clock times follow the site time zone (Settings → Public site, else SITE_TIMEZONE):

CommandWhenWhat it does
stats:flushevery minuteMoves buffered article view/read counters into the database
ads:flushevery minuteMoves buffered ad impression/click counters into ad_stats
automation:tickevery minuteEvaluates time and threshold based automation triggers
social:publish-dueevery minuteDispatches queued social posts whose time has come
marketing:campaigns-runevery minuteStarts newsletter campaigns whose scheduled time has passed
frontend:push-breakingevery minutePushes new breaking articles that were not pushed yet
platform:heartbeatevery minuteHeartbeat shown on System health
platform:webhooks-retryevery minuteRetries failed outgoing webhooks
desk:fetch-sourcesevery 5 minutesFetches due RSS/Atom/JSON sources into the news inbox
authors:notify-followersevery 10 minutesE-mails followers about new articles by authors they follow
membership:renewhourlyBills due subscription periods, dunning, expiry
membership:remindhourly at :30Sends renewal reminders before a membership renews or ends
articles:prune-editingdaily at 03:40Removes abandoned article drafts and expired edit locks
finance:posthourly at :15Posts paid invoices, donations, classifieds to the ledger (idempotent)
health:check --recordhourlyRecords system-health history
frontend:newsletter-send dailydaily 07:00Daily digest to subscribers of the daily list
frontend:newsletter-send weeklyFridays 08:00Weekly digest
ads:expiry-alertdaily 08:00Closes finished campaigns, alerts about campaigns ending soon
frontend:epaper-builddaily 05:30Builds e-paper PDFs with headless Chrome
authors:close-month1st of month 02:00Writes monthly author revenue-share statements
demo:resetdaily 03:00Only when APP_DEMO=true and not production — never on a real site

Check what is registered with php artisan schedule:list. The Platform → System health screen reports the scheduler heartbeat and whether a worker is needed.

Queue worker

Newsletter campaigns, push alerts (web push and FCM), newsletter digests, social posts and some mails are queued jobs. The default QUEUE_CONNECTION=sync runs them inline (fine for a small test, slow for campaigns). For production use Redis or the database driver plus a worker under Supervisor:

[program:mediadesk-worker]
command=php /var/www/mediadesk/artisan queue:work redis --sleep=3 --tries=1 --max-time=3600
user=www-data
numprocs=2
autostart=true
autorestart=true
stopasgroup=true
stopwaitsecs=3600
redirect_stderr=true
stdout_logfile=/var/log/mediadesk-worker.log

The project has no jobs or failed_jobs migration. For the database queue driver run php artisan queue:table, php artisan queue:failed-table and php artisan migrate first — or use Redis. Run php artisan queue:restart after every deploy.

What’s New in 1.2.0

Where to find each new feature. To move an existing site to 1.2.0, follow Upgrading & Demo Mode.

FeatureWhereWhat it does
Update databaseSystem → System updateAfter a file upload, shows the current version, the update version and the database changes waiting. The button backs up the database, runs migrations, syncs roles and permissions, and clears caches.
BackupsSystem → OperationsDaily and on-demand database backups, with retention, download, restore and delete. Daily backups need the server cron job.
Google Drive copiesSettings → Integrations → Storage → Google Drive backupsOptional remote copy of each database backup. Client Secret and Refresh Token are encrypted in the database.
App controlSystem → OperationsApp maintenance mode, latest and minimum versions, optional or required updates, and store links.
Failed jobs and app errorsSystem → OperationsRetry or remove failed queue jobs and review sanitized app error reports.
Bottom tabsSite → Navigation → Mobile bottom tabsOne list for the website and app. The first five active items show. Category, page and link tabs need an app built from this release.
Default themeSettings → Public site → Default themeLight, Dark or System for readers who have not chosen a mode. A reader’s own choice is kept.
App download sectionSettings → Public site → Show the app download cardShows Android and iOS links on the website. Each enabled link must be a full HTTPS URL.
E-paper editionsWebsite and appReaders open real PDF editions. Page previews need Poppler on the server; without it, Open PDF still works.
Article viewsWebsite and appViews are tracked, and Most Read shows the server counts.
Read aloud and libraryAppDevice-voice read aloud, following feeds, guest library, library backup and account security tools. See the mobile app guide.
MediaDesk 1.2.0 updates, backups and operations MediaDesk 1.2.0 website, mobile app and newsroom features

Upgrading & Demo Mode

Version 1.2.0 includes a professional updater under System → System update. It supports official ZIP upload and FTP/cPanel updates finished from the admin panel. Packages are checked before protected files are touched, and the updater coordinates maintenance mode, backups, migrations, permission sync, caches and queue workers. The manual server workflow below remains available.

System Update methods in 1.2.0
  • ZIP: upload the official ZIP in System → System update. MediaDesk validates its manifest and every declared SHA-256 checksum before applying it.
  • FTP/cPanel: version 1.2.0 changes the PHP packages and the website styles, and the download has no vendor/ or public/build/. On your computer, open the new WebSourceCode folder and run composer install --no-dev --optimize-autoloader, npm ci and npm run build, then delete node_modules. Upload everything over the old program files, including vendor/ and public/build/, while preserving .env, storage/ and uploads. In File Manager, delete every .php file in bootstrap/cache/. If you skip this, old cached routes can make the update screen show 404. Then open System → System update and press Update database. No SSH is needed.
  • Status: after the upload the screen shows the current version (1.0.0 for installations that never recorded one), the update version and the number of database changes waiting. When the database update finishes, the current version changes to the new version and the panel closes. The database is backed up first; if a step fails, the site reopens, the version stays the same and you can press the button again.
  • After updating from source: run npm run build so the latest styles and scripts load. The site and app open in Light mode for readers who have not chosen a mode; set Settings → Public site → Default theme to System to keep following the device. Your bottom tabs are not changed.

The updater enables maintenance mode, creates private database and affected-file backups, validates protected paths, records update history, restarts queues and restores service. If file installation fails, replaced files are rolled back automatically. Database migrations already committed may require restoring the matching pre-update database backup.

Install locked dependencies with composer install --no-dev --optimize-autoloader when a release changes composer.lock. Do not run composer update on the production server.

Before you start
  • Read the Changelog entry of every version between yours and the new one, including its “Upgrade notes” (version-specific steps). Your version is the one at the top of the documentation that came with your download (and in AppSourceCode/pubspec.yaml for the app).
  • Back up the database (mysqldump --single-transaction yourdb > backup.sql), .env and storage/ (it holds uploads and the installer lock storage/installed).
  • Keep a list of any files you changed yourself (custom Blade views, CSS). An upgrade overwrites them; prefer Settings → Site code and Site phrases for customisations, which live in the database and survive upgrades.
Upgrade steps (every version)
# 1. Maintenance page while files change (everyone sees it until step 7)
php artisan down --refresh=15

# 2. Upload the new WebSourceCode over the old one.
#    Do NOT overwrite or delete: .env, storage/, public/storage (link), public/uploads (if you created it)

# 3. Dependencies and front-end assets
composer install --no-dev --optimize-autoloader
npm ci && npm run build

# 4. Database changes (adds new tables/columns; never drops your data)
php artisan migrate --force

# 5. New permissions for new admin screens
php artisan cms:sync-permissions

# 6. Clear every cache, then rebuild the production caches
php artisan optimize:clear
php artisan optimize

# 7. Restart queue workers so they load the new code, then go live
php artisan queue:restart
php artisan up
  • New .env keys: compare the new .env.example with your .env (diff .env.example .env) and copy over any key you do not have. Missing keys fall back to safe defaults.
  • OPcache: if opcache.validate_timestamps=0, reload PHP-FPM (sudo systemctl reload php8.3-fpm) or the old code keeps running.
  • No SSH? Run step 3 on your computer and upload vendor/ and public/build. Steps 4–7 then need your host’s “run command” tool or a one-off cron entry; ask your host if unsure.
  • Do not run the installer again, migrate:fresh, db:wipe or db:seed on a live site. The installer stays locked as long as storage/installed exists.
  • Mobile app: copy your changes (API_BASE, app name, icon, bundle id, Firebase files) into the new AppSourceCode, run flutter pub get, raise version: in pubspec.yaml and build a new release.

Rolling back: put the old files back, restore the database dump taken before step 1 (a newer migration may have changed tables), then run steps 6 and 7.

Version-specific notes
Upgrading toExtra steps
1.2.0From 1.0.0, use the official updater or upload the new files and run composer install --no-dev --optimize-autoloader, then php artisan mediadesk:database-update --release=1.2.0. Do not run composer update. Configure Google Drive backup in Admin; no backup keys belong in .env.
1.0.0Initial release; nothing to migrate.

Each future release adds a row here and an “Upgrade notes” list in the Changelog whenever it needs more than the steps above (for example a new .env key, a one-time artisan command or a new server requirement).

Demo mode (evaluation only)

Demo content for evaluation. On a throw-away database ALLOW_DEMO_SEED=true php artisan db:seed --class=FrontendDemoSeeder --force loads a sample newspaper (articles in bn/en/ar/fr, authors, topics, podcasts, live events, comments, static pages). It deletes all existing article, category, podcast and live-update rows first. The installer sets APP_ENV=production, where demo seeders refuse to run unless ALLOW_DEMO_SEED=true (or APP_DEMO=true) is set. Photos, clips and audio are not bundled, so the sample stories use placeholder thumbnails.

APP_DEMO=true is the one switch for the public-demo behaviour: a read-only showcase for any user with the role slug demo and for the published demo logins (the accounts in config/demo.php and *@demo.mediadesk.test; switch this part off with DEMO_READONLY_ACCOUNTS=false): non-GET requests return 403 and password/key inputs are masked. It also shows the one-click demo login buttons and enables the nightly demo:reset (only when APP_ENV is not production). Keep APP_DEMO=false on a real site.

Reader Site & Skins

The public site lives at /{locale}/… (for example /en, /bn, /ar, /fr). The root URL redirects to the default language you choose in Settings → Public site. Each reader can pick one of 11 skins — Modern, Classic, Magazine, Night, Compact, Local, Alo, Times, Wire, Broadcast and Star — plus light/dark mode; the admin sets the default skin. A skin can be forced for a visit with ?skin=classic.

Home page, default skin: breaking bar, hero story, latest, trending
Home page, default skin: breaking bar, hero story, latest, trending
Modern skin
Modern skin
Classic skin
Classic skin
Magazine skin
Magazine skin
Night (dark) skin
Night (dark) skin
Compact skin
Compact skin
Local skin
Local skin
Category page with filters (Politics)
Category page with filters (Politics)

Reading Features

  • Home — breaking bar, hero, featured, latest, trending, editor’s picks, videos; stories rail; Latest / For You / Following tabs; the block order is set in Home layout.
  • Article page — key points, reading time, author, share, text-size and listen controls, related stories, reactions, comments, reader polls, premium truncation for paywalled stories, sponsor disclosure for sponsored articles.
  • Personalisation — bookmarks, reading history, follow topics and authors, authors and topics directories.
  • Archive calendar and search across the active language.
Article page with video-free layout, sidebar and related stories
Article page with video-free layout, sidebar and related stories
Video article
Video article
Archive calendar
Archive calendar
Topics directory
Topics directory
Authors directory
Authors directory

Languages & RTL

Four languages ship: Bangla (bn), English (en), Arabic (ar, right-to-left) and French (fr). Every public URL carries the language, every page emits hreflang alternates, and content falls back to the default language with a language chip when a translation is missing. Admins add more languages under Language and edit interface phrases under Appearance → Site phrases (see Fonts, Widgets, Phrases). Arabic automatically flips the layout.

Bangla home page
Bangla home page
Arabic home page (right-to-left layout)
Arabic home page (right-to-left layout)

Adding a language: create it in the Language module (code, name, direction, status active), copy lang/en/site_*.json to lang/{code}/ and translate, then translate content per language in each article.

Live, Podcasts, E-paper

  • Live blogs — events with timed, pinnable updates; the live hub lists current and ended coverage.
  • Podcasts — episode list, built-in player, premium episodes, podcast RSS (/{locale}/podcast.xml).
  • E-paper — HTML page-by-page reader per day and language, archive of the last days, and real multi-page PDF editions for download (free or members-only).
  • Shorts and Web stories — vertical video and swipe stories.
Live blog with timed updates
Live blog with timed updates
Live hub
Live hub
Podcasts page
Podcasts page
E-paper reader with 7-day archive
E-paper reader with 7-day archive
Shorts
Shorts
Web stories
Web stories

Accounts & Membership

Readers register, verify their e-mail and sign in at /{locale}/account. The membership page lists plans (Free, Premium monthly/yearly, Corporate in the demo) with a checkout that uses any gateway you enabled. Premium articles show an intro and a paywall card; one-off pay-per-post purchases and corporate seats are supported. Everything is explained in Membership and Payment Gateways.

Download my data and delete my account

Signed-in readers manage their own data from the Privacy and your data section of /{locale}/account.

  • Download my data gives a JSON file with the account, memberships, invoices, purchases, comments, poll votes, follows, devices, newsletter sign-up and contact messages. Passwords, tokens, payment provider references and internal hashes are left out.
  • Delete my account is at /{locale}/account/delete. Use this address as the account-deletion link that app stores ask for. Readers confirm with their password, or with a confirmation link sent by e-mail that works for 60 minutes. Readers who signed up with Google need the e-mail link, because they have no password they know.
  • Deleting ends the membership immediately, stops future payments and removes the profile, follows, saved items, devices, newsletter sign-up and contact messages. Comments and poll votes stay without the reader’s identity. Unused membership time is not refunded automatically.
  • Invoices, payment records and offline payment proofs stay for accounting, linked to the anonymised account. Articles published under the reader’s byline stay with the newsroom.
  • Deletion is refused while an author payout is pending or approved. It works once the payout has been paid or rejected.

The wording on the deletion page is a sensible default, not legal advice. Check it against your privacy policy and the data-retention rules in your country.

Membership plans and newsletter sign-up
Membership plans and newsletter sign-up
Reader sign-in
Reader sign-in

Community & Extras

  • Comments and reactions with moderation (default: every comment is held until approved) and optional captcha.
  • Reader polls, newsletter with double opt-in, one-click unsubscribe and an optional popup.
  • Submit a story form (reader tips and stories land in the news inbox) and a contact form (lands in the contact inbox).
  • Classifieds and notices (jobs, tenders, obituaries, business listings; paid listings go through finance), support / donations page (/{locale}/support).
  • Bangladesh pack (prayer times, weather, currency rates, Dhaka Stock Exchange) — values are demo data until you configure the fetch.
Classifieds and notices
Classifieds and notices
Bangladesh pack (sample values)
Bangladesh pack (sample values)
Reader story submission form
Reader story submission form
Contact form
Contact form

The Bangladesh pack shows sample weather, exchange-rate and DSE values until CONTENT_FETCH_ENABLED=true and the endpoints in config/content.php are set; php artisan content:bd-fetch refreshes them. Prayer times are computed on the server. Turn the whole pack off in Modules if you do not need it.

SEO, PWA & Consent

  • XML sitemaps (/sitemap.xml, news sitemap /sitemap-news.xml), robots.txt, llms.txt, JSON-LD, RSS feeds, canonical + hreflang.
  • Installable PWA with offline fallback and web-push opt-in (service worker at /sw.js).
  • Cookie consent: GA4, Meta Pixel and AdSense load only after the visitor consents.
  • Speed: stateless pages are served with Cache-Control: public, s-maxage=300 so a CDN can cache them.

Dashboard & Navigation

Sign in at /login. The left sidebar groups the admin into: Content, Newsroom, Marketing, Monetisation, Analytics, Platform, Appearance, plus To-do list, Activity logs, Login activity, Language and Settings. A group disappears when its module is switched off or when your role lacks the permission. The dashboard shows totals (articles, published today, views, categories, subscribers, pending comments, total comments, admin users), a 14-day publishing chart, a split by article type and the top articles by views.

Admin dashboard with totals, 14-day chart and article-type split
Admin dashboard with totals, 14-day chart and article-type split
Activity log: who changed what
Activity log: who changed what

Modules (16 toggles)

Platform → Modules (/admin/platform/modules) switches product areas on and off. A disabled module hides its sidebar menu and makes its public and API URLs answer 404. The 16 modules: AI Studio, Marketing (campaigns, push), Advertisement, Subscription / Membership, E-Paper, Podcast, Video, Shorts / Stories, Live Blog, Newsletter, Analytics, Bangladesh Pack, Mobile / Public API v1, Classifieds, Automation, Finance. Switch off what you do not sell: a news site without e-paper simply disables E-Paper.

Platform modules with the menus and URL patterns each one controls
Platform modules with the menus and URL patterns each one controls

Users & Roles

User Roles → Users creates admin-panel accounts; Roles defines what each role may do (permission keys per area such as article, campaign, push, finance, integrations). Built-in roles in the demo: Super Admin, Admin, Editor, Publisher, Reporter, Translator, Freelancer / Contributor and User. After an upgrade run php artisan cms:sync-permissions so new permission keys are registered and granted to Super Admin. Two-factor authentication (TOTP) is set up under Platform → Security, where you can also require it per role. Reader accounts (website and app visitors) are separate from admin users and are managed under Membership → Members.

Staff self sign-up is off by default. The staff panel page /register only exists when PANEL_REGISTRATION=true is set in .env. Self-registered accounts always get the least-privileged User role (profile and password only) and an admin must assign a real role. Readers have their own sign-up on the public site, which this switch does not affect.

Admin users
Admin users
Roles with permission counts
Roles with permission counts

Articles & Editor

Content → Articles lists every story with filters (type, category, status, flag, language, missing language). New article opens the editor: slug, category, author, type (news, video, gallery, investigation…), publish date/time and expiry, flags (breaking, premium, featured), topics and a tab per active language for title, excerpt, key points and body. The rich body editor supports headings, lists, links, tables, figures and inline images; unsafe HTML is removed automatically. Excerpt and SEO title/description are plain text. Stories scheduled in the future go live when published_at passes (no job needed). Revisions, fact-check and corrections are tracked from the editor; the workflow state (Draft → In Review → Approved → Published) is the same one the Story desk uses. The AI copilot buttons live in this editor.

Post formats. Choosing one of these types opens a builder panel under the body, with text per language: Trivia quiz and Personality quiz (questions, answers, results; scored on the server, so it also works without JavaScript), Recipe, Sorted list (Top N), Table of contents, Event and Gallery captions. Each format can be switched off in the settings; switched-off formats disappear from the type list.

Autosave, edit locks and conflict protection

The editor protects unsaved work and stops editors overwriting each other. Nothing needs to be configured.

  • Autosave. Changes are saved every 15 seconds to a private draft for the signed-in editor. A draft never changes the article, so autosaving a live story does not change what readers see. Press Save to apply your work.
  • Restoring a draft. If you leave without saving, the next visit offers Restore draft or Discard. Autosave pauses until you choose, so the draft on offer is not replaced. If the server cannot be reached, the draft is kept in the browser until it can.
  • Who is editing. A banner shows who has the article open, and the Articles list shows a badge. The lock is advisory: any editor with update permission can press Take over editing, and a lock is released when its holder closes the tab or stops responding for 2 minutes.
  • Conflict protection. If the article changed after you opened it, on any screen, your save is stopped and your text stays in the form. You can open the latest version, or choose Replace it with my version. The version you replace stays under Revisions.

Drafts hold the text and settings fields. Media is not included, so attach cover images and files again after restoring. Rows newly added in a post format builder may not come back; the editor tells you how many fields could not be placed. Unused drafts are removed after 30 days.

Trivia quiz builder: questions, answers and score-based results
Trivia quiz builder: questions, answers and score-based results
A trivia quiz on the public site
A trivia quiz on the public site
Recipe builder: times, nutrition, ingredients and steps
Recipe builder: times, nutrition, ingredients and steps
A recipe article on the public site
A recipe article on the public site
Articles list with language badges and status
Articles list with language badges and status
New article form with per-language tabs
New article form with per-language tabs

Comments, Inbox, Subscribers

  • Comments — moderation queue with Pending / Approved / Spam / Rejected tabs and bulk actions. Comments are held for approval by default (FRONTEND_COMMENTS_AUTO_APPROVE=false).
  • Contact inbox — messages from the public contact form with status (new, read, replied, archived) and reply by e-mail.
  • Subscribers — newsletter subscribers with list, language, source and state (confirmed, pending, unsubscribed); CSV export.
Comment moderation
Comment moderation
Contact inbox
Contact inbox
Newsletter subscribers
Newsletter subscribers

Taxonomy, Media, Bulk Posts

Under Content you also manage categories and subcategories, tags/topics, authors (profiles, bio, social), locations, static pages (about, policies, advertise, careers — Markdown, per language), preview links for unpublished stories, the Media library (uploads, responsive image conversions and a picker used by logos and articles), Web stories and Bulk post upload (CSV/JSON import with validation and a downloadable template, plus export; author roles may use it only when you enable bulkpost_authors_enabled). An article cover is used for its hero, cards and Open Graph/X social preview; gallery items remain separate. The editor's inline images are URL-based and are not Media Library attachments. Signed preview links show the last saved draft, in-review or scheduled story in the real reader template, are time-limited, and send noindex / no-store headers. WordPress sites can be imported from a WXR file under Import, Export, Backup.

Bulk post upload and export
Bulk post upload and export
Web stories list
Web stories list

Story Desk & Calendar

Newsroom → Story desk (/admin/desk/board) is the editorial board: Draft, In Review, Approved and Published columns with priority, beat, reporter and deadline on each card. Switch to List for sortable rows, Deadline calendar for the month view, and My Desk for your own assignments, deadlines, revisions and approvals.

Story desk board
Story desk board
Deadline calendar
Deadline calendar
Story list with state, priority and reporter
Story list with state, priority and reporter

News Inbox & Feeds

News inbox collects raw leads: RSS/Atom feeds, JSON APIs, government notices, press-release feeds, reader tips from the contact form and reader story submissions. Triage each item (Review, Assign, Ignore, Archive) or Create draft article from it. Add feeds on the Sources tab; desk:fetch-sources pulls due sources every five minutes. Feeds are fetched through an SSRF guard (public http/https hosts only).

News inbox with source badges and triage actions
News inbox with source badges and triage actions
Feed sources
Feed sources

Team, Tasks, Trusted

  • Desk team — reporter performance (published, views, average completion, deadlines met), link admin users to reporter/author profiles, role presets.
  • Newsroom tasks — a Kanban of to-do / doing / in review / done tasks tied to stories and people.
  • Trusted authors — people whose “submit for review” goes straight to Approved; everyone else waits for an editor.
Desk team and reporter performance
Desk team and reporter performance
Newsroom task board
Newsroom task board
Trusted authors
Trusted authors

AI Copilot & Intake

The AI Copilot (module AI Studio) works inside the article editor: rewrite, shorten, expand, translate, summarise, headline suggestions, SEO title/description and tag suggestions. AI intake (Automation → AI intake) turns a web URL, pasted text or an uploaded PDF into an unpublished draft article with the source recorded. The default provider is Anthropic (Claude); Google Gemini and OpenAI can be selected in Integrations. Limits: AI_MONTHLY_TOKEN_LIMIT (0 = unlimited) and AI_USER_DAILY_CALLS (default 200).

AI intake: draft from URL, pasted text or PDF
AI intake: draft from URL, pasted text or PDF

You must bring your own API key. With no key the copilot reports “not configured”. Output is a draft: an editor must review it. The AI paths were tested with faked HTTP, not against a live provider — use Integrations → Test connection and Tools → Diagnostics after adding your key.

Rules & Run Log

Automation → Automation rules is a trigger → actions engine. Triggers include article published, article views reach a threshold, ad campaign expiring and schedule (daily/hourly); actions include notifying editors, generating a social package, queueing a social post, sending breaking push, and sending a digest. Each run is logged on Run log with its status and reason (for example “conditions did not match”). The automation:tick command evaluates time and threshold triggers every minute.

Automation rules with triggers and actions
Automation rules with triggers and actions
Run log
Run log

Social Packages & Calendar

One content, everywhere: Social packages generates per-network copy (Facebook, LinkedIn, X, Telegram) and a social card image for an article; Social calendar and Posts schedule and track them; Social accounts & UTM stores the connected accounts and UTM presets that tag every link. Accounts connect through OAuth (Facebook, LinkedIn, X) or a Telegram bot token; the driver can be set to log for a dry run that publishes nothing. Social cards use headless Chrome when present (correct Bangla/Arabic shaping) and fall back to GD.

Social packages per article
Social packages per article
Social calendar
Social calendar
Social posts and delivery status
Social posts and delivery status
Accounts (demo accounts use the dry-run driver)
Accounts (demo accounts use the dry-run driver)
Connect accounts with OAuth
Connect accounts with OAuth

Social publishing was tested with faked HTTP only. Create your own Facebook, LinkedIn and X developer apps (see Common Setup) and test with the log driver first.

Newsletter & Push

Marketing → Campaigns builds newsletter campaigns for a subscriber list/segment with scheduling; Push alerts sends web-push (VAPID) and, when Firebase is configured, FCM push to app devices. “Send now” only queues the job — a queue worker delivers (see Cron & Workers). Rate is throttled with SENDER_MAIL_RATE (mails/second). Automatic digests: frontend:newsletter-send daily|weekly (07:00 daily, Friday 08:00), de-duplicated per subscriber and issue; mail carries one-click List-Unsubscribe. Live → Breaking alerts pushes a breaking story on demand and frontend:push-breaking does it automatically every minute.

Breaking alerts (push sender not configured warning shown until VAPID keys are set)
Breaking alerts (push sender not configured warning shown until VAPID keys are set)
Email templates preview
Email templates preview

Without VAPID keys web push is a no-op and without FCM credentials app push sends nothing; both show a warning banner on the Push screens. Delivery to real push services has not been verified here, only with faked HTTP: use Tools → Diagnostics to send one test push first.

Lists & Popup

Appearance → Newsletter popup & import controls the sign-up popup (trigger by delay, scroll or either, frequency cap in days, mobile on/off, consent tick-box, per-language copy) and imports subscribers. Subscribers carry list memberships (daily, weekly, breaking, sports, tech…), language and suppression state; after repeated delivery failures a subscriber is automatically suppressed. php artisan frontend:marketing-stats prints counts per list, language and status.

Newsletter popup settings and import
Newsletter popup settings and import

Advertisers & Campaigns

Monetisation → Advertisers is a light advertiser CRM (company, contact, e-mail, status). Ad campaigns define placement (header, sidebar, article middle/bottom, home banners), schedule, pricing (CPM, CPC or fixed), creative and live impression/click counts (buffered and flushed by ads:flush). Finished campaigns close automatically and ads:expiry-alert mails ads_alert_email about campaigns ending soon. House ads fill empty slots; AdSense (ads_adsense_client plus slot ids) and ads.txt are configured in Settings. Advertisers can also log in at /{locale}/advertiser/login (self-service portal) to see campaigns, request new ones and pay invoices. Tools → Ad slot check shows which source wins each placement.

Advertisers
Advertisers
Ad campaigns with placement, dates, pricing and counts
Ad campaigns with placement, dates, pricing and counts

The “Demo Advertiser Ltd” and “Dhaka Motors” rows are sample data. House ads are placeholders until you add campaigns or AdSense.

Invoices & Sponsorship

Invoices are created per advertiser/campaign with due dates and statuses (draft, sent, paid, void); Finance → Ad invoices lists them with View, PDF and Mark paid. Advertisers can pay online through any enabled gateway; offline payments are marked paid by hand. Sponsorships mark an article as “Sponsored” with a sponsor and a disclosure line that readers see in lists, feeds and the API.

Ad invoices
Ad invoices
Sponsored articles
Sponsored articles
Documents and payments
Documents and payments

Author Programme

Author applications (reader story authors apply to write), Badges and Author rewards: a monthly revenue-share based on verified unique views × a rate per 1,000 views, with a minimum payout, withdrawal requests and a statement per month (authors:close-month on the 1st at 02:00). Human verification of views (stats_human_verify) keeps bots from earning rewards.

Revenue-share settings, withdrawals, statements
Revenue-share settings, withdrawals, statements

Revenue, Ledger, P&L

The Finance module turns payments into books. Revenue shows income by stream (advertising, subscriptions, e-paper, sponsored content, classifieds, donations), MRR, paying subscribers, churn and overdue invoices. The Ledger is an auto-posted double-entry-style list (income and expense accounts, manual entries with attachment, CSV export). Profit & loss summarises by month and account. Posting is idempotent: finance:post runs hourly and can be run by hand any time.

Revenue dashboard by stream
Revenue dashboard by stream
Ledger with auto-posted and manual entries
Ledger with auto-posted and manual entries
Profit & loss by month
Profit & loss by month

Figures in the screenshots are demo data in BDT.

Receivables & Accounts

  • Receivables — unpaid ad invoices aged into current, 1–30, 31–60, 61–90 and 90+ day buckets per advertiser.
  • Chart of accounts — income and expense accounts (e.g. ad revenue, hosting, salaries, reporter commissions).
  • Classifieds — submitted notices (jobs, tenders, obituaries) with fees, approval and payment.
  • Donations — the public support page: gateway, currencies, suggested amounts, and received donations.
  • Settings — base currency and exchange rates used to report multi-currency income.
Receivables aging
Receivables aging
Chart of accounts
Chart of accounts
Classifieds and notices
Classifieds and notices
Support / donations
Support / donations
Base currency and rates
Base currency and rates

Payment Gateways

One payment layer serves memberships, ad invoices, donations and single-article purchases. 13 options: Stripe, PayPal, SSLCOMMERZ, bKash, Nagad, Razorpay, Mercado Pago, iyzico, PayTabs, Paystack, NOWPayments (crypto), Bank transfer (with proof upload) and Manual/offline. A gateway appears at checkout only when its credentials exist. Enter them in Settings → Integrations (encrypted). Webhook URL for every gateway: https://YOUR-DOMAIN/membership/webhook/{gateway}. Stripe supports automatic off-session renewal; the other gateways send a signed pay-now e-mail and follow the dunning schedule (retries at 1, 3 and 5 days, expiry after a 7-day grace). Full per-gateway steps are in Common Setup → Payment gateways.

Every gateway adapter was written against the provider’s public documentation and tested with faked HTTP only. Run each one through its sandbox before taking real money. Supply your own merchant accounts and keys.

Plans, Members, Coupons

Monetisation → Membership has four screens: Plans (tier free/premium/corporate, interval month/year, multi-currency prices, trial days, features, ad-free flag, corporate seats), Members (reader accounts with subscription, invoices, manual subscription, refund and credit notes), Coupons (percent or fixed, plan restriction, max uses, expiry) and Bank transfers (proof-of-payment queue: approve or reject, receipt download). Renewals run hourly through membership:renew; a failed charge puts the subscription in past due with access during the grace window.

Renewal reminders

Readers are e-mailed in their own language before a membership period ends, and get an app push as well when FCM is configured. The message states what will really happen:

SituationWhat the reader is toldWhen
Card saved with StripeThe amount will be charged automatically on the date, and they can cancel before then7, 3 and 1 days before
Every other gateway, or no saved cardA link to pay will arrive on the date, and they can also pay from their account7, 3 and 1 days before
Canceled, free or admin-granted membershipAccess ends on the date, and they can subscribe againOnce, 3 days before

Change the days under Settings → Public site → Membership, for example 7, 3, 1. Enter 0 to switch reminders off. A step older than the current period is skipped, so nobody hears “renews in 7 days” right after starting a 7-day trial. After a scheduler outage only the latest step is sent. Check what is due without sending anything with php artisan membership:remind --dry-run.

Membership plans
Membership plans
Members (reader accounts)
Members (reader accounts)

Paywall & Pay-per-post

Mark an article Premium and non-members see the intro plus a paywall card (truncation happens on the server). A metered free-article count and a per-article price (pay-per-post, price per currency) are supported; a purchase unlocks that article for that reader and appears in GET /api/v1/me/entitlements. E-paper PDF downloads can be free or members-only with EPAPER_DOWNLOAD=free|premium. Corporate plans let the owner invite seats from the account page.

Live Blogs & Breaking

Live → Live events creates a live blog (title, summary, status live/ended, languages) and its timed updates (text, pinned, embed, image). The public page polls for new updates and the API exposes /{locale}/live for the app. Breaking alerts lists breaking-flagged articles and lets you push them to subscribers.

Live events
Live events
Breaking alerts
Breaking alerts

Podcasts & Web Stories

Live → Podcasts manages episodes (cover, audio, duration, category, per-language title and host, publish date; drafts and future dates stay off the site). Content → Podcast shows groups episodes into shows with season and episode numbers; every show gets a public page (/{lang}/podcast-shows/{slug}) and its own RSS feed with iTunes tags (…/feed.xml) for Apple Podcasts or Spotify. Content → Episodes manages shows and episodes (audio, duration, transcripts, premium flag, per-language titles). Premium episode audio can be moved to the private disk with php artisan podcast:protect-audio. Content → Web stories builds swipe stories (slides, status, languages).

Podcast episodes
Podcast episodes
Episodes with premium flag and transcripts
Episodes with premium flag and transcripts
Public podcast shows page
Public podcast shows page

E-paper Editions

Content → E-paper uploads or builds one edition per day × language × edition. Automatic PDFs: php artisan frontend:epaper-build {date?} {--locale=*} {--edition=*} {--force} renders each day with headless Chrome into a multi-page PDF (A-series sheet, 8 pages) from the same layouts as the web reader; it runs daily at 05:30 in the site time zone. A language needs at least 30 published translations (EPAPER_MIN_STORIES) to be built. Set EPAPER_CHROME if Chrome/Chromium is not auto-detected, and EPAPER_BASE_URL if the build host reaches the site on another URL. Downloads are signed 10-minute links with Range support; EPAPER_DOWNLOAD=premium turns them into a membership perk.

E-paper editions: upload, status, pages, downloads
E-paper editions: upload, status, pages, downloads

Needs Google Chrome or Chromium on the server. See Common Setup → Headless Chrome.

Analytics

Analytics reports editorial performance: views, reads and read-through over a range, daily bars, engagement (reactions, comments, shares), and breakdowns by language, category and author, with CSV export. Counters are buffered in cache and flushed every minute by stats:flush; views are human-verified by default. GA4 and Meta Pixel ids are optional extras in Settings → Public site and load only after consent.

Editorial analytics
Editorial analytics

SEO & Sitemaps

SEO (/admin/seo): indexing and sitemap layout (flat or segmented by month/category/page/author), optional Google Indexing API pings on publish/update/delete, and per-entity overrides (meta title, description, keywords, noindex, FAQ) for articles, categories and pages in each language. Tools → Sitemaps regenerates sitemaps and feeds. Redirects manages 301/302 rules (auto-created when a slug changes). SEO → Robots & AI crawlers sets whether the site is indexable, a default allow/disallow for AI crawlers (GPTBot, ClaudeBot, Google-Extended and others) with a per-bot override, extra Allow:/Disallow: lines, and shows a live preview of robots.txt. SEO → llms.txt sets how many latest and popular stories are listed and an intro text per language. Before anything is saved there, SEO_AI_CRAWLERS=allow|disallow is the default.

SEO settings and overrides
SEO settings and overrides
Sitemaps
Sitemaps
Redirects
Redirects
Robots.txt and AI crawler rules
Robots.txt and AI crawler rules

Home Layout & Navigation

Appearance → Home layout is a block builder: order, show/hide and edit the home blocks (lead story, live TV, featured tiles, weather & markets widgets, region strips, header ad slot, latest, editor’s picks, most read, e-paper banner, category blocks). Navigation builds the header, “more” dropdown, footer and bottom menus with per-language labels and mega-menus.

Navigation → Mobile bottom tabs controls the bottom bar of the mobile website and the mobile app from one list; the first five active items are shown. Home, Shorts, Live, E-paper and Search stay inside the app’s tab bar. Categories, pages and other sections open as a new screen, and other links open in the browser. Add a More item to open the mobile menu on the website and the More screen in the app. Changes to the app bar need an app built from this version.

Site → Visual theme sets the public site colours (background, surface, text, muted text, lines), corner radius, content width, spacing, density and the header and footer variants. Save a draft, open the preview, then publish; readers see nothing until you publish.

Home layout block builder
Home layout block builder
Navigation builder
Navigation builder
Visual theme editor with draft and publish
Visual theme editor with draft and publish

Fonts, Widgets, Phrases

  • Fonts — upload woff2/woff/ttf families and assign one per language, plus base size and scale (so Bangla and Arabic read well).
  • Sidebar widgets — build the widget stack for the home page and the article sidebar: popular posts, latest posts, tags, a poll, newsletter sign-up, an ad, social links or custom HTML (scripts are removed on save). Titles per language; drag rows to re-order. A placement with no widgets keeps the built-in layout.
  • Site phrases (/admin/phrases) — edit every interface string per language (public site, mobile app, admin panel), with completeness bars and JSON import/export.
  • Email templates — preview transactional mails in any language.
Fonts per language
Fonts per language
Sidebar widget builder
Sidebar widget builder
Phrase editor with completeness per file
Phrase editor with completeness per file

Health, System, Webhooks

  • System health (/admin/platform/health) — PHP, database, cache, queue, failed jobs, writable folders, public/storage link, disk usage, scheduler heartbeat, payment keys and renewal freshness. CLI: php artisan health:check.
  • System — maintenance mode (admins keep access), media storage disk status, CDN purge (Cloudflare, Bunny or generic URL).
  • Webhooks — outgoing signed webhooks (article published/updated, comment created, subscriber confirmed, member subscribed, invoice paid) with retry and delivery log (platform:webhooks-retry). Each request carries X-MediaDesk-Signature: sha256=HMAC_SHA256(timestamp + "." + body, secret). New endpoints start disabled; private and local addresses are blocked. Deliveries are sent by the queue, so run a queue worker in production, and the scheduler for retries. article.published is sent when an article is saved as published; a story scheduled for later does not send it when its time arrives.
  • Tools → Diagnostics — test e-mail, AI key, storage write and a test push.
  • Known issues & docs (/admin/tools/help) — lists any Markdown pages you keep in a docs/ folder of the project (for example your own internal notes). The package does not include such pages: this HTML documentation is the complete guide, and the API specification is documentation/openapi.yaml.
System health
System health
Maintenance, storage and CDN
Maintenance, storage and CDN
Outgoing webhooks
Outgoing webhooks
Diagnostics
Diagnostics

Security & Protection

  • Two-factor authentication (TOTP app) with an option to require it for chosen roles; active sessions list.
  • Protection → Security & lockouts — account lockout after failed sign-ins (default 5 attempts / 15 minutes), per-IP sign-in limit, password policy (length, character classes, breached-password check), comment link policy, human verification of views.
  • Email blacklist (emails/domains with wildcards) and Site code (custom CSS/JS/head code, consent-gated).
  • Activity log and Login activity record admin actions and sign-ins; geo-restriction and reCAPTCHA / Turnstile (Integrations → Captcha) protect forms.
Two-factor authentication and sessions
Two-factor authentication and sessions
Brute-force and password policy
Brute-force and password policy
Email blacklist
Email blacklist
Site code
Site code

REST API & API Clients

The public JSON API lives under /api/v1 (read-only, cacheable, 120 requests/minute/IP; OpenAPI spec in openapi.yaml beside this guide). Reader endpoints (login, register, /me, devices, subscription, comment edit/delete) use Laravel Sanctum bearer tokens (180-day expiry). Platform → API clients issues partner tokens: a syndication feed partner can read articles, and a client with the write-articles scope can POST /api/v1/articles — the article always lands in review and is never auto-published. Image URLs are downloaded through an SSRF guard (max 8 MB, JPEG/PNG/WebP).

API clients and syndication partners
API clients and syndication partners
curl https://your-domain.com/api/v1/languages
curl https://your-domain.com/api/v1/en/home

Import, Export, Backup

  • Platform → Data — import a WordPress WXR file (posts, categories, tags, authors, featured images; dry-run option) and export articles (one row per translation) to JSON or CSV. CLI: php artisan platform:import-wxr, php artisan platform:export-articles.
  • System → Operations creates private local SQL backups on demand and every day at 02:30 in the site timezone. It provides retention, download, restore and delete controls. Restore first creates a pre-restore safety backup.
  • Optional Google Drive copies are configured under Settings → Integrations → Storage → Google Drive backups. Client Secret and Refresh Token are encrypted in the database; use Test connection before enabling. A Drive failure never removes the local backup.
  • php artisan media:migrate-disk public s3 --dry-run moves media between local, S3, R2 and B2.
Import / export
Import / export
Database backup
Database backup

Database backups do not include uploaded media. Continue backing up storage/app/public, .env and any external object-storage bucket separately. Keep the scheduler cron active for automatic backups.

General, Public Site, Mail

  • General settings — organisation name, contact, currency, date/time format, light/dark logos, favicon, default social image.
  • Public site — default language and skin, per-language site name, tagline, description, footer copyright, address; brand colour, social links, feature flags (comments, newsletter, reactions, push, stories, paywall, e-paper, podcasts, live), AdSense/GA4/Pixel ids.
  • Mail setting — the SMTP form (the same fields are in Integrations → Mail / SMTP).
  • Database backup, reCAPTCHA.
General settings with logos
General settings with logos
Public site settings per language
Public site settings per language
Mail settings
Mail settings

Integrations Hub

Settings → Integrations (/admin/settings/integrations) is the one place for third-party credentials. Values are stored encrypted in the settings table and override .env; secrets are never shown again (a blank box keeps the stored value) and most groups have a Test connection button. Groups: General (Anthropic, AI default provider, Gemini, OpenAI, Mail/SMTP, Mailgun/Postmark/SES, Brevo, Captcha), Storage (media disk, S3, R2, B2, Google Drive backups), Push (VAPID, Firebase FCM), Payments (the 13 options above), Social (Google sign-in, Facebook, LinkedIn, X, Telegram, social-card renderer) and Platform (Google Indexing, CDN purge, Pusher/Ably). Google Drive backup is database-only and never reads credentials from .env. Existing environment-backed integration values can be copied in once with php artisan integrations:import-env. Rotating APP_KEY makes stored secrets unreadable — re-enter them. Step-by-step setup for each group is in Common Setup.

Integrations hub: Anthropic (AI) group selected
Integrations hub: Anthropic (AI) group selected

Troubleshooting FAQ

Find the symptom you see, then work through the fixes in order. Each answer says who can do it. First stop for every problem: System → System health in the admin panel (it flags most causes below in plain words) and, for the server administrator, the last lines of storage/logs/laravel.log.

System → System health with the queue, failed jobs, folder permission and scheduler rows numbered
System → System health: (2) queue driver, (3) failed jobs, (4) folder permissions, (5) cron heartbeat. “Warn” on optional keys (AI, push) is normal until you add them.
“Permission denied”, a blank 500 page, or “The stream or file …/storage/logs/laravel.log could not be opened”

Why: the web server user (often www-data, nginx or your cPanel user) cannot write to storage/ or bootstrap/cache/. Common after uploading files by FTP as another user, or after running php artisan as root. System health shows Writable storage/… as Fail. Who: server admin.

  1. Give the folders to the web user

    sudo chown -R www-data:www-data storage bootstrap/cache (replace www-data with your web user), then sudo chmod -R 775 storage bootstrap/cache. Never use 777.

  2. Shared hosting (no SSH)

    In the file manager set storage and bootstrap/cache (and everything inside) to 755 or 775, “apply recursively”.

  3. Run artisan as the web user from now on

    sudo -u www-data php artisan …, so new cache and log files get the right owner.

  4. SELinux (CentOS, Rocky, Alma)

    sudo chcon -R -t httpd_sys_rw_content_t storage bootstrap/cache.

  5. Check

    Reload System health: all Writable rows should be Ok.

Uploaded images are broken (404) or show another site’s files

Why: the public/storage link is missing or points to an old folder (it stores an absolute path, so it breaks when the project is moved). Who: server admin.

  1. Recreate the link

    rm public/storage && php artisan storage:link. No SSH: ask your host to create a symlink from public/storage to storage/app/public.

  2. Check

    readlink public/storage must print this project’s storage/app/public. System health row public/storage link = Ok.

  3. Using S3/R2/B2?

    The bucket (or its CDN) must be publicly readable, and the public / CDN base URL in the storage integration must be correct.

Large uploads (video, PDF) fail with “The file failed to upload” or HTTP 413

Why: PHP or the web server limits request size. Who: server admin.

Fix: raise upload_max_filesize and post_max_size in php.ini (for example 64M), client_max_body_size 64m; in nginx (or LimitRequestBody in Apache), then reload PHP-FPM and the web server.

Queue worker is not processing jobs: newsletters stay “sending”, push never arrives, social posts stay “queued”

Why: with QUEUE_CONNECTION=redis or database, jobs wait until a worker picks them up. No worker, a worker on the wrong connection or a worker running old code all look the same: nothing happens. Who: server admin.

  1. Is a worker running?

    sudo supervisorctl status should show mediadesk-worker RUNNING. If not: sudo supervisorctl reread && sudo supervisorctl update && sudo supervisorctl start mediadesk-worker:* (config in Cron, Queue & Workers).

  2. Same connection as .env?

    The worker command names a connection (queue:work redis). It must match QUEUE_CONNECTION in .env. A redis worker never sees jobs stored in the database queue, and vice versa.

  3. Database driver: do the tables exist?

    The project ships no jobs migration. Run php artisan queue:table, php artisan queue:failed-table, php artisan migrate --force.

  4. Redis driver: is Redis reachable?

    redis-cli ping answers PONG; the phpredis extension is installed (php -m | grep redis); REDIS_HOST, REDIS_PASSWORD are right.

  5. Old code or old settings?

    After any deploy or .env change: php artisan optimize:clear, php artisan optimize, php artisan queue:restart. Workers keep the code they started with until restarted.

  6. Jobs failing instead of waiting?

    php artisan queue:failed lists them with the error; fix the cause (usually mail or push credentials), then php artisan queue:retry all. System health row Failed jobs should return to 0.

  7. Watch it work

    Run php artisan queue:work --once -v by hand: it processes one job and prints the result or the error.

No way to run a worker (basic shared hosting)? Set QUEUE_CONNECTION=sync: jobs then run immediately inside the request. Fine for small lists; large newsletters will be slow.

Scheduled articles do not publish; view counts, renewals and digests do not update; System health says “no heartbeat”

Why: the cron entry is missing or fails. Who: server admin.

  1. Is it installed for the right user?

    sudo crontab -u www-data -l should show * * * * * cd /path/to/project && php artisan schedule:run >> /dev/null 2>&1.

  2. Use full paths

    Cron has a short PATH. Use the full PHP path from which php (for example /usr/bin/php8.3) and the full project path. On cPanel use the PHP 8.3 binary your host documents (for example /usr/local/bin/ea-php83).

  3. Run it by hand

    php artisan schedule:run in the project folder: errors print here. php artisan schedule:list shows the next run of each task.

  4. Check

    Within two minutes the System health Scheduler row turns Ok.

Payment webhook rejected (HTTP 400 “invalid signature”), or a paid order stays “pending”

Why: MediaDesk only trusts a webhook whose signature matches the secret you saved. A wrong or old secret, a test/live mix-up, a wrong URL, or a proxy that changes the request body all end in 400 and the payment is not applied. Who: admin panel for secrets; server admin for CDN/firewall.

  1. Look at the gateway’s delivery log

    Stripe: Developers → Webhooks → your endpoint; PayPal: Webhooks events; other gateways have a similar “IPN / webhook history”. A 400 response with {"error":"invalid signature"} means the secret is wrong; 404 means the URL or gateway name is wrong; 403/5xx or a challenge page means a firewall/CDN is in the way.

  2. URL exactly right

    https://your-domain.com/membership/webhook/stripe — no language prefix (/en/), no trailing slash, https, gateway name in lower case (stripe, paypal, sslcommerz, razorpay, mercadopago, paytabs, paystack, nowpayments, …).

  3. Secret copied from the same endpoint and mode

    Stripe: each endpoint has its own whsec_…, test mode and live mode have different ones, and the stripe listen CLI prints yet another. Paste the one that belongs to this URL and mode into Integrations → Payments → Stripe → Webhook signing secret (see the screenshot in Payment Gateways). PayPal: the Webhook id must be the id of this webhook in the same sandbox/live app. Razorpay, PayTabs, NOWPayments: the webhook/IPN secret you typed in their dashboard. Save, then press Resend in the gateway dashboard.

  4. Sandbox switch matches the keys

    Sandbox on with live keys (or the reverse) fails verification or verifies against the wrong account. For Stripe the key prefix decides (sk_test_ / sk_live_); Test connection warns on a mismatch.

  5. Server clock

    Stripe signatures expire after 5 minutes. Make sure the server time is synced (timedatectl shows “System clock synchronized: yes”).

  6. CDN / firewall

    Do not cache, minify, challenge or rewrite POST /membership/webhook/*. On Cloudflare add a WAF skip rule for that path (Bot Fight Mode and “I’m under attack” block webhooks). See the bypass list in CDN.

  7. Secrets unreadable after a move

    If APP_KEY changed, stored secrets read as empty: re-enter them.

Receiving MediaDesk’s own outgoing webhooks (Webhooks screen, /admin/platform/webhooks) and your check fails? Compute HMAC_SHA256(X-MediaDesk-Timestamp + "." + raw body, secret) over the raw request body (before JSON parsing) and compare it with the hex part of X-MediaDesk-Signature: sha256=….

E-mails never arrive (password reset, newsletter confirmation, invoices)

Who: admin panel.

  1. Mail is still in “log” mode

    System health row Mail (log) means mails are only written to storage/logs/laravel.log. Configure Settings → Integrations → Email → Mail / SMTP.

  2. Send a test

    Test connection on the Mail tab, or System → Diagnostics → Send test email. The error text (wrong password, port blocked, TLS) tells you what to change. Port 587 + TLS or 465 + SSL; many hosts block port 25.

  3. Arrives in spam

    Use a From address on your own domain and add the SPF, DKIM and DMARC records your mail provider gives you.

  4. Only bulk mail is missing

    Newsletters are queued: see queue worker.

My change (logo, setting, article) does not show on the site

Why: lists are cached for about 60 seconds, a CDN for about 5 minutes, and your browser caches images. Who: anyone; the last step is for the server admin.

  1. Wait a minute and hard-reload

    Ctrl/Cmd + Shift + R, or open a private window.

  2. Visual theme?

    Theme changes need Publish saved draft, not only Save theme draft.

  3. CDN in front?

    System → Maintenance / CDN → Purge.

  4. Still old

    php artisan optimize:clear then php artisan optimize.

Browser notification prompt never shows, or app push is not delivered
  • Web: VAPID keys missing (System health Web push (VAPID) = not configured): Integrations → Push & realtime → Web push (VAPID) → Generate VAPID keys. The site must use HTTPS. The prompt appears after the 2nd article view or from the footer “Notifications” button.
  • App: the app needs your own Firebase config files at build time, and the server needs the project id and service-account JSON (Push). iOS also needs an APNs key in Firebase; the iOS simulator cannot receive push.
  • Both are sent by the queue: see queue worker.
Other common problems
SymptomCause / fix
Every page redirects to /installThe installer has not finished, or storage/installed was not copied after a move. Copy it back or set APP_INSTALL=true in .env, then php artisan config:clear
Page loads assets from 127.0.0.1:5173 and looks unstyledpublic/hot exists from npm run dev. Delete it and run npm run build
Unstyled admin or site after an upgradepublic/build missing: run npm ci && npm run build
Secrets in Integrations read as empty after a deployAPP_KEY changed. Restore the old key or re-enter the secrets
Comments accepted but invisibleThey wait for moderation (Content → Comments) unless auto-approve is on
Site shows noindex / Disallow: /Set APP_ENV=production or SEO_INDEXABLE=true
A language added in admin answers 404The language is not active, or lang/{code}/site_*.json files are missing
E-paper PDF build failsChrome/Chromium not found (set EPAPER_CHROME); fewer than 30 published translations in that language; EPAPER_BASE_URL not reachable from the server itself
AI copilot says “not configured”No key for the chosen default provider: AI Keys
artisan serve is slow with video/audioThe PHP development server has no Range/gzip support. Use nginx/Apache for real testing

Still stuck? Run php artisan mediadesk:doctor (prints a fix hint for each failing check) and include its output and the last 50 lines of storage/logs/laravel.log when you contact support.

Honest Notes

  • Bring your own keys. AI providers, payment gateways, push (VAPID/FCM), social networks and S3/R2/B2 need your own accounts, and these providers (for example Stripe, PayPal and Firebase/FCM) may charge their own fees, which are not included in the item price. They were tested against faked HTTP, not live services.
  • No author-controlled services. MediaDesk never routes your traffic through the author’s servers. Web push uses minishlink/web-push (MIT) with the VAPID key pair you generate (Generate VAPID keys or php artisan frontend:push-keys); no VAPID key ships in the code, and notifications go straight from your server to the push endpoint each reader’s browser issues (Google, Mozilla, Apple). App push goes from your server to Google FCM with your own service account. There is no licence server, telemetry or phone-home call.
  • Demo data. Screenshots show sample articles, “Demo” advertisers, invoices and ledger rows, and sample weather/stock/prayer values. A fresh install is empty.
  • Queue worker and cron are required for campaigns, push, social, renewals, e-paper and the ledger.
  • Mobile purchases. The app opens membership and pay-per-post checkout in the device’s external browser (never inside the app); Apple/Google store billing is not implemented. Where the store does not allow external purchase links, build the app with CHECKOUT_LINKS=false (see Payments & store policy).
  • Not included: Meta Conversions API, multiple e-paper editions per day (only dhaka), bookmark sync across devices (saved articles live on the device).

Changelog

Version 1.2.0 — 3 November 2026
  • Official ZIP upload and FTP/cPanel updates finished from the admin panel, with checksums, protected paths, backups and rollback.
  • Installed/new version and pending database status plus one-click database migrations and permission synchronization.
  • Scheduled/on-demand local backups, restore controls and optional automatic Google Drive copies configured securely in Admin.
  • Server-managed app maintenance, minimum/latest versions, optional/required updates and store links.
  • Dynamic web-managed app bottom navigation and improved Shorts autoplay and tap-to-pause/resume.
  • Platform Operations tools for failed queue jobs and sanitized app error reports.
  • App download section on the website, with Android and iOS links set under Settings → Public site.
  • E-paper lists real PDF editions on the website and in the app, with protected PDF access and page previews.
  • Article views are tracked on the website and in the app, and Most Read shows the server counts.
  • App: read articles aloud with the device voice, following feeds, guest library, library backup and account security tools.
  • Category/listing, pagination, play/pause indicator and authenticated-reader redirect fixes.
  • Reader self-service: download my data and delete my account on the website, including Google sign-in accounts.
  • Renewal reminders by e-mail and app push before a membership renews or ends, with the days set in Admin.
  • Article editor autosave to a private draft, restore, who-is-editing locks with takeover, and protection against overwriting newer changes.
  • Fixed saving of Bangla and Arabic text in key points, poll options, plan features, live key points and AI headlines, which could fail with an error or be cut apart.
  • Fixed deleted accounts blocking a later Google sign-up with the same address, and deleted or banned readers staying signed in on other browsers.

Upgrade from 1.0.0: take a full backup, use System → System update, or follow the manual workflow above. Install locked dependencies with composer install; do not run composer update.

Version 1.0.0 — initial release
  • Public reader site with 11 skins, visual theme editor, 4 languages (Bangla, English, Arabic RTL, French), PWA, live blog, podcasts and podcast shows, e-paper, web stories and shorts.
  • Interactive post formats (quizzes, recipes, lists, table of contents, events) and a sidebar widget builder.
  • Newsroom ERP: story desk, inbox and feeds, tasks, revisions, fact-check and corrections.
  • Optional AI Copilot and AI intake with your own Anthropic, Gemini or OpenAI-compatible key.
  • Automation engine, social packages, newsletters and push campaigns.
  • Advertising, membership, pay-per-post, classifieds, donations, finance reports and 13 payment options.
  • REST API (api/v1) with an OpenAPI document, partner syndication and article write API.
  • Flutter app for Android and iOS; checkout opens in the external browser.
  • Web installer that only installs into an empty database and locks itself after the first run.
  • RBAC, two-factor authentication, signed webhooks, WordPress import, backups and a module manager.
  • Uploads are stored under server-chosen file names whose extension comes from the detected file type; HTML, SVG, script and other renderable types are refused on every upload path.
  • Demo mode (APP_DEMO=true): the published demo logins are read-only and see API keys masked (DEMO_READONLY_ACCOUNTS).

Future updates are listed here with their version number and the upgrade steps from Updating.

Sources & Credits

MediaDesk is built on open-source software. All third-party code keeps its own license; the item license covers MediaDesk’s own code only.

ComponentLicense
Laravel 13, Fortify, Sanctum, TinkerMIT
Spatie Laravel Activitylog, Laravel MedialibraryMIT
Guzzle, Intervention Image, Flysystem S3, Ziggy, Symfony componentsMIT
minishlink/web-pushMIT
Tailwind CSS 4, ViteMIT
FlyonUI 2.4.1 — the free community edition from npm (flyonui), not FlyonUI Pro. Bundled by Vite from node_modules; two of its helper files are in public/dashboard-assets/third-party/flyonui/ with its LICENSE. It includes Preline UI plugins (Preline UI, MIT + Preline UI Fair Use License, text included in that LICENSE file)MIT
ApexCharts 4.7.0, pinned on purpose: it is an MIT release (LICENSE shipped in public/dashboard-assets/third-party/apexcharts/). Later 5.x releases moved to a Community/Commercial dual license, so do not upgrade it without checking that licenseMIT
DataTables, DataTables ButtonsMIT
jQuery, Lodash, Axios, JSZipMIT
Dropzone 6.0.0-beta.2 (npm tarball integrity sha512-k44yLuFF…h9fQ==; its package.json and LICENSE file both say MIT, © 2021 Matias Meno). LICENSE shipped in public/dashboard-assets/third-party/dropzone/MIT
pdfmake 0.3.11 (LICENSE shipped next to it). Its vfs_fonts.js embeds only the four Roboto files (Regular, Medium, Italic, MediumItalic), © Google, Apache 2.0 — license text in LICENSE-Roboto-Apache-2.0.txt. No other or proprietary font is embeddedMIT / Apache 2.0 (fonts)
Flatpickr, Raty, SweetAlert2, Summernote (article editor)MIT
D3 3.5.17, Datamaps, TopoJSON client 3.1.0 (LICENSE file next to each)BSD-3 / MIT / ISC
hls.jsApache 2.0
Tabler Icons, Lucide (via Iconify)MIT / ISC
Circle Flags, Flagpack (via Iconify)MIT
Fonts: Inter, Cairo, Noto Sans Bengali, Noto Serif Bengali, Noto Naskh Arabic, Source Serif 4 (Fontsource)SIL Open Font License 1.1
Flutter app: Flutter, provider, http, cached_network_image, shared_preferences, url_launcher, intl, just_audio, flutter_secure_storage, app_linksBSD-3 / MIT / Apache 2.0
Flutter app: Firebase Core and Firebase Messaging (firebase_core, firebase_messaging). The SDK code is BSD-3; using Firebase requires your own Firebase project and acceptance of Google’s Firebase / Google APIs Terms of Service (see Firebase disclosure)BSD-3 + Google service terms
Flutter app fonts: Inter, Source Serif 4, Noto Sans Bengali, Noto Naskh Arabic (OFL text next to each file in assets/fonts/)SIL Open Font License 1.1
Demo photos, video and audio

The photos, clips and tracks below appear on the live demo only and are not included in the download: they are Creative Commons or public-domain works, used under their own licenses, and are not part of the item license. When you load the optional demo data, articles and podcasts get generated placeholder thumbnails and the demo video articles and podcast audio are skipped. Upload your own media from the admin.

ItemAuthorLicenseLink
Article photo national-1S.M.M.Musabbir Uddin / Wikimedia CommonsCC BY 4.0source
Article photo national-2TausifAlHossain / Wikimedia CommonsCC BY-SA 4.0source
Article photo national-3Ashikul Islam Anik / Wikimedia CommonsCC BY-SA 4.0source
Article photo world-4U.S. Department of Agriculture / Wikimedia CommonsPublic domainsource
Article photo world-6Artem Svetlov from Moscow, Russia / Wikimedia CommonsCC BY 2.0source
Article photo politics-8Press Information Department / Wikimedia CommonsPublic domainsource
Article photo economy-10Fahad Faisal / Wikimedia CommonsCC BY-SA 4.0source
Article photo economy-11Torsten Dettlaff / Wikimedia CommonsCC BY-SA 4.0source
Article photo national-39Heptagon / Wikimedia CommonsPublic domainsource
Article photo sports-17The President's Office, Maldives / Wikimedia CommonsCC BY 4.0source
Article photo sports-43A.yeasin10 / Wikimedia CommonsCC BY-SA 4.0source
Article photo national-50Nahian Bin Shafiq / Wikimedia CommonsCC BY-SA 4.0source
Article photo world-5Roman Ismayilov / Wikimedia CommonsCC BY 4.0source
Article photo politics-7Press Information Department / Wikimedia CommonsPublic domainsource
Article photo politics-12Salim Khandoker / Wikimedia CommonsCC BY-SA 4.0source
Article photo economy-13أشرف اليماني / Wikimedia CommonsCC BY-SA 4.0source
Article photo sports-15Acabashi / Wikimedia CommonsCC BY-SA 4.0source
Article photo sports-16Robert Drummond / Wikimedia CommonsCC0source
Article photo world-40Eric Kilby / Wikimedia CommonsCC BY-SA 2.0source
Article photo politics-41Press Information Department / Wikimedia CommonsPublic domainsource
Article photo economy-42Bikrampur / Wikimedia CommonsCC0source
Article photo world-49Leonhard Lenz / Wikimedia CommonsCC0source
Article photo economy-14Leocadio Sebastian / Wikimedia CommonsCC BY 2.0source
Article photo politics-9Press Information Department / Wikimedia CommonsPublic domainsource
Article photo health-27Press Information Department / Wikimedia CommonsPublic domainsource
Article photo environment-36Soumyajit Nandy / Wikimedia CommonsCC BY-SA 4.0source
Article photo education-30Narek75 / Wikimedia CommonsCC BY-SA 4.0source
Article photo lifestyle-33Authentic Stock / StockSnapCC0 1.0source
Article photo technology-45Christopher Hedreyd - PIA 4A CALABARZON / Wikimedia CommonsPublic domainsource
Article photo health-28Shixart1985 / Wikimedia CommonsCC BY 2.0source
Article photo entertainment-18Valsts kanceleja/ State Chancellery from Rīga, Latvija / Wikimedia CommonsCC BY-SA 2.0source
Article photo education-31Australian Humanitarian Partnership (AHP) Bangladesh / Wikimedia CommonsCC BY-SA 4.0source
Article photo entertainment-19usbotschaftberlin / RawpixelCC0 1.0source
Article photo entertainment-20Jorge Royan / Wikimedia CommonsCC BY-SA 3.0source
Article photo technology-21Rawpixel (public domain contributor)CC0 1.0source
Article photo technology-22Director Mtonga Kenya / Wikimedia CommonsCC BY-SA 4.0source
Article photo technology-23Markus Spiske / RawpixelCC0 1.0source
Article photo opinion-24Mike Prince from Bangalore, India / Wikimedia CommonsCC BY 2.0source
Article photo opinion-25Humphrey Bolton / Wikimedia CommonsCC BY-SA 2.0source
Article photo opinion-26Kristin Hardwick / StockSnapCC0 1.0source
Article photo lifestyle-34Kingshuk Mondal / Wikimedia CommonsCC BY 4.0source
Article photo environment-37Yahya / Wikimedia CommonsCC BY-SA 4.0source
Article photo health-29Rawpixel (public domain contributor)CC0 1.0source
Article photo entertainment-44Shixart1985 / Wikimedia CommonsCC BY 2.0source
Article photo opinion-47Solomon203 / Wikimedia CommonsCC BY-SA 4.0source
Article photo education-32Gary Todd / Wikimedia CommonsCC0source
Article photo technology-46Rawpixel (public domain contributor)CC0 1.0source
Article photo lifestyle-35Shixart1985 / Wikimedia CommonsCC BY 2.0source
Article photo opinion-48Ferdous / Wikimedia CommonsCC BY-SA 3.0source
Article photo environment-38Shobhla-Himachal / Wikimedia CommonsCC BY 4.0source
Gallery photo education-31-1Joel Solomon from Denver, CO, USACC BY 2.0source
Gallery photo education-31-2Muhammad Jubayer HossainCC BY-SA 4.0source
Gallery photo education-31-3DFID - UK Department for International DevelopmentCC BY 2.0source
Gallery photo education-31-4Motiur Rahman OniCC BY-SA 4.0source
Gallery photo education-31-5Beyond AccessCC BY-SA 2.0source
Gallery photo education-31-6Beyond AccessCC BY-SA 2.0source
Gallery photo sports-43-1StewartCC BY 2.0source
Gallery photo sports-43-2SebleoufCC BY-SA 4.0source
Gallery photo sports-43-3OctagonCC BY 3.0source
Gallery photo sports-43-4The African Union Mission in SomaliaCC0 1.0source
Gallery photo sports-43-5Frankie FouganthinCC BY-SA 4.0source
Gallery photo sports-43-6https://www.flickr.com/photos/normalityrelief/CC BY-SA 2.0source
Gallery photo lifestyle-34-1Shihabur RahmanCC BY-SA 4.0source
Gallery photo lifestyle-34-2Fabian LambeckCC BY-SA 4.0source
Gallery photo lifestyle-34-3joiseyshowaaCC BY-SA 2.0source
Gallery photo lifestyle-34-4Hiroki OgawaCC BY 3.0source
Gallery photo lifestyle-34-5Soumyajit NandyCC BY-SA 4.0source
Gallery photo lifestyle-34-6Soumyajit NandyCC BY-SA 4.0source
Gallery photo environment-37-1Shagorkhan2CC BY-SA 4.0source
Gallery photo environment-37-2KroisenbrunnerCC BY-SA 4.0source
Gallery photo environment-37-3Shafiul Islam ShaikotCC BY 4.0source
Gallery photo environment-37-4Adam Jones from Kelowna, BC, CanadaCC BY-SA 2.0source
Gallery photo environment-37-5KroisenbrunnerCC BY-SA 4.0source
Gallery photo environment-37-6KritzolinaCC BY-SA 4.0source
Podcast photo 1rawpixel (CC0)CC0 1.0source
Podcast photo 2rawpixel (CC0)CC0 1.0source
Podcast photo 3rawpixel (CC0)CC0 1.0source
Podcast photo 4rawpixel (CC0)CC0 1.0source
Podcast photo 5Ryan McGuireCC0 1.0source
Podcast photo 6rawpixel (CC0)CC0 1.0source
Epaper photo 1rawpixel (CC0)CC0 1.0source
Epaper photo 2Douglas W. JonesCC0 1.0source
Epaper photo 3Markus SpiskeCC0 1.0source
Video clip: Traffic situation in Mango Avenue, Cebu CityLady01vCC BY-SA 4.0source
Video clip: Aythorpe Roding CC v Terling CC - batsman hits cover drivesAcabashiCC BY-SA 4.0source
Video clip: WSIL Newsroom on Election NightWSIL NewsCC BY 3.0source
Video clip: Rainfall in East Legon 03Ibnali1CC BY-SA 4.0source
Podcast audio: Ambient VoyagerZeropageCC BY 3.0source
Podcast audio: Keep CalmJahzzarCC BY-SA 3.0source
Podcast audio: Corvois calmTorley on PianoCC BY-SA 3.0source