Vis達o geral Para Programadores ( Componentes)
V 1. 0
Índice Índice .......................................................................................................................................iii Índice de Figuras ..................................................................................................................... v Índice de Tabelas.................................................................................................................... vii 1
O Joomla para programadores ........................................................................................ 9 1.1
Desenvolvendo componentes para o Joomla................................................................................. 16
1.2
Instalando um componente ............................................................................................................ 18
1.3
Definindo a lógica............................................................................................................................ 20
1.4
Definindo a apresentação ............................................................................................................... 21
iii
Ă?ndice de Figuras Figura 9 Diagrama de do processamento de um pedido no Joomla. _________________________________ 11 Figura 10 Estrutura de ficheiros tĂpica de um componente ________________________________________ 18
v
Ă?ndice de Tabelas Tabela 6 Principais ficheiros da raiz do site Joomla ______________________________________________ 9 Tabela 7 VariĂĄveis globais mais importantes usadas num programa Joomla __________________________ 10 Tabela 8 Nomenclatura usada nos principais ficheiros de um componente ____________________________ 17
vii
Visão Geral para Programadores
1 O Joomla para programadores O primeiro aspecto a reter quando se começa a desenvolver no Joomla é o facto de que o Joomla per si não é um produto de software, é apenas uma framework interpretadora de componentes, módulos, mambots e templates (conhecidos por extensões) dos quais o Joomla é completamente alheio do que fazem ou do seu aspecto. É um programa escrito em PHP, usa como motor de base de dados o MySQL e o APACHE como servidor. Outro aspecto muito importante e que geralmente não é mencionado é que não existe o conceito de pagina Web como o conhecemos, pois tudo o que aparece no browser é gerado a partir de um único ficheiro, o típico e conhecido index.php que se encontra na raiz do site. Devido á abstracção do Joomla relativamente as suas extensões é possível desenvolver subsistemas que podem ser publicados separadamente sem o sistema original. Isto permite manipular, gerir ou remover parte do sistema sem afectar o resto do sistema. Qualquer um pode desenvolver uma extensão para o Joomla desde que cumpra os métodos prescritos para a criação dos mesmos. Mais acerca do funcionamento das extensões pode ser encontrado nos próximos capítulos. Para entender como funciona a plataforma Joomla vamos analisar como são processados os pedidos ao servidor e entender quais são os ficheiros envolvidos e as principais classes, variáveis globais envolvidas no processo. Primeiro vamos conhecer melhor a hierarquia de ficheiros do Joomla.
Tabela 1 Principais ficheiros da raiz do site Joomla Nome
Descrição
Administrator
Pasta onde se encontram todos os ficheiros que tratam da parte administrativa do site.
Components
Todos os ficheiros dos componentes instalados, excepto os da parte administrativa.
Includes
Ficheiros do núcleo do Joomla
Laguages
Todos os ficheiros de linguagem
hugosoares2@gmail.com
9
Visão Geral para Programadores
Manbots
Todos os ficheiros dos mambots do site.
Modules
Todos os ficheiros dos módulos de “front-end” instalados.
Templates
Ficheiros de todos os templates de “front-end”.
Index.php
Ficheiro de inicio do “front-end” do site.
Index2.php
Ficheiro de inicio do “back-end” do site.
Configuration.php
Ficheiro que contem várias variáveis de configuração do site.
Globals.php
Ficheiro que trata de declarar várias variáveis globais usadas durante a execução do programa.
Agora uma nota sobre algumas variáveis que nos irão acompanhar ao longo deste relatório bem como em qualquer programa desenvolvido na plataforma Joomla.
Tabela 2 Variáveis globais mais importantes usadas num programa Joomla Nome
Descrição
$_VALID_MOS
Permite implementar uma medida de segurança que impede o acesso directo aos ficheiros de código
URL ou POST
NÃO
excepto index.php $option
Especifica qual o componente a carregar
SIM
$act
Especifica o tipo de informação a carregar
SIM
$task
Especifica o que fazer ao tipo de informação especificada em $act, por exemplo guardar ou apagar.
$Itemid
SIM
Especifica o id único do elemento que está a fazer um pedido, por exemplo o id de um link de menu. Indica
SIM
qual o conteúdo a apresentar. $database
Um objecto de conexão à base de dados. Implementa vários métodos que facilitam a manipulação dos
NÃO
dados.
hugosoares2@gmail.com
10
Visão Geral para Programadores
$mainframe
Objecto que possui muitas rotinas de interacção com o Joomla.
NÃO
Na tabela em cima o modificador de “url ou post” significa que esta variável é iniciada algures no código a partir das variáveis get ou post com o mesmo nome, recebidas no servidor após os pedidos pelo utilizador. Estas variáveis são usadas para que o servidor execute as tarefas que foram pedidas pelo utilizador.
Figura 1 Diagrama de do processamento de um pedido no Joomla.
1- Carregar índex.php e definir $_VALID_MOS 2- Existe o ficheiro configuration.php? 3- Carregar configuration.php 4- Carregar joomla.php 5- Carregar database.php 6- Carregar frontend.php hugosoares2@gmail.com
11
Visão Geral para Programadores
7- Existem as variáveis option ou itemid? 8- Criar objecto de base de dados (variável $database) 9- Tentar obter uma option se não foi especificado em 7 10- Criar objecto mosMainframe 11- Criar o array $_MOS_OPTION 12- Carregar o componente 13- Carregar o template 14- Carregar frontend.html.php 15- mosShowHead 16- mosLoadModules 17- mosMainBody 18- Mostrar a página
No diagrama em cima cada passo é da cor do ficheiro que está na realidade a executar o código, os ficheiros de cor branca indicam que executam apenas código de apoio a algumas tarefas. Para explicar cada passo iremos usar a letra D para representar o diagrama seguido do ponto decimal e o número do passo (exemplo [D.3]). Alguns passos foram omitidos por uma razão de simplicidade, pois correspondem apenas à garantia de compatibilidade com antigas versões do PHP e do Joomla. Sempre que um utilizador visita o site, escrevendo o endereço ou carregando num link, o Joomla recebe um pedido e executa uma série de tarefas para determinar o tipo de utilizador que fez o pedido, a secção pretendida e o conteúdo em particular a ser exibido.
Como seria de esperar o primeiro ficheiro a ser executado é o index.php [D.1] encontrado no directório de raiz do site. Se examinarmos este ficheiro podemos ver que não tem nenhum código html próprio. Isto é porque este ficheiro apenas executa uma quantidade de tarefas e delega funções mas não gera nenhum conteúdo. Note que no diagrama a sua execução só termina quando o conteúdo é apresentado ao utilizador. O conteúdo exibido é recolhido e
hugosoares2@gmail.com
12
Visão Geral para Programadores
formatado por outros ficheiros – os componentes e módulos – que são incluídos pelo index.php. A primeira tarefa do index.php é definir a variável global $_VALID_MOS com o código define (‘_VALID_MOS’, 1), assim a primeira linha em todos os outros do Joomla é: defined ( '_VALID_MOS' ) or die( 'Direct Access to this location is not allowed.' ); Isto é uma medida de segurança que impossibilita o acesso directo aos ficheiros do Joomla excepto ao index.php. Assim, se tentar-mos aceder, por exemplo, ao ficheiro mod_mainmenu.php directamente pelo seu url, $_VALID_MOS, que é definida em index.php, não terá sido definida logo o utilizador só verá uma mensagem que dirá ‘Direct Access to this location is not allowed.'. De seguida index.php verifica a existência de configuration.php [D.2]. Este ficheiro contém a configuração básica do Joomla que é usada pelo núcleo do site, os seus componentes, módulos e templates. Se o ficheiro configuration.php não existir o Joomla carrega um script de instalação que permite instalar o Joomla com as definições por defeito bem como alguma informação inserida pelo utilizador como o nome da base de dados e o nome de usuário e palavra-chave do MySQL. Este ficheiro é uma lista de variáveis usadas para guardar informação de caminhos para directórios internos, nome de usuário e palavra-chave do MySQL, fuso horário e outras informações de administração. Se o ficheiro configuration.php existir então é carregado [D.3] e a execução do Joomla corre o seu rumo normal.
O próximo passo é carregar o joomla.php [D.4]. Este é um dos ficheiros com maior carga de trabalhos da API do Joomla e contém muitas classes e funções que tornam o trabalho do programador muito mais fácil. Algumas destas classes e funções serão descritas mais a frente mas para já interessa saber que este ficheiro é muito importante e é aconselhável perder algum tempo a conhece-lo melhor.
Outro ficheiro muito importante para o programador é database.php. Este ficheiro é carregado pelo Joomla [D.5] e contém todas as classes e funções necessárias para interagir com a base de dados tornando estas capacidades acessíveis a todos os ficheiros subsequentes. Entraremos em pormenor mais a frente mas para já é importante saber que este ficheiro oferece um vasto leque de funções de query com diferentes tipos de retorno e funções de debugging.
hugosoares2@gmail.com
13
Visão Geral para Programadores
O próximo ficheiro de núcleo a ser carregado é o frontend.php [D.6], que trabalha em série com Joomla.php para levar a cabo a maior parte das tarefas do Joomla como iniciar e carregar módulos ou carregar o conteúdo principal de uma página. Os templates interagem directamente com este ficheiro pois ele está directamente ligado à apresentação do conteúdo. Depois de todos os ficheiros do núcleo do Joomla serem carregados o index.php tenta identificar o estado de ‘option’ e ItemId’ [D.7] que são passadas pela url query string ou pelo post dependendo do tipo de pedido, para identificar qual o conteúdo pretendido pelo utilizador. Se este passo falhar o Joomla simplesmente passa para o próximo passo. De seguida é criado um novo objecto de base de dados [D.8], criando uma instancia da classe database que naturalmente se encontra em database.php. Como o HTTP não mantém uma ligação permanente ao servidor sempre que é feito um novo pedido é necessário criar um novo objecto de conexão á base de dados.
Em [D.9] o Joomla volta tratar da questão do ‘option’ e ‘Itemid’. Se em [D.7] ‘option’ não foi definido mas foi definido um ‘Itemid’ então o Joomla tenta descobrir qual o componente a carregar procurando na base de dados pelo item de menu especificado em ‘itemid’ que, por sua vez, contém toda a informação do conteúdo desse link. Se o anterior falhar então o Joomla baseia-se na ‘option’ e ‘Itemid’ do primeiro elemento do menu principal que se assume ser a página inicial do site. Note que tudo isto só acontece se não for especificado um componente através do ‘option’ caso contrario o componente será carregado e tratado no próximo passo.
O próximo passo é criar um objecto mosMainframe [D.10] na variável global $mainframe. Este objecto proporciona uma grande carga de trabalhos por ter muitas rotinas de interacção com o sistema. Muitas das rotinas deste objecto serão usadas pelo componentes que virá a ser carregado, por exemplo para ler do ficheiro configuration.php carregado em [D.3], determinar o template usado, colocar alguma informação na parte ‘header’ e ‘metadata’ do código HTML da página. O mosMainframe também iniciará uma sessão ou actualizará no caso de esta não ter expirado. As sessões permitem que um utilizador previamente autenticado navegue de página para página sem ter de inserir novamente as suas credenciais.
hugosoares2@gmail.com
14
Visão Geral para Programadores
Neste momento, o Joomla irá preparar-se para executar o código do componente que foi determinado nos passos 7 ou 9. Apesar de tudo o conteúdo resultante desta execução ainda não será apresentado, em vez disso é guardado num ‘buffer’ temporário para que mais tarde possa ser “misturado” com o HTML do template. No ficheiro index.php é criado um array chamando $_MOS_OPTION [D.11] e de seguida é usada a capacidade de “buffering” do PHP para capturar o conteúdo de saída do componente. Isto é feito da seguinte forma: - Chamar o comando PHP ob_start() - Executar o código do componente - Retirar o conteúdo do “buffer” usando ob_get_contents() - Colocar o conteúdo em $_MOS_OPTION para mais tarde ser usado - Limpar o “buffer” com ob_end_clean() Finalmente chega o momento de carregar o “template” [D.13]. Este deverá ter chamadas a procedimentos do “frontend.php” mosShowHead, mosLoadModules e mosMainBody. Para carregar os módulos para uma dada posição o ficheiro “frontend.php” primeiro carrega “frontend.html.php” [D.14] e só depois inicia os módulos escrevendo o HTML. O “frontend.html.php” primeiro verifica se um módulo em particular é um módulo instalado ou um módulo “stored”, sendo que a diferença é que os módulos instalados são ficheiros separados no directório dos módulos e os módulos “stored” são criados na área de administração dos módulos e guardados na base de dados. Depois de carregado o “frontend.html.php”, a função mosShowHead() [D.15] chama a função do objecto mosMainframe, getHead() que retorna a informação do “header” previamente tratada no passo 10.
O mosLoadModules() poderá ser chamado quantas vezes for necessário dependendo das regiões definidas no “template”. O Joomla permite no máximo 26 posições com nomes como Top, Left, Right, Bottom, user1, user2… Estas posições organizam os módulos em grupos. Quando no mosLoadModules() é passado um parâmetro(primeiro) indicando a posição, o Joomla carrega todos os módulos do grupo dessa posição. Após executar o código do modulo o “frontend.php” envolve-o em código HTML adicional se for especificado um parâmetro (segundo) adicional de estilo em mosLoadModules(). Por exemplo mosLoadModules('left', -1) indica que 'left' é a grupo de posição e -1 o estilo do hugosoares2@gmail.com
15
Visão Geral para Programadores
módulo. Neste caso -1 indica que não é necessário adicionar mais nenhum HTML e que o título não deve ser mostrado. O parâmetro de estilo ‘0’ é o estilo por defeito e indica que o módulo deve ser envolvido numa tabela da classe “moduletable”.
A parte final do código a ser executada é o mosMainBody() [D.17] que é uma função muito simples pois a sua única tarefa é ir buscar o conteúdo de saída do componente ao array $_MOS_OPTION que foi por sua vez guardado em $GLOBALS no passo 12. Neste momento todo o HTML de saída dos componentes, módulos e Joomla é junto para mostrar a página gerada ao utilizador [D.18].
1.1 Desenvolvendo componentes para o Joomla Como já vimos em capítulos anteriores os componentes são compostos por uma parte de administração e uma parte visível ao utilizador. Em termos de hierarquia de ficheiros estas duas
partes
encontram-se
separadas,
a
parte
de
administração
encontra-se
em
“administrator/components/com_*” e parte de “front-end” em “components/com_*” (todos os componentes começam com “com_” seguidos no seu nome). Podemos fazer absolutamente tudo o que quisermos com eles desde que se usem os métodos e regras necessárias a sua integração no Joomla. Essas regras incluem usar determinadas nomenclaturas nos ficheiros do componente, criar um ficheiro xml com a lista dos ficheiros do componente e informação de instalação, usar métodos da framework para tarefas internas, criar ficheiros de instalação e remoção. Embora o Joomla ofereça vários métodos e classes de apoio ao desenvolvimento de extensões, estas não são indispensáveis para o funcionamento dos mesmos. Se, por exemplo, tivermos um programa em PHP que queremos integrar no Joomla como componente podemos facilmente, dependendo da complexidade do programa, fazer a transição apenas com pequenas alterações de código para garantir a segurança e alguma integração com outros componentes. Geralmente o desenvolvimento de componentes segue uma arquitectura bem definida e simples que embora não sendo obrigatória é bastante aconselhada pois simplifica muito a compreensão de todos os aspectos do programa. Basicamente é aconselhada a separação de hugosoares2@gmail.com
16
Visão Geral para Programadores
tarefas de lógica e de apresentação em ficheiros separados. As tarefas de lógica podem ser gravar, listar, ler da base de dados, que fazem chamadas a métodos da parte de apresentação que se encarregam de imprimir todo o HTML para formulários, listagens de conteúdo, etc. O ficheiro de apresentação é constituído sempre por pelo menos uma classe com vários métodos e sem construtor que será usada como camada de apresentação do componente. Um terceiro ficheiro que é muito frequente usar é o de classes da base de dados. Este contém classes que são representações das tabelas da base de dados usadas pelo componente. Estas classes podem ser uma extensão da classe do Joomla chamada “mosDBTable” e fazer uso de algumas funções que ai estão implementadas para simplificar a comunicação com a base de dados.
Tabela 3 Nomenclatura usada nos principais ficheiros de um componente Nomenclatura Ficheiro
Administração
“front-end”
Lógica
admin.(*).php
(*).php
Apresentação
admin.(*).html.php
(*).html.php
Classes
admin.(*).class.php
(*).class.php
(*) - Nome do componente.
É muito importante usarmos a nomenclatura descrita em cima porque quando o Joomla carrega um componente, baseia-se no nome deste para descobrir qual o ficheiro inicial do programa, que como já deve ter suspeitado é o ficheiro de lógica. Outra vantagem de usar esta nomenclatura é o facto de existirem algumas funções internas que facilitam, por exemplo, a inclusão dos vários ficheiros bastando para isso indicar se quer o de classes, de apresentação ou de lógica.
hugosoares2@gmail.com
17
Visão Geral para Programadores
1.2 Instalando um componente Os componentes são publicados no formato de compressão zip e seguem uma hierarquia de ficheiros própria que é necessária para que o Joomla os reconheça como válidos. É prática corrente usar directórios separados para os diferentes tipos de ficheiros, assim tipicamente existirá um directório de imagens, um directório de javascripts, um directório de folhas de estilo css e na raiz todos os ficheiros de código fonte PHP.
Figura 2 Estrutura de ficheiros típica de um componente
Para instalar o componente o Joomla começa por procurar nos elementos do componente um ficheiro xml com o mesmo nome do componente (na figura em cima podemos ver o ficheiro gilt.xml). Este ficheiro contém a descrição de todos os ficheiros do componente, que usa para copiar e criar os directórios na área de font-end e de back-end, os comandos sql necessários para interagir com a base dados (criando assim a estrutura de dados usada pelo componente) e outra informação acerca do componente.
hugosoares2@gmail.com
18
Visão Geral para Programadores
O esquema seguinte exemplifica uma possível estrutura do ficheiro xml de instalação. <?xml version="1.0"?> <mosinstall type="component" version="4.5.1"> <name>Gilt</name> <creationDate>May 2006</creationDate> <author>Hugo Jorge Soares</author> <copyright>All rights reserved</copyright> <authorEmail>hugosoares2@gmail.com</authorEmail> <authorUrl>gilt.isep.ipp.pt</authorUrl> <version>1.0</version> <description>Gilt component</description> <files><filename>gilt.php</filename></files> <images><filename>images/logo.gif</filename></images> <install> <queries> <query>CREATE TABLE inscricoes (total INT(11) NULL ,name VARCHAR( 80 ) NOT NULL ,email VARCHAR( 80 ) NOT NULL ,morada VARCHAR( 150 ) ,telefone INT( 9 ) ,events TEXT NOT NULL,id TINYINT auto_increment NOT NULL ,PRIMARY KEY ( id )) ENGINE = INNODB;</query> </queries>
</install>
<uninstall><queries> <query>DROP TABLE Pessoa_Events;</query></queries> </uninstall> <installfile>install.gilt.php</installfile> <uninstallfile>uninstall.gilt.php</uninstallfile> <administration> <menu>GILT</menu> <submenu><menu act="peo">People</menu></submenu> <files><filename>admin.gilt.php</filename></files> </administration> </mosinstall>
Como podemos ver começa-mos por descrever alguma informação sobre o componente como o autor a data, etc. De seguida temos uma lista de ficheiros que devem ser copiados para a área de front-end (files e images). Nas secções install e uninstall temos a possibilidade de escrever os comandos sql necessários para a instalação ou remoção do componente, que serão
hugosoares2@gmail.com
19
Visão Geral para Programadores
executados nessa altura. Os nódulos xml installfile e uninstallfile indicam respectivamente qual o ficheiro a carregar aquando da instalação ou remoção do componente. A última parte é respeitante à área de administração e permite construir o menu de administração do componente indicando quais os links e os valores a serem passados nas variáveis de url. Tal como para p front-end também é obrigatória a descrição dos ficheiros que compõem esta área. Como já foi dito o Joomla carrega um ficheiro aquando da instalação ou remoção do componente. No caso de instalação o Joomla tenta correr a função com_install() e na remoção com_uninstall(). Estas funções têm de existir forçosamente e nelas podemos inserir comandos adicionais para completar a instalação ou remoção. Isto é particularmente útil para fazer algum tipo de integração com componentes ou módulos existentes como por exemplo a criação de links de menu.
1.3 Definindo a lógica Para definir as tarefas a executar o ficheiro de lógica usa as variáveis que já conhecemos, $act e $task, em duas cláusulas switch encadeadas como exemplifica o código seguinte: switch($act){ case ‘eventos’: switch ($task){ case ‘save’: save($option,$act); break; case ‘list: list($option); break; } break; case ’pessoas’: …. }
hugosoares2@gmail.com
20
Visão Geral para Programadores
Desta forma podemos direccionar a execução do código no sentido pretendido pelo pedido do utilizador, fazendo as chamadas das respectivas funções que tratam da tarefa requerida.
Todas as funções que tenham como resultado final a apresentação de conteúdo devem fazer chamadas a métodos do ficheiro de apresentação. No entanto previamente à chamada destes métodos toda a lógica deve ser tratada, incluindo leituras à base de dados o que implica enviar apenas os dados a serem mostrados. Isto é feito usando a classe de acesso a dados “database” que se encontra no ficheiro database.php e tem como variável global $database. Nesta camada temos a possibilidade de obter dados da base dados sob a forma de arrays de objectos representativos das tabelas, que usaremos para enviar os dados para a camada de apresentação do componente.
As funções que tenham como resultado apenas a interacção com a base de dados (gravar, actualizar, apagar) apenas fazem chamadas à camada de acesso a dados do Joomla e geralmente não interagem com mais nenhum ficheiro. Estas Funções podem fazer uso das classes do ficheiro de classes para facilitar a interacção com a base de dados. Geralmente são chamadas em resposta a um envio de um formulário usando o método post.
1.4 Definindo a apresentação O ficheiro de apresentação é constituído por pelo menos uma classe geralmente com o nome lógico de HTML pois é só disto que ela trata. Esta classe não possui construtor porque não existe necessidade de ser instanciada pois apenas os seus métodos são de interesse. Estes métodos efectuam tarefas bem específicas pelo que geralmente englobam todo o processamento no seu corpo. Recebem como parâmetro, sob a forma de arrays, os dados que devem mostrar e escrevem o HTML para formatar e apresentar esse conteúdo. Na realidade o componente só tratará de escrever algum do HTML do bloco “body” da página o resto faz parte dos módulos e claro do “template”. Existem alguns cuidados a ter quando estamos a desenvolver em HTML especialmente no que respeita aos formulários pois o Joomla coloca algumas regras, nomeadamente: •
Os atributos name, id e class da tag form têm que ser “adminForm”
hugosoares2@gmail.com
21
Visão Geral para Programadores
•
O atributo action da tag form deverá ser “index.php” para o “front-end” e “index2.php” para o “back-end”.
•
Como o formulário irá ser tratado na parte de lógica então teremos de colocar em elementos escondidos o valor das variáveis $option, $act e $task. Desta forma quando o formulário for submetido ao servidor o Joomla carregará o nosso componente através de $option, $act indicará qual a acção e $task a tarefa. Por exemplo: <input type="hidden" name="option" value="com_gilt" /> <input type="hidden" name="act" value="eventos" /> <input type="hidden" name="task" value="gravar" />
Neste caso quando o formulário fosse submetido o Joomla iria carregar o componente “gilt” e chamar o método que trata de gravar os eventos. •
Usar o método addCustomHeadTag( $html ) do objecto mosMainframe para inserir algum código necessário no bloco head da página (lembrar que todo o html só é inscrito numa fase posterior, dai a necessidade de um método deste género).
•
Ter bastante cuidado com o uso de javascript para que não existam conflitos com o scripts que o Joomla usa para o seu funcionamento.
•
Usar os métodos do Joomla para criar as barras de ferramentas que se usam para as tarefas de autoria, pois estas incluem automaticamente alguns scripts necessários para interagir com outros elementos do formulário.
Quanto ao aspecto do conteúdo deveremos sempre que possível usar os atributos class dos elementos HTML para definirmos o seu estilo. Existem standards para os nomes de classes css que os “templates” devem usar, logo se usarmos esses nomes para os atributos class, temos a garantia de que o aspecto de toda a página será homogéneo. Para conhecermos estes nomes temos de investigar o css do template que está ser usado.
hugosoares2@gmail.com
22
Visão Geral para Programadores
Este trabalho está licenciado sob uma Licença Creative Commons Atribuição-Uso NãoComercial-Compartilhamento pela mesma Licença 2.5. Para ver uma cópia desta licença, visite http://creativecommons.org/licenses/by-nc-sa/2.5/ ou envie uma carta para Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA
hugosoares2@gmail.com
23