WebMatrix
- Gerenciando Usuários, Pedidos e Categorias - 9
Na oitava parte deste artigo iniciamos a implementação da área de administração do site vamos continuar criando um link na página principal para acessar a área de administração e criar as tarefas do administrador.
Obs: Enquanto eu escrevia esta série de artigos foi liberada a versão 2 do WebMatrix e a área de administração vai ser criada usando esta versão.
Para baixar a nova versão clique no link: http://www.microsoft.com/web/webmatrix/
Criando o link para a área de
administração - Ajustando o Helper ResumoContas
Embora possamos acessar a área de administração do site navegando pela URL /Admin seria mais adequado fornecer um link após o login na página do site de forma que o acesso fosse feito apenas clicando neste link.O processo administrativo que iremos definir em nosso site incluem:
Para fazer isso vamos alterar o Helper ResumoConta() do arquivo MembershipHelpers.cshtml que esta na pasta App_Code e incluir uma nova seção para exibir no canto superior de cada página o link para a área de administração.
Abra o arquivo App_Code/MembershipHelpers.cshtml e inclua o código destacado conforme mostra a figura abaixo:
![]() |
Este código exibirá um hiperlink adicional com o nome Admin para a área de administração do site para os usuários que após se autenticarem forem membros do grupo de administradores.
![]() |
Gerenciando os usuários administradores
Agora que temos a infraestrutura para a área de
segurança de administração do nosso site, vamos precisar de uma maneira de
adicionar e remover usuários existentes com perfil de administrador.
Vamos criar uma nova pasta chamada Users dentro da Pasta Admin e
dentro dela vamos criar uma página Default.cshtml que irá listar todos os
membros atuais com função de administrador com um botão para remoção do usuário
e também com uma opção de incluir novos membros.
Clique com botão direito sobre a pasta Admin e selecione Nova Pasta (New Folder) e informe o nome Users.
A seguir Clique com botão direito sobre a pasta Users e selecione Novo Arquivo (New File), selecione o template CSHTML e informe o nome Default.cshtml e clique no botão OK.
Agora abra o arquivo Default.cshtml e substitua o código existente pelo código abaixo:
@{
Layout = "~/Shared/Layouts/_AdminLayout.cshtml";
Page.Title = "Usuários Administradores";
var usuarioAIncuir = "";
var role = "Admin";
if(IsPost)
{
usuarioAIncuir = Request["usuarioAIncuir"];
if(usuarioAIncuir != null)
{
// Incuir o usuário no perfil admin
if (WebSecurity.UserExists(usuarioAIncuir) && !Roles.IsUserInRole(usuarioAIncuir, role))
{
Roles.AddUsersToRoles(new [] { usuarioAIncuir }.ToArray(),new [] { role }.ToArray());
}
else
{
ModelState.AddError("usuarioAIncuir", "Não foi possível incluir usuário como Admin");
}
}
else if (Request["usuarioARemover"] != null)
{
// Remove usuário
var usuarioARemover = Request["usuarioARemover"];
if (Roles.IsUserInRole(usuarioARemover, role))
{
Roles.RemoveUsersFromRoles(new [] { usuarioARemover }.ToArray(),new [] { role }.ToArray());
}
}
}
var users = Roles.GetUsersInRole(role);
}
<h1>Usuários Administradores</h1>
<table class="grid">
<tr>
<th colspan="3" class="gridHeader">Usuário</th>
</tr>
@foreach (var user in users)
{
<tr>
<td id="mainColumn">@user</td>
<td>
@{
var buttonState = "";
if(user == WebSecurity.CurrentUserName)
{
buttonState = "disabled=disabled";
}
}
<form action="" method="post">
@Html.Hidden("usuarioARemover", user)
<input type="submit" value="Remover" @buttonState />
</form>
</td>
</tr>
}
</table>
<h2>Incluir Usuário</h2>
<form action="" method="post">
<p>
Usuário: @Html.TextBox("usuarioAIncuir", usuarioAIncuir)
<input type="submit" value="Incluir" />
@Html.ValidationMessage("usuarioAIncuir")
</p>
</form>
|
O manipulador de solicitação POST verifica se um campo usuarioAIncuir foi passado nos valores do formulário postado. Se ele for passado, nós sabemos que o usuário submeteu o formulário 'Adicionar usuário Admin', então tentamos adicionar o usuário no perfil admin.
Caso contrário, se um campo usuarioARemover for apresentado, sabemos que o usuário clicou no botão "Remover', para que tente remover o usuário. Aqui usamos um mecanismo semelhante ao usado na página do Carrinho de Compras.
A página Default.cshtml com o código acima deverá ter a seguinte aparência:
![]() |
Note que não permitimos que usuário atualmente logado seja removido, fazemos
isso desabilitando o botão remover para este usuário.
Para incluir um novo usuário exigimos que seja digitado o nome do usuário da
conta que desejamos adicionar ao perfil de administrador, poderíamos ter
fornecido uma lista suspensa de usuários registrados mas por uma questão de
desempenho preferimos não fazê-lo.
Criando a página de Administração dos pedidos
Vamos precisar de uma área em nosso site, onde podemos ver os pedidos dos
clientes e onde poderemos marcá-los como enviados. O processo de administração
dos pedidos será composto por duas páginas.
A primeira página irá listar um resumo de todas os pedidos em um WebGrid. Cada
linha da WebGrid irá exibir um hiperlink para levar o usuário para a segunda
página, que
exibe os detalhes completos do pedido.
Vamos começar criando a página de resumo dos pedidos.
Clique com botão direito sobre a pasta Admin e selecione Nova Pasta (New Folder) e informe o nome Pedidos.
A seguir Clique com botão direito sobre a pasta Pedidos e selecione Novo Arquivo (New File), selecione o template CSHTML e informe o nome Default.cshtml e clique no botão OK.
Agora abra o arquivo Default.cshtml e substitua o código existente pelo código abaixo:
@{
Layout = "~/Shared/Layouts/_AdminLayout.cshtml";
Page.Title = "Pedidos";
var exibirPedidosEnviados = false;
if(IsPost)
{
exibirPedidosEnviados = Request["exibirPedidosEnviados"].AsBool();
}
var db = Database.Open("TecnoSite");
var sqlCommand = "SELECT Pedidos.pedidoid, Pedidos.pedidoEnviado, Pedidos.pedidoTotal, " +
"Pedidos.pedidoDataHora, UserProfile.Email FROM Pedidos " +
"INNER JOIN UserProfile ON Pedidos.usuarioid = UserProfile.UserId " +
"WHERE pedidoEnviado = @0 " +
"ORDER BY pedidoDataHora DESC";
var resultado = db.Query(sqlCommand, exibirPedidosEnviados); var pedidosGrid = new WebGrid(source: resultado, rowsPerPage: 20); } <h1>Resumo dos Pedidos</h1>
<form action="Default" method="post">
<p>
Pedidos não Enviados @Html.RadioButton("exibirPedidosEnviados", "false", !exibirPedidosEnviados)
Pedidos Enviados @Html.RadioButton("exibirPedidosEnviados", "true", exibirPedidosEnviados)
<input type="submit" value="Visualizar"/>
</p>
</form>
<div class="grid">
@pedidosGrid.GetHtml(
tableStyle: "grid",headerStyle: "gridHeader",alternatingRowStyle: "gridAlt",footerStyle: "gridFooter",
columns: pedidosGrid.Columns(pedidosGrid.Column("pedidoid","No. Pedido"),
pedidosGrid.Column("Email","Cliente",format: @<text><a href="mailto:@item.Email">@item.Email</a></text>),
pedidosGrid.Column("pedidoEnviado","Enviado?"),
pedidosGrid.Column("pedidoTotal","Valor Total",
format: @<text> R$@item.pedidoTotal</text>),
pedidosGrid.Column("pedidoDataHora","Data Pedido"),
pedidosGrid.Column(null,null,format: @<text> <a href="PedidosDetalhes/@item.pedidoid">Detalhes</a></text>)))
</div>
|
Neste código estamos usando o Helper WebGrid para exibir um resumo dos pedidos, dessa forma podemos incluir a habilidade de paginação e ordenação no grid.
Também incluímos um formulário que permite ao administrador escolher se deseja visualizar os pedidos enviados ou os não enviados.
Abaixo vemos o resultado da página Default.cshtml da pasta /Admin/Pedidos:
![]() |
Olhando o código, você pode ver que a instrução SQL para obter os dados para
o WebGrid contém um INNER JOIN entre as tabelas Pedidos e
UserProfile. Isso nos permite recuperar o nome do usuário (e-mail) do
cliente, com base na UserId armazenado em cada pedido.
A cláusula WHERE da instrução SQL filtra os resultados com base na coluna
pedidoEnviado que por padrão é definido como False. O
corpo da página contém o nosso helper WebGrid, que é preenchido a partir
dos resultados a consulta SQL
A grade é simples, exceto que a última coluna não é associado a uma coluna de
dados, mas exibe um hiperlink para a página de detalhes do pedido, passando o
pedidoid na URL.
Criando a página de detalhes do pedido
A página Detalhes
dos pedidos exibe os detalhes completos de qualquer pedido especifico, incluindo
a data e hora do pedido, detalhes de despacho, detalhes de todos os itens dentro
do pedido e status de envio.
A página inclui um checkbox "pedido enviado?
, que o administrador pode marcar para indicar o pedido foi despachado. Isto irá
definir o campo pedidoEnviado
como True, o qual irá
colocar o pedido na lista de pedidos enviados na lista de resumo dos pedidos.
Vamos criar uma nova página na pasta /Admin/Pedidos/
chamada PedidoDetalhes.cshtml
e incluir o código abaixo neste arquivo:
@{
Layout = "~/Shared/Layouts/_AdminLayout.cshtml";
Page.Title = "Detalhes do Pedido";
// Pega o pedidoid da URL, ou define para 0 se não localizada
var pedidoid = !UrlData[0].IsEmpty() ? UrlData[0] : "0";
var db = Database.Open("TecnoSite");
if (IsPost)
{
pedidoid = Request["pedidoid"];
var enviado = Request["enviado"].AsBool();
var sqlUpdate = "UPDATE Pedidos SET pedidoEnviado = @0 WHERE pedidoid = @1";
db.Execute(sqlUpdate, enviado, pedidoid);
Response.Redirect("~/Admin/Pedidos/");
}
// Pega cabeçaho dos detalhes
var sqlCommand = "SELECT Pedidos.*, UserProfile.Email FROM Pedidos " +
"INNER JOIN UserProfile ON Pedidos.usuarioid = UserProfile.UserId " +
"WHERE pedidoid = @0 ";
var _pedido = db.QuerySingle(sqlCommand, pedidoid);
// Pega os itens dos pedidos
var itemsSql = "SELECT * FROM PedidoItens WHERE pedidoid = @0";
var pedidoItens = db.Query(itemsSql, pedidoid);
}
@if (_pedido == null)
{
<p>Não foi possível obter detalhes do pedido.</p>
}
else
{
<text><h1>Pedido</h1>
<p><strong>ID do Pedido:</strong> @_pedido.pedidoid</p>
<p><strong>Cliente:</strong> <a href="mailto:@_pedido.Email">@_pedido.Email</a></p>
<p><strong>Data Pedido: </strong>@_pedido.pedidoDataHora</p>
<h2>Despacho</h2>
<p><strong>Endereço:</strong> @_pedido.endereco</p>
<p><strong>Cidade: </strong>@_pedido.cidade</p>
<p><strong>Estado:</strong> @_pedido.estado</p>
<p><strong>Cep:</strong> @_pedido.cep</p>
<p><strong>País: </strong>@_pedido.pais</p>
<form action="PedidoDetalhes" method="post" id="shippedForm">
@Html.Hidden("pedidoid", _pedido.pedidoid)
Pedido Enviado?
@Html.CheckBox("shipped", _pedido.pedidoEnviado, new { value = "true" })
<input type="submit" value="Atualizar" />
</form>
<h2>Itens do Pedido</h2>
<table id="cartTable" border="0">
<tr>
<th class="product">Produto</th>
<th class="price">Preço</th>
</tr>
@foreach (var item in pedidoItens)
{
<tr>
<td class="product">@Produtos.GetNomeProdutoPorID(item.produtoid)</td>
<td class="price">R$@item.preco</td>
</tr>
}
<tr class="cartTotal">
<td>Valor Total: </td>
<td>R$@_pedido.pedidoTotal</td>
</tr>
</table>
</text>
}
|
Quando a página inicialmente carrega, o código do pedido - pedidoid -
é recuperado da URL e duas consultas de banco de dados são executadas.
A primeira consulta preenche a metade superior da página, incluindo a seção
Detalhes do despacho, a segunda consulta recupera todos os itens individuais que
fazem parte do pedido sendo exibidos na seção Itens do Pedido.
Se o usuário marcar a caixa de seleção "Pedido Enviado?" e clicar no
botão "Atualizar", o valor do do campo pedidoEnviado no banco de dados
será atualizado para refletir o valor da caixa de seleção e o usuário é
retornado para a página Resumo dos pedidos.
Ao realizar essa ação, você vai notar que o pedido agora é listado na relação
dos pedidos enviados da página Resumo dos pedidos.
Abaixo vemos a página PedidosDetalhes.cshtml sendo exibida:
![]() |
Definindo a página Categorias dos produtos
Os produtos do catálogo do TecnoSite são organizados em categorias.
Todos os produtos devem ser atribuídos a uma categoria.
As categorias de produtos são armazenados na tabela de Categorias da
nossa base de dados e são usados para construir dinamicamente o menu Shared/Partials/_Categorias.cshtml
visto no lado esquerdo de cada página do site.
Precisamos uma forma de adicionar, editar e excluir itens da tabela
Categorias, para que possamos manter o catálogo de produtos bem organizado.
Portanto, adicione uma nova subpasta chamada Categorias dentro da pasta
Admin e dentro dela vamos criar uma nova página chamada Default.cshtml.
Esta página será usado para listar todas as categorias da tabela e permitirá que
os administradores adicionem uma nova categoria.
Clique com botão direito sobre a pasta Admin e selecione Nova Pasta (New Folder) e informe o nome Categorias.
A seguir Clique com botão direito sobre a pasta Categorias e selecione Novo Arquivo (New File), selecione o template CSHTML e informe o nome Default.cshtml e clique no botão OK;
A seguir inclua o código abaixo neste arquivo substituindo o código gerado por padrão:
@{
Layout = "~/Shared/Layouts/_AdminLayout.cshtml";
Page.Title = "Categorias";
var categoriaTitulo = "";
var db = Database.Open("TecnoSite");
if(IsPost && !Request["categoriaTitulo"].IsEmpty())
{
categoriaTitulo = Request["categoriaTitulo"];
if(!Admin.CategoriaExiste(categoriaTitulo))
{
var sqlInsert = "INSERT INTO Categorias (categoriaTitulo) VALUES (@0)";
db.Execute(sqlInsert, categoriaTitulo);
}
else
{
ModelState.AddError("categoriaTitulo", "Categoryia já exsite");
}
}
var sqlCommand = "SELECT * FROM Categorias";
var _categorias = db.Query(sqlCommand);
}
<h1>Categorias</h1>
<table class="grid">
<tr>
<th colspan="3" class="gridHeader">Categoria</th>
</tr>
@foreach (var categoria in _categorias)
{
<tr>
<td id="mainColumn">@categoria.categoriaTitulo</td>
<td><a href="EditaCategoria/@categoria.categoriaid">Edita</a></td>
<td><a href="DeletaCategoria/@categoria.categoriaid">Deleta</a></td>
</tr>
}
</table>
<h2>Nova Categoria</h2>
<form action="" method="post">
<p>Título da Categoria: @Html.TextBox("categoriaTitulo", categoriaTitulo)
<input type="submit" value="Incluir" />
@Html.ValidationMessage("categoriaTitulo")
</p>
</form>
|
O resultado desta será a exibição da seguinte página:
![]() |
Em sua carga inicial, a página recupera todas as categorias do banco de dados e
as apresenta em uma tabela, com dois links ao lado de cada categoria para enviar
o usuário para as páginas Editar e Excluir.
Embaixo da lista de categorias, um formulário HTML é exibido para permitir ao
usuário adicionar um novo item na tabela Categorias.
Quando este formulário é enviado de volta para o servidor, o método Admin.CategoriaExiste()
será chamado para garantir que nenhuma categoria duplicada será criada.
Se o método retornar false, o registro é inserido na tabela Categorias e
a página exibida novamente. Se a categoria já existe, um erro é adicionado ao
ModelStateDictionary, a ser exibida para o usuário através de uma chamada ao
método Helper Html.ValidationMessage() na seção Nova Categoria.
Admin.CategoriaExiste() é uma função razor personalizada que iremos criar
a seguir. Estamos criando como uma função, ao invés de apenas adicionar o código
diretamente na página, pois vamos usar este método novamente para realizar a
edição das categorias.
Adicionar um novo arquivo chamado Admin.cshtml dentro da pasta do
App_Code.
A seguir inclua o código abaixo neste arquivo:
@* Admin Funcões *@
@functions {
public static bool CategoriaExiste(string categoriaTitulo)
{
var db = Database.Open("TecnoSite");
var sqlQuery = "SELECT categoriaid FROM Categorias WHERE categoriaTitulo = @0";
return db.Query(sqlQuery, categoriaTitulo).Count() > 0;
}
}
|
O método CategoriaExiste() aceita um parâmetro string chamado categoriaTitulo e retorna um valor boleano (True/False). O valor do parâmetro é procurado no banco de dados e se nenhuma linha for encontrada o resultado retornará false.
Editando Categorias
A página para editar Categorias deverá exibir uma caixa de texto permitindo que um administrador altere o título da categoria. O código no tratamento da requisição POST é parecido com o usado na página das Categorias.
Quando o formulário for postado de volta vamos chamar a função Razor Admin.CategoriaExiste() para ter certeza de não estarmos duplicando uma categoria já criada, antes de submeter a atualização ao banco de dados.
Vamos incluir um novo arquivo na pasta Admin/Categorias chamado EditaCategoria.cshtml.
A seguir Clique com botão direito sobre a pasta Categorias e selecione Novo Arquivo (New File), selecione o template CSHTML e informe o nome EditaCategoria.cshtml e clique no botão OK;
A seguir substitua o código gerado pelo código abaixo:
@{
Layout = "~/Shared/Layouts/_AdminLayout.cshtml";
Page.Title = "Edita Categoria";
// Pega a categoriaid daf URL, ou define para zero se não existir
var categoriaid = !UrlData[0].IsEmpty() ? UrlData[0] : "0";
var categoriaTitulo = "";
var db = Database.Open("TecnoSite");
if(IsPost)
{
categoriaid = Request["categoriaid"];
categoriaTitulo = Request["categoriaTitulo"];
// Validação
if (categoriaTitulo.IsEmpty())
{
ModelState.AddError("categoriaTitulo", "O título da Categoria não pode ser vazio");
}
if(Admin.CategoriaExiste(categoriaTitulo))
{
ModelState.AddError("categoriaTitulo", "Esta Categoria já existe");
}
if(ModelState.IsValid)
{
var sqlUpdate = "UPDATE Categorias SET categoriaTitulo = @0 WHERE categoriaid = @1";
db.Execute(sqlUpdate, categoriaTitulo, categoriaid);
Response.Redirect("~/Admin/Categorias/");
}
}
else
{
var sqlCommand = "SELECT * FROM Categorias WHERE categoriaid = @0";
categoriaTitulo = db.QuerySingle(sqlCommand, categoriaid).categoriaTitulo;
}
}
<h1>Edita Categoria</h1>
<form action="" method="post">
<p>
Título da Categoria: @Html.Hidden("categoriaid", categoriaid)
@Html.TextBox("categoriaTitulo", categoriaTitulo)
<input type="submit" value="Atualizar" />
@Html.ValidationMessage("categoriaTitulo")
</p>
</form>
|
Executando o projeto novamente, efetuando o login com perfil Admin e editando uma categoria iremos obter a seguinte página:
![]() |
Deletando Categorias
Quando formos deletar uma categoria temos que tomar cuidado para não deletar uma categoria que possua produtos associado a ela.
O código desta página é muito simples e chama uma função helper para verificar se a categoria esta vazia antes de prosseguir com o processo de exclusão.
A exclusão é executada usando o código da categoria, categoriaid, passada na URL a partir da página das categorias.(Default.chstml)
Ao acessar a página para deletar uma categoria e se a categoria estiver vazia será apresentada uma mensagem se confirmação e dois botões.
Um dos botões é o botão - Deleta - que submete o formulário (ou seja processa a deleção) que é processada pelo manipulador de requisição POST no código da página.
O segundo botão é o botão - Cancela - o qual usa o código JavaScript no atributo OnClick para enviar o usuário diretamente para a página das categorias (Default.cshtml) sem submeter o formulário.
Se o usuário admin acessar a página e a categoria não estiver vazia, uma mensagem será exibida ao usuário informando que a categoria não pode ser deletada pois possui produtos associados a ela.
Vamos incluir um novo arquivo na pasta Admin/Categorias chamado DeletaCategoria.cshtml.
A seguir Clique com botão direito sobre a pasta Categorias e selecione Novo Arquivo (New File), selecione o template CSHTML e informe o nome DeletaCategoria.cshtml e clique no botão OK;
A seguir substitua o código gerado pelo código abaixo:
@{
Layout = "~/Shared/Layouts/_AdminLayout.cshtml";
Page.Title = "Deleta Categoria";
var categoriaid = UrlData[0];
if (categoriaid.IsEmpty()) {
Response.Redirect("~/Admin/Categorias/");
}
var db = Database.Open("TecnoSite");
if (IsPost)
{
var sqlDeleta = "DELETE FROM Categorias WHERE categoriaid = @0";
db.Execute(sqlDeleta, categoriaid);
Response.Redirect("~/Admin/Categorias/");
}
var sqlSeleciona = "SELECT * FROM Categorias WHERE categoriaid = @0";
var categoria = db.QuerySingle(sqlSeleciona, categoriaid);
}
<h1>Deleta Categoria</h1>
@if (!Admin.CategoriaVazia(categoriaid))
{
<p>Não foi possível deletar a categoria @categoria.CategoriaTitulo pois ela possui produtos associados.</p>
}
else
{
<p>Confirma a exclusão da categoria : @categoria.CategoriaTitulo ?</p>
<p style="margin:">
<form action="" method="post" id="deleteForm">
<input type="button" onclick="window.location = '@Href("~/Admin/Categorias/")';" value="Cancela" />
<input type="submit" value="Deleta" />
</form>
</p>
}
|
Esta pagina esta chamando uma função Razor chamada CategoriaVazia que verifica se a categoria não possui produtos associados antes de concluir a exclusão.
Temos que criar esta função no arquivo Admin.cshtml presente na pasta App_Code.
Abra o arquivo Admin.cshtml e inclua a função CategoriaVazia conforme o código destacado em azul abaixo:
@* Admin Funcões *@
@functions
{
public static bool CategoriaExiste(string categoriaTitulo)
{
var db = Database.Open("TecnoSite");
var sqlQuery = "SELECT categoriaid FROM Categorias WHERE categoriaTitulo = @0";
return db.Query(sqlQuery, categoriaTitulo).Count() > 0;
}
public static bool CategoriaVazia(string categoriaId)
{
var db = Database.Open("TecnoSite");
var sqlQuery = "SELECT produtoid FROM Produtos WHERE categoria = @0";
return db.Query(sqlQuery, categoriaId).Count() == 0;
}
}
|
Executando o projeto novamente e navegado até a página para gerenciar categorias podemos ter os seguintes resultados:
1- Se a categoria estiver vazia :
![]() |
2- Se a categoria tiver produtos associados:
![]() |
Aguarde a continuação da área de administração onde iremos implementar a administração dos produtos : WebMatrix - Gerenciando Produtos - 10
Referências: