Main Service
Main ServiceMain Service  ·  Formação Técnica

Formação Técnica
em Moodle

Ótica de programação — capacitação de um colaborador interno na arquitetura, no desenvolvimento de plugins, na customização de temas e nas boas práticas de código.

Plataformasalavirtual.unisced.edu.mz
VersãoMoodle 5.1 · PHP 8.4 · PostgreSQL
Módulos4 · com código real e diagramas
Sobre esta formação

Tornar um colaborador autónomo no código do Moodle

  • Objetivo: capacitar para criar, personalizar e manter código na Sala Virtual sem depender de terceiros.
  • Abordagem: teoria curta + código real desta instalação + diagramas.
  • Pré-requisitos: PHP, HTML/CSS/JS, SQL e Git básicos.
01

Arquitetura

Como o Moodle está construído.

02

Plugins

Desenvolver e personalizar.

03

Temas

PHP · HTML · CSS · JS.

04

Boas práticas

Segurança e manutenção.

Contexto — o sistema que vamos dominar

Não é um Moodle qualquer — é uma plataforma à escala

39,7 mil

utilizadores ativos

1.130

cursos · 561 categorias

200 mil

inscrições

8,5 M

registos de resultados

  • Núcleo Moodle 5.1 + plugins próprios: local_uniutils, local_secretaria, block_uniblock, tema edutor.
  • Integração com o sistema académico eSURA/SGR — matrículas, resultados, finanças e inscrições sincronizam automaticamente.
Contexto — stack & ambiente

Do que é feito · e onde se programa

CamadaTecnologia
LinguagemPHP 8.4
Base de dadosPostgreSQL (prefixo mdl_)
Servidor webApache · webroot public/
Ficheirosmoodledata/ (fora do webroot)
Front-endMustache · SCSS · JS (AMD)
  • Ambiente local já montado em Docker: contentores web (PHP+Apache) e db (PostgreSQL).
  • Código vivo code/public/ está montado no contentor — editar = ver de imediato.
  • Acesso http://localhost:8888
01
Módulo 1

Arquitetura do Moodle

Compreender como o Moodle está organizado: diretórios, base de dados, ciclo de vida de um pedido, o sistema de plugins e as APIs do núcleo — a base de tudo o resto.

1.1 · Princípio fundador

No Moodle, quase tudo é um plugin

Em palavras simples

Pensa no Moodle como um telemóvel: o sistema base é pequeno e estável, e tudo o resto são apps (plugins) que se instalam por cima — sem nunca abrir o telemóvel.

Um núcleo pequeno e estável + centenas de plugins. A regra de ouro: nunca se altera o núcleo — estende-se através de plugins, hooks e APIs. Assim, cada atualização do Moodle não destrói o nosso trabalho.

Núcleo
lib/ · admin/ · APIs
+
Plugins core
quiz, forum, boost…
+
Plugins próprios
uniutils, edutor…
=
Sala Virtual
a nossa plataforma
1.2 · Estrutura de diretórios (layout 5.x)

Onde vive o código

Em palavras simples

Como numa casa: só uma divisão (public/) tem porta para a rua; os ficheiros privados e a despensa (moodledata) ficam trancados lá dentro, longe dos visitantes.

# raiz do projeto
config.php          # BD, wwwroot, dataroot
public/             # ← WEBROOT (Apache aponta aqui)
  index.php
  lib/              # APIs do núcleo
  admin/  course/  user/ …
  mod/ block/ theme/ local/ # plugins
admin/cli/          # scripts CLI (fora do webroot)
moodledata/         # ficheiros, cache (fora do webroot)
  • public/ é o único diretório servido pela web (novidade do Moodle 5.0 — mais seguro).
  • config.php fica na raiz; public/config.php só o carrega.
  • moodledata/ nunca é acessível pela web — guarda ficheiros e caches.
1.3 · Os três lugares onde vivem os dados

Código · Base de dados · Ficheiros

Em palavras simples

Os dados moram em três sítios diferentes — o programa (disco), as informações (base de dados) e os ficheiros enviados (moodledata) — e nunca se misturam.

Código (disco)

Os plugins e o núcleo em public/. Versionado em Git.

Base de dados

Tabelas mdl_* em PostgreSQL: utilizadores, cursos, notas, configurações.

moodledata

Ficheiros enviados, sessões, caches, ficheiros temporários.

  • Configuração vive em config.php (ligação) e na tabela mdl_config / mdl_config_plugins (definições).
  • Nunca se acede à BD diretamente do código — usa-se sempre a API $DB (Módulo 4).
1.4 · Ciclo de vida de um pedido

O que acontece a cada page load

Em palavras simples

Sempre que alguém abre uma página, o Moodle repete os mesmos passos por ordem — como uma recepção que confirma quem és e o que podes ver antes de te entregar o conteúdo.

1 · Pedido
browser → public/…/page.php
2 · config.php
carrega lib/setup.php
3 · Setup
$CFG·$DB·$USER·$PAGE·$OUTPUT
4 · Sessão/Login
require_login()
5 · Permissões
require_capability()
6 · Lógica + BD
$DB->get_records()
7 · Output
header → conteúdo → footer

Estas variáveis globais — $CFG, $DB, $USER, $PAGE, $OUTPUT — estão disponíveis em qualquer página depois de incluir config.php.

1.5 · As APIs do núcleo

O "kit de ferramentas" que vamos usar

Em palavras simples

São ferramentas já feitas: em vez de reinventar a roda, chamamos a função do Moodle que já sabe guardar na base de dados, desenhar HTML ou verificar permissões.

Database (DML/DDL)

$DB + XMLDB para esquema independente do motor.

Output / Renderers

$OUTPUT, Mustache, renderers personalizáveis.

Page / $PAGE

contexto, URL, layout, título, recursos JS/CSS.

Access

papéis, contextos, capacidades, require_capability.

Navigation

menus e árvore de navegação (via lib.php).

Events & Observers

reagir a acontecimentos do sistema.

Cache (MUC)

camada de cache unificada para desempenho.

Forms

moodleform — validação e sesskey.

Web Services & Tasks

APIs externas e tarefas agendadas (cron).

1.6 · Papéis · Contextos · Capacidades

O modelo de permissões

Em palavras simples

É como crachás num edifício: a capacidade é uma chave, o papel é o crachá com várias chaves, e o contexto é onde esse crachá funciona — um curso, uma categoria, ou todo o site.

  • Capacidade uma permissão atómica, ex.: mod/quiz:grade.
  • Papel conjunto de capacidades (student, teacher, manager…).
  • Contexto onde o papel se aplica: Sistema → Categoria → Curso → Módulo.
  • Herança contextos herdam de cima para baixo.

Exemplo real desta instalação:

PapelEdita conteúdo?
teacher (tutor)❌ só avalia/interage
editingteacher✅ sim
manager✅ administração

No código: require_capability('x/y:z',$context).

1.7 · A arquitetura desta instalação

Moodle + sistema académico eSURA/SGR

Em palavras simples

O Moodle não inventa notas nem matrículas — recebe-as do sistema académico (eSURA) e mostra-as. Quem manda nos dados oficiais é o eSURA.

eSURA / SGR
matrículas · resultados · finanças · inscrições · tickets
local_uniutils
web services (entrada) · tarefas · observers
Núcleo Moodle
cursos · notas · fóruns
  • local_secretaria → portal do estudante (Resultados, Extrato, Dados Pessoais…).
  • block_uniblock → atalhos para a Secretaria Online.
  • theme_edutor → identidade visual e página de login.
02
Módulo 2

Desenvolvimento &
personalização de plugins

Criar plugins do zero, construir blocos e integrar com sistemas externos — usando os padrões reais do uniutils, secretaria e uniblock.

2.1 · Tipos de plugin & nomes

"Frankenstyle": tipo_nome

Em palavras simples

Cada plugin tem um nome único tipo_nome, como um apelido: a primeira parte diz que tipo é, a segunda é o nome próprio.

TipoPasta
locallocal/ — o mais flexível
blockblocks/
modmod/ — atividades
themetheme/
auth / enrolautenticação / inscrição
local_uniutils   // tipo=local nome=uniutils
block_uniblock   // tipo=block nome=uniblock
theme_edutor

Este nome (o componente) usa-se em tudo: classes, strings, capacidades, tabelas.

2.2 · Anatomia de um plugin

Os ficheiros que importam

Em palavras simples

Um plugin é só uma pasta com ficheiros de nomes combinados — o Moodle sabe onde "espreitar" (version.php, lang/, db/…) para o reconhecer e instalar.

local/exemplo/
├── version.php      # identidade (obrigatório)
├── lib.php           # callbacks por convenção
├── settings.php      # definições de admin
├── index.php         # uma página
├── lang/en/…        # strings (obrigatório)
├── db/access.php     # capacidades
├── db/install.xml    # tabelas (XMLDB)
├── db/services.php   # web services
├── db/tasks.php      # tarefas agendadas
└── classes/          # código com autoload
  • version.php e lang/en são obrigatórios.
  • classes/ é onde vive a lógica moderna (namespaces + autoload).
  • db/ descreve BD, permissões, serviços e tarefas — declarativo.
2.3 · version.php — o coração

Sem isto, não há plugin

Em palavras simples

É o bilhete de identidade do plugin: diz o nome e o número da versão. Subir esse número avisa o Moodle de que há novidades para instalar.

defined('MOODLE_INTERNAL') || die();

$plugin->component = 'local_exemplo';   // = à pasta. Frankenstyle.
$plugin->version   = 2026060200;        // AAAAMMDDXX — subir a cada mudança de BD
$plugin->requires  = 2024100700;        // versão mínima do Moodle
$plugin->maturity  = MATURITY_STABLE;
$plugin->release   = '1.0.0';
Regra de ouro

Mudou o esquema da base de dados? Incremente $plugin->version. É esse número que dispara a instalação/upgrade.

2.4 · O padrão de qualquer página

Login → contexto → permissão → output

Em palavras simples

Antes de mostrar fosse o que fosse, a página pergunta: estás autenticado? tens permissão? — só depois vai buscar dados e desenha o ecrã. Sempre por esta ordem.

require_once(__DIR__.'/../../config.php');

require_login();                              // 1) exige sessão
$context = context_system::instance();
require_capability('local/exemplo:view', $context);  // 2) permissão

$PAGE->set_url(new moodle_url('/local/exemplo/index.php'));
$PAGE->set_context($context);
$PAGE->set_title(get_string('pluginname','local_exemplo'));

$avisos = $DB->get_records('local_exemplo_aviso');   // 3) dados

echo $OUTPUT->header();
// … conteúdo …
echo $OUTPUT->footer();
2.5 · Construir um Bloco

O padrão do block_uniblock

Em palavras simples

Um bloco é uma "caixa" lateral. O método get_content() é o sítio onde decidimos o que essa caixa mostra.

class block_exemplo extends block_base {
  function init() { $this->title = get_string('pluginname','block_exemplo'); }
  function applicable_formats() { return ['all'=>true]; }

  function get_content() {           // ← a lógica do bloco
    if ($this->content !== null) return $this->content;
    $this->content = new stdClass();
    $this->content->text = html_writer::link(
        new moodle_url('/secretaria/resultados'),
        get_string('examinationsdetails','local_uniutils'));
    return $this->content;
  }
}

É exatamente assim que o bloco real gera os atalhos da Secretaria Online.

2.6 · Integração — Web Services

Como o eSURA "empurra" dados para o Moodle

Em palavras simples

Um web service é uma "porta" por onde outro sistema entra e fala com o Moodle — com chave (token) e regras — para lhe entregar dados, como as notas.

Declarardb/services.php

$functions = [
 'local_exemplo_save' => [
  'classname'=>'local_exemplo\\external\\save',
  'methodname'=>'execute',
  'type'=>'write',
  'capabilities'=>'local/exemplo:manage',
 ],
];

Implementarclasses/external/save.php

class save extends external_api {
 static function execute_parameters(){…}
 static function execute($dados){
   self::validate_context($ctx);
   require_capability(…);
   // gravar com $DB
 }
 static function execute_returns(){…}
}

Ativa-se em Admin ▸ Web services, cria-se um utilizador de serviço e um token — como a conta esura@unisced.edu.mz.

2.6 · Web Services — o caminho dos dados

Do eSURA até à base de dados

eSURA / SGR cliente REST server.php + wstoken external_api valida + permissão $DB API de dados mdl_* tabelas ✗ rejeita se inválido

Cada parâmetro é validado e a permissão verificada antes de tocar na base de dados.

2.7 · Tarefas agendadas & Observers

Trabalho automático e reativo

Em palavras simples

Duas formas de o código agir sozinho: a tarefa agendada corre a horas certas (um despertador); o observer reage quando algo acontece (um sensor de movimento).

Tarefa agendada (cron)

namespace local_exemplo\task;
class export_task
   extends \core\task\scheduled_task {
 function execute() {
   mtrace('a exportar…');
 }
}

Regista-se em db/tasks.php (como o export_grades_task real).

Observer (reagir a eventos)

// db/events.php
$observers = [[
 'eventname'=>'\\mod_quiz\\event\\
    attempt_submitted',
 'callback'=>'\\local_exemplo\\
    observer::quiz_submitted',
]];

O uniutils usa isto (quiz_observer) para tratar resultados de testes.

2.8 · Chamar sistemas externos

Falar com o eSURA/SGR a partir do código

Em palavras simples

Quando o Moodle precisa de pedir algo a outro sistema, faz uma "chamada" pela internet — mas nunca durante o clique do utilizador, senão a página fica à espera.

// REST — cliente cURL do Moodle (respeita proxy/segurança)
$curl = new \curl();
$curl->setHeader('Authorization: Basic '.base64_encode("$user:$pass"));
$resposta = $curl->post('https://esura.…/service/...', $payload);

// SOAP — como o secretaria faz para os tickets (osTicket)
$client = new \SoapClient($wsdlurl);
$client->__soapCall('ostTicket.open', $dados);
Regra crítica de desempenho

Nunca faça chamadas externas durante o pedido do estudante (bloqueiam a página). Faça-as numa tarefa agendada/adhoc. Credenciais sempre em settings.php, nunca fixas no código.

03
Módulo 3

Customização de temas

Controlar a aparência com PHP, HTML (Mustache), CSS (SCSS) e JavaScript — sobre o tema real edutor, um tema-filho do Boost.

3.1 · Como o Moodle desenha uma página

Quatro peças trabalham juntas

Em palavras simples

Para montar uma página, o Moodle junta quatro coisas: a moldura (layout), o molde do HTML (Mustache), quem preenche os dados (renderer) e o estilo (SCSS).

Layouts

PHP que define a moldura (cabeçalho, colunas, rodapé).

Mustache

Templates HTML .mustache para cada componente.

Renderers

PHP que prepara os dados para os templates.

SCSS

Estilo, compilado para CSS e servido por styles.php.

Um tema personaliza qualquer uma destas peças — sem tocar no núcleo.

3.2 · Anatomia do tema edutor

Um tema-filho do Boost

Em palavras simples

O nosso tema herda tudo de um tema base (Boost) e só muda o necessário — como decorar uma casa já construída, em vez de a levantar de raiz.

# theme/edutor/config.php
$THEME->name = 'edutor';
$THEME->parents = ['boost'];   # herda do Boost
$THEME->scss = fn($t)=>
   theme_edutor_get_main_scss_content($t);
$THEME->prescsscallback  = '…get_pre_scss';
$THEME->extrascsscallback = '…get_extra_scss';
$THEME->rendererfactory =
   'theme_overridden_renderer_factory';
$THEME->layouts = [ … ];
  • parents=[boost] herda tudo do Boost; só personalizamos o necessário.
  • scss / settings 23 ficheiros de definições (hero, login, rodapé…).
  • 34 templates Mustache sobrepostos (login, header, course…).
3.3 · CSS — o pipeline SCSS

De .scss a CSS no browser

Em palavras simples

Escrevemos o estilo em SCSS (um CSS turbinado, com variáveis); o Moodle "cozinha-o" para CSS normal e guarda em cache — por isso é preciso limpar a cache para ver mudanças.

pre_scss
variáveis (cores, definições)
main scss
preset + parciais do tema
extra_scss
CSS personalizado do admin
styles.php
compila + cache
  • Parciais em scss/theme/: _header.scss, _footer.scss, _courseformat.scss
  • Cache agressivo → depois de mexer em SCSS, purgar caches (ou themedesignermode em dev).
3.4 · HTML — templates Mustache

Sobrepor um template do núcleo

Em palavras simples

Para mudar o HTML de algo, copiamos o seu "molde" (.mustache) para o nosso tema e editamos — o Moodle passa a usar a nossa versão.

Copiar o template do componente para o tema, mantendo o caminho — o Moodle usa a versão do tema:

# o tema sobrepõe o formulário de login do núcleo:
theme/edutor/templates/core/loginform.mustache

# dentro: HTML + variáveis {{…}} e secções {{#…}}
{{#showloginform}}
  <form class="login-form" action="{{loginurl}}">
    <input name="username" …>
  </form>
{{/showloginform}}

Lógica de zero no template: só apresentação. Os dados vêm do renderer.

3.5 · PHP (Renderers) & JavaScript

Lógica de apresentação & interatividade

Em palavras simples

O renderer prepara em PHP aquilo que vai aparecer; o JavaScript (módulos AMD) acrescenta a interatividade depois de a página carregar.

Renderer — sobrepor saída do núcleo

namespace theme_edutor\output;
class core_renderer
   extends \core_renderer {
 function favicon() { … }
}

Ativado por theme_overridden_renderer_factory.

JavaScript — módulos AMD

// amd/src/main.js
export const init = () => { … };

// no PHP:
$PAGE->requires->js_call_amd(
  'theme_edutor/main','init');

JS compila com grunt amdamd/build/.

3.6 · Boa prática de temas

Tema-filho, não editar o edutor

Em palavras simples

Para não perder o trabalho na próxima atualização, criamos um "tema-filho" por cima do edutor — as mudanças pequenas cabem só no campo de SCSS personalizado.

  • Para alterações pequenas use o campo SCSS personalizado nas definições do tema (não toca em ficheiros).
  • Para alterações grandes crie um tema-filho com $THEME->parents=['edutor'] e sobreponha só o necessário.
  • Assim as atualizações do edutor não apagam o seu trabalho.

Caso real desta instalação — o login escondia o formulário:

/* SCSS personalizado (admin) */
.form-col.col-12 {
  display: none;   /* ← escondia user/pass */
}

Uma linha de SCSS muda o comportamento visível — sem editar código.

04
Módulo 4

Boas práticas, segurança
& manutenção

Escrever código seguro, testável e fácil de manter — para que a plataforma dure e evolua com confiança.

4.1 · Qualidade de código

As três regras inegociáveis

Em palavras simples

Três hábitos que evitam dores de cabeça: nunca mexer no núcleo, seguir o estilo de código combinado, e guardar tudo em Git.

Nunca editar o núcleo

Só plugins, callbacks e hooks. Caso contrário, perde-se na atualização.

Coding Style

Seguir o estilo oficial do Moodle. Validar com o linter / Plugin CI.

Git & versões

Tudo versionado. version.php sobe a cada entrega.

  • i18n todo o texto via get_string() — nunca fixo no código.
  • Lógica em classes/ com namespaces e autoload; lib.php só para callbacks.
4.2 · Segurança — entrada & base de dados

Validar tudo · SQL com placeholders

Em palavras simples

Nunca confiar no que vem do utilizador: limpar a entrada e usar "campos" (?) nas consultas, para ninguém conseguir injetar comandos na base de dados.

// ENTRADA: limpar sempre com PARAM_*
$id = required_param('id', PARAM_INT);

// BD: NUNCA concatenar variáveis (injeção SQL)
$DB->get_records_sql(
  'SELECT * FROM {local_exemplo_aviso} WHERE titulo = ?',
  [$titulo]);                 // ✅ parâmetro seguro

// ❌ NUNCA: "... WHERE titulo = '$titulo'"
  • Permissões sempre require_capability() — não confiar na navegação.
  • Tabelas via {nometabela} (sem mdl_).
4.3 · Segurança — sessão, output & privacidade

Proteger o utilizador e os dados

Em palavras simples

Exigir sessão, proteger os formulários contra fraude (sesskey), "escapar" o texto antes de o mostrar e respeitar o RGPD se guardarmos dados pessoais.

require_login + sesskey

Toda a página exige sessão. Formulários via moodleform validam sesskey (anti-CSRF).

Escapar a saída

format_string() (títulos), format_text() (HTML), s() (atributos) — contra XSS.

Privacy API (RGPD)

Guarda dados pessoais? classes/privacy/provider.php é obrigatório (exportar/eliminar).

Princípio do menor privilégio

Capacidades específicas; contas de serviço só com o necessário.

4.4 · Testes & integração contínua

Código que se prova a si mesmo

Em palavras simples

Testes automáticos são um "alarme": correm sozinhos e avisam se uma alteração partiu algo que já funcionava.

PHPUnit

Testes unitários da lógica (em tests/). Base de dados de teste isolada.

Behat

Testes de comportamento no browser (fluxos do utilizador).

Moodle Plugin CI

Linters + estilo + testes automáticos a cada commit (GitHub Actions).

Mesmo poucos testes nos pontos críticos (web services, cálculos) evitam regressões caras.

4.5 · Depuração & manutenção

O fluxo de trabalho diário

Em palavras simples

Ligar o modo de erros para ver o que se passa, limpar caches quando algo "não atualiza", e ter sempre backups dos três sítios (código, base de dados, ficheiros).

Depurar (em config.php):

$CFG->debug = (E_ALL | E_STRICT);
$CFG->debugdisplay = 1;

Comandos do dia-a-dia:

php admin/cli/upgrade.php
php admin/cli/purge_caches.php
php admin/cli/cron.php
  • Limpar caches à mínima estranheza — o Moodle faz cache de tudo.
  • Upgrades testar sempre numa cópia antes de produção; ler as notas de versão.
  • Backups código (Git) + base de dados + moodledata — os três.
  • Documentar cada plugin próprio e a integração eSURA.
Módulo 4 · Workflow de desenvolvimento

O ciclo de trabalho diário

⟲ repetir até estar pronto 1 · Editar code/public/… upgrade.php instala / migra BD purge_caches limpa as caches 4 · Testar localhost:8888

Editar é imediato (código montado no contentor) · purgar caches resolve 90% dos "não atualiza".

Plano

Plano de capacitação

Um percurso sugerido para tornar o colaborador autónomo, em fases com marcos verificáveis.

Percurso sugerido

Da arquitetura à autonomia

FaseFocoMarco
1 · FundaçõesArquitetura, ambiente Docker, BD, ciclo de pedidoMapear o sistema; navegar o código
2 · Primeiro pluginPlugin local + bloco + capacidades + BDlocal_exemplo a funcionar
3 · IntegraçãoWeb services, tarefas, observers, eSURALer/escrever via serviço externo
4 · TemasSCSS, Mustache, renderers, tema-filhoPersonalizar o edutor com segurança
5 · QualidadeSegurança, testes, CI, manutençãoPlugin com testes + revisão de segurança
Recursos

Para continuar a aprender

Oficial

moodledev.io — APIs, tipos de plugin, coding style.

Plugins DB

moodle.org/plugins — exemplos reais e referência.

Código local

uniutils · secretaria · uniblock · edutor.

Plugin CI

moodlehq/moodle-plugin-ci.

Main ServiceFim · Obrigado

Pronto para construir.

Arquitetura · Plugins · Temas · Boas práticas — a base para evoluir a Sala Virtual com autonomia, segurança e qualidade.

Moodle 5.1PHP 8.4PostgreSQL SCSS · Mustache · AMDeSURA/SGR
Main ServiceFormação Técnica · Sala Virtual UnISCED
Main Service · Formação Técnica em Moodle
← → · espaço · navegar
01 / 36