feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run

- Base: wintercms/winter branch 1.2 (full framework)
- Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS
- Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication
- Partials: hero (offline-first), features, modes (offline/nube toggle),
  screenshots, pricing (3 planes), comparison, FAQ, CTA
- Plugin VivesPOS.Site with ContactForm
- Dockerfile: PHP 8.2 Apache, port 80, healthcheck
- Added winter/wn-pages, blog, sitemap, seo plugins
- Active theme set to vivespos
This commit is contained in:
2026-08-21 19:29:00 -06:00
commit 1f72193a64
3266 changed files with 531480 additions and 0 deletions

View File

@@ -0,0 +1,262 @@
<?php namespace System\Models;
use Exception;
use Illuminate\Support\Facades\App;
use Throwable;
use ReflectionClass;
use Winter\Storm\Database\Model;
use Winter\Storm\Support\Str;
/**
* Model for logging system errors and debug trace messages
*
* @package winter\wn-system-module
* @author Alexey Bobkov, Samuel Georges
*/
class EventLog extends Model
{
protected const EXCEPTION_LOG_VERSION = 2;
protected const EXCEPTION_SNIPPET_LINES = 12;
/**
* @var string The database table used by the model.
*/
protected $table = 'system_event_logs';
/**
* @var array List of attribute names which are json encoded and decoded from the database.
*/
protected $jsonable = ['details'];
/**
* Returns true if this logger should be used.
*/
public static function useLogging(): bool
{
return (
!defined('WINTER_NO_EVENT_LOGGING') &&
class_exists('Model') &&
Model::getConnectionResolver() &&
static::hasDatabaseTable() &&
LogSetting::get('log_events')
);
}
/**
* Creates a log record
*/
public static function add(string $message, string $level = 'info', ?array $details = null): static
{
$record = new static;
$record->message = $message;
$record->level = $level;
if ($details !== null) {
$record->details = (array) $details;
}
try {
$record->save();
}
catch (Exception $ex) {
}
return $record;
}
/**
* Creates an exception log record
*/
public static function addException(Throwable $throwable, string $level = 'error'): static
{
$record = new static;
$record->message = $throwable->getMessage();
$record->level = $level;
$record->details = $record->getDetails($throwable);
try {
$record->save();
}
catch (Exception $ex) {
}
return $record;
}
/**
* Beautify level value.
*/
public function getLevelAttribute(string $level): string
{
return ucfirst($level);
}
/**
* Creates a shorter version of the message attribute,
* extracts the exception message or limits by 100 characters.
*/
public function getSummaryAttribute(): string
{
if (preg_match("/with message '(.+)' in/", $this->message, $match)) {
return $match[1];
}
// Get first line of message
preg_match('/^([^\n\r]+)/m', $this->message, $matches);
return Str::limit($matches[1] ?? '', 500);
}
/**
* Constructs the details array for logging
*/
public function getDetails(Throwable $throwable): array
{
return [
'logVersion' => static::EXCEPTION_LOG_VERSION,
'exception' => $this->exceptionToArray($throwable),
'environment' => $this->getEnviromentInfo(),
];
}
/**
* Convert a throwable into an array of data for logging
*/
protected function exceptionToArray(Throwable $throwable): array
{
return [
'type' => $throwable::class,
'message' => $throwable->getMessage(),
'file' => $throwable->getFile(),
'line' => $throwable->getLine(),
'snippet' => $this->getSnippet($throwable->getFile(), $throwable->getLine()),
'trace' => $this->exceptionTraceToArray($throwable->getTrace()),
'stringTrace' => $throwable->getTraceAsString(),
'code' => $throwable->getCode(),
'previous' => $throwable->getPrevious()
? $this->exceptionToArray($throwable->getPrevious())
: null,
];
}
/**
* Generate an array trace with extra data not provided by the default trace
*
* @throws \ReflectionException
*/
protected function exceptionTraceToArray(array $trace): array
{
foreach ($trace as $index => $frame) {
if (!isset($frame['file']) && isset($frame['class'])) {
$ref = new ReflectionClass($frame['class']);
$frame['file'] = $ref->getFileName();
if (!isset($frame['line']) && isset($frame['function']) && !str_contains($frame['function'], '{')) {
foreach (file($frame['file']) as $line => $text) {
if (preg_match(sprintf('/function\s.*%s/', $frame['function']), $text)) {
$frame['line'] = $line + 1;
break;
}
}
}
}
$trace[$index] = [
'file' => $frame['file'] ?? null,
'line' => $frame['line'] ?? null,
'function' => $frame['function'] ?? null,
'class' => $frame['class'] ?? null,
'type' => $frame['type'] ?? null,
'snippet' => !empty($frame['file']) && !empty($frame['line'])
? $this->getSnippet($frame['file'], $frame['line'])
: '',
'in_app' => ($frame['file'] ?? null) ? $this->isInAppError($frame['file']) : false,
'arguments' => array_map(function ($arg) {
if (is_numeric($arg)) {
return $arg;
}
if (is_string($arg)) {
return "'$arg'";
}
if (is_null($arg)) {
return 'null';
}
if (is_bool($arg)) {
return $arg ? 'true' : 'false';
}
if (is_array($arg)) {
return 'Array';
}
if (is_object($arg)) {
return get_class($arg);
}
if (is_resource($arg)) {
return 'Resource';
}
}, $frame['args'] ?? []),
];
}
return $trace;
}
/**
* Get the code snippet referenced in a trace
*/
protected function getSnippet(string $file, int $line): array
{
if (str_contains($file, ': eval()\'d code')) {
return [];
}
$lines = file($file);
if (count($lines) < static::EXCEPTION_SNIPPET_LINES) {
return $lines;
}
return array_slice(
$lines,
$line - (static::EXCEPTION_SNIPPET_LINES / 2),
static::EXCEPTION_SNIPPET_LINES,
true
);
}
/**
* Get environment details to record with the exception
*/
protected function getEnviromentInfo(): array
{
if (app()->runningInConsole()) {
return [
'context' => 'CLI',
'testing' => app()->runningUnitTests(),
'env' => app()->environment(),
];
}
return [
'context' => 'Web',
'backend' => method_exists(app(), 'runningInBackend') ? app()->runningInBackend() : false,
'testing' => app()->runningUnitTests(),
'url' => app('url')->current(),
'method' => app('request')->method(),
'env' => app()->environment(),
'ip' => app('request')->ip(),
'userAgent' => app('request')->header('User-Agent'),
];
}
/**
* Helper to work out if a file should be considered "In App" or not
*/
protected function isInAppError(string $file): bool
{
if (basename($file) === 'index.php' || basename($file) === 'artisan') {
return false;
}
return !Str::startsWith($file, base_path('vendor')) && !Str::startsWith($file, base_path('modules'));
}
}