Одностраничная админка (псевдостраницы)

Приведенная здесь одностраничная админка удобна для создания админки на том же корневом шаблоне, на котором строятся и обычные страницы, то есть, админка будет в стиле сайта.

Концепция построения админки

  • Админка строится на одной странице, а отдельные страницы будут являться псевдостраницами.
  • Каждой псевдостранице будет соответствовать свой блок, внутрь которого, естественно, можно назначить другие блоки.
  • Все блоки псевдостраниц будут находиться на одной странице, но отображаться будет только один, соответствующий запросу, сделанному из меню навигации.

Для примера построим клиенский раздел сайта. Структуру админки зададим в головном шаблоне client. Все остальные шаблоны поместим в подпапку client. Верстка выполнена в стандарте Bootstrap 3.

Так называемый головной шаблон админки нужно назначать в содержательный блок страницы.

Головной шаблон админки

client.tdd

<?php
# Блок для настройки других блоков (в данном уроке не приводится)
$titles[1] => 'Настройки';
$types[1] => 'block(client/setting, new)';

$params['no-edit-buttons'] = true;

/** 
 * $templates array [$field => [$template, $isscript, $name]]
 * @var string $template Имя служебного шаблона или обычного шаблона.
 * @var int $isscript Если обычный шаблон - 0, если служебный скрипт - 1.
 * @var string $name Название пункта навигации. Если пусто, то не отоображается в навигации
 */
$templates = [
    # Кастомизированные служебные шаблоны для регистрации пользователей (в данном уроке не приводятся)
    2 => ['user-info',       1, 'Регистрационные данные'],
    3 => ['user-activation', 1],
    4 => ['login',          1],
    5 => ['password-restore', 1],
    # Обычные шаблоны
    6 => ['profiles',       0, 'Мой профиль'],
    ...
]; 

$dat= Dat::get($blockInfo, []); # Получить номера всех блоков
foreach ($templates as $field => $t) {
    $types[$field] = 'block(client/'.$t[0].', new)'; # Автоматическое назначение шаблонов
    $titles[$field] = $t[2];
    if (Blox::getScriptName()=='page') {
        if ($field == $_GET['field']) { # Текущий блок
            if ($t[1]) # Формируем запрос к служебному скрипту
                Request::add('src='.$dat[$field].'&script='.$t[0]);
        } else # Все блоки, кроме текущего, блокируются
            $fields['none'][] = $field; 
    }
}
# Передаем данные на остальные блоки.
$GLOBALS['client']['templates'] = $templates;
?>

client.tpl

<div class="row">
    <div class="col-md-3">
        <!-- Меню навигации админки -->
        <ul class="nav nav-pills">
            <?php
            foreach ($GLOBALS['client']['templates'] as $field=>$t) {
                if ($t[2]) {
                    $href = '?page='.$page.'&field='.$field;
                    $clas = ($_GET['field'] == $field) ? ' class="active"' : '';
                    echo'<li'.$clas.'><a href="'.$href.'">'.$t[2].'</a></li>';
                } else
                    continue;
            }
            ?>
        </ul>
    </div>
    <div class="col-md-9">
        <?php
        echo $dat[1]; # Блок с настройками выводится всегда
        echo $dat[$_GET['field']]; # Выводим только один содержательный блок
        ?>
    </div>
</div>

Страница профиля пользователя

В качестве примера содержательного блока админки приведем шаблон для редактирования данных о пользователе profiles.

Это, в общем, не простой шаблон, и его можно было сделать путем кастомизации служебного скрипта ?edit. Однако, здесь применяются простые текстовые поля ввода, поэтому сделаем обычный шаблон, но данные будем отправлять на служебный скрипт ?update. Если в вашей админке будет много страниц с формами редактирования, стоит все же подумать о кастомизации служебного скрипта ?edit.

profiles.tdd

<?php
$titles = [
    1 => 'id пользователя',
    2 => 'Телефоны',
    3 => 'Адрес доставки',
];  
$types = [
    1 => 'int(6) not null default 0',
    2 => 'varchar(255)',
    3 => 'text',
];
$params = [
    'multi-record' => true,
    'no-new-rec-button' => true,
    
];
$fields['no-text-editor'] = [3];
# Выводим только одну запись, соотв. текущему пользователю
if (Blox::getScriptName() == 'page') {
    if ($userId = Blox::info('user','id'))
        Request::add('block='.$blockInfo['id'].'&p[1]='.$userId);
}
?>

profiles.tpl

<?php
if (!$tab) { # Создать новую запись, если ее нет
    if ($userId = Blox::info('user','id'))
        $dat = Dat::insert($blockInfo, [1 => $userId]);   
}
?>
<h1><?=$GLOBALS['client']['templates'][$_GET['field']][2]?></h1>
<!--Отправка данных на служебный скрипт ?update --> 
<form action="?update&block=<?=$block?>&rec=<?=$dat['rec']?>&pagehref=<?=Blox::getPageHref(true)?>" method="post">
    <input type="hidden" name="dat[1]" value="<?=$dat[1]?>" />
    <div class="form-horizontal">
        <div class="form-group">
            <label for="login-login" class="col-sm-3 control-label">Телефоны</label>
            <div class="col-sm-9">
                <input type="text" class="form-control" name="dat[2]" value="<?=$dat[2]?>" />
            </div>
        </div>
        <div class="form-group">
            <label for="login-login" class="col-sm-3 control-label">Адрес доставки</label>
            <div class="col-sm-9">
                <input type="text" class="form-control" name="dat[3]" value="<?=$dat[3]?>" />
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-3 col-sm-9">
                <button name="button-ok" type="submit" value="ok" class="btn btn-primary">Сохранить</button>
            </div>
        </div>
    </div>
</form>

profiles.tuh

<?php
/**
 * Массовый пользователь после регистрации не получает никаких прав.
 * Права даются программно с помощью класса Permission.
 */
if (Dat::get( # Соответствует ли запись текущему пользователю
    ['src-block-id'=>$_GET['block'], 'tpl'=>'client/profiles'], 
    ['rec'=>$_GET['rec'], 1=>Blox::info('user','id')]
)) {
    Permission::add( # Разрешение на редактирование одной записи
        'record', 
        [$_GET['block'], $_GET['rec']], 
        ['edit'=>true]
    );
}
?>