diff --git a/product/ERP5Form/Extensions/ERP5Site_createModuleScribus.py b/product/ERP5Form/Extensions/ERP5Site_createModuleScribus.py index 94f28a88b3ee0b5c161827a730b908db53abe002..233ed56b1fca4192dcc7a048e9dbbd9d6484be08 100755 --- a/product/ERP5Form/Extensions/ERP5Site_createModuleScribus.py +++ b/product/ERP5Form/Extensions/ERP5Site_createModuleScribus.py @@ -1,7 +1,11 @@ +# This code is to be removed in near future, because we will migrate +# to a Python Script. This is put temporarily for a demo. + def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, portal_skins_folder=None, object_portal_type=None, object_title=None, module_id=None, module_title=None, selection_index=None, selection_name=None, import_scribus_file=None, - import_pdf_file=None, **kw) : + import_pdf_file=None, option_html=None, import_image_1=None, import_image_2=None, + import_image_3=None, **kw) : """ Creates a module, portal_type and ERP5Form from a scribus and PDFForm file""" context = self @@ -13,6 +17,13 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, from Products.ERP5Form.CreatePropertySheet import LocalGenerator generator = LocalGenerator() + # importing module to get an access to the 'searchFolder' method + # needed to be able to list the objects in 'list_object_view' form + from Products.ERP5.ERP5Site import ERP5Site + + from zLOG import LOG + + #import Products.ERP5Form.Folder # TODO # - Allow in the module only the new document, we must activate the filter # - handle an optionnal "description" parameter @@ -27,7 +38,10 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, portal_types = portal.portal_types object_portal_type_id = object_portal_type + + # Create a new portal_type for the module + # portal_types.manage_addTypeInformation( 'ERP5 Type Information' , typeinfo_name = 'ERP5Type: ERP5 Folder' , id = module_portal_type @@ -38,15 +52,19 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, # Set allowed content types module_portal_type_value.allowed_content_types = (object_portal_type_id, ) module_portal_type_value.filter_content_types = 1 - + # building a list of all the portal_type actions action_list = module_portal_type_value.listActions() + # cleaning all portal_type informations module_portal_type_value.deleteActions(selections=range(0, len(action_list))) - + # declaring form names form_view_pdf = object_portal_type_id.replace(' ','') + '_view' +\ object_portal_type_id.replace(' ','') + 'AsPdf' form_view_list = object_title.replace(' ','') + 'Module_view' +\ object_portal_type_id.replace(' ','') + 'List' + # declaring css dtml name + form_css_id = object_portal_type_id.replace(' ','') + '.css' + # Parameters to addAction : id, name, action, condition, permission, # category, visible=1, REQUEST=None module_portal_type_value.addAction( "view" @@ -57,13 +75,13 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, , "object_view" ) + # SKIN PROCESSING # Create the skin directory if does not exist yet portal_skins_folder_name = portal_skins_folder portal_skins = portal.portal_skins if not portal_skins_folder_name in portal.portal_skins.objectIds(): portal_skins.manage_addFolder(portal_skins_folder_name) skin_folder = portal.portal_skins[portal_skins_folder_name] - # Add new folders into skin paths. for skin_name, selection in portal_skins.getSkinPaths(): selection = selection.split(',') @@ -75,30 +93,52 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, , add_skin = 1 ) - # Create the default ERP5 Form in order to view the object + # ERP FORM PROCESSING + # Create the default ERP5 Form in order to view the objects form_view_id = object_portal_type_id.replace(' ','') + '_view' factory = skin_folder.manage_addProduct['ERP5Form'] factory.addERP5Form( form_view_id , title = object_title ) form_view_id_object = skin_folder[form_view_id] - form_view_id_object.rename_group('Default', 'left') - default_groups = ['right', 'center', 'bottom', 'hidden'] - for group in default_groups: - form_view_id_object.add_group(group) - + #if option_html != None : + # form_view_id_object.rename_group('Default') + #form_view_id_object.rename_group('Default', 'left') + if option_html != 1: + # using default ERP5 positioning convention + # creating groups + form_view_id_object.rename_group('Default','left') + default_groups = ['right', 'center', 'bottom', 'hidden'] + for group in default_groups: + form_view_id_object.add_group(group) + # page groups corresponding to graphic view are defined on the flyn + # when parsing pages to get fields. + # default field will be removed at the end + + + + # Define CSS file and object informations + # first CSS file name + form_css_id = object_portal_type_id.replace(' ','') + '_css.css' + # then CSS content object (string) + form_css_content = "" + #properties_css_dict is used to store class informations + properties_css_dict = {} + + # Create Module ERP5 Form in order to view the module object_title_view = object_title + ' Module View' factory = skin_folder.manage_addProduct['ERP5Form'] factory.addERP5Form( form_view_list , title = object_title_view - ) + ) form_view_list_object = skin_folder[form_view_list] form_list_id = form_view_list_object.id form_list = form_view_list_object.restrictedTraverse(form_list_id) - + #defining groups for objects listing form_view_list_object.rename_group('Default', 'left') - #default_groups = ['right', 'center', 'bottom', 'hidden'] + default_groups = ['right', 'center', 'bottom', 'hidden'] + # adding groups for group in default_groups: form_view_list_object.add_group(group) @@ -106,17 +146,14 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, for word in module_title.split(): title_module += str(word.capitalize() + ' ') - - # Add ListBox Field + # Add ListBox Field to list the created objects id = 'listbox' title = title_module field_type = 'ListBox' form_view_list_object.manage_addField(id, title, field_type) - + # manage ListBox settings - values_settings = {} - values_settings['pt'] = "form_list" values_settings['action'] = "Base_doSelect" @@ -127,11 +164,24 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, # manage edit property of ListBox field_attributes = getattr(form_view_list_object, id) field_attributes.values['lines'] = 20 + # adding field columns field_attributes.values['columns'] = [('id', 'ID'), ('title', 'Title'), ('description', 'Description')] field_attributes.values['list_action'] = 'list' field_attributes.values['search'] = 1 field_attributes.values['select'] = 1 field_attributes.values['selection_name'] = '%s_selection' % module_id + # adding 'list_method' to be able to list the objects of a folder + # WARNING : this field does not contains the name of the method (as + # a string instance) but the method itself (as method instance) + list_method = getattr(ERP5Site,'searchFolder') + # a stange BUG occurs when saving method in field_attributes + # method adress is well get and saved in the listField + # but crashing when registrering new portal type inside + # erp5... I do not know why ! + #field_attributes.values['list_method'] = list_method + print "METHOD : name = %s" % list_method.__name__ + print "METHOD : type = %s" % list_method.__class__ + print "METHOD : doc = %s" % list_method.__doc__ form_id = form_view_id_object.id form = form_view_id_object.restrictedTraverse(form_id) @@ -139,93 +189,290 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, # import and manage Scribus File xml_string = ScribusParser.getContentFile(import_scribus_file) + page_number_int = 0 + if xml_string == None: - #print "no field was defined in the Scribus file" + LOG("ScribusParser",1,"Scribus file is empty !") + print "no field was defined in the Scribus file" pass else: # get properties from Scribus File output_string = str(xml_string) - + + #getting page objects with their attributes + #LOG("ScribusParser",0,"getXmlObjectProperties...") + print " createmodule > ScribusParser.getXmlObjectProperties" text_field_list = ScribusParser.getXmlObjectsProperties(xml_string) + print " createmodule < ScribusParser.getXmlObjectProperties\n" + #splitting tooltip-text to recover usefull attributes + #LOG("ScribusParser",0,"getPropertiesConversion...") + print " createmodule > ScribusParser.getPropertiesConversion" widget_properties = ScribusParser.getPropertiesConversion(text_field_list) + print " createmodule < ScribusParser.getPropertiesConversion\n" # add field from OrderedWidgetProperties in ERP5 Module created - radiofield_widget_properties = {} position = {} # personal_properties is used to create PropertySheet personal_properties_list = [] - - for index in range(len(widget_properties)): - id = str(widget_properties[index][0]) - properties_field = widget_properties[index][1] + page_number_int = len(widget_properties) + + # declaring dicts used to generate CSS file + # css_page is a class container. each class is composed in the css_page + # before being saved in a css_dict + properties_css_page = {} + # css_dict_head contains all the 'global' class, reffering to PAGE + properties_css_dict_head = {} + # css_dict_standard contains all the fields classes, when no error occurs + properties_css_dict_standard = {} + # css_dict_error contains the same thing, but in case error occurs. + # there background is different so that users can see where the problem + # is on the graphic view + properties_css_dict_error = {} + # css_dict_err_d contains coordinates and color to display text-error + properties_css_dict_err_d = {} + + # declaring page size + # FIXME : this value has to be taken from the image size or the document size. + # to be dynamicly set + page_height = 850 + page_width = 610 + + # DO NOT WORK ! + # print "opening image" + # page_image = Image.open(import_image_1) + # print "getting properties" + # page_image_properties = page_image.size + # print page_image_properties + # print " getting width" + # page_width = int(page_image_properties[1]) + # page_height = int(page_image_properties[0]) + + LOG("ScribusParser",0,"begining interpretation of data") + print " createmodule > begining data interpretation" + #iterating pages + for page_iterator in range(len(widget_properties)): + page_number = str(page_iterator) + page_content = widget_properties[page_number] + page_id = "page_" + page_number + + if option_html == 1: + # Processing current page for CSS data + # getting properties + properties_css_page = {} + properties_css_page['position'] = 'relative' + if page_iterator == 0: + properties_css_page['margin-top'] = "0px" + else: + properties_css_page['margin-top']= "%spx" % (page_height + 20) + # adding properties dict to golbal dict + properties_css_dict_head[page_id] = properties_css_page + + # creating image class for background + properties_css_background = {} + # making background id + properties_css_background_id = page_id + '_background' + #getting properties + properties_css_background['position'] = 'absolute' + properties_css_dict_head[properties_css_background_id] = properties_css_background + + #creating corresponding page group to form + if page_number == '0': + # if first page, renaming 'default' group into 0 group + form_view_id_object.rename_group('Default',page_id) + print " > renamed 'default' group to %s" % page_id + else : + # adding bandt new group for page + form_view_id_object.add_group(page_id) + print " > added new group %s " % page_id - if properties_field.has_key('type'): - field_type = str(properties_field['type']) - title = str(properties_field['title']) - form_view_id_object.manage_addField(id, title, field_type) - - context = skin_folder[form_view_id] - form_id = context.id - - # modify value of property - form = context.restrictedTraverse(form_id) - field_attributes = getattr(form, id) - - type = 'string' - - if field_type == 'DateTimeField': - type = 'date' - field_attributes.values['input_order'] = properties_field['input_order'] - field_attributes.values['date_only'] = properties_field['date_only'] - field_attributes.values['required'] = properties_field['required'] - - elif field_type == 'RelationStringField': - portal_type_item = properties_field['portal_type'].capitalize() - field_attributes.values['portal_type'] = [(portal_type_item, portal_type_item)] - field_attributes.values['base_category'] = properties_field['base_category'] - field_attributes.values['catalog_index'] = properties_field['catalog_index'] - field_attributes.values['default_module'] = properties_field['default_module'] - - elif field_type == 'RadioField': - radiofield_widget_properties[id] = {'description' : ''} - items = [] - for word_item in properties_field['items']: - items.append((word_item, word_item.capitalize())) - - field_attributes.values['items'] = items - - position[id] = properties_field['order'] - - # check that the property is local ... - if id.startswith('my') and not ( - # ... and not in black list - # FIXME: this list must be configurable outside this script - id.startswith('my_source') or - id.startswith('my_destination') or - id in ('my_start_date', 'my_stop_date') ) : - personal_properties = { 'id' : id[3:], - 'description' : '', - 'type' : type, - 'mode': 'w' } - personal_properties_list.append(personal_properties) + #iterating pageobjects in page + for index in range(len(page_content)): + (id, properties_field) = page_content[index] + + if properties_field.has_key('type'): + field_type = str(properties_field['type']) + title = str(properties_field['title']) + form_view_id_object.manage_addField(id, title, field_type) + + context = skin_folder[form_view_id] + form_id = context.id + + if option_html ==1: + # Processing object for CSS data + #declaring dict containing all css data + # _stand for general display + properties_css_object_stand = {} + # _error when an error occurs + properties_css_object_error = {} + # _err_d to diplay the text error + properties_css_object_err_d = {} + #defining global properties + properties_css_object_stand['position'] = 'absolute' + properties_css_object_error['position'] = 'absolute' + properties_css_object_err_d['position'] = 'absolute' + properties_css_object_stand['padding'] = '0px' + properties_css_object_error['padding'] = '0px' + properties_css_object_err_d['padding'] = '0px' + #getting position and size + properties_css_object_stand['width'] = str(properties_field['size_x']) + 'px' + properties_css_object_error['width'] = str(properties_field['size_x']) + 'px' + properties_css_object_stand['height'] = str(properties_field['size_y']) + 'px' + properties_css_object_error['height'] = str(properties_field['size_y']) + 'px' + properties_css_object_stand['margin-left'] = str(properties_field['position_x']) + 'px' + properties_css_object_error['margin-left'] = str(properties_field['position_x']) + 'px' + properties_css_object_err_d['margin-left'] = str(page_width + 20 ) + 'px' + properties_css_object_stand['margin-top'] = str(properties_field['position_y']) + 'px' + properties_css_object_error['margin-top'] = str(properties_field['position_y']) + 'px' + properties_css_object_err_d['margin-top'] = str(properties_field['position_y']) + 'px' + # adding special text_color for text error + properties_css_object_err_d['color'] = 'rgb(255,0,0)' + # adding properties to relatives dicts + properties_css_dict_standard[id] = properties_css_object_stand + properties_css_dict_error[id] = properties_css_object_error + properties_css_dict_err_d[id] = properties_css_object_err_d + # then getting additional properties + if properties_field['required'] ==1: + # field is required: using special color + # color is specified as light-blue when standard + # color = 'green' when error + properties_css_dict_standard[id]['background'] = 'rgb(192,192,255)' + properties_css_dict_error[id]['background'] = 'rgb(128,128,255)' + else: + properties_css_dict_standard[id]['background'] = '#F6FFFF' + properties_css_dict_error[id]['background'] = 'rgb(255,64,64)' + + #adding field to the corresponding page group + position[id] = page_id + form_view_id_object.move_field_group(id,'page_0',position[id]) + #print " > added %s to %s (graphic mode) " % (id,position[id]) + #print properties_css_dict_error[id]['background'] + #print properties_css_dict_standard[id]['background'] + else: + # no graphic view + # position is defined corresponding to ERP5 view + position[id] = properties_field['order'] + form_view_id_object.move_field_group(id,'left',position[id]) + #print " > added %s to %s (ERP mode)" % (id,position[id]) + + # modify value of property + form = context.restrictedTraverse(form_id) + field_attributes = getattr(form, id) + + type = 'string' - # Order field - for field in form.get_fields(): - key = str(field.id) - if position.has_key(key) == 1 and position[key] == 'right': - field.move_field_group(key, 'left', 'right') + if field_type == 'DateTimeField': + type = 'date' + field_attributes.values['input_order'] = properties_field['input_order'] + field_attributes.values['date_only'] = properties_field['date_only'] + field_attributes.values['required'] = properties_field['required'] + + elif field_type == 'RelationStringField': + portal_type_item = properties_field['portal_type'].capitalize() + field_attributes.values['portal_type'] = [(portal_type_item, portal_type_item)] + field_attributes.values['base_category'] = properties_field['base_category'] + field_attributes.values['catalog_index'] = properties_field['catalog_index'] + field_attributes.values['default_module'] = properties_field['default_module'] + + elif field_type == 'RadioField': + radiofield_widget_properties[id] = {'description' : ''} + items = [] + for word_item in properties_field['items'].split('|'): + items.append((word_item, word_item.capitalize())) + + field_attributes.values['items'] = items + + elif field_type in ['StringField','IntegerField','Floatfield']: + field_attributes.values['maximum_input'] = properties_field['maximum_input'] + #print " => saved 'maximum_input' value" + + # check that the property is local ... + if id.startswith('my') and not ( + # ... and not in black list + # FIXME: this list must be configurable outside this script + id.startswith('my_source') or + id.startswith('my_destination') or + id in ('my_start_date', 'my_stop_date') ) : + personal_properties = { 'id' : id[3:], + 'description' : '', + 'type' : type, + 'mode': 'w' } + + personal_properties_list.append(personal_properties) + + # adding 'page_end' class to add a div at the end of the last page + # in order to display the full last page under Konqueror + properties_css_page = {} + properties_css_page['position'] = 'relative' + properties_css_page['margin-top'] = "%spx" % str( page_height) + properties_css_dict_head['page_end'] = properties_css_page + print " createmodule < end of data interpretation" + print "\n" + + + # CSS CLASS + if option_html ==1: + print " createmodule > printing output from css_class_generator" + form_css_content = "/*-- special css form generated through ScribusUtils module --*/" + form_css_content += "/*-- to have a graphic rendering with 'form_html' page template --*/\n" + form_css_content += "/* head : classes declared for general purpose */\n" + # iterating classes in document's head + for class_name in properties_css_dict_head.keys(): + # getting class properties_dict + class_properties = properties_css_dict_head[class_name] + # joining exerything + output_string = "." + str(class_name) + " {" \ + + "; ".join(["%s:%s" % (id, val) for id, val in class_properties.items()]) \ + + "}" + # adding current line to css_content_object + form_css_content += output_string + "\n" + form_css_content += "\n/* standard field classes */ \n" + # adding standard classes + for class_name in properties_css_dict_standard.keys(): + class_properties = properties_css_dict_standard[class_name] + output_string = "." + str(class_name) + " {" \ + + "; ".join(["%s:%s" % (id,val) for id,val in class_properties.items()]) \ + + "}" + form_css_content += output_string + "\n" + form_css_content += "\n/* error field classes */\n" + #adding error classes + for class_name in properties_css_dict_error.keys(): + class_properties = properties_css_dict_error[class_name] + output_string = "." + str(class_name) + "_error {" \ + + "; ".join(["%s:%s" % (id,val) for id, val in class_properties.items()]) \ + + "}" + form_css_content += output_string + "\n" + form_css_content += "\n/* text_error field classes */ \n" + # adding field error classes + for class_name in properties_css_dict_err_d.keys(): + class_properties = properties_css_dict_err_d[class_name] + output_string = "." + str(class_name) + "_error_display {" \ + + "; ".join(["%s:%s" % (id,val) for id,val in class_properties.items()]) \ + + "}" + form_css_content += output_string + "\n" + + #print form_css_content + print " createmodule < end output \n" + + print " createmodule > creating output CSS file" + factory.addDTMLDocument(form_css_id,"css",form_css_content) + print " createmodule < CSS file creation\n" - # manage_settings + print " createmodule > managing Form settings" + # manage global form settings values = {} - values['title'] = str(object_portal_type) values['row_length'] = 4 values['name'] = str(form_view_id) - values['pt'] = "form_view" + # the 'pt' field has to be changed from 'form_view' to 'form_html' + # when generating graphic interface. + if option_html ==1: + values['pt'] = "form_html" + else: + values['pt'] = "form_view" values['action'] = "Base_edit" values['update_action'] = "" values['method'] = 'POST' @@ -233,29 +480,119 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, values['encoding'] = "UTF-8" values['stored_encoding'] = 'UTF-8' values['unicode_mode'] = 0 - # set the form settings for key, value in values.items(): setattr(form, key, value) + print " createmodule < settings managed\n" + + + print " createmodule > managing PDF settings" # Import and manage PDF File before filling of default TALES expressions in cells + # first import the PDFForm in the skin folder factory.addPDFForm(form_view_pdf, object_title, pdf_file = import_pdf_file) + for c in skin_folder.objectValues(): + if c.getId() == form_view_pdf : - for cell_name in c.getCellNames(): + print " %s selected" % c.getId() + cell_name_list = c.getCellNames() + print " %s" % cell_name_list + for cell_name in cell_name_list: if cell_name[0:3] == 'my_': - cellName = [] + cellName_list = [] for word in cell_name[3:].split('_'): word = word.capitalize() - cellName.append(word) - if cellName[-1] == 'List' : - TALES = 'python: ", ".join(here.get' + "".join(cellName) + '())' + cellName_list.append(word) + if cellName_list[-1] == 'List' : + TALES = 'python: ", ".join(here.get' + "".join(cellName_list) + '())' else : - TALES = 'python: here.get' + "".join(cellName) + '()' + TALES = 'python: here.get' + "".join(cellName_list) + '()' + print " cell : %s => TALES expression : %s " % (cell_name,TALES) c.setCellTALES(cell_name, TALES) + print " createmodule < PDF settings managed\n" + + + + + print " createmodule > importing background pictures" + # Import and register background images for HTML display + if option_html == 1: + + # saving pdf content to aspecific file on hard disk + temp_pdf = open('/tmp/ScribusUtilsTempPDF.tmp','w') + # moving cursor to begining of file + import_pdf_file.seek(0) + # reading content + temp_content = import_pdf_file.read() + print " > inputfile read : %sb" % len(temp_content) + # writing content to outputfile + temp_pdf.write(temp_content) + print " > inputfile written" + # closing outputfile + temp_pdf.close() + + # running first conversion from PDF to PPM + import commands + result = commands.getstatusoutput('pdftoppm -r 72 /tmp/ScribusUtilsTempPDF.tmp /tmp/ScribusUtilsTempPPM') + print " > pdftoppm result(%s) : %s" % (result[0],result[1]) + + # running second conversion from PPM to JPEG + result = commands.getstatusoutput('convert /tmp/ScribusUtilsTempPPM* jpg:/tmp/ScribusUtilsTempJPG') + print " > convert result(%s) : %s" % (result[0],result[1]) + + # getting list of resulting pictures + result = commands.getstatusoutput('ls /tmp/ | grep ScribusUtilsTempJPG') + print " > getting list of final pictures" + + image_number = 0 + for image in result[1].split('\n'): + # result[1] contains the output string + # splitting this output string in lines to get + # the string parameters + + # opening resulting pictures + temp_jpg = open('/tmp/%s' % image,'r') + print " > open picture : len=%sb" % len(temp_jpg.read()) + + # saving content to zope folder + form_page_id = object_portal_type_id.replace(' ','')+ "_background_" + str(image_number) + add_image = skin_folder.manage_addProduct['OFSP'].manage_addImage + add_image(form_page_id,temp_jpg,"background image") + + # incrementing image number before going to next one + image_number +=1 + + # deleting temporary files + result = commands.getstatusoutput('rm -f /tmp/ScribusUtilsTemp*') + print " > remove temp files" + + #factory = skin_folder.manage_addProduct['Images'] + """ + if page_number_int > 0 : + # image specified for first page_element + form_page_id = object_portal_type_id.replace(' ','') + "_background_" + "0" + add_image = skin_folder.manage_addProduct['OFSP'].manage_addImage + add_image(form_page_id,import_image_1,"background image") + if page_number_int > 1 : + form_page_id = object_portal_type_id.replace(' ','') + "_background_" + "1" + add_image = skin_folder.manage_addProduct['OFSP'].manage_addImage + add_image(form_page_id,import_image_2,"background image 2") + if page_number_int > 2: + form_page_id = object_portal_type_id.replace(' ','') + "_background_" + "2" + add_image = skin_folder.manage_addProduct['OFSP'].manage_addImage + add_image(form_page_id,import_image_3,"background image 3") + """ + print " createmodule < background pictures imported\n" + + # get background picture size. + #skin_folder + + # Create PropertySheet and Document + print " createmodule > PropertySheet and Document creation" name_file = '' title_module = '' for word in object_portal_type.split(): @@ -263,15 +600,21 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, title_module += str(word.capitalize() + ' ') generator.generateLocalPropertySheet(name_file, personal_properties_list) + print " PropertySheet : %s" % name_file generator.generateLocalDocument(name_file, object_portal_type) - + print " Document : %s" % name_file + # Reload register local property sheets from Products.ERP5Type.Utils import initializeLocalPropertySheetRegistry initializeLocalPropertySheetRegistry() + # Reload register local classes from Products.ERP5Type.Utils import initializeLocalDocumentRegistry initializeLocalDocumentRegistry() + print " createmodule < PropertyShett and Document imported\n" + + # Then add the portal_type corresponding to the new object typeinfo_name_ERP5Type = str('ERP5Type: ERP5 ' + object_portal_type) @@ -340,5 +683,3 @@ def ERP5Site_createModuleScribus(self, form_id=None, module_portal_type=None, portal.REQUEST.RESPONSE.redirect( redirect_url ) - - diff --git a/product/ERP5Form/ScribusUtils.py b/product/ERP5Form/ScribusUtils.py index 98c0b0104cb0334d968bb756f5321f1a5bc97c5c..8597acbead7257c827e1f4b1ef24dad00dd04e5c 100755 --- a/product/ERP5Form/ScribusUtils.py +++ b/product/ERP5Form/ScribusUtils.py @@ -2,6 +2,7 @@ # # Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved. # Guy Oswald OBAMA <guy@nexedi.com> +# thomas <thomas@nexedi.com> # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -19,6 +20,11 @@ # ############################################################################## +# This code is under refactoring. This code will change in near future +# with a lot of cleanups. This is stored only for a temporary purpose. +# Do not rely on the real implementation. It is assumed that the code is +# improved and modified significantly by thomas. + from Products.PythonScripts.Utility import allow_class from ZPublisher.HTTPRequest import FileUpload from xml.dom.ext.reader import PyExpat @@ -30,98 +36,535 @@ from StringIO import StringIO from zLOG import LOG import imghdr import random -import getopt, sys, os, string +import getopt, sys, os from urllib import quote class ScribusParser: """ - Parses a Scribus file + Parses a Scribus file with PDF-elements inside """ - # Declarative security + #declare security security = ClassSecurityInfo() + + security.declarePublic('getObjectTooltipProperty') + def getObjectTooltipProperty(self, check_key, default_value, object_name, object_dict): + """ + check if 'check_key' exists in 'object_dict' and has a value + if true, then returns this value, else returns 'default_value' and log 'object_name' + """ + #return object_dict.get(check_key, None) or default_value + if object_dict.has_key(check_key): + # 'check_key' exists + if len(object_dict[check_key]) != 0: + # check_key corresponding value is not null + # returning this value + return object_dict[check_key] + else: + # check_key is null, logging and asigning default value + print " > " + str(object_name) + " has an invalid '" + str(check_key) \ + + "' : using default = " + str(default_value) + LOG("WARNING : " + str(object_name),0,"invalid " + str(check_key) \ + + ": using " + str(default_value)) + return default_value + else: + # check_key is null, logging and asigning default value + print " > " + str(object_name) + " has no '" + str(check_key) \ + + "' : using default = " + str(default_value) + LOG("WARNING : " + str(object_name),0,"no " + str(check_key) \ + + ": using " + str(default_value)) + return default_value - security.declarePublic('getXmlObjectsProperties') + + security.declarePublic('getStringInt') + def getStringInt(self,input_string): + """ + convert a string containing an integer or a long number + into an integer, as should do the int() function (if it + was not crashing when trying to do such a convertion). + This function is used to convert position and size values + to integers to prevent script from crashing. + """ + return int(input_string.split('.')[0]) + + + security.declarePublic('getXmlObjectProperties') def getXmlObjectsProperties(self, xml_string): - # Create the PyExpat reader + """ + takes a string containing a whole document and returns + a full dict of 'PAGE', containing a dict of 'PAGEOBJECT', + containing a dict of all the relative attributes + """ + + #ERP5 access_type + #Create the PyExpat reader + print "\n => ScibusParser.getXmlObjectProperties" + print " > create reader" + LOG("ScribusUtils",1,"create reader") reader = PyExpat.Reader() - + # Create DOM tree from the xml string + print " > create DOM tree" dom_tree = reader.fromString(xml_string) - - text_field_list = {} + #dom_tree = minidom.parse(xml_string) + + ##External script acces_type + ## testing procedure outside erp5. Put these lines in comment if this + ## module is called inside erp5. + #print " => opening socket..." + #sock = open(xml_string) + #print " => reading the whole file..." + #dom_tree = minidom.parse(sock) + #print " => closing socket..." + #sock.close() + # creating the root from the input file + # does not depend on the kind of access (w or w/o erp5) dom_root = dom_tree.documentElement - page_object_list = dom_root.getElementsByTagName("PAGEOBJECT") - - # Take Xml objects properties - for page_object in page_object_list: - text_field_properties = {} - field_name = None - for attribute in page_object.attributes: - node_name = str(attribute.nodeName) + + #making a listing of all the PAGE object + print " > making listing of all PAGE objects" + page_list = dom_root.getElementsByTagName("PAGE") + + returned_page_dict = {} + + #for each PAGE object, searching for PAGEOBJECT + for page in page_list: + + + # getting page number + # parsing method from the previous ScribusUtils + page_number = -1 + for attribute in page.attributes: + node_name = str(attribute.nodeName) node_value = str(attribute.nodeValue) - if node_name == 'ANNAME': - if node_value != '': - field_name = node_value - else: - text_field_properties[node_name] = node_value - if field_name != None: - text_field_list[field_name] = text_field_properties - - return text_field_list + if node_name == 'NUM': + page_number = node_value + + print " > PAGE NUM=" + str(page_number) + + # making a listing of all PAGEOBJECT in a specified PAGE + page_object_list = page.getElementsByTagName("PAGEOBJECT") + + # initialising global output dictionary containing pages of elements + returned_page_object_dict = {} + + # for each PAGEOBJECT, building dict with atributes + for page_object in page_object_list: + + # initialising + returned_page_object = {} + field_name = None + + #iterating PAGEOBJECT attributes + #old parsing method emlployed also here + for attribute in page_object.attributes: + node_name = str(attribute.nodeName) + node_value = str(attribute.nodeValue) + + + #iterating through PAGEOBJECT attributes + #for attribute in page_object.attributes.keys(): + # node_name = str(attribute) + # node_value = str(page_object.attributes[attribute].value) + + + if node_name == 'ANNAME': + if node_value != '': + #if 'PAGEOBJECT' contains an attribute 'ANNAME' not null then + #this value is considered as the 'PAGEOBJECT' name + field_name = node_value.replace(' ','_') + else: + #for others attributes, just adding them to the dictionary as + #standard attributes + returned_page_object[node_name] = node_value + + if field_name != None: + #if 'PAGEOBJECT' has a valid name, then adding it to the global + #dictionary containing all the 'PAGEOBJECT' of the 'PAGE' + returned_page_object_dict[field_name] = returned_page_object + print " > PAGEOBJECT = " + str(field_name) + + #after having scanned all 'PAGEOBJECT' from a 'PAGE', adding the + #relatives information to the list of 'PAGE' before going to the next one + #in case the page is not empty + if len(returned_page_object_dict) != 0: + returned_page_dict[page_number] = returned_page_object_dict + + print "=> end ScribusParser.getXmlObjectProperties" + return returned_page_dict + + security.declarePublic('getPropertiesConversion') - def getPropertiesConversion(self, text_field_list): - # Get Scribus field properties - - field_scribus_properties_dict = {} - - for field_name in text_field_list.keys(): - text_field_properties = text_field_list[field_name] - field_scribus_properties_dict[field_name] = text_field_properties['ANTOOLTIP'] - - widget_properties_list = [] - index = 1 - - while index < len(field_scribus_properties_dict): - for key, item in field_scribus_properties_dict.items(): - if string.atoi(item[:3]) == index: - property_field_list = item[4:].split('#') - widget_properties_buffer = {} - for property_field in property_field_list: - property_field_split = property_field.split(':') - if property_field_split[0] == 'items': - property_field_split[1] = property_field_split[1].split('|') - widget_properties_buffer[property_field_split[0]] = property_field_split[1] - widget_properties_list.append((key, widget_properties_buffer)) - break - index = index + 1 - - for key, item in field_scribus_properties_dict.items(): - if string.atoi(item[:3]) == 999: - property_field_list = item[4:].split('#') - widget_properties_buffer = {} - for property_field in property_field_list: - property_field_split = property_field.split(':') - widget_properties_buffer[property_field_split[0]] = property_field_split[1] - widget_properties_list.append((key, widget_properties_buffer)) + def getPropertiesConversion(self, text_page_dict): + """ + takes a dict generated from 'getXmlObjectsProperties' method and returns a + dict of PAGE including a list with usefull 'PAGEOBJECT' attributes updated + with standard attributes and special informations contained in the + 'ANTOOLTIP' attribute. + + usefull attributes are + - position & size + - type & inputformat (for erp5 and html) + - creation order (using 'nb' property) + - erp5 relative position (left, right, etc.) + - title information + - other properties (read_only, multiline, etc.) + - etc. + + for each PAGE, all PAGEOBJECT are sorted according to their creation order + 'nb' + """ + + print "\n => ScribusParser.getPropertiesConversion" + returned_page_dict = {} + + # declaring ScribusParser object to run other functions + sp = ScribusParser() - return widget_properties_list + + for page_number in text_page_dict.keys(): + # iterating through 'PAGE' object of the document + # id = page_number + # content = page_content + page_content = text_page_dict[page_number] + + print " => PAGE = " + str(page_number) + + # declaring special lists used to generate nb for all objects + # this 'nb' property is usefull to define the object creation order + # all objects are sorted (has nb / has no nb) and all objects without + # nb attribte are added t othe end of the 'has nb' list + nb_property_nbkey_list = [] + nb_property_nonbkey_list = [] + + # declaring output object + returned_object_dict = {} + + # if page_content.haskey('my_fax_field') + # print "my_fax_field" + for object_name in page_content.keys(): + # iterating through 'PAGEOBJECT' of the page + # id = object_name + # content = object_content + object_content = page_content[object_name] + + print " => PAGEOBJECT = " + str(object_name) + # recovering other attributes from 'ANTOOLTIP' + text_tooltipfield_properties = object_content['ANTOOLTIP'] + #declaring output file + tooltipfield_properties_dict = {} + #splitting the different attributes + tooltipfield_properties_list = text_tooltipfield_properties.split('#') + + print " " + str(tooltipfield_properties_list) + + + + # test if first argument is nb according to previous naming-conventions + # i.e composed of three digits without id 'nb:' written + if str(tooltipfield_properties_list[0]).isdigit(): + # first value of tooltilfield is digit : assuming this is an creation-order + # information compliant with the previous naming convention + # modifying this field to make it compatible with new convention + print " => first element = " + str(tooltipfield_properties_list[0] + " is digit...") + LOG("WARNING : " + str(object_name),0,"out-of-date naming convention found" \ + + "for tooltipfield, please check naming_conventions") + temp_nb = tooltipfield_properties_list[0] + # deleting actual entry + tooltipfield_properties_list.remove(temp_nb) + # adding new entry to the list + temp_nb_text = "nb:" + str(temp_nb) + tooltipfield_properties_list.append(temp_nb_text) + # end of translating work to get new standard compliant code + for tooltipfield_property in tooltipfield_properties_list: + #printing each property before spliting + print " " + str(tooltipfield_property) + # splitting attribute_id / attribute_value + tooltipfield_properties_split = tooltipfield_property.split(':') + if len(tooltipfield_properties_split) == 2: + tooltipfield_id = tooltipfield_properties_split[0] + tooltipfield_value = tooltipfield_properties_split[1] + # making dictionary from 'ANTOOLTIP' attributes + tooltipfield_properties_dict[tooltipfield_id] = tooltipfield_value + # end of 'ANTOOLTIP' parsing + + + + # getting usefull attributes from scribus 'PAGEOBJECT' and 'ANTOOLTIP' + # -------------------------------------------------------------------- + object_properties = {} + + # getting object position and size + object_properties['position_x'] = sp.getObjectTooltipProperty('XPOS','0',object_name,object_content) + object_properties['position_y'] = sp.getObjectTooltipProperty('YPOS','0',object_name,object_content) + object_properties['size_x'] = sp.getObjectTooltipProperty('WIDTH','100',object_name,object_content) + object_properties['size_y'] = sp.getObjectTooltipProperty('HEIGHT','17',object_name,object_content) + # converting values to integer-compliant to prevent errors when using them + object_properties['position_x'] = str(float(object_properties['position_x'])) + object_properties['position_x'] = str(sp.getStringInt(object_properties['position_x'])) + object_properties['position_y'] = str(sp.getStringInt(object_properties['position_y'])) + object_properties['size_x'] = str(sp.getStringInt(object_properties['size_x'])) + object_properties['size_y'] = str(sp.getStringInt(object_properties['size_y'])) + + + # getting object title + # object title can only be user-specified in the 'tooltip' dict + object_properties['title'] = sp.getObjectTooltipProperty('title', object_name, object_name, tooltipfield_properties_dict) + + + # getting object order position for erp5 form + temp_order = sp.getObjectTooltipProperty('order','none',object_name,tooltipfield_properties_dict) + if temp_order not in ['left','right']: + # temp_order invalid + # trying to get it from document + if sp.getStringInt(object_properties['position_x']) > 280.0 : + temp_order = 'right' + else : + temp_order = 'left' + object_properties['order'] = temp_order + + + + + # getting special ANFLAG sub-properties + temp_ANFLAG = long(sp.getObjectTooltipProperty('ANFLAG','0',object_name,object_content)) + # initialising results + anflag_properties = {} + anflag_properties['noScroll'] = 0 + anflag_properties['noSpellCheck'] = 0 + anflag_properties['editable'] = 0 + anflag_properties['password'] = 0 + anflag_properties['multiline'] = 0 + anflag_properties['noExport'] = 0 + anflag_properties['required'] = 0 + anflag_properties['readOnly'] = 0 + # analysing result + print " => ANFLAG = " + str(object_content['ANFLAG']) + if temp_ANFLAG - 8388608 >= 0: + # substracting value + temp_ANFLAG = temp_ANFLAG - long(8388608) + # 'do not scroll' field + # adding property + anflag_properties['noscroll'] = 1 + if temp_ANFLAG - 4194304 >= 0: + temp_ANFLAG = temp_ANFLAG - 4194304 + # 'do not spell check' field + anflag_properties['noSpellCheck'] = 1 + if temp_ANFLAG - 262144 >= 0: + temp_ANFLAG = temp_ANFLAG - 262144 + # 'editable' field + anflag_properties['editable'] = 1 + if temp_ANFLAG - 8192 >= 0: + temp_ANFLAG = temp_ANFLAG - 8192 + # 'password' field + anflag_properties['password'] = 1 + if temp_ANFLAG - 4096 >= 0: + temp_ANFLAG = temp_ANFLAG - 4096 + # 'multiline' field + anflag_properties['multiline'] = 1 + if temp_ANFLAG - 4 >= 0: + temp_ANFLAG = temp_ANFLAG - 4 + # 'do not export data' field + anflag_properties['noExport'] = 1 + if temp_ANFLAG - 2 >= 0: + temp_ANFLAG = temp_ANFLAG - 2 + # 'required field + anflag_properties['required'] = 1 + if temp_ANFLAG == 1: + # 'read only" field + anflag_properties['readOnly'] = 1 + + # getting maximum number of caracters the field can hold + # note : only for textfields + object_properties['maximum_input'] = sp.getObjectTooltipProperty('ANMC','0',object_name,object_content) + print " => MaxInput = %s" % object_properties['maximum_input'] + + # getting object type : + # first checking for user-specified type in 'tooltip' properties + if tooltipfield_properties_dict.has_key('type'): + # 'type' id in tooltip : using it and ignoring other 'type' information + # in scribus properties + object_properties['type'] = tooltipfield_properties_dict['type'] + # if no user-specified type has been found, trying to find scribus-type + elif object_content.has_key('ANTYPE'): + # from scribus type (selected in the scribus PDF-form properties) + object_type = str(object_content['ANTYPE']) + if object_type == '2': + #type 2 = PDF-Button + object_properties['type'] = 'Button' + elif object_type == '3': + #type 3 = PDF-Text : Stringfield by default + object_properties['type'] = 'StringField' + if anflag_properties['multiline'] == 1: + # Stringfield is multiline, converting to TextAreaField + object_properties['type'] = 'TextAreaField' + elif object_content.has_key('ANFORMAT'): + object_format = str(object_content['ANFORMAT']) + # checking kind of Stringfield + if object_format == '1': + #type is number + object_properties['type'] = 'IntegerField' + elif object_format == '2': + #type is percentage + object_properties['type'] = 'FloatField' + elif object_format == '3': + #type is date + object_properties['type'] = 'DateTimeField' + elif object_format == '4': + #type is time + object_properties['type'] = 'DateTimeField' + elif object_type == '4': + # type 4 = PDF-Checkbox + object_properties['type'] = 'CheckBoxField' + elif object_type == '5': + # type 5 = PDF-Combobox + object_properties['type'] = 'ComboBox' + elif object_type == '6': + # type 6 = PDF-ListBox + object_properties['type'] = 'ListBox' + else: + # object type not found in user-properties neither in document-properties + # logging and initialising with default type + LOG("WARNING : " + str(object_name),0,"no 'type' found, please check your document properties or use 'tooltips' properties") + print " => no type specified : assuming default = StringField" + object_properties['type'] = 'StringField' + print " type = " + str(object_properties['type']) + + + # getting 'required' property + # first checking from user data in 'tooltip' + temp_required = sp.getObjectTooltipProperty('required','none',object_name,tooltipfield_properties_dict) + if temp_required == 'none': + # no 'required' property in 'tooltip' + # cheking global PAGEOBJECT properties for 'required' (found in anflag) + temp_required = anflag_properties['required'] + object_properties['required'] = temp_required + + + # getting type properties for special types + # checkbox objects belongs to a group of checkbox + if str(object_properties['type']) == 'CheckBox' : + # checking if THIS checkbox is in a group + object_properties['group'] = sp.getObjectTooltipProperty('group', '0', object_name, tooltipfield_properties_dict) + print " group = " + str(object_properties['group']) + + + #object is listbox, and listbox have several possible values + if str(object_properties['type']) == 'ListBox' : + #checking if this listbox has different possible values + object_properties['items'] = sp.getObjectTooltipProperty('items', '', object_name, tooltipfield_properties_dict) + + + #object is datetimefield and need several informations + if str(object_properties['type']) == 'DateTimeField': + #checking if field has inpu_order property + object_properties['input_order'] = sp.getObjectTooltipProperty('input_order','day/month/year',object_name,tooltipfield_properties_dict) + # + #checking if field has date_only property + object_properties['date_only'] = sp.getObjectTooltipProperty('date_only','1',object_name,tooltipfield_properties_dict) + + + # object is relationstringfield and ned some informations + # FIXME : quelle est la valeur par defaut pour des champs de ce type ? + if str(object_properties['type']) == 'RelationStringField': + object_properties['base_category'] = sp.getObjectTooltipProperty('base_category','0',object_name,tooltipfield_properties_dict) + object_properties['catalog_index'] = sp.getObjectTooltipProperty('catalog_index','0',object_name,tooltipfield_properties_dict) + object_properties['default_module'] = sp.getObjectTooltipProperty('default_module','0',object_name,tooltipfield_properties_dict) + + + + # getting creation order from 'tooltip' properties + # used to create ERP5 objects in a special order + if tooltipfield_properties_dict.has_key('nb') and str(tooltipfield_properties_dict['nb']).isdigit(): + # object has a nb properties containing its creation position + # adding the object in the ordered list + nb_value = int(tooltipfield_properties_dict['nb']) + print " =>'nb' property specified : using it to order PAGEOBJECT elements" + # iterating through existing list to find right position + # before inserting value + if len(nb_property_nbkey_list) == 0: + print " => 'nb' list empty : adding without sorting" + # list is empty : adding value without sort + nb_property_nbkey_list.insert(0,(nb_value,object_name)) + elif nb_property_nbkey_list[len(nb_property_nbkey_list)-1][0] <= nb_value: + print " => 'nb' end : adding at the end" + # last element is smaller than new element : adding at the end + nb_property_nbkey_list.append((nb_value,object_name)) + else: + print " => checking for place to add the element" + # searching where to insert the element in the ordered list + for temp_key in range(len(nb_property_nbkey_list)): + temp_value = nb_property_nbkey_list[temp_key][0] + temp_content = nb_property_nbkey_list[temp_key][1] + print " @" + str(temp_key) + " temp=" + str(temp_value) + "/" + str(nb_value) + if nb_value < temp_value: + #first position where actual 'nb' is smaller than temp 'nb' + # inserting new couple (nb_value,object_name) here + print " inserting here : " + str(temp_value) + "/" + str(nb_value) + nb_property_nbkey_list.insert(temp_key,(nb_value,object_name)) + # element has been insered , no need to continue the search => breaking + break + else: + # object has no nb property. logging and adding it to the list of + # nb-less objects. Script will automatically find a 'nb' value for this element + LOG("WARNING : " + str(object_name),0,"no 'nb' defined : finding a free slot") + print " => no 'nb' property specified : post-processing will try to define one" + nb_property_nonbkey_list.append(object_name) + + # adding current object with its relative properties to the dict + # before going to the next page_object + returned_object_dict[object_name] = object_properties + + + # final processing before returning full page with modified + # page_object_properties : setting 'nb' property to all objects + # without user-specified 'nb' property + for object_name in nb_property_nonbkey_list: + # listing all objects with no 'nb' declared + # defining final position in output list : absolute pos + relative pos + object_position = len(nb_property_nbkey_list) + 1 + # and addind it to the end of the final nb-list + # to give them a 'nb' property + nb_property_nbkey_list.append((object_position,object_name)) + print " => final sorting before returning value, " + str(len(nb_property_nbkey_list)) + " elements" + # now all page_object are referenced in the list, we just need to sort + # the elements in the good order. for that a new list of objects is needed + returned_object_list = [] + for nb_ind in range(len(nb_property_nbkey_list)): + # iterating through final nb-list + # getting list-object information + (nb_key, nb_value) = nb_property_nbkey_list[nb_ind] + # setting object's 'nb' property + returned_object_dict[nb_value]['nb'] = nb_ind + 1 + # add the object at the end of the new list + returned_object_list.append((nb_value,returned_object_dict[nb_value])) + LOG("INFO : " + str(nb_value),0,"creation order =" + str(nb_ind)) + print " > " + str(nb_value) + " has nb:" + str(nb_ind) + + + + # adding returned list of object to the page dict + # before going to the next page + returned_page_dict[page_number] = returned_object_list + + + + # returning final dict containing all the modified data + print " => end ScribusParser.getPropertiesConversion" + return returned_page_dict + security.declareProtected('Import/Export objects', 'getContentFile') def getContentFile(self, file_descriptor): """ Get file content """ return file_descriptor.read() - InitializeClass(ScribusParser) allow_class(ScribusParser) - - - \ No newline at end of file