msdn04_capa.JPG

Clique aqui para ler todos os artigos desta edição

 

Crie seu Weblog com ASP.NET, JavaScript e OleDB (Parte II)

por Marco Bellinaso

Esta é a segunda parte do artigo sobre Weblog, onde você verá como selecionar o blog em um período de datas, trabalhar com janelas de calendário pop-up, inserir comentários e administrar o blog.

 

Selecionando um Intervalo e Carregando o Blog

Na primeira parte foi abordada a definição de conteúdo de página no arquivo ASPX, mas não foi analisado nenhum código que realmente carregue o conteúdo do blog. Para permitir que o usuário selecionasse um intervalo de um único dia, de uma semana ou de todo o mês, foi utilizado o controle Calendar com a propriedade SelectionMode definida como DayWeekMonth. É boa idéia fornecer caixas de texto para as datas de início e fim desejadas pelo usuário, caso ele queira selecionar as últimas duas semanas ou os últimos 45 dias, por exemplo. A Figura 1 mostra os novos controles adicionados à página com a semana toda selecionada no Calendar.

 

image001.gif

Figura 1 Intervalos

 

Se, devido a um postback, a página não for carregada, você deve selecionar um intervalo padrão, por exemplo, a última semana. No entanto, no caso de blogs atualizados com muita freqüência, poderá ser mais apropriado carregar os dados para menos dias e, no caso de blogs raramente atualizados, para todo o mês. Assim como no recurso de notificação de comentários, a melhor opção é deixar a escolha para o administrador do blog, usando uma chave personalizada no arquivo web.config que permita especificar o número de dias para o intervalo padrão. O código a seguir mostra como a chave personalizada é lida a partir do arquivo, feito o Parse para Integer e usada para calcular um intervalo para os últimos n dias, bem como a maneira como o intervalo é destacado no calendário:

 

Private Sub Page_Load(...) Handles MyBase.Load

    If Not IsPostBack Then

        Dim defPeriod As Integer = Integer.Parse( _

            ConfigurationSettings.AppSettings("Blog_DefaultPeriod"))

        Dim fromDate = Date.Today.Subtract(New TimeSpan(defPeriod -_

            1,0,0,0))

        BlogCalendar.SelectedDates.SelectRange(fromDate, Date.Today)

        BindData()

    End If

End Sub

 

A data de início é calculada subtraindo-se n-1 dias da data de hoje. A chamada para BindData carrega os dados para o intervalo selecionado e os vincula ao Repeater e a seus controles internos. Esse método chama o método GetData da classe de negócio do Blog desenvolvido anteriormente (parte I) e passa as datas de início e fim da data selecionada no calendário, que são lidas a partir da coleção SelectedDates:

 

Private Sub BindData()

    Dim ds As DataSet = m_BlogManager.GetData( _

        BlogCalendar.SelectedDates(0), _

        BlogCalendar.SelectedDates(_

        BlogCalendar.SelectedDates.Count - 1))

    Blog.DataSource = ds.Tables("Messages").DefaultView

    Blog.DataBind()

End Sub

 

Caso tenha sido selecionado um dia, a coleção SelectedDates terá apenas um item e as datas de início e fim serão iguais. Quando o usuário clica no calendário, a página é enviada novamente (é feito o postback), o novo intervalo é automaticamente selecionado e você manipula o evento SelectionChanged do calendário para que ele chame outra vez o BindData para o novo intervalo. Por fim, você deve manipular o evento Click do botão Load para selecionar o intervalo personalizado especificado no calendário e carregar os dados do blog. No procedimento deste evento, é feito o parse no conteúdo dos dois controles de entrada e são obtidas duas datas. Embora seja possível adicionar validadores no final do procedimento para assegurar que o formato de data esteja correto antes de enviar o formulário, a análise poderia gerar uma exceção se o formato de data não fosse válido. Nesse caso, foi utilizada a data atual (veja a Listagem 1).

 

Listagem 1 Carregando as datas

Private Sub LoadBlog_Click(...) Handles LoadBlog.Click

    Dim fromDate, toDate As Date

    Try

        fromDate = Date.Parse(IntervalFrom.Text)

    Catch

        fromDate = Date.Today

    End Try

    ' Faça o mesmo para o toDate...

    ' Se toDate for anterior a fromDate, defina toDate = fromDate

    If toDate < fromDate Then toDate = fromDate

    BlogCalendar.SelectedDates.SelectRange(fromDate, toDate)

    BlogCalendar.VisibleDate = toDate

    BindData()

End Sub

 

Observe o uso do VisibleDate do calendário (para assegurar que a data final esteja visível no calendário). Isso é necessário porque, se o usuário selecionasse um período de dois meses anteriores, a seleção não estaria visível no calendário. Em vez disso, o calendário mostraria o mês atual, o que não seria muito claro.

 

Calendário Pop-up

Na configuração atual, se o usuário quiser selecionar um intervalo diferente de um único dia, semana ou mês, ele precisará digitar manualmente a data de início e de fim nas duas caixas de texto e fazer referência a um calendário externo. Além disso, ele poderá digitar a data em um formato inválido. Por esses motivos, é boa prática oferecer um calendário pop-up. Quando o usuário clicar em uma data, o calendário será fechado e a data aparecerá no controle textbox da janela principal. Esse recurso pode ser facilmente reproduzido em ASP.NET por meio do controle Calendar e de um pouco de JavaScript no lado cliente.

Primeiro, vamos cuidar do código ASPX da janela-pai. Adicione um link em uma imagem que abre a janela pop-up por meio de uma chamada à seguinte função JavaScript:

 

   

 

O procedimento JavaScript espera receber o nome do controle textbox que será preenchido com a data selecionada, juntamente com a largura e a altura da janela do calendário pop-up que será aberta. Veja aqui o código JavaScript que deverá entrar na seção já definida no canto superior da página:

 

function PopupPicker(ctl,w,h)

{

    var PopupWindow=null;

    settings='width='+ w + ',height='+ h;

    PopupWindow=window.open('DatePicker.aspx?Ctl=' +

        ctl,'DatePicker',settings);

    PopupWindow.focus();

}

 

Esse código usa window.open para abrir a janela pop-up com uma dimensão especificada, não-ajustável, sem nenhuma barra de rolagem, menu, barra de ferramentas ou barra de status. O primeiro parâmetro é a URL da página a ser carregada na nova janela; no código mostrado, ele carrega uma página DatePicker.aspx com um parâmetro ctl, cujo valor é passado como uma entrada para o código PopupPicker.

Agora que terminamos a página principal, é hora de escrever a página DatePicker.aspx que processa o calendário. Essa página possui um controle Calendar cujas propriedades Width e Height estão definidas como 100%, a fim de que cubra a página inteira. Outro item importante a ser observado no arquivo ASPX é o código JavaScript no lado cliente, que pega uma seqüência de caracteres como entrada e a utiliza como valor para o controle de entrada do formulário-pai e cujo nome é passado na seqüência de caracteres de consulta (query string). Por fim, ele fecha o formulário pop-up propriamente dito. O código JavaScript pode ser visto na Listagem 2.

 

Listagem 2 Definindo a data

function SetDate(dateValue)

{

    // pega na querystring o valor do parâmetro ctl,

    // ou seja, o nome do controle de entrada do formulário-pai

    // que o usuário deseja definir com a data selecionada

    ctl = window.location.search.substr(5);

    // define o valor do controle com a data fornecida

    thisForm =

      window.opener.document.forms[0].elements[ctl].value = dateValue;

    // fecha esta pop-up

    self.close();

}

 

Quando o usuário clicar em um link no controle Calendar, em vez do processamento normal que submete o formulário e seleciona o dia clicado, faremos com que seja invocado o procedimento JavaScript personalizado. Por padrão, todos os links processados pelo controle Calendar geram um postback para o servidor. Em vez disso, vamos fazer com que eles apontem para o código SetDate personalizado. Graças ao evento DayRender do Calendar (acionado a cada vez que um dia é processado e o qual fornece uma referência para a célula da tabela que está sendo criada), alterar a saída padrão das células de tabela que contêm os links para o dia é muito fácil. O fragmento de código a seguir substitui o conteúdo da célula padrão por meu próprio controle de hyperlink, que possui o mesmo texto mas aponta para o código JavaScript:

 

Private Sub DatePicker_DayRender(...) Handles DatePicker.DayRender

    Dim hl As New HyperLink()

    hl.Text = CType(e.Cell.Controls(0), LiteralControl).Text

    hl.NavigateUrl = "javascript:SetDate('" & _

        e.Day.Date.ToShortDateString() & "');"

    e.Cell.Controls.Clear()

    e.Cell.Controls.Add(hl)

End Sub

 

O valor passado para o código JavaScript é a data do dia selecionado, em formato abreviado (geralmente mm/dd/aa). Esse valor será usado para o controle de entrada no formulário-pai. A Figura 2 mostra a janela pop-up resultante.

 

image002.gif

Figura 2 Calendário Pop-up

 

Como você pode ver, os controles do servidor (Server Controls) ASP.NET podem ser altamente personalizados. Outra situação em que você poderá usar o evento DayRender dessa maneira consiste no redirecionamento para outra página, com a data incluída na seqüência de caracteres de consulta (query string) - em vez de redirecionar para a segunda página no servidor após um postback com a data. Para fazer isso, simplesmente substitua a linha na qual você define a propriedade NavigateUrl do hyperlink para algo semelhante ao seguinte:

 

hl.NavigateUrl = "SecondPage.aspx?Date=" & e.Day.Date.ToShortDateString()

 

 

Postando Comentários

Na Figura 2 da parte I do artigo (edição número 3 - janeiro/2004), você viu que, abaixo de cada mensagem, existe um link "Post your own comment" (Publique seu próprio comentário). Quando o usuário clica nesse link, é exibida uma caixa com controles de entrada que permite a inclusão de comentários. Você pode adivinhar como ela funciona porque usamos a mesma técnica para criar a lista de comentários. A caixa Comments com seus controles de entrada é declarada dentro de uma DIV cujo estilo de exibição é inicialmente definido como "none", para que ela não esteja visível. Quando se clica nesse link, o estilo é alterado e a página é rolada até o canto inferior até que se torne visível. Para evitar o envio de códigos HTML desnecessários, que tornariam a página mais lenta, é definida uma única caixa de comentários no canto inferior da página (em vez de uma caixa para cada mensagem). A Figura 3 mostra a caixa de comentários e os controles de entrada necessários.

 

image003.gif

Figura 3 Postando um Comentário

 

Como você especifica a mensagem para a qual o comentário é postado? Uma boa solução é armazenar a ID da mensagem-pai em um textbox hidden ASP.NET para quando o usuário clicar no link "Post your own comment". Quando o botão Post (Enviar) for pressionado, esse valor poderá ser recuperado do codebehind. Observe que você não pode usar a propriedade Visible para ocultar o controle, porque quando Visible estiver definido como False o controle será oculto e o código HTML não será enviado para o cliente. Você precisa usar o mesmo estilo de exibição utilizado para a DIV. A DIV e o controle textbox são declarados desta forma:

 

  

   Post your own comment

 

A rotina ShowCommentBox JavaScript recebe a ID da mensagem a ser comentada e a utiliza como o valor do controle textbox oculto recém-declarado:

 

function ShowCommentBox(msgID)

{

    document.forms[0].ParentMessageID.value = msgID;

    ShowCommentBox2();

}

 

O código que realmente torna a caixa de comentários visível e rola a página para baixo está localizado em uma rotina separada (o código ShowCommentBox2). Chamaremos esse código novamente quando quisermos mostrar a caixa de comentários sem configurar o atributo de valor do controle textbox oculto:

 

function ShowCommentBox2()

{

    CommentBox.style.display = "";

    window.location.href = '#CommentBoxAnchor';

}

 

Agora, só nos resta criar o clique no botão Post (Enviar) para chamar o InsertComment da instância Business.Blog e vincular novamente os dados atualizados ao Repeater:

 

Private Sub PostComment_Click(...) Handles PostComment.Click

    m_BlogManager.InsertComment(Integer.Parse(ParentMessageID.Text), _

        Author.Text, Email.Text, Comment.Text)

    BindData()

 

    ' redefina o valor dos controles de entrada

    ParentMessageID.Text = ""

    ' redefina as outras textbox visíveis...

End Sub

 

Administrando o Blog

O código para as tarefas do usuário está quase completo. Os usuários podem ler as mensagens relacionadas ao intervalo selecionado e postar comentários. O proprietário do blog, por outro lado, precisa adicionar, inserir e excluir as mensagens e comentários diretamente no banco de dados. Para permitir esse acesso, a próxima etapa será desenvolver uma página de login e modificar a página principal do blog de modo que, quando o administrador estiver conectado, a página mostre os controles adicionais que permitem executar operações administrativas. A página de login é composta de caixas de texto para o nome e a senha do usuário, uma caixa de seleção para a opção "persistent login" (login persistente) e um botão de envio. Como você terá apenas um administrador e não precisará de segurança por função, armazenar as credenciais no arquivo web.config será suficiente. A Listagem 3 mostra a classe codebehind. Se as credenciais especificadas forem válidas, ele autenticará o usuário e o redirecionará para a página Default.aspx.

 

Listagem 3 Classe Codebehind da Página de Login

Imports System.Web.Security

 

Public Class Login

  Inherits System.Web.UI.Page

 

  ' Web Form Designer Generated Code here...

 

    Protected UserName As System.Web.UI.WebControls.TextBox

    Protected Password As System.Web.UI.WebControls.TextBox

    Protected Persistent As System.Web.UI.WebControls.CheckBox

    Protected WithEvents LoginUser As System.Web.UI.WebControls.Button

    Protected InvalidLogin As System.Web.UI.WebControls.Label

 

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As _

        System.EventArgs) Handles MyBase.Load

        If Not Page.IsPostBack Then

            ' se a querystring contiver um parâmetro "Action=logout", faça logout

            ' e exclua o cookie

            If Request.Params("Action") = "logout" Then

                FormsAuthentication.SignOut()

            End If

        End If

    End Sub

 

    Protected Sub LoginUser_Click(ByVal sender As System.Object, _

        ByVal e As System.EventArgs) Handles LoginUser.Click

        ' verifique o nome de usuário e senha

        If FormsAuthentication.Authenticate(UserName.Text, Password.Text)      

        Then

            ' se ok, salve o cookie

            FormsAuthentication.SetAuthCookie(UserName.Text, _

                Persistent.Checked)

            ' redirecione para Default.aspx

            Response.Redirect("Default.aspx", True)

        Else

            ' se as credenciais estiverem erradas, mostre a mensagem de erro

            InvalidLogin.Visible = True

        End If

    End Sub

End Class

 

Na página Default.aspx, adicionamos os controles de edição, que só estarão visíveis se o usuário for autenticado. Na parte superior da página, declare um painel (panel) com um link "logout" que aponte para Login.aspx com o parâmetro "action=logout" na seqüência de caracteres de consulta (query string), e uma tabela com as caixas de texto para especificar o título e o conteúdo da mensagem. Esse painel poderá ser exibido ou oculto no Page_Load, da seguinte forma:

 

MessageBox.Visible = User.Identity.IsAuthenticated

 

Quando o administrador preenche as caixas de texto e clica no botão Post (Enviar), é acionado o evento Click no lado servidor. Utilizamos a função InsertMessage para adicionar a nova mensagem ao banco de dados e chamar o BindData para carregá-la no Repeater.

Agora, é hora de adicionarmos a funcionalidade de edição. Para fazer isso, adicione um controle LinkButton ao ItemTemplate do Repeater: