Example #1
0
    def mform(self, r, **attr):
        """
            Get the mobile form for the target resource

            @param r: the S3Request instance
            @param attr: controller attributes

            @returns: a JSON string
        """

        resource = self.resource

        msince = r.get_vars.get("msince")
        if msince:
            msince = s3_parse_datetime(msince)

        # Get the mobile form
        mform = S3MobileForm(resource).serialize(msince=msince)

        # Add controller and function for data exchange
        mform["controller"] = r.controller
        mform["function"] = r.function

        # Convert to JSON
        output = json.dumps(mform, separators=SEPARATORS)

        current.response.headers = {"Content-Type": "application/json"}
        return output
Example #2
0
    def __receive(self, r, **attr):
        """
            Respond to an incoming push

            @param r: the S3Request
            @param attr: the controller attributes
        """

        mixed = attr.get("mixed", False)
        get_vars = r.get_vars

        s3db = current.s3db
        db = current.db

        # Identify the sending repository
        repository_uuid = get_vars.get("repository")
        connector = None

        if repository_uuid:

            rtable = s3db.sync_repository
            query = rtable.uuid == repository_uuid
            row = current.db(query).select(limitby=(0, 1)).first()
            if row:
                connector = S3SyncRepository(row)

        # Check that the repository is registered and allowed to push
        if connector is None or not connector.accept_push:
            r.error(403, current.ERROR.NOT_PERMITTED)

        current.log.debug("S3Sync PUSH from %s (%s)" %
                          (connector.name, connector.apitype))

        # Get strategy and policy
        default_update_policy = S3ImportItem.POLICY.NEWER
        default_conflict_policy = S3ImportItem.POLICY.MASTER

        # Identify the synchronization task
        ttable = s3db.sync_task
        if not mixed:
            query = (ttable.repository_id == connector.id) & \
                    (ttable.resource_name == r.tablename) & \
                    (ttable.deleted != True)
            task = db(query).select(limitby=(0, 1)).first()
        else:
            task = None

        last_sync = None
        if task:
            strategy = task.strategy
            update_policy = task.update_policy or default_update_policy
            conflict_policy = task.conflict_policy or default_conflict_policy
            if update_policy not in ("THIS", "OTHER"):
                last_sync = task.last_pull

        else:
            policies = S3ImportItem.POLICY
            p = get_vars.get("update_policy", None)
            values = {"THIS": "OTHER", "OTHER": "THIS"}
            switch = lambda p: p in values and values[p] or p
            if p and p in policies:
                p = switch(p)
                update_policy = policies[p]
            else:
                update_policy = default_update_policy
            p = get_vars.get("conflict_policy", None)
            if p and p in policies:
                p = switch(p)
                conflict_policy = policies[p]
            else:
                conflict_policy = default_conflict_policy
            msince = get_vars.get("msince", None)
            if msince is not None:
                last_sync = s3_parse_datetime(msince)
            s = get_vars.get("strategy", None)
            if s:
                s = str(s).split(",")
                methods = S3ImportItem.METHOD
                strategy = [
                    method for method in methods.values() if method in s
                ]
            else:
                strategy = ttable.strategy.default

        # Get the source
        source = r.read_body()

        # Import resource
        resource = r.resource

        try:
            result = connector.receive(
                source,
                resource,
                strategy=strategy,
                update_policy=update_policy,
                conflict_policy=conflict_policy,
                last_sync=last_sync,
                onconflict=self.onconflict,
                mixed=mixed,
            )
        except IOError:
            current.auth.permission.fail()
        except SyntaxError:
            e = sys.exc_info()[1]
            r.error(400, e)
        except NotImplementedError:
            r.error(405, "Synchronization method not supported for repository")

        log = self.log
        log.write(
            repository_id=connector.id,
            resource_name="mixed" if mixed else resource.tablename,
            transmission=log.IN,
            mode=log.PUSH,
            action="receive",
            remote=result.get("remote", False),
            result=result.get("status", log.NONE),
            message=result.get("message", ""),
        )

        return result.get("response")
Example #3
0
    def __send(self, r, **attr):
        """
            Respond to an incoming pull

            @param r: the S3Request
            @param attr: the controller attributes
        """

        mixed = attr.get("mixed", False)
        get_vars = r.get_vars

        resource = r.resource

        # Identify the requesting repository
        repository_uuid = get_vars.get("repository")
        connector = None

        if repository_uuid:

            rtable = current.s3db.sync_repository
            query = rtable.uuid == repository_uuid
            row = current.db(query).select(limitby=(0, 1)).first()
            if row:
                connector = S3SyncRepository(row)

        if connector is None:
            # Use a dummy repository with Eden API
            connector = S3SyncRepository(
                Storage(
                    id=None,
                    name="unknown",
                    apitype="eden",
                ))

        current.log.debug("S3Sync PULL from %s (%s)" %
                          (connector.name, connector.apitype))

        # Additional export parameters
        start = get_vars.get("start", None)
        if start is not None:
            try:
                start = int(start)
            except ValueError:
                start = None
        limit = get_vars.get("limit", None)
        if limit is not None:
            try:
                limit = int(limit)
            except ValueError:
                limit = None
        msince = get_vars.get("msince", None)
        if msince is not None:
            msince = s3_parse_datetime(msince)

        # Sync filters from peer
        filters = {}
        for k, v in get_vars.items():
            if k[0] == "[" and "]" in k:
                tablename, urlvar = k[1:].split("]", 1)
                if urlvar:
                    if not tablename or tablename == "~":
                        tablename = resource.tablename
                    f = filters.get(tablename, {})
                    u = f.get(urlvar, None)
                    if u:
                        u = "%s&%s" % (u, v)
                    else:
                        u = v
                    f[urlvar] = u
                    filters[tablename] = f
        if not filters:
            filters = None

        try:
            result = connector.send(
                resource,
                start=start,
                limit=limit,
                msince=msince,
                filters=filters,
                mixed=mixed,
            )
        except NotImplementedError:
            r.error(405, "Synchronization method not supported for repository")

        log = self.log
        log.write(
            repository_id=connector.id,
            resource_name="mixed" if mixed else resource.tablename,
            transmission=log.IN,
            mode=log.PULL,
            action="send",
            remote=result.get("remote", False),
            result=result.get("status", log.NONE),
            message=result.get("message", ""),
        )

        return result.get("response")
Example #4
0
    def __receive(self, r, **attr):
        """
            Respond to an incoming push

            @param r: the S3Request
            @param attr: the controller attributes
        """

        _debug("S3Sync.__receive")

        s3db = current.s3db
        db = current.db

        # Identify the sending repository
        repository = Storage(id=None)
        if "repository" in r.vars:
            ruid = r.vars["repository"]
            rtable = s3db.sync_repository
            row = db(rtable.uuid == ruid).select(limitby=(0, 1)).first()
            if row:
                repository = row
        if not repository.id or \
           not repository.accept_push:
            r.error(403, current.ERROR.NOT_PERMITTED)

        # Get strategy and policy
        default_update_policy = S3ImportItem.POLICY.NEWER
        default_conflict_policy = S3ImportItem.POLICY.MASTER

        ttable = s3db.sync_task
        query = (ttable.repository_id == repository.id) & \
                (ttable.resource_name == r.tablename) & \
                (ttable.deleted != True)
        task = db(query).select(limitby=(0, 1)).first()
        last_sync = None
        if task:
            strategy = task.strategy
            update_policy = task.update_policy or default_update_policy
            conflict_policy = task.conflict_policy or default_conflict_policy
            if update_policy not in ("THIS", "OTHER"):
                last_sync = task.last_pull
        else:
            policies = S3ImportItem.POLICY
            p = r.get_vars.get("update_policy", None)
            values = {"THIS": "OTHER", "OTHER": "THIS"}
            switch = lambda p: p in values and values[p] or p
            if p and p in policies:
                p = switch(p)
                update_policy = policies[p]
            else:
                update_policy = default_update_policy
            p = r.get_vars.get("conflict_policy", None)
            if p and p in policies:
                p = switch(p)
                conflict_policy = policies[p]
            else:
                conflict_policy = default_conflict_policy
            msince = r.get_vars.get("msince", None)
            if msince is not None:
                last_sync = s3_parse_datetime(msince)
            s = r.get_vars.get("strategy", None)
            if s:
                s = str(s).split(",")
                methods = S3ImportItem.METHOD
                strategy = [method for method in methods.values()
                                   if method in s]
            else:
                strategy = ttable.strategy.default

        # Other parameters
        ignore_errors = True

        # Get the source
        source = r.read_body()

        # Import resource
        resource = r.resource
        onconflict = lambda item: self.onconflict(item, repository, resource)
        try:
            output = resource.import_xml(source, format="xml",
                                         ignore_errors=ignore_errors,
                                         strategy=strategy,
                                         update_policy=update_policy,
                                         conflict_policy=conflict_policy,
                                         last_sync=last_sync,
                                         onconflict=onconflict)
        except IOError:
            current.auth.permission.fail()
        except SyntaxError:
            e = sys.exc_info()[1]
            r.error(400, e)

        log = self.log

        if resource.error_tree is not None:
            # Validation error (log in any case)
            if ignore_errors:
                result = log.WARNING
            else:
                result = log.FATAL
            message = "%s" % resource.error
            for element in resource.error_tree.findall("resource"):
                error_msg = element.get("error", "unknown error")

                error_fields = element.findall("data[@error]")
                if error_fields:
                    for field in error_fields:
                        error_msg = field.get("error", "unknown error")
                        if error_msg:
                            msg = "(UID: %s) %s.%s=%s: %s" % \
                                    (element.get("uuid", None),
                                     element.get("name", None),
                                     field.get("field", None),
                                     field.get("value", field.text),
                                     error_msg)
                            message = "%s, %s" % (message, msg)
                else:
                    msg = "(UID: %s) %s: %s" % \
                          (element.get("uuid", None),
                           element.get("name", None),
                           error_msg)
                    message = "%s, %s" % (message, msg)
        else:
            result = log.SUCCESS
            message = "data received from peer"

        log.write(repository_id=repository.id,
                  resource_name=resource.tablename,
                  transmission=log.IN,
                  mode=log.PUSH,
                  result=result,
                  message=message)

        return output
Example #5
0
    def __send(self, r, **attr):
        """
            Respond to an incoming pull

            @param r: the S3Request
            @param attr: the controller attributes
        """

        _debug("S3Sync.__send")

        resource = r.resource

        # Identify the requesting repository
        repository_id = None
        if "repository" in r.vars:

            db = current.db
            s3db = current.s3db

            ruid = r.vars["repository"]
            rtable = s3db.sync_repository
            ttable = s3db.sync_task

            left = ttable.on((rtable.id == ttable.repository_id) & \
                             (ttable.resource_name == resource.tablename))

            row = db(rtable.uuid == ruid).select(rtable.id,
                                                 ttable.id,
                                                 left=left,
                                                 limitby=(0, 1)).first()
            if row:
                repository_id = row[rtable.id]
                task_id = row[ttable.id]

        # Additional export parameters
        _vars = r.get_vars
        start = _vars.get("start", None)
        if start is not None:
            try:
                start = int(start)
            except ValueError:
                start = None
        limit = _vars.get("limit", None)
        if limit is not None:
            try:
                limit = int(limit)
            except ValueError:
                limit = None
        msince = _vars.get("msince", None)
        if msince is not None:
            msince = s3_parse_datetime(msince)

        # Sync filters from peer
        filters = {}
        for k, v in _vars.items():
            if k[0] == "[" and "]" in k:
                tablename, urlvar = k[1:].split("]", 1)
                if urlvar:
                    if not tablename or tablename == "~":
                        tablename = resource.tablename
                    f = filters.get(tablename, {})
                    u = f.get(urlvar, None)
                    if u:
                        u = "%s&%s" % (u, v)
                    else:
                        u = v
                    f[urlvar] = u
                    filters[tablename] = f
        if not filters:
            filters = None

        # Export the resource
        output = resource.export_xml(start=start,
                                     limit=limit,
                                     filters=filters,
                                     msince=msince)
        count = resource.results

        # Set content type header
        headers = current.response.headers
        headers["Content-Type"] = "text/xml"

        # Log the operation
        log = self.log
        log.write(repository_id=repository_id,
                  resource_name=r.resource.tablename,
                  transmission=log.IN,
                  mode=log.PULL,
                  result=log.SUCCESS,
                  message="data sent to peer (%s records)" % count)

        return output
Example #6
0
    def __receive(self, r, **attr):
        """
            Respond to an incoming push

            @param r: the S3Request
            @param attr: the controller attributes
        """

        mixed = attr.get("mixed", False)
        get_vars = r.get_vars

        s3db = current.s3db
        db = current.db

        # Identify the sending repository
        repository_uuid = get_vars.get("repository")
        connector = None

        if repository_uuid:

            rtable = s3db.sync_repository
            query = rtable.uuid == repository_uuid
            row = current.db(query).select(limitby=(0, 1)).first()
            if row:
                connector = S3SyncRepository(row)

        if connector is None:
            # Repositories must be registered to push, so that we
            # can track sync times and log operations properly
            r.error(403, "Registration required")

        current.log.debug("S3Sync PUSH from %s (%s)" % (connector.name,
                                                        connector.apitype,
                                                        ))

        # Get strategy and policy
        default_update_policy = S3ImportItem.POLICY.NEWER
        default_conflict_policy = S3ImportItem.POLICY.MASTER

        # Identify the synchronization task
        ttable = s3db.sync_task
        if not mixed:
            query = (ttable.repository_id == connector.id) & \
                    (ttable.resource_name == r.tablename) & \
                    (ttable.deleted != True)
            task = db(query).select(limitby=(0, 1)).first()
        else:
            task = None

        last_sync = None
        if task:
            strategy = task.strategy
            update_policy = task.update_policy or default_update_policy
            conflict_policy = task.conflict_policy or default_conflict_policy
            if update_policy not in ("THIS", "OTHER"):
                last_sync = task.last_pull

        else:
            policies = S3ImportItem.POLICY
            p = get_vars.get("update_policy", None)
            values = {"THIS": "OTHER", "OTHER": "THIS"}
            switch = lambda p: p in values and values[p] or p
            if p and p in policies:
                p = switch(p)
                update_policy = policies[p]
            else:
                update_policy = default_update_policy
            p = get_vars.get("conflict_policy", None)
            if p and p in policies:
                p = switch(p)
                conflict_policy = policies[p]
            else:
                conflict_policy = default_conflict_policy
            msince = get_vars.get("msince", None)
            if msince is not None:
                last_sync = s3_parse_datetime(msince)
            s = get_vars.get("strategy", None)
            if s:
                s = str(s).split(",")
                methods = S3ImportItem.METHOD
                strategy = [method for method in methods.values()
                                   if method in s]
            else:
                strategy = ttable.strategy.default

        # Get the source
        source = r.read_body()

        # Import resource
        resource = r.resource

        try:
            result = connector.receive(source,
                                       resource,
                                       strategy = strategy,
                                       update_policy = update_policy,
                                       conflict_policy = conflict_policy,
                                       last_sync = last_sync,
                                       onconflict = self.onconflict,
                                       mixed = mixed,
                                       )
        except IOError:
            current.auth.permission.fail()
        except SyntaxError:
            e = sys.exc_info()[1]
            r.error(400, e)
        except NotImplementedError:
            r.error(405, "Synchronization method not supported for repository")

        log = self.log
        log.write(repository_id = connector.id,
                  resource_name = "mixed" if mixed else resource.tablename,
                  transmission = log.IN,
                  mode = log.PUSH,
                  action = "receive",
                  remote = result.get("remote", False),
                  result = result.get("status", log.NONE),
                  message = result.get("message", ""),
                  )

        return result.get("response")
Example #7
0
    def __send(self, r, **attr):
        """
            Respond to an incoming pull

            @param r: the S3Request
            @param attr: the controller attributes
        """

        mixed =  attr.get("mixed", False)
        get_vars = r.get_vars

        resource = r.resource

        # Identify the requesting repository
        repository_uuid = get_vars.get("repository")
        connector = None

        if repository_uuid:

            rtable = current.s3db.sync_repository
            query = rtable.uuid == repository_uuid
            row = current.db(query).select(limitby=(0, 1)).first()
            if row:
                connector = S3SyncRepository(row)

        if connector is None:
            # Use a dummy repository with Eden API
            connector = S3SyncRepository(Storage(id = None,
                                                 name = "unknown",
                                                 apitype = "eden",
                                                 ))

        current.log.debug("S3Sync PULL from %s (%s)" % (connector.name,
                                                        connector.apitype))

        # Additional export parameters
        start = get_vars.get("start", None)
        if start is not None:
            try:
                start = int(start)
            except ValueError:
                start = None
        limit = get_vars.get("limit", None)
        if limit is not None:
            try:
                limit = int(limit)
            except ValueError:
                limit = None
        msince = get_vars.get("msince", None)
        if msince is not None:
            msince = s3_parse_datetime(msince)

        # Sync filters from peer
        filters = {}
        for k, v in get_vars.items():
            if k[0] == "[" and "]" in k:
                tablename, urlvar = k[1:].split("]", 1)
                if urlvar:
                    if not tablename or tablename == "~":
                        tablename = resource.tablename
                    f = filters.get(tablename, {})
                    u = f.get(urlvar, None)
                    if u:
                        u = "%s&%s" % (u, v)
                    else:
                        u = v
                    f[urlvar] = u
                    filters[tablename] = f
        if not filters:
            filters = None

        try:
            result = connector.send(resource,
                                    start = start,
                                    limit = limit,
                                    msince = msince,
                                    filters = filters,
                                    mixed = mixed,
                                    )
        except NotImplementedError:
            r.error(405, "Synchronization method not supported for repository")

        log = self.log
        log.write(repository_id = connector.id,
                  resource_name = "mixed" if mixed else resource.tablename,
                  transmission = log.IN,
                  mode = log.PULL,
                  action = "send",
                  remote = result.get("remote", False),
                  result = result.get("status", log.NONE),
                  message = result.get("message", ""),
                  )

        return result.get("response")
Example #8
0
    def __receive(self, r, **attr):
        """
            Respond to an incoming push

            @param r: the S3Request
            @param attr: the controller attributes
        """

        _debug("S3Sync.__receive")

        s3db = current.s3db
        db = current.db

        # Identify the sending repository
        repository = Storage(id=None)
        if "repository" in r.vars:
            ruid = r.vars["repository"]
            rtable = s3db.sync_repository
            row = db(rtable.uuid == ruid).select(limitby=(0, 1)).first()
            if row:
                repository = row
        if not repository.id or \
           not repository.accept_push:
            r.error(403, current.ERROR.NOT_PERMITTED)

        # Get strategy and policy
        default_update_policy = S3ImportItem.POLICY.NEWER
        default_conflict_policy = S3ImportItem.POLICY.MASTER

        ttable = s3db.sync_task
        query = (ttable.repository_id == repository.id) & \
                (ttable.resource_name == r.tablename) & \
                (ttable.deleted != True)
        task = db(query).select(limitby=(0, 1)).first()
        last_sync = None
        if task:
            strategy = task.strategy
            update_policy = task.update_policy or default_update_policy
            conflict_policy = task.conflict_policy or default_conflict_policy
            if update_policy not in ("THIS", "OTHER"):
                last_sync = task.last_pull
        else:
            policies = S3ImportItem.POLICY
            p = r.get_vars.get("update_policy", None)
            values = {"THIS": "OTHER", "OTHER": "THIS"}
            switch = lambda p: p in values and values[p] or p
            if p and p in policies:
                p = switch(p)
                update_policy = policies[p]
            else:
                update_policy = default_update_policy
            p = r.get_vars.get("conflict_policy", None)
            if p and p in policies:
                p = switch(p)
                conflict_policy = policies[p]
            else:
                conflict_policy = default_conflict_policy
            msince = r.get_vars.get("msince", None)
            if msince is not None:
                last_sync = s3_parse_datetime(msince)
            s = r.get_vars.get("strategy", None)
            if s:
                s = str(s).split(",")
                methods = S3ImportItem.METHOD
                strategy = [
                    method for method in methods.values() if method in s
                ]
            else:
                strategy = ttable.strategy.default

        # Other parameters
        ignore_errors = True

        # Get the source
        source = r.read_body()

        # Import resource
        resource = r.resource
        onconflict = lambda item: self.onconflict(item, repository, resource)
        try:
            output = resource.import_xml(source,
                                         format="xml",
                                         ignore_errors=ignore_errors,
                                         strategy=strategy,
                                         update_policy=update_policy,
                                         conflict_policy=conflict_policy,
                                         last_sync=last_sync,
                                         onconflict=onconflict)
        except IOError:
            current.auth.permission.fail()
        except SyntaxError:
            e = sys.exc_info()[1]
            r.error(400, e)

        log = self.log

        if resource.error_tree is not None:
            # Validation error (log in any case)
            if ignore_errors:
                result = log.WARNING
            else:
                result = log.FATAL
            message = "%s" % resource.error
            for element in resource.error_tree.findall("resource"):
                error_msg = element.get("error", "unknown error")

                error_fields = element.findall("data[@error]")
                if error_fields:
                    for field in error_fields:
                        error_msg = field.get("error", "unknown error")
                        if error_msg:
                            msg = "(UID: %s) %s.%s=%s: %s" % \
                                    (element.get("uuid", None),
                                     element.get("name", None),
                                     field.get("field", None),
                                     field.get("value", field.text),
                                     error_msg)
                            message = "%s, %s" % (message, msg)
                else:
                    msg = "(UID: %s) %s: %s" % \
                          (element.get("uuid", None),
                           element.get("name", None),
                           error_msg)
                    message = "%s, %s" % (message, msg)
        else:
            result = log.SUCCESS
            message = "data received from peer"

        log.write(repository_id=repository.id,
                  resource_name=resource.tablename,
                  transmission=log.IN,
                  mode=log.PUSH,
                  result=result,
                  message=message)

        return output
Example #9
0
    def __send(self, r, **attr):
        """
            Respond to an incoming pull

            @param r: the S3Request
            @param attr: the controller attributes
        """

        _debug("S3Sync.__send")

        resource = r.resource

        # Identify the requesting repository
        repository_id = None
        if "repository" in r.vars:

            db = current.db
            s3db = current.s3db

            ruid = r.vars["repository"]
            rtable = s3db.sync_repository
            ttable = s3db.sync_task

            left = ttable.on((rtable.id == ttable.repository_id) & \
                             (ttable.resource_name == resource.tablename))

            row = db(rtable.uuid == ruid).select(rtable.id,
                                                 ttable.id,
                                                 left=left,
                                                 limitby=(0, 1)).first()
            if row:
                repository_id = row[rtable.id]
                task_id = row[ttable.id]

        # Additional export parameters
        _vars = r.get_vars
        start = _vars.get("start", None)
        if start is not None:
            try:
                start = int(start)
            except ValueError:
                start = None
        limit = _vars.get("limit", None)
        if limit is not None:
            try:
                limit = int(limit)
            except ValueError:
                limit = None
        msince = _vars.get("msince", None)
        if msince is not None:
            msince = s3_parse_datetime(msince)

        # Sync filters from peer
        filters = {}
        for k, v in _vars.items():
            if k[0] == "[" and "]" in k:
                tablename, urlvar = k[1:].split("]", 1)
                if urlvar:
                    if not tablename or tablename == "~":
                        tablename = resource.tablename
                    f = filters.get(tablename, {})
                    u = f.get(urlvar, None)
                    if u:
                        u = "%s&%s" % (u, v)
                    else:
                        u = v
                    f[urlvar] = u
                    filters[tablename] = f
        if not filters:
            filters = None

        # Export the resource
        output = resource.export_xml(start=start,
                                     limit=limit,
                                     filters=filters,
                                     msince=msince)
        count = resource.results

        # Set content type header
        headers = current.response.headers
        headers["Content-Type"] = "text/xml"

        # Log the operation
        log = self.log
        log.write(repository_id=repository_id,
                  resource_name=r.resource.tablename,
                  transmission=log.IN,
                  mode=log.PULL,
                  result=log.SUCCESS,
                  message="data sent to peer (%s records)" % count)

        return output