diff --git a/apps/advanced/environments/dev/common/config/main-local.php b/apps/advanced/environments/dev/common/config/main-local.php
index dc1233a..d488438 100644
--- a/apps/advanced/environments/dev/common/config/main-local.php
+++ b/apps/advanced/environments/dev/common/config/main-local.php
@@ -11,6 +11,9 @@ return [
         'mail' => [
             'class' => 'yii\swiftmailer\Mailer',
             'viewPath' => '@common/mail',
+            // send all mails to a file by default. You have to set
+            // 'useFileTransport' to false and configure a transport
+            // for the mailer to send real emails.
             'useFileTransport' => true,
         ],
     ],
diff --git a/apps/basic/config/web.php b/apps/basic/config/web.php
index f9013d8..7fee63a 100644
--- a/apps/basic/config/web.php
+++ b/apps/basic/config/web.php
@@ -19,6 +19,9 @@ $config = [
         ],
         'mail' => [
             'class' => 'yii\swiftmailer\Mailer',
+            // send all mails to a file by default. You have to set
+            // 'useFileTransport' to false and configure a transport
+            // for the mailer to send real emails.
             'useFileTransport' => true,
         ],
         'log' => [
diff --git a/docs/guide/tutorial-mailing.md b/docs/guide/tutorial-mailing.md
index 0ee16df..7f386ff 100644
--- a/docs/guide/tutorial-mailing.md
+++ b/docs/guide/tutorial-mailing.md
@@ -204,6 +204,23 @@ Then inside view file you can use following code:
 ```
 
 
+Testing and debugging
+---------------------
+
+Developer often a to check, what actual emails are sent by application, what was their content and so on.
+Such ability is granted by Yii via `yii\mail\BaseMailer::useFileTransport`. If enabled, this option enforces
+saving mail message data into the local files instead of regular sending. These files will be saved under
+`yii\mail\BaseMailer::fileTransportPath`, which is '@runtime/mail' by default.
+
+> Note: you can either save messages to the file or send them to actual recipients, but can not do both simultaneously.
+
+Mail message file can be opened by regular text file editor, so you can browse actual message headers, content and so on.
+This mechanism amy prove itself, while debugging application or running unit test.
+
+> Note: mail message file content is composed via `\yii\mail\MessageInterface::toString()`, so it depends on actual
+  mail extension you are using in your application.
+
+
 Creating your own mail solution
 -------------------------------
 
diff --git a/extensions/elasticsearch/CHANGELOG.md b/extensions/elasticsearch/CHANGELOG.md
index ceaa33f..60ab3fe 100644
--- a/extensions/elasticsearch/CHANGELOG.md
+++ b/extensions/elasticsearch/CHANGELOG.md
@@ -4,11 +4,12 @@ Yii Framework 2 elasticsearch extension Change Log
 2.0.0-rc under development
 --------------------------
 
+- Bug #3587: Fixed an issue with storing empty records (cebe)
+- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
+- Enh #3527: Added `highlight` property to Query and ActiveRecord. (Borales)
 - Chg: asArray in ActiveQuery is now equal to using the normal Query. This means, that the output structure has changed and `with` is supported anymore. (cebe)
 - Chg: Deletion of a record is now also considered successful if the record did not exist. (cebe)
 - Chg: Requirement changes: Yii now requires elasticsearch version 1.0 or higher (cebe)
-- Enh #3520: Added `unlinkAll()`-method to active record to remove all records of a model relation (NmDimas, samdark, cebe)
-- Enh #3527: Added `highlight` property to Query and ActiveRecord. (Borales)
 
 
 2.0.0-beta April 13, 2014
@@ -27,7 +28,9 @@ Yii Framework 2 elasticsearch extension Change Log
              All relational queries are now directly served by `ActiveQuery` allowing to use
              custom scopes in relations (cebe)
 
+
 2.0.0-alpha, December 1, 2013
 -----------------------------
 
 - Initial release.
+
diff --git a/extensions/elasticsearch/Command.php b/extensions/elasticsearch/Command.php
index 7528f0f..6654efd 100644
--- a/extensions/elasticsearch/Command.php
+++ b/extensions/elasticsearch/Command.php
@@ -80,7 +80,11 @@ class Command extends Component
      */
     public function insert($index, $type, $data, $id = null, $options = [])
     {
-        $body = is_array($data) ? Json::encode($data) : $data;
+        if (empty($data)) {
+            $body = '{}';
+        } else {
+            $body = is_array($data) ? Json::encode($data) : $data;
+        }
 
         if ($id !== null) {
             return $this->db->put([$index, $type, $id], $options, $body);
diff --git a/extensions/gii/CHANGELOG.md b/extensions/gii/CHANGELOG.md
index bc2818e..69510d2 100644
--- a/extensions/gii/CHANGELOG.md
+++ b/extensions/gii/CHANGELOG.md
@@ -6,6 +6,7 @@ Yii Framework 2 gii extension Change Log
 
 - Bug #1263: Fixed the issue that Gii and Debug modules might be affected by incompatible asset manager configuration (qiangxue)
 - Bug #3265: Fixed incorrect controller class name validation (suralc)
+- Bug #3693: Fixed broken Gii preview when a file is unchanged (cebe)
 - Enh #2018: Search model is not required anymore in CRUD generator (johonunu)
 - Enh #3088: The gii module will manage their own URL rules now (qiangxue)
 - Enh #3222: Added `useTablePrefix` option to the model generator for Gii (horizons2)
diff --git a/extensions/gii/assets/gii.js b/extensions/gii/assets/gii.js
index ad07252..0790770 100644
--- a/extensions/gii/assets/gii.js
+++ b/extensions/gii/assets/gii.js
@@ -45,7 +45,14 @@ yii.gii = (function ($) {
             $modal.find('.modal-title').text($link.data('title'));
             $modal.find('.modal-body').html('Loading ...');
             $modal.modal('show');
-            var checked = $('a.' + $modal.data('action') + '[href="' + $link.attr('href') + '"]').closest('tr').find('input').get(0).checked;
+            var checkbox = $('a.' + $modal.data('action') + '[href="' + $link.attr('href') + '"]').closest('tr').find('input').get(0);
+            var checked = false;
+            if (checkbox) {
+                checked = checkbox.checked;
+                $modal.find('.modal-checkbox').removeClass('disabled');
+            } else {
+                $modal.find('.modal-checkbox').addClass('disabled');
+            }
             $modal.find('.modal-checkbox span').toggleClass('glyphicon-check', checked).toggleClass('glyphicon-unchecked', !checked);
             $.ajax({
                 type: 'POST',
diff --git a/tests/unit/data/ar/Order.php b/tests/unit/data/ar/Order.php
index 8252b59..56701c1 100644
--- a/tests/unit/data/ar/Order.php
+++ b/tests/unit/data/ar/Order.php
@@ -32,6 +32,11 @@ class Order extends ActiveRecord
         return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
     }
 
+    public function getOrderItemsWithNullFK()
+    {
+        return $this->hasMany(OrderItemWithNullFK::className(), ['order_id' => 'id']);
+    }
+
     public function getItems()
     {
         return $this->hasMany(Item::className(), ['id' => 'item_id'])
@@ -72,7 +77,7 @@ class Order extends ActiveRecord
     public function getBooksWithNullFK()
     {
         return $this->hasMany(Item::className(), ['id' => 'item_id'])
-            ->via('orderItemsWithNullFk')
+            ->via('orderItemsWithNullFK')
             ->where(['category_id' => 1]);
     }
 
diff --git a/tests/unit/extensions/elasticsearch/ActiveRecordTest.php b/tests/unit/extensions/elasticsearch/ActiveRecordTest.php
index 045f112..4b05aa2 100644
--- a/tests/unit/extensions/elasticsearch/ActiveRecordTest.php
+++ b/tests/unit/extensions/elasticsearch/ActiveRecordTest.php
@@ -186,6 +186,16 @@ class ActiveRecordTest extends ElasticSearchTestCase
         $db->createCommand()->flushIndex('yiitest');
     }
 
+    public function testSaveNoChanges()
+    {
+        // this should not fail with exception
+        $customer = new Customer();
+        // insert
+        $customer->save(false);
+        // update
+        $customer->save(false);
+    }
+
     public function testFindAsArray()
     {
         // asArray
diff --git a/tests/unit/framework/ar/ActiveRecordTestTrait.php b/tests/unit/framework/ar/ActiveRecordTestTrait.php
index beaa9f0..34f2fa0 100644
--- a/tests/unit/framework/ar/ActiveRecordTestTrait.php
+++ b/tests/unit/framework/ar/ActiveRecordTestTrait.php
@@ -773,23 +773,23 @@ trait ActiveRecordTestTrait
         /** @var Order $order */
         $order = $orderClass::findOne(1);
         $this->assertEquals(2, count($order->books));
-        $this->assertEquals(6, $orderItemClass::find()->count());
+        $orderItemCount = $orderItemClass::find()->count();
         $this->assertEquals(5, $itemClass::find()->count());
         $order->unlinkAll('books', true);
         $this->afterSave();
         $this->assertEquals(5, $itemClass::find()->count());
-        $this->assertEquals(4, $orderItemClass::find()->count());
+        $this->assertEquals($orderItemCount - 2, $orderItemClass::find()->count());
         $this->assertEquals(0, count($order->books));
 
         // via model without delete
         $this->assertEquals(2, count($order->booksWithNullFK));
-        $this->assertEquals(6, $orderItemsWithNullFKClass::find()->count());
+        $orderItemCount = $orderItemsWithNullFKClass::find()->count();
         $this->assertEquals(5, $itemClass::find()->count());
         $order->unlinkAll('booksWithNullFK',false);
         $this->afterSave();
         $this->assertEquals(0, count($order->booksWithNullFK));
-        $this->assertEquals(2,$orderItemsWithNullFKClass::find()->where(['AND', ['item_id' => [1, 2]], ['order_id' => null]])->count());
-        $this->assertEquals(6, $orderItemsWithNullFKClass::find()->count());
+        $this->assertEquals(2, $orderItemsWithNullFKClass::find()->where(['AND', ['item_id' => [1, 2]], ['order_id' => null]])->count());
+        $this->assertEquals($orderItemCount, $orderItemsWithNullFKClass::find()->count());
         $this->assertEquals(5, $itemClass::find()->count());
 
         // via table is covered in \yiiunit\framework\db\ActiveRecordTest::testUnlinkAllViaTable()
diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php
index dcbd96d..e29f97c 100644
--- a/tests/unit/framework/db/ActiveRecordTest.php
+++ b/tests/unit/framework/db/ActiveRecordTest.php
@@ -543,30 +543,30 @@ class ActiveRecordTest extends DatabaseTestCase
         /** @var \yii\db\ActiveRecordInterface $orderItemClass */
         $orderItemClass = $this->getOrderItemClass();
         /** @var \yii\db\ActiveRecordInterface $itemClass */
-        $itemClass = $this->getOrderItemClass();
+        $itemClass = $this->getItemClass();
         /** @var \yii\db\ActiveRecordInterface $orderItemsWithNullFKClass */
         $orderItemsWithNullFKClass = $this->getOrderItemWithNullFKmClass();
 
         // via table with delete
         /** @var Order $order */
         $order = $orderClass::findOne(1);
-        $this->assertEquals(2, count($order->books));
-        $this->assertEquals(6, $orderItemClass::find()->count());
+        $this->assertEquals(2, count($order->booksViaTable));
+        $orderItemCount = $orderItemClass::find()->count();
         $this->assertEquals(5, $itemClass::find()->count());
         $order->unlinkAll('booksViaTable', true);
         $this->afterSave();
         $this->assertEquals(5, $itemClass::find()->count());
-        $this->assertEquals(4, $orderItemClass::find()->count());
-        $this->assertEquals(0, count($order->books));
+        $this->assertEquals($orderItemCount - 2, $orderItemClass::find()->count());
+        $this->assertEquals(0, count($order->booksViaTable));
 
         // via table without delete
-        $this->assertEquals(2, count($order->booksWithNullFK));
-        $this->assertEquals(6, $orderItemsWithNullFKClass::find()->count());
+        $this->assertEquals(2, count($order->booksWithNullFKViaTable));
+        $orderItemCount = $orderItemsWithNullFKClass::find()->count();
         $this->assertEquals(5, $itemClass::find()->count());
         $order->unlinkAll('booksWithNullFKViaTable',false);
-        $this->assertEquals(0, count($order->booksWithNullFK));
+        $this->assertEquals(0, count($order->booksWithNullFKViaTable));
         $this->assertEquals(2,$orderItemsWithNullFKClass::find()->where(['AND', ['item_id' => [1, 2]], ['order_id' => null]])->count());
-        $this->assertEquals(6, $orderItemsWithNullFKClass::find()->count());
+        $this->assertEquals($orderItemCount, $orderItemsWithNullFKClass::find()->count());
         $this->assertEquals(5, $itemClass::find()->count());
     }
 }