INF.03-06-24.01-SGStyczeń 2024 · 38 kroków · ok. 73 min · 34 kryteria z klucza
Rozwiązanie krok po kroku: Aplikacja biura podróży (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza: baza podroze z czterema zapytaniami, strona biuro.php z banerem, trzema blokami obok siebie (promocje, galeria, kontakt), blokiem dawnych wyjazdów i stopką oraz style w styl9.css. Obie wersje spełniają wszystkie 34 kryteria klucza - różni je sposób pisania kodu, nie wynik.
Czym ta wersja różni się od podstawowej:
SQL czytelnie: klauzule w osobnych liniach, aliasy tabel w złączeniu (JOIN ... ON), warunek dostepna = FALSE, dawne wyjazdy posortowane po dacie,
układ na siatce CSS (display: grid na <body>, kolumny 20% / 60% / 20%) zamiast float i clear, galeria zdjęć na flexboksie, kolory w zmiennych CSS, bloki w znacznikach, które mówią, czym są (header, aside, main, section, footer),
PHP na PDO (klucz przyjmuje MySQLi albo PDO): połączenie i oba zapytania na początku pliku biuro.php, w try/catch; w treści strony już tylko pętle foreach, a każdy tekst z bazy przechodzi przez funkcję e() (htmlspecialchars), także w atrybutach alt i title.
Baza i zdjęcia pochodzą z prawdziwego archiwum materialy6 (załącznik do arkusza, hasło w treści zadania). Zapytania są policzone na MariaDB 10.4 - tej samej, która jest w XAMPP. Podgląd strony to wynik prawdziwego uruchomienia kodu z danego kroku w PHP 8.2; najedź na zdjęcie w galerii, a pojawi się dymek z nazwą miasta, jak na obrazie 2. Sprawdzony jest też komunikat przy niedziałającej bazie.
Jak liczą się punkty. Klucz ma 34 kryteria (R.1.1 - R.5.7), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 26 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
Etap 1: Baza danych
Kroki 1-7 · po tym etapie masz 6 z 34 kryteriów (pkt)
Krok 1: Baza danych podroze
Kryteria:
R.1.1 Wykonano import tabel do bazy danych podroze czynność udokumentowano plikiem o nazwie import w formacie PNG
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie podroze.
Rozpakuj materialy6.7z z pulpitu (hasło $poDrozE&!) do folderu z numerem zdającego: egzamin.sql i zdjęcia 1.jpg - 9.jpg. W phpMyAdmin: Nowa, nazwa podroze, porównywanie np. utf8mb4_polish_ci.
Tabele z egzamin.sql mają zestaw znaków zapisany we własnej definicji (latin1), więc wybór dla bazy ich nie zmienia - dane i tak są bez polskich liter („Francja, Paryz”).
Krok 2: Import tabel z pliku egzamin.sql
Kryteria:
R.1.1 Wykonano import tabel do bazy danych podroze czynność udokumentowano plikiem o nazwie import w formacie PNG
ok. 2 min
Polecenie z arkusza
Do bazy podroze zaimportuj tabele z pliku egzamin.sql z rozpakowanego archiwum.
Zaznaczona baza podroze, Import → egzamin.sql. W zadaniu potrzebne są dwie tabele z obrazu 1: zdjecia (nazwa pliku i podpis) i wycieczki (cel, data wyjazdu, cena, pole logiczne dostepna i klucz obcy zdjecia_id wskazujący zdjęcie celu).
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonano import tabel do bazy danych podroze czynność udokumentowano plikiem o nazwie import w formacie PNG
ok. 2 min
Polecenie z arkusza
Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG pod nazwą import. Zrzut powinien obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel.
Print Screen i zapis jako import.png - import w PNG, a zrzuty zapytań w JPEG (kw1.jpg - kw4.jpg) z widoczną treścią zapytania.
Typowe błędy
Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany.
Krok 4: Zapytanie 1: zdjęcia według podpisu
Kryteria: ,
R.1.2 Zapisano plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonano zapytanie 1 wybierające jedynie pola nazwaPliku i podpis z tabeli zdjecia sortując je rosnąco według kolumny podpis. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwaPliku, podpis FROM zdjecia ORDER BY podpis; (opcjonalnie ASC) oraz wynik działania udokumentowano zrzutem, na którym widocznych jest dokładnie 9 rekordów dla kolejnych nazw plików 8, 3, 5, 1, 9, 7, 6, 4, 2, jedynie pola nazwaPliku i podpis
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie pola nazwaPliku i podpis z tabeli zdjecia sortując je rosnąco według kolumny podpis.
SELECT nazwaPliku, podpis / FROM zdjecia - dwie kolumny,
ORDER BY podpis - rosnąco, czyli alfabetycznie; ASC jest domyślne.
Wynik: 9 zdjęć w kolejności plików 8, 3, 5, 1, 9, 7, 6, 4, 2 - od Barcelony do Wenecji (R.1.3). Zapytanie do kwerendy.txt, zrzut kw1.jpg. To zapytanie wyśle Skrypt 1 - w tej kolejności stanie galeria.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT nazwaPliku, podpisFROM zdjeciaORDER BY podpis;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: wycieczki niedostępne
Kryteria:
R.1.4 Wykonano zapytanie 2 wybierające jedynie pola cel i dataWyjazdu z tabeli wycieczki dla wycieczek, które nie są dostępne. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT cel, dataWyjazdu FROM wycieczki WHERE dostepna = FALSE; (lub =0) oraz wynik działania udokumentowano zrzutem, na którym widoczne są dokładnie 3 rekordy: Francja, Paryz, 2019-07-14; Hiszpania, Barcelona 2019-07-14; Hiszpania, Barcelona 2019-08-14;
ok. 2 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pola cel i dataWyjazdu z tabeli wycieczki dla wycieczek, które nie są dostępne.
WHERE dostepna = FALSE - pole logiczne (tinyint(1)) przechowuje fałsz jako 0; słowo FALSE mówi wprost, o co chodzi, i daje ten sam wynik co = 0,
ORDER BY dataWyjazdu, cel - wyjazdy od najdawniejszego; przy tej samej dacie decyduje cel. Arkusz kolejności nie podaje, ale lista „W poprzednich latach byliśmy...” czyta się w porządku dat.
Wynik: 3 wycieczki - Paryż 2019-07-14 oraz Barcelona 2019-07-14 i 2019-08-14, w tej samej kolejności co w kluczu (R.1.4). Zrzut kw2.jpg. To zapytanie wyśle Skrypt 2.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT nazwaPliku, podpisFROM zdjeciaORDER BY podpis;-- Zapytanie 2SELECT cel, dataWyjazduFROM wycieczkiWHERE dostepna = FALSEORDER BY dataWyjazdu, cel;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: drogie wycieczki z podpisem zdjęcia
Kryteria:
R.1.5 Wykonano zapytanie 3 wybierające jedynie pola cel i cena z tabeli wycieczki oraz odpowiadające im pole podpis z tabeli zdjecia , dla wycieczek, których cena jest wyższa niż 1300 zł. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT cel, cena, podpis FROM zdjecia JOIN wycieczki ON zdjecia.id = wycieczki.zdjecia_id WHERE cena > 1300; (możliwe też INNER JOIN lub porównanie kluczy po WHERE) oraz wynik działania udokumentowano zrzutem, na którym widoczne są dokładnie 4 rekordy: Francja, Paryz 1350 Paryz; Hiszpania, Barcelona 1500 Barcelona; Hiszpania, Barcelona 1500 Barcelona; Hiszpania, Barcelona 1400 Barcelona;
ok. 3 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pola cel i cena z tabeli wycieczki oraz odpowiadające im pole podpis z tabeli zdjecia, dla wycieczek, których cena jest wyższa niż 1300 zł. Zapytanie wykorzystuje relację.
FROM wycieczki AS w / JOIN zdjecia AS z - aliasy tabel, krótkie nazwy używane w całym zapytaniu. Przy złączeniu każda kolumna dostaje przedrostek swojej tabeli (w.cena, z.podpis) i od razu widać, skąd pochodzi,
ON z.id = w.zdjecia_id - relacja: wycieczka dostaje zdjęcie, na które wskazuje jej klucz obcy,
WHERE w.cena > 1300 - „wyższa niż”, więc Paryż za równe 1300 zł nie wchodzi.
Wynik: 4 wiersze - Paryż 1350 i trzy razy Barcelona (1500, 1500, 1400), każdy z podpisem swojego zdjęcia (R.1.5). Zrzut kw3.jpg.
Typowe błędy
FROM wycieczki, zdjecia bez warunku łączącego daje iloczyn kartezjański - każdą wycieczkę z każdym zdjęciem.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT nazwaPliku, podpisFROM zdjeciaORDER BY podpis;-- Zapytanie 2SELECT cel, dataWyjazduFROM wycieczkiWHERE dostepna = FALSEORDER BY dataWyjazdu, cel;-- Zapytanie 3SELECT w.cel, w.cena, z.podpisFROM wycieczki AS wJOIN zdjecia AS z ON z.id = w.zdjecia_idWHERE w.cena > 1300;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: usunięcie tabeli uzytkownik
Kryteria:
R.1.6 Wykonano zapytanie 4 usuwające tabelę uzytkownik . W pliku z kwerendami lub na zrzucie istnieje zapis np.: DROP TABLE uzytkownik; oraz wynik działania udokumentowano zrzutem, na którym widnieje komunikat o poprawnym usunięciu lub spis tabel bazy danych bez tabeli uzytkownik
ok. 2 min
Polecenie z arkusza
Zapytanie 4: usuwające tabelę uzytkownik.
DROP TABLE usuwa całą tabelę - strukturę i dane; DELETE skasowałby tylko wiersze. phpMyAdmin pyta o potwierdzenie (R.1.6).
Sprawdzone na bazie: przed zapytaniem tabele to kadra, matematycy, typy, uslugi, uzytkownik, wycieczki, wyniki, zdjecia, po nim zostają kadra, matematycy, typy, uslugi, wycieczki, wyniki, zdjecia. Zrzut kw4.jpg - komunikat albo lista tabel bez uzytkownik.
Typowe błędy
DELETE FROM uzytkownik opróżnia tabelę, ale jej nie usuwa - to nie spełnia R.1.6.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT nazwaPliku, podpisFROM zdjeciaORDER BY podpis;-- Zapytanie 2SELECT cel, dataWyjazduFROM wycieczkiWHERE dostepna = FALSEORDER BY dataWyjazdu, cel;-- Zapytanie 3SELECT w.cel, w.cena, z.podpisFROM wycieczki AS wJOIN zdjecia AS z ON z.id = w.zdjecia_idWHERE w.cena > 1300;-- Zapytanie 4DROP TABLE uzytkownik;
Podgląd strony po tym kroku
Etap 2: Strona HTML
Kroki 8-19 · po tym etapie masz 14 z 34 kryteriów (pkt)
Krok 8: Plik biuro.php w HTML5
Kryteria: ,
R.2.1 Witrynę internetową zapisano w pliku PHP o nazwie biuro
R.2.2 Zastosowano deklarację języka HTML5 <!DOCTYPE html>, zadeklarowano język witryny polski np. <html lang = "pl"> oraz zapisano jawnie właściwy standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Cechy witryny: Składa się ze strony o nazwie biuro.php zapisanej w języku HTML 5.
Jedna strona, biuro.php (R.2.1, R.2.2). Dopóki nie ma w niej kodu PHP, podgląd pokazuje ją wprost. Zdjęcia 1.jpg - 9.jpg leżą obok strony.
R.2.2 Zastosowano deklarację języka HTML5 <!DOCTYPE html>, zadeklarowano język witryny polski np. <html lang = "pl"> oraz zapisano jawnie właściwy standard kodowania polskich znaków
Krok 10: Kodowanie polskich znaków i widok na telefonie
Kryteria:
R.2.2 Zastosowano deklarację języka HTML5 <!DOCTYPE html>, zadeklarowano język witryny polski np. <html lang = "pl"> oraz zapisano jawnie właściwy standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Jawnie zastosowany właściwy standard kodowania polskich znaków.
<meta charset="UTF-8"> (R.2.2), pod nim <meta name="viewport"> - telefon pokaże stronę w swojej szerokości. Arkusz drugiej linii nie wymaga.
R.3.3 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl9.css , formatowanie CSS pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamiast #)
ok. 1 min
Polecenie z arkusza
Arkusz stylów w pliku o nazwie styl9.css prawidłowo połączony z kodem strony.
Nazwa z cyfrą: styl9.css.
Typowe błędy
styl.css bez dziewiątki odcina wszystkie style (R.3.3).
R.2.4 Układ strony zdefiniowano dzieląc ją na bloki: baner, lewy, środkowy, prawy, dane i stopka. Zastosowano znaczniki sekcji
ok. 3 min
Polecenie z arkusza
Podział strony na bloki: na górze baner, poniżej obok siebie trzy bloki: lewy, środkowy i prawy, poniżej blok z danymi, poniżej stopka. Podział zrealizowany za pomocą znaczników sekcji tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z Obrazem 2.
Każdy blok w znaczniku, który mówi, czym jest (R.2.4):
baner - <header>,
blok środkowy z galerią - <main class="gallery">, treść główna strony,
bloki lewy (promocje) i prawy (kontakt) - dwa <aside>, treści poboczne obok głównej,
blok z danymi o dawnych wyjazdach - <section class="history">,
stopka - <footer>.
Bloki dostają klasy z nazwą treści, a nie położenia (promotions, a nie left): jeśli kiedyś promocje przejdą na prawą stronę, nazwa w kodzie nadal będzie prawdziwa.
R.2.5 W banerze zapisano nagłówek h1: "BIURO PODRÓŻY", w blokach lewym, środkowym i prawym przynajmniej jeden h2 z: "Promocje", "W tym roku jedziemy do...", "Kontakt", w bloku dane h3: "W poprzednich latach byliśmy..." oraz przynajmniej jeden paragraf
ok. 1 min
Polecenie z arkusza
Zawartość banera: nagłówek pierwszego stopnia o treści: „BIURO PODRÓŻY”.
R.2.5 W banerze zapisano nagłówek h1: "BIURO PODRÓŻY", w blokach lewym, środkowym i prawym przynajmniej jeden h2 z: "Promocje", "W tym roku jedziemy do...", "Kontakt", w bloku dane h3: "W poprzednich latach byliśmy..." oraz przynajmniej jeden paragraf
R.2.7 W bloku lewym zapisano tabelę o 2 kolumnach i 3 wierszach, zastosowano znaczniki <table>, <tr>, <td>
ok. 2 min
Polecenie z arkusza
Zawartość bloku lewego: Nagłówek drugiego stopnia o treści: „Promocje”. Tabela o dwóch kolumnach i trzech wierszach, wypełniona danymi: Warszawa od 600 zł, Wenecja od 1200 zł, Paryż od 1200 zł.
<h2> i tabela 3 × 2: trzy <tr>, w każdym dwie komórki <td> (R.2.5, R.2.7). Bez wiersza nagłówkowego - arkusz go nie przewiduje, a obraz 2 go nie pokazuje.
R.2.5 W banerze zapisano nagłówek h1: "BIURO PODRÓŻY", w blokach lewym, środkowym i prawym przynajmniej jeden h2 z: "Promocje", "W tym roku jedziemy do...", "Kontakt", w bloku dane h3: "W poprzednich latach byliśmy..." oraz przynajmniej jeden paragraf
ok. 1 min
Polecenie z arkusza
Zawartość bloku środkowego: Nagłówek drugiego stopnia o treści: „W tym roku jedziemy do...”. Efekt działania skryptu 1.
<h2>; pod nim Skrypt 1 wstawi galerię zdjęć (R.2.5).
R.2.5 W banerze zapisano nagłówek h1: "BIURO PODRÓŻY", w blokach lewym, środkowym i prawym przynajmniej jeden h2 z: "Promocje", "W tym roku jedziemy do...", "Kontakt", w bloku dane h3: "W poprzednich latach byliśmy..." oraz przynajmniej jeden paragraf
Zawartość bloku prawego: Nagłówek drugiego stopnia o treści: „Kontakt”. Odnośnik do adresu e-mail [email protected] o treści: „napisz do nas”. Paragraf (akapit) o treści „telefon: 444555666”.
odnośnik z przedrostkiem mailto: otwiera program pocztowy z gotowym adresatem (R.3.2),
akapit ma treść z arkusza, a sam numer jest odnośnikiem tel: - na telefonie dotknięcie numeru rozpoczyna połączenie. W href numer z kierunkowym kraju (+48), widoczny tekst zostaje jak w arkuszu (R.2.5).
Typowe błędy
href="[email protected]" bez mailto: przeglądarka traktuje jak plik w bieżącym folderze.
R.2.5 W banerze zapisano nagłówek h1: "BIURO PODRÓŻY", w blokach lewym, środkowym i prawym przynajmniej jeden h2 z: "Promocje", "W tym roku jedziemy do...", "Kontakt", w bloku dane h3: "W poprzednich latach byliśmy..." oraz przynajmniej jeden paragraf
ok. 1 min
Polecenie z arkusza
Zawartość bloku z danymi: Nagłówek trzeciego stopnia o treści: „W poprzednich latach byliśmy...”. Lista numerowana (uporządkowana), a w niej efekt działania skryptu 2.
<h3> i pusta lista <ol> - elementy <li> wypisze Skrypt 2 (R.2.5).
Kroki 20-29 · po tym etapie masz 25 z 34 kryteriów (pkt)
Krok 20: Plik styl9.css: kolory z arkusza w zmiennych
ok. 2 min
Polecenie z arkusza
Styl CSS zdefiniowany w całości w zewnętrznym pliku o nazwie styl9.css.
Kolory arkusza jako zmienne CSS w :root: ciemny róż banera i stopki, róż bloków bocznych, MistyRose bloku środkowego i bloku z danymi, Maroon ramki i pierwszych liter. Reguły odwołują się do nich przez var(--nazwa). Sama definicja nic nie zmienia na stronie.
R.4.2 Ustawiono kolor tła #BA6B6C dla banera i stopki, #EF9A9A dla bloku lewego i prawego, MistyRose dla bloku z danymi i środkowego
R.4.3 Ustawiono biały kolor czcionki dla banera i stopki
R.4.6 Ustawiono marginesy wewnętrzne (padding) 7px dla banera i stopki, 50px dla bloku z danymi, marginesy zewnętrzne (margin) 7px dla obrazu oraz auto dla tabeli
ok. 1 min
Polecenie z arkusza
Wspólne dla banera i stopki: kolor tła #BA6B6C, biały kolor czcionki, marginesy wewnętrzne 7 px.
Jedna reguła dla dwóch znaczników (R.4.2, R.4.3, R.4.6).
R.4.2 Ustawiono kolor tła #BA6B6C dla banera i stopki, #EF9A9A dla bloku lewego i prawego, MistyRose dla bloku z danymi i środkowego
R.4.5 Ustawiono wysokość 500px dla bloków lewego, środkowego i prawego i 120px dla selektora img
ok. 2 min
Polecenie z arkusza
Dla bloku lewego i prawego: kolor tła #EF9A9A, szerokość 20%, wysokość 500 px. Dla bloku środkowego: kolor tła MistyRose, szerokość 60%, wysokość 500 px.
Kolory i wysokość 500px (R.4.2, R.4.5). Szerokości 20%, 60% i 20% wpisze siatka w następnym kroku.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu (prawidłowo zastosowano właściwości CSS decydujące o układzie strony, np. float i clear albo display flex albo grid albo position)
R.4.4 Ustawiono szerokość 20% dla bloków lewego i prawego, 60% dla środkowego (width lub grid-template-columns lub flex), 80% dla selektora table
ok. 3 min
Polecenie z arkusza
Podział strony na bloki: na górze baner, poniżej obok siebie trzy bloki: lewy, środkowy i prawy, poniżej blok z danymi, poniżej stopka.
<body> staje się siatką o trzech kolumnach: 20%, 60%, 20% - szerokości z arkusza, zapisane w grid-template-columns (klucz to przyjmuje, R.4.4).
grid-template-areas rysuje plan z obrazu 2: baner przez całą szerokość, pod nim promocje, galeria i kontakt, potem dawne wyjazdy i stopka - też na całą szerokość. W wersji podstawowej ten sam układ dają float: left na trzech blokach i clear: both w bloku z danymi (R.3.1).
Typowe błędy
Szerokości bloków w siatce wpisuj tylko w grid-template-columns. Dodatkowe width: 60% w galerii liczyłoby się od jej kolumny - galeria zajęłaby 36% strony.
R.4.2 Ustawiono kolor tła #BA6B6C dla banera i stopki, #EF9A9A dla bloku lewego i prawego, MistyRose dla bloku z danymi i środkowego
R.4.6 Ustawiono marginesy wewnętrzne (padding) 7px dla banera i stopki, 50px dla bloku z danymi, marginesy zewnętrzne (margin) 7px dla obrazu oraz auto dla tabeli
ok. 1 min
Polecenie z arkusza
Dla bloku z danymi: kolor tła MistyRose, marginesy wewnętrzne 50 px.
R.4.7 Ustawiono dla selektora ol punktor wielka cyfra rzymska (list-style lub list-style-type: upper- roman;) oraz dla selektora li wyrównanie tekstu do lewej strony
ok. 1 min
Polecenie z arkusza
Dla selektora listy numerowanej: punktor jest wielką cyfrą rzymską. Dla selektora elementu listy: wyrównanie tekstu do lewej strony.
Selektory znaczników ol i li - arkusz zastrzega, że styl ma być zdefiniowany wyłącznie nimi (R.4.7). Wielkie cyfry rzymskie to upper-roman.
Typowe błędy
Styl dla .history ol albo klasy nie spełnia R.4.7 - klucz uznaje tylko selektory znaczników.
R.4.4 Ustawiono szerokość 20% dla bloków lewego i prawego, 60% dla środkowego (width lub grid-template-columns lub flex), 80% dla selektora table
R.4.6 Ustawiono marginesy wewnętrzne (padding) 7px dla banera i stopki, 50px dla bloku z danymi, marginesy zewnętrzne (margin) 7px dla obrazu oraz auto dla tabeli
R.4.8 Ustawiono obramowanie dla tabeli: 1px solid Maroon
ok. 1 min
Polecenie z arkusza
Dla selektora tabeli: marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę, szerokość 80%, obramowanie linią ciągłą o szerokości 1 px i kolorze Maroon.
Selektor table (R.4.4, R.4.6, R.4.8). Ramkę ma tylko tabela, komórki nie - na obrazie 2 widać jedną ramkę wokół całości.
R.4.5 Ustawiono wysokość 500px dla bloków lewego, środkowego i prawego i 120px dla selektora img
R.4.6 Ustawiono marginesy wewnętrzne (padding) 7px dla banera i stopki, 50px dla bloku z danymi, marginesy zewnętrzne (margin) 7px dla obrazu oraz auto dla tabeli
R.4.9 Ustawiono dla obrazu cień box-shadow: 7px 7px 7px DimGray;
ok. 2 min
Polecenie z arkusza
Dla selektora obrazu: wysokość 120 px, marginesy zewnętrzne 7 px, cień o przesunięciu 7 px w obu osiach, rozmyciu 7 px i kolorze DimGray.
.photos - pojemnik na zdjęcia galerii (powstanie w Skrypcie 1) jako flexbox z zawijaniem i wyśrodkowaniem. Zdjęcia w zwykłej linii tekstu miałyby między sobą dodatkową szparę ze spacji w kodzie HTML; we flexboksie odstęp to wyłącznie marginesy z arkusza.
Kroki 30-35 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 30: Połączenie PDO na początku strony i zamknięcie
Kryteria: ,
R.5.1 Skrypt zawiera działające połączenie z serwerem bazy danych i wybór bazy podroze
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie
ok. 4 min
Polecenie z arkusza
Napisany w języku PHP. Łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie podroze. Na końcu działania skrypt zamyka połączenie z serwerem.
Dostęp do bazy stoi na początku pliku biuro.php, przed <!DOCTYPE html>: najpierw dane, potem strona. Klucz przyjmuje bibliotekę MySQLi albo PDO - tu PDO:
new PDO('mysql:host=localhost;dbname=podroze;charset=utf8mb4', 'root', '', [...]) - serwer, baza podroze i kodowanie w jednym napisie (DSN), potem użytkownik i puste hasło (R.5.1),
ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek; FETCH_ASSOC - wiersze jako tablice z nazwami kolumn,
$pdo = null zamyka połączenie. Oba zapytania staną przed tą linią, więc zamknięcie będzie ostatnią operacją na bazie (R.5.2),
try/catch (PDOException) - gdy baza nie działa, $error niesie zrozumiały komunikat zamiast treści wyjątku z nazwą serwera i użytkownika.
Podgląd pokazuje wynik uruchomienia strony - skrypty jeszcze niczego nie wypisują.
biuro.phpPHPzmienione: 12 linii
<?phptry {$pdo = new PDO('mysql:host=localhost;dbname=podroze;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$pdo = null;} catch (PDOException) {$error = 'Nie udało się wczytać danych biura. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Poznaj Europę</title> <linkrel="stylesheet"href="styl9.css"></head><body> <header> <h1>BIURO PODRÓŻY</h1> </header> <asideclass="promotions"> <h2>Promocje</h2> <table> <tr> <td>Warszawa</td> <td>od 600 zł</td> </tr> <tr> <td>Wenecja</td> <td>od 1200 zł</td> </tr> <tr> <td>Paryż</td> <td>od 1200 zł</td> </tr> </table> </aside> <mainclass="gallery"> <h2>W tym roku jedziemy do...</h2> </main> <asideclass="contact"> <h2>Kontakt</h2> <ahref="mailto:[email protected]">napisz do nas</a> <p>telefon: <ahref="tel:+48444555666">444555666</a></p> </aside> <sectionclass="history"> <h3>W poprzednich latach byliśmy...</h3> <ol> </ol> </section> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (biuro.php)
Krok 31: Skrypt 1: zapytanie 1 i funkcja e()
Kryteria:
R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 1 lub skrypt 2 wysyła zapytanie 2
ok. 3 min
Polecenie z arkusza
Skrypt 1: Wysyła do bazy danych zapytanie 1.
$pdo->query(...)->fetchAll() wysyła zapytanie 1 i od razu odbiera wszystkie 9 wierszy jako listę tablic z kluczami nazwaPliku i podpis (R.5.3). Zapytanie nie zawiera danych od użytkownika, więc nie trzeba go przygotowywać (prepare),
$photos = [] przed try - gdy baza zawiedzie, galeria jest pusta, a strona nadal się wyświetla,
funkcja e() (od escape) przepuszcza tekst przez htmlspecialchars z ENT_QUOTES: zamienia <, >, & i oba rodzaje cudzysłowów na encje. Tekst z bazy trafi do atrybutów alt i title - podpis z cudzysłowem (Wieża "Eiffla") bez tej zamiany zamknąłby atrybut w połowie.
biuro.phpPHPzmienione: 9 linii
<?php$photos = [];try {$pdo = new PDO('mysql:host=localhost;dbname=podroze;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$photos = $pdo->query('SELECT nazwaPliku, podpis FROM zdjecia ORDER BY podpis')->fetchAll();$pdo = null;} catch (PDOException) {$error = 'Nie udało się wczytać danych biura. Spróbuj ponownie za chwilę.';}functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Poznaj Europę</title> <linkrel="stylesheet"href="styl9.css"></head><body> <header> <h1>BIURO PODRÓŻY</h1> </header> <asideclass="promotions"> <h2>Promocje</h2> <table> <tr> <td>Warszawa</td> <td>od 600 zł</td> </tr> <tr> <td>Wenecja</td> <td>od 1200 zł</td> </tr> <tr> <td>Paryż</td> <td>od 1200 zł</td> </tr> </table> </aside> <mainclass="gallery"> <h2>W tym roku jedziemy do...</h2> </main> <asideclass="contact"> <h2>Kontakt</h2> <ahref="mailto:[email protected]">napisz do nas</a> <p>telefon: <ahref="tel:+48444555666">444555666</a></p> </aside> <sectionclass="history"> <h3>W poprzednich latach byliśmy...</h3> <ol> </ol> </section> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
R.2.8 W bloku środkowym umieszczono przynajmniej jeden obraz z tekstem alternatywnym (w HTML lub skrypcie)
R.5.4 Skrypt 1 wyświetla obrazy na podstawie danych zwróconych zapytaniem, w ten sposób, że pole nazwaPliku jest źródłem obrazu, a pole podpis jest tekstem alternatywnym obrazu
ok. 3 min
Polecenie z arkusza
Dane z każdego zwróconego zapytaniem wiersza są wykorzystane do wyświetlenia kolejnych obrazów, w ten sposób, że pole nazwaPliku jest źródłem obrazu, a pole podpis jest tekstem alternatywnym obrazu.
Pętla foreach ($photos as $photo): ... endforeach; - zapis z dwukropkiem przeznaczony do szablonów: znacznik <img> stoi zwykłym HTML-em, a PHP wstawia tylko wartości przez <?= e(...) ?>. Nazwa pliku idzie do src, podpis do alt (R.2.8, R.5.4). Bez sklejania znaczników w napisach i bez ukośników przed cudzysłowami.
Podgląd: dziewięć zdjęć od Barcelony do Wenecji, ułożonych przez flexbox w trzy rzędy.
biuro.phpPHPzmienione: 5 linii
<?php$photos = [];try {$pdo = new PDO('mysql:host=localhost;dbname=podroze;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$photos = $pdo->query('SELECT nazwaPliku, podpis FROM zdjecia ORDER BY podpis')->fetchAll();$pdo = null;} catch (PDOException) {$error = 'Nie udało się wczytać danych biura. Spróbuj ponownie za chwilę.';}functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Poznaj Europę</title> <linkrel="stylesheet"href="styl9.css"></head><body> <header> <h1>BIURO PODRÓŻY</h1> </header> <asideclass="promotions"> <h2>Promocje</h2> <table> <tr> <td>Warszawa</td> <td>od 600 zł</td> </tr> <tr> <td>Wenecja</td> <td>od 1200 zł</td> </tr> <tr> <td>Paryż</td> <td>od 1200 zł</td> </tr> </table> </aside> <mainclass="gallery"> <h2>W tym roku jedziemy do...</h2> <divclass="photos"><?phpforeach ($photosas$photo): ?> <imgsrc="<?= e($photo['nazwaPliku']) ?>"alt="<?= e($photo['podpis']) ?>"><?phpendforeach; ?> </div> </main> <asideclass="contact"> <h2>Kontakt</h2> <ahref="mailto:[email protected]">napisz do nas</a> <p>telefon: <ahref="tel:+48444555666">444555666</a></p> </aside> <sectionclass="history"> <h3>W poprzednich latach byliśmy...</h3> <ol> </ol> </section> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (biuro.php)
Krok 33: Skrypt 1: dymek z podpisem
Kryteria:
R.5.5 Skrypt 1 definiuje atrybut title dla każdego obrazu z tekstem z pola podpis zwróconym zapytaniem
ok. 1 min
Polecenie z arkusza
Pole podpis jest tekstem alternatywnym obrazu oraz tekstem pokazującym się w dymku, gdy kursor znajdzie się na grafice, patrz Obraz 2, dymek do grafiki z Londynem.
Dymek po najechaniu to atrybut title - ten sam podpis drugi raz (R.5.5). alt czyta czytnik ekranu i pokazuje przeglądarka, gdy obrazu brak; title to wskazówka po najechaniu. Najedź w podglądzie na zdjęcie Londynu.
biuro.phpPHPzmienione: 1 linia
<?php$photos = [];try {$pdo = new PDO('mysql:host=localhost;dbname=podroze;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$photos = $pdo->query('SELECT nazwaPliku, podpis FROM zdjecia ORDER BY podpis')->fetchAll();$pdo = null;} catch (PDOException) {$error = 'Nie udało się wczytać danych biura. Spróbuj ponownie za chwilę.';}functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Poznaj Europę</title> <linkrel="stylesheet"href="styl9.css"></head><body> <header> <h1>BIURO PODRÓŻY</h1> </header> <asideclass="promotions"> <h2>Promocje</h2> <table> <tr> <td>Warszawa</td> <td>od 600 zł</td> </tr> <tr> <td>Wenecja</td> <td>od 1200 zł</td> </tr> <tr> <td>Paryż</td> <td>od 1200 zł</td> </tr> </table> </aside> <mainclass="gallery"> <h2>W tym roku jedziemy do...</h2> <divclass="photos"><?phpforeach ($photosas$photo): ?> <imgsrc="<?= e($photo['nazwaPliku']) ?>"alt="<?= e($photo['podpis']) ?>"title="<?= e($photo['podpis']) ?>"><?phpendforeach; ?> </div> </main> <asideclass="contact"> <h2>Kontakt</h2> <ahref="mailto:[email protected]">napisz do nas</a> <p>telefon: <ahref="tel:+48444555666">444555666</a></p> </aside> <sectionclass="history"> <h3>W poprzednich latach byliśmy...</h3> <ol> </ol> </section> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (biuro.php)
Krok 34: Skrypt 2: dawne wyjazdy w liście
Kryteria: , ,
R.2.6 W bloku z danymi umieszczono jedną listę numerowaną, zastosowano znaczniki <ol>, <li> (w skrypcie lub HTML)
R.5.6 Skrypt 2 wyświetla każdy zwrócony wiersz w elemencie listy w formacie: „Dnia <data> pojechaliśmy do <cel>”, gdzie < > oznacza pole zwrócone zapytaniem
R.5.7 Zastosowano znaczące nazewnictwo dla wszystkich zmiennych lub funkcji w języku polskim lub angielskim. Nazwy odzwierciedlają cel zastosowania
ok. 4 min
Polecenie z arkusza
Skrypt 2: Wysyła do bazy danych zapytanie 2. Każdy zwrócony zapytaniem wiersz jest wyświetlany w elemencie listy, według wzoru: „Dnia <data> pojechaliśmy do <cel>”, gdzie w znakach < > zapisano pola zwracane zapytaniem.
drugie zapytanie na tym samym połączeniu, przed jego zamknięciem; $pastTrips startuje pusta przed try (R.5.3),
pętla wypisuje jeden <li> na wiersz. Kolumny są po nazwach ($trip['dataWyjazdu'], $trip['cel']), więc kolejność pól w zdaniu nie zależy od kolejności kolumn w zapytaniu (R.2.6, R.5.6),
data stoi w znaczniku <time datetime="..."> - przeglądarki i wyszukiwarki wiedzą, że to data. Na ekranie zostaje w postaci z bazy, jak wymaga wzór arkusza.
Podgląd: trzy elementy listy numerowane I, II, III, jak na obrazie 2. Nazwy mówią, co przechowują: $photos, $pastTrips, $trip, e() (R.5.7).
biuro.phpPHPzmienione: 7 linii
<?php$photos = [];$pastTrips = [];try {$pdo = new PDO('mysql:host=localhost;dbname=podroze;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$photos = $pdo->query('SELECT nazwaPliku, podpis FROM zdjecia ORDER BY podpis')->fetchAll();$pastTrips = $pdo->query('SELECT cel, dataWyjazdu FROM wycieczki WHERE dostepna = FALSE ORDER BY dataWyjazdu, cel')->fetchAll();$pdo = null;} catch (PDOException) {$error = 'Nie udało się wczytać danych biura. Spróbuj ponownie za chwilę.';}functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Poznaj Europę</title> <linkrel="stylesheet"href="styl9.css"></head><body> <header> <h1>BIURO PODRÓŻY</h1> </header> <asideclass="promotions"> <h2>Promocje</h2> <table> <tr> <td>Warszawa</td> <td>od 600 zł</td> </tr> <tr> <td>Wenecja</td> <td>od 1200 zł</td> </tr> <tr> <td>Paryż</td> <td>od 1200 zł</td> </tr> </table> </aside> <mainclass="gallery"> <h2>W tym roku jedziemy do...</h2> <divclass="photos"><?phpforeach ($photosas$photo): ?> <imgsrc="<?= e($photo['nazwaPliku']) ?>"alt="<?= e($photo['podpis']) ?>"title="<?= e($photo['podpis']) ?>"><?phpendforeach; ?> </div> </main> <asideclass="contact"> <h2>Kontakt</h2> <ahref="mailto:[email protected]">napisz do nas</a> <p>telefon: <ahref="tel:+48444555666">444555666</a></p> </aside> <sectionclass="history"> <h3>W poprzednich latach byliśmy...</h3> <ol><?phpforeach ($pastTripsas$trip): ?> <li>Dnia <timedatetime="<?= e($trip['dataWyjazdu']) ?>"><?= e($trip['dataWyjazdu']) ?></time> pojechaliśmy do <?= e($trip['cel']) ?></li><?phpendforeach; ?> </ol> </section> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (biuro.php)
Krok 35: Komunikat, gdy baza nie działa
ok. 2 min
Polecenie z arkusza
Zawartość bloku środkowego: Efekt działania skryptu 1.
Gdy połączenie albo zapytanie się nie uda, catch ustawia $error, a blok środkowy wyświetla komunikat pod nagłówkiem zamiast pustego miejsca po galerii. isset($error) sprawdza, czy zmienna w ogóle powstała.
Sprawdzone z błędną nazwą bazy: strona wyświetla wszystkie bloki, komunikat i pustą listę wyjazdów, bez ostrzeżeń PHP. Klucz tego nie ocenia.
biuro.phpPHPzmienione: 3 linie
<?php$photos = [];$pastTrips = [];try {$pdo = new PDO('mysql:host=localhost;dbname=podroze;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$photos = $pdo->query('SELECT nazwaPliku, podpis FROM zdjecia ORDER BY podpis')->fetchAll();$pastTrips = $pdo->query('SELECT cel, dataWyjazdu FROM wycieczki WHERE dostepna = FALSE ORDER BY dataWyjazdu, cel')->fetchAll();$pdo = null;} catch (PDOException) {$error = 'Nie udało się wczytać danych biura. Spróbuj ponownie za chwilę.';}functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Poznaj Europę</title> <linkrel="stylesheet"href="styl9.css"></head><body> <header> <h1>BIURO PODRÓŻY</h1> </header> <asideclass="promotions"> <h2>Promocje</h2> <table> <tr> <td>Warszawa</td> <td>od 600 zł</td> </tr> <tr> <td>Wenecja</td> <td>od 1200 zł</td> </tr> <tr> <td>Paryż</td> <td>od 1200 zł</td> </tr> </table> </aside> <mainclass="gallery"> <h2>W tym roku jedziemy do...</h2><?phpif (isset($error)): ?> <pclass="error"><?= e($error) ?></p><?phpendif; ?> <divclass="photos"><?phpforeach ($photosas$photo): ?> <imgsrc="<?= e($photo['nazwaPliku']) ?>"alt="<?= e($photo['podpis']) ?>"title="<?= e($photo['podpis']) ?>"><?phpendforeach; ?> </div> </main> <asideclass="contact"> <h2>Kontakt</h2> <ahref="mailto:[email protected]">napisz do nas</a> <p>telefon: <ahref="tel:+48444555666">444555666</a></p> </aside> <sectionclass="history"> <h3>W poprzednich latach byliśmy...</h3> <ol><?phpforeach ($pastTripsas$trip): ?> <li>Dnia <timedatetime="<?= e($trip['dataWyjazdu']) ?>"><?= e($trip['dataWyjazdu']) ?></time> pojechaliśmy do <?= e($trip['cel']) ?></li><?phpendforeach; ?> </ol> </section> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Kroki 36-37 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 36: Co jest inaczej i dlaczego
ok. 3 min
Polecenie z arkusza
Ocenie będzie podlegać 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej, skrypt połączenia z bazą.
Obie wersje dają te same punkty. Różnice:
wersja podstawowa
ta wersja
SQL
zapytania w jednej linii, dostepna = 0
klauzule w liniach, = FALSE, aliasy tabel, ORDER BY w zapytaniu 2
układ
float: left + clear: both
siatka 20% / 60% / 20% na <body>
bloki
<section> z identyfikatorami left, middle...
aside, main, section z klasami od treści
kolory
wpisane w każdej regule
zmienne w :root
galeria
zdjęcia w linii tekstu
flexbox .photos
biblioteka
mysqli_* w dwóch blokach PHP w treści
PDO na początku pliku, w HTML-u tylko wyświetlanie
kolumny wiersza
numery ($row[1] przed $row[0])
nazwy ($trip['dataWyjazdu'])
atrybuty alt/title
podpis wprost z bazy
przez e()
błąd bazy
ostrzeżenia PHP
komunikat dla użytkownika
Sprawdzone: podgląd każdego kroku to prawdziwe uruchomienie w PHP 8.2 na MariaDB, układ zmierzony w Chrome (bloki 20/60/20% w jednym rzędzie po 500 px, 9 zdjęć po 120 px w trzech rzędach mieszczących się w galerii, lista z cyframi rzymskimi), awaria bazy daje komunikat bez ostrzeżeń.
Czas przeznaczony na wykonanie zadania wynosi 150 minut.
Wersja podstawowa, gdy liczy się czas: funkcje mysqli_* są w tabeli 1 arkusza, a float wystarczy do trzech bloków obok siebie.
Ta wersja, gdy PDO i siatkę CSS pisze się z pamięci. Szablon z foreach: ... endforeach; i <?= e(...) ?> jest odporny na najczęstszy błąd egzaminacyjny - pomylone cudzysłowy w echo budującym znacznik <img>.
Mieszanie też daje pełne punkty: siatka i zmienne z tej wersji, mysqli z podstawowej.
Krok 38 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 38: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
Po zakończeniu pracy utwórz w folderze z numerem zdającego plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Nagraj płytę z rezultatami pracy.
W przeglądarka.txt nazwa przeglądarki, w której strona była sprawdzana.
W folderze: zdjęcia 1.jpg - 9.jpg, biuro.php, import.png, kw1.jpg - kw4.jpg, kwerendy.txt, przeglądarka.txt, styl9.css. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.
Autor rozwiązania: Bartosz Bryniarski. Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.