[{"data":1,"prerenderedAt":2109},["ShallowReactive",2],{"navigation_docs_en":3,"blog-post-\u002Fblog\u002Fkleeja-4-release":58,"blog-post-\u002Fblog\u002Fkleeja-4-release-surround":2102},[4,25,44],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":6},"Getting Started",false,"\u002Fgetting-started","en",[10,15,20],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F2.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Update & Upgrade","\u002Fgetting-started\u002Fupdate-upgrade","en\u002F1.getting-started\u002F3.update-upgrade","i-lucide-arrow-up-circle",{"title":26,"icon":6,"path":27,"stem":8,"children":28,"page":6},"Guides","\u002Fguides",[29,34,39],{"title":30,"path":31,"stem":32,"icon":33},"Install Styles","\u002Fguides\u002Finstall-styles","en\u002F2.guides\u002F1.install-styles","i-lucide-palette",{"title":35,"path":36,"stem":37,"icon":38},"Install Plugins","\u002Fguides\u002Finstall-plugins","en\u002F2.guides\u002F2.install-plugins","i-lucide-plug",{"title":40,"path":41,"stem":42,"icon":43},"config.php File","\u002Fguides\u002Fconfig-file","en\u002F2.guides\u002F3.config-file","i-lucide-file-cog",{"title":45,"icon":6,"path":46,"stem":8,"children":47,"page":6},"Development","\u002Fdevelopment",[48,53],{"title":49,"path":50,"stem":51,"icon":52},"Plugin Development","\u002Fdevelopment\u002Fplugin-development","en\u002F3.development\u002F1.plugin-development","i-lucide-blocks",{"title":54,"path":55,"stem":56,"icon":57},"Styles Development","\u002Fdevelopment\u002Fstyles-development","en\u002F3.development\u002F2.styles-development","i-lucide-paintbrush",{"id":59,"title":60,"author":61,"body":64,"date":2093,"description":2094,"extension":2095,"image":2096,"meta":2097,"navigation":1323,"path":2098,"seo":2099,"stem":2100,"__hash__":2101},"blog_en\u002Fblog\u002Fkleeja-4-release.md","Kleeja 4: What's New and What Changes When You Upgrade",{"name":62,"github":63},"Mitan Omar","MitanOmar",{"type":65,"value":66,"toc":2057},"minimark",[67,71,75,78,86,91,96,99,106,109,128,134,138,141,149,155,158,162,165,168,179,185,192,199,203,206,233,237,252,258,262,265,271,275,278,284,288,295,342,346,395,399,402,416,420,431,435,444,458,462,490,494,562,566,569,728,732,736,741,744,747,751,754,778,782,792,798,802,809,813,826,840,844,854,890,894,898,908,1103,1117,1138,1142,1214,1218,1231,1257,1261,1268,1431,1442,1446,1452,1476,1479,1611,1615,1647,1672,1676,1724,1840,1844,1850,1929,1954,1958,1962,2011,2015,2024,2028,2050,2053],[68,69,70],"warning",{},"This post covers an upcoming release. Kleeja 4 will be released on 30 October 2026.",[72,73,74],"p",{},"Kleeja 4 is the biggest release we have shipped in years. We rebuilt the control panel from scratch, redesigned the Bootstrap style to make it the default, rewrote the template engine and the updater, and cleaned the database of columns Kleeja no longer reads.",[72,76,77],{},"Some of these changes break compatibility with plugins and styles written for Kleeja 3. This post has four parts: what's new in Kleeja 4, the plugins available for it, what may break after you upgrade, and how to upgrade safely. If your site runs Kleeja 3, read the last two parts before you start.",[79,80,81,85],"note",{},[82,83,84],"strong",{},"Kleeja 4 requirements:"," PHP 8.2 or newer, and the PDO extension with the MySQL\u002FMariaDB driver, or with SQLite 3.35 or newer. The updater and the plugin and style store also need the ZipArchive extension.",[87,88,90],"h2",{"id":89},"whats-new-in-kleeja-4","What's new in Kleeja 4",[92,93,95],"h3",{"id":94},"a-new-control-panel-called-damask","A new control panel called Damask",[72,97,98],{},"Damask replaces Masmak, the control panel theme that has served Kleeja for many years. It is built on Bootstrap 5.3 and Font Awesome 7 without jQuery, works in both Arabic and English in either direction, and adapts to phone screens.",[72,100,101],{},[102,103],"img",{"alt":104,"src":105},"The Damask control panel dashboard","\u002Fimages\u002Fdocs\u002Fen\u002Fdashboard_1.png",[72,107,108],{},"Here is what you will find in it:",[110,111,112,116,119,122,125],"ul",{},[113,114,115],"li",{},"A light mode, a dark mode and an automatic mode that follows your device setting.",[113,117,118],{},"A dashboard where you choose which boxes appear, with a chart of uploads.",[113,120,121],{},"Bulk selection in tables, and copy buttons for values like the cron link.",[113,123,124],{},"A \"Kleeja blog\" tab on the dashboard that shows the latest posts from this blog in your panel's language.",[113,126,127],{},"A redesigned \"Kleeja development team\" page that shows the contributors and the project's activity on GitHub in charts.",[72,129,130],{},[102,131],{"alt":132,"src":133},"The Damask dashboard in dark mode","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fdashboard-dark.png",[92,135,137],{"id":136},"a-help-page-inside-the-control-panel","A Help page inside the control panel",[72,139,140],{},"A new \"Help\" page sits at the end of the side menu. It has a guide for every control panel page that explains what the page does and how to use it, with tips, warnings and frequently asked questions. It also has general guides, including \"First steps after installing\", \"A routine for running your service\", \"Keep your service safe\" and \"Troubleshooting\".",[72,142,143,144,148],{},"The help button in the top bar opens the guide of the page you are on. The search on the Help page ignores Arabic diacritics and the different forms of a letter, so \"اضافه\" finds \"إضافة\". Press ",[145,146,147],"code",{},"\u002F"," to jump to the search box.",[72,150,151],{},[102,152],{"alt":153,"src":154},"The Help page, with the guides filtered by a search for \"upload\"","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fhelp.png",[72,156,157],{},"Installed plugins appear on this page too, with their description and a link to their settings. Plugin developers can write a full guide for their plugin, and the developers section below shows how.",[92,159,161],{"id":160},"a-new-bootstrap-style-now-the-default","A new Bootstrap style, now the default",[72,163,164],{},"We redesigned the Bootstrap style from the ground up on Bootstrap 5.3 with the Kleeja brand colors. It is now the default style for new sites, and the fallback style too: when the active style is missing a template, Kleeja takes it from Bootstrap.",[72,166,167],{},"What your visitors notice:",[110,169,170,173,176],{},[113,171,172],{},"Light, dark and automatic modes, which visitors pick from the top bar.",[113,174,175],{},"The upload form checks the file type and size before it sends the file, so visitors don't wait for an upload the server will reject.",[113,177,178],{},"Copy buttons next to file links after an upload.",[72,180,181],{},[102,182],{"alt":183,"src":184},"Upload results in the dark mode of the Bootstrap style, with a copy button next to every link","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fupload-result.png",[72,186,187,188,191],{},"The style no longer carries its own copies of Bootstrap and jQuery. It loads Bootstrap and Font Awesome from ",[145,189,190],{},"includes\u002Fstatic_shared_files",", a folder it shares with the control panel.",[72,193,194,195,198],{},"The old Default style has left the Kleeja core, and its new version lives in the store as ",[145,196,197],{},"og_default",". We come back to it in the breaking changes section.",[92,200,202],{"id":201},"a-safer-updater","A safer updater",[72,204,205],{},"An upgrade that fails halfway is worse than no upgrade, so we rewrote the updater. The new updater:",[110,207,208,211,218,221,224,227,230],{},[113,209,210],{},"Checks that every file it will replace is writable before it touches any file, and lists the files whose permissions you need to fix.",[113,212,213,214,217],{},"Saves the files it replaces or deletes in ",[145,215,216],{},"cache\u002Fbackup.zip",", and restores them if copying any file fails.",[113,219,220],{},"Deletes files of the old version that are no longer part of Kleeja, such as the folder of a replaced control panel theme.",[113,222,223],{},"Closes the site for maintenance during the upgrade, then puts it back the way it was, so it never opens a site you had closed yourself.",[113,225,226],{},"Stops at the first failed database update, shows you the error, and continues from there on the next try.",[113,228,229],{},"If an update stops after it copies the files but before it finishes the database, founders see a notice on the dashboard with an \"Update the database\" button that finishes the job.",[113,231,232],{},"Reminds you to update your plugins and styles after the upgrade, with two buttons that take you to their pages.",[92,234,236],{"id":235},"a-store-for-kleeja-4","A store for Kleeja 4",[72,238,239,240,243,244,251],{},"Kleeja 4 has its own list in the plugin and style store: the ",[145,241,242],{},"kleeja-4-catalog.json"," file in the ",[245,246,250],"a",{"href":247,"rel":248},"https:\u002F\u002Fgithub.com\u002Fkleeja\u002Fstore-catalog",[249],"nofollow","kleeja\u002Fstore-catalog"," repository. The store now shows only the plugins and styles that are compatible with your Kleeja version, so you won't install a plugin only to find out it doesn't work.",[72,253,254],{},[102,255],{"alt":256,"src":257},"The Kleeja Store tab on the Plugins page","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fstore.png",[92,259,261],{"id":260},"redesigned-emails","Redesigned emails",[72,263,264],{},"Kleeja now sends its emails as HTML, with a plain text copy for mail apps that don't show HTML. We rewrote the password reset emails, the notifications for new reports and contact messages, and the admin replies to them. They are clearer now, and their buttons take the recipient to the right page.",[72,266,267],{},[102,268],{"alt":269,"src":270},"The new password reset email","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Femail.png",[92,272,274],{"id":273},"a-captcha-with-the-kleeja-brand","A captcha with the Kleeja brand",[72,276,277],{},"We redrew the captcha image with the Kleeja colors and logo. It is now as tall as a form field, and each character is drawn at its own angle.",[72,279,280],{},[102,281],{"alt":282,"src":283},"The new captcha on the contact form","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fcaptcha.png",[92,285,287],{"id":286},"a-new-template-engine","A new template engine",[72,289,290,291,294],{},"The template engine is the part that turns the ",[145,292,293],{},".html"," files of a style into PHP code. We rewrote it to be safer and clearer about errors:",[110,296,297,300,311,314,323],{},[113,298,299],{},"It runs no PHP code that it didn't write itself. A condition that contains PHP code counts as false, and a warning says so.",[113,301,302,303,306,307,310],{},"If you forget to close an ",[145,304,305],{},"\u003CIF>"," or a ",[145,308,309],{},"\u003CLOOP>",", it tells you the template name and the line number instead of showing a cryptic PHP error.",[113,312,313],{},"It saves compiled templates in a way that never lets another request read a half-written file.",[113,315,316,317,319,320,322],{},"Nested loops (a ",[145,318,309],{}," inside a ",[145,321,309],{},") work without the inner loop overwriting the values of the outer one.",[113,324,325,326,329,330,333,334,337,338,341],{},"It adds output filters: ",[145,327,328],{},"|e"," to escape HTML, ",[145,331,332],{},"|url"," for links, ",[145,335,336],{},"|js"," for JavaScript values and ",[145,339,340],{},"|int"," for numbers.",[92,343,345],{"id":344},"fixes-your-visitors-notice","Fixes your visitors notice",[110,347,348,351,354,365,376,379,389,392],{},[113,349,350],{},"Resuming downloads works properly now, so a download manager can finish a file whose download was interrupted.",[113,352,353],{},"Arabic file names stay intact when a file is downloaded, in every browser.",[113,355,356,357,360,361,364],{},"Double-encoded text is gone, like ",[145,358,359],{},"&amp;amp;"," or ",[145,362,363],{},"&quot;"," showing up in file, user and group names and in messages. We fix it when the text is displayed, without changing your data.",[113,366,367,368,371,372,375],{},"If you installed Kleeja on ",[145,369,370],{},"http:\u002F\u002F"," and enabled HTTPS later, Kleeja switches your site link in the settings to ",[145,373,374],{},"https:\u002F\u002F"," on its own.",[113,377,378],{},"Members stay logged in after they change their name, email or password from their profile.",[113,380,381,382,360,385,388],{},"The report form accepts only links that start with ",[145,383,384],{},"http",[145,386,387],{},"https",".",[113,390,391],{},"Banning members by name works with names that contain special characters.",[113,393,394],{},"Protected action links in the control panel, like delete links, stay valid for an extra hour, so Kleeja doesn't reject one just because you opened the page a few minutes before the hour changed.",[92,396,398],{"id":397},"what-came-after-kleeja-327","What came after Kleeja 3.2.7",[72,400,401],{},"Kleeja 4 is the first release after 3.2.7, so it also includes the work we did since then that hasn't been released yet:",[110,403,404,407,410,413],{},[113,405,406],{},"The database layer moved to PDO, and queries now pass values as bound parameters instead of putting them in the query text.",[113,408,409],{},"A new error page with the Kleeja brand.",[113,411,412],{},"A redesigned installer with a dark mode, which loads its files from your server instead of a CDN.",[113,414,415],{},"Uploading a plugin from a zip file as GitHub generates it for repository releases.",[92,417,419],{"id":418},"cleanup","Cleanup",[72,421,422,423,426,427,430],{},"We dropped database columns that Kleeja no longer reads: the old visit counters in the ",[145,424,425],{},"stats"," table, the ",[145,428,429],{},"session_id"," column in the users table, and five columns in the plugins table. We also deleted old files that nothing uses anymore, and dropped Internet Explorer support in the control panel.",[87,432,434],{"id":433},"plugins-for-kleeja-4","Plugins for Kleeja 4",[72,436,437,438,443],{},"Every official plugin has its own repository ",[245,439,442],{"href":440,"rel":441},"https:\u002F\u002Fgithub.com\u002Fkleeja",[249],"on GitHub"," with its own releases, and the store installs the release that the Kleeja 4 catalog lists for it. We released new versions of the official plugins for Kleeja 4, and every one of them:",[110,445,446,449,452,455],{},[113,447,448],{},"Works with Kleeja 4, PHP 8.2 and the new database layer.",[113,450,451],{},"Has settings pages redesigned for Damask, split into tabs.",[113,453,454],{},"Has a full guide on the Help page, in Arabic and English.",[113,456,457],{},"Has an icon that shows in the store and on the Help page.",[92,459,461],{"id":460},"two-new-plugins","Two new plugins",[110,463,464,481],{},[113,465,466,469,470,473,474,477],{},[82,467,468],{},"Kleeja SMTP Mailer"," (",[145,471,472],{},"kj_smtp_mailer",") 1.4 sends all of Kleeja's mail through your own SMTP server instead of the PHP mail function, so password recovery messages and your replies stay out of spam folders. It fills in the settings of Hostinger or Gmail in one click, and its test tab checks your settings, sends a test message and shows the reason when the server refuses it. It sends the messages in Kleeja's new email design, and sets the visitor's address as the reply address of message and report alerts.",[475,476],"br",{},[102,478],{"alt":479,"src":480},"The test tab of Kleeja SMTP Mailer after a successful test","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fsmtp-mailer-test.png",[113,482,483,469,486,489],{},[82,484,485],{},"Kleeja Advanced Stats",[145,487,488],{},"kleeja_advanced_stats",") 1.3 reports what happened on your service between two dates: uploads, storage, downloads, messages, reports and new users, compared with the period before, with charts. It shows the most downloaded, largest and most reported files, when people upload by hour and by day, and saves the report as a PDF in your browser. Other plugins can add their own stats in a tab of their own, as Kleeja Payment does.",[92,491,493],{"id":492},"plugins-with-new-features","Plugins with new features",[110,495,496,511,526,541,553],{},[113,497,498,469,501,504,505,507],{},[82,499,500],{},"PDF Viewer",[145,502,503],{},"pdf_viewer",") 2.0 has a new viewer built on Mozilla's PDF.js, replacing the old one. It has a toolbar to move through pages and zoom, full screen and a presentation mode, and visitors can select and copy the text. It opens large files fast because it reads the pages it shows first, and it follows the page language and dark mode.",[475,506],{},[102,508],{"alt":509,"src":510},"The new PDF viewer on a file's download page","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fpdf-viewer.png",[113,512,513,469,516,519,520,522],{},[82,514,515],{},"Video & Audio Player",[145,517,518],{},"video_player",") 2.1 moves to the latest Video.js. It has playback speed, picture-in-picture and keyboard shortcuts, double tap on touch screens to skip back or ahead, and a compact bar for audio files. The video box takes the shape of the video, so a portrait phone video isn't drawn small.",[475,521],{},[102,523],{"alt":524,"src":525},"The video player on a file's download page","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fvideo-player.png",[113,527,528,469,531,534,535,537],{},[82,529,530],{},"Kleeja Multi-FTP Uploading",[145,532,533],{},"kj_ftp",") 2.0 adds a \"Connection test\" tab that checks an FTP account from end to end: the host, the port, FTPS, the sign-in, the root folder, uploading and deleting a file, and the public link. It names the setting that is wrong. Each account supports FTPS and passive mode, and the video player and PDF viewer play files stored on FTP servers.",[475,536],{},[102,538],{"alt":539,"src":540},"The Connection test tab after all checks passed","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fftp-connection-test.png",[113,542,543,469,546,549,550,552],{},[82,544,545],{},"Kleeja Payment",[145,547,548],{},"kleeja_payment",") 2.0.4 shows the payment options as cards, sends clearer emails to buyers, adds its payment stats to Kleeja Advanced Stats, and supports the ",[145,551,197],{}," style. The PayPal and Stripe libraries come inside the release package that the store installs.",[113,554,555,469,558,561],{},[82,556,557],{},"KJ Comment",[145,559,560],{},"kj_comment",") 1.1.4 deletes the comments of a file along with the file, whether you delete it from the control panel, its uploader deletes it, or it is removed by its deletion link or by the cleanup of old files.",[92,563,565],{"id":564},"the-other-plugins","The other plugins",[72,567,568],{},"We ported these plugins to Kleeja 4 with the shared improvements above:",[570,571,572,588],"table",{},[573,574,575],"thead",{},[576,577,578,582,585],"tr",{},[579,580,581],"th",{},"Plugin",[579,583,584],{},"Version",[579,586,587],{},"What it does",[589,590,591,607,621,635,648,661,675,688,702,715],"tbody",{},[576,592,593,601,604],{},[594,595,596,597,600],"td",{},"KJ reCaptcha (",[145,598,599],{},"kj_recaptcha",")",[594,602,603],{},"2.0",[594,605,606],{},"Replaces the security code with Google reCAPTCHA (v2 checkbox, invisible badge or v3) and protects the control panel login",[576,608,609,615,618],{},[594,610,611,612,600],{},"Kleeja X SendFile (",[145,613,614],{},"kj_x_sendfile",[594,616,617],{},"1.1",[594,619,620],{},"Hands file transfers to Apache or Nginx, so a long download doesn't keep a PHP process busy",[576,622,623,629,632],{},[594,624,625,626,600],{},"Advanced Watermark (",[145,627,628],{},"advanced_watermark",[594,630,631],{},"1.2",[594,633,634],{},"Stamps images with your logo or a line of text, with a preview before your visitors upload anything",[576,636,637,643,645],{},[594,638,639,640,600],{},"Kleeja Admin Firewall (",[145,641,642],{},"kj_ban_admin",[594,644,617],{},[594,646,647],{},"Bans an IP address after 10 wrong passwords at the control panel sign-in",[576,649,650,656,658],{},[594,651,652,653,600],{},"KJ Download Speed Limit (",[145,654,655],{},"kj_download_speed_limit",[594,657,631],{},[594,659,660],{},"Sets the download speed of each group",[576,662,663,669,672],{},[594,664,665,666,600],{},"Meta SEO (",[145,667,668],{},"kj_meta_seo",[594,670,671],{},"1.2.0",[594,673,674],{},"Adds a description, keywords, Open Graph and X Card tags to every page",[576,676,677,683,685],{},[594,678,679,680,600],{},"KJ AMP SEO (",[145,681,682],{},"kj_amp_seo",[594,684,671],{},[594,686,687],{},"Gives every download page a fast AMP version, with share buttons and AdSense ads",[576,689,690,696,699],{},[594,691,692,693,600],{},"KJ SiteMap SEO (",[145,694,695],{},"kj_sitemap_seo",[594,697,698],{},"2.0.0",[594,700,701],{},"Publishes an XML sitemap of the 100 latest files and images",[576,703,704,710,712],{},[594,705,706,707,600],{},"Language Switch (",[145,708,709],{},"language_switch",[594,711,603],{},[594,713,714],{},"Adds a language menu to the top of every page, next to the color mode menu in the Bootstrap style",[576,716,717,723,725],{},[594,718,719,720,600],{},"Advanced Extras (",[145,721,722],{},"advanced_extras",[594,724,617],{},[594,726,727],{},"Gives each page its own extra header and footer",[87,729,731],{"id":730},"breaking-changes","Breaking changes",[92,733,735],{"id":734},"for-site-owners","For site owners",[737,738,740],"h4",{"id":739},"php-82-is-now-the-minimum","PHP 8.2 is now the minimum",[72,742,743],{},"Kleeja 4 doesn't run on PHP older than 8.2, while Kleeja 3.2.7 ran on PHP 8.0. If you upload Kleeja 4 to a server that runs PHP 8.0 or 8.1, your site shows an error page instead of its pages.",[68,745,746],{},"The updater in Kleeja 3 only checks for PHP 8, so it can offer you the upgrade to Kleeja 4 while your site runs on PHP 8.0. Check your PHP version yourself before you upgrade.",[737,748,750],{"id":749},"all-plugins-are-disabled-after-the-upgrade","All plugins are disabled after the upgrade",[72,752,753],{},"The Kleeja 4 database update disables every installed plugin. Kleeja 3 plugins weren't written for this release, and running them without an update can bring down the whole site. Their data and settings stay in the database, and you enable each plugin again after you update it.",[72,755,756,757,760,761,760,764,760,767,760,770,773,774,777],{},"Most store plugins have a Kleeja 4 version, listed in the \"Plugins for Kleeja 4\" section above. At the time of writing, these plugins don't have one yet: ",[145,758,759],{},"menu_toggle",", ",[145,762,763],{},"rebrandly",[145,765,766],{},"vbulletin_integration",[145,768,769],{},"phpbb_integration",[145,771,772],{},"thumbs_generator"," and ",[145,775,776],{},"kjp_account_charger",". If your site depends on one of them, wait before you upgrade.",[737,779,781],{"id":780},"the-default-style-has-left-the-core","The Default style has left the core",[72,783,784,785,788,789,791],{},"The database update deletes the ",[145,786,787],{},"styles\u002Fdefault"," folder. If your site uses Default, or a style built on it, the update downloads ",[145,790,197],{}," from GitHub and switches your site to it. If the download fails, it switches your site to Bootstrap.",[72,793,794,795,797],{},"As a result, a custom style built on Default doesn't stay active after the upgrade, and any change you made inside the ",[145,796,787],{}," folder itself is deleted.",[737,799,801],{"id":800},"masmak-is-gone","Masmak is gone",[72,803,804,805,808],{},"We removed the old control panel theme, Masmak. Plugin pages in the control panel were written for it with Bootstrap 4 and jQuery, and Damask doesn't load jQuery. Damask converts common Bootstrap 4 attributes (such as ",[145,806,807],{},"data-toggle",") to their Bootstrap 5 equivalents, but a plugin page that depends on jQuery won't work properly until you update the plugin.",[737,810,812],{"id":811},"kleeja-3-styles-may-need-an-update","Kleeja 3 styles may need an update",[72,814,815,816,760,819,360,822,825],{},"The Bootstrap style no longer ships ",[145,817,818],{},"css\u002Fbootstrap.min.css",[145,820,821],{},"js\u002Fjquery.min.js",[145,823,824],{},"js\u002Fbootstrap.min.js",". Styles built on it that load those files from it show up unstyled, or lose parts of their JavaScript. The new template engine is also stricter, so a template with an unclosed tag shows an error page.",[72,827,828,829,760,831,760,834,773,837,388],{},"The store has Kleeja 4 versions of ",[145,830,197],{},[145,832,833],{},"bootstrap_black",[145,835,836],{},"dragdrop",[145,838,839],{},"kleeja_return",[737,841,843],{"id":842},"the-updater-deletes-what-isnt-kleeja","The updater deletes what isn't Kleeja",[72,845,846,847,773,850,853],{},"From Kleeja 4 on, the updater deletes files in Kleeja's own folders (like ",[145,848,849],{},"admin",[145,851,852],{},"includes",") that aren't part of the new release. It also replaces any core file you edited yourself.",[72,855,856,857,760,860,760,863,760,866,760,869,773,872,875,876,879,880,773,883,886,887,889],{},"The updater doesn't touch the ",[145,858,859],{},"cache",[145,861,862],{},"plugins",[145,864,865],{},"uploads",[145,867,868],{},"styles",[145,870,871],{},"images",[145,873,874],{},"install"," folders, the languages you added to ",[145,877,878],{},"lang",", or the files in the root folder like ",[145,881,882],{},"config.php",[145,884,885],{},".htaccess",". The files it replaces or deletes stay in ",[145,888,216],{}," until the next upgrade.",[92,891,893],{"id":892},"for-plugin-and-style-developers","For plugin and style developers",[737,895,897],{"id":896},"declare-that-your-plugin-supports-kleeja-4","Declare that your plugin supports Kleeja 4",[72,899,900,901,903,904,907],{},"Kleeja 4 reads its store from ",[145,902,242],{},", and shows only the items whose compatibility range (",[145,905,906],{},"kleeja_version",") includes the installed Kleeja version. Add your plugin or style to that file like this:",[909,910,915],"pre",{"className":911,"code":912,"filename":242,"language":913,"meta":914,"style":914},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"type\": \"plugin\",\n  \"name\": \"my_plugin\",\n  \"kleeja_version\": {\n    \"min\": \"4.0.0\",\n    \"max\": \"4.9.9\"\n  },\n  \"file\": {\n    \"version\": \"2.0\",\n    \"url\": \"https:\u002F\u002Fgithub.com\u002Fme\u002Fmy_plugin\u002Farchive\u002F2.0.zip\"\n  }\n}\n","json","",[145,916,917,926,954,975,989,1012,1032,1038,1052,1072,1091,1097],{"__ignoreMap":914},[918,919,922],"span",{"class":920,"line":921},"line",1,[918,923,925],{"class":924},"sMK4o","{\n",[918,927,929,932,936,939,942,945,949,951],{"class":920,"line":928},2,[918,930,931],{"class":924},"  \"",[918,933,935],{"class":934},"spNyl","type",[918,937,938],{"class":924},"\"",[918,940,941],{"class":924},":",[918,943,944],{"class":924}," \"",[918,946,948],{"class":947},"sfazB","plugin",[918,950,938],{"class":924},[918,952,953],{"class":924},",\n",[918,955,957,959,962,964,966,968,971,973],{"class":920,"line":956},3,[918,958,931],{"class":924},[918,960,961],{"class":934},"name",[918,963,938],{"class":924},[918,965,941],{"class":924},[918,967,944],{"class":924},[918,969,970],{"class":947},"my_plugin",[918,972,938],{"class":924},[918,974,953],{"class":924},[918,976,978,980,982,984,986],{"class":920,"line":977},4,[918,979,931],{"class":924},[918,981,906],{"class":934},[918,983,938],{"class":924},[918,985,941],{"class":924},[918,987,988],{"class":924}," {\n",[918,990,992,995,999,1001,1003,1005,1008,1010],{"class":920,"line":991},5,[918,993,994],{"class":924},"    \"",[918,996,998],{"class":997},"sBMFI","min",[918,1000,938],{"class":924},[918,1002,941],{"class":924},[918,1004,944],{"class":924},[918,1006,1007],{"class":947},"4.0.0",[918,1009,938],{"class":924},[918,1011,953],{"class":924},[918,1013,1015,1017,1020,1022,1024,1026,1029],{"class":920,"line":1014},6,[918,1016,994],{"class":924},[918,1018,1019],{"class":997},"max",[918,1021,938],{"class":924},[918,1023,941],{"class":924},[918,1025,944],{"class":924},[918,1027,1028],{"class":947},"4.9.9",[918,1030,1031],{"class":924},"\"\n",[918,1033,1035],{"class":920,"line":1034},7,[918,1036,1037],{"class":924},"  },\n",[918,1039,1041,1043,1046,1048,1050],{"class":920,"line":1040},8,[918,1042,931],{"class":924},[918,1044,1045],{"class":934},"file",[918,1047,938],{"class":924},[918,1049,941],{"class":924},[918,1051,988],{"class":924},[918,1053,1055,1057,1060,1062,1064,1066,1068,1070],{"class":920,"line":1054},9,[918,1056,994],{"class":924},[918,1058,1059],{"class":997},"version",[918,1061,938],{"class":924},[918,1063,941],{"class":924},[918,1065,944],{"class":924},[918,1067,603],{"class":947},[918,1069,938],{"class":924},[918,1071,953],{"class":924},[918,1073,1075,1077,1080,1082,1084,1086,1089],{"class":920,"line":1074},10,[918,1076,994],{"class":924},[918,1078,1079],{"class":997},"url",[918,1081,938],{"class":924},[918,1083,941],{"class":924},[918,1085,944],{"class":924},[918,1087,1088],{"class":947},"https:\u002F\u002Fgithub.com\u002Fme\u002Fmy_plugin\u002Farchive\u002F2.0.zip",[918,1090,1031],{"class":924},[918,1092,1094],{"class":920,"line":1093},11,[918,1095,1096],{"class":924},"  }\n",[918,1098,1100],{"class":920,"line":1099},12,[918,1101,1102],{"class":924},"}\n",[72,1104,1105,1106,773,1109,1112,1113,1116],{},"Update ",[145,1107,1108],{},"plugin_kleeja_version_min",[145,1110,1111],{},"plugin_kleeja_version_max"," in ",[145,1114,1115],{},"init.php"," too, because Kleeja refuses to install a plugin outside that range.",[1118,1119,1120,1121,1112,1124,1126,1127,1130,1131,1133,1134,1137],"tip",{},"While you develop, define ",[145,1122,1123],{},"IGNORE_STORE_COMPATIBILITY",[145,1125,882],{}," to make the store show every item whatever its compatibility. And if you add a ",[145,1128,1129],{},"preview"," key to your plugin's entry, in the same shape as ",[145,1132,1045],{},", a site that defines ",[145,1135,1136],{},"DEV_STAGE"," downloads it instead of the published version, so you can test your new version before you publish it.",[737,1139,1141],{"id":1140},"database","Database",[110,1143,1144,1155,1169],{},[113,1145,1146,1147,1150,1151,1154],{},"The database layer moved to PDO through the ",[145,1148,1149],{},"KleejaDatabase"," class. Pass values in ",[145,1152,1153],{},"BIND"," instead of putting them in the query text.",[113,1156,1157,1158,1161,1162,773,1165,1168],{},"The second argument of ",[145,1159,1160],{},"$SQL->query()"," is now the array of bound values, where it used to be a boolean for transactions. The ",[145,1163,1164],{},"set_names()",[145,1166,1167],{},"client_encoding()"," methods are gone.",[113,1170,1171,1172,760,1175,760,1178,760,1181,773,1184,1187,1188,1190,1191,760,1194,760,1197,760,1200,773,1203,1206,1207,1209,1210,1213],{},"We dropped the ",[145,1173,1174],{},"plg_icon",[145,1176,1177],{},"plg_uninstall",[145,1179,1180],{},"plg_instructions",[145,1182,1183],{},"plg_store",[145,1185,1186],{},"plg_files"," columns from the plugins table, the ",[145,1189,429],{}," column from the users table, and the ",[145,1192,1193],{},"last_file",[145,1195,1196],{},"today",[145,1198,1199],{},"counter_today",[145,1201,1202],{},"counter_yesterday",[145,1204,1205],{},"counter_all"," columns from the ",[145,1208,425],{}," table. Any query that names one of them fails. The ",[145,1211,1212],{},"$stat_last_file"," variable is gone along with them.",[737,1215,1217],{"id":1216},"control-panel-pages","Control panel pages",[72,1219,1220,1221,760,1224,773,1227,1230],{},"Damask builds its pages with Bootstrap 5.3 and Font Awesome 7, and doesn't load jQuery. Move your plugin's pages to Bootstrap 5 classes and to JavaScript without jQuery. The old global functions like ",[145,1222,1223],{},"checkAll()",[145,1225,1226],{},"confirm_form()",[145,1228,1229],{},"get_kleeja_link()"," are still there.",[72,1232,1233,1234,1237,1238,1241,1242,1245,1246,1249,1250,1253,1254,388],{},"The control panel page icons in ",[145,1235,1236],{},"$ext_icons"," are now Font Awesome 7 names without the ",[145,1239,1240],{},"fa-"," prefix, like ",[145,1243,1244],{},"folder-open"," instead of ",[145,1247,1248],{},"folder-open-o",". The site's side menu items have a new ",[145,1251,1252],{},"icon"," key that takes an icon name the same way, and an item that a plugin adds without an icon gets ",[145,1255,1256],{},"puzzle-piece",[737,1258,1260],{"id":1259},"template-engine","Template engine",[72,1262,1263,1264,1267],{},"A condition that contains PHP code, like a name that starts with ",[145,1265,1266],{},"$"," or a function call, is now always false. Use variable names and comparisons only:",[909,1269,1274],{"className":1270,"code":1271,"filename":1272,"language":1273,"meta":914,"style":914},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003C!-- No longer works: PHP code inside the condition -->\n\u003CIF NAME=\"$config['mod_writer']\">...\u003C\u002FIF>\n\n\u003C!-- Write it like this -->\n\u003CIF NAME=\"config.mod_writer == 1\">...\u003C\u002FIF>\n\n\u003C!-- The new filters -->\n\u003Ca href=\"{config.siteurl}?q={query|url}\">{title|e}\u003C\u002Fa>\n\u003Cscript>\n  var fileName = {file_name|js};\n\u003C\u002Fscript>\n","styles\u002Fmy_style\u002Findex_body.html","html",[145,1275,1276,1282,1319,1325,1330,1357,1361,1366,1395,1404,1423],{"__ignoreMap":914},[918,1277,1278],{"class":920,"line":921},[918,1279,1281],{"class":1280},"sHwdD","\u003C!-- No longer works: PHP code inside the condition -->\n",[918,1283,1284,1287,1291,1294,1297,1299,1302,1304,1307,1311,1314,1316],{"class":920,"line":928},[918,1285,1286],{"class":924},"\u003C",[918,1288,1290],{"class":1289},"swJcz","IF",[918,1292,1293],{"class":934}," NAME",[918,1295,1296],{"class":924},"=",[918,1298,938],{"class":924},[918,1300,1301],{"class":947},"$config['mod_writer']",[918,1303,938],{"class":924},[918,1305,1306],{"class":924},">",[918,1308,1310],{"class":1309},"sTEyZ","...",[918,1312,1313],{"class":924},"\u003C\u002F",[918,1315,1290],{"class":1289},[918,1317,1318],{"class":924},">\n",[918,1320,1321],{"class":920,"line":956},[918,1322,1324],{"emptyLinePlaceholder":1323},true,"\n",[918,1326,1327],{"class":920,"line":977},[918,1328,1329],{"class":1280},"\u003C!-- Write it like this -->\n",[918,1331,1332,1334,1336,1338,1340,1342,1345,1347,1349,1351,1353,1355],{"class":920,"line":991},[918,1333,1286],{"class":924},[918,1335,1290],{"class":1289},[918,1337,1293],{"class":934},[918,1339,1296],{"class":924},[918,1341,938],{"class":924},[918,1343,1344],{"class":947},"config.mod_writer == 1",[918,1346,938],{"class":924},[918,1348,1306],{"class":924},[918,1350,1310],{"class":1309},[918,1352,1313],{"class":924},[918,1354,1290],{"class":1289},[918,1356,1318],{"class":924},[918,1358,1359],{"class":920,"line":1014},[918,1360,1324],{"emptyLinePlaceholder":1323},[918,1362,1363],{"class":920,"line":1034},[918,1364,1365],{"class":1280},"\u003C!-- The new filters -->\n",[918,1367,1368,1370,1372,1375,1377,1379,1382,1384,1386,1389,1391,1393],{"class":920,"line":1040},[918,1369,1286],{"class":924},[918,1371,245],{"class":1289},[918,1373,1374],{"class":934}," href",[918,1376,1296],{"class":924},[918,1378,938],{"class":924},[918,1380,1381],{"class":947},"{config.siteurl}?q={query|url}",[918,1383,938],{"class":924},[918,1385,1306],{"class":924},[918,1387,1388],{"class":1309},"{title|e}",[918,1390,1313],{"class":924},[918,1392,245],{"class":1289},[918,1394,1318],{"class":924},[918,1396,1397,1399,1402],{"class":920,"line":1054},[918,1398,1286],{"class":924},[918,1400,1401],{"class":1289},"script",[918,1403,1318],{"class":924},[918,1405,1406,1409,1412,1414,1417,1420],{"class":920,"line":1074},[918,1407,1408],{"class":934},"  var",[918,1410,1411],{"class":1309}," fileName ",[918,1413,1296],{"class":924},[918,1415,1416],{"class":924}," {",[918,1418,1419],{"class":1309},"file_name|js",[918,1421,1422],{"class":924},"};\n",[918,1424,1425,1427,1429],{"class":920,"line":1093},[918,1426,1313],{"class":924},[918,1428,1401],{"class":1289},[918,1430,1318],{"class":924},[72,1432,1433,1434,1437,1438,1441],{},"If your plugin adds its own tags to templates, use the new ",[145,1435,1436],{},"style_parse_func_step_2"," hook, and add \"regex → replacement\" pairs to the ",[145,1439,1440],{},"$rep"," array. They run on the compiled code.",[737,1443,1445],{"id":1444},"styles-built-on-default-or-bootstrap","Styles built on Default or Bootstrap",[72,1447,1448,1449,1451],{},"Make a style built on Default depend on Bootstrap or on ",[145,1450,197],{}," instead:",[909,1453,1458],{"className":1454,"code":1455,"filename":1456,"language":1457,"meta":914,"style":914},"language-ini shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# was: depend_on = default\ndepend_on = bootstrap\n","styles\u002Fmy_style\u002Finfo.txt","ini",[145,1459,1460,1465],{"__ignoreMap":914},[918,1461,1462],{"class":920,"line":921},[918,1463,1464],{"class":1280},"# was: depend_on = default\n",[918,1466,1467,1470,1473],{"class":920,"line":928},[918,1468,1469],{"class":1289},"depend_on",[918,1471,1472],{"class":924}," =",[918,1474,1475],{"class":1309}," bootstrap\n",[72,1477,1478],{},"If your style loads Bootstrap or jQuery from the Bootstrap style, load Bootstrap 5.3 and Font Awesome from the shared folder, the way the Bootstrap style itself does:",[909,1480,1483],{"className":1270,"code":1481,"filename":1482,"language":1273,"meta":914,"style":914},"\u003CIF NAME=\"lang.DIR==rtl\">\n\u003Clink rel=\"stylesheet\" href=\"{config.siteurl}includes\u002Fstatic_shared_files\u002Fbootstrap\u002Fcss\u002Fbootstrap.rtl.min.css\">\n\u003CELSE>\n\u003Clink rel=\"stylesheet\" href=\"{config.siteurl}includes\u002Fstatic_shared_files\u002Fbootstrap\u002Fcss\u002Fbootstrap.min.css\">\n\u003C\u002FIF>\n\u003Clink rel=\"stylesheet\" href=\"{config.siteurl}includes\u002Fstatic_shared_files\u002Ffontawesome\u002Fcss\u002Fall.min.css\">\n","styles\u002Fmy_style\u002Fheader.html",[145,1484,1485,1504,1536,1545,1574,1582],{"__ignoreMap":914},[918,1486,1487,1489,1491,1493,1495,1497,1500,1502],{"class":920,"line":921},[918,1488,1286],{"class":924},[918,1490,1290],{"class":1289},[918,1492,1293],{"class":934},[918,1494,1296],{"class":924},[918,1496,938],{"class":924},[918,1498,1499],{"class":947},"lang.DIR==rtl",[918,1501,938],{"class":924},[918,1503,1318],{"class":924},[918,1505,1506,1508,1511,1514,1516,1518,1521,1523,1525,1527,1529,1532,1534],{"class":920,"line":928},[918,1507,1286],{"class":924},[918,1509,1510],{"class":1289},"link",[918,1512,1513],{"class":934}," rel",[918,1515,1296],{"class":924},[918,1517,938],{"class":924},[918,1519,1520],{"class":947},"stylesheet",[918,1522,938],{"class":924},[918,1524,1374],{"class":934},[918,1526,1296],{"class":924},[918,1528,938],{"class":924},[918,1530,1531],{"class":947},"{config.siteurl}includes\u002Fstatic_shared_files\u002Fbootstrap\u002Fcss\u002Fbootstrap.rtl.min.css",[918,1533,938],{"class":924},[918,1535,1318],{"class":924},[918,1537,1538,1540,1543],{"class":920,"line":956},[918,1539,1286],{"class":924},[918,1541,1542],{"class":1289},"ELSE",[918,1544,1318],{"class":924},[918,1546,1547,1549,1551,1553,1555,1557,1559,1561,1563,1565,1567,1570,1572],{"class":920,"line":977},[918,1548,1286],{"class":924},[918,1550,1510],{"class":1289},[918,1552,1513],{"class":934},[918,1554,1296],{"class":924},[918,1556,938],{"class":924},[918,1558,1520],{"class":947},[918,1560,938],{"class":924},[918,1562,1374],{"class":934},[918,1564,1296],{"class":924},[918,1566,938],{"class":924},[918,1568,1569],{"class":947},"{config.siteurl}includes\u002Fstatic_shared_files\u002Fbootstrap\u002Fcss\u002Fbootstrap.min.css",[918,1571,938],{"class":924},[918,1573,1318],{"class":924},[918,1575,1576,1578,1580],{"class":920,"line":991},[918,1577,1313],{"class":924},[918,1579,1290],{"class":1289},[918,1581,1318],{"class":924},[918,1583,1584,1586,1588,1590,1592,1594,1596,1598,1600,1602,1604,1607,1609],{"class":920,"line":1014},[918,1585,1286],{"class":924},[918,1587,1510],{"class":1289},[918,1589,1513],{"class":934},[918,1591,1296],{"class":924},[918,1593,938],{"class":924},[918,1595,1520],{"class":947},[918,1597,938],{"class":924},[918,1599,1374],{"class":934},[918,1601,1296],{"class":924},[918,1603,938],{"class":924},[918,1605,1606],{"class":947},"{config.siteurl}includes\u002Fstatic_shared_files\u002Ffontawesome\u002Fcss\u002Fall.min.css",[918,1608,938],{"class":924},[918,1610,1318],{"class":924},[737,1612,1614],{"id":1613},"email","Email",[72,1616,1617,1620,1621,760,1624,773,1627,1630,1631,1634,1635,1638,1639,1642,1643,1646],{},[145,1618,1619],{},"send_mail()"," still accepts a plain text, and now also accepts an array of ",[145,1622,1623],{},"text",[145,1625,1626],{},"button",[145,1628,1629],{},"alert"," blocks that it renders with the ",[145,1632,1633],{},"includes\u002Fmail_template.html"," template. The ",[145,1636,1637],{},"send_mail"," hooks still get ",[145,1640,1641],{},"$body"," as plain text, and now also get ",[145,1644,1645],{},"$html_body",". If your plugin sends the email itself, over SMTP for example, send both versions.",[72,1648,1649,1650,760,1653,760,1656,760,1659,773,1662,1665,1666,773,1669,388],{},"We removed the language keys that built the old emails: ",[145,1651,1652],{},"GET_LOSTPASS_MSG",[145,1654,1655],{},"U_REPORT_ON",[145,1657,1658],{},"BY_EMAIL",[145,1660,1661],{},"ADMIN_REPLIED",[145,1663,1664],{},"REPLIED_ON_CAL",", along with ",[145,1667,1668],{},"PHP_8_REQUIRED",[145,1670,1671],{},"PDO_EXT_REQUIRED",[737,1673,1675],{"id":1674},"new-tools-for-developers","New tools for developers",[110,1677,1678,1701,1708,1714],{},[113,1679,1680,1683,1684,760,1687,760,1690,773,1693,1696,1697,1700],{},[145,1681,1682],{},"Plugins::installFromStore('name')"," downloads a plugin from the store, installs it and enables it in one step, so your plugin can install another plugin it depends on. It comes with ",[145,1685,1686],{},"Plugins::download()",[145,1688,1689],{},"install()",[145,1691,1692],{},"enable()",[145,1694,1695],{},"disable()",", and ",[145,1698,1699],{},"Plugins::lastError()"," tells you why one of them failed.",[113,1702,1703,1704,1707],{},"The ",[145,1705,1706],{},"admin_help_guides"," hook adds your plugin's guide to the Help page. The last guide on the Help page itself has a complete example.",[113,1709,1703,1710,1713],{},[145,1711,1712],{},"kleeja_mail_template_func"," hook changes the email template.",[113,1715,1716,1719,1720,1723],{},[145,1717,1718],{},"kleeja_html_decode()"," returns a text as its author typed it, however many times it was encoded, and ",[145,1721,1722],{},"kleeja_html_display()"," encodes it once for display.",[909,1725,1730],{"className":1726,"code":1727,"filename":1728,"language":1729,"meta":914,"style":914},"language-php shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","$kleeja_plugin['my_plugin']['install'] = function ($plg_id) {\n    \u002F\u002F install and enable the plugin that your plugin depends on\n    if (!Plugins::installFromStore('kj_smtp_mailer')) {\n        kleeja_log(Plugins::lastError());\n    }\n};\n","plugins\u002Fmy_plugin\u002Finit.php","php",[145,1731,1732,1776,1781,1814,1831,1836],{"__ignoreMap":914},[918,1733,1734,1736,1739,1742,1745,1747,1749,1752,1754,1756,1758,1761,1763,1766,1769,1772,1774],{"class":920,"line":921},[918,1735,1266],{"class":924},[918,1737,1738],{"class":1309},"kleeja_plugin",[918,1740,1741],{"class":924},"[",[918,1743,1744],{"class":924},"'",[918,1746,970],{"class":947},[918,1748,1744],{"class":924},[918,1750,1751],{"class":924},"][",[918,1753,1744],{"class":924},[918,1755,874],{"class":947},[918,1757,1744],{"class":924},[918,1759,1760],{"class":924},"]",[918,1762,1472],{"class":924},[918,1764,1765],{"class":934}," function",[918,1767,1768],{"class":924}," ($",[918,1770,1771],{"class":1309},"plg_id",[918,1773,600],{"class":924},[918,1775,988],{"class":924},[918,1777,1778],{"class":920,"line":928},[918,1779,1780],{"class":1280},"    \u002F\u002F install and enable the plugin that your plugin depends on\n",[918,1782,1783,1787,1790,1793,1796,1800,1803,1805,1807,1809,1812],{"class":920,"line":956},[918,1784,1786],{"class":1785},"s7zQu","    if",[918,1788,1789],{"class":924}," (!",[918,1791,1792],{"class":997},"Plugins",[918,1794,1795],{"class":924},"::",[918,1797,1799],{"class":1798},"s2Zo4","installFromStore",[918,1801,1802],{"class":924},"(",[918,1804,1744],{"class":924},[918,1806,472],{"class":947},[918,1808,1744],{"class":924},[918,1810,1811],{"class":924},"))",[918,1813,988],{"class":924},[918,1815,1816,1819,1821,1823,1825,1828],{"class":920,"line":977},[918,1817,1818],{"class":1798},"        kleeja_log",[918,1820,1802],{"class":924},[918,1822,1792],{"class":997},[918,1824,1795],{"class":924},[918,1826,1827],{"class":1798},"lastError",[918,1829,1830],{"class":924},"());\n",[918,1832,1833],{"class":920,"line":991},[918,1834,1835],{"class":924},"    }\n",[918,1837,1838],{"class":920,"line":1014},[918,1839,1422],{"class":924},[87,1841,1843],{"id":1842},"how-to-upgrade-to-kleeja-4-safely","How to upgrade to Kleeja 4 safely",[72,1845,1846,1847,1849],{},"You can upgrade from Kleeja 3 with the update button in the control panel. The updater copies the Kleeja 4 files, then Kleeja 4 updates the database, refreshes the Bootstrap style from the release package, switches your site from Default to ",[145,1848,197],{}," if it uses Default, and reopens the site. The one requirement the Kleeja 3 updater doesn't check is the PHP version, so check it yourself before you start.",[1851,1852,1853,1857,1860,1864,1875,1879,1882,1886,1889,1893,1896,1905,1909,1916,1922,1926],"steps",{},[92,1854,1856],{"id":1855},"take-a-full-backup","Take a full backup",[72,1858,1859],{},"Copy all of your site's files, and export the database from your hosting panel. If your site runs on SQLite, the database file is one of the files. Don't start until you are sure you can restore both.",[92,1861,1863],{"id":1862},"check-the-server-requirements","Check the server requirements",[72,1865,1866,1867,1870,1871,1874],{},"Check in your hosting panel that PHP is version 8.2 or newer, and that the PDO and ZipArchive extensions are enabled. If they aren't, ask your hosting company to upgrade PHP before you do anything else. If your site runs on SQLite, make sure the SQLite library is version 3.35 or newer (",[145,1868,1869],{},"phpinfo()"," shows it under ",[145,1872,1873],{},"pdo_sqlite","), because Kleeja 4 drops database columns and older versions can't.",[92,1876,1878],{"id":1877},"review-your-plugins-and-styles","Review your plugins and styles",[72,1880,1881],{},"Write down the plugins and the style your site uses, and look for each of them in the \"Plugins for Kleeja 4\" section above. If your site depends on a plugin that has no Kleeja 4 version, postpone the upgrade. If you have a custom style built on Default, or you edited core files yourself, save your changes somewhere else.",[92,1883,1885],{"id":1884},"try-it-on-a-test-copy-first","Try it on a test copy first",[72,1887,1888],{},"Copy your site and its database to a subdomain or to your own computer, and run the upgrade there. Open the upload page, the download page and the control panel, and try uploading and downloading a file.",[92,1890,1892],{"id":1891},"update-from-the-control-panel","Update from the control panel",[72,1894,1895],{},"Open the \"Check for updates\" page of your Kleeja 3 control panel, and click \"update now!\". The updater downloads Kleeja 4, replaces the files and updates the database in three steps, and closes the site for maintenance while it works. Keep the page open until all three steps are done.",[72,1897,1898,1899,1901,1902,1904],{},"The database update drops the old columns, disables the plugins, switches your site from Default to ",[145,1900,197],{}," if it uses Default, and deletes the Masmak and ",[145,1903,787],{}," folders.",[92,1906,1908],{"id":1907},"reload-the-control-panel","Reload the control panel",[72,1910,1911,1912,1915],{},"When the update is done, reload the control panel with ",[145,1913,1914],{},"Ctrl+F5",", because Damask replaced Masmak and the CSS and JavaScript files have changed. If the dashboard shows a \"The database needs an update\" notice, click \"Update the database\" in it, and Kleeja finishes what is left.",[72,1917,1918],{},[102,1919],{"alt":1920,"src":1921},"The \"database needs an update\" notice on the dashboard","\u002Fimages\u002Fblog\u002Fkleeja-4\u002Fen\u002Fdb-update-notice.png",[92,1923,1925],{"id":1924},"update-and-enable-your-plugins-and-styles","Update and enable your plugins and styles",[72,1927,1928],{},"Open the \"Plugins\" page in the control panel, update each plugin, then enable them one at a time and open your site after each one. If something breaks, you will know which plugin caused it. Then open the \"Styles\" page and update your style.",[79,1930,1931,1934,1935,1940,1941,1943,1944,1947,1948,1950,1951,1953],{},[82,1932,1933],{},"Upgrading by hand:"," if your server doesn't have ZipArchive, or the updater can't write to your site's files, download Kleeja 4.0.0 from the ",[245,1936,1939],{"href":1937,"rel":1938},"https:\u002F\u002Fgithub.com\u002Fkleeja\u002Fkleeja\u002Freleases",[249],"releases page on GitHub"," and upload its files over your site's files, including the ",[145,1942,874],{}," folder. Then open ",[145,1945,1946],{},"\u002Finstall\u002Fupdate.php",", sign in with the admin username and password, and delete the ",[145,1949,874],{}," folder once the update is done. The package doesn't contain ",[145,1952,882],{},", so your database connection settings stay as they are.",[1955,1956,1957],"caution",{},"If the upgrade fails, don't try to repair the live site while it is half upgraded. Restore the backup of your files and your database together, then tell us what happened.",[92,1959,1961],{"id":1960},"tips-for-handling-the-breaking-changes","Tips for handling the breaking changes",[110,1963,1964,1974,1983,1989,2005],{},[113,1965,1966,1969,1970,1973],{},[82,1967,1968],{},"Move your changes into a plugin."," If you edit core files directly, the updater replaces them on every upgrade. Hooks (",[145,1971,1972],{},"runHook",") exist on almost every page, and a small plugin keeps your change safe from future upgrades.",[113,1975,1976,1979,1980,1982],{},[82,1977,1978],{},"Build your style on Bootstrap."," A style that depends on Bootstrap through ",[145,1981,1469],{}," inherits every template you didn't change, so template fixes reach you with every upgrade without you writing them.",[113,1984,1985,1988],{},[82,1986,1987],{},"Don't upgrade while a plugin your site relies on still has no version."," If your service depends on a forum integration, for example, keeping your site on Kleeja 3.2.7 until that plugin's version is out is better than a site that runs without it.",[113,1990,1991,1997,1998,2001,2002,2004],{},[82,1992,1993,1994,1996],{},"Enable ",[145,1995,1136],{}," on the test copy."," Add ",[145,1999,2000],{},"define('DEV_STAGE', true);"," to its ",[145,2003,882],{},", and Kleeja shows every error and compiles templates on every request. You will find style and plugin errors quickly, but don't enable it on your live site.",[113,2006,2007,2010],{},[82,2008,2009],{},"Read the Help page after the upgrade."," The \"Find your way around\" guide and the page guides get you familiar with Damask quickly.",[87,2012,2014],{"id":2013},"thank-you","Thank you",[72,2016,2017,2018,2023],{},"Among ",[245,2019,2022],{"href":2020,"rel":2021},"https:\u002F\u002Fgithub.com\u002FRouatbiH",[249],"Hani Rouatbi","'s contributions to this release: fixes for resumable downloads, for updating the login cookie after a profile change, for verifying the file deletion code, and for a number of other bugs. Thank you, Hani, and thank you to everyone who reported a bug, translated or suggested an idea.",[87,2025,2027],{"id":2026},"links","Links",[110,2029,2030,2037,2044],{},[113,2031,2032],{},[245,2033,2036],{"href":2034,"rel":2035},"https:\u002F\u002Fgithub.com\u002Fkleeja\u002Fkleeja",[249],"Kleeja on GitHub",[113,2038,2039],{},[245,2040,2043],{"href":2041,"rel":2042},"https:\u002F\u002Fgithub.com\u002Fkleeja\u002Fkleeja\u002Fissues",[249],"Report an issue",[113,2045,2046],{},[245,2047,2049],{"href":247,"rel":2048},[249],"The store catalog",[72,2051,2052],{},"If you run into a problem while upgrading, open an issue on GitHub with your PHP version, your database type and the plugins you had enabled, and we will help you.",[2054,2055,2056],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":914,"searchDepth":928,"depth":928,"links":2058},[2059,2072,2077,2081,2091,2092],{"id":89,"depth":928,"text":90,"children":2060},[2061,2062,2063,2064,2065,2066,2067,2068,2069,2070,2071],{"id":94,"depth":956,"text":95},{"id":136,"depth":956,"text":137},{"id":160,"depth":956,"text":161},{"id":201,"depth":956,"text":202},{"id":235,"depth":956,"text":236},{"id":260,"depth":956,"text":261},{"id":273,"depth":956,"text":274},{"id":286,"depth":956,"text":287},{"id":344,"depth":956,"text":345},{"id":397,"depth":956,"text":398},{"id":418,"depth":956,"text":419},{"id":433,"depth":928,"text":434,"children":2073},[2074,2075,2076],{"id":460,"depth":956,"text":461},{"id":492,"depth":956,"text":493},{"id":564,"depth":956,"text":565},{"id":730,"depth":928,"text":731,"children":2078},[2079,2080],{"id":734,"depth":956,"text":735},{"id":892,"depth":956,"text":893},{"id":1842,"depth":928,"text":1843,"children":2082},[2083,2084,2085,2086,2087,2088,2089,2090],{"id":1855,"depth":956,"text":1856},{"id":1862,"depth":956,"text":1863},{"id":1877,"depth":956,"text":1878},{"id":1884,"depth":956,"text":1885},{"id":1891,"depth":956,"text":1892},{"id":1907,"depth":956,"text":1908},{"id":1924,"depth":956,"text":1925},{"id":1960,"depth":956,"text":1961},{"id":2013,"depth":928,"text":2014},{"id":2026,"depth":928,"text":2027},"2026-10-10","A tour of Kleeja 4: a new control panel, a new Bootstrap style and a safer updater, plus the breaking changes and how to upgrade safely.","md","\u002Fimages\u002Fblog\u002Fkleeja-4.png",{},"\u002Fblog\u002Fkleeja-4-release",{"title":60,"description":2094},"en\u002Fblog\u002Fkleeja-4-release","Zp9mDwa_umRO6G-whvMrCrvZQZ7PVARIzJgMVqzcjNk",[2103,2108],{"title":2104,"path":2105,"stem":2106,"description":2107,"children":-1},"Hello World","\u002Fblog\u002Fhello-world","en\u002Fblog\u002Fhello-world","This is the first blog post in our Kleeja blog.",null,1792539827844]