View Issue Details

IDProjectCategoryView StatusLast Update
0011207ScribusScripterpublic2019-12-08 21:24
ReporterQuLogic Assigned Tojghali  
PrioritynormalSeverityfeatureReproducibilityalways
Status closedResolutionfixed 
PlatformLinuxOSFedoraOS Version17
Product Version1.5.0svn 
Summary0011207: [PATCH] Scripter Python 3 compatibility
DescriptionCMake will happily allow building scribus against Python 3, even though the code doesn't work with it.

Using the useful Porting to Python 3 guide [1], I have written a preliminary patch that enables the Scripting plugin to compile against Python3. It's possible that some things could be simplified, as I wasn't sure what the minimum Python version Scribus is expected to support. I'm no C++ or Qt expert either, so it's possible that I did something wrong/inefficiently/unconventionally.

This patch is against 1.5.0svn, but mostly applies (with a bit of fuzz) to 1.4.2svn, except for objpdffile.cpp, which I guess must have changed a lot.

[1] http://python3porting.com/cextensions.html
Steps To Reproduce1. Install Python3 development libraries.
2. Run cmake: No errors
3. Build scribus: Fails compiling scriptplugin
Tagsdiscussion, patch
PatchYes

Activities

QuLogic

2012-11-24 07:51

reporter  

scribus-python3.diff (17,357 bytes)   
Index: scribus/plugins/scriptplugin/cmdgetsetprop.cpp
===================================================================
--- scribus/plugins/scriptplugin/cmdgetsetprop.cpp	(revision 17900)
+++ scribus/plugins/scriptplugin/cmdgetsetprop.cpp	(working copy)
@@ -18,13 +18,13 @@
 	if (PyString_Check(arg))
 		// It's a string. Look for a pageItem by that name. Do NOT accept a
 		// selection.
-		return getPageItemByName(QString::fromUtf8(PyString_AsString(arg)));
-	else if (PyCObject_Check(arg))
+		return getPageItemByName(PyString_AsQString(arg));
+	else if (PyCapsule_CheckExact(arg))
 	{
-		// It's a PyCObject, ie a wrapped pointer. Check it's not NULL
+		// It's a PyCapsule, ie a wrapped pointer. Check it's not NULL
 		// and return it.
 		// FIXME: Try to check that its a pointer to a QObject instance
-		QObject* tempObject = (QObject*)PyCObject_AsVoidPtr(arg);
+		QObject* tempObject = (QObject*)PyCapsule_GetPointer(arg, NULL);
 		if (!tempObject)
 		{
 			PyErr_SetString(PyExc_TypeError, "INTERNAL: Passed NULL PyCObject");
@@ -44,7 +44,7 @@
 
 PyObject* wrapQObject(QObject* obj)
 {
-	return PyCObject_FromVoidPtr((void*)obj, NULL);
+	return PyCapsule_New((void*)obj, NULL, NULL);
 }
 
 
@@ -414,7 +414,7 @@
 	{
 		matched = true;
 		if (PyString_Check(objValue))
-			success = obj->setProperty(propertyName, QString::fromUtf8(PyString_AsString(objValue)));
+			success = obj->setProperty(propertyName, PyString_AsQString(objValue));
 		else if (PyUnicode_Check(objValue))
 		{
 			// Get a pointer to the internal buffer of the Py_Unicode object, which is UCS2 formatted
@@ -432,7 +432,7 @@
 		{
 			// FIXME: should raise an exception instead of mangling the string when
 			// out of charset chars present.
-			QString utfString = QString::fromUtf8(PyString_AsString(objValue));
+			QString utfString = PyString_AsQString(objValue);
 			success = obj->setProperty(propertyName, utfString.toAscii());
 		}
 		else if (PyUnicode_Check(objValue))
@@ -465,7 +465,7 @@
 		if (!objRepr)
 			return NULL;
 		// Extract the repr() string
-		QString reprString = QString::fromUtf8(PyString_AsString(objRepr));
+		QString reprString = PyString_AsQString(objRepr);
 		Py_DECREF(objRepr);
 
 		// And return an error
Index: scribus/plugins/scriptplugin/cmdmani.cpp
===================================================================
--- scribus/plugins/scriptplugin/cmdmani.cpp	(revision 17900)
+++ scribus/plugins/scriptplugin/cmdmani.cpp	(working copy)
@@ -354,7 +354,6 @@
 
 PyObject *scribus_groupobj(PyObject* /* self */, PyObject* args)
 {
-	char *Name = const_cast<char*>("");
 	PyObject *il = 0;
 	if (!PyArg_ParseTuple(args, "|O", &il))
 		return NULL;
@@ -378,8 +377,7 @@
 			// FIXME: We might need to explicitly get this string as utf8
 			// but as sysdefaultencoding is utf8 it should be a no-op to do
 			// so anyway.
-			Name = PyString_AsString(PyList_GetItem(il, i));
-			PageItem *ic = GetUniqueItem(QString::fromUtf8(Name));
+			PageItem *ic = GetUniqueItem(PyString_AsQString(PyList_GetItem(il, i)));
 			if (ic == NULL)
 			{
 				delete tempSelection;
Index: scribus/plugins/scriptplugin/cmdvar.h
===================================================================
--- scribus/plugins/scriptplugin/cmdvar.h	(revision 17900)
+++ scribus/plugins/scriptplugin/cmdvar.h	(working copy)
@@ -36,7 +36,46 @@
 
 #include "scribus.h"
 
+#if PY_MAJOR_VERSION >= 3
+	#define PyString_Check PyUnicode_Check
+	#ifdef PyUnicode_GET_LENGTH
+		#define PyString_Size PyUnicode_GET_LENGTH
+	#else
+		#define PyString_Size PyUnicode_GET_SIZE
+	#endif
+	#define PyString_FromString PyUnicode_FromString
+	#define PyString_FromStringAndSize PyUnicode_FromStringAndSize
+	inline QString PyString_AsQString(PyObject *arg) {
+		PyObject *tmp = PyUnicode_AsUTF8String(arg);
+		QString qs = QString::fromUtf8(PyBytes_AsString(tmp));
+		Py_DECREF(tmp);
+		return qs;
+	}
+	#define PyInt_Check PyLong_Check
+	#define PyInt_FromLong PyLong_FromLong
+	#define PyInt_AsLong PyLong_AsLong
+	#if PY_MINOR_VERSION == 0
+		#define PyCapsule_Type PyCObject_Type
+		#define PyCapsule_CheckExact(capsule) PyCObject_Check(capsule)
+		#define PyCapsule_New(pointer, name, destructor) PyCObject_FromVoidPtr(pointer, destructor)
+		#define PyCapsule_GetPointer(capsule, name) PyCObject_AsVoidPtr(capsule)
+	#endif
+#else
+	#define PyString_AsQString(arg) QString::fromUtf8(PyString_AsString((arg)))
+	#if PY_MINOR_VERSION < 7
+		#define PyCapsule_Type PyCObject_Type
+		#define PyCapsule_CheckExact(capsule) PyCObject_Check(capsule)
+		#define PyCapsule_New(pointer, name, destructor) PyCObject_FromVoidPtr(pointer, destructor)
+		#define PyCapsule_GetPointer(capsule, name) PyCObject_AsVoidPtr(capsule)
 
+		#if PY_MINOR_VERSION <= 5
+			#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
+			#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+		#endif
+	#endif
+#endif
+
+
 class ScripterCore;
 
 // Globals for testing Qt properties and probably other more intresting future
Index: scribus/plugins/scriptplugin/objimageexport.cpp
===================================================================
--- scribus/plugins/scriptplugin/objimageexport.cpp	(revision 17900)
+++ scribus/plugins/scriptplugin/objimageexport.cpp	(working copy)
@@ -32,7 +32,7 @@
 	Py_XDECREF(self->name);
 	Py_XDECREF(self->type);
 	Py_XDECREF(self->allTypes);
-	self->ob_type->tp_free((PyObject *)self);
+	Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
 static PyObject * ImageExport_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/)
@@ -154,11 +154,22 @@
 	int dpi = qRound(100.0 / 2.54 * self->dpi);
 	im.setDotsPerMeterY(dpi);
 	im.setDotsPerMeterX(dpi);
+#if PY_MAJOR_VERSION >= 3
+	PyObject *type = PyUnicode_AsUTF8String(self->type);
+	if (!im.save(PyString_AsQString(self->name), PyBytes_AsString(type)))
+	{
+	        Py_DECREF(type);
+		PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData());
+		return NULL;
+	}
+        Py_DECREF(type);
+#else
 	if (!im.save(PyString_AsString(self->name), PyString_AsString(self->type)))
 	{
 		PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData());
 		return NULL;
 	}
+#endif
 // 	Py_INCREF(Py_True); // return True not None for backward compat
  //	return Py_True;
 //	Py_RETURN_TRUE;
@@ -185,11 +196,22 @@
 	int dpi = qRound(100.0 / 2.54 * self->dpi);
 	im.setDotsPerMeterY(dpi);
 	im.setDotsPerMeterX(dpi);
+#if PY_MAJOR_VERSION >= 3
+	PyObject *type = PyUnicode_AsUTF8String(self->type);
+	if (!im.save(value, PyBytes_AsString(type)))
+	{
+	        Py_DECREF(type);
+		PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData());
+		return NULL;
+	}
+        Py_DECREF(type);
+#else
 	if (!im.save(value, PyString_AsString(self->type)))
 	{
 		PyErr_SetString(ScribusException, QObject::tr("Failed to export image", "python error").toLocal8Bit().constData());
 		return NULL;
 	}
+#endif
 // 	Py_INCREF(Py_True); // return True not None for backward compat
  //	return Py_True;
 //	Py_RETURN_TRUE;
@@ -203,8 +225,7 @@
 };
 
 PyTypeObject ImageExport_Type = {
-	PyObject_HEAD_INIT(NULL)   // PyObject_VAR_HEAD
-	0,
+	PyVarObject_HEAD_INIT(NULL, 0)   // PyObject_VAR_HEAD
 	const_cast<char*>("ImageExport"), // char *tp_name; /* For printing, in format "<module>.<name>" */
 	sizeof(ImageExport),   // int tp_basicsize, /* For allocation */
 	0,  // int tp_itemsize; /* For allocation */
Index: scribus/plugins/scriptplugin/objpdffile.cpp
===================================================================
--- scribus/plugins/scriptplugin/objpdffile.cpp	(revision 17900)
+++ scribus/plugins/scriptplugin/objpdffile.cpp	(working copy)
@@ -111,7 +111,7 @@
 	Py_XDECREF(self->imagepr);
 	Py_XDECREF(self->printprofc);
 	Py_XDECREF(self->info);
-	self->ob_type->tp_free((PyObject *)self);
+	Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
 static PyObject * PDFfile_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/)
@@ -1010,12 +1010,12 @@
 	int n = PyList_Size(self->fonts);
 	for ( int i=0; i<n; ++i){
 		QString tmpFon;
-		tmpFon = QString(PyString_AsString(PyList_GetItem(self->fonts, i)));
+		tmpFon = PyString_AsQString(PyList_GetItem(self->fonts, i));
 		ScCore->primaryMainWindow()->doc->pdfOptions().EmbedList.append(tmpFon);
 	}
 // apply file attribute
 	QString fn;
-	fn = QString(PyString_AsString(self->file));
+	fn = PyString_AsQString(self->file);
 	ScCore->primaryMainWindow()->doc->pdfOptions().fileName = fn;
 // apply pages attribute
 	std::vector<int> pageNs;
@@ -1107,7 +1107,7 @@
 //		 }
 //		 ScCore->primaryMainWindow()->doc->pdfOptions().LPISettings[QString(s)]=lpi;
 		QString st;
-		st = QString(PyString_AsString(PyList_GetItem(t,0)));
+		st = PyString_AsQString(PyList_GetItem(t,0));
 		lpi.Frequency = PyInt_AsLong(PyList_GetItem(t, 1));
 		lpi.Angle = PyInt_AsLong(PyList_GetItem(t, 2));
 		lpi.SpotFunc = PyInt_AsLong(PyList_GetItem(t, 3));
@@ -1136,8 +1136,8 @@
 		if (self->aanot)
 			Perm += 32;
 		ScCore->primaryMainWindow()->doc->pdfOptions().Permissions = Perm;
-		ScCore->primaryMainWindow()->doc->pdfOptions().PassOwner = QString(PyString_AsString(self->owner));
-		ScCore->primaryMainWindow()->doc->pdfOptions().PassUser = QString(PyString_AsString(self->user));
+		ScCore->primaryMainWindow()->doc->pdfOptions().PassOwner = PyString_AsQString(self->owner);
+		ScCore->primaryMainWindow()->doc->pdfOptions().PassUser = PyString_AsQString(self->user);
 	}
 	if (self->outdst == 0)
 	{
@@ -1157,9 +1157,9 @@
 			self->intenti = minmaxi(self->intenti, 0, 3);
 			ScCore->primaryMainWindow()->doc->pdfOptions().Intent2 = self->intenti;
 			ScCore->primaryMainWindow()->doc->pdfOptions().EmbeddedI = self->noembicc;
-			ScCore->primaryMainWindow()->doc->pdfOptions().SolidProf = PyString_AsString(self->solidpr);
-			ScCore->primaryMainWindow()->doc->pdfOptions().ImageProf = PyString_AsString(self->imagepr);
-			ScCore->primaryMainWindow()->doc->pdfOptions().PrintProf = PyString_AsString(self->printprofc);
+			ScCore->primaryMainWindow()->doc->pdfOptions().SolidProf = PyString_AsQString(self->solidpr);
+			ScCore->primaryMainWindow()->doc->pdfOptions().ImageProf = PyString_AsQString(self->imagepr);
+			ScCore->primaryMainWindow()->doc->pdfOptions().PrintProf = PyString_AsQString(self->printprofc);
 			if (ScCore->primaryMainWindow()->doc->pdfOptions().Version == PDFOptions::PDFVersion_X3)
 			{
 				ScColorProfile profile;
@@ -1171,7 +1171,7 @@
 					Components = 4;
 				if (profile.colorSpace() == ColorSpace_Cmy)
 					Components = 3;
-				ScCore->primaryMainWindow()->doc->pdfOptions().Info = PyString_AsString(self->info);
+				ScCore->primaryMainWindow()->doc->pdfOptions().Info = PyString_AsQString(self->info);
 				self->bleedt = minmaxd(self->bleedt, 0, ScCore->primaryMainWindow()->view->Doc->pageHeight()*ScCore->primaryMainWindow()->view->Doc->unitRatio());
 				ScCore->primaryMainWindow()->doc->pdfOptions().bleeds.Top = self->bleedt/ScCore->primaryMainWindow()->view->Doc->unitRatio();
 				self->bleedl = minmaxd(self->bleedl, 0, ScCore->primaryMainWindow()->view->Doc->pageWidth()*ScCore->primaryMainWindow()->view->Doc->unitRatio());
@@ -1219,8 +1219,7 @@
 };
 
 PyTypeObject PDFfile_Type = {
-	PyObject_HEAD_INIT(NULL) // PyObject_VAR_HEAD
-	0,		      //
+	PyVarObject_HEAD_INIT(NULL, 0) // PyObject_VAR_HEAD
 	const_cast<char*>("PDFfile"), // char *tp_name; /* For printing, in format "<module>.<name>" */
 	sizeof(PDFfile),     // int tp_basicsize, /* For allocation */
 	0,		    // int tp_itemsize; /* For allocation */
Index: scribus/plugins/scriptplugin/objprinter.cpp
===================================================================
--- scribus/plugins/scriptplugin/objprinter.cpp	(revision 17900)
+++ scribus/plugins/scriptplugin/objprinter.cpp	(working copy)
@@ -62,7 +62,7 @@
 	Py_XDECREF(self->cmd);
 	Py_XDECREF(self->pages);
 	Py_XDECREF(self->separation);
-	self->ob_type->tp_free((PyObject *)self);
+	Py_TYPE(self)->tp_free((PyObject *)self);
 }
 
 static PyObject * Printer_new(PyTypeObject *type, PyObject * /*args*/, PyObject * /*kwds*/)
@@ -407,16 +407,16 @@
 	PSfile = false;
 
 //    ReOrderText(ScCore->primaryMainWindow()->doc, ScCore->primaryMainWindow()->view);
-	prn = QString(PyString_AsString(self->printer));
-	fna = QString(PyString_AsString(self->file));
-	fil = (QString(PyString_AsString(self->printer)) == QString("File")) ? true : false;
+	prn = PyString_AsQString(self->printer);
+	fna = PyString_AsQString(self->file);
+	fil = (PyString_AsQString(self->printer) == QString("File")) ? true : false;
 	std::vector<int> pageNs;
 	PrintOptions options;
 	for (int i = 0; i < PyList_Size(self->pages); ++i) {
 		options.pageNumbers.push_back((int)PyInt_AsLong(PyList_GetItem(self->pages, i)));
 	}
 	int Nr = (self->copies < 1) ? 1 : self->copies;
-	SepName = QString(PyString_AsString(self->separation));
+	SepName = PyString_AsQString(self->separation);
 	options.printer   = prn;
 	options.prnEngine = (PrintEngine) self->pslevel;
 	options.toFile    = fil;
@@ -438,7 +438,7 @@
 	options.bleeds.Bottom = 0.0;
 	if (!PrinterUtil::checkPrintEngineSupport(options.printer, options.prnEngine, options.toFile))
 		options.prnEngine = PrinterUtil::getDefaultPrintEngine(options.printer, options.toFile);
-	printcomm = QString(PyString_AsString(self->cmd));
+	printcomm = PyString_AsQString(self->cmd);
 	QMap<QString, QMap<uint, FPointArray> > ReallyUsed;
 	ReallyUsed.clear();
 	ScCore->primaryMainWindow()->doc->getUsedFonts(ReallyUsed);
@@ -527,8 +527,7 @@
 };
 
 PyTypeObject Printer_Type = {
-	PyObject_HEAD_INIT(NULL)   // PyObject_VAR_HEAD
-	0,			 //
+	PyVarObject_HEAD_INIT(NULL, 0)   // PyObject_VAR_HEAD
 	const_cast<char*>("Printer"), // char *tp_name; /* For printing, in format "<module>.<name>" */
 	sizeof(Printer),   // int tp_basicsize, /* For allocation */
 	0,		       // int tp_itemsize; /* For allocation */
Index: scribus/plugins/scriptplugin/scriptercore.cpp
===================================================================
--- scribus/plugins/scriptplugin/scriptercore.cpp	(revision 17900)
+++ scribus/plugins/scriptplugin/scriptercore.cpp	(working copy)
@@ -253,7 +253,6 @@
 
 	PyThreadState *state = NULL;
 	QFileInfo fi(fileName);
-	QByteArray na = fi.fileName().toLocal8Bit();
 	// Set up a sub-interpreter if needed:
 	PyThreadState* global_state = NULL;
 	if (!inMainInterpreter)
@@ -272,8 +271,21 @@
 		// Init the scripter module in the sub-interpreter
 		initscribus(ScCore->primaryMainWindow());
 	}
+#if PY_MAJOR_VERSION >= 3
 	// Make sure sys.argv[0] is the path to the script
+	wchar_t* comm[2];
+	std::wstring na = fi.fileName().toStdWString();
+	comm[0] = const_cast<wchar_t*>(na.c_str());
+	// and tell the script if it's running in the main intepreter or
+	// a subinterpreter using the second argument, ie sys.argv[1]
+	if (inMainInterpreter)
+		comm[1] = const_cast<wchar_t*>(L"ext");
+	else
+		comm[1] = const_cast<wchar_t*>(L"sub");
+#else
+	// Make sure sys.argv[0] is the path to the script
 	char* comm[2];
+	QByteArray na = fi.fileName().toLocal8Bit();
 	comm[0] = na.data();
 	// and tell the script if it's running in the main intepreter or
 	// a subinterpreter using the second argument, ie sys.argv[1]
@@ -281,6 +293,7 @@
 		comm[1] = const_cast<char*>("ext");
 	else
 		comm[1] = const_cast<char*>("sub");
+#endif
 	PySys_SetArgv(2, comm);
 	// call python script
 	PyObject* m = PyImport_AddModule((char*)"__main__");
@@ -344,7 +357,7 @@
 			}
 			else
 			{
-				QString errorMsg = PyString_AsString(errorMsgPyStr);
+				QString errorMsg = PyString_AsQString(errorMsgPyStr);
 				// Display a dialog to the user with the exception
 				QClipboard *cp = QApplication::clipboard();
 				cp->setText(errorMsg);
Index: scribus/plugins/scriptplugin/scriptplugin.cpp
===================================================================
--- scribus/plugins/scriptplugin/scriptplugin.cpp	(revision 17900)
+++ scribus/plugins/scriptplugin/scriptplugin.cpp	(working copy)
@@ -171,11 +171,13 @@
 	}
 #endif
 	Py_Initialize();
+#if PY_MAJOR_VERSION < 3
 	if (PyUnicode_SetDefaultEncoding("utf-8"))
 	{
 		qDebug("Failed to set default encoding to utf-8.\n");
 		PyErr_Clear();
 	}
+#endif
 
 	scripterCore = new ScripterCore(ScCore->primaryMainWindow());
 	Q_CHECK_PTR(scripterCore);
@@ -564,6 +566,21 @@
 	{NULL, (PyCFunction)(0), 0, NULL} /* sentinel */
 };
 
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef moduledef = {
+	PyModuleDef_HEAD_INIT,
+	"scribus",           /* m_name */
+	NULL,                /* m_doc */
+	-1,                  /* m_size */
+	scribus_methods,     /* m_methods */
+	NULL,                /* m_reload */
+	NULL,                /* m_traverse */
+	NULL,                /* m_clear */
+	NULL,                /* m_free */
+};
+#endif
+
+
 void initscribus_failed(const char* fileName, int lineNo)
 {
 	qDebug("Scripter setup failed (%s:%i)", fileName, lineNo);
@@ -585,7 +602,11 @@
 	PyType_Ready(&Printer_Type);
 	PyType_Ready(&PDFfile_Type);
 	PyType_Ready(&ImageExport_Type);
+#if PY_MAJOR_VERSION >= 3
+	m = PyModule_Create(&moduledef);
+#else
 	m = Py_InitModule((char*)"scribus", scribus_methods);
+#endif
 	Py_INCREF(&Printer_Type);
 	PyModule_AddObject(m, (char*)"Printer", (PyObject *) &Printer_Type);
 	Py_INCREF(&PDFfile_Type);
scribus-python3.diff (17,357 bytes)   

Kunda

2014-07-13 19:20

updater   ~0032725

Hi QuLogic, apologies for not knowing about this.
The Scripter code since you published this patch has changed. Would you be interested to help port it to the newest iteration of Scribus Scripter2 ?

ale

2014-07-14 14:11

manager   ~0032735

i'll be working on porting the new scripter to python 3 and pyqt5 in this repository:

https://github.com/aoloe/scribus-plugin-scripter

collaboration, hints, and co. are welcome :-)

QuLogic

2014-07-16 07:08

reporter   ~0032776

I can certainly look into it, though I will have to see about updating my build environment first.

Kunda

2014-07-16 16:21

updater   ~0032781

QuLogic, great to hear from you. Thank you for your contribution. Check-in with ale when you are ready. Cheers!

ale

2014-08-03 18:53

manager   ~0033162

This patch is for the old scripter.

Personally, I'm focused on getting the new scripter to work again...

But the patch might be helpful for finding out what has to be changed.

Kunda

2014-10-22 03:56

updater   ~0034118

bump

ale

2019-11-23 13:16

manager   ~0047120

i guess that this is fixed now

Issue History

Date Modified Username Field Change
2012-11-24 07:51 QuLogic New Issue
2012-11-24 07:51 QuLogic File Added: scribus-python3.diff
2012-11-24 07:54 QuLogic Tag Attached: patch
2014-07-13 19:20 Kunda Note Added: 0032725
2014-07-13 19:20 Kunda Status new => feedback
2014-07-14 01:29 Kunda Summary Python 3 compatibility => [PATCH] Scripter Python 3 compatibility
2014-07-14 14:11 ale Note Added: 0032735
2014-07-16 07:08 QuLogic Note Added: 0032776
2014-07-16 07:08 QuLogic Status feedback => new
2014-07-16 16:21 Kunda Note Added: 0032781
2014-07-16 16:37 Kunda Assigned To => ale
2014-07-16 16:37 Kunda Status new => assigned
2014-08-03 18:53 ale Note Added: 0033162
2014-10-22 03:56 Kunda Note Added: 0034118
2014-10-24 22:43 cbradney Patch => Yes
2016-05-16 22:23 Kunda Tag Attached: discussion
2019-11-23 13:16 ale Assigned To ale => jghali
2019-11-23 13:16 ale Status assigned => resolved
2019-11-23 13:16 ale Resolution open => fixed
2019-11-23 13:16 ale Note Added: 0047120
2019-12-08 21:24 cbradney Status resolved => closed