def _on_setup_widgets(self): """Hook. """ from ._api import dispense_entity # List of items to process ol = htmler.Ol() for eid in self.attr('eids', self.attr('ids', [])): entity = dispense_entity(self.attr('model'), eid) self.add_widget( widget.input.Hidden(uid='eids-' + eid, name='eids', value=eid)) ol.append_child( htmler.Li(entity.odm_ui_mass_action_entity_description())) self.add_widget(widget.static.HTML(uid='eids-text', em=ol)) # Submit button submit_button = self.get_widget( 'action_submit') # type: widget.button.Submit submit_button.value = lang.t('odm_ui@continue') submit_button.icon = 'angle-double-right' # Cancel button self.add_widget( widget.button.Link(uid='action_cancel', weight=100, value=lang.t('odm_ui@cancel'), href=self.referer or self.redirect or router.base_url(), icon='fa fas fa-ban', form_area='footer'))
def _on_setup_widgets(self): e_type = self.attr('e_type') eids = self.attr('eids', []) ol = htmler.Ol() for eid in eids: if e_type == 'role': ol.append_child(htmler.Li(auth.get_role(uid=eid).name)) elif e_type == 'user': ol.append_child(htmler.Li(auth.get_user(uid=eid).login)) self.add_widget( widget.static.Text( uid='confirmation_text', text=lang.t('auth_admin@delete_{}_confirmation'.format( self.attr('e_type'))), )) self.add_widget(widget.static.HTML( uid='uids_text', em=ol, )) self.add_widget( widget.button.Link(uid='action_cancel', weight=100, form_area='footer', href=self.referer or self.redirect or router.base_url(), value=lang.t('auth_admin@cancel'), icon='fa fas fa-ban')) self.submit_button.color = 'btn btn-danger' self.submit_button.icon = 'fa fas fa-trash' self.submit_button.value = lang.t('auth_admin@delete')
def exec(self) -> Union[str, http.RedirectResponse]: # Redirect to the base URL if user is already authenticated if not auth.get_current_user().is_anonymous: return self.redirect(self.arg('__redirect', router.base_url())) # Determine driver's name from argument or get default try: driver_name = self.arg('driver', _api.get_driver().name) except auth.error.DriverNotRegistered: raise self.not_found() rule_name = self.arg('_pytsite_router_rule_name') if 'sign_in' in rule_name: form_type = 'sign-in' form = _api.sign_in_form(self.request, driver_name) elif 'sign_up' in rule_name: # Check if sign up is enabled if not auth.is_sign_up_enabled(): raise self.not_found() form_type = 'sign-up' form = _api.sign_up_form(self.request, driver_name) elif 'restore_account' in rule_name: form_type = 'restore-account' form = _api.restore_account_form(self.request, driver_name) form.redirect = router.base_url() else: raise ValueError('Unsupported form type') if not form.redirect: form.redirect = router.base_url() metatag.t_set('title', form.title) tpl_args = { 'driver': driver_name, 'form_type': form_type, 'form': form, } try: return router.call('auth_ui_form', tpl_args) except routing.error.RuleNotFound: return tpl.render('auth_ui/form', tpl_args)
def _on_setup_widgets(self): from ._api import dispense_entity model = self.attr('model') eid = self.attr('eid') # Setting up form's widgets through entity hook and global event entity = dispense_entity(model, eid) entity.odm_ui_m_form_setup_widgets(self) events.fire('odm_ui@m_form_setup_widgets.{}'.format(model), frm=self, entity=entity) if self.current_step == 1: # Entity model self.add_widget( widget.input.Hidden( uid='model', value=model, form_area='hidden', )) # Entity ID self.add_widget( widget.input.Hidden( uid='eid', value=eid, form_area='hidden', )) # Entity ref self.add_widget( widget.input.Hidden( uid='ref', value=entity.ref if not entity.is_new else None, form_area='hidden', )) # Cancel button URL cancel_href = self.redirect if not cancel_href or cancel_href == 'ENTITY_VIEW': if self.referer != self.location and self.referer: cancel_href = self.referer elif not entity.is_new and entity.odm_ui_view_url(): cancel_href = entity.odm_ui_view_url() else: cancel_href = router.base_url() # Cancel button self.add_widget( widget.button.Link( uid='action_cancel_' + str(self.current_step), weight=150, value=lang.t('odm_ui@cancel'), icon='fa fas fa-fw fa-remove fa-times', href=cancel_href, form_area='footer', ))
def exec(self): """Unsubscribe from content digest """ s = _odm.dispense('content_digest_subscriber', self.arg('sid')) if s: s.f_set('enabled', False).save() _router.session().add_success_message( _lang.t('content_digest@unsubscription_successful')) return self.redirect(_router.base_url())
def exec(self): # Check for error from Google error = self.arg('error') if error == 'access_denied': raise self.forbidden( lang.t('auth_ui_google@user_declined_authorization')) # Check for code from Google code = self.arg('code') if code: # Restore flow from session flow = router.session().get( 'google_oauth2_flow') # type: OAuth2WebServerFlow if not flow: raise self.forbidden('Cannot retrieve stored OAuth2 flow') # Exchange code to credentials credentials = flow.step2_exchange(code) user = auth.get_current_user() user.set_option('google_oauth2_credentials', credentials.to_json()) user.save() final_redirect = router.session().get( 'google_oauth2_final_redirect', router.base_url()) router.session().pop('google_oauth2_flow') router.session().pop('google_oauth2_final_redirect') return self.redirect(final_redirect) else: # Request new code from Google scope = self.args.pop('scope') # type: str if scope and ',' in scope: scope = scope.split(',') flow = auth_google.create_oauth2_flow( scope, router.current_url(True, query=dict(self.args))) router.session()['google_oauth2_flow'] = flow router.session()['google_oauth2_final_redirect'] = self.args.pop( '__redirect', router.base_url()) return self.redirect(flow.step1_get_authorize_url())
def exec(self): try: # Search for user user = next( auth.find_users( query.Query(query.Eq('confirmation_hash', self.arg('code'))))) except StopIteration: # No user found, redirect to sign in URL return self.redirect(_api.sign_in_url(redirect=router.base_url())) try: auth.switch_user_to_system() user.confirmation_hash = None if user.status == auth.USER_STATUS_WAITING: user.status = auth.get_new_user_status() user.save() finally: auth.restore_user() router.session().add_success_message( lang.t('auth_ui@registration_confirm_success')) return self.redirect(_api.sign_in_url(redirect=router.base_url()))
def exec(self): auth.sign_out(auth.get_current_user()) return self.redirect(self.arg('__redirect', router.base_url()))
def _on_setup_widgets(self): role_uid = self.attr('role_uid') role = auth.get_role(uid=role_uid) if role_uid != '0' else None self.add_widget(widget.input.Text( uid='name', value=role.name if role else None, label=self.t('name'), required=True, enabled=role.name not in ('anonymous', 'user') if role else True, )) self.add_rule('name', auth.validation.AuthEntityFieldUnique( e_type='role', field_name='name', exclude_uids=role.uid if role else None, )) self.add_widget(widget.input.Text( uid='description', value=role.description if role else None, label=self.t('description'), required=True, enabled=role.name not in ('anonymous', 'user') if role else True, )) # Permissions tabs perms_tabs = widget.select.Tabs( uid='permissions', label=self.t('permissions') ) # Permissions tabs content for g_name, g_desc in sorted(permissions.get_permission_groups().items(), key=lambda x: x[0]): if g_name == 'auth': continue perms = permissions.get_permissions(g_name) if not perms: continue # Tab tab_id = 'permissions-' + g_name perms_tabs.add_tab(tab_id, lang.t(g_desc)) # Tab's content perms_tabs.add_widget(widget.select.Checkboxes( uid='permission-checkboxes-' + tab_id, name='permissions', items=[(p[0], lang.t(p[1])) for p in perms], value=role.permissions if role else [], ), tab_id) self.add_widget(perms_tabs) # "Cancel" button self.add_widget(widget.button.Link( uid='action_cancel', weight=100, form_area='footer', icon='fa fas fa-fw fa-ban', value=self.t('cancel'), href=self.referer or self.redirect or router.base_url(), ))
def _on_setup_widgets(self): user_uid = self.attr('user_uid') user = auth.get_user(uid=user_uid) if user_uid != '0' else None c_user = auth.get_current_user() row_1 = self.add_widget(widget.container.Card( uid='row_1', header=self.t('registration_info'), body_css='row', )) row_1_left = row_1.append_child(widget.container.Container( uid='row_1_left', css='col-xs-12 col-12 col-sm-4 col-md-2', )) row_1_center = row_1.append_child(widget.container.Container( uid='row_1_center', css='col-xs-12 col-12 col-sm-4 col-md-5', )) row_1_right = row_1.append_child(widget.container.Container( uid='row_1_right', css='col-xs-12 col-12 col-sm-4 col-md-5', )) # Picture row_1_left.append_child(file_ui.widget.ImagesUpload( uid='picture', value=user.picture if user else None, max_file_size=3, label=self.t('photo'), )) # Login row_1_center.append_child(widget.input.Email( uid='login', value=user.login if user else None, label=self.t('email'), required=True, enabled=c_user.is_admin, max_length=auth.LOGIN_MAX_LENGTH, )) self.add_rule('login', auth.validation.AuthEntityFieldUnique( e_type='user', field_name='login', exclude_uids=user.uid if user else None, )) # Nickname row_1_center.append_child(widget.input.Text( uid='nickname', value=user.nickname if user else None, label=self.t('nickname'), required=True, max_length=auth.NICKNAME_MAX_LENGTH, )) self.add_rules('nickname', ( auth.user_nickname_rule, auth.validation.AuthEntityFieldUnique( e_type='user', field_name='nickname', exclude_uids=user.uid if user else None, ) )) # Birth date row_1_center.append_child(widget.select.DateTime( uid='birth_date', value=user.birth_date if user else None, label=self.t('birth_date'), timepicker=False, )) # Gender row_1_center.append_child(widget.select.Select( uid='gender', value=user.gender if user else None, label=self.t('gender'), items=[ ('m', self.t('male')), ('f', self.t('female')), ] )) # First name row_1_right.append_child(widget.input.Text( uid='first_name', value=user.first_name if user else None, label=self.t('first_name'), required=True, max_length=auth.FIRST_NAME_MAX_LENGTH, )) # Middle name row_1_right.append_child(widget.input.Text( uid='middle_name', value=user.middle_name if user else None, label=self.t('middle_name'), max_length=auth.MIDDLE_NAME_MAX_LENGTH, )) # Last name row_1_right.append_child(widget.input.Text( uid='last_name', value=user.last_name if user else None, label=self.t('last_name'), max_length=auth.LAST_NAME_MAX_LENGTH, )) # Position row_1_right.append_child(widget.input.Text( uid='position', value=user.position if user else None, label=self.t('position'), max_length=auth.USER_POSITION_MAX_LENGTH, )) # Row 2 row_2 = self.add_widget(widget.container.Container( uid='row_2', body_css='row', )) # Row 2 left row_2_left = row_2.append_child(widget.container.Container( uid='row_2_left', css='col-xs-12 col-12 col-sm-6 col-lg-8', )) # Contact info contact = row_2_left.append_child(widget.container.Card( uid='contact', body_css='row', header=self.t('contact_info'), )) # Contact info left contact_left = contact.append_child(widget.container.Container( uid='contact_left', css='col-xs-12 col-12 col-lg-6' )) # Country contact_left.append_child(widget.input.Text( uid='country', value=user.country if user else None, label=self.t('country'), max_length=auth.COUNTRY_MAX_LENGTH, )) # Province contact_left.append_child(widget.input.Text( uid='province', value=user.province if user else None, label=self.t('province'), max_length=auth.PROVINCE_MAX_LENGTH, )) # City contact_left.append_child(widget.input.Text( uid='city', value=user.city if user else None, label=self.t('city'), max_length=auth.CITY_MAX_LENGTH, )) # District contact_left.append_child(widget.input.Text( uid='district', value=user.district if user else None, label=self.t('district'), max_length=auth.DISTRICT_MAX_LENGTH, )) # Contact info right contact_right = contact.append_child(widget.container.Container( uid='contact_right', css='col-xs-12 col-12 col-lg-6' )) # Street contact_right.append_child(widget.input.Text( uid='street', value=user.street if user else None, label=self.t('street'), max_length=auth.STREET_MAX_LENGTH, )) # House number contact_right.append_child(widget.input.Text( uid='building', value=user.building if user else None, label=self.t('building'), max_length=auth.BUILDING_MAX_LENGTH, )) # Apt number contact_right.append_child(widget.input.Text( uid='apt_number', value=user.apt_number if user else None, label=self.t('apt_number'), max_length=auth.APT_NUMBER_MAX_LENGTH, )) # Phone contact_right.append_child(widget.input.Text( uid='phone', value=user.phone if user else None, label=self.t('phone'), max_length=auth.PHONE_MAX_LENGTH, )) # URLs contact.append_child(widget.input.StringList( uid='urls', value=user.urls if user else None, label=self.t('social_links'), max_rows=10, add_btn_label=self.t('add_link'), css='col-xs-12 col-12', )) self.add_rule('urls', validation.rule.Url()) # Row 2 right row_2_right = row_2.append_child(widget.container.Container( uid='row_2_right', css='col-xs-12 col-12 col-sm-6 col-lg-4', )) # Cover picture card cover_picture_card = row_2_right.append_child(widget.container.Card( uid='cover_picture_card', header=self.t('cover_picture'), )) # Cover picture cover_picture_card.append_child(file_ui.widget.ImagesUpload( uid='cover_picture', thumb_width=1200, thumb_height=450, max_file_size=5, value=user.cover_picture if user else None, )) # Security card security = row_2_right.append_child(widget.container.Card( uid='security', header=self.t('security'), )) # User account confirmed if c_user.is_admin and auth.is_sign_up_confirmation_required(): security.append_child(widget.select.Checkbox( uid='is_confirmed', value=user.is_confirmed if (user and user.is_confirmed) else None, label=self.t('user_account_is_confirmed'), )) # Profile is public security.append_child(widget.select.Checkbox( uid='is_public', value=user.is_public if user else None, label=self.t('this_is_public_profile'), )) # New password security.append_child(widget.input.Password( uid='password', label=self.t('new_password'), autocomplete='new-password', )) # New password confirm security.append_child(widget.input.Password( uid='password_confirm', label=self.t('new_password_confirmation'), autocomplete='new-password', )) # Row 3 row_3 = self.add_widget(widget.container.Card( uid='row_3', header=self.t('about_yourself'), )) # Description row_3.append_child(widget.input.TextArea( uid='description', value=user.description if user else '', max_length=auth.USER_DESCRIPTION_MAX_LENGTH, )) # Row 4 if c_user.is_admin: admin = self.add_widget(widget.container.Card( uid='admin', header=self.t('administration'), body_css='row', )) admin.append_child(widget.select.Select( uid='status', value=user.status if user else auth.get_new_user_status(), label=self.t('status'), items=auth.get_user_statuses(), required=True, append_none_item=False, css='col-xs-12 col-12 col-md-3' )) admin.append_child(_widget.RolesCheckboxes( uid='roles', value=user.roles if user else [auth.get_role(r) for r in auth.get_new_user_roles()], label=self.t('roles'), css='col-xs-12 col-12 col-md-3' )) # "Cancel" button self.add_widget(widget.button.Link( uid='action_cancel', weight=100, form_area='footer', icon='fa fas fa-fw fa-ban', value=self.t('cancel'), href=self.referer or self.redirect or router.base_url(), ))
def exec(self): # Delayed import to prevent circular dependency from . import _api # Checking if the model is registered model = self.arg('model') if not _api.is_model_registered(model): raise self.not_found() # Getting finder f = _api.find(model) # Breadcrumb breadcrumb = widget.select.Breadcrumb('content-index-breadcrumb') breadcrumb.append_item(lang.t('content@home_page'), router.base_url()) # Filter by term term_field_name = self.arg('term_field') term_alias = self.arg('term_alias') term = None if term_field_name and f.mock.has_field(term_field_name): term_field = f.mock.get_field( term_field_name) # type: odm.field.Ref if term_alias and term_field.model: for term_model in term_field.model: term = taxonomy.find(term_model).eq('alias', term_alias).first() if term: self.args['term'] = term if isinstance(f.mock.fields[term_field_name], odm.field.Ref): f.eq(term_field_name, term) elif isinstance(f.mock.fields[term_field_name], odm.field.RefsList): f.inc(term_field_name, term) metatag.t_set('title', term.title) breadcrumb.append_item(term.title) else: raise self.not_found() else: raise self.not_found() # Filter by author author_nickname = self.arg('author') if author_nickname: try: author = auth.get_user(nickname=author_nickname) f.eq('author', author.uid) self.args['author'] = author metatag.t_set( 'title', lang.t('content@articles_of_author', {'name': author.first_last_name})) if term: breadcrumb.pop_item() breadcrumb.append_item( term.title, router.rule_url( 'content@index', { 'model': model, 'term_field': term_field_name, 'term_alias': term_alias, })) breadcrumb.append_item(author.first_last_name) except auth.error.UserNotFound: raise self.not_found() self.args.update({ 'finder': f, 'breadcrumb': breadcrumb, }) try: # Call a controller provided by application return router.call('content_index', self.args) except routing.error.RuleNotFound: # Render a template provided by application return tpl.render('content/index', self.args)
def exec(self): from . import _api model = self.arg('model') entity = _api.find(model, status='*', check_publish_time=False) \ .eq('_id', self.arg('eid')) \ .first() # type: _model.ContentWithURL # Check entity existence if not entity: raise self.not_found() # Check permissions if not entity.odm_auth_check_entity_permissions(CONTENT_PERM_VIEW): raise self.not_found() # Show non-published entities only to authors and users who can edit or delete them c_user = auth.get_current_user() if (entity.has_field('publish_time') and entity.publish_time > datetime.now()) or \ (entity.has_field('status') and entity.status in (CONTENT_STATUS_UNPUBLISHED, CONTENT_STATUS_WAITING)): if not (entity.author == c_user or entity.odm_auth_check_entity_permissions( [PERM_MODIFY, PERM_DELETE])): raise self.not_found() # Show warnings about unpublished entities if entity.has_field( 'publish_time') and entity.publish_time > datetime.now(): router.session().add_warning_message( lang.t('content@content_warning_future_publish_time')) if entity.has_field('status') and entity.status in ( CONTENT_STATUS_UNPUBLISHED, CONTENT_STATUS_WAITING): router.session().add_warning_message( lang.t('content@content_status_warning_{}'.format( entity.status))) # Breadcrumb breadcrumb = widget.select.Breadcrumb('content-index-breadcrumb') breadcrumb.append_item(lang.t('content@home_page'), router.base_url()) entity.content_breadcrumb(breadcrumb) # Meta title if entity.has_field('title'): title = entity.title metatag.t_set('title', title) metatag.t_set('og:title', title) metatag.t_set('twitter:title', title) # Meta description if entity.has_field('description'): description = entity.f_get('description') metatag.t_set('description', description) metatag.t_set('og:description', description) metatag.t_set('twitter:description', description) # Meta keywords if entity.has_field('tags'): metatag.t_set('keywords', entity.f_get('tags', as_string=True)) # Meta image if entity.has_field('images') and entity.images: metatag.t_set('twitter:card', 'summary_large_image') image_w = 900 image_h = 500 image_url = entity.images[0].get_url(width=image_w, height=image_h) metatag.t_set('og:image', image_url) metatag.t_set('og:image:width', str(image_w)) metatag.t_set('og:image:height', str(image_h)) metatag.t_set('twitter:image', image_url) else: metatag.t_set('twitter:card', 'summary') # Various metatags metatag.t_set('og:type', 'article') metatag.t_set('og:url', entity.url) metatag.t_set('article:publisher', entity.url) # 'Author' metatag if entity.has_field('author') and entity.author: metatag.t_set('author', entity.author.first_last_name) metatag.t_set('article:author', entity.author.first_last_name) # Alternate languages URLs for lng in lang.langs(False): f_name = 'localization_' + lng if entity.has_field(f_name) and entity.f_get(f_name): hreflang.put(lng, entity.f_get(f_name).url) else: hreflang.remove(lng) # Update args self.args.update({ 'entity': entity, 'breadcrumb': breadcrumb, }) # Notify listeners events.fire('content@view', entity=entity) try: # Call a controller provided by application return router.call('content_view', self.args) except routing.error.RuleNotFound: # Render a template provided by application return tpl.render('content/view', self.args)
def generate_rss(model: str, filename: str, lng: str = '*', finder_setup: Callable[[odm.SingleModelFinder], None] = None, item_setup: Callable[[feed.xml.Serializable, Content], None] = None, length: int = 20): """Generate RSS feeds """ # Setup finder finder = find(model, language=lng) if finder_setup: finder_setup(finder) # Preparing output directory output_dir = path.join(reg.get('paths.static'), 'feed') if not path.exists(output_dir): makedirs(output_dir, 0o755, True) # Create generator content_settings = reg.get('content') parser = feed.rss.Parser() # Get <channel> element channel = parser.get_children('channel')[0] # Channel title channel.append_child(feed.rss.em.Title(content_settings.get('home_title_' + lng) or 'UNTITLED')) # Channel description channel.append_child(feed.rss.em.Description(content_settings.get('home_description_' + lng))) # Channel link channel.append_child(feed.rss.em.Link(router.base_url())) # Channel language channel.append_child(feed.rss.em.Language(lng)) # Channel logo logo_url = router.url(reg.get('content.rss_logo_url', 'assets/app/img/logo-rss.png')) channel.append_child(feed.rss.yandex.Logo(logo_url)) square_logo_url = router.url(reg.get('content.rss_square_logo_url', 'assets/app/img/logo-rss-square.png')) channel.append_child(feed.rss.yandex.Logo(square_logo_url, square=True)) # Append channel's items for entity in finder.get(length): item = feed.rss.em.Item() try: item.append_child(feed.rss.em.Title(entity.title)) item.append_child(feed.rss.em.Link(entity.url)) item.append_child(feed.rss.em.PdaLink(entity.url)) item.append_child(feed.rss.em.Description(entity.description if entity.description else entity.title)) item.append_child(feed.rss.em.PubDate(entity.publish_time)) item.append_child(feed.rss.em.Author('{} ({})'.format(entity.author.login, entity.author.first_last_name))) except odm.error.FieldNotDefined: pass # Section if entity.has_field('section'): item.append_child(feed.rss.em.Category(entity.section.title)) # Tags if entity.has_field('tags'): for tag in entity.tags: item.append_child(feed.rss.pytsite.Tag(tag.title)) # Images if entity.has_field('images') and entity.images: # Attaching all the images as enclosures for img in entity.images: item.append_child(feed.rss.em.Enclosure(url=img.get_url(), length=img.length, type=img.mime)) # Video links if entity.has_field('video_links') and entity.video_links: m_group = item.append_child(feed.rss.media.Group()) for link_url in entity.video_links: m_group.add_widget(feed.rss.media.Player(url=link_url)) # Body if entity.has_field('body'): item.append_child(feed.rss.yandex.FullText(entity.f_get('body', process_tags=False, remove_tags=True))) item.append_child(feed.rss.content.Encoded(entity.f_get('body', process_tags=False, remove_tags=True))) item.append_child(feed.rss.pytsite.FullText(entity.f_get('body', process_tags=False))) if item_setup: item_setup(item, entity) channel.append_child(item) # Write feed content out_path = path.join(output_dir, '{}-{}.xml'.format(filename, lng)) with open(out_path, 'wt', encoding='utf-8') as f: f.write(parser.generate()) logger.info("RSS feed successfully written to '{}'.".format(out_path))
def _generate_sitemap(): """Generate content sitemap """ global _sitemap_generation_works if _sitemap_generation_works: raise RuntimeError('Sitemap generation is still in progress') _sitemap_generation_works = True logger.info('Sitemap generation start.') output_dir = path.join(reg.get('paths.static'), 'sitemap') if path.exists(output_dir): rmtree(output_dir) makedirs(output_dir, 0o755, True) sitemap_index = sitemap.Index() links_per_file = 50000 loop_count = 1 loop_links = 1 sm = sitemap.Sitemap() sm.add_url(router.base_url(), datetime.now(), 'always', 1) for lng in lang.langs(): for model in reg.get('content.sitemap_models', ()): logger.info( "Sitemap generation started for model '{}', language '{}'". format(model, lang.lang_title(lng))) for entity in _api.find(model, language=lng): # type: ContentWithURL sm.add_url(entity.url, entity.publish_time) loop_links += 1 # Flush sitemap if loop_links >= links_per_file: loop_count += 1 loop_links = 0 sitemap_path = sm.write( path.join(output_dir, 'data-%02d.xml' % loop_count), True) logger.info( "'{}' successfully written with {} links".format( sitemap_path, loop_links)) sitemap_index.add_url( router.url('/sitemap/{}'.format( path.basename(sitemap_path)))) del sm sm = sitemap.Sitemap() # If non-flushed sitemap exist if len(sm): sitemap_path = sm.write( path.join(output_dir, 'data-%02d.xml' % loop_count), True) logger.info("'{}' successfully written with {} links.".format( sitemap_path, loop_links)) sitemap_index.add_url( router.url('/sitemap/{}'.format(path.basename(sitemap_path)))) if len(sitemap_index): sitemap_index_path = sitemap_index.write( path.join(output_dir, 'index.xml')) logger.info("'{}' successfully written.".format(sitemap_index_path)) logger.info('Sitemap generation stop.') _sitemap_generation_works = False