├── lib ├── helpers.php ├── hooks.php └── translations.php ├── logger.php ├── logger.png ├── mvc ├── controller.php ├── model.php └── view.php ├── package.json ├── readme.md └── widgets └── logger ├── logger.html.php └── logger.php /lib/helpers.php: -------------------------------------------------------------------------------- 1 | 0 && $lines >= 0) 22 | { 23 | // Figure out how far back we should jump 24 | $seek = min(ftell($f), $buffer); 25 | 26 | // Do the jump (backwards, relative to where we are) 27 | fseek($f, -$seek, SEEK_CUR); 28 | 29 | // Read a chunk and prepend it to our output 30 | $output = ($chunk = fread($f, $seek)).$output; 31 | // Jump back to where we started reading 32 | fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR); 33 | 34 | // Decrease our line counter 35 | $lines -= substr_count($chunk, "\n"); 36 | 37 | } 38 | 39 | // While we have too many lines 40 | // (Because of buffer size we might have read too many) 41 | while($lines++ < 0) 42 | { 43 | // Find first newline and remove all text before that 44 | $output = substr($output, strpos($output, "\n") + 1); 45 | } 46 | 47 | $output = explode("\n",rtrim($output, "\n")); 48 | 49 | // Close file and return 50 | fclose($f); 51 | return $output; 52 | } 53 | 54 | 55 | function translation($string) { 56 | 57 | $translation = c::get('logger.translation', false); 58 | 59 | if(!$translation) { 60 | $translations = require __DIR__ . DS . 'translations.php'; 61 | $language = c::get('logger.language', c::get('panel.language')); 62 | 63 | if (! array_key_exists($language, $translations)) { 64 | $language = 'en'; 65 | } 66 | 67 | $translation = $translations[$language]; 68 | } 69 | 70 | if(array_key_exists($string, $translation)) { 71 | $string = $translation[$string]; 72 | } 73 | 74 | return $string; 75 | } 76 | 77 | function createLogfile($path) { 78 | try { 79 | $file = f::write($path, ''); 80 | } catch(Exception $e) { 81 | throw new Exception('The file could not be created'); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/hooks.php: -------------------------------------------------------------------------------- 1 | hook('panel.site.update', function ($site, $oldSite) { 3 | logger()->save('site.update', $site->content()->toArray(), $oldSite->content()->toArray()); 4 | }); 5 | kirby()->hook('panel.page.create', function ($page) { 6 | logger()->save('page.create', $page->uri()); 7 | }); 8 | kirby()->hook('panel.page.update', function ($page, $oldpage) { 9 | logger()->save('page.update', $page->uri(), $page->content()->toArray(), $oldpage->content()->toArray()); 10 | }); 11 | kirby()->hook('panel.page.delete', function ($page) { 12 | logger()->save('page.delete', $page->uri()); 13 | }); 14 | kirby()->hook('panel.page.sort', function ($page) { 15 | logger()->save('page.sort', $page->uri()); 16 | }); 17 | kirby()->hook('panel.page.hide', function ($page) { 18 | logger()->save('page.hide', $page->uri()); 19 | }); 20 | kirby()->hook('panel.page.move', function ($newPage, $oldPage) { 21 | logger()->save('page.move', $oldPage->uri(), $newPage->uri()); 22 | }); 23 | kirby()->hook('panel.file.upload', function ($file) { 24 | logger()->save('file.upload', $file->page()->uri().'/'.$file->filename()); 25 | }); 26 | kirby()->hook('panel.file.replace', function ($file, $oldFile) { 27 | logger()->save('file.replace', $file->page()->uri().'/'.$file->filename(), $file->page()->uri().'/'.$oldFile->filename()); 28 | }); 29 | kirby()->hook('panel.file.rename', function ($file) { 30 | logger()->save('file.rename', $file->page()->uri().'/'.$file->filename()); 31 | }); 32 | kirby()->hook('panel.file.update', function ($file, $oldFile) { 33 | logger()->save('file.update', $file->page()->uri().'/'.$file->filename(), $file->meta()->toArray(), $oldFile->meta()->toArray()); 34 | }); 35 | kirby()->hook('panel.file.sort', function ($file) { 36 | logger()->save('file.sort', $file->page()->uri().'/'.$file->filename()); 37 | }); 38 | kirby()->hook('panel.file.delete', function ($file) { 39 | logger()->save('file.delete', $file->page()->uri().'/'.$file->filename()); 40 | }); 41 | -------------------------------------------------------------------------------- /lib/translations.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'site.update' => 'Updated site options', 5 | 'page.create' => 'Created page %s', 6 | 'page.update' => 'Updated page %s', 7 | 'page.delete' => 'Deleted page %s', 8 | 'page.sort' => 'Sorted page %s', 9 | 'page.hide' => 'Hid page %s', 10 | 'page.move' => 'Moved page %1$s to %2$s', 11 | 'file.upload' => 'Uploaded file %s', 12 | 'file.replace' => 'Replaced file %2$s through file %1$s', 13 | 'file.rename' => 'Renamed file %s', 14 | 'file.update' => 'Updated file %s', 15 | 'file.sort' => 'Sorted file %s', 16 | 'file.delete' => 'Deleted file %s', 17 | 'user' => 'User', 18 | 'date' => 'Date Time', 19 | 'action' => 'Action', 20 | 'changes' => 'Changed fields', 21 | ], 22 | 'de' => [ 23 | 'site.update' => 'Seiteneinstellungen aktualisiert', 24 | 'page.create' => 'Seite %s erstellt', 25 | 'page.update' => 'Seite %s aktualisiert', 26 | 'page.delete' => 'Seite %s gelöscht', 27 | 'page.sort' => 'Seite %s sortiert', 28 | 'page.hide' => 'Seite %s versteckt', 29 | 'page.move' => 'Seite %1$s nach %2$s verschoben', 30 | 'file.upload' => 'Datei %s hochgeladen', 31 | 'file.replace' => 'Datei %2$s durch %1$s ersetzt', 32 | 'file.rename' => 'Datei %s umbenannt', 33 | 'file.update' => 'Datei %s aktualisiert', 34 | 'file.sort' => 'Datei %s sortiert', 35 | 'file.delete' => 'Datei %s gelöscht', 36 | 'user' => 'Benutzer', 37 | 'date' => 'Datum Uhrzeit', 38 | 'action' => 'Aktion', 39 | 'changes' => 'Geänderte Felder', 40 | ], 41 | 'nl' => [ 42 | 'site.update' => 'Site-opties veranderd', 43 | 'page.create' => 'Pagina %s aangemaakt', 44 | 'page.update' => 'Pagina %s veranderd', 45 | 'page.delete' => 'Pagina %s verwijderd', 46 | 'page.sort' => 'Pagina %s gesorteerd', 47 | 'page.hide' => 'Pagina %s verborgen', 48 | 'page.move' => 'Pagina %1$s naar %2$s verplaatst', 49 | 'file.upload' => 'Bestand %s geüpload', 50 | 'file.replace' => 'Bestand %2$s voor bestand %1$s vervangen', 51 | 'file.rename' => 'Bestand %s hernoemd', 52 | 'file.update' => 'Bestand %s veranderd', 53 | 'file.sort' => 'Bestand %s gesorteerd', 54 | 'file.delete' => 'Bestand %s verwijderd', 55 | 'user' => 'Gebruiker', 56 | 'date' => 'Datum Tijd', 57 | 'action' => 'Actie', 58 | 'changes' => 'Veranderde velden', 59 | ], 60 | 'fr' => [ 61 | 'site.update' => 'Paramètres du site modifiés', 62 | 'page.create' => 'Page %s créée', 63 | 'page.update' => 'Page %s modifiée', 64 | 'page.delete' => 'Page %s supprimée', 65 | 'page.sort' => 'Page %s classée', 66 | 'page.hide' => 'Page %s masquée', 67 | 'page.move' => 'Page %1$s déplacée vers %2$s', 68 | 'file.upload' => 'Fichier %s transféré', 69 | 'file.replace' => 'Fichier %2$s remplacé par %1$s', 70 | 'file.rename' => 'Fichier %s renommé', 71 | 'file.update' => 'Fichier %s modifié', 72 | 'file.sort' => 'Fichier %s classé', 73 | 'file.delete' => 'Fichier %s supprimé', 74 | 'user' => 'Utilisateur', 75 | 'date' => 'Date Heure', 76 | 'action' => 'Action', 77 | 'changes' => 'Champs modifiés', 78 | ], 79 | 'pt_BR' => [ 80 | 'site.update' => 'Alterou as opções do site', 81 | 'page.create' => 'Criou a página %s', 82 | 'page.update' => 'Atualizou a página %s', 83 | 'page.delete' => 'Excluiu a página %s', 84 | 'page.sort' => 'Alterou a ordem da página %s', 85 | 'page.hide' => 'Escondeu a página %s', 86 | 'page.move' => 'Moveu a página %1$s para %2$s', 87 | 'file.upload' => 'Adicionou o arquivo %s', 88 | 'file.replace' => 'Substituiu o arquivo %2$s por %1$s', 89 | 'file.rename' => 'Renomeou o arquivo %s', 90 | 'file.update' => 'Atualizou o arquivo %s', 91 | 'file.sort' => 'Alterou a ordem do arquivo %s', 92 | 'file.delete' => 'Excluiu o arquivo %s', 93 | 'user' => 'Usuário', 94 | 'date' => 'Data Tempo', 95 | 'action' => 'Ação', 96 | 'changes' => 'Campos alterados', 97 | ], 98 | 'pt_PT' => [ 99 | 'site.update' => 'Alterou as opções do site', 100 | 'page.create' => 'Criou a página %s', 101 | 'page.update' => 'Atualizou a página %s', 102 | 'page.delete' => 'Excluiu a página %s', 103 | 'page.sort' => 'Alterou a ordem da página %s', 104 | 'page.hide' => 'Escondeu a página %s', 105 | 'page.move' => 'Moveu a página %1$s para %2$s', 106 | 'file.upload' => 'Adicionou o ficheiro %s', 107 | 'file.replace' => 'Substituiu o ficheiro %2$s por %1$s', 108 | 'file.rename' => 'Renomeou o ficheiro %s', 109 | 'file.update' => 'Atualizou o ficheiro %s', 110 | 'file.sort' => 'Alterou a ordem do ficheiro %s', 111 | 'file.delete' => 'Excluiu o ficheiro %s', 112 | 'user' => 'Usuário', 113 | 'date' => 'Data Tempo', 114 | 'action' => 'Ação', 115 | 'changes' => 'Campos alterados', 116 | ], 117 | ]; 118 | -------------------------------------------------------------------------------- /logger.php: -------------------------------------------------------------------------------- 1 | 8 | * @copyright Sonja Broda 9 | * @link https://github.com/texnixe/kirby-logger 10 | * @license MIT 11 | */ 12 | 13 | 14 | 15 | 16 | function logger() { 17 | require_once(__DIR__ . DS . 'mvc' . DS . 'model.php'); 18 | return new Kirby\Panel\Models\Logger; 19 | } 20 | 21 | require_once(__DIR__.DS.'lib'.DS.'hooks.php'); 22 | require_once(__DIR__ . DS . 'lib' . DS . 'helpers.php'); 23 | 24 | // call site()->user() only when in Panel to prevent cookie being set 25 | if(function_exists('panel') && $panel = panel()) { 26 | 27 | // Load widgets 28 | if(site()->user() && in_array(site()->user()->role(), c::get('logger.roles', ['admin']))) { 29 | kirby()->set('widget', 'logger', __DIR__. DS . 'widgets' . DS . 'logger'); 30 | } 31 | 32 | $panel->routes = array_merge([ 33 | [ 34 | 'pattern' => 'logger', 35 | 'action' => function() { 36 | require 'mvc/controller.php'; 37 | require 'mvc/model.php'; 38 | 39 | $logger = new LoggerController; 40 | echo $logger->index(); 41 | }, 42 | 'method' => 'GET|POST' 43 | ], 44 | ], $panel->routes); 45 | } 46 | -------------------------------------------------------------------------------- /logger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/texnixe/kirby-logger/a2c71214a9ccd0ba208d393675002aa694c4103b/logger.png -------------------------------------------------------------------------------- /mvc/controller.php: -------------------------------------------------------------------------------- 1 | $logger->getChanges()]); 13 | $content->_root = dirname(__DIR__); 14 | 15 | return $this->layout('app', [ 16 | 'topbar' => new Topbar('logger', $logger), 17 | 'content' => $content 18 | ]); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /mvc/model.php: -------------------------------------------------------------------------------- 1 | logfile = c::get('logger.filepath', kirby()->roots()->index() . DS . 'logger/log.txt'); 17 | $this->user = $this->getUser(); 18 | 19 | if(! file_exists($this->logfile)) { 20 | createLogfile($this->logfile); 21 | } 22 | } 23 | 24 | public function getUser() { 25 | return site()->user()? site()->user()->username():'anonymous'; 26 | } 27 | 28 | public function save(...$params) { 29 | $message = $this->getMessage($params[0], array_slice($params, 1)); 30 | $diff = array(); 31 | 32 | if(strpos($params[0], '.update')) { 33 | $diff = $this->getDiff(array_slice($params, -2)[0], array_slice($params, -1)[0]); 34 | } 35 | 36 | 37 | $file = $this->logfile; 38 | $data = array( 39 | 'user' => $this->getUser(), 40 | 'date' => date('Y-m-d H:i:s'), 41 | 'message' => $message, 42 | 'changed' => !empty($diff)? implode('|', $diff): '' 43 | ); 44 | 45 | $data = implode(', ', $data) . "\n"; 46 | file_put_contents($file, $data, FILE_APPEND | LOCK_EX); 47 | 48 | } 49 | 50 | protected function getMessage($key, $params) { 51 | array_unshift($params, translation($key)); 52 | return sprintf(...$params); 53 | } 54 | 55 | protected function getDiff($new, $old) { 56 | 57 | $diff = array(); 58 | 59 | foreach($new as $field => $value) { 60 | if(isset($old[$field])) { 61 | if($value !== $old[$field]) { 62 | $diff[] = $field; 63 | } 64 | } 65 | } 66 | return $diff; 67 | } 68 | 69 | public function getChanges() { 70 | $filepath = c::get('logger.filepath', kirby()->roots()->index() . DS . 'logger/log.txt'); 71 | if(! file_exists($filepath)) { 72 | createLogfile($filepath); 73 | } 74 | $entries = c::get('logger.entries', 50); 75 | $changes = tail($filepath, $entries); 76 | 77 | return $changes; 78 | } 79 | 80 | public function topbar($topbar) { 81 | $topbar->append(purl('logger'), 'Logger'); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /mvc/view.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | user()->role(), c::get('logger.roles', ['admin']))): ?> 7 |
8 |
9 |
10 |

Logger

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 |
37 |
38 | 39 | 40 | 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logger", 3 | "description": "Kirby Logger", 4 | "author": "Sonja Broda ", 5 | "license": "MIT", 6 | "version": "1.3.1", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/texnixe/kirby-logger" 10 | }, 11 | "type": "kirby-plugin" 12 | } 13 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Kirby Logger 2 | 3 | Logger is a [Kirby CMS](https://getkirby.com) plugin that logs all changes to the content folder to a log file. Admins can view all changes in a separate view in the panel, which is accessible via a dashboard widget. 4 | 5 | If you don't want to or cannot use Git to track changes to the content folder but want keep an eye on who changed what and when, this plugin might be for you. 6 | 7 | **Note:** As of version 1.3.0, the date and time fields were combined. Old log files will therefore not be compatible with the new version. 8 | 9 | ![](logger.png) 10 | 11 | ## Requirements 12 | 13 | - PHP 5.6+ 14 | 15 | ## Installation 16 | 17 | ### Download 18 | 19 | [Download the files](https://github.com/texnixe/kirby-logger/archive/master.zip) and place them inside `site/plugins/logger`. 20 | 21 | ### Kirby CLI 22 | Installing via Kirby's [command line interface](https://github.com/getkirby/cli): 23 | 24 | $ kirby plugin:install texnixe/kirby-logger 25 | 26 | To update Logger, run: 27 | 28 | $ kirby plugin:update texnixe/kirby-logger 29 | 30 | ### Git Submodule 31 | You can add the Logger plugin as a Git submodule. 32 | 33 | $ cd your/project/root 34 | $ git submodule add https://github.com/texnixe/kirby-logger.git site/plugins/logger 35 | $ git submodule update --init --recursive 36 | $ git commit -am "Add Kirby Logger" 37 | 38 | Run these commands to update the plugin: 39 | 40 | $ cd your/project/root 41 | $ git submodule foreach git checkout master 42 | $ git submodule foreach git pull 43 | $ git commit -am "Update submodules" 44 | $ git submodule update --init --recursive 45 | 46 | ## Options 47 | The following options can be set in your `/site/config/config.php`: 48 | 49 | ```php 50 | c::set('logger.roles', ['admin']); 51 | c::set('logger.entries', 50); 52 | c::set('logger.language', 'en'); 53 | c::set('logger.translation', [ 54 | 'site.update' => 'Changed site options', 55 | 'page.create' => 'Created page %s', 56 | 'page.update' => 'Updated page %s', 57 | 'page.delete' => 'Deleted page %s', 58 | 'page.sort' => 'Sorted page %s', 59 | 'page.hide' => 'Hid page %s', 60 | 'page.move' => 'Moved page %1$s to %2$s', 61 | 'file.upload' => 'Uploaded file %s', 62 | 'file.replace' => 'Replaced file %s', 63 | 'file.rename' => 'Renamed file %s', 64 | 'file.update' => 'Updated file %s', 65 | 'file.sort' => 'Sorted file %s', 66 | 'file.delete' => 'Deleted file %s', 67 | 'user' => 'User', 68 | 'date' => 'Date', 69 | 'time' => 'Time', 70 | 'action' => 'Action' 71 | ]); 72 | ``` 73 | ### logger.roles 74 | 75 | An array of roles that are allowed to view the log (and the Panel widget) 76 | 77 | ### logger.entries 78 | 79 | Number of changes to show in the Panel view. Default: 50 80 | 81 | ### logger.language 82 | 83 | Default language for log entries; choose one of the currently four translations provided with the plugin ('en', 'de', 'pt_BR', 'pt_PT'). 84 | 85 | ### logger.translation 86 | 87 | An array with custom translations. This will override the default translations set by the `logger.language` option. 88 | 89 | ## Changelog 90 | 91 | ### 1.3.1 92 | 93 | - Added pagination 94 | 95 | ### 1.3.0 96 | 97 | - Combined date/time field 98 | 99 | ## Credits 100 | 101 | This plugin is inspired by and heavily reuses code of these two great Kirby plugins: 102 | 103 | - [Auto Git](https://github.com/pedroborges/kirby-autogit) plugin by @pedroborges 104 | - [Static Translations](https://github.com/rasteiner/kirbytranslations) plugin by @rastainer 105 | 106 | ## License 107 | 108 | Logger is open-sourced software licensed under the MIT license. 109 | 110 | Copyright © 2016 Sonja Broda info@texniq.de https://www.texniq.de 111 | -------------------------------------------------------------------------------- /widgets/logger/logger.html.php: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /widgets/logger/logger.php: -------------------------------------------------------------------------------- 1 | 'Logger', 5 | 'html' => function() { 6 | return tpl::load(__DIR__ . DS .'logger.html.php'); 7 | }, 8 | ); 9 | --------------------------------------------------------------------------------