Example #1
0
    def merge(self, remote_file, remote_content, automerge_rules,
              amerge_ref_is_local):

        # Fetch local content for this file
        local_file, local_content = self.get_local_content(remote_file)

        # Merge
        if remote_content is None:
            if local_content is None:
                merged_content = None
            else:
                merged_content = local_content

        elif local_content and local_content != remote_content:

            # If automerge selected try one or more strategies
            merge_strategy = None
            if automerge_rules:
                # Select reference. Automerge will try to keep the information
                # in it according to the rules. If it fails manual merge will
                # fire.
                if amerge_ref_is_local:
                    merged_content, merge_strategy = automerge(
                        local_content,
                        remote_content,
                        automerge_rules,
                    )
                else:
                    merged_content, merge_strategy = automerge(
                        remote_content,
                        local_content,
                        automerge_rules,
                    )

            if merge_strategy is None:
                old_local_file = "%s.local" % local_file
                local.write_file(old_local_file, local_content)
                local.write_file(local_file, remote_content)
                local.mergetool(old_local_file, local_file)
                merged_content = local.read_file(local_file)
                # Clean up extra temporary file
                os.remove(old_local_file)
            else:
                local.write_file(local_file, merged_content)
                if self.verbose > 0:
                    print("merged by %s" % merge_strategy)

        else:

            # No local content, or local matches remote
            merged_content = remote_content
            local.local_edit(local_file, merged_content, no_edit=True)

        return {
            'local': local_content,
            'remote': remote_content,
            'merged': merged_content
        }
Example #2
0
 def manual_edit(local_file, content):
     """Write empty file with given text"""
     if content['remote']:
         if content['remote'] == content['merged']:
             return content['merged']
         elif content['remote'] != content['merged']:
             raise Exception(
                 "File exists in remote and differs in content")
     else:
         return local.local_edit(local_file,
                                 initial_text,
                                 no_edit=True)
Example #3
0
def edit_paper_url(url):

    from vimbox.remote.paper_backend import StorageBackEnd, DOC_REGEX
    title, did, doc_id = DOC_REGEX.match(url).groups()
    paper_token = local.load_config()['DROPBOX_TOKEN']
    client = StorageBackEnd(paper_token)
    response = client.file_download(url)
    local_folder = local.get_local_file('.paper/')
    if not os.path.isdir(local_folder):
        os.mkdir(local_folder)
    local_file = "%s/%s--%s-%s.md" % (local_folder, title, did, doc_id)
    content = local.local_edit(local_file, response['content'])
    # Update remote if there are changes
    if content != response['content']:
        response = client.files_upload(content, url, response['revision'])
        if response['status'] == 'api-error':
            print(response['alert'])
    os.remove(local_file)
Example #4
0
    def update_rules(self, remote_file, content, password, status,
                     register_folder, remove_local):

        # Update remote
        if content['edited'] != content['remote']:

            # Try to push into remote
            if status != 'connection-error':
                error = self._push(content['edited'],
                                   remote_file,
                                   password=password)
            else:
                error = 'connection-error'

            # Inform the user
            if error is None:
                # We pushed sucessfully
                if self.verbose > 0:
                    print("%-12s %s" % (yellow("pushed"), remote_file))
            elif error == 'connection-error':
                # Offline
                if self.verbose > 0:
                    print("%-12s %s" % (red("offline"), remote_file))
            elif error == 'api-error':
                # This is not a normal state. Probably bug on our side or API
                # change/bug on the backend.
                if self.verbose > 0:
                    print("%-12s %s" % (red("api-error"), remote_file))
                    print("API error (something bad happened)")
            else:
                # This is most certainly a bug on our side
                raise Exception("Unknown _push error %s" % error)

            # Update local after updating remote

            # Register file in cache
            if register_folder and error != 'api-error':
                self.register_file(remote_file, password is not None)

            # Remove local copy if solicited, otherwise update it with new
            # content
            local_file = self.get_local_file(remote_file)
            if remove_local and os.path.isfile(local_file):
                if (content['merged'] != content['remote']
                        and error is not None):
                    # If we could not update the remote but we are forced to
                    # remove the file we must prompt user first
                    _ = input(
                        "We are offline but I have to remove local files."
                        " Will open the file once more for you to save "
                        "stuff, then it will be nuked. (press any key "
                        "when ready)")
                    local.local_edit(local_file, content['merged'])
                # Remove local file if solicited
                os.remove(local_file)
                if self.verbose > 0:
                    print("%-12s %s" % (red("cleaned"), local_file))

            elif content['local'] != content['merged']:
                # If local content was changed we need to update
                local.local_edit(local_file, content['merged'], no_edit=True)

        elif content['merged'] != content['local'] and not remove_local:
            # We overwrote local with remote
            local_file = self.get_local_file(remote_file)
            local.local_edit(local_file, content['merged'], no_edit=True)
            if self.verbose > 0:
                print("%-12s %s" % (yellow("pulled"), remote_file))
            if register_folder:
                self.register_file(remote_file, password is not None)

        elif all(content[key] is None
                 for key in ['merged', 'local', 'remote', 'edited']):
            # Edit aborted
            pass

        else:
            # No changes needed on either side
            if self.verbose > 0:
                print("%-12s %s" % (green("in-sync"), remote_file))
            if remove_local and os.path.isfile(local_file):
                os.remove(local_file)
                if self.verbose > 0:
                    print("%-12s %s" % (red("cleaned"), local_file))
            if register_folder:
                self.register_file(remote_file, password is not None)
Example #5
0
    def sync(self,
             remote_file,
             remove_local=None,
             force_creation=False,
             register_folder=True,
             password=None,
             automerge_rules=None,
             amerge_ref_is_local=False,
             edits=None):
        """
        Syncronize remote and local content with optional edit

        Edits will happen on a local copy that will be uploded when finished.

        remove_local        After sucesful push remove local content
        force_creation      Mandatory for new file on remote
        register_folder     Store file path in local cache
        password            Optional encryption/decription on client side
        automerge_rules     Allowed way to automerge
        amerge_ref_is_local If valid automerge use local as reference (default
                            is remote)
        """

        # Sanity checks
        if remote_file[-1] == '/':
            raise VimboxClientError("Can not edit folders")
        if remove_local is None:
            remove_local = self.config['remove_local']

        # Fetch remote content, merge if neccesary with local.mergetool
        # will provide local, remote and merged copies
        content, fetch_status, password = self.pull(
            remote_file,
            force_creation,
            password=password,
            automerge_rules=automerge_rules,
            amerge_ref_is_local=amerge_ref_is_local)

        # Apply edit if needed
        local_file = self.get_local_file(remote_file)
        dirname = os.path.dirname(local_file)
        if not os.path.isdir(dirname):
            os.mkdir(dirname)
        if edits:
            content['edited'] = edits(local_file, content)
        else:
            # No edits (still need to be sure we update local)
            content['edited'] = local.local_edit(local_file,
                                                 content['merged'],
                                                 no_edit=True)

        # Update local and remote
        # Abort if file being created but no changes
        if (content['edited'] is None and content['remote'] is not None
                and fetch_status != 'connection-error'):
            # For debug purposes
            VimboxClientError(
                "\nInvalid state: edited local_file non existing but remote"
                " does\n")

        # Pull again if recovered offline status
        if fetch_status == 'connection-error':
            content2, fetch_status, password = self.pull(
                remote_file,
                force_creation,
                password=password,
                automerge_rules=automerge_rules,
                amerge_ref_is_local=amerge_ref_is_local)
            if fetch_status != 'connection-error':
                content = content2
                content['edited'] = content['remote']

        self.update_rules(remote_file, content, password, fetch_status,
                          register_folder, remove_local)

        return content['local'] == content['remote']
Example #6
0
 def manual_edit(local_file, content):
     """Prompt user for edit"""
     return local.local_edit(local_file, content['merged'])