2 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
4 # This file is part of SiteCorePy.
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.
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.
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/>.
19 """A Python_ interface to SiteCore_ via WebDriver_.
21 Because I like my wrists too much to enter data by hand.
23 Note that WebDriver is currently being merged into Selenium_.
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/
36 from selenium.firefox.webdriver import WebDriver
37 except ImportError, e:
38 WebDriver = e # allow some functionality without selenium
43 def get_logger(verbose=0):
47 verbosities = [ # in order of decreasing severity
54 logger = logging.getLogger('sitecore')
55 if verbose >= len(verbosities):
57 logger.setLevel(verbosities[verbose])
58 # create console handler and set the same level
59 ch = logging.StreamHandler()
60 ch.setLevel(verbosities[verbose])
62 formatter = logging.Formatter(
63 '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
65 ch.setFormatter(formatter)
72 class SiteCoreConnection (object):
73 def __init__(self, url, verbose=0):
74 if isinstance(WebDriver, ImportError):
75 raise WebDriver # *now* require sitecore
77 self.logger = get_logger(verbose)
81 'Start a single Firefox instance'
82 self.logger.info('starting Firefox')
87 'Stop the Firefox instance'
88 self.logger.info('stopping Firefox')
91 def login(self, username=None, password=None):
93 username = getpass.getpass('username: ')
95 password = getpass.getpass('password: ')
96 self.logger.info('logging in as %s' % username)
97 uname = self.w.find_element_by_id('Login_UserName')
98 uname.send_keys(username)
99 pword = self.w.find_element_by_id('Login_Password')
100 pword.send_keys(password)
101 submit = self.w.find_element_by_id('Login_Login')
106 time.sleep(self.wait_time)
108 def find_nav_section(self, section):
109 self.logger.info('finding nav section %s' % section)
110 nav_bar = self.w.find_element_by_xpath("//tr[@id='ContentTreeHolder']")
111 link = nav_bar.find_element_by_link_text(section)
112 link_id = link.get_attribute('id')
113 img_id = link_id.replace('_Node_', '_Glyph_')
114 img = nav_bar.find_element_by_id(img_id)
115 div = nav_bar.find_element_by_xpath("//img[@id='%s']/.." % img_id)
116 return (div, img, link)
118 def expand_nav_section(self, section):
119 div,img,link = self.find_nav_section(section)
120 src = img.get_attribute('src')
121 if 'collapse' in src:
122 self.logger.info('nav section %s already expanded' % section)
123 return # already expanded, no further action needed.
124 self.logger.info('nav section %s already expanded' % section)
125 assert 'expand' in src, src
126 self.logger.info('expanding nav section %s' % section)
128 time.sleep(self.wait_time)
130 def open_nav_section(self, section):
131 div,img,link = self.find_nav_section(section)
132 self.logger.info('opening nav section %s' % section)
134 time.sleep(self.wait_time)
136 def lock_section(self):
137 self.w.find_element_by_link_text('Lock and Edit').click()
138 time.sleep(self.wait_time)
140 def save_section(self):
141 self.logger.info('saving current section')
142 self.w.find_element_by_id('Ribbon_Nav_HomeStrip').click()
143 time.sleep(self.wait_time)
144 self.w.find_element_by_link_text('Save').click()
145 time.sleep(self.wait_time)
147 def publish_section(self, message):
149 self.logger.info('publishing current section')
150 self.w.find_element_by_link_text('Review').click()
151 time.sleep(self.wait_time)
152 self.w.find_element_by_link_text('Approve for publication').click()
153 time.sleep(self.wait_time)
154 windows = self.w.get_window_handles()
155 current_window = self.w.get_current_window_handle()
156 self.logger.info('handling popup %s (from %s)'
157 % (current_window, windows))
158 #self.w.switch_to_window(name)
159 a = self.w.switch_to_active_element()
160 a.send_keys(message+'\n')
162 def find_field(self, field_regexp, name=None):
163 self.logger.info('finding %s field' % name)
164 # found id start by manually focusing in the element and running
165 # >>> a = s.w.switch_to_active_element()
166 # >>> a.get_attribute('id')
167 # FIELD*, but the number * changes between sessions/versions
169 for tag in ['input', 'textarea', 'iframe']:
170 fields.extend([f for f in self.w.find_elements_by_tag_name(tag)
171 if f.get_attribute('id').startswith('FIELD')])
174 granddad = f.find_element_by_xpath('/../..')
175 if field_regexp.match(granddad.get_text()) != None: