Look for textarea fields as well as input fields
[sitecorepy.git] / sitecore / __init__.py
1 #!/usr/bin/env python
2 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
3 #
4 # This file is part of SiteCorePy.
5 #
6 # SiteCorePy is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by the
8 # Free Software Foundation, either version 2 of the License, or (at your
9 # option) any later version.
10 #
11 # SiteCorePy is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with SiteCorePy.  If not, see <http://www.gnu.org/licenses/>.
18
19 """A Python_ interface to SiteCore_ via WebDriver_.
20
21 Because I like my wrists too much to enter data by hand.
22
23 Note that WebDriver is currently being merged into Selenium_.
24
25 .. _Python: http://www.python.org/
26 .. _SiteCore: http://www.sitecore.net/
27 .. _WebDriver: http://code.google.com/p/selenium/
28 .. _Selenium: http://code.google.com/p/selenium/
29 """
30
31 import getpass
32 import logging
33 import time
34
35 try:
36     from selenium.firefox.webdriver import WebDriver
37 except ImportError, e:
38     WebDriver = e  # allow some functionality without selenium
39
40 def get_logger(verbose=0):
41     verbosities = [  # in order of decreasing severity
42         logging.CRITICAL,
43         logging.ERROR,
44         logging.WARN,
45         logging.INFO,
46         logging.DEBUG,
47         ]
48     logger = logging.getLogger('sitecore')
49     if verbose >= len(verbosities):
50         verbose = -1
51     logger.setLevel(verbosities[verbose])
52     # create console handler and set the same level
53     ch = logging.StreamHandler()
54     ch.setLevel(verbosities[verbose])
55     # create formatter
56     formatter = logging.Formatter(
57         '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
58     # add formatter to ch
59     ch.setFormatter(formatter)
60     # add ch to logger
61     logger.addHandler(ch)
62     return logger
63
64
65 class SiteCoreConnection (object):
66     def __init__(self, url, verbose=0):
67         if isinstance(WebDriver, ImportError):
68             raise WebDriver  # *now* require sitecore
69         self.url = url
70         self.logger = get_logger(verbose)
71         self.wait_time = 5
72
73     def start(self):
74         'Start a single Firefox instance'
75         self.logger.info('starting Firefox')
76         self.w = WebDriver()
77         self.w.get(self.url)
78
79     def stop(self):
80         'Stop the Firefox instance'
81         self.logger.info('stopping Firefox')
82         self.w.quit()
83
84     def login(self, username=None, password=None):
85         if username == None:
86             username = getpass.getpass('username: ')
87         if password == None:
88             password = getpass.getpass('password: ')
89         self.logger.info('logging in as %s' % username)
90         uname = self.w.find_element_by_id('Login_UserName')
91         uname.send_keys(username)
92         pword = self.w.find_element_by_id('Login_Password')
93         pword.send_keys(password)
94         submit = self.w.find_element_by_id('Login_Login')
95         submit.click()
96         del(username)
97         del(password)
98
99         time.sleep(self.wait_time)
100
101     def find_nav_section(self, section):
102         self.logger.info('finding nav section %s' % section)
103         nav_bar = self.w.find_element_by_xpath("//tr[@id='ContentTreeHolder']")
104         link = nav_bar.find_element_by_link_text(section)
105         link_id = link.get_attribute('id')
106         img_id = link_id.replace('_Node_', '_Glyph_')
107         img = nav_bar.find_element_by_id(img_id)
108         div = nav_bar.find_element_by_xpath("//img[@id='%s']/.." % img_id)
109         return (div, img, link)
110
111     def expand_nav_section(self, section):
112         div,img,link = self.find_nav_section(section)
113         src = img.get_attribute('src')
114         if 'collapse' in src:
115             self.logger.info('nav section %s already expanded' % section)
116             return  # already expanded, no further action needed.
117             self.logger.info('nav section %s already expanded' % section)
118         assert 'expand' in src, src
119         self.logger.info('expanding nav section %s' % section)
120         img.click()
121         time.sleep(self.wait_time)
122
123     def open_nav_section(self, section):
124         div,img,link = self.find_nav_section(section)
125         self.logger.info('opening nav section %s' % section)
126         link.click()
127         time.sleep(self.wait_time)
128
129     def lock_section(self):
130         self.w.find_element_by_link_text('Lock and Edit').click()
131         time.sleep(self.wait_time)
132
133     def save_section(self):
134         self.logger.info('saving current section')
135         self.w.find_element_by_link_text('Save').click()
136         time.sleep(self.wait_time)
137
138     def publish_section(self, message):
139         self.save_section()
140         self.logger.info('publishing current section')
141         self.w.find_element_by_link_text('Review').click()
142         time.sleep(self.wait_time)
143         self.w.find_element_by_link_text('Approve for publication').click()
144         time.sleep(self.wait_time)
145         windows = self.w.get_window_handles()
146         current_window = self.w.get_current_window_handle()
147         self.logger.info('handling popup %s (from %s)'
148                          % (current_window, windows))
149         #self.w.switch_to_window(name)
150         a = self.w.switch_to_active_element()
151         a.send_keys(message+'\n')
152
153     def find_field(self, field):
154         self.logger.info('finding %s field' % field)
155         # found id start by manually focusing in the element and running
156         #   >>> a = s.w.switch_to_active_element()
157         #   >>> a.get_attribute('id')
158         # FIELD*, but the number * changes between sessions/versions
159         fields = []
160         for tag in ['input', 'textarea']:
161             fields.extend([f for f in self.w.find_elements_by_tag_name(tag)
162                            if f.get_attribute('id').startswith('FIELD')])
163         match = False
164         for f in fields:
165             granddad = f.find_element_by_xpath('/../..')
166             if granddad.get_text() == field:
167                 match = True
168                 break
169         if match != True:
170             raise KeyError(field)
171         return (granddad, f)