feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
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:
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendUsers extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('backend_users', function (Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->string('first_name')->nullable();
|
||||
$table->string('last_name')->nullable();
|
||||
$table->string('login')->unique('login_unique')->index('login_index');
|
||||
$table->string('email')->unique('email_unique');
|
||||
$table->string('password');
|
||||
$table->string('activation_code')->nullable()->index('act_code_index');
|
||||
$table->string('persist_code')->nullable();
|
||||
$table->string('reset_password_code')->nullable()->index('reset_code_index');
|
||||
$table->text('permissions')->nullable();
|
||||
$table->boolean('is_activated')->default(0);
|
||||
$table->integer('role_id')->unsigned()->nullable()->index('admin_role_index');
|
||||
$table->timestamp('activated_at')->nullable();
|
||||
$table->timestamp('last_login')->nullable();
|
||||
$table->timestamps();
|
||||
$table->timestamp('deleted_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('backend_users');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendUserGroups extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('backend_user_groups', function (Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->string('name')->unique('name_unique');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('backend_user_groups');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendUsersGroups extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('backend_users_groups', function (Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->integer('user_id')->unsigned();
|
||||
$table->integer('user_group_id')->unsigned();
|
||||
$table->primary(['user_id', 'user_group_id'], 'user_group');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('backend_users_groups');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendUserThrottle extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('backend_user_throttle', function (Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->integer('user_id')->unsigned()->nullable()->index();
|
||||
$table->string('ip_address')->nullable()->index();
|
||||
$table->integer('attempts')->default(0);
|
||||
$table->timestamp('last_attempt_at')->nullable();
|
||||
$table->boolean('is_suspended')->default(0);
|
||||
$table->timestamp('suspended_at')->nullable();
|
||||
$table->boolean('is_banned')->default(0);
|
||||
$table->timestamp('banned_at')->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('backend_user_throttle');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendUserPreferences extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('backend_user_preferences', function (Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->integer('user_id')->unsigned();
|
||||
$table->string('namespace', 100);
|
||||
$table->string('group', 50);
|
||||
$table->string('item', 150);
|
||||
$table->text('value')->nullable();
|
||||
$table->index(['user_id', 'namespace', 'group', 'item'], 'user_item_index');
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('backend_user_preferences');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendAccessLog extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('backend_access_log', function (Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->integer('user_id')->unsigned();
|
||||
$table->string('ip_address')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('backend_access_log');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendAddDescriptionField extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::table('backend_user_groups', function (Blueprint $table) {
|
||||
$table->string('code')->nullable()->index('code_index');
|
||||
$table->text('description')->nullable();
|
||||
$table->boolean('is_new_user_default')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
// Schema::table('backend_user_groups', function (Blueprint $table) {
|
||||
// $table->dropColumn('code');
|
||||
// $table->dropColumn('description');
|
||||
// $table->dropColumn('is_new_user_default');
|
||||
// });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
use Backend\Models\User as AdminModel;
|
||||
|
||||
class DbBackendAddSuperuserFlag extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::table('backend_users', function (Blueprint $table) {
|
||||
$table->boolean('is_superuser')->default(false);
|
||||
});
|
||||
|
||||
AdminModel::all()->each(function ($user) {
|
||||
if ($user->hasPermission('superuser')) {
|
||||
$user->is_superuser = true;
|
||||
$user->save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
// Schema::table('backend_users', function (Blueprint $table) {
|
||||
// $table->dropColumn('is_superuser');
|
||||
// });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
/**
|
||||
* This migration addresses a MySQL specific issue around STRICT MODE.
|
||||
* In past versions, Laravel would give timestamps a bad default value
|
||||
* of "0" considered invalid by MySQL. Strict mode is disabled and the
|
||||
* the timestamps are patched up. Credit for this work: Dave Shoreman.
|
||||
*/
|
||||
class DbBackendTimestampFix extends Migration
|
||||
{
|
||||
protected $backendTables = [
|
||||
'backend_users',
|
||||
'backend_user_groups',
|
||||
'backend_access_log',
|
||||
];
|
||||
|
||||
public function up()
|
||||
{
|
||||
DbDongle::disableStrictMode();
|
||||
|
||||
foreach ($this->backendTables as $table) {
|
||||
DbDongle::convertTimestamps($table);
|
||||
}
|
||||
|
||||
// Use this opportunity to reset backend preferences for stable
|
||||
Db::table('backend_user_preferences')
|
||||
->where('namespace', 'backend')
|
||||
->where('group', 'backend')
|
||||
->where('item', 'preferences')
|
||||
->delete();
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
use Backend\Models\UserRole;
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendUserRoles extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
Schema::create('backend_user_roles', function (Blueprint $table) {
|
||||
$table->engine = 'InnoDB';
|
||||
$table->increments('id');
|
||||
$table->string('name')->unique('role_unique');
|
||||
$table->string('code')->nullable()->index('role_code_index');
|
||||
$table->text('description')->nullable();
|
||||
$table->text('permissions')->nullable();
|
||||
$table->boolean('is_system')->default(0);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
// This detects older builds and performs a migration to include
|
||||
// the new role system. This column will exist for new installs
|
||||
// so this heavy migration process does not need to execute.
|
||||
$this->migratePreviousBuild();
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('backend_user_roles');
|
||||
}
|
||||
|
||||
protected function migratePreviousBuild()
|
||||
{
|
||||
// Role not found in the users table, perform a complete migration.
|
||||
// Merging group permissions with the user and assigning the user
|
||||
// with the first available role.
|
||||
if (!Schema::hasColumn('backend_users', 'role_id')) {
|
||||
Schema::table('backend_users', function (Blueprint $table) {
|
||||
$table->integer('role_id')->unsigned()->nullable()->index('admin_role_index');
|
||||
});
|
||||
|
||||
$this->createSystemUserRoles();
|
||||
$this->migratePermissionsFromGroupsToRoles();
|
||||
}
|
||||
|
||||
// Drop permissions column on groups table as it is no longer needed.
|
||||
if (Schema::hasColumn('backend_user_groups', 'permissions')) {
|
||||
Schema::table('backend_user_groups', function (Blueprint $table) {
|
||||
$table->dropColumn('permissions');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected function createSystemUserRoles()
|
||||
{
|
||||
Db::table('backend_user_roles')->insert([
|
||||
'name' => 'Publisher',
|
||||
'code' => UserRole::CODE_PUBLISHER,
|
||||
'description' => 'Site editor with access to publishing tools.',
|
||||
]);
|
||||
|
||||
Db::table('backend_user_roles')->insert([
|
||||
'name' => 'Developer',
|
||||
'code' => UserRole::CODE_DEVELOPER,
|
||||
'description' => 'Site administrator with access to developer tools.',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function migratePermissionsFromGroupsToRoles()
|
||||
{
|
||||
$groups = Db::table('backend_user_groups')->get();
|
||||
$roles = [];
|
||||
$permissions = [];
|
||||
|
||||
/*
|
||||
* Carbon copy groups to roles
|
||||
*/
|
||||
foreach ($groups as $group) {
|
||||
if (!isset($group->name) || !$group->name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$roles[$group->id] = Db::table('backend_user_roles')->insertGetId([
|
||||
'name' => $group->name,
|
||||
'description' => $group->description,
|
||||
'permissions' => $group->permissions ?? null
|
||||
]);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
}
|
||||
|
||||
$permissions[$group->id] = $group->permissions ?? null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Assign a user with the first available role
|
||||
*/
|
||||
$found = [];
|
||||
$joins = Db::table('backend_users_groups')->get();
|
||||
|
||||
foreach ($joins as $join) {
|
||||
if (!$roleId = array_get($roles, $join->user_group_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$userId = $join->user_id;
|
||||
|
||||
if (!isset($found[$userId])) {
|
||||
Db::table('backend_users')->where('id', $userId)->update([
|
||||
'role_id' => $roleId
|
||||
]);
|
||||
}
|
||||
|
||||
$found[$userId][] = $join->user_group_id;
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge group permissions in to user
|
||||
*/
|
||||
foreach ($found as $userId => $groups) {
|
||||
$userPerms = [];
|
||||
|
||||
foreach ($groups as $groupId) {
|
||||
if (!$permString = array_get($permissions, $groupId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$perms = json_decode($permString, true);
|
||||
$userPerms = array_merge($userPerms, $perms);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
}
|
||||
}
|
||||
|
||||
if (count($userPerms) > 0) {
|
||||
$this->splicePermissionsForUser($userId, $userPerms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function splicePermissionsForUser($userId, $permissions)
|
||||
{
|
||||
/*
|
||||
* Look up user and splice the provided permissions in
|
||||
*/
|
||||
$user = Db::table('backend_users')->where('id', $userId)->first();
|
||||
if (!$user) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$currentPerms = $user->permissions ? json_decode($user->permissions, true) : [];
|
||||
$newPerms = array_merge($permissions, $currentPerms);
|
||||
|
||||
Db::table('backend_users')->where('id', $userId)->update([
|
||||
'permissions' => json_encode($newPerms)
|
||||
]);
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendAddDeletedAt extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
if (!Schema::hasColumn('backend_users', 'deleted_at')) {
|
||||
Schema::table('backend_users', function (Blueprint $table) {
|
||||
$table->timestamp('deleted_at')->nullable()->after('updated_at');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
if (Schema::hasColumn('backend_users', 'deleted_at')) {
|
||||
Schema::table('backend_users', function (Blueprint $table) {
|
||||
$table->dropColumn('deleted_at');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendAddUserMetadata extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
if (!Schema::hasColumn('backend_users', 'metadata')) {
|
||||
Schema::table('backend_users', function (Blueprint $table) {
|
||||
$table->mediumText('metadata')->nullable()->after('permissions');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
if (Schema::hasColumn('backend_users', 'metadata')) {
|
||||
Schema::table('backend_users', function (Blueprint $table) {
|
||||
$table->dropColumn('metadata');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
use Winter\Storm\Database\Schema\Blueprint;
|
||||
use Winter\Storm\Database\Updates\Migration;
|
||||
|
||||
class DbBackendAddUsersGroupsDeleteAt extends Migration
|
||||
{
|
||||
public function up()
|
||||
{
|
||||
if (!Schema::hasColumn('backend_users_groups', 'deleted_at')) {
|
||||
Schema::table('backend_users_groups', function (Blueprint $table) {
|
||||
$table->timestamp('deleted_at')->nullable()->after('user_group_id');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public function down()
|
||||
{
|
||||
if (Schema::hasColumn('backend_users_groups', 'deleted_at')) {
|
||||
Schema::table('backend_users_groups', function (Blueprint $table) {
|
||||
$table->dropColumn('deleted_at');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
33
modules/backend/database/seeds/DatabaseSeeder.php
Normal file
33
modules/backend/database/seeds/DatabaseSeeder.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php namespace Backend\Database\Seeds;
|
||||
|
||||
use Str;
|
||||
use Seeder;
|
||||
use Eloquent;
|
||||
use Backend\Database\Seeds\SeedSetupAdmin;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* Run the database seeds.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
$shouldRandomizePassword = SeedSetupAdmin::$password === '';
|
||||
$adminPassword = $shouldRandomizePassword ? Str::random(22) : SeedSetupAdmin::$password;
|
||||
|
||||
Eloquent::unguarded(function () use ($adminPassword) {
|
||||
// Generate a random password for the seeded admin account
|
||||
$adminSeeder = new \Backend\Database\Seeds\SeedSetupAdmin;
|
||||
$adminSeeder->setDefaults([
|
||||
'password' => $adminPassword
|
||||
]);
|
||||
$this->call($adminSeeder);
|
||||
});
|
||||
|
||||
return ($shouldRandomizePassword)
|
||||
? 'The following password has been automatically generated for the "admin" account: <fg=yellow;options=bold>' . $adminPassword . '</>'
|
||||
: '';
|
||||
}
|
||||
}
|
||||
62
modules/backend/database/seeds/SeedSetupAdmin.php
Normal file
62
modules/backend/database/seeds/SeedSetupAdmin.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php namespace Backend\Database\Seeds;
|
||||
|
||||
use Seeder;
|
||||
use Backend\Models\User;
|
||||
use Backend\Models\UserRole;
|
||||
use Backend\Models\UserGroup;
|
||||
|
||||
class SeedSetupAdmin extends Seeder
|
||||
{
|
||||
public static $email = 'admin@example.com';
|
||||
public static $login = 'admin';
|
||||
public static $password = '';
|
||||
public static $firstName = 'Admin';
|
||||
public static $lastName = 'Person';
|
||||
|
||||
public function setDefaults($values)
|
||||
{
|
||||
if (!is_array($values)) {
|
||||
return;
|
||||
}
|
||||
foreach ($values as $attribute => $value) {
|
||||
static::$$attribute = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
UserRole::create([
|
||||
'name' => 'Publisher',
|
||||
'code' => UserRole::CODE_PUBLISHER,
|
||||
'description' => 'Site editor with access to publishing tools.',
|
||||
]);
|
||||
|
||||
$role = UserRole::create([
|
||||
'name' => 'Developer',
|
||||
'code' => UserRole::CODE_DEVELOPER,
|
||||
'description' => 'Site administrator with access to developer tools.',
|
||||
]);
|
||||
|
||||
$group = UserGroup::create([
|
||||
'name' => 'Owners',
|
||||
'code' => UserGroup::CODE_OWNERS,
|
||||
'description' => 'Default group for website owners.',
|
||||
'is_new_user_default' => false
|
||||
]);
|
||||
|
||||
$user = User::create([
|
||||
'email' => static::$email,
|
||||
'login' => static::$login,
|
||||
'password' => static::$password,
|
||||
'password_confirmation' => static::$password,
|
||||
'first_name' => static::$firstName,
|
||||
'last_name' => static::$lastName,
|
||||
'permissions' => [],
|
||||
'is_superuser' => true,
|
||||
'is_activated' => true,
|
||||
'role_id' => $role->id
|
||||
]);
|
||||
|
||||
$user->addGroup($group);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user