Notesminder-напоминалка О Новых Документах Для Lotus

  • Автор темы Автор темы morpheus
  • Дата начала Дата начала
M

morpheus

Доброго всем времени сутокпредлагаю Вашему вниманию простенькую, но для "моих" нужд необходимую программу.<p>ВНИМАНИЕ! Программа обновилась. Актуальная версия 1.0.0.7
  • теперь вы можете использовать программу для нескольких баз данных (XML-файл с настройками).
  • нет необходимости вносить изменения в ваши базы данных - программа может просто проходится по всем документам в виде
  • программа поддерживает @Formula (но НЕ в имени сервера/базы/вида, только в полях относящихся к нотес-документам)
  • добавились всплывающие уведомления (как в ICQ)ю Сообщения автоматически пропадают через 15 сек (можно настроить).
  • теперь вы можете сохранять изменения внесённые в закладках настроек.
<p>Суть: Приблуда каждые ХХХ минут стучится в заранее указанную базу данных Lotus Notes Domino и проверяет наличие для текущего пользователя "новых" документов. Если появилось что-то новое, об этом сообщается "облачком" из системного трея(см. скрины). Новое сообщение
inComing.png
Это, фактически, аналог встроенного minder'а в клиент Notes, с той лишь разницей, что мониторить можно любую доступную базу, проделав для этого минимум дизайнерский ухищрений.<p>Архитектура: Приблуда работает используя domino.tlb версии 6.5.4 ( тестировалась на 7.0.2 и 8.5.х клиентах ), в себе не хранит никаких данных (кроме тех что отображены на экране) и использует текущие настройки пользователя (расположение ИД-файла, подключения к серверу - используются нотес-имена). <p>Требования:Инсталлированный (или зарегистрированный как СОМ-Сервер в реестре Windows ) клиент Нотес, FrameWork 3.5 и выше, предоставление доступа для самого приложения (не всегда). Представление в целевой БД с первой сортировочной колонкой = имя текущего пользователя а также целевые документы должны иметь поле Subject - на основании которого и вычисляется что показать в списке<p>Начало работы и настройка: Для начала работы необходимо разархивировать скачанный архив в отдельную папку и запустить приложение. В закладках "Настройка" указать имя сервера Domino/XXX, путь к базе данных folder\dbname.nsf, а также имя представления MySuperView. С остальным думаю разберётесь.<p>Настройки теперь расширились. Из интерфейса программы можно поменять лишь некоторые параметры, а именно:
  • выводить всплывающие уведомления
  • Время "жизни" всплывающего уведомления
  • сворачивать в трей при открытии уведомления в лотус
Для работы с базами данных используется отдельный XML файл - "dbConfig.xml". В котором, в тегах "DB" указываются подробные настройки как работать с базой: <div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Описание тэгов</div></div><div class="sp-body"><div class="sp-content">
<th>TAG</th> <th>Поддерживаются @формулы</th> <th>Описание</th> <th>Пример</th> server нет Сервер баз данных. имя из лотуса.MyServer/ORG dbpath нет путь к базе данных.FOLDER\itProjects.nsf nview нет наименование вида/папкиvMyView key ДА Ключ для отработки ф-ции GetAllDocumentsByKey. В случае если пусто - обработаются все документы в виде. @UserName urlfield ДА Наименование поля/формула в котором содержится ссылка на документ. В случае если пусто - открывается документ-уведомление. ParentDocumentURL_FIELD interval НЕТ Периодичность обращений к базе даных. размерность минуты 1 field_subject ДА Наименование поля/формула в котором содержится тема документа Subject field_sender ДА Наименование поля/формула в котором содержится отправитель документа From field_date ДА Наименование поля/формула в котором содержится дата документа. Не работает @Created field_name_to_change ДА Наименование поля/формула в которое вносятся изменения в документе уведомлении. Н.: информация о том что документ прочитан readStatus field_value_to_change ДА Значения поля/формула которое вносится в документ уведомление. 1
<p>ВНИМАНИЕ: При написании @Formula будьте внимательны - XML имеет некоторую специфику работы с символам - ковычки, апостроф, тильда, символ ограничивающий теги и т.д. - описываются . Пример правильной записи динамической формулы @middle есть в архиве!<p>Ввод пароля ( средствами нотес, приблуда об этом вообще ничего "не знает" )
Password.JPG
Пасмотрим что тут у нас
Messages.JPG
Сама программка<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Старая версия</div></div><div class="sp-body"><div class="sp-content">З.Ы. В принципе ничего не мешает доделать опцию запоминания пароля, но я специально не делал этого, т.к. не располагаю достаточными знаниями в криптовании, и не уверен что смогу достойно защитить пароли ( пока что ).
 

Вложения

  • Tray.JPG
    Tray.JPG
    9,1 КБ · Просмотры: 1 299
Вот, добрался.

Попробую пошагово показать как я реализовывал данную программку.

  1. Создаём Windows Forms Application.
  2. Добавить библиотеку типов - domino.tlb. Для этого в меню Project\Add Reference где в проводнике и находим нужную библиотеку (ищите в месте установке клиента Lotus Notes). Visual Studio сам создаст "обёртку" к данной библиотеке и Вы сможете использовать "родные" для Вас notes-классы.
  3. Я для себя определил следующюю схему:объявляю глобальную переменную нотес-сессии(Domino.NotesSession nsSession), по желанию пользователя инициирую, а сбор данных веду в отдельном потоке.
  4. Для сбора данных в отдельном потоке использую отдельный самописный клас(ThreadsElements) который "асинхронно" используется в компоненте
  5. Собранные данные скидываю в компоненту , а при необходимости вывожу всплывающее сообщение из системного трея ( компонента ).

Листинг на C# ( Visual Studio 2010 )
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Компоненты - описание</div></div><div class="sp-body"><div class="sp-content">
/*
* imageList1 - компонента, контейнер рисунков(для отображения в сис.трее)
* dtGridMain - компонента-таблица для отображения списка новых сообщений
* backgroundWorker1 - комп.-для работы в отдельном потоке(читать литературу)
* cmsIconMenu - комп.-контекстное меню, для иконки
* btnConnect - кнопка включить
* btnOff - кнопка "свернуть в трей"
* btnClose - кнопка "Выключить и выйти"
* tbCntrlMain -комп.-табуляторный контрол с 2мя закладками ( tabPage1 и tpOptions )
* txtbxServer, txtbxDataBase, txtbxView - комп.-текстбоксы с настройками "куда" смотреть
* comboBox1 - комп.-комбобокс в которой выбираеться временной интервал для запросов
* rbDBlClickChColor, rbDBlClickDelFromGrid - комп.-радобатон для выбора действия при клике на новом сообщении ( менять цвет или удалять из списка)
* chBxHideInTrayOnDblClick - комп.-чекбокс для сворачивания окна программки при открытии ссылки на новое сообщение.
*/

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Обьявление гл.переменных </div></div><div class="sp-body"><div class="sp-content">
C++:
		Domino.NotesSession nsSession; // глобальная переменная, для сессии 
ThreadsElements asyncObj = new ThreadsElements(); // глобальная переменная для работы в потоке, описан ниже
Boolean mClose = false; // для понимания закрыть программу или свернуть

// Константы - номера иконок для сис. трея. обозначает номер рисунка в imageList1
int iOffLine = 0;
int iOnLine = 5;
int iNewMsg = 1;

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Кнопка включить</div></div><div class="sp-body"><div class="sp-content">
C++:
private void btnConnect_Click(object sender, EventArgs e)
{
if (nsSession == null) {
try // создаём сессию
{ nsSession = new Domino.NotesSession(); }
catch (Exception err)
{
MessageBox.Show(err.Message);
}
} 

try // тут может быть ошибка, пользователь отменяет ввод пароля кнопкой Cancel
{ nsSession.Initialize(); } // иницируем, можем сразу передать пароль. например nsSession.Initialize("myPassWord001"); 
catch (Exception err)
{
nsSession = null;
MessageBox.Show(err.Message);

// Включились в офф-лайн вещание
// nicnMain.Icon = Icon.FromHandle(((Bitmap)imageList1.Images[iOffLine]).GetHicon()); // Пока отключено
}

if (nsSession == null)
{ 
nicnMain.ShowBalloonTip(300, "Ошибка", "Подключиться не удалось", ToolTipIcon.Error);
nicnMain.Text = "Авто оповещение [Откл.]";
}
else
{
if ( asyncObj.IsConnected == false ) 
asyncObj = new ThreadsElements() // создаем обьект для работы в отдельном потоке и задаём параметры
{					  
IsConnected = true, 
ServerName = txtbxServer.Text, 
DBName = txtbxDataBase.Text, 
ViewName = txtbxView.Text,
s = nsSession,
iSleep = System.Convert.ToInt32(comboBox1.Text) ,
IsFirstTimeRun = true
};

// Включились в он-лайн вещание
// nicnMain.Icon = Icon.FromHandle(((Bitmap)imageList1.Images[iOnLine]).GetHicon()); // Пока отключено
nicnMain.Text = "Авто оповещение [Bкл.]";

backgroundWorker1.RunWorkerAsync(asyncObj); // начинаем паралельный поток по сбору данных
tbCntrlMain.TabPages["tpOptions"].Enabled = false; // делаем закладку опций неактивной
tbCntrlMain.SelectedTab = tbCntrlMain.TabPages[0]; // переходим на закладку с таблицей
btnConnect.Enabled = false; // Кнопку Включить "дизейблим", чтобы больше не клацали
Hide(); // Скрываем приложение в трей
}
}

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Кнопка - свернуть в трей</div></div><div class="sp-body"><div class="sp-content">
C++:
 private void btnOff_Click(object sender, EventArgs e)
{
Hide();  
}

Компонента backgroundWorker1 имеет два события DoWork (зделать чтотоd отдельном потоке) и RunWorkerCompleted(что сделать по завершении выполнения потока)
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">backgroundWorker1</div></div><div class="sp-body"><div class="sp-content">
C++:
		// дополнительный поток ( для запроса в кабинет )
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ThreadsElements teTest = e.Argument as ThreadsElements; // берём объект для работы в тек. потоке ( см. синхронизацию переменных в потоках + google )

if (teTest.IsFirstTimeRun == true) // первый раз поток без задержки
{ 
teTest.IsFirstTimeRun = false;
}
else // зделать "задержку" что бы не дёргать базу слишком часто
{
if (teTest.iSleep > 0)
{ 
Thread.Sleep(teTest.iSleep); // исходя из выбранного значения в comboBox1
}
else
{ 
Thread.Sleep(300000); // по умолчанию 5 минут
}
}

if (teTest.s == null)  // Нотес-сессия		
{
try // тут может быть ошибка, пользователь отменяет воод пароля
{ teTest.s.Initialize(); }
catch (Exception err)
{
teTest.s = null;
teTest.IsConnected = false;
MessageBox.Show(err.Message);

// Включились в офф-лайн вещание
// nicnMain.Icon = Icon.FromHandle(((Bitmap)imageList1.Images[iOffLine]).GetHicon());
nicnMain.Text = "Авто оповещение [Откл.]";
}
}

if (teTest.s == null) { nicnMain.ShowBalloonTip(300, "Ошибка", "Подключение потеряно", ToolTipIcon.Info); }
else
{
try
{
Domino.NotesDatabase ndbTest;
Domino.NotesDocument ndTmp;
Domino.NotesDocumentCollection ndColl;
Domino.NotesView nvTest;
NotesMemos nmTmp;
int iNew = 0;

ndbTest = teTest.s.GetDatabase(teTest.ServerName, teTest.DBName, false); // Подключаемся к базе				 
nvTest = ndbTest.GetView(teTest.ViewName); // берём нужный "вид"

ndColl = nvTest.GetAllDocumentsByKey(nsSession.UserName, true); // берём документы по ключу
ndTmp = ndColl.GetFirstDocument(); // берём первый документ

while (ndTmp != null)
{
if (teTest.HasURL(ndTmp.NotesURL.ToString())==false) // а был ли такой документ в нашей коллекции, если небыл то...
{
iNew++;

nmTmp = new NotesMemos(); // доп. клас с описанием отдельно нотес-документа, см.ниже
nmTmp.sNotesURL = ndTmp.NotesURL.ToString(); // Нотес-урл для открытия из таблицы
nmTmp.sCreatedDate = ndTmp.Created.ToString(); // Дата создания документа
nmTmp.sTitle = ndTmp.GetFirstItem("Subject").Text.ToString(); // тема из документ 
// тут можете собрать сколько угодно информации из документа, насколько хватит фантазии

teTest.AllInfo.Add(nmTmp);	 // вносим наш "документ" в list всех документов, см.ниже
}

ndTmp = ndColl.GetNextDocument(ndTmp);
}
}
catch (Exception nErr)
{
nicnMain.ShowBalloonTip(3000, "Подключение потеряно", nErr.Message, ToolTipIcon.Error);
}

e.Result = teTest; // определяем результат выполнения потока
}	 
}

// по окончанию потока, запустить его заново
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
ThreadsElements teTest = e.Result as ThreadsElements;
int n = 0;
int c = 0;

if (teTest.IsConnected) // проверяем статус подключения
{	 
foreach ( NotesMemos sURL in teTest.AllInfo ) // для всех записей NotesMemos в list-коллекции
{
if (sURL.IsNew) // а новый ли это элемент
{
n = dtGridMain.Rows.Add(); // берём номер новой строки в таблице

dtGridMain.Rows[n].Cells[0].Value = (n+1).ToString(); // порядковый номер
dtGridMain.Rows[n].Cells[1].Value = sURL.sCreatedDate; // дата создания
dtGridMain.Rows[n].Cells[2].Value = sURL.sTitle; // Тема из документа
dtGridMain.Rows[n].Cells[3].Value = sURL.sNotesURL; // ссылка для открытия. Колонка скрыта

c++;
sURL.IsNew = false; // ставим поментку, что документ уже не новый
}
}

if (c > 0) // Есть новые сообщения
{
nicnMain.ShowBalloonTip(3000, "Внимание", "Новых сообщений: " + c.ToString() + " \n" + "Всего " + dtGridMain.Rows.Count.ToString(), ToolTipIcon.Info);
nicnMain.Text = "У Вас новых/всего сообщений " + c.ToString() + " / " + dtGridMain.Rows.Count.ToString() + "";

// Включились в офф-лайн вещание
// nicnMain.Icon = Icon.FromHandle(((Bitmap)imageList1.Images[iNewMsg]).GetHicon());
}
}
else
{
asyncObj = new ThreadsElements()
{
IsConnected = true,
ServerName = txtbxServer.Text,
DBName = txtbxDataBase.Text,
ViewName = txtbxView.Text,
iSleep = System.Convert.ToInt32(comboBox1.Text),
s = nsSession
};
}


backgroundWorker1.RunWorkerAsync(asyncObj);
}

И так, таблицу наполнили, запросы идут, уведомление из трея вылазит.
Осталось дорисовать открытия документа по двойному клику из ДатаГрид'a
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Двойной клик по записи в таблице</div></div><div class="sp-body"><div class="sp-content">
C++:
 // Управление открытием ссылок
private void dtGridMain_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
int n = e.RowIndex;

try
{
System.Diagnostics.Process.Start(dtGridMain.Rows[n].Cells["NURL"].Value.ToString()); // открываем notesUrl
dtGridMain.ClearSelection(); // убираем выделение в таблице

// при открытии свернуть в трей
if (chBxHideInTrayOnDblClick.Checked)
Hide();

if (rbDBlClickChColor.Checked) // просто поменять цвет при открытии из таблицы
{
for (int i = 0; i <= dtGridMain.Rows[e.RowIndex].Cells.Count; i++)
{
dtGridMain.Rows[e.RowIndex].Cells[i].Style.BackColor = Color.LightGray;
}
}
else // удалить строку при открытии из таблицы
{
nicnMain.Text = "У Вас всего сообщений " + dtGridMain.Rows.Count.ToString();				
dtGridMain.Rows.Remove(dtGridMain.Rows[e.RowIndex]);
}
}
catch { }
}

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">При закрытии - свернуть в трей</div></div><div class="sp-body"><div class="sp-content">
C++:
 // При закрытии свернуть в трей
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason != CloseReason.TaskManagerClosing &&
e.CloseReason != CloseReason.WindowsShutDown && mClose != true)			 
e.Cancel = true;			
this.WindowState = FormWindowState.Minimized;
Hide();
}

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">Класс для работы в паралельном потоке</div></div><div class="sp-body"><div class="sp-content">
C++:
	public class ThreadsElements
{
// Properties public
public Boolean IsConnected = false; // индикатор подключения
public Domino.NotesSession s = null; // обьект - сессия
public String ServerName = ""; // имя сервера
public String DBName = ""; // путь к базе
public String ViewName = ""; // имя предаствления
public Boolean IsFirstTimeRun = false; // запущенно первый раз
public List<NotesMemos> AllInfo = new List<NotesMemos>(); // List(список) NotesMemos(см.ниже) записей
public int iSleep // = 300000; // каждые пять минут // задержка
{
get 
{
return iMlSSlep;
}
set 
{
if (value > 0)
{
iMlSSlep = value * 1000 * 60; // минуты в миллисекунды ... приблизительно
}
else
{
iMlSSlep = 300000;
}
}
}

// Properties private
private int iMlSSlep = 300000;

// Functions and Methods		
public Boolean HasURL ( String sURL ) // Проверка на наличии в коллекции найденного документа
{

if (AllInfo.Count > 0)
{
foreach (NotesMemos k in AllInfo)
{
if (k.sNotesURL.Equals(sURL)) return true;
}
}

return false;
}

}

<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">NotesMemos - класс для описания нотес-документа</div></div><div class="sp-body"><div class="sp-content">
C++:
public class NotesMemos 
{
public String sNotesURL = ""; // ссылка на документ
public String sCreatedDate = ""; // дата создания
public String sTitle = ""; // тема
public Boolean IsNew = true; // новенький
}
 
А реально чтобы данная программа работала без установленного клиента Lotus Notes?
 
shproteg
не думаю, хотя когда то видел тему про "урезаного клиента" ... но не понимаю зачем это, без лотуса сама программка теряет всякий смысл
 
Смысл в том что есть еще "тонкие" вэб клиенты а оповещалки у них нет вот и хотелось бы "независимую" оповещалку найти
 
shproteg
если веб - клиенты то оповещалку просто разместить на веб-морде - тут вам ajax в помощь
 
А можно ли выводить пароль не в COM-окне, а в OLE, как это делает сам клиент? Задача в том, чтобы выбрать нужный Location, а не пользовать данные из notes.ini последнего пользователя, заходившего в клиент.

И вообще бомба была бы, если на указанное количество секунд выводить не просто уведомление "новых столько-то", а выводить перечень уведомлений, и при щелчке на любом открывать сам док в Лотусе (OLE-то ведь инициализирован при вводе пароля).
Ну и для писем выводить имя отправителя и тему.

Ещё дать возможность натравливать на несколько баз, а во всплывающем окошке выводить:
Почта: Re: test (Х-ков Сидор П.-вич)
Документы: Док №ххх (Бухгалтерова Бухгалтерка Бухгалтеровна)
 
А можно ли выводить пароль не в COM-окне, а в OLE, как это делает сам клиент?
- это наврят


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

И вообще бомба была бы, если на указанное количество секунд выводить не просто уведомление "новых столько-то", а выводить перечень уведомлений, и при щелчке на любом открывать сам док в Лотусе (OLE-то ведь инициализирован при вводе пароля).
Ну и для писем выводить имя отправителя и тему.
можно

Ещё дать возможность натравливать на несколько баз, а во всплывающем окошке выводить:
- думаю тоже можно будет допилить
 
Пытаюсь скачать и не могу, - последний NOD говорит, что "Потенциальная угроза" и не даёт. Опция "Отключить защиту от вирусных и шпионских программ" не помогает. Как-нибудь можно обойти (вырубить антивирус не могу - политики)? Можно передать файл на проверку туда, тогда они через пару дней исключат из списка угроз.
 
А как сделать, чтобы не надо было вводить пароль от ид при запуске?
Программк не всегда выходит из трея.
 
А как сделать, чтобы не надо было вводить пароль от ид при запуске?
Я не смогу обеспечить "безопасноть" такого пароля, т.к. прийдёться хранить его в открытом виде (ну или в очень "легко" зашифрованном).

Программк не всегда выходит из трея.
Есть такое, "отловить" не получается ((
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!