source: enpraxis.staticsite/trunk/enpraxis/staticsite/utilities/staticsiteutility.py @ 334

Revision 334, 17.5 KB checked in by brent, 4 years ago (diff)

Fixed image full screen view and back link.

Line 
1import os
2import re
3import string
4import Globals
5from BeautifulSoup import BeautifulSoup
6from os import makedirs as os_makedirs
7from os.path import join as os_join
8from os.path import lexists as os_lexists
9from os.path import split as os_split
10
11from urllib2 import urlopen
12from urllib2 import HTTPError
13from urlparse import urlparse, urlunparse
14from Products.PythonScripts.standard import url_quote
15
16from OFS.SimpleItem import SimpleItem
17from zope.interface import implements
18from interfaces import IStaticSiteUtility
19from zope.component import getUtility, getMultiAdapter
20
21class StaticSiteUtility(SimpleItem):
22    """ Deploy a static site """
23
24    implements(IStaticSiteUtility)
25
26    def deploySite(self, context):
27        """ Deploy the site """
28        ssprops = context.portal_url.portal_properties.staticsite_properties
29        dpath = self._getDeploymentPath(ssprops.getProperty('deployment_path'))
30        # Deploy default objects
31        self.deploySiteStructure(context, ssprops, dpath)
32        # Deploy site object
33        self.deployObject(context.absolute_url(), context, context.Type(), dpath, ssprops, True)
34        # Deploy objects based on catalog search
35        brains = context.portal_catalog.searchResults(
36            path={'query':'/'.join(context.getPhysicalPath()),
37                  'depth':1,},
38            review_state=ssprops.getProperty('states_to_add'))
39        for brain in brains:
40            self.deployObject(brain.getURL(),
41                              context,
42                              brain.Type,
43                              dpath,
44                              ssprops,
45                              folderish=brain.is_folderish)
46            self.traverse(brain, dpath, ssprops)
47
48    def deploySiteStructure(self, context, ssprops, dpath):
49        """ Get the base framework and resources needed to build the chrome locally  """
50        portal_url = context.portal_url()
51        portal_catalog = context.portal_catalog
52        url = urlparse(portal_url)
53        urlpath = url[2].split('/')
54
55        # Deploy base files that are used sitewide in the chrome
56        for x in ssprops.getProperty('base_files'):
57            objurl = urlunparse((url[0], url[1], '/'.join(urlpath + [x]), url[3], url[4], url[5]))
58            path = self._getObjPath(objurl, portal_url, dpath)
59            raw = self._httpget(objurl)
60            self._writeFile(path, raw)
61
62        # Deploy CSS
63        cssreg = context.portal_css
64        cssurl = cssreg.absolute_url()
65        skinname = context.getCurrentSkinName()
66        for x in cssreg.getEvaluatedResources(context):
67            if x.getRendering() != 'inline':
68                surl = '%s/%s/%s' %(cssurl, skinname, x.getId())
69                path = self._getObjPath(surl, portal_url, dpath)
70                surl = '%s/%s/%s' %(cssurl, url_quote(skinname), x.getId())
71                raw = self._httpget(surl)
72                self._writeFile(path, raw)
73
74        # Deploy CSS Images into the appropriate skin folder
75        for x in ssprops.getProperty('css_images'):
76            iurl = '%s/%s' %(portal_url, x)
77            surl = '%s/%s/%s' %(cssurl, skinname, x)
78            path = self._getObjPath(surl, portal_url, dpath)
79            raw = self._httpget(iurl)
80            self._writeFile(path, raw)
81
82        # Deploy Javascript
83        jsreg = context.portal_javascripts
84        jsurl = jsreg.absolute_url()
85        for x in jsreg.getEvaluatedResources(context):
86            if x.getInline() != True:           
87                surl = '%s/%s/%s' %(jsurl, skinname, x.getId())
88                path = self._getObjPath(surl, portal_url, dpath)
89                surl = '%s/%s/%s' %(jsurl, url_quote(skinname), x.getId())
90                raw = self._httpget(surl)
91                self._writeFile(path, raw)   
92
93        # Deploy site actions
94        site_actions = context.portal_actions.site_actions.listActions()
95        for x in site_actions:
96            if x.id not in ssprops.getProperty('actions_to_ignore') and x.visible == True:
97                action_url = x.url_expr.split('/')[-1]
98                url = '%s/%s' % (portal_url, action_url)
99                path = self._getObjPath(url, portal_url, dpath)
100                path += '.html'
101                raw = self._httpget(url)       
102                self._writeFile(path, raw)
103       
104    def traverse(self, brain, dpath, ssprops):
105        """ Traverse the site. """
106        brains = brain.portal_catalog.searchResults(
107            path={'query':brain.getPath(), 'depth':1},
108                  review_state=ssprops.getProperty('states_to_add'))
109        for br in brains:
110            self.deployObject(br.getURL(),
111                              br.portal_url,
112                              br.Type,
113                              dpath,
114                              ssprops,
115                              folderish=br.is_folderish)
116            if br.is_folderish:
117                self.traverse(br, dpath, ssprops)
118           
119    def deployObject(self, url, portal, ctype, dpath, ssprops, folderish=False, hastext=False):
120        """ Deploy an object """
121        path = self._getObjPath(url, portal.portal_url(), dpath)
122        if folderish:
123            print '@@@ '+  url + '/index.html'
124            self.processDocument(url, portal, dpath, ssprops, True)
125        elif ctype in ['Page']:
126            # Fix this so that it deals with the case where you have both a file and a file.html in the
127            # same folder
128            print '*** '+  url
129            self.processDocument(url, portal, dpath, ssprops)
130        else:
131            raw = self._httpget(url)
132            self._writeFile(path, raw)
133            aurl = urlparse(url)
134            aurl = urlunparse((aurl[0], aurl[1], aurl[2] + '-view.html', aurl[3], aurl[4], aurl[5]))
135            print 'vvv '+  aurl
136            self.processDocument(url + '/view', portal, dpath, ssprops, alturl=aurl)
137            if ctype in ['Image']:
138                aurl = urlparse(url)
139                aurl = urlunparse((aurl[0], aurl[1], aurl[2] + '-image_view_fullscreen.html', aurl[3], aurl[4], aurl[5]))
140                print 'iii '+  aurl
141                self.processDocument(url + '/image_view_fullscreen', portal, dpath, ssprops, alturl=aurl)
142
143
144    def _getDeploymentPath(self, sp):
145        """ Get the default static path location. """
146        dpath = os_split(Globals.BobobaseName)[0]
147        return os_join(dpath, sp)
148       
149    def _getObjPath(self, url, portal_url, dpath):
150        """ Get the object path based on the deployment path. """
151        if portal_url + '/' in url:
152            objpath = url.replace(portal_url + '/', '')
153        else:
154            objpath = url.replace(portal_url, '')
155        path = dpath
156        for x in objpath.split('/'):
157            path = os_join(path, x)
158        return path
159   
160    def _createDirectory(self, path):
161        """ Create a directory on the filesystem """     
162        if not os_lexists(path):
163            os_makedirs(path)
164           
165    def _httpget(self, url):
166        """ Get html for the url """
167        try:
168            f = urlopen(url)
169            data = f.read()
170            f.close()
171            return data
172        except HTTPError, e:           
173            print '!!! Error : %s %s for url: %s' % (e.code, e.msg, e.filename)
174            return ''
175           
176    def _writeFile(self, fn, data):
177        self._createDirectory(os_split(fn)[0])
178        f = open(fn, 'w')
179        f.write(data)
180        f.close()
181   
182    def processDocument(self, url, portal, dpath, ssprops, isFolderish=False, alturl=None):
183        if alturl:
184            aurl = alturl
185        else:
186            aurl = url
187        path = self._getObjPath(aurl, portal.portal_url(), dpath)
188        raw = self._httpget(url)
189        soup = BeautifulSoup(raw)
190        self.deployDocumentActions(portal, aurl, dpath, soup, ssprops)
191        if isFolderish:
192            curl = aurl + '/index.html'
193        else:
194            curl = aurl
195        body = self.runDocumentFilters(portal, curl, soup, ssprops)
196        mpath = path
197        if isFolderish:
198            mpath += '/index.html'
199        elif '.htm' not in mpath:
200            mpath += '.html'
201        self._writeFile(mpath, body)
202
203    def deployDocumentActions(self, portal, current, dpath, soup, ssprops):
204        raw_da = soup.find('div', {'class':'documentActions'})
205        if raw_da:
206            da = portal.portal_actions.document_actions.listActions()
207            for x in da:
208                if x.id not in ssprops.getProperty('actions_to_ignore'):
209                    act = raw_da.find('li', id='document-action-%s' %x.id)
210                    if act:
211                        link = act.find('a')
212                        if link and link.has_key('href'):
213                            url = link['href']
214                            upath = urlparse(link['href'])
215                            if upath[2].split('/')[-1] in ssprops.getProperty('views_to_add'):
216                                mpath = self._getObjPath(url, portal.portal_url(), dpath)
217                                mpath = os.path.split(mpath)
218                                raw = self._httpget(url)
219                                asoup = BeautifulSoup(raw)
220                                body = self.runDocumentFilters(portal, portal.portal_url(), asoup, ssprops)
221                                mpath = '%s-%s.html' %(mpath[0], mpath[1])
222                                self._writeFile(mpath, body)
223
224    def runViewFilters(self, id, portal, current, soup, ssprops):
225        self.filterIgnoredSections(soup, ssprops)
226        self.filterIgnoredActions(soup, ssprops)
227        self.filterCSSLinks(soup, current)
228        links = self.getDocumentLinks(soup)
229        for x in links:
230            orig = x['href']
231            x['href'] = self.filterDocumentLink(x['href'],
232                                                current,
233                                                portal,
234                                                ssprops.getProperty('views_to_add'))
235            print '   (view-%s) %s => %s' %(id, orig, x['href'])
236        return soup.prettify()
237
238    def filterViewLink(self, link, current, views):
239        lnk = link
240        lnk = self._convertLinkToAbsolute(lnk, current)
241        lnk = self._convertViewLink(lnk, views)
242        lnk = self._convertLinkToRelative(lnk, current)
243        return lnk
244
245    def _convertViewLink(self, link, views):
246        result = link
247        url = urlparse(link)
248        path = url[2].split('/')
249        if path[-1] in views:
250            if len(path) > 1:
251                path[-2] += '-%s.html' %path[-1]
252                path = path[:-1]
253                result = urlunparse((url[0], url[1], '/'.join(path), url[3], url[4], url[5]))
254        return result
255   
256    def runDocumentFilters(self, portal, current, soup, ssprops):
257        self.filterBaseTag(soup, current)
258        self.filterIgnoredSections(soup, ssprops)
259        self.filterIgnoredActions(soup, ssprops)
260        self.filterDocActionImages(soup, portal.portal_url(), current)
261        self.filterCSSLinks(soup, current)
262        self.filterJSLinks(soup, current)
263        self.filterImageFullscreenBackLink(soup, current)
264        links = self.getDocumentLinks(soup)
265        for x in links:
266            orig = x['href']
267            x['href'] = self.filterDocumentLink(x['href'],
268                                                current,
269                                                portal,
270                                                ssprops.getProperty('views_to_add'))
271            print '   %s => %s' %(orig, x['href'])
272
273        return soup.prettify()
274
275    def filterBaseTag(self, soup, current):
276        base = soup.findAll('base')
277        for x in base:
278            if x.has_key('href'):
279                url = x['href']
280                url = url.split(');')[0]
281                url = self._convertLinkToRelative(url, current)
282                x['href'] = url               
283
284    def filterIgnoredActions(self, soup, ssprops):
285        ftags = soup.findAll('div') + soup.findAll('li')
286        for x in ftags:
287            if x.has_key('id'):
288                id = x['id']
289                if 'document-action' in id:
290                    act = id.split('-')[-1]
291                    if act in ssprops.getProperty('actions_to_ignore'):
292                        x.extract()
293
294    def filterIgnoredSections(self, soup, ssprops):
295        for x in ssprops.getProperty('sections_to_ignore'):
296            tag = soup.find('div', id=x)
297            if tag:
298                tag.extract()
299
300    def filterDocActionImages(self, soup, portal_url, current):
301        tags = soup.findAll('li')
302        for x in tags:
303            if x.has_key('id'):
304                id = x['id']
305                if 'document-action' in id:                   
306                    for z in x.findAll('img'):
307                        src = z['src']
308                        surl = '%s/%s' % (portal_url, src)
309                        url = self._convertLinkToRelative(surl, current)                       
310                        z['src'] = url
311
312    def filterCSSLinks(self, soup, current):
313        #There are 2 cases, importing stylesheets, and linked stylesheets, process both
314        styles = soup.findAll('style', type="text/css")
315        for x in styles:
316            body = x.contents[0]
317            if '@import' in body:
318                url = body.split('url(')[-1]
319                url = url.split(');')[0]
320                url = self._convertLinkToRelative(url, current)
321                x.contents[0].replaceWith('<!-- @import url(%s); -->' %url)         
322        styles = soup.findAll('link', type="text/css")
323        for x in styles:
324            if x.has_key('href'):
325                url = x['href']
326                url = url.split(');')[0]
327                url = self._convertLinkToRelative(url, current)
328                x['href'] = url
329
330    def filterJSLinks(self, soup, current):
331        scripts = soup.findAll('script', type="text/javascript")
332        for x in scripts:
333            if x.has_key('src') == True:
334                url = x['src']
335                url = url.split(');')[0]
336                url = self._convertLinkToRelative(url, current)
337                x['src'] = url
338
339    def getDocumentLinks(self, soup):
340        tags = soup.findAll('a')
341        links = []
342        for tag in tags:
343            if tag.has_key('href'):
344                url = urlparse(tag['href'])
345                if not url[1] or 'localhost' in url[1]:
346                    links.append(tag)
347        return links
348
349    def filterImageFullscreenBackLink(self, soup, current):
350        if 'image_view_fullscreen' in current:
351            back = soup.find('a')
352            if back:
353                lt = back.find('span')
354                if lt:
355                    lt.contents[0].replaceWith('Back to Image')
356                if back.has_key('href'):
357                    back['href'] = current.replace('image_view_fullscreen.html', 'view.html')
358
359    def filterDocumentLink(self, link, current, portal, views):
360        lnk = link
361        url = urlparse(lnk)
362        if url[2] and 'javascript' != url[0]:
363            lnk = self._convertLinkToAbsolute(lnk, current)
364            lnk = self._convertObjectLink(lnk, portal, views)
365            lnk = self._convertLinkToRelative(lnk, current)
366        return lnk
367
368    def _convertLinkToAbsolute(self, link, current):
369        result = link
370        c = urlparse(current)
371        hr = urlparse(link)
372        if not hr[0] and not hr[1]:
373            cp = c[2].split('/')
374            hp = hr[2].split('/')
375            p = []
376            for y in hp:
377                if '.' == y:
378                    pass
379                elif '..' == y:
380                    cp = cp[:-1]
381                else:
382                    p.append(y)
383            result = urlunparse((c[0], c[1], '/'.join(cp + p), hr[3], hr[4], hr[5]))
384        return result
385
386    def _convertObjectLink(self, link, portal, views):
387        # This only works for absoulute links
388        result = link
389        hr = urlparse(link)
390        p = urlparse(portal.portal_url())
391        if p[1] == hr[1]:
392            h = hr[2].split('/')
393            view = ''
394            if h[-1] in views:
395                view = h[-1]
396                h = h[:-1]
397            results = portal.portal_catalog.searchResults(query={'path':'/'.join(h),}, id=h[-1])
398            if results:
399                path = ''
400                if results[0].is_folderish:
401                    if view:
402                        path = '/'.join(h) + '/index' + '-%s.html' %view
403                    else:
404                        path = '/'.join(h) + '/index.html'
405                else:
406                    if view:
407                        path = '/'.join(h) + '-%s.html' %view
408                    elif h:
409                        path = '/'.join(h)
410                        if 'Page' == results[0].Type and '.htm' not in path:
411                            path += '.html'
412                result = urlunparse((hr[0], hr[1], path, hr[3], hr[4], hr[5]))
413            elif link == portal.portal_url():
414                # Link points to site root
415                result = urlunparse((hr[0], hr[1], '/'.join(h) + '/index.html', hr[3], hr[4], hr[5]))
416        return result
417
418    def _convertLinkToRelative(self, link, current):
419        # This will break if the last item in the url path is a folder
420        # Make sure you rewrite the link path before you call this function
421        hr = urlparse(link)
422        c = urlparse(current)
423        if c[1] == hr[1]:
424            url1 = c[2].split('/')
425            url2 = hr[2].split('/')
426            index = 0
427            while url1[index:] and url2[index:] and url1[index] == url2[index]:
428                index += 1
429            p = []
430            for y in range(len(url1[index+1:])):
431                p.append('..')
432            p = p + url2[index:]
433            return urlunparse(('', '', '/'.join(p), hr[3], hr[4], hr[5]))
434       
Note: See TracBrowser for help on using the repository browser.