mirror of
https://github.com/Icinga/icinga2.git
synced 2025-04-08 17:05:25 +02:00
parent
92896311f3
commit
8cc6368954
@ -29,6 +29,7 @@
|
||||
#include "base/scriptvariable.hpp"
|
||||
#include "base/context.hpp"
|
||||
#include "base/clicommand.hpp"
|
||||
#include "base/console.hpp"
|
||||
#include "config.h"
|
||||
#include <boost/program_options.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
@ -166,6 +167,9 @@ int Main(void)
|
||||
visibleDesc.add_options()
|
||||
("help", "show this help message")
|
||||
("version,V", "show version information")
|
||||
#ifndef _WIN32
|
||||
("color", "use VT100 color codes even when stdout is not a terminal")
|
||||
#endif /* _WIN32 */
|
||||
("define,D", po::value<std::vector<std::string> >(), "define a constant")
|
||||
("library,l", po::value<std::vector<std::string> >(), "load a library")
|
||||
("include,I", po::value<std::vector<std::string> >(), "add include search directory")
|
||||
@ -201,6 +205,13 @@ int Main(void)
|
||||
ConfigCompiler::CompileFile(initconfig);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if (vm.count("color")) {
|
||||
Console::SetType(std::cout, Console_VT100);
|
||||
Console::SetType(std::cerr, Console_VT100);
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
if (vm.count("define")) {
|
||||
BOOST_FOREACH(const String& define, vm["define"].as<std::vector<std::string> >()) {
|
||||
String key, value;
|
||||
|
@ -23,7 +23,7 @@ mkclass_target(streamlogger.ti streamlogger.thpp)
|
||||
mkclass_target(sysloglogger.ti sysloglogger.thpp)
|
||||
|
||||
set(base_SOURCES
|
||||
application.cpp application.thpp array.cpp clicommand.cpp configerror.cpp context.cpp
|
||||
application.cpp application.thpp array.cpp clicommand.cpp configerror.cpp console.cpp context.cpp
|
||||
convert.cpp debuginfo.cpp dictionary.cpp dynamicobject.cpp dynamicobject.thpp dynamictype.cpp
|
||||
exception.cpp fifo.cpp filelogger.cpp filelogger.thpp logger.cpp logger.thpp
|
||||
netstring.cpp networkstream.cpp object.cpp objectlock.cpp process.cpp
|
||||
|
221
lib/base/console.cpp
Normal file
221
lib/base/console.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
/******************************************************************************
|
||||
* Icinga 2 *
|
||||
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software Foundation *
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#include "base/console.hpp"
|
||||
#include "base/initialize.hpp"
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
INITIALIZE_ONCE(&Console::DetectType);
|
||||
|
||||
static ConsoleType l_ConsoleType = Console_Dumb;
|
||||
|
||||
ConsoleColorTag::ConsoleColorTag(int color)
|
||||
: m_Color(color)
|
||||
{ }
|
||||
|
||||
std::ostream& icinga::operator<<(std::ostream& fp, const ConsoleColorTag& cct)
|
||||
{
|
||||
fp.flush();
|
||||
|
||||
#ifndef _WIN32
|
||||
if (l_ConsoleType == Console_VT100)
|
||||
Console::PrintVT100ColorCode(fp, cct.m_Color);
|
||||
#else /* _WIN32 */
|
||||
if (l_ConsoleType == Console_Windows) {
|
||||
fp.flush();
|
||||
Console::SetWindowsConsoleColor(fp, cct.m_Color);
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
void Console::DetectType(void)
|
||||
{
|
||||
l_ConsoleType = Console_Dumb;
|
||||
|
||||
#ifndef _WIN32
|
||||
if (isatty(1))
|
||||
l_ConsoleType = Console_VT100;
|
||||
#else /* _WIN32 */
|
||||
l_ConsoleType = Console_Windows;
|
||||
#endif /* _WIN32 */
|
||||
}
|
||||
|
||||
void Console::SetType(std::ostream& fp, ConsoleType type)
|
||||
{
|
||||
if (&fp == &std::cout || &fp == &std::cerr)
|
||||
l_ConsoleType = type;
|
||||
}
|
||||
|
||||
ConsoleType Console::GetType(std::ostream& fp)
|
||||
{
|
||||
if (&fp == &std::cout || &fp == &std::cerr)
|
||||
return l_ConsoleType;
|
||||
else
|
||||
return Console_Dumb;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
void Console::PrintVT100ColorCode(std::ostream& fp, int color)
|
||||
{
|
||||
if (color == Console_Normal) {
|
||||
fp << "\33[0m";
|
||||
return;
|
||||
}
|
||||
|
||||
switch (color & 0xff) {
|
||||
case Console_ForegroundBlack:
|
||||
fp << "\33[30m";
|
||||
break;
|
||||
case Console_ForegroundRed:
|
||||
fp << "\33[31m";
|
||||
break;
|
||||
case Console_ForegroundGreen:
|
||||
fp << "\33[32m";
|
||||
break;
|
||||
case Console_ForegroundYellow:
|
||||
fp << "\33[33m";
|
||||
break;
|
||||
case Console_ForegroundBlue:
|
||||
fp << "\33[34m";
|
||||
break;
|
||||
case Console_ForegroundMagenta:
|
||||
fp << "\33[35m";
|
||||
break;
|
||||
case Console_ForegroundCyan:
|
||||
fp << "\33[36m";
|
||||
break;
|
||||
case Console_ForegroundWhite:
|
||||
fp << "\33[37m";
|
||||
break;
|
||||
}
|
||||
|
||||
switch (color & 0xff00) {
|
||||
case Console_BackgroundBlack:
|
||||
fp << "\33[40m";
|
||||
break;
|
||||
case Console_BackgroundRed:
|
||||
fp << "\33[41m";
|
||||
break;
|
||||
case Console_BackgroundGreen:
|
||||
fp << "\33[42m";
|
||||
break;
|
||||
case Console_BackgroundYellow:
|
||||
fp << "\33[43m";
|
||||
break;
|
||||
case Console_BackgroundBlue:
|
||||
fp << "\33[44m";
|
||||
break;
|
||||
case Console_BackgroundMagenta:
|
||||
fp << "\33[45m";
|
||||
break;
|
||||
case Console_BackgroundCyan:
|
||||
fp << "\33[46m";
|
||||
break;
|
||||
case Console_BackgroundWhite:
|
||||
fp << "\33[47m";
|
||||
break;
|
||||
}
|
||||
|
||||
if (color & Console_Bold)
|
||||
fp << "\33[1m";
|
||||
}
|
||||
#else /* _WIN32 */
|
||||
void Console::SetWindowsConsoleColor(std::ostream& fp, int color)
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
|
||||
HANDLE hConsole;
|
||||
|
||||
if (&fp == &std::cout)
|
||||
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
else if (&fp == &std::cerr)
|
||||
hConsole = GetStdHandle(STD_ERROR_HANDLE);
|
||||
else
|
||||
return;
|
||||
|
||||
if (!GetConsoleScreenBufferInfo(hConsole, &consoleInfo))
|
||||
return;
|
||||
|
||||
WORD attrs = 0;
|
||||
|
||||
if (color == Console_Normal)
|
||||
attrs = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
|
||||
switch (color & 0xff) {
|
||||
case Console_ForegroundBlack:
|
||||
attrs |= 0;
|
||||
break;
|
||||
case Console_ForegroundRed:
|
||||
attrs |= FOREGROUND_RED;
|
||||
break;
|
||||
case Console_ForegroundGreen:
|
||||
attrs |= FOREGROUND_GREEN;
|
||||
break;
|
||||
case Console_ForegroundYellow:
|
||||
attrs |= FOREGROUND_RED | FOREGROUND_GREEN;
|
||||
break;
|
||||
case Console_ForegroundBlue:
|
||||
attrs |= FOREGROUND_BLUE;
|
||||
break;
|
||||
case Console_ForegroundMagenta:
|
||||
attrs |= FOREGROUND_RED | FOREGROUND_BLUE;
|
||||
break;
|
||||
case Console_ForegroundCyan:
|
||||
attrs |= FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
break;
|
||||
case Console_ForegroundWhite:
|
||||
attrs |= FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (color & 0xff00) {
|
||||
case Console_BackgroundBlack:
|
||||
attrs |= 0;
|
||||
break;
|
||||
case Console_BackgroundRed:
|
||||
attrs |= BACKGROUND_RED;
|
||||
break;
|
||||
case Console_BackgroundGreen:
|
||||
attrs |= BACKGROUND_GREEN;
|
||||
break;
|
||||
case Console_BackgroundYellow:
|
||||
attrs |= BACKGROUND_RED | BACKGROUND_GREEN;
|
||||
break;
|
||||
case Console_BackgroundBlue:
|
||||
attrs |= BACKGROUND_BLUE;
|
||||
break;
|
||||
case Console_BackgroundMagenta:
|
||||
attrs |= BACKGROUND_RED | BACKGROUND_BLUE;
|
||||
break;
|
||||
case Console_BackgroundCyan:
|
||||
attrs |= BACKGROUND_GREEN | BACKGROUND_BLUE;
|
||||
break;
|
||||
case Console_BackgroundWhite:
|
||||
attrs |= BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (color & Console_Bold)
|
||||
attrs |= FOREGROUND_INTENSITY;
|
||||
|
||||
SetConsoleTextAttribute(hConsole, attrs);
|
||||
}
|
||||
#endif /* _WIN32 */
|
103
lib/base/console.hpp
Normal file
103
lib/base/console.hpp
Normal file
@ -0,0 +1,103 @@
|
||||
/******************************************************************************
|
||||
* Icinga 2 *
|
||||
* Copyright (C) 2012-2014 Icinga Development Team (http://www.icinga.org) *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or *
|
||||
* modify it under the terms of the GNU General Public License *
|
||||
* as published by the Free Software Foundation; either version 2 *
|
||||
* of the License, or (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the Free Software Foundation *
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef CONSOLE_H
|
||||
#define CONSOLE_H
|
||||
|
||||
#include "base/i2-base.hpp"
|
||||
#include <ostream>
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
enum ConsoleColor
|
||||
{
|
||||
Console_Normal,
|
||||
|
||||
// bit 0-7: foreground
|
||||
Console_ForegroundBlack = 1,
|
||||
Console_ForegroundRed = 2,
|
||||
Console_ForegroundGreen = 3,
|
||||
Console_ForegroundYellow = 4,
|
||||
Console_ForegroundBlue = 5,
|
||||
Console_ForegroundMagenta = 6,
|
||||
Console_ForegroundCyan = 7,
|
||||
Console_ForegroundWhite = 8,
|
||||
|
||||
// bit 8-15: background
|
||||
Console_BackgroundBlack = 256,
|
||||
Console_BackgroundRed = 266,
|
||||
Console_BackgroundGreen = 267,
|
||||
Console_BackgroundYellow = 268,
|
||||
Console_BackgroundBlue = 269,
|
||||
Console_BackgroundMagenta = 270,
|
||||
Console_BackgroundCyan = 271,
|
||||
Console_BackgroundWhite = 272,
|
||||
|
||||
// bit 16-23: flags
|
||||
Console_Bold = 65536
|
||||
};
|
||||
|
||||
enum ConsoleType
|
||||
{
|
||||
Console_Dumb,
|
||||
#ifndef _WIN32
|
||||
Console_VT100,
|
||||
#else /* _WIN32 */
|
||||
Console_Windows,
|
||||
#endif /* _WIN32 */
|
||||
};
|
||||
|
||||
class I2_BASE_API ConsoleColorTag
|
||||
{
|
||||
public:
|
||||
ConsoleColorTag(int color);
|
||||
|
||||
friend I2_BASE_API std::ostream& operator<<(std::ostream& fp, const ConsoleColorTag& cct);
|
||||
|
||||
private:
|
||||
int m_Color;
|
||||
};
|
||||
|
||||
/**
|
||||
* Console utilities.
|
||||
*
|
||||
* @ingroup base
|
||||
*/
|
||||
class I2_BASE_API Console
|
||||
{
|
||||
public:
|
||||
static void DetectType(void);
|
||||
|
||||
static void SetType(std::ostream& fp, ConsoleType type);
|
||||
static ConsoleType GetType(std::ostream& fp);
|
||||
|
||||
#ifndef _WIN32
|
||||
static void PrintVT100ColorCode(std::ostream& fp, int color);
|
||||
#else /* _WIN32 */
|
||||
static void SetWindowsConsoleColor(std::ostream& fp, int color);
|
||||
#endif /* _WIN32 */
|
||||
|
||||
private:
|
||||
Console(void);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CONSOLE_H */
|
@ -106,11 +106,8 @@ void icinga::Log(LogSeverity severity, const String& facility,
|
||||
logger->ProcessLogEntry(entry);
|
||||
}
|
||||
|
||||
if (Logger::IsConsoleLogEnabled() && entry.Severity >= Logger::GetConsoleLogSeverity()) {
|
||||
static bool tty = StreamLogger::IsTty(std::cout);
|
||||
|
||||
StreamLogger::ProcessLogEntry(std::cout, tty, entry);
|
||||
}
|
||||
if (Logger::IsConsoleLogEnabled() && entry.Severity >= Logger::GetConsoleLogSeverity())
|
||||
StreamLogger::ProcessLogEntry(std::cout, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "base/streamlogger.hpp"
|
||||
#include "base/utility.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
#include "base/console.hpp"
|
||||
#include <iostream>
|
||||
|
||||
using namespace icinga;
|
||||
@ -78,7 +79,6 @@ void StreamLogger::BindStream(std::ostream *stream, bool ownsStream)
|
||||
|
||||
m_Stream = stream;
|
||||
m_OwnsStream = ownsStream;
|
||||
m_Tty = IsTty(*stream);
|
||||
|
||||
m_FlushLogTimer = make_shared<Timer>();
|
||||
m_FlushLogTimer->SetInterval(1);
|
||||
@ -90,10 +90,9 @@ void StreamLogger::BindStream(std::ostream *stream, bool ownsStream)
|
||||
* Processes a log entry and outputs it to a stream.
|
||||
*
|
||||
* @param stream The output stream.
|
||||
* @param tty Whether the output stream is a TTY.
|
||||
* @param entry The log entry.
|
||||
*/
|
||||
void StreamLogger::ProcessLogEntry(std::ostream& stream, bool tty, const LogEntry& entry)
|
||||
void StreamLogger::ProcessLogEntry(std::ostream& stream, const LogEntry& entry)
|
||||
{
|
||||
String timestamp = Utility::FormatDateTime("%Y-%m-%d %H:%M:%S %z", entry.Timestamp);
|
||||
|
||||
@ -101,35 +100,28 @@ void StreamLogger::ProcessLogEntry(std::ostream& stream, bool tty, const LogEntr
|
||||
|
||||
stream << "[" << timestamp << "] ";
|
||||
|
||||
if (tty) {
|
||||
switch (entry.Severity) {
|
||||
case LogNotice:
|
||||
stream << "\x1b[1;34m"; // blue
|
||||
break;
|
||||
case LogInformation:
|
||||
stream << "\x1b[1;32m"; // green
|
||||
break;
|
||||
case LogWarning:
|
||||
stream << "\x1b[1;33m"; // yellow;
|
||||
break;
|
||||
case LogCritical:
|
||||
stream << "\x1b[1;31m"; // red
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
ConsoleColor color;
|
||||
|
||||
switch (entry.Severity) {
|
||||
case LogNotice:
|
||||
color = Console_ForegroundBlue;
|
||||
break;
|
||||
case LogInformation:
|
||||
color = Console_ForegroundGreen;
|
||||
break;
|
||||
case LogWarning:
|
||||
color = Console_ForegroundYellow;
|
||||
break;
|
||||
case LogCritical:
|
||||
color = Console_ForegroundRed;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
stream << Logger::SeverityToString(entry.Severity);
|
||||
} catch (const std::exception&) {
|
||||
/* bail early */
|
||||
return;
|
||||
}
|
||||
|
||||
if (tty)
|
||||
stream << "\x1b[0m"; // clear colors
|
||||
|
||||
stream << ConsoleColorTag(color);
|
||||
stream << Logger::SeverityToString(entry.Severity);
|
||||
stream << ConsoleColorTag(Console_Normal);
|
||||
stream << "/" << entry.Facility << ": " << entry.Message << "\n";
|
||||
}
|
||||
|
||||
@ -140,25 +132,6 @@ void StreamLogger::ProcessLogEntry(std::ostream& stream, bool tty, const LogEntr
|
||||
*/
|
||||
void StreamLogger::ProcessLogEntry(const LogEntry& entry)
|
||||
{
|
||||
ProcessLogEntry(*m_Stream, m_Tty, entry);
|
||||
ProcessLogEntry(*m_Stream, entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the specified stream is a terminal.
|
||||
*
|
||||
* @param stream The stream.
|
||||
* @returns true if the stream is a terminal, false otherwise.
|
||||
*/
|
||||
bool StreamLogger::IsTty(std::ostream& stream)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
/* Eww... */
|
||||
if (&stream == &std::cout)
|
||||
return isatty(fileno(stdout));
|
||||
|
||||
if (&stream == &std::cerr)
|
||||
return isatty(fileno(stderr));
|
||||
#endif /*_ WIN32 */
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -44,8 +44,7 @@ public:
|
||||
|
||||
void BindStream(std::ostream *stream, bool ownsStream);
|
||||
|
||||
static void ProcessLogEntry(std::ostream& stream, bool tty, const LogEntry& entry);
|
||||
static bool IsTty(std::ostream& stream);
|
||||
static void ProcessLogEntry(std::ostream& stream, const LogEntry& entry);
|
||||
|
||||
protected:
|
||||
virtual void ProcessLogEntry(const LogEntry& entry);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "base/stdiostream.hpp"
|
||||
#include "base/debug.hpp"
|
||||
#include "base/objectlock.hpp"
|
||||
#include "base/console.hpp"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/algorithm/string/join.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
@ -144,8 +145,8 @@ void ObjectListCommand::PrintObject(std::ostream& fp, bool& first, const String&
|
||||
else
|
||||
fp << "Object '";
|
||||
|
||||
fp << "\x1b[1;34m" << internal_name << "\x1b[0m" << "'"; //blue
|
||||
fp << " of type '" << "\x1b[1;34m" << type << "\x1b[0m" << "':\n"; //blue
|
||||
fp << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << internal_name << ConsoleColorTag(Console_Normal) << "'";
|
||||
fp << " of type '" << ConsoleColorTag(Console_ForegroundBlue | Console_Bold) << type << ConsoleColorTag(Console_Normal) << "':\n";
|
||||
|
||||
PrintProperties(fp, properties, debug_hints, 2);
|
||||
|
||||
@ -166,7 +167,7 @@ void ObjectListCommand::PrintProperties(std::ostream& fp, const Dictionary::Ptr&
|
||||
Value val = kv.second;
|
||||
|
||||
/* key & value */
|
||||
fp << std::setw(indent) << " " << "* " << "\x1b[1;32m" << key << "\x1b[0m"; //green
|
||||
fp << std::setw(indent) << " " << "* " << ConsoleColorTag(Console_ForegroundGreen) << key << ConsoleColorTag(Console_Normal);
|
||||
|
||||
/* extract debug hints for key */
|
||||
Dictionary::Ptr debug_hints_fwd;
|
||||
@ -201,8 +202,8 @@ void ObjectListCommand::PrintHints(std::ostream& fp, const Dictionary::Ptr& debu
|
||||
|
||||
void ObjectListCommand::PrintHint(std::ostream& fp, const Array::Ptr& msg, int indent)
|
||||
{
|
||||
fp << std::setw(indent) << " " << "\x1b[1;36m" "% " << msg->Get(0) << " modified in '" << msg->Get(1) << "', lines "
|
||||
<< msg->Get(2) << ":" << msg->Get(3) << "-" << msg->Get(4) << ":" << msg->Get(5) << "\x1b[0m" "\n"; //cyan
|
||||
fp << std::setw(indent) << " " << ConsoleColorTag(Console_ForegroundCyan) << "% " << msg->Get(0) << " modified in '" << msg->Get(1) << "', lines "
|
||||
<< msg->Get(2) << ":" << msg->Get(3) << "-" << msg->Get(4) << ":" << msg->Get(5) << ConsoleColorTag(Console_Normal) << "\n";
|
||||
}
|
||||
|
||||
void ObjectListCommand::PrintTypeCounts(std::ostream& fp, const std::map<String, int>& type_count)
|
||||
|
@ -302,7 +302,10 @@ void ConfigItem::WriteObjectsFile(const String& filename)
|
||||
persistentItem->Set("type", item->GetType());
|
||||
persistentItem->Set("name", item->GetName());
|
||||
persistentItem->Set("abstract", item->IsAbstract());
|
||||
persistentItem->Set("properties", item->GetProperties());
|
||||
{
|
||||
ObjectLock olock(item);
|
||||
persistentItem->Set("properties", item->GetProperties());
|
||||
}
|
||||
persistentItem->Set("debug_hints", item->GetDebugHints());
|
||||
|
||||
String json = JsonSerialize(persistentItem);
|
||||
|
Loading…
x
Reference in New Issue
Block a user