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

Drupal. Отправка уведомлений о днях рождения друзей

Данный код использует модули ur_relationships и privatemsg. Сам код вставляется в действе rule, которое реагирует на событие залогинивания пользователя.

$account->uid, 'rtid' => $active_relationships, 'approved' => TRUE));

// Создаем массив с ID друзей
// We have to do this if() twice because we could
// be a requester or a requestee, and we never know which.
$friends = array();
$users_birthday = array();
foreach ($relationships as $relationship) {
if ($account->uid == $relationship->requester_id) {
$friends[$relationship->requestee_id] = array();
}
if ($account->uid == $relationship->requestee_id) {
$friends[$relationship->requester_id] = array();
}
}
// Finally, we load content profiles for our selected type
// and for our friends from the array above.
// Загружаем Материалы с профилями пользователей
foreach (array_keys($friends) as $friend_uid) {
$friend_profile = content_profile_load($profile_type, $friend_uid);
$friend_birthdate = $friend_profile->$birthday_field;
$friend_birthday = date('m-d', strtotime($friend_birthdate[0]['value']));

// Если дата рождения пользователя находится между датой последнего логина и $birthday(сегодня +1 день),
// то добавляем данные пользователя в массив $users_birthday
if($users_last_login_date_str<=$friend_birthday && $friend_birthday<=$birthday){ $user_birthday = user_load($friend_uid); $users_birthday[$friend_uid]['uid']=$friend_uid; //$users_birthday[$friend_uid]['birthdate']=$friend_birthdate[0]['value']; $users_birthday[$friend_uid]['birthdate'] = date('d-m-y', strtotime($friend_birthdate[0]['value'])); $users_birthday[$friend_uid]['fio']=$friend_profile->field_first_name_profile[0]['value'] . ' ' . $friend_profile->field_last_name_profile[0]['value'] . ' ' . $friend_profile->field_middle_name_profile[0]['value'] ;
}
}

// Если есть пользователи у которых было или будет Д.Р. в указанном промежутке дат, то шлем текущему пользователю сообщение об этом
if (!empty($users_birthday)) {
//----------------- send message --------------------------------
$index_sql = "INSERT INTO {pm_index} (mid, thread_id, uid, is_new, deleted) VALUES (%d, %d, %d, %d, 0)";
// 1) Save the message body first.
$args = array();
//$args[] = $message['subject']; // Тема сообщения
$args[] = "Дни Рождения.";
//$args[] = $message['author']->uid; // ID Отправляющего
$args[] = 1;
//$args[] = $message['body']; // Тело сообщения
$temp_body="

Здравствуйте!

Уведомляем Вас о облизжайших Днях Рождения ваших контактов:

";
foreach($users_birthday as $tub)
{
$tdob = $tub['birthdate'];// format_date($tub['birthdate'], $type = 'custom', $format = 'd-m-y')
$temp_body.= '

Дата: '. $tdob . ' ' . ''. $tub['fio'].''.'

';
///view_another_user_profile/61
//...
}
$args[] = $temp_body;
//$args[] = $message['format'];// Формат сообщения
$args[] = 2;
//$args[] = $message['timestamp']; // Время отправки
$args[] = time();
$message_sql = "INSERT INTO {pm_message} (subject, author, body, format, timestamp) VALUES ('%s', %d, '%s', %d, %d)";
db_query($message_sql, $args);
$mid = db_last_insert_id('pm_message', 'mid');
$message['mid'] = $mid;

// Thread ID is the same as the mid if it's the first message in the thread.
if (!isset($message['thread_id'])) {
$message['thread_id'] = $mid;
}

// 2) Save message to recipients.

db_query($index_sql, $mid, $message['thread_id'], $user->uid, 1);

// When author is also the recipient, we want to set message to UNREAD.
// All other times the message is set to READ.
$is_new = isset($message['recipients'][$message['author']->uid]) ? 1 : 0;

// Also add a record for the author to the pm_index table.
if (!db_query($index_sql, $mid, $message['thread_id'], $message['author']->uid, $is_new)) {
return FALSE;
}
}

?>

Drupal Печать формы из Views Php CustomField

Для того, чтобы напечатать форму из нужного нам views, нужно создать поле Php типа CustomField и в нем написать следующий php код.

$user->uid,
'name' => (isset($user->name) ? $user->name : ''),
'type' => $node_type,
);
// Invoke hook_nodapi and hook_node
node_object_prepare($node);

// Можно использовать уже существующую ноду, например
// $node = node_load(123);
// отображаем форму:
$output = drupal_get_form($form_id, $node);

print $output;

error_reporting(1);
?>



// $node_type -Тип ноды

C# MemoryOutExeption при использовании Image.FromStream и при закрытии MemoryStream

Конструкция

public static Image GetImage(byte[] data) {

if (data == null) return null;

MemoryStream ms = new MemoryStream(data);

Image img = Image.FromStream(ms);

// closing the stream later crashes the application

// ms.Close(); // => memory leak

return img;

}

при перерисовке изображения создаст OUT OF MEMORY exception.

Если закрывать MemoryStream, то будет memory leak



Для решения проблемы нужно использовать следующий фрагмент

Image img = Image.FromStream(ms);

Image ReturnMe = (Image)img.Clone();

img = null;

ms.close();



Рабочий вариант:

public static Image GetImage(byte[] data) {

if (data == null) return null;

MemoryStream ms = new MemoryStream(data);

Image img = Image.FromStream(ms);

Image ReturnMe = (Image)img.Clone();

img = null;

ms.close();

return ReturnMe;

}

вторник, 16 августа 2011 г.

Drupal - Передача аргументов во view типа блок

Views не предусматривает передачу аргументов в блок. Однако, это можно обойти довольно простым способом. Для этого в настройках аргумента выбираем для Action to take if argument is not present значение Provide default argument. И в качестве Default argument type используем например такой PHP Code:

$path = $_GET['q']; // используем это если нам нужен оригинальный путь
//$path = drupal_get_path_alias($_GET['q']); // или это, если нам нужен алиас
$path = explode('/', $path); // распарсим путь
return $path[1];

Сперто отсюда

вторник, 9 августа 2011 г.

Мультисайтинг (многосайтовость) - это просто

Мультисайтинг (многосайтовость) - это просто
Изображение пользователя Макс Кириленко.


Автор: Макс Кириленко, 30.11.2006
Рубрика: Сайтостроение (Drupal, CMS, дизайн,...)


Введение

Студия Razgonka.ru специализируется на мультисайтинге.

Мультисайтинг (многосайтовость) имеет два разных значения. В этой статье я буду говорить в первую очередь об объединении посетительской базы между разными сайтами. Именно оно наиболее интересно с точки зрения наших клиентов.

Необходимость такого объединения очевидна. Любая бабушка знает, что семечками нужно торговать не на углу своего дома, а хотя бы на остановке метро. Еще лучше торговля идет на колхозном рынке. Но там бабушек не видно, другие доходы - другие продавцы.

Мультисайтинг с объединенными посетителями (они же покупатели) это аналог колхозного рынка. Если посетитель не купит у одного торговца, то неизбежно купит у другого. Так или иначе, всякий пришедший на рынок оставляет там больше денег, чем проходя мимо одиноко стоящей бабушки.

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

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

Теперь о технической стороне мультисайтинга. Студия Razgonka.ru специализируется на установке сайтов на CMS Друпал, поэтому рассказывать о мультисайтинге буду на его примере.



Теория, виды мультисайтинга

Различают 2 вида мультисайтинга.

1-ый. Мультисайтинг с общим движком. Много независимых сайтов используют один движок. Друпал работать в режиме "один движок, много сайтов".

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

Общий движок, варианты

Если сайтов немного (2-5), то можно завести отдельную папочку, куда кидать эталонную версию версию Друпала и всех модулей.

Мультисайтинг с общим движком на бэкапелке (программа, делающая backup, резервную копию). При выходе очередной версии Друпала или модулей, переносить вручную или бэкапелкой эталонную версию по локальным папкам. Откуда они будут заливаться на сайт при очередном обновлении сайта.

Мультисайтинг с общим движком на FTP-синхронизаторе. Если мы не собираемся отлаживать локально сайты с использованием последней версии Друпала и модулей, то можно мультисайтинг с общим движком еще проще. Берется FTP-синхронизатор, позволяющий сделать профили. С его помощью заливается содержимое эталонной папки напрямую на сайты.

Мультисайтинг с общим движком средствами сервера. Если часть сайтов расположена на одном хостинге (сервере), то появляется возможность перенести папку с эталонным движком на сервер. Физически папка с Друпалом будет одна для всех сайтов. Сервер сам будет "размножать" эту папку для всех сайтов. Помимо правки настроек сервера потребуется некоторое изменение установок Друпала. Об этой технике уже много писалось на Drupal.ru. Воспользуйтесь поиском или советами Акселя на http://drupal.ru/node/763 , http://drupal.ru/node/1453

Общие таблицы, варианты

Вариант 1. Если каждый сайт хранит таблицы в своей независимой базе, то сайты полностью независимы (с точки зрения посетителей).

Комментарий. И что толку? таких независимых сайтов миллионы. Цивилизация рождалась в городах, а не в отдельных хуторах.

Вариант 2. Сайты объединяют свою базу пользователей, но сохраняют независимость в остальном. Здесь таблицы, отвечающие за пользователей, у сайтов общие, а все остальное разное.

Это самый рабочий вариант. О нем ниже будет подробнее.

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

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

Посетителям тоже неудобно иметь дело с частичным зеркалированием сайта. Ссылки на зеркальное содержимое показываются в его браузере еще не просмотренными. Посетитель идет по ним и у него начинается дежа вю. Когда он наконец понимает, что он это уже точно читал, у посетителя сразу падает доверие к цвету посещенных/непосещенных ссылок. И он обзывает любителя зеркалирования разными словами за свое потерянное время. Весь дальнейший просмотр сайтов из мультисайтовой связки проходит под давлением - "Читал это уже или еще не читал?".

Вариант 4. Сайты объединяются полностью. Получаются "зеркала".

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

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


Идеальный вариант разделения таблиц

Итак, если проводить границу объединения таблиц, то самое оптимальное объединить зарегистрированных пользователей, но не замахиваться на объединение содержимого,т.е. вариант 2. Сайты сохраняют независимое содержимое с точки зрения поисковиков и посетителей. Но при этом посетители свободно переходят с одного сайта на другой с сохранением своего "лица" и покупательской "истории".



Техника подачи мультисайтинга на сайтах

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

Если сайты в мультисайтовой связке работают независимо, то в таком объединении нет смысла. Пользователи и не знают, что где-то в мире есть другие сайты, на которых можно воспользоваться тем же логином/паролем. Надо в блоке логина на каждом сайте из мультисайтовой связки повесить объявление: "Подходит логин/пароль с любого из следующий сайтов". И дать список сайтов из связки. При наведении мышки на ссылку всплывает надпись, чем занимается соответствующий сайт. Если посетитель уже зарегистрирован на каком-то из сайтов связки, то это побуждает его использовать имеющийся логин/пароль, чтобы обрести лицо при использовании нового сайта. Все остальные с интересом могут пройти по ссылкам, чтобы посмотреть, что там интересного есть.

Кроме объединения пользовательских логинов/паролей можно объединить между сайтами и базу покупателей. Если пользователь сделал покупку на одном из сайтов, то на другом сайте из связки он может сделать покупку в упрощенном варианте. Все его адреса и координаты подставятся из анкеты, которую он заполнял при своей первой покупке на другом сайте.

Объединение другой информации

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


Техника мультисайтинга с общими таблицами

Есть два варианта.

Вариант 1. Объединение на разных префиксах. Здесь держат в одной базе несколько разных версий друпаловских таблиц для разных сайтов. Уникальные таблицы идут с префиксом сайта, общие таблицы идут вообще без префикса.

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

Вариант 2. Выделение общих таблиц в отдельную базу. Уникальные таблицы каждый сайт держит в своей личной базе (без префиксов). Общие таблицы выделены в отдельную базу.

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

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


Объединение новых и старых сайтов

Самое простое объединить несколько новых сайтов. Чуть сложнее присоединить в мультисайтинге новый сайт к старому. Присоединять старый сайт к старому можно, но сложно. Потребуется перенести в присоединяемом сайте информацию о пользователях на область свободных UserID. И корректно переименовать многие таблиц, завязанных на UserID.

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


Пусть у нас есть старый сайт site1.ru и новый сайт site2.ru. И есть 3 базы:

base: site1 здесь пока хранятся все таблицы для старого сайта
user: firstuser
password: pasfirstuser

base: site2 здесь будут хранится уникальные таблицы для нового сайта
user: firstuser
password: pasfirstuser

base: common здесь будут хранится общие таблицы для всех сайтов
user: firstuser
password: pasfirstuser


Обратите внимание, что все три базы работают с общим юзером firstuser. Вы не сможете задать для каждой базы отдельного юзера.


Разделение таблиц

Начинаем с того, что разделяем базу site1 на две части. Общие таблицы в мультисайтовой связке перенесем в базу common. А уникальные таблицы для Site1.ru оставим в родной базе site1. После чего старый сайт будет работать на двух базах.

По шагам

Шаг 1. Заходим на Site1.ru как суперюзер.

http://www.site1.ru/admin/settings

Переводим сайт в режим офф-лайн. В качестве объяснения пишем:

"Сайт Site1.ru закрыт на профилактическое обслуживание сегодня, такого-то числа, в субботу в 1:00 по Московскому времени. Через два часа, в 3:00, он откроется снова. Пожалуйста, зайдите позже. Благодарим за терпение."

Шаг 2. Делаем бэкапы базы site1.

Для бэкапа используйте Sypex Dumper Lite http://sypex.net/ . Укажите в скрипте в 38-ой строчке кодировку utf-8 :
define('CHARSET', 'utf-8'); // define('CHARSET', 'cp1251');

При бэкапе можно указать фильтр, отбрасывающий нужные и отбрасывающий ненужные таблицы


Вся база site1 разбивается на 3 части:

а) общие файлы (authmap, profile_fields, profile_values, sequences, sessions, users)

б) ненужные таблицы ^locales*, ^cache, ^search*, которые можно восстановить и без бэкапа

в) остальные таблицы, они уникальны для site1 и не используются в site2

Кладем скрипт dumper.php в директорию tmp, назначаем на нее права 777. Запускаем в браузере

http://www.site1.ru/tmp/dumper.php

Вводим логин/пароль:

user: firstuser
password: pasfirstuser

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

Делаем общий бэкап, в фильтре указываем

^locales*, ^cache, ^search*

чтобы избавится от таблиц, которые можно восстановить без бэкапа.

Скачиваем файл с бэкапом. Смотрим, можем ли мы распаковать его и все ли нормально с кодировкой.

Если все нормально, то делаем бэкап общих таблиц, указав фильтр:

authmap, profile_fields, profile_values, sequences, sessions, users

Скачиваем файл и опять проверяем его.

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

Возвращаемся на страницу бэкапа и переключаем скрипт снова в режим бекапа. Бэкапим базу common, скачиваем ее и смотрим, все ли нормально с кодировкой.

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

Шаг 3. Меняем в локальной версии Site1.ru файл /sites/default/settings.php :

//$db_url = 'mysql://username:password@localhost/databasename';

$db_url = 'mysql://firstuser:pasfirstuser@localhost/site1';

// $db_prefix = '';

$db_prefix = array(
"default" => "site1.", // эта строчка разная у каждого сайта в мультисайтовой связке
"users" => "common.",
"sessions" => "common.",
"authmap" => "common.",
"sequences" => "common.",
"profile_fields" => "common.",
"profile_values" => "common.",
);

Такая конструкция означает, что Друпал на Site1.ru будет брать по умолчанию таблицы из базы site1, а 6 общих таблиц - из базы common.

Точка на конце будет отделять имя базы от имени таблицы. Повторюсь, для базы site1 и common юзер и его пароль должны быть одинаковыми, иначе не будут видны те таблицы, у которых база не допускает до себя юзера firstuser с паролем pasfirstuser .



Теперь у нас все готово для того, чтобы Site1.ru заработал на двух базах.

Шаг 4. Заливаем файл /sites/default/settings.php на Site1.ru

Шаг 5. Удаляем в базе уникальных таблиц site1 ставшие ненужными общие таблицы
authmap, profile_fields, profile_values, sequences, sessions, users

Шаг 6. Ходим по Site1 как суперюзер и проверяем, все ли в порядке.

Шаг 7. Идем в таблицу sequences (счетчики)

В ней 10 записей:

files_fid
view_view_vid
access_aid
comments_cid
menu_mid
node_nid
node_revisions_vid
term_data_tid
vocabulary_vid

и

users_uid

При добавлении новых нод, меню, комментариев, файлов,... Друпал, бегающий на Site1.ru будет создавать в таблице sequences новые записи, добавляя в качестве префикса дефолтную для него базу site1:

site1.access_aid
site1.comments_cid
site1.files_fid
site1.menu_mid
site1.node_nid
site1.node_revisions_vid
site1.term_data_tid
site1.view_view_vid
site1.vocabulary_vid

И только при создании нового юзера он добавит префикс common к users_uid:

common.users_uid

Если бы Site1.ru поднимался с нуля, то в этом не было бы ничего страшного, каждый бы новый счетчик начал бы работать с 1. Но у нас Site1.ru уже имеет много мастерила и юзеров. Поэтому при попытке создания нового материала, комментария, добавления файла,... и добавления нового юзера Друпал проверит, есть ли в наличии запись с соответствующим счетчиком (site1.node_nid, site1.comments_cid,... и common.users_uid). Если такой счетчик Drupal не обнаружит, то он создаст соответствующую запись, присвоит ей значение 1 и начнет создавать материал (ноду, комментарий,... или юзера) с номером 1. Но при попытке сохранить материал или юзера под номером 1 Друпал обнаружит, что в базе уже есть материал или юзер под таким номером. И тогда он пожалуется на этот факт и откажется добавлять материал или юзера.

Нужно вручную создать в базе common записи:

site1.access_aid
site1.comments_cid
site1.files_fid
site1.menu_mid
site1.node_nid
site1.node_revisions_vid
site1.term_data_tid
site1.view_view_vid
site1.vocabulary_vid
и
common.users_uid

И перенести из них значения старых счетчиков

access_aid
comments_cid
files_fid
menu_mid
node_nid
node_revisions_vid
term_data_tid
view_view_vid
vocabulary_vid
и
users_uid

После чего записи счетчиков со старыми именами (без префиксов) можно удалить.

При определенной сноровке можно эту операцию сделать путем правки файла с бэкапом общих таблиц. Нужно в таблице sequences добавить к записи users_uid префикс к 9-ти записям префикс "common.", а к остальным 9-ти записям префикс site1. Снова сжать файл и восстановить его пустую базу common.

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

Шаг 8. Заводим нового пользователя. Проверяем:

а) получилось ли завести нового пользователя
б) в базе common в таблице users должна появится новая запись
в) в таблице sequences запись common.users_uid должна увеличится на 1.

Если все в порядке, то заходите на http://www.site1.ru/admin/settings и переводите сайт в режим он-лайн.

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



Присоединение нового сайта к мультисайтовой связке

Шаг 1. Заводим базу для уникальных таблиц второго сайта

base: site2
user: firstuser
password: pasfirstuser

Шаг 2. Бэкапим базу common и site1

Шаг 3. Меняем в локальной версии Site2.ru файл /sites/default/settings.php следующим образом:

//$db_url = 'mysql://username:password@localhost/databasename';

$db_url = 'mysql://firstuser:pasfirstuser@localhost/site2';

// $db_prefix = '';

$db_prefix = array(
"default" => "site2.", // эта строчка разная у каждого сайта в мультисайтовой связке
"users" => "common.",
"sessions" => "common.",
"authmap" => "common.",
"sequences" => "common.",
"profile_fields" => "common.",
"profile_values" => "common.",
);


Шаг 3. Устанавливаем Drupal на Site2.ru как обычно. При запуске скрипта создания таблиц уникальные таблицы будут без префиксов заведены в базе site1, а общие 6 таблиц заводится в базе common не будут, потому что они уже существуют.

Править таблицу sequences нет нужды. По мере работы Site2.ru сайт будет добавлять свои собственные записи:

site2.access_aid
site2.comments_cid
site2.files_fid
site2.menu_mid
site2.node_nid
site2.node_revisions_vid
site2.term_data_tid
site2.view_view_vid
site2.vocabulary_vid

А если придется создавать на Site2.ru нового юзера, то Друпал увеличит на 1 общий счетчик юзеров common.users_uid в котором хранится общее число пользователей в Site1.ru.



Вот, собственно, и все добавление нового сайта к готовой связке.

Следующие новые сайты добавляются по такой же схеме.


Замечания

1. Раз база пользователей общая, то попытка во время установки Site2.ru добавить суперюзера окончится ничем. Если на сайте Site1.ru уже было 2000 юзеров, то будет добавлен всего-навсего юзер 2001. Когда после установки Site2.ru видите страницу с приглашением добавить юзера, не поддавайтесь на провокацию. Лучше идите в блок Логин/Пароль и вводите туда данные суперюзера №1 с Site1.ru. Если Site2.ru признал такой логин-пароль как суперюзерский, то значит мультисайтинг начал работать.

2. Если ставите на сайт Друпал, то не назначайте себя суперюзером. Рано или поздно вы уйдете с сайта, придет другой человек, который будет админить и он поменяет ваше имя как суперюзера. Следом поменяются имена у всех написанных вами статей.

Сегодня суперюзер это вы. Завтра это другой человек. Послезавтра 10 сайтов объединяются в один и суперюзер становится один на все 10 сайтов. После-после завтра какой-то сайт откалывается от мультисайтовой связки и тут же поменяет название суперюзера, а вместе с ним и авторство статей, написанным суперюзером мультисайтовой связки. Суперюзер лицо техническое, назовите его 999 и никогда ничего не пишите на сайте от его имени. Себе же заведите логин-пароль как юзер-2, дайте ему всевозможные права в роли администратора и пишите в свое удовольствие.

3. Как видим, добавление нового сайта к готовой мультисайтовой связке очень простое. Нужно только поменять несколько строчек в /sites/default/settings.php. Если ставите сайт и есть планы цеплять к нему другие на мультисайтинг, то имеет смысл даже первый сайт в связке с самого начала готовить к мультисайтовой работе. Это потребует создания общей базы common и добавления нескольких строчек в /sites/default/settings.php.

4. Добавление старого сайта к старому требует много дополнительной работы. Поэтому с самого начала стройте все свои сайты как единую мультисайтовую связку. Это намного проще, чем развивать сайты порознь, а потом объединять их.



Мое мнение

Мультисайтинг на общих таблицах в отдельной базе делается на Друпале очень просто.

Первый сайт на мультисайтинг я перевел за 1,5 часа неспешного разбирания и протоколирования своих действий для статьи. Не успел даже выпить все приготовленное пиво. Улыбка У вас же это наверняка получится быстрее.

Новый сайт присоединил к связке за 10 минут.

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

Не бойтесь мультисайтинга. Друпал прекрасно с ним работает. Чем раньше вы получите опыт мультисайтинга на Друпале, тем быстрее вырастет ваша первая мультисайтовая связка.



Модули, полезные при мультисайтинге

Shared Sign-Ons. Позволяет пользователям ограничиться залогиниванием на одном лишь сайте сайте. При переходе внутри мультисайтовой связки другие сайты будут узнавать такого пользователя без дополнительной аутентификации. В марте 2007 г. у этого модуля были проблемы из-за которых он работал устойчиво только при отключенном кэширования сайта.
Fierce SSO. Альтернатива Single Sign-On. Позволяет узнавать пользователей, залогинившихся даже с сайтов не на Друпале.
Registration role. Этот модуль позволяет назначать пользователям при регистрации заранее заданные роли. Можно на каждом сайте из связки завести отдельную роль, названную по имени сайта и присваивать зарегистрировавшимся пользователям роль, названную именем сайта. Такая информация служит для разделения пользователей по месту их исходной регистрации. Эта информация может быть полезна при выплате коммиссионных за приведенных покупателей. Или когда настанет время "выделить" сайт из связки вместе с пользователями, которые зарегистрировались на этом сайте
Multisite Manager. Модуль позволяет плодить сайты на основе центрального сайта
Multiple Domains. Позовляет создавать частичные клоны основного сайта. Например, можно вынести работу E-commerce или работу с пользовательскими аккаунтами на отдельный протокол https. Требует модуль single sign-on.
Robotstxt позволяет создать отдельный robots.txt для каждого сайта в связке.

Оригинал статьи: http://www.razgonka.ru/multisiting

понедельник, 4 июля 2011 г.

Отладка друпала

В главной папке сайта создаем файлик debug.php следующего содержания : 

<?php
/**
 * Save any output to file
 *
 * @param any_type $variable
 * @param string $comment
 */
function ___save_debug($variable, $comment = NULL) {
  $fp = @fopen('__dbg.txt', 'a');
  if( is_array($variable) || is_object($variable) ) {
    $variable = print_r($variable, true);
  }
  $tmp = $comment." : ".$variable."\n\n===========================\n";
  fwrite($fp, $tmp);
  fclose($fp);
}

/**
 * Returns print version of variable.
 * Put result on the top of the page.
 *
 * @param any_type $variable
 * @param string $comment
 */
function ___print_debug($variable, $comment = NULL) {
  $print_var = print_r($variable, true);
  drupal_set_message('<p><strong>'.$comment.'</strong></p><pre>'.$print_var.'</pre>');
}
?>

в index.php добавляем

require_once './debug.php';

То есть, он начнет выглядеть следующим образом:

require_once './includes/bootstrap.inc';
require_once './debug.php';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

$return = menu_execute_active_handler();
........

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

Рецепт. Если не работает сериализация в C#

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

Пример:

[Serializable]
    public class SpamedUsers
    {
        #region Parametres
        public List<User> Users; // = new List<User>();
        #endregion
. . .


[Serializable]
    public class User
    {
        public string Name;
        public DateTime LastSpamedTime;

        public User (string name, DateTime lastspamedtime)
        {
            Name = name;
            LastSpamedTime = lastspamedtime;
        }

        public User() // без этого конструктора сериализовать не получится объекты данного класса
        {
        }

суббота, 14 мая 2011 г.

Drupal XmlRpc Date Api Services. Решение проблемы с глюками.

Немного помучившись с  отправкой поля типа дата через xml-rpc на друпаловский сайт из программы на C# было найдено такое решение:

XmlRpcStruct node = new XmlRpcStruct();

string field_exclusive_date_to = "10.05.2024 0:00:00";
DateTime dtfield_exclusive_date_to = DateTime.Parse(field_exclusive_date_to);
AddArrayDateToNode("field_exclusive_date_to", dtfield_exclusive_date_to, node);

/// <summary>
        /// Добавляет в ноду поле с датой (Drupal Date)
        /// </summary>
        /// <param name="fieldname"></param>
        /// <param name="dt"></param>
        /// <param name="node"></param>
        /// <remarks>Нужно написать в блоге каком то как это делать, ато все ебуться и никто еще не нашел.</remarks>
        private static void AddArrayDateToNode(string fieldname, DateTime dt, XmlRpcStruct node)
        {
            XmlRpcStruct sMainDate = new XmlRpcStruct();
            XmlRpcStruct sDate = new XmlRpcStruct();
            sDate["year"] = dt.Year.ToString();
            sDate["month"] = dt.Month.ToString();
            sDate["day"] = dt.Day.ToString();
            sMainDate["value"] = sDate;
            object[] oDate = new object[] { sMainDate };
            node[fieldname] = oDate;
        }

Все дело в том, что на  Drupal сайте Date Api хранит дату немного не в том формате в котором отображает. Следовательно получение и отправка поля CCK типа Date будет сильно различаться.



То есть слать нужно структуру такого формата для поля с датой:
[field_next_call_date] => Array
(
[0] => Array
(
[value] => Array
(
[year] => 2014
[month] => 7
[day] => 7
)
)
)
И в таком формате для checkbox cck field.
[field_checkboxes] => Array
(
[value] => Array
(
[1991] => 1991
[1985] => 1985
)
1991 - это id термина таксономии связанного с чекбоксом
)

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

Как сделать автоувеличение картинок в друпал.

Вот возникла у меня такая задача: Имеем друпал сайт, CKEDITOR и нужно сделать так, чтобы картинки которые добавляются в контент страниц, по клику открывались через Thickbox. Трахался я минут 30 с гуглем и прочими секретными инструментами. Уже собирался модули патчить, хотя это как бы и не лучший выход по многим причинам. И вот!!! Наконец-то я нашел. Нужно просто взять image_resize_filter , установить его, посмотреть на странице описания модуля как его настраивать, сделать все по инструкции, которая там есть и все замечательно получится без какой-либо правки модулей и т.д. Очень удобно, замечательно и красиво.

Еще одно примечание, если вы хотите, чтобы при клике на увеличенную картинку автоматом производился переход к следующей, то нужно указать атрибут [roadtrip]. 

Будет время напишу подробней. Кому нужно велком в каменты. 

суббота, 11 декабря 2010 г.

MemoryTools - Утилита для работы с памятью других приложений.

Здравствуйте!

Представляю Вашему вниманию свою новую утилиту для работы с памятью. Название ее MemoryTools. Скачать полный исходный код и бинарники вы можете тут.
Итак, теперь немного о самой утилите и ее функциональных возможностях. Возьмем для примера достаточно известную игрушку под названием World Of Warcraft, и немного помучаем ее.

Как только вы запустите утилиту, вы увидите следующее:

Теперь вам нужно выбрать из выпадающего списка процесс к которому мы собираемся подключаться и нажмем кнопку Attach.(в данном случае это будет wow).
Теперь выберем оффсет и вставим туда такое значение 0x008b3e36(значение CurrentRealm, которое может менятся при выходе новых патчей игры),  нажмем кнопку show  и мы увидим название текущего реалма.
Снизу этого окна мы видим базовый адрес приложения. Если поставим галочку AutoRefresh,  то нужное нам значение будет обновляться с заданным в миллисекундах интервалом.  Значение в поле оффсет получается вычитанием адреса в приложении от его базового адреса.
Теперь перейдем на вкладку OffsetFinder.

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

LuaDoString: 0x0039d8c0
LastGlueState: 0x0097bd4c
IsLoadingOrConnecting: 0x0097bd6c
LuaGetTop: 0x00035300
ClickToMove: 0x008cbb70

Это как раз и есть те оффсеты, которые мы вставляем в первую вкладку.
Так-же, на главной вкладке мы можем нажать на кнопочку DisAsm и получить кусок дизасемблированного листинга, начиная с указанного адреса или оффсета.
Еще одно примечание, программу нужно запускать с правами администратора.
Удачи.

понедельник, 22 ноября 2010 г.

Программист - прагматик

Вот недавно попалась в руки офигенная книга. Называется она "Программист-прагматик". Рассказывается о том, как быть настоящим программером. О методологии, подходах, проектированнию, хитростях. В общем о многих вещах, о которых нигде в других местах сборно не рассказывается. В общем must be readed.

понедельник, 8 ноября 2010 г.

C# Работа с прокси.

IEProxy.ProxyEnabled = cbUseProxy.Checked;
IEProxy.ProxyServer = tbProxyName.Text + ":" + tbPort.Text;

using System;
using Microsoft.Win32;

namespace ProxyII
{
    /// <summary>
    /// Summary description for IEProxy.
    /// </summary>
    public class IEProxy
    {
        #region Variables

        private static readonly RegistryKey currentUser = Registry.CurrentUser;       
        private static RegistryKey _InternetSettings;

        #endregion

        #region Public static methods
        /// <summary>
        /// Open the key where Internet Explorer store's its proxy setting
        /// </summary>
        private static void OpenInternetSettings()
        {
            _InternetSettings = currentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Internet Settings", true);
        }
       
        /// <summary>
        /// Proxy Server name. It can be IP, IP:PORT or a HTTP URL with PORT
        /// </summary>
        public static string ProxyServer
        {
            get
            {
                OpenInternetSettings();
               
                string value = (string)_InternetSettings.GetValue( "ProxyServer", string.Empty );
                _InternetSettings.Close();

                return value;
            }
            set
            {
                OpenInternetSettings();
               
                _InternetSettings.SetValue( "ProxyServer", value );   
                _InternetSettings.Close();
            }
        }

        /// <summary>
        /// True means proxy setting is applied, otherwise proxy is ignored
        /// </summary>
        public static bool ProxyEnabled
        {
            get
            {
                OpenInternetSettings();
               
                int value = (int)_InternetSettings.GetValue( "ProxyEnable", 0 );
                _InternetSettings.Close();

                return (value > 0);
            }
            set
            {
                OpenInternetSettings();
       
                int setValue = ( value ? 1 : 0 );
                _InternetSettings.SetValue( "ProxyEnable", setValue );
                _InternetSettings.Close();
            }
        }

        public static bool BypassProxyForLocal
        {
            get
            {
                OpenInternetSettings();

                string value = (string) _InternetSettings.GetValue( "ProxyOverride", string.Empty );
                _InternetSettings.Close();

                // If bypass proxy set, then it should contain <local>
                if( value.IndexOf("<local>") >= 0 )
                    return true;
                else
                    return false;

               
            }
            set
            {
                OpenInternetSettings();

                string existingValue = (string) _InternetSettings.GetValue( "ProxyOverride", string.Empty );
                if( existingValue.IndexOf("<local>") >= 0 )
                {
                    if( !value )
                        existingValue = existingValue.Replace( ";" + Environment.NewLine + "<local>", "" );

                }
                else
                {
                    // does not contain the local keyword. Add it.
                    if( value )
                        existingValue += ";" + Environment.NewLine + "<local>";
                }

                _InternetSettings.SetValue( "ProxyOverride", existingValue );
                _InternetSettings.Close();
            }
        }

        #endregion
    }
}

четверг, 28 октября 2010 г.

Как искать паттерны в IdaPython

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

print '0x%.08X' % FindBinary(INF_BASEADDR, SEARCH_DOWN, '83 3D ? ? ? 00 00 75')

вторник, 26 октября 2010 г.

C# Работа со временем.

Вот есть в шарпе класс DateTime, который позволяет работать с датой временем и т.д. А вот если нам нужно только время, без даты? Чтобы дата не учитывалась. Например, мы пишем программу, которая будет выполнять определенные задачи по расписанию. Допустим в определенное время запускать и в определенное время останавливать какие-то процессы. Для этого нам потребуется класс, который может достаточно просто представлять время, но без даты. Вот этот класс(или вернее структура):

namespace Helpers
{
    public struct Time //: IComparable
    {
        private int minuteOfDay;
        public static Time Midnight = "0:00";
        private static int MIN_OF_DAY = 60 * 24;
        public Time(int minuteOfDay)
        {
            if (minuteOfDay >= (60 * 24) || minuteOfDay < 0)
                throw new ArgumentException("Skal ligge i intervallet 0 - 1439", "minuteOfDay");
            this.minuteOfDay = minuteOfDay;
        }
        public Time(int hour, int minutes)
        {
            if (hour < 0 || hour > 23)
                throw new ArgumentException("Must be in the range 0-23", "hour");
            if (minutes < 0 || minutes > 59)
                throw new ArgumentException("Must be in the range 0-59", "minutes");
            minuteOfDay = (hour * 60) + minutes;
        }
        #region Operators
        public static implicit operator Time(string s)
        {
            var parts = s.Split(':');
            if (parts.Length != 2)
                throw new ArgumentException("Time must be specified on the form tt:mm");
            return new Time(int.Parse(parts[0]), int.Parse(parts[1]));
        }

        public static bool operator >(Time t1, Time t2)
        {
            return t1.MinuteOfDay > t2.MinuteOfDay;
        }
        public static bool operator <(Time t1, Time t2)
        {
            return t1.MinuteOfDay < t2.MinuteOfDay;
        }
        public static bool operator >=(Time t1, Time t2)
        {
            return t1.MinuteOfDay >= t2.MinuteOfDay;
        }
        public static bool operator <=(Time t1, Time t2)
        {
            return t1.MinuteOfDay <= t2.MinuteOfDay;
        }
        public static bool operator ==(Time t1, Time t2)
        {
            return t1.GetHashCode() == t2.GetHashCode();
        }
        public static bool operator !=(Time t1, Time t2)
        {
            return t1.GetHashCode() != t2.GetHashCode();
        }
        /// Time
        /// Minutes that remain to
        /// Time conferred minutes
        public static Time operator +(Time t, int min)
        {
            if (t.minuteOfDay + min < (24 * 60))
            {
                t.minuteOfDay += min;
                return t;
            }
            else
            {
                t.minuteOfDay = (t.minuteOfDay + min) % MIN_OF_DAY;
                return t;
            }
        }
        public static Time operator -(Time t, int min)
        {
            if (t.minuteOfDay - min > -1)
            {
                t.minuteOfDay -= min;
                return t;
            }
            else
            {
                t.minuteOfDay = MIN_OF_DAY + (t.minuteOfDay - min);
                return t;
            }
        }
        public static TimeSpan operator -(Time t1, Time t2)
        {
            return TimeSpan.FromMinutes(Time.Span(t2, t1));
        }
        #endregion

        public int Hour
        {
            get
            {
                return (int)(minuteOfDay / 60);
            }
        }
        public int Minutes
        {
            get
            {
                return minuteOfDay % 60;
            }
        }

        public int MinuteOfDay
        {
            get { return minuteOfDay; }
        }
        public Time AddHours(int hours)
        {
            return this + (hours * 60);
        }
        public int CompareTo(Time other)
        {
            return this.minuteOfDay.CompareTo(other.minuteOfDay);
        }
        #region Overrides
        public override int GetHashCode()
        {
            return minuteOfDay.GetHashCode();
        }
        public override string ToString()
        {
            return string.Format("{0}:{1:00}", Hour, Minutes);
        }
        #endregion
        /// 
        /// Safe enumerering - whatever interval applied max days 
        /// 
        /// Start time
        /// Spring in minutes
        /// 
        public static IEnumerable<Time> Range(Time start, int step)
        {
            return Range(start, start, step);
        }
        /// 
        /// Safe enumerering - whatever interval applied max days
        /// 
        public static IEnumerable<Time> Range(Time start, Time stop, int step)
        {
            int offset = start.MinuteOfDay;
            for (var i = 0; i < Time.Span(start, stop); i += step)
            {
                yield return Time.Midnight + (i + offset);
            }
        }
        /// 
        /// Calculates the number of minutes between t1 and t2
        /// 
        public static int Span(Time t1, Time t2)
        {
            if (t1 < t2) // same day
                return t2.MinuteOfDay - t1.MinuteOfDay;
            else // over midnight
                return MIN_OF_DAY - t1.MinuteOfDay + t2.MinuteOfDay;
        }
    }
}


Применять его можно примерно так:

Time tStop = "12:03";
Time tStart = "13:20";

DateTime dtNow = DateTime.Now;
Time tNow = new Time(dtNow.Hour, dtNow.Minute);



int t = Time.Span(tStart, tStop);
int t1 = Time.Span(tStart, tNow);
if (t1>1440) t1 = t1-1440;
int t2 = Time.Span(tNow, tStop);
int tsum = t1 + t2;


if (tsum == t) // Если выполнилось это условие, то запускаем нужное задание (то есть время сейчас между
12:03 и 13:20)

if (tsum != t) // Если выполнилось это условие, то останавливаем нужное задание (то есть время сейчас не между 12:03 и 13:20 )
















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

четверг, 21 октября 2010 г.

Закрытие всех работающих экземпляров процесса Notepad.exe

// Просто завершение процесса 
 
private void ButtonStop_Click(object sender, System.EventArgs e)
{
   System.Diagnostics.Process[] myProcesses;
   myProcesses = 
      System.Diagnostics.Process.GetProcessesByName("Notepad");
   foreach (System.Diagnostics.Process instance in myProcesses)
   {
      instance.CloseMainWindow();
      instance.Close();
   }
} 
 
//  Настройка ожидания завершения выполнения процесса Блокнота
private void ButtonStop_Click(object sender, System.EventArgs e)
{
   System.Diagnostics.Process[] myProcesses;
   myProcesses = 
      System.Diagnostics.Process.GetProcessesByName("Notepad");
   foreach (System.Diagnostics.Process instance in myProcesses)
   {
      instance.CloseMainWindow();
      instance.WaitForExit(3000);
      instance.Close();
   }
}

 

среда, 22 сентября 2010 г.

Установка Apache, php и mysql в Ubuntu

Итак, приступим:
Способ первый:
запускаем терминал и выполняем такие команды:
sudo apt-get install apache2
sudo apt-get install php5 libapache2-mod-php5
sudo invoke-rc.d apache2 restart
sudo apt-get install mysql-server
sudo invoke-rc.d apache2 restart

Все готово для работы.

Способ второй:
В терминале запускаем всего одну команду
sudo tasksel install lamp-server

Далее для обоих способов:
в терминале вводим:
sudo gedit /var/www/test.php

Пишем и сохраняем:

В браузере переходим по ссылке http://localhost/test.php, где вы увидите всю информацию о PHP.

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

Запуск Apache2:
sudo invoke-rc.d apache2 start

Остановка Apache2:
sudo invoke-rc.d apache2 stop

Перезапуск Apache2:
sudo invoke-rc.d apache2 restart

Изменение пароля mysql
sudo mysqladmin -u root password новый_пароль

Подключение к консоли mysql
mysql -u root -p

Дополнительное удобство
Еще одна фишка, которая мне показалась безусловно удобной,описана далее. Ее суть состоит в том, что можно в браузере набрать ссылку вроде test.com.dev и без проблем откроется наш сайт, который сохранен на локальном харде в домашней папке в sites/test.com.dev/public .
Допустим сайт наш будет называться test.com, и храниться в папке sites, как описано выше. Создаем папку для нашего сайта:
mkdir -p ~/sites/test.com.dev/public
Далее создаём файлик для этого сайта:
sudo gedit /etc/apache2/sites-available/test.com.dev
и вписываем в него следующие строки:
ServerName test.com.dev
ServerAlias www.test.com.dev
DocumentRoot /home/Имя_Пользователя/sites/test.com.dev/public

где Имя_Пользователя нужно заменить на ваше имя пользователя.
Сохраняем файл, закрываем gedit и говорим апачу, что этот сайт теперь нужно обслуживать:
sudo a2ensite test.com.dev
При выполнении, эта команда скажет вам, что апачу необходимо перезагрузить данные конфигурации, что мы и сделаем:
sudo /etc/init.d/apache2 reload
Теперь, настроим удобное обращение к сайту. Для этого пропишем, что хост test.com.dev обслуживается на нашем компьютере, локально. Открываем файл /etc/hosts
sudo gedit /etc/hosts
находим там строку, начинающуюся на 127.0.0.1 (обычно, это первая строка) и вписываем в её конец, через пробел от предыдущих данных, test.com.dev. Сохраняем файл, закрываем редактор.
Теперь кидаем какой-нибудь файлик index.html в папочку нашего сайта (/home/$USER/Sites/www.test.com.dev/public)
Всё, теперь уже можно вписать в адресную строку браузера http://test.com.dev/ и увидеть, что сайт вас приветствует.

Еще редактор текстов здесь немного дебильный (да простят меня его разработчики, но это так) - так что в текст вкралось немного ошибок... :-(  Некоторые тэги отсеиваются, и всплывающее окно бесит.

Еще  нужно написать скриптик, который все вышеописанное делает автоматом...


П.С.: при подготовке данного документа использовались материалы с таких источников:
http://rotuka.com/2008/10/lamp-ubuntu-apache-mysql-php/

установка и настройка ssh в Ubuntu

Итак, сегодня мы научимся устанавливать, пользоваться  очень популярной, или скорее очень полезной штукой под названием ssh.
Как говорит Википедия:
SSH (англ. Secure Shell — «безопасная оболочка»[1]) — сетевой протокол прикладного уровня, позволяющий производить удалённое управление операционной системой и туннелирование TCP-соединений (например, для передачи файлов). Сходен по функциональности с протоколами Telnet и rlogin, но, в отличие от них, шифрует весь трафик, включая и передаваемые пароли. SSH допускает выбор различных алгоритмов шифрования. SSH-клиенты и SSH-серверы имеются для большинства сетевых операционных систем.
Еще раз напоминаю что я пользуюсь Ubuntu (если у Вас другая ось, то напишите и я добавлю материала про нее), чтобы в винде не пытались вводить что-то типа sudo apt-get ... и т.д.

Установка

Сначала установим ssh сервер на машине к которой мы будем подключаться. Для этого в консоли мы вводим:
sudo apt-get install openssh-client
Теперь, следующей командой установим ssh клиент, на машине с которой мы будем подключаться к той, на которой установлен ssh сервер
sudo apt-get install openssh-server
Вся конфигурация нашего сервера производиться изменением файла /etc/ssh/sshd_config. Давайте посмотрим что же там в этом файле за опции такие. Сначала сделаем резервную копию файла конфигурации:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.original

теперь откроем его в терминале:
sudo nano /etc/ssh/sshd_config
Вы увидите следующие директивы:
  • Port - указывает порт по которому сервер будет ожидать входящего соединения. Например, Port 1234 означает что к серверу можно будет подключиться по порту 1234.
  • ListenAddress - указывает какой протокол/интерфейс будет прослушиваться ssh.
  • Protocol - позволяет выбрать версию протокола 1 или 2. Рекомендуется протокол 2.
  • HostKey - Ключевые файлы для второй версии протокола SSH
  • UsePrivilegeSeparation - хз. Оставляем включенным для безопасности.
и еще куча полезных опций о которых я напишу немного позже.
Теперь перезагружаем ssh демон
sudo /etc/init.d/ssh restart

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

И теперь мы можем смело к нему конектиться. Для этого в терминале клиента воодим следующую команду
ssh -p номер_порта host.name
Где номер_порта на котором висит ssh сервер, а host.name - это имя хоста сервера или его IP адрес
Монтирование файловой системы сервера производиться с помощью команды SSHFS
Для установки sshfs введем команду:
sudo apt-get install sshfs
Монтирование производиться следующей командой:
sshfs remoteuser@remotehost:/путь/к/удаленной_директории локальная_директория

Где remoteuser - имя удаленного пользователя, remotehost - имя удаленного хоста или его ip адрес, /путь/к/удаленной_директории - удаленая директория которую вы хотите примонтировать, локальная_директория - это место куда вы хотите примонтировать удаленную папку
Для отмонтирования удаленной директории используем команду:
fusermount -u локальная_директория
Ну вот, для начала вроде должно хватить, потом напишу больше