Skip to content
Browse files

merge from yiidoc

  • Loading branch information...
1 parent 8ea353b commit 9b624942886cf94ae69e8e3ddd9a4b2309e9aa80 qiang.xue committed Jul 4, 2009
View
113 docs/blog/pl/comment.admin.txt
@@ -0,0 +1,113 @@
+Administrowanie komentarzami
+=================
+
+Administrowanie komentarzami zawiera aktualizowanie, usuwanie oraz zatwierdzanie komentarzy.
+Operacja te są zaimplementowane jako akcje w klasie kontrolera `CommentController`.
+
+
+Aktualizowanie i usuwanie komentarzami
+------------------------------
+
+Kod wygenerowany przez narzędzie `yiic` do aktualizowania oraz usuwania komentarzy
+pozostaje w większej części niezmieniony. Ponieważ wspieramy podgląd komentarza
+podczas aktualizacji komentarza, musimy tylko zmienić metodę `actionUpdate()`
+kontrolera `CommentController` w następujący sposób:
+
+~~~
+[php]
+public function actionUpdate()
+{
+ $comment=$this->loadComment();
+
+ if(isset($_POST['Comment']))
+ {
+ $comment->attributes=$_POST['Comment'];
+ if(isset($_POST['previewComment']))
+ $comment->validate('update');
+ else if(isset($_POST['submitComment']) && $comment->save())
+ $this->redirect(array('post/show',
+ 'id'=>$comment->postId,
+ '#'=>'c'.$comment->id));
+ }
+
+ $this->render('update',array('comment'=>$comment));
+}
+~~~
+
+Kod ten jest bardzo podobny do tego z PostController`.
+
+
+Zatwierdzanie komentarzy
+------------------
+
+Kiedy komentarz jest nowo utworzony, oczekuje on na zatwierdzenie i musi zostać zatwierdzony
+jeśli ma on być widoczny dla gości. Zatwierdzanie komentarza to przede wszystkim zmiana
+kolumny zawierającej status komentarza.
+
+Utworzymy metodę `actionApprove()` w kontrolerze `CommentController` następująco:
+
+~~~
+[php]
+public function actionApprove()
+{
+ if(Yii::app()->request->isPostRequest)
+ {
+ $comment=$this->loadComment();
+ $comment->approve();
+ $this->redirect(array('post/show',
+ 'id'=>$comment->postId,
+ '#'=>'c'.$comment->id));
+ }
+ else
+ throw new CHttpException(400,'Invalid request...');
+}
+~~~
+
+W powyższym kodzie, gdy akcja zatwierdzenia `approve` jest wywoływana poprzez żądanie
+POST, wywołujemy metodę `approve()` zdefiniowaną w modelu komentarza `Comment` aby zmienić
+status. Następnie przekierowujemy przeglądarkę użytkownika do strony wyświetlającej wiadomość,
+do której należy ten komentarz.
+
+Modyfikujemy również metodę `actionList()` modelu komentarza `Comment` aby
+wyświetlała listę komentarzy, które czekają na zatwierdzenie.
+
+~~~
+[php]
+public function actionList()
+{
+ $criteria=new CDbCriteria;
+ $criteria->condition='Comment.status='.Comment::STATUS_PENDING;
+
+ $pages=new CPagination(Comment::model()->count());
+ $pages->pageSize=self::PAGE_SIZE;
+ $pages->applyLimit($criteria);
+
+ $comments=Comment::model()->with('post')->findAll($criteria);
+
+ $this->render('list',array(
+ 'comments'=>$comments,
+ 'pages'=>$pages,
+ ));
+}
+~~~
+
+W widoku `list` wyświetlamy szczegóły każdego komentarza, który oczekuje na zatwierdzenie.
+W szczególności, pokazujemy link do zatwierdzenia `approve` w następujący sposób:
+
+~~~
+[php]
+<?php if($comment->status==Comment::STATUS_PENDING): ?>
+ <span class="pending">Pending approval</span> |
+ <?php echo CHtml::linkButton('Approve', array(
+ 'submit'=>array('comment/approve','id'=>$comment->id),
+ )); ?> |
+<?php endif; ?>
+~~~
+
+Używamy [CHtml::linkButton()] zamiast [CHtml::link()] ponieważ pierwsza powoduje wywołanie
+żądania POST, w odróżnieniu od drugiej, wywołującej żądanie GET.
+Zaleca się aby żądania GET nie modyfikowały danych na serwerze. W przeciwnym przypadku
+możemy mieć do czynienia z niebezpieczeństwem, że użytkownik nieumyślnie może zmienić
+dane po stronie serwera parokrotnie jeśli będzie odświeżał stronę kilka razy.
+
+<div class="revision">$Id: comment.admin.txt 1050 2009-05-22 20:06:18Z qiang.xue $</div>
View
120 docs/blog/pl/comment.create.txt
@@ -0,0 +1,120 @@
+Tworzenie i wyświetlanie komentarzy
+================================
+
+W części tej zaimplementujemy funkcje wyświetlania oraz tworzenia komentarzy.
+
+
+Wyświetlanie komentarzy
+-------------------
+
+Zamiast osobnych stron do wyświetlania i tworzenia komentarzy użyjemy strony
+wyświetlającej wiadomości. Poniżej wyświetlonej zawartości każdej wiadomości,
+wyświetlimy listę komentarzy należących do tej wiadomości a pod nimi formularz
+tworzenia komentarza.
+
+W celu wyświetlenia komentarzy na stronie wiadomości, zmienimy metodę `actionShow()`
+kontrolera `PostController` w następujacy sposób:
+
+~~~
+[php]
+public function actionShow()
+{
+ $post=$this->loadPost();
+ $this->render('show',array(
+ 'post'=>$post,
+ 'comments'=>$post->comments,
+ ));
+}
+~~~
+
+Zauważ, że wyrażenie `$post->comments` jest poprawne ponieważ zdefiniowaliśmy relację
+`comments` w klasie `Post`. Wywołanie tego wyrażenia uruchomi ukryte zapytanie
+JOIN do bazy danych, które zwróci komentarze należące do aktualnej wiadomości.
+Funkcjonalność ta znana jest jako [leniwe zapytanie relacyjne](http://www.yiiframework.com/doc/guide/database.arr).
+
+Zmodyfikujemy również widok `show` poprzez dołączenie wyświetlania komentarzy
+na końcu wyświetlania wiadomości, nad czym nie będziemy się tutaj zbytnio rozwodzić.
+
+Tworzenie komentarzy
+-----------------
+
+Aby obsłużyć tworzenie komentarzy, najpierw zmienimy metodę `actionShow()` kontrolera `PostController`
+w następujący sposób:
+
+~~~
+[php]
+public function actionShow()
+{
+ $post=$this->loadPost();
+ $comment=$this->newComment($post);
+ $this->render('show',array(
+ 'post'=>$post,
+ 'comments'=>$post->comments,
+ 'newComment'=>$comment,
+ ));
+}
+
+protected function newComment($post)
+{
+ $comment=new Comment;
+ if(isset($_POST['Comment']))
+ {
+ $comment->attributes=$_POST['Comment'];
+ $comment->postId=$post->id;
+ $comment->status=Comment::STATUS_PENDING;
+
+ if(isset($_POST['previewComment']))
+ $comment->validate('insert');
+ else if(isset($_POST['submitComment']) && $comment->save())
+ {
+ Yii::app()->user->setFlash('commentSubmitted','Thank you...');
+ $this->refresh();
+ }
+ }
+ return $comment;
+}
+~~~
+
+W powyższym kodzie, wołamy metodę `newComment()` zanim wygenerujemy widok `show`.
+W metodzie `newComment()` generujemy instancję komentarza `Comment` i sprawdzamy
+czy formularz komentarza został przesłany. Formularz może zostać przesłany poprzez
+kliknięcie zarówno przycisku submit lub też przycisku podglądu. Dla pierwszego
+spróbujemy zapisać komentarz i wyświetlić migawkę (ang. flash message).
+Migawka jest wyświetlana tylko raz, co oznacza zniknie ona, że jeśli odświeżymy stronę.
+
+Zmodyfikujemy również widok `show` poprzez dołączenie formularza tworzenia komentarza.
+
+~~~
+[php]
+......
+<?php $this->renderPartial('/comment/_form',array(
+ 'comment'=>$newComment,
+ 'update'=>false,
+)); ?>
+~~~
+
+Osadziliśmy formularz tworzenia komentarza w częściowym widoku `/wwwroot/blog/protected/views/comment/_form.php`.
+Zmienna `$newComment` jest przekazywana do metody `actionShow`. Jej głównym zadaniem jest
+przechowywanie informacji o danych wejściowych użytkownika. Zmienna `update`
+ustawiona jest na wartość false, co oznacza, że formularz komentarza jest używany do
+tworzenia nowego komentarza.
+
+W celu wsparcia podglądu komentarza, dodamy przycisk podglądu do formularza tworzenia
+komentarza. Gdy przycisk podglądu zostanie kliknięty, podgląd komentarza jest wyświetlany
+na górze strony. Poniżej znajduje się zaktualizowany kod formularza komentarza:
+
+~~~
+[php]
+...formularz komentarza z przyciskiem podglądu...
+
+<?php if(isset($_POST['previewComment']) && !$comment->hasErrors()): ?>
+<h3>Preview</h3>
+<div class="comment">
+ <div class="author"><?php echo $comment->authorLink; ?> says:</div>
+ <div class="time"><?php echo date('F j, Y \a\t h:i a',$comment->createTime); ?></div>
+ <div class="content"><?php echo $comment->contentDisplay; ?></div>
+</div><!-- post preview -->
+<?php endif; ?>
+~~~
+
+<div class="revision">$Id: comment.create.txt 1157 2009-06-22 15:25:02Z qiang.xue $</div>
View
148 docs/blog/pl/comment.model.txt
@@ -0,0 +1,148 @@
+Dostosowywanie modelu komentarza
+=========================
+
+Tak jak model wiadomości `Post`, musimy dostosować metody `rules()`, `relations()`
+oraz `safeAttributes()` dla modelu `Comment`. Dodatkowo potrzebujemy zmienić
+metodę `attributeLabels()` aby zdeklarować niestandardowe etykiety dla pewnych atrybutów.
+
+
+Dostosowywanie metody `rules()`
+----------------------------
+
+Najpierw dostosowujemy reguły sprawdzania poprawności wygenerowane przez narzędzie `yiic`.
+Następujące reguły są używane dla komentarzy:
+
+~~~
+[php]
+public function rules()
+{
+ return array(
+ array('author,email,content', 'required'),
+ array('author,email,url','length','max'=>128),
+ array('email','email'),
+ array('url','url'),
+ array('verifyCode', 'captcha', 'on'=>'insert',
+ 'allowEmpty'=>!Yii::app()->user->isGuest),
+ );
+}
+~~~
+
+W powyższym kodzie, określamy, że atrybuty: autor `author`, e-mail `email` oraz zawartość `content`
+są atrybutami wymaganymi (nie mogą być puste); długość atrybutu autor `author`, e-mail `email` oraz adres URL `url`
+nie może przekraczać 128 (znaków); atrybut e-maila `email` musi być poprawnym adresem mailowym;
+atrybut adresu URL `url` musi być poprawnym adresem URL; a atrybut weryfikacji kodu `verifyCode`
+powinien zostać sprawdzony pod względem zgodności z kodem wygenerowanym przez [CAPTCHA](http://en.wikipedia.org/wiki/Captcha).
+
+Powyższy atrybut `verifyCode` jest używany przede wszystkim do przechowywania kodu weryfikującego,
+który użytkownik wprowadził aby dodać komentarz. Ponieważ nie jest on obecny w tabeli
+komentarzy `Comment`, musimy zadeklarować go bezpośrednio jako publiczną zmienną.
+Do sprawdzania jego poprawności używamy specjalnego walidatora o nazwie `captcha`,
+który reprezentuje klasę [CCaptchaValidator]. Ponadto sprawdzenie poprawności
+odbędzie się podczas wstawiania (ang. insert) nowego komentarza (popatrz na opcję `on`).
+Dla uwierzytelnionych użytkowników nie ma potrzeby tego czynić (popatrz na opcję `allowEmpty`).
+
+
+Dostosowywanie metody `safeAttributes()`
+-------------------------------------
+
+Następnie dostosowujemy metodę `safeAttributes()` aby zdefiniować, które atrybuty
+mogą być przypisywane grupowo.
+
+~~~
+[php]
+public function safeAttributes()
+{
+ return array('author', 'email', 'url', 'content', 'verifyCode');
+}
+~~~
+
+Oznacza to również, że formularz komentarza będzie zawierał te pola, aby zebrać informacje
+o autorze, e-mailu, adresie URL, zawartości oraz kodzie weryfikującym.
+
+
+Dostosowywanie metody `relations()`
+--------------------------------
+
+Podczas tworzenia portletu "ostatnich komentarzy", potrzebujemy wylistować ostatnie
+komentarze wraz z odpowiadającymi im informacjami o wiadomościach. Dlatego też,
+musimy dostosować metodę `relations()` aby określić relację z wiadomością.
+
+~~~
+[php]
+public function relations()
+{
+ return array(
+ 'post'=>array(self::BELONGS_TO, 'Post', 'postId',
+ 'joinType'=>'INNER JOIN'),
+ );
+}
+~~~
+
+Zauważ, że typ złączenia dla relacji `post` to `INNER JOIN`. Dzieje się tak, ponieważ
+komentarz należy do wiadomości.
+
+
+Dostosowywanie metody `attributeLabels()`
+--------------------------------------
+
+Na koniec potrzebujemy dostosować metodę `attributeLabels()` aby zdefiniować
+niestandardowe etykiety dla atrybutów. Metoda da zwraca tablicę zawierająca pary
+nazwa-etykieta. Podczas wywoływania metody [CHtml::activeLabel()] w celu wyświetlenia
+etykiety atrybutu, sprawdzi ona najpierw czy zadeklarowana niestandardową etykietę.
+Jeśli nie, użyje ona algorytmu do wygenerowania domyślnych etykiet.
+
+~~~
+[php]
+public function attributeLabels()
+{
+ return array(
+ 'author'=>'Name',
+ 'url'=>'Website',
+ 'content'=>'Comment',
+ 'verifyCode'=>'Verification Code',
+ );
+}
+~~~
+
+> Tip|Wskazówka: Algorytm służący do generowania domyślnych etykiet oparty jest na
+nazwach atrybutów. Najpierw dzieli nazwę na słowa biorąc po uwagę wielkość liter.
+Następnie zmienia pierwszą literę w każdym słowie na dużą literę. Na przykład,
+nazwa `verifyCode` otrzyma domyślną etykietę `Verify Code`.
+
+
+Dostosowywanie procesu zapisywania
+--------------------------
+
+Ponieważ chcemy przechowywać w wiadomości ilość komentarzy, gdy dodajemy lub usuwamy
+komentarz, musimy poprawić odpowiadającą wiadomości ilość komentarzy. Uzyskujemy to
+poprzez nadpisanie metod `afterSave()` oraz `afterDelete()` dla modelu komentarza `Comment`.
+Również nadpisujemy metodę `beforeValidate()` modelu, tak że możemy konwertować zawartość
+z formatu Markdown do formatu HTML oraz zapisywać czas utworzenia.
+
+
+~~~
+[php]
+protected function beforeValidate($on)
+{
+ $parser=new CMarkdownParser;
+ $this->contentDisplay=$parser->safeTransform($this->content);
+ if($this->isNewRecord)
+ $this->createTime=time();
+ return true;
+}
+
+protected function afterSave()
+{
+ if($this->isNewRecord && $this->status==Comment::STATUS_APPROVED)
+ Post::model()->updateCounters(array('commentCount'=>1), "id={$this->postId}");
+}
+
+protected function afterDelete()
+{
+ if($this->status==Comment::STATUS_APPROVED)
+ Post::model()->updateCounters(array('commentCount'=>-1), "id={$this->postId}");
+}
+~~~
+
+
+<div class="revision">$Id: comment.model.txt 788 2009-03-06 04:23:06Z qiang.xue $</div>
View
93 docs/blog/pl/final.deployment.txt
@@ -0,0 +1,93 @@
+Końcowe dopieszczanie oraz wdrażanie
+============================
+
+Zbliżamy się do zakończenia prac nad naszym blogiem. Zanim go wdrożymy chcielibyśmy dokonać
+kilku ulepszeń.
+
+
+Zmiana strony domowej
+------------------
+
+Ustawimy stronę wyświetlająca listę wiadomości jako stronę domową. W tym celu
+zmienimy [konfigurację aplikacji](http://www.yiiframework.com/doc/guide/basics.application#application-configuration)
+w następujący sposób,
+
+~~~
+[php]
+return array(
+ ......
+ 'defaultController'=>'post',
+ ......
+);
+~~~
+
+> Tip|Wskazówka: Ponieważ kontroler `PostController` zawiera już akcję `list` jako
+domyślną akcje, podczas wchodzenia na stronę domową aplikacji zobaczymy rezultat
+wygenerowany przez akcję `list` kontrolera wiadomości.
+
+
+Włączenie buforowania schematu bazy
+-----------------------
+
+Ponieważ rekord aktywny wykorzystuje metadane pochodzące z tabel w celu określenia
+informacji o kolumnach, zajmuje mu trochę czasu ich odczytanie oraz przeanalizowanie.
+Może to nie być problemem podczas fazy tworzenia aplikacji ale dla aplikacji działającek
+w trybie produkcyjnym, jest to totalne marnotrawstwo czasu, jeśli schemat bazy danych
+nie zmienia się. Dlatego też, włączymy buforowanie schematu poprzez zmodyfikowanie
+konfiguracji aplikacji w następujący sposób,
+
+
+~~~
+[php]
+return array(
+ ......
+ 'components'=>array(
+ ......
+ 'cache'=>array(
+ 'class'=>'CDbCache',
+ ),
+ 'db'=>array(
+ 'class'=>'system.db.CDbConnection',
+ 'connectionString'=>'sqlite:/wwwroot/blog/protected/data/blog.db',
+ 'schemaCachingDuration'=>3600,
+ ),
+ ),
+);
+~~~
+
+W powyższym kodzie, najpierw dodaliśmy komponent buforowania `cache`, który używa
+domyślnie bazy danych SQLite jako miejsce składowania bufora. Jeśli twój serwer jest
+wyposażony w inne rozszerzenie buforujące, takie jak APC, moglibyśmy przełączyć
+się równie dobrze na jego używanie. Zmodyfikujemy również komponent `db` poprzez
+ustawienie jego właściwości [schemaCachingDuration|CDbConnection::schemaCachingDuration]
+na wartość 3600, co oznacza, że przeanalizowane dane schematu bazy danych pozostaną
+ważne w buforze na okres 3600 sekund.
+
+Wyłączanie trybu debugowania
+------------------------
+
+Zmodyfikujemy plik wejściowy `/wwwroot/blog/index.php` poprzez usunięcie linii
+definiującej stałą `YII_DEBUG`. Stała ta jest użyteczna podczas fazy tworzenia, gdyż
+pozwala ona Yii wyświetlać więcej informacji użytecznych dla debuggowania w przypadku
+wystąpienia błędu. Jednakże, jeśli aplikacja działa w trybie produkcyjnym, wyświetlanie
+takich informacji nie jest najlepszym pomysłem ponieważ mogą one zawierać wrażliwe informacje
+takie jak miejsce, gdzie skrypt się znajduje, oraz zawartość jego pliku, itp.
+
+
+Wdrażanie aplikacji
+-------------------------
+
+Końcowy proces wdrażania aplikacji składa się głównie z kopiowania katalogu `/wwwroot/blog`
+do katalogu docelowego. Następujący wykaz czynności kontrolnych pokazuje
+wszystkie potrzebne kroki:
+
+ 1. Zainstaluj Yii w docelowym miejscu jeśli nie jest ono jeszcze tam dostępne;
+ 2. Skopiuj całą zawartość katalogu `/wwwroot/blog` do miejsca docelowego;
+ 3. Wyedytuj plik skrytu wejściowego `index.php` poprze wskazanie zmiennej `$yii` na nowy plik rozruchowy (ang. bootstrap file);
+ 4. Wyedytuj plik `protected/yiic.php` poprzez ustawienie zmiennej `$yiic` wskazujące
+ na nowy plik `yiic.php`;
+ 5. Zmień uprawnienia do katalogów `assets` oraz `protected/runtime` tak aby były one
+ zapisywalne przez proces Web serwera.
+
+
+<div class="revision">$Id: final.deployment.txt 681 2009-02-16 04:57:01Z qiang.xue $</div>
View
21 docs/blog/pl/final.error.txt
@@ -0,0 +1,21 @@
+Dostosowywanie wyświetlania błędów
+=========================
+
+Nasza aplikacja blogowa używa szablonu dostarczanego przez Yii do wyświetlania różnych
+błędów. Ponieważ styl oraz redakcja tekstu różni się od tego czego oczekujemy, chcemy
+dostosować te szablony. Aby to zrobić, utworzymy szereg plików widoku w katalogu
+`/wwwroot/blog/protected/views/system`.
+
+Najpierw utworzymy plik nazwany `error.php`. Jest to domyślny widok, który będzie
+używany do wyświetlania wszystkich rodzajów błędów, jeśli bardziej szczegółowy
+plik widoku błędów nie będzie dostępny. Ponieważ ten plik widoku jest używany
+kiedy wystąpi błąd, nie powinien on zawierać bardzo skomplikowanej logiki PHP, która
+może powodować dalsze błędy. Zauważ również, że pliki widoków błędów nie używają układu.
+Dlatego też, powinien posiadać kompletny wygląd strony.
+
+Tworzymy również plik o nazwie `error403.php` w celu wyświetlania błędu HTTP 403 (brak uwierzytelnienia)
+oraz plik o nazwie `error404.php` do wyświetlania błędu HTTP 404 (nie znaleziono strony).
+
+Aby poznać więcej szczegółów o nazywaniu tych plików widoku błędów, spójrz do [Przewodnika](http://www.yiiframework.com/doc/guide/topics.error#displaying-errors).
+
+<div class="revision">$Id: final.error.txt 1049 2009-05-22 20:00:35Z qiang.xue $</div>
View
83 docs/blog/pl/final.future.txt
@@ -0,0 +1,83 @@
+Dalsze rozszerzanie
+===================
+
+Używanie tematów
+-------------
+
+Bez pisania dodatkowej linijki kodu nasz blog jest gotowy do używania
+[tematów](http://www.yiiframework.com/doc/guide/topics.theming). Aby użyć nowy
+temat musimy przede wszystkim utworzyć temat poprzez napisanie niestandardowego
+pliku widoku dla tematu. Na przykład, aby móc używać temat o nazwie `classic`,
+który używa innego układy strony utworzymy plik układu
+`/wwwroot/blog/themes/classic/views/layouts/main.php`. Musimy również zmienić konfigurację
+aplikacji aby wskazać, że wybraliśmy temat `classic`:
+
+~~~
+[php]
+return array(
+ ......
+ 'theme'=>'classic',
+ ......
+);
+~~~
+
+
+Umiędzynaradawianie
+--------------------
+
+Możemy również umiędzynarodowić nasz blog, tak, że każda jego strona może być wyświetlana w innym
+języku. Wymaga to przede wszystkim działania w dwóch aspektach. Po pierwsze, możemy
+utworzyć plik widoku w innym języku. Na przykład dla strony `list` kontrolera `PostController`,
+możemy utworzyć plik widoku `/wwwroot/blog/protected/views/post/zh_cn/list.php`.
+Jeśli aplikacja jest skonfigurowana do używania uproszczonego języka chińskiego
+(kod języka to `zh_cn`), Yii automatycznie użyje tego nowego pliku widoku zamiast
+oryginalnego.
+
+Po drugie, możemy utworzyć tłumaczenia komunikatów generowanych przez kod.
+Tłumaczenie komunikatów powinno zostać zapisane jako pliki w katalogu
+`/wwwroot/blog/protected/messages`. Potrzebujemy również zmodyfikować kod, gdzie używane
+są łańcuchy znaków poprzez zamknięcie ich w wywołaniu metody `Yii::t()`.
+
+Aby uzyskać więcej informacji o umiędzynaradawianiu, spójrz do
+[Przewodnika](http://www.yiiframework.com/doc/guide/topics.i18n).
+
+
+Polepszanie wydajności przy użyciu buforowania
+--------------------------------
+
+Chociaż framework Yi jest sam w sobie [bardzo wydajny](http://www.yiiframework.com/performance/),
+niekoniecznie prawdą jest, że aplikacja napisana w Yii jest wydajna. Jest kilka miejsc
+w naszym blogu, gdzie możemy zwiększyć wydajność. Na przykład, portlet chmurki tagów
+może być jednym z wąskich gardeł wydajnościowych ponieważ zawiera on złożone zapytanie
+do bazy danych oraz złożoną logikę PHP.
+
+Możemy używać wyrafinowanych [funkcji buforowania](http://www.yiiframework.com/doc/guide/caching.overview)
+dostarczanych przez Yii do zwiększenia wydajności. Jednym z najbardziej użytecznych komponentów
+Yii jest [COutputCache], który buforuje części wyświetlanej strony, tak, że kod
+zajmujący się generowaniem fragmentu nie musi być wykonywany dla każdego żądania.
+Na przykład w pliku widoku `/wwwroot/blog/protected/views/layouts/main.php` możemy
+zamknąć portlet chmurki tagów w [COutputCache]:
+
+~~~
+[php]
+<?php if($this->beginCache('tagCloud', array('duration'=>3600))) { ?>
+
+<?php $this->widget('TagCloud'); ?>
+
+<?php $this->endCache(); } ?>
+~~~
+
+W powyższym kodzie, dla każdego żądania, wyświetlanie chmurki tagów będzie dostarczane
+z bufora zamiast generowane w locie. Zbuforowana zawartość pozostanie ważna w buforze
+na okres 3600 sekund.
+
+
+Dodawanie nowych funkcji
+-------------------
+
+Nasza aplikacja posiada jedynie kilka bardzo prostych funkcjonalności. Aby stać się
+kompletnym systemem blogowym potrzebne są nowe funkcje, na przykład, portlet kalendarza,
+powiadomienia przez email, kategoryzowanie wiadomości, portlet wiadomości zarchiwizowanych, itp.
+Pozostawimy implementację tych funkcji zainteresowanym czytelnikom
+
+<div class="revision">$Id: final.future.txt 712 2009-02-19 15:11:15Z qiang.xue $</div>
View
46 docs/blog/pl/final.logging.txt
@@ -0,0 +1,46 @@
+Logowanie błędów
+==============
+
+Aplikacja działająca produktywnie często potrzebuje wyszukanego rejestrowania
+dla różnych zdarzeń. W naszym blogu, będziemy chcieli logować błędy występujące
+podczas jego użytkowania. Takie błędy mogą brać się z pomyłek programisty bądź
+też z niewłaściwego użytkowania aplikacji przez użytkowników. Rejestrowanie tych
+błędów pomoże nam usprawniać naszą aplikację.
+
+Udostępniamy logowanie błędów poprzez modyfikację
+[konfiguracji aplikacji](http://www.yiiframework.com/doc/guide/basics.application#application-configuration)
+w następujący sposób:
+
+~~~
+[php]
+return array(
+ 'preload'=>array('log'),
+
+ ......
+
+ 'components'=>array(
+ 'log'=>array(
+ 'class'=>'CLogRouter',
+ 'routes'=>array(
+ array(
+ 'class'=>'CFileLogRoute',
+ 'levels'=>'error, warning',
+ ),
+ ),
+ ),
+ ......
+ ),
+);
+~~~
+
+Przy użyciu powyższej konfiguracji, jeśli wystąpi błąd bądź też ostrzeżenie,
+szczegółowa informacja zostanie zarejestrowana i zapisana w pliku znajdującym się
+w katalogu `/wwwroot/blog/protected/runtime`.
+
+Komponent `log` oferuje bardziej zaawansowane funkcjonalności, takie jak wysyłanie
+zalogowanej wiadomości na daną listę adresów mailowych, wyświetlanie zarejestrowanych
+wiadomości w oknie konsoli JavaScript, itp. Aby uzyskać więcej szczegółów, zajrzyj do
+[Przewodnika](http://www.yiiframework.com/doc/guide/topics.logging).
+
+
+<div class="revision">$Id: final.logging.txt 868 2009-03-21 17:35:21Z qiang.xue $</div>
View
60 docs/blog/pl/final.url.txt
@@ -0,0 +1,60 @@
+Upiększanie URLs
+================
+
+Adresy URL łączące różne strony naszego blogu wyglądają obecnie brzydko.
+Na przykład, URL dla strony wyświetlającej wiadomości wygląda następująco:
+
+~~~
+/index.php?r=post/show&id=1
+~~~
+
+W części tej opiszemy jak upiększyć te adresy URL i uczynić je przyjaznymi dla SEO.
+Naszym celem jest używanie następujących adresów URL w aplikacji:
+
+ * `/index.php/tag/yii`: prowadzi do strony pokazującej listę wiadomości zawierających tak `yii`;
+ * `/index.php/posts`: prowadzi do strony pokazującej ostatnie wiadomości;
+ * `/index.php/post/1`: prowadzi do strony pokazującej szczegóły wiadomości o ID równym 1;
+ * `/index.php/post/update/1`: prowadzi do strony, która pozwala aktualizować wiadomość o ID 1;
+
+Aby osiągnąć nasz cel, zmodyfikujemy
+[konfigurację aplikacji](http://www.yiiframework.com/doc/guide/basics.application#application-configuration)
+w następujący sposób:
+
+~~~
+[php]
+return array(
+ ......
+ 'components'=>array(
+ ......
+ 'urlManager'=>array(
+ 'urlFormat'=>'path',
+ 'rules'=>array(
+ 'tag/<tag>'=>'post/list',
+ 'posts'=>'post/list',
+ 'post/<id:\d+>'=>'post/show',
+ 'post/update/<id:\d+>'=>'post/update',
+ ),
+ ),
+ ),
+);
+~~~
+W powyższym kodzie, skonfigurowaliśmy komponent [urlManager](http://www.yiiframework.com/doc/guide/topics.url)
+poprzez ustawienie jego właściwości `urlFormat` jako `path` oraz dodanie zestawu reguł `rules`.
+
+Reguły używane są przez `urlManager` do analizowanie i tworzenia URLi w zaprojektowanym
+formacie. Na przykład, pierwsza reguła mówi, że jeśli żądamy URLa `/index.php/tag/yii`
+komponent `urlManager` powinien być odpowiedzialny za wysłania żądania do
+[trasy](http://www.yiiframework.com/doc/guide/basics.controller#route) `post/list`
+oraz wygenerowania parametru GET o nazwie`tag` i wartości `yii`. Z drugiej strony,
+podczas tworzenia URL-a o ścieżce `post/list` oraz parametrze `tag`, komponent
+`urlManager` również użyje tej reguły do wygenerowania pożądanego adresu URL
+`/index.php/tag/yii`. Z tego też powodu mówimy, że `urlManager` jest dwukierunkowym
+menadżerem URLi.
+
+Komponent `urlManager` może później upiększyć nasze URLe, np. poprzez ukrycie `index.php`
+w URLach, dodanie sufisku takiego jak `.html` do URLi. Możemy uzyskać te funkcje
+w prosty sposób poprzez konfigurowanie różnych właściwości `urlManager` w konfiguracji
+aplikacji. Aby uzyskać więcej szczegółów, zajrzyj do [przewodnika](http://www.yiiframework.com/doc/guide/topics.url).
+
+
+<div class="revision">$Id: final.url.txt 693 2009-02-17 19:16:49Z qiang.xue $</div>
View
109 docs/blog/pl/portlet.base.txt
@@ -0,0 +1,109 @@
+Ustalanie architektury portletu
+=============================
+
+Funkcjonalności takie jak "ostatnie komentarze", "chmurka tagów" najlepiej jest zaimplementować
+w postaci [portletu](http://en.wikipedia.org/wiki/Portlet). Portlet jest podobnym do wtyczki
+komponentem interfejsu użytkownika, który generuje fragment kodu HTML. W części tej opiszemy
+jak ustanowić architekture portletu dla naszej aplikacji blogowej.
+
+Opierając się na analizie wymagań, potrzebujemy czterech różnych portletów:
+portlet logowania, portlet "menu użytkownika", portlet "chmurki tagów" oraz
+portlet "ostatnich postów". Portlety te zostaną umieszczone w bocznym pasku każdej strony.
+
+Tworzenie klasy `Portlet`
+------------------------
+
+Definiujemy klasę o nazwie `Portlet` by służyła jako klasa podstawowa dla wszystkich
+naszych czterech portletów. Klasa bazowa zawiera wspólne właściwości oraz metody
+współdzielone przez wszystkie portlety. Na przykład, definiuje właściwość tytułu `title`,
+która reprezentuj tytuł portletu; definiuje jak ozdobić portlet używając okalającej
+ramki z obramowaniem z kolorowym tłem.
+
+Następujący kod pokazuje definicję klasy bazowej portletu `Portlet`. Ponieważ portlet często
+zawiera zarówno logikę oraz prezentację zdefinowaliśmy klasę `Portlet` jako dziedziczącą
+po [CWidget], co oznacza iż portlet jest [widżetem](http://www.yiiframework.com/doc/guide/basics.view)
+i może zostać osadzony w widoku poprzez użycie metody [widget()|CBaseController::widget].
+
+~~~
+[php]
+class Portlet extends CWidget
+{
+ public $title; // the portlet title
+ public $visible=true; // whether the portlet is visible
+ // ...other properties...
+
+ public function init()
+ {
+ if($this->visible)
+ {
+ // render the portlet starting frame
+ // render the portlet title
+ }
+ }
+
+ public function run()
+ {
+ if($this->visible)
+ {
+ $this->renderContent();
+ // render the portlet ending frame
+ }
+ }
+
+ protected function renderContent()
+ {
+ // klasa pochodna powinna nadpisać tą metodę
+ // aby móc generować swą aktualną zawartość
+ }
+}
+~~~
+
+W powyższym kodzie, metody `init()` oraz `run()` są metodami wymaganymi przez klasę [CWidget]
+i są one wołane automatycznie kiedy widżet jest renderowany w widoku. Klasa dziedzicząca z
+`Portlet` przede wszystkim musi nadpisać metodę `renderContent()` aby wygenerować aktualną
+zawartość portletu.
+
+
+Dostosowywanie układu strony (ang. Customizing Page Layout)
+-----------------------
+
+Nadszedł czas, aby dopasować układ strony, tak, abyśmy mogli umieścić portlety
+w pasku bocznym. Układ strony jest wyłącznie determinowany przez plik widoku układu
+`/wwwroot/blog/protected/views/layouts/main.php`. Generuje on wspólne części
+(np. nagłówek, stopkę) różnych stron i osadza w odpowiednich miejscach dynamiczną
+zawartość, która została wygenerowana przez poszczególne widoki akcji.
+
+Nasz blog będzie używał następującego układu:
+
+~~~
+[php]
+<html>
+<head>
+......
+<?php echo CHtml::cssFile(Yii::app()->baseUrl.'/css/main.css'); ?>
+<title><?php echo $this->pageTitle; ?></title>
+</head>
+
+<body>
+
+...nagłówek...
+
+<div id="sidebar">
+...lista portletów...
+</div>
+
+<div id="content">
+<?php echo $content; ?>
+</div>
+
+...stópka...
+
+</body>
+</html>
+~~~
+
+Poza dostosowaniem pliku widoku układu, musimy również dopasować plik CSS
+`/wwwroot/blog/css/main.css`, tak aby końcowy wygląd miał postać tego co widzimy na
+[blogu demo](http://www.yiiframework.com/demos/blog/). Nie będziemy wchodzić tutaj w szczegóły.
+
+<div class="revision">$Id: portlet.base.txt 682 2009-02-16 05:01:41Z qiang.xue $</div>
View
86 docs/blog/pl/portlet.comments.txt
@@ -0,0 +1,86 @@
+Tworzenie portletu ostatnich komentarzy
+================================
+
+W części tej utworzymy portlet, który wyświetli listę komentarzy, które zostały ostatnie
+opublikowane.
+
+
+Tworzenie klasy `RecentComments`
+-------------------------------
+
+Tworzymy klasę `RecentComments` w pliku `/wwwroot/blog/protected/components/RecentComments.php`.
+Plik ten ma następującą zawartość:
+
+~~~
+[php]
+<?php
+class RecentComments extends Portlet
+{
+ public $title='Recent Comments';
+
+ public function getRecentComments()
+ {
+ return Comment::model()->findRecentComments();
+ }
+
+ protected function renderContent()
+ {
+ $this->render('recentComments');
+ }
+}
+~~~
+
+W powyższym kodzie wywołaliśmy metodę `findRecentComments`, która jest zdefiniowana
+w klasie `Comment` w następujący sposób:
+
+
+~~~
+[php]
+class Comment extends CActiveRecord
+{
+ ......
+
+ public function findRecentComments($limit=10)
+ {
+ $criteria=array(
+ 'condition'=>'Comment.status='.self::STATUS_APPROVED,
+ 'order'=>'Comment.createTime DESC',
+ 'limit'=>$limit,
+ );
+ return $this->with('post')->findAll($criteria);
+ }
+}
+~~~
+
+
+Tworzenie widoku `recentComments`
+-------------------------
+
+Widok `recentComments` jest zapisany w pliku `/wwwroot/blog/protected/components/views/recentComments.php`.
+Widok po prostu wyświetla każdy komentarz zwrócony przez metodę `RecentComments::getRecentComments()`.
+
+
+Używanie portletu `RecentComments`
+------------------------------
+
+Zmodyfikujemy plik układu `/wwwroot/blog/protected/views/layouts/main.php` by osadzić
+w nim ten ostatni portlet.
+
+~~~
+[php]
+......
+<div id="sidebar">
+
+<?php $this->widget('UserLogin',array('visible'=>Yii::app()->user->isGuest)); ?>
+
+<?php $this->widget('UserMenu',array('visible'=>!Yii::app()->user->isGuest)); ?>
+
+<?php $this->widget('TagCloud'); ?>
+
+<?php $this->widget('RecentComments'); ?>
+
+</div>
+......
+~~~
+
+<div class="revision">$Id: portlet.comments.txt 671 2009-02-13 21:55:24Z qiang.xue $</div>
View
141 docs/blog/pl/portlet.login.txt
@@ -0,0 +1,141 @@
+Tworzenie portletu logowania
+======================
+
+Szkielet aplikacji, który stworzyliśmy zawiera stronę logowania. W części tej,
+przekonwertujemy tą stronę na portlet logowania o nazwie `UserLogin`.
+Portlet będzie wyświetlany w pasku bocznym strony, jeśli aktualny użytkownik
+jest nieuwierzytelnionym gościem. Jeśli logowanie powiedzie się, portlet zniknie
+a pojawi się w jego miejsce wcześniej przez nas stworzone portlet z menu użytkownika.
+
+
+Tworzenie klasy `UserLogin`
+--------------------------
+
+Tak jak w przypadku portletu menu użytkownika utworzymy klasę `UserLogin`, która
+będzie zawierała logikę portletu logowania użytkownika oraz zapiszemy ją w pliku
+`/wwwroot/blog/protected/components/UserLogin.php`. Plik ten ma następującą zawartość:
+
+~~~
+[php]
+<?php
+class UserLogin extends Portlet
+{
+ public $title='Login';
+
+ protected function renderContent()
+ {
+ $form=new LoginForm;
+ if(isset($_POST['LoginForm']))
+ {
+ $form->attributes=$_POST['LoginForm'];
+ if($form->validate())
+ $this->controller->refresh();
+ }
+ $this->render('userLogin',array('form'=>$form));
+ }
+}
+~~~
+
+
+Kod w metodzie `renderContent()` został skopiowany z metody `actionLogin()` kontrolera
+`SiteController`, który wygenerowaliśmy na początku przy użyciu narzędzia `yiic`.
+Zmienimy głównie wywołanie metody `render()` na generowanie widoku o nazwie `userLogin`.
+Zauważ również, że utworzyliśmy obiekt klasy `LoginForm` w tej metodzie. Klasa ta
+reprezentuje dane wejściowe użytkownika, które zbieramy z formularza logowania.
+Znajduje się ona w pliku `/wwwroot/blog/protected/models/LoginForm.php`
+i została wygenerowana przez narzędzie `yiic` podczas tworzenia szkieletu aplikacji.
+
+
+Tworzenie widoku `userLogin`
+-------------------------
+
+Zawartość widoku `userLogin` również pochodzi w większej części z widoku `login`
+z akcji `login` kontrolera `SiteController`. Widok zapisany jest w pliku
+`/wwwroot/blog/protected/components/views/userLogin.php` i posiada następującą zawartość:
+
+~~~
+[php]
+<?php echo CHtml::beginForm(); ?>
+<div class="row">
+<?php echo CHtml::activeLabel($form,'username'); ?>
+<br/>
+<?php echo CHtml::activeTextField($form,'username') ?>
+<?php echo CHtml::error($form,'username'); ?>
+</div>
+<div class="row">
+<?php echo CHtml::activeLabel($form,'password'); ?>
+<br/>
+<?php echo CHtml::activePasswordField($form,'password') ?>
+<?php echo CHtml::error($form,'password'); ?>
+</div>
+<div class="row">
+<?php echo CHtml::activeCheckBox($form,'rememberMe'); ?>
+<?php echo CHtml::label('Remember me next time',CHtml::getActiveId($form,'rememberMe')); ?>
+</div>
+<div class="row">
+<?php echo CHtml::submitButton('Login'); ?>
+<p class="hint">You may login with <b>demo/demo</b></p>
+</div>
+<?php echo CHtml::endForm(); ?>
+~~~
+
+W formularzu logowania wyświetlamy pole tekstowe dla nazwy użytkownika oraz pole
+przeznaczone do wpisywania hasła. Wyświetlamy również checkbox wskazujący kiedy stan
+zalogowania użytkownika powinien być zapamiętywany nawet jeśli przeglądarka zostanie
+zamknięta. Widok posiada lokalną zmienną nazwaną `$form`, która pochodzi z danych
+przesłanych z wywołania metody `render()` w `UserLogin::renderContent()`.
+
+Ponieważ model danych `LoginForm` posiada reguły sprawdzania poprawności (podobnie
+jak model `Post`), podczas przesyłania przez użytkownika formularza, model dokona
+operacji sprawdzania poprawności danych. Jeśli wystąpił jakikolwiek błąd podczas sprawdzania
+formularz wyświetli informację o nim tuż przy niepoprawnie wypełnionym polu wejściowym
+przy pomocy [CHtml::error()].
+
+
+Używanie portletu `UserLogin`
+-------------------------
+
+Użyjemy porltetu `UserLogin` podobnie jak `UserMenu` poprzez zmodyfikowania pliku
+widoku `/wwwroot/blog/protected/views/layouts/main.php` w następujący sposób:
+
+~~~
+[php]
+......
+<div id="sidebar">
+
+<?php $this->widget('UserLogin',array('visible'=>Yii::app()->user->isGuest)); ?>
+
+<?php $this->widget('UserMenu',array('visible'=>!Yii::app()->user->isGuest)); ?>
+
+</div>
+......
+~~~
+
+Zauważ, że portlet `UserLogin` jest widoczny tylko gdy aktualny użytkownik jest gościem,
+w przeciwieństwie do portletu `UserMenu`.
+
+
+Testowanie portletu `UserLogin`
+---------------------------
+Aby przetestować portlet `UserLogin`, wykonaj następujące kroki:
+
+ 1. Otwórz adres URL `http://www.example.com/blog/index.php`. Jeśli bieżący użytkownik
+ nie jest zalogowany, powinniśmy być w stanie zobaczyć portlet `UserLogin`.
+ 2. Bez wprowadzania jakichkolwiek danych do formularza logowania, po kliknięciu w przycisk
+ logowania `Login` powinniśmy zobaczyć wiadomości o błędach.
+ 3. Spróbuj zalogować się przy użyciu nazwy użytkownika `demo` oraz hasła `demo`.
+ Aktualna strona zostanie odświeżona, porltet `UserLogin` zniknie a portlet `UserMenu`
+ pojawi się.
+ 4. Kliknij w pozycję menu `Logout` w portecie `UserMenu`, powinieneś zauważyć, ze portlet
+ `UserMenu` zniknął podczas gdy portlet `UserLogin` pojawił się znów.
+
+
+Podsumowania
+-------
+
+Pottlet `UserLogin` jest typowym przykładem, który jest zgodny z wzorcem
+projektowym MVC. Używa on modelu `LoginForm` do reprezentowania danych oraz reguł biznesowych;
+używa on widoku `userLogin` do generowania interfejsu użytkownika oraz używa klasy
+`UserLogin` (jako minikontroler) do zarządzania modelem oraz widokiem.
+
+<div class="revision">$Id: portlet.login.txt 884 2009-03-24 11:08:27Z qiang.xue $</div>
View
150 docs/blog/pl/portlet.menu.txt
@@ -0,0 +1,150 @@
+Tworzenie portletu menu użytkownika
+==========================
+
+W części tej, utworzymy nasz pierwszy, konkretny portlet - portlet menu użytkownika,
+który wyświetla listę pozycji w menu, które są dostępne tylko dla uwierzytelnionych
+użytkowników. Menu zawiera cztery pozycje:
+
+ * zatwierdź komentarz: hiperłącze, które prowadzi do lity komentarzy czekających na zatwierdzenie;
+ * utwórz nową wiadomość: hiperłącze, które prowadzi do strony tworzenia wiadomości;
+ * zarządzanie wiadomości: hiperłącze, które prowadzi do strony zarządzania wiadomościami;
+ * wylogowanie: link będący przyciskiem, który wyloguje aktualnego użytkownika;
+
+
+Tworzenie klasy `UserMenu`
+-------------------------
+
+Tworzymy klasę `UserMenu` opisującą część logiczną portletu menu użytkownika.
+Klasa ta jest zachowana w pliku `/wwwroot/blog/protected/components/UserMenu.php`,
+który posiada następującą zawartość:
+
+~~~
+[php]
+<?php
+class UserMenu extends Portlet
+{
+ public function init()
+ {
+ $this->title=CHtml::encode(Yii::app()->user->name);
+ parent::init();
+ }
+
+ protected function renderContent()
+ {
+ $this->render('userMenu');
+ }
+}
+~~~
+
+Klasa `UserMenu` dziedziczy z klasy `Portlet`, którą wcześniej utworzyliśmy.
+Nadpisuje zarówno metodę `init()` jak i metodę `renderContent()` z klasy `Portlet`.
+Pierwsza metoda ustawia tytuł jako nazwę aktualnego użytkownika; druga generuje
+zawartość ciała portletu poprzez wygenerowanie widoku o nazwie `userMenu`.
+
+> Tip|Wskazówka: Zauważ, że nie dołączamy jawnie klasy portletu `Portlet`,
+nawet jeśli odnosimy się do niej w kodzie. W poprzednich częściach opisaliśmy już
+przyczynę tego, dlaczego tak się dzieje.
+
+
+Tworzenie widoku `userMenu`
+------------------------
+
+Następnie, tworzymy widok `userMenu`, który jest zapisany w pliku
+`/wwwroot/blog/protected/components/views/userMenu.php`:
+
+~~~
+[php]
+<ul>
+<li><?php echo CHtml::link('Approve Comments', array('comment/list'))
+ . ' (' . Comment::model()->pendingCommentCount . ')'; ?></li>
+<li><?php echo CHtml::link('Create New Post',array('post/create')); ?></li>
+<li><?php echo CHtml::link('Manage Posts',array('post/admin')); ?></li>
+<li><?php echo CHtml::linkButton('Logout',array(
+ 'submit'=>'',
+ 'params'=>array('command'=>'logout'),
+)); ?></li>
+</ul>
+~~~
+
+> Info|Info: Domyślnie, pliki widoku dla widżetu powinny się znajdować w podkatalogu
+`views` katalogu zawierającego klasę widżetu. Nazwa pliku powinna być taka sam jak
+nazwa widoku.
+
+W widoku wywołujemy metodę [CHtml::link] aby utworzyć potrzebne hiperłącza;
+wołamy również metodę [CHtml::linkButton] aby utworzyć łącze będące przyciskiem,
+które zadziała tak jak zwykły przycisk. Gdy przycisk jest klikany, przesyła on
+niejawnie formularz do aktualnej strony wraz z parametrem `command` o wartości `logout`.
+
+W celu odpowiedzi na kliknięcie hiperłącza `logout`, potrzebujemy zmodyfikować
+metodę `init()` dla UserMenu` w następujący sposób:
+
+~~~
+[php]
+public function init()
+{
+ if(isset($_POST['command']) && $_POST['command']==='logout')
+ {
+ Yii::app()->user->logout();
+ $this->controller->redirect(Yii::app()->homeUrl);
+ }
+
+ $this->title=CHtml::encode(Yii::app()->user->name);
+ parent::init();
+}
+~~~
+
+W metodzie `init()`, sprawdzamy czy istnieje zmienna `command` w POST, której wartością
+jest `logout`. Jeśli tak, wylogowujemy aktualnego użytkownika i przekierowujemy jego
+przeglądarkę na stronę główną aplikacji. Zauważ, że metoda `redirect()` pośrednio zakończy
+działanie aktualnej aplikacji.
+
+
+Używanie portletu `UserMenu`
+------------------------
+
+Nadszedł już czas, aby użyć nasz nowo ukończony portlet `UserMenu`. Zmienimy
+plik widoku układu `/wwwroot/blog/protected/views/layouts/main.php` następująco:
+
+~~~
+[php]
+......
+
+<div id="sidebar">
+
+<?php $this->widget('UserMenu',array('visible'=>!Yii::app()->user->isGuest)); ?>
+
+</div>
+
+......
+~~~
+
+W powyższym kodzie wołamy metodę `widget()` w celu wygenerowania i wywołania instancji
+klasy `UserMenu`. Ponieważ portlet powinien być wyświetlany tylko dla uwierzytelnionych
+użytkowników, ustawimy jej właściwość `visible` odpowiednio do wartości właściwości `isGuest`
+dla aktualnego użytkownika.
+
+Testowanie portletu `UserMenu`
+--------------------------
+
+Przetestujmy to co dotychczas mamy.
+
+ 1. Otwórz okno przeglądarki i wprowadź URL `http://www.example.com/blog/index.php`.
+ Upewnij się, że nic nie wyświetla się na pasku bocznym tej strony.
+ 2. Kliknij na hiperłącze `Login` i wypełnij formularz logowania. Jeśli zakończy się
+ to sukcesem, sprawdź czy portlet `UserMenu` pojawił się na pasku bocznym i czy jego
+ tytuł zawiera nazwę użytkownika.
+ 3. Kliknij na hiperłącze 'Logout w portlecie `UserMenu`. Sprawdź czy akcja wylogowania
+ powiodła się i portlet `UserMenu` portlet zniknął.
+
+
+Podsumowanie
+-------
+
+To co stworzyliśmy to portlet, który można wielokrotnie, ponownie używać. Możemy
+do w prosty sposób użyć ponownie w innych projektach z drobnymi lub bez żadnych
+modyfikacji. Więcej, zaprojektowanie tego portletu zgadza się ściśle z filozofią
+mówiącą, że warstwa logiczna i prezentacyjna powinny być rozdzielone. Chociaż
+nie zwróciliśmy na to uwagi w poprzednich częsciach, praktyka taka ma miejsce
+niemal wszędzie w typowych aplikacjach opartych na Yii.
+
+<div class="revision">$Id: portlet.menu.txt 671 2009-02-13 21:55:24Z qiang.xue $</div>
View
70 docs/blog/pl/portlet.tags.txt
@@ -0,0 +1,70 @@
+Tworzenie portletu chmurki tagów
+==========================
+
+[Chmurka tagów](http://en.wikipedia.org/wiki/Tag_cloud) wyświetla listę tagów
+wiadomości wraz z wizualnymi ozdobnikami, podpowiadającymi jak bardzo popularny
+jest każdy z tagów.
+
+
+Tworzenie klasy `TagCloud`
+-------------------------
+
+Tworzymy klasę `TagCloud` w pliku `/wwwroot/blog/protected/components/TagCloud.php`.
+Plik ten ma następującą zawartość:
+
+~~~
+[php]
+<?php
+class TagCloud extends Portlet
+{
+ public $title='Tags';
+
+ public function getTagWeights()
+ {
+ return Tag::model()->findTagWeights();
+ }
+
+ protected function renderContent()
+ {
+ $this->render('tagCloud');
+ }
+}
+~~~
+
+Powyżej wywołaliśmy metodę `findTagWeights` zdefiniowaną w klasie `Tag`. Metoda ta
+zwraca listę tagów wraz z ich relatywną wagą częstości. Jeśli tag powiązany jest
+z większą ilością wiadomości, otrzymuje on wyższą wagę. Będziemy używali tych wag
+do kontroli sposoby wyświetlania tagów.
+
+
+Tworzenie widoku `tagCloud`
+-------------------------
+
+Widok `tagCloud` jest zapisany w pliku `/wwwroot/blog/protected/components/views/tagCloud.php`.
+Dla każdego tagu zwróconego przez `TagCloud::getTagWeights()`, wyświetla on hyperlink,
+który będzie wiódł do strony wyświetlającej wiadomości zawierające ten tag. Rozmiar
+czcionki tego linku zależy od wartości wagi tagu. Im większa waga, tym większy rozmiar czcionki.
+
+
+Używanie portletu `TagCloud`
+-------------------------
+
+Używanie portletu chmurki tagów `TagCloud` jest bardzo proste. Modyfikujemy wplik układu
+`/wwwroot/blog/protected/views/layouts/main.php` w następujący sposób:
+
+~~~
+[php]
+......
+<div id="sidebar">
+
+<?php $this->widget('UserLogin',array('visible'=>Yii::app()->user->isGuest)); ?>
+
+<?php $this->widget('UserMenu',array('visible'=>!Yii::app()->user->isGuest)); ?>
+
+<?php $this->widget('TagCloud'); ?>
+
+</div>
+......
+~~~
+
+<div class="revision">$Id: portlet.tags.txt 671 2009-02-13 21:55:24Z qiang.xue $</div>
View
129 docs/blog/pl/post.admin.txt
@@ -0,0 +1,129 @@
+Administrowanie wiadomościami
+==============
+
+Administrowanie wiadomościami przede wszystkim odnosi się do wylistowania wiadomości
+w widoku administratora oraz usuwania wiadomości. Są one wykonywane odpowiednio przez operację
+`admin` oraz operację usuwania `delete`. Kod wygenerowany przez narzędzie `yiic` nie
+wymaga zbytniej modyfikacji. Poniżej wyjaśniamy przede wszystkim jak te dwie operacje
+zostały zaimplementowane.
+
+Wyświetlenie listy wiadomości w widoku tabelarycznym
+-----------------------------
+
+Operacja `admin` wyświetla wszystkie wiadomości (włączając zarówno opublikowane jak i nieopublikowane)
+w widoku tabelarycznym. Widok wspiera wielokolumnowe sortowanie oraz stronicowanie. Poniżej
+znajduje się metoda `actionAdmin()` kontrolera `PostController`:
+
+~~~
+[php]
+public function actionAdmin()
+{
+ $criteria=new CDbCriteria;
+
+ $pages=new CPagination(Post::model()->count());
+ $pages->applyLimit($criteria);
+
+ $sort=new CSort('Post');
+ $sort->defaultOrder='status ASC, createTime DESC';
+ $sort->applyOrder($criteria);
+
+ $posts=Post::model()->findAll($criteria);
+
+ $this->render('admin',array(
+ 'posts'=>$posts,
+ 'pages'=>$pages,
+ 'sort'=>$sort,
+ ));
+}
+~~~
+
+Powyższy kod jest bardzo podobny do tego z akcji `actionList()`. Główna różnica to, że
+tutaj używamy obiektu [CSort] do reprezentowania informacji o sortowaniu (np. która
+kolumna będzie posortowana i w jakim kierunku). Obiekt [CSort] jest używany przez
+widok `admin` w celu generowania odpowiednich odnośników w komórkach nagłówkowych tabelki.
+Klikanie na tych linkach spowoduje, że aktualna strona zostanie odświeżona a dane
+zostaną posortowane przy użyciu tej kolumny.
+
+Poniżej znajduje się kod dla widoku `admin`:
+
+~~~
+[php]
+<h2>Manage Posts</h2>
+
+<table class="dataGrid">
+ <tr>
+ <th><?php echo $sort->link('status'); ?></th>
+ <th><?php echo $sort->link('title'); ?></th>
+ <th><?php echo $sort->link('createTime'); ?></th>
+ <th><?php echo $sort->link('updateTime'); ?></th>
+ </tr>
+<?php foreach($posts as $n=>$post): ?>
+ <tr class="<?php echo $n%2?'even':'odd';?>">
+ <td><?php echo CHtml::encode($post->statusText); ?></td>
+ <td><?php echo CHtml::link(CHtml::encode($post->title),
+ array('show','id'=>$post->id)); ?></td>
+ <td><?php echo date('F j, Y',$post->createTime); ?></td>
+ <td><?php echo date('F j, Y',$post->updateTime); ?></td>
+ </tr>
+<?php endforeach; ?>
+</table>
+
+<br/>
+<?php $this->widget('CLinkPager',array('pages'=>$pages)); ?>
+~~~
+
+Kod ten jest bardzo prosty. Iterujemy listę wiadomości i wyświetlamy je w postaci tabelki.
+W komórkach nagłówkowych tabelki używamy obiektu [CSort] do wygenerowania odnośników w celu
+sortowania. Na końcu, osadzamy widżet [CLinkPager] aby wyświetlał przyciski stronicowania
+jeśli zajdzie taka potrzeba.
+
+> Tip|Wskazówka: Podczas wyświetlania tekstu wołamy metodę [CHtml::encode()] do zakodowania
+znajdujących się w nim wpisów HTML. Chroni to przed [atakami XSS](http://www.yiiframework.com/doc/guide/topics.security).
+
+
+Usuwanie wiadomości
+--------------
+
+Podczas gdy wiadomość jest wyświetlana przy użyciu operacji `show`, pokazujemy link
+`delete` jeśli aktualny użytkownik jest właścicielem systemu. Kliknięcie na tym przycisku
+spowoduje usunięcie wiadomości. Ze względu na to, że usunięcie wiadomości zmienia dane
+po stronie serwera, używamy żądania POST aby wywołać usuwanie. Zatem, używamy następującego
+kodu do wygenerowania przycisku usuwania `delete`:
+
+~~~
+[php]
+<?php echo CHtml::linkButton('Delete',array(
+ 'submit'=>array('post/delete','id'=>$post->id),
+ 'confirm'=>"Are you sure to delete this post?",
+)); ?>
+~~~
+
+Metoda [CHtml::linkButton()] generuje przycisk łącza (ang. link button), który
+tak jak zwykły przycisk. Kliknięcie na ten link spowoduje przesłanie zakodowanego
+formularza HTML w postaci metody POST. Tutaj określamy, że formularz powinien zostać
+przesłany do URLa wygenerowane zgodnie z `array('post/delete','id'=>$post->id)`.
+W naszej aplikacji bloga wygenerowany URL będzie miał następującą postać
+`/blog/index.php?r=post/delete&id=1`, która odpowiada akcji `delete` w kontrolerze `PostController`.
+Określiliśmy również, że okno zawierające potwierdzeniem powinno wyskoczyć w momencie
+kliknięcia na ten link. Daje to użytkownikowi szansę na ponowne rozpatrzenie swojego
+żądania usunięcia.
+
+Kod dla operacji `delete` jest oczywisty. Nie będziemy do wyjaśniać tutaj.
+
+~~~
+[php]
+public function actionDelete()
+{
+ if(Yii::app()->request->isPostRequest)
+ {
+ // we only allow deletion via POST request
+ $this->loadPost()->delete();
+ $this->redirect(array('list'));
+ }
+ else
+ throw new CHttpException(400,'Invalid request...');
+}
+~~~
+
+
+<div class="revision">$Id: post.admin.txt 1050 2009-05-22 20:06:18Z qiang.xue $</div>
View
201 docs/blog/pl/post.create.txt
@@ -0,0 +1,201 @@
+Tworzenie i aktualizowanie wiadomości
+===========================
+
+Mając gotowy model wiadomości `Post`, potrzebujemy dopracować akcje oraz widoki dla
+kontrolera wiadomości `PostController`. W części tej najpierw dostosujemy kontrolę dostępności
+operacji CRUD; następnie zmodyfikujemy kod implementujący operacje tworzenia `create` oraz
+aktualizowania `update`; na koniec zaimplementujemy funkcję podglądu dla obu operacji.
+
+
+Dostosowywanie kontroli dostępności
+--------------------------
+
+Pierwszą rzeczą jaką chcemy zrobić jest dostosowanie
+[kontroli dostępu](http://www.yiiframework.com/doc/guide/topics.auth#access-control-filter)
+ze względu na to, że kod wygenerowany przez `yiic` nie pasuje do naszych wymagań.
+
+Modyfikujemy metodę `accessRules()` w pliku `/wwwroot/blog/protected/controllers/PostController.php`
+w następujący sposób:
+
+~~~
+[php]
+public function accessRules()
+{
+ return array(
+ array('allow', // zezwól wszystkim użytkownikom wykonać akcje 'list' oraz 'show'
+ 'actions'=>array('list', 'show'),
+ 'users'=>array('*'),
+ ),
+ array('allow', // zezwól uwierzytelnionym użytkownikom wykonywać każdą akcję
+ 'users'=>array('@'),
+ ),
+ array('deny', // odmów wszystkim użytkownikom
+ 'users'=>array('*'),
+ ),
+ );
+}
+~~~
+
+Powyższe reguły oznaczają, że wszyscy użytkownicy mogą uzyskać dostęp do akcji
+listowania `list` oraz wyświetlania `show`, zaś uwierzytelnieni użytkownicy mogą
+uzyskać dostęp do każdej akcji, włączając w to akcję administratora `admin`.
+Użytkownik powinien spotkać się z odmową dla każdego innego scenariusza.
+Zauważ, że reguły te przetwarzane są w kolejności w jakiej zostały tutaj pokazane.
+Pierwsza reguła pasująca do aktualnego kontekstu decyduje o uzyskaniu dostępu.
+Na przykład, jeśli aktualny użytkownik to właściciel systemu, który próbuje
+odwiedzić stronę służącą do tworzenia wiadomości, druga reguła będzie pasowała
+i zagwarantuje ona dostęp dla tego użytkownika.
+
+Dostosowywanie operacji tworzenia `create` oraz aktualizacji `update`
+--------------------------------------------
+
+Operacje tworzenia `create` i aktualizacji `update` są bardzo podobne.
+Obie potrzebują wyświetlić formularz HTML w celu zebrania danych wejściowych od użytkownika,
+sprawdzenia ich poprawności i zapisania ich w bazie danych. Główną różnicą jest to,
+że operacja aktualizacji `update` wypełni wstępnie formularz danymi z istniejącej
+wiadomości znalezionymi w bazie danych. Z tego też powodu, narzędzie `yiic`
+generuje częściowy widok`/wwwroot/blog/protected/views/post/_form.php`, który jest
+osadzany w obu widokach `create` oraz `update` w celu wygenerowania potrzebnego formularza HTML.
+
+Na początek zmienimy plik `_form.php`, tak że formularz HTML będzie zbierał jedynie
+dane wejściowe, które chcemy: tytuł, `title`, zawartość `content` oraz status `status`.
+Używamy pól ze zwykłym tekstem aby zebrać dane wejściowe dla pierwszych dwóch atrybutów
+oraz listy rozwijanej do zebrania danych wejściowych dla statusu `status`. Opcje listy rozwijanej
+stanowią teksty wyświetlające dopuszczalne statusy wiadomości:
+
+~~~
+[php]
+<?php echo CHtml::activeDropDownList($post,'status',Post::model()->statusOptions); ?>
+~~~
+
+> Tip|Wskazówka: W powyższym kodzie, możemy również użyć `Post::model()->getStatusOptions()` zamiast
+`Post::model()->statusOptions` aby zwrócić dopuszczalne opcje statusu. Powodem dla którego
+używamy ostatniego wyrażenia jest to, że `Post` jest komponentem, który pozwala nam uzyskać
+dostęp do [właściwości](http://www.yiiframework.com/doc/guide/basics.component#component-property)
+zdefiniowanych pod postacią metod getterów.
+defined in terms of getter methods.
+
+Następnie zmodyfikujemy klasę wiadomości `Post`, tak, że będzie ona automatycznie ustawiać
+pewne atrybuty (np. czas utworzenia `createTime`, ID autora `authorId`) zanim wiadomość
+zapisywana jest do bazy danych. Nadpisujemy metodę `beforeValidate()` następująco:
+
+~~~
+[php]
+protected function beforeValidate($on)
+{
+ $parser=new CMarkdownParser;
+ $this->contentDisplay=$parser->safeTransform($this->content);
+ if($this->isNewRecord)
+ {
+ $this->createTime=$this->updateTime=time();
+ $this->authorId=Yii::app()->user->id;
+ }
+ else
+ $this->updateTime=time();
+ return true;
+}
+~~~
+
+W metodzie tej używamy [CMarkdownParser] aby skonwertować zawartość w
+[formacie Markdown](http://daringfireball.net/projects/markdown/) do postaci HTML
+i zapisania rezultatu do `contentDisplay`. Pozwala to uniknąć powtarzającej się
+konwersji pomiędzy formatami podczas wyświetlania wiadomości. Jeśli mamy do czynienia
+z nową wiadomości, ustawiamy jej atrybuty czas utworzenia `createTime` oraz
+ID autora `authorId`; w przeciwnym przypadku ustawiamy datę aktualizacji `updateTime`
+aby wskazywała aktualny czas. Zauważ, że metoda ta będzie wywołana automatycznie
+gdy wywołamy metodę sprawdzenia poprawności `validate()` lub zapisu `save()` modelu.
+
+
+Ponieważ chcemy zapisać otagowania wiadomości do tabeli `Tag` potrzebujemy również następującej
+metody w klasie `Post`, która będzie wywołana automatycznie po zapisaniu wiadomości
+do bazy danych:
+
+~~~
+[php]
+protected function afterSave()
+{
+ if(!$this->isNewRecord)
+ $this->dbConnection->createCommand(
+ 'DELETE FROM PostTag WHERE postId='.$this->id)->execute();
+
+ foreach($this->getTagArray() as $name)
+ {
+ if(($tag=Tag::model()->findByAttributes(array('name'=>$name)))===null)
+ {
+ $tag=new Tag(array('name'=>$name));
+ $tag->save();
+ }
+ $this->dbConnection->createCommand(
+ "INSERT INTO PostTag (postId, tagId) VALUES ({$this->id},{$tag->id})")->execute();
+ }
+}
+
+public function getTagArray()
+{
+ // rozbij ciąg tagów na zestaw tagów
+ return array_unique(
+ preg_split('/\s*,\s*/',trim($this->tags),-1,PREG_SPLIT_NO_EMPTY)
+ );
+}
+~~~
+
+Powyżej, najpierw czyścimy tabelę `PostTag` z wierszy powiązanych z aktualną wiadomością.
+Następnie wstawiamy nowe tagi do tabeli `Tag` i dodajemy referencję do tabeli `PostTag`.
+Zawarta tu logika jest trochę złożona. Zamiast używania
+[rekordu aktywnego](http://www.yiiframework.com/doc/guide/database.ar)
+napisaliśmy czyste zapytanie SQL i wywołaliśmy je przy użyciu połączenia do bazy danych.
+
+> Tip|Wskazówka: Jest dobrym zwyczajem trzymać logikę biznesową, taką jak powyższy kod
+`beforeValidate()` oraz `afterSave()` w modelu zamiast w kontrolerze.
+
+
+Implementacja funkcji podglądu
+----------------------------
+
+Poza powyższymi dostosowaniami, chcemy również dodać funkcję podglądu, która pozwoli nam
+podejrzeć wiadomość zanim zapiszemy ją do bazy danych.
+
+Najpierw zmienimy plik widoku `_form.php` w celu dodatnia przycisku podglądu `preview`
+oraz wyświetlenia podglądu. Podgląd jest wyświetlany jedynie wtedy, gdy przycisk
+podglądu został naciśnięty i nie wystąpił żaden błąd podczas sprawdzania poprawności.
+
+~~~
+[php]
+<?php echo CHtml::submitButton('Preview',array('name'=>'previewPost')); ?>
+......
+<?php if(isset($_POST['previewPost']) && !$post->hasErrors()): ?>
+...wyświetl podgląd wiadomości $post tutaj...
+<?php endif; ?>
+~~~
+
+Następnie zmieniamy metody `actionCreate()` oraz `actionUpdate()` kontrolera `PostController`
+aby odpowiadały na żądanie podglądu. Poniżej prezentujemy zaktualizowany kod
+`actionCreate()`, który jest bardzo podobny do tego w `actionUpdate()`:
+
+~~~
+[php]
+public function actionCreate()
+{
+ $post=new Post;
+ if(isset($_POST['Post']))
+ {
+ $post->attributes=$_POST['Post'];
+ if(isset($_POST['previewPost']))
+ $post->validate();
+ else if(isset($_POST['submitPost']) && $post->save())
+ $this->redirect(array('show','id'=>$post->id));
+ }
+ $this->render('create',array('post'=>$post));
+}
+~~~
+
+W powyższym kodzie, jeśli przycisk podglądu został kliknięty, wywołujemy metodę
+`$post->validate()` aby sprawdzić poprawność danych wejściowych; w przeciwnym przypadku
+jeśli przycisk submit (prześlij) został kliknięty, spróbujemy zapisach wiadomość
+poprzez wywołanie metody `$post->save()`, która w ukryciu dokona sprawdzenia poprawności.
+Jeśli zapis powiódł się (nie wystąpiły żadne błędy sprawdzani poprawności i dane zostały
+zapisane do bazy danych bez błędów), przekierowujemy przeglądarkę użytkownika
+aby pokazała nowo utworzoną wiadomość.
+
+
+<div class="revision">$Id: post.create.txt 763 2009-02-27 14:42:50Z qiang.xue $</div>
View
118 docs/blog/pl/post.display.txt
@@ -0,0 +1,118 @@
+Wyświetlanie wiadomości
+================
+
+W naszej aplikacji blogowej wiadomość może być wyświetlana wśród listy wiadomości lub
+samodzielnie. Pierwszy przypadek zaimplementowany jest jako operacja `list`,
+drugi przypadek jako operacja `show`. W części tej, dostosujemy obie operacje,
+aby spełniały nasze początkowe wymagania.
+
+Dostosowywanie operacji wyświetlania `show`
+----------------------------
+
+Operacja wyświetlania `show` jest zaimplementowana poprzez metodę `actionShow()`
+w kontrolerze `PostController`. To co wyświetla jest generowane przez widok `show`
+znajdujący się w pliku `/wwwroot/blog/protected/views/post/show.php`.
+
+Poniżej znajduje się odpowiedni kod implementujący operację wyświetlania `show`
+w kontrolerze `PostController`:
+
+~~~
+[php]
+public function actionShow()
+{
+ $this->render('show',array(
+ 'post'=>$this->loadPost(),
+ ));
+}
+
+private $_post;
+
+protected function loadPost($id=null)
+{
+ if($this->_post===null)
+ {
+ if($id!==null || isset($_GET['id']))
+ $this->_post=Post::model()->findbyPk($id!==null ? $id : $_GET['id']);
+ if($this->_post===null || Yii::app()->user->isGuest &&
+ $this->_post->status!=Post::STATUS_PUBLISHED)
+ throw new CHttpException(404,'The requested post does not exist.');
+ }
+ return $this->_post;
+}
+~~~
+
+Nasza zmiana dotyczy przede wszystkim metody `loadPost()`. W metodzie tej odpytujemy
+tabelę z wiadomościami `Post` w zależności od parametru `id` GET. Jeśli wiadomość nie
+została znalezione lub jeśli nie jest ona opublikowana (gdy użytkownik jest gościem),
+rzucimy błędem HTTP 404. W przeciwnym przypadku obiekt wiadomości jest zwracany
+do metody `actionShow()`, która z kolei przekazuje obiekt wiadomości do widoku `show`
+w celu późniejszego jej wyświetlenia.
+
+> Tip|Wskazówka: Yii przechwytuje wyjątki HTTP (instancje [CHttpException]) i wyświetla
+je na stronie błędów używającej pewnych predefiniowanych szablonów. Szablony te
+można dostosować dla aplikacji, co zostanie opisane w szczegółach na końcu tego samouczka.
+
+Zmiana w widoku `show` dotyczy przede wszystkim dostosowania formatowania oraz stylów
+wyświetlenia wiadomości. Nie będziemy tutaj wdawać się w szczegóły.
+
+
+Dostosowywanie operacji wyświetlenia listy `list`
+----------------------------
+
+Podobnie jak operację wyświetlenie `show`, dostosujemy operację wyświetlenia listy `list`
+w dwóch miejscach: metodzie `actionList()` w kontrolerze `PostController` oraz
+pliku widoku `/wwwroot/blog/protected/views/post/list.php`. Musimy przede wszsytkim dodać
+wsparcie dla wyświetlania listy wiadomości, które powiązane są z określonym tagiem.
+
+Poniżej znajduje się zmieniona metoda `actionList()` kontrolera `PostController`:
+
+~~~
+[php]
+public function actionList()
+{
+ $criteria=new CDbCriteria;
+ $criteria->condition='status='.Post::STATUS_PUBLISHED;
+ $criteria->order='createTime DESC';
+
+ $withOption=array('author');
+ if(!empty($_GET['tag']))
+ {
+ $withOption['tagFilter']['params'][':tag']=$_GET['tag'];
+ $postCount=Post::model()->with($withOption)->count($criteria);
+ }
+ else
+ $postCount=Post::model()->count($criteria);
+
+ $pages=new CPagination($postCount);
+ $pages->applyLimit($criteria);
+
+ $posts=Post::model()->with($withOption)->findAll($criteria);
+
+ $this->render('list',array(
+ 'posts'=>$posts,
+ 'pages'=>$pages,
+ ));
+}
+~~~
+
+W powyższym kodzie, najpierw tworzymy kryteria, które mówią, że wyłącznie opublikowane
+wiadomości powinny zostać wylistowane i posortowane zgodnie z ich datą utworzenia
+w porządku malejącym. Następnie obliczamy całkowitą ilość wiadomości spełniających
+kryteria. Ilość ta jest używana przez komponent stronicowania, aby poprawnie obliczyć
+na jak wielu stronach powinna zostać wyświetlona lista wiadomości. Na koniec pobieramy
+dane wiadomości z bazy danej i przesyłamy je do widoku `list` aby je wyświetlić.
+
+Zauważ, że jeśli w zmiennej GET znajduje się parametr `tag`, będziemy odpytywać bazę,
+przy użyciu relacji `tagFilter` używając odpowiednich wartości parametru GET.
+Dołączenie `tagFilter` do zapytania relacyjnego zapewni, że tylko pojedyncze zapytanie SQL JOIN
+jest używane aby uzyskać wiadomości o określonym tagu. Bez tego wywołania, Yii podzieli
+zapytanie na dwa oddzielne zapytania SQL (w powodu wydajności) i zwróci niepoprawny rezultat.
+
+Dwie zmienne są przekazywane do widoku `list`: wiadomości `$posts` oraz strony `$pages`.
+Pierwsza określa listę wiadomości, które mają zostać wyświetlone, druga zaś zawiera
+informacje o stronicowaniu (np. jak wiele stron mamy w ogóle, jaką mamy aktualnie stronę).
+Widok `list` zawiera [widżet](http://www.yiiframework.com/doc/guide/basics.view#widget)
+stronicowania, który automatycznie wyświetla wiadomości w oddzielnych stronach jeśli
+jest ich zbyt wiele.
+
+<div class="revision">$Id: post.display.txt 1200 2009-07-04 17:46:06Z qiang.xue $</div>
View
204 docs/blog/pl/post.model.txt
@@ -0,0 +1,204 @@
+Dostosowywanie modelu wiadomości
+======================
+
+Klasa postu `Post`, wygenerowana przez narzędzie `yiic` musi zostać zmodyfikowana przede głównie w trzech miejscach:
+
+ - w metodzie `rules()`: określającej reguły sprawdzania poprawności dla atrybutów modelu;
+ - w metodzie `relations()`: definiującej obiekty pokrewne;
+ - w metodzie `safeAttributes()`: określającej które atrybuty mogą zostać masowo przypisane (używanie głównie podczas przekazywania danych wejściowych użytkownika do modelu);
+
+> Info|Info: [Model](http://www.yiiframework.com/doc/guide/basics.model) zawiera listę
+atrybutów, każdy z nich jest powiązany z odpowiadającą mu kolumną w bazie danych.
+Atrybuty mogą być zadeklarowane bezpośrednio jako jako zmienne klasy lub też pośrednio bez żadnej deklaracji.
+
+
+Dostosowywanie metody `rules()`
+----------------------------
+
+Najpierw określamy zasady sprawdzania poprawności, które pozwalają nam upewnić się,
+że wartości atrybutów dostarczone przez dane wprowadzone przez użytkownika są poprawne,
+zanim zostaną zapisane do bazy danych. Na przykład, atrybut `status` dla postu `Post`
+powinien posiadać wartość 0, 1 lub 2. Narzędzie `yiic` również generuje zasady sprawdzania
+poprawności dla każdego modelu. Jednakże, reguły te bazują na infomracjach o kolumnie
+tabeli i mogą być nieodpowiednie.
+
+W oparciu o analizę potrzeb, modyfikujemy metodę `rules()` w następujący sposób:
+
+~~~
+[php]
+public function rules()
+{
+ return array(
+ array('title, content, status', 'required'),
+ array('title', 'length', 'max'=>128),
+ array('status', 'in', 'range'=>array(0, 1, 2)),
+ array('tags', 'match', 'pattern'=>'/^[\w\s,]+$/',
+ 'message'=>'Tagi mogą posiadać wyłącznie znaki słów.'),
+ );
+}
+~~~
+
+W powyższym kodzie określiliśmy, że atrybuty tytułu `title`, zawartości `content` i statusu `status` są
+atrybutami wymaganymi (muszą być wypełnione); długość tytułu `title` nie powinna
+przekraczać 128 (znaków); wartość atrybutu statusu `status` powinna być 0 (wersja robocza, ang. draft),
+1 (opublikowana, ang. published) lub 2 (zarchiwizowana, ang. archived); a atrybut otagowania `tags`
+powinien zawierać wyłącznie znaki słów oraz przecinki. Wszystkie pozostałe atrybuty
+(np. `id`, `createTime`) nie będą sprawdzane ponieważ ich wartości nei pochodzą
+za danych wprowadzonych przez użytkownika.
+
+Po wprowadzeniu tych zmian, możemy odwiedzić ponownie stronę tworzenia postów
+w celu weryfikacji czy nowe zasady sprawdzania poprawności mają miejsce.
+
+> Info|Info: Reguły sprawdzania poprawności używane są podczas wywołania metody
+[validate()|CModel::validate] lub metody [save()|CActiveRecord::save] instancji modelu.
+Aby uzyskać więcej informacji o tym jak budować reguły walidacji, spójrz do [Przewodnika](http://www.yiiframework.com/doc/guide/form.model#declaring-validation-rules).
+
+
+Dostosowywanie metody `safeAttributes()`
+-------------------------------------
+
+Następnie dostosowujemy metodę `safeAttributes()` aby określić, które atrybuty mogą zostać
+grupowo przypisane. Podczas przekazywania danych wprowadzonych przez użytkownika do instancji
+modelu, często używamy następującego grupowego przypisania w celu uproszczenia naszego kodu:
+
+~~~
+[php]
+$post->attributes=$_POST['Post'];
+~~~
+
+Bez używanie powyższego grupowego przypisanie, mielibyśmy do czynienia z następującym, długim kodem:
+
+~~~
+[php]
+$post->title=$_POST['Post']['title'];
+$post->content=$_POST['Post']['content'];
+......
+~~~
+
+Chociaż grupowe przypisanie jest bardzo wygodne, posiada ono potencjalnie niebezpieczeństwo,
+ że złosliwy użytkownik może próbować wypełnić atrybut, którego wartość powinna
+ być tylko do odczytu luv też powinna być zmieniana przez programistę wyłącznie w kodzie.
+ Na przykład, ID postu `id`, który jest aktualnie aktualizowany nie powinno zostać zmienione.
+
+Aby zabezpieczyć się przed takim niebezpieczeństwem powinniśmy dostosować metodę `safeAttributes()`
+następująco, tak, że pozwala ona tylko atrybutom tytułu `title`, zawartości `content`, statusu `status`
+i otagowania `tags` być przypisanymi grupowo:
+
+~~~
+[php]
+public function safeAttributes()
+{
+ return array('title', 'content', 'status', 'tags');
+}
+~~~
+
+> Tip|Wskazówka: Prostym sposobem do zidentyfikownia, które atrybuty powinny być
+umieszczone na bezpiecznej liście jest obserwacja formularza HTML, który jest
+używany do zbierania danych od użytkownika. Atrybuty modelu, które pojawiają się
+w formularzu w celu otrzymania danych wprowadzonych przez użytkownika mogą zostać
+zadeklarowane jako bezpieczne. Ponieważ te atrybuty otrzymywane są z wejść wprowadzonych
+przez użytkowników końcowych, powinny one zazwyczaj być powiązane z pewnymi regułami
+sprawdzania poprawności.
+
+
+Dostosowywanie metody `relations()`
+--------------------------------
+
+Na samym końcu dostosowujemy metodę `relations()` w celu zdefiniowania obiektów powiązanych
+przez do wiadomości. Poprzez zadeklarowanie tych powiązanych obiektów w metodzie
+`relations()`, możemy wykorzystać potęgę funkcji
+[relacyjnego aktywnego rekordu (RAR)](http://www.yiiframework.com/doc/guide/database.arr)
+w celu uzyskanie dostępu do informacji z powiązanych z wiadomością obiektów, takich jak
+jej autor oraz komentarze, bez potrzeby pisanie złożonych wyrażeń SQL z JOIN.
+
+Dostosowujemy metodę `relations()` w następujący sposób:
+
+~~~
+[php]
+public function relations()
+{
+ return array(
+ 'author'=>array(self::BELONGS_TO, 'User', 'authorId'),
+ 'comments'=>array(self::HAS_MANY, 'Comment', 'postId',
+ 'order'=>'??.createTime'),
+ 'tagFilter'=>array(self::MANY_MANY, 'Tag', 'PostTag(postId, tagId)',
+ 'together'=>true,
+ 'joinType'=>'INNER JOIN',
+ 'condition'=>'??.name=:tag'),
+ );
+}
+~~~
+
+Powyższe relacje stwierdzają, że:
+
+ * Wiadomość należy do autora, którego reprezentuje klasa `User` a relacja pomiędzy nimi zostaje określona
+ w oparciu o wartość atrybutu `authorId` wiadomości;
+ * Wiadomość posiada wiele komentarzy, które reprezentuje klasa `Comment` a relacja
+ pomiędzy nimi zostaje określona w oparciu o wartość atrybutu `postId` tych komentarzy.
+ Komentarze te powinny zostać posortowane odpowiednio do czasu ich utworzenia.
+
+
+Relacja filtru tagów `tagFilter` jest trochę złożona. Jest ona używana do jawnego połączenia
+tabeli wiadomości `Post` z tabelą otagowania `Tag` i wybrania tylko tych wierszy zawierających
+określony tag. Pokażemy jak używać tej relacji gdy zaimplementujemy funkcjonalność
+wyświetlania wiadomości.
+
+Przy użyciu powyższej deklaracji relacji, możemy w łatwy sposób uzyskać dostęp do
+autora oraz komentarzy wiadomości w następujący sposób:
+
+~~~
+[php]
+$author=$post->author;
+echo $author->username;
+
+$comments=$post->comments;
+foreach($comments as $comment)
+ echo $comment->content;
+~~~
+
+Aby uzyskać więcej informacji o tym jak deklarować i używać relacji, sprawdź [Poradnik](http://www.yiiframework.com/doc/guide/database.arr).
+
+
+Reprezentacja statusu pod postacią tekstu
+---------------------------
+
+Ponieważ status wiadomości jest przechowywany jako wartość całkowita (integer) w bazie danych
+potrzebujemy dostarczyć jego reprezentacji tekstowej, po to aby była ona wyświetlona w bardziej
+przystępnym formacie dla użytkownika końcowego. Z tego też powodu, zmodyfikujemy model `Post`
+w następujący sposób:
+
+~~~
+[php]
+class Post extends CActiveRecord
+{
+ const STATUS_DRAFT=0;
+ const STATUS_PUBLISHED=1;
+ const STATUS_ARCHIVED=2;
+
+ ......
+
+ public function getStatusOptions()
+ {
+ return array(
+ self::STATUS_DRAFT=>'Draft',
+ self::STATUS_PUBLISHED=>'Published',
+ self::STATUS_ARCHIVED=>'Archived',
+ );
+ }
+
+ public function getStatusText()
+ {
+ $options=$this->statusOptions;
+ return isset($options[$this->status]) ? $options[$this->status]
+ : "unknown ({$this->status})";
+ }
+}
+~~~
+
+W powyższym kodzie zdefiniowaliśmy stałe w klasie w celu reprezentowania możliwych wartości
+statusów. Stałe te są przede wszystkim używane w kodzie, aby uczynić go łatwiejszym w utrzymaniu.
+Definiujemy również metodę `getStatusOptions()`, która zwraca mapowanie pomiędzy wartością całkowitą
+statusu a wyświetlanym tekstem. I wreszcie, definiujemy metodę `getStatusText()`,
+która po prostu zwraca tekstowy status wyświetlany użytkownikowi końcowemu.
+
+<div class="revision">$Id: post.model.txt 796 2009-03-09 02:17:07Z qiang.xue $</div>
View
133 docs/blog/pl/prototype.auth.txt
@@ -0,0 +1,133 @@
+Uwierzytelnianie użytkownika
+===================
+
+Nasza aplikacja blogu potrzebuje umieć rozróżnić użytkownika systemy od gościa. Dlatego też, potrzebujemy
+zaimplementować funkcjonalność [uwierzytelniania użytkowników](http://www.yiiframework.com/doc/guide/topics.auth).
+
+
+Jak zapewnie się zorientowałeś szkielet aplikacji dostarcza już uwierzytelnienia użytkownika
+poprzez sprawdzanie czy nazwa użytkownika i pasmo to jednocześnie `demo` lub `admin`.
+W części tej zmodyfikujemy odpowiadający temu kod, tak aby uweerzytelnienie odbywało się
+w oparciu o bazę danych `User`.
+
+Uwierzytelnienie użytkownika wykonywane jest w klasie implementującej interfejs [IUserIdentity].
+Szkielet aplikacji używa do tego celu klasy `UserIdentity`. Klasa ta znajduje się
+w pliku `/wwwroot/blog/protected/components/UserIdentity.php`.
+
+> Tip|Wskazówka: Dla wygody, nazwa pliku klasy musi być taka sama jak odpowiadają
+mu nazwa klasy i zakończona rozszerzeniem `.php`. Używając tej konwencji, można
+odnosić się do klasy używając [aliasów ścieżek](http://www.yiiframework.com/doc/guide/basics.namespace).
+Na przykład, możemy odnosić się do klasy `UserIdentity` przy użyciu aliasu
+`application.components.UserIdentity`. Wiele API w Yii jest w stanie rozpoznawać
+aliasy ścieżek (np. [Yii::createComponent()|YiiBase::createComponent]),
+dodatkowo używanie aliasów, pozwala uniknąć konieczności umieszczania absolutnych ścieżek do plików w kodzie.
+Występowanie tych ostatnich często powoduje problemy podczas wdrażania aplikacji.
+
+Zmodyfikujemy klasę `UserIdentity` w następujący sposób:
+
+~~~
+[php]
+<?php
+class UserIdentity extends CUserIdentity
+{
+ private $_id;
+
+ public function authenticate()
+ {
+ $username=strtolower($this->username);
+ $user=User::model()->find('LOWER(username)=?',array($username));
+ if($user===null)
+ $this->errorCode=self::ERROR_USERNAME_INVALID;
+ else if(md5($this->password)!==$user->password)
+ $this->errorCode=self::ERROR_PASSWORD_INVALID;
+ else
+ {
+ $this->_id=$user->id;
+ $this->username=$user->username;
+ $this->errorCode=self::ERROR_NONE;
+ }
+ return !$this->errorCode;
+ }
+
+ public function getId()
+ {
+ return $this->_id;
+ }
+}
+~~~
+
+W metodzie `authenticate(), używamy klasy `User` do wyszukiwania wiersza danych w tabeli
+`User`, którego kolumna `username` posiada tą samą wartość co nazwa użytkownika, bez uwzględnienia
+wielkości liter. Przypominamy, że klasa `User` została utworzona za pomocą narzędzia
+`yiic` w poprzedniej części. Ponieważ klasa `User` dziedziczy z [CActiveRecord],
+ możemy wykorzystać [funkcjonalność rekordu aktywnego](http://www.yiiframework.com/doc/guide/database.ar)
+ i uzyskać dostęp do tabeli `User` w obiektowy (OOP) sposób.
+
+W klasie `UserIdentity` nadpisujemy również metodę `getId()`, która zwraca wartość `id`
+dla znalezionego w tabeli `User` użytkownika. Poprzednia implementacja zwracała nazwę użytkownika (username).
+Obie właściwości: nazwa użytkownika `username` oraz jego ID `id` będą zachowane w sesji użytkownika user
+oraz będzie można do nich uzyskać dostęp poprzez `Yii::app()->user` z dowolnego miejsca w naszym kodzie.
+
+
+> Tip|Wskazówka: W klasie `UserIdentity` odnosimy się do klasy [CUserIdentity] bez
+bezpośredniego włączania odpowiadającego jej pliku klasy. Dzieje się tak, gdyż
+klasa [CUserIdentity] jest rdzenną klasą dostarczaną przez framework Yii.
+Yii automatycznie załączy plik klasy dla każdej z rdzennych klas, podczas pierwszego
+odniesienia się do niej. Dokładnie to samo zrobimy z klasą `User`. Dzieje się tak
+ponieważ plik klasy `User` znajduje się w katalogu `/wwwroot/blog/protected/models`,
+który został dodany do `include_path` PH zgodnie z następującymi liniami znajdującymi
+się w konfiguracji aplikacji:
+
+>
+> ~~~
+> [php]
+> return array(
+> ......
+> 'import'=>array(
+> 'application.models.*',
+> 'application.components.*',
+> ),
+> ......
+> );
+> ~~~
+>
+> Powyższa konfiguracja mówi, iż każda z klasy, której plik klasy znajduje się w katalogu
+`/wwwroot/blog/protected/models` lub też `/wwwroot/blog/protected/components`
+będzie automatycznie załączony jeśli odnosimy się do klasy po raz pierwszy.
+
+Klasa `UserIdentity` jest używana przede wszystkim w klasie `LoginForm`
+w celu uwierzytelnienia użytkownika bazującego na wprowadzonej na stronie logowania
+nazwie użytkownika i haśle. Następujący fragment kodu pokazuje w jaki sposób
+klasa `UserIdentity` jest używana:
+
+~~~
+[php]
+$identity=new UserIdentity($username,$password);
+$identity->authenticate();
+switch($identity->errorCode)
+{
+ case UserIdentity::ERROR_NONE:
+ Yii::app()->user->login($identity);
+ break;
+ ......
+}
+~~~
+
+> Info|Info: Ludzie często mylą tożsamość (UserIdentity) i komponent użytkownika aplikacji `user`.
+Pierwsze (tożsamość) reprezentuje sposób przeprowadzania uwierzytelnienia, druga zaś
+używana jest do reprezentowania informacji związanych z aktualnym użytkownikiem.
+Aplikacja może posiadać tylko jeden komponent użytkownika `user`, ale może posiadać
+jedną lub więcej klas tożsamościowych w zależności od rodzaju wpieranego sposobu uwierzytelniania.
+Po uwierzytelnieniu, instancja zawierająca tożsamość może przekazać swoje informacje o stanie
+do komponentu użytkownika `user`, tak, że ten jest globalnie dostępne poprzez właściwość `user`.
+
+W celu przetestowania zmodyfikowanej klasy `UserIdentity`, możemy otworzyć adres URL
+`http://www.example.com/blog/index.php` i spróbować zalogować się przy użyciu nazwy użytkownika
+oraz hasła przechowywanego w tabeli `User`. Jeśli użyjemy bazy danej dostarczonej przez
+[mego blogu](http://www.yiiframework.com/demos/blog/), powinniśmy być w stanie zalogować się
+przy użyciu nazwy użytkownika `demo` oraz hasła `demo`. Zauważ, że ten system blogowy nie
+dostarcza funkcjonalności zarządania użytkownikami. W rezultacie użytkownik nie może zmienić
+swojego konta czy też stworzyć nowe poprzez interfejs sieciowy. Funkcjonalność zarządzania użytkownikami
+może zostać rozważona jako przyszłe rozszerzenie dla naszej aplikacji.
+
+<div class="revision">$Id: prototype.auth.txt 689 2009-02-17 11:39:10Z weizhuo $</div>
View
30 docs/blog/pl/prototype.summary.txt
@@ -0,0 +1,30 @@
+Podusmowanie
+=======
+
+Osiągnęliśmy pierwszy kamień milowy. Podsumujmy zatem co już zrobiliśmy:
+
+ 1. Zidentyfikowaliśmy wymagania, które muszą zostać spełnione;
+ 2. Zainstalowaliśmy framework Yii;
+ 3. Utworzyliśmy szkielet aplikacji;
+ 4. Zaprojektowaliśmy i utworzyliśmy bazę danych dla naszego bloga;
+ 5. Zmodyfikowaliśmy konfigurację aplikacji poprzez dodanie połączenia z bazą danych;
+ 6. Wygenerowaliśmy kod, który implementuje podstawowe operacje CRUD zarówno dla wiadomości jak i komentarzy;
+ 7. Zmodyfikowaliśmy metodę uwierzytelniania tak aby korzystała z tabeli `User`.
+
+Dla większości nowych projektów najwięcej czasu zostanie poświęconego dla kroku 1 oraz 2 dla tego kamienia milowego.
+
+Chociaż kod wygenerowany przez narzędzie `yiic` implementuje funkcjonalnie w pełni operacje
+CRUD dla tabeli bazy danych, często musi on być modyfikowany w poszczególnych aplikacjach.
+Z tego powodu w następnych dwóch krokach milowych, praca którą wykonamy skupi się na
+dostosowywaniu wygenerowanego kodu CRUD dla wiadomości i komentarzy w taki sposób
+aby spełnić nasze początkowe wymagania.
+
+Ogólnie rzecz biorą, najpierw modyfikujemy klasę [modelu](http://www.yiiframework.com/doc/guide/basics.model)
+poprzez dodawanie odpowiednich reguł [sprawdzania poprawności](http://www.yiiframework.com/doc/guide/form.model#declaring-validation-rules)
+i deklarowania [obiektów relacyjnych](http://www.yiiframework.com/doc/guide/database.arr#declaring-relationship).
+Następnie modyfikujemy [akcje kontrolera](http://www.yiiframework.com/doc/guide/basics.controller)
+oraz kod [widoku](http://www.yiiframework.com/doc/guide/basics.view) dla każdej z poszczególnych
+operacji CRUD.
+
+
+<div class="revision">$Id: prototype.summary.txt 681 2009-02-16 04:57:01Z qiang.xue $</div>
View
62 docs/blog/ru/start.design.txt
@@ -0,0 +1,62 @@
+Общая структура
+==============
+
+Основываясь на анализе требований, мы установили, что наше приложение блога
+требует наличия четырех таблиц БД для хранения данных: `User`, `Post`,
+`Comment` и `Tag`:
+
+ * `User` хранит пользовательскую информацию, включая имя пользователя
+(username) и пароль;
+ * `Post` хранит информацию о постах. Главным образом, содержит следующие
+колонки:
+ - `title`: необходимо, заголовок поста;
+ - `content`: необходимо, содержимое поста, использующее
+[формат Markdown](http://daringfireball.net/projects/markdown/syntax);
+ - `status`: необходимо, статус поста, может принимать значения:
+ * `draft`: пост находится в черновом варианте и невидим для публики;
+ * `published`: пост опубликован;
+ * `archived`: пост с истекшим сроком действия, архивный, невидим
+публике.
+ - `tags`: опционально, список разделенных запятой слов, относящих пост к
+той или иной категории;
+ * `Comment` хранит информацию о комментариях постов. Каждый комментарий
+ассоциируется с некоторым постом и, главным образом, содержит следующие
+колонки:
+ - `name`: необходимо, имя автора;
+ - `email`: необходимо, email автора;
+ - `website`: опционально, URL-адрес веб-сайта автора;
+ - `content`: необходимо, содержимое комментария, использующее
+[формат Markdown](http://daringfireball.net/projects/markdown/syntax);
+ - `status`: необходимо, статус комментария, показывающий, утвержден ли
+комментарий (значение 1) или нет (значение 0);
+ * `Tag` хранит информацию о тегах постов. Каждый пост может иметь несколько
+постов, а каждый тег может быть присвоен нескольким постам. Таблица `Tag`,
+галвным образом, используется портлетом "облако тегов", которому нужно
+посчитать частоту использования каждого тега.
+
+Следующая диаграмма сущность-отношение (ER) показывает структуру таблиц и
+отношений между ними. Обратите внимание, что отношение между сущностями `Post`
+и `Tag` - многие-ко-многим. Мы используем таблицу `PostTag` для разъединения
+данного отношения в два отношения один-ко-многим.
+
+![Диаграмма сущность-отношение БД системы управления блогом](schema.png)
+
+Законченные выражения SQL, соответствующие ER-диаграмме выше, вы можете найти в
+[демо-блоге](http://www.yiiframework.com/demos/blog/). В нашей установленной
+копии, они находятся в файле
+`/wwwroot/yii/demos/blog/protected/data/schema.sqlite.sql`.
+
+
+Мы разделили разработку нашего приложения на несколько основных точек:
+
+ * Точка 1: создание прототипа системы управления блогом. Он должен содержать
+большую часть требуемой функциональности;
+ * Точка 2: завершение управления постами. Включает создание, удаление, показ
+списком постов, показ отдельного поста;
+ * Точка 3: завершение управления комментариями. Включает создание, обновление,
+удаление, показ списком и утверждение комментариев к постам;
+ * Точка 4: реализация портлетов. Включает портлеты меню пользователя, формы
+входа, облака тегов и недавних комментариев;
+ * Точка 5: финальная оптимизация и развертывание.
+
+<div class="revision">$Id: start.design.txt 685 2009-02-17 01:45:48Z qiang.xue $</div>
View
29 docs/blog/ru/start.overview.txt
@@ -0,0 +1,29 @@
+Создание блога с использованием Yii
+===================================
+
+Данное учебное пособие описывает процесс создания блога, показанного в
+[демонстрационном приложении](http://www.yiiframework.com/demos/blog/).
+Каждый шаг разработки описан подробно и может быть применён при создании других
+приложений. В дополнение к
+[полному руководству](http://www.yiiframework.com/doc/guide/ru/index) и
+[API](http://www.yiiframework.com/doc/api/) данное пособие показывает,
+вместо полного и подробного описания, пример практического применения Yii.
+
+Для того, чтобы читать данное пособие, не обязательно знать Yii.
+Тем не менее, начальное знание объектно-ориентированного программирования
+и баз данных помогут легче понять пособие.
+
+Данное учебное пособие выпущено в соответствии с [положениями о документации Yii](http://www.yiiframework.com/doc/terms/).
+
+Переводчики
+-----------
+- Bethrezen
+- Scott Tiger, krugloff.sergey
+- Александр Макаров, Sam Dark ([rmcreative.ru](http://rmcreative.ru/))
+- Константин Мирин, Konstantin Mirin ([programmersnotes.info](http://programmersnotes.info/))
+- xenon
+- Алексей Лукьяненко, Caveman ([caveman.ru](http://caveman.ru/))
+- multif
+- cr0t
+
+<div class="revision">$Id: start.overview.txt 683 2009-02-16 05:20:17Z qiang.xue $</div>
View
29 docs/blog/ru/start.requirements.txt
@@ -0,0 +1,29 @@
+Анализ требований
+=====================
+
+Система управления блогом, которую мы разрабатываем - это однопользовательская
+система. Владельцу системы будут доступны следующие действия:
+
+ * Вход и Выход
+ * Создание, обновление и удаление постов
+ * Публикация, депубликация и архивация постов
+ * Утверждение и удаление комментариев
+
+Всем остальным пользователям и гостям будут доступны действия:
+
+ * Чтение постов
+ * Создание комментариев
+
+Дополнительные требования для системы:
+
+ * Страница `Главная` системы должна показывать список недавних постов.
+ * Если страница содержит более 10 постов, она должна разбиваться на несколько
+страниц.
+ * Система должна иметь возможность показа поста вместе с комментариями к нему.
+ * Система должна иметь возможность показа списка постов по определенному тегу.
+ * Система должна иметь возможность показа облака тегов, показывающее частоту их использования.
+ * Система должна иметь возможность показа списка недавних комментариев.
+ * Система должна иметь возможность смены темы оформления.
+ * Система должна использовать URL-адреса, оптимизированные для поисковых роботов.
+
+<div class="revision">$Id: start.requirements.txt 681 2009-02-16 04:57:01Z qiang.xue $</div>
View
132 docs/blog/ru/start.testdrive.txt
@@ -0,0 +1,132 @@
+Знакомимся с Yii
+====================
+
+В данном разделе мы опишем создание основы приложения, которая будет служить
+нашей отправной точкой. Для простоты, примем, что `document root` нашего
+веб-сервера - это `/wwwroot` и соответствующий URL-адрес -
+`http://www.example.com/`.
+
+
+Установка Yii
+--------------
+
+Сначала мы установим фрейморк Yii. Скачаем копию архива релиза Yii (версии
+1.0.3 или выше) с сайта
+[www.yiiframework.com](http://www.yiiframework.com/download) и распакуем в
+директорию `/wwwroot/yii`. Убедимся, что получили директорию
+`/wwwroot/yii/framework`.
+
+> Tip|Подсказка: Фреймворк Yii может быть установлен в любом месте файловой
+системы. Директория `framework` содержит весь код фреймворка и это единственная
+необходимая директория при развертывании приложения. Одна установка Yii может
+использоваться многими приложениями.
+
+После установки Yii откроем окно браузера и перейдем по URL-адресу
+`http://www.example.com/yii/requirements/index.php`. Мы увидим анализатор
+требований, поставляемый в релизе Yii. Убедимся, что наши веб-сервер и
+установленная версия PHP соответствуют минимальным требованиям Yii. В
+частности, мы должны включить расширения PHP `pdo` и `pdo_sqlite`, которые
+требуются нашему блогу для доступа к БД SQLite.
+
+
+Создание основы приложения
+-----------------------------
+
+Далее, используем утилиту `yiic` для создания основы приложения в директории
+`/wwwroot/blog`. Утилита `yiic` - это утилита командной строки, поставляемая в
+релизе Yii. Она может быть использована для генерации кода для определенных
+задач.
+
+Откроем окно командной строки и выполним следующую команду::
+
+~~~
+% /wwwroot/yii/framework/yiic webapp /wwwroot/blog
+Create a Web application under '/wwwroot/blog'? [Yes|No]y
+......
+~~~
+
+> Tip|Подсказка: Чтобы использовать утилиту `yiic` как показано выше, путь к
+>PHP должен быть прописан в системной переменной. Есил это не так, то мы можем
+>использовать следующую команду:
+>
+>~~~
+> путь/к/php /wwwroot/yii/framework/yiic.php webapp /wwwroot/blog
+>~~~
+
+Попробуем запустить свежесозданное приложение. Откроем браузер и перейдем по
+URL-адресу `http://www.example.com/blog/index.php`. Мы должны увидеть, что наше
+приложение имеет 3 полнофункциональных страницы: `Главная`, `Контакты` и `Вход`.
+
+Ниже мы кратко опишем, что же имеем в основе приложения.
+
+###Входной скрипт
+
+У нас есть файл
+[входного скрипта](http://www.yiiframework.com/doc/guide/ru/basics.entry) -
+`/wwwroot/blog/index.php` с таким содержимым:
+
+~~~
+[php]
+<?php
+$yii='/wwwroot/framework/yii.php';
+$config=dirname(__FILE__).'/protected/config/main.php';
+
+// удалить следующую строку в режиме production
+defined('YII_DEBUG') or define('YII_DEBUG',true);
+
+require_once($yii);
+Yii::createWebApplication($config)->run();
+~~~
+
+Это единственный скрипт, к которому пользователи имеют прямой доступ. Сначала
+скрипт подключает файл начальной загрузки `yii.php`. Затем создает экземпляр
+[приложения](http://www.yiiframework.com/doc/guide/ru/basics.application) с
+определенными настройками и выполняет приложение.
+
+
+###Базовая директория приложения
+
+Также, у нас есть
+[базовая директория приложения](http://www.yiiframework.com/doc/guide/ru/basics.application#application-base-directory)
+`/wwwroot/blog/protected`. Большая часть нашего кода и данных будет храниться
+в этой директории и должна быть защищена от прямого доступа веб-пользователей.
+Для [веб-сервера Apache](http://httpd.apache.org/) мы помещаем в эту директорию
+файл `.htaccess` со следующим содержанием:
+
+~~~
+deny from all
+~~~
+
+Пожалуйста, для настройки других веб-серверов обратитесь к соответствующей
+документации, как защитить директорию от прямого доступа веб-пользователями.
+
+
+Последовательность работы приложения
+--------------------
+
+Чтобы помочь понять работу Yii, мы опишем основную последовательность работы в
+нашей основе приложения, когда пользователь обращается к странице `Контакты`:
+
+ 1. [Входной скрипт](http://www.yiiframework.com/doc/guide/ru/basics.entry)
+выполняется веб-сервером для обработки запроса;
+ 2. Экземпляр
+[приложения](http://www.yiiframework.com/doc/guide/ru/basics.application)
+создается и конфигурируется с определенными в файле конфигурации
+`/wwwroot/blog/protected/config/main.php` начальными значениями;
+ 3. Приложение разрешает (обрабатывает) запрос в
+[контроллер](http://www.yiiframework.com/doc/guide/ru/basics.controller) и
+[действие контроллера](http://www.yiiframework.com/doc/guide/ru/basics.controller#action).
+Запрос страницы `Контакты` разрешается в контроллер `site` и действие `contact`;
+ 4. Приложение создает контроллер `site` в виде экземпляра класса
+`SiteController` и выполняет его;
+ 5. Экземпляр класса `SiteController` выполняет действие `contact` вызовом его
+метода `actionContact()`;
+ 6. Метод `actionContact()` генерирует (и показывает)
+[представление](http://www.yiiframework.com/doc/guide/ru/basics.view) `contact`
+веб-пользователю. Внутренне, это достигается подключением файла представления
+`/wwwroot/blog/protected/views/site/contact.php` и включения результата в файл
+[макета](http://www.yiiframework.com/doc/guide/ru/basics.view#layout)
+`/wwwroot/blog/protected/views/layouts/main.php`.
+
+
+<div class="revision">$Id: start.testdrive.txt 681 2009-02-16 04:57:01Z qiang.xue $</div>
View
36 docs/blog/ru/toc.txt
@@ -0,0 +1,36 @@
+* Начало
+ - [Обзор](start.overview)
+ - [Знакомимся с Yii](start.testdrive)
+ - [Анализ требований](start.requirements)
+ - [Общая структура](start.design)
+
+* Начальное прототипирование
+ - [Настройка БД](prototype.database)
+ - [Генерация каркаса](prototype.scaffold)
+ - [Аутентификация](prototype.auth)
+ - [Итог](prototype.summary)
+
+* Управление записями
+ - [Доработка модели Post](post.model)
+ - [Создание и редактирование](post.create)
+ - [Отображение](post.display)
+ - [Управление записями](post.admin)
+
+* Упраление комментариями
+ - [Доработка модели Comment](comment.model)
+ - [Создание и отображение](comment.create)
+ - [Упраление комментариями](comment.admin)
+
+* Портлеты
+ - [Архитектура портлетов](portlet.base)
+ - [Меню пользователя](portlet.menu)
+ - [Вход в систему](portlet.login)
+ - [Облако тэгов](portlet.tags)
+ - [Последние комментарии](portlet.comments)
+
+* Последние штрихи
+ - [Человекопонятные URL](final.url)
+ - [Журналирование ошибок](final.logging)
+ - [Свой вид отображения ошибок](final.error)
+ - [Тонкая настройка и развёртывание](final.deployment)
+ - [Дальнейшие улучшения](final.future)
View
52 docs/guide/ru/changes.txt
@@ -0,0 +1,52 @@
+Новые возможности
+=================
+
+На этой странице кратко излагаются новые возможности, внесённые в каждом релизе Yii.
+
+Версия 1.0.7
+-------------
+
+ * Добавлена поддержка отображения информации стека вызовов в трассирующих сообщениях
+ - [Сохранение контекста сообщений](/doc/guide/topics.logging#logging-context-information)
+
+ * В отношениях AR добавлена опция `index`, позволяющая использовать значения
+столбца в качестве ключей массива связанных объектов.
+ - [Параметры реляционного запроса](/doc/guide/database.arr#relational-query-options)
+
+Версия 1.0.6
+-------------
+
+ * Добавлена поддержка использования именованной группы условий с методами `update` и `delete`:
+ - [Именованные группы условий](/doc/guide/database.ar#named-scopes)
+
+ * Добавлена поддержка использования именованной группы условий в параметре `with` реляционных правил:
+ - [Реляционные запросы с именованными группами условий](/doc/guide/ru/database.arr#relational-query-with-named-scopes)
+
+ * Добавлена поддержка профилирования SQL-запросов:
+ - [Профилирование SQL-запросов](/doc/guide/topics.logging#profiling-sql-executions)
+
+ * Добавлена поддержка журналирования дополнительной информации контекста сообщений:
+ - [Сохранение контекста сообщений](/doc/guide/topics.logging#logging-context-information)
+
+ * Добавлена поддержка настройки одиночного URL-правила установкой его параметров `urlFormat` и `caseSensitive`:
+ - [Человекопонятные URL](/doc/guide/topics.url#user-friendly-urls)
+
+ * Добавлена возможность отображения ошибок приложения в действии контроллера:
+ - [Управление отображением ошибок в действии контроллера](/doc/guide/topics.error#handling-errors-using-an-action)
+
+Версия 1.0.5
+-------------
+
+ * Active Record расширена поддержкой именованных групп условий:
+ - [Именованные группы условий](/doc/guide/database.ar#named-scopes)
+ - [Именованная группа условий по умолчанию](/doc/guide/database.ar#default-named-scope)
+ - [Реляционные запросы с именованными группами условий](/doc/guide/database.arr#relational-query-with-named-scopes)
+
+
+ * Active Record расширена поддержкой отложенной загрузки с динамическими параметрами реляционного запроса:
+ - [Динамические параметры реляционного запроса](/doc/guide/database.arr#dynamic-relational-query-options)
+
+ * Расширен класс [CUrlManager] поодержкой параметризованных маршрутов в URL-правилах:
+ - [Параметризация маршрутов](/doc/guide/topics.url#parameterizing-routes)
+
+<div class="revision">$Id: changes.txt 1168 2009-06-23 14:52:48Z qiang.xue $</div>
View
171 docs/guide/ru/topics.performance.txt
@@ -0,0 +1,171 @@
+Улучшение производительности
+============================
+
+Производительность веб-приложения зависит от многих факторов. Главные из них —
+обращение к базе данных, файловой системе и пропускная способность сети.
+В Yii, для уменьшения падения производительности из-за самого фреймворка, учтён
+каждый из этих факторов. Несмотря на это, многие части приложения можно улучшить
+для получения более высокой производительности.
+
+Включение расширения APC
+------------------------
+
+Включение [расширения PHP APC](http://www.php.net/manual/ru/book.apc.php) —
+возможно, самый простой способ улучшить общую производительность приложения.
+Расширение оптимизирует и кэширует промежуточный код PHP и выигрывает время,
+затрачиваемое на интерпретацию скриптов PHP при каждом запросе.
+
+Отключение режима отладки
+-------------------------
+
+Отключение режима отладки — ещё один лёгкий способ увеличить производительность.
+Приложение Yii работает в режиме отладки если константа `YII_DEBUG` определена
+как true. Режим отладки полезен при разработке, но не лучшим образом влияет на
+производительность из-за использования большего числа компонентов. К примеру,
+при журналировании ошибок, с каждым сообщением может записываться дополнительная
+информация.
+
+Использование `yiilite.php`
+---------------------------
+
+Если используется [расширение PHP APC](http://www.php.net/manual/ru/book.apc.php),
+мы можем заменить `yii.php` другим загрузчиком — `yiilite.php`. Это даст приложению
+ещё больший прирост производительности.
+
+Файл `yiilite.php` поставляется к комплекте с каждой версией Yii и представляет
+собой собранные вместе часто используемые классы. Все комментарии и выражения трассировки
+вырезаются, поэтому использование `yiilite.php` уменьшает количество подключаемых файлов
+и выполняемого кода.
+
+Стоит заметить, что использование `yiilite.php` без APC может отрицательно
+повлиять на производительность, так как `yiilite.php` включает в себя классы,
+которые могут не требоваться при каждом запросе и отнимать некоторое время на
+парсинг. Также было отмечено, что на некоторых конфигурациях сервера `yiilite.php`
+медленнее даже при использовании APC. Лучший способ принятия решения об
+использовании `yiilite.php` — провести тесты на прилагающемся демонстрационном
+приложении `hello world`.
+
+Использование кэширования
+-------------------------
+
+Как уже было описано в разделе «[кэширование](/doc/guide/caching.overview)», Yii
+предоставляет несколько решений, которые могут значительно увеличить
+производительность приложения. Если генерация каких-либо данных занимает много
+времени, мы можем использовать [кэширование данных](/doc/guide/caching.data) для
+того, чтобы делать это не так часто. Если часть страницы остаётся неизменной, мы
+можем использовать [кэширование фрагментов](/doc/guide/caching.fragment). Если вся
+страница не меняется, можно использовать [кэширование страниц](/doc/guide/caching.page).
+
+Если используется [Active Record](/doc/guide/database.ar), можно включить
+кэширование структуры базы данных. Это можно сделать, установив в настройках
+свойству [CDbConnection::schemaCachingDuration] значение, большее 0.
+
+Кроме описанных настроек приложения можно использовать кэширование на уровне
+сервера. Описанное выше
+[кэширование APC](/doc/guide/topics.performance#enabling-apc-extension) относится
+как раз к ним. Существуют и другие решения, такие как
+[Zend Optimizer](http://Zend.com/ZendOptimizer), [eAccelerator](http://eaccelerator.net/)
+и [Squid](http://www.squid-cache.org/).
+
+Оптимизация базы данных
+-----------------------
+
+Получение данных из базы часто является узким местом производительности
+приложения. Несмотря на то, что кэширование смягчить потери, оно не решает
+проблему полностью. Когда в базе содержатся огромные объёмы данных, и нужно
+обновить кэш, получение данных может быть чрезмерно растратным при неверном
+составлении схемы данных или запросов.
+
+Будьте осмотрительны при выборе индексов. Их использование может значительно
+ускорить `SELECT`-запросы, но замедляет запросы `INSERT`, `UPDATE` и `DELETE`.
+
+Для сложных запросов рекомендуется создать view в базе данных вместо использования
+запросов из кода PHP, которые СУБД разбирает каждый раз.
+
+Не злоупотребляйте [Active Record](/doc/guide/database.ar). Хоть [Active
+Record](/doc/guide/database.ar) и является удобной проекцией данных в стиле ООП,
+но производительность при её использовании, из за использования объектов для
+представления каждой строки результата, падает. Для приложений, интенсивно
+работающих с данными, рекомендуется использовать [DAO](/doc/guide/database.dao)
+или API для работы с СУБД на ещё более низком уровне.
+
+Последний по счёту, но не по значению совет: используйте `LIMIT` в
+`SELECT`-запросах. Так вы сможете избежать получение избыточных данных из базы и
+расхода требующейся для их хранения памяти, выделенной PHP.
+
+Минимизация файлов скриптов
+---------------------------
+
+Сложные страницы часто включают большое количество внешних файлов JavaScript и
+CSS. Так как каждый файл равен дополнительному запросу к серверу, мы должны
+уменьшить число файлов путём их слияния. Также не лишним будет уменьшить размер
+каждого их них для уменьшения времени передачи по сети. Существует немало
+инструментов для выполнения этих двух задач.
+
+Для страницы, генерируемой Yii, не исключено, что некоторые скрипты подключаются
+компонентами, код которых изменять не хочется (например, компоненты ядра Yii).
+Как минимизировать такие скрипты показано далее.
+
+> Note|Примечание: Возможность использования `scriptMap`, описанная далее,
+> доступна с версии 1.0.3.
+
+Для начала опишем, какие файлы минимизировать. Зададим свойство
+[scriptMap|CClientScript::scriptMap] компонента
+[clientScript|CWebApplication::clientScript]. Это можно сделать как в настройках