From 39a80a13d3e9ff9733a92b0e4a2b3ddc7a08d350 Mon Sep 17 00:00:00 2001 From: Hao Liu Date: Sun, 3 May 2015 11:55:06 +0800 Subject: [PATCH] release 2.9.0 --- HISTORY.rst | 17 +++++++ README.md | 4 ++ completions.py | 3 +- config/commands/main.sublime-commands | 4 +- config/messages/2.9.0.md | 21 +++++++++ config/settings/package.sublime-settings | 2 +- config/settings/toolingapi.sublime-settings | 22 +-------- context.py | 1 + docs/utilities.md | 5 ++ main.py | 22 +++++---- messages.json | 1 + processor.py | 8 +++- salesforce/api/metadata.py | 29 ++++++++---- salesforce/api/tooling.py | 5 +- util.py | 51 ++++++++++++++++----- 15 files changed, 139 insertions(+), 56 deletions(-) create mode 100644 config/messages/2.9.0.md diff --git a/HISTORY.rst b/HISTORY.rst index db92594..6d5e06f 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -4,6 +4,23 @@ Release History --------------- +Release 2.9.0 (2015-05-03) +++++++++++++++++++ +* Fix bug for messy code in debug log detail +* Enhancement for not refreshing sidebar when ``retrieve_files_from_other_server`` +* Enhancement for adding folder name to retrieve request when ``list_package`` for folders +* Enhancement for package.xml completion for folder name of Document, EmailTemplate, Dashboard and Report +* Enhancement for package.xml completion for AuraDefinitionBundle +* Enhancement for sobject completion, if there are two matched statements, ``insert prd`` and ``Product2 prd``, plugin will choose the second one as matched +* Enhancement for ``toggle_metadata_objects``, you can toggle metadata objects continually util you press ``ESC`` to exit +* Enhancement for ``generate_sobject_soql``, you can choose whether generate sobject SOQL of ``Custom``, ``Updateable`` or ``Createable`` +* Update workspace of default build-in project from ``C:/ForcedotcomWorkspace`` to empty +* Update name of default build-in project from ``pro-test`` to ``pro-sublime`` +* Update for ``toggle_metadata_objects``, after subscribe a new metadata object, don't refresh its folder again, just after you finish all toggle, you will need to confirm whether use refresh all subscribed metadata together +* Add ``toggle_metadata_objects`` document in ``docs/utilities.md`` +* Remove four deprecated settings, ``keep_config_history``, ``output_session_info``, ``delay_seconds_for_hidden_output_panel_when_failed`` and ``get_static_resource_body`` + + Release 2.8.9 (2015-04-28) ++++++++++++++++++ * Fix urgent bug for issue #22 diff --git a/README.md b/README.md index e2782af..ce19849 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@ You can install this plugin by searching ``haoide`` in package control, if you d If you are still using ``SublimeApex``, please remove it and install ``haoide``, because ``SublimeApex`` is no longer supported. +This plugin is in improvement stage, many ideas are generated in daily work of mine or my friends, if you have any idea or find any issue, please log issue by clicking ``Report Issue`` in the main menu, thanks for you help on improving this plugin. + +All of my motivation on this plugin come from your star, if you think this plugin is helpful in your daily work, please **star`` this plugin. + # Quick Link + Change Logs + All Demos diff --git a/completions.py b/completions.py index eea19a5..59d3242 100644 --- a/completions.py +++ b/completions.py @@ -60,8 +60,7 @@ def on_query_completions(self, view, prefix, locations): # File name completion if ch != ".": # List File Names - for name in util.get_metadata_elements(_dir): - completion_list.append((name+"\t"+_type, name)) + completion_list = util.get_metadata_elements(_type, folder) # Child content of file name completion if ch == ".": diff --git a/config/commands/main.sublime-commands b/config/commands/main.sublime-commands index a206d65..b8c2028 100644 --- a/config/commands/main.sublime-commands +++ b/config/commands/main.sublime-commands @@ -2,8 +2,8 @@ {"caption": "HaoIDE: Switch Project", "command": "switch_project"}, { - "caption": "HaoIDE: Toggle Metdata Types", - "command": "toggle_metadata_types" + "caption": "HaoIDE: Toggle Metdata Objects", + "command": "toggle_metadata_objects" }, { diff --git a/config/messages/2.9.0.md b/config/messages/2.9.0.md new file mode 100644 index 0000000..e81d4b1 --- /dev/null +++ b/config/messages/2.9.0.md @@ -0,0 +1,21 @@ +Build 2.9.0 +----------- +Release Date: 3 May 2015 + +* Fix bug for messy code in debug log detail +* Enhancement for not refreshing sidebar when ``retrieve_files_from_other_server`` +* Enhancement for adding folder name to retrieve request when ``list_package`` for folders +* Enhancement for package.xml completion for folder name of Document, EmailTemplate, Dashboard and Report +* Enhancement for package.xml completion for AuraDefinitionBundle +* Enhancement for sobject completion, if there are two matched statements, ``insert prd`` and ``Product2 prd``, plugin will choose the second one as matched +* Enhancement for ``toggle_metadata_objects``, you can toggle metadata objects continually util you press ``ESC`` to exit +* Enhancement for ``generate_sobject_soql``, you can choose whether generate sobject SOQL of ``Custom``, ``Updateable`` or ``Createable`` +* Update ``inFolder`` and ``metaFile`` in ``metaObjects`` settings from string to boolean +* Update workspace of default build-in project from ``C:/ForcedotcomWorkspace`` to empty +* Update name of default build-in project from ``pro-test`` to ``pro-sublime`` +* Update for ``toggle_metadata_objects``, after subscribe a new metadata object, don't refresh its folder again, just after you finish all toggle, you will need to confirm whether use refresh all subscribed metadata together +* Add ``toggle_metadata_objects`` document in ``docs/utilities.md`` +* Remove four deprecated settings, ``keep_config_history``, ``output_session_info``, ``delay_seconds_for_hidden_output_panel_when_failed`` and ``get_static_resource_body`` + +Notes: You should restart your sublime after ``HaoIDE`` is upgraded +----------- \ No newline at end of file diff --git a/config/settings/package.sublime-settings b/config/settings/package.sublime-settings index 0859010..9ba6c35 100644 --- a/config/settings/package.sublime-settings +++ b/config/settings/package.sublime-settings @@ -1,6 +1,6 @@ { "name": "haoide", - "version": "2.8.9", + "version": "2.9.0", "description": "haoide is a Sublime Text 3 plugin for Salesforce and used for swift development on Force.com", "author": "Hao Liu", "email": "mouse.mliu@gmail.com", diff --git a/config/settings/toolingapi.sublime-settings b/config/settings/toolingapi.sublime-settings index 3a2100c..48f7484 100644 --- a/config/settings/toolingapi.sublime-settings +++ b/config/settings/toolingapi.sublime-settings @@ -13,7 +13,7 @@ // workspace can be "/Users//salesforce/workspace" "workspace" : "", "projects" : { - "pro-test" : { + "pro-sublime" : { /** * Below are Required Properties */ @@ -44,7 +44,7 @@ "allowed_packages" : [], // Allow custom workspace for every single project - "workspace": "C:/ForcedotcomWorkspace" + "workspace": "" } }, @@ -98,10 +98,6 @@ // Debug Mode "debug_mode": false, - // Indicate whether output session info in the console when you switch the project - // Deprecated, replace by `.config/session.json` - "output_session_info" : false, - // Indicate whether keep local change history of code file after code is saved successfully "keep_local_change_history" : false, @@ -116,11 +112,6 @@ // whether the console will be hidden automatically "hidden_console_on_modify" : false, - // Every time when you save component and error happened, the console will be open. - // however, you want it to be hidden automatically after several seconds - // Deprecated Settings - "delay_seconds_for_hidden_output_panel_when_failed" : 9999, - // Every time when you save component and succeed, the output panel will be open. // however, you want it to be hidden automatically after several seconds "delay_seconds_for_hidden_output_panel_when_succeed" : 1, @@ -128,11 +119,6 @@ // Indicate whether need to reload symbol tables when creating new project "reload_symbol_tables_when_create_project" : false, - // Indicate whether download StaticResource body - // This job is processed by Metadata API - // Deprecated, replaced by ``metadata api`` - "get_static_resource_body" : false, - // Indicate whether disable sobject fields completion "disable_fields_completion" : false, @@ -161,10 +147,6 @@ // Indicate whether keep execute_anonymous, query and run test history to local "keep_operation_history" : true, - // Indicate whether keep plugin settings, session, users and record types - // Deprecated, because we want to reuse the cache, so we must keep it - "keep_config_history" : true, - // Indicate whether switch project back to original after `deploy` or `retrieve from other server` is executed "switch_back_after_migration": true, diff --git a/context.py b/context.py index b9c2548..06a6f16 100644 --- a/context.py +++ b/context.py @@ -168,6 +168,7 @@ def get_settings(): settings["metadata_folders"] = [c["directoryName"] for c in components] settings["subscribed_metadata_objects"] = [c["xmlName"] for c in components if c["subscribe"]] settings["subscribed_metadata_folders"] = [c["directoryName"] for c in components if c["subscribe"]] + settings["metadata_objects_in_folder"] = [c["xmlName"] for c in components if c["inFolder"] == "true"] for component in components: settings[component["xmlName"]] = component settings[component["directoryName"]] = component diff --git a/docs/utilities.md b/docs/utilities.md index 16f134c..bce861a 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -33,6 +33,11 @@ There are some utilities to keep your work efficient as below * Click ``HaoIDE > Create Package.xml`` in the sidebar menu * You will see the created package.xml in it +## Toggle Metadata Objects +* Open your command palette and input HaoIDE: ``Toggle Metadata Objects`` +* Subscribe the metadata object util all you want are finished, press ``ESC`` +* After that, you should execute ``update_project`` command to download subscribed metadata + ## Combine Package.xml in folders to only one Step by Step If we have many ``package.xml`` files, for example, every developer will have his/her own ``package.xml`` to deploy, or every requirement will have one ``package.xml`` file, at the last stage of project implementation, only one deployment is required, so, how can we combine these package.xml files to only one. diff --git a/main.py b/main.py index 7f1b0a9..3514b99 100644 --- a/main.py +++ b/main.py @@ -226,7 +226,9 @@ def run(self): self.window.show_quick_panel(self.component_types, self.on_done) def on_done(self, index): - if index == -1: return + if index == -1: + self.window.run_command("update_project") + return chosen_type = self.component_types[index].split("=>")[1] subscribe_type, is_subscribe = None, False @@ -241,11 +243,7 @@ def on_done(self, index): sublime.status_message("%s is %s" % (subscribe_type, "subscribed" if is_subscribe else "unsubscribed")) - if not is_subscribe: return - settings = context.get_settings() - _dir = os.path.join(settings["workspace"], "src", settings[chosen_type]["directoryName"]) - types = util.build_folder_types([_dir]) - processor.handle_refresh_folder(types) + sublime.set_timeout(lambda:sublime.active_window().run_command("toggle_metadata_objects"), 10) class ReloadSobjectCacheCommand(sublime_plugin.WindowCommand): def __init__(self, *args, **kwargs): @@ -323,7 +321,15 @@ def run(self): def on_done(self, index): if index == -1: return - processor.handle_generate_sobject_soql(self.sobjects[index]) + self.sobject = self.sobjects[index] + + self.filters = ["all", "updateable", "createable", "custom"] + self.display_filters = [a.capitalize() for a in self.filters] + sublime.set_timeout(lambda:self.window.show_quick_panel(self.display_filters, self.on_choose_action), 10) + + def on_choose_action(self, index): + if not index: return + processor.handle_generate_sobject_soql(self.sobject, self.filters[index]) class ExportQueryToCsv(sublime_plugin.TextCommand): def run(self, edit): @@ -836,7 +842,7 @@ def on_done(self, index): # Change the chosen project as default # Split with ") " and get the second project name default_project = self.projects[index].split(") ")[1] - util.switch_project(default_project) + util.switch_project(default_project, False) types = {self.xmlName : [self._name]} processor.handle_retrieve_package(types, self.extract_to, diff --git a/messages.json b/messages.json index f8f717a..04885c0 100644 --- a/messages.json +++ b/messages.json @@ -9,5 +9,6 @@ "2.8.7": "config/messages/2.8.7.md", "2.8.8": "config/messages/2.8.8.md", "2.8.9": "config/messages/2.8.9.md", + "2.9.0": "config/messages/2.9.0.md", "install": "config/messages/install.txt" } \ No newline at end of file diff --git a/processor.py b/processor.py index 8694f64..e07c534 100644 --- a/processor.py +++ b/processor.py @@ -1041,7 +1041,7 @@ def handle_new_view_thread(thread, timeout): ThreadProgress(api, thread, wait_message, wait_message + ' Succeed') handle_new_view_thread(thread, timeout) -def handle_generate_sobject_soql(sobject, timeout=120): +def handle_generate_sobject_soql(sobject, filter, timeout=120): def handle_new_view_thread(thread, timeout): if thread.is_alive(): sublime.set_timeout(lambda: handle_new_view_thread(thread, timeout), timeout) @@ -1065,7 +1065,11 @@ def handle_new_view_thread(thread, timeout): settings = context.get_settings() api = ToolingApi(settings) - thread = threading.Thread(target=api.combine_soql, args=(sobject, )) + if filter != "all": + args = (sobject, filter, ) + else: + args = (sobject, ) + thread = threading.Thread(target=api.combine_soql, args=args) thread.start() wait_message = 'Generate SOQL for ' + sobject ThreadProgress(api, thread, wait_message, wait_message + ' Succeed') diff --git a/salesforce/api/metadata.py b/salesforce/api/metadata.py index 6461893..86b3bd4 100644 --- a/salesforce/api/metadata.py +++ b/salesforce/api/metadata.py @@ -317,8 +317,6 @@ def prepare_members(self, _types): """ type_with_folders = {} for record in records: - print (json.dumps(record)) - print (json.dumps(records)) if record["type"] == "EmailFolder": _type = "EmailTemplate" else: @@ -339,18 +337,33 @@ def prepare_members(self, _types): records.extend(self.list_package({_type : _folders})) - types_with_elements = {} + """Example of types_with_elements: + { + "EmailTemplate": ["test1/template1", "test2/template2"], + "Dashboard": ["test1/dashboard1"] + } + """ + type_with_files = {} for record in records: _type = record["type"] - if _type in types_with_elements: - types_with_elements[_type].append(record["fullName"]) + # Add files to elements + if _type in type_with_files: + type_with_files[_type].append(record["fullName"]) else: - types_with_elements[_type] = [record["fullName"]] + type_with_files[_type] = [record["fullName"]] + # Add folder and files in it to elements for _type in _types: - if _type in types_with_elements: - _types[_type] = types_with_elements[_type] + # Add file to related type + if _type in type_with_files: + _types[_type] = type_with_files[_type] + + # Add folder to related type + if _type in type_with_folders: + files = _types[_type] + files.extend(type_with_folders[_type]) + _types[_type] = files return _types diff --git a/salesforce/api/tooling.py b/salesforce/api/tooling.py index aabf341..3cb6052 100644 --- a/salesforce/api/tooling.py +++ b/salesforce/api/tooling.py @@ -528,7 +528,7 @@ def query_logs(self, last_n_logs, user_id=None): self.result = self.query(soql, is_toolingapi=True) return self.result - def combine_soql(self, sobject, contains_compound=True): + def combine_soql(self, sobject, action=None, contains_compound=True): """ Get the full field list soql by sobject * sobject -- sobject name, for example, Account, Contact @@ -545,6 +545,7 @@ def combine_soql(self, sobject, contains_compound=True): for field in fields: # http://www.salesforce.com/us/developer/docs/api/Content/compound_fields_address.htm if not contains_compound and field.get("queryByDistance"): continue + if not action or not field[action]: continue sobject_fields += field.get("name") + ", " self.result = { @@ -691,7 +692,7 @@ def retrieve_body(self, retrieve_url, timeout=120): try: response = requests.get(url, verify=False, headers=headers, timeout=timeout) - response.headers = "utf-8" + response.encoding = "UTF-8" except requests.exceptions.RequestException as e: self.result = { "Error Message": "Network connection timeout when issuing RETRIVING BODY request", diff --git a/util.py b/util.py index 8c71d5e..1099d64 100644 --- a/util.py +++ b/util.py @@ -376,6 +376,13 @@ def remove_comments(view, regions): is_comment_region = True break + # Check whether DML statement, for example + # insert prd | update prd | delete prd + # insert is not the correct variable type + pattern = '(insert|update|upsert|delete|undelete)+\\s+' + if re.match(pattern, view.substr(region), re.IGNORECASE): + continue + # If region is comment statement, just skip if not is_comment_region: matched_regions.append(region) @@ -399,7 +406,7 @@ def get_variable_type(view, pt, pattern): # 1. If no matched regions # 2. Only one matched region # 3. More than one matched region - if not uncomment_regions: + if not uncomment_regions: return "" elif len(uncomment_regions) == 1: matched_region = uncomment_regions[0] @@ -2323,7 +2330,7 @@ def display_active_project(view): display_message = "Default Project => " + settings["default_project_name"] view.set_status('default_project', display_message) -def switch_project(chosen_project): +def switch_project(chosen_project, add_to_workspace=True): """ Set the default project to the chosen one """ @@ -2348,7 +2355,8 @@ def switch_project(chosen_project): view.set_status('default_project', "Default Project => %s" % chosen_project) - add_project_to_workspace(context.get_settings()) + if add_to_workspace: + add_project_to_workspace(context.get_settings()) def add_project_to_workspace(settings): """Add new project folder to workspace @@ -2439,7 +2447,7 @@ def subl(args=[]): executable_path = app_path + "Contents/SharedSupport/bin/subl" subprocess.Popen([executable_path] + args) -def get_metadata_elements(metadata_dir): +def get_metadata_elements(meta_type, meta_folder): """ Get the name list by specified metadataObject Arguments: @@ -2451,24 +2459,45 @@ def get_metadata_elements(metadata_dir): names -- elements in the specified metadataObject folder """ - + settings = context.get_settings() elements = [] + completion_list = [] + metadata_dir = os.path.join(settings["workspace"], "src", meta_folder) for parent, dirnames, filenames in os.walk(metadata_dir): for _file in filenames: if _file.endswith("-meta.xml"): continue base, full_name = os.path.split(_file) name = full_name[:full_name.rfind(".")] - # Some metadata type have folders, for example, - # Document, Email, Dashboard or Report + # Some metadata type have folders if parent != metadata_dir: folder = os.path.split(parent)[1] - elements.append("%s/%s" % (folder, name)) - continue - elements.append(name) + # Document, Email, Dashboard or Report + print (meta_type, settings["metadata_objects_in_folder"]) + if meta_type in settings["metadata_objects_in_folder"]: + # Add folder to list + if folder not in elements: + elements.append(folder) + completion_list.append(("%s\t%s Folder" % (folder, meta_type), folder)) + + # Add files in folder to list + element = "%s/%s" % (folder, name) + elements.append(element) + completion_list.append(("%s\t%s" % (element, meta_type), element)) + continue + + # AuraDefinitionBundle + if meta_folder == "aura" and folder not in elements: + elements.append(folder) + completion_list.append(("%s\t%s" % (folder, meta_type), folder)) + continue + # Others + elif name not in elements: + elements.append(name) + completion_list.append(("%s\t%s" % (name, meta_type), name)) - return elements + return completion_list def export_profile_settings(): settings = context.get_settings()