1 //////////////////////////////////////////////////////////////////////////
2 //
3 // pgAdmin III - PostgreSQL Tools
4 //
5 // Copyright (C) 2002 - 2016, The pgAdmin Development Team
6 // This software is released under the PostgreSQL Licence
7 //
8 // frmHint.cpp - PostgreSQL Guru hints
9 //
10 //////////////////////////////////////////////////////////////////////////
11 
12 
13 #include "pgAdmin3.h"
14 
15 // wxWindows headers
16 #include <wx/wx.h>
17 #include <wx/image.h>
18 #include <wx/file.h>
19 #include "wx/wxhtml.h"
20 #include <wx/tipdlg.h>
21 
22 // App headers
23 #include "copyright.h"
24 #include "ctl/ctlMenuToolbar.h"
25 #include "frm/frmHint.h"
26 #include "frm/frmMain.h"
27 #include "schema/pgObject.h"
28 #include "utils/misc.h"
29 #include "utils/utffile.h"
30 
31 // Icons
32 #include "images/hint.pngc"
33 #include "images/hint2.pngc"
34 
35 
36 
37 #define HINT_CANSUPPRESS 1
38 #define HINT_CANABORT    2
39 #define HINT_CANFIX      4
40 #define HINT_YESNO       8
41 
42 
43 struct make_dumb_compilers_happy        // unnamed structs don't make all compilers happy...
44 {
45 	const wxChar *hintPage;
46 	const wxChar *hintCaption;
47 	const wxChar *fixText;
48 	const wxChar *helpItem;
49 	int flags;
50 }
51 hintArray[] =
52 {
53 	{
54 		HINT_CONNECTSERVER,
55 		__("Server not listening"),
56 		0,
57 		wxT("pg/runtime-config#runtime-config-connection"),
58 		HINT_CANSUPPRESS
59 	},
60 	{
61 		HINT_MISSINGHBA,
62 		__("Server denies access"),
63 		0,
64 		wxT("pg/client-authentication#auth-pg-hba-conf"),
65 		HINT_CANSUPPRESS
66 	},
67 	{
68 		HINT_MISSINGIDENT,
69 		__("Ident authentication failed"),
70 		0,
71 		wxT("pg/auth-methods#auth-ident"),
72 		HINT_CANSUPPRESS
73 	},
74 	{
75 		HINT_PRIMARYKEY,
76 		__("Creation of primary key suggested"),
77 		0,
78 		wxT("pg/ddl-constraints"),
79 		HINT_CANSUPPRESS | HINT_CANABORT
80 	},
81 	{
82 		HINT_FKINDEX,
83 		__("Creation of index in referencing table suggested"),
84 		0,
85 		wxT("pg/ddl-constraints#ddl-constraints-fk"),
86 		HINT_CANSUPPRESS | HINT_CANABORT
87 	},
88 	{
89 		HINT_VACUUM,
90 		__("Running VACUUM recommended"),
91 		__("VACUUM"),
92 		wxT("pg/maintenance#routine-vacuuming"),
93 		HINT_CANSUPPRESS | HINT_CANFIX
94 	},
95 	{
96 		HINT_VACUUM_FULL,
97 		__("Running VACUUM FULL not recommended"),
98 		__("VACUUM FULL"),
99 		wxT("pg/sql-vacuum"),
100 		HINT_CANSUPPRESS | HINT_CANABORT
101 	},
102 	{
103 		HINT_QUERYRUNTIME,
104 		__("Query took a long time to complete"),
105 		0,
106 		wxT("query"),
107 		HINT_CANSUPPRESS | HINT_CANABORT | HINT_YESNO
108 	},
109 	{
110 		HINT_INSTRUMENTATION,
111 		__("Server instrumentation not installed"),
112 		0,
113 		wxEmptyString,
114 		HINT_CANSUPPRESS
115 	},
116 	{
117 		HINT_INSTRUMENTATION_91_WITH,
118 		__("Server instrumentation not installed"),
119 		0,
120 		wxEmptyString,
121 		HINT_CANSUPPRESS | HINT_CANFIX
122 	},
123 	{
124 		HINT_INSTRUMENTATION_91_WITHOUT,
125 		__("Server instrumentation not installed"),
126 		0,
127 		wxEmptyString,
128 		HINT_CANSUPPRESS
129 	},
130 	{
131 		HINT_ENCODING_UNICODE,
132 		__("Database encoding is Unicode"),
133 		0,
134 		wxT("pg/multibyte"),
135 		HINT_CANSUPPRESS
136 	},
137 	{
138 		HINT_ENCODING_ASCII,
139 		__("Database encoding is SQL_ASCII"),
140 		0,
141 		wxT("pg/multibyte"),
142 		HINT_CANSUPPRESS
143 	},
144 	{
145 		HINT_READONLY_NOPK,
146 		__("Can't edit tables without primary key"),
147 		0,
148 		wxT("editgrid"),
149 		HINT_CANSUPPRESS
150 	},
151 	{
152 		HINT_AUTOVACUUM,
153 		_("Enabling autovacuum recommended"),
154 		0,
155 		wxT("pg/maintenance#autovacuum"),
156 		HINT_CANSUPPRESS
157 	},
158 	{
159 		HINT_OBJECT_EDITING,
160 		_("Editing views, stored procedures or functions"),
161 		0,
162 		wxT("query"),
163 		HINT_CANSUPPRESS | HINT_CANABORT
164 	},
165 	{
166 		HINT_SAVING_PASSWORDS,
167 		_("Saving passwords"),
168 		0,
169 		wxT("pg/libpq-pgpass"),
170 		HINT_CANSUPPRESS | HINT_CANABORT
171 	},
172 	{ 0, 0, 0, 0 }
173 };
174 
175 
176 BEGIN_EVENT_TABLE(frmHint, DialogWithHelp)
177 	EVT_BUTTON(XRCID("btnFix"), frmHint::OnFix)
178 END_EVENT_TABLE();
179 
180 
181 #define chkSuppress     CTRL_CHECKBOX("chkSuppress")
182 #define htmlHint        (XRCCTRL(*this, "htmlHint", wxHtmlWindow))
183 #define btnFix          CTRL_BUTTON("btnFix")
184 #define btnYes          CTRL_BUTTON("wxID_YES")
185 #define btnNo           CTRL_BUTTON("wxID_NO")
186 #define btnHelp         CTRL_BUTTON("wxID_HELP")
187 
frmHint(wxWindow * fr,bool _force)188 frmHint::frmHint(wxWindow *fr, bool _force) : DialogWithHelp(0)
189 {
190 	force = _force;
191 	SetFont(settings->GetSystemFont());
192 	LoadResource(fr, wxT("frmHint"));
193 	RestorePosition();
194 
195 	if (force)
196 		btnCancel->Disable();
197 
198 	SetIcon(*hint_png_ico);
199 }
200 
201 
202 
GetPage(const wxChar * hintPage)203 wxString frmHint::GetPage(const wxChar *hintPage)
204 {
205 	wxString page;
206 
207 	wxString cn = settings->GetCanonicalLanguageName();
208 	if (cn.IsEmpty())
209 		cn = wxT("en_US");
210 
211 	wxString filename = docPath + wxT("/") + cn + wxT("/hints/") + hintPage + wxT(".html");
212 
213 	if (!wxFile::Exists(filename))
214 		filename = docPath + wxT("/en_US/hints/") + hintPage + wxT(".html");
215 	if (wxFile::Exists(filename))
216 	{
217 		wxUtfFile file(filename, wxFile::read, wxFONTENCODING_UTF8);
218 		file.Read(page);
219 	}
220 
221 	return page;
222 }
223 
224 
SetHint(int hintno,const wxString & info)225 void frmHint::SetHint(int hintno, const wxString &info)
226 {
227 	hintnos.Add(hintno);
228 	SetHint(info);
229 };
230 
231 
SetHint(const wxArrayInt & _hintnos,const wxString & info)232 void frmHint::SetHint(const wxArrayInt &_hintnos, const wxString &info)
233 {
234 	hintnos = _hintnos;
235 	SetHint(info);
236 }
237 
238 
SetHint(const wxString & info)239 void frmHint::SetHint(const wxString &info)
240 {
241 	currentHint = hintnos.Item(0);
242 
243 	bool canSuppress = false;
244 	if (hintnos.GetCount() == 1)
245 	{
246 		if (hintArray[currentHint].flags & HINT_CANSUPPRESS)
247 			canSuppress = true;
248 		SetTitle(_("Guru Hint") + wxString(wxT(" - ")) + wxGetTranslation(hintArray[currentHint].hintCaption));
249 
250 		wxString page = GetPage(hintArray[currentHint].hintPage);
251 		page.Replace(wxT("<INFO>"), HtmlEntities(info));
252 
253 		htmlHint->SetPage(page);
254 	}
255 	else
256 	{
257 		SetTitle(_("Guru Hints"));
258 
259 		wxString header = GetPage(wxT("multiple"));
260 		wxString pages;
261 
262 		if (header.IsEmpty())
263 		{
264 			header =
265 			    wxT("<html><head>\n")
266 			    wxT("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=windows-1252\">\n")
267 			    wxT("<link rel=\"STYLESHEET\" type=\"text/css\" href=\"../pgadmin3.css\">\n")
268 			    wxT("<title>Guru Hints</title></head>\n")
269 			    wxT("<body><p>\n");
270 		}
271 		else
272 		{
273 			int o = header.Find(wxT("</body>"));
274 			if (o > 0)
275 				header = header.Left(o) + wxT("<p>");
276 		}
277 
278 		size_t i;
279 		for (i = 0 ; i < hintnos.GetCount() ; i++)
280 		{
281 			int hintno = hintnos.Item(i);
282 			if (hintArray[hintno].flags & HINT_CANSUPPRESS)
283 				canSuppress = true;
284 			wxString page = GetPage(hintArray[hintno].hintPage);
285 			int a = page.Find(wxT("<body>"));
286 			int o = page.Find(wxT("</body>"));
287 			if (a < 0)
288 				a = 0;
289 			if (o < 0)
290 				o = wxString::npos;
291 
292 
293 			int ha = page.Find(wxT("<H3>"));
294 			int ho = page.Find(wxT("</H3>"));
295 			if (ha < 0)
296 				ha = page.Find(wxT("<h3>"));
297 			if (ho < 0)
298 				ho = page.Find(wxT("</h3>"));
299 
300 			if (ha > a && ho > ha)
301 			{
302 				wxString hintTitle = page.Mid(ha + 4, ho - ha - 4);
303 
304 				pages  += page.Mid(a, ha - a)
305 				          + wxT("<H3><A Name=\"") + hintArray[hintno].hintPage + wxT("\">")
306 				          + hintTitle + wxT("</A>")
307 				          + page.Mid(ho, o - ho);
308 				header += wxString(wxT("<A HREF=\"#")) + hintArray[hintno].hintPage + wxT("\">")
309 				          + hintTitle + wxT("</A><BR>");
310 			}
311 			else
312 				pages += page.Mid(a, o - a);
313 		}
314 
315 		pages.Replace(wxT("<INFO>"), info);
316 		pages.Replace(wxT("<INFO/>"), info);
317 
318 		htmlHint->SetPage(header + wxT("</p>") + pages + wxT("</body></html>\n"));
319 	}
320 
321 	chkSuppress->SetValue(false);
322 	chkSuppress->Enable(!force && canSuppress);
323 
324 	if (force || !(hintArray[currentHint].flags & HINT_CANABORT))
325 		btnCancel->Disable();
326 	if (!(hintArray[currentHint].flags & HINT_CANFIX))
327 		btnFix->Hide();
328 
329 	if (!(hintArray[currentHint].flags & HINT_CANFIX))
330 		btnFix->Hide();
331 	else if (hintArray[currentHint].fixText)
332 		btnFix->SetLabel(wxGetTranslation(hintArray[currentHint].fixText));
333 
334 	if (hintArray[currentHint].flags & HINT_YESNO)
335 		btnOK->Hide();
336 	else
337 	{
338 		btnYes->Hide();
339 		btnNo->Hide();
340 	}
341 
342 	if (hintArray[currentHint].helpItem == wxEmptyString)
343 		btnHelp->Disable();
344 	else
345 		btnHelp->Enable();
346 };
347 
348 
~frmHint()349 frmHint::~frmHint()
350 {
351 	if (!force && chkSuppress->GetValue())
352 	{
353 		size_t i;
354 		for (i = 0 ; i < hintnos.GetCount() ; i++)
355 		{
356 			int hintno = hintnos.Item(i);
357 			if (hintArray[hintno].flags & HINT_CANSUPPRESS)
358 				settings->Write(wxString(wxT("Hints/")) + hintArray[hintno].hintPage, wxT("Suppress"));
359 		}
360 	}
361 	SavePosition();
362 }
363 
364 
GetHelpPage() const365 wxString frmHint::GetHelpPage() const
366 {
367 	const wxChar *helpItem = hintArray[currentHint].helpItem;
368 	if (helpItem)
369 		return helpItem;
370 
371 	return wxT("guruhints");
372 }
373 
374 
ResetHints()375 void frmHint::ResetHints()
376 {
377 	int hintno = 0;
378 	while (hintArray[hintno].hintPage)
379 	{
380 		settings->Write(wxString(wxT("Hints/")) + hintArray[hintno].hintPage, wxString(wxEmptyString));
381 		hintno++;
382 	}
383 }
384 
385 
GetHintNo(const wxString & hint)386 int frmHint::GetHintNo(const wxString &hint)
387 {
388 	int hintno = 0;
389 	while (hintArray[hintno].hintPage)
390 	{
391 		if (hintArray[hintno].hintPage == hint)
392 			return hintno;
393 		hintno++;
394 	}
395 	return -1;
396 }
397 
398 
WantHint(const wxString & hint)399 bool frmHint::WantHint(const wxString &hint)
400 {
401 	int hintno = GetHintNo(hint);
402 	if (hintno < 0)
403 		return false;
404 
405 	return WantHint(hintno);
406 }
407 
408 
WantHint(int hintno)409 bool frmHint::WantHint(int hintno)
410 {
411 	if (hintno < 0)
412 		return false;
413 
414 	if ((hintArray[hintno].flags & HINT_CANSUPPRESS) && settings->GetSuppressGuruHints())
415 		return false;
416 
417 	if (settings->Read(wxString(wxT("Hints/")) + hintArray[hintno].hintPage, wxEmptyString) != wxT("Suppress"))
418 		return true;
419 
420 	return false;
421 }
422 
423 
424 
ShowHint(wxWindow * fr,const wxArrayString & hints,const wxString & info,bool force)425 int frmHint::ShowHint(wxWindow *fr, const wxArrayString &hints, const wxString &info, bool force)
426 {
427 	wxArrayInt hintnos;
428 	size_t i;
429 
430 	if(!hints.GetCount())
431 		return wxID_OK;
432 	for (i = 0 ; i < hints.GetCount() ; i++)
433 	{
434 		int hintNo = GetHintNo(hints.Item(i));
435 		if (hintNo >= 0 && (force || WantHint(hintNo)))
436 			hintnos.Add(hintNo);
437 	}
438 
439 	if (!hintnos.GetCount())
440 		return wxID_OK;
441 
442 	frmHint *frm = new frmHint(fr, force);
443 	frm->SetHint(hintnos, info);
444 
445 	frm->CenterOnParent();
446 	int rc = frm->ShowModal();
447 	delete frm;
448 
449 	if ((rc == wxID_CANCEL || rc == -1))
450 		rc = wxID_OK;
451 
452 	return rc;
453 }
454 
455 
ShowHint(wxWindow * fr,const wxString & hint,const wxString & info,bool force)456 int frmHint::ShowHint(wxWindow *fr, const wxString &hint, const wxString &info, bool force)
457 {
458 	int rc = wxID_OK;
459 	int hintno = GetHintNo(hint);
460 
461 	if (WantHint(hintno))
462 	{
463 		frmHint *frm = new frmHint(fr, force);
464 		frm->SetHint(hintno, info);
465 
466 		frm->CenterOnParent();
467 		rc = frm->ShowModal();
468 		delete frm;
469 
470 		if ((rc == wxID_CANCEL || rc == -1) && !(hintArray[hintno].flags & HINT_CANABORT))
471 			rc = wxID_OK;
472 	}
473 	return rc;
474 }
475 
476 
OnFix(wxCommandEvent & ev)477 void frmHint::OnFix(wxCommandEvent &ev)
478 {
479 	EndModal(HINT_RC_FIX);
480 }
481 
482 
483 
hintFactory(menuFactoryList * list,wxMenu * mnu,ctlMenuToolbar * toolbar,bool bigTool)484 hintFactory::hintFactory(menuFactoryList *list, wxMenu *mnu, ctlMenuToolbar *toolbar, bool bigTool) : actionFactory(list)
485 {
486 	mnu->Append(id, _("Hints"), _("Display helpful hints on current object."));
487 	if (toolbar)
488 	{
489 		const wxBitmap &bmp = (bigTool ? *hint_png_bmp : *hint2_png_bmp);
490 		toolbar->AddTool(id, wxEmptyString, bmp, _("Display helpful hints on current object."));
491 	}
492 }
493 
494 
StartDialog(frmMain * form,pgObject * obj)495 wxWindow *hintFactory::StartDialog(frmMain *form, pgObject *obj)
496 {
497 	if (obj)
498 		obj->ShowHint(form, true);
499 	else
500 		wxLogMessage(_("There are no hints available for the current object."));
501 
502 	return 0;
503 }
504 
505 
CheckEnable(pgObject * obj)506 bool hintFactory::CheckEnable(pgObject *obj)
507 {
508 	return obj && obj->GetCanHint();
509 }
510 
511