├── po
├── potfiles.in
├── Makefile
└── preprocess.php
├── www
├── images
│ ├── admin-remove.png
│ ├── admin-remove@2x.png
│ ├── swat-tool-link-icons.png
│ ├── admin-dialog-exception.png
│ ├── admin-dialog-question.png
│ ├── admin-menu-help-arrow.png
│ ├── admin-dialog-exception@2x.png
│ ├── admin-dialog-question@2x.png
│ ├── admin-swat-actions-arrow.png
│ ├── swat-tool-link-icons@2x.png
│ ├── admin-swat-actions-arrow@2x.png
│ ├── admin-checkbox-radio-buttons.png
│ ├── admin-message-display-dismiss.png
│ ├── admin-checkbox-radio-buttons@2x.png
│ ├── admin-message-display-dismiss@2x.png
│ ├── admin-swat-textarea-editor-icons.png
│ ├── admin-swat-textarea-editor-icons@2x.png
│ ├── admin-title-link-cell-renderer-icons.png
│ ├── admin-title-link-cell-renderer-icons@2x.png
│ ├── admin-swat-textarea-editor-icons-highlight.png
│ └── admin-swat-textarea-editor-icons-highlight@2x.png
├── styles
│ ├── admin-user-index-page.css
│ ├── admin-change-password-page.css
│ ├── admin-profile.css
│ ├── admin-two-factor-authentication-page.css
│ ├── admin-approval-page.css
│ ├── admin-note.css
│ ├── admin-login-page.css
│ ├── admin-group-link-cell-renderer.css
│ ├── admin-menu.css
│ └── admin-layout.css
└── javascript
│ ├── admin-order.js
│ └── admin-login.js
├── resources
├── mockups
│ ├── palette.png
│ ├── photo10572.jpg
│ ├── photo22419.jpg
│ ├── beach-palette.png
│ ├── Swat-Admin-Colors.png
│ └── Swat-Admin-Example.png
├── admin-checkbox-radio-buttons.xcf
└── admin-title-link-cell-renderer-icons.xcf
├── locale
├── en_CA
│ └── LC_MESSAGES
│ │ └── admin.mo
├── en_GB
│ └── LC_MESSAGES
│ │ └── admin.mo
└── en_US
│ └── LC_MESSAGES
│ └── admin.mo
├── sql
├── tables
│ ├── AdminGroup.sql
│ ├── AdminSection.sql
│ ├── AdminUserInstanceBinding.sql
│ ├── AdminUserHistory.sql
│ ├── AdminSubComponent.sql
│ ├── AdminUserAdminGroupBinding.sql
│ ├── AdminComponentAdminGroupBinding.sql
│ ├── AdminComponent.sql
│ └── AdminUser.sql
├── views
│ └── AdminUserLastLoginView.sql
├── triggers
│ └── AdminUserHistoryInsertTrigger.sql
├── functions
│ ├── getAdminMenu.mysql.sql
│ └── getAdminMenu.pgsql.sql
└── admin-changes.sql
├── phpstan.dist.neon
├── Admin
├── layouts
│ ├── AdminLoginLayout.php
│ ├── AdminLayout.php
│ └── AdminMenuXMLRPCServerLayout.php
├── pages
│ ├── AdminXMLRPCServer.php
│ ├── order.xml
│ ├── confirmation.xml
│ ├── approval.xml
│ ├── AdminDBOrder.php
│ ├── AdminDBEdit.php
│ ├── AdminDBDelete.php
│ └── AdminConfirmation.php
├── AdminImportantNavBarEntry.php
├── templates
│ ├── AdminCustomTemplate.php
│ ├── AdminMSWordTemplate.php
│ ├── AdminLoginTemplate.php
│ └── AdminDefaultTemplate.php
├── exceptions
│ ├── AdminUserException.php
│ ├── AdminException.php
│ ├── AdminNotFoundException.php
│ └── AdminNoAccessException.php
├── components
│ ├── AdminSite
│ │ ├── front.xml
│ │ ├── Front.php
│ │ ├── Logout.php
│ │ ├── forgot-password.xml
│ │ ├── two_factor_authentication.xml
│ │ ├── reset-password.xml
│ │ ├── Exception.php
│ │ ├── login.xml
│ │ ├── change-password.xml
│ │ ├── TwoFactorAuthentication.php
│ │ ├── ChangePassword.php
│ │ └── profile.xml
│ ├── AdminUser
│ │ ├── include
│ │ │ ├── AdminUserTableView.php
│ │ │ └── HistoryCellRenderer.php
│ │ ├── details.xml
│ │ ├── login-history.xml
│ │ ├── Delete.php
│ │ ├── LoginHistory.php
│ │ ├── Details.php
│ │ ├── Reactivate.php
│ │ ├── edit.xml
│ │ └── index.xml
│ ├── AdminSection
│ │ ├── Edit.php
│ │ ├── edit.xml
│ │ ├── Order.php
│ │ ├── index.xml
│ │ ├── Index.php
│ │ └── Delete.php
│ ├── AdminGroup
│ │ ├── Index.php
│ │ ├── edit.xml
│ │ ├── index.xml
│ │ ├── Delete.php
│ │ └── Edit.php
│ ├── AdminSubComponent
│ │ ├── edit.xml
│ │ ├── Order.php
│ │ ├── Delete.php
│ │ └── Edit.php
│ └── AdminComponent
│ │ ├── Order.php
│ │ ├── edit.xml
│ │ ├── Delete.php
│ │ ├── Edit.php
│ │ └── index.xml
├── AdminDependencyEntryWrapper.php
├── AdminDependencySummaryWrapper.php
├── AdminTableViewOrderableColumn.php
├── dataobjects
│ ├── AdminGroupWrapper.php
│ ├── AdminSectionWrapper.php
│ ├── AdminComponentWrapper.php
│ ├── AdminUserWrapper.php
│ ├── AdminUserHistoryWrapper.php
│ ├── AdminSubComponentWrapper.php
│ ├── AdminUserHistory.php
│ ├── AdminGroup.php
│ └── AdminSection.php
├── AdminMenuSubcomponent.php
├── AdminMenuSection.php
├── AdminMenuComponent.php
├── AdminDependencyItem.php
├── AdminDependencySummary.php
├── AdminGroupLinkCellRenderer.php
├── AdminTreeTitleLinkCellRenderer.php
├── AdminUniqueEntry.php
├── AdminTwoFactorAuthentication.php
├── AdminNavBar.php
├── AdminDependencyEntry.php
├── AdminTreeControlCellRenderer.php
├── AdminPagination.php
├── AdminDateLinkCellRenderer.php
├── AdminSearchOperatorFlydown.php
├── AdminNote.php
├── AdminUI.php
├── Admin.php
├── AdminMenuStore.php
└── AdminControlCellRenderer.php
├── phpcs.xml
├── .gitignore
├── .editorconfig
├── phpstan-baseline.neon
├── Jenkinsfile
├── .github
├── pull_request_template.md
└── workflows
│ └── pull-requests.yml
├── README.md
├── dependencies
└── admin.yaml
├── .php-cs-fixer.php
└── composer.json
/po/potfiles.in:
--------------------------------------------------------------------------------
1 | ../Admin
2 |
--------------------------------------------------------------------------------
/www/images/admin-remove.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-remove.png
--------------------------------------------------------------------------------
/resources/mockups/palette.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/resources/mockups/palette.png
--------------------------------------------------------------------------------
/www/images/admin-remove@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-remove@2x.png
--------------------------------------------------------------------------------
/resources/mockups/photo10572.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/resources/mockups/photo10572.jpg
--------------------------------------------------------------------------------
/resources/mockups/photo22419.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/resources/mockups/photo22419.jpg
--------------------------------------------------------------------------------
/locale/en_CA/LC_MESSAGES/admin.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/locale/en_CA/LC_MESSAGES/admin.mo
--------------------------------------------------------------------------------
/locale/en_GB/LC_MESSAGES/admin.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/locale/en_GB/LC_MESSAGES/admin.mo
--------------------------------------------------------------------------------
/locale/en_US/LC_MESSAGES/admin.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/locale/en_US/LC_MESSAGES/admin.mo
--------------------------------------------------------------------------------
/resources/mockups/beach-palette.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/resources/mockups/beach-palette.png
--------------------------------------------------------------------------------
/www/images/swat-tool-link-icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/swat-tool-link-icons.png
--------------------------------------------------------------------------------
/www/images/admin-dialog-exception.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-dialog-exception.png
--------------------------------------------------------------------------------
/www/images/admin-dialog-question.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-dialog-question.png
--------------------------------------------------------------------------------
/www/images/admin-menu-help-arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-menu-help-arrow.png
--------------------------------------------------------------------------------
/resources/mockups/Swat-Admin-Colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/resources/mockups/Swat-Admin-Colors.png
--------------------------------------------------------------------------------
/resources/mockups/Swat-Admin-Example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/resources/mockups/Swat-Admin-Example.png
--------------------------------------------------------------------------------
/www/images/admin-dialog-exception@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-dialog-exception@2x.png
--------------------------------------------------------------------------------
/www/images/admin-dialog-question@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-dialog-question@2x.png
--------------------------------------------------------------------------------
/www/images/admin-swat-actions-arrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-swat-actions-arrow.png
--------------------------------------------------------------------------------
/www/images/swat-tool-link-icons@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/swat-tool-link-icons@2x.png
--------------------------------------------------------------------------------
/resources/admin-checkbox-radio-buttons.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/resources/admin-checkbox-radio-buttons.xcf
--------------------------------------------------------------------------------
/sql/tables/AdminGroup.sql:
--------------------------------------------------------------------------------
1 | create table AdminGroup (
2 | id serial not null,
3 | title varchar(255),
4 | primary key(id)
5 | );
6 |
7 |
--------------------------------------------------------------------------------
/www/images/admin-swat-actions-arrow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-swat-actions-arrow@2x.png
--------------------------------------------------------------------------------
/www/images/admin-checkbox-radio-buttons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-checkbox-radio-buttons.png
--------------------------------------------------------------------------------
/www/images/admin-message-display-dismiss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-message-display-dismiss.png
--------------------------------------------------------------------------------
/www/images/admin-checkbox-radio-buttons@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-checkbox-radio-buttons@2x.png
--------------------------------------------------------------------------------
/www/images/admin-message-display-dismiss@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-message-display-dismiss@2x.png
--------------------------------------------------------------------------------
/www/images/admin-swat-textarea-editor-icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-swat-textarea-editor-icons.png
--------------------------------------------------------------------------------
/resources/admin-title-link-cell-renderer-icons.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/resources/admin-title-link-cell-renderer-icons.xcf
--------------------------------------------------------------------------------
/www/images/admin-swat-textarea-editor-icons@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-swat-textarea-editor-icons@2x.png
--------------------------------------------------------------------------------
/www/images/admin-title-link-cell-renderer-icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-title-link-cell-renderer-icons.png
--------------------------------------------------------------------------------
/www/images/admin-title-link-cell-renderer-icons@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-title-link-cell-renderer-icons@2x.png
--------------------------------------------------------------------------------
/www/images/admin-swat-textarea-editor-icons-highlight.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-swat-textarea-editor-icons-highlight.png
--------------------------------------------------------------------------------
/www/images/admin-swat-textarea-editor-icons-highlight@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cviebrock/admin/master/www/images/admin-swat-textarea-editor-icons-highlight@2x.png
--------------------------------------------------------------------------------
/www/styles/admin-user-index-page.css:
--------------------------------------------------------------------------------
1 | tr.inactive td.email,
2 | tr.inactive td.name,
3 | tr.inactive td.enabled,
4 | tr.inactive td.last-login {
5 | opacity: 0.5;
6 | }
7 |
--------------------------------------------------------------------------------
/www/styles/admin-change-password-page.css:
--------------------------------------------------------------------------------
1 | /* Change Password Page Styles */
2 |
3 | #change_password_frame {
4 | width: 28em;
5 | margin: 4em auto 1em auto;
6 | text-align: left;
7 | }
8 |
--------------------------------------------------------------------------------
/sql/views/AdminUserLastLoginView.sql:
--------------------------------------------------------------------------------
1 | create or replace view AdminUserLastLoginView as
2 | select usernum, max(login_date) as last_login, instance from AdminUserHistory
3 | group by usernum, instance;
4 |
--------------------------------------------------------------------------------
/www/styles/admin-profile.css:
--------------------------------------------------------------------------------
1 | /* Change Password Page Styles */
2 |
3 | .admin-two-factor-secret {
4 | font-size: 10px;
5 | color: #333;
6 | }
7 |
8 | #two_fa_token_field input {
9 | width: 8rem;
10 | }
11 |
--------------------------------------------------------------------------------
/sql/tables/AdminSection.sql:
--------------------------------------------------------------------------------
1 | create table AdminSection (
2 | id serial NOT null,
3 | title varchar(255),
4 | description varchar(255),
5 | displayorder integer default 0,
6 | visible boolean default true not null,
7 | primary key(id)
8 | );
9 |
--------------------------------------------------------------------------------
/phpstan.dist.neon:
--------------------------------------------------------------------------------
1 | includes:
2 | - phpstan-baseline.neon
3 |
4 | parameters:
5 | phpVersion: 80200
6 | level: 1
7 | paths:
8 | - Admin
9 | - po
10 | editorUrl: '%%file%%:%%line%%'
11 | editorUrlTitle: '%%file%%:%%line%%'
12 |
--------------------------------------------------------------------------------
/sql/tables/AdminUserInstanceBinding.sql:
--------------------------------------------------------------------------------
1 | create table AdminUserInstanceBinding (
2 | usernum int not null references AdminUser(id) on delete cascade,
3 | instance int not null references Instance(id) on delete cascade,
4 | primary key (usernum, instance)
5 | );
6 |
--------------------------------------------------------------------------------
/www/styles/admin-two-factor-authentication-page.css:
--------------------------------------------------------------------------------
1 | /* Change Password Page Styles */
2 |
3 | #two_fa_frame {
4 | width: 28em;
5 | margin: 4em auto 1em auto;
6 | text-align: left;
7 | }
8 |
9 | #two_fa_token_field input {
10 | width: 8rem;
11 | }
12 |
--------------------------------------------------------------------------------
/Admin/layouts/AdminLoginLayout.php:
--------------------------------------------------------------------------------
1 | content;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/sql/tables/AdminUserHistory.sql:
--------------------------------------------------------------------------------
1 | create table AdminUserHistory (
2 | id serial,
3 | usernum integer not null
4 | constraint AdminUserHistory_usernum references AdminUser(id)
5 | on delete cascade,
6 | login_date timestamp,
7 | login_agent varchar(255),
8 | remote_ip varchar(15),
9 | instance integer references Instance(id) on delete cascade,
10 | primary key(id)
11 | );
12 |
13 |
--------------------------------------------------------------------------------
/sql/tables/AdminSubComponent.sql:
--------------------------------------------------------------------------------
1 | create table AdminSubComponent (
2 | id serial not null,
3 | component integer not null
4 | constraint AdminSubcomponent_component references AdminComponent(id)
5 | on delete cascade,
6 | title varchar(255),
7 | shortname varchar(50),
8 | visible boolean default false not null,
9 | displayorder integer default 0,
10 | primary key(id)
11 | );
12 |
13 |
--------------------------------------------------------------------------------
/Admin/exceptions/AdminUserException.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | ./Admin
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/front.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | To get started, choose an item from the menu to the left.
7 |
8 |
9 |
--------------------------------------------------------------------------------
/sql/tables/AdminUserAdminGroupBinding.sql:
--------------------------------------------------------------------------------
1 | create table AdminUserAdminGroupBinding (
2 | usernum integer not null
3 | constraint AdminUserAdminGroupBinding_usernum references AdminUser(id)
4 | on delete cascade,
5 | groupnum integer not null
6 | constraint AdminUserAdminGroupBinding_groupnum references AdminGroup(id)
7 | on delete cascade,
8 | primary key(usernum, groupnum)
9 | );
10 |
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | vendor/
5 | node_modules/
6 |
7 | # misc
8 | .DS_Store
9 | .env
10 | .idea/
11 | .php-cs-fixer.cache
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 | *.swp
16 |
17 | # dependency lock file
18 | composer.lock
19 | yarn.lock
20 |
21 | # overrides for local tooling
22 | /phpstan.neon
23 |
--------------------------------------------------------------------------------
/Admin/AdminDependencyEntryWrapper.php:
--------------------------------------------------------------------------------
1 | row_wrapper_class = AdminDependencyEntry::class;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Admin/AdminDependencySummaryWrapper.php:
--------------------------------------------------------------------------------
1 | row_wrapper_class = AdminDependencySummary::class;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/sql/tables/AdminComponentAdminGroupBinding.sql:
--------------------------------------------------------------------------------
1 | create table AdminComponentAdminGroupBinding (
2 | component integer not null
3 | constraint AdminComponentAdminGroupBinding_component references AdminComponent(id)
4 | on delete cascade,
5 | groupnum integer not null
6 | constraint AdminComponentAdminGroupBinding_groupnum references AdminGroup(id)
7 | on delete cascade,
8 | primary key(component, groupnum)
9 | );
10 |
11 |
--------------------------------------------------------------------------------
/sql/tables/AdminComponent.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE AdminComponent (
2 | id serial NOT NULL,
3 | shortname character varying(255),
4 | title character varying(255),
5 | description text,
6 | displayorder integer DEFAULT 0,
7 | section integer NOT NULL
8 | constraint AdminComponent_section references AdminSection(id)
9 | on delete cascade,
10 | enabled boolean DEFAULT true NOT NULL,
11 | visible boolean DEFAULT true NOT NULL,
12 | primary key(id)
13 | );
14 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 | end_of_line = lf
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 | indent_style = space
13 | indent_size = 2
14 |
15 | [*.php]
16 | indent_size = 4
17 |
18 | [*.{diff,md}]
19 | trim_trailing_whitespace = false
20 |
21 | [Jenkinsfile]
22 | indent_size = 4
23 |
--------------------------------------------------------------------------------
/Admin/AdminTableViewOrderableColumn.php:
--------------------------------------------------------------------------------
1 | link = $_GET['source'] ?? '';
14 | $this->unset_get_vars = ['source'];
15 | parent::displayHeader();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/www/styles/admin-note.css:
--------------------------------------------------------------------------------
1 | .admin-note {
2 | margin: 16px 0;
3 | background: #fff9c4;
4 | color: #888;
5 | color: rgba(0,0,0,0.54);
6 | padding: 12px;
7 | border-radius: 2px;
8 | box-shadow: 0 0 2px rgba(0,0,0,0.12), 0 2px 2px rgba(0,0,0,0.24);
9 | }
10 |
11 | .swat-frame .admin-note {
12 | margin: 0 0 16px;
13 | }
14 |
15 | .admin-note-title {
16 | margin: 0;
17 | color: #888;
18 | color: rgba(0,0,0,0.54);
19 | }
20 |
21 | .admin-note dd {
22 | margin-bottom: 0.5em;
23 | margin-left: 1em;
24 | }
25 |
--------------------------------------------------------------------------------
/Admin/dataobjects/AdminGroupWrapper.php:
--------------------------------------------------------------------------------
1 | row_wrapper_class = AdminGroup::class;
17 | $this->index_field = 'id';
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/sql/triggers/AdminUserHistoryInsertTrigger.sql:
--------------------------------------------------------------------------------
1 | CREATE OR REPLACE FUNCTION updateAdminUserHistory() RETURNS trigger AS $$
2 | BEGIN
3 | delete from AdminUserHistory where usernum = NEW.usernum
4 | AND id not in (select id from AdminUserHistory
5 | where usernum = NEW.usernum order by login_date desc limit 9);
6 |
7 | RETURN NULL;
8 | END;
9 | $$ LANGUAGE 'plpgsql';
10 |
11 | CREATE TRIGGER AdminUserHistoryInsertTrigger AFTER INSERT ON AdminUserHistory
12 | FOR EACH ROW EXECUTE PROCEDURE updateAdminUserHistory();
13 |
--------------------------------------------------------------------------------
/Admin/dataobjects/AdminSectionWrapper.php:
--------------------------------------------------------------------------------
1 | row_wrapper_class = AdminSection::class;
17 | $this->index_field = 'id';
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Admin/AdminMenuSubcomponent.php:
--------------------------------------------------------------------------------
1 | shortname = $shortname;
19 | $this->title = $title;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Admin/dataobjects/AdminComponentWrapper.php:
--------------------------------------------------------------------------------
1 | row_wrapper_class = AdminComponent::class;
17 | $this->index_field = 'id';
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Admin/dataobjects/AdminUserWrapper.php:
--------------------------------------------------------------------------------
1 | row_wrapper_class = SwatDBClassMap::get(AdminUser::class);
18 | $this->index_field = 'id';
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Admin/dataobjects/AdminUserHistoryWrapper.php:
--------------------------------------------------------------------------------
1 | row_wrapper_class = AdminUserHistory::class;
17 | $this->index_field = 'id';
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Admin/dataobjects/AdminSubComponentWrapper.php:
--------------------------------------------------------------------------------
1 | row_wrapper_class = AdminSubComponent::class;
17 | $this->index_field = 'id';
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/include/AdminUserTableView.php:
--------------------------------------------------------------------------------
1 | is_active) {
14 | $classes[] = 'active';
15 | } else {
16 | $classes[] = 'inactive';
17 | }
18 |
19 | return $classes;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/sql/tables/AdminUser.sql:
--------------------------------------------------------------------------------
1 | create table AdminUser (
2 | id serial not null,
3 | email varchar(50) not null,
4 | name varchar(100) not null,
5 | password varchar(255) not null,
6 | password_salt varchar(50),
7 | password_tag varchar(50),
8 | password_tag_date timestamp,
9 | force_change_password boolean not null default true,
10 | enabled boolean not null default true,
11 | all_instances boolean not null default false,
12 | activation_date timestamp,
13 | two_fa_secret varchar(255),
14 | two_fa_enabled boolean not null default false,
15 | two_fa_timeslice integer not null default 0,
16 | primary key(id)
17 | );
18 |
--------------------------------------------------------------------------------
/Admin/AdminMenuSection.php:
--------------------------------------------------------------------------------
1 | id = $id;
21 | $this->title = $title;
22 | $this->components = [];
23 | $this->show = true;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Admin/exceptions/AdminException.php:
--------------------------------------------------------------------------------
1 | getMessage();
16 | $message .= "\n" . $error->getUserInfo();
17 | $code = $error->getCode();
18 | }
19 |
20 | parent::__construct($message, $code);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Admin/exceptions/AdminNotFoundException.php:
--------------------------------------------------------------------------------
1 | title = Admin::_('Not Found');
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/Front.php:
--------------------------------------------------------------------------------
1 | ui->loadFromXML(__DIR__ . '/front.xml');
15 | $this->navbar->popEntry();
16 | }
17 |
18 | // build phase
19 |
20 | protected function buildInternal()
21 | {
22 | $note = $this->ui->getWidget('note');
23 | $note->title = sprintf(
24 | Admin::_('Welcome to the %s Admin!'),
25 | $this->app->config->site->title
26 | );
27 | $this->buildMessages();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Admin/AdminMenuComponent.php:
--------------------------------------------------------------------------------
1 | id = $id;
22 | $this->shortname = $shortname;
23 | $this->title = $title;
24 | $this->description = $description;
25 | $this->subcomponents = [];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Admin/pages/order.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Order Options
8 |
9 |
10 |
11 | Custom Order
12 |
13 |
14 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/phpstan-baseline.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | ignoreErrors:
3 | -
4 | message: "#^Access to an undefined property AdminSessionModule\\:\\:\\$history\\.$#"
5 | count: 1
6 | path: Admin/AdminSessionModule.php
7 |
8 | -
9 | message: "#^Access to an undefined property AdminSessionModule\\:\\:\\$user\\.$#"
10 | count: 10
11 | path: Admin/AdminSessionModule.php
12 |
13 | -
14 | message: "#^Variable \\$transaction might not be defined\\.$#"
15 | count: 1
16 | path: Admin/pages/AdminDBConfirmation.php
17 |
18 | -
19 | message: "#^Variable \\$transaction might not be defined\\.$#"
20 | count: 1
21 | path: Admin/pages/AdminDBEdit.php
22 |
23 | -
24 | message: "#^Variable \\$transaction might not be defined\\.$#"
25 | count: 1
26 | path: Admin/pages/AdminDBOrder.php
27 |
--------------------------------------------------------------------------------
/www/javascript/admin-order.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Adds an order change event handler to a SwatChangeOrder that checks a radio
3 | * button when the order changes
4 | *
5 | * @param String radio_button_id the XHTML id of the radio button to check.
6 | * @param SwatChangeOrder change_order the change order widget to add event
7 | * handlers to.
8 | */
9 | function AdminOrder(radio_button_id, change_order)
10 | {
11 | this.radio_button = document.getElementById(radio_button_id);
12 | if (change_order instanceof SwatChangeOrder)
13 | change_order.orderChangeEvent.subscribe(
14 | this.orderChangeHandler, this, true);
15 | }
16 |
17 | AdminOrder.prototype.orderChangeHandler = function()
18 | {
19 | if (this.radio_button)
20 | this.radio_button.checked = true;
21 | };
22 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/include/HistoryCellRenderer.php:
--------------------------------------------------------------------------------
1 | date !== null) {
18 | echo ' (';
19 | $anchor = new SwatHtmlTag('a');
20 | $anchor->setContent($this->title);
21 | $anchor->href = sprintf('AdminUser/Details&id=%s', $this->user);
22 | $anchor->display();
23 | echo ')';
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Admin/AdminDependencyItem.php:
--------------------------------------------------------------------------------
1 | 0 && password.value.length > 0) {" +
22 | " submit.focus();" +
23 | " } else if (email.value.length > 0 && password.value.length == 0) {" +
24 | " password.focus();" +
25 | " } else {" +
26 | " email.focus();" +
27 | " }" +
28 | "}", 100);
29 | }
30 |
--------------------------------------------------------------------------------
/Admin/components/AdminSection/Edit.php:
--------------------------------------------------------------------------------
1 | getObject()->title
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Admin/pages/confirmation.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | false
12 |
13 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Admin/templates/AdminMSWordTemplate.php:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 | HTML;
18 |
19 | header('Content-Type: application/msword');
20 | header(
21 | 'Content-Disposition: attachment; filename=' . $data->filename . '.doc'
22 | );
23 | echo $data->content;
24 | echo <<<'HTML'
25 |
26 |
27 |
28 |
29 | HTML;
30 | // @codingStandardsIgnoreEnd
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent any
3 | stages {
4 | stage('Install Composer Dependencies') {
5 | steps {
6 | sh 'rm -rf composer.lock vendor/'
7 | sh 'composer install'
8 | }
9 | }
10 |
11 | stage('Check Code Style for Modified Files') {
12 | when {
13 | not {
14 | branch 'master'
15 | }
16 | }
17 | steps {
18 | sh '''
19 | files=$(git diff-tree --diff-filter=ACRM --no-commit-id --name-only -r HEAD)
20 | if [ -n "$files" ]; then
21 | composer run phpcs:ci $files
22 | fi
23 | '''
24 | }
25 | }
26 |
27 | stage('Check Code Style for Entire Project') {
28 | when {
29 | branch 'master'
30 | }
31 | steps {
32 | sh 'composer run phpcs:ci'
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # Description
2 |
3 | Add a description of new changes, the reason for new changes, and how the new
4 | changes work here.
5 |
6 | # Testing Instructions (optional)
7 |
8 | Add step-by-step instructions for testing the PR, if necessary.
9 |
10 | 1. Check out this PR
11 | 2. …
12 |
13 | # Developer Checklist
14 |
15 | Before requesting review for this PR, make sure the following tasks are
16 | complete:
17 |
18 | - [ ] I added a link to the relevant Shortcut story, if applicable
19 | - [ ] I added testing instructions, if any
20 | - [ ] I made sure existing CI checks pass
21 | - [ ] I checked that all requirements of the ticket are fulfilled
22 |
23 | # Reviewer Checklist
24 |
25 | Before merging this PR, make sure the following tasks are complete:
26 |
27 | - [ ] I made sure there are no active labels that block merge
28 | - [ ] I followed the testing instructions
29 | - [ ] I made sure the CI checks pass
30 | - [ ] I reviewed the file changes on GitHub
31 | - [ ] I checked that all requirements of the ticket (if any) are fulfilled
32 |
--------------------------------------------------------------------------------
/Admin/AdminDependencySummary.php:
--------------------------------------------------------------------------------
1 | count = $data->count;
31 | $this->parent = $data->parent;
32 | $this->status_level = $data->status_level;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Admin/components/AdminGroup/Index.php:
--------------------------------------------------------------------------------
1 | ui->loadFromXML(__DIR__ . '/index.xml');
16 | }
17 |
18 | // process phase
19 |
20 | protected function processActions(SwatView $view, SwatActions $actions)
21 | {
22 | switch ($actions->selected->id) {
23 | case 'delete':
24 | $this->app->replacePage('AdminGroup/Delete');
25 | $this->app->getPage()->setItems($view->getSelection());
26 | break;
27 | }
28 | }
29 |
30 | // build phase
31 |
32 | protected function getTableModel(SwatView $view): AdminGroupWrapper
33 | {
34 | $sql = 'select id, title from AdminGroup order by title';
35 |
36 | return SwatDB::query($this->app->db, $sql, AdminGroupWrapper::class);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Admin/layouts/AdminLayout.php:
--------------------------------------------------------------------------------
1 | addHtmlHeadEntrySet($yui->getHtmlHeadEntrySet());
19 |
20 | $this->addHtmlHeadEntry('packages/admin/styles/admin-layout.css');
21 | $this->addHtmlHeadEntry('packages/admin/styles/admin-swat-local.css');
22 | }
23 |
24 | protected function getTagByFlagFile()
25 | {
26 | $tag = null;
27 |
28 | $www_root = dirname($_SERVER['SCRIPT_FILENAME']);
29 | $filename = $www_root . DIRECTORY_SEPARATOR .
30 | '..' . DIRECTORY_SEPARATOR . '.resource-tag';
31 |
32 | if (file_exists($filename) && is_readable($filename)) {
33 | $tag = trim(file_get_contents($filename));
34 | }
35 |
36 | return $tag;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.github/workflows/pull-requests.yml:
--------------------------------------------------------------------------------
1 | name: Pull Requests
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | runner:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Check out repository code
14 | uses: actions/checkout@v4
15 |
16 | - name: Setup PHP with tools
17 | uses: silverorange/actions-setup-php@v2
18 | with:
19 | php-version: '8.2'
20 | extensions: gd # required by silverorange/Site
21 |
22 | - name: Get composer cache directory
23 | id: composer-cache
24 | run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
25 |
26 | - name: Cache dependencies
27 | uses: actions/cache@v4
28 | with:
29 | path: ${{ steps.composer-cache.outputs.dir }}
30 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
31 | restore-keys: ${{ runner.os }}-composer-
32 |
33 | - name: Install PHP dependencies
34 | run: 'composer install'
35 |
36 | - name: Run tests
37 | timeout-minutes: 5
38 | run: |
39 | composer run phpcs:ci
40 | composer run phpstan:ci
41 |
--------------------------------------------------------------------------------
/Admin/AdminGroupLinkCellRenderer.php:
--------------------------------------------------------------------------------
1 | addStyleSheet(
16 | 'packages/admin/styles/admin-group-link-cell-renderer.css'
17 | );
18 | }
19 |
20 | /**
21 | * Gets the array of CSS classes that are applied to this user-interface
22 | * object.
23 | *
24 | * User-interface objects aggregate the list of user-specified classes and
25 | * may add static CSS classes of their own in this method.
26 | *
27 | * @return array the array of CSS classes that are applied to this
28 | * user-interface object
29 | *
30 | * @see SwatUIObject::getCSSClassString()
31 | */
32 | protected function getCSSClassNames()
33 | {
34 | $classes = ['admin-group-link-cell-renderer'];
35 |
36 | return array_merge($classes, $this->classes);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Admin/pages/approval.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Approve
9 |
10 |
11 | Delete
12 | Are you sure you want to delete?
13 |
14 |
15 | Skip
16 |
17 |
18 | text/xml
19 |
20 |
21 |
22 |
23 | text/xml
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/www/styles/admin-login-page.css:
--------------------------------------------------------------------------------
1 | /* Login Page Styles */
2 |
3 | #login_frame,
4 | #forgot_password_frame {
5 | width: 360px;
6 | margin: 4em auto 1em auto;
7 | text-align: left;
8 | }
9 |
10 | #reset_password_frame {
11 | width: 40em;
12 | margin: 4em auto 1em auto;
13 | text-align: left;
14 | }
15 |
16 | #email.swat-entry,
17 | #password.swat-entry,
18 | #confirm_password.swat-entry {
19 | width: 314px;
20 | }
21 |
22 | #forgot_container.swat-displayable-container {
23 | text-align: center;
24 | }
25 |
26 | #forgot.swat-tool-link,
27 | #forgot.swat-tool-link:link,
28 | #forgot.swat-tool-link:visited,
29 | #forgot.swat-tool-link:active,
30 | #forgot.swat-tool-link:hover {
31 | color: #1976d2;
32 | font-size: 11px;
33 | border: 0;
34 | background: transparent;
35 | text-decoration: underline;
36 | padding: 4px 8px;
37 | margin: 1px 0 0 0;
38 | display: inline-block;
39 | cursor: pointer;
40 | text-shadow: none;
41 | border-radius: 0;
42 | box-shadow: none;
43 | }
44 |
45 | #forgot.swat-tool-link .swat-tool-link-title {
46 | text-decoration: underline;
47 | }
48 |
49 | #forgot.swat-tool-link:active,
50 | #forgot.swat-tool-link:hover {
51 | position: static;
52 | top: 0;
53 | left: 0;
54 | color: #666;
55 | }
56 |
--------------------------------------------------------------------------------
/Admin/layouts/AdminMenuXMLRPCServerLayout.php:
--------------------------------------------------------------------------------
1 | initMenu();
22 | }
23 |
24 | /**
25 | * Initializes layout menu view.
26 | */
27 | protected function initMenu()
28 | {
29 | if ($this->menu === null) {
30 | $menu_store = SwatDB::executeStoredProc(
31 | $this->app->db,
32 | 'getAdminMenu',
33 | $this->app->db->quote(
34 | $this->app->session->getUserId(),
35 | 'integer'
36 | ),
37 | AdminMenuStore::class
38 | );
39 |
40 | $class = $this->app->getMenuViewClass();
41 | $this->menu = new $class($menu_store, $this->app);
42 | }
43 |
44 | $this->menu->init();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Admin
2 | =====
3 | Admin is a framework for back-end administration systems. Admin is built using
4 | [Swat](https://github.com/silverorange/swat) and
5 | [Site](https://github.com/silverorange/site).
6 |
7 | Installation
8 | -----------
9 | Make sure the silverorange composer repository is added to the `composer.json`
10 | for the project and then run:
11 |
12 | ```sh
13 | composer require silverorange/admin
14 | ```
15 |
16 | Enabling 2FA (Two Factor Authentication)
17 | -----------
18 | 1. Install the Admin package ≥ `6.1.0`
19 | 2. Add two composer packages:
20 |
21 | ```sh
22 | composer require robthree/twofactorauth
23 | composer require bacon/bacon-qr-code
24 | ```
25 |
26 | 3. Run `composer install`
27 |
28 | 4. Add the new database fields:
29 |
30 | ```sql
31 | alter table adminuser add two_fa_secret varchar(255);
32 | alter table adminuser add two_fa_enabled boolean not null default false;
33 | alter table adminuser add two_fa_timeslice integer not null default 0;
34 | ```
35 |
36 | 5. Edit your `.ini` files (both stage and production) and add:
37 |
38 | ```
39 | [admin]
40 | two_fa_enabled = On
41 | ```
42 |
43 | 6. Let your users know! They will now see 2FA setup in the “Login Settings” in the top-right corner.
44 |
--------------------------------------------------------------------------------
/Admin/exceptions/AdminNoAccessException.php:
--------------------------------------------------------------------------------
1 | user = $user;
32 | $this->title = Admin::_('No Access');
33 | }
34 |
35 | /**
36 | * Gets the user that was denied access.
37 | *
38 | * @return AdminUser the user that was denied access
39 | */
40 | public function getUser()
41 | {
42 | return $this->user;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/www/styles/admin-group-link-cell-renderer.css:
--------------------------------------------------------------------------------
1 | .admin-group-link-cell-renderer:visited,
2 | .admin-group-link-cell-renderer:link,
3 | .admin-group-link-cell-renderer {
4 | cursor: default;
5 | padding: 3px 8px;
6 | text-decoration: none;
7 | margin-left: 8px;
8 | position: relative;
9 | top: -1px;
10 | color: #fff;
11 | background: #009688;
12 | font-weight: 800;
13 | font-family: "Open Sans", sans-serif;
14 | font-size: 11px;
15 | text-transform: uppercase;
16 | border-radius: 2px;
17 | border: 0;
18 | box-shadow: 0 0 2px rgba(0,0,0,0.12), 0 2px 2px rgba(0,0,0,0.24);
19 | transition: box-shadow 0.2s ease;
20 | }
21 |
22 | .admin-group-link-cell-renderer:hover {
23 | background: #00897b;
24 | }
25 |
26 | .admin-group-link-cell-renderer:focus {
27 | outline: none;
28 | border: 0;
29 | box-shadow: 0 0 4px rgba(0,0,0,0.12), 0 4px 4px rgba(0,0,0,0.24);
30 | }
31 |
32 | .admin-group-link-cell-renderer:active {
33 | background: #26a69a;
34 | box-shadow: 0 0 8px rgba(0,0,0,0.12), 0 8px 8px rgba(0,0,0,0.24);
35 | }
36 |
37 | span.admin-group-link-cell-renderer,
38 | span.admin-group-link-cell-renderer:hover,
39 | span.admin-group-link-cell-renderer:active {
40 | opacity: 0.4;
41 | background: #888;
42 | box-shadow: 0 0 2px rgba(0,0,0,0.12), 0 2px 2px rgba(0,0,0,0.24);
43 | }
44 |
--------------------------------------------------------------------------------
/sql/functions/getAdminMenu.mysql.sql:
--------------------------------------------------------------------------------
1 | CREATE PROCEDURE getAdminMenu(param_userid integer)
2 | SELECT AdminComponent.shortname, AdminComponent.title,
3 | AdminComponent.description,
4 | AdminComponent.section, AdminSection.title AS section_title,
5 | AdminComponent.id AS component_id,
6 | AdminSubComponent.title AS subcomponent_title,
7 | AdminSubComponent.shortname AS subcomponent_shortname
8 | FROM AdminComponent
9 |
10 | LEFT OUTER JOIN AdminSubComponent on
11 | AdminSubComponent.component = AdminComponent.id and
12 | AdminSubComponent.visible = true
13 |
14 | INNER JOIN AdminSection ON
15 | AdminComponent.section = AdminSection.id
16 |
17 | WHERE AdminSection.visible = true AND
18 | AdminComponent.enabled = true AND
19 | AdminComponent.visible = true AND
20 | AdminComponent.id IN (
21 | SELECT component
22 | FROM AdminComponentAdminGroupBinding
23 | INNER JOIN AdminUserAdminGroupBinding ON
24 | AdminComponentAdminGroupBinding.groupnum =
25 | AdminUserAdminGroupBinding.groupnum
26 | WHERE AdminUserAdminGroupBinding.usernum = param_userid)
27 |
28 | ORDER BY AdminSection.displayorder, AdminSection.title,
29 | AdminComponent.section, AdminComponent.displayorder,
30 | AdminComponent.title, AdminSubComponent.displayorder,
31 | AdminSubComponent.title;
32 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/Logout.php:
--------------------------------------------------------------------------------
1 | layout->logout_form;
15 | $form->process();
16 |
17 | if ($form->isProcessed() && $form->isAuthenticated()) {
18 | // log out
19 | $this->app->session->logout();
20 | $this->app->relocate($this->app->getBaseHref());
21 | } else {
22 | // add error message
23 | $message = new SwatMessage(
24 | Admin::_('Unable to log out.'),
25 | 'warning'
26 | );
27 |
28 | $message->secondary_content =
29 | Admin::_('In order to ensure your security, we were ' .
30 | 'unable to process your logout request. Please try again.');
31 |
32 | $this->app->messages->add($message);
33 |
34 | // go back where we came from
35 | $url = (isset($_SERVER['HTTP_REFERER'])) ?
36 | $_SERVER['HTTP_REFERER'] : 'Front';
37 |
38 | $this->app->relocate($url);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Admin/AdminTreeTitleLinkCellRenderer.php:
--------------------------------------------------------------------------------
1 | child_count) > 0) {
20 | $this->setFromStock($this->base_stock_id . '-with-contents');
21 | } else {
22 | $this->setFromStock($this->base_stock_id);
23 | }
24 |
25 | // setting stock_id overrides base_stock_id
26 | parent::setStockType();
27 | }
28 |
29 | protected function getTitle()
30 | {
31 | if (intval($this->child_count) === 0) {
32 | return Admin::_('no sub-items');
33 | }
34 |
35 | return sprintf(
36 | Admin::ngettext(
37 | '%d sub-item',
38 | '%d sub-items',
39 | $this->child_count
40 | ),
41 | $this->child_count
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/details.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Login History
6 |
7 |
8 |
14 |
15 | Agent
16 |
17 | login_agent
18 |
19 |
20 |
21 | Remote IP
22 |
23 | remote_ip
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Admin/components/AdminSection/edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Section
6 |
7 |
8 | Title
9 |
10 | true
11 | 255
12 |
13 |
14 |
15 | Show in Menu?
16 |
17 | true
18 |
19 |
20 |
21 | Description
22 |
23 | 255
24 |
25 |
26 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Admin/AdminUniqueEntry.php:
--------------------------------------------------------------------------------
1 | size = 20;
26 | }
27 |
28 | /**
29 | * Processes this unique entry.
30 | *
31 | * Ensures the value entered by the user is unique and if it is not unique
32 | * attaches an error message to the control.
33 | */
34 | public function process()
35 | {
36 | parent::process();
37 |
38 | if ($this->alphanum && preg_match('/[^[:alnum:]\-_]/u', $this->value)) {
39 | $message = Admin::_('The %s field can only contain letters and ' .
40 | 'numbers. Spaces and other special characters are not ' .
41 | 'allowed.');
42 |
43 | $this->addMessage(new SwatMessage($message, 'error'));
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/forgot-password.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Reset Forgotten Password
6 |
7 |
8 |
9 |
10 | Forgot your password? No problem. Simply enter your email address and a link to create a new password will be sent to you.
11 |
12 |
13 | Email
14 |
15 | true
16 | 25
17 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/two_factor_authentication.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Two Factor Authentication
7 |
8 | You’ll need the six-digit code from your authenticator to continue.
9 |
10 |
11 |
12 | Verification code
13 |
14 | false
15 | 6
16 | 7
17 | true
18 |
19 |
20 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Admin/components/AdminGroup/edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Group
7 |
8 |
9 | Title
10 |
11 | true
12 | 255
13 |
14 |
15 |
16 | Component Access
17 | false
18 |
19 |
20 |
21 | Users in Group
22 | false
23 |
24 |
25 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Admin/pages/AdminDBOrder.php:
--------------------------------------------------------------------------------
1 | app->db);
19 | $this->saveDBData();
20 | $transaction->commit();
21 | } catch (SwatDBException $e) {
22 | $transaction->rollback();
23 |
24 | $message = new SwatMessage(
25 | Admin::_('A database error has occured. The item was not saved.'),
26 | 'system-error'
27 | );
28 |
29 | $this->app->messages->add($message);
30 | $e->process();
31 | } catch (SwatException $e) {
32 | $message = new SwatMessage(
33 | Admin::_('An error has occured. The item was not saved.'),
34 | 'system-error'
35 | );
36 |
37 | $this->app->messages->add($message);
38 | $e->process();
39 | }
40 | }
41 |
42 | protected function saveDBData()
43 | {
44 | $this->saveIndexes();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Admin/dataobjects/AdminUserHistory.php:
--------------------------------------------------------------------------------
1 | table = 'AdminUserHistory';
48 | $this->id_field = 'integer:id';
49 | $this->registerDateProperty('login_date');
50 | $this->registerInternalProperty('instance', SiteInstance::class);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/reset-password.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Update Password
6 |
7 |
8 |
9 |
10 | New Password
11 | Password is case-sensitive.
12 |
13 | 4
14 | true
15 |
16 |
17 |
18 | Confirm New Password
19 |
20 |
21 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/Admin/components/AdminSubComponent/edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Sub-Component
7 |
8 |
9 | Title
10 |
11 | true
12 | 255
13 |
14 |
15 |
16 | Short Name
17 |
18 | true
19 | 50
20 |
21 |
22 |
23 | Show in menu?
24 |
25 | true
26 |
27 |
28 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Admin/AdminTwoFactorAuthentication.php:
--------------------------------------------------------------------------------
1 | createSecret();
19 | }
20 |
21 | public function getQrCodeDataUri($title, $secret, $size = 400)
22 | {
23 | $two_fa = new TwoFactorAuth(new BaconQrCodeProvider());
24 |
25 | return $two_fa->getQRCodeImageAsDataUri($title, $secret, $size);
26 | }
27 |
28 | public function validateToken($secret, $token, &$timeslice)
29 | {
30 | // strip all non numeric characters like spaces and dashes that people
31 | // might enter (e.g. Authy adds spaces for readability)
32 | $token = preg_replace('/[^0-9]/', '', $token);
33 |
34 | // The timeslice is used to make sure tokens before this
35 | // can't be used to authenticate again. There's a "window" of token
36 | // use and without this, someone could capture the code, and re-use it.
37 | $two_fa = new TwoFactorAuth(new BaconQrCodeProvider());
38 | $success = $two_fa->verifyCode($secret, $token, 1, null, $timeslice);
39 |
40 | return $success;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Admin/AdminNavBar.php:
--------------------------------------------------------------------------------
1 | link !== null && $link) {
31 | echo '';
32 | $a_tag = new SwatHtmlTag('a');
33 | if ($first) {
34 | $a_tag->class = 'swat-navbar-first';
35 | }
36 |
37 | $a_tag->href = $entry->link;
38 | $a_tag->setContent($entry->title);
39 | $a_tag->display();
40 | echo '
';
41 | } else {
42 | parent::displayEntry($entry, $link, $first);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Admin/AdminDependencyEntry.php:
--------------------------------------------------------------------------------
1 | id = $data->id;
50 | $this->title = $data->title;
51 | $this->parent = $data->parent;
52 | $this->status_level = $data->status_level;
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Admin/templates/AdminLoginTemplate.php:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {$data->title}
21 |
22 |
23 |
24 |
25 |
26 | {$data->html_head_entries}
27 |
28 | body_classes}>
29 |
30 | {$data->content}
31 |
32 |
33 |
34 |
35 | HTML;
36 | // @codingStandardsIgnoreEnd
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Admin/AdminTreeControlCellRenderer.php:
--------------------------------------------------------------------------------
1 | visible) {
24 | return;
25 | }
26 |
27 | $this->width = 22;
28 | $this->height = 22;
29 |
30 | if ($this->childcount == 0) {
31 | $this->title = Admin::_('View Details');
32 | $this->alt = Admin::_('Details');
33 | $this->image = 'packages/admin/images/admin-generic-document.png';
34 | } else {
35 | $this->title = sprintf(
36 | Admin::ngettext(
37 | 'View Details (%s sub-item)',
38 | 'View Details (%s sub-items)',
39 | $this->childcount
40 | ),
41 | SwatString::numberFormat($this->childcount)
42 | );
43 |
44 | $this->alt = Admin::_('Details');
45 | $this->image =
46 | 'packages/admin/images/admin-document-with-contents.png';
47 | }
48 |
49 | parent::render();
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Admin/components/AdminGroup/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Groups
7 |
8 |
9 | New Group
10 | AdminGroup/Edit
11 | create
12 |
13 |
14 |
15 |
16 |
17 |
18 | id
19 |
20 |
21 |
22 | Title
23 |
24 | title
25 | AdminGroup/Edit?id=%s
26 | id
27 | edit
28 |
29 |
30 |
31 |
32 |
33 | delete…
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Admin/dataobjects/AdminGroup.php:
--------------------------------------------------------------------------------
1 | table = 'AdminGroup';
36 | $this->id_field = 'integer:id';
37 | }
38 |
39 | /**
40 | * Loads the components that this group has access to.
41 | *
42 | * @return AdminComponentWrapper the components this group has access to
43 | */
44 | protected function loadComponents()
45 | {
46 | $sql = sprintf(
47 | 'select AdminComponent.*
48 | from AdminComponent
49 | inner join AdminComponentAdmingroupBinding on
50 | AdminComponentAdminGroupBinding.component =
51 | AdminComponent.id
52 | where groupnum = %s',
53 | $this->db->quote($this->id, 'integer')
54 | );
55 |
56 | return SwatDB::query($this->db, $sql, AdminComponentWrapper::class);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/po/Makefile:
--------------------------------------------------------------------------------
1 | ##
2 | ## i18n Makefile
3 | ##
4 |
5 | DOMAIN = admin
6 | XGETTEXT = xgettext --from-code=UTF-8
7 | MSGFMT = msgfmt -c
8 | MSGMERGE = msgmerge --no-fuzzy-matching
9 | MSGUNIQ = msguniq
10 | INSTALL = install
11 | INSTALLDIR = mkdir -p
12 | DATA_MODE = 660
13 |
14 | TRANSLATIONS := en_CA en_US en_GB
15 | MO_FILES := $(patsubst %,%.mo, $(TRANSLATIONS))
16 |
17 | POTFILE = $(DOMAIN).pot
18 |
19 | TARGETS = $(MO_FILES)
20 |
21 | LOCALEDIR = ../locale
22 |
23 | ######################################
24 |
25 | all:
26 | make $(TARGETS)
27 |
28 | potfiles-php:
29 | rm -f $@-t1 $@-t2 $@
30 | (sed -e '/^#/d' < potfiles.in) > $@-t1
31 | (for i in `cat $@-t1`; do find $$i -name \*.php; done) >> $@-t2
32 | (for i in `cat $@-t2`; do php preprocess.php $$i > $$i.gettext; echo $$i.gettext; done) >> $@
33 | rm -f $@-t1 $@-t2
34 |
35 | potfiles-xml:
36 | rm -f $@-t $@
37 | (sed -e '/^#/d' < potfiles.in) > $@-t
38 | (for i in `cat $@-t`; do find $$i -name \*.xml; done) >> $@
39 | rm -f $@-t
40 |
41 | pot: $(POTFILE)
42 |
43 | $(POTFILE): potfiles-php potfiles-xml
44 | $(XGETTEXT) -o $(POTFILE) -L Php -f potfiles-php
45 | $(XGETTEXT) -j -o $(POTFILE) -L Glade -f potfiles-xml
46 | $(MSGUNIQ) -o $(POTFILE) $(POTFILE)
47 | for i in `cat potfiles-php`; do rm -f $$i; done
48 | rm -f potfiles-php
49 |
50 | %.mo: %.po
51 | $(MSGFMT) -o $@ $<
52 |
53 | install:
54 | for i in $(TRANSLATIONS); do \
55 | $(INSTALLDIR) $(LOCALEDIR)/$$i/LC_MESSAGES ; \
56 | $(INSTALL) -m $(DATA_MODE) $$i.mo $(LOCALEDIR)/$$i/LC_MESSAGES/$(DOMAIN).mo ; \
57 | done
58 |
59 | update: pot
60 | for i in $(TRANSLATIONS); do \
61 | $(MSGMERGE) -U $$i.po $(POTFILE); \
62 | done
63 |
64 | clean:
65 | rm -f potfiles-* *.mo *.pot
66 |
--------------------------------------------------------------------------------
/dependencies/admin.yaml:
--------------------------------------------------------------------------------
1 | ##
2 | ## Static Web-resource dependencies for the Admin package
3 | ##
4 | ## Copyright (c) 2010 silverorange
5 | ##
6 | ## This library is free software; you can redistribute it and/or modify
7 | ## it under the terms of the GNU Lesser General Public License as
8 | ## published by the Free Software Foundation; either version 2.1 of the
9 | ## License, or (at your option) any later version.
10 | ##
11 | ## This library is distributed in the hope that it will be useful,
12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 | ## Lesser General Public License for more details.
15 | ##
16 | ## You should have received a copy of the GNU Lesser General Public
17 | ## License along with this library; if not, write to the Free Software
18 | ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 | ##
20 | Admin:
21 | Depends:
22 | # Package Dependencies
23 | - Swat
24 | - Site
25 |
26 | Provides:
27 |
28 | # JavaScript resources
29 | packages/admin/javascript/admin-login.js:
30 | packages/admin/javascript/admin-menu.js:
31 | packages/admin/javascript/admin-order.js:
32 |
33 | # Style-sheet resources
34 | packages/admin/styles/admin-approval-page.css:
35 | packages/admin/styles/admin-change-password-page.css:
36 | packages/admin/styles/admin-group-link-cell-renderer.css:
37 | packages/admin/styles/admin-layout.css:
38 | packages/admin/styles/admin-login-page.css:
39 | packages/admin/styles/admin-menu.css:
40 | packages/admin/styles/admin-note.css:
41 | packages/admin/styles/admin-title-link-cell-renderer.css:
42 |
43 | # vim: set expandtab tabstop=2 shiftwidth=2 softtabstop=2:
44 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/login-history.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Admin User Login History
6 |
7 |
8 |
9 | Email
10 |
11 | email
12 | AdminUser/Details?id=%s
13 | usernum
14 |
15 |
16 |
17 | Login Time
18 |
19 | login_date
20 |
21 |
22 |
23 | Agent
24 |
25 | login_agent
26 |
27 |
28 |
29 | Remote IP
30 |
31 | remote_ip
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Admin/components/AdminSection/Order.php:
--------------------------------------------------------------------------------
1 | parent = SiteApplication::initVar('parent');
20 | $form = $this->ui->getWidget('order_form');
21 | $form->addHiddenField('parent', $this->parent);
22 | }
23 |
24 | // process phase
25 |
26 | protected function saveIndex($id, $index)
27 | {
28 | SwatDB::updateColumn(
29 | $this->app->db,
30 | 'AdminSection',
31 | 'integer:displayorder',
32 | $index,
33 | 'integer:id',
34 | [$id]
35 | );
36 | }
37 |
38 | // build phase
39 |
40 | protected function buildInternal()
41 | {
42 | $frame = $this->ui->getWidget('order_frame');
43 | $frame->title = Admin::_('Order Sections');
44 | parent::buildInternal();
45 | }
46 |
47 | protected function loadData()
48 | {
49 | $order_widget = $this->ui->getWidget('order');
50 | $order_widget->addOptionsByArray(SwatDB::getOptionArray(
51 | $this->app->db,
52 | 'AdminSection',
53 | 'title',
54 | 'id',
55 | 'displayorder, title'
56 | ));
57 |
58 | $sql = 'select sum(displayorder) from AdminSection';
59 | $sum = SwatDB::queryOne($this->app->db, $sql, 'integer');
60 | $options_list = $this->ui->getWidget('options');
61 | $options_list->value = ($sum == 0) ? 'auto' : 'custom';
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Admin/components/AdminGroup/Delete.php:
--------------------------------------------------------------------------------
1 | getItemList('integer');
19 | $sql = sprintf($sql, $item_list);
20 | $num = SwatDB::exec($this->app->db, $sql);
21 |
22 | $message = new SwatMessage(sprintf(
23 | Admin::ngettext(
24 | 'One admin group has been deleted.',
25 | '%s admin groups have been deleted.',
26 | $num
27 | ),
28 | SwatString::numberFormat($num)
29 | ));
30 |
31 | $this->app->messages->add($message);
32 | }
33 |
34 | // build phase
35 |
36 | protected function buildInternal()
37 | {
38 | parent::buildInternal();
39 |
40 | $item_list = $this->getItemList('integer');
41 |
42 | $dep = new AdminListDependency();
43 | $dep->setTitle(Admin::_('group'), Admin::_('groups'));
44 | $dep->entries = AdminListDependency::queryEntries(
45 | $this->app->db,
46 | 'AdminGroup',
47 | 'integer:id',
48 | null,
49 | 'text:title',
50 | 'title',
51 | 'id in (' . $item_list . ')',
52 | AdminDependency::DELETE
53 | );
54 |
55 | $message = $this->ui->getWidget('confirmation_message');
56 | $message->content = $dep->getMessage();
57 | $message->content_type = 'text/xml';
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/sql/functions/getAdminMenu.pgsql.sql:
--------------------------------------------------------------------------------
1 | CREATE TYPE type_admin_menu AS (
2 | shortname varchar(255),
3 | title varchar(255),
4 | description text,
5 | section integer,
6 | section_title varchar(255),
7 | component_id integer,
8 | subcomponent_title varchar(255),
9 | subcomponent_shortname varchar(255)
10 | );
11 |
12 | CREATE OR REPLACE FUNCTION getAdminMenu(integer) RETURNS SETOF type_admin_menu AS $$
13 | DECLARE
14 | param_userid ALIAS FOR $1;
15 | returned_row type_admin_menu%ROWTYPE;
16 | BEGIN
17 | FOR returned_row IN
18 | SELECT AdminComponent.shortname, AdminComponent.title,
19 | AdminComponent.description,
20 | AdminComponent.section, AdminSection.title AS section_title,
21 | AdminComponent.id,
22 | AdminSubComponent.title as subcomponent_title,
23 | AdminSubCOmponent.shortname as subcomponent_shortname
24 | FROM AdminComponent
25 |
26 | LEFT OUTER JOIN AdminSubComponent on
27 | AdminSubComponent.component = AdminComponent.id and
28 | AdminSUbComponent.visible = true
29 |
30 | INNER JOIN AdminSection ON
31 | AdminComponent.section = AdminSection.id
32 |
33 | WHERE AdminSection.visible = true AND
34 | AdminComponent.enabled = true AND
35 | AdminComponent.visible = true AND
36 | AdminComponent.id IN (
37 | SELECT component
38 | FROM AdminComponentAdminGroupBinding
39 | INNER JOIN AdminUserAdminGroupBinding ON
40 | AdminComponentAdminGroupBinding.groupnum =
41 | AdminUserAdminGroupBinding.groupnum
42 | WHERE AdminUserAdminGroupBinding.usernum = param_userid)
43 |
44 | ORDER BY AdminSection.displayorder, AdminSection.title,
45 | AdminComponent.section, AdminComponent.displayorder,
46 | AdminComponent.title, AdminSubComponent.displayorder,
47 | AdminSubComponent.title
48 | LOOP
49 | RETURN NEXT returned_row;
50 | END LOOP;
51 |
52 | RETURN;
53 | END;
54 | $$ LANGUAGE 'plpgsql';
55 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/Exception.php:
--------------------------------------------------------------------------------
1 | app, AdminDefaultTemplate::class);
18 | }
19 |
20 | // init phase
21 |
22 | public function init()
23 | {
24 | parent::init();
25 |
26 | $this->container = new SwatFrame();
27 | $this->container->classes[] = 'admin-exception-container';
28 | }
29 |
30 | // build phase
31 |
32 | public function build()
33 | {
34 | parent::build();
35 | if (isset($this->layout->navbar)) {
36 | $this->layout->navbar->popEntry();
37 | $this->layout->navbar->popEntry();
38 | $this->layout->navbar->createEntry('Error');
39 | }
40 | }
41 |
42 | protected function display()
43 | {
44 | ob_start();
45 |
46 | printf('%s
', $this->getSummary());
47 |
48 | echo 'This error has been reported.
';
49 |
50 | if ($this->exception !== null) {
51 | $this->exception->process(false);
52 | }
53 |
54 | $content_block = new SwatContentBlock();
55 | $content_block->content = ob_get_clean();
56 | $content_block->content_type = 'text/xml';
57 |
58 | $this->container->add($content_block);
59 | $this->container->display();
60 | }
61 |
62 | // finalize phase
63 |
64 | public function finalize()
65 | {
66 | parent::finalize();
67 | $this->layout->addHtmlHeadEntrySet(
68 | $this->container->getHtmlHeadEntrySet()
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Admin/AdminPagination.php:
--------------------------------------------------------------------------------
1 | id, $_GET)) {
28 | $this->setCurrentPage($_GET[$this->id]);
29 | }
30 | }
31 |
32 | /**
33 | * Gets the base link for all page links.
34 | *
35 | * This removes all unwanted variables from the current HTTP GET variables
36 | * and adds all wanted variables ones back into the link string.
37 | *
38 | * @return string the base link for all pages with cleaned HTTP GET
39 | * variables
40 | */
41 | protected function getLink()
42 | {
43 | $vars = $_GET;
44 |
45 | $this->unset_get_vars[] = $this->id;
46 | $this->unset_get_vars[] = 'source';
47 |
48 | foreach ($vars as $name => $value) {
49 | if (in_array($name, $this->unset_get_vars)) {
50 | unset($vars[$name]);
51 | }
52 | }
53 |
54 | if ($this->link === null) {
55 | $link = '?';
56 | } else {
57 | $link = $this->link . '?';
58 | }
59 |
60 | foreach ($vars as $name => $value) {
61 | $link .= $name . '=' . urlencode($value) . '&';
62 | }
63 |
64 | $link .= urlencode($this->id) . '=%s';
65 |
66 | return $link;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/login.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Login
6 |
7 |
8 | Email
9 | SHOW_OPTIONAL
10 |
11 | true
12 | 25
13 | 1
14 |
15 |
16 |
17 | Password
18 | SHOW_OPTIONAL
19 |
20 | true
21 | 18
22 | 2
23 |
24 |
25 |
26 |
27 | Forgot your password?
28 | AdminSite/ForgotPassword
29 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Admin/AdminDateLinkCellRenderer.php:
--------------------------------------------------------------------------------
1 | date = $this->date;
55 | $date_renderer->format = $this->format;
56 | $date_renderer->time_zone_format = $this->time_zone_format;
57 | $date_renderer->display_time_zone = $this->display_time_zone;
58 |
59 | ob_start();
60 | $date_renderer->render();
61 |
62 | return ob_get_clean();
63 | }
64 |
65 | protected function getTitle()
66 | {
67 | return parent::getText();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/change-password.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Change Password
7 |
8 | The password provided to you was automatically generated. For your security, please choose a new password for your account.
9 |
10 |
11 |
12 | Old Password
13 |
14 | false
15 | 4
16 | true
17 |
18 |
19 |
20 | New Password
21 |
22 | false
23 | 4
24 | true
25 |
26 |
27 |
28 | Confirm New Password
29 |
30 | true
31 |
32 |
33 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Admin/dataobjects/AdminSection.php:
--------------------------------------------------------------------------------
1 | table = 'AdminSection';
60 | $this->id_field = 'integer:id';
61 | }
62 |
63 | /**
64 | * @return AdminComponentWrapper
65 | */
66 | protected function loadComponents()
67 | {
68 | $sql = sprintf(
69 | 'select * from AdminComponent
70 | where section = %s
71 | order by displayorder, title',
72 | $this->db->quote($this->id, 'integer')
73 | );
74 |
75 | return SwatDB::query($this->db, $sql, AdminComponentWrapper::class);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Admin/components/AdminComponent/Order.php:
--------------------------------------------------------------------------------
1 | parent = SiteApplication::initVar('parent');
20 | $form = $this->ui->getWidget('order_form');
21 | $form->addHiddenField('parent', $this->parent);
22 | }
23 |
24 | // process phase
25 |
26 | protected function saveIndex($id, $index)
27 | {
28 | SwatDB::updateColumn(
29 | $this->app->db,
30 | 'AdminComponent',
31 | 'integer:displayorder',
32 | $index,
33 | 'integer:id',
34 | [$id]
35 | );
36 | }
37 |
38 | // build phase
39 |
40 | protected function buildInternal()
41 | {
42 | $frame = $this->ui->getWidget('order_frame');
43 | $frame->title = Admin::_('Order Components');
44 | parent::buildInternal();
45 | }
46 |
47 | protected function loadData()
48 | {
49 | $where_clause = sprintf(
50 | 'section = %s',
51 | $this->app->db->quote($this->parent, 'integer')
52 | );
53 |
54 | $order_widget = $this->ui->getWidget('order');
55 | $order_widget->addOptionsByArray(SwatDB::getOptionArray(
56 | $this->app->db,
57 | 'AdminComponent',
58 | 'title',
59 | 'id',
60 | 'displayorder, title',
61 | $where_clause
62 | ));
63 |
64 | $sql = 'select sum(displayorder) from AdminComponent where ' .
65 | $where_clause;
66 |
67 | $sum = SwatDB::queryOne($this->app->db, $sql, 'integer');
68 | $options_list = $this->ui->getWidget('options');
69 | $options_list->value = ($sum == 0) ? 'auto' : 'custom';
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Admin/AdminSearchOperatorFlydown.php:
--------------------------------------------------------------------------------
1 | visible) {
29 | return;
30 | }
31 |
32 | $this->options = [];
33 | $this->show_blank = false;
34 |
35 | foreach ($this->operators as $op) {
36 | $this->addOption($op, self::getOperatorTitle($op));
37 | }
38 |
39 | parent::display();
40 | }
41 |
42 | private static function getOperatorTitle($id)
43 | {
44 | switch ($id) {
45 | case AdminSearchClause::OP_EQUALS:
46 | return Admin::_('is');
47 |
48 | case AdminSearchClause::OP_GT:
49 | return '>';
50 |
51 | case AdminSearchClause::OP_GTE:
52 | return '>=';
53 |
54 | case AdminSearchClause::OP_LT:
55 | return '<';
56 |
57 | case AdminSearchClause::OP_LTE:
58 | return '<=';
59 |
60 | case AdminSearchClause::OP_CONTAINS:
61 | return Admin::_('contains');
62 |
63 | case AdminSearchClause::OP_STARTS_WITH:
64 | return Admin::_('starts with');
65 |
66 | case AdminSearchClause::OP_ENDS_WITH:
67 | return Admin::_('ends with');
68 |
69 | default:
70 | throw new Exception('AdminSearchOperatorFlydown: unknown operator');
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/.php-cs-fixer.php:
--------------------------------------------------------------------------------
1 | in(__DIR__);
9 |
10 | return (new Config())
11 | ->setParallelConfig(ParallelConfigFactory::detect(null, null, 2**18-1))
12 | ->setRules([
13 | '@PhpCsFixer' => true,
14 | '@PHP82Migration' => true,
15 | 'indentation_type' => true,
16 |
17 | // Overrides for (opinionated) @PhpCsFixer and @Symfony rules:
18 |
19 | // Align "=>" in multi-line array definitions, unless a blank line exists between elements
20 | 'binary_operator_spaces' => ['operators' => ['=>' => 'align_single_space_minimal']],
21 |
22 | // Subset of statements that should be proceeded with blank line
23 | 'blank_line_before_statement' => ['statements' => ['case', 'continue', 'declare', 'default', 'return', 'throw', 'try', 'yield', 'yield_from']],
24 |
25 | // Enforce space around concatenation operator
26 | 'concat_space' => ['spacing' => 'one'],
27 |
28 | // Use {} for empty loop bodies
29 | 'empty_loop_body' => ['style' => 'braces'],
30 |
31 | // Don't change any increment/decrement styles
32 | 'increment_style' => false,
33 |
34 | // Forbid multi-line whitespace before the closing semicolon
35 | 'multiline_whitespace_before_semicolons' => ['strategy' => 'no_multi_line'],
36 |
37 | // Clean up PHPDocs, but leave @inheritDoc entries alone
38 | 'no_superfluous_phpdoc_tags' => ['allow_mixed' => true, 'remove_inheritdoc' => false],
39 |
40 | // Ensure that traits are listed first in classes
41 | // (it would be nice to enforce more, but we'll start simple)
42 | 'ordered_class_elements' => ['order' => ['use_trait']],
43 |
44 | // Ensure that param and return types are sorted consistently, with null at end
45 | 'phpdoc_types_order' => ['sort_algorithm' => 'alpha', 'null_adjustment' => 'always_last'],
46 |
47 | // Yoda style is too weird
48 | 'yoda_style' => false,
49 | ])
50 | ->setIndent(' ')
51 | ->setLineEnding("\n")
52 | ->setFinder($finder);
53 |
--------------------------------------------------------------------------------
/Admin/AdminNote.php:
--------------------------------------------------------------------------------
1 | addStyleSheet('packages/admin/styles/admin-note.css');
29 | }
30 |
31 | /**
32 | * Displays this content.
33 | *
34 | * Merely performs an echo of the content.
35 | */
36 | public function display()
37 | {
38 | if (!$this->visible) {
39 | return;
40 | }
41 |
42 | SwatWidget::display();
43 |
44 | $div = new SwatHtmlTag('div');
45 | $div->id = $this->id;
46 | $div->class = $this->getCSSClassString();
47 | $div->open();
48 |
49 | if ($this->title != '') {
50 | $header_tag = new SwatHtmlTag('h3');
51 | $header_tag->class = 'admin-note-title';
52 | $header_tag->setContent($this->title);
53 | $header_tag->display();
54 | }
55 |
56 | if ($this->content != '') {
57 | $content_div = new SwatHtmlTag('div');
58 | $content_div->class = 'admin-note-content';
59 | $content_div->setContent($this->content, $this->content_type);
60 | $content_div->display();
61 | }
62 |
63 | $div->close();
64 | }
65 |
66 | /**
67 | * Gets the array of CSS classes that are applied to this note.
68 | *
69 | * @return array the array of CSS classes that are applied to this note
70 | */
71 | protected function getCSSClassNames()
72 | {
73 | $classes = ['admin-note'];
74 |
75 | return array_merge($classes, $this->classes);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Admin/templates/AdminDefaultTemplate.php:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | {$data->title}
21 |
22 |
23 |
24 |
25 |
26 | {$data->html_head_entries}
27 |
28 | body_classes}>
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | {$data->header}
38 |
39 | {$data->navbar}
40 |
41 |
42 | {$data->content}
43 |
44 |
45 |
46 |
47 | {$data->menu}
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | HTML;
57 | // @codingStandardsIgnoreEnd
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Admin/AdminUI.php:
--------------------------------------------------------------------------------
1 | getWidget($widget_id)->value;
35 | }
36 |
37 | return $values;
38 | }
39 |
40 | /**
41 | * Sets values of widgets.
42 | *
43 | * Convenience method to set values of multiple widgets at once.
44 | * This method is useful when using {@link SwatDB::rowQuery()}
45 | * but only works if the widget id and field name are the same, if this
46 | * is not the case you should manually set the values.
47 | *
48 | * If a widget id-value pair is passed for a widget that does not exist,
49 | * that value is ignored.
50 | *
51 | * @param array $values an array of widget values indexed by widget ids
52 | */
53 | public function setValues(array $values)
54 | {
55 | foreach ($values as $id => $value) {
56 | try {
57 | $widget = $this->getWidget($id);
58 | $widget->value = $values[$id];
59 | } catch (SwatWidgetNotFoundException $e) {
60 | // ignore
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Admin/components/AdminComponent/edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Component
7 |
8 |
9 | Title
10 |
11 | true
12 | 255
13 |
14 |
15 |
16 | Short Name
17 |
18 | true
19 | 255
20 |
21 |
22 |
23 | Section
24 |
25 | true
26 |
27 |
28 |
29 | Show in Menu?
30 |
31 | true
32 |
33 |
34 |
35 | Enabled?
36 |
37 | true
38 |
39 |
40 |
41 | Description
42 |
43 |
44 |
45 | Groups
46 |
47 |
48 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/Admin/Admin.php:
--------------------------------------------------------------------------------
1 | '0',
63 | 'admin.two_fa_enabled' => false,
64 | ];
65 | }
66 |
67 | public static function init()
68 | {
69 | if (self::$is_initialized) {
70 | return;
71 | }
72 |
73 | Swat::init();
74 | Site::init();
75 |
76 | self::setupGettext();
77 |
78 | SwatUI::mapClassPrefixToPath('Admin', 'Admin');
79 |
80 | self::$is_initialized = true;
81 | }
82 |
83 | /**
84 | * Prevent instantiation of this static class.
85 | */
86 | private function __construct() {}
87 | }
88 |
--------------------------------------------------------------------------------
/www/styles/admin-menu.css:
--------------------------------------------------------------------------------
1 | /* Admin menu styles */
2 |
3 | .admin-menu {
4 | margin-top: 7px;
5 | margin-bottom: 1em;
6 | color: #666;
7 | }
8 |
9 | /* layouts with menu on left */
10 | .yui-t1 .admin-menu,
11 | .yui-t2 .admin-menu,
12 | .yui-t3 .admin-menu {
13 | margin-left: 1em;
14 | }
15 |
16 | /* layouts with menu on right */
17 | .yui-t4 .admin-menu,
18 | .yui-t5 .admin-menu,
19 | .yui-t6 .admin-menu {
20 | margin-right: 1em;
21 | }
22 |
23 | .admin-menu ul {
24 | list-style-type: none;
25 | margin: 0;
26 | padding: 0;
27 | color: #837352;
28 | }
29 |
30 | .admin-menu ul .menu-section-title {
31 | display: block;
32 | margin: 0;
33 | padding: 12px 12px 2px;
34 | color: #888;
35 | font-size: 85%;
36 | }
37 |
38 | .admin-menu ul .menu-section-title span {
39 | display: block;
40 | }
41 |
42 | .admin-menu li li a {
43 | display: block;
44 | padding: 3px 12px;
45 | }
46 |
47 | .admin-menu ul ul {
48 | display: block;
49 | list-style-type: none;
50 | padding: 0;
51 | }
52 |
53 | .admin-menu ul ul ul li {
54 | padding-left: 18px;
55 | font-size: 85%;
56 | }
57 |
58 | .admin-menu a:link,
59 | .admin-menu a:visited {
60 | text-decoration: none;
61 | }
62 |
63 | .admin-menu a:hover {
64 | background: #e2e0df;
65 | }
66 |
67 | .admin-menu ul ul li {
68 | position: relative;
69 | }
70 |
71 | .admin-menu ul .admin-menu-help {
72 | position: absolute;
73 | right: -186px;
74 | width: 200px;
75 | top: -13px;
76 | display: none;
77 | }
78 |
79 | .admin-menu ul .admin-menu-help-arrow {
80 | position: absolute;
81 | left: 1px;
82 | top: 14px;
83 | background: url(../images/admin-menu-help-arrow.png) no-repeat 0 0;
84 | width: 19px;
85 | height: 19px;
86 | display: block;
87 | }
88 |
89 | .admin-menu ul .admin-menu-help-content {
90 | margin-left: 19px;
91 | display: block;
92 | background: #111;
93 | background: rgba(0,0,0,0.9);
94 | color: #eee;
95 | padding: 12px 16px;
96 | border-radius: 4px;
97 | box-shadow: 0 2px 2px -1px rgba(0,0,0,0.5);
98 | }
99 |
100 | .admin-menu ul .admin-menu-help-content p:last-child {
101 | margin-bottom: 0;
102 | }
103 |
104 |
105 | /* Menu-hiding styles */
106 |
107 | body .admin-menu-hide img {
108 | display: none;
109 | }
110 |
111 | body .admin-menu-show {
112 | display: none;
113 | }
114 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "silverorange/admin",
3 | "description": "Framework for backend admin website.",
4 | "type": "library",
5 | "keywords": [
6 | "framework",
7 | "admin",
8 | "management"
9 | ],
10 | "homepage": "https://github.com/silverorange/admin",
11 | "license": "LGPL-2.1",
12 | "authors": [
13 | {
14 | "name": "Charles Waddell",
15 | "email": "charles@silverorange.com"
16 | },
17 | {
18 | "name": "Isaac Grant",
19 | "email": "isaac@silverorange.com"
20 | },
21 | {
22 | "name": "Michael Gauthier",
23 | "email": "mike@silverorange.com"
24 | },
25 | {
26 | "name": "Nathan Frederikson",
27 | "email": "nathan@silverorange.com"
28 | },
29 | {
30 | "name": "Nick Burka",
31 | "email": "nick@silverorange.com"
32 | },
33 | {
34 | "name": "Steven Garrity",
35 | "email": "steven@silverorange.com"
36 | }
37 | ],
38 | "repositories": [
39 | {
40 | "type": "composer",
41 | "url": "https://composer.silverorange.com",
42 | "only": [
43 | "silverorange/*"
44 | ]
45 | }
46 | ],
47 | "require": {
48 | "php": ">=8.2.0",
49 | "ext-mbstring": "*",
50 | "silverorange/site": "^15.3.2",
51 | "silverorange/swat": "^7.9.2"
52 | },
53 | "require-dev": {
54 | "bacon/bacon-qr-code": "^3.0",
55 | "friendsofphp/php-cs-fixer": "3.64.0",
56 | "phpstan/phpstan": "^1.12",
57 | "robthree/twofactorauth": "^3.0"
58 | },
59 | "suggest": {
60 | "robthree/twofactorauth": "required for the use of two factor authentication",
61 | "bacon/bacon-qr-code": "required to show QR codes for two factor auth"
62 | },
63 | "autoload": {
64 | "classmap": [
65 | "Admin/"
66 | ]
67 | },
68 | "scripts": {
69 | "phpcs": "./vendor/bin/php-cs-fixer check -v",
70 | "phpcs:ci": "./vendor/bin/php-cs-fixer check --config=.php-cs-fixer.php --no-interaction --show-progress=none --diff --using-cache=no -vvv",
71 | "phpcs:write": "./vendor/bin/php-cs-fixer fix -v",
72 | "phpstan": "./vendor/bin/phpstan analyze",
73 | "phpstan:ci": "./vendor/bin/phpstan analyze -vvv --no-progress --memory-limit 2G",
74 | "phpstan:baseline": "./vendor/bin/phpstan analyze --generate-baseline"
75 | },
76 | "config": {
77 | "sort-packages": true,
78 | "allow-plugins": {
79 | "php-http/discovery": true
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Admin/components/AdminSection/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Sections
7 |
8 |
9 | New Section
10 | AdminSection/Edit
11 | create
12 |
13 |
14 | Change Order
15 | AdminSection/Order
16 | change-order
17 |
18 |
19 |
20 |
21 |
22 |
23 | id
24 |
25 |
26 |
27 | Title
28 |
29 | title
30 | AdminSection/Edit?id=%s
31 | id
32 | edit
33 |
34 |
35 |
36 | Show in Menu
37 |
38 | visible
39 |
40 |
41 |
42 |
43 |
44 | delete…
45 |
46 |
47 | show
48 |
49 |
50 | hide
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/TwoFactorAuthentication.php:
--------------------------------------------------------------------------------
1 | app, AdminLoginTemplate::class);
15 | }
16 |
17 | protected function initInternal()
18 | {
19 | parent::initInternal();
20 |
21 | $this->ui->loadFromXML(__DIR__ . '/two_factor_authentication.xml');
22 |
23 | $form = $this->ui->getWidget('two_fa_form');
24 | $form->action = 'AdminSite/TwoFactorAuthentication';
25 |
26 | // remember where we came from
27 | $form->addHiddenField('relocate_uri', $this->app->getUri());
28 |
29 | $user = $this->app->session->user;
30 | if ($user->is2FaAuthenticated() || !$user->two_fa_enabled) {
31 | $this->app->relocate('./');
32 | }
33 | }
34 |
35 | // process phase
36 |
37 | protected function processInternal()
38 | {
39 | parent::processInternal();
40 |
41 | $form = $this->ui->getWidget('two_fa_form');
42 | if ($form->isProcessed()) {
43 | if (!$form->hasMessage()) {
44 | $this->validate2Fa();
45 | }
46 |
47 | if (!$form->hasMessage()) {
48 | $this->app->session->user->set2FaAuthenticated();
49 |
50 | // go back where we came from
51 | $uri = $form->getHiddenField('relocate_uri');
52 | $this->app->relocate($uri);
53 | }
54 | }
55 | }
56 |
57 | protected function validate2Fa()
58 | {
59 | $success = $this->app->session->loginWithTwoFactorAuthentication(
60 | $this->ui->getWidget('two_fa_token')->value
61 | );
62 |
63 | if (!$success) {
64 | $this->ui->getWidget('two_fa_token')->addMessage(
65 | new SwatMessage(
66 | Admin::_(
67 | 'Your two factor authentication token doesn’t ' .
68 | 'match. Try again, or contact support for help.'
69 | ),
70 | 'error'
71 | )
72 | );
73 | }
74 | }
75 |
76 | // finalize phase
77 |
78 | public function finalize()
79 | {
80 | parent::finalize();
81 |
82 | $this->layout->addHtmlHeadEntry(
83 | 'packages/admin/styles/admin-two-factor-authentication-page.css'
84 | );
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Admin/components/AdminComponent/Delete.php:
--------------------------------------------------------------------------------
1 | getItemList('integer');
19 | $sql = sprintf($sql, $item_list);
20 | $num = SwatDB::exec($this->app->db, $sql);
21 |
22 | $message = new SwatMessage(sprintf(
23 | Admin::ngettext(
24 | 'One component has been deleted.',
25 | '%s components have been deleted.',
26 | $num
27 | ),
28 | SwatString::numberFormat($num)
29 | ));
30 |
31 | $this->app->messages->add($message);
32 | }
33 |
34 | // build phase
35 |
36 | protected function buildInternal()
37 | {
38 | parent::buildInternal();
39 |
40 | $item_list = $this->getItemList('integer');
41 |
42 | $dep = new AdminListDependency();
43 | $dep->setTitle(Admin::_('component'), Admin::_('components'));
44 | $dep->entries = AdminListDependency::queryEntries(
45 | $this->app->db,
46 | 'AdminComponent',
47 | 'integer:id',
48 | null,
49 | 'text:title',
50 | 'displayorder, title',
51 | 'id in (' . $item_list . ')',
52 | AdminDependency::DELETE
53 | );
54 |
55 | $dep_subcomponents = new AdminSummaryDependency();
56 | $dep_subcomponents->setTitle(
57 | Admin::_('sub-component'),
58 | Admin::_('sub-components')
59 | );
60 |
61 | $dep_subcomponents->summaries = AdminSummaryDependency::querySummaries(
62 | $this->app->db,
63 | 'AdminSubComponent',
64 | 'integer:id',
65 | 'integer:component',
66 | 'component in (' . $item_list . ')',
67 | AdminDependency::DELETE
68 | );
69 |
70 | $dep->addDependency($dep_subcomponents);
71 |
72 | $message = $this->ui->getWidget('confirmation_message');
73 | $message->content = $dep->getMessage();
74 | $message->content_type = 'text/xml';
75 |
76 | if ($dep->getStatusLevelCount(AdminDependency::DELETE) == 0) {
77 | $this->switchToCancelButton();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Admin/pages/AdminDBEdit.php:
--------------------------------------------------------------------------------
1 | app->db);
23 | $this->saveDBData();
24 | $transaction->commit();
25 | } catch (SwatDBException $e) {
26 | $transaction->rollback();
27 |
28 | $message = new SwatMessage(
29 | Admin::_(
30 | 'A database error has occurred. The item was not saved.'
31 | ),
32 | 'system-error'
33 | );
34 |
35 | $e->processAndContinue();
36 | $relocate = false;
37 | } catch (SwatException $e) {
38 | $message = new SwatMessage(
39 | Admin::_(
40 | 'An error has occurred. The item was not saved.'
41 | ),
42 | 'system-error'
43 | );
44 |
45 | $e->processAndContinue();
46 | $relocate = false;
47 | }
48 |
49 | if ($message !== null) {
50 | $this->app->messages->add($message);
51 | }
52 |
53 | return $relocate;
54 | }
55 |
56 | /**
57 | * Save the data from the database.
58 | *
59 | * This method is called to save data from the widgets after processing.
60 | * Sub-classes should implement this method and perform whatever actions
61 | * are necessary to store the data. Widgets can be accessed through the
62 | * $ui class variable.
63 | */
64 | abstract protected function saveDBData(): void;
65 |
66 | // build phase
67 |
68 | protected function loadData()
69 | {
70 | $this->loadDBData();
71 |
72 | return true;
73 | }
74 |
75 | /**
76 | * Load the data from the database.
77 | *
78 | * This method is called to load data to be edited into the widgets.
79 | * Sub-classes should implement this method and perform whatever actions
80 | * are necessary to obtain the data. Widgets can be accessed through the
81 | * $ui class variable.
82 | */
83 | abstract protected function loadDBData();
84 | }
85 |
--------------------------------------------------------------------------------
/sql/admin-changes.sql:
--------------------------------------------------------------------------------
1 | insert into AdminSection (id, title, description, displayorder, visible) values (1, 'Admin Settings', null, 100, true);
2 | SELECT setval('Adminsection_id_seq', max(id)) FROM AdminSection;
3 |
4 | INSERT INTO AdminComponent (id, shortname, title, description, displayorder, section, enabled, visible)
5 | VALUES (
6 | 1,
7 | 'AdminUser',
8 | 'Admin Users',
9 | E'Manage who can log into the admin.\n\nAlso set group membership for admin users.',
10 | 4,
11 | 1,
12 | true,
13 | true
14 | );
15 |
16 | INSERT INTO AdminComponent (id, shortname, title, description, displayorder, section, enabled, visible)
17 | VALUES (
18 | 2,
19 | 'AdminGroup',
20 | 'Admin Groups',
21 | E'Manage admin group membership, and admin group component access.',
22 | 5,
23 | 1,
24 | true,
25 | true
26 | );
27 |
28 | INSERT INTO AdminComponent (id, shortname, title, description, displayorder, section, enabled, visible)
29 | VALUES (
30 | 3,
31 | 'AdminSection',
32 | 'Admin Sections',
33 | E'Manage the sections in the admin menu.',
34 | 3,
35 | 1,
36 | true,
37 | true
38 | );
39 |
40 | INSERT INTO AdminComponent (id, shortname, title, description, displayorder, section, enabled, visible)
41 | VALUES (
42 | 4,
43 | 'AdminComponent',
44 | 'Admin Components',
45 | E'Manage the available tools in the admin.\n\nOrganize tools in sections, and set admin group access for specific tools.',
46 | 1,
47 | 1,
48 | true,
49 | true
50 | );
51 |
52 | INSERT INTO AdminComponent (id, shortname, title, description, displayorder, section, enabled, visible)
53 | VALUES (
54 | 5,
55 | 'AdminSubComponent',
56 | 'Admin Sub-Components',
57 | NULL,
58 | 2,
59 | 1,
60 | true,
61 | false
62 | );
63 |
64 | INSERT INTO AdminComponent (id, shortname, title, description, displayorder, section, enabled, visible)
65 | VALUES (
66 | 5,
67 | 'Front',
68 | 'Front Page',
69 | NULL,
70 | 0,
71 | 1,
72 | true,
73 | false
74 | );
75 |
76 | SELECT setval('admincomponent_id_seq', max(id)) FROM AdminComponent;
77 |
78 | -- default sub-components
79 | insert into AdminSubComponent (id, component, title, shortname, visible, displayorder) values (1, 1, 'Login History', 'LoginHistory', true, 0);
80 |
81 | SELECT setval('adminsubcomponent_id_seq', max(id)) FROM AdminSubComponent;
82 |
83 | -- default admin groups
84 | insert into AdminGroup (id, title) values (1, 'Default Group');
85 |
86 | SELECT setval('admingroup_id_seq', max(id)) FROM AdminGroup;
87 |
88 | -- default AdminComponentAdminGroupBinding bindings
89 | insert into AdminComponentAdminGroupBinding (component, groupnum)
90 | select AdminComponent.id, AdminGroup.id from AdminComponent, AdminGroup;
91 |
92 |
93 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/Delete.php:
--------------------------------------------------------------------------------
1 | getItemList('integer');
18 | $sql = sprintf(
19 | 'delete from AdminUser where id in (%s) and id != %s',
20 | $item_list,
21 | $this->app->db->quote($this->app->session->getUserId(), 'integer')
22 | );
23 |
24 | $num = SwatDB::exec($this->app->db, $sql);
25 |
26 | $message = new SwatMessage(sprintf(
27 | Admin::ngettext(
28 | 'One admin user has been deleted.',
29 | '%s admin users have been deleted.',
30 | $num
31 | ),
32 | SwatString::numberFormat($num)
33 | ));
34 |
35 | $this->app->messages->add($message);
36 | }
37 |
38 | // build phase
39 |
40 | public function buildInternal()
41 | {
42 | parent::buildInternal();
43 |
44 | $item_list = $this->getItemList('integer');
45 |
46 | $where_clause = sprintf(
47 | 'id in (%s) and id != %s',
48 | $item_list,
49 | $this->app->db->quote($this->app->session->getUserId(), 'integer')
50 | );
51 |
52 | $dep = new AdminListDependency();
53 | $dep->setTitle(Admin::_('admin user'), Admin::_('admin users'));
54 | $dep->entries = AdminListDependency::queryEntries(
55 | $this->app->db,
56 | 'AdminUser',
57 | 'integer:id',
58 | null,
59 | 'text:name',
60 | 'name',
61 | $where_clause,
62 | AdminDependency::DELETE
63 | );
64 |
65 | $message = $this->ui->getWidget('confirmation_message');
66 | $message->content = $dep->getMessage();
67 | $message->content_type = 'text/xml';
68 |
69 | if ($dep->getItemCount() == 0) {
70 | $this->switchToCancelButton();
71 | }
72 |
73 | // display can't delete self message if current account is in selection
74 | if ($this->items->contains($this->app->session->getUserId())) {
75 | $header = new SwatHtmlTag('h3');
76 | $header->setContent(
77 | Admin::_('You cannot delete your own account.')
78 | );
79 |
80 | $message->content .= $header;
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/LoginHistory.php:
--------------------------------------------------------------------------------
1 | ui->loadFromXML(__DIR__ . '/login-history.xml');
16 |
17 | // set a default order on the table view
18 | $index_view = $this->ui->getWidget('index_view');
19 | $index_view->setDefaultOrderbyColumn(
20 | $index_view->getColumn('login_date')
21 | );
22 |
23 | $this->navbar->createEntry(Admin::_('Login History'));
24 | }
25 |
26 | // process phase
27 |
28 | protected function processInternal()
29 | {
30 | parent::processInternal();
31 |
32 | $instance_id = $this->app->getInstanceId();
33 |
34 | $pager = $this->ui->getWidget('pager');
35 | $sql = 'select count(id) from AdminUserHistory where instance %s %s';
36 | $pager->total_records = SwatDB::queryOne($this->app->db, sprintf(
37 | $sql,
38 | SwatDB::equalityOperator($instance_id),
39 | $this->app->db->quote($instance_id, 'integer')
40 | ));
41 |
42 | $pager->link = 'AdminUser/LoginHistory';
43 | $pager->process();
44 | }
45 |
46 | // build phase
47 |
48 | protected function buildInternal()
49 | {
50 | parent::buildInternal();
51 |
52 | // set default time zone
53 | $date_column =
54 | $this->ui->getWidget('index_view')->getColumn('login_date');
55 |
56 | $date_renderer = $date_column->getRendererByPosition();
57 | $date_renderer->display_time_zone = $this->app->default_time_zone;
58 | }
59 |
60 | protected function getTableModel(SwatView $view): ?SwatTableModel
61 | {
62 | $pager = $this->ui->getWidget('pager');
63 | $this->app->db->setLimit($pager->page_size, $pager->current_record);
64 |
65 | $instance_id = $this->app->getInstanceId();
66 |
67 | $sql = 'select usernum, login_date, login_agent, remote_ip, email,
68 | AdminUser.name
69 | from AdminUserHistory
70 | inner join AdminUser on AdminUser.id = AdminUserHistory.usernum
71 | where instance %s %s
72 | order by %s';
73 |
74 | $sql = sprintf(
75 | $sql,
76 | SwatDB::equalityOperator($instance_id),
77 | $this->app->db->quote($instance_id, 'integer'),
78 | $this->getOrderByClause($view, 'login_date desc')
79 | );
80 |
81 | return SwatDB::query($this->app->db, $sql);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/Details.php:
--------------------------------------------------------------------------------
1 | ui->loadFromXML(__DIR__ . '/details.xml');
20 |
21 | $this->id = intval(SiteApplication::initVar('id'));
22 |
23 | $this->initUser();
24 |
25 | // set a default order on the table view
26 | $index_view = $this->ui->getWidget('index_view');
27 | $index_view->setDefaultOrderbyColumn(
28 | $index_view->getColumn('login_date')
29 | );
30 | }
31 |
32 | protected function initUser()
33 | {
34 | $class_name = SwatDBClassMap::get(AdminUser::class);
35 | $this->user = new $class_name();
36 | $this->user->setDatabase($this->app->db);
37 |
38 | if (!$this->user->load($this->id)) {
39 | throw new AdminNotFoundException(
40 | sprintf(
41 | Admin::_('User with id "%s" not found.'),
42 | $this->id
43 | )
44 | );
45 | }
46 | }
47 |
48 | // build phase
49 |
50 | protected function buildInternal()
51 | {
52 | parent::buildInternal();
53 |
54 | $frame = $this->ui->getWidget('index_frame');
55 | $frame->subtitle = sprintf($this->user->name);
56 |
57 | // rebuild the navbar
58 | $this->navbar->createEntry('Login History', 'AdminUser/LoginHistory');
59 | $this->navbar->createEntry($this->user->name);
60 |
61 | // set default time zone
62 | $date_column =
63 | $this->ui->getWidget('index_view')->getColumn('login_date');
64 |
65 | $date_renderer = $date_column->getRendererByPosition();
66 | $date_renderer->display_time_zone = $this->app->default_time_zone;
67 | }
68 |
69 | protected function getTableModel(SwatView $view): AdminUserHistoryWrapper
70 | {
71 | $instance_id = $this->app->getInstanceId();
72 |
73 | $sql = 'select * from AdminUserHistory
74 | where usernum = %s and instance %s %s
75 | order by %s';
76 |
77 | $sql = sprintf(
78 | $sql,
79 | $this->app->db->quote($this->user->id, 'integer'),
80 | SwatDB::equalityOperator($instance_id),
81 | $this->app->db->quote($instance_id, 'integer'),
82 | $this->getOrderByClause($view, 'login_date desc')
83 | );
84 |
85 | return SwatDB::query($this->app->db, $sql, AdminUserHistoryWrapper::class);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Admin/components/AdminSubComponent/Order.php:
--------------------------------------------------------------------------------
1 | parent = SiteApplication::initVar('parent');
20 | $form = $this->ui->getWidget('order_form');
21 | $form->addHiddenField('parent', $this->parent);
22 | }
23 |
24 | // process phase
25 |
26 | protected function saveIndex($id, $index)
27 | {
28 | SwatDB::updateColumn(
29 | $this->app->db,
30 | 'AdminSubComponent',
31 | 'integer:displayorder',
32 | $index,
33 | 'integer:id',
34 | [$id]
35 | );
36 | }
37 |
38 | // build phase
39 |
40 | protected function buildInternal()
41 | {
42 | parent::buildInternal();
43 |
44 | $frame = $this->ui->getWidget('order_frame');
45 | $frame->title = Admin::_('Order Sub-Components');
46 |
47 | // rebuild the navbar
48 | $parent_title = SwatDB::queryOneFromTable(
49 | $this->app->db,
50 | 'AdminComponent',
51 | 'text:title',
52 | 'id',
53 | $this->parent
54 | );
55 |
56 | // pop two entries because the AdminDBOrder base class adds an entry
57 | $this->navbar->popEntries(2);
58 | $this->navbar->createEntry('Admin Components', 'AdminComponent');
59 | $this->navbar->createEntry(
60 | $parent_title,
61 | 'AdminComponent/Details?id=' . $this->parent
62 | );
63 |
64 | $this->navbar->createEntry('Order Sub-Components');
65 | }
66 |
67 | protected function loadData()
68 | {
69 | $where_clause = sprintf(
70 | 'component = %s',
71 | $this->app->db->quote($this->parent, 'integer')
72 | );
73 |
74 | $order_list = $this->ui->getWidget('order');
75 | $order_list_options = SwatDB::getOptionArray(
76 | $this->app->db,
77 | 'AdminSubComponent',
78 | 'title',
79 | 'id',
80 | 'displayorder, title',
81 | $where_clause
82 | );
83 |
84 | $order_list->addOptionsByArray($order_list_options);
85 |
86 | $sql = 'select sum(displayorder) from AdminSubComponent
87 | where ' . $where_clause;
88 |
89 | $sum = SwatDB::queryOne($this->app->db, $sql, 'integer');
90 | $radio_list = $this->ui->getWidget('options');
91 | $radio_list->value = ($sum == 0) ? 'auto' : 'custom';
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Admin/AdminMenuStore.php:
--------------------------------------------------------------------------------
1 | getMessage());
30 | }
31 |
32 | $this->sections = [];
33 | $section = null;
34 | $component = null;
35 |
36 | do {
37 | while ($row = $rs->fetchRow(MDB2_FETCHMODE_OBJECT)) {
38 | if ($section === null || $row->section != $section->id) {
39 | $section = new AdminMenuSection(
40 | $row->section,
41 | $row->section_title
42 | );
43 |
44 | $this->sections[$row->section] = $section;
45 | }
46 |
47 | if ($component === null
48 | || $row->component_id != $component->id) {
49 | $component = new AdminMenuComponent(
50 | $row->component_id,
51 | $row->shortname,
52 | $row->title,
53 | $row->description
54 | );
55 |
56 | $section->components[$row->shortname] = $component;
57 | }
58 |
59 | if ($row->subcomponent_shortname != '') {
60 | $subcomponent = new AdminMenuSubcomponent(
61 | $row->subcomponent_shortname,
62 | $row->subcomponent_title
63 | );
64 |
65 | $component->subcomponents[$row->subcomponent_shortname] =
66 | $subcomponent;
67 | }
68 | }
69 | } while ($rs->nextResult());
70 | }
71 |
72 | public function getComponentByName($name)
73 | {
74 | foreach ($this->sections as $section) {
75 | foreach ($section->components as $component) {
76 | if ($component->shortname === $name) {
77 | return $component;
78 | }
79 | }
80 | }
81 |
82 | return null;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Admin/components/AdminSection/Index.php:
--------------------------------------------------------------------------------
1 | ui->loadFromXML(__DIR__ . '/index.xml');
15 | }
16 |
17 | // process phase
18 |
19 | protected function processActions(SwatView $view, SwatActions $actions)
20 | {
21 | $num = count($view->checked_items);
22 | $message = null;
23 |
24 | switch ($actions->selected->id) {
25 | case 'delete':
26 | $this->app->replacePage('AdminSection/Delete');
27 | $this->app->getPage()->setItems($view->getSelection());
28 | break;
29 |
30 | case 'show':
31 | SwatDB::updateColumn(
32 | $this->app->db,
33 | 'AdminSection',
34 | 'boolean:visible',
35 | true,
36 | 'id',
37 | $view->getSelection()
38 | );
39 |
40 | $message = new SwatMessage(sprintf(
41 | Admin::ngettext(
42 | 'One section has been shown.',
43 | '%s sections have been shown.',
44 | $num
45 | ),
46 | SwatString::numberFormat($num)
47 | ));
48 |
49 | break;
50 |
51 | case 'hide':
52 | SwatDB::updateColumn(
53 | $this->app->db,
54 | 'AdminSection',
55 | 'boolean:visible',
56 | false,
57 | 'id',
58 | $view->getSelection()
59 | );
60 |
61 | $message = new SwatMessage(sprintf(
62 | Admin::ngettext(
63 | 'One section has been hidden.',
64 | '%s sections have been hidden.',
65 | $num
66 | ),
67 | SwatString::numberFormat($num)
68 | ));
69 |
70 | break;
71 | }
72 |
73 | if ($message !== null) {
74 | $this->app->messages->add($message);
75 | }
76 | }
77 |
78 | // build phase
79 |
80 | protected function getTableModel(SwatView $view): AdminSectionWrapper
81 | {
82 | $sql = 'select id, title, visible
83 | from AdminSection
84 | order by displayorder';
85 |
86 | $sections = SwatDB::query($this->app->db, $sql, AdminSectionWrapper::class);
87 |
88 | if (count($sections) == 0) {
89 | $this->ui->getWidget('order_tool')->visible = false;
90 | }
91 |
92 | return $sections;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/Admin/components/AdminSubComponent/Delete.php:
--------------------------------------------------------------------------------
1 | parent = $parent;
16 | }
17 |
18 | // process phase
19 |
20 | protected function processDBData(): void
21 | {
22 | parent::processDBData();
23 |
24 | $item_list = $this->getItemList('integer');
25 |
26 | $sql = 'delete from AdminSubComponent where id in (%s)';
27 |
28 | $sql = sprintf($sql, $item_list);
29 | $num = SwatDB::exec($this->app->db, $sql);
30 |
31 | $message = new SwatMessage(sprintf(
32 | Admin::ngettext(
33 | 'One sub-component has been deleted.',
34 | '%s sub-components have been deleted.',
35 | $num
36 | ),
37 | SwatString::numberFormat($num)
38 | ));
39 |
40 | $this->app->messages->add($message);
41 | }
42 |
43 | // build phase
44 |
45 | protected function buildInternal()
46 | {
47 | parent::buildInternal();
48 |
49 | $item_list = $this->getItemList('integer');
50 |
51 | $dep = new AdminListDependency();
52 | $dep->setTitle(Admin::_('sub-component'), Admin::_('sub-components'));
53 | $dep->entries = AdminListDependency::queryEntries(
54 | $this->app->db,
55 | 'AdminSubComponent',
56 | 'integer:id',
57 | null,
58 | 'text:title',
59 | 'displayorder, title',
60 | 'id in (' . $item_list . ')',
61 | AdminDependency::DELETE
62 | );
63 |
64 | $message = $this->ui->getWidget('confirmation_message');
65 | $message->content = $dep->getMessage();
66 | $message->content_type = 'text/xml';
67 |
68 | if ($dep->getStatusLevelCount(AdminDependency::DELETE) == 0) {
69 | $this->switchToCancelButton();
70 | }
71 |
72 | // rebuild the navbar
73 | $component_title = SwatDB::queryOneFromTable(
74 | $this->app->db,
75 | 'AdminComponent',
76 | 'text:title',
77 | 'id',
78 | $this->parent
79 | );
80 |
81 | // pop two entries because the AdminDBOrder base class adds an entry
82 | $this->navbar->popEntries(2);
83 | $this->navbar->createEntry(
84 | Admin::_('Admin Components'),
85 | 'AdminComponent'
86 | );
87 |
88 | $this->navbar->createEntry(
89 | $component_title,
90 | 'AdminComponent/Details?id=' . $this->parent
91 | );
92 |
93 | $this->navbar->createEntry(Admin::_('Delete Sub-Component(s)'));
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/Reactivate.php:
--------------------------------------------------------------------------------
1 | navbar->popEntry();
17 | $this->navbar->createEntry(Admin::_('Reactivate'));
18 | }
19 |
20 | // process phase
21 |
22 | protected function processDBData(): void
23 | {
24 | parent::processDBData();
25 |
26 | $locale = SwatI18NLocale::get();
27 |
28 | $now = new SwatDate();
29 | $now->toUTC();
30 |
31 | $sql = sprintf(
32 | 'update AdminUser set activation_date = %s
33 | where id in (%s)',
34 | $this->app->db->quote($now->getDate(), 'date'),
35 | $this->getItemList('integer')
36 | );
37 |
38 | $num = SwatDB::exec($this->app->db, $sql);
39 |
40 | $this->app->messages->add(
41 | new SwatMessage(
42 | sprintf(
43 | Admin::ngettext(
44 | 'One admin user has been reactivated.',
45 | '%s admin users have been reactivated.',
46 | $num
47 | ),
48 | $locale->formatNumber($num)
49 | )
50 | )
51 | );
52 | }
53 |
54 | // build phase
55 |
56 | public function buildInternal()
57 | {
58 | parent::buildInternal();
59 |
60 | $users = SwatDB::query(
61 | $this->app->db,
62 | sprintf(
63 | 'select name from AdminUser
64 | where id in (%s)
65 | order by name asc',
66 | $this->getItemList('integer')
67 | ),
68 | AdminUserWrapper::class
69 | );
70 |
71 | ob_start();
72 |
73 | $h3_tag = new SwatHtmlTag('h3');
74 | $h3_tag->setContent(
75 | Admin::ngettext(
76 | 'Reactivate the following admin user?',
77 | 'Reactivate the following admin users?',
78 | count($users)
79 | )
80 | );
81 | $h3_tag->display();
82 |
83 | if (count($users) > 0) {
84 | echo '';
85 | foreach ($users as $user) {
86 | $li_tag = new SwatHtmlTag('li');
87 | $li_tag->setContent($user->name);
88 | $li_tag->display();
89 | }
90 | echo '
';
91 | }
92 |
93 | $message = $this->ui->getWidget('confirmation_message');
94 | $message->content = ob_get_clean();
95 | $message->content_type = 'text/xml';
96 |
97 | if (count($users) === 0) {
98 | $this->switchToCancelButton();
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Admin/components/AdminSection/Delete.php:
--------------------------------------------------------------------------------
1 | getItemList('integer');
19 | $sql = sprintf($sql, $item_list);
20 | $num = SwatDB::exec($this->app->db, $sql);
21 |
22 | $message = new SwatMessage(sprintf(
23 | Admin::ngettext(
24 | 'One admin section has been deleted.',
25 | '%s admin sections have been deleted.',
26 | $num
27 | ),
28 | SwatString::numberFormat($num)
29 | ));
30 |
31 | $this->app->messages->add($message);
32 | }
33 |
34 | // build phase
35 |
36 | protected function buildInternal()
37 | {
38 | parent::buildInternal();
39 |
40 | $item_list = $this->getItemList('integer');
41 |
42 | $dep = new AdminListDependency();
43 | $dep->setTitle(Admin::_('section'), Admin::_('sections'));
44 | $dep->entries = AdminListDependency::queryEntries(
45 | $this->app->db,
46 | 'AdminSection',
47 | 'integer:id',
48 | null,
49 | 'text:title',
50 | 'title',
51 | 'id in (' . $item_list . ')',
52 | AdminDependency::DELETE
53 | );
54 |
55 | $dep_components = new AdminListDependency();
56 | $dep_components->setTitle(
57 | Admin::_('component'),
58 | Admin::_('components')
59 | );
60 |
61 | $dep_components->entries = AdminListDependency::queryEntries(
62 | $this->app->db,
63 | 'AdminComponent',
64 | 'integer:id',
65 | 'integer:section',
66 | 'text:title',
67 | 'title',
68 | 'section in (' . $item_list . ')',
69 | AdminDependency::DELETE
70 | );
71 |
72 | $dep->addDependency($dep_components);
73 |
74 | $dep_subcomponents = new AdminSummaryDependency();
75 | $dep_subcomponents->setTitle(
76 | Admin::_('sub-component'),
77 | Admin::_('sub-components')
78 | );
79 |
80 | $dep_subcomponents->summaries = AdminSummaryDependency::querySummaries(
81 | $this->app->db,
82 | 'AdminSubComponent',
83 | 'integer:id',
84 | 'integer:component',
85 | 'component in (select id from AdminComponent where section in (' .
86 | $item_list . '))',
87 | AdminDependency::DELETE
88 | );
89 |
90 | $dep_components->addDependency($dep_subcomponents);
91 |
92 | $message = $this->ui->getWidget('confirmation_message');
93 | $message->content = $dep->getMessage();
94 | $message->content_type = 'text/xml';
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Admin/pages/AdminDBDelete.php:
--------------------------------------------------------------------------------
1 | single_delete = (bool) SiteApplication::initVar(
24 | 'single_delete',
25 | false,
26 | SiteApplication::VAR_POST
27 | );
28 |
29 | $id = SiteApplication::initVar('id', null, SiteApplication::VAR_GET);
30 |
31 | if ($id !== null) {
32 | $this->setItems([$id]);
33 | $this->single_delete = true;
34 | $form = $this->ui->getWidget('confirmation_form');
35 | $form->addHiddenField('single_delete', $this->single_delete);
36 | }
37 |
38 | $yes_button = $this->ui->getWidget('yes_button');
39 | $yes_button->setFromStock('delete');
40 |
41 | $no_button = $this->ui->getWidget('no_button');
42 | $no_button->setFromStock('cancel');
43 |
44 | $this->navbar->popEntry();
45 | $this->navbar->createEntry(Admin::_('Delete'));
46 | }
47 |
48 | // process phase
49 |
50 | protected function generateMessage(Throwable $e)
51 | {
52 | if ($e instanceof SwatDBException) {
53 | $message = new SwatMessage(
54 | Admin::_('A database error has occured.
55 | The item(s) were not deleted.'),
56 | 'system-error'
57 | );
58 | } else {
59 | $message = new SwatMessage(
60 | Admin::_('An error has occured.
61 | The item(s) were not deleted.'),
62 | 'system-error'
63 | );
64 | }
65 |
66 | $this->app->messages->add($message);
67 | }
68 |
69 | protected function relocate()
70 | {
71 | $form = $this->ui->getWidget('confirmation_form');
72 | $url = $form->getHiddenField(self::RELOCATE_URL_FIELD);
73 | // always search for only the component name with slashes. This helps
74 | // for cases where the page name has a match to the component name we're
75 | // checking against, and if the trailing slash is missing, we're
76 | // redirecting to the index anyway.
77 | $component = '/' . $this->getComponentName() . '/';
78 |
79 | // if the component name is in the relocate url, relocate to the
80 | // the component index page to prevent relocating to a details page that
81 | // will no longer exist.
82 | if (mb_strpos($url, $component) === false
83 | || $form->button->id == 'no_button') {
84 | parent::relocate();
85 | } else {
86 | $this->app->relocate($this->getComponentName());
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/edit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | User
7 |
8 |
9 | Name
10 |
11 | true
12 | 100
13 |
14 |
15 |
16 | Email
17 |
18 | true
19 | 50
20 |
21 |
22 |
23 | Enabled
24 |
25 | true
26 |
27 |
28 |
29 | 2FA Enabled
30 |
31 | false
32 |
33 |
34 |
35 | Belongs to Groups
36 |
37 |
38 |
39 | Belongs to Sites
40 | false
41 |
42 |
43 |
44 | Change Password
45 | false
46 |
47 | New Password
48 |
49 | false
50 | 4
51 |
52 |
53 |
54 | Confirm New Password
55 | Leave Password fields blank for them to remain the same.
56 |
57 |
58 |
59 | Force User to Change Password on Login
60 |
61 | true
62 |
63 |
64 |
65 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/Admin/AdminControlCellRenderer.php:
--------------------------------------------------------------------------------
1 | visible) {
31 | return;
32 | }
33 |
34 | if ($this->stock_id === null) {
35 | $this->setFromStock('details', false);
36 | } else {
37 | $this->setFromStock($this->stock_id, false);
38 | }
39 |
40 | parent::render();
41 | }
42 |
43 | /**
44 | * Sets the values of this control cell renderer to a stock type.
45 | *
46 | * Valid stock type ids are:
47 | *
48 | * - details (default)
49 | * - edit
50 | *
51 | * @param string $stock_id the identifier of the stock type to use
52 | * @param bool $overwrite_properties whether to overwrite properties if
53 | * they are already set
54 | *
55 | * @throws SwatUndefinedStockTypeException
56 | */
57 | public function setFromStock($stock_id, $overwrite_properties = true)
58 | {
59 | switch ($stock_id) {
60 | case 'details':
61 | $title = Admin::_('View Details');
62 | $alt = Admin::_('Details');
63 | $image = 'packages/admin/images/admin-generic-document.png';
64 | $width = '22';
65 | $height = '22';
66 | break;
67 |
68 | case 'edit':
69 | $title = Admin::_('Edit');
70 | $alt = Admin::_('Edit');
71 | $image = 'packages/admin/images/admin-edit.png';
72 | $width = '22';
73 | $height = '22';
74 | break;
75 |
76 | default:
77 | throw new SwatUndefinedStockTypeException(
78 | "Stock type with id of '{$stock_id}' not found.",
79 | 0,
80 | $stock_id
81 | );
82 | }
83 |
84 | if ($overwrite_properties || ($this->title === null)) {
85 | $this->title = $title;
86 | }
87 |
88 | if ($overwrite_properties || ($this->alt === null)) {
89 | $this->alt = $alt;
90 | }
91 |
92 | if ($overwrite_properties || ($this->image === null)) {
93 | $this->image = $image;
94 | }
95 |
96 | if ($overwrite_properties || ($this->width === null)) {
97 | $this->width = $width;
98 | }
99 |
100 | if ($overwrite_properties || ($this->height === null)) {
101 | $this->height = $height;
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/Admin/components/AdminUser/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Admin Users
7 |
8 |
9 | New User
10 | AdminUser/Edit
11 | create
12 |
13 |
14 |
15 | false
16 |
17 |
18 |
19 |
20 |
21 | id
22 |
23 |
24 |
25 | is_active
26 |
27 | active_title
28 |
29 |
30 |
31 | Email
32 |
33 | email
34 | AdminUser/Edit?id=%s
35 | id
36 | person
37 |
38 |
39 |
40 | Name
41 |
42 | name
43 |
44 |
45 |
46 | Enabled
47 |
48 | enabled
49 |
50 |
51 |
52 | 2FA Enabled
53 |
54 | two_fa_enabled
55 |
56 |
57 |
58 | Last Login
59 |
60 | last_login
61 |
62 |
63 | history
64 | id
65 | last_login
66 |
67 |
68 |
69 |
70 |
71 | reactivate…
72 |
73 |
74 | delete…
75 |
76 |
77 | enable
78 |
79 |
80 | disable
81 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/ChangePassword.php:
--------------------------------------------------------------------------------
1 | app, AdminLoginTemplate::class);
15 | }
16 |
17 | protected function initInternal()
18 | {
19 | parent::initInternal();
20 |
21 | $this->ui->loadFromXML(__DIR__ . '/change-password.xml');
22 |
23 | $confirm = $this->ui->getWidget('confirm_password');
24 | $confirm->password_widget = $this->ui->getWidget('password');
25 |
26 | $form = $this->ui->getWidget('change_password_form');
27 | $form->action = 'AdminSite/ChangePassword';
28 |
29 | // remember where we came from
30 | $form->addHiddenField('relocate_uri', $this->app->getUri());
31 | }
32 |
33 | // process phase
34 |
35 | protected function processInternal()
36 | {
37 | parent::processInternal();
38 |
39 | $crypt = $this->app->getModule('SiteCryptModule');
40 |
41 | $form = $this->ui->getWidget('change_password_form');
42 | if ($form->isProcessed()) {
43 | $this->validatePasswords();
44 | if (!$form->hasMessage()) {
45 | $password = $this->ui->getWidget('password')->value;
46 |
47 | $user = $this->app->session->user;
48 | $user->setPasswordHash($crypt->generateHash($password));
49 | $user->force_change_password = false;
50 | $user->save();
51 |
52 | $this->app->session->login($user->email, $password);
53 |
54 | $message = new SwatMessage(
55 | Admin::_('Your password has been updated.')
56 | );
57 |
58 | $this->app->messages->add($message);
59 |
60 | // go back where we came from
61 | $uri = $form->getHiddenField('relocate_uri');
62 | $this->app->relocate($uri);
63 | }
64 | }
65 | }
66 |
67 | protected function validatePasswords()
68 | {
69 | $user = $this->app->session->user;
70 |
71 | $old_password = $this->ui->getWidget('old_password')->value;
72 | $new_password = $this->ui->getWidget('password')->value;
73 |
74 | if ($old_password === null || $new_password === null) {
75 | return;
76 | }
77 |
78 | // make sure old password is not the same as new password
79 | if ($old_password == $new_password) {
80 | $message = new SwatMessage(Admin::_('Your new password can not be ' .
81 | 'the same as your old password'), 'error');
82 |
83 | $this->ui->getWidget('password')->addMessage($message);
84 | }
85 |
86 | $crypt = $this->app->getModule('SiteCryptModule');
87 |
88 | $password_hash = $user->password;
89 | $password_salt = $user->password_salt;
90 |
91 | // make sure old password is correct
92 | if (!$crypt->verifyHash(
93 | $old_password,
94 | $password_hash,
95 | $password_salt
96 | )) {
97 | $this->ui->getWidget('old_password')->addMessage(
98 | new SwatMessage(
99 | Admin::_('Your old password is not correct'),
100 | 'error'
101 | )
102 | );
103 | }
104 | }
105 |
106 | // finalize phase
107 |
108 | public function finalize()
109 | {
110 | parent::finalize();
111 |
112 | $this->layout->addHtmlHeadEntry(
113 | 'packages/admin/styles/admin-change-password-page.css'
114 | );
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Admin/components/AdminComponent/Edit.php:
--------------------------------------------------------------------------------
1 | initSections();
40 | $this->initGroups();
41 | }
42 |
43 | protected function initSections()
44 | {
45 | $section_flydown = $this->ui->getWidget('section');
46 | $section_flydown_options = SwatDB::getOptionArray(
47 | $this->app->db,
48 | 'AdminSection',
49 | 'title',
50 | 'id',
51 | 'displayorder'
52 | );
53 |
54 | $section_flydown->addOptionsByArray($section_flydown_options);
55 | }
56 |
57 | protected function initGroups()
58 | {
59 | $group_list = $this->ui->getWidget('groups');
60 | $group_list_options = SwatDB::getOptionArray(
61 | $this->app->db,
62 | 'AdminGroup',
63 | 'title',
64 | 'id',
65 | 'title'
66 | );
67 |
68 | $group_list->addOptionsByArray($group_list_options);
69 | }
70 |
71 | // process phase
72 |
73 | protected function validate(): void
74 | {
75 | $shortname_widget = $this->ui->getWidget('shortname');
76 | $shortname = $shortname_widget->value;
77 |
78 | $should_validate_shortname = (!$this->isNew() || $shortname != '');
79 | if ($should_validate_shortname
80 | && !$this->validateShortname($shortname)) {
81 | $message = new SwatMessage(
82 | Admin::_('Shortname already exists and must be unique.'),
83 | 'error'
84 | );
85 |
86 | $shortname_widget->addMessage($message);
87 | }
88 | }
89 |
90 | protected function postSaveObject()
91 | {
92 | $this->updateGroupBindings();
93 | }
94 |
95 | protected function updateGroupBindings()
96 | {
97 | $group_list = $this->ui->getWidget('groups');
98 |
99 | SwatDB::updateBinding(
100 | $this->app->db,
101 | 'AdminComponentAdminGroupBinding',
102 | 'component',
103 | $this->getObject()->id,
104 | 'groupnum',
105 | $group_list->values,
106 | 'AdminGroup',
107 | 'id'
108 | );
109 | }
110 |
111 | protected function getSavedMessagePrimaryContent()
112 | {
113 | return sprintf(
114 | Admin::_('Component “%s” has been saved.'),
115 | $this->getObject()->title
116 | );
117 | }
118 |
119 | // build phase
120 |
121 | protected function loadObject()
122 | {
123 | parent::loadObject();
124 |
125 | if (!$this->isNew()) {
126 | $this->loadGroupBindings();
127 | }
128 | }
129 |
130 | protected function loadGroupBindings()
131 | {
132 | $group_list = $this->ui->getWidget('groups');
133 | $group_list->values = SwatDB::queryColumn(
134 | $this->app->db,
135 | 'AdminComponentAdminGroupBinding',
136 | 'groupnum',
137 | 'component',
138 | $this->getObject()->id
139 | );
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/Admin/components/AdminComponent/index.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Components
7 |
8 |
9 | New Component
10 | AdminComponent/Edit
11 | create
12 |
13 |
14 |
15 |
16 |
17 | section
18 | section
19 |
20 | section_title
21 |
22 |
23 | Change Order
24 | AdminComponent/Order?parent=%s
25 | section
26 | section_order_sensitive
27 |
28 |
29 |
30 |
31 | id
32 |
33 |
34 |
35 | Title
36 |
37 | title
38 | AdminComponent/Details?id=%s
39 | id
40 |
41 |
42 |
43 | Short Name
44 |
45 | shortname
46 |
47 |
48 |
49 | Show in Menu
50 |
51 | visible
52 |
53 |
54 |
55 | Enabled
56 |
57 | enabled
58 |
59 |
60 |
61 |
62 |
63 | delete…
64 |
65 |
66 | show
67 |
68 |
69 | hide
70 |
71 |
72 | enable
73 |
74 |
75 | disable
76 |
77 |
78 | change section…
79 |
80 | false
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/Admin/pages/AdminConfirmation.php:
--------------------------------------------------------------------------------
1 | ui->loadFromXML($this->getUiXml());
20 | $this->navbar->createEntry(Admin::_('Confirmation'));
21 | }
22 |
23 | protected function getUiXml()
24 | {
25 | return __DIR__ . '/confirmation.xml';
26 | }
27 |
28 | // process phase
29 |
30 | protected function processInternal()
31 | {
32 | parent::processInternal();
33 |
34 | $form = $this->ui->getWidget('confirmation_form');
35 | $relocate = false;
36 |
37 | if ($form->isAuthenticated()) {
38 | if ($form->isProcessed()) {
39 | if ($this->ui->getWidget('no_button')->hasBeenClicked()) {
40 | // if the no (aka cancel) button has been hit, relocate even
41 | // if the form doesn't validate or process.
42 | $relocate = true;
43 | } elseif (!$form->hasMessage()) {
44 | // only process the response if the form validated and we're
45 | // not already relocating.
46 | $this->processResponse();
47 |
48 | $relocate = true;
49 | }
50 | }
51 |
52 | if ($relocate) {
53 | $this->relocate();
54 | }
55 | } else {
56 | $message = new SwatMessage(Admin::_('There is a problem with the ' .
57 | 'information submitted.'), 'warning');
58 |
59 | $message->secondary_content =
60 | Admin::_('In order to ensure your security, we were unable to ' .
61 | 'process your request. Please try again.');
62 |
63 | $this->app->messages->add($message);
64 | }
65 | }
66 |
67 | /**
68 | * Process the response.
69 | *
70 | * This method is called to perform whatever processing is required in
71 | * response to the button clicked. It is called by the
72 | * {@link AdminConfirmation::process} method.
73 | */
74 | abstract protected function processResponse(): void;
75 |
76 | /**
77 | * Relocates to the previous page after processsing confirmation response.
78 | */
79 | protected function relocate()
80 | {
81 | $form = $this->ui->getWidget('confirmation_form');
82 | $url = $form->getHiddenField(self::RELOCATE_URL_FIELD);
83 | $this->app->relocate($url);
84 | }
85 |
86 | // build phase
87 |
88 | protected function buildInternal()
89 | {
90 | parent::buildInternal();
91 | $this->buildForm();
92 | $this->buildMessages();
93 | }
94 |
95 | protected function buildForm()
96 | {
97 | $form = $this->ui->getWidget('confirmation_form');
98 | $form->action = $this->source;
99 |
100 | if ($form->getHiddenField(self::RELOCATE_URL_FIELD) === null) {
101 | $url = $this->getRefererURL();
102 | $form->addHiddenField(self::RELOCATE_URL_FIELD, $url);
103 | }
104 | }
105 |
106 | /**
107 | * Switches the default yes/no buttons to a cancel button.
108 | *
109 | * Call this method if a confirmation page is displayed and the desired
110 | * action of the user is impossible.
111 | */
112 | protected function switchToCancelButton()
113 | {
114 | $this->ui->getWidget('yes_button')->visible = false;
115 | $this->ui->getWidget('no_button')->title = Admin::_('Cancel');
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/Admin/components/AdminSubComponent/Edit.php:
--------------------------------------------------------------------------------
1 | initAdminComponent();
39 | }
40 |
41 | protected function initAdminComponent()
42 | {
43 | if ($this->isNew()) {
44 | $parent_id = SiteApplication::initVar('parent');
45 |
46 | if ($parent_id === null) {
47 | throw new AdminNotFoundException(
48 | 'Must supply a Component ID for newly created ' .
49 | 'Sub-Compoenets.'
50 | );
51 | }
52 |
53 | $class_name = SwatDBClassMap::get(AdminComponent::class);
54 | $this->admin_component = new $class_name();
55 | $this->admin_component->setDatabase($this->app->db);
56 |
57 | if (!$this->admin_component->load($parent_id)) {
58 | throw new AdminNotFoundException(
59 | sprintf(
60 | 'Component with id "%s" not found.',
61 | $parent_id
62 | )
63 | );
64 | }
65 | } else {
66 | $this->admin_component = $this->getObject()->component;
67 | }
68 | }
69 |
70 | // process phase
71 |
72 | protected function validate(): void
73 | {
74 | $shortname_widget = $this->ui->getWidget('shortname');
75 | $shortname = $shortname_widget->value;
76 |
77 | $should_validate_shortname = (!$this->isNew() || $shortname != '');
78 | if ($should_validate_shortname
79 | && !$this->validateShortname($shortname)) {
80 | $message = new SwatMessage(
81 | Admin::_('Shortname already exists and must be unique.'),
82 | 'error'
83 | );
84 |
85 | $shortname_widget->addMessage($message);
86 | }
87 | }
88 |
89 | protected function updateObject()
90 | {
91 | parent::updateObject();
92 |
93 | if ($this->isNew()) {
94 | $this->getObject()->component = $this->admin_component;
95 | }
96 | }
97 |
98 | protected function getSavedMessagePrimaryContent()
99 | {
100 | return sprintf(
101 | Admin::_('Sub-Component “%s” has been saved.'),
102 | $this->getObject()->title
103 | );
104 | }
105 |
106 | // build phase
107 |
108 | protected function buildForm()
109 | {
110 | parent::buildForm();
111 |
112 | $form = $this->ui->getWidget('edit_form');
113 | $form->addHiddenField('parent', $this->admin_component->id);
114 | }
115 |
116 | protected function buildNavBar()
117 | {
118 | $this->navbar->popEntry();
119 |
120 | $this->navbar->createEntry(
121 | Admin::_('Admin Components'),
122 | 'AdminComponent'
123 | );
124 |
125 | $this->navbar->createEntry(
126 | $this->admin_component->title,
127 | sprintf(
128 | 'AdminComponent/Details?id=%s',
129 | $this->admin_component->id
130 | )
131 | );
132 |
133 | $this->navbar->createEntry(
134 | ($this->isNew())
135 | ? Admin::_('Add Sub-Component')
136 | : Admin::_('Edit Sub-Component')
137 | );
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/Admin/components/AdminSite/profile.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Login Settings
7 |
8 |
9 | Name
10 |
11 | true
12 |
13 |
14 |
15 | Email
16 |
17 | 50
18 | true
19 |
20 |
21 |
22 | false
23 | 2FA Enabled
24 |
25 | Yes, two factor authentication is enabled for this account.
26 |
27 |
28 |
29 | Change Password
30 | false
31 |
32 | Old Password
33 |
34 | false
35 | 4
36 |
37 |
38 |
39 | New Password
40 |
41 | false
42 | 4
43 |
44 |
45 |
46 | Confirm New Password
47 |
48 |
49 |
50 |
51 | Two Factor Authentication
52 | false
53 | false
54 |
55 | Step 1: Scan the QR code below using an authenticator app like 1Password, LastPass, Google Authenticator, or Authy.
56 |
57 | Scan QR Code
58 |
59 | text/xml
60 |
61 |
62 |
63 |
64 | Step 2: After scanning the QR code, your app will display a verification code which you will enter below
65 |
66 | Verification code
67 |
68 | false
69 | 6
70 | 7
71 |
72 |
73 |
74 |
75 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/www/styles/admin-layout.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #f7f5f3;
3 | color: #333;
4 | margin: 0;
5 | padding: 0;
6 | font-family: "Open Sans", sans-serif;
7 | }
8 |
9 | /* turn off 10px left/right margins from default YUI grid */
10 | #doc, #doc2, #doc3 {
11 | margin: 0;
12 | }
13 |
14 | a:link,
15 | a:visited {
16 | color: #1976d2;
17 | }
18 |
19 | a:hover {
20 | color: #333;
21 | }
22 |
23 | a:active {
24 | color: #000;
25 | }
26 |
27 | img {
28 | border: 0;
29 | }
30 |
31 | /* YUI Reset basics */
32 | strong {
33 | font-weight: bold;
34 | }
35 |
36 | em {
37 | font-style: italic;
38 | }
39 |
40 | p {
41 | margin: 0 0 1em 0;
42 | }
43 |
44 | #yui-main .yui-b {
45 | margin-top: 1em;
46 | }
47 |
48 | /* layouts with menu on left */
49 | .yui-t1 #yui-main .yui-b,
50 | .yui-t2 #yui-main .yui-b,
51 | .yui-t3 #yui-main .yui-b {
52 | margin-right: 1em;
53 | }
54 |
55 | /* layouts with menu on right */
56 | .yui-t4 #yui-main .yui-b,
57 | .yui-t5 #yui-main .yui-b,
58 | .yui-t6 #yui-main .yui-b {
59 | margin-left: 1em;
60 | }
61 |
62 | #doc3 {
63 | min-width: 74.923em;
64 | }
65 |
66 |
67 | /* Header Styles */
68 |
69 | #hd {
70 | padding: 2px 0;
71 | color: #888;
72 | }
73 |
74 | #hd a { color: #555; }
75 | #hd a:hover { color: #777; }
76 | #hd a:active { color: #888; }
77 |
78 | #hd #admin-syslinks {
79 | float: right;
80 | margin: 0 0 0 2em;
81 | position: relative;
82 | top: -2px;
83 | z-index: 1;
84 | }
85 |
86 | #hd h1 {
87 | margin: 0;
88 | padding: 0;
89 | font-size: 100%;
90 | display: inline;
91 | font-weight: normal;
92 | }
93 |
94 |
95 | /* Basic styles to build up from YUI reset */
96 |
97 | h1, h2, h3, h4, h5, h6{ font-weight: bold; }
98 |
99 | h1 { font-size: 152%; }
100 | h2 { font-size: 136%; }
101 | h3 { font-size: 122%; }
102 | h4 { font-size: 107%; }
103 | h5 { font-size: 92%; }
104 | h6 { font-size: 85%; }
105 |
106 |
107 | /* Content Styles */
108 |
109 | .admin-light {
110 | color: #666;
111 | }
112 |
113 |
114 | /* Confirmation dialogs */
115 |
116 | #confirmation_container {
117 | background: url(../images/admin-dialog-question.png) -4px -4px no-repeat;
118 | padding: 0 0 0 52px;
119 | min-height: 48px;
120 | margin: 8px 0 32px;
121 | }
122 |
123 | #confirmation_container > h3 {
124 | margin-top: 0;
125 | padding-top: 9px;
126 | }
127 |
128 | #confirmation_frame ul {
129 | list-style-type: disc;
130 | padding-left: 24px;
131 | }
132 |
133 | #confirmation_frame ul li {
134 | margin-left: 16px;
135 | margin-top: 0.1em;
136 | }
137 |
138 |
139 | /* Errors */
140 |
141 | .swat-frame.admin-exception-container .swat-frame-contents {
142 | background-image: url(../images/admin-dialog-exception.png);
143 | background-position: 12px 20px;
144 | background-repeat: no-repeat;
145 | padding: 24px 16px 48px 68px;
146 | min-height: 48px;
147 | }
148 |
149 | .admin-exception-container .swat-exception {
150 | margin-left: 0;
151 | margin-right: 0;
152 | }
153 |
154 |
155 | /* Logout button */
156 |
157 | address { font-style: normal; }
158 |
159 | #logout {
160 | display: inline;
161 | margin: 0;
162 | padding: 0;
163 | }
164 |
165 | #logout div.swat-form-field,
166 | #logout div.swat-form-field-contents {
167 | display: inline;
168 | margin: 0;
169 | }
170 |
171 | #logout input.swat-button {
172 | margin: 0;
173 | padding: 4px 8px;
174 | vertical-align: middle;
175 | font-size: 11px;
176 | }
177 |
178 | #admin-identifier {
179 | vertical-align: middle;
180 | }
181 |
182 | #admin-navbar {
183 | position: relative;
184 | top: 2px;
185 | vertical-align: middle;
186 | }
187 |
188 | .clearfix:after {
189 | content: ".";
190 | display: block;
191 | height: 0;
192 | clear: both;
193 | visibility: hidden;
194 | }
195 |
196 | .clearfix {
197 | display: inline-block;
198 | }
199 |
200 | @media (-webkit-min-device-pixel-ratio: 1.5),
201 | (min--moz-device-pixel-ratio: 1.5),
202 | (-o-min-device-pixel-ratio: 3/2),
203 | (min-resolution: 1.5dppx) {
204 |
205 | #confirmation_container {
206 | background-image: url(../images/admin-dialog-question@2x.png);
207 | background-size: 48px 48px
208 | }
209 |
210 | .swat-frame.admin-exception-container .swat-frame-contents {
211 | background-image: url(../images/admin-dialog-exception@2x.png);
212 | background-size: 48px 48px
213 | }
214 |
215 | }
216 |
--------------------------------------------------------------------------------
/Admin/components/AdminGroup/Edit.php:
--------------------------------------------------------------------------------
1 | initUsers();
35 | $this->initComponents();
36 | }
37 |
38 | protected function initUsers()
39 | {
40 | $user_list = $this->ui->getWidget('users');
41 | $user_list_options = SwatDB::getOptionArray(
42 | $this->app->db,
43 | 'AdminUser',
44 | 'name',
45 | 'id',
46 | 'name'
47 | );
48 |
49 | $user_list->addOptionsByArray($user_list_options);
50 | }
51 |
52 | protected function initComponents()
53 | {
54 | $component_list = $this->ui->getWidget('components');
55 | $component_list_options = SwatDB::getGroupedOptionArray(
56 | $this->app->db,
57 | 'AdminComponent',
58 | 'title',
59 | 'id',
60 | 'AdminSection',
61 | 'title',
62 | 'id',
63 | 'section',
64 | 'AdminSection.displayorder, AdminSection.title, ' .
65 | 'AdminComponent.displayorder, AdminComponent.title'
66 | );
67 |
68 | $component_list->setTree($component_list_options);
69 | }
70 |
71 | // process phase
72 |
73 | protected function postSaveObject()
74 | {
75 | $this->updateUserBindings();
76 | $this->updateComponentBindings();
77 | }
78 |
79 | protected function updateUserBindings()
80 | {
81 | $user_list = $this->ui->getWidget('users');
82 |
83 | SwatDB::updateBinding(
84 | $this->app->db,
85 | 'AdminUserAdminGroupBinding',
86 | 'groupnum',
87 | $this->getObject()->id,
88 | 'usernum',
89 | $user_list->values,
90 | 'AdminUser',
91 | 'id'
92 | );
93 | }
94 |
95 | protected function updateComponentBindings()
96 | {
97 | $component_list = $this->ui->getWidget('components');
98 |
99 | SwatDB::updateBinding(
100 | $this->app->db,
101 | 'AdminComponentAdminGroupBinding',
102 | 'groupnum',
103 | $this->getObject()->id,
104 | 'component',
105 | $component_list->values,
106 | 'AdminComponent',
107 | 'id'
108 | );
109 | }
110 |
111 | protected function getSavedMessagePrimaryContent()
112 | {
113 | return sprintf(
114 | Admin::_('Group “%s” has been saved.'),
115 | $this->getObject()->title
116 | );
117 | }
118 |
119 | // build phase
120 |
121 | protected function loadObject()
122 | {
123 | parent::loadObject();
124 |
125 | if (!$this->isNew()) {
126 | $this->loadUserBindings();
127 | $this->loadComponentBindings();
128 | }
129 | }
130 |
131 | protected function loadUserBindings()
132 | {
133 | $user_list = $this->ui->getWidget('users');
134 | $user_list->values = SwatDB::queryColumn(
135 | $this->app->db,
136 | 'AdminUserAdminGroupBinding',
137 | 'usernum',
138 | 'groupnum',
139 | $this->getObject()->id
140 | );
141 | }
142 |
143 | protected function loadComponentBindings()
144 | {
145 | $component_list = $this->ui->getWidget('components');
146 | $component_list->values = SwatDB::queryColumn(
147 | $this->app->db,
148 | 'AdminComponentAdminGroupBinding',
149 | 'component',
150 | 'groupnum',
151 | $this->getObject()->id
152 | );
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/po/preprocess.php:
--------------------------------------------------------------------------------
1 |