Przejdź do głównej treści
E.14-04-20.06-SGCzerwiec 2020 · 48 kroków · ok. 93 min · 39 kryteriów z klucza

Rozwiązanie krok po kroku: Sklep z ozdobami choinkowymi (Wersja podstawowa)

Sklep z ozdobami choinkowymi: baza sklep z tabelą zamowienia utworzoną zapytaniem SQL, klucz główny, użytkownik klient, który może tylko dopisywać zamówienia, i pierwsze zamówienie Jana Kowalskiego. Do tego trzy zdjęcia ozdób wykadrowane do kwadratu 300 × 300 px, animacja GIF zmieniająca je co 2 sekundy, dwie strony - galeria.html i zamowienie.php - z banerem, trzema panelami i stopką, style w styl.css, skrypt JavaScript, który po wskazaniu ozdoby kursorem wypisuje jej cenę, i skrypt PHP zapisujący dane z formularza w bazie.

To arkusz E.14 z czerwca 2020 - kwalifikacji formuły 2012, którą zastąpiła INF.03. Kroki idą zdanie po zdaniu za treścią arkusza - przy każdym jest polecenie z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Skrypty są najpierw napisane najprościej (atrybut onmouseover, funkcje mysqli z tabeli 1 arkusza), a na końcu skrypt PHP jest sprawdzony na prawdziwym wstrzyknięciu SQL i zabezpieczony.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (flex, PDO)” jest drugi sposób rozwiązania tego samego arkusza: układ na flex we wspólnym pojemniku, semantyczne znaczniki, addEventListener w osobnym pliku skryptu, PDO z zapytaniem przygotowanym i walidacją danych - obie wersje są zgodne z kluczem.

Zdjęcia pochodzą z oryginalnego archiwum CKE egzamin4.zip (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zapytania są wykonane na MariaDB 10.4 - tej samej, która jest w XAMPP, skrypt PHP uruchomiony w PHP 8.2, a skrypt JavaScript sprawdzony w przeglądarce Chrome.

Jak liczą się punkty. Klucz ma 39 kryteriów (R.1.1 - R.5.7), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 30 punktów - można stracić 9 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Baza danych

    Kroki 1-8 · po tym etapie masz 7 z 39 kryteriów (pkt)

    Krok 1: Folder zdającego i archiwum egzamin4.zip

    ok. 2 min
    Polecenie z arkusza

    Aby wykonać zadanie zaloguj się bez hasła na konto Egzamin. Na pulpicie znajduje się folder egzamin4.zip, z plikami potrzebnymi do rozwiązania zadania, zabezpieczony hasłem 6hM9N#k@ Na pulpicie utwórz folder i nazwij go Twoim numerem PESEL. Wszystkie pliki utworzone w czasie egzaminu umieść w tym folderze.

    Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum egzamin4.zip rozpakowujesz programem 7-Zip albo Eksploratorem Windows, podając hasło 6hM9N#k@ (wielkość liter i znaki #, @ mają znaczenie). W środku jest folder egzamin z trzema zdjęciami ozdób: choinka.jpg, mikolaj.jpg, renifer.jpg - każde 3872 × 2592 px.

    Do folderu z numerem PESEL trafia wszystko, co powstanie: kwerendy.txt, zrzuty, eksport tabeli, grafiki, animacja, strony i styl.

    Typowe błędy

    Pliki zapisane poza folderem zdającego (np. tylko w htdocs) nie trafią na płytę - a ocenia się wyłącznie to, co jest na płycie.

  2. Krok 2: Baza sklep w phpMyAdmin

    ok. 2 min
    Polecenie z arkusza

    Za pomocą narzędzia phpMyAdmin, utwórz bazę sklep. Stosując kwerendy SQL wykonaj operacje na bazie danych. Kwerendy zapisz w pliku kwerendy.txt.

    W XAMPP Control Panel uruchom Apache i MySQL, otwórz http://localhost/phpmyadmin, w lewym panelu Nowa, nazwa sklep, porównywanie np. utf8mb4_polish_ci, Utwórz.

    Inaczej niż w wielu arkuszach, tu tabela nie powstaje kreatorem: wszystkie pięć operacji wykonujesz zapytaniami SQL na karcie SQL bazy sklep i każde zapytanie kopiujesz do pliku kwerendy.txt w folderze zdającego, z opisem, która to kwerenda.

  3. Krok 3: Kwerenda 1: tabela zamowienia

    Kryteria: ,

    R.1.2 Zapisano plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Zapisano zapytanie 1 tworzące tabelę zamowienia zawierającą pola: id, imie, nazwisko, adres_email, liczba_choinek, liczba_mikolajow, liczba_reniferow, info. W pliku z kwerendami istnieje zapis (możliwe alternatywne typy), np.: CREATE TABLE `zamowienia` ( `id` int NOT NULL, `imie` text, `nazwisko` text, `adres_email` text, `liczba_choinek` int, `liczba_mikolajow` int, `liczba_reniferow` int, `info` tinyint(1) );

    ok. 4 min
    Polecenie z arkusza

    Kwerenda 1: tworząca tabelę zamowienia wraz kolumnami: id typ całkowity bez znaku, nie może przyjmować wartości NULL; imie, nazwisko, adres_email typu tekstowego; liczba_choinek, liczba_mikolajow, liczba_reniferow typu całkowitego; info typu logicznego (decyzja klienta, czy chce otrzymywać informacje handlowe). Wszystkie pola oprócz id domyślnie przyjmują wartość NULL

    Każda linia w nawiasie to jedna kolumna: nazwa, typ i ewentualne ograniczenia.

    • id INT UNSIGNED NOT NULL - całkowity bez znaku (UNSIGNED, same liczby nieujemne) i nie może być NULL,
    • imie, nazwisko, adres_email - typ tekstowy TEXT (klucz podaje ten typ; VARCHAR(50) też jest tekstowy),
    • trzy liczby sztuk - INT,
    • info BOOLEAN - typ logiczny. W MariaDB to skrót od TINYINT(1): 1 oznacza „tak”, 0 - „nie”,
    • DEFAULT NULL przy każdej kolumnie oprócz id - wartość domyślna NULL. MariaDB i tak przyjmuje ją domyślnie dla kolumn bez NOT NULL, ale jawny zapis odpowiada wprost na polecenie arkusza.

    Zapytanie zapisujesz w kwerendy.txt - plik z co najmniej jednym zapytaniem z treści zadania daje R.1.2, a samo zapytanie R.1.3. Podgląd: karta Struktura tabeli po wykonaniu kwerend 1 i 2.

    Typowe błędy

    Średnik po ostatniej kolumnie przed nawiasem zamykającym (info BOOLEAN DEFAULT NULL,) to błąd składni - przecinki stoją tylko między kolumnami.

    kwerendy.txt Tekst nowy plik
    -- Kwerenda 1
    CREATE TABLE zamowienia (
        id INT UNSIGNED NOT NULL,
        imie TEXT DEFAULT NULL,
        nazwisko TEXT DEFAULT NULL,
        adres_email TEXT DEFAULT NULL,
        liczba_choinek INT DEFAULT NULL,
        liczba_mikolajow INT DEFAULT NULL,
        liczba_reniferow INT DEFAULT NULL,
        info BOOLEAN DEFAULT NULL
    );
    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Kwerenda 2: klucz główny id

    Kryteria:

    R.1.4 Zapisano zapytanie 2 ustawiająca pole id jako klucz główny. W pliku z kwerendami istnieje zapis, np.: ALTER TABLE `zamowienia` ADD PRIMARY KEY(`id`); lub ustawiono pole id jako klucz podstawowy w zapytaniu z R.1.3

    ok. 2 min
    Polecenie z arkusza

    Kwerenda 2: ustawiająca pole id jako klucz główny

    ALTER TABLE zmienia istniejącą tabelę, a ADD PRIMARY KEY (id) dodaje klucz główny na kolumnie id (R.1.4). Klucz główny wymaga wartości unikalnych i różnych od NULL - dlatego kolumna miała NOT NULL już przy tworzeniu.

    ⚠️ Luka w arkuszu: arkusz nie każe ustawić id jako autonumerowanego (AUTO_INCREMENT). Kwerenda 5 podaje numer 1 ręcznie, ale formularz na stronie zamowienie.php nie ma pola id - skrypt PHP musi więc sam wyliczyć kolejny numer. Rozwiązanie jest w etapie „Skrypt PHP”, a wersja nowoczesna dokłada autonumerowanie w tej samej kwerendzie 2.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Kwerenda 1
    CREATE TABLE zamowienia (
        id INT UNSIGNED NOT NULL,
        imie TEXT DEFAULT NULL,
        nazwisko TEXT DEFAULT NULL,
        adres_email TEXT DEFAULT NULL,
        liczba_choinek INT DEFAULT NULL,
        liczba_mikolajow INT DEFAULT NULL,
        liczba_reniferow INT DEFAULT NULL,
        info BOOLEAN DEFAULT NULL
    );
    
    -- Kwerenda 2
    ALTER TABLE zamowienia ADD PRIMARY KEY (id);
  5. Krok 5: Kwerenda 3: użytkownik klient

    Kryteria:

    R.1.5 Zapisano zapytanie 3 tworzące użytkownika klient z widocznym hasłem klient123 W pliku z kwerendami istnieje zapis np.: CREATE USER 'klient' IDENTIFIED BY 'klient123';

    ok. 2 min
    Polecenie z arkusza

    Kwerenda 3: tworząca użytkownika klient z hasłem klient123

    CREATE USER tworzy konto serwera bazy danych (nie wiersz w tabeli). Pełna nazwa konta to 'klient'@'localhost' - użytkownik i komputer, z którego może się łączyć; sama nazwa 'klient' (jak w przykładzie z klucza) oznacza dowolny komputer 'klient'@'%'. IDENTIFIED BY 'klient123' ustawia hasło; w bazie zostaje zapisany jego skrót, ale w kwerendy.txt hasło jest widoczne, a tego wymaga R.1.5.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Kwerenda 1
    CREATE TABLE zamowienia (
        id INT UNSIGNED NOT NULL,
        imie TEXT DEFAULT NULL,
        nazwisko TEXT DEFAULT NULL,
        adres_email TEXT DEFAULT NULL,
        liczba_choinek INT DEFAULT NULL,
        liczba_mikolajow INT DEFAULT NULL,
        liczba_reniferow INT DEFAULT NULL,
        info BOOLEAN DEFAULT NULL
    );
    
    -- Kwerenda 2
    ALTER TABLE zamowienia ADD PRIMARY KEY (id);
    
    -- Kwerenda 3
    CREATE USER 'klient'@'localhost' IDENTIFIED BY 'klient123';
  6. Krok 6: Kwerenda 4: uprawnienie tylko do wpisywania

    Kryteria:

    R.1.6 Zapisano zapytanie 4 nadające użytkownikowi uprawnienia jedynie do wpisywania rekordów do tabel bazy sklep . W pliku z kwerendami istnieje zapis np.: GRANT INSERT ON sklep.* TO klient;

    ok. 2 min
    Polecenie z arkusza

    Kwerenda 4: nadająca użytkownikowi uprawnienia jedynie do wpisywania rekordów do tabel bazy sklep

    • GRANT INSERT - jedno uprawnienie: dopisywanie wierszy (bez SELECT, UPDATE, DELETE),
    • ON sklep.* - do wszystkich tabel bazy sklep (gwiazdka po kropce),
    • TO 'klient'@'localhost' - dla konta z kwerendy 3, z tą samą częścią po @.

    Sprawdzone na serwerze: zalogowany jako klient dopisuje wiersz bez problemu, a SELECT kończy się błędem „SELECT command denied” (R.1.6). Tak wygląda konto dla formularza zamówień - nawet gdyby ktoś przejął hasło, nie odczyta danych klientów.

    Typowe błędy

    Nazwa konta w GRANT musi być taka sama jak w CREATE USER - 'klient'@'localhost' i 'klient'@'%' to dwa różne konta.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Kwerenda 1
    CREATE TABLE zamowienia (
        id INT UNSIGNED NOT NULL,
        imie TEXT DEFAULT NULL,
        nazwisko TEXT DEFAULT NULL,
        adres_email TEXT DEFAULT NULL,
        liczba_choinek INT DEFAULT NULL,
        liczba_mikolajow INT DEFAULT NULL,
        liczba_reniferow INT DEFAULT NULL,
        info BOOLEAN DEFAULT NULL
    );
    
    -- Kwerenda 2
    ALTER TABLE zamowienia ADD PRIMARY KEY (id);
    
    -- Kwerenda 3
    CREATE USER 'klient'@'localhost' IDENTIFIED BY 'klient123';
    
    -- Kwerenda 4
    GRANT INSERT ON sklep.* TO 'klient'@'localhost';
  7. Krok 7: Kwerenda 5: zamówienie Jana Kowalskiego

    Kryteria:

    R.1.7 Zapisano zapytanie 5 dodające rekord z danymi: Jan Kowalski, [email protected]. W pliku z kwerendami istnieje zapis np.: INSERT INTO `zamowienia` VALUES ('1', 'Jan', 'Kowalski', '[email protected]', NULL, NULL, NULL, NULL); możliwe również z nazwami pól lub przez SET oraz na zrzucie widoczny jest komunikat o poprawnie wykonanej kwerendzie lub wstawiony rekord

    ok. 3 min
    Polecenie z arkusza

    Kwerenda 5: dodająca rekord z danymi: Jan Kowalski, [email protected] Wykonanie kwerendy udokumentuj zrzutem o nazwie kw5.jpg, zrzut powinien zawierać cały obszar ekranu, z widocznym paskiem zadań

    • INSERT INTO zamowienia (id, imie, nazwisko, adres_email) - lista kolumn, do których trafią wartości,
    • VALUES (1, 'Jan', 'Kowalski', '[email protected]') - wartości w tej samej kolejności. Numer id trzeba podać, bo kolumna nie jest autonumerowana i nie ma wartości domyślnej.

    Pozostałe cztery kolumny przyjmują wartość domyślną NULL. phpMyAdmin pokazuje komunikat „1 wiersz dodany.” - zrzut całego ekranu z paskiem zadań zapisujesz jako kw5.jpg (R.1.7).

    Typowe błędy

    Bez kolumny id na liście zapytanie zakończy się błędem „Field id doesn't have a default value” - XAMPP działa w trybie ścisłym.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Kwerenda 1
    CREATE TABLE zamowienia (
        id INT UNSIGNED NOT NULL,
        imie TEXT DEFAULT NULL,
        nazwisko TEXT DEFAULT NULL,
        adres_email TEXT DEFAULT NULL,
        liczba_choinek INT DEFAULT NULL,
        liczba_mikolajow INT DEFAULT NULL,
        liczba_reniferow INT DEFAULT NULL,
        info BOOLEAN DEFAULT NULL
    );
    
    -- Kwerenda 2
    ALTER TABLE zamowienia ADD PRIMARY KEY (id);
    
    -- Kwerenda 3
    CREATE USER 'klient'@'localhost' IDENTIFIED BY 'klient123';
    
    -- Kwerenda 4
    GRANT INSERT ON sklep.* TO 'klient'@'localhost';
    
    -- Kwerenda 5
    INSERT INTO zamowienia (id, imie, nazwisko, adres_email) VALUES (1, 'Jan', 'Kowalski', '[email protected]');
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Krok 8: Eksport tabeli do pliku zamowienia.sql

    Kryteria:

    R.1.1 Wykonano eksport tabeli zamowienia lub bazy sklep do pliku zamowienia.sql. Czas generowania wpisany w nagłówku pliku jest zgodny z datą i godziną egzaminu

    ok. 2 min
    Polecenie z arkusza

    Tabelę zamowienia wyeksportuj do pliku zamowienia.sql

    Kliknij tabelę zamowienia (nie całą bazę), karta Eksport, format SQL, Wykonaj, a pobrany plik zapisz jako zamowienia.sql w folderze zdającego. W pliku jest CREATE TABLE ze strukturą, INSERT INTO z wierszem Jana Kowalskiego i klucz główny. phpMyAdmin wpisuje w nagłówek czas generowania - musi się zgadzać z datą i godziną egzaminu (R.1.1).

    Plik w archiwum tego rozwiązania wyeksportowano z tej samej tabeli narzędziem mysqldump - struktura i dane są takie same, inny jest tylko nagłówek.

    zamowienia.sql SQL nowy plik
    -- MariaDB dump 10.19  Distrib 10.4.27-MariaDB, for Win64 (AMD64)
    --
    -- Host: localhost    Database: sklep
    -- ------------------------------------------------------
    -- Server version	10.4.27-MariaDB
    
    /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
    /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
    /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
    /*!40101 SET NAMES utf8mb4 */;
    /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
    /*!40103 SET TIME_ZONE='+00:00' */;
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    
    --
    -- Table structure for table `zamowienia`
    --
    
    DROP TABLE IF EXISTS `zamowienia`;
    /*!40101 SET @saved_cs_client     = @@character_set_client */;
    /*!40101 SET character_set_client = utf8 */;
    CREATE TABLE `zamowienia` (
      `id` int(10) unsigned NOT NULL,
      `imie` text DEFAULT NULL,
      `nazwisko` text DEFAULT NULL,
      `adres_email` text DEFAULT NULL,
      `liczba_choinek` int(11) DEFAULT NULL,
      `liczba_mikolajow` int(11) DEFAULT NULL,
      `liczba_reniferow` int(11) DEFAULT NULL,
      `info` tinyint(1) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_polish_ci;
    /*!40101 SET character_set_client = @saved_cs_client */;
    
    --
    -- Dumping data for table `zamowienia`
    --
    
    LOCK TABLES `zamowienia` WRITE;
    /*!40000 ALTER TABLE `zamowienia` DISABLE KEYS */;
    INSERT INTO `zamowienia` VALUES (1,'Jan','Kowalski','[email protected]',NULL,NULL,NULL,NULL);
    /*!40000 ALTER TABLE `zamowienia` ENABLE KEYS */;
    UNLOCK TABLES;
    /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
    
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
    /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
    /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
    
    -- Dump completed
    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Etap 2: Grafika i animacja

    Kroki 9-12 · po tym etapie masz 10 z 39 kryteriów (pkt)

    Krok 9: Kadrowanie zdjęć do proporcji 1:1

    ok. 4 min
    Polecenie z arkusza

    Przygotowanie grafiki: obrazy choinka, mikolaj, renifer wykadrowane do proporcji 1:1

    Zdjęcia mają 3872 × 2592 px (proporcje 3:2), a ozdoba wisi mniej więcej na środku. W GIMP-ie: narzędzie Kadrowanie (Shift+C), w opcjach Stały: Proporcje i 1:1, zaznacz obraz od góry do dołu i przesuń kadr na środek, Enter. Kwadrat ma 2592 × 2592 px. Podgląd: żółta ramka kadru na zdjęciu choinki.

    Typowe błędy

    Skalowanie do 300 × 300 bez kadrowania spłaszcza ozdoby - klucz wymaga zachowania proporcji 1:1.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 9
  10. Krok 10: Rozmiar 300 × 300 px

    Kryteria:

    R.2.1 Zapisano przynajmniej jeden obraz z choinka, mikolaj, renifer o rozmiarze 300 px na 300 px (± 2 px, z zachowaniem proporcji 1:1)

    ok. 3 min
    Polecenie z arkusza

    rozmiar grafiki 300 px na 300 px

    Obraz → Skaluj obraz, szerokość 300 px przy zamkniętym łańcuchu - wysokość też będzie 300. Plik → Eksportuj jako choinka.jpg, mikolaj.jpg, renifer.jpg w folderze zdającego (arkusz wymienia je na liście plików na płycie). Trzy obrazy 300 × 300 px dają R.2.1 (klucz dopuszcza ±2 px).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 10
  11. Krok 11: Animacja z trzech obrazów

    Kryteria:

    R.2.2 Utworzono animację animacja.gif składającą się z trzech obrazów

    ok. 3 min
    Polecenie z arkusza

    Przygotowanie animacji o nazwie animacja.gif: wykonana z wcześniej przygotowanych grafik choinka, mikolaj, renifer

    W GIMP-ie animacja to obraz z kilkoma warstwami - każda warstwa jest jedną klatką. Otwórz choinka.jpg, potem Plik → Otwórz jako warstwy i wskaż mikolaj.jpg i renifer.jpg. W oknie Warstwy najniższa warstwa to pierwsza klatka animacji, więc kolejność od dołu: choinka, mikołaj, renifer. Plik → Eksportuj jako animacja.gif - po kliknięciu Eksportuj pojawia się okno opcji GIF, w którym zaznaczasz Jako animację i Pętla (R.2.2).

    Typowe błędy

    Bez zaznaczonego Jako animację GIMP spłaszczy warstwy i zapisze jeden nieruchomy obraz z wierzchnią warstwą.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 11
  12. Krok 12: Zmiana obrazu co 2 sekundy i zrzut interwal.jpg

    Kryteria:

    R.2.3 Obrazy w animacji zmieniają się co 2 sekundy. Ustawiony czas interwału udokumentowano zrzutem interwal.jpg , który zawiera cały obszar ekranu

    ok. 2 min
    Polecenie z arkusza

    obrazy zmieniane co 2 sekundy. Należy wykonać zrzut ekranu dokumentujący ustawienie czasu i zapisać jako plik interwal.jpg. Zrzut powinien zawierać cały obszar ekranu, z widocznym paskiem zadań

    W tym samym oknie opcji GIF pole Opóźnienie między klatkami, gdy nieokreślone podaje się w milisekundach: 2 sekundy to 2000 ms. Zaznacz też Użyj podanego opóźnienia dla wszystkich klatek, żeby GIMP nie wziął czasu z nazw warstw. Zanim klikniesz Eksportuj, zrób zrzut całego ekranu z tym oknem i paskiem zadań i zapisz go jako interwal.jpg (R.2.3).

    Podgląd to odtworzenie okna opcji z ustawionym czasem; plik animacja.gif w zasobach rozwiązania ma trzy klatki po 2000 ms (sprawdzone po zapisie).

    Typowe błędy

    Wpisanie 2 w pole opóźnienia daje 2 milisekundy - klatki migają tak szybko, że animacja jest nieczytelna.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 12
  13. Etap 3: Strona galeria.html

    Kroki 13-25 · po tym etapie masz 18 z 39 kryteriów (pkt)

    Krok 13: Plik galeria.html i kodowanie polskich znaków

    ok. 1 min
    Polecenie z arkusza

    Witryna składa się z podstron galeria.html, zamowienie.php. Wspólne wymagania dla wszystkich podstron: Kodowanie polskich znaków

    Najpierw powstaje cała strona galeria.html, a zamowienie.php będzie jej kopią ze zmienionym panelem środkowym. <meta charset="UTF-8"> na początku <head>, plik zapisany w kodowaniu UTF-8 - wtedy „Zamówienie” i „Wybrałeś choinkę” wyświetlą się poprawnie.

    galeria.html HTML nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  14. Krok 14: Podpięcie arkusza stylów styl.css

    Kryteria:

    R.3.6 Przynajmniej jedna strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css formatowanie pochodzi jedynie od pliku CSS. 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

    Strona połączona z zewnętrznym arkuszem stylów CSS o nazwie styl.css

    <link rel="stylesheet" href="styl.css"> w <head>. Formatowanie ma pochodzić wyłącznie z tego pliku - bez atrybutów style w HTML-u (R.3.6).

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  15. Krok 15: Tytuł strony

    Kryteria:

    R.2.5 Przynajmniej jednej stronie nadano tytuł: "Sklep”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony: Sklep

    <title>Sklep</title> - napis na karcie przeglądarki (R.2.5).

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  16. Krok 16: Bloki: baner, trzy panele i stopka

    Kryteria:

    R.2.6 Układ przynajmniej jednej strony zdefiniowano dzieląc ją na baner, panel lewy, środkowy i prawy oraz stopka za pomocą znaczników sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: baner, panel lewy, środkowy i prawy oraz stopka za pomocą znaczników sekcji

    Baner <header>, stopka <footer>, a trzy panele to znaczniki <section> z identyfikatorami left, middle, right - po nich CSS nada panelom różne kolory i szerokości (R.2.6).

    galeria.html HTML zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
        </header>
        <section id="left">
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  17. Krok 17: Baner: nagłówek pierwszego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści: „Ozdoby – sklep”

    <h1> w banerze. Arkusz pisze półpauzę „–”, obraz 1 i klucz zwykły łącznik „-” - klucz dopuszcza oba zapisy.

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  18. Krok 18: Panel lewy: nagłówek drugiego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość panelu lewego: nagłówek drugiego stopnia o treści: „OZDOBY”

    <h2> wielkimi literami, tak jak w arkuszu - treść wpisujesz wielkimi literami, a nie zamieniasz stylem.

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  19. Krok 19: Odnośnik „Galeria”

    Kryteria:

    R.3.4 Na przynajmniej jednej stronie istnieje odnośnik „Galeria”, który prowadzi do pliku galeria.html

    ok. 1 min
    Polecenie z arkusza

    odnośnik „Galeria” prowadzący do pliku galeria.html

    href="galeria.html" - ścieżka względna, sama nazwa pliku z tego samego folderu (klucz wymaga ścieżki względnej). Na stronie galerii odnośnik prowadzi do niej samej - to wspólne menu obu stron. <br> przenosi kolejny odnośnik do nowej linii (R.3.4).

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  20. Krok 20: Odnośnik „Zamówienie”

    Kryteria:

    R.3.5 Na przynajmniej jednej stronie istnieje odnośnik „Zamówienie”, który prowadzi do pliku zamowienie.php

    ok. 1 min
    Polecenie z arkusza

    poniżej odnośnik „Zamówienie” prowadzący do pliku zamowienie.php

    Plik zamowienie.php - bez polskich liter w nazwie, napis odnośnika - z nimi (R.3.5). Strona PHP działa tylko otwarta przez http://localhost/….

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  21. Krok 21: Panel prawy: animacja

    Kryteria:

    R.2.9 W bloku prawym umieszczono plik animacja.gif

    ok. 1 min
    Polecenie z arkusza

    Zawartość panelu prawego: animacja.gif

    Animowany GIF wstawia się zwykłym <img> - przeglądarka sama odtwarza klatki (R.2.9). Arkusz nie podaje tekstu alternatywnego, ale alt należy do poprawnego obrazu.

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  22. Krok 22: Stopka: autor strony

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: nagłówek trzeciego stopnia o treści: „Autor strony:”, dalej wstawiony numer PESEL zdającego

    <h3> w stopce. Na egzaminie po dwukropku wpisujesz swój numer PESEL; tu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski.

    ⚠️ Rozbieżność treść-ilustracja: na obrazie 1 w stopce widać „Stronę opracował: 00000000000”, a treść arkusza i klucz (R.2.7) wymagają „Autor strony:”. Obowiązuje treść i klucz.

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  23. Krok 23: Panel środkowy: trzy ozdoby obok siebie

    Kryteria:

    R.2.8 W bloku środkowym strony galeria.html umieszczono obrazy choinka, mikolaj, renifer z tekstem alternatywnym „ozdoby choinkowe”

    ok. 2 min
    Polecenie z arkusza

    Zawartość panelu środkowego dla strony galeria.html: Obrazy choinka, mikolaj, renifer z tekstem alternatywnym „ozdoby choinkowe”, umieszczone obok siebie

    Trzy <img> jeden po drugim stoją obok siebie, bo obraz jest elementem liniowym. Wszystkie mają ten sam tekst alternatywny „ozdoby choinkowe” (R.2.8). Na razie mają 300 px - CSS zmniejszy je do kółek 150 px.

    galeria.html HTML zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <img src="choinka.jpg" alt="ozdoby choinkowe">
            <img src="mikolaj.jpg" alt="ozdoby choinkowe">
            <img src="renifer.jpg" alt="ozdoby choinkowe">
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  24. Krok 24: Paragraf „Wybierz ozdobę w kółku”

    Kryteria:

    R.2.7 Na przynajmniej jednej stronie zapisano nagłówki: w banerze h1: "Ozdoby - sklep”, w bloku lewym h2 "OZDOBY", w stopce h3: "Autor strony" i dalej wstawiony numer PESEL zdającego oraz zapisano w bloku środkowym paragraf (znacznik <p>)

    ok. 1 min
    Polecenie z arkusza

    Paragraf (akapit) o treści: „Wybierz ozdobę w kółku”

    Paragraf <p> pod obrazami. Nagłówki h1, h2, h3 i paragraf w bloku środkowym dają R.2.7.

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <img src="choinka.jpg" alt="ozdoby choinkowe">
            <img src="mikolaj.jpg" alt="ozdoby choinkowe">
            <img src="renifer.jpg" alt="ozdoby choinkowe">
            <p>Wybierz ozdobę w kółku</p>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  25. Krok 25: Pusty paragraf na komunikat skryptu

    ok. 1 min
    Polecenie z arkusza

    Efekt działania skryptu 1 umieszczony w kolejnym paragrafie

    Drugi paragraf zostaje pusty - wypełni go skrypt po wskazaniu ozdoby kursorem. Identyfikator id="info" pozwoli skryptowi go odnaleźć.

    galeria.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <img src="choinka.jpg" alt="ozdoby choinkowe">
            <img src="mikolaj.jpg" alt="ozdoby choinkowe">
            <img src="renifer.jpg" alt="ozdoby choinkowe">
            <p>Wybierz ozdobę w kółku</p>
            <p id="info"></p>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  26. Etap 4: Strona zamowienie.php

    Kroki 26-30 · po tym etapie masz 22 z 39 kryteriów (pkt)

    Krok 26: Plik zamowienie.php: kopia galerii z nowym panelem środkowym

    Kryteria:

    R.2.4 Witrynę internetową zapisano w plikach galeria.html, zamowienie.php oraz z astosowano właściwy standard kodowania polskich znaków na przynajmniej jednej stronie

    ok. 2 min
    Polecenie z arkusza

    Zawartość panelu środkowego dla strony zamowienie.php: Paragraf o treści: „Dodaj użytkownika”

    Kopiujesz galeria.html do pliku zamowienie.php - baner, panel lewy, animacja i stopka są wspólne. W panelu środkowym zamiast obrazów i dwóch paragrafów zostaje jeden paragraf „Dodaj użytkownika”. Obie strony mają nazwy z arkusza i kodowanie UTF-8 (R.2.4).

    zamowienie.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.php)
  27. Krok 27: Formularz wysyłający dane metodą post

    ok. 1 min
    Polecenie z arkusza

    Formularz: Dane wysyłane metodą post do tego samego pliku

    method="post" - dane jadą w treści żądania, a action="zamowienie.php" - do tego samego pliku. Po wysłaniu serwer wykona skrypt PHP z tej strony.

    zamowienie.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
            </form>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.php)
  28. Krok 28: Pola Imię i Nazwisko

    ok. 2 min
    Polecenie z arkusza

    Pola edycyjne i podpisy zgodne z Obrazem 2

    Podpis i pole w jednej linii, <br> po każdym polu - jak na obrazie 2. Atrybut name to nazwa, pod którą skrypt odczyta wartość: $_POST['name'], $_POST['surname'].

    zamowienie.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
            </form>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.php)
  29. Krok 29: Pole e-mail sprawdzane przez przeglądarkę

    Kryteria:

    R.3.3 Pole edycyjne do wpisania adresu email jest typu email

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne do wprowadzenia adresu email jest odpowiedniego typu tak, aby poprawność adresu była sprawdzana automatycznie

    type="email" - przed wysłaniem przeglądarka sprawdza, czy wpis wygląda jak adres e-mail; bez znaku @ pokaże dymek „Uwzględnij znak @ w adresie e-mail” i nie wyśle formularza, jak na obrazie 2 (R.3.3).

    zamowienie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
                e-mail: <input type="email" name="email"><br>
            </form>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.php)
  30. Krok 30: Przycisk WYŚLIJ

    Kryteria: ,

    R.2.10 W bloku środkowym strony zamowienie.php umieszczono formularz z trzema polami edycyjnymi i przyciskiem WYŚLIJ

    R.3.2 Dane z formularza przesyłane są za pomocą metody POST, zastosowano przycisk do wysyłania danych z formularza

    ok. 1 min
    Polecenie z arkusza

    Przycisk WYŚLIJ wysyła dane z formularza

    type="submit" wysyła formularz. Trzy pola i przycisk w panelu środkowym dają R.2.10, a metoda POST z przyciskiem wysyłającym - R.3.2.

    zamowienie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
                e-mail: <input type="email" name="email"><br>
                <input type="submit" value="WYŚLIJ">
            </form>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.php)
  31. Etap 5: Styl CSS

    Kroki 31-37 · po tym etapie masz 32 z 39 kryteriów (pkt)

    Krok 31: Plik styl.css: krój, kolor i wyśrodkowanie

    Kryteria:

    R.4.1 Ustawiono formatowanie dla * lub html, lub body, lub kontenera całej strony: czcionka Garamond białego koloru, wyśrodkowana

    ok. 1 min
    Polecenie z arkusza

    Styl CSS zawiera formatowanie: domyślnie: czcionka Garamond koloru białego, tekst wyśrodkowany

    Reguła na body - dziedziczą ją wszystkie elementy (R.4.1). Garamond jest w Windows; gdyby go nie było, przeglądarka użyje kroju domyślnego.

    styl.css CSS nowy plik
    body {
        font-family: Garamond;
        color: white;
        text-align: center;
    }
    Podgląd strony po tym kroku (galeria.html)
  32. Krok 32: Baner i stopka: tło i margines wewnętrzny

    Kryteria:

    R.4.6 Ustawiono wszystkie marginesy wewnętrzne (padding) dla banera i stopki na 10px

    ok. 1 min
    Polecenie z arkusza

    dla banera i stopki: kolor tła ForestGreen, margines wewnętrzny 10 px

    Jedna reguła dla dwóch selektorów oddzielonych przecinkiem. Margines wewnętrzny to padding; padding: 10px ustawia go z czterech stron naraz (R.4.6).

    styl.css CSS zmienione: 4 linie
    body {
        font-family: Garamond;
        color: white;
        text-align: center;
    }
    header, footer {
        background-color: ForestGreen;
        padding: 10px;
    }
    Podgląd strony po tym kroku (galeria.html)
  33. Krok 33: Panel lewy i prawy

    ok. 1 min
    Polecenie z arkusza

    dla panelu lewego i prawego: kolor tła ForestGreen, wysokość 490 px, szerokość: 25%, rozmiar czcionki 30 px

    Selektory identyfikatorów #left, #right z krzyżykiem - w HTML-u są id. Dwa panele mają identyczny wygląd, więc jedna reguła.

    Typowe błędy

    Kropka zamiast krzyżyka (.left) nie trafi w panel - a klucz przy R.3.6 uznaje wtedy połączenie ze stylem za niespełnione.

    styl.css CSS zmienione: 6 linii
    body {
        font-family: Garamond;
        color: white;
        text-align: center;
    }
    header, footer {
        background-color: ForestGreen;
        padding: 10px;
    }
    #left, #right {
        background-color: ForestGreen;
        height: 490px;
        width: 25%;
        font-size: 30px;
    }
    Podgląd strony po tym kroku (galeria.html)
  34. Krok 34: Panel środkowy

    Kryteria: , , ,

    R.4.2 Ustawiono kolor tła dla panelu środkowego na Chocolate, dla pozostałych: ForestGreen

    R.4.3 Ustawiono wysokość trzech paneli na 490px

    R.4.4 Ustawiono szerokość panelu lewego i prawego na 25%, środkowego na 50%

    R.4.5 Ustawiono rozmiar czcionki dla panelu lewego i prawego na 30px, środkowego na 25px

    ok. 1 min
    Polecenie z arkusza

    dla panelu środkowego: kolor tła Chocolate, wysokość 490 px, szerokość: 50%, rozmiar czcionki 25 px

    Kolory tła (R.4.2), wysokości 490 px (R.4.3), szerokości 25% + 50% + 25% = 100% (R.4.4) i rozmiary czcionek 30/25/30 px (R.4.5) są już ustawione.

    styl.css CSS zmienione: 6 linii
    body {
        font-family: Garamond;
        color: white;
        text-align: center;
    }
    header, footer {
        background-color: ForestGreen;
        padding: 10px;
    }
    #left, #right {
        background-color: ForestGreen;
        height: 490px;
        width: 25%;
        font-size: 30px;
    }
    #middle {
        background-color: Chocolate;
        height: 490px;
        width: 50%;
        font-size: 25px;
    }
    Podgląd strony po tym kroku (galeria.html)
  35. Krok 35: Ozdoby w kółkach 150 × 150 px

    Kryteria: ,

    R.4.7 Ustawiono styl wyłącznie dla obrazów z panelu środkowego: wysokość i szerokość 150px

    R.4.8 Ustawiono zaokrąglenie rogów obrazu (border-radius) 100% (lub wartości >= 50%)

    ok. 1 min
    Polecenie z arkusza

    wyłącznie dla obrazów z panelu środkowego: rozmiar 150 px na 150 px, zaokrąglenie rogów obrazu 100%

    Selektor potomka #middle img - tylko obrazy z panelu środkowego; animacja w panelu prawym zostaje 300 px (R.4.7). border-radius: 100% zaokrągla rogi tak mocno, że kwadrat staje się kołem - już 50% wystarcza, a klucz przyjmuje każdą wartość od 50% (R.4.8). Stąd „ozdoba w kółku” z treści strony.

    Typowe błędy

    Sam selektor img zmniejszy też animację w panelu prawym - kryterium mówi „wyłącznie dla obrazów z panelu środkowego”.

    styl.css CSS zmienione: 5 linii
    body {
        font-family: Garamond;
        color: white;
        text-align: center;
    }
    header, footer {
        background-color: ForestGreen;
        padding: 10px;
    }
    #left, #right {
        background-color: ForestGreen;
        height: 490px;
        width: 25%;
        font-size: 30px;
    }
    #middle {
        background-color: Chocolate;
        height: 490px;
        width: 50%;
        font-size: 25px;
    }
    #middle img {
        width: 150px;
        height: 150px;
        border-radius: 100%;
    }
    Podgląd strony po tym kroku (galeria.html)
  36. Krok 36: Zielony tekst w polach i na przycisku

    Kryteria:

    R.4.9 Ustawiono zielony kolor czcionki dla wszystkich elementów formularza

    ok. 1 min
    Polecenie z arkusza

    dla selektora pola edycyjnego i przycisku: zielony kolor czcionki

    Pola tekstowe, pole e-mail i przycisk to elementy <input>, więc jeden selektor input obejmuje wszystkie (R.4.9). color dotyczy tekstu wpisywanego w pole i napisu WYŚLIJ.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: Garamond;
        color: white;
        text-align: center;
    }
    header, footer {
        background-color: ForestGreen;
        padding: 10px;
    }
    #left, #right {
        background-color: ForestGreen;
        height: 490px;
        width: 25%;
        font-size: 30px;
    }
    #middle {
        background-color: Chocolate;
        height: 490px;
        width: 50%;
        font-size: 25px;
    }
    #middle img {
        width: 150px;
        height: 150px;
        border-radius: 100%;
    }
    input {
        color: green;
    }
    Podgląd strony po tym kroku (zamowienie.php)
  37. Krok 37: Układ trzech paneli obok siebie

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest w pełni zgodny z obrazem 1 w arkuszu

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: baner, panel lewy, środkowy i prawy oraz stopka za pomocą znaczników sekcji

    float: left na sekcjach ustawia trzy panele obok siebie w kolejności z kodu: lewy, środkowy, prawy - razem 100% szerokości. Stopka z clear: both zaczyna się pod nimi. Układ odpowiada obrazowi 1 (R.3.1), także na stronie zamowienie.php, która korzysta z tego samego pliku stylów.

    Typowe błędy

    Panele mają razem dokładnie 100% - obramowanie albo boczny padding choćby na jednym z nich zepchnie panel prawy pod spód.

    styl.css CSS zmienione: 6 linii
    body {
        font-family: Garamond;
        color: white;
        text-align: center;
    }
    header, footer {
        background-color: ForestGreen;
        padding: 10px;
    }
    #left, #right {
        background-color: ForestGreen;
        height: 490px;
        width: 25%;
        font-size: 30px;
    }
    #middle {
        background-color: Chocolate;
        height: 490px;
        width: 50%;
        font-size: 25px;
    }
    #middle img {
        width: 150px;
        height: 150px;
        border-radius: 100%;
    }
    input {
        color: green;
    }
    section {
        float: left;
    }
    footer {
        clear: both;
    }
    Podgląd strony po tym kroku (galeria.html)
  38. Etap 6: Skrypt JavaScript

    Kroki 38-40 · po tym etapie masz 35 z 39 kryteriów (pkt)

    Krok 38: Funkcja i komunikat dla choinki

    Kryteria:

    R.5.1 Napisano skrypt dla strony galeria.html w języku JavaScript, który zawiera przynajmniej jedną instrukcję zgodną ze składnią języka

    ok. 3 min
    Polecenie z arkusza

    Skrypt 1 ze strony galeria.html: Skrypt jest uruchamiany po stronie przeglądarki

    Skrypt w znaczniku <script> na końcu <body> - przeglądarka wykona go, gdy strona jest już wczytana (R.5.1). Funkcja selectOrnament(number) dostaje numer ozdoby i pod warunkiem if wpisuje komunikat do paragrafu: document.getElementById("info") odnajduje paragraf po identyfikatorze, a przypisanie do innerHTML zmienia jego treść. Na razie funkcja zna tylko choinkę.

    galeria.html HTML zmienione: 7 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <img src="choinka.jpg" alt="ozdoby choinkowe">
            <img src="mikolaj.jpg" alt="ozdoby choinkowe">
            <img src="renifer.jpg" alt="ozdoby choinkowe">
            <p>Wybierz ozdobę w kółku</p>
            <p id="info"></p>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
        <script>
            function selectOrnament(number) {
                if (number == 1) {
                    document.getElementById("info").innerHTML = "Wybrałeś choinkę. Cena 10 zł";
                }
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  39. Krok 39: Wywołanie funkcji po wskazaniu obrazu kursorem

    Kryteria:

    R.5.3 W skrypcie 1 istnieje funkcja (funkcje) wywoływana po wskazaniu kursorem każdego obrazu z panelu środkowego

    ok. 2 min
    Polecenie z arkusza

    Po wskazaniu kursorem obrazu w panelu środkowym jest wyświetlany komunikat w paragrafie przeznaczonym dla tej akcji (obraz 1)

    Zdarzenie „kursor wjechał na element” to mouseover; atrybut onmouseover przy każdym obrazie wywołuje funkcję z numerem ozdoby: 1 - choinka, 2 - mikołaj, 3 - renifer (R.5.3). Sprawdzone w Chrome: po najechaniu na choinkę w paragrafie pojawia się „Wybrałeś choinkę. Cena 10 zł”, a dwie pozostałe ozdoby jeszcze nic nie robią. Komunikat zostaje po zjechaniu kursorem - arkusz nie każe go czyścić.

    galeria.html HTML zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <img src="choinka.jpg" alt="ozdoby choinkowe" onmouseover="selectOrnament(1)">
            <img src="mikolaj.jpg" alt="ozdoby choinkowe" onmouseover="selectOrnament(2)">
            <img src="renifer.jpg" alt="ozdoby choinkowe" onmouseover="selectOrnament(3)">
            <p>Wybierz ozdobę w kółku</p>
            <p id="info"></p>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
        <script>
            function selectOrnament(number) {
                if (number == 1) {
                    document.getElementById("info").innerHTML = "Wybrałeś choinkę. Cena 10 zł";
                }
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  40. Krok 40: Komunikaty dla Mikołaja i renifera

    Kryteria:

    R.5.4 Skrypt 1 wyświetla pod obrazami w paragrafie tekst dla choinki: "Wybrałeś choinkę. Cena 10 zł"; dla Mikołaja: "Wybrałeś Mikołaja. Cena 12 zł"; dla renifera: „Wybrałeś renifera. Cena 8 zł”

    ok. 3 min
    Polecenie z arkusza

    wybór choinki: „Wybrałeś choinkę. Cena 10 zł”; wybór Mikołaja: „Wybrałeś mikołaja. Cena 12 zł”; wybór renifera: „Wybrałeś renifera. Cena 8 zł”

    Dwie gałęzie else if - dla numeru 2 i 3. Sprawdzone w Chrome dla każdego obrazu: trzy różne komunikaty w paragrafie pod ozdobami (R.5.4). Podgląd pokazuje stan z obrazu 1 - kursor na mikołaju.

    Arkusz pisze „Wybrałeś mikołaja”, a klucz „Wybrałeś Mikołaja” - klucz dopuszcza błędy wielkości liter, a wielka litera pasuje do sposobu, w jaki arkusz pisze o tej ozdobie („wybór Mikołaja”).

    Typowe błędy

    Napis w JavaScript w cudzysłowach, a polskie litery w pliku zapisanym w UTF-8 - inaczej zamiast „zł” pojawią się krzaki.

    galeria.html HTML zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <img src="choinka.jpg" alt="ozdoby choinkowe" onmouseover="selectOrnament(1)">
            <img src="mikolaj.jpg" alt="ozdoby choinkowe" onmouseover="selectOrnament(2)">
            <img src="renifer.jpg" alt="ozdoby choinkowe" onmouseover="selectOrnament(3)">
            <p>Wybierz ozdobę w kółku</p>
            <p id="info"></p>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
        <script>
            function selectOrnament(number) {
                if (number == 1) {
                    document.getElementById("info").innerHTML = "Wybrałeś choinkę. Cena 10 zł";
                } else if (number == 2) {
                    document.getElementById("info").innerHTML = "Wybrałeś Mikołaja. Cena 12 zł";
                } else if (number == 3) {
                    document.getElementById("info").innerHTML = "Wybrałeś renifera. Cena 8 zł";
                }
            }
        </script>
    </body>
    </html>
    Podgląd strony po tym kroku (galeria.html)
  41. Etap 7: Skrypt PHP

    Kroki 41-45 · po tym etapie masz 39 z 39 kryteriów (pkt)

    Krok 41: Miejsce skryptu i połączenie z bazą sklep

    Kryteria: ,

    R.5.2 Napisano skrypt dla formularza w języku PHP, który zawiera przynajmniej jedną instrukcję zgodną ze składnią języka

    R.5.5 W skrypcie 2 zastosowano funkcję realizującą połączenie z serwerem bazy danych na localhost, użytkownik root, bez hasła, baza danych sklep

    ok. 3 min
    Polecenie z arkusza

    Skrypt 2 uruchamiany po wysłaniu danych z formularza na stronie zamowienie.php: Napisany w języku PHP. Skrypt łączy się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie sklep

    Blok <?php … ?> stoi w pliku zamowienie.php zaraz za formularzem (R.5.2). Pierwsza instrukcja to mysqli_connect z tabeli 1 arkusza: serwer localhost, użytkownik root, puste hasło '', baza sklep - połączenie trafia do zmiennej $connection (R.5.5).

    Skrypt łączy się jako root, bo tego wymaga arkusz, choć do zapisu zamówień wystarczyłoby konto klient z kwerendy 3 - na prawdziwym serwerze strona łączyłaby się właśnie nim. Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2.

    zamowienie.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
                e-mail: <input type="email" name="email"><br>
                <input type="submit" value="WYŚLIJ">
            </form>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.php)
  42. Krok 42: Odczyt danych z formularza

    ok. 2 min
    Polecenie z arkusza

    Tworzy w tabeli zamowienia bazy danych nowy rekord z danymi przesłanymi formularzem (zmodyfikowane zapytanie 5)

    • isset($_POST['name']) jest prawdziwe dopiero po wysłaniu formularza - przy zwykłym wejściu na stronę skrypt niczego nie zapisze,
    • trzy wartości z pól o nazwach name, surname, email trafiają do zmiennych.

    Podgląd: wysłany formularz z danymi Anny Nowak - na razie nic nie trafia do bazy.

    Typowe błędy

    Odczyt $_POST['name'] bez isset przy pierwszym wejściu na stronę daje ostrzeżenie „Undefined array key”, a pusty wiersz trafiłby do bazy.

    zamowienie.php PHP zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
                e-mail: <input type="email" name="email"><br>
                <input type="submit" value="WYŚLIJ">
            </form>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    if (isset($_POST['name'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $email = $_POST['email'];
    }
    ?>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 41.

  43. Krok 43: Kolejny numer zamówienia

    ok. 3 min
    Polecenie z arkusza

    Tworzy w tabeli zamowienia bazy danych nowy rekord z danymi przesłanymi formularzem (zmodyfikowane zapytanie 5)

    Kolumna id nie jest autonumerowana (arkusz tego nie wymagał), a formularz nie ma pola na numer - skrypt musi go wyliczyć sam. SELECT MAX(id) zwraca największy numer w tabeli, mysqli_fetch_row - wiersz wyniku jako tablicę, a $row[0] + 1 - kolejny wolny numer: po zamówieniu Jana Kowalskiego (1) będzie 2. W pustej tabeli MAX(id) zwraca NULL, a null + 1 w PHP daje 1 - też dobrze.

    Gdyby skrypt przepisał z kwerendy 5 numer 1 na sztywno, pierwsze wysłanie formularza skończyłoby się błędem „Duplicate entry '1' for key 'PRIMARY'”.

    zamowienie.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
                e-mail: <input type="email" name="email"><br>
                <input type="submit" value="WYŚLIJ">
            </form>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    if (isset($_POST['name'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $email = $_POST['email'];
        $result = mysqli_query($connection, "SELECT MAX(id) FROM zamowienia;");
        $row = mysqli_fetch_row($result);
        $id = $row[0] + 1;
    }
    ?>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 41.

  44. Krok 44: Zapytanie 5 z danymi z formularza

    Kryteria:

    R.5.7 Skrypt 2 wysyła do bazy danych zapytanie 5 zmodyfikowane w ten sposób, że odpowiednimi wartościami pól są dane pobrane z formularza

    ok. 3 min
    Polecenie z arkusza

    Tworzy w tabeli zamowienia bazy danych nowy rekord z danymi przesłanymi formularzem (zmodyfikowane zapytanie 5)

    Kwerenda 5 z kwerendy.txt, w której w miejscu danych Jana Kowalskiego stoją zmienne: numer $id bez apostrofów (liczba), a '$name', '$surname', '$email' w apostrofach (tekst). Zapytanie jest w cudzysłowie "…", więc PHP podstawia wartości zmiennych (R.5.7).

    Sprawdzone: po wysłaniu formularza z danymi Anny Nowak tabela ma dwa wiersze - Jan Kowalski (1) i Anna Nowak (2), a pozostałe kolumny mają NULL.

    zamowienie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
                e-mail: <input type="email" name="email"><br>
                <input type="submit" value="WYŚLIJ">
            </form>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    if (isset($_POST['name'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $email = $_POST['email'];
        $result = mysqli_query($connection, "SELECT MAX(id) FROM zamowienia;");
        $row = mysqli_fetch_row($result);
        $id = $row[0] + 1;
        mysqli_query($connection, "INSERT INTO zamowienia (id, imie, nazwisko, adres_email) VALUES ($id, '$name', '$surname', '$email');");
    }
    ?>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 41.

  45. Krok 45: Zamknięcie połączenia

    Kryteria:

    R.5.6 Po wykonaniu wszystkich operacji na bazie zastosowano funkcję zamknięcia połączenia z serwerem bazy danych

    ok. 1 min
    Polecenie z arkusza

    Po wykonaniu operacji na bazie danych skrypt zamyka połączenie z serwerem

    mysqli_close($connection) na końcu bloku, poza warunkiem - wykona się po każdym wejściu na stronę (R.5.6). Strona spełnia wszystkie kryteria arkusza; dalej dwa kroki o zabezpieczeniach, których klucz nie sprawdza.

    zamowienie.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
                e-mail: <input type="email" name="email"><br>
                <input type="submit" value="WYŚLIJ">
            </form>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    if (isset($_POST['name'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $email = $_POST['email'];
        $result = mysqli_query($connection, "SELECT MAX(id) FROM zamowienia;");
        $row = mysqli_fetch_row($result);
        $id = $row[0] + 1;
        mysqli_query($connection, "INSERT INTO zamowienia (id, imie, nazwisko, adres_email) VALUES ($id, '$name', '$surname', '$email');");
    }
    mysqli_close($connection);
    ?>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 41.

  46. Etap 8: Dobre praktyki (poza kluczem)

    Kroki 46-47 · po tym etapie masz 39 z 39 kryteriów (pkt)

    Krok 46: Zabezpieczenie: zapytanie przygotowane

    ok. 4 min
    Polecenie z arkusza

    Tworzy w tabeli zamowienia bazy danych nowy rekord z danymi przesłanymi formularzem (zmodyfikowane zapytanie 5)

    Dane z formularza trafiają do zapytania wprost - to SQL injection. Sprawdzone na wersji z poprzednich kroków:

    • wpis w polu Imię x', 'y', '[email protected]'), (999, 'Haker', 'Haker', '[email protected]') -- zamyka listę wartości i dopisuje drugi wiersz z numerem 999; -- zamienia resztę oryginalnego zapytania w komentarz. Podgląd: tabela po takim wysłaniu,
    • nazwisko z apostrofem, np. O'Neill, rozbija składnię - PHP 8.1 i nowsze zgłaszają wyjątek mysqli_sql_exception i strona kończy się błędem „Fatal error”.

    Lekarstwo to zapytanie przygotowane: mysqli_prepare z czterema znakami ?, mysqli_stmt_bind_param z typami 'isss' (liczba i trzy napisy) i mysqli_stmt_execute. Baza dostaje treść zapytania i dane osobno, więc dane nigdy nie staną się kodem SQL. Sprawdzone po zmianie: ten sam wpis zapisuje się jako zwykłe (dziwne) imię w jednym wierszu, a O'Neill zapisuje się poprawnie. Stara linia zostaje w komentarzu.

    Typowe błędy

    Liczba liter w 'isss' musi równać się liczbie znaków ? i liczbie zmiennych.

    zamowienie.php PHP zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
                e-mail: <input type="email" name="email"><br>
                <input type="submit" value="WYŚLIJ">
            </form>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    if (isset($_POST['name'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $email = $_POST['email'];
        $result = mysqli_query($connection, "SELECT MAX(id) FROM zamowienia;");
        $row = mysqli_fetch_row($result);
        $id = $row[0] + 1;
        // mysqli_query($connection, "INSERT INTO zamowienia (id, imie, nazwisko, adres_email) VALUES ($id, '$name', '$surname', '$email');");
        $statement = mysqli_prepare($connection, 'INSERT INTO zamowienia (id, imie, nazwisko, adres_email) VALUES (?, ?, ?, ?);');
        mysqli_stmt_bind_param($statement, 'isss', $id, $name, $surname, $email);
        mysqli_stmt_execute($statement);
    }
    mysqli_close($connection);
    ?>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.php)
    Podgląd strony po kroku 46
  47. Krok 47: Zabezpieczenie: sprawdzenie danych na serwerze

    ok. 3 min
    Polecenie z arkusza

    Pole edycyjne do wprowadzenia adresu email jest odpowiedniego typu tak, aby poprawność adresu była sprawdzana automatycznie

    type="email" sprawdza adres w przeglądarce - wygoda dla użytkownika, nie zabezpieczenie. Żądanie POST można wysłać z pominięciem formularza (np. programem curl), i wersja podstawowa zapisała wtedy w bazie zamówienie z adresem nie-email. Dane trzeba więc sprawdzić na serwerze:

    • trim() zdejmuje spacje z brzegów, a ?? '' daje pusty napis, gdy pola w ogóle nie wysłano (zamiast isset),
    • filter_var(…, FILTER_VALIDATE_EMAIL) zwraca adres, gdy jest poprawny, albo false,
    • zapis następuje tylko wtedy, gdy imię i nazwisko nie są puste, a adres jest poprawny.

    Sprawdzone: puste imię i adres nie-email nie trafiają do bazy, a „ Anna ” ze spacjami zapisuje się jako „Anna”. Stare linie zostają w komentarzu; na egzaminie wystarczy wersja z kroku „Zamknięcie połączenia”.

    zamowienie.php PHP zmienione: 8 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <link rel="stylesheet" href="styl.css">
        <title>Sklep</title>
    </head>
    <body>
        <header>
            <h1>Ozdoby - sklep</h1>
        </header>
        <section id="left">
            <h2>OZDOBY</h2>
            <a href="galeria.html">Galeria</a><br>
            <a href="zamowienie.php">Zamówienie</a>
        </section>
        <section id="middle">
            <p>Dodaj użytkownika</p>
            <form method="post" action="zamowienie.php">
                Imię: <input type="text" name="name"><br>
                Nazwisko: <input type="text" name="surname"><br>
                e-mail: <input type="email" name="email"><br>
                <input type="submit" value="WYŚLIJ">
            </form>
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    // if (isset($_POST['name'])) {
    //     $name = $_POST['name'];
    //     $surname = $_POST['surname'];
    //     $email = $_POST['email'];
    $name = trim($_POST['name'] ?? '');
    $surname = trim($_POST['surname'] ?? '');
    $email = filter_var(trim($_POST['email'] ?? ''), FILTER_VALIDATE_EMAIL);
    if ($name !== '' && $surname !== '' && $email !== false) {
        $result = mysqli_query($connection, "SELECT MAX(id) FROM zamowienia;");
        $row = mysqli_fetch_row($result);
        $id = $row[0] + 1;
        // mysqli_query($connection, "INSERT INTO zamowienia (id, imie, nazwisko, adres_email) VALUES ($id, '$name', '$surname', '$email');");
        $statement = mysqli_prepare($connection, 'INSERT INTO zamowienia (id, imie, nazwisko, adres_email) VALUES (?, ?, ?, ?);');
        mysqli_stmt_bind_param($statement, 'isss', $id, $name, $surname, $email);
        mysqli_stmt_execute($statement);
    }
    mysqli_close($connection);
    ?>
        </section>
        <section id="right">
            <img src="animacja.gif" alt="Animacja z ozdobami">
        </section>
        <footer>
            <h3>Autor strony: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamowienie.php)
  48. Etap 9: Oddanie pracy

    Krok 48 · po tym etapie masz 39 z 39 kryteriów (pkt)

    Krok 48: Plik przeglądarka.txt i nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    UWAGA: Po zakończeniu pracy utwórz w folderze z numerem PESEL plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz strony. W folderze z numerem zdającego muszą być: animacja.gif, choinka.jpg, galeria.html, kw5.jpg, kwerendy.txt, interwal.jpg, mikolaj.jpg, przeglądarka.txt, renifer.jpg, styl.css, zamowienia.sql, zamowienie.php. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    Typowe błędy

    Płyta, której nie da się odczytać, to zero punktów za wszystkie rezultaty - sprawdź ją, zanim oddasz.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

    Podgląd strony bez zmian - taki jak po kroku 47.

Podgląd

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: . 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.

Potwierdzenie

Informacja