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,43 @@
<?php namespace Backend\Controllers;
use Backend;
use BackendMenu;
use Backend\Classes\Controller;
use System\Classes\SettingsManager;
/**
* Access Logs controller
*
* @package winter\wn-system-module
* @author Alexey Bobkov, Samuel Georges
*/
class AccessLogs extends Controller
{
/**
* @var array Extensions implemented by this controller.
*/
public $implement = [
\Backend\Behaviors\ListController::class,
];
/**
* @var array Permissions required to view this page.
*/
public $requiredPermissions = ['system.access_logs'];
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
BackendMenu::setContext('Winter.System', 'system', 'settings');
SettingsManager::setContext('Winter.Backend', 'access_logs');
}
public function index_onRefresh()
{
return $this->listRefresh();
}
}

View File

@@ -0,0 +1,241 @@
<?php namespace Backend\Controllers;
use ApplicationException;
use Backend;
use BackendAuth;
use Backend\Classes\Controller;
use Config;
use Exception;
use Flash;
use Mail;
use Request;
use ValidationException;
use Validator;
use Winter\Storm\Foundation\Http\Middleware\CheckForTrustedHost;
/**
* Authentication controller
*
* @package winter\wn-backend-module
* @author Alexey Bobkov, Samuel Georges
*
*/
class Auth extends Controller
{
/**
* @var array Public controller actions
*/
protected $publicActions = ['index', 'signin', 'signout', 'restore', 'reset'];
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->layout = 'auth';
}
/**
* Default route, redirects to signin.
*/
public function index()
{
return Backend::redirect('backend/auth/signin');
}
/**
* Displays the log in page.
*/
public function signin()
{
if (BackendAuth::user()) {
return Backend::redirect('backend');
}
$this->bodyClass = 'signin';
// Clear Cache and any previous data to fix invalid security token issue
$this->setResponseHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
try {
if (post('postback')) {
return $this->signin_onSubmit();
}
$this->bodyClass .= ' preload';
} catch (Exception $ex) {
Flash::error($ex->getMessage());
}
}
public function signin_onSubmit()
{
$rules = [
'login' => 'required|between:2,255',
'password' => 'required|between:4,255'
];
$validation = Validator::make(post(), $rules);
if ($validation->fails()) {
throw new ValidationException($validation);
}
if (is_null($remember = Config::get('cms.backendForceRemember', true))) {
$remember = (bool) post('remember');
}
// Authenticate user
$user = BackendAuth::authenticate([
'login' => post('login'),
'password' => post('password')
], $remember);
// Redirect to the intended page after successful sign in
return Backend::redirectIntended('backend');
}
/**
* Logs out a backend user.
*/
public function signout()
{
if (BackendAuth::isImpersonator()) {
BackendAuth::stopImpersonate();
} else {
BackendAuth::logout();
}
// Add HTTP Header 'Clear Site Data' to purge all sensitive data upon signout
if (Request::secure()) {
$this->setResponseHeader('Clear-Site-Data', 'cache, cookies, storage, executionContexts');
}
return Backend::redirect('backend');
}
/**
* Request a password reset verification code.
*/
public function restore()
{
$this->bodyClass = 'restore';
try {
if (post('postback')) {
return $this->restore_onSubmit();
}
} catch (Exception $ex) {
Flash::error($ex->getMessage());
}
}
/**
* Submits the restore form.
*/
public function restore_onSubmit()
{
// Force Trusted Host verification on password reset link generation
// regardless of config to protect against host header poisoning
$trustedHosts = Config::get('app.trustedHosts', false);
if ($trustedHosts === false) {
$hosts = CheckForTrustedHost::processTrustedHosts(true);
if (count($hosts)) {
Request::setTrustedHosts($hosts);
// Trigger the host validation logic
Request::getHost();
}
}
$rules = [
'login' => 'required|between:2,255'
];
$validation = Validator::make(post(), $rules);
if ($validation->fails()) {
throw new ValidationException($validation);
}
$user = BackendAuth::findUserByLogin(post('login'));
if ($user) {
$code = $user->getResetPasswordCode();
$link = Backend::url('backend/auth/reset/' . $user->id . '/' . $code);
$data = [
'name' => $user->full_name,
'link' => $link,
];
Mail::send('backend::mail.restore', $data, function ($message) use ($user) {
$message->to($user->email, $user->full_name)->subject(trans('backend::lang.account.password_reset'));
});
}
Flash::success(trans('backend::lang.account.restore_success'));
return Backend::redirect('backend/auth/signin');
}
/**
* Reset backend user password using verification code.
*/
public function reset($userId = null, $code = null)
{
$this->bodyClass = 'reset';
try {
if (post('postback')) {
return $this->reset_onSubmit();
}
if (!$userId || !$code) {
throw new ApplicationException(trans('backend::lang.account.reset_error'));
}
} catch (Exception $ex) {
Flash::error($ex->getMessage());
}
$this->vars['code'] = $code;
$this->vars['id'] = $userId;
}
/**
* Submits the reset form.
*/
public function reset_onSubmit()
{
if (!post('id') || !post('code')) {
throw new ApplicationException(trans('backend::lang.account.reset_error'));
}
$rules = [
'password' => 'required|between:4,255'
];
$validation = Validator::make(post(), $rules);
if ($validation->fails()) {
throw new ValidationException($validation);
}
$code = post('code');
$user = BackendAuth::findUserById(post('id'));
if (!$user || !$user->checkResetPasswordCode($code)) {
throw new ApplicationException(trans('backend::lang.account.reset_error'));
}
if (!$user->attemptResetPassword($code, post('password'))) {
throw new ApplicationException(trans('backend::lang.account.reset_fail'));
}
$user->clearResetPassword();
Flash::success(trans('backend::lang.account.reset_success'));
return Backend::redirect('backend/auth/signin');
}
}

View File

@@ -0,0 +1,196 @@
<?php namespace Backend\Controllers;
use View;
use Cache;
use Config;
use Backend;
use Response;
use System\Models\File as FileModel;
use Backend\Classes\Controller;
use ApplicationException;
use Exception;
use RuntimeException;
/**
* Backend files controller
*
* Used for delivering protected system files, and generating URLs
* for accessing them.
*
* @package winter\wn-backend-module
* @author Alexey Bobkov, Samuel Georges
*
*/
class Files extends Controller
{
/**
* Output file, or fall back on the 404 page
*/
public function get($code = null)
{
try {
return $this->findFileObject($code)->output('inline', true);
}
catch (Exception $ex) {
}
return Response::make(View::make('backend::404'), 404);
}
/**
* Output thumbnail, or fall back on the 404 page
*/
public function thumb($code = null, $width = 100, $height = 100, $mode = 'auto', $extension = 'auto')
{
try {
return $this->findFileObject($code)->outputThumb(
$width,
$height,
compact('mode', 'extension'),
true
);
}
catch (Exception $ex) {
}
return Response::make(View::make('backend::404'), 404);
}
/**
* Attempt to return a redirect to a temporary URL to the asset instead of streaming the asset - if supported
*
* @param System|Models\File $file
* @param string|null $path Optional, defaults to the getDiskPath() of the file
* @return string|null
*/
protected static function getTemporaryUrl($file, $path = null)
{
// Get the disk used
$disk = $file->getDisk();
if (empty($path)) {
$path = $file->getDiskPath();
}
// Check to see if the URL has already been generated
$pathKey = 'backend.file:' . $path;
$url = Cache::get($pathKey, null);
if (is_null($url) && $disk->exists($path)) {
$expires = now()->addSeconds(Config::get('cms.storage.uploads.temporaryUrlTTL', 3600));
$url = Cache::remember($pathKey, $expires, function () use ($disk, $path, $expires) {
// Attempt to generate a temporary URL, if a RuntimeException occurs it's "probably"
// because the driver doesn't support that method
try {
return $disk->temporaryUrl($path, $expires);
} catch (RuntimeException $ex) {
return false;
}
});
}
// Limit the return types to strings or null
if (!is_string($url) || empty($url)) {
$url = null;
}
return $url;
}
/**
* Returns the URL for downloading a system file.
* @param $file System\Models\File
* @return string
*/
public static function getDownloadUrl($file)
{
$url = static::getTemporaryUrl($file);
if (!empty($url)) {
return $url;
} else {
return Backend::url('backend/files/get/' . self::getUniqueCode($file));
}
}
/**
* Returns the URL for downloading a system file.
* @param $file System\Models\File
* @param $width int
* @param $height int
* @param $options array
* @return string
*/
public static function getThumbUrl($file, $width, $height, $options)
{
$url = static::getTemporaryUrl($file, $file->getDiskPath($file->getThumbFilename($width, $height, $options)));
if (!empty($url)) {
return $url;
} else {
return Backend::url('backend/files/thumb/' . self::getUniqueCode($file)) . '/' . $width . '/' . $height . '/' . $options['mode'] . '/' . $options['extension'];
}
}
/**
* Returns a unique code used for masking the file identifier.
* @param $file System\Models\File
* @return string
*/
public static function getUniqueCode($file)
{
if (!$file) {
return null;
}
$hash = md5($file->file_name . '!' . $file->disk_name);
return base64_encode($file->id . '!' . $hash);
}
/**
* Locates a file model based on the unique code.
* @param $code string
* @return System\Models\File
*/
protected function findFileObject($code)
{
if (!$code) {
throw new ApplicationException('Missing code');
}
$parts = explode('!', base64_decode($code));
if (count($parts) < 2) {
throw new ApplicationException('Invalid code');
}
list($id, $hash) = $parts;
if (!$file = FileModel::find((int) $id)) {
throw new ApplicationException('Unable to find file');
}
/**
* Ensure that the file model utilized for this request is
* the one specified in the relationship configuration
*/
if ($file->attachment) {
$fileModel = $file->attachment->{$file->field}()->getRelated();
/**
* Only attempt to get file model through its assigned class
* when the assigned class differs from the default one that
* the file has already been loaded from
*/
if (get_class($file) !== get_class($fileModel)) {
$file = $fileModel->find($file->id);
}
}
$verifyCode = self::getUniqueCode($file);
if ($code != $verifyCode) {
throw new ApplicationException('Invalid hash');
}
return $file;
}
}

View File

@@ -0,0 +1,81 @@
<?php namespace Backend\Controllers;
use Backend;
use Redirect;
use BackendMenu;
use Backend\Classes\Controller;
use Backend\Widgets\ReportContainer;
/**
* Dashboard controller
*
* @package winter\wn-backend-module
* @author Alexey Bobkov, Samuel Georges
*
*/
class Index extends Controller
{
use \Backend\Traits\InspectableContainer;
/**
* @var array Permissions required to view this page.
* @see checkPermissionRedirect()
*/
public $requiredPermissions = [];
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
BackendMenu::setContextOwner('Winter.Backend');
$this->addCss('/modules/backend/assets/css/dashboard/dashboard.css', 'core');
}
public function index()
{
if ($redirect = $this->checkPermissionRedirect()) {
return $redirect;
}
$this->initReportContainer();
$this->pageTitle = 'backend::lang.dashboard.menu_label';
BackendMenu::setContextMainMenu('dashboard');
}
public function index_onInitReportContainer()
{
$this->initReportContainer();
return ['#dashReportContainer' => $this->widget->reportContainer->render()];
}
/**
* Prepare the report widget used by the dashboard
* @param Model $model
* @return void
*/
protected function initReportContainer()
{
new ReportContainer($this, 'config_dashboard.yaml');
}
/**
* Custom permissions check that will redirect to the next
* available menu item, if permission to this page is denied.
*/
protected function checkPermissionRedirect()
{
if (!$this->user->hasAccess('backend.access_dashboard')) {
if ($first = array_first(BackendMenu::listMainMenuItems())) {
return Redirect::intended($first->url);
}
return Backend::redirect('backend/myaccount');
}
}
}

View File

@@ -0,0 +1,38 @@
<?php namespace Backend\Controllers;
use BackendMenu;
use Backend\Classes\Controller;
use Backend\Widgets\MediaManager;
/**
* Backend Media Manager
*
* @package winter\wn-backend-module
* @author Alexey Bobkov, Samuel Georges
*/
class Media extends Controller
{
/**
* @var array Permissions required to view this page.
*/
public $requiredPermissions = ['media.*'];
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
BackendMenu::setContext('Winter.Backend', 'media', true);
$this->pageTitle = 'backend::lang.media.menu_label';
$manager = new MediaManager($this, 'manager');
$manager->bindToController();
}
public function index()
{
$this->bodyClass = 'compact-container';
}
}

View File

@@ -0,0 +1,92 @@
<?php
namespace Backend\Controllers;
use Backend\Behaviors\FormController;
use Backend\Classes\Controller;
use Backend\Facades\BackendAuth;
use Backend\Facades\BackendMenu;
use System\Classes\SettingsManager;
use Winter\Storm\Database\Builder;
/**
* My Account controller
*
* Allows any authenticated backend user to manage their own account settings.
* Isolated from the Users controller to prevent privilege escalation via
* handler dispatch on a controller with degraded permissions.
*
* @package winter\wn-backend-module
* @author Winter CMS
*/
class MyAccount extends Controller
{
/**
* @var array Extensions implemented by this controller.
*/
public $implement = [
FormController::class,
];
/**
* @var array methods that are blocked from being called as actions
*/
protected $guarded = ['create', 'update', 'preview'];
/**
* @var array Permissions required to view this page.
* Empty array any logged-in user can access their own account.
*/
public $requiredPermissions = [];
/**
* @var string HTML body tag class
*/
public $bodyClass = 'compact-container';
public $formLayout = 'sidebar';
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
BackendMenu::setContext('Winter.System', 'system', 'users');
SettingsManager::setContext('Winter.Backend', 'myaccount');
}
public function formExtendQuery(Builder $query): void
{
$query->whereKey($this->user->getKey());
}
/**
* My Account page
*/
public function index()
{
$this->pageTitle = 'backend::lang.myaccount.menu_label';
return $this->asExtension('FormController')->update($this->user->id, 'myaccount');
}
/**
* Save handler for the My Account form
*/
public function index_onSave()
{
$result = $this->asExtension('FormController')->update_onSave($this->user->id, 'myaccount');
/*
* If the password or login name has been updated, reauthenticate the user
*/
$loginChanged = $this->user->login != post('User[login]');
$passwordChanged = strlen(post('User[password]'));
if ($loginChanged || $passwordChanged) {
BackendAuth::login($this->user->reload(), true);
}
return $result;
}
}

View File

@@ -0,0 +1,79 @@
<?php namespace Backend\Controllers;
use Backend\Classes\Controller;
use Backend\Facades\Backend;
use Backend\Facades\BackendMenu;
use Backend\Models\Preference as PreferenceModel;
use Illuminate\Support\Facades\Lang;
use System\Classes\SettingsManager;
use Winter\Storm\Exception\ApplicationException;
use Winter\Storm\Support\Facades\File;
use Winter\Storm\Support\Facades\Flash;
/**
* Editor Settings controller
*
* @package winter\wn-backend-module
* @author Alexey Bobkov, Samuel Georges
*
*/
class Preferences extends Controller
{
public $implement = [
\Backend\Behaviors\FormController::class,
];
/**
* @var array Permissions required to view this page.
*/
public $requiredPermissions = ['backend.manage_preferences'];
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->addJs('/modules/backend/assets/js/preferences/preferences.js', 'core');
BackendMenu::setContext('Winter.System', 'system', 'mysettings');
SettingsManager::setContext('Winter.Backend', 'preferences');
}
public function index()
{
$this->pageTitle = 'backend::lang.backend_preferences.menu_label';
$this->asExtension('FormController')->update();
}
/**
* Remove the code editor tab if there is no permission.
*/
public function formExtendFields($form)
{
if (!$this->user->hasAccess('backend.manage_own_editor')) {
$form->removeTab('backend::lang.backend_preferences.code_editor');
}
}
public function index_onSave()
{
return $this->asExtension('FormController')->update_onSave();
}
public function index_onResetDefault()
{
$model = $this->formFindModelObject();
$model->resetDefault();
Flash::success(Lang::get('backend::lang.form.reset_success'));
return Backend::redirect('backend/preferences');
}
public function formFindModelObject()
{
return PreferenceModel::instance();
}
}

View File

@@ -0,0 +1,39 @@
<?php namespace Backend\Controllers;
use BackendMenu;
use Backend\Classes\Controller;
use System\Classes\SettingsManager;
/**
* Backend user groups controller
*
* @package winter\wn-backend-module
* @author Alexey Bobkov, Samuel Georges
*
*/
class UserGroups extends Controller
{
/**
* @var array Extensions implemented by this controller.
*/
public $implement = [
\Backend\Behaviors\FormController::class,
\Backend\Behaviors\ListController::class,
];
/**
* @var array Permissions required to view this page.
*/
public $requiredPermissions = ['backend.manage_users'];
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
BackendMenu::setContext('Winter.System', 'system', 'users');
SettingsManager::setContext('Winter.System', 'administrators');
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Backend\Controllers;
use Backend\Classes\Controller;
use Backend\Facades\BackendMenu;
use System\Classes\SettingsManager;
/**
* Backend user groups controller
*
* @package winter\wn-backend-module
* @author Alexey Bobkov, Samuel Georges
*
*/
class UserRoles extends Controller
{
/**
* @var array Extensions implemented by this controller.
*/
public $implement = [
\Backend\Behaviors\FormController::class,
\Backend\Behaviors\ListController::class,
\Backend\Behaviors\RelationController::class,
];
/**
* @var array Permissions required to view this page.
*/
public $requiredPermissions = ['backend.manage_users'];
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
BackendMenu::setContext('Winter.System', 'system', 'users');
SettingsManager::setContext('Winter.System', 'administrators');
/*
* Only super users can access
*/
$this->bindEvent('page.beforeDisplay', function () {
if (!$this->user->isSuperUser()) {
abort(403);
}
});
}
}

View File

@@ -0,0 +1,258 @@
<?php
namespace Backend\Controllers;
use Backend\Behaviors\FormController;
use Backend\Behaviors\ListController;
use Backend\Behaviors\RelationController;
use Backend\Classes\Controller;
use Backend\Facades\Backend;
use Backend\Facades\BackendAuth;
use Backend\Facades\BackendMenu;
use Backend\Models\UserGroup;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Response;
use Illuminate\Support\Str;
use System\Classes\SettingsManager;
use Winter\Storm\Support\Facades\Flash;
use Winter\Storm\Support\Facades\Mail;
/**
* Backend user controller
*
* @package winter\wn-backend-module
* @author Alexey Bobkov, Samuel Georges
*
*/
class Users extends Controller
{
/**
* @var array Extensions implemented by this controller.
*/
public $implement = [
FormController::class,
ListController::class,
RelationController::class,
];
/**
* @var array Permissions required to view this page.
*/
public $requiredPermissions = ['backend.manage_users'];
/**
* @var string HTML body tag class
*/
public $bodyClass = 'compact-container';
public $formLayout = 'sidebar';
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
BackendMenu::setContext('Winter.System', 'system', 'users');
SettingsManager::setContext('Winter.System', 'administrators');
}
/**
* Extends the list query to hide superusers if the current user is not a superuser themselves
*/
public function listExtendQuery($query)
{
if (!$this->user->isSuperUser()) {
$query->where('is_superuser', false);
}
}
/**
* Prevents non-superusers from even seeing the is_superuser filter
*/
public function listFilterExtendScopes($filterWidget)
{
if (!$this->user->isSuperUser()) {
$filterWidget->removeScope('is_superuser');
}
}
/**
* Strike out deleted records
*/
public function listInjectRowClass($record, $definition = null)
{
if ($record->trashed()) {
return 'strike';
}
}
/**
* Extends the form query to prevent non-superusers from accessing superusers at all
*/
public function formExtendQuery($query)
{
if (!$this->user->isSuperUser()) {
$query->where('is_superuser', false);
}
// Ensure soft-deleted records can still be managed
$query->withTrashed();
}
/**
* Before creating a new user, generate password if auto-generate is enabled
*/
public function formBeforeCreate($model)
{
if (post('User._auto_generate_password')) {
$password = Str::random(22);
$model->password = $password;
$model->password_confirmation = $password;
}
}
/**
* Update controller
*/
public function update($recordId, $context = null)
{
// Users cannot edit themselves, only use My Account
if ($context != 'myaccount' && $recordId == $this->user->id) {
return Backend::redirect('backend/myaccount');
}
return $this->asExtension('FormController')->update($recordId, $context);
}
/**
* Handle restoring users
*/
public function update_onRestore($recordId)
{
$this->formFindModelObject($recordId)->restore();
Flash::success(Lang::get('backend::lang.form.restore_success', ['name' => Lang::get('backend::lang.user.name')]));
return Redirect::refresh();
}
/**
* Impersonate this user
*/
public function update_onImpersonateUser($recordId)
{
if (!$this->user->hasAccess('backend.impersonate_users')) {
return Response::make(Lang::get('backend::lang.page.access_denied.label'), 403);
}
$model = $this->formFindModelObject($recordId);
BackendAuth::impersonate($model);
Flash::success(Lang::get('backend::lang.account.impersonate_success'));
return Backend::redirect('backend/myaccount');
}
/**
* Unsuspend this user
*/
public function update_onUnsuspendUser($recordId)
{
$model = $this->formFindModelObject($recordId);
$model->unsuspend();
Flash::success(Lang::get('backend::lang.account.unsuspend_success'));
return Redirect::refresh();
}
/**
* Backward compatibility redirect to the new MyAccount controller.
*/
public function myaccount()
{
return Backend::redirect('backend/myaccount');
}
/**
* Add available permission fields to the User form.
* Mark default groups as checked for new Users.
*/
public function formExtendFields($form)
{
if ($form->getContext() == 'myaccount') {
return;
}
if (!$this->user->isSuperUser()) {
$form->removeField('is_superuser');
}
/*
* Add permissions tab
*/
$form->addTabFields($this->generatePermissionsField());
/*
* Mark default groups
*/
if (!$form->model->exists) {
$defaultGroupIds = UserGroup::where('is_new_user_default', true)->lists('id');
$groupField = $form->getField('groups');
if ($groupField) {
$groupField->value = $defaultGroupIds;
}
}
}
/**
* Adds the permissions editor widget to the form.
* @return array
*/
protected function generatePermissionsField()
{
return [
'permissions' => [
'tab' => 'backend::lang.user.permissions',
'type' => 'Backend\FormWidgets\PermissionEditor',
'trigger' => [
'action' => 'disable',
'field' => 'is_superuser',
'condition' => 'checked'
]
]
];
}
/**
* Send password reset mail
*/
public function update_onManualPasswordReset($recordId)
{
$user = $this->formFindModelObject($recordId);
if ($user) {
$code = $user->getResetPasswordCode();
$link = Backend::url('backend/auth/reset/' . $user->id . '/' . $code);
$data = [
'name' => $user->full_name,
'link' => $link,
];
Mail::send('backend::mail.restore', $data, function ($message) use ($user) {
$message->to($user->email, $user->full_name)->subject(trans('backend::lang.account.password_reset'));
});
}
Flash::success(Lang::get('backend::lang.account.manual_password_reset_success'));
return Redirect::refresh();
}
}

View File

@@ -0,0 +1,4 @@
<p>
<?= e(trans('backend::lang.access_log.hint', ['days' => 60])) ?>
</p>

View File

@@ -0,0 +1,9 @@
<div data-control="toolbar" class="loading-indicator-container">
<a
href="javascript:;"
data-request="onRefresh"
data-load-indicator="<?= e(trans('backend::lang.list.updating')) ?>"
class="btn btn-primary wn-icon-refresh">
<?= e(trans('backend::lang.list.refresh')) ?>
</a>
</div>

View File

@@ -0,0 +1,16 @@
# ===================================
# Filter Scope Definitions
# ===================================
scopes:
created_at:
label: backend::lang.access_log.created_at
type: daterange
conditions: created_at >= ':after' AND created_at <= ':before'
user:
label: backend::lang.access_log.login
modelClass: Backend\Models\User
conditions: user_id in (:filtered)
nameFrom: login

View File

@@ -0,0 +1,17 @@
# ===================================
# List Behavior Config
# ===================================
title: backend::lang.access_log.menu_label
list: ~/modules/backend/models/accesslog/columns.yaml
modelClass: Backend\Models\AccessLog
noRecordsMessage: backend::lang.list.no_records
recordsPerPage: 30
showSetup: true
toolbar:
buttons: list_toolbar
search:
prompt: backend::lang.list.search_prompt
filter: config_filter.yaml

View File

@@ -0,0 +1,5 @@
<div class="padded-container container-flush">
<?= $this->makeHintPartial('backend_accesslogs_hint', 'hint') ?>
</div>
<?= $this->listRender() ?>

View File

@@ -0,0 +1,33 @@
<h2><?= e(trans('backend::lang.account.enter_new_password')) ?></h2>
<?= Form::open() ?>
<input type="hidden" name="postback" value="1" />
<input type="hidden" name="id" value="<?= e($id) ?>" />
<input type="hidden" name="code" value="<?= e($code) ?>" />
<div class="form-elements" role="form">
<div class="form-group text-field horizontal-form">
<!-- Password -->
<input
type="password"
name="password"
value=""
class="form-control password icon lock"
placeholder="<?= e(trans('backend::lang.account.password_placeholder')) ?>"
autocomplete="off"
maxlength="255" />
<!-- Submit Login -->
<button type="submit" class="btn btn-primary pull-right">
<?= e(trans('backend::lang.account.reset')) ?>
</button>
</div>
<p class="pull-right forgot-password">
<a href="<?= Backend::url('backend/auth') ?>" class="text-muted">
<?= e(trans('backend::lang.form.cancel')) ?>
</a>
</p>
</div>
<?= Form::close() ?>

View File

@@ -0,0 +1,31 @@
<h2><?= e(trans('backend::lang.account.enter_login')) ?></h2>
<?= Form::open() ?>
<input type="hidden" name="postback" value="1" />
<div class="form-elements" role="form">
<div class="form-group text-field horizontal-form">
<input
type="text"
name="login"
value="<?= e(post('login')) ?>"
class="form-control icon user"
placeholder="<?= e(trans('backend::lang.account.login_placeholder')) ?>"
autocomplete="off"
maxlength="255" />
<button type="submit" class="btn btn-primary restore-button">
<?= e(trans('backend::lang.account.restore')) ?>
</button>
</div>
<p class="pull-right forgot-password">
<a href="<?= Backend::url('backend/auth') ?>" class="text-muted">
<?= e(trans('backend::lang.form.cancel')) ?>
</a>
</p>
</div>
<?= Form::close() ?>
<?= $this->fireViewEvent('backend.auth.extendRestoreView') ?>

View File

@@ -0,0 +1,60 @@
<h2><?= e(Backend\Models\BrandSetting::get('app_tagline')) ?></h2>
<?= Form::open() ?>
<input type="hidden" name="postback" value="1" />
<div class="form-elements" role="form">
<div class="form-group text-field horizontal-form">
<!-- Login -->
<input
type="text"
name="login"
value="<?= e(post('login')) ?>"
class="form-control icon user"
placeholder="<?= e(trans('backend::lang.account.login_placeholder')) ?>"
autocomplete="off"
maxlength="255" />
<!-- Password -->
<input
type="password"
name="password"
value=""
class="form-control icon lock"
placeholder="<?= e(trans('backend::lang.account.password_placeholder')) ?>"
autocomplete="off"
maxlength="255" />
<!-- Submit Login -->
<button type="submit" class="btn btn-primary login-button">
<?= e(trans('backend::lang.account.login')) ?>
</button>
</div>
<?php if (is_null(config('cms.backendForceRemember', true))): ?>
<!-- Remember checkbox -->
<div class="form-group checkbox-field horizontal-form remember">
<div class="checkbox custom-checkbox">
<input
type="checkbox"
id="remember"
name="remember" />
<label for="remember">
<?= e(trans('backend::lang.account.remember_me')) ?>
</label>
</div>
</div>
<?php endif; ?>
<p class="wn-icon-lock pull-right forgot-password">
<!-- Forgot your password? -->
<a href="<?= Backend::url('backend/auth/restore') ?>" class="text-muted">
<?= e(trans('backend::lang.account.forgot_password')) ?>
</a>
</p>
</div>
<?= Form::close() ?>
<?= $this->fireViewEvent('backend.auth.extendSigninView') ?>

View File

@@ -0,0 +1,23 @@
# ===================================
# Dashboard Config
# ===================================
defaultWidgets:
welcome:
class: Backend\ReportWidgets\Welcome
sortOrder: 50
configuration:
ocWidgetWidth: 7
systemStatus:
class: System\ReportWidgets\Status
sortOrder: 60
configuration:
ocWidgetWidth: 7
activeTheme:
class: Cms\ReportWidgets\ActiveTheme
sortOrder: 70
configuration:
ocWidgetWidth: 5

View File

@@ -0,0 +1,23 @@
<?= Form::open(['class'=>'layout-relative dashboard-container']) ?>
<div id="dashReportContainer" class="report-container loading">
<!-- Loading -->
<div class="loading-indicator-container">
<div class="loading-indicator indicator-center">
<span></span>
<div><?= e(trans('backend::lang.list.loading')) ?></div>
</div>
</div>
</div>
<?= Form::close() ?>
<?php Block::put('head'); ?>
<script>
Snowboard.ready(() => {
Snowboard.request(null, 'onInitReportContainer', {
success: () => {
$('#dashReportContainer').removeClass('loading');
},
});
});
</script>
<?php Block::endPut(true); ?>

View File

@@ -0,0 +1,5 @@
<?= Block::put('body') ?>
<?= Form::open(['class'=>'layout', 'onsubmit'=>'return false']) ?>
<?= $this->widget->manager->render() ?>
<?= Form::close() ?>
<?= Block::endPut() ?>

View File

@@ -0,0 +1,12 @@
# ===================================
# Form Behavior Config
# ===================================
name: backend::lang.user.name
form: ~/modules/backend/models/user/fields.yaml
modelClass: Backend\Models\User
defaultRedirect: backend/myaccount
update:
redirect: backend/myaccount
redirectClose: backend/myaccount

View File

@@ -0,0 +1,56 @@
<?php if ($this->user->hasAccess('backend.manage_users')): ?>
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.user.menu_label')) ?></a></li>
<li><?= e(trans($this->pageTitle)) ?></li>
</ul>
<?php Block::endPut() ?>
<?php endif ?>
<?php if (!$this->fatalError): ?>
<?php Block::put('form-contents') ?>
<div class="layout">
<div class="layout-row">
<?= $this->formRenderOutsideFields() ?>
<?= $this->formRenderPrimaryTabs() ?>
</div>
<div class="form-buttons">
<div class="loading-indicator-container">
<button
type="submit"
data-request="onSave"
data-browser-validate
data-request-data="redirect:0"
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-primary">
<?= e(trans('backend::lang.form.save')) ?>
</button>
</div>
</div>
</div>
<?php Block::endPut() ?>
<?php Block::put('form-sidebar') ?>
<div class="hide-tabs"><?= $this->formRenderSecondaryTabs() ?></div>
<?php Block::endPut() ?>
<?php Block::put('body') ?>
<?= Form::open(['class'=>'layout stretch']) ?>
<?= $this->makeLayout('form-with-sidebar') ?>
<?= Form::close() ?>
<?php Block::endPut() ?>
<?php else: ?>
<div class="control-breadcrumb">
<?= Block::placeholder('breadcrumb') ?>
</div>
<div class="padded-container">
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
<p><a href="<?= Backend::url('backend') ?>" class="btn btn-default"><?= e(trans('backend::lang.form.return_to_list')) ?></a></p>
</div>
<?php endif ?>

View File

@@ -0,0 +1,24 @@
form, fieldset, h5, h6, pre, blockquote, ol, dl, dt, dd, address, dd, dtm, div, td, th, hr {
margin: 0;
padding: 0;
}
/* This is a comment */
body {
background-color: white;
font: 62.5% Helvetica, Arial, Tahoma, Verdana, Helvetica, sans-serif;
}
p {
font-size: 12px;
}
strong {
font-weight: bold;
}
span.alert {
color: #ff0000;
border: 1px solid #ff0000;
padding: 2rem;
}

View File

@@ -0,0 +1,122 @@
<p class="help-block" style="margin-bottom: -15px">
<?= e(trans('backend::lang.editor.preview')) ?>:
<a href="#" data-switch-lang="css">CSS</a> |
<a href="#" data-switch-lang="html">HTML</a> |
<a href="#" data-switch-lang="javascript">JavaScript</a> |
<a href="#" data-switch-lang="twig">Twig</a> |
<a href="#" data-switch-lang="php">PHP</a>
</p>
<script type="x-template" data-lang-snippet="css">
form, fieldset, h5, h6, pre, blockquote, ol, dl, dt, dd, address, dd, dtm, div, td, th, hr {
margin: 0;
padding: 0;
}
/* This is a comment */
body {
background-color: white;
font: 62.5% Helvetica, Arial, Tahoma, Verdana, Helvetica, sans-serif;
}
p {
font-size: 12px;
}
strong {
font-weight: bold;
}
span.alert {
color: #ff0000;
border: 1px solid #ff0000;
padding: 2rem;
}
</script>
<script type="x-template" data-lang-snippet="html">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Winter CMS</title>
</head>
<body>
<h1>Winter CMS</h1>
<!-- The number one platform for content management -->
<div class="container">
<p style="font-weight: bold">Winter CMS is a free, open-source, self-hosted CMS platform based on the Laravel PHP Framework.</p>
</div>
</body>
</html>
</script>
<script type="x-template" data-lang-snippet="javascript">
((Snowboard) => {
class WinterCms extends Snowboard.PluginBase {
// Create a new function
myFunction(arg) {
arg = arg.toLowerCase();
return arg;
}
counter() {
let count = 0;
count = count + 1;
return count;
}
}
})(winter.Snowboard)
</script>
<script type="x-template" data-lang-snippet="twig">
<h1>My Winter Page - {{ title }}</h1>
{% if show %}
<p>Winter is coming!</p>
{% else %}
<p>Winter is not coming!</p>
{% endif %}
{% for item in items %}
<p>{{ item }}</p>
{% endfor %}
</script>
<script type="x-template" data-lang-snippet="php">
<?php echo '<?php
namespace Winter;
use \Package\AnotherClass as BaseClass;
/**
* This is Winter CMS.
*
* @author Winter CMS maintainers
*/
class Winter extends Laravel implements Simplicity
{
const NAME = \'Winter CMS\';
public string $site = \'My site\';
// You can construct anything
public function __construct($site)
{
$this->site = $site;
$this->begin();
}
protected function begin()
{
$this->achieveAwesome();
}
}'; ?>
</script>

View File

@@ -0,0 +1,8 @@
# ===================================
# Form Behavior Config
# ===================================
name: backend::lang.backend_preferences.menu_label
form: ~/modules/backend/models/preference/fields.yaml
modelClass: Backend\Models\Preference
defaultRedirect: system/settings

View File

@@ -0,0 +1,41 @@
<?php if (!$this->fatalError): ?>
<?= Form::open(['class'=>'layout']) ?>
<div class="layout-row">
<?= $this->formRender() ?>
</div>
<div class="form-buttons">
<div class="loading-indicator-container">
<button
type="submit"
data-request="onSave"
data-browser-validate
data-request-data="redirect:0"
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-primary">
<?= e(trans('backend::lang.form.save')) ?>
</button>
<span class="btn-text">
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('system/settings') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
</span>
<button
type="button"
class="btn btn-danger pull-right"
data-request="onResetDefault"
data-load-indicator="<?= e(trans('backend::lang.form.resetting')) ?>"
data-request-confirm="<?= e(trans('backend::lang.form.action_confirm')) ?>">
<?= e(trans('backend::lang.form.reset_default')) ?>
</button>
</div>
</div>
<?= Form::close() ?>
<?php else: ?>
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
<p><a href="<?= Backend::url('system/settings') ?>" class="btn btn-default"><?= e(trans('system::lang.settings.return')) ?></a></p>
<?php endif ?>

View File

@@ -0,0 +1,8 @@
<div data-control="toolbar">
<a href="<?= Backend::url('backend/users') ?>" class="btn btn-default wn-icon-chevron-left">
<?= e(trans('backend::lang.user.return')) ?>
</a>
<a href="<?= Backend::url('backend/usergroups/create') ?>" class="btn btn-primary wn-icon-plus">
<?= e(trans('backend::lang.user.group.new')) ?>
</a>
</div>

View File

@@ -0,0 +1,16 @@
# ===================================
# Form Behavior Config
# ===================================
name: backend::lang.user.group.name
form: ~/modules/backend/models/usergroup/fields.yaml
modelClass: Backend\Models\UserGroup
defaultRedirect: backend/usergroups
create:
redirect: backend/usergroups/update/:id
redirectClose: backend/usergroups
update:
redirect: backend/usergroups
redirectClose: backend/usergroups

View File

@@ -0,0 +1,16 @@
# ===================================
# List Behavior Config
# ===================================
title: backend::lang.user.group.list_title
list: ~/modules/backend/models/usergroup/columns.yaml
modelClass: Backend\Models\UserGroup
recordUrl: backend/usergroups/update/:id
noRecordsMessage: backend::lang.list.no_records
recordsPerPage: 25
showSetup: true
toolbar:
buttons: list_toolbar
search:
prompt: backend::lang.list.search_prompt

View File

@@ -0,0 +1,46 @@
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.user.menu_label')) ?></a></li>
<li><a href="<?= Backend::url('backend/usergroups') ?>"><?= e(trans('backend::lang.user.group.menu_label')) ?></a></li>
<li><?= e(trans($this->pageTitle)) ?></li>
</ul>
<?php Block::endPut() ?>
<?php if (!$this->fatalError): ?>
<?= Form::open(['class'=>'layout']) ?>
<div class="layout-row">
<?= $this->formRender() ?>
</div>
<div class="form-buttons">
<div class="loading-indicator-container">
<button
type="submit"
data-request="onSave"
data-browser-validate
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
class="btn btn-primary">
<?= e(trans('backend::lang.form.create')) ?>
</button>
<button
type="button"
data-request="onSave"
data-browser-validate
data-request-data="close:1"
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
class="btn btn-default">
<?= e(trans('backend::lang.form.create_and_close')) ?>
</button>
</div>
</div>
<?= Form::close() ?>
<?php else: ?>
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
<p><a href="<?= Backend::url('backend/usergroups') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.group.return')) ?></a></p>
<?php endif ?>

View File

@@ -0,0 +1,54 @@
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.user.menu_label')) ?></a></li>
<li><a href="<?= Backend::url('backend/usergroups') ?>"><?= e(trans('backend::lang.user.group.menu_label')) ?></a></li>
<li><?= e(trans($this->pageTitle)) ?></li>
</ul>
<?php Block::endPut() ?>
<?php if (!$this->fatalError): ?>
<?= Form::open(['class'=>'layout']) ?>
<div class="layout-row">
<?= $this->formRender() ?>
</div>
<div class="form-buttons">
<div class="loading-indicator-container">
<button
type="submit"
data-request="onSave"
data-browser-validate
data-request-data="redirect:0"
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-primary">
<?= e(trans('backend::lang.form.save')) ?>
</button>
<button
type="button"
data-request="onSave"
data-browser-validate
data-request-data="close:1"
data-hotkey="ctrl+enter, cmd+enter"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-default">
<?= e(trans('backend::lang.form.save_and_close')) ?>
</button>
<button
type="button"
class="wn-icon-trash-o btn-icon danger pull-right"
data-request="onDelete"
data-load-indicator="<?= e(trans('backend::lang.form.deleting')) ?>"
data-request-confirm="<?= e(trans('backend::lang.user.group.delete_confirm')) ?>">
</button>
</div>
</div>
<?= Form::close() ?>
<?php else: ?>
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
<p><a href="<?= Backend::url('backend/usergroups') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.group.return')) ?></a></p>
<?php endif ?>

View File

@@ -0,0 +1 @@
<?= $this->relationRender('users') ?>

View File

@@ -0,0 +1,8 @@
<div data-control="toolbar">
<a href="<?= Backend::url('backend/users') ?>" class="btn btn-default wn-icon-chevron-left">
<?= e(trans('backend::lang.user.return')) ?>
</a>
<a href="<?= Backend::url('backend/userroles/create') ?>" class="btn btn-primary wn-icon-plus">
<?= e(trans('backend::lang.user.role.new')) ?>
</a>
</div>

View File

@@ -0,0 +1,16 @@
# ===================================
# Form Behavior Config
# ===================================
name: backend::lang.user.role.name
form: ~/modules/backend/models/userrole/fields.yaml
modelClass: Backend\Models\UserRole
defaultRedirect: backend/userroles
create:
redirect: backend/userroles/update/:id
redirectClose: backend/userroles
update:
redirect: backend/userroles
redirectClose: backend/userroles

View File

@@ -0,0 +1,16 @@
# ===================================
# List Behavior Config
# ===================================
title: backend::lang.user.role.list_title
list: ~/modules/backend/models/userrole/columns.yaml
modelClass: Backend\Models\UserRole
recordUrl: backend/userroles/update/:id
noRecordsMessage: backend::lang.list.no_records
recordsPerPage: 25
showSetup: true
toolbar:
buttons: list_toolbar
search:
prompt: backend::lang.list.search_prompt

View File

@@ -0,0 +1,10 @@
# ===================================
# Relation Behavior Config
# ===================================
users:
label: backend::lang.user.name
view:
list: ~/modules/backend/models/user/columns.yaml
toolbarButtons: add|remove
recordUrl: backend/users/update/:id

View File

@@ -0,0 +1,46 @@
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.user.menu_label')) ?></a></li>
<li><a href="<?= Backend::url('backend/userroles') ?>"><?= e(trans('backend::lang.user.role.menu_label')) ?></a></li>
<li><?= e(trans($this->pageTitle)) ?></li>
</ul>
<?php Block::endPut() ?>
<?php if (!$this->fatalError): ?>
<?= Form::open(['class'=>'layout']) ?>
<div class="layout-row">
<?= $this->formRender() ?>
</div>
<div class="form-buttons">
<div class="loading-indicator-container">
<button
type="submit"
data-request="onSave"
data-browser-validate
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
class="btn btn-primary">
<?= e(trans('backend::lang.form.create')) ?>
</button>
<button
type="button"
data-request="onSave"
data-browser-validate
data-request-data="close:1"
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.creating')) ?>"
class="btn btn-default">
<?= e(trans('backend::lang.form.create_and_close')) ?>
</button>
</div>
</div>
<?= Form::close() ?>
<?php else: ?>
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
<p><a href="<?= Backend::url('backend/userroles') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.role.return')) ?></a></p>
<?php endif ?>

View File

@@ -0,0 +1,54 @@
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.user.menu_label')) ?></a></li>
<li><a href="<?= Backend::url('backend/userroles') ?>"><?= e(trans('backend::lang.user.role.menu_label')) ?></a></li>
<li><?= e(trans($this->pageTitle)) ?></li>
</ul>
<?php Block::endPut() ?>
<?php if (!$this->fatalError): ?>
<?= Form::open(['class'=>'layout']) ?>
<div class="layout-row">
<?= $this->formRender() ?>
</div>
<div class="form-buttons">
<div class="loading-indicator-container">
<button
type="submit"
data-request="onSave"
data-browser-validate
data-request-data="redirect:0"
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-primary">
<?= e(trans('backend::lang.form.save')) ?>
</button>
<button
type="button"
data-request="onSave"
data-browser-validate
data-request-data="close:1"
data-hotkey="ctrl+enter, cmd+enter"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-default">
<?= e(trans('backend::lang.form.save_and_close')) ?>
</button>
<button
type="button"
class="wn-icon-trash-o btn-icon danger pull-right"
data-request="onDelete"
data-load-indicator="<?= e(trans('backend::lang.form.deleting')) ?>"
data-request-confirm="<?= e(trans('backend::lang.user.role.delete_confirm')) ?>">
</button>
</div>
</div>
<?= Form::close() ?>
<?php else: ?>
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
<p><a href="<?= Backend::url('backend/userroles') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.role.return')) ?></a></p>
<?php endif ?>

View File

@@ -0,0 +1,14 @@
<?php if ($this->user->hasAccess('backend.impersonate_users')): ?>
<div class="loading-indicator-container">
<button
type="button"
data-request="onImpersonateUser"
data-load-indicator="<?= e(trans('backend::lang.account.impersonate_working')) ?>"
data-request-confirm="<?= e(trans('backend::lang.account.impersonate_confirm')) ?>"
class="btn btn-danger wn-icon-user-secret"
style="width: 100%; text-align: center"
>
<?= e(trans('backend::lang.account.impersonate')) ?>
</button>
</div>
<?php endif ?>

View File

@@ -0,0 +1,12 @@
<div class="loading-indicator-container">
<button
type="button"
data-request="onManualPasswordReset"
data-load-indicator="<?= e(trans('backend::lang.account.sending')) ?>"
data-request-confirm="<?= e(trans('backend::lang.account.manual_password_reset_confirm')) ?>"
class="btn btn-primary wn-icon-envelope"
style="width: 100%; text-align: center"
>
<?= e(trans('backend::lang.account.password_reset_email')) ?>
</button>
</div>

View File

@@ -0,0 +1,14 @@
<?php if ($formModel->isSuspended()): ?>
<div class="loading-indicator-container">
<button
type="button"
data-request="onUnsuspendUser"
data-load-indicator="<?= e(trans('backend::lang.account.unsuspend_working')) ?>"
data-request-confirm="<?= e(trans('backend::lang.account.unsuspend_confirm')) ?>"
class="btn btn-danger wn-icon-unlock-alt"
style="width: 100%; text-align: center"
>
<?= e(trans('backend::lang.account.unsuspend')) ?>
</button>
</div>
<?php endif ?>

View File

@@ -0,0 +1,9 @@
<div class="layout-row min-size">
<div class="callout callout-danger">
<div class="header">
<i class="icon-trash"></i>
<h3><?= e(trans('backend::lang.user.trashed_hint_title')) ?></h3>
<p><?= e(trans('backend::lang.user.trashed_hint_desc')) ?></p>
</div>
</div>
</div>

View File

@@ -0,0 +1,29 @@
<div data-control="toolbar">
<a href="<?= Backend::url('backend/users/create') ?>" class="btn btn-primary wn-icon-plus">
<?= e(trans('backend::lang.user.new')) ?>
</a>
<?php if ($this->user->isSuperUser()): ?>
<a href="<?= Backend::url('backend/userroles') ?>" class="btn btn-default wn-icon-address-card">
<?= e(trans('backend::lang.user.role.list_title')) ?>
</a>
<?php endif ?>
<a href="<?= Backend::url('backend/usergroups') ?>" class="btn btn-default wn-icon-group">
<?= e(trans('backend::lang.user.group.list_title')) ?>
</a>
<?php /* @todo
<div class="btn-group">
<button
class="btn btn-default wn-icon-ban-circle"
disabled="disabled"
data-trigger-action="enable"
data-trigger=".control-list input[type=checkbox]"
data-trigger-condition="checked">Ban</button>
<button
class="btn btn-default wn-icon-trash-o"
disabled="disabled"
data-trigger-action="enable"
data-trigger=".control-list input[type=checkbox]"
data-trigger-condition="checked">Delete</button>
</div>
*/ ?>
</div>

View File

@@ -0,0 +1,30 @@
# ===================================
# Filter Scope Definitions
# ===================================
scopes:
is_superuser:
label: backend::lang.user.superuser
type: switch
conditions:
- is_superuser = 0
- is_superuser = 1
login_date:
label: backend::lang.user.last_login
type: daterange
conditions: last_login >= ':after' AND last_login <= ':before'
role_id:
label: backend::lang.user.role.name
modelClass: Backend\Models\UserRole
conditions: role_id in (:filtered)
nameFrom: name
show_deleted:
label: backend::lang.user.show_deleted
type: checkbox
modelClass: Backend\Models\User
scope: withTrashed
default: 0

View File

@@ -0,0 +1,16 @@
# ===================================
# Form Behavior Config
# ===================================
name: backend::lang.user.name
form: ~/modules/backend/models/user/fields.yaml
modelClass: Backend\Models\User
defaultRedirect: backend/users
create:
redirect: backend/users/update/:id
redirectClose: backend/users
update:
redirect: backend/users
redirectClose: backend/users

View File

@@ -0,0 +1,19 @@
# ===================================
# List Behavior Config
# ===================================
title: backend::lang.user.list_title
list: ~/modules/backend/models/user/columns.yaml
modelClass: Backend\Models\User
recordUrl: backend/users/update/:id
noRecordsMessage: backend::lang.list.no_records
recordsPerPage: 20
showSetup: true
# showCheckboxes: true
toolbar:
buttons: list_toolbar
search:
prompt: backend::lang.list.search_prompt
filter: config_filter.yaml

View File

@@ -0,0 +1,31 @@
# ===================================
# Relation Behavior Config
# ===================================
throttle:
label: backend::lang.user.throttle_tab_label
view:
list:
columns:
ip_address:
label: backend::lang.user.throttle_ip_address
searchable: true
attempts:
label: backend::lang.user.throttle_attempts
width: 100px
align: center
last_attempt_at:
label: backend::lang.user.throttle_last_attempt
type: datetime
searchable: true
suspended_at:
label: backend::lang.user.throttle_suspended_at
type: datetime
searchable: true
toolbarButtons: delete|refresh
showSearch: true
showSorting: true
recordsPerPage: 10
defaultSort:
column: last_attempt_at
direction: desc

View File

@@ -0,0 +1,68 @@
<?php if ($this->user->hasAccess('backend.manage_users')): ?>
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.user.menu_label')) ?></a></li>
<li><?= e(trans($this->pageTitle)) ?></li>
</ul>
<?php Block::endPut() ?>
<?php endif ?>
<?php if (!$this->fatalError): ?>
<?php Block::put('form-contents') ?>
<div class="layout">
<div class="layout-row">
<?= $this->formRenderOutsideFields() ?>
<?= $this->formRenderPrimaryTabs() ?>
</div>
<div class="form-buttons">
<div class="loading-indicator-container">
<button
type="submit"
data-request="onSave"
data-browser-validate
data-request-data="redirect:0"
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-primary">
<?= e(trans('backend::lang.form.save')) ?>
</button>
<?php if ($this->user->hasAccess('backend.manage_users')): ?>
<button
type="button"
data-request="onSave"
data-browser-validate
data-request-data="close:1"
data-hotkey="ctrl+enter, cmd+enter"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-default">
<?= e(trans('backend::lang.form.save_and_close')) ?>
</button>
<?php endif ?>
</div>
</div>
</div>
<?php Block::endPut() ?>
<?php Block::put('form-sidebar') ?>
<div class="hide-tabs"><?= $this->formRenderSecondaryTabs() ?></div>
<?php Block::endPut() ?>
<?php Block::put('body') ?>
<?= Form::open(['class'=>'layout stretch']) ?>
<?= $this->makeLayout('form-with-sidebar') ?>
<?= Form::close() ?>
<?php Block::endPut() ?>
<?php else: ?>
<div class="control-breadcrumb">
<?= Block::placeholder('breadcrumb') ?>
</div>
<div class="padded-container">
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
<p><a href="<?= Backend::url('backend/users') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.return')) ?></a></p>
</div>
<?php endif ?>

View File

@@ -0,0 +1,88 @@
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.user.menu_label')) ?></a></li>
<li><?= e(trans($this->pageTitle)) ?></li>
</ul>
<?php Block::endPut() ?>
<?php if (!$this->fatalError): ?>
<?php Block::put('form-contents') ?>
<?php if ($formModel->trashed()): ?>
<?= $this->makePartial('hint_trashed') ?>
<?php endif; ?>
<div class="layout">
<div class="layout-row">
<?= $this->formRenderOutsideFields() ?>
<?= $this->formRenderPrimaryTabs() ?>
</div>
<div class="form-buttons">
<div class="loading-indicator-container">
<button
type="submit"
data-request="onSave"
data-browser-validate
data-request-data="redirect:0"
data-hotkey="ctrl+s, cmd+s"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-primary">
<?= e(trans('backend::lang.form.save')) ?>
</button>
<button
type="button"
data-request="onSave"
data-browser-validate
data-request-data="close:1"
data-hotkey="ctrl+enter, cmd+enter"
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
class="btn btn-default">
<?= e(trans('backend::lang.form.save_and_close')) ?>
</button>
<span class="btn-text">
<?= e(trans('backend::lang.form.or')) ?> <a href="<?= Backend::url('backend/users') ?>"><?= e(trans('backend::lang.form.cancel')) ?></a>
</span>
<?php if ($formModel->trashed()): ?>
<button
type="button"
class="wn-icon-user-plus btn-icon info pull-right"
data-request="onRestore"
data-load-indicator="<?= e(trans('backend::lang.form.restoring')) ?>"
data-request-confirm="<?= e(trans('backend::lang.form.confirm_restore')) ?>">
</button>
<?php else: ?>
<button
type="button"
class="wn-icon-trash-o btn-icon danger pull-right"
data-request="onDelete"
data-load-indicator="<?= e(trans('backend::lang.form.deleting')) ?>"
data-request-confirm="<?= e(trans('backend::lang.user.delete_confirm')) ?>">
</button>
<?php endif; ?>
</div>
</div>
</div>
<?php Block::endPut() ?>
<?php Block::put('form-sidebar') ?>
<div class="hide-tabs"><?= $this->formRenderSecondaryTabs() ?></div>
<?php Block::endPut() ?>
<?php Block::put('body') ?>
<?= Form::open(['class'=>'layout stretch']) ?>
<?= $this->makeLayout('form-with-sidebar') ?>
<?= Form::close() ?>
<?php Block::endPut() ?>
<?php else: ?>
<div class="control-breadcrumb">
<?= Block::placeholder('breadcrumb') ?>
</div>
<div class="padded-container">
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
<p><a href="<?= Backend::url('backend/users') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.return')) ?></a></p>
</div>
<?php endif ?>