1. Projekt Konceptualny
Przedmiotem projektu jest portal spełniający rolę czytnika kanałów RSS. Czytnik udostępniany użytkownikom zostanie w modelu Software As A Service.
Kanały RSS to dokumenty w formacie XML, zawierające informacje o wpisach na blogach, czy listach artykułów na stronach internetowych. W formie tej udostępniane mogą być wszelkiego rodzaju informacje mające charakter strumieniowego przekazywania danych, jak komentarze, wydarzenia.
Specyficznym elementem tego czytnika będzie jego aktywność. Posiadać on będzie dodatkową logikę pozwalającą na wykonywanie przez aplikacji akcji niezależnie od obecności na portalu użytkownika. Na podstawie filtrów zdefiniowanych przez użytkownika artykuły mogą być ignorowane z jego listy, lub użytkownik może być informowany o ich pojawieniu się kanałami komunikacji bezpośredniej (jak Email czy SMS).
— Rafał Trójniak
1.2. Analiza stanu wyjściowego
Na rynku znajduje się wiele czytników kanałów RSS. Można je znaleźć w formie wtyczek do popularnych przeglądarek, czy klientów poczty elektronicznej. Działają również jako widgety na pulpit oraz aplikacje stand-alone. Istnieje również grupa czytników działających w postaci stron internetowych.
Wiele z tych czytników pozwala na okresowe sprawdzanie kanałów i powiadamianie o nowych wpisach. Nie znalazłem jednak czytnika, który mógłby pozwalać na zastosowanie powiadomienia kanałem aktywnym (jak mail czy SMS), oraz pozwalającego na definicję filtrów.
Interesującym aspektem może być również wykorzystanie w przyszłości powyższych mechanizmów w połączeniu z panującą aktualnie modą na tworzenie rozwiązań społecznościowych.
— Rafał Trójniak
1.3. Analiza wymagań użytkownika (wstępna)
Funkcjonalności :
Funkcjami bazy danych muszą być :
Przechowywanie bazy kanałów i wpisów
Możliwość analizy w celu wyszukiwania wpisów należących do użytkownika
Możliwość aktualizacji stanu wpisów
Możliwość archiwizacji starych wpisów
— Rafał Trójniak
1.4. Określenie scenariuszy użycia
Rejestracja
Użytkownik klika link Register
Zostaje przekierowany na stronę Rejestracji
Użytkownik musi wprowadzić wymagane dane: adres email, hasło, imie, nazwisko
Potwierdzenie wprowadzonych danych poprzez przycisk register
Weryfikacja wprowadzonych danych(czy dany adres e-mail nie jest zarejestrowany, czy wszystkie dane zostały wprowadzone)
Przekierowanie użytkownika do głównej strony serwisu po pozytywnym przejściu rejestracji
Logowanie
Użytkownik wybiera link do logowania
Zostaje przekierowany na podstronę logowania
Wprowadza dane: adres e-mail oraz hasło
Wybiera przycisk logowania
Następuje weryfikacja:
Sprawdzane jest czy dany adres email istnieje już w bazie
Sprawdzane jest czy dany adres email pasuje z podanym hasłem
W innym przypadku występuje powiadomienie o niepowodzeniu, oraz jego prawdopodobnej przyczynie
Użytkownik zostaje przekierowany do głównego panelu aplikacji
Modyfikacja danych użytkownika
Użytkownik wybiera Change User Data
Do każdorazowej zmiany potrzebne jest podanie aktualnego hasła
Użytkownik zmienia swoje dane - ma do wyboru imię, nazwisko, oraz hasło
Następnie użytkownik musi wybrać przycisk Change data
Następuje weryfikacja danych
W zależności od wprowadzonych danych aplikacja może wyświetlić uwagi co do odpowiednich pól
W przypadku powodzenia użytkownik zostaje powiadomiony o pomyślnej zmianie danych
Dodawanie nowego kanału RSS
Wybranie z menu pozycji Add New Channel
Pojawia sie pole w które należy wpisać adres kanału RSS
Po wprowadzeniu należy wybrać przycisk Add Channel
Następuje weryfikacja czy aby podany adres jest na pewno poprawny
Po pozytywnej weryfikacji użytkownik otrzymuje informacje o dodaniu kanału do bazy kanałów użytkownika
Usuwanie istniejącego kanału RSS
Użytkownik wybiera zakładkę z wszystkimi kanałami
Wybiera kanał, który chciałby usunąć ze swojej bazy poprzez kliknięcie przycisku delete obok tego kanału
Następnie wyświetlana jest strona na której zawarte są wszystkie szczegóły tego kanału, takie jak nazwa, link itd.
Użytkownik wybiera przycisk delete, po czym dany kanał zostaje usunięty z bazy danych użytkownika
1.5. Identyfikacja funkcji
Przechowywanie informacji o użytkownikach
Przechowywanie informacji o kanałach RSS
Przechowywanie informacji o kanałach RSS należących do danego użytkownika
Przechowywanie i udostępnianie informacji o konkretnych wpisach z kanału RSS(tytuł, krótki opis, link do pełnego artykułu, tekst artykułu)
Sprawdzanie na bieżąco czy kanały nie mają nowych wpisów
Powiadomianie e-mailem użytkowników zaintersowanych kanałami
1.6. Analiza hierarchii funkcji projektowanej aplikacji
1.7. Budowa i analiza diagramu przepływu danych
1.8. Wybór encji (obiektów) i ich atrybutów.
Wyróżnione są następujące encje:
Użytkownik
Imię
Nazwisko
Adres email
Hasło
Kanał
Wpis
1.9. Projektowanie powiązań (relacji) pomiędzy encjami. Konstrukcja diagramu ERD (Entity-Relationship Diagram)
1.10. Projekt diagramów STD (State Transition Diagram diagramy przejść pomiędzy stanami)
2. Projekt logiczny
2.1. Projekt tabel
Diagram ERD
Kod SQL
Kod, który odpowiada za tworzenie bazy danych
SHOW WARNINGS|
-- -----------------------------------------------------
-- Table `User`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `User` (
`idUser` INT NOT NULL AUTO_INCREMENT ,
`email` TINYTEXT NOT NULL ,
`password` CHAR(40) NOT NULL ,
`salt` CHAR(23) NOT NULL ,
`meta` TEXT NOT NULL ,
`created` DATETIME NOT NULL ,
`lastLogin` DATETIME NOT NULL ,
`emailHash` CHAR(40) NOT NULL
PRIMARY KEY (`idUser`) )
ENGINE = InnoDB|
SHOW WARNINGS|
-- -----------------------------------------------------
-- Table `Channel`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Channel` (
`idChannel` INT NOT NULL AUTO_INCREMENT ,
`url` VARCHAR(256) CHARACTER SET 'ascii' NOT NULL ,
`title` TEXT NOT NULL ,
`lastChecked` DATETIME NOT NULL ,
`nextCheck` DATETIME NOT NULL ,
`meta` TEXT NOT NULL ,
UNIQUE INDEX `url_UNIQUE` (`url` ASC) ,
PRIMARY KEY (`idChannel`) )
ENGINE = InnoDB|
SHOW WARNINGS|
-- -----------------------------------------------------
-- Table `ChannelUser`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `ChannelUser` (
`channel` INT NOT NULL ,
`user` INT NOT NULL ,
`added` DATETIME NOT NULL ,
PRIMARY KEY (`channel`, `user`) ,
INDEX `fk_Channel2User_User1` (`user` ASC) ,
INDEX `fk_Channel2User_Channel` (`channel` ASC) ,
CONSTRAINT `fk_Channel2User_Channel`
FOREIGN KEY (`channel` )
REFERENCES `Channel` (`idChannel` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `fk_Channel2User_User1`
FOREIGN KEY (`user` )
REFERENCES `User` (`idUser` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB|
SHOW WARNINGS|
-- -----------------------------------------------------
-- Table `Item`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Item` (
`idItem` INT(11) NOT NULL AUTO INCREMENT,
`channel` INT NOT NULL ,
`title` TEXT NOT NULL ,
`description` TEXT NOT NULL ,
`link` VARCHAR(256) CHARACTER SET 'ascii' NULL ,
`meta` TEXT NOT NULL ,
PRIMARY KEY (`idItem`) ,
INDEX `fk_Item_Channel1` (`channel` ASC) ,
CONSTRAINT `fk_Item_Channel1`
FOREIGN KEY (`channel` )
REFERENCES `Channel` (`idChannel` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB|
SHOW WARNINGS|
-- -----------------------------------------------------
-- Table `ItemUser`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `ItemUser` (
`item` INT NOT NULL ,
`user` INT NOT NULL ,
`readed` DATETIME NOT NULL ,
`meta` TEXT NOT NULL,
PRIMARY KEY (`item`, `user`) ,
INDEX `fk_Item2User_User1` (`user` ASC) ,
INDEX `fk_Item2User_Item1` (`item` ASC) ,
CONSTRAINT `fk_Item2User_Item1`
FOREIGN KEY (`item` )
REFERENCES `Item` (`idItem` )
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `fk_Item2User_User1`
FOREIGN KEY (`user` )
REFERENCES `User` (`idUser` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB|
SHOW WARNINGS|
CREATE TABLE IF NOT EXISTS `Filter` (
`idFilter` INT(11) NOT NULL ,
`user` INT(11) NOT NULL ,
`expression` TEXT NOT NULL ,
`counter` INT(11) NOT NULL DEFAULT 0 ,
PRIMARY KEY (`idFilter`) ,
INDEX `fk_Filter_User1` (`user` ASC) ,
CONSTRAINT `fk_Filter_User1`
FOREIGN KEY (`user` )
REFERENCES `User` (`idUser` )
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB |
2.2. Słowniki danych
User - tabela przechowująca dane na temat użytkowników aplikacji
idUser, INT NOT NULL, AUTOINCREMENT - klucz główny,unikalne ID użytkownika
email, TINYTEXT NOT NULL - adres email, będący również loginem
password, CHAR(40) NOT NULL - hasło zahashowane
salt, CHAR(23) NOT NULL - sól do hasła
meta, TEXT NOT NULL - informacje dodatkowe u użytkowniku
created, DATETIME NOT NULL - data stworzenia konta
lastLogin, DATETIME NOT NULL - data ostatniego logowania
emailHash, CHAR(40) NOT NULL - zahashowane hasło
Channel - tabel przechowująca dane na temat wszystkich kanałów wprowadzonych do serwisu
idChannel, INT NOT NULL AUTO_INCREMENT - klucz główny,unikalne ID kanału
url, VARCHAR(256) CHARACTER SET 'ascii' NOT NULL - adres
URL kanału, wartość unikalna
title, TEXT NOT NULL - tytuł kanału
lastChecked, DATETIME NOT NULL - kiedy ostatni raz został dany kanał sprawdzowny
nextCheck, DATETIME NOT NULL - zaplanowane następne sprawdzenie danego kanału
meta, TEXT NOT NULL - informacje dodatkowe o kanale
ChannelUser - tabel przechowująca relacje pomiędzy użytkownikiem a kanałami, które zasubskrybował
channel, INT NOT NULL - klucz obcy, klucz główny, referencja do idChannel z tabeli channel - kanał, który należy do danego użytkownika
user, INT NOT NULL - klucz obcy, referencja do idUser z tabeli User - użytkownik, który zasubskrybował dany kanał.
added, DATETIME NOT NULL - czas i data, w którym dany użytkownik wybrał kanał
Item - tabela przechowująca informacje na temat poszczególnych wpisów danego anału
idItem, INT(11) NOT NULL AUTO INCREMENT - klucz główny, unikalne ID wpisu
channel, INT NOT NULL - klucz obcy odnoszący się do tabeli Channel - kanał z którego odczytywane są wpisy
title, TEXT NOT NULL - pole przechowujące tytuł wpisu
description, TEXT NOT NULL - pole przechowujące opis wpisu
link, VARCHAR(256) CHARACTER SET 'ascii' NULL - adres
URL wpisu
meta, TEXT NOT NULL - informacje dodatkowe na temat wpisu (np. data, pełniejszy opis, cały artykuł itd)
ItemUser - tabela przechowująca relację użytkownik-wpis. Reprezentuje wpisy, które dany użytkownik przeczytał
item, INT NOT NULL - klucz główny, klucz obcy odnoszący sie do idItem z tabeli Item
user, INT NOT NULL - klucz obcy odnoszący sie do idUser z tabeli User
readed, DATETIME NOT NULL - data i czas, w którym ostatni raz dany wpis został odwiedzony
meta, TEXT NOT NULL - dane dodatkowe
Filter
idFilter, INT(11) NOT NULL - klucz główny, unikalny ID filta
user, INT(11) NOT NULL - klucz obcy, odnoszący się do idUser z tabli User
expression, TEXT NOT NULL - wyrażenie, które definiuje filtr
counter. INT(11) NOT NULL DEFAULT 0 - reprezentuje ilosc znalezionych elementow
2.3. Analiza zależności funkcyjnych i normalizacja tabel (dekompozycja do 3NF, BCNF, 4NF, 5NF)
1NF - pierwsza postać normalna
Tabele w bazie spełniają warunek postaci normalnej, jeśli każda z tych tabel spełnia następujące warunki:
opisuje jeden obiekt,
wartości atrybutów są atomowe
nie zawiera kolekcji (powtarzających się grup informacji),
posiada klucz główny,
kolejność wierszy może być dowolna.
Baza danych spełnia warunki 1NF gdyż wszystkie wartości atrybutów są atomowe (przy założeniu, że ciąg string nie może być rozbijany na podstringi), nie zawiera powtarzających się grup informacji. Dodatkowo każda tabela posiada klucz główny(prosty bądź złożony)
2NF - druga postać normalna
Baza jest w drugiej postaci normalnej, wtedy gdy spełniona jest 1NF oraz gdy wartości nie-kluczowe są zależne od klucza głównego.
Gdy występuje tylko klucz prosty nie ma z tym problemu, należy sprawdzic czy wartości nie-kluczowe kluczy złożonych są aby zależne od całego klucza, a nie np od jego części.
Następujące tabele posiadają klucz główny prosty:
Należy sprawdzic tabele, które mają klucze złożone:
W obu przypadkach wartości nie-kluczowe zależą od wartości składowych klucza głównego, zatem baza danych jest w postaci 2NF
3NF - trzecia postać normalna
Wszystkie wartości nie-kluczowe zależą bezpośrednio od wartości klucza głównego, dlatego baza jest w 3NF
2.4. Projektowanie operacji na danych
Działanie użytkownika
Te zapytania wynikają bezpośrednio z działań użytkownika za pomocą interfejsu webowego aplikacji.
Ich częstość występowania zależy wprost od aktywności użytkowników, a ich czasy wykonywania przekładają się na czasy odpowiedzi aplikacji.
Kanały
Dodawanie Nowego kanału
INSERT INTO `Channel` (`url`, `title`, `lastChecked`, `nextCheck`, `meta`) VALUES ('#Url#', '#title#', NOW(), NOW(), '#meta#')
Subskrypcja kanału do konta
INSERT INTO `ChannelUser` (`channel`, `user`, `added`) VALUES (#channelId#, #userId#, NOW())
Cofnięcie subskrypcji
DELETE FROM `ChannelUser` WHERE `channel` = #channelId# AND `user` = #userId#
Pobieranie listy kanasłów subskrybowanych przez użytkownika
SELECT `ChannelUser`.*, `Channel`.* FROM `ChannelUser` JOIN `Channel` ON `ChannelUser`.channel = `Channel`.idChannel WHERE (USER=#UserId#)
Pobieranie listy wpisów dla jednego kanału wraz z jego metadanymi (przeglądanie kanału)
SELECT `Item`.*,`ItemUser`.* FROM `ItemUser` RIGHT JOIN `Item` ON (`ItemUser`.item = `Item`.idItem AND USER=#UserId# ) WHERE (idItem IN ( #ListOfChannellIds#)) ORDER BY `idItem` DESC LIMIT 10;
Użytkownicy
Logowanie użytkownika do systemu
SELECT `User`.* FROM `User` WHERE (emailHash='#HashOfEmail#') AND (email='#Email#') AND (password=sha(concat(salt,'#Password#')))
Działanie daemona
Zapytania te wykonywane są przez proces działający w tle.
Wynikają one pośrednio z aktywności użytkowników, jak i monitorowanych zasobów i sygnałów (stron, filtrów, komend administratora).
Zapytania te w mniejszy sposób związane są z interakcją użytkownika w systemie.
Czasy ich wykonania mogą się wydłużać, nie powinny jednak blokować zapytań z poprzedniej grupy.
Sprawdzanie istniejnia pojedynczego wpisu
SELECT `Item`.* FROM `Item` WHERE (channel='#channelId#') AND (link='#EntryUrl#') LIMIT 1
Dodawanie nowego wpisu do kanału
INSERT INTO `Item` (`channel`, `title`, `description`, `link`, `meta`) VALUES (#ChannelId#, '#Title#', '#Description#', '#EntryUrl#', '#EntryMetadata#')
Dodawanie identyfikatora wpisu do kolejki analizy
INSERT INTO `ItemAnalyze` (`item`) VALUES (#EntryId#)
— Rafał Trójniak
3. Raport końcowy
3.1. Implementacja bazy danych
(utworzenie bazy, implementacja obiektów) (realizacja struktury pustej bazy
w oparciu o diagramy ERD i SQL)
Projekt bazy
Baza została stworzona przy użyciu wcześniej zaprojektowanego diagramu ERD:
Tworzenie bazy
W projekcie zostało wykorzystane narzędzie do zautomatyzowanego procesu budowania aplikacji - Apache Ant.
Dzięki niemu możliwe było wykorzystanie dbdeploy - narzędzia do zarządzania zmianami w bazie danych, dzięki któremu cały zespół mógł bez większych komplikacji update'ować bazy danych w lokalnych środkowiskach.
Skrypt dbdeploy : dbdeploy.xml
Skrypt SQL do utworzenia pustej bazy: dbdeploy.sql
Do rozpoczęcia pracy z aplikacją nie są potrzebne żadne dane początkowe, które należałoby wprowadzić do bazy danych.
3.2. Zdefiniowanie interfejsów do prezentacji, edycji i obsługi danych
(formularze; należy zaprojektować strukturę każdego formularza oraz powiązania między nimi
w oparciu o diagramy FHF, DFD i STD).
Poniżej zostaną przedstawione formularze służące do wprowadzania danych
Rejestracja
Logowanie
Zmiana hasła
Dodawanie kanalu
Oznaczanie wpisu wg wybranego filtru
3.3. Zdefiniowanie dokumentów do przetwarzania i prezentacji danych
Widoki umożliwiające prezentacje danych:
Dane użytkownika
Lista kanałów
Lista wpisów na kanale
3.4. Zdefiniowanie panelu sterowania aplikacji (należy pamiętać o dostosowaniu do potrzeb użytkownika
Aplikacja nie wymaga (na tym etapie rozwoju) panelu sterowania. Dlatego taka funkcja nie została zaimplementowana.
Zdefiniowanie makropoleceń dla realizacji typowych operacji
Makropolecenia wykonujące mapowanie obiektów do bazy danych oparte są o komponent Zend_Db_Table będący implementacją wzorca projektowego Brama danych tabeli(Table Data Gateway)
— Rafał Trójniak
3.5. Uruchamianie i testowanie aplikacji
Uruchamianie aplikacji
W celu uruchomienia aplikacji na skonfigurowanym serwerze HTTP należy :
Zaimportować strukturę bazy danych
Skopiować pliki na swerer
Ustawić parametry dostępu do bazy danych w pliki application/configs/application.ini
Uruchomić z katalogu głównego projektu demona przy pomocy komendy ./daemon production
Testowanie
W obrębie back-endu ( warstwa usług ) w celu testowania wykonane zostały testy jednostkowe.
Użycie testów jednostkowych pozwoliło na :
Uniezależnienie tempa pracy nad backendem od pozostałych elementów
Użycie testów jednostkowych jako formy dokumentacji pokazującej sposób wykorzystania elementów
Przeprowadzenia częstszych testów
W razie potrzeby przebudowy dużej części kodu kontrolując jego spójność z aplikacją
Aby wykonać testy w skonfigurowanym środowisku produkcyjnym należy zmienić katalog roboczy na tests i wykonać komendę phpunit
— Rafał Trójniak
3.6. Wprowadzanie danych
Do działania aplikacji nie potrzebne są żadne początkowe dane do wprowadzenia. Wystarczy tylko stworzyć pustą bazę danych.
Wprowadzanie danych odbywa sie ręcznie poprzez formularze do wypełniania.
Dodatkowo w tle działa cron, który update'uje wpisy na kanale co jakiś ustalony czas.
3.7. Wdrażanie systemu do użytkowania
Do poprawnego działania aplikacji potrzebne będą:
serwer HTTP (Apache + mod_rewrite lub nginx z regułkami rewrite)
PHP 5.3 (moduły curl, mysql)
Baza danych SQL (testowane na MySQL) współpracująca z interpreterem PHP
[opcjonalnie] Apache Ant - wykorzystane w celu wersjonowania bazy danych
[opcjonalnie] PHPUnit - testy jednostkowe aplikacji
— Rafał Trójniak
3.8. Przeprowadzenie szkolenia użytkowników.
Aplikacja jest zaprojektowana w sposób intuicyjny, dlatego też jakakolwiek dokumentacja dla użytkownika wydaje sie byc zbędna. Niech zostanie również podkreślone to, że aplikacja ma być prosta w obsłudze i ma służyć jednemu celowi - wygodnemu przeglądaniu wpisów RSS.
Niemniej jednak można byłoby napisać FAQ lub pomoc dla użytkownika do obsługi nieco bardziej skomplikowanych operacji, jak np. tworzenie zaawansownych filtrów.
3.9. Zapewnienie dokumentacji technicznej i użytkowej
Z punktu widzenia klienta aplikacja na aktualnym etapie nie potrzebuje administracji oraz zarządzania. Użytkownicy sami zarządzają aplikacją.
Dokumentacje techniczną może stanowić ninejszy dokument.
3.10. Zapewnienie obsługiwania systemu po wdrożeniu
Utrzymywanie systemu sprowadza się do utrzymania serwera www oraz serwera baz danych. Należy również zwrócić uwagę na jakość połączenia internetowego (cron sprawdzający aktualność kanałów).
Co do samej aplikacji błędy znalezione przez użytkowników można raportować do obsługi technicznej systemu (zwykle admin).
3.11. Rozwijanie i modyfikowanie aplikacji
Aplikacja jest zaprojektowana w taki sposób,że wzbogacanie ją o kolejne moduły nie powinno stanowić problemu dla developerów. Wszystko jest ułożone w ustrukturyzowany sposób, sama aplikacja jest wykonana w modelu MVC.
W trakcie rozwijania aplikacji padło kilka pomysłów na funkcjonalności, jednak z braku czasu nie udało się ich zaimplementować.
Były to m.in. :
Wyszukiwanie artykułów podobnych do aktualnie czytanego
Usuwanie artykułów dublujących się, jednak pochodzących z różnych kanałów
Powiadamianie SMS
Utworzenie koszyków kategorii
3.12. Opracowanie doświadczeń wynikających z realizacji projektu
Przy realizacji aplikacji duży procent czasu został poświęcony na projektowanie systemu(bazy danych + model + kontrolery + akcje). Dzięki temu uniknięto niepotrzebnych błędów i nieporozumień w trakcie implementacji funkcjonalności systemu.
Jednym z głównych aspektów, który należy opisać jest czas który należało poświęcić na wdrożenie się w zagadnienia i technologie implementacyjne. Projekt został napisany w PHP przy użyciu Zend Framework, który posiada bogatą dokumentację, jednak dla początkującego może być lekko niezrozumiała. Kolejnym minusem wynikającym z użycia takiej a nie innej technologii były problemy podczas samej implementacji. PHP jest językiem z dynamiczną typizacją która to sprawiała problemy na etapie rozwijania aplikacji.
Niemniej jednak ZF dostarcza masę bibliotek dzięki którym można skrócić czas poświęcony pisaniu kodu o kilkadziesiąt procent.
3.13. Wykaz literatury, załączniki