вторник, 8 ноября 2011 г.

Массивы в PL/SQL

PL/SQL, являясь процедурным расширением языка структурированных запросов SQL, имеет все необходимые составляющие для полнофункционального программирования логики. Ветвления, циклы, массивы и т.д. - все как у людей:) Работа с массивами в PL/SQL, однако, отличается от стандартного для многих распространенных языков программирования подхода.

С массивами, как известно, можно делать три вещи: объявлять массив, добавлять/изменять/читать данные по индексу и обходить массив в цикле. Эти действия мы и рассмотрим.

среда, 14 сентября 2011 г.

Использование Ext JS 4 виджетов в рамках Ext JS 3 интерфейса

Ext JS 4 представляет разработчикам много новых возможностей, в числе которых, например, большое количество новых графиков и диаграмм. Сравните - для четвертой версии приведено 22 примера использования элементов Chart против всего четырех примеров для третьей версии. Внушающе, не правда ли?

Однако что, если в наличии есть большое приложение, реализованное на третьей версии Ext JS и возникает необходимость использования некоторых виджетов четвертой версии? Например, типична ситуация, когда в приложение нужно добавить новые диаграммы, но переписывать всё на "четверку" нецелесообразно.

среда, 31 августа 2011 г.

Справочники и классификаторы

Редкая крупная база данных и программная система на её основе сегодня обходится без использования справочников и классификаторов. Ситуация с применением этих терминов очень интересна благодаря тому, что по существу определения им никто толком дать не может. Кто-то говорит, что справочник - особый тип классификатора, кто-то - что классификатор это подтип справочника. Так что же это такое и в чем разница?

Как ни странно, ответ на этот вопрос следует искать в названиях терминов. Классификатор - классифицирует, справочник - хранит справочные данные. Фактически, это полностью описывает  оба термина. Классификаторы разрабатываются для группировки объектов по некоторым критериям с целью последующей удобной работы с этими группами и в группах, часто бывают иерархическими. Пример классификатора - ОКЭР (Общероссийский классификатор экономических регионов России), который содержит в себе информацию о регионах, разбитую по группам, в качестве критерия классификации выступают различные экономические показатели.

Справочник содержит перечень объектов, не занимаясь при этом классификацией. В то же время он также может быть иерархическим - например, простой справочник регионов РФ. Сравните - иерархический справочник регионов, имеющий структуру типа федеральные округа -> области -> населенные пункты, и классификатор экономических регионов, хранящий отдельно разные группы с разными экономическими показателями.

Одним из возможных критериев для определения, чем является набор данных - справочником или классификатором - может служить следующий подход. При составлении классификатора всегда нужно затратить определенные ресурсы на классификацию, для справочника этих затрат нет. Согласитесь, что составление классификации регионов и районов по экономическим признакам требует проведения огромного количества работы, начиная от выявления признаков классификации и заканчивая собственно проведением процесса группировки, в то время как процесс разбивки страны на регионы классификацией назвать никак нельзя, работа по составлению справочника не включает в себя указанные затраты. В этом разница.

понедельник, 29 августа 2011 г.

Oracle Database: получить поля таблицы и комментарии к ним


Проектная документации на разработку программных систем, имеющих в основе базу данных, подразумевает наличие описания структур БД. Как правило, в техническом проекте приводится схема БД, получаемая обратной разработкой (reverse engineering) готовых таблиц, и информация об атрибутах таблиц. Для составления схем можно использовать бесплатный SQL Developer Data Modeler (как пользоваться), для описания атрибутов таблиц же удобно составить следующий SQL запрос:

SELECT col.table_name,
  col.column_name,
  col.data_type,
  col.data_length,
  col.data_precision,
  col.data_scale,
  col.nullable,
  com.comments
FROM ALL_TAB_COLUMNS col
LEFT JOIN user_col_comments com
ON (col.table_name  = com.table_name
AND col.column_name = com.column_name)
WHERE col.TABLE_NAME = 'PUO_FAIP'
ORDER BY table_name,
  column_name;

Как видим, запрос возвращает поля таблицы PUO_FAIP (column_name), тип данных в поле (data_type), длину данных для текстовых типов (data_length), точность и масштаб для числовых типов (data_precision и data_scale соответственно), возможность поля хранить значение NULL (nullable) и, наконец, комментарий (comments). Полученные данные легко экспортировать в текстовый редактор для оформления документации.

PS. При создании таблиц рекомендуется сразу создавать и комментарии к ее полям, это просто:
-- комментарий к полю
COMMENT ON COLUMN SCHEMA_NAME.TABLE_NAME.COLUMN_NAME IS
   'Текстовый комментарий к полю таблицы';

-- комментарий к таблице
COMMENT ON TABLE SCHEMA_NAME.TABLE_NAME IS
   'Текстовый комментарий к таблице';

понедельник, 15 августа 2011 г.

ExtJS: разработка интерфейса независимо от источников данных. Кросс-доменный AJAX для загрузки хранилищ

Удаленные источники для хранилищ ExtJS - это источники, расположенные в другом домене или, как их еще называют, кросс-доменные источники. Вероятно, вы знаете о том, что традиционные XMLHttpRequest запросы (известные в народе как AJAX запросы) имеют ряд ограничений, связанных в первую очередь с безопасностью - в частности, XMLHttpRequest запросы могут быть посланы только к адресу с одноименным протоколом, доменом и портом по отношению к запрашивающей странице. Таким образом, нельзя сделать традиционный AJAX запрос с сайта site1.com к сайту site2.com.

Тем не менее, такая необходимость часто возникает, в таких случаях используется так называемый cross-domain  (кросс-доменный) AJAX. Стоит отметить, что непосредственно к AJAX эти технологии уже не относятся, название в данном случае - не более, чем дань традиции. О методах, используемых в кросс-доменных запросах, можно почитать, например, в статье на javascript.ru.

Одной из проблем разработки интерфейсов на ExtJS является зависимость от динамически загружаемых данных: фактически, большинство компонентов фреймворка используют те или иные данные, получаемые при помощи AJAX запросов. Например, так выглядит традиционное описание хранилища:

myStore = new Ext.data.JsonStore({
    url:    'getUsers',
    method: 'POST',
    root: 'items',
    fields: [{name:"id"},{name:"name"}]
});

URL, указанный первым параметром, ссылается на страницу того же домена, в котором должна находиться разрабатываемая страница, иначе AJAX запрос на загрузку данных не сможет быть произведен. Таким образом, разрабатываемая html страница и javascript код в ее составе должны быть частью домена-источника данных. Это не всегда удобно и не всегда возможно.

Использование кросс-доменных запросов в таких случаях - отличное решение. Посмотрите, как просто можно заставить работать ExtJS приложение с данными любого домена:

myStore = new Ext.data.JsonStore({
    proxy: new Ext.data.ScriptTagProxy({
            url:    'http://vasilij.com/getUsers',
    }),
    method: 'POST',
    root: 'items',
    fields: [{name:"id"},{name:"name"}]
});

В код сервлета необходимо добавить отслеживание GET параметра callback. Дело в том, что при использовании ScriptTagProxy, ExtJS добавляет в строку запроса параметр callback, таким образом, что в нашем случае запрос будет иметь вид:

http://vasilij.com/getUsers&callback=stcCallback10

Возвращаемый JSON должен быть обернут в функцию, название которой соответствует переданному параметру callback. В случае сервлета:

String callback = request.getParameter("callback");
if (callback == null){
  response.setContentType("text/javascript");
  out.println(jsonString);
}
else{
  response.setContentType("application/x-json");
  out.println(callback.concat("(").concat(jsonString).concat(")"));
}

Таким образом, при отсутствии callback параметра в строке запроса возвращается обычный JSON, в противном случае JSON оборачивается в функцию.

После внесения в код приложения описанных изменений можно разрабатывать интерфейс абсолютно независимо от всей остальной части приложения; сервлеты-поставщики данных могут находиться в любом домене, и даже простая html страница на рабочем столе с подключенным ExtJS сможет получать данные и корректно их обрабатывать и отображать. После окончания процесса разработки и тестирования можно заменить Ext.data.ScriptTagProxy на Ext.data.HttpProxy для использования AJAX запросов.

Приятного кодинга вам!

четверг, 11 августа 2011 г.

Pagination SQL query for Oracle database

Pagination is a process of dividing information into pages, it's daily task for web developer independently of used technology. Pagination is often described with respect to LAMP technologies (PHP and MySQL sites), for Oracle database it is not very different, but we must write other SQL query because Oracle don't support LIMIT and OFFSET parameters, actual for MySQL. In case of MySQL pagination query is:

SELECT id, name FROM users ORDER BY name LIMIT start, limit

This query for Oracle is shown below:

SELECT *
FROM
  (SELECT a.*,
    rownum AS rn
  FROM
    (SELECT id, name FROM users ORDER BY name
    ) a
  WHERE rownum < (start+limit)+1
  )
WHERE rn >= start+1;

Rownum is number of current row in sampling, note that it starts with 1. ORDER is necessary parameter, otherwise rownum will not have sence.

If input data is page number and page size, Oracle query is:

SELECT * FROM
(
    SELECT a.*, rownum rn
    FROM
    (
        SELECT id, name from users ORDER BY name
    ) a
    WHERE rownum < ((pageNumber * pageSize) + 1 )
)
WHERE rn >= (((pageNumber-1) * pageSize) + 1)

среда, 3 августа 2011 г.

Использование dbms_output в SQL*Plus и производных продуктах

При разработке на PL/SQL в SQL*Plus или продуктах, его использующих, часто возникает необходимость использования вывода результатов не в табличном, а простом, текстовом виде. Для этого используется PL/SQL пакет dbms_output.

Например, чтобы вывести строку 'Hello, World!', необходимо написать следующий код:

dbms_output.put_line('Hello, World!');

Код корректен - однако, приветствие миру вы, вероятно, не увидите. Для включения вывода пакета dbms_output необходимо добавить следующую строку:

set serveroutput on

Теперь можно использовать dbms_output на всю катушку:

set serveroutput on
begin
  for x in (select id, name from authors) loop
    dbms_output.put_line('Автор ' || rpad(x.name, 30) || ' имеет идентификатор ' || x.id);
  end loop;
end;

При выводе больших строк, возможно, вы столкнетесь с ошибкой buffer overflow, limit of 20000 bytes, сообщающую о переполнении буфера в 20000 байт. Для увеличения размера буфера можно использовать следующую команду:

DBMS_OUTPUT.ENABLE(500000);

Удачного дебага!

пятница, 8 июля 2011 г.

ExtJS labelWidth или задаем ширину пометок к полям формы

В ExtJS метка Label используется как аналог традиционному HTML элементу <label></label>, используемому для определения текстовых названий полей формы. Зачастую возникает необходимость дополнительно стилизовать эти метки, для этого в ExtJS используется параметр labelStyle, которому соответствует строка с CSS описанием. Естественно, таким образом можно задать цвет метки, изменить шрифт, ширину элемента и т.д:

{
  fieldLabel: 'Введите Ваше имя',
  name: 'userName',
  labelStyle: 'color: red; width: 200px; font-weight: bold;'
}

Однако, задав ширину метки в лоб через labelStyle, можно наткнуться на ряд неприятных и не всегда очевидных последствий, например, как показано на скриншоте ниже.



Проблема здесь заключается в том, что позиционированием элементов занимается ExtJS, если же вы меняете размер и расположение через CSS, вы начинаете сами отвечать за эти действия. Логичным выходом из сложившейся ситуации видится использование встроенных механизмов ExtJS для изменения размеров элементов - и это абсолютно верно.

Для изменения ширины меток Label необходимо использовать параметр слоя form, именуемый labelWidth. Да, именно слоя form, а не самого элемента формы field (например, TextField). Ширина всех меток на форме указывается в параметрах формы, как показано ниже:

myform = {
  labelWidth: 140,
  layout: 'form',
  style: 'padding: 15px;',
  defaults: {
    xtype: 'displayfield',
    labelStyle: 'color: #767782; font-weight: bold; width: 320px;'
  },
  items: [
    {
      fieldLabel: 'Ваше имя, сэр!',
      name: 'sirName'
    },
    {
      fieldLabel: 'Ваше пароль, милейший!',
      name: 'sirPassword'
    }
  ]
}

Таким образом мы получим корректное поведение ExtJS и корректное отображение меток формы.

среда, 29 июня 2011 г.

Парсим текст из .doc файла


Задача получения текста из .doc файла возникает нередко и способов решения масса - это и COM объекты под Windows, и сухие реализации на чистом php (хорошие и не очень), и разнообразные библиотеки для всех мыслимых и нет языков программирования. Однако хакерский подход, как известно, заключается в максимальной простоте, универсальности и обходе большинства проблем в лоб:). Поэтому воспользуемся готовым проверенным временем и миллионами мух решением - Antiword.

Antiword - консольная утилита для получения plain текста из файлов Microsoft Word версий 2, 6, 7, 97, 2000, 2002 и 2003. Иными словами, с новым форматом docx Antiword не работает, и если есть необходимость интегрирации с ним, придется искать другое решение. Я писал парсер doc файлов для загрузки данных в БД, поэтому мою задачу утилита решает на ура.

Итак, скачать Antiword можно на официальном сайте - есть версии для Windows и Linux. Качаем архив, распаковываем его в C:\Antiword\ (я буду рассматривать Windows, Linux команды аналогичны), запускаем консоль. Тестим!

C:\Antiword\antiword.exe C:\myDocument.doc

Собственно все уже работает - неплохо для одной простой команды, верно? Здесь C:\myDocument.doc это документ, текст которого нас интересует. При таком вызове команды результат работы будет полностью выведен в консоль, причем не всегда в приятной кодировке. Чтобы настроить вывод текста в кодировке cp1251 (стандартная кодировка для кириллицы в Windows), необходимо модифицировать команду следующим образом:

C:\Antiword\antiword.exe -m cp1251.txt C:\myDocument.doc

Теперь текст выводится в кодировке cp1251. Все равно крякозябры? Да у вас не настроена консоль, батенька! Исправляем установкой шрифта терминала Lucida Console и выполнением команды:

chcp 1251

Теперь все ок? Так-то. Хорошо, но как вывести plain текст в файл? О, да вы еще и о перенаправлении стандартного вывода в файл не слышали:)

C:\Antiword\antiword.exe -m cp1251.txt C:\myDocument.doc >> C:\plainText.txt

Теперь текст из вордовского файла C:\myDocument.doc магическим образом помещен в txt документ C:\plainText.txt.

В процессе конвертирования Antiword старается сохранить форматирование текста максимально близко к оригиналу. В частности, он вставляет переносы строк в соответствии со стандартной шириной страницы, изменить это поведение можно указанием параметра -w, который принимает количество символов в одной строке. Указав 0, мы отключаем режим переноса строк и все абзацы будут помещены в одну длинную строку:

C:\Antiword\antiword.exe -m cp1251.txt -w 0 C:\myDocument.doc >> C:\plainText.txt

В случае необходимости использования функционала Antiword в своей программе, просто вызывайте эту команду с подстановкой нужных параметров и считывайте результат из текстового файла или стандартного вывода. В случае php:

$fileContent = shell_exec("C:\Antiword\antiword.exe -m cp1251.txt -w 0 C:\myDocument.doc");

Таким образом, в переменную $fileContent будет загружен полный текст документа в виде строки. Для вашего языка программирования воспользуйтесь аналогичной функцией вызова системных команд.

И не забывайте принцип KISS - Keep It Simple, Stupid:)

Настройка кодировки консоли Windows


Проблема: полностью описана на картинке выше. Задача формулируется в получении из картинки слева картинки справа:). Предполагается, что выводимый текст находится в кодировке Windows-1251 (cp1251), вывод текста в соответствующей кодировке из программы является задачей непосредственно программы, в статье рассматривается вопрос отображения текста консолью.

Решение.

1. Выполните в консоли следующую команду

chcp 1251

2. Смените шрифт консоли на Lucida Console.

Вуаля!

Windows: deletion of service

Problem: some ugly Windows programs (such as Mail.ru Guard, i hate it!) use technology of services for permanent keeping its own code in RAM. It means that useless program spends your memory and it is a natural desire to delete it. But in case of mentioned Mail.ru Guard (and some other soft) simple operation of program removing don't removes the service and it will start every time with Windows.

But dont worry! There is simple method to stop and delete haunting service:)

Solution.

Run terminal as Administrator and perform this code:

sc delete service_name

where service_name is name of ugly service. For example:

sc delete "Guard.Mail.ru"

Be happy!

вторник, 21 июня 2011 г.

Vim: separate .swp files and sources of project


Do you want separate .swp files of Vim and source files of your project? Nothing is easier! Simple add in your .vimrc file this line:
set directory=C:\Temp\

пятница, 10 июня 2011 г.

Deploying servlets on Weblogic server with weblogic.Deployer


Good day!

I want to tell you about servlets deployment on Oracle Weblogic server. Its my first post in English, so sorry for my language - its not my native:)

So, we developed some Java servlets and want to deploy them on server for testing it. If you read this article, i can suggest, that you are interesting in Weblogic server. There are several ways to deploy servlets, for example using Weblogic Administration console or using weblogic.Deployer tool. I like command-line tools, because its really simple, fast and give me many ways to automate my work. Weblogic.Deployer - command-line utilite for fast deploying applications on weblogic server.

First, we need create war file (Web Application Archive, see wikipedia) with our servlets. In windows (unix's way is not so different) we need write this code in terminal:

chdir C:\dir\with\servlets\
jar -cvf  myServlets.war *

First line changes current directory to directory with our servlets. Second line creates one war file with all files of our servlets - html, css, js, WEB-INF directory with web.xml and so on. So we have myServlets.war file.

Now we can deploy it -  with weblogic.Deployer utilite it is really simple:

java -cp C:\Oracle\Middleware\wlserver_10.3\server\lib\weblogic.jar weblogic.Deployer -adminurl t3://localhost:7001 -user myusername -password mycoolpass -deploy myServlets.war

Yeah! Is it not cool? Some notes. Weblogic must be started for work with weblogic.Deployer - deploying can be done only on the running server, of course. Also console application must be deployed (you can load url http://localhost:7001/console for it, its very simple). C:\Oracle\Middleware\wlserver_10.3\ is directory with installed Weblogic server. localhost:7001 is URL of my Weblogic Administrtion console (note that I use t3 instead of http protocol), myusername is username for administration console and mycoolpass is password for this account. So, we need just 2 commands for create war file and deploy it on server! 2 commands is too much? Ok, we can merge it:

jar -cvf  myServlets.war * && java -cp C:\Oracle\Middleware\wlserver_10.3\server\lib\weblogic.jar weblogic.Deployer -adminurl t3://localhost:7001 -user myusername -password mycoolpass -deploy myServlets.war

This way of deploying java servlets is more fast than using web interface of administration console.

четверг, 9 июня 2011 г.

ExtJS 3 grid colspan или группировка колонок



Задача: необходимо объединить несколько колонок таблицы grid. В чистом HTML для этого существует атрибут colspan, но как реализовать это в элементах ExtJS?
В данной статье рассматривается третья версия ExtJS.

Решение.

Необходимо воспользоваться плагином ColumnHeaderGroup, пример использования которого можно посмотреть здесь.

Итак, для осуществления группировки колонок, необходимо:

  1. подключить js файл плагина (ColumnHeaderGroup.js)
  2. в коде Ext.onReady() описать заголовок таблицы следующим образом:

    // объявляем массив для хранения первой строки заголовка
    var row1 = [];
    // заполняем его
    row1.push({
      header: ''
    });
    row1.push({
      header: 'Ставка, руб./1 GT',
      colspan: '3',   // да-да, это аналог того самого colspan
      align: 'center' // выравнивание текста в ячейке
    });
  3. создать заголовок таблицы, используя плагин:

    groupedRows = new Ext.ux.grid.ColumnHeaderGroup({
      rows: [ row1 ]
    });

    В случае, если таких строк с объединенными колонками будет несколько - просто перечислите их черех запятую в массиве rows:

    groupedRows = new Ext.ux.grid.ColumnHeaderGroup({
      rows: [ row1, row2, row3 ]
    });


  4. подключить инициализированный плагин к таблице grid:

    grid = new Ext.grid.GridPanel({                
      xtype: 'grid',
      store: companiesStore,
      plugins: [ groupedRows ],
      border: false, frame: false,
      columns: columnModel
    });

Таким образом, заголовок таблицы делится на 2 типа строк - самая нижняя строка, описываемая в параметре columns модели GridPanel согласно всем стандартным правилам описания колонок таблицы grid, и все остальные строки, располагающиеся выше и объединяющие колонки при помощи плагина. Закомментируйте строку plugins: [ groupedRows ] и в заголовке останется только одна строка, нижняя, все верхние строки должны описываться как показано выше при инициализации плагина.

Описанный способ позволяет создавать сколь угодно сложные структуры заголовков таблиц.

понедельник, 23 мая 2011 г.

ExtJS 3 суммирование строк таблицы Grid


Рассматриваемая сегодня задача встречается очень часто: необходимо добавить в таблицу Grid дополнительную строку, которая будет выводить сумму значений из расположенных выше строк. В статье рассматривается ExtJS 3, в новой четвертой версии описанный функционал уже включен в ядро и хорошо документирован.


Создано несколько версий плагина GridSummary с различными доработками, они описаны здесь. Примеры использования плагина можно посмотреть в исходном варианте плагина здесь, но в проекте лучше использовать финальную на момент написания статьи версию. Да, для загрузки файлов необходимо зарегистрироваться на официальном форуме разработчкиков ExtJS (http://www.sencha.com/forum/), если вы до сих пор этого не сделали.

Ниже описан процесс подключения и конфигурирования плагина.

Первым шагом является подключение css и js файлов:

<link rel="stylesheet" href="gridsummary.css"/>
<script src='./js/gridsummary.js'></script>

Обратите внимание, что оба файла необходимо подключать после всех файлов самого ExtJS.

Вторым шагом является подключение плагина в теле функции onReady():

var summary = new Ext.ux.grid.GridSummary();
formDataGrid = new Ext.grid.EditorGridPanel({
  plugins: [ summary ],  store: myStore,
  cm: [
    {
      header: 'Компания',
     dataIndex: 'comp',
      summaryRenderer: function(){ return 'Всего: ';}
    }
    {
      header: 'Цена',
      dataIndex: 'price' ,
      summaryType: 'sum'
    },
    {
      header: 'Цифра какая-то',
      dataIndex: 'someInt',
      summaryType: 'avg'
    }
    //...
  ]

Как вы видите, все довольно просто. Вы указываете плагин summary и в описание ColumnModel добавляете описание параметров работы плагина. Основных параметра два - это summaryType, определяющий тип обработки данных (суммирование sum, вычисление среднего avg, подсчет количества строк count и др.) и summaryRenderer - функция-рендер для ячейки строки суммы, в случае указания  она определяет итоговый состав и стиль выводимых данных.

Например, для вывода положительной суммы зеленым цветом, а отрицательной красным, необходимо написать такой простенький рендер:

...
summaryRender: function pctChange(val) {
  if (val > 0) {
    return '<span style="color: green;">' + val + '%</span>';
  } else if(val < 0) {
    return '<span style="color: red;">' + val + '%</span>';
  }
  return val;
}
...

Успехов!

ExtJS найти элемент и изменить его содержимое

Задача: необходимо на лету по ID элемента изменить его содержимое. Например, моей задачей было изменение текста ссылки, позволяющей сворачивать и разворачивать элемент на странице. Когда элемент свернут, текст ссылки должен быть "развернуть", когда развернут - соответственно, "свернуть".

Подобные задачи легко решаются любым современным Javascript фреймворком. В случае ExtJS изменение содержимого элемента с id area будет выглядеть так:

Ext.fly('toggleLink').update('Свернуть описание');

Найти элемент по его классу также несложно, можно воспользоваться полноценным CSS селектором:

var parent = Ext.get("IdOfParent");
var aHrefs = parent.select("div.myCoolDivClass a").elements;

четверг, 19 мая 2011 г.

JavaScript is_int

Простейшая функция, проверяющая, является ли переменная целым числом. Своего рода аналог php is_int().

function is_int(x) { 
   var y = parseInt(x); 
   if (isNaN(y)) return false; 
   return x == y && x.toString() == y.toString(); 
}

В ExtJS есть отличная функция Ext.num(i, 0), проверяющая, является ли i целым числом и возвращающая его в случае успеха или 0 (второй параметр) в случае неудачи. Например:

i = Ext.num(i, 0)

среда, 18 мая 2011 г.

Vim переместить вкладку



Проблема: в GVim необходимо переместить вкладку (таб) вправо или влево.

Решение:

tabmove 3

Здесь 3 - номер, куда необходимо переместить вкладку. Вместо tabmove можно использовать сокращение tabm.

Перемещаться между вкладками можно сочетанием gt и gT (вправо и влево соответственно) или Ctrl+Page_Down и Ctrl+Page_Up.

PS. Я люблю vim, да.

понедельник, 2 мая 2011 г.

Восстановить загрузчик GRUB

Проблема: вы установили Linux с GRUB загрузчиком и потом установили Windows - именно в таком порядке. И если GRUB не трогает загрузчик Windows, то последний не обладает той же дружелюбностью и перезаписывает GRUB на раз, после чего загрузка Linux становится невозможной и при перезапуске компьютера автоматически начинает загружаться Windows, даже не вспоминая о второй установленной ОС.

Решение: необходимо загрузиться с Live CD Linux и выполнить ряд операций в консоли.

sudo grub
find /boot/grub/stage2

Результатом выполнения последней команды будет, например, (hd0,2). Используем это в следующей команде:

root (hd0,2)
setup (hd0)

На этом все - при последующей перезагрузке вас снова встретит меню загрузчика GRUB. Отредактировать это меню можно в /boot/grub/menu.lst - там можно поменять имена загружаемых ОС и изменить их порядок, например, выбрать ОС для загрузки по умолчанию, поставив ее первой в списке.

понедельник, 25 апреля 2011 г.

Грамотно убиваем Backdoor.Win32.Shiz

Мой рабочий ноутбук поразил неизвестный мне вирус:) Знаете, ковырять вирусы для меня - это как решать некую головоломку или, скажем, японский кроссворд разгадывать. Весьма увлекательное занятие, стоит сказать!

На ноутбуке стоит корпоративный Symantec, который, как оказалось, не предоставляет должной защиты. Все началось с пресловутого подтормаживания компьютера. Зайдя в процессы, я увидел много незнакомых, и первая мысль была - проверить все незнакомые на virustotal.com, что я и сделал. Однако, тот молчал как рыба и уверенно указывал на отсутствие вредоносного кода в моих  запущенных приложениях. Кто не в курсе - Virustotal это бесплатный онлайн инструмент для проверки добросовестности любых ваших файлов: вы загружаете файл и система проверяет его множеством антивирусов, выводя отчет по каждому. Очень и очень полезный сервис для всех интересующихся информационной безопасностью.

Итак, вторая мысль - скачать on demand версию Kaspersky и drWeb (virus removal tool и cureIT соответственно). Virus removal tool и CureIT - бесплатные утилиты для удаления вирусов, не предоставляющие защиты в реальном времени, но использующие последние вирусные базы, это лучшее решение для лечения разного рода вредоносных программ и именно с них следует начинать.

Каково же было мое удивление, когда, вбив в поисковике kaspersky, я наблюдал стремительно закрывающийся браузер! Так, это что-то новенькое, проверим в другом... При попытке зайти на kaspersky.ru вылетели все установленные у меня версии браузеров - Chrome, IE, Opera, Firefox. Скачать бесплатную антивирусную утилиту AVZ также не удалось.

Меня эта уловка вирусописателей порядком порадовала, и я недолго думая пошел в обход - благо, что у меня есть свои сервера в интернете и я могу воспользоваться ими для выкачивания любых объектов из сети с целью последующей передачи на рабочий компьютер. Зайдя по SSH на сервер, я тремя командами загружаю на него необходимые утилиты:

wget avz
wget virus_removal_tool
wget cureIT

здесь вместо avz, virus_removal_tool и cureIT, разумеется, необходимо подставить URL адреса этих программ. Затем через SFTP я слил скачанные утилиты к себе на комп и продолжил головоломку:) Если вы ничего не знаете не знаете о серверах, unix'ах и wget, но попали в аналогичную ситуацию - просто скачайте названные утилиты с незараженного компьютера.

Тааак, устанавливаем касперского, запускаем... Хрен! Программа вылетает. Хм, это начинает казаться интересным. Лезу опять в процессы, пересматриваю их. Решаю убить explorer.exe - это оболочка windows, отвечающая за работу проводника, отрисовку меню ПУСК и т.д. Совершенно ненужная вещь в общем-то=).

При отключении explorer'а браузеры стали стабильно работать с сайтами антивирусных производителей - агааа! Вот ты где, красавчик наш:) Не запуская explorer, открываю касперского и запускаю проверку. Тот довольно быстро находит зараженные файлы, лечит их и перезагружает уже здоровый комп.

Вирус побежден. Не все уж так и сложно оказалось;)

Итак, мои советы по борьбе с Backdoor.Win32.Shiz:

  1. Запускаем браузер
  2. жмем WIN+R, вводим cmd и жмем Enter. Откроется окно командной строкой windows. Да, кто не в курсе - кнопка WIN - это клавиша на вашей клавиатуре в нижнем ряду с нарисованным флажком.
  3. ALT+CTRL+DELETE, вкладка Процессы. Находим explorer.exe, правой кнопкой мыши - завершить, соглашаемся. Пропала кнопка ПУСК и нижняя панель - это нормально, не пугаемся. Все вернется после перезагрузки или после применения описанных ниже действий.
  4. зажимаем ALT и жмем TAB несколько раз для переключения в окно браузера
  5. скачиваем антивирусные утилиты от Касперского и DrWeb (мне, впрочем, помог Virus removal tool и CureIT не понадобился)
  6. устанавливаем Virus removal tool (он не интегрируется в систему и, как правило, устанавливается в директорию на рабочем столе), запускаем его. Чтобы запустить, перейдите в окно командной строки и введите там полный адрес к установленной программе, в моем случае "d:\Documents and Settings\AGoloburdin\Рабочий стол\Virus Removal Tool\setup_9.0.0.722_25.04.2011_10-16\setup_9.0.0.722_25.04.2011_10-16.com". Если вы не знаете, в какое место поставилась программа - запустите explorer.exe, найдите программу и скопируйте путь до нее и затем остановите explorer.exe снова.

    Для запуска остановленного процесса в окне диспетчера задач нужно выбрать пункт меню Файл->Новая задача, в открывшемся окне ввести explorer.exe и назжать Ок, программа запустится. Для останова - выберите ее на вкладке Процессы, нажмите правой кнопкой мыши и выберите Завершить процесс.
  7. в запустившемся Virus removal tool жмем Начать проверку.
На этом все. Здоровых компьютеров вам:).

пятница, 22 апреля 2011 г.

Поднять proxy сервер за 2 минуты

Цель: максимально быстро поднять proxy сервер для HTTP(S) трафика. В примере используем Debian.

Решение: ставим и запускаем ffproxy:

sudo apt-get install ffproxy 
sudo /etc/init.d/ffproxy start

Ваш прокси уже доступен по ip адресу сервера и дефолтному порту 8080 - и если ничего больше вам не нужно, дальше можете не читать.

При необходимости, порт, на который прокси будет принимать соединения, можно изменить в конфигурационном файле /etc/ffproxy/ffproxy.conf. Например, port 3128.

Команды останова и рестарта сервера стандартны:

sudo /etc/init.d/ffproxy stop|restart

Чтобы разрешить подключение только из одной подсети или одного ip, необходимо подправить /etc/ffproxy/db/access.ip, введя регулярное выражение для принимаемых ip. Для разрешения подключения только одного адреса необходимо ввести ^195.0.0.1$, где 195.0.0.1 - адрес, с которого будет разрешено подключение. Знакомые с регулярными выражениями смогут без труда подправить адрес для приема соединений из подсети.

Есть необходимость организовать цепочку из прокси серверов для усложнения поиска действительного источника? Нет ничего проще - в конфигурационном файле /etc/ffproxy/ffproxy.conf добавьте параметры

forward_proxy 195.10.10.12
forward_proxy_port 8082

где forward_proxy и forward_proxy_port - ip и порт внешнего прокси сервера, с которого наш сервер будет запрашивать данные.

понедельник, 18 апреля 2011 г.

ExtJS проблемы в Internet Explorer

ExtJS, как гласит официальный сайт, является кроссплатформенным фреймворком для построения Rich Internet Application - "богатых интернет приложений", то есть, приложений, обладающих функциональностью обычных настольных программ. И это действительно так.

Однако, нередко при работе с ExtJS некоторые проблемы с кроссбраузерностью все-таки наблюдаются. Тем не менее, стоит отдать должное разработчикам фреймворка, эти проблемы обусловлены в первую очередь разными реализациями Javascript в разных браузерах и для их устранения следует всего лишь следовать хорошему тону программирования.

Нередко наблюдается, когда интерфейс отлично функционирует в Firefox, но даже не отрисовывается в Internet Explorer и браузер выдает многочисленные сообщения об ошибках.
Как правило, причина такого поведения заключается в разной реализации массивов Javascript. Известно, что в ExtJS для конфигурирования элементов используются исключительно объекты и массивы объектов, например:

vp = new Ext.Viewport({
    layout: 'border',
    renderTo: Ext.getBody(),
    items: [
        firstElement,
        secondElement,
        thirdElement
    ]
});

Здесь создается Viewport, элементами которого являются firstElement, secondElement и thirdElement - и этот код будет работать одинаково во всех заявленных разработчиками ExtJS реализациях браузеров. Однако, попробуйте выполнить этот код:

vp = new Ext.Viewport({
    layout: 'border',
    renderTo: Ext.getBody(),
    items: [
        firstElement,
        secondElement,
        thirdElement, // запятая
    ]
});

Internet Explorer выдаст ошибку. Проблема в реализации массивов Javascript: если после последнего элемента стоит запятая, IE считает, что в конце массива присутствует еще один, пустой, элемент. ExtJS пытается обработать этот элемент, но, так как он фактически пуст, генерируется ошибка.

Другие реализации Javascript (в Firefox, например) на запятую после последнего элемента массива внимания не обращают, однако завершать последний элемент запятой считается дурным тоном программирования.

Приведенная ошибка - наиболее часто встречающаяся при разработке на ExtJS, и ее необходимо всегда иметь в виду. Будьте внимательны к последним элементам массивов и объектов, и это упростит вам жизнь при последующем тестировании кроссбраузерности ваших интерфейсов.

Вторая проблема - использование в коде конструкции console.log() для вывода сообщений в консоль Firebug или Chrome. Internet Explorer, к сожалению, не имеет консоли как таковой, поэтому попытки ее использования генерируют ошибку; необходимо помнить, что после окончания тестирования и отладки интерфейса, если вы использовали вывод сообщений в консоль, их необходимо удалить или закомментировать. Впрочем, эта ошибка, в отличие от вышеописанной, легко определяется.

суббота, 16 апреля 2011 г.

Защита конфиденциальных данных

У всех нас есть информация, которую можно отнести к конфиденциальной - это пароли доступа к различным ресурсам, pin коды карт, некоторые важные документы. В категорию конфиденциальных данных может попасть любая иная информация, кража которой нежелательна для ее владельца.

Хотите быть уверенными в том, что ваши данные не будут просмотрены третьими лицами - добро пожаловать. Вопрос защиты данных актуален, поэтому я хочу осветить этот вопрос подробно. Описанные методики кроссплатформенны и одинаково работают для всех.

Виды конфиденциальной информации


Итак, условно всю информацию, требующую отдельного подхода к ее защите, можно разделить на две группы: это пароли доступа к ресурсам и важные документы. К первой группе относятся все данные для успешной аутентификации в различных системах (онлайн-банкинг, почта, электронные деньги и т.д.); ко второй - например, паспортные данные (если по каким-то причинам вы храните их в цифровом виде), другие важные файлы/документы.

Хранение паролей и аутентификационной информации


Из-за специфики первого типа информации - а это, как правило, пара логин-пароль и, возможно, адрес ресурса - их удобно хранить в виде зашифрованной базы данных. Для этой цели лучше всего использовать программу KeyPass, поддерживающую современные алгоритмы шифронания (AES, Rijndael, Twofish), бесплатную и распространяемую с открытыми исходными кодами. Последний пункт в частности гарантирует отсутствие в программе различных backdoors, то есть скрытых способов доступа к данным без пароля, специально зашитых в программу. KeyPass кроссплатформен - есть версии для Windows, Linux, Mac OS и даже Android, что позволит пользоваться вашей базой паролей в любом месте и на любом компьютере. Стоит отметить, что есть портабельные версии программы, позволяющие работать, например, с флеш носителя без установки в систему.



KeyPass шифрует всю введенную информацию, а не только пароли, как делают некоторые другие аналогичные системы, таким образом, никто не сможет получить доступ ни к одному полю в вашей базе - ни к логину, ни к адресу ресурса, ни к любой другой информации. KeyPass предоставляет защиту от keylogger программ, позволяющих перехватывать вводимую с клавиатуры информацию. Это одно из лучших решений на сегодняшний день.

Принцип работы с программой прост - вы создаете новую базу паролей (File -> New), вводите мастер-пароль и сохраняете файл с базой. Созданный файл и будет вашим контейнером паролей, который можно безопасно хранить. При открытии базы KeyPass попросит вас ввести мастер пароль, введя который, вы получаете доступ ко всей сохраненной внутри информации. Рекомендации по созданию паролей приведены в конце этой статьи, обязательно обратите на них внимание.

Также стоит отметить программу 1password. Это уже небесплатное решение (40$), однако, по-прежнему кроссплатформенное (Windows, Mac OS, IOS, Android) и очень удобное. 1password имеет возможность безопасно хранить помимо паролей заметки, контактные данные; умеет автоматически вставлять логины-пароли на сайтах, тесно интегрируясь с браузерами. Безусловно, эта программа стоит своих денег, однако я все же предпочитаю open source решение.

Хранение конфиденциальных документов


Распространенная ситуация: вам необходимо иметь при себе на флеш носителе некоторую важную информацию, кражу которой нельзя допустить. В такой ситуации нужно иметь в виду возможность потери носителя и соответственно кражи всей незащищенной информации. Та же ситуация возникает при переносе информации на ноутбуке. Впрочем, действительно важные документы необходимо защищать и на домашнем стационарном компьютере, и на рабочем.

Для решения этой задачи используются криптоконтейнеры. По сути, криптоконтейнер - это отдельный файл или целый раздел на диске, содержащий в себе в зашифрованном виде некоторые документы. Существует возможность создавать также скрытые разделы на диске.

Вообще говоря, разработано немалое количество программных решений для создания криптонтейнеров и работы с ними, мы рассмотрим TrueCrypt - кроссплатформенная бесплатная программа с открытыми исходными кодами. TrueCrypt шифрует файлы на лету, позволяет создавать криптоконтейнеры-файлы и может шифровать целые разделы жесткого диска. Шифруется полностью все содержимое контейнера, включая имена файлов и директорий.



Криптоконтейнер TrueCrypt подключается в системе как обычный логический диск. Вы открываете TrueCrypt, выбираете контейнер, вводите мастер-пароль или указываете файл-ключ и в вашей системе появляется дополнительный логический диск, с которым вы работаете как с обычным диском - создаете файлы, директории, изменяете их. Закончив работу, вы отмонтируете диск в TrueCrypt, нажав соответствующую кнопку; все сохранения уже сохранены в ваш криптоконтейнер.

Создание криптоконтейнеров


После установки TrueCrypt вам будет предложено пройти краткий туториал по работе с программой, я рекомендую просмотреть его для знакомства с программой, он действительно прост и эффективен, хотя сведения по работе с TrueCrypt я приведу нижу. Процесс создания криптоконтейнера состоит из нескольких диалоговых окон, задающих размер контейнера (максимально возможный, увеличить его впоследствии будет нельзя) используемый алгоритм шифрования, мастер-пароль.

Если английский вам неприятен, то рекомендую перед началом работы с программой русифицировать ее, выбрав пункт меню Settings->Language и выбрав русский язык. Если его нет в списке - загрузите языковой пакет отсюда и скопируйте его в папку с установленным TrueCrypt (например, C:\Program Files\TrueCrypt).

Для создания криптоконтейнера необходимо провести ряд несложный действий:

  • жмем создать том;
  • указываем создать зашифрованный файловый контейнер;
  • выбираем обычный или скрытый том TrueCrypt (в обычных случаях достаточно обычного);
  • указываем размещение тома - файл криптоконтейнера, в котором впоследствии будут храниться документы;
  • выбираете алгоритм шифрования (можно оставить по умолчанию AES) и хеш-алгоритм (например, SHA-512);
  • размер тома - максимальный объем информации, который можно будет поместить в контейнер, столько же всегда будет весить файл-криптоконтейнер вне зависимости от наполнения;
  • задаем пароль от контейнера (можно также задать дополнительно ключевые файлы, которые впоследствии необходимо будет предоставить помимо пароля для работы с содержимым контейнера);
  • задаем файловую систему (можно оставить FAT);
  • жмем разметить и выход.


Чтобы подключить созданный ранее контейнер - жмем Файл, выбираем файл-контейнер, жмем смонтировать, вводим пароль, при необходимости указываем ключевой файл. В вашей системе появится новый диск с данными, с которыми вы можете работать как с обычными данными. По окончанию работы жмете в TrueCrypt Размонтировать и диск автоматически удалится из системы, все данные будут сохранены в зашифрованном виде в контейнере.

Теперь в случае потери файла-криптоконтейнера можно ни о чем не беспокоиться - ваша информация надежно защищена. Впрочем, если ваш мастер-пароль не слишком прост, о чем пойдет речь ниже.

О хороших паролях


В заключение повествования расскажу о том, что такое хороший пароль и почему пароль непременно должен быть хорошим.

Как правило, наиболее слабым звеном в любой системе защиты является человек. Какие бы алгоритмы шифрования и методики защиты информации не применялись - пароль вида 123 подбирается за предельно сжатые сроки, современные компьютеры и оптимизированные программы брутфорса (подбора) паролей позволяют проводить подбор со скоростью несколько миллионов элементов в секунду.

Таким образом, для обеспечения достаточной безопасности необходимо использовать пароли, которые невозможно подобрать за разумное время. Это не должны быть значимые слова, они подбираются в первую очередь по специальным словарям.  В пароле должны присутствовать прописные и строчные буквы, цифры, специальные символы ($#!^&;,?). Длина должна быть не менее 8 символов, но всегда больше - лучше. Неплохим вариантом может быть, например, пароль из двух слов в разных регистрах, связанных специальными символами web?48deSign.

Пара слов о терморектальном криптоанализе


Как я уже говорил, наиболее слабым звеном в системе защиты является человеческий фактор, к сожалению, сложные пароли и системы шифрования не помогают в случае применения банального насилия. Поэтому, если вы работаете с имеющими коммерчускую ценность данными, помните: лучший способ сохранить их - не упоминать о их наличии.

Безопасных документов вам!

четверг, 14 апреля 2011 г.

Рекомендации начинающим изучение Ext JS

По своему опыту использования Ext JS могу сказать, что в общем и целом этот фреймворк меня приятно удивил и я уверенно могу сказать, что время на его изучение себя оправдывает. Ниже - несколько общих слов о Ext JS и рекомендации решившим начать работу с ним.

Зачем


Ext JS позволяет писать сложные интерфейсы по-настоящему быстро, полностью соответствуя концепции RAD - Rapid Application Development. Не нужно заниматься html и css кодированием для разработки по-настоящему богатых функциональных интерфейсов, выполненных в едином стиле, вместо этого вы пишете несложный javascript код, выполняющий всю работу за вас.

Команда Ext JS создала целую коллекцию примеров (для 3й версии Ext JS) для ознакомления с основными возможностями фреймворка. Посмотрите, какие изящные конструкции вы сможете создавать минимальным количеством JS кода! Абсолютно все конфигурируется под себя, каждый элемент обладает огромным количеством событий (events), позволяющих реагировать на различные действия со стороны пользователя. Разработчикам удалось создать действительно очень и очень гибкий инструмент для построения классых веб интерфейсов.

Возможно, вы уже работали с некоторыми javascript фреймворками, например, JQuery или Prototype и уже используете их в своих проектах. Ext JS спроектирован таким образом, чтобы обеспечить возможность корректной работы и с другими фреймворками, таким образом, вы можете абсолютно спокойно оставить ваши наработки на JQuery и начать внедрять элементы Ext JS. Дело в том, что Ext JS осуществляет работу с DOM (Document Object Model, объектная модель документа) посредством так называемых адаптеров, причем в комплекте поставки уже есть 4 адаптера - родной ext, jquery, prototype и yui. Другими словами, фреймворк может использовать разные библиотеки для выборки элементов страницы, поэтому можно не беспокоться о совместимости и при желании продолжать пользоваться параллельно другим знакомым фреймворком.

С чего начать


Начать, пожалуй, следует с некоторых вводных статей о Ext JS. Для русскоязычного пользователя отличным вступлением может послужить небольшой цикл статей от techwork.ru. Обратите внимание, что в статьях описана третья версия, однако, прочитав этот материал, вы уже будете иметь общее представление о фреймворке и работе с ним.

Дальнейшее изучение Ext JS я бы порекомендовал продолжить с книгой, коих в общем-то достаточно. Да, к сожалению, на русский язык материалы по фреймворку пока не переведены, поэтому для ознакомления потребуется некоторое знание технического английского. Впрочем, спешу успокоить тех, кого это пугает - язык в книгах по Ext JS довольно простой.

Отличная книга - Learning Ext JS 3.2 (вот). В сети есть pdf в отличном качестве, который можно распечатать, почитать на мониторе/iPad'е/электронной книге.

В настоящий момент я пишу книгу Ext JS 4: разработка Rich Internet Applications на русском языке.

Разработчику


Безусловно, одним из наиболее часто используемых источников информации является официальная документация (для третьей версии Ext JS) по API Ext JS. Приведены все конфигурационные опции, в показательных случаях примеры использования - составленная весьма удобным образом, эта документация служит отличным справочным пособием любого Ext JS разработчика.

Выше я упоминал готовые примеры (для третьей версии Ext JS) использования фреймворка - чрезвычайно полезный инструмент, когда нужно быстро посмотреть, как реализуется какой-то функционал. К тому же, читать коды от самих разработчиков очень интересно и познавательно.

Официальные Tutorials (руководства) также являются зачастую хорошим источником для восполнения недостающих знаний.

В общем случае, для начала работы с Ext JS требуется не более 20 минут знакомства с документацией или литературой - потратьте это время и уже скоро сможете оценить всю мощь этого замечательного инструмента. Классных интерфейсов вам!

вторник, 12 апреля 2011 г.

Gmail tips & tricks

Что это?!

Начнем, пожалуй, с того, что я люблю GMail! Да-да, это действительно лучшее решение электронной почты на сегодняшний день. Ниже пойдет речь о том, почему я так считаю, также я расскажу о некоторых возможностях GMail, о которых вам наверняка будет интересно знать.

Итак, поехали!

Если у вас уже есть электронная почта не-gmail, то вы сейчас думаете - если я заведу еще одну почту, то мне придется каждый день проверять 2 ящика: мой старый и новый gmail. Подумаете - и будете правы! Но только не в случае использования gmail.

Во-первых, GMail умеет сам собирать почту из других почтовых сервисов. Если у вас несколько ящиков - воспользуйтесь этой функцией и, черт возьми, вы не поймете, как раньше обходились без нее! У меня несколько активных email адресов и читать всю почту в одном месте и в одном интерфейсе чрезвычайно круто, поверьте.

Настраивается эта функция без лишних плясок - Настройки, Аккаунты, Получать почту из других аккаунтов, Добавить свой почтовый аккаунт. В открывшемся окне вводим свой логин, пароль, адрес pop3 сервера (при желании отправлять почту с не-gmail ящика из единого интерфейса gmail - еще и smtp сервер) - и, собственно, все. Все письма старого ящика будут автоматически загружаться в gmail.

И целого мира адреса мало


Иногда возникает ситуация, когда одного адреса почты бывает мало. Банальный случай - вы хотите зарегистрироваться повторно на каком-то сайте, но создавать дополнительный ящик для этого не хотите. И здесь GMail приходит на помощь! Все адреса с точкой внутри логина в любом его  месте фактически являются одним адресом. Другими словами, все письма на vasya.pupkin@gmail.com, vasyapupkin@gmail.com, и даже v.a.s.y.a.pupk.in@gmail.com придут в один ящик. Пупкина не проведешь!

Обсуждения - диалогом


Письма в GMail отображаются в режиме диалога (как смс в iphone или android). Это значит, что конкретная переписка не разбросана по всему ящику, а сосредоточена в одном месте. Вам ответили  - вы видите весь ход обсуждения. В случае использования не-диалогового режима старое письмо длинной переписки приходится искать в гуще давних сообщений - если вам не приходилось этого делать, поверьте на слово, это ужасно.

Вообще про продуманный интерфейс в gmail говорить не стоит - это самая удобная система почты из всех, что я видел. К слову говоря, весь интерфейс работает с одной страницы без ее перезагрузки, что позволяет достичь истинно быстрой и удобной работы. Например, новые письма автоматически появляются на странице сразу после их появлении в ящике без каких-то действий с вашей стороны.

Отлично, идем дальше.


GMail поддерживает темы, причем не какие-то непонятные страшные темы, а отличные дизайнерские шкурки для вашей почты. Мне, например, нравится тема "рабочий стол", но это дело вкуса, разумеется.

Метки и фильтры - друзья человека



Метки. Удобнейший инструмент работы с почтой! Например, все письма в моем ящике можно разделить на несколько категорий. Это уведомления о сообщениях и комментариях из социальных сетей, это рабочие письма, письма друзей и родственников и т.д. Согласитесь, было бы удобно каким-то образом каталогизировать эту информацию, что позволит быстрее находить нужное письмо и хранить все в более структурированном виде.

Для этих целей в gmail созданы так называемые метки или ярлыки. Каждый ярлык имеет свое имя и цвет, и все письма с этим ярлыком помечаются соответствующим образом.

Назначить письму ярлык можно либо вручную, либо автоматически при помощи фильтров - еще одного мощного инструмента для работы с почтой в gmail. При помощи фильтра можно найти письма от конкретных людей, содержащие или не содержащие конкретные слова в тексте или теме письма. С отфильтрованными таким образом письмами можно выполнить ряд действий: автоматически архивировать, пометить, назначить ярлык и даже удалить. Приходит письмо - и автоматически ему присваивается ярлык или оно автоматически помечается как важное - согласитесь, это удобно.

Создать фильтр можно в соответствующем разделе настроек.

Да где же это письмо-то?


Как известно, GMail - служба Google. А это значит, что мощнейшие средства поиска встроены и в вашу почту. Например, чтобы найти в вашем gmail ящике все письма от президента России, необходимо в строке поиска ввести from:president@kremlin.ru. Найти все непрочитанные письма: label:unread. Ограничить поиск датами: добавьте after: 01/08/2010 before: 01/09/2010. Быть может, мы ищем письмо с вложениями? Нет ничего проще: has:attachment. Нас интересуют письма в спаме: in:spam. Полный список параметров для поиска можно посмотреть здесь.

О плюшках


Вы еще не воодушевлены gmail? Я добью вас экспериментальными функциями, расположенными в одноименном разделе настроек вашего gmail.

Хотите включать изображения в ваши письма так же просто, как в word? Нет ничего проще - включите расширение Вставка картинок. Для добавления картинки в текст письма необходимо просто нажать кнопку на панели редактирования письма и указать загружаемую картинку. Никаких сторонних сервисов для хранения изображений - картинка уже в тексте письма.

У вас были случаи, когда поздно ночью вы отправляли письмо, о котором жалели утром? Тогда функция Защита от необдуманных писем - для вас. Официальное описание модуля лучше всего характеризует его назначение:
Если вы включите эту функцию, то каждый раз перед отправкой письма вечером или ночью, когда люди чаще всего принимают поспешные решения, на экран будет выводиться простая арифметическая задачка. Если вы устали, но еще в состоянии решить её, смело отправляйте письмо. В противном случае лучше хорошо отоспаться, а утром заняться почтой. Зная себя лучше других, вы можете сами задать время, когда будут появляться эти задачки.
На странице экспериментальных функций вы сможете найти другие интересные расширения.

Стоит отметить и чат Google Talk в окне почты, позволяющий в онлайн режиме обмениваться сообщениями с другими пользователями, фактически это IM в окне браузера. Недавно в российский gmail была добавлена поддержка аудио и видео чатов - не ради рекламы, но ради справедливости отмечу, что однажды скайп отказался совершать видео звонок, в то время как GTalk позволил отлично пообщаться с другим городом.

Ээээ...Вы еще здесь?

понедельник, 11 апреля 2011 г.

ExtJS выделить строку в таблице grid

Проблема: выделить одну строку (например, первую) в таблице grid сразу после ее загрузки. Казалось бы, проблемы как таковой здесь нет - получили SelectionModel таблицы и сделали selectRow(rowIndex). По сути - все действительно так, однако необходимо выбрать нужный момент для выполнения этого кода. Попробуйте, например, выделить строку в событии afterrender или render - увы, выделение не сработает. Решение, тем не менее, безусловно есть.

Решение: необходимо воспользоваться событием viewready, возникающим, когда таблица grid полностью отрисована на странице и готова к манипуляциям. Как гласит документация, viewready используется как раз для осуществления выделения строки по умолчанию.

Предположим, что нам нужно выделить первую строку таблицы:

grid = new Ext.grid.EditorGridPanel({
  store: myStore,
  height: 500,
  border: false, frame: false,
  cm: myColumnModel,
  selModel: new Ext.grid.RowSelectionModel({singleSelect:true}),
  listeners: {
    viewready: function(g){
      g.getSelectionModel().selectRow(0);
    }
  }

Обратите внимание, что в случае необходимости использования так называемой модели выделений SelectionModel необходимо явно указывать selModel в конфигурации таблицы, в противном случае вызов метода grid.getSelectionModel() вызовет ошибку.

пятница, 8 апреля 2011 г.

ExtJS убрать обрезку строк в таблице Grid

Таблицы ExtJS (GridPanel и EditorGridPanel) отлично подходят для отображения и удобного редактирования различных типов данных. Однако по умолчанию ячейки таблицы не показывают большие строки полностью, обрезая их до видимой части, как видно на рисунке ниже.

Иногда такое поведение неприемлемо и необходимо предоставить пользователю возможность видеть всю текстовую информацию целиком.
 

Сделать это чрезвычайно просто - необходимо перезаписать CSS параметр .x-grid3-cell-inner для редактирования стиля ячейки и свойства .x-grid3-hd-inner для редактирования стиля заголовка таблицы соответственно. Таким образом, чтобы включить режим просмотра всего текста (wrap) в ячейке, необходимо обозначить следующий стиль:

.x-grid3-cell-inner { white-space: normal; }

Аналогичный эффект заголовкам таблицы придаст следующий CSS код:

.x-grid3-hd-inner { white-space: normal; }

Спасибо.

Вообще говоря, подобным образом можно править все стили, используемые в ExtJS интерфейсе - узнать названия стилей и посмотреть их текущие параметры можно в Firebug'е.

ExtJS сделать ячейку/столбец Editor Grid нередактируемой

Проблема: на входе имеем таблицу Editor Grid (EditorGridPanel), в которой необходимо запретить редактирование некоторых ячеек, строк или столбцов. Известно, что по умолчанию все ячейки таблицы EditorGridPanel являются редактируемыми.

Решение. Как всегда, разработчики ExtJS продумали все за нас - необходимо перезаписать конфигурационную функцию isCellEditable модели колонок ColumnModel, определяющую возможность редактирования конкретной ячейки по индексу ее колонки и строки.

var store = new Ext.data.Store({...});
var colModel = new Ext.grid.ColumnModel({
  columns: [...],
  isCellEditable: function(col, row) {
    if (col == 2) return false; else return true;
  }
});
var grid = new Ext.grid.GridPanel({
  store: store,
  colModel: colModel,
  ...
});

В приведенном примере разрешается редактировать всю таблицу, кроме третьей колонки - строки и колонки начинают нумерацию с нуля, поэтому в теле функции isCellEditable проверяется значение col, равное 2.

Неплохо, не так ли? Усложним задачу. Что, если нам нужно решить, стоит ли позволять редактировать ячейку в зависимости от данных в другой колонке? Нет ничего проще!

var store = new Ext.data.Store({...});
var colModel = new Ext.grid.ColumnModel({
  columns: [...],
  isCellEditable: function(col, row) {
    if (col == 2){
      rec = store.getAt(row);
      if (rec.get('notEdit') === true)
        return false;
      else
        return true;
    } else
      return true;
  }
});
var grid = new Ext.grid.GridPanel({
  store: store,
  colModel: colModel,
  ...
});

В данном случае проверяется значение поля notEdit рассматриваемой записи, и если в нем хранится true - ячейка в третьем столбце становится нередактируемой. Остальные столбцы и ячейки при этом продолжают нормально функционировать.

четверг, 7 апреля 2011 г.

ExtJS получить параметры URL/GET параметры

Проблема: необходимо использовать GET параметры, содержащиеся в URL. Например, по каким-то причинам клиенту на JS необходимо получить доступ к параметру id запроса http://site/page.php?id=132.

Решение. Двухстрочная функция, расширяющая возможности ExtJS.

Ext.getUrlParam = function(param) {
    var params = Ext.urlDecode(location.search.substring(1));
    return param ? params[param] : params;
};

Использование:

id = Ext.getUrlParam('id');

воскресенье, 3 апреля 2011 г.

Анализ дискового пространства

Ситуация заканчивающегося свободного места на жестком диске знакома, думаю, каждому. И всегда хочется знать, куда же это самое место расходуется? Существует немало программ, помогающих решать эту проблему, платных и не очень. Я хочу рассказать о JDiskReport - бесплатная Java программа для анализа дискового пространства.



При запуске программа спрашивает, какую директорию или диск ей необходимо сканировать. Таким образом, можно детально проанализировать как целый диск, так и одну отдельно взятую директорию, занимающую подозрительно много места.

Закончив сканирование указанного пространства, JDiskReport строит отличную круговую диаграмму, показанную выше. На любой сегмент можно нажать и посмотреть статистику выбранной директории. На вкладке Top 50 приводятся 50 самый больших файлов - удобно, согласитесь. Можно посмотреть статистику по типам файлов - для этого предназначена вкладка Types.


Кошмар - у меня 6.5 гигабайт dll файлов!

Интересно, сколько на жестком диске старых документов? Не вопрос - вкладка Modified расскажет все о вашей старой и новой информации.


Оказалось, что преобладают документы, измененные в последние 1-2 года. Хотя и старых файлов хватает - за 10 лет никто не изменил 3.5 гигабайта моего жесткого диска:).

Да, для каждого типа отчета можно выбрать режим вывода - столбчатая диаграмма, круговая диаграмма или просто таблица. На любой вкус, так сказать.

Скачать этот must have можно на официальном сайте JDiskReport, есть версии для Windows и Mac OS.

Конвертирование PDF в Word [doc, rtf]

Лучший онлайн инструмент для конвертирования PDF в Word (на выбор - doc или rtf) - здесь.

Загружаете pdf, вводите свой email и через пару минут получаете письмо с doc файлом. Уличная магия и только.




Да, как снять защиту с PDF документов на копирование, печать и т.п. я писал здесь.

Снятие ограничений с PDF файла


Простите, но меня чрезвычайно раздражает, когда я не могу распечатать скачанный PDF документ или скопировать текст из оного! К чему все эти ограничения?

Онлайн решение hack-pdf.com поможет решить описанную проблему. Никаких регистраций и требований, просто загружаете PDF документ, ожидаете его обработки - мой 120 страничный pdf был разблокирован не более чем за 5 секунд.

Да, как легко и просто сконвертировать PDF в doc я писал здесь.

четверг, 31 марта 2011 г.

Работа с JSON в Java

JSON (JavaScript Object Notation) - текстовый формат обмена данными, легко читаемый людьми и основанный на синтаксисе Javascript. JSON, как правило, используется с Javascript, если быть более точным - при обмене данными между Javascript и сервером. Стоит отметить, что JSON обладает существенным преимуществом перед XML - он менее избыточен.

Пример JSON объекта:

{
  userId: 32,
  firstName: "Алексей",
  lastName: "Голобурдин",
  address: {
    country: "Россия",
    city: "Москва"
  },
  phone: "8 (905) 777 77 77"
}

Как видно, все предельно просто. Фактически, JSON объект является валидным Javascript кодом, то есть может быть исполнен интерпретатором Javascript. Но как сгенерировать JSON на сервере, и как распарсить пришедший от клиента JSON?

PHP программисты сейчас довольно ухмыльнутся, ведь у них есть стандартные функции json_encode и json_decode, позволяющие легко и просто гонять PHP сущности в JSON и обратно.

Java программистам приходится сложнее - надо поддерживать свою крутость (где PHP - Personal HomePage language, и где крутая Enterprise Java), поэтому с JSON приходится работать путем сторонних библиотек, коих на офсайте json.org приводится немало. Программисты Python здесь тоже ухмыльнутся - их дзен "Должен быть только один, и лучше всего очевидный, способ сделать это".

Итак, в данной статье рассматривается использование отличной java библиотеки для генерирования и парсинга JSON - json-simple, официальный сайт на google code. Кстати, там можно найти немало отличных примеров!

Вот таблица сопоставления типов JSON и Java:

JSONJava
stringjava.lang.String
numberjava.lang.Number
true | falsejava.lang.Number
nullnull
arrayjava.util.List
objectjava.util.Map

Тааак, и как нам всем этим добром пользоваться?

Примеры давай, примеры!


Cоздадим JSON строку!

//import org.json.simple.JSONObject;
  
JSONObject resultJson = new JSONObject();

resultJson.put("name","foo");
resultJson.put("num",new Integer(100));
resultJson.put("is_vip",new Boolean(true));
resultJson.put("nickname",null);
System.out.print(obj.toString());
// {"name": "foo", "num": 100, "is_vip": true, "nickname: null}

Ну ладно, круто. А что-то посложнее?

JSONArray ar = new JSONArray();
JSONObject obj = new JSONObject();
JSONObject resultJson = new JSONObject();

ar.add("first");
ar.add(new Integer(100));

obj.put("one", "two");
obj.put("three", "four");

resultJson.put("paramsArray", ar);
resultJson.put("paramsObj", obj);
resultJson.put("paramsStr", "some string");
System.out.print(obj.toString());
// {"paramsArray": ["first", 100],
//  "paramsObj": {"one": "two", "three": "four"},
//  "paramsStr": "some string"}

Думаю, комментарии здесь излишни, все действительно чрезвычайно интуитивно и просто. Другие примеры генерирования JSON можно посмотреть на офсайте.

Парсинг JSON


А вот с парсингом дела обстоят намного хуже. Шутка:)



String json = "{paramsArray: [\"first\", 100],"
            + "paramsObj: {one: \"two\", three: \"four\"},"
            + "paramsStr: \"some string\"}";

JSONParser parser = new JSONParser();

Object obj = parser.parse(json);
JSONObject jsonObj = (JSONObject) obj;
System.out.println(jsonObj.get("paramsStr"));
// some string

JsonObject jo = jsonObj.get("paramsObj");
System.out.println(jo.get("three"));
// four

JsonArray ja = jsonObj.get("paramsArray");
System.out.println(ja.get(1));
// 100

Вот такая отличная библиотека. Приятной работы!

среда, 30 марта 2011 г.

AUTO_INCREMENT поле в Oracle

Знакомые с MySQL программисты при первом знакомстве с СУРБД Oracle, вероятно, удивятся отсутствию в ней такой приятной возможности, как создание auto_increment полей, то есть полей, численное значение которых автоматически увеличивается при создании новых записей. Как правило, auto_increment поля используются как primary key - первичные ключи для таблиц.

В MySQL эти поля создаются буквально на лету:

CREATE TABLE users(
    id MEDIUMINT NOT NULL AUTO_INCREMENT,
    name CHAR(30) NOT NULL, PRIMARY KEY (id)
);

В такую таблицу можно вставлять записи, не задумываясь об автоматической генерации первичного ключа id, который будет увеличиваться на 1 для каждой новой записи. Иными словами, можно выполнить запрос вида

INSERT INTO userns(name) VALUES ("sterx"),("alex"),("kime");

и для каждой из созданных записей в случае MySQL будет автоматически вычислен id.

В Oracle auto_increment поля создаются несколько иначе, используются так называемые последовательности (sequence), или, другими словами, счетчики. Создадим последовательность:

CREATE SEQUENCE users_seq
START WITH 1 
INCREMENT BY 1 
NOMAXVALUE;

Как видно из объявления, последовательность начинается с 1 (конечно, при необходимости, можно задать другое значение), шаг последовательности также равен 1, последовательность не имеет максимального значения.

Теперь при создании записей в таблице нам необходимо присваивать полю id текущее значение счетчика-последовательности (или следующее значение счетчика в зависимости от реализации). Это можно сделать двумя способами - создать триггер, отслеживающий создание записей в таблице и присваивающий полю id значение счетчика, либо в самой команде INSERT использовать значение счетчика для задания значения id. Использование триггера с одной стороны усложняет реализацию, с другой стороны упрощает команды INSERT, поэтому выбор способа установки id оставлю на усмотрение читателя.

Рассмотрим оба варианта.

Использование триггера


create or replace trigger users_id_trg
before insert on users
for each row
begin
  if :new.id is null then
    select users_seq.nextval into :new.id from dual;
  end if;
end;

В теле триггера мы проверяем, что id новой записи не установлен пользователем, так как по своим причинам пользователь может захотеть установить значение id самостоятельно, не полагаясь на триггер. Если это не так - вычисляется новое значение счетчика и id устанавливается автоматически.

Использование хитрого INSERT


INSERT INTO users (id, name) VALUES (users_seq.nextval, 'sterx');

В данном случае новое значение счетчика вычисляется при каждом вызове INSERT, значение поля id вычисляется соответственно в самом теле команды без триггера.

PS. Созданные последовательности и триггеры легко удалить, поэтому не бойтесь с ними работать.

DROP SEQUENCE users_seq;
DROP TRIGGER users_id_trg;

вторник, 29 марта 2011 г.

ExtJS: изменились ли записи в хранилище?

Проблема: необходимо узнать, изменились ли записи в хранилище Store . Пример практической задачи, сводящейся к описанной - например, проверка состояния таблицы EditorGridPanel.

Решение:

if (Store.getModifiedRecords().length == 0)
    alert('Записи изменены');
else
    alert('Записи не изменены');

Аналогично можно проверить, например, изменились ли записи EditorGridPanel:

if (EditorGridPanelElement.store.getModifiedRecords().length == 0)
    alert('Записи изменены');
else
    alert('Записи не изменены');

Однако, этот подход не обнаруживает события удаления записей из хранилища! Таким образом, удалив одну или несколько записей, мы изменили хранилище, однако getModifiedRecords() не вернет нам ничего - событие удаления необходимо отслеживать самостоятельно. Предлагаемый автором вариант предельно прост: при загрузке хранилища мы устанавливаем ему метку dirtyMark, равную false - операции удаления не производились; при удалении обновляем метку в true:

  var myStore = new Ext.data.JsonStore({
    url: 'serviceUrl/',
    root: 'result',
    fields: myFields,
    listeners: {
      remove: function(){
        myStore.dirtyMark = true;
      },
      load: function() {
        myStore.dirtyMark = false;
      },
      save: {
        myStore.dirtyMark = false;
      }
    }
  });

Условие проверки изменения состояния хранилища в таком случае будет выглядеть следующим образом:

if (Store.getModifiedRecords().length == 0
       && myStore.dirtyMark === true)
    alert('Записи изменены');
else
    alert('Записи не изменены');

понедельник, 28 марта 2011 г.

ExtJS FormPanel, распределенный по табам TabPanel


Суть: имеем форму FormPanel, распределенную по нескольким табам (вкладкам) TabPanel. В качестве примера можно привести форму 4 на официальной странице примеров ExtJS.

Проблема: при отправке формы отправляются поля не все табов, если быть точным - отправляются только просмотренные табы.

Метод решения: очевидно, необходимо автоматически "просмотреть" или отрендерить (отрисовать) все табы TabPanel.

Решение: добавить в конфигурацию TabPanel параметр deferredRender: false, отвечающий за отложенный рендеринг табов. При установке этого параметра в false все табы рендерятся при первой возможности, не дожидаясь действий со стороны пользователя.

ExtJS ComboBox не отправляет valueField?

При использовании в формах ExtJS выпадающего списка ComboBox вы, должно быть, заметили проблему: форма отправляет отображаемое значение выпадающего списка (displayField), а не его фактическое значение (valueField). Если провести аналогию с обычным <select> HTML - отправляется Вася, а не 3.

<select name="user">
    <option value="1">Саша</option>
    <option value="2">Иннокентий</option>
    <option value="3">Вася</option>
</select>

Чтобы решить данную проблему и заставить ExtJS отправлять 3 вместо Вася, необходимо добавить конфигурационный параметр hiddenName:


var userName = new Ext.form.ComboBox({
    id:'userName',
    name: 'userName',
    fieldLabel: 'Имя',
    store: usersStore,
    displayField: 'name',
    valueField: 'id',
    hiddenName : 'razdel'
});

Будьте внимательны, параметр hiddenName должен быть уникальным для каждого поля формы - он используется при ее отправке вместо параметра name.

Протестировать параметры при отправке формы удобно с помощью Firebug.

воскресенье, 27 марта 2011 г.

RESTful приложение на ExtJS 3



О том, что такое RESTful приложение и почему это круто можно почитать на Wiki, хабре или даже в статье ibm. Фактически, RESTful приложение - это приложение, построенное на вызове удаленных REST веб сервисов, выполняющих CRUD функции (Create, Read, Update, Delete - создать, прочитать, обновить и удалить соответственно). Каждый REST сервис характеризуется URI, по которому он вызывается, и некоторым набором принимаемых и возвращаемых параметров.

Особенность REST подхода состоит в активном использовании HTTP методов для выполнения CRUD функций: вместо использования URI адресов типа

/user/23/
/user/23/?action=delete
/user/23/?action=update


предлагается использовать стандартный адрес /user/23/, обращение к которому ведется с использованием HTTP команд GET, DELETE, PUT. Для создания нового элемента используется HTTP команда POST (в нашем примере к адресу /user/ - ID создаваемого элемента до момента создания мы, как правило, не знаем). Для приложений, функциональность которых не выходит за рамки CRUD, а можно уверенно сказать, что это большинство современных веб приложений, RESTful подход является лучшим выбором для создания веб сервисов сервисов и API.



В данной статье я предлагаю рассмотреть создание простейшего RESTful приложения с использованием ExtJS 3 на клиентской стороне. Серверную часть можно написать на любой любимой технологии - будь то Java, PHP, Python или что-то еще.

В этой статье подразумевается, что Ext уже подключен, также рекомендуется использование связки Firefox + Firebug для отладки Javascript и просмотра http заголовков запросов и ответов. Итак, поехали!

Будем использовать хранилище (Store) с настроенным HttpProxy. Дело в том, что HttpProxy поддерживает api для установки методов загрузки, обновления и удаления данных в хранилищах Store:

var myProxy = new Ext.data.HttpProxy({
    api: {
        read:    {url: 'app/users/', method: 'GET'},
        create:  {url: 'app/users/', read: 'POST'},
        update:  {url: 'app/users/', method: 'PUT'},
        destroy: {url: 'app/users/', method: 'DELETE'}
    }
});

Как видим, можно настроить разные URI адреса для действий чтения, обновления и т.д., однако это плохо согласуется с идеологией REST, поэтому мы будем использовать один адрес с разными передаваемыми параметрами и разными HTTP методами. Стоит отметить, что по умолчанию используется метод POST, поэтому в каждом случае следует вручную указывать параметр method.

Сконфигурируем JsonReader и JsonWriter:

var myReader = new Ext.data.JsonReader({
    successProperty: 'success', // json элемент со статусом
                                // результата (false or true)
    idProperty: 'id',
    root: 'data', // корневой элемент с данными
}, [
    {name: 'id'},
    {name: 'email', allowBlank: false},
    {name: 'first', allowBlank: false},
    {name: 'last', allowBlank: false}
]);

var myWriter = new Ext.data.JsonWriter({
    encode: true,         // важно! кодировать Store
    writeAllFields: false // сохранять только измененные поля
});

Теперь можно сконфигурировать само хранилище. Фактически, мы указываем здесь reader, writer и proxy:

var myStore = new Ext.data.Store({
    id: 'user',
    proxy: myProxy,
    reader: myReader,
    writer: myWriter,
    idProperty: 'id', // важно! Id элементов хранилища
    autoSave: true // при изменении хранилища будет автоматически
                   // сформирован запрос на его сохранение
});

Теперь при изменении хранилища - то есть изменении записей в хранилище - будет автоматически посылаться запрос к серверу для сохранения всех модификаций. Если по каким-то причинам мы хотим отправлять запрос на сохранение текущего состояния хранилища сами, необходимо установить конфигурационный параметр autoSave хранилища в false; в таком случае запрос на сохранение будет сформирован при вызове метода save() хранилища:

myStore.save();

Обратите внимание на параметр idProperty хранилища; в случае некорректной установки этого параметра при сохранении измененного элемента все равно будет создан запрос на создание нового элемента вместо сохранения существующего (POST вместо PUT). Это связано с тем, что каждая запись в хранилище однозначно идентифицируется параметром id, который должен быть связан с реальным идентификатором нашей записи (id в JsonReader - как правило, идентификатор записи в базе данных).

Также обратите внимание, что если используется mapping в параметрах fields для JsonReader - в idProperty нужно указывать реальное название параметра в Json данных, а не название колонки данных в Ext'е. Для тех, кто не в курсе - маппинг это сопоставление имен параметров в Json данных и имен этих же параметров в Ext для манипуляции с ними, при желании можно задавать разные имена. Иными словами, мы могли бы так настроить JsonReader:

var myReader = new Ext.data.JsonReader({
    successProperty: 'success', // json элемент со статусом
                                // результата (false or true)
    idProperty: 'id',
    root: 'data', // корневой элемент с данными
}, [
    {name: 'ExtIdentificator', mapping: 'realJsonId'},
    {name: 'email', allowBlank: false},
    {name: 'first', allowBlank: false},
    {name: 'last', allowBlank: false}
]);

Для idParamater в таком случае надо указывать realJsonId.

Итак, теперь мы умеем сохранять данные хранилища. Но как модифицировать эти данные? Как правило, хранилище используется либо для загрузки данных с сервера с последующим использованием в формах или других элементах страницы, либо для загрузки данных в таблицу Grid. В случае использования EditorGridPanel данные таблицы и данные хранилища полностью синхронизированы, то есть при изменении данных таблицы автоматически меняется и хранилище, с ним связанное. Это первый способ изменения данных хранилища.

Другой способ - ручной. Например, мы хотим обновить данные первого пользователя в хранилище myStore:

myStore.getAt(0).data.items[0].name = 'Alexey';

myStore.getAt(0) возвращает первую запись (Record) хранилища; свойство data записи хранит в себе все данные записи.

Таким образом, изменив одним из описанных способов хранилище, мы имеем возможность сохранить новое состояние хранилища либо автоматически (путем настройки хранилища autoSave: true), либо вручную путем вызова метода хранилища save().

Пример использования JsonWriter можно посмотреть здесь.