O Style System 2 (S2) é uma linguagem de programação que permite ao utilizador escrever estilos (templates) para a plataforma de Blogs do SAPO. Em S2 o Blog é representado por classes de objectos, o que permite ao utilizador ter um maior controlo no modo como o conteúdo do Blog é representado.
O Blog é representado por um conjunto de views:
Recent Page
Entry Page
Reply Page
Month Page
Day Page
Tags Page
Year Page
Página principal do Blog, onde são mostrados os últimos posts.
Exemplo: http://blogs.blogs.sapo.pt
Página que mostra um post e todos os comentários (e discussões).
Exemplo: http://blogs.blogs.sapo.pt/430.html
Página de resposta a um post (ou comentário).
Exemplo: http://blogs.blogs.sapo.pt/430.html?mode=reply
Página de arquivo dos posts por mês.
Exemplo: http://blogs.blogs.sapo.pt/2006/03/
Página de arquivo dos posts por dia.
Exemplo: http://blogs.blogs.sapo.pt/2006/03/06/
Página com as estatisticas de utilização das tags.
Exemplo: http://blogs.blogs.sapo.pt/tag/
Página com o resumo dos meses do ano que tem posts.
Exemplo: http://blogs.blogs.sapo.pt/2006/
Cada view é composta por vários objectos, que vão representar o conteúdo da página.
Algumas das classes mais importantes:
Entry
Comment
User
Representa um post no blog.
A classe Entry tem vários atributos:
Título
Texto
Data
Tags
Autor
Representa um comentário a um post ou a outro comentário.
A classe Comment tem os seguintes atributos:
Texto
Data
Autor
Representa o autor do post ou comentário.
A classe User tem os seguintes atributos:
Nome (Nickname)
Link para área pessoal
A personalização do Blog é feita quando criamos um estilo S2 para o Blog.
Os estilos S2 podem ser criados com ajuda de um interface gráfico (no caso da personalização básica e intermédia) ou então manualmente (personalização avançada).
Um estilo é um conjunto de 1 até 6 layers (camadas) de tipos diferentes que vão definir o aspecto e funcionalidades do Blog.
Existem os seguintes layers:
Core – Layer principal do S2. É onde estão definidas as classes, funções e propriedades de base do S2.
I18NC – Layer de internacionalização do layer Core.
Layout – Layer de customização do estilo. É o layer que vai definir como vai ser representado o Blog.
I18N – Layer de internacionalização do layer Layout.
Theme – Layer com novos temas (cores, fontes, etc...) do layer Layout.
User – Layer com personalizações do layer Layout.
Um estilo tem sempre que incluir o layer Core. Os layers I18N, Theme e User só podem ser associados ao estilo se existir um layer Layout.
O Estilo criado por omissão para todos os Blogs é composto por 3 layers: Core, I18NC (PT1) e Layout (SAPO).
Para criar um novo estilo para o Blog vamos à página de personalização e escolhemos o Layout pretendido ao clicar no botão “Usar este template”. Esta acção vai criar um novo estilo com os layer Core, I18NC e Layout.
Na página de personalização intermédia temos um conjunto de propriedades que podemos personalizar no nosso Blog. Ao gravarmos as nossas personalizações é criado um novo layer do tipo User com os novos valores das propriedades. O estilo personalizado fica assim definido com 4 layers: Core, I18NC, Layout e User.
Na página de personalização avançada podemos definir uma nova cascade style sheet (css) para o nosso Blog. O Editor de css cria um layer do tipo Theme, onde define a nova css do Blog. O estilo personalizado fica definido com 5 layers: Core, I18NC, Layout, Theme e User.
Nesta página também podemos fazer a gestão de todos os nossos layers e estilos e consultar a documentação.
A documentação está dividida nos seguintes tópicos:
Documentação Oficial S2 – Guia de referência sobre a linguagem S2
Guia de desenvolvimento de templates – Guia de introdução ao desenvolvimento de layers e estilos.
Layers públicas – Listagem dos layers públicos existentes na plataforma de Blogs. Esta é uma boa secção de referência para o desenvolvimento de novos layers porque o código fonte dos layers está disponivel para consulta.
Nesta área podemos criar, editar e apagar os nossos layers.
A página está divida em duas secções:
Lista de layers
Criação de layers
Na secção “Your layers” podemos editar e apagar as layers.
Na secção “Create Layer” podemos criar novas layers. Esta secção está divida em duas partes:
Top Level Layer
Layout Specific Layer
As layers do tipo Layout são definidas na secção Top Level Layer porque estas layers estão associadas à layer Core.
As layers do tipo I18N, Theme e User são definidas na secção Layout Specific Layers porque tem de ficar associadas a uma layer do tipo Layout.
O Editor de layers divide-se em 3 partes:
Área de edição
Barra lateral (Navigation / Reference Pane)
Área de mensagens (Build pane)
A Barra lateral está dividida em 4 tabs:
Nav – Links de navegação para as funções e propriedades definidas no nosso layer.
Classes – Links para as páginas de documentação das Classes S2.
Funcs – Links para as páginas de documentação das Funções S2.
Props – Links para as páginas de documentação das Propriedades S2.
Na Área de mensagens aparecem as mensagens de sucesso ou de erro quando gravamos ou compilamos o layer.
Nesta área podemos criar, editar, associar e apagar os nossos estilos.
A página está dividida em duas secções:
Lista de estilos
Criação de um novo estilo
Na lista de estilos podemos editar, apagar e associar um estilo ao nosso Blog.
Na página de edição / criação de um estilo vamos escolher os layers que vão fazer parte do estilo.
Para criar um novo layer vamos à área de personalização avançada e escolhemos o link “As suas layers” na secção de Opções Avançadas.
Nesta página vamos poder fazer a gestão das nossas layers. Podemos criar, editar e apagar layers.
Para criar uma nova layer do tipo layout vamos à secção “Create top-level layer”, escolhemos a opção “layout” no campo “Type” e clicamos no botão “Create”. O novo layer aparece na secção “Your layers”.
O próximo passo é editar o layer, para criarmos o novo estilo. Clicamos no botão “Edit” correspondente ao layer que vamos editar e vamos para o editor de layers.
Na Área de edição aparece o conteúdo do nosso layer. Nas primeiras linhas temos que identificar o layer. Para isso usamos o objecto layerinfo:
layerinfo "type" = "layout"; layerinfo "name" = "";
O campo type é obrigatório e indica qual o tipo do layer (layout, i18n, theme ou user).
O campo name é opcional, mas deve ser preenchido para identificarmos o layer nas outras áreas de personalização do Blog.
O próximo passo é a customização de cada uma das views do Blog. Cada view do Blog é representada por uma classe e temos que redifinir os métodos print* de cada uma dessas classes.
A classe Page é uma classe genérica, que não tem representação directa no Blog. Todas as outras classes que representam uma view herdam os métodos e atributos da classe Page. Por isso as redifinições dos métodos desta classe passam para todas as views.
Na classe Page vamos redifinir os métodos print() e print_entry().
O método print() vai definir a organização e o aspecto do Blog:
Stylesheet (cores, fontes, imagens de fundo)
Localização das Sidebars e componentes
Localização da área de Posts
O método print_entry() vai definir a organização e o aspecto dos posts:
Data
Título
Conteúdo (Texto, Tags, Sinto-me, Música)
Assinatura do post (Autor)
Links (Link do post, comentários, resposta)
A classe RecentPage representa a view dos últimos posts no Blog.
Como o aspecto do Blog e dos posts já estão definidos na classe Page, só falta definir como é que vamos apresentar a lista com os últimos posts. Por isso vamos redifinir o método print_body().
A classe EntryPage representa a view de um post e comentários associados.
Nesta view temos que redifinir os seguintes métodos:
print_body()
print_comments(Comment[] cs)
print_comment(Comment c)
print_comment_partial(Comment c)
O método print_body() define como vão ser representados o post e a lista de comentários.
O método print_comments() define como vai ser representada a lista de comentários.
O método print_comment() define como vai ser representado um comentário:
Data
Texto
Autor
Links (Link do comentário, resposta, início de discussão)
O método print_comment_partial() define como vai ser representado um comentário parcial (colapsado):
Data
Autor
Links (Link do comentário)
A classe ReplyPage representa o form de resposta a um post ou comentário.
Nesta view vamos redifinir o método print_body(), que define como vai ser mostrado o form de resposta.
A classe MonthPage representa o arquivo mensal de posts.
Nesta view vamos redifinir os seguintes métodos:
print_body()
print_subjectlist()
O método print_body() define como vão ser representados os posts do mês do arquivo.
O método print_subjectlist() define como vai ser representada a lista de posts do mês de arquivo.
A classe DayPage representa os posts de um dia do mês.
Nesta view vamos redifinir o método print_body() para representar a lista de posts do dia do mês.
Em cada layer do tipo Layout é possivel usar ou definir propriedades que podem representar:
Labels
Cores
Tipo de letra (fonte, tamanho, cores)
Componentes HTML
As propriedades podem ser personalizadas no wizard de personalização intermédia. A principal vantagem deste wizard é que permite alterar os valores destas propriedades sem alterar o Layout. As alterações são guardadas em layers do tipo User, que ficam associados ao Blog onde foram criados.
Podemos definir propriedades dos seguintes tipos:
string – Representa uma cadeia de caracteres.
int – Representa um número inteiro
boolean – Representa um valor verdadeiro ou falso (true / false)
Color – Representa uma cor.
Cada um dos tipos de propriedade pode ser representado por um ou vários componentes no wizard de personalização:
string, int, boolean:
input text
textarea
select box
radio list
Color:
Color picker
No Layout podem ser definidas propriedades que não são visiveis no wizard, por dependerem de valores de outras propriedades. Neste caso, estas propriedades têm de ser inicializadas na função prop_init().
As propriedades são agrupadas por funcionalidade, e os grupos são representados por Tabs no wizard de personalização.
No topo do editor temos um botão “Save & Compile”, que grava e compila o layer. Quando carregamos no botão, aparece uma mensagem na àrea de mensagens (Build Pane) a indicar o sucesso da operação ou uma mensagem de erro a indicar em que linha ocorreu o erro.
###############################################################################
#
# Exemplo de referencia de um layer do tipo Layout
#
###############################################################################
#
# Identificação do Layer
# Estes dados não são opcionais.
#
layerinfo type = "layout";
layerinfo name = "demo";
###############################################################################
#
# Nomes dos Grupos de propriedades
#
# Os nomes aqui definidos aparecem nos tabs da personalização intermédia
#
propgroup imagem = "Imagem";
propgroup cores = "Cores";
###############################################################################
#
# Propriedades
#
# Declaração de propriedades, por grupo
#
propgroup imagem {
property string intro_imagem {}
property string bg_image {
des = "Imagem de fundo";
note = "Indique o url da imagem.";
url = "1";
}
property string bg_image_repeat {
des = "Repetição da imagem de fundo";
values = "repeat|Repetir imagem|no-repeat|Não repetir";
}
property string bg_image_align {
des = "Alinhamento da imagem de fundo";
values = "center|Centrada|top|No topo|bottom|Em baixo";
}
property string bg_image_options {
des = "Opções da Imagem de fundo";
noui = 1;
}
}
propgroup cores {
property Color color_bg {
des = "Cor de fundo?";
}
}
###############################################################################
#
# Funções do Core
#
# function prop_init ()
# Função para inicialização das propriedades.
# Esta é a primeira função a ser executada quando a página do Blog é mostrada
#
# function print_stylesheet ()
# Função que gera a stylesheet do Blog
# Esta função é chamada no método Page::print
#
function prop_init () {
#
# A Fazer: Escrever o código de inicialização das propriedades.
#
# Exemplo:
#
if ($*bg_image == "" ) {
# If url to background image is empty,
# do not include image options to stylesheet
$*bg_image_options = "";
}
else {
$*bg_image_options = """url('$*bg_image') $*bg_image_repeat $*bg_image_align""";
}
}
function print_stylesheet () {
#
# A Fazer: Escrever o conteúdo da stylesheet
#
# Exemplo:
#
println """
body { margin:10px; background: $*bg_image_options $*color_bg; }
p { margin:0px;}
.clear { clear: both; height: 0px; line-height: 0px; font-size: 1px;}
""";
}
###############################################################################
#
# Novas funções
#
# function print_entry (Page p, Entry e, Color bgcolor, Color fgcolor, bool hide_text)
# Função que escreve o conteúdo de um post
#
function print_entry (Page p, Entry e, Color bgcolor, Color fgcolor, bool hide_text) {
#
# A Fazer: Escrever código para conteúdo do post
#
# Exemplo:
#
var string subject = $e.subject ? $e.subject : "...";
var string time_post = $e.time->time_format();
var string username = $e.poster.name; # Nickname
if (not $hide_text) {
println """<div class="title">$subject</div>""";
$e->print_text();
# User name
var string user_signature = "Publicado por $username às $time_post";
# Post Link
var string entry_link = """<a href="$e.permalink_url">Link do post</a>""";
# Build signature
var string sign = "$user_signature";
if ($entry_link != "") { $sign = $sign + " | $entry_link"; }
# Print signature
println """<div class="sign">$sign</div>""";
println """<br class="clear" />""";
println """<div class="sepB"></div> """;
}
}
###############################################################################
#
# Page
#
# Classe genérica que representa uma página do Blog.
# Todas as views do Blog herdam os atributos e métodos desta classe.
#
# function Page::print()
# Método principal para gerar uma página do Blog.
#
# function Page::print_entry (Entry e)
# Método que escreve o conteúdo de um post numa página do Blog.
#
function Page::print() {
#
# A Fazer: Escrever código para gerar a página do Blog
#
# Exemplo:
#
var string title = $.global_title;
var string subtitle = $.global_subtitle;
# Print page
print """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" """;
println """ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">""";
println "<html>";
# Print head
println "<head>";
# Include stylesheet
println """<style type="text/css">""";
print_stylesheet();
println """</style>""";
$this->print_head(); # Include rss/atom feed links
println """<title>$.global_title</title>""";
println "</head>";
# Print body
println """
<body>
<div id="container">
<!--header-->
<div id="header">
<div class="title"><a href="$.base_url">$title</a></div>
<div class="descri">$subtitle</div>
</div><br class="clear" />
<!--header-->
<!--posts-->
<div id="posts">
""";
$this->print_body(); # Prints page content
println """
</div>
<!--/posts-->
<br class="clear" />
</div>
</body>
</html>
""";
}
function Page::print_entry (Entry e) {
#
# Método para escrever um post numa página do Blog.
#
# Exemplo:
#
print_entry($this, $e, null Color, null Color, false);
}
###############################################################################
#
# RecentPage
#
# Últimos posts no Blog.
# É a página de entrada do Blog.
#
# function RecentPage::print_body ()
# Escreve os últimos posts
#
function RecentPage::print_body () {
#
# A Fazer: Escrever código para mostrar os últimos posts
#
# Exemplo:
#
var string date_post;
foreach var Entry e ($.entries) {
$date_post = $e.time->date_format("long_day");
println """<div class="date">$date_post</div>""";
print_entry($this, $e, null Color, null Color, false);
}
}
###############################################################################
#
# EntryPage
#
# Representa um post e os seus comentários
#
# function EntryPage::print_body ()
# Escreve um post e os comentários ao post.
#
# function EntryPage::print_comments (Comment[] cs)
# Escreve lista de comentários.
#
# function EntryPage::print_comment (Comment c)
# Escreve um comentário.
#
# function EntryPage::print_comment_partial (Comment c)
# Escreve um comentário parcial.
#
function EntryPage::print_body () {
#
# A Fazer: Escrever código para a página do post
#
# Exemplo:
#
# Date Post
var string date_post = $.entry.time->date_format("long_day");
println """<div class="date">$date_post</div>""";
# Print Entry
print_entry($this, $.entry, null Color, null Color, false);
# Print Comments
var int num_comments = size $.comments;
if ($num_comments > 0) {
println """
<div class="sepB"></div>
<div id="comentar">
<div class="title">Comentários:</div>
""";
$this->print_comments($.comments);
var string reply_link;
var string reply_url = $.entry.comments.post_url;
$reply_link = """<a href="$reply_url">Comentar post</a>""";
println """
<!--link comentar post-->
<p>$reply_link</p>
<div class="sepB"></div>
<!--/link comentar post-->
</div>
<div class="sepB"></div>
<!--/comentar-->
""";
}
}
function EntryPage::print_comments (Comment[] cs) {
#
# A Fazer: Escrever código para mostrar os comentários ao post
#
# Exemplo:
#
if (size $cs == 0) { return; }
foreach var Comment c ($cs) {
if (not($c.screened or $c.deleted)) {
if ($c.full) {
$this->print_comment($c);
}
else {
$this->print_comment_partial($c);
}
println """<div class="sep"></div>""";
# Print Threads
$this->print_comments($c.replies);
}
}
}
function EntryPage::print_comment (Comment c) {
#
# A Fazer: Escrever código para mostrar um comentário
#
# Exemplo:
#
var string poster;
var string poster_url = "";
# Poster
if (defined $c.poster) {
var string poster_url = $c.poster.user_url{"userinfo"};
$poster = """<a href="$poster_url">$c.poster.name</a>""";
}
else {
$poster = "Anónimo";
}
# Comment date
var string date_comment = $c.time->date_format("long") + " às " + $c.time->time_format();
# Navigation link to comment (threads)
println """<a name="$c.anchor"></a>""";
# Comment text
println """
<div class="campo1">De:</div>
<div class="campo2C"><em>$poster</em></div>
<br class="clear" />
<div class="campo1">Data:</div>
<div class="campo2C">$date_comment</div>
<br class="clear" />
<div class="campo1">Comentário:</div>
<div class="campo2C"><span class="txt">$c.text</span></div>
<br class="clear" />
""";
# Comment links
var string link_list = "";
var string list_sep = "";
var string reply_link = "";
var string thread_url = "";
var string parent_url = "";
if ($c.frozen) {
$reply_link = "Congelado";
}
else {
var string reply_url = $c.reply_url;
var string reply_options = "";
$reply_link = """<a href="$reply_url" $reply_options>Responder comentário</a>""";
}
$link_list = $reply_link;
$list_sep = " | ";
if ($c.parent_url != "") {
$link_list = $link_list + $list_sep + """<a href="$c.parent_url">Início discussão</a>""";
}
if ($c.thread_url != "") {
$link_list = $link_list + $list_sep + """<a href="$c.thread_url">Responder ao comentário</a>""";
}
println """
<div class="campo1"></div>
<div class="campo2C"><span class="options">$link_list</span></div>
<br class="clear" />
<div class="sepB"></div>
""";
}
function EntryPage::print_comment_partial (Comment c) {
#
# A Fazer: Escrever código para mostrar um comentário parcial
#
# Exemplo:
#
var string poster;
var string poster_url = "";
# Poster
if (defined $c.poster) {
var string poster_url = $c.poster.user_url{"userinfo"};
$poster = """<a href="$poster_url">$c.poster.name</a>""";
}
else {
$poster = "Anónimo";
}
# Comment date
var string date_comment = $c.time->date_format("long") + " às " + $c.time->time_format();
# Navigation link to comment (threads)
println """<a name="$c.anchor"></a>""";
# Comment text
println """
<div class="campo1">De:</div>
<div class="campo2C"><em>$poster</em></div>
<br class="clear" />
<div class="campo1">Data:</div>
<div class="campo2C">$date_comment</div>
<br class="clear" />
""";
# Comment links
var string link_list = """<a href="$c.permalink_url">Ler comentário</a> """;
println """
<div class="campo1"></div>
<div class="campo2C"><span class="options">$link_list</span></div>
<br class="clear" />
<div class="sepB"></div>
""";
}
###############################################################################
#
# ReplyPage
#
# Representa a página de Resposta a um post (ou comentário)
#
# ReplyPage::print_body()
# Escreve o form de resposta.
#
function ReplyPage::print_body {
#
# A Fazer: Escrever código para mostrar um form de resposta ao post (ou comentário)
#
# Exemplo:
#
if (not $.entry.comments.enabled) {
print "<h2>$*text_reply_nocomments_header</h2><p>$*text_reply_nocomments</p>";
return;
}
# Date Post
var string date_post = $.entry.time->date_format("long_day");
println """<div class="date">$date_post</div>""";
# Print Entry
print_entry($this, $.entry, null Color, null Color, false);
# Comments
println """
<div class="sepB"></div>
<div id="comentar">
<div class="title">Comentar:</div>
""";
$.form->print(); # Reply Form
println """
<br class="clear" />
</div>
<div class="sepB"></div>
""";
}
###############################################################################
#
# MonthPage
#
# Representa o Arquivo Mensal.
#
# MonthPage::print_body ()
# Escreve os posts publicados num mês
#
# MonthDay::print_subjectlist ()
# Escreve os posts publicados num dia
#
function MonthPage::print_body () {
#
# A Fazer: Escrever código para mostrar todos os post do mês
#
# Exemplo:
#
var bool print_div = false;
foreach var MonthDay d (reverse $.days) {
if ($d.has_entries) {
if ($print_div) {
println """<div class="sep"></div>""";
}
$d->print_subjectlist();
$print_div = true;
}
}
}
function MonthDay::print_subjectlist () {
#
# A Fazer: Escrever código para mostar todos os posts de um dia
#
# Exemplo:
#
var Page p = get_page();
var bool print_date = true;
var string date_post;
foreach var Entry e (reverse $.entries) {
if ($print_date) {
$date_post = $e.time->date_format("long_day");
println """<div class="date">$date_post</div>""";
}
print_entry($p, $e, null Color, null Color, false);
$print_date = false;
}
}
###############################################################################
#
# DayPage
#
# Representa o Arquivo Diário
#
# DayPage::print_body()
# Escreve os posts de publicados num dia
#
function DayPage::print_body() {
#
# A Fazer: Escrever código para mostrar todos os posts publicados num dia
#
# Exemplo:
#
if ($.has_entries) {
# Date Post
var string date = $.date->date_format("long_day");
println """<div class="date">$date</div>""";
# Print Entries
foreach var Entry e (reverse $.entries) {
$this->print_entry($e);
}
} else {
"<p>$*text_noentries_day</p>";
}
}