~launchpad-pqm/launchpad/devel

13811.2.1 by Jeroen Vermeulen
Fix some of the lint people left in the past few days.
1
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
11644.2.5 by Ian Booth
More plumbing
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
13570.1.7 by Nigel Babu
Use the search, but it doesn't work :(
14
from canonical.launchpad.searchbuilder import any
13570.1.9 by Nigel Babu
It works! Huzzah!
15
from canonical.launchpad.webapp import LaunchpadView
11644.2.21 by Ian Booth
Changes as per code review
16
from lp.app.errors import NotFoundError
13811.2.1 by Jeroen Vermeulen
Fix some of the lint people left in the past few days.
17
from lp.bugs.interfaces.bugtask import (
18
    BugTaskSearchParams,
19
    IBugTaskSet,
20
    )
11644.2.21 by Ian Booth
Changes as per code review
21
from lp.code.errors import (
22
    CannotHaveLinkedBranch,
23
    InvalidNamespace,
24
    NoLinkedBranch,
25
    NoSuchBranch,
26
    )
27
from lp.code.interfaces.branchlookup import IBranchLookup
28
from lp.registry.interfaces.product import InvalidProductName
11644.2.5 by Ian Booth
More plumbing
29
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
30
13570.1.9 by Nigel Babu
It works! Huzzah!
31
class LinkCheckerAPI(LaunchpadView):
11644.2.21 by Ian Booth
Changes as per code review
32
    """Validates Launchpad shortcut links.
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
33
34
    This class provides the endpoint of an Ajax call to .../+check-links.
35
    When invoked with a collection of links harvested from a page, it will
36
    check the validity of each one and send a response containing those that
37
    are invalid. Javascript on the page will set the style of invalid links to
38
    something appropriate.
39
40
    This initial implementation supports processing links like the following:
41
        /+branch/foo/bar
42
43
    The implementation can easily be extended to handle other forms by
44
    providing a method to handle the link type extracted from the json
45
    request.
46
    """
11644.2.5 by Ian Booth
More plumbing
47
11644.2.6 by Ian Booth
Working
48
    def __init__(self, context, request):
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
49
        # We currently only use the request.
50
        # self.context = context
11644.2.6 by Ian Booth
Working
51
        self.request = request
52
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
53
        # Each link type has it's own validation method.
54
        self.link_checkers = dict(
55
            branch_links=self.check_branch_links,
13570.1.3 by Nigel Babu
merged devel in
56
            bug_links=self.check_bug_links,
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
57
        )
58
11644.2.6 by Ian Booth
Working
59
    def __call__(self):
11644.2.8 by Ian Booth
Next round of work
60
        result = {}
11644.2.9 by Ian Booth
Do not follow invalid links
61
        links_to_check_data = self.request.get('link_hrefs')
11912.1.1 by Ian Booth
Initial coding
62
        if links_to_check_data is None:
63
            return simplejson.dumps(result)
11644.2.8 by Ian Booth
Next round of work
64
        links_to_check = simplejson.loads(links_to_check_data)
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
65
66
        for link_type in links_to_check:
67
            links = links_to_check[link_type]
13943.1.13 by Nigel Babu
Better code!
68
            link_info = self.link_checkers[link_type](links)
69
            result[link_type] = link_info
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
70
71
        self.request.response.setHeader('Content-type', 'application/json')
72
        return simplejson.dumps(result)
73
74
    def check_branch_links(self, links):
75
        """Check links of the form /+branch/foo/bar"""
11912.2.1 by Ian Booth
Add back good changes after testfix revert
76
        invalid_links = {}
11644.2.12 by Ian Booth
Add linkchecker implementation and add unit test
77
        branch_lookup = getUtility(IBranchLookup)
78
        for link in links:
79
            path = link.strip('/')[len('+branch/'):]
80
            try:
81
                branch_lookup.getByLPPath(path)
11644.2.21 by Ian Booth
Changes as per code review
82
            except (CannotHaveLinkedBranch, InvalidNamespace,
83
                    InvalidProductName, NoLinkedBranch, NoSuchBranch,
11912.2.1 by Ian Booth
Add back good changes after testfix revert
84
                    NotFoundError) as e:
85
                invalid_links[link] = self._error_message(e)
13943.1.1 by Nigel Babu
Add a "title" attribute with bug title
86
        return {'invalid': invalid_links}
13570.1.7 by Nigel Babu
Use the search, but it doesn't work :(
87
13570.1.8 by Nigel Babu
And now it works, except for the anon use-case
88
    def check_bug_links(self, links):
13570.1.11 by Nigel Babu
changed the comment
89
        """Checks links of the form /bugs/100"""
13570.1.4 by Nigel Babu
Check for bugs too
90
        invalid_links = {}
13943.1.1 by Nigel Babu
Add a "title" attribute with bug title
91
        valid_links = {}
13570.1.9 by Nigel Babu
It works! Huzzah!
92
        user = self.user
13943.1.11 by Nigel Babu
Changes per review
93
        # List of all the bugs we are checking.
13943.1.13 by Nigel Babu
Better code!
94
        bugs_ids = set([int(link[len('/bugs/'):]) for link in links])
95
        if bugs_ids:
13570.1.7 by Nigel Babu
Use the search, but it doesn't work :(
96
            params = BugTaskSearchParams(
97
                user=user, status=None,
13943.1.13 by Nigel Babu
Better code!
98
                bug=any(*bugs_ids))
13943.1.1 by Nigel Babu
Add a "title" attribute with bug title
99
            bugtasks = getUtility(IBugTaskSet).search(params)
100
            for task in bugtasks:
101
                valid_links['/bugs/' + str(task.bug.id)] = task.bug.title
13943.1.11 by Nigel Babu
Changes per review
102
                # Remove valid bugs from the list of all the bugs.
13973.1.1 by Nigel Babu
Fix the regression and add tests
103
                if task.bug.id in bugs_ids:
104
                    bugs_ids.remove(task.bug.id)
13943.1.11 by Nigel Babu
Changes per review
105
            # We should now have only invalid bugs in bugs list
13943.1.13 by Nigel Babu
Better code!
106
            for bug in bugs_ids:
107
                invalid_links['/bugs/%d' % bug] = (
13811.2.1 by Jeroen Vermeulen
Fix some of the lint people left in the past few days.
108
                    "Bug %s cannot be found" % bug)
13943.1.7 by Nigel Babu
More lint fixes
109
        return {'valid': valid_links, 'invalid': invalid_links}
13811.2.1 by Jeroen Vermeulen
Fix some of the lint people left in the past few days.
110
11912.2.1 by Ian Booth
Add back good changes after testfix revert
111
    def _error_message(self, ex):
112
        if hasattr(ex, 'display_message'):
113
            return ex.display_message
114
        return str(ex)