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: