View Issue Details

IDProjectCategoryView StatusLast Update
0011818ScribusUser Interfacepublic2015-12-04 10:33
ReporterJLuc Assigned To 
PrioritynormalSeverityminorReproducibilityhave not tried
Status newResolutionopen 
Product Version1.5.0svn 
Summary0011818: [PATCH] No page range check in PDF export dialog
DescriptionIn the PDF export dialog, when the "pages" input specifies a too large page number or an interval out of the range of the existing pages, then no warning is issued and a PDF including only the last page of the PDF is created.

For ex, in a 10 page document, input "20-30" will lead to a one page doc with the 10th page.

Scribus should detect that and issue an error.
Additional InformationIt's easy to do such a mistake when a page number offset has been specified in the document section, because the screen shows biger page number than there are pages actualy in the document. In such a case, its difficult to understand why there is only one page in the PDF.
Tags#easyhack
PatchYes

Relationships

related to 0013225 closedjghali "Replace All" in Search/Replace dialog freezes Scribus in determined condition 
related to 0012550 new Display printed page number too wherever internal page number is displayed 
related to 0013451 closedjghali Invalid PDF when specifying a non-existing page to export 

Activities

JLuc

2014-01-10 17:46

developer   ~0031020

There is no warning neither when the page range is totally invalid, like 'sdfsfsd' : it creates an empty PDF.

ale

2014-01-22 05:57

manager   ~0031171

imo, no warning needed on overflow: just do something meaningful (like printing up to the first/last page).

for invalid input: do nothing (with warning) or print all (no warning?)

vajra

2014-01-22 15:42

reporter   ~0031172

Last edited: 2014-01-22 15:45

I have made changes in two files:
1. /scribus/ui/pdfopts.cpp
2. /scribus/ui/pdfopts.h

Acceptable inputs are:(as scribus says when hovering on text input box)
Insert a comma separated list of tokens where a token can be * for all the pages, 1-5 for a range of pages or a single page number.

Invalid inputs are:
1. single page number out of range
2. range of pages out of range
3. string containing characters other than white space, tab, numeric characters, '-','.'.
4. string containing characters white space, tab, numeric characters, '-','.' but in unacceptable form

On invalid input warning message box will pop up saying: "invalid pagerangestring". user needs to press "OK" pushbutton in order to proceed further

JLuc

2014-01-23 00:08

developer   ~0031176

I tested the patch and it works as said.

JLuc

2014-01-27 19:00

developer   ~0031210

I found a little bug : the test also triggers an error in case "Export all pages" is checked. In such a case, the test should not be run at all.

+ also please correct your patch so as to follow scribus coding standards : http://wiki.scribus.net/canvas/Official:Coding_Standards These are readability rules. Mostly, you ought to use spaces everywhere required, and linebreaks before opening braces... Thanks !

vajra

2014-01-27 19:49

reporter  

fix.diff (6,718 bytes)   
Index: scribus/ui/pdfopts.cpp
===================================================================
--- scribus/ui/pdfopts.cpp	(revision 18743)
+++ scribus/ui/pdfopts.cpp	(working copy)
@@ -151,6 +151,24 @@
 
 void PDFExportDialog::DoExport()
 {
+    //starts here: checking validity of pagerangestring
+    if(Options->AllPages->isChecked()==0)
+    {
+    	QString pageRangeStringtoCheck=Options->PageNr->text();
+	  
+	    bool invalidPageRangeString=checkPageRangeStringValidity(pageRangeStringtoCheck,m_doc->Pages->count());
+	    if (invalidPageRangeString )//showing warning box if invalid pagerangestring
+	    {
+
+	        QMessageBox::warning(this,
+	                             CommonStrings::trWarning,
+	                             tr("Invalid pagerangeString: \n"),
+	                             CommonStrings::tr_OK);
+	        return;
+	    }
+	}
+    //ends here: checking validity of pagerangestring
+
 	bool createPath = false;
 	QString fn = QDir::fromNativeSeparators(fileNameLineEdit->text());
 	// Checking if the path exists
@@ -198,13 +216,149 @@
 		m_presEffects[Options->PgSel].Di = Options->EDirection_2_2->currentIndex();
 		m_opts.LPISettings[Options->SelLPIcolor].Frequency = Options->LPIfreq->value();
 		m_opts.LPISettings[Options->SelLPIcolor].Angle = Options->LPIangle->value();
-		m_opts.LPISettings[Options->SelLPIcolor].SpotFunc = Options->LPIfunc->currentIndex();
-		accept();
+        m_opts.LPISettings[Options->SelLPIcolor].SpotFunc = Options->LPIfunc->currentIndex();
+        accept();
 	}
-	else
+	else{
 		return;
+	}
 }
+bool PDFExportDialog::checkPageRangeStringValidity(QString pagerangestringtocheck,int sourcePageCount ){
+    QString tmp(pagerangestringtocheck);
+        QString token;
+        int from, to, pageNr;
+        do
+        {
 
+            if (tmp.indexOf(",") == -1)
+            {
+
+                token = tmp;
+                tmp = "";
+            }
+            else
+            {
+                token = tmp.left(tmp.indexOf(","));
+                tmp = tmp.right(tmp.length() - tmp.indexOf(",") - 1);
+            }
+
+            token = token.trimmed();
+            //start:
+            if(token.isEmpty()){
+                //error
+                return true;//for null string
+            }
+            //end:
+            if (token == "*") // Import all source doc pages
+            {
+
+            }
+            else if (token.indexOf("-") != -1) // import a range of source doc pages
+            {
+                //start:
+                //here strings 'from' and 'to' can only contain numeric characters and single dot
+                //also they must be in rage 1 to max doc pages
+                QString fromstring=QString(token.left(token.indexOf("-")));
+                fromstring=fromstring.trimmed();
+                QString tostring=QString(token.right(token.length() - token.indexOf("-") - 1));
+                tostring=tostring.trimmed();
+                int k;
+                int dotcount=0;
+                if(fromstring.isEmpty()){
+                    //error
+                    return true;
+                }
+                for(k=0;k<fromstring.size();k++){
+                   if(fromstring.at(k)=='.'){
+                       dotcount++;
+                   }
+                   else if((48<=fromstring.at(k))&&(fromstring.at(k)<=57)){
+                       //fine
+
+                   }
+                   else
+                       return true;//error
+
+                }
+                if(dotcount>1){
+                    return true;//error
+                }
+                dotcount=0;
+                if(tostring.isEmpty()){
+                    //error
+                    return true;
+                }
+                for(k=0;k<tostring.size();k++){
+                    if(tostring.at(k)=='.'){
+                        dotcount++;
+                    }
+                    else if((48<=tostring.at(k))&&(tostring.at(k)<=57)){
+                        //fine
+
+                    }
+                    else
+                        return true;//error
+
+                }
+                if(dotcount>1){
+                    return true;//error
+                }
+                //end:
+                from = QString(token.left(token.indexOf("-"))).toInt();
+                to = QString(token.right(token.length() - token.indexOf("-") - 1)).toInt();
+                //also they must be in rage 1 to max doc pages
+                //start : comparing with pages doc contain
+                if((from<=sourcePageCount)&&(1<=from)){
+
+                    //input page number is within range
+                }
+                else{
+
+                    return true; //out of range option
+                }
+                if((to<=sourcePageCount)&&(1<=to)){
+
+                    //input page number is within range
+                }
+                else{
+
+                    return true; //out of range option
+                }
+                //end : comparing with pages doc contain
+            }
+            else // import single source doc page
+            {
+                //start:
+                QString pageNrString=token;
+                int k;
+                int dotcount=0;
+                for(k=0;k<pageNrString.size();k++){
+                   if(pageNrString.at(k)=='.'){
+                       dotcount++;
+                   }
+                   else if((48<=pageNrString.at(k))&&(pageNrString.at(k)<=57)){
+                       //fine
+
+                   }
+                   else
+                       return true;//error
+
+                }
+                if(dotcount>1){
+                    return true;//error
+                }
+                //end:
+                pageNr = token.toInt();
+                if ((pageNr > 0) && (pageNr <= sourcePageCount)){
+
+                }
+                else{
+                    return true;//error
+                }
+            }
+        } while (!tmp.isEmpty());
+    return false;
+}
 void PDFExportDialog::ChangeFile()
 {
 	QString fn;
@@ -231,6 +385,8 @@
 	fileNameLineEdit->setText( QDir::toNativeSeparators(fileName) );
 }
 
+
+
 void PDFExportDialog::updateDocOptions()
 {
 	m_opts.fileName = QDir::fromNativeSeparators(fileNameLineEdit->text());
Index: scribus/ui/pdfopts.h
===================================================================
--- scribus/ui/pdfopts.h	(revision 18743)
+++ scribus/ui/pdfopts.h	(working copy)
@@ -60,6 +60,7 @@
 	void fileNameChanged();
 	void enableSave();
 	void disableSave();
+    bool checkPageRangeStringValidity(QString pagerangestringtocheck,int sourcePageCount);
 
 protected:
 	// Widgets
fix.diff (6,718 bytes)   

vajra

2014-01-27 19:54

reporter   ~0031211

Last edited: 2014-01-27 19:55

Now the test do not get triggered in case "Export all pages" is checked.

JLuc

2014-01-27 20:19

developer   ~0031212

Thanks for the fix vajra.

Note to whom it might concern : String analysis code is adapted from the parsePagesString(QString pages, std::vector<int>* pageNs, int sourcePageCount) function code in util.cpp - I wonder if some core parsing function could not be called instead by both of these functions.

vajra

2014-01-29 19:54

reporter   ~0031218

I think parsePagesString(QString pages, std::vector<int>* pageNs, int sourcePageCount) function in util.cpp is core parsing function.
It requires similar modifications like I made.
But in that case it's behavior will need to be tested in all import and export cases.

JLuc

2014-01-29 20:18

developer   ~0031219

My feeling is that i's possible to change code so that
- util.cpp:ParsePageString returns exactly the same returns as now (its called in various part of code)
- your checking-correctness function does the same as now too,
- none of them duplicates the parsing code inline
- they both call a common PageStringGeneralParser function to do the parsing part

That new function could probably either an array of pages numbers, when ok, or a detailed error string in case of an error.

Does a core dev feel this is relevant / or irrelevant ?

cbradney

2014-01-29 23:02

administrator   ~0031220

I've let jluc know my thoughts here.. so lets see v2 of the patch once thats communicated.

JLuc

2014-01-30 10:19

developer   ~0031225

Last edited: 2014-01-30 13:31

Hello Vajra
We have talked about your patch, and here are the outcomes of this dicussion, as i can write them down.

- instead of using constants like 48 or 57 you should use more semantic code : calling a function to get ascii code of '0' and '9'.

- we have discovered you do a special case for dots. We thought it might be related to accepting 3..10 pages. But I tested this morning and it doesnt seem to work. Why did you introduce that special dot case ?
I think MrB liked that '..' idea quite but as for me i think its better and simpler not to introduce a new special syntax.

- the util.cpp:ParsePageString and validity check function should accept and understand the sames strings. It doesnt anymore, now that you have introduced that dot case : this is a very example of why duplicating code should be avoided.

So as to avoid duplicating code, here is what we thought would be best :

As for now, util.cpp:ParsePageString is a procedure : it doesnt return anything as a function.
1- Include validity checks code INTO that method,
2- Change it into a boolean function that return true/false depending on the validity check result.
3- Doing so, be carefull to not change the NPages * arg that is used by other calls of this method.
4- Use the newly returned result of this function in the PDF dialog validity check.

MrB gave a hint for the 2nd step : <<
from = QString(token.left(token.indexOf("-"))).toInt();
becomes
from = QString(token.left(token.indexOf("-"))).toInt(&ok);
where ok is a bool
If any of those return a false, then the function should bail out and return false
>>

We also discussed the display error way and thought of avoiding the popup message and testing 'live' as the user types a key in the pagerange input box. I allready told you of this ability (inspired by the Style Edit rename behaviour, in case user types an allready existing style name) but I feel this is not a priority as for now.

Other comment : <<good first attempt.. learning experience>> !

JLuc

2014-02-24 00:38

developer   ~0031378

I've uploaded a 'jl_pagerangecheck.diff' patch that implements the required checks.
It changes the function allready in util.cpp so as to use it : it now returns 2 error messages depending on the mistake, or none when everything is fine.

JLuc

2014-02-24 14:15

developer  

jl_pagerangecheck_2.diff (3,668 bytes)   
Index: scribus/util.cpp
===================================================================
--- scribus/util.cpp	(révision 18833)
+++ scribus/util.cpp	(copie de travail)
@@ -699,25 +699,29 @@
 }
 
 //CB Moved from scribus.cpp
-void parsePagesString(QString pages, std::vector<int>* pageNs, int sourcePageCount)
+const QString parsePagesString(QString pages, std::vector<int>* pageNs, int sourcePageCount)
 {
-	QString tmp(pages);
 	QString token;
+    const QString msgMax(QObject::tr("Page number is too high : there is only %1 page(s) in the document").arg(sourcePageCount));
+    const QString msgFormat(QObject::tr("Use the '...' button or select a set of page numbers.<br>For example : '1,3,5-8' will select pages 1, 3 and 5 to 8."));
+
 	int from, to, pageNr;
 	do
 	{
-		if (tmp.indexOf(",") == -1)
+		if (pages.indexOf(",") == -1)
 		{
-			token = tmp;
-			tmp = "";
+			token = pages;
+			pages = "";
 		}
 		else
 		{
-			token = tmp.left(tmp.indexOf(","));
-			tmp = tmp.right(tmp.length() - tmp.indexOf(",") - 1);
+			token = pages.left(pages.indexOf(","));
+			pages = pages.right(pages.length() - pages.indexOf(",") - 1);
 		}
 
 		token = token.trimmed();
+		pages = pages.trimmed();
+
 		if (token == "*") // Import all source doc pages
 		{
 			for (int i = 1; i <= sourcePageCount; ++i)
@@ -730,9 +734,9 @@
 			if ((from != 0) && (to != 0))
 			{
 				if (from > sourcePageCount)
-					from = sourcePageCount;
+					return msgMax;
 				if (to > sourcePageCount)
-					to = sourcePageCount;
+					return msgMax;
 				if (from == to)
 					pageNs->push_back(to);
 				else if (from < to)
@@ -746,14 +750,21 @@
 						pageNs->push_back(i);
 				}
 			}
+			else
+				return msgFormat;
 		}
 		else // import single source doc page
 		{
 			pageNr = token.toInt();
 			if ((pageNr > 0) && (pageNr <= sourcePageCount))
 				pageNs->push_back(pageNr);
+			else if (!pageNr)
+				return msgFormat;
+			else
+				return msgMax;
 		}
-	} while (!tmp.isEmpty());
+	} while (!pages.isEmpty());
+    return pageNs->empty() ? msgFormat : "";
 }
 
 void tDebug(QString message)
Index: scribus/ui/pdfopts.cpp
===================================================================
--- scribus/ui/pdfopts.cpp	(révision 18833)
+++ scribus/ui/pdfopts.cpp	(copie de travail)
@@ -151,6 +151,21 @@
 
 void PDFExportDialog::DoExport()
 {
+	if(Options->AllPages->isChecked() == 0)
+	{   // checking validity of pagerangestring
+		const QString pageRangeStringtoCheck=Options->PageNr->text();
+		std::vector<int> pageNs;
+		const QString ParseError = parsePagesString(pageRangeStringtoCheck, &pageNs, m_doc->Pages->count());
+		if (!ParseError.isEmpty()) //showing warning box if invalid pagerangestring
+		{
+			QMessageBox::warning(this,
+								 CommonStrings::trWarning,
+								 ParseError,
+								 CommonStrings::tr_OK);
+			return;
+		}
+	}
+
 	bool createPath = false;
 	QString fn = QDir::fromNativeSeparators(fileNameLineEdit->text());
 	// Checking if the path exists
Index: scribus/util.h
===================================================================
--- scribus/util.h	(révision 18833)
+++ scribus/util.h	(copie de travail)
@@ -157,7 +157,7 @@
 const QString SCRIBUS_API getStringFromSequence(NumFormat type, uint position, QString asterix="*");
 const QString SCRIBUS_API arabicToRoman(uint i);
 const QString SCRIBUS_API numberToLetterSequence(uint i);
-void SCRIBUS_API parsePagesString(QString pages, std::vector<int>* pageNs, int sourcePageCount);
+const QString SCRIBUS_API parsePagesString(QString pages, std::vector<int>* pageNs, int sourcePageCount);
 
 /*! \brief performance measurements.
 It prints given message with it current timestamp.
jl_pagerangecheck_2.diff (3,668 bytes)   

JLuc

2014-02-24 14:18

developer   ~0031379

I've uploaded new version of the patch.
It should not change anymore now.
Please have a look and merge when possible.

cbradney

2014-02-25 21:38

administrator   ~0031400

Not possible to merge patch for 3 reasons:
- the parsing code should be able to run without a GUI like it used to
- tmp was used as a modifiable string, pages shouldnt be modified
- the strings shouldnt be constant anyway as they will need to be modified by the languageChanged events

JLuc

2014-02-26 10:17

developer   ~0031406

Last edited: 2014-02-28 08:56

Some answers and questions :

-1 since the parse function is called in multiple places, these various places might potentially want to display error message too. That's why i included the error message computation into the parse function. In case the parse function doesnt compute the error message and returns a simple error code, then the message computation will have to be done in all places. We could then create a function or method for this. I will do that.

-2 Why should pages variable not be modified ? BTW it was not declared as const.

-3 The strings are const but not statics so their computation will lead to different results if language changes.

- As for the following code change :
if (from > sourcePageCount)
-					from = sourcePageCount;
+					return msgMax;

it will change the behaviour of the parse function in case it was called with too big pageNumbers. That is the desired behaviour for Pages > Import. And a first look at other calling point shows that this is also the desired behaviour for them (better warn of input mistake rather than treat "too big number" as "last page number" as if it was ok).

JLuc

2014-03-11 08:27

developer   ~0031496

The pagerange parsing is also used in various other places that should also call the check validity function
- PDF open dialog
- ...

JLuc

2014-07-01 19:28

developer   ~0032394

comment by mrb :
"we want to be able to parse page string data without the GUI.. ie, re-use the function elsewhere perhaps
 so it should return an error code rather than throwing up a message
 
let code in the GUI handle the error then that can throw up a message"

that is in scribus/ui folder

Kunda

2014-07-13 12:18

updater   ~0032714

modified title of issue to prominently indicate it is a patch

Kunda

2014-08-31 04:46

updater   ~0033386

Last edited: 2014-08-31 04:52

Vajra, any comment or follow-up on Jluc's comment 0011818:0032394 ? Thanks

JLuc

2014-09-01 22:25

updater   ~0033446

Kunda : Vajra is not there anymore. I created the last version of the patch and probably should bring the strings out to some separate file so as to fit scribus policy.

Kunda

2014-09-02 12:30

updater   ~0033456

Jluc, thanks for the update. Hopefully the changes you make are not too time-consuming. Thanks for stepping in :)

JLuc

2015-10-22 17:55

updater   ~0036839

Page range check addded for 0013451 see http://bugs.scribus.net/view.php?id=13451#c36838

Kunda

2015-10-22 18:05

updater   ~0036840

Jluc, did jean use your patch ?

JLuc

2015-10-22 19:08

updater   ~0036844

No. He used an allready existing function.
Feature is not same neither :
- "my" old patch checks input and triggers an error when there is an issue.
- Jean's patch deletes the not existing pages off the requsted list of pages, and only triggers an error when there is no page left at all after this.

Kunda

2015-10-25 13:08

updater   ~0036911

jluc, do you want to talk to jean about this to sort out differences and see if the rest of your patch can be integrated ?

JLuc

2015-10-25 13:33

updater   ~0036912

Jean used an allready function that does much of what my patch does. Rather then my long new code, it would probably be better to tweak that existing function so it returns an error code when it receives some not valid pages, beside returning the set of corrected pages.

Kunda

2015-11-27 14:14

updater   ~0037701

jluc, please advise about the future of this ticket

JLuc

2015-11-30 08:05

updater   ~0037735

Warning or error checking is still missing
- when user request can not be fullfilled as requested
= when scribus has to change the input so as to create some pdf
= when user inputs invalid pages

Best would be when this range check is done before submiting the dialog :
when input is checked each time a key is pressed, the submit button can be greyed out and inactive as long as there is an error (= as long as valid page range is different from input check range).

ale

2015-11-30 15:29

manager   ~0037740

i really don't understand why you want so many warnings.
only the case where scribus cannot fulfill the request should issue a warning.

if you type 1 to 99 and you have only three pages, there is no warning needed: just create a pdf with the three existing pages.

i would only issue a warning if no page can be printed.

please have a look to the way libreoffice is managing the printing.
it looks sane to me.

JLuc

2015-11-30 19:53

updater   ~0037743

"if you type 1 to 99 and you have only three pages," : that's what i call "not fullfilling the request". Same as when you ask for white wine and you got red wine : better to warn before opening the bottle.

Kunda

2015-11-30 21:20

updater   ~0037751

ale, can you illustrate what LO does that is more 'sane' ?

cbradney

2015-11-30 22:25

administrator   ~0037756

LO allows the max page to be above the number of pages in the doc, that makes no sense at all

ale

2015-12-01 12:12

manager   ~0037772

afaik, allowing the page number to overflow is a standard behavior in many if not most programs.

if the pdf can be created: just do it.

out of range errors are in my experience very rare. what is more likely is that one writes a wrong but valid range (or list of pages). so why bother with the corner cases, if we cannot catch the main issues?

JLuc

2015-12-02 09:41

updater   ~0037783

Last edited: 2015-12-02 09:51

When using sections page offset, its very common to make such errors, and its important to be warned (related : 0012550 "Display printed page number too wherever internal page number is displayed").

For example, when writing on the 7th SLA file part of a 200 page book, pages number offset start at 120. All pages number in the layouted pages are displaed as 120, 121, 122... and it's a very likely error to use these page numbers to request some PDF, instead of using the 'internal page numbers'.

It's the same for smaller offsets or errors that lead to only parts of the requested pdf.

It should not be 'if the pdf can be created, do it', it should be 'when the requested pdf can be created, do it. When it's not possible to create the requested pdf, dont do it and warn'.

JLuc

2015-12-02 09:45

updater   ~0037784

Warning doesnt have to be a popup ; some event ONCHANGE of the pages input can test whether it leads to a valid or not empty result, and grey out inactive or re-activate the "do it" button (as has been done for the search dialog when requested values are not valid see 0013225).

ale

2015-12-04 10:33

manager   ~0037814

as said, it's much more likely that the page numbers you're entering are wrong but valid.

i give up.

ps.: your case about physical and logical page numbers is (as often) a real case, but in my eyes you're (as often) tunneling towards warning about it instead of looking for a solution to the issue.

Issue History

Date Modified Username Field Change
2013-10-28 16:55 JLuc New Issue
2014-01-10 17:46 JLuc Note Added: 0031020
2014-01-10 17:47 JLuc Tag Attached: easyhack
2014-01-22 05:57 ale Note Added: 0031171
2014-01-22 15:28 vajra File Added: fix.diff
2014-01-22 15:42 vajra Note Added: 0031172
2014-01-22 15:45 vajra Note Edited: 0031172
2014-01-22 16:21 vajra File Deleted: fix.diff
2014-01-22 16:21 vajra File Added: fix.diff
2014-01-23 00:08 JLuc Note Added: 0031176
2014-01-27 19:00 JLuc Note Added: 0031210
2014-01-27 19:48 vajra File Deleted: fix.diff
2014-01-27 19:49 vajra File Added: fix.diff
2014-01-27 19:54 vajra Note Added: 0031211
2014-01-27 19:55 vajra Note Edited: 0031211
2014-01-27 20:19 JLuc Note Added: 0031212
2014-01-29 19:54 vajra Note Added: 0031218
2014-01-29 20:18 JLuc Note Added: 0031219
2014-01-29 23:02 cbradney Note Added: 0031220
2014-01-30 10:19 JLuc Note Added: 0031225
2014-01-30 13:31 JLuc Note Edited: 0031225
2014-02-24 00:33 JLuc File Added: jl_pagerangecheck.diff
2014-02-24 00:38 JLuc Note Added: 0031378
2014-02-24 14:15 JLuc File Deleted: jl_pagerangecheck.diff
2014-02-24 14:15 JLuc File Added: jl_pagerangecheck_2.diff
2014-02-24 14:18 JLuc Note Added: 0031379
2014-02-25 21:38 cbradney Note Added: 0031400
2014-02-26 10:17 JLuc Note Added: 0031406
2014-02-26 10:18 JLuc Note Edited: 0031406
2014-02-28 08:56 JLuc Note Edited: 0031406
2014-03-11 08:27 JLuc Note Added: 0031496
2014-07-01 19:28 JLuc Note Added: 0032394
2014-07-13 12:18 Kunda Note Added: 0032714
2014-07-13 12:18 Kunda Status new => feedback
2014-07-13 12:18 Kunda Summary No page range check in PDF export dialog => [PATCH] No page range check in PDF export dialog
2014-08-31 04:46 Kunda Note Added: 0033386
2014-08-31 04:52 Kunda Note Edited: 0033386
2014-09-01 22:25 JLuc Note Added: 0033446
2014-09-01 22:25 JLuc Status feedback => new
2014-09-02 12:30 Kunda Note Added: 0033456
2014-10-15 17:07 Kunda Tag Renamed easyhack => #easyhack
2014-10-24 22:57 Kunda Patch => Yes
2015-10-22 15:59 JLuc Relationship added related to 0013451
2015-10-22 17:55 JLuc Note Added: 0036839
2015-10-22 18:05 Kunda Note Added: 0036840
2015-10-22 19:08 JLuc Note Added: 0036844
2015-10-25 13:08 Kunda Note Added: 0036911
2015-10-25 13:33 JLuc Note Added: 0036912
2015-11-27 14:14 Kunda Note Added: 0037701
2015-11-30 08:05 JLuc Note Added: 0037735
2015-11-30 15:29 ale Note Added: 0037740
2015-11-30 19:53 JLuc Note Added: 0037743
2015-11-30 21:20 Kunda Note Added: 0037751
2015-11-30 22:25 cbradney Note Added: 0037756
2015-12-01 12:12 ale Note Added: 0037772
2015-12-02 09:41 JLuc Note Added: 0037783
2015-12-02 09:45 JLuc Note Added: 0037784
2015-12-02 09:46 JLuc Relationship added related to 0013225
2015-12-02 09:47 JLuc Relationship added related to 0012550
2015-12-02 09:51 JLuc Note Edited: 0037783
2015-12-04 10:33 ale Note Added: 0037814