Repository navigation
PNLCS 1.5.0
The stable release, offered to every installation. It contains everything in
1.5.0-beta.1 and 1.5.0-beta.2 (below), and the changes listed here, which
were made after 1.5.0-beta.2.
Added
- Charge in the invoice's billing currency for Stripe, Mollie and PayPal,
off by default (each gateway's settings under Setup β Payment
Gateways). A customer billed in their own currency (Setup β
Currencies) is charged the amount the invoice shows, at the invoice's own
rate, instead of the shop currency converted by their bank. Thank you,
@ertugrulkeyvan. - Free subdomains for hosting orders (Setup β General β Domains): a
hosting order can take a name under one of the shop's own domains, such as
yourname.example-sites.com, instead of a domain of its own. Reserved
names (www,mail,ns1,autodiscover,adminand more, or your own
list) and names another service already has are refused. The parent domains
need a wildcard DNS record; see Selling domains.
Thank you, @ertugrulkeyvan. - The shop's social media profiles (Setup β General β Search engines
and sharing): X, Facebook, Instagram, LinkedIn, YouTube, TikTok, Threads
and Medium. The default home page footer links to them and the home page
names them to search engines; nothing is added until a profile is set.
Thank you, @ertugrulkeyvan. - aaPanel server module for Sub aaPanel accounts: create, suspend,
unsuspend, terminate, password and package changes, disk and bandwidth
usage, and Login to panel. It needs aaPanel Pro with Sub aaPanel
installed; see aaPanel hosting accounts. The
connection test, the package list and an unlicensed panel's refusal were
checked on aaPanel 8.0.6 with Sub aaPanel 3.0.4; account changes have not
yet been run against a licensed panel. Thank you,
@Alex9001. - Panelica mail certificate help: when the certificate for a customer's
mail host could not be issued, their email page lists the DNS records it
needs and offers to try again. Thank you,
@ertugrulkeyvan. - Spanish has every text of the language files, addressing the reader as
"usted". Thank you, @wmedia. - German client area and email texts, and all 32 email templates, from
Dejan Wolf (WHOST, Austria), in the formal "Sie" the German translation
uses.
Changed
- Email templates in a new language start from the templates shipped for
it (German and Polish) instead of copies of the English ones. - Panelica mail host: mail apps are told
mail.<customer's domain>, the
name the panel's certificate is for, or the host name the panel gives the
certificate; before, they were told a name built from the server's
hostname. The first mailbox of a domain asks the panel for that
certificate when it has none. Thank you,
@ertugrulkeyvan. - Panelica "Login to panel" opens the panel by the server's host name
when the panel runs behind NAT, instead of an internal address the browser
cannot reach. Thank you, @ertugrulkeyvan. - The language in the address: links in the core views and the flavor
and aurora themes carry the page's language, so no redirect is needed; the
language switcher adds?langonly on the way back to the default
language; a file at the root (/sw.js,/manifest.json, a verification
file) keeps one address in every language. Thank you,
@ertugrulkeyvan. - Admin screens: about 100 texts on 37 admin screens that showed English
in every language are translated. Thank you,
@ertugrulkeyvan. - Updates: when an update stops because the container runs an older
panelica/pnlcs-runtimeimage, the page andpnlcs:updatealso say how
Panelica replaces it (Docker Manager β Update to the new tag).
Fixed
- iyzico: an invoice billed in lira is charged the lira amount the
invoice shows, not today's conversion of it. Thank you,
@ertugrulkeyvan. - Hosting tabs: when the hosting control panel does not answer, the
customer gets a message instead of a "Server Error" page; after a form the
panel may already have carried out, they are told to check before trying
again. Thank you, @ertugrulkeyvan. - Products: a product or product group named like an existing one gets
an address of its own (vps-s-2) and can be ordered; before, its order
link opened the other product or gave a 404, and a second group with the
same name could not be saved. Thank you,
@ertugrulkeyvan. - Products: saving a product that has no module no longer gives it
Panelica's default limits, so the price list no longer shows resources
nobody chose. Thank you, @ertugrulkeyvan. - Automation status: the page lists the scheduled tasks (it showed none),
and on the Docker image it says the scheduler already runs inside the
container instead of asking for a host cron line. The cron note says
"every minute", which is what the line does. - Updates: a check or update request that the scheduler picked up but
that never finished no longer leaves the page waiting for ever; it says the
request was lost, also on a page that was kept open. Thank you,
@ertugrulkeyvan. - Search engines and sharing: a page that writes its own sharing tags,
such as a blog post, no longer gets a second set from the layout. Thank
you, @ertugrulkeyvan.
Security
- PayPal: a capture is credited only in the currency the shop charges in,
or the invoice's billing currency when that option is on. Before, a capture
of 100 in a currency worth far less than the shop's (100 HUF against a
100.00 USD invoice) could mark the invoice paid. Thank you,
@ertugrulkeyvan. - Two-factor setup draws its QR code on the server. The setup address,
which carries the shared secret, is no longer sent to an outside QR
service. Thank you, @ertugrulkeyvan.
Database changes
2026_10_10_000001_ship_translated_email_templates: a German or Polish
email template that you have not made your own and that is still exactly
the English text gets the shipped translation, its subject and its message
each on their own. A field you wrote yourself stays.2026_10_12_000001_give_products_with_a_shared_slug_their_own: a product
whose slug is empty or already used by an older product gets one of its own
(vps-s-2,vps-s-3), so its order address changes; the oldest keeps its
slug, and every other product is untouched.
For theme and module authors
social_profiles()returns the profiles that are set (network => address);
the new partialpartials/social-linksdraws them as icon links.- A page that writes its own sharing tags sets
@section('seo_own', '1');
the layout then prints neitherpartials/seo-metanor
partials/locale-alternatesfor it. Themes
lists the sections the layout reads. - Write links as
url('/path'), not as plain paths, so they keep the page's
language when the language is in the address. - The two-factor setup views receive
$qrImage, an SVG data URI;$qrUrl
is still passed, so an overriding view keeps working until it switches. - New server module
aapanel(modules/Servers/AaPanel).
Views a theme may override that changed
resources/views/admin/affiliates/show.blade.php,
admin/auth/enable-two-factor.blade.php,admin/calendar/index.blade.php,
admin/clients/edit.blade.php,admin/clients/show.blade.php,
admin/config/addon-output.blade.php,admin/config/automation.blade.php,
admin/config/billable-items.blade.php,admin/config/bundles.blade.php,
admin/config/custom-fields.blade.php,
admin/config/domain-pricing.blade.php,admin/config/gateways.blade.php,
admin/config/notifications.blade.php,admin/config/promotions.blade.php,
admin/config/servers.blade.php,admin/config/ticket-escalation.blade.php,
admin/config/ticket-spam.blade.php,admin/config/updates.blade.php,
admin/domains/index.blade.php,admin/domains/show.blade.php,
admin/invoices/create.blade.php,admin/invoices/show.blade.php,
admin/layouts/app.blade.php,admin/logs/module.blade.php,
admin/orders/show.blade.php,admin/products/create.blade.php,
admin/products/create-group.blade.php,admin/products/edit.blade.php,
admin/projects/create.blade.php,admin/projects/edit.blade.php,
admin/projects/index.blade.php,admin/projects/show.blade.php,
admin/quotes/create.blade.php,admin/quotes/edit.blade.php,
admin/quotes/show.blade.php,admin/reports/show.blade.php,
admin/settings/appearance.blade.php,admin/settings/general.blade.php,
admin/ssl/show.blade.php,admin/whois.blade.php.resources/views/client/auth/enable-two-factor.blade.php,
client/cart/configure.blade.php,client/layouts/app.blade.php,
client/services/hosting/email.blade.php,
client/services/show.blade.php,client/tickets/create.blade.php.resources/views/errors/403.blade.php,errors/404.blade.php,
errors/500.blade.php,partials/locale-alternates.blade.php,
partials/seo-meta.blade.php,sections/domain-search.blade.php,
sections/footer.blade.php,sections/hosting-plans.blade.php,
sections/navigation.blade.php,sections/styles.blade.php,
sections/topbar.blade.php,sections/vps-plans.blade.php.themes/aurora/views/sections/hero.blade.php,
themes/aurora/views/sections/navigation.blade.php,
themes/flavor/views/client/layouts/app.blade.php,
themes/flavor/views/sections/footer.blade.php,
themes/flavor/views/sections/navigation.blade.php.
Also in 1.5.0: from 1.5.0-beta.2
Added
- Product mapping in the WHMCS importer. The services mapper maps each
WHMCS product to a PNLCS product; an unmapped one is still matched by name.
Two services on the same domain (hosting and e-mail, a VPS and its
maintenance) are imported as two services, and each client gets a note
listing their WHMCS services, so the result can be checked against the
source. A re-import updates the services 1.4.0 imported instead of copying
them. Thank you, @hedon77. - The language in the address, off by default (Setup β Languages β
Settings). The default language keeps today's addresses; every other
active language is served under its code (/en/client/store), so search
engines can index every language and a shared link opens in the language it
was copied in. Social sign-in and the e-mail confirmation link keep their
own addresses. Thank you,
@ertugrulkeyvan. - Search engines and link previews (Setup β General β Search engines
and sharing): a description on every client-facing page, Open Graph and X
card tags so a shared link shows a title, text and image, and a sitemap at
/sitemap.xmlthat updates itself./robots.txtcan be edited there once
the shippedpublic/robots.txtfile is removed; while the file is there,
the web server answers with it as before. Thank you,
@ertugrulkeyvan. - Tracking codes and a cookie consent bar (Setup β General β Tracking
and cookie consent): a Google Tag Manager container with Google Consent
Mode v2, so ads and analytics wait for the visitor's consent, a consent bar
where "necessary cookies only" is as easy as "accept", and code for the
head and the end of every page. Nothing is added to a page until something
is set. Thank you, @ertugrulkeyvan. - A number format per currency (Setup β Currencies):
1,234.56,
1.234,56,1 234,56or1234.56. Every currency keeps1,234.56until
you choose otherwise; the cart's running totals follow the format too.
Thank you, @ertugrulkeyvan. - A promotion code in a link: any page opened with
?promo=CODE(or
?promocode=CODE, as WHMCS links write it) remembers the code, and the cart
applies it once it holds a product the code is for. Every promotion rule
still applies, and a code the customer typed is never replaced. Thank you,
@ertugrulkeyvan.
Changed
- Translations. The progress under Setup β Languages counts a text as
translated only when it is no longer the English text, and Translate
missing now also translates texts still in English (names that stay the
same, such as Stripe, are not sent again). About 460 texts of the client
hosting pages, the e-mail setup guide, the SSL page and the home page's apps
section were missing in German, Polish and Chinese; they are translated,
with a few German machine-translation mistakes corrected. English, Turkish,
German, Polish and Chinese are complete; a text a community language does
not have yet shows in English. - Domain availability is checked through the registrar assigned to each
domain extension, with the default registrar's bulk check kept for the
rest. Thank you, @terbora-core. - The client page shows the client's information and billing identity in
one panel. Thank you, @hedon77.
Fixed
- Invoices: the "All" tab and the "All invoices" menu entry list paid and
unpaid invoices; the list still opens on Unpaid. Thank you,
@ertugrulkeyvan. - Stripe: the pay button names the currency ("Pay β¬12.00 by card"); the
amount sent to Stripe is unchanged. Thank you,
@ertugrulkeyvan. - Updates: clicking Update now right after a check finished could be
refused with "the updater is busy" - the page said "ready" while the
updater was still cleaning up. The page now says so only once it is done.
For developers
- Every language file is checked before it ships: it loads, keeps every
placeholder the English text declares, and carries exactly the English
markup. The community languages are reported, not enforced, so adding an
English text no longer requires writing it in 25 languages.
Translations explains what is checked,
texts that stay the same as English and how a language becomes complete;
translators are credited in the README.
Database changes
whmcs_import_profiles.product_mapping(JSON, nullable) and
services.whmcs_product_name(string, nullable): new columns.2026_10_09_000001_drop_translation_rows_now_shipped_in_files: the texts
above used to be delivered as rows ofdynamic_translations; they now ship
in the language files, and a row that says exactly what the file says is
removed (it would hide later corrections to the file). A text you changed
yourself is different from the file and stays. What the pages show does
not change.
For theme and module authors
- New hook point
SitemapUrls: return addresses (strings, or
['loc' => ..., 'lastmod' => ...]) to add your public pages to the sitemap. - Client layouts should print
ClientAreaHeadOutputand
ClientAreaFooterOutput(the tracking codes and the consent bar use them);
the flavor theme now does. - New view partials:
partials/seo-meta,partials/locale-alternates,
partials/consent-bar, and thesitemapview.
Views a theme may override that changed
resources/views/admin/clients/show.blade.php,
admin/config/currencies.blade.php,admin/config/languages/index.blade.php,
admin/invoices/index.blade.php,admin/layouts/app.blade.php,
admin/settings/general.blade.php,admin/whmcs-import/mapper.blade.php.resources/views/client/announcements/show.blade.php,
client/auth/forgot-password.blade.php,client/auth/invite.blade.php,
client/auth/login.blade.php,client/auth/register.blade.php,
client/auth/reset-password.blade.php,client/auth/two-factor.blade.php,
client/cart/configure.blade.php,client/kb/show.blade.php,
client/layouts/app.blade.php,
client/layouts/partials/language-selector.blade.php,
client/unsubscribe.blade.php.resources/views/legal/layout.blade.php,sections/topbar.blade.php,
welcome.blade.php;themes/flavor/views/client/layouts/app.blade.php,
themes/flavor/views/welcome.blade.php.
Also in 1.5.0: from 1.5.0-beta.1
Changed
- The WHMCS import screens are redesigned. A header that says what the
importer promises (read only, preview before importing, every record
logged) and the three steps; the connection form in groups and saved
connections as cards; the import type as tabs; a count of the fields mapped;
the preview, run and save buttons in a bar that stays in reach; the preview
side by side; the log as coloured totals. Right to left and on phones, in
every language.
For developers
- The beta channel is covered end to end: tests for which channel offers
which release, and an update lab scenario in which the stable channel does
not see a beta, the beta channel updates to it, and then to the stable
release made from the same commit.
Database changes
- None.
For theme and module authors
- Nothing changed for modules, themes or hooks.
Views a theme may override that changed
resources/views/admin/whmcs-import/index.blade.php,
mapper.blade.phpandlog.blade.php; new:_styles.blade.phpand
_steps.blade.phpin the same folder.
The full history is in CHANGELOG.md. The package below is signed; the updater checks the signature before it uses it.