~launchpad-pqm/launchpad/devel

11644.2.5 by Ian Booth
More plumbing
1
# Copyright 2009 Canonical Ltd.  This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
4
# pylint: disable-msg=E0211,E0213
5
6
__metaclass__ = type
7
__all__ = [
8
    'LinkCheckerAPI',
9
    ]
10
11644.2.6 by Ian Booth
Working
11
import simplejson
11644.2.21 by Ian Booth
Changes as per code review
12
from zope.component import getUtility
13
14
from lp.app.errors import NotFoundError
15
from lp.code.errors import (
16
    CannotHaveLinkedBranch,
17
    InvalidNamespace,
18
    NoLinkedBranch,
19
    NoSuchBranch,
20
    )
21
from lp.code.interfaces.branchlookup import IBranchLookup
22
from lp.registry.interfaces.product import InvalidProductName
11644.2.5 by Ian Booth
More plumbing
23
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
24
11644.2.5 by Ian Booth
More plumbing
25
class LinkCheckerAPI:
11644.2.21 by Ian Booth
Changes as per code review
26
    """Validates Launchpad shortcut links.
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
27
28
    This class provides the endpoint of an Ajax call to .../+check-links.
29
    When invoked with a collection of links harvested from a page, it will
30
    check the validity of each one and send a response containing those that
31
    are invalid. Javascript on the page will set the style of invalid links to
32
    something appropriate.
33
34
    This initial implementation supports processing links like the following:
35
        /+branch/foo/bar
36
37
    The implementation can easily be extended to handle other forms by
38
    providing a method to handle the link type extracted from the json
39
    request.
40
    """
11644.2.5 by Ian Booth
More plumbing
41
11644.2.6 by Ian Booth
Working
42
    def __init__(self, context, request):
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
43
        # We currently only use the request.
44
        # self.context = context
11644.2.6 by Ian Booth
Working
45
        self.request = request
46
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
47
        # Each link type has it's own validation method.
48
        self.link_checkers = dict(
49
            branch_links=self.check_branch_links,
50
        )
51
11644.2.6 by Ian Booth
Working
52
    def __call__(self):
11644.2.8 by Ian Booth
Next round of work
53
        result = {}
11644.2.9 by Ian Booth
Do not follow invalid links
54
        links_to_check_data = self.request.get('link_hrefs')
11912.1.1 by Ian Booth
Initial coding
55
        if links_to_check_data is None:
56
            return simplejson.dumps(result)
11644.2.8 by Ian Booth
Next round of work
57
        links_to_check = simplejson.loads(links_to_check_data)
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
58
59
        for link_type in links_to_check:
60
            links = links_to_check[link_type]
61
            invalid_links = self.link_checkers[link_type](links)
11644.2.21 by Ian Booth
Changes as per code review
62
            result['invalid_'+link_type] = invalid_links
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
63
64
        self.request.response.setHeader('Content-type', 'application/json')
65
        return simplejson.dumps(result)
66
67
    def check_branch_links(self, links):
68
        """Check links of the form /+branch/foo/bar"""
11912.2.1 by Ian Booth
Add back good changes after testfix revert
69
        invalid_links = {}
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
70
        branch_lookup = getUtility(IBranchLookup)
71
        for link in links:
72
            path = link.strip('/')[len('+branch/'):]
73
            try:
74
                branch_lookup.getByLPPath(path)
11644.2.21 by Ian Booth
Changes as per code review
75
            except (CannotHaveLinkedBranch, InvalidNamespace,
76
                    InvalidProductName, NoLinkedBranch, NoSuchBranch,
11912.2.1 by Ian Booth
Add back good changes after testfix revert
77
                    NotFoundError) as e:
78
                invalid_links[link] = self._error_message(e)
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
79
        return invalid_links
11912.2.1 by Ian Booth
Add back good changes after testfix revert
80
81
    def _error_message(self, ex):
82
        if hasattr(ex, 'display_message'):
83
            return ex.display_message
84
        return str(ex)