FreeBSD: некоторые особенности сборки ядра Сергей Супрунов makeoptions MODULES_OVERRIDE='cd9660 netgraph nfsserver nfsclient' # cd /usr/src/sys/modules/<имя_модуля> # make # make install options INCLUDE_CONFIG_FILE # cd /boot/kernel # strings -n 3 kernel | sed -n 's/^___//p' ----------------------------------------------------------------------------------------------------------------- Не допустите потери данных! Ленточная библиотека к вашим услугам Виталий Банковский hNumber = bSpeed/hSpeed tar -xzvf amanda-2.4.5p1.tar.gz cd amanda-2.4.5p1 ./configure --with-user=root --with-group=root make make install org "Bigsoft Home" mailto report@domain.com inparallel 12 etusage 600000 Kbps etimeout 208000 Runtapes 5 tpchanger "chg-zd-mtx" tapedev "/dev/nst0" changerfile "/usr/local/etc/amanda/DailySet1/changer" changerdev "/dev/sg2" tapetype LTO2 amrecover_changer "chg-zd-mtx" eject > 1 sleep 5 cleanmax 10 changerdev /dev/sg0 havebarcode 1 havereader=1 labelfile /var/log/amanda/labelfile.txt offline_before_unload=1 offlinestatus=1 OFFLINE_BEFORE_UNLOAD=1 holdingdisk hd1 { comment "main holding disk" directory "/home/amanda" # Путь к данным на диске use 300 Gb # Доступное пространство chunksize 1Gb # Разбить данные на куски по 1 Гб } define dumptype default { global estimate calcsize program "GNUTAR" dumpcycle 14 comment "root partitions dumped with tar" compress server fast index priority medium exclude list "amandaexclude" tape_splitsize 20 Gb } server1.domain.com / { default } server1.domain.com /boot { default } server1.domain.com /var { default } server1.domain.com /usr { default } server1.domain.com /home { default } amlabel DailySet1 DailySet1-NR slot SlotID #!/usr/bin/perl use strict; foreach my $i(1..60) { system("amlabel -f DailySet1 DailySet1-$i slot $i"); } amdump DailySet1 server1.domain.com /home amadmin DailySet1 info servername [disk] amadmin DailySet1 info server1.domain.com /usr define dumptype default { ...... exclude list "amandaexclude" ...... } log/httpd/* log/httpd2/* log/httpd2-ssl/* log/httpsd/* log/maillog* tmp/* *.tmp *.temp /home/mysql/ /home/www/ /home/users/user1 /home/users/user2 find /home -t d -mindepth 2 -maxdepth 2 find /home -t d -mindepth 1 -maxdepth 1 /home/mysql/ /home/www/ /home/users/ /home/users/user1 /home/users/user2 server1.domain.com /home { default2 exclude append /home/users/user1 exclude append /home/users/user2 } server1.domain.com /home/users/user1 { default } server1.domain.com /home/users/user2 { default } amadmin DailySet1 info server1.domain.com /home amtape DailySet1 label DailySet1-1 amrestore /dev/nst0 server1.domain.com /home amtape DailySet1 label DailySet1-59 amrestore /dev/nst0 server1.domain.com /home ----------------------------------------------------------------------------------------------------------------- Доступ к Active Directory с помощью прилинкованного SQL-сервера Иван Коробко Provider=ADsDSOObject;Encrypt Password=False;Integrated Security=SSPI;Mode=Read;Bind Flags=0;ADSI Flag=-2147483648 sp_addlinkedserver [ @server = ] 'server' [ , [ @srvproduct = ] 'product_name' ] [ , [ @provider = ] 'provider_name' ] [ , [ @datasrc = ] 'data_source' ] [ , [ @location = ] 'location' ] [ , [ @provstr = ] 'provider_string' ] [ , [ @catalog = ] 'catalog' ] Set objConnection = СreateОbject("ADODB.Connection") Set objCommand = СreateОbject("ADODB.Command") objConnection.CommandTimeout = 120 objConnection.Provider = "ADsDSOObject" objConnection.Open "Active Directory Provider" Set objCommand.ActiveConnection = objConnection Set sql=objconnection.execute("SELECT cn FROM 'LDAP://DC=firm,DC=ru' WHERE objectClass='Computer'" ) sql.Movefirst Do Until sql.EOF Wscript.Echo sql.Fields("cn").Value sql.MoveNext Loop openquery( linked_server , 'query' ) select * from openquery (ADSI, ' select cn from ''LDAP://DC=firm,DC=ru'' where objectclass=''computer'' ') openquery( linked_server , 'query' ) Select Convert(varchar(50), cn) as UserName, Convert(varchar(50), description) From openquery (ADSI, '…') Select cn, description from 'LDAP://DC=firm,DC=ru' where objectclass='person' and not objectclass='computer' Select Convert(varchar(50), cn) as UserName, Convert(varchar(50), description) From openquery (ADSI, 'Select cn, description from ''LDAP://DC=firm,DC=ru'' where objectclass=''person'' and not objectclass=''computer'' ') set a=СreateОbject("Adodb.Сonnection") str="driver={sql server};server=SQLServer;trusted_connected=yes;" a.open(str) a.cursorlocation=3 set rs=СreateОbject("Аdodb.Recordset") str_q="select * from ОpenQuery (ADSI, 'select cn from ''LDAP://DC=firm,DC=ru'' where objectclass=''computer'' ')" rs.open str_q, a, 1, 2, 1 rs.movefirst Do Until rs.EOF Wscript.Echo rs.Fields(0).Value rs.MoveNext Loop objectclass=''person'' and not objectclass=''computer'' not telephonenumber=''*'' select convert(varchar(50), cn) as UserName, convert(varchar(50), telephonenumber) as Tel from openquery (ADSI, ' select cn, telephonenumber from ''LDAP://DC=firm,DC=ru'' where objectclass=''person'' and not objectclass=''computer'' and not telephonenumber=''*тел.*'' ') select convert(varchar(50), cn) as UserName, convert(varchar(50), telephonenumber) as Tel from openquery (ADSI, ' select cn, telephonenumber from ''LDAP://DC=firm,DC=ru'' where objectclass=''person'' and not objectclass=''computer'' /* and not telephonenumber=''*тел.*'' */ ') where telephonenumber is null Domain = GetObject("LDAP://rootDSE").Get("defaultNamingContext") Set oUser = GetObject("LDAP://cn=Ivanov,Ivan,"& Domain) oUser.Put "Description", "Иванов Иван Петрович" oUser.SetInfo Set oUser = Nothing Domain = "LDAP://" + GetObject("LDAP://rootDSE").Get("defaultNamingContext") set a=СreateОbject("Adodb.Сonnection") str="driver={sql server};server=SQLServer; ? trusted_connected=yes;" a.open(str) a.cursorlocation=3 set rs=СreateОbject("Аdodb.Recordset") str_q="select convert(varchar(50), distinguishedName ) as Path, convert(varchar(50), telephonenumber) as Tel from openquery (ADSI, 'select distinguishedName , telephonenumber from ''"+Domain+"'' where objectclass=''person'' and not objectclass=''computer'' and not telephonenumber=''*тел.*'' ')" rs.open str_q, a, 1, 2, 1 rs.movefirst Do Until rs.EOF WriteDate (rs.Fields(Path).Value, rs.Fields(Tel).Value) rs.MoveNext Loop Function WriteDate(a,b) Set oUser = GetObject("LDAP://"& a) oUser.Put " telephonenumber", b oUser.SetInfo Set oUser = Nothing End Function ----------------------------------------------------------------------------------------------------------------- Строим сетевую инфраструктуру для системы 1С:Предприятие Сергей Алаев iptables -t nat –I PREROUTING -p tcp -d <внешний IP-адрес proxy-сервера> --dport <выделенный порт на proxy-сервере, например, 10443> -j DNAT --to-destination :3389 iptables –t filter –I FORWARD –s -d <внешний IP proxy-сервера> -p tcp --port <выделенный порт на proxy-сервере, например, 10443> -j ACCEPT ./appserverclient -s192.168.6.39 -a"1С Предприятие" -uAdministrator -ppassword ----------------------------------------------------------------------------------------------------------------- Система анализа квот на базе File Server Resource Manager Иван Коробко C:\Quota.vbs [Quota Path] [Quota Limit MB] [Quota Used MB] [Quota Used Percent] [Quota Free MB] [Source Io Owner] Set objArgs = WScript.Arguments a0=objArgs(0) a1=objArgs(1) a2=objArgs(2) a3=objArgs(4) a4=objArgs(4) a5=objArgs(5) Set sec = CreateObject("AdsSecurity") Set sd = sec.GetSecurityDescriptor("FILE://"+PathToFolder) Set Dacl = sd.DiscretionaryAcl For Each ace In Dacl Wscript.Echo ace.Trustee Wscript.Echo ace.AccessMask Wscript.Echo ace.AceType Next GroupName=ace.Trustee Right(cstr(GroupName), Len(GroupName)-InStr(GroupName,"\")) Set Obj=GetObject("WinNT://"&domain&"/"& group&",group") For Each member In obj.members Wscript.Echo cstr(member.name) Next Set Obj=nothing regsvr32 /s c:\Program Files\Microsoft\ADSI Resource Kit, Samples and Utilities\ResourceKit\ADsSecurity.dll MAIL_SUBJECT="Квота папки "+right(a0,len(a0)-cstr(a0,"\")) +" использована на "+a3+"% ("+a(2)+" Мб). Размер квоты "+a(1)+" Мб" mail_body = mail_body + "Уважаемый(ая) " + mail_to + "!"+ vbNewLine mail_body = mail_body + "Вы использовали установленную квоту папки " + right(a0,len(a0)-cstr(a0,"\")) +" на " + a3 + "%." + vbNewLine mail_body = mail_body + "Общий размер квоты на папку" + a1+ " MB, из них занято " + a2 +" MB." + vbNewLin + vbNewLine mail_body = mail_body + "Необходимо удалить ненужные данные с этой папки." + vbNewLin + vbNewLine + vbNewTab mail_body = mail_body + "С уважением, Служба поддержки." + vbNewLin + vbNewLine MAIL_FROM="Support@Firm.Ru" MAIL_SERVER="Mail.Firm.ru" MAIL_TO="" MAIL_BODY="" MAIL_SUBJECT="" … MAIL_SUBJECT=… MAIL_BODY = MAIL_BODY + … … Set iMsg = CreateObject("CDO.Message") Set iConf = CreateObject("CDO.Configuration") Set Flds = iConf.Fields Flds.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2 Flds.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = MAIL_SERVER Flds.Item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 10 Flds.Update iMsg.Configuration = iConf iMsg.To = MAIL_TO iMsg.HTMLBody = MAIL_BODY iMsg.From = MAIL_FROM iMsg.Subject = MAIL_SUBJECT iMsg.Send ----------------------------------------------------------------------------------------------------------------- Vyatta – Linux-дистрибутив для роутеров Сергей Яремчук # xorpsh # edit interfaces ethernet eth0 # set description "WAN" # set address 192.168.1.58 prefix-length 24 # show # commit # up # delete description # set interfaces ethernet eth1 address 10.10.10.10 prefix-length 24 # set interfaces loopback lo address 10.10.10.10 prefix-length 32 # commit # set system host-name router # set system domain-name domain.com root@vyatta# commit # set protocols static route 0.0.0.0/0 next-hop 192.168.1.1 # set system name-server 192.168.1.1 # set service ssh # set service # create service nat rule 1 # edit service nat rule 1 # set type source # set translation-type masquerade # set outbound-interface eth0 # set protocols all # set source network 10.10.10.0/24 # set destination network 0.0.0.0/0 # top # create service nat rule 2 # edit service nat rule 2 # set type destination # set translation-type static # set inbound-interface eth0 # set protocols tcp # set source network 0.0.0.0/0 # set destination address 192.168.1.58 # set destination port-name http # set inside-address address 10.10.10.30 # top # show service nat # set firewall name FW-1 rule 1 action accept # set firewall name FW-1 rule 1 source address 170.20.0.45 # set interfaces ethernet eth0 firewall in name FW-1 # commit # show firewall name FW-1 # set firewall name FW-1 rule 1 protocol tcp # set firewall name FW-1 rule 1 destination port-name http # set service http # save /mnt/floppy/config/config.boot # install-system ----------------------------------------------------------------------------------------------------------------- Система аутентификации веб-пользователей WebAuth Сергей Яремчук $ sudo apt-cache search webauth $ sudo apt-cache search webauth $ ./autogen $ ./configure --enable-mod_webkdc $ make $ make check $ sudo make install $ sudo make install-tests $ sudo make install-webkdc $ sudo mkdir -p /etc/apache2/webauth $ sudo mkdir -p /etc/apache2/webkdc $ sudo grep User /etc/apache2/apache2.conf $ sudo chown -R www-data:www-data /etc/apache2/webkdc /etc/apache2/webauth # Загружаем модуль LoadModule webauth_module /usr/lib/apache2/modules/mod_webauth.so # Расположение AES-ключей и токенов для связи с WebKDC # относительно корня веб-вервера WebAuthKeyring webauth/keyring WebAuthServiceTokenCache webauth/service_token_cache # Расположение keytab-файла Kerberos WebAuthKeytab webauth/keytab # Адрес, куда будет перенаправлен пользователь для регистрации WebAuthLoginURL https://grinder.com/login/ # Адрес для связи с сервером WebKDC WebAuthWebKdcURL https://grinder.com/webkdc-service/ # Название сервиса, используемого для связи с WebKDC WebAuthWebKdcPrincipal service/webkdc@GRINDER.COM # Автоматическое перенаправление пользователя на SSL, # при попытке соединиться по не защищенному каналу в доступе # будет отказано и журнале появится запись: # mod_webauth: connection is not https, denying request WebAuthSSLRedirect on WebAuthDebug on # Если используется сам подписанный сертификат # для работы с WebKDC, добавляем WebAuthWebKdcSSLCertFile webauth/webkdc.cert # В отладочном режиме можно включить доступ # к URL /webauth-status, который покажет статус работы # mod_webauth # # SetHandler webauth # Order allow,deny # Allow from all # $ sudo apt-get install krb5-admin-server krb5-kdc krb5-config krb5-user krb5-clients $ sudo /etc/init.d/krb5-kdc restart $ sudo /etc/init.d/krb5-admin-server restart $ sudo kdb5_util create –s $ sudo kadmin.local -q "addprinc admin/admin" $ sudo kadmin.local -p admin/admin $ sudo ln –s /etc/krb5.keytab /etc/apache2/webauth/keytab $ sudo chown www-data:www-data /etc/apache2/webauth/keytab AuthType WebAuth Require valid-user AuthType WebAuth require valid-user AuthType WebAuth require valid-user order deny,allow deny from all allow from 192.168 satisfy any WebAuthDoLogout on # Загружаем модуль LoadModule webkdc_module /usr/lib/apache2/modules/mod_webkdc.so WebKdcServiceTokenLifetime 30d WebKdcKeyring webkdc/keyring WebKdcKeytab webkdc/keytab WebKdcTokenAcl webkdc/token.acl WebKdcDebug on LogLevel debug # Создадим алиасы ScriptAlias /login "webkdc/login.fcgi" ScriptAlias /logout "webkdc/logout.fcgi" Alias /images "webkdc/images/" Alias /help.html "webkdc/help.html" $ cd /etc/apache2/webkdc $ sudo wa_keyring -f ./keyring add 2d $ sudo cat keyring $ sudo wa_keyring -f keyring list our $KEYRING_PATH = "/etc/apache2/webkdc/keyring"; our $TEMPLATE_PATH = "/usr/share/weblogin/generic/templates"; # Адрес, по которому будет находиться сервис WebKDC our $URL = "https://localhost/webkdc-service/"; $ sudo cp webauth-3.5.4/src/modules/webkdc/token.acl /etc/apache2/webkdc/ krb5:service/*@grinder.com id $ sudo /etc/init.d/apache2 restart ----------------------------------------------------------------------------------------------------------------- Организуем доступ к базам данных при разработке кроссплатформенных приложений на C++/wxWidgets Владимир Тряпичко Файл setupvars.bat "C:/Program Files/Microsoft Visual Studio 8/VC/bin/vcvars32.bat" Файл export.bat lib.exe /def:sqlite3.def /machine:x86 /out:sqlite3.lib setupvars export $(ProjectDir)../databaselayer/sqlite/include $(ProjectDir)../databaselayer/include $(ProjectDir)../databaselayer/sqlite/lib $(ProjectDir)../databaselayer/lib advapi32.lib comctl32.lib uuid.lib rpcrt4.lib wxbase28ud.lib wxmsw28ud_core.lib wxmsw28ud_adv.lib wxpngd.lib wxcode_msw28ud_databaselayer_sqlite.lib sqlite3.lib #ifndef _SQLITE_TEST_MAINFRAME_H #define _SQLITE_TEST_MAINFRAME_H #include class SQLiteTestMainFrame : public wxFrame { void CreateControls(); public: SQLiteTestMainFrame(); bool Create(wxWindow * parent, wxWindowID id, const wxString & title); DECLARE_EVENT_TABLE() void OnExit(wxCommandEvent & event); }; #endif #include "SQLiteTestMainFrame.h" #include "SQLiteTestApp.h" BEGIN_EVENT_TABLE(SQLiteTestMainFrame, wxFrame) EVT_MENU(wxID_EXIT, SQLiteTestMainFrame::OnExit) END_EVENT_TABLE() SQLiteTestMainFrame::SQLiteTestMainFrame() { Create(NULL, wxID_ANY, _("SQLite Addressbook")); } bool SQLiteTestMainFrame::Create(wxWindow * parent, wxWindowID id, const wxString & title) { bool res = wxFrame::Create(parent, id, title, wxDefaultPosition, wxSize(700, 500)); if(res) { CreateControls(); } return res; } void SQLiteTestMainFrame::CreateControls() { wxMenuBar * menuBar = new wxMenuBar; SetMenuBar(menuBar); wxMenu * fileMenu = new wxMenu; fileMenu->Append(wxID_EXIT, _("Exit\tAlt+F4")); menuBar->Append(fileMenu, _("File")); CreateStatusBar(2); Centre(); } void SQLiteTestMainFrame::OnExit(wxCommandEvent & event) { wxUnusedVar(event); Close(); } #ifndef _SQLITE_TEST_APP_H #define _SQLITE_TEST_APP_H #include #include #include class SQLiteTestApp : public wxApp { DatabaseLayer * m_Database; public: virtual bool OnInit(); virtual int OnExit(); bool ConnectToDatabase(); DatabaseLayer * GetDatabase(); }; DECLARE_APP(SQLiteTestApp) #endif #include #include #include "SQLiteTestApp.h" #include "SQLiteTestMainFrame.h" IMPLEMENT_APP(SQLiteTestApp) bool SQLiteTestApp::OnInit() { if(!ConnectToDatabase()) { wxFAIL_MSG(_("Error connecting to database!")); return false; } wxImage::AddHandler(new wxPNGHandler); wxImage::AddHandler(new wxJPEGHandler); SQLiteTestMainFrame * frame = new SQLiteTestMainFrame; SetTopWindow(frame); frame->Show(); return true; } int SQLiteTestApp::OnExit() { if(m_Database) { if(m_Database->IsOpen()) { m_Database->Close(); } wxDELETE(m_Database); } return wxApp::OnExit(); } bool SQLiteTestApp::ConnectToDatabase() { m_Database = new SqliteDatabaseLayer(); wxString db_filename(wxT("addressbook.db")); PreparedStatement * pStatement(NULL); bool bCreate = !wxFileExists(db_filename); if(bCreate) { wxMessageBox(_("Database does not exist... recreating.")); } try { m_Database->Open(db_filename); // Try to recreate tables try { m_Database->RunQuery( wxT("CREATE TABLE groups(id integer primary key, \ name varchar(128) not null,\ description varchar(512));")); } catch(DatabaseLayerException & e) {wxUnusedVar(e);} try { m_Database->RunQuery(wxT("CREATE TABLE persons(\ id integer primary key,\ groupid integer not null,\ first_name varchar(64) not null,\ last_name varchar(64) not null,\ gender boolean not null,\ address varchar(128) not null,\ city varchar(64) not null,\ country varchar(32) not null,\ phone varchar(32),\ email varchar(128),\ website varchar(260));")); } catch(DatabaseLayerException & e) {wxUnusedVar(e);} if(bCreate) { m_Database->RunQuery( wxT("INSERT INTO groups(name, description) \ VALUES ('Friends', 'My friends')")); pStatement = m_Database->PrepareStatement( wxT("INSERT INTO persons(groupid, \ first_name,last_name,gender,address,city,\ country,phone) VALUES \ (?,?,?,?,?,?,?,?)")); if (pStatement) { pStatement->SetParamInt(1, 1); pStatement->SetParamString(2, _("John")); pStatement->SetParamString(3, _("Doe")); pStatement->SetParamBool(4, 1); pStatement->SetParamString(5, _("SomeStreet st., 123/45")); pStatement->SetParamString(6, _("Some City")); pStatement->SetParamString(7, _("Some Country")); pStatement->SetParamString(8, _("+000-00-000-00-00")); pStatement->RunQuery(); m_Database->CloseStatement(pStatement); pStatement = NULL; } } } catch(DatabaseLayerException & e) { if(pStatement) { m_Database->CloseStatement(pStatement); pStatement = NULL; } wxFAIL_MSG(e.GetErrorMessage()); return false; } return true; } DatabaseLayer * SQLiteTestApp::GetDatabase() { return m_Database; } ./wxActiveRecord ... // COMMENT OUT THE ONES YOU DON'T USE //#define AR_USE_POSTGRESQL #define AR_USE_SQLITE //#define AR_USE_MYSQL //#define AR_USE_FIREBIRD ... ... #include "wxActiveRecord.h" ... ... #include #include "Group.h" #include "Person.h" class SQLiteTestApp : public wxApp { Group * m_GroupTable; Person * m_PersonTable; ... public: ... Group * GetGroupTable(); Person * GetPersonTable(); }; ... ... int SQLiteTestApp::OnExit() { wxDELETE(m_PersonTable); wxDELETE(m_GroupTable); ... } bool SQLiteTestApp::ConnectToDatabase() { ... try { m_GroupTable = new Group(wxGetApp().GetDatabase(), wxT("groups")); m_PersonTable = new Person(wxGetApp().GetDatabase(), wxT("persons")); } catch(DatabaseLayerException & e) { wxActiveRecord::ProcessException(e); } return true; } ... $(WXWIN)/art/addbookm.xpm $(WXWIN)/art/delbookm.xpm $(WXWIN)/art/new.xpm $(WXWIN)/art/delete.xpm ./art ... class SQLiteTestMainFrame : public wxFrame { wxListBox * m_GroupsListBox; wxListView * m_PersonsListView; wxHtmlWindow * m_PersonInfoPanel; wxToolBar * CreateToolBar(); ... }; #endif ... #include #include "new.xpm" #include "delete.xpm" #include "addbookm.xpm" #include "delbookm.xpm" ... void SQLiteTestMainFrame::CreateControls() { ... wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL); SetSizer(sizer); wxSplitterWindow * splitter = new wxSplitterWindow(this, wxID_ANY, wxDefaultPosition, wxSize(500, 400), wxSP_3DSASH); splitter->SetMinimumPaneSize(100); sizer->Add(splitter, 1, wxEXPAND); m_GroupsListBox = new wxListBox(splitter, ID_GROUPS_LISTBOX, wxDefaultPosition, wxDefaultSize); wxSplitterWindow * personsplitter = new wxSplitterWindow(splitter, wxID_ANY, wxDefaultPosition, wxSize(500, 400), wxSP_3DSASH); personsplitter->SetMinimumPaneSize(100); m_PersonsListView = new wxListView(personsplitter, ID_PERSONS_LISTCTRL, wxDefaultPosition, wxDefaultSize, wxLC_REPORT); m_PersonsListView->InsertColumn(0, _("First Name"), wxLIST_FORMAT_LEFT, 120); m_PersonsListView->InsertColumn(1, _("Last Name"), wxLIST_FORMAT_LEFT, 120); ... personsplitter->SetSashGravity(1.0) splitter->SplitVertically(m_GroupsListBox, personsplitter, 160); personsplitter->SplitHorizontally(m_PersonsListView, m_PersonInfoPanel, personsplitter->GetSize().GetHeight()-180); SetToolBar(CreateToolBar()); ... } wxToolBar * SQLiteTestMainFrame::CreateToolBar() { wxToolBar * toolBar = new wxToolBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTB_FLAT|wxTB_TEXT); toolBar->AddTool(ID_ADD_GROUP, _("Add Group"), wxBitmap(addbookm_xpm)); toolBar->AddTool(ID_DELETE_GROUP, _("Remove Group"), wxBitmap(delbookm_xpm)); toolBar->AddSeparator(); ... toolBar->Realize(); return toolBar; } ... ... #include class RecordIDClientData : public wxClientData { int m_ID; public: RecordIDClientData(int id) : m_ID(id) {} int GetID() {return m_ID;} ... }; ... void SQLiteTestMainFrame::FillGroupsList() { m_GroupsListBox->Freeze(); m_GroupsListBox->Clear(); GroupRowSet * allGroups = wxGetApp().GetGroupTable()->All(); for(unsigned long i = 0; i < allGroups->Count(); ++i) { m_GroupsListBox->Append(allGroups->Item(i)->name, new RecordIDClientData(allGroups->Item(i)->id)); } if(m_GroupsListBox->GetCount()) { m_GroupsListBox->SetSelection(0); RecordIDClientData * data = (RecordIDClientData *) m_GroupsListBox->GetClientObject( m_GroupsListBox->GetSelection()); if(data) { FillPersonsList(data->GetID()); } } m_GroupsListBox->Thaw(); } void SQLiteTestMainFrame::FillPersonsList(int groupid) { m_PersonsListView->Freeze(); m_PersonsListView->DeleteAllItems(); GroupRow * thisGroup = wxGetApp().GetGroupTable()->Id(groupid); if(thisGroup) { PersonRowSet * allPersons = thisGroup->GetPersons(); long item(0); for(unsigned long i = 0; i < allPersons->Count(); ++i) { item = m_PersonsListView->InsertItem(item, allPersons->Item(i)->first_name); m_PersonsListView->SetItem(item, 1, allPersons->Item(i)->last_name); m_PersonsListView->SetItem(item, 2, allPersons->Item(i)->email); m_PersonsListView->SetItem(item, 3, allPersons->Item(i)->phone); m_PersonsListView->SetItemData(item, (long)allPersons->Item(i)->id); } if(m_PersonsListView->GetItemCount()) { m_PersonsListView->Select(0); } } m_PersonsListView->Thaw(); } void SQLiteTestMainFrame:: OnGroupListBoxSelected(wxCommandEvent & event) { RecordIDClientData * data = (RecordIDClientData *)event.GetClientObject(); if(data) { FillPersonsList(data->GetID()); } } void SQLiteTestMainFrame:: OnPersonListViewSelected(wxListEvent & event) { long personid = event.GetData(); PersonRow * person = wxGetApp().GetPersonTable()->Id((int)personid); if(person) { m_PersonInfoPanel->SetPage(wxString::Format( wxT("

%s %s

"), person->first_name, person->last_name)); m_PersonInfoPanel->AppendToPage(wxString::Format( wxT("Gender: %s"), (person->gender?wxT("Male"):wxT("Female")))); m_PersonInfoPanel->AppendToPage(wxT("
")); ... } else { m_PersonInfoPanel->SetPage( _("

Can't find info about \ selected person

")); } } void SQLiteTestMainFrame:: OnPersonInfoPanelLinkClicked(wxHtmlLinkEvent & event) { #if defined(__WXMSW__) ShellExecute(NULL, NULL, event.GetLinkInfo().GetHref().GetData(), NULL, NULL, SW_SHOW); #else wxExecute(event.GetLinkInfo().GetHref()); #endif } #ifndef _EDIT_GROUP_DIALOG_H #define _EDIT_GROUP_DIALOG_H #include class EditGroupDialog : public wxDialog { wxString m_GroupName; wxString m_GroupDescription; void CreateControls(); public: EditGroupDialog(wxWindow * parent); bool Create(wxWindow * parent, wxWindowID id, const wxString title); const wxString & GetGroupName(); const wxString & GetGroupDescription(); }; #endif ... void EditGroupDialog::CreateControls() { wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL); SetSizer(sizer); wxStaticText * nameLabel = new wxStaticText(this, wxID_ANY, _("Name:")); wxStaticText * descriptionLabel = new wxStaticText(this, wxID_ANY, _("Description:")); wxTextCtrl * nameEdit = new wxTextCtrl(this, ID_EGD_NAME_TEXTCTRL, wxEmptyString); wxTextCtrl * descriptionEdit = new wxTextCtrl(this, ID_EGD_DESCRIPTION_TEXTCTRL, wxEmptyString, wxDefaultPosition, wxSize(-1, 150), wxTE_MULTILINE); nameEdit->SetValidator( wxGenericValidator(&m_GroupName)); descriptionEdit->SetValidator( wxGenericValidator(&m_GroupDescription)); wxFlexGridSizer * fg_sizer = new wxFlexGridSizer(2, 2, 0, 0); fg_sizer->Add(nameLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5); fg_sizer->Add(nameEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5); fg_sizer->Add(descriptionLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5); fg_sizer->Add(descriptionEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5); fg_sizer->AddGrowableCol(1); sizer->Add(fg_sizer, 1, wxEXPAND|wxTOP|wxLEFT|wxRIGHT, 5); sizer->Add(CreateButtonSizer(wxID_OK|wxID_CANCEL), 0, wxALIGN_RIGHT|wxALL, 5); } ... #ifndef _EDIT_PERSON_DIALOG_H #define _EDIT_PERSON_DIALOG_H #include class EditPersonDialog : public wxDialog { wxString m_FirstName; wxString m_LastName; wxString m_Address; wxString m_City; wxString m_Country; wxString m_Email; wxString m_Phone; void CreateControls(); public: EditPersonDialog(wxWindow * parent); bool Create(wxWindow * parent, wxWindowID id, const wxString title); const wxString & GetFirstName(); const wxString & GetLastName(); const wxString & GetAddress(); const wxString & GetCity(); const wxString & GetCountry(); const wxString & GetEmail(); const wxString & GetPhone(); }; #endif ... void EditPersonDialog::CreateControls() { wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL); SetSizer(sizer); wxStaticText * firstnameLabel = new wxStaticText(this, wxID_ANY, _("First Name:")); wxStaticText * lastnameLabel = new wxStaticText(this, wxID_ANY, _("Last Name:")); ... wxTextCtrl * firstnameEdit = new wxTextCtrl(this, ID_EPD_FIRSTNAME_TEXTCTRL, wxEmptyString); wxTextCtrl * lastnameEdit = new wxTextCtrl(this, ID_EPD_LASTNAME_TEXTCTRL, wxEmptyString); ... firstnameEdit->SetMinSize(wxSize(150,-1)); firstnameEdit->SetValidator( wxGenericValidator(&m_FirstName)); lastnameEdit->SetValidator( wxGenericValidator(&m_LastName)); ... wxFlexGridSizer * fg_sizer = new wxFlexGridSizer(2, 2, 0, 0); fg_sizer->Add(firstnameLabel, 0, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL|wxALL, 5); fg_sizer->Add(firstnameEdit, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL|wxALL, 5); ... fg_sizer->AddGrowableCol(1); sizer->Add(fg_sizer, 1, wxEXPAND|wxTOP|wxLEFT|wxRIGHT, 5); sizer->Add(CreateButtonSizer(wxID_OK|wxID_CANCEL), 0, wxALIGN_RIGHT|wxALL, 5); sizer->Fit(this); } ... ... void SQLiteTestMainFrame::OnAddGroup(wxCommandEvent & event) { EditGroupDialog * dlg = new EditGroupDialog(this); if(dlg->ShowModal() == wxID_OK) { GroupRow * newGroup = wxGetApp().GetGroupTable()->New(); newGroup->name = dlg->GetGroupName(); newGroup->description = dlg->GetGroupDescription(); newGroup->Save(); FillGroupsList(); } dlg->Destroy(); } void SQLiteTestMainFrame:: OnRemoveGroup(wxCommandEvent & event) { int selection = m_GroupsListBox->GetSelection(); RecordIDClientData * data = (RecordIDClientData *) m_GroupsListBox->GetClientObject(selection); if(data) { GroupRow * thisGroup = wxGetApp().GetGroupTable()->Id(data->GetID()); if(thisGroup && (wxMessageBox( _("Do you really want to delete this group?"), _("Delete group"), wxYES_NO) == wxYES)) { PersonRowSet * thisPersons = thisGroup->GetPersons(); for(unsigned long i = 0; i < thisPersons->Count(); ++i) { thisPersons->Item(i)->Delete(); } thisGroup->Delete(); m_GroupsListBox->Delete(selection); if(m_GroupsListBox->GetCount()) { m_GroupsListBox->SetSelection(selection < m_GroupsListBox->GetCount() ? selection : 0); data = (RecordIDClientData *) m_GroupsListBox->GetClientObject( m_GroupsListBox->GetSelection()); if(data) { FillPersonsList(data->GetID()); } } } } } void SQLiteTestMainFrame:: OnAddPerson(wxCommandEvent & event) { RecordIDClientData * data = (RecordIDClientData *) m_GroupsListBox->GetClientObject( m_GroupsListBox->GetSelection()); if(data) { GroupRow * thisGroup = wxGetApp().GetGroupTable()->Id(data->GetID()); if(thisGroup) { EditPersonDialog * dlg = new EditPersonDialog(this); if(dlg->ShowModal() == wxID_OK) { PersonRow * newPerson = wxGetApp().GetPersonTable()->New(); newPerson->groupid= thisGroup->id; newPerson->first_name = dlg->GetFirstName(); newPerson->last_name = dlg->GetLastName(); ... newPerson->Save(); FillPersonsList(thisGroup->id); } dlg->Destroy(); } } } void SQLiteTestMainFrame:: OnRemovePerson(wxCommandEvent & event) { long selection = m_PersonsListView->GetFirstSelected(); PersonRow * thisPerson = wxGetApp().GetPersonTable()->Id( (int)m_PersonsListView->GetItemData(selection)); if(thisPerson && (wxMessageBox( _("Do you really want to delete this record?"), _("Delete person"), wxYES_NO) == wxYES)) { int groupid = thisPerson->groupid; thisPerson->Delete(); m_PersonsListView->DeleteItem(selection); if(m_PersonsListView->GetItemCount()) { m_PersonsListView->Select(wxMin(selection, m_PersonsListView->GetItemCount()-1)); } } } void SQLiteTestMainFrame:: OnRemoveGroupUpdateUI(wxUpdateUIEvent & event) { event.Enable(m_GroupsListBox->GetSelection() >= 0); } void SQLiteTestMainFrame:: OnAddPersonUpdateUI(wxUpdateUIEvent & event) { event.Enable(m_GroupsListBox->GetSelection() >= 0); } void SQLiteTestMainFrame:: OnRemovePersonUpdateUI(wxUpdateUIEvent & event) { event.Enable(m_PersonsListView->GetFirstSelected() >= 0); } ... ./configure –enable-static make make instal `wx-config --cxxflags` {ProjDirPath}/art {ProjDirPath}/wxActiveRecord {ProjDirPath}/../databaselayer/include `wx-config --cxxflags` SqliteDatabaseLayer sqlite3 {ProjDirPath}/../databaselayer/Debug `wx-config --libs` -----------------------------------------------------------------------------------------------------------------