diff --git a/openerp-server b/openerp-server index c42651dc419..5fdbb28eeb6 100755 --- a/openerp-server +++ b/openerp-server @@ -88,8 +88,11 @@ def setup_pid_file(): def preload_registry(dbname): """ Preload a registry, and start the cron.""" - db, pool = openerp.pooler.get_db_and_pool(dbname, update_module=config['init'] or config['update'], pooljobs=False) - pool.get('ir.cron').restart(db.dbname) + try: + db, pool = openerp.pooler.get_db_and_pool(dbname, update_module=config['init'] or config['update'], pooljobs=False) + pool.get('ir.cron').restart(db.dbname) + except Exception: + logging.exception('Failed to initialize database `%s`.', dbname) def run_test_file(dbname, test_file): """ Preload a registry, possibly run a test file, and start the cron.""" diff --git a/openerp/addons/base/i18n/en_GB.po b/openerp/addons/base/i18n/en_GB.po index b2f4c97bce7..45a31676521 100644 --- a/openerp/addons/base/i18n/en_GB.po +++ b/openerp/addons/base/i18n/en_GB.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openobject-server\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2011-01-11 11:14+0000\n" -"PO-Revision-Date: 2011-09-16 12:13+0000\n" +"PO-Revision-Date: 2011-09-22 14:47+0000\n" "Last-Translator: John Bradshaw \n" "Language-Team: English (United Kingdom) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-17 04:54+0000\n" -"X-Generator: Launchpad (build 13955)\n" +"X-Launchpad-Export-Date: 2011-09-23 04:38+0000\n" +"X-Generator: Launchpad (build 14012)\n" #. module: base #: view:ir.filters:0 @@ -4192,7 +4192,7 @@ msgstr "Menus" #. module: base #: selection:base.language.install,lang:0 msgid "Serbian (Latin) / srpski" -msgstr "" +msgstr "Serbian (Latin) / srpski" #. module: base #: model:res.country,name:base.il @@ -4368,7 +4368,7 @@ msgstr "" #. module: base #: view:base.language.import:0 msgid "- module,type,name,res_id,src,value" -msgstr "" +msgstr "- module,type,name,res_id,src,value" #. module: base #: selection:base.language.install,lang:0 @@ -4387,7 +4387,7 @@ msgstr "" #. module: base #: help:ir.model.fields,relation:0 msgid "For relationship fields, the technical name of the target model" -msgstr "" +msgstr "For relationship fields, the technical name of the target model" #. module: base #: selection:base.language.install,lang:0 @@ -4402,7 +4402,7 @@ msgstr "Inherited View" #. module: base #: view:ir.translation:0 msgid "Source Term" -msgstr "" +msgstr "Source Term" #. module: base #: model:ir.ui.menu,name:base.menu_main_pm @@ -4412,7 +4412,7 @@ msgstr "Project" #. module: base #: field:ir.ui.menu,web_icon_hover_data:0 msgid "Web Icon Image (hover)" -msgstr "" +msgstr "Web Icon Image (hover)" #. module: base #: view:base.module.import:0 @@ -4432,7 +4432,7 @@ msgstr "Create User" #. module: base #: view:partner.clear.ids:0 msgid "Want to Clear Ids ? " -msgstr "" +msgstr "Want to Clear Ids ? " #. module: base #: field:publisher_warranty.contract,name:0 @@ -4484,17 +4484,17 @@ msgstr "Fed. State" #. module: base #: field:ir.actions.server,copy_object:0 msgid "Copy Of" -msgstr "" +msgstr "Copy Of" #. module: base #: field:ir.model,osv_memory:0 msgid "In-memory model" -msgstr "" +msgstr "In-memory model" #. module: base #: view:partner.clear.ids:0 msgid "Clear Ids" -msgstr "" +msgstr "Clear Ids" #. module: base #: model:res.country,name:base.io @@ -4516,7 +4516,7 @@ msgstr "Field Mapping" #. module: base #: view:publisher_warranty.contract:0 msgid "Refresh Validation Dates" -msgstr "" +msgstr "Refresh Validation Dates" #. module: base #: view:ir.model:0 @@ -4587,7 +4587,7 @@ msgstr "_Ok" #. module: base #: help:ir.filters,user_id:0 msgid "False means for every user" -msgstr "" +msgstr "False means for every user" #. module: base #: code:addons/base/module/module.py:198 @@ -4636,6 +4636,7 @@ msgstr "Contacts" msgid "" "Unable to delete this document because it is used as a default property" msgstr "" +"Unable to delete this document because it is used as a default property" #. module: base #: view:res.widget.wizard:0 @@ -4689,7 +4690,7 @@ msgstr "" #: code:addons/orm.py:1350 #, python-format msgid "Insufficient fields for Calendar View!" -msgstr "" +msgstr "Insufficient fields for Calendar View!" #. module: base #: selection:ir.property,type:0 @@ -4702,6 +4703,8 @@ msgid "" "The path to the main report file (depending on Report Type) or NULL if the " "content is in another data field" msgstr "" +"The path to the main report file (depending on Report Type) or NULL if the " +"content is in another data field" #. module: base #: help:res.config.users,company_id:0 @@ -4763,7 +4766,7 @@ msgstr "Close" #. module: base #: selection:base.language.install,lang:0 msgid "Spanish (MX) / Español (MX)" -msgstr "" +msgstr "Spanish (MX) / Español (MX)" #. module: base #: view:res.log:0 @@ -4798,7 +4801,7 @@ msgstr "Publisher Warranty Contracts" #. module: base #: help:res.log,name:0 msgid "The logging message." -msgstr "" +msgstr "The logging message." #. module: base #: field:base.language.export,format:0 @@ -5033,7 +5036,7 @@ msgstr "" #. module: base #: help:ir.cron,interval_number:0 msgid "Repeat every x." -msgstr "" +msgstr "Repeat every x." #. module: base #: wizard_view:server.action.create,step_1:0 @@ -5093,6 +5096,8 @@ msgid "" "If specified, this action will be opened at logon for this user, in addition " "to the standard menu." msgstr "" +"If specified, this action will be opened at logon for this user, in addition " +"to the standard menu." #. module: base #: view:ir.values:0 @@ -5103,7 +5108,7 @@ msgstr "Client Actions" #: code:addons/orm.py:1806 #, python-format msgid "The exists method is not implemented on this object !" -msgstr "" +msgstr "The exists method is not implemented on this object !" #. module: base #: code:addons/base/module/module.py:336 @@ -5128,7 +5133,7 @@ msgstr "Connect Events to Actions" #. module: base #: model:ir.model,name:base.model_base_update_translations msgid "base.update.translations" -msgstr "" +msgstr "base.update.translations" #. module: base #: field:ir.module.category,parent_id:0 @@ -5139,7 +5144,7 @@ msgstr "Parent Category" #. module: base #: selection:ir.property,type:0 msgid "Integer Big" -msgstr "" +msgstr "Integer Big" #. module: base #: selection:res.partner.address,type:0 @@ -5173,7 +5178,7 @@ msgstr "Communication" #. module: base #: view:ir.actions.report.xml:0 msgid "RML Report" -msgstr "" +msgstr "RML Report" #. module: base #: model:ir.model,name:base.model_ir_server_object_lines @@ -5221,7 +5226,7 @@ msgstr "Nigeria" #: code:addons/base/ir/ir_model.py:250 #, python-format msgid "For selection fields, the Selection Options must be given!" -msgstr "" +msgstr "For selection fields, the Selection Options must be given!" #. module: base #: model:ir.actions.act_window,name:base.action_partner_sms_send @@ -5269,6 +5274,13 @@ msgid "" "installed the CRM, with the history tab, you can track all the interactions " "with a partner such as opportunities, emails, or sales orders issued." msgstr "" +"Customers (also called Partners in other areas of the system) helps you " +"manage your address book of companies whether they are prospects, customers " +"and/or suppliers. The partner form allows you to track and record all the " +"necessary information to interact with your partners from the company " +"address to their contacts as well as pricelists, and much more. If you " +"installed the CRM, with the history tab, you can track all interactions with " +"a partner such as opportunities, emails, or sales orders issued." #. module: base #: model:res.country,name:base.ph @@ -5293,7 +5305,7 @@ msgstr "Content" #. module: base #: help:ir.rule,global:0 msgid "If no group is specified the rule is global and applied to everyone" -msgstr "" +msgstr "If no group is specified the rule is global and applied to everyone" #. module: base #: model:res.country,name:base.td @@ -5370,6 +5382,9 @@ msgid "" "groups. If this field is empty, OpenERP will compute visibility based on the " "related object's read access." msgstr "" +"If you have groups, the visibility of this menu will be based on these " +"groups. If this field is empty, OpenERP will compute visibility based on the " +"related object's read access." #. module: base #: model:ir.actions.act_window,name:base.action_ui_view_custom @@ -5511,7 +5526,7 @@ msgstr "Spanish (EC) / Español (EC)" #. module: base #: help:ir.ui.view,xml_id:0 msgid "ID of the view defined in xml file" -msgstr "" +msgstr "ID of the view defined in xml file" #. module: base #: model:ir.model,name:base.model_base_module_import @@ -5527,7 +5542,7 @@ msgstr "American Samoa" #. module: base #: help:ir.actions.act_window,res_model:0 msgid "Model name of the object to open in the view window" -msgstr "" +msgstr "Model name of the object to open in the view window" #. module: base #: field:res.log,secondary:0 @@ -5707,11 +5722,15 @@ msgid "" "Warning: if \"email_from\" and \"smtp_server\" aren't configured, it won't " "be possible to email new users." msgstr "" +"If an email is provided, the user will be sent a message welcoming them.\n" +"\n" +"Warning: if \"email_from\" and \"smtp_server\" aren't configured, it won't " +"be possible to email new users." #. module: base #: selection:base.language.install,lang:0 msgid "Flemish (BE) / Vlaams (BE)" -msgstr "" +msgstr "Flemish (BE) / Vlaams (BE)" #. module: base #: field:ir.cron,interval_number:0 @@ -5761,7 +5780,7 @@ msgstr "ir.actions.todo" #: code:addons/base/res/res_config.py:94 #, python-format msgid "Couldn't find previous ir.actions.todo" -msgstr "" +msgstr "Couldn't find previous ir.actions.todo" #. module: base #: view:ir.actions.act_window:0 @@ -5776,7 +5795,7 @@ msgstr "Custom Shortcuts" #. module: base #: selection:base.language.install,lang:0 msgid "Vietnamese / Tiếng Việt" -msgstr "" +msgstr "Vietnamese / Tiếng Việt" #. module: base #: model:res.country,name:base.dz @@ -5791,7 +5810,7 @@ msgstr "Belgium" #. module: base #: model:ir.model,name:base.model_osv_memory_autovacuum msgid "osv_memory.autovacuum" -msgstr "" +msgstr "osv_memory.autovacuum" #. module: base #: field:base.language.export,lang:0 @@ -5824,30 +5843,30 @@ msgstr "Companies" #. module: base #: view:res.lang:0 msgid "%H - Hour (24-hour clock) [00,23]." -msgstr "" +msgstr "%H - Hour (24-hour clock) [00,23]." #. module: base #: model:ir.model,name:base.model_res_widget msgid "res.widget" -msgstr "" +msgstr "res.widget" #. module: base #: code:addons/base/ir/ir_model.py:258 #, python-format msgid "Model %s does not exist!" -msgstr "" +msgstr "Model %s does not exist!" #. module: base #: code:addons/base/res/res_lang.py:159 #, python-format msgid "You cannot delete the language which is User's Preferred Language !" -msgstr "" +msgstr "You cannot delete the language which is User's Preferred Language !" #. module: base #: code:addons/fields.py:103 #, python-format msgid "Not implemented get_memory method !" -msgstr "" +msgstr "Not implemented get_memory method !" #. module: base #: view:ir.actions.server:0 @@ -5894,7 +5913,7 @@ msgstr "Neutral Zone" #. module: base #: selection:base.language.install,lang:0 msgid "Hindi / हिंदी" -msgstr "" +msgstr "Hindi / हिंदी" #. module: base #: view:ir.model:0 @@ -5941,7 +5960,7 @@ msgstr "Window Actions" #. module: base #: view:res.lang:0 msgid "%I - Hour (12-hour clock) [01,12]." -msgstr "" +msgstr "%I - Hour (12-hour clock) [01,12]." #. module: base #: selection:publisher_warranty.contract.wizard,state:0 @@ -5979,12 +5998,14 @@ msgid "" "View type: set to 'tree' for a hierarchical tree view, or 'form' for other " "views" msgstr "" +"View type: set to 'tree' for a hierarchical tree view, or 'form' for other " +"views" #. module: base #: code:addons/base/res/res_config.py:421 #, python-format msgid "Click 'Continue' to configure the next addon..." -msgstr "" +msgstr "Click 'Continue' to configure the next addon..." #. module: base #: field:ir.actions.server,record_id:0 @@ -6025,7 +6046,7 @@ msgstr "" #: code:addons/base/ir/ir_actions.py:629 #, python-format msgid "Please specify server option --email-from !" -msgstr "" +msgstr "Please specify server option --email-from !" #. module: base #: field:base.language.import,name:0 @@ -6085,6 +6106,7 @@ msgid "" "It gives the status if the tip has to be displayed or not when a user " "executes an action" msgstr "" +"It shows if the tip is to be displayed or not when a user executes an action" #. module: base #: view:ir.model:0 @@ -6141,7 +6163,7 @@ msgstr "Code" #. module: base #: model:ir.model,name:base.model_res_config_installer msgid "res.config.installer" -msgstr "" +msgstr "res.config.installer" #. module: base #: model:res.country,name:base.mc @@ -6185,7 +6207,7 @@ msgstr "Sequence Codes" #. module: base #: selection:base.language.install,lang:0 msgid "Spanish (CO) / Español (CO)" -msgstr "" +msgstr "Spanish (CO) / Español (CO)" #. module: base #: view:base.module.configuration:0 @@ -6193,6 +6215,8 @@ msgid "" "All pending configuration wizards have been executed. You may restart " "individual wizards via the list of configuration wizards." msgstr "" +"All pending configuration wizards have been executed. You may restart " +"individual wizards via the list of configuration wizards." #. module: base #: wizard_button:server.action.create,step_1,create:0 @@ -6202,7 +6226,7 @@ msgstr "Create" #. module: base #: view:ir.sequence:0 msgid "Current Year with Century: %(year)s" -msgstr "" +msgstr "Current Year with Century: %(year)s" #. module: base #: field:ir.exports,export_fields:0 @@ -6217,13 +6241,13 @@ msgstr "France" #. module: base #: model:ir.model,name:base.model_res_log msgid "res.log" -msgstr "" +msgstr "res.log" #. module: base #: help:ir.translation,module:0 #: help:ir.translation,xml_id:0 msgid "Maps to the ir_model_data for which this translation is provided." -msgstr "" +msgstr "Maps to the ir_model_data for which this translation is provided." #. module: base #: view:workflow.activity:0 @@ -6317,7 +6341,7 @@ msgstr "Todo" #. module: base #: field:ir.attachment,datas:0 msgid "File Content" -msgstr "" +msgstr "File Content" #. module: base #: model:res.country,name:base.pa @@ -6334,12 +6358,13 @@ msgstr "Ltd" msgid "" "The group that a user must have to be authorized to validate this transition." msgstr "" +"The group that a user must have to be authorized to validate this transition." #. module: base #: constraint:res.config.users:0 #: constraint:res.users:0 msgid "The chosen company is not in the allowed companies for this user" -msgstr "" +msgstr "The chosen company is not in the allowed companies for this user" #. module: base #: model:res.country,name:base.gi @@ -6361,6 +6386,7 @@ msgstr "Pitcairn Island" msgid "" "We suggest to reload the menu tab to see the new menus (Ctrl+T then Ctrl+R)." msgstr "" +"We suggest reloading the menu tab to see the new menus (Ctrl+T then Ctrl+R)." #. module: base #: model:ir.actions.act_window,name:base.action_rule @@ -6413,7 +6439,7 @@ msgstr "Search View" #. module: base #: sql_constraint:res.lang:0 msgid "The code of the language must be unique !" -msgstr "" +msgstr "The code of the language must be unique !" #. module: base #: model:ir.actions.act_window,name:base.action_attachment @@ -6456,7 +6482,7 @@ msgstr "Write Access" #. module: base #: view:res.lang:0 msgid "%m - Month number [01,12]." -msgstr "" +msgstr "%m - Month number [01,12]." #. module: base #: field:res.bank,city:0 @@ -6514,7 +6540,7 @@ msgstr "English (US)" #: view:ir.model.data:0 #: model:ir.ui.menu,name:base.ir_model_data_menu msgid "Object Identifiers" -msgstr "" +msgstr "Object Identifiers" #. module: base #: model:ir.actions.act_window,help:base.action_partner_title_partner @@ -6522,11 +6548,13 @@ msgid "" "Manage the partner titles you want to have available in your system. The " "partner titles is the legal status of the company: Private Limited, SA, etc." msgstr "" +"Manage the partner titles you want to have available in your system. The " +"partner title is the legal status of the company: Private Limited, SA, etc." #. module: base #: view:base.language.export:0 msgid "To browse official translations, you can start with these links:" -msgstr "" +msgstr "To browse official translations, you can start with these links:" #. module: base #: code:addons/base/ir/ir_model.py:484 @@ -6535,6 +6563,8 @@ msgid "" "You can not read this document (%s) ! Be sure your user belongs to one of " "these groups: %s." msgstr "" +"You can not read this document (%s) ! Be sure your user belongs to one of " +"these groups: %s." #. module: base #: view:res.bank:0 @@ -6553,7 +6583,7 @@ msgstr "Installed version" #. module: base #: selection:base.language.install,lang:0 msgid "Mongolian / монгол" -msgstr "" +msgstr "Mongolian / монгол" #. module: base #: model:res.country,name:base.mr @@ -6568,7 +6598,7 @@ msgstr "ir.translation" #. module: base #: view:base.module.update:0 msgid "Module update result" -msgstr "" +msgstr "Module update result" #. module: base #: view:workflow.activity:0 @@ -6590,7 +6620,7 @@ msgstr "Parent Company" #. module: base #: selection:base.language.install,lang:0 msgid "Spanish (CR) / Español (CR)" -msgstr "" +msgstr "Spanish (CR) / Español (CR)" #. module: base #: field:res.currency.rate,rate:0 @@ -6630,6 +6660,9 @@ msgid "" "for the currency: %s \n" "at the date: %s" msgstr "" +"No rate found \n" +"for the currency: %s \n" +"at the date: %s" #. module: base #: model:ir.actions.act_window,help:base.action_ui_view_custom @@ -6637,6 +6670,8 @@ msgid "" "Customized views are used when users reorganize the content of their " "dashboard views (via web client)" msgstr "" +"Customised views are used when users reorganise the content of their " +"dashboard views (via web client)" #. module: base #: field:ir.model,name:0 @@ -6675,7 +6710,7 @@ msgstr "Icon" #. module: base #: help:ir.model.fields,model_id:0 msgid "The model this field belongs to" -msgstr "" +msgstr "The model this field belongs to" #. module: base #: model:res.country,name:base.mq @@ -6685,7 +6720,7 @@ msgstr "Martinique (French)" #. module: base #: view:ir.sequence.type:0 msgid "Sequences Type" -msgstr "" +msgstr "Sequences Type" #. module: base #: model:ir.actions.act_window,name:base.res_request-act @@ -6709,7 +6744,7 @@ msgstr "Or" #: model:ir.actions.act_window,name:base.res_log_act_window #: model:ir.ui.menu,name:base.menu_res_log_act_window msgid "Client Logs" -msgstr "" +msgstr "Client Logs" #. module: base #: model:res.country,name:base.al @@ -6728,6 +6763,8 @@ msgid "" "You cannot delete the language which is Active !\n" "Please de-activate the language first." msgstr "" +"You cannot delete a language which is active !\n" +"Please de-activate the language first." #. module: base #: view:base.language.install:0 @@ -6736,6 +6773,8 @@ msgid "" "Please be patient, this operation may take a few minutes (depending on the " "number of modules currently installed)..." msgstr "" +"Please be patient, this operation may take a few minutes (depending on the " +"number of modules currently installed)..." #. module: base #: field:ir.ui.menu,child_id:0 @@ -6754,18 +6793,18 @@ msgstr "Problem in configuration `Record Id` in Server Action!" #: code:addons/orm.py:2316 #, python-format msgid "ValidateError" -msgstr "" +msgstr "ValidateError" #. module: base #: view:base.module.import:0 #: view:base.module.update:0 msgid "Open Modules" -msgstr "" +msgstr "Open Modules" #. module: base #: model:ir.actions.act_window,help:base.action_res_bank_form msgid "Manage bank records you want to be used in the system." -msgstr "" +msgstr "Manage bank records you want to be used in the system." #. module: base #: view:base.module.import:0 @@ -6783,6 +6822,8 @@ msgid "" "The path to the main report file (depending on Report Type) or NULL if the " "content is in another field" msgstr "" +"The path to the main report file (depending on Report Type) or NULL if the " +"content is in another field" #. module: base #: model:res.country,name:base.la @@ -6809,6 +6850,8 @@ msgid "" "The sum of the data (2nd field) is null.\n" "We can't draw a pie chart !" msgstr "" +"The sum of the data (2nd field) is null.\n" +"We can't draw a pie chart !" #. module: base #: model:ir.ui.menu,name:base.menu_lunch_reporting @@ -6830,7 +6873,7 @@ msgstr "Togo" #. module: base #: selection:ir.module.module,license:0 msgid "Other Proprietary" -msgstr "" +msgstr "Other Proprietary" #. module: base #: selection:workflow.activity,kind:0 @@ -6841,7 +6884,7 @@ msgstr "Stop All" #: code:addons/orm.py:412 #, python-format msgid "The read_group method is not implemented on this object !" -msgstr "" +msgstr "The read_group method is not implemented on this object !" #. module: base #: view:ir.model.data:0 @@ -6861,7 +6904,7 @@ msgstr "Cascade" #. module: base #: field:workflow.transition,group_id:0 msgid "Group Required" -msgstr "" +msgstr "Group Required" #. module: base #: view:ir.actions.configuration.wizard:0 @@ -6884,17 +6927,19 @@ msgid "" "Enable this if you want to execute missed occurences as soon as the server " "restarts." msgstr "" +"Enable this if you want to execute missed occurences as soon as the server " +"restarts." #. module: base #: view:base.module.upgrade:0 msgid "Start update" -msgstr "" +msgstr "Start update" #. module: base #: code:addons/base/publisher_warranty/publisher_warranty.py:144 #, python-format msgid "Contract validation error" -msgstr "" +msgstr "Contract validation error" #. module: base #: field:res.country.state,name:0 @@ -6921,7 +6966,7 @@ msgstr "ir.actions.report.xml" #. module: base #: model:res.partner.title,shortcut:base.res_partner_title_miss msgid "Mss" -msgstr "" +msgstr "Mss" #. module: base #: model:ir.model,name:base.model_ir_ui_view @@ -6931,7 +6976,7 @@ msgstr "ir.ui.view" #. module: base #: constraint:res.partner:0 msgid "Error ! You can not create recursive associated members." -msgstr "" +msgstr "Error ! You can not create recursive associated members." #. module: base #: help:res.lang,code:0 @@ -6946,7 +6991,7 @@ msgstr "OpenERP Partners" #. module: base #: model:ir.ui.menu,name:base.menu_hr_manager msgid "HR Manager Dashboard" -msgstr "" +msgstr "HR Manager Dashboard" #. module: base #: code:addons/base/module/module.py:253 @@ -6954,11 +6999,12 @@ msgstr "" msgid "" "Unable to install module \"%s\" because an external dependency is not met: %s" msgstr "" +"Unable to install module \"%s\" because an external dependency is not met: %s" #. module: base #: view:ir.module.module:0 msgid "Search modules" -msgstr "" +msgstr "Search modules" #. module: base #: model:res.country,name:base.by @@ -6983,6 +7029,10 @@ msgid "" "not connect to the system. You can assign them groups in order to give them " "specific access to the applications they need to use in the system." msgstr "" +"Create and manage users that will connect to the system. Users can be " +"deactivated should there be a period of time during which they will/should " +"not connect to the system. You can assign them groups to give them specific " +"access to the applications they need to use." #. module: base #: selection:res.request,priority:0 @@ -6998,13 +7048,13 @@ msgstr "Street2" #. module: base #: model:ir.actions.act_window,name:base.action_view_base_module_update msgid "Module Update" -msgstr "" +msgstr "Module Update" #. module: base #: code:addons/base/module/wizard/base_module_upgrade.py:95 #, python-format msgid "Following modules are not installed or unknown: %s" -msgstr "" +msgstr "Following modules are not installed or unknown: %s" #. module: base #: view:ir.cron:0 @@ -7033,7 +7083,7 @@ msgstr "Open Window" #. module: base #: field:ir.actions.act_window,auto_search:0 msgid "Auto Search" -msgstr "" +msgstr "Auto Search" #. module: base #: field:ir.actions.act_window,filter:0 @@ -7079,25 +7129,25 @@ msgstr "Load" #: help:res.config.users,name:0 #: help:res.users,name:0 msgid "The new user's real name, used for searching and most listings" -msgstr "" +msgstr "The new user's real name, used for searching and most listings" #. module: base #: code:addons/osv.py:154 #: code:addons/osv.py:156 #, python-format msgid "Integrity Error" -msgstr "" +msgstr "Integrity Error" #. module: base #: model:ir.model,name:base.model_ir_wizard_screen msgid "ir.wizard.screen" -msgstr "" +msgstr "ir.wizard.screen" #. module: base #: code:addons/base/ir/ir_model.py:223 #, python-format msgid "Size of the field can never be less than 1 !" -msgstr "" +msgstr "Size of the field can never be less than 1 !" #. module: base #: model:res.country,name:base.so @@ -7107,7 +7157,7 @@ msgstr "Somalia" #. module: base #: selection:publisher_warranty.contract,state:0 msgid "Terminated" -msgstr "" +msgstr "Terminated" #. module: base #: model:res.partner.category,name:base.res_partner_category_13 @@ -7117,7 +7167,7 @@ msgstr "Important customers" #. module: base #: view:res.lang:0 msgid "Update Terms" -msgstr "" +msgstr "Update Terms" #. module: base #: field:partner.sms.send,mobile_to:0 @@ -7136,7 +7186,7 @@ msgstr "Arguments" #: code:addons/orm.py:716 #, python-format msgid "Database ID doesn't exist: %s : %s" -msgstr "" +msgstr "Database ID doesn't exist: %s : %s" #. module: base #: selection:ir.module.module,license:0 @@ -7152,7 +7202,7 @@ msgstr "GPL Version 3" #: code:addons/orm.py:836 #, python-format msgid "key '%s' not found in selection field '%s'" -msgstr "" +msgstr "key '%s' not found in selection field '%s'" #. module: base #: view:partner.wizard.ean.check:0 @@ -7163,7 +7213,7 @@ msgstr "Correct EAN13" #: code:addons/orm.py:2317 #, python-format msgid "The value \"%s\" for the field \"%s\" is not in the selection" -msgstr "" +msgstr "The value \"%s\" for the field \"%s\" is not in the selection" #. module: base #: field:res.partner,customer:0 diff --git a/openerp/addons/base/test/test_osv_expression.yml b/openerp/addons/base/test/test_osv_expression.yml index 97ab6c9e83c..43fea5c16cf 100644 --- a/openerp/addons/base/test/test_osv_expression.yml +++ b/openerp/addons/base/test/test_osv_expression.yml @@ -466,3 +466,38 @@ assert len(all_ids) == 1, "Must match India only, got %r"%all_ids all_ids = self.search(cr, uid, [('name', '=ilike', 'z%')]) assert len(all_ids) == 3, "Must match only countries with names starting with Z (currently 3), got %r"%all_ids +- + Use the create_date column on res.country (which doesn't declare it in _columns). +- + !python {model: res.country }: | + ids = self.search(cr, uid, [('create_date', '<', '2001-01-01 12:00:00')]) + + +- + Verify that invalid expressions are refused, even for magic fields +- + !python {model: res.country }: | + try: + self.search(cr, uid, [('does_not_exist', '=', 'foo')]) + raise AssertionError('Invalid fields should not be accepted') + except ValueError: + pass + + try: + self.search(cr, uid, [('create_date', '>>', 'foo')]) + raise AssertionError('Invalid operators should not be accepted') + except ValueError: + pass + + import psycopg2 + try: + cr._default_log_exceptions = False + cr.execute('SAVEPOINT expression_failure_test') + self.search(cr, uid, [('create_date', '=', "1970-01-01'); --")]) + # if the above search gives no error, the operand was not escaped! + cr.execute('RELEASE SAVEPOINT expression_failure_test') + raise AssertionError('Operands should always be SQL escaped') + except psycopg2.DataError: + # Should give: 'DataError: invalid input syntax for type timestamp' or similar + cr.execute('ROLLBACK TO SAVEPOINT expression_failure_test') + diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index bbf4569cba8..cb8cfea583f 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -126,6 +126,7 @@ import logging from openerp.tools import flatten, reverse_enumerate import fields import openerp.modules +from openerp.osv.orm import MAGIC_COLUMNS #.apidoc title: Domain Expressions @@ -413,7 +414,7 @@ class expression(object): # check if the expression is valid if not is_leaf(e): - raise ValueError('Bad domain expression: %r, %r is not a valid term.' % (exp, e)) + raise ValueError("Invalid term %r in domain expression %r" % (e, exp)) # normalize the leaf's operator e = normalize_leaf(*e) @@ -421,41 +422,47 @@ class expression(object): left, operator, right = e working_table = table # The table containing the field (the name provided in the left operand) - fargs = left.split('.', 1) + field_path = left.split('.', 1) # If the field is _inherits'd, search for the working_table, # and extract the field. - if fargs[0] in table._inherit_fields: + if field_path[0] in table._inherit_fields: while True: - field = working_table._columns.get(fargs[0]) + field = working_table._columns.get(field_path[0]) if field: self.__field_tables[i] = working_table break - next_table = working_table.pool.get(working_table._inherit_fields[fargs[0]][0]) + next_table = working_table.pool.get(working_table._inherit_fields[field_path[0]][0]) if next_table not in self.__all_tables: self.__joins.append('%s."%s"=%s."%s"' % (next_table._table, 'id', working_table._table, working_table._inherits[next_table._name])) self.__all_tables.add(next_table) working_table = next_table # Or (try to) directly extract the field. else: - field = working_table._columns.get(fargs[0]) + field = working_table._columns.get(field_path[0]) if not field: if left == 'id' and operator == 'child_of': ids2 = to_ids(right, table) dom = child_of_domain(left, ids2, working_table) self.__exp = self.__exp[:i] + dom + self.__exp[i+1:] + else: + # field could not be found in model columns, it's probably invalid, unless + # it's one of the _log_access special fields + # TODO: make these fields explicitly available in self.columns instead! + if field_path[0] not in MAGIC_COLUMNS: + raise ValueError("Invalid field %r in domain expression %r" % (left, exp)) continue field_obj = table.pool.get(field._obj) - if len(fargs) > 1: + if len(field_path) > 1: if field._type == 'many2one': - right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context) - self.__exp[i] = (fargs[0], 'in', right) + right = field_obj.search(cr, uid, [(field_path[1], operator, right)], context=context) + self.__exp[i] = (field_path[0], 'in', right) # Making search easier when there is a left operand as field.o2m or field.m2m if field._type in ['many2many', 'one2many']: - right = field_obj.search(cr, uid, [(fargs[1], operator, right)], context=context) - right1 = table.search(cr, uid, [(fargs[0], 'in', right)], context=context) + right = field_obj.search(cr, uid, [(field_path[1], operator, right)], context=context) + right1 = table.search(cr, uid, [(field_path[0], 'in', right)], context=context) self.__exp[i] = ('id', 'in', right1) if not isinstance(field, fields.property): @@ -657,6 +664,12 @@ class expression(object): def __leaf_to_sql(self, leaf, table): left, operator, right = leaf + # final sanity checks - should never fail + assert operator in (TERM_OPERATORS + ('inselect',)), \ + "Invalid operator %r in domain term %r" % (operator, leaf) + assert leaf in (TRUE_LEAF, FALSE_LEAF) or left in table._all_columns \ + or left in MAGIC_COLUMNS, "Invalid field %r in domain term %r" % (left, leaf) + if leaf == TRUE_LEAF: query = 'TRUE' params = [] @@ -704,8 +717,8 @@ class expression(object): query = '(%s OR %s."%s" IS NULL)' % (query, table._table, left) elif check_nulls and operator == 'not in': query = '(%s AND %s."%s" IS NOT NULL)' % (query, table._table, left) # needed only for TRUE. - else: # Must not happen. - pass + else: # Must not happen + raise ValueError("Invalid domain term %r" % (leaf,)) elif right == False and (left in table._columns) and table._columns[left]._type=="boolean" and (operator == '='): query = '(%s."%s" IS NULL or %s."%s" = false )' % (table._table, left, table._table, left) @@ -725,15 +738,12 @@ class expression(object): elif (operator == '=?'): if (right is False or right is None): + # '=?' is a short-circuit that makes the term TRUE if right is None or False query = 'TRUE' params = [] - elif left in table._columns: - format = table._columns[left]._symbol_set[0] - query = '(%s."%s" = %s)' % (table._table, left, format) - params = table._columns[left]._symbol_set[1](right) else: - query = "(%s.\"%s\" = '%%s')" % (table._table, left) - params = right + # '=?' behaves like '=' in other cases + query, params = self.__leaf_to_sql((left, '=', right), table) elif left == 'id': query = '%s.id %s %%s' % (table._table, operator) @@ -749,11 +759,11 @@ class expression(object): query = '(unaccent(%s."%s") %s unaccent(%s))' % (table._table, left, sql_operator, format) else: query = '(%s."%s" %s %s)' % (table._table, left, sql_operator, format) - else: - if self.has_unaccent and sql_operator in ('ilike', 'not ilike'): - query = "(unaccent(%s.\"%s\") %s unaccent('%s'))" % (table._table, left, sql_operator, right) - else: - query = "(%s.\"%s\" %s '%s')" % (table._table, left, sql_operator, right) + elif left in MAGIC_COLUMNS: + query = "(%s.\"%s\" %s %%s)" % (table._table, left, sql_operator) + params = right + else: # Must not happen + raise ValueError("Invalid field %r in domain term %r" % (left, leaf)) add_null = False if need_wildcard: diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index d0f012bd112..d0bbc630357 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -55,7 +55,6 @@ import types import warnings from lxml import etree -import expression import fields import openerp import openerp.netsvc as netsvc @@ -586,6 +585,17 @@ class MetaModel(type): self.module_to_models.setdefault(self._module, []).append(self) +# Definition of log access columns, automatically added to models if +# self._log_access is True +LOG_ACCESS_COLUMNS = { + 'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', + 'create_date': 'TIMESTAMP', + 'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', + 'write_date': 'TIMESTAMP' +} +# special columns automatically created by the ORM +MAGIC_COLUMNS = ['id'] + LOG_ACCESS_COLUMNS.keys() + class BaseModel(object): """ Base class for OpenERP models. @@ -2492,7 +2502,7 @@ class BaseModel(object): # iterate on the database columns to drop the NOT NULL constraints # of fields which were required but have been removed (or will be added by another module) columns = [c for c in self._columns if not (isinstance(self._columns[c], fields.function) and not self._columns[c].store)] - columns += ('id', 'write_uid', 'write_date', 'create_uid', 'create_date') # openerp access columns + columns += MAGIC_COLUMNS cr.execute("SELECT a.attname, a.attnotnull" " FROM pg_class c, pg_attribute a" " WHERE c.relname=%s" @@ -2559,7 +2569,7 @@ class BaseModel(object): column_data = self._select_column_data(cr) for k, f in self._columns.iteritems(): - if k in ('id', 'write_uid', 'write_date', 'create_uid', 'create_date'): + if k in MAGIC_COLUMNS: continue # Don't update custom (also called manual) fields if f.manual and not update_custom_fields: @@ -2861,23 +2871,17 @@ class BaseModel(object): def _add_log_columns(self, cr): - logs = { - 'create_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', - 'create_date': 'TIMESTAMP', - 'write_uid': 'INTEGER REFERENCES res_users ON DELETE SET NULL', - 'write_date': 'TIMESTAMP' - } - for k in logs: + for field, field_def in LOG_ACCESS_COLUMNS.iteritems(): cr.execute(""" SELECT c.relname FROM pg_class c, pg_attribute a WHERE c.relname=%s AND a.attname=%s AND c.oid=a.attrelid - """, (self._table, k)) + """, (self._table, field)) if not cr.rowcount: - cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, k, logs[k])) + cr.execute('ALTER TABLE "%s" ADD COLUMN "%s" %s' % (self._table, field, field_def)) cr.commit() self.__schema.debug("Table '%s': added column '%s' with definition=%s", - self._table, k, logs[k]) + self._table, field, field_def) def _select_column_data(self, cr): @@ -4142,7 +4146,6 @@ class BaseModel(object): domain = [('active', '=', 1)] if domain: - import expression e = expression.expression(cr, user, domain, self, context) tables = e.get_tables() where_clause, where_params = e.to_sql() @@ -4368,7 +4371,7 @@ class BaseModel(object): for f in fields: ftype = fields[f]['type'] - if self._log_access and f in ('create_date', 'create_uid', 'write_date', 'write_uid'): + if self._log_access and f in LOG_ACCESS_COLUMNS: del data[f] if f in default: @@ -4630,6 +4633,9 @@ class BaseModel(object): return True +# keep this import here, at top it will cause dependency cycle errors +import expression + class Model(BaseModel): """Main super-class for regular database-persisted OpenERP models. diff --git a/openerp/sql_db.py b/openerp/sql_db.py index 79606b9101c..8038c39486d 100644 --- a/openerp/sql_db.py +++ b/openerp/sql_db.py @@ -183,6 +183,8 @@ class Cursor(object): self.__caller = False self.__closer = False + self._default_log_exceptions = True + def __del__(self): if not self.__closed: # Oops. 'self' has not been closed explicitly. @@ -199,7 +201,7 @@ class Cursor(object): self._close(True) @check - def execute(self, query, params=None, log_exceptions=True): + def execute(self, query, params=None, log_exceptions=None): if '%d' in query or '%f' in query: self.__logger.warn(query) self.__logger.warn("SQL queries cannot contain %d or %f anymore. " @@ -212,11 +214,11 @@ class Cursor(object): params = params or None res = self._obj.execute(query, params) except psycopg2.ProgrammingError, pe: - if log_exceptions: + if self._default_log_exceptions or log_exceptions: self.__logger.error("Programming error: %s, in query %s", pe, query) raise except Exception: - if log_exceptions: + if self._default_log_exceptions or log_exceptions: self.__logger.exception("bad query: %s", self._obj.query or query) raise