Створення стислих резервних копій бази даних Access, MS Office, Програмні керівництва, статті

 

Абсолютно необхідний алгоритм для виживання в наш неспокійний час засилля китайських компонентів 😉 Думаю, нікому не треба пояснювати, наскільки велика цінність резервної копії в умовах краху бази (хоча, за роки роботи з многомегабайтние базами Access 2000 були глюки, звичайно, але щоб база даних зруйнувалася без можливості самовідновлення, такого випадку у мене ще не було). Але не будемо забувати про апаратних проблемах, “кривих руках” і помилках користувачів ..


Поділюся своїм способом архівації зі стисненням в формат. RAR, який дозволяє архівувати із стисненням, зменшуючи розмір стиснутої архівної копії в 9 і більше разів. У мене одна 248 Мб база Access 2000 скорочується до 15,6 Мб – я очам в перший раз не повірив ;), правда, там зберігалися тільки дані в таблицях, досить одноманітні, ось WinRar і виявляє повторювані ланцюжки …


У WinRar взагалі дуже багато налаштувань, за ними відсилаю Вас до його великої довідці і на сайт rarlab.com/, А опишу тільки ті, що застосовую. З іншого боку, якщо когось цікавить ліцензійна чистота (все таки Rar не безкоштовний), можна використовувати будь-який архіватор, аби він підтримував управління з командного рядка (7-Zip (приклад використання нижче), InfoZip і т.п.). Якщо Ви хочете офіційно продавати своє творіння, уважно ознайомтеся з умовами використання – буває, що архіватор безкоштовний тільки для некомерційного використання.


На цьому закінчимо правові питання 😉 і приступимо до вивчення коду ….


Почну трохи здалеку …


Справа в тому, що досвідчені розробники завжди тримають дані в одному файлі (MDB), а додаток в іншому (MDE). Не будемо згадувати ADP і SQL (це я особливо розумним ;). Так легко організувати роботу в мережі – достатньо розкидати файл-додаток на кожен комп’ютер в мережі і, якщо необхідно, змінити посилання на файл з таблицями, що лежить десь на сервері. Зменшується навантаження на мережу, так як всі форми, звіти вже знаходяться на комп’ютері клієнта, по мережі передаються тільки дані, легко оновлювати додаток – досить замінювати тільки програми на нові версії. Не є винятком і я 😉 – всі проекти підтримую в такому вигляді.


У моїх програмах є головна (початкова) форма frmMainMenu, яка відкриваючись на весь екран, емулює красиве користувача меню для вибору подальших дій. Ця форма ніяк не пов’язана з таблицями (Немає джерела записів, не плутайте зі стандартною кнопкової формою Access!) І, отже, не пов’язана з файлом бази даних (з таблицями), тобто при відкритті даної форми файл бази даних ще не зайнятий і доступний для обробки архіватором. З цього випливає висновок, що архівувати файл з даними можна тільки, якщо до нього немає доступу ні по мережі, ні локально – ніхто більше не відкрив файл. Звичайно, це накладає обмеження на архівацію, але це і єдино правильний шлях – абсолютно неприпустимо архівувати дані під час роботи, може, йде індексація, оновлення, видалення, та хіба мало що в базі може відбуватися …. Тому ми повинні вивісити грізне попередження перед початком архівації і, можливо, перевірити доступність файлу. Друга умова – архівування відбувається на комп’ютері, на якому самі дані і знаходяться, хоча це й не критично (можна обчислити розташування файлу з даними з будь-якого віддаленого програми по шляхах до таблиць, але я цього не роблю). Щоб запобігти втраті даних через збій жорсткого диска, необхідно періодично скидати архівні копії на CD / DVD-RW або на інші комп’ютери (питання спірне). Але це вже інші, адміністративні проблеми …


Ось таке довге, але, вважаю, необхідне вступ …


Стартова форма frmMainMenu відкривається на комп’ютерах користувачів миттєво (ні ж ще звернень до даних, які лежать на іншому комп’ютері), закриває весь екран монітора (незалежно від дозволу монітора) і створює робочу обстановку за рахунок підібраною експериментальним шляхом тональної гами ;), деякою інформацією про програму, логотипу розробника і інтерактивного меню. Приступимо до розгляду програмного коду:


Option Compare Database
Option Explicit


“API для визначення – чи є файл
Private Declare Function PathFileExists Lib “shlwapi.dll” _
Alias “PathFileExistsA” (ByVal pszPath As String) As Long
“Якщо хочете уникнути використання API, простіше скористатися


“Оператором Dir () – нижче в статті наведені приклади


Private Sub LaunchArhiving


“Збережемо шлях бази
strAppPath = Application.CurrentProject.Path & “”
“Спочатку перевіримо – чи є файл MyDataBase_Data.mdb він у цьому ж каталозі лежати повинен
“Якщо він є, тоді це сервер і можна архівувати сам файл бази …
If PathFileExists(strAppPath & “MyDataBase_Data.mdb”) = 1 Then
“Значить ми сидимо на сервері, можна архівувати дані за допомогою WinRar
“Запуск архівації
If MsgBox (“Перед початком архівації перевірте,” & _
“Щоб база була закрита на всіх інших комп’ютерах в мережі.” & VbCrLf & _
“Інакше при спробі архівації виникне помилка.” & VbCrLf & vbCrLf & _
“Починаємо архівацію?”, VbExclamation + vbYesNo + vbDefaultButton2, _
“Архівація даних”) = vbYes Then DoCmd.OpenForm (“frmArchiving”)
Else
MsgBox “Архівація даних необхідно запускати тільки на комп’ютері,” & _
“На якому сама база і знаходиться.”, VbInformation, _
“Архівація даних”
End If
End Sub


Зверніть увагу: після перевірки не запускається архівація, а відкривається форма frmArchiving. Ця форма зроблена спливаючій і модальної (особисто в мене ще й без заголовка ;), спливаючій в центрі екрану. Її мета – інформувати користувача про те, що йде архівація (будь ласка, почекайте, йде архівація даних, бла, бла, бла …., красиву анімацію на Flash або відео ;). Ну і звичайно, справжнє її призначення – Запустити і відстежити закінчення процесу архівації. Ось цей цікавий код ми і розглянемо. Привожу весь код модуля форми, благо, він невеликий, немає сенсу його ділити на порції:


Option Compare Database
Option Explicit

“Для визначення завершення програми
Private Declare Function OpenProcess Lib “kernel32” (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function GetExitCodeProcess Lib “kernel32” (ByVal hProcess As Long, lpExitCode As Long) As Long
Private Declare Function CloseHandle Lib “kernel32” (ByVal hObject As Long) As Long

Const STILL_ACTIVE = 259
Const PROCESS_QUERY_INFORMATION = 1024

Private idProg As Long “утримуємо Handle запущеної нами проги
Private bIsStarted As Boolean

Private Sub Form_Open(Cancel As Integer)
Dim strAppPath As String
“Збережемо шлях бази
strAppPath = Application.CurrentProject.Path & “”


“Ось тут треба перевірити наявність файлу WinRar.exe в папці … AddOns


“Повідомлення що не знайдений архіватор WinRar.exe в папці … AddOns і вихід


“А також наявність папки … Arhives


“Повідомлення, що не знайдена папка Arhives для зберігання архівів і вихід
idProg = Shell(strAppPath & “AddOnsWinRAR a -r -m5 -ag_DD-MM-YYYY_HH-MM ” & _


         strAppPath & “ArhivesMyDataBase_Data ” & strAppPath & _


         “MyDataBase_Data.mdb”,vbHide)
bIsStarted = True
End Sub

Private Sub Form_Timer()
Dim IsRunning As Boolean
Dim hProc As Long, iRet As Long

If bIsStarted Then
“Стартували і чекаємо-с, коли закінчиться зовнішня програма
    hProc = OpenProcess(PROCESS_QUERY_INFORMATION, False, idProg)
    If hProc <> vbNull Then GetExitCodeProcess hProc, iRet
    IsRunning = (iRet = STILL_ACTIVE)
    CloseHandle hProc

If Not IsRunning Then “процесу більше немає – все закінчилося



        DoCmd.Close
    End If
End If
End Sub


Відстежуємо дві події форми: Відкриття та Таймер (необхідно встановити в 1000). Виконуємо запуск з підпапки AddOns виконуваного файлу WinRar з ключами командного рядка. Зверніть увагу на ключ-ag_DD-MM-YYYY_HH-MM, тобто, кожен раз коли ми запускаємо архіватор, буде сформовано архів з новою датою, доданої до імені архіву. Так архіви і будуть збиратися в підпапці Arhives. Дуже зручно по добавочному суффиксу визначати дату і час створення архіву, також легко сортувати.


У події форми Таймер, виникає кожну секунду (1000), відстежуємо хендл запущеного нами у події Form_Open процесу. Після перевірки процесу (все ще активний?), Якщо процес уже не активний, то, значить, запущений нами процес WinRar.exe закінчив роботу і ми закриваємо форму-попередження, якщо ще активний, то будемо чекати наступної події таймера.


Ну і красиве же API :). На жаль, код цього API не мій, зізнаюся, я тільки вніс невеликі поправки, не знаю, хто автор. Працює чудово під Win2000 і WinXP (під Win95, 98, МЕ не тестував, зважаючи повного неприйняття цих нестабільних систем).


Як я і говорив вище, можна з легкістю використовувати будь архіватор, що підтримує командний рядок, бажано, щоб він додавав до імені архіву ще дату і час, як Rar. Хоча, без проблем можна створювати ім’я архіву, використовуючи програмний код VBA і “підсовувати” складене ІмяАрхіва & Format (date, “_dd-mm-YYYY_”) & Format (time, “_HH-MM”) будь-якому архіватор (на прикладі з 7-Zip нижче). Якщо Ви ризикнете 😉 використовувати WinRar, то просто покладіть в папку AddOns файл WinRar.exe. Більше нічого для роботи йому не потрібно.


Раз ми тут говоримо про архівації, поділюся ще одним маленьким программерскім порадою: “накопичувальна” архівація програми під час розробки за допомогою спеціальної настройки WinRar. Є у мене хороша звичка 😉 після кількох змін у програмі клацати правою кнопкою мишки на файлі програми в Експлоурері (я віддаю перевагу ТС) і вибирати з контекстного меню команду WinRar -> Додати до архіву “ІмяФайла.rar”. Вся хитрість в тому, що в параметрах самого архіватора Rar: “Параметри” -> “Профіль за замовчуванням” -> “Резервні копії”, я налаштував “Додавати до імені архіву закінчення по масці _dd-mm-yyyy_hh-mm” і тепер, вибираючи дану команду, завжди отримую новий файл архіву з додаванням поточної дати і часу створення. Не раз мені доводилося повертатися назад, і завдяки цьому способу я завжди безпомилково вибирав правильну “точку відкату”. Роблю резервні копії я під час роботи дуже часто, навіть якщо вношу незначні зміни в програму, це стало зовсім необтяжливим 😉


Продовження теми:


Кнопка для виконання архівації, звичайно, хороша, але … справа в тому, що користувачі постійно забувають її натискати. Як показала практика, просто забувають, не спеціально ;). Тому я доповнив процедуру виходу з бази нагадуванням про архівації. Дуже зручно, коли користувач закриває програму – йому пропонують зробити архівацію. Тут вже ніяк не забудеш 😉


Архівація проходить по алгоритму, описаного вище з невеликим доповненням. Проблема в тому, що після виклику форми (і запуску архівації) Access відразу виконує команду Quit. Програма закривається, а архівація продовжує допрацьовувати у фоновому режимі. Начебто б нічого страшного на перший погляд …. архів бази нормально створиться (ну хіба що буде трохи гальмувати комп’ютер якийсь час – WinRar забирає всі ресурси процесора). Але, подумайте, коли закривають програму – значить зазвичай вимикають комп’ютер! Користувач не чекає закінчення (і не знає), що там йде фоновий процес – він просто вимикає комп’ютер і …. створення архіву теж обривається (зрозуміло, яка цінність такого архіву).


Спочатку я спробував видавати попередження – почекайте хвилину …. бла, бла, бла. Яку хвилину, коли можна – все це було дуже неочевидно – дивитися на індикатор дискової активності …. да … Довелося замислитися над більш правильною реалізацією і, як завжди, було знайдено просте рішення 😉


Ось так я спочатку написав … І які проблеми виникли, ви вже знаєте.


DoCmd.OpenForm (“frmArchiving”)


DoCmd.Quit


а “правильна” реалізація буде ось така:


DoCmd.OpenForm (“frmArchiving”), acNormal, , , , acDialog


DoCmd.Quit


хитрість в тому, що форма frmArchiving відкривається як діалогове вікно і Access не може виконати наступний рядок коду, поки діалогове вікно не буде закрито. Це-то нам і необхідно! Чекаємо закінчення архівного процесу та закриття форми frmArchiving. Ось тільки після цього Access і приступає до виконання рядки DoCmd.Quit.


Останній штрих: в режимі розробки у властивості форми приберіть кнопку віконного меню і кнопку закриття, задайте зрозумілу підпис форми і у Вас буде відкриватися форма frmArchiving в діалоговому режимі без можливість закриття нетерплячим користувачем 😉


Йде далі. Як я вже писав вище програми та дані у мене зберігаються окремо, база лежить на сервері, там теж йде робота, а клієнтські програми розкидані на машинах користувачів. Клієнтський додаток один і немає різниці, чи працює воно на сервері або на клієнтській машині. Я веду до того, що необхідна перевірка – чи лежить файл MyDataBase_Data.mdb в тій же папці, що і додаток. Якщо лежить, то це сервер і можна робити архівацію. Все це вже було описано вище, я просто освіжають пам’ять ;). Також необхідно перевіряти наявність файла-блокування MyDataBase_Data.ldb. Якщо цей файл існує – значить база відкрита з якоїсь машини, неважливо з якою, все одно архівацію виробляти не можна (та й не вийде). Тому, навіть і не заїкався про архівації і виходимо мовчки ;). Якщо ж умови для створення архівної копії сприятливі, тоді ставимо запитання-нагадування. Я вважаю, таке питання необхідний, а раптом пожежа і необхідно терміново вимкнути комп’ютер ;). Жарт, звичайно.


Ось і весь код кнопки виходу:


“На виході (натискання кнопки Вихід)
“Перевіряємо наявність файлу MyDataBase_Data.mdb якщо є то
“Перевіряємо наявність файлу MyDataBase_Data.ldb якщо його немає то
“Пропонуємо перед вихід зробити архівацію, якщо згоден те, архівація і вихід …….
If PathFileExists (strAppPath & “MyDataBase_Data.mdb”) = 1 Then “означає, файл існує
    If PathFileExists(strAppPath & “MyDataBase_Data.ldb”) <> 1 Then


“Значить, файл блокування не існує – можна архівувати базу
If MsgBox (“Не забувайте створювати архівну копію бази даних при завершенні роботи.” & VbCrLf & vbCrLf & _
“Створити резервну копію бази зараз?”, VbInformation + vbYesNo + vbDefaultButton2, _
“Архівація даних”) = vbYes Then
            DoCmd.OpenForm (“frmArchiving”), acNormal, , , , acDialog
        End If
    End If
End If
DoCmd.Quit


Думки на додаток : Оскільки архіви створюються на одній машині (зазвичай вона має і один диск), передбачте можливість резервного копіювання-синхронізації створених архівів на інші машини в мережі або на інші диски, якщо у Вас не RAID-масив. Це вже питання адміністрування, можливо, питання безпеки. Синхронізацію легко і швидко можна організувати, наприклад, за допомогою безкоштовної (для жителів USSR) програми nnBackUp www.nncron.ru/, Вставивши виклик програми під обліковим записом адміністратора в реєстр (HKLM. .. Run) інших комп’ютерів мережі (тоді користувачі з правами Користувач не будуть бачити консольного вікна виконання програми і не зможуть вплинути на сам процес синхронізації і отримувати доступ до папок архівів, до яких можливий доступ тільки з правами адміністратора). Цей спосіб реалізований особисто і можу стверджувати, що синхронізатор nnBackUp працює швидко і стабільно.


Практичний досвід застосування: в общем-то описана вище ідея хороша своєї академичностью ;). Але випробувавши на практиці вищевикладений код, я прийшов до висновку, що код необхідно спрощувати, і сильно! 😉


Розмірковуючи над спрощенням коду, я прийшов до такого результату.


В подія стартовою форми (тієї, що в мене завжди відкрита на екрані, як бекграунд) Form_Unload додаємо наступний код:


Private Sub Form_Unload(Cancel As Integer)
Dim strAppPath As String
Dim idProg As Long
Dim DirectoryFound As String “для перевірки існування папки

“Збережемо шлях бази
strAppPath = Application.CurrentProject.Path & “”
“На виході з програми –
“Перевіряємо наявність файлу Порушення ПДД_data.mdb, якщо є, то
“Перевіряємо наявність файлу Порушення ПДД_data.ldb, якщо його немає, то
“Пропонуємо перед вихід зробити архівацію, якщо згоден те, архівація і вихід …….
If Dir (strAppPath & “Нарушенія_ПДД_data.mdb”) <> “” Then


“Значить, файл Нарушенія_ПДД_data.mdb існує – це сервер!
If Dir (strAppPath & “Нарушенія_ПДД_data.ldb”) = “” Then


“Значить, файл блокування не існує – ніхто не займає файл
If MsgBox (“Не забувайте створювати архівну копію бази даних при завершенні роботи.” & VbCrLf & vbCrLf & _
“Створити резервну копію бази зараз?”, VbInformation + vbYesNo, _
“Архівація даних після завершення роботи”) = vbYes Then
“Перевірити, чи є файл WinRar.exe в папці AddOns
            If Dir(strAppPath & “AddOnsWinRAR.exe”) = “” Then
MsgBox “Не знайдено архіватор WinRar.exe в підпапці AddOns!”, VbCritical, “Не можу почати архівацію даних”
                Exit Sub
            End If
“Перевірити наявність папки Archives
            DirectoryFound = Dir(strAppPath & “Arhives”, vbDirectory)
            If (Len(DirectoryFound) = 0 Or Err = 76) Then “errPathNotFound = 76
MsgBox “Не знайдена папка Arhives для розміщення архіву”, vbCritical, “Не знайдена папка для архівів”
                Exit Sub
            End If
idProg = Shell (strAppPath & “AddOnsWinRAR a-r-m5-ag_DD-MM-YYYY_HH-MM” & strAppPath & “ArhivesНарушенія_ПДД_data” & strAppPath & “Нарушенія_ПДД_data.mdb”, vbNormalFocus)
        End If
    End If
End If
End Sub
 


Цей код дає нам суцільні переваги:


По перше: візуалізація процесу архівації, який можна і пригальмувати і скасувати (сама база і додаток вже закрилося, на екрані залишається тільки віконце архіватора) – юзер не совається на стільці в здогадах, коли ж закінчиться весь цей дивний процес ;), по-друге: в папку, де лежить WinRar.exe, покладіть також файл довідки WinRAR.hlp – юзер може почитати відмінне опис процесу архівації під час архівації, клацнувши на кнопці [Довідка] (раджу і Вам почитати ;), по-третє: значно спростився програмний код і зверніть увагу – весь процес перенесений на подію Unload (стартової) форми – тобто неважливо, як користувач закриває додаток – хрестиком у правому верхньому кутку форми або передбаченої кнопкою виходу, питання (але тільки за сприятливих для архівації умовах!) йому все одно не уникнути ;).


Та й ще WinRar – прекрасний архіватор, може бути, найкращий, але не безкоштовний, тому всім буде добре ;), якщо Ви будете використовувати легально придбану версію.


Приклад з використанням безкоштовного архіватора 7-Zip 4.13 beta, як я і погрожував 😉 Треба сказати, що відмінний архіватор 7-Zip www.7-zip.org/, Написаний Ігорем Павловим, поширюється вільно і Ви також можете вільно застосовувати його у Ваших розробках.


У цьому прикладі я використовую стиснення в стандартний Zip (що думаю, цілком виправдано, з огляду на підтримку цього стандарту в самій Windows XP) методом стиснення Normal – дуже швидко і досить сильно – 48 Мб стискаються до 6.2 Мб. (До слова, WinRar за прикладом вище робить з 48 Мб 4.3 Мб, але там режим стиснення виставлений на максимум). Якщо режим стиснення виставити на максимум і в 7-Zip, тоді архів зменшується до 6 Mb, але значно збільшується час створення архіву – все-таки я вирішив зупинитися на нормальному методі стиснення Zip.


Є два шляхи використання архіватора 7-Zip:


перший спосіб: якщо у Вас вже встановлено цей архіватор, то просто скопіюйте файл 7zg.exe з папки архіватора (за умовчанням “C: Program Files7-Zip ..”) в папку AddOns бази даних. Ну і в настройках архіватора можна перемкнутися на російську мову, якщо хочете.


другий спосіб: якщо Ви не хочете інсталювати архіватор або плануєте переносити базу на іншу машину, трохи більше “мудрований” – архіватор 7-Zip потрібно для стиснення всього три файли, строго розташованих в ієрархічній структурі. Ви можете залишити мінімальну структуру для створення архівів за допомогою 7-Zip – особисто я зробив так:


в папку AddOns переписав файл 7zg.exe


в папці AddOns створив підпапку Codecs і переписав туди файл Deflate.dll


в папці AddOns створив підпапку Formats і переписав туди файл zip.dll


це мінімум, що потрібно архіватор 7-Zip для стиснення в Zip (ніякої додаткової реєстрації компонентів проводити не потрібно).


Для зручності російськомовних користувачів можна ще в папку AddOns покласти файл ru.txt – русифікатор 7-Zip (для зручності, щоб не було неоднозначності, я перейменував цей файл русифікатора в 7Zip-ru.txt) і в реєстрі за адресою: HKEY_CURRENT_USERSoftware7-ZIP вказати в строковому параметрі точний шлях до файлу-русифікатори: у мене С: PDDAddOns7Zip-ru.txt. Якщо цього не зробити, що інтерфейс буде інгліш 😉


Після таких маніпуляцій можна приступати до вивчення коду – вважайте, що мінімальний 7-Zip вже “інстальований” на машині.


За основу візьмемо останній код, де в якості архіватора використовується WinRar і “переточити” його під 7-Zip:


Private Sub Form_Unload(Cancel As Integer)
Dim strAppPath As String
Dim idProg As Long
Dim DirectoryFound As String “для перевірки існування папки

“Збережемо шлях бази
strAppPath = Application.CurrentProject.Path & “”
“На виході з програми –
“Перевіряємо наявність файлу Порушення ПДД_data.mdb, якщо є, то
“Перевіряємо наявність файлу Порушення ПДД_data.ldb, якщо його немає, то
“Пропонуємо перед вихід зробити архівацію, якщо згоден те, архівація і вихід …….
If Dir (strAppPath & “Нарушенія_ПДД_data.mdb”) <> “” Then


“Значить, файл Нарушенія_ПДД_data.mdb існує – це сервер!
If Dir (strAppPath & “Нарушенія_ПДД_data.ldb”) = “” Then


“Значить, файл блокування не існує – ніхто не займає файл
If MsgBox (“Не забувайте створювати архівну копію бази даних при завершенні роботи.” & VbCrLf & vbCrLf & _
“Створити резервну копію бази зараз?”, VbInformation + vbYesNo, _
“Архівація даних після завершення роботи”) = vbYes Then
“Перевірити, чи є файл 7zg.exe в папці AddOns
            If Dir(strAppPath & “AddOns7zg.exe”) = “” Then
MsgBox “Не знайдено архіватор 7zg.exe в підпапці AddOns!”, VbCritical, “Не можу почати архівацію даних”
                Exit Sub
            End If
“Перевірити наявність папки Archives
            DirectoryFound = Dir(strAppPath & “Arhives”, vbDirectory)
            If (Len(DirectoryFound) = 0 Or Err = 76) Then “errPathNotFound = 76
MsgBox “Не знайдена папка Arhives для розміщення архіву”, vbCritical, “Не знайдена папка для архівів”
                Exit Sub
            End If


“Запуск архіватора
idProg = Shell (strAppPath & “AddOns7zg.exe a-tzip” & strAppPath & “ArhivesНарушенія_ПДД_data” & Format (date, “_dd-mm-YYYY_”) & Format (time, “_HH-MM”) & “. zip” & strAppPath & “Нарушенія_ПДД_data.mdb”, vbNormalFocus)
        End If
    End If
End If
End Sub
 


От і все. Тепер Ви з чистою совістю можете експлуатувати архівацію на базі 7-Zip, ніхто Вам і слова поганого не скаже 😉

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*