"date", "name"=>"r_{$this->name}[]", "value"=>@$inputs["r_{$this->name}"][0] ]), BR(), INPUT([ "type"=>"date", "name"=>"r_{$this->name}[]", "value"=>@$inputs["r_{$this->name}"][1] ]) ); } } class ActorColumn extends Column { public function __construct($name, $title) { parent::__construct($name, $title); $this->sortable = false; } public function get_sql_filter(): string { $driver = $this->table->db->getAttribute(\PDO::ATTR_DRIVER_NAME); switch ($driver) { case "pgsql": return "((LOWER(username) = LOWER(:{$this->name}_0)) OR (address && cast(:{$this->name}_1 as inet)))"; default: return "((username = :{$this->name}_0) OR (address = :{$this->name}_1))"; } } public function read_input($inputs) { return emptyHTML( INPUT([ "type" => "text", "name" => "r_{$this->name}[]", "placeholder" => "Username", "value" => @$inputs["r_{$this->name}"][0] ]), BR(), INPUT([ "type" => "text", "name" => "r_{$this->name}[]", "placeholder" => "IP Address", "value" => @$inputs["r_{$this->name}"][1] ]) ); } public function modify_input_for_read($input): array { list($un, $ip) = $input; if (empty($un)) { $un = null; } if (empty($ip)) { $ip = null; } return [$un, $ip]; } public function display($row): HTMLElement { $ret = emptyHTML(); if ($row['username'] != "Anonymous") { $ret->appendChild(A(["href"=>make_link("user/{$row['username']}"), "title"=>$row['address']], $row['username'])); $ret->appendChild(BR()); } $ret->appendChild($row['address']); return $ret; } } class MessageColumn extends Column { public function __construct(string $name, string $title) { parent::__construct($name, $title); $this->sortable = false; } public function get_sql_filter(): string { return "({$this->name} LIKE :{$this->name}_0 AND priority >= :{$this->name}_1)"; } public function read_input(array $inputs) { $ret = emptyHTML( INPUT([ "type"=>"text", "name"=>"r_{$this->name}[]", "placeholder"=>$this->title, "value"=>@$inputs["r_{$this->name}"][0] ]) ); $options = [ "Debug" => SCORE_LOG_DEBUG, "Info" => SCORE_LOG_INFO, "Warning" => SCORE_LOG_WARNING, "Error" => SCORE_LOG_ERROR, "Critical" => SCORE_LOG_CRITICAL, ]; $s = SELECT(["name"=>"r_{$this->name}[]"]); $s->appendChild(OPTION(["value"=>""], '-')); foreach ($options as $k => $v) { $attrs = ["value"=>$v]; if ($v == @$inputs["r_{$this->name}"][1]) { $attrs["selected"] = true; } $s->appendChild(OPTION($attrs, $k)); } $ret->appendChild($s); return $ret; } public function modify_input_for_read($input) { list($m, $l) = $input; if (empty($m)) { $m = "%"; } else { $m = "%$m%"; } if (empty($l)) { $l = 0; } return [$m, $l]; } public function display($row) { $c = "#000"; switch ($row['priority']) { case SCORE_LOG_DEBUG: $c = "#999"; break; case SCORE_LOG_INFO: $c = "#000"; break; case SCORE_LOG_WARNING: $c = "#800"; break; case SCORE_LOG_ERROR: $c = "#C00"; break; case SCORE_LOG_CRITICAL: $c = "#F00"; break; } return SPAN(["style"=>"color: $c"], rawHTML($this->scan_entities($row[$this->name]))); } protected function scan_entities(string $line) { $line = preg_replace_callback("/Image #(\d+)/s", [$this, "link_image"], $line); $line = preg_replace_callback("/Post #(\d+)/s", [$this, "link_image"], $line); $line = preg_replace_callback("/>>(\d+)/s", [$this, "link_image"], $line); return $line; } protected function link_image($id) { $iid = int_escape($id[1]); return ">>$iid"; } } class LogTable extends Table { public function __construct(\FFSPHP\PDO $db) { parent::__construct($db); $this->table = "score_log"; $this->base_query = "SELECT * FROM score_log"; $this->size = 100; $this->limit = 100000; $this->set_columns([ new ShortDateTimeColumn("date_sent", "Time"), new TextColumn("section", "Module"), new ActorColumn("username_or_address", "User"), new MessageColumn("message", "Message"), new ActionColumn("id"), ]); $this->order_by = ["date_sent DESC"]; $this->table_attrs = ["class" => "zebra"]; } } class LogDatabase extends Extension { /** @var LogDatabaseTheme */ protected Themelet $theme; public function onInitExt(InitExtEvent $event) { global $config; $config->set_default_int("log_db_priority", SCORE_LOG_INFO); } public function onDatabaseUpgrade(DatabaseUpgradeEvent $event) { global $database; if ($this->get_version("ext_log_database_version") < 1) { $database->create_table("score_log", " id SCORE_AIPK, date_sent TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, section VARCHAR(32) NOT NULL, username VARCHAR(32) NOT NULL, address SCORE_INET NOT NULL, priority INT NOT NULL, message TEXT NOT NULL "); //INDEX(section) $this->set_version("ext_log_database_version", 1); } } public function onSetupBuilding(SetupBuildingEvent $event) { $sb = $event->panel->create_new_block("Logging (Database)"); $sb->add_choice_option("log_db_priority", [ LOGGING_LEVEL_NAMES[SCORE_LOG_DEBUG] => SCORE_LOG_DEBUG, LOGGING_LEVEL_NAMES[SCORE_LOG_INFO] => SCORE_LOG_INFO, LOGGING_LEVEL_NAMES[SCORE_LOG_WARNING] => SCORE_LOG_WARNING, LOGGING_LEVEL_NAMES[SCORE_LOG_ERROR] => SCORE_LOG_ERROR, LOGGING_LEVEL_NAMES[SCORE_LOG_CRITICAL] => SCORE_LOG_CRITICAL, ], "Debug Level: "); } public function onPageRequest(PageRequestEvent $event) { global $database, $user; if ($event->page_matches("log/view")) { if ($user->can(Permissions::VIEW_EVENTLOG)) { $t = new LogTable($database->raw_db()); $t->inputs = $_GET; $this->theme->display_events($t->table($t->query()), $t->paginator()); } } } public function onPageSubNavBuilding(PageSubNavBuildingEvent $event) { global $user; if ($event->parent==="system") { if ($user->can(Permissions::VIEW_EVENTLOG)) { $event->add_nav_link("event_log", new Link('log/view'), "Event Log"); } } } public function onUserBlockBuilding(UserBlockBuildingEvent $event) { global $user; if ($user->can(Permissions::VIEW_EVENTLOG)) { $event->add_link("Event Log", make_link("log/view")); } } public function onLog(LogEvent $event) { global $config, $database, $user; $username = ($user && $user->name) ? $user->name : "null"; // not installed yet... if ($this->get_version("ext_log_database_version") < 1) { return; } if ($event->priority >= $config->get_int("log_db_priority")) { $database->execute(" INSERT INTO score_log(date_sent, section, priority, username, address, message) VALUES(now(), :section, :priority, :username, :address, :message) ", [ "section"=>$event->section, "priority"=>$event->priority, "username"=>$username, "address"=>get_real_ip(), "message"=>$event->message ]); } } }