- Replace IRedMailClient REST API class with direct MySQL/PDO connection to vmail database
- Move iRedMail config from global config table to per-company integrations (like Zammad)
- Add iRedMail integration card to API settings with DB host/name/user/password/port fields
- Add iRedMail checkbox to integrations section in company settings
- Change Hallinta tab visibility: show for admins (not just superadmins) when module enabled
- API endpoints now use requireCompany() + requireSuperAdmin() and get config from integrations
- Password hashing uses SSHA512 (iRedMail default)
- Mask db_password in API responses (like token masking for Zammad)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add 'hallinta' to ALL_MODULES so it appears in company settings
- Change fallback from ALL_MODULES to DEFAULT_MODULES (new modules not enabled by default)
- Hallinta tab requires both module enabled + superadmin role
- Add per-company configurable postal codes for "todennäköinen" availability
- Saatavuus API returns true/todennäköinen/false based on customer data + postal codes
- Show "Todennäköinen" badge in availability queries list
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Timer is now a simple checkbox + days field. When enabled, rule actions
are scheduled for X days after ticket creation at 10:00 AM instead of
applying immediately. Removed no_activity condition in favor of simple
time-based scheduling stored on the ticket (delay_rule_id + delay_execute_at).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds optional delay_days + delay_condition (no_activity) to ticket rules.
When a ticket has no activity for X days, timed rules automatically apply
actions (e.g., escalate priority to urgent). Checked on each ticket list load.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sendTelegramAlert and telegram_test used non-existent dbGetCompany().
Replaced with dbLoadCompanies() + loop to find company name.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Both bot_token and chat_id are now per-company. If a company
doesn't set its own bot_token, falls back to the global one.
Removed parenthetical hints from labels, all admins can now
configure their own Telegram bot.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each company can now have its own Telegram channel/group for alerts.
- Bot token: global (superadmin only, shared across companies)
- Chat ID: per-company (stored in integrations table config)
- sendTelegramAlert reads chat_id from company integration
- Test message shows company name
- Non-superadmin users can't see/edit bot token
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mobile improvements:
- Fix overflow issues with global max-width/overflow-x hidden
- Stack ticket detail selects on mobile instead of one long row
- Compact header: hide less important buttons, scrollable right side
- Stat cards in 2-column grid on mobile
- Force flex-wrap on inline-styled flex containers
- Hide subtitle and user-info on small phones
- Ticket selects full-width on small phones
IP allow list:
- Support # comments after IP addresses (e.g. "192.168.1.1 # Office VPN")
- Updated placeholder with comment examples
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical fixes:
- company_logo_upload: validate user has access to target company
- All delete functions (db.php): accept optional company_id parameter
for defense-in-depth filtering (customers, devices, ipam, guides,
leads, tickets, archives, mailboxes, rules, templates, todos)
- All API delete calls now pass company_id to db layer
- ticket_bulk_delete: per-ticket company_id filtering
- todo_comment/time/subtask operations: verify todo belongs to company
- dbGetMailbox: optional company_id scoping, used in smtp_test
- requireCompanyOrParam: no longer mutates session permanently
- Fix _dbFetch typo in zammad_attachment (was runtime error)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- When changing ticket status in intra, now syncs to Zammad too
(close in intra → close in Zammad, etc.)
- Prevent Zammad sync from reopening locally closed tickets
unless Zammad has genuinely new activity (updated_at changed)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Superadmin sees users grouped by company with header rows
- Admins can now set user or admin role when creating/editing users
- Admin role change restricted to own company only
- Prevents admin from modifying superadmin roles
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New provision_ssl API endpoint runs certbot for new domains
- SSL button appears next to domain textarea for superadmin
- Shell script on server handles Apache config + Let's Encrypt
- DNS check skips domains without resolution to avoid certbot errors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Incremental sync now excludes closed/merged/removed tickets from query
- Full sync still fetches everything
- Reopened tickets (closed→new/open in Zammad) are handled correctly:
existing ticket is updated instead of creating duplicate
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- zammad_reply endpoint now sets intra status uusi→kasittelyssa
- Also updates Zammad ticket state to open
- Fixed stateMap: Zammad open now maps to kasittelyssa instead of avoin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Lisää BCC-kenttä vastauslomakkeeseen (HTML, JS, API)
- To/CC/BCC tallentuvat tiketille pysyvästi (seuraava vastaus muistaa muutokset)
- Lisää to_email ja bcc sarakkeet tickets-tauluun
- BCC-tuki SMTP-lähetykseen (RCPT TO ilman headeria)
- Korjaa allekirjoitukset: buildSignaturesWithDefaults() generoi nyt oletukset
myös Zammad-sähköposteille (support@web1.fi ym.), ei pelkille mailboxeille
- Allekirjoituksiin lisätty puhelinnumero
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
white-space:pre-wrap ei toimi kaikissa sähköpostiohjelmissa/Zammadissa.
Vaihdettu käyttämään nl2br() rivinvaihdoille ja peräkkäisille
välilyönneille, jotka toimivat universaalisti HTML-sähköposteissa.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Reverse DNS -haku tallentaa hostnamen IP:n rinnalle (paljastaa
operaattorin ja alueen, esim. dsl-hel-123.elisa.fi)
- Duplikaattikyselyn (sama osoite+postinumero+kaupunki) ei tallenneta
uudelleen samalle yritykselle
- IP/hostname -sarake lisätty taulukkoon
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Endpoint näytti vain aktiivisen yrityksen kyselyt, mutta kyselyt
tallennetaan API-avaimen yrityksen alle. Nyt näytetään kaikkien
käyttäjän yritysten kyselyt + yrityssarake taulukkoon.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Jokainen nettisivujen kautta tehty saatavuustarkistus tallennetaan
tietokantaan (osoite, postinumero, kaupunki, tulos, IP, referer).
Kyselyt näkyvät Asiakkaat > Saatavuuskyselyt -välilehdellä
sivutettuna taulukkona.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sekä Zammad- että SMTP-vastaukset sisältävät nyt koko viestiketjun
vastauksessa. Zammad-vastauksissa HTML-blockquoteilla, SMTP:ssä
plain-text > -quoting-muodossa. Vain uusi viesti tallennetaan
tietokantaan, ketju näkyy vain lähetettävässä sähköpostissa.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
JS ei lähettänyt käyttäjän muokkaamaa To/CC-kenttää Zammad-vastauksen
mukana — backend käytti aina alkuperäistä lähettäjää tietokannasta.
Nyt käyttäjän syöttämä To/CC välitetään API:lle ja Zammadille.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lisätty subtype: 'reply' Zammad-artikkelin luontiin — ilman tätä
Zammad luo artikkelin mutta ei lähetä sähköpostia vastaanottajalle.
Myös plain-text muunnetaan HTML:ksi ja subject saa Re: -etuliitteen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Puhelinnumero-kenttä yrityksen asetuksissa tallennetaan tietokantaan
ja näkyy automaattisesti kaikissa oletusallekirjoituksissa viimeisenä
rivinä (sekä SMTP- että Zammad-postilaatikoille).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Asetukset-sivulla näytetään nyt myös Zammad-sähköpostiosoitteet
(esim. support@web1.fi) allekirjoitusten alla. Allekirjoitus liitetään
automaattisesti Zammad-vastauksiin. Tallennetaan avaimella
"zammad:email@osoite.fi". Uusi ticket_zammad_emails API-endpoint
hakee uniikit vastaanotto-osoitteet tietokannasta.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Zammad-tiketeille näytetään vastaanotto-osoite (esim. support@web1.fi)
lähettäjäkentässä eikä SMTP-postilaatikkolistaa — vastaus menee
Zammad API:n kautta.
2. Ensimmäisen artikkelin to-osoite tallennetaan zammad_to_email kenttään
on-demand artikkelien haussa.
3. Korjattu _dbFetchRow → _dbFetchOne zammad_reply endpointissa.
4. sanitizeHtml() renderöi viestien HTML turvallisesti.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sync hakee nyt vain tikettien metatiedot (status, aihe, ryhmä) —
ei enää satoja getArticles-kutsuja jokaiselle tiketille. Artikkelit
haetaan on-demand ticket_detail-endpointissa kun käyttäjä avaa
tiketin. Nopeuttaa synkkausta dramaattisesti.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-refresh hakee nyt vain viimeisen synkkauksen jälkeen muuttuneet
tiketit (updated_at + 5min marginaali). Artikkelit haetaan vain
uusille tai muuttuneille tiketeille. "Hae postit" -nappi tekee
edelleen full syncin (full=true). Nopeuttaa autopäivitystä merkittävästi.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Hae postit -nappi ajaa nyt myös zammad_sync automaattisesti
sähköpostien haun jälkeen (ohitetaan hiljaa jos Zammad ei käytössä).
2. Kun suljettuun/ratkaistuun tikettiin tulee uusi viesti,
tiketti avautuu uudelleen "uusi"-tilaan (aiemmin "käsittelyssä").
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Zammad-ryhmät eivät näkyneet postilaatikoiden näkyvyydessä koska ne
haettiin tickets-globaalista joka on tyhjä ennen tiketti-tabin avaamista.
Lisätty ticket_zammad_groups API-endpoint joka hakee uniikit ryhmät
suoraan tietokannasta.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Paginointi: 100 tikettiä/sivu, navigointipalkki sivujen välillä
- Filtterit resetoivat sivunumeron 1:ksi
- Select All valitsee vain nykyisen sivun tiketit
- Zammad-synkronointi tallentaa source=zammad ja zammad_group
- Postilaatikoiden näkyvyysasetuksissa Zammad-ryhmät (Zammad)-merkinnällä
- Zammad-ryhmien piilotus filtteröi tiketit samalla tavalla kuin postilaatikot
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Luotu Noxus SVG-logo (violetti heksagoni + N)
- Demo-yritys nimetty uudelleen Noxukseksi violetilla värillä
- API-tabi piilotettuna ellei yrityksellä ole integraatioita päällä
(superadmin näkee aina)
- check_auth palauttaa has_integrations-lipun
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Kun Zammad-URL syötettiin ilman protokollaa (esim. desk.web1.fi),
cURL ei osannut muodostaa oikeaa osoitetta ja synkronointi epäonnistui.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Superadmin-käyttäjällä oli user_companies-taulussa vain yksi yritys,
joten hän ei voinut vaihtaa tai muokata muita yrityksiä (esim. intra.web1.fi).
Nyt superadmin saa kaikki yritykset sessioon loginissa ja check_auth:ssa,
ja requireCompany() + company_switch ohittavat company-tarkistuksen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Kaikki integraatio/zammad-endpointit kutsuivat requireCompany() ilman
return-arvon talteenottoa, jolloin $companyId oli null ja aiheutti
Fatal error. Korjattu: integrations, integration_save, integration_test,
zammad_groups, zammad_sync, zammad_reply.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Yritykset-tab: integraatio-checkboxit tallentavat vain enabled-tilan
- API-tab: Zammad/Saatavuus/Telegram kortit näkyvät kun integraatio päällä
- Zammad-asetukset (URL, token, ryhmät, synkronointi) kokonaan API-tabissa
- integration_save: tyhjä config ei ylikirjoita olemassaolevia asetuksia
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Integraatiot erillinen table-card yrityksen asetuksissa (vain superadmin)
- Zammad-konfiguraatio (URL, token, ryhmät, synkronointi) siirretty API-tabiin
- Saatavuus-API, Telegram ja Zammad kortit näkyvät API-tabissa kun integraatio on enabloitu
- Korjattu integration_save ja integration_test: puuttuva $input json_decode
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Moduulit, integraatiot ja IP-rajoitukset piilotetaan yritysadminilta (vain superadmin näkee)
- Saatavuus-API ja Telegram checkboxit tallentavat tilan heti muutoksessa
- session_regenerate_id(false) estää race conditionin kirjautumisen jälkeen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Login: jos kaikki käyttäjän yritykset IP-estetty → estä
kirjautuminen kokonaan. Valitsee automaattisesti ei-estetyn
yrityksen aktiiviseksi.
- check_auth: jos aktiivinen yritys IP-estetty → vaihda
sallittuun. Jos kaikki estetty → kirjaa ulos.
- company_update: vain superadmin saa muuttaa allowed_ips-kenttää.
Estää adminia poistamasta IP-rajoitusta itseltään.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Admin poistaa käyttäjän vain nykyisestä yrityksestä (user_companies).
Käyttäjä poistetaan kokonaan vasta kun ei kuulu enää yhteenkään
yritykseen. Superadmin poistaa edelleen kokonaan.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sääntöjen kenttänimet: DB käyttää type_set/status_set mutta
API lähetti set_type/set_status → nyt dbSaveTicketRule hyväksyy
molemmat ja matching lukee oikeat DB-kenttänimet
- Migraatio: täytä fetched_message_ids olemassaolevien tikettien
message_id:illä niin poistetut viestit eivät tule takaisin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Korjaa implode()-bugi: $email['to'] on string, ei array
- Lisää fetched_message_ids-taulu joka estää poistettujen
tikettien uudelleenluonnin seuraavassa haussa
- Viestit haetaan postilaatikosta vain kertaalleen
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Vastauspohjat, Säännöt ja Asetukset siirretty omiksi alinaveikseen
tikettilistan overlay-napeista. Säännöt-välilehdelle lisätty
tikettityyppien hallinta (lisää/poista). Tyypit tallennetaan
tietokantaan yrityskohtaisesti ja populoidaan dynaamisesti
kaikkiin dropdown-valikoihin.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>