def queue_index_actions(blog, include_manual=False): ''' Pushes to the publishing queue all the index pages for a given blog that are marked for Immediate publishing. :param blog: The blog object whose index templates will be pushed to the queue. :param include_manual: If set to True, all templates, including those set to the Manual publishing mode, will be pushed to the queue. Default is False, since those templates are not pushed in most publishing actions. ''' templates = blog.index_templates.select().where( Template.publishing_mode != publishing_mode.do_not_publish) if include_manual is False: templates = templates.select().where( Template.publishing_mode == publishing_mode.immediate) if templates.count() == 0: raise Template.DoesNotExist( "No valid index templates exist for blog {}.".format(blog.for_log)) mappings = TemplateMapping.select().where( TemplateMapping.template << templates) fileinfos = FileInfo.select().where(FileInfo.template_mapping << mappings) for f in fileinfos: Queue.push(job_type=job_type.index, priority=1, blog=blog, site=blog.site, data_integer=f.id)
def queue_index_actions(blog): ''' Pushes to the publishing queue all the index pages for a given blog that are marked for Immediate publishing. ''' try: templates = Template.select().where( Template.blog == blog, Template.template_type == template_type.index, Template.publishing_mode == publishing_mode.immediate) if templates.count() == 0: raise Template.DoesNotExist except Template.DoesNotExist: raise Template.DoesNotExist( "No index templates exist for blog {}.".format(blog.for_log)) else: mappings = TemplateMapping.select().where( TemplateMapping.template << templates) fileinfos = FileInfo.select().where( FileInfo.template_mapping << mappings) for f in fileinfos: push_to_queue(job_type=job_type.index, priority=1, blog=blog, site=blog.site, data_integer=f.id)
def build_indexes_fileinfos(templates): ''' Rebuilds a fileinfo entry for a given main index. This will need to be run every time we create a new index type, or change a mapping. (Most of these should have a 1:1 mapping) A control message should not be needed, since these are 1:1 This will port the code currently found in build_blog_fileinfo, much as the above function did. ''' n = 0 for template in templates: n += 1 index_mappings = TemplateMapping.select().where( TemplateMapping.template == template) blog = index_mappings[0].template.blog tags = template_tags(blog_id=blog.id) for i in index_mappings: path_string = tpl(tpl_oneline(i.path_string), **tags.__dict__) if path_string == '': continue master_path_string = path_string add_page_fileinfo(None, i, master_path_string, blog.url + "/" + master_path_string, blog.path + '/' + master_path_string) return n
def build_pages_fileinfos(pages): ''' Creates fileinfo entries for the template mappings associated with an iterable list of Page objects. ''' n = 0 for page in pages: n += 1 template_mappings = page.template_mappings if template_mappings.count() == 0: raise TemplateMapping.DoesNotExist( 'No template mappings found for this page.') tags = template_tags(page_id=page.id) for t in template_mappings: path_string = generate_date_mapping(page.publication_date.date(), tags, t.path_string) if path_string == '': continue master_path_string = path_string + "." + page.blog.base_extension add_page_fileinfo(page, t, master_path_string, page.blog.url + "/" + master_path_string, page.blog.path + '/' + master_path_string, str(page.publication_date)) return n
def queue_index_actions(blog, include_manual=False): ''' Pushes to the publishing queue all the index pages for a given blog that are marked for Immediate publishing. :param blog: The blog object whose index templates will be pushed to the queue. :param include_manual: If set to True, all templates, including those set to the Manual publishing mode, will be pushed to the queue. Default is False, since those templates are not pushed in most publishing actions. ''' templates = blog.index_templates.select().where( Template.publishing_mode != publishing_mode.do_not_publish) if include_manual is False: templates = templates.select().where( Template.publishing_mode == publishing_mode.immediate) if templates.count() == 0: raise Template.DoesNotExist("No valid index templates exist for blog {}.".format( blog.for_log)) mappings = TemplateMapping.select().where(TemplateMapping.template << templates) fileinfos = FileInfo.select().where(FileInfo.template_mapping << mappings) for f in fileinfos: Queue.push(job_type=job_type.index, priority=1, blog=blog, site=blog.site, data_integer=f.id)
def delete(template): t0 = FileInfo.delete().where( FileInfo.template_mapping << template.mappings) t0.execute() t1 = TemplateMapping.delete().where( TemplateMapping.id << template.mappings) t1.execute() t2 = Template.delete().where(Template.id == template.id) t2.execute()
def build_archives_fileinfos(pages): ''' Takes a page (maybe a collection of same) and produces fileinfos for the date-based archive entries for each ''' counter = 0 mapping_list = {} for page in pages: tags = template_tags(page_id=page.id) if page.archive_mappings.count() == 0: raise TemplateMapping.DoesNotExist( 'No template mappings found for the archives for this page.') for m in page.archive_mappings: path_string = generate_date_mapping(page.publication_date, tags, m.path_string) if path_string == '': continue if path_string in mapping_list: continue # tag_context = generate_archive_context_from_page(m.archive_xref, page.blog, page) mapping_list[path_string] = (( None, m, path_string, page.blog.url + "/" + path_string, page.blog.path + '/' + path_string, ), (page)) for n in mapping_list: counter += 1 new_fileinfo = add_page_fileinfo(*mapping_list[n][0]) archive_context = [] m = mapping_list[n][0][1] for r in m.archive_xref: archive_context.append(archive_functions[r]["format"]( archive_functions[r]["mapping"](mapping_list[n][1]))) for t, r in zip(archive_context, m.archive_xref): new_fileinfo_context = FileInfoContext.get_or_create( fileinfo=new_fileinfo, object=r, ref=t) new_fileinfo.mapping_sort = archive_context new_fileinfo.save() # @return mapping_list return counter
def export_theme_for_blog(blog_id): from core.models import Template, TemplateMapping, KeyValue theme_to_export = Template.select().where( Template.blog == blog_id) theme = {} theme["title"] = theme_to_export[0].theme.title theme["description"] = theme_to_export[0].theme.description theme["data"] = {} for n in theme_to_export: theme["data"][n.id] = {} theme["data"][n.id]["template"] = json_dump(n) mappings_to_export = TemplateMapping.select().where( TemplateMapping.template == n) theme["data"][n.id]["mapping"] = {} for m in mappings_to_export: theme["data"][n.id]["mapping"][m.id] = json_dump(m) theme["kv"] = {} kv_list = [] top_kvs = KeyValue.select().where( KeyValue.object == 'Theme', KeyValue.objectid == theme_to_export[0].theme.id, KeyValue.is_schema == True) for n in top_kvs: kv_list.append(n) while len(kv_list) > 0: theme["kv"][kv_list[0].id] = json_dump(kv_list[0]) next_kvs = KeyValue.select().where( KeyValue.parent == kv_list[0], KeyValue.is_schema == True) for f in next_kvs: kv_list.append(f) del kv_list[0] import settings with open(settings.APPLICATION_PATH + settings._sep + "install" + settings._sep + "templates.json", "w", encoding='utf-8') as output_file: output_file.write(json.dumps(theme, indent=1, sort_keys=True, allow_nan=True)) return theme
def new_template(blog_id, tpl_type): with db.atomic() as txn: user = auth.is_logged_in(request) blog = Blog.load(blog_id) permission = auth.is_blog_designer(user, blog) auth.check_template_lock(blog) mappings_index = template_mapping_index.get(tpl_type, None) if mappings_index is None: raise Exception('Mapping type not found') template = Template( blog=blog, theme=blog.theme, template_type=tpl_type, publishing_mode=publishing_mode.do_not_publish, body='', ) template.save(user) template.title = 'Untitled Template #{}'.format(template.id) template.save(user) if tpl_type != template_type.media: new_template_mapping = TemplateMapping( template=template, is_default=True, path_string="'" + utils.create_basename(template.title, blog) + "'") new_template_mapping.save() from core.cms import fileinfo fileinfo.build_mapping_xrefs((new_template_mapping, )) from settings import BASE_URL redirect(BASE_URL + '/template/{}/edit'.format(template.id))
def build_pages_fileinfos(pages, template_mappings=None): ''' Creates fileinfo entries for the template mappings associated with an iterable list of Page objects. :param pages: List of page objects to build fileinfos for. ''' fileinfos = [] for n, page in enumerate(pages): if template_mappings is None: mappings = page.template_mappings else: mappings = template_mappings if mappings.count() == 0: raise TemplateMapping.DoesNotExist('No template mappings found for this page.') tags = template_tags(page=page) for t in mappings: # path_string = replace_mapping_tags(t.path_string) path_string = generate_date_mapping( page.publication_date_tz.date(), tags, replace_mapping_tags(t.path_string)) # for tag archives, we need to return a list from the date mapping # in the event that we have a tag present that's an iterable like the tag list # e.g., for /%t/%Y, for a given page that has five tags # we return five values, one for each tag, along with the year if path_string == '' or path_string is None: continue # master_path_string = path_string fileinfos.append( add_page_fileinfo(page, t, path_string, page.blog.url + "/" + path_string, page.blog.path + '/' + path_string, str(page.publication_date_tz)) ) return fileinfos
def build_indexes_fileinfos(templates): ''' Rebuilds a fileinfo entry for a given main index. This will need to be run every time we create a new index type, or change a mapping. (Most of these should have a 1:1 mapping) A control message should not be needed, since these are 1:1 This will port the code currently found in build_blog_fileinfo, much as the above function did. :param templates: A list of templates, typically for main indexes, to rebuild fileinfo entries for. ''' for n, template in enumerate(templates): index_mappings = TemplateMapping.select().where( TemplateMapping.template == template) blog = index_mappings[0].template.blog tags = template_tags(blog_id=blog.id) for i in index_mappings: path_string = replace_mapping_tags(i.path_string) path_string = eval(path_string, tags.__dict__) if path_string == '' or path_string is None: continue # why are we doing this twice? # path_string = replace_mapping_tags(path_string) # raise Exception(path_string) master_path_string = path_string add_page_fileinfo(None, i, master_path_string, blog.url + "/" + master_path_string, blog.path + '/' + master_path_string) try: return n + 1 except Exception: return 0
def theme_apply_to_blog(theme, blog): ''' Applies a given theme to a given blog. Removes and regenerates fileinfos for the pages on the blog. ''' from core import cms cms.purge_fileinfos(blog.fileinfos) mappings_to_remove = TemplateMapping.delete().where( TemplateMapping.template << blog.templates) mappings_to_remove.execute() theme_to_remove = Template.delete().where(Template.blog == blog) theme_to_remove.execute() theme_install_to_blog(theme, blog)
def template_save(request, user, cms_template, blog=None): ''' Core logic for saving changes to a template. ''' # TODO: move the bulk of this into the actual model # the .getunicode stuff should be moved out, # make that part of the ui # we should just submit cms_template as self, # make whatever mods to it are needed in the ui func, # and perform the validation we did elsewhere, perhaps from core.cms import fileinfo, invalidate_cache from core.utils import is_blank from core.error import TemplateSaveException, PageNotChanged import datetime status = [] _forms = request.forms cms_template.title = _forms.getunicode('template_title') cms_template.body = _forms.getunicode('template_body') if is_blank(cms_template.title): cms_template.title = "New Template (#{})".format(cms_template.id) mode = _forms.getunicode('publishing_mode') if mode in publishing_mode.modes: cms_template.publishing_mode = mode else: raise TemplateSaveException("Invalid publishing mode selected.") cms_template.modified_date = datetime.datetime.utcnow() try: cms_template.save(user) except PageNotChanged as e: status.append("(Template unchanged.)") except Exception as e: raise e new_mappings = [] for n in _forms: if n.startswith('template_mapping_'): mapping_id = int(n[len('template_mapping_'):]) try: template_mapping = TemplateMapping.get( TemplateMapping.id == mapping_id) except TemplateMapping.DoesNotExist: raise TemplateSaveException( 'Template mapping with ID #{} does not exist.'.format( mapping_id)) else: if is_blank(_forms.getunicode(n)): raise TemplateSaveException( 'Template mapping #{} ({}) cannot be blank. Use None to specify no mapping.' .format(mapping_id, template_mapping.path_string)) else: if _forms.getunicode(n) != template_mapping.path_string: template_mapping.path_string = _forms.getunicode(n) new_mappings.append(template_mapping) for n in new_mappings: n.save() status.append("Mapping #{} ({}) rebuilt.".format(n.id, n.path_string)) if new_mappings: fileinfo.build_mapping_xrefs(new_mappings) build_action = "all" else: build_action = "fast" invalidate_cache() # TODO: eventually everything after this will be removed b/c of AJAX save # tags = template_tags(template_id=cms_template.id, user=user) save_action = _forms.getunicode('save') from core.libs.bottle import response from settings import BASE_URL from core.models import Queue x_open = False if int(save_action) in (2, 3): if cms_template.template_type == template_type.page: x_open = True response.add_header( 'X-Open', '{}/template/{}/queue/{}'.format(BASE_URL, cms_template.id, build_action)) if cms_template.template_type == template_type.archive: x_open = True response.add_header( 'X-Open', '{}/template/{}/queue/{}'.format(BASE_URL, cms_template.id, build_action)) if cms_template.template_type in (template_type.include, template_type.index): # I don't think this is needed anymore, we can remove it # TODO: test it # if new_mappings: # cms.build_archives_fileinfos_by_mappings(cms_template) for f in cms_template.fileinfos_published: Queue.push(job_type=f.template_mapping.template.template_type, blog=cms_template.blog, site=cms_template.blog.site, data_integer=f.id) status.append( "{} files regenerated from template and sent to publishing queue.". format(cms_template.fileinfos_published.count())) if blog is not None: blog.theme_modified = True blog.save() from core.log import logger logger.info("Template {} edited by user {}.".format( cms_template.for_log, user.for_log)) response.body = ' '.join(status) if x_open: return response else: return response.body
def build_archives_fileinfos(pages): ''' Takes a page (maybe a collection of same) and produces fileinfos for the date-based archive entries for each :param pages: List of pages to produce fileinfos for date-based archive entries for. ''' counter = 0 mapping_list = {} try: for page in pages: tags = template_tags(page=page) if page.archive_mappings.count() == 0: raise TemplateMapping.DoesNotExist('No template mappings found for the archives for this page.') s = [] for m in page.archive_mappings: q = replace_mapping_tags(m.path_string) s.append(q) paths_list = eval_paths(m.path_string, tags.__dict__) if type(paths_list) in (list,): paths = [] for n in paths_list: if n is None: continue p = page.proxy(n[0]) # FIXME: eliminate the need for page proxies passed manually # at this stage of the process we should generate those # page context in one column, whatever it is, and path strings in another paths.append((p, n[1])) else: paths = ( (page, paths_list) ,) for page, path in paths: path_string = generate_date_mapping(page.publication_date_tz, tags, path, do_eval=False) if path_string == '' or path_string is None: continue if path_string in mapping_list: continue mapping_list[path_string] = ( (None, m, path_string, page.blog.url + "/" + path_string, page.blog.path + '/' + path_string,) , (page), ) # raise Exception(s) for counter, n in enumerate(mapping_list): # TODO: we should bail if there is already a fileinfo for this page? new_fileinfo = add_page_fileinfo(*mapping_list[n][0]) FileInfoContext.delete().where(FileInfoContext.fileinfo == new_fileinfo).execute() archive_context = [] m = mapping_list[n][0][1] for r in m.archive_xref: archive_context.append( archive_functions[r]["format"]( archive_functions[r]["mapping"](mapping_list[n][1]) ) ) for t, r in zip(archive_context, m.archive_xref): new_fileinfo_context = FileInfoContext.get_or_create( fileinfo=new_fileinfo, object=r, ref=t ).save() new_fileinfo.mapping_sort = '/'.join(archive_context) new_fileinfo.save() return counter + 1 except Exception: return 0
def build_archives_fileinfos_by_mappings(template, pages=None, early_exit=False): # build list of mappings if not supplied # if the list has no dirty mappings, exit # also check to make sure we're not using a do-not-publish template # TODO: Maybe the smart thing to do is to check the # underlying archive type for the template first, # THEN create the mappings, so we don't have to do the awkward # stuff that we do with tag archives # counter = 0 mapping_list = {} if pages is None: pages = template.blog.pages.published for page in pages: tags = template_tags(page=page) if page.archive_mappings.count() == 0: raise TemplateMapping.DoesNotExist('No template mappings found for the archives for this page.') for mapping in template.mappings: paths_list = eval_paths(mapping.path_string, tags.__dict__) if type(paths_list) in (list,): paths = [] for n in paths_list: if n is None: continue p = page.proxy(n[0]) paths.append((p, n[1])) else: paths = ( (page, paths_list) ,) for page, path in paths: path_string = generate_date_mapping(page.publication_date_tz, tags, path, do_eval=False) if path_string == '' or path_string is None: continue if path_string in mapping_list: continue mapping_list[path_string] = ( (None, mapping, path_string, page.blog.url + "/" + path_string, page.blog.path + '/' + path_string,) , (page), ) if early_exit and len(mapping_list) > 0: # return mapping_list break fileinfo_list = [] for n in mapping_list: # TODO: we should bail if there is already a fileinfo for this page? new_fileinfo = add_page_fileinfo(*mapping_list[n][0]) FileInfoContext.delete().where(FileInfoContext.fileinfo == new_fileinfo).execute() archive_context = [] m = mapping_list[n][0][1] for r in m.archive_xref: archive_context.append( archive_functions[r]["format"]( archive_functions[r]["mapping"](mapping_list[n][1]) ) ) for t, r in zip(archive_context, m.archive_xref): new_fileinfo_context = FileInfoContext.get_or_create( fileinfo=new_fileinfo, object=r, ref=t ) new_fileinfo.mapping_sort = '/'.join(archive_context) new_fileinfo.save() fileinfo_list.append(new_fileinfo) return fileinfo_list
def save(request, user, cms_template): status = '' _forms = request.forms cms_template.title = _forms.getunicode('template_title') cms_template.body = _forms.getunicode('template_body') if is_blank(cms_template.title): cms_template.title = "New Template (#{})".format(cms_template.id) mode = _forms.getunicode('publishing_mode') if mode in publishing_mode.modes: cms_template.publishing_mode = mode else: raise TemplateSaveException("Invalid publishing mode selected.") cms_template.save() mappings = [] for n in _forms: if n.startswith('template_mapping_'): mapping_id = int(n[len('template_mapping_'):]) try: template_mapping = TemplateMapping.get( TemplateMapping.id == mapping_id) except TemplateMapping.DoesNotExist: raise TemplateSaveException( 'Template mapping with ID #{} does not exist.'.format( mapping_id)) else: if is_blank(_forms.getunicode(n)): raise TemplateSaveException( 'Template mapping #{} ({}) cannot be blank.'.format( mapping_id, template_mapping.path_string)) else: if _forms.getunicode(n) != template_mapping.path_string: template_mapping.path_string = _forms.getunicode(n) # need to check for mapping validation # if invalid, return some kind of warning # not an exception per se? # template_mapping.save() mappings.append(template_mapping) for n in mappings: n.save() status += " Mapping #{} ({}) rebuilt.".format(n.id, n.path_string) build_mapping_xrefs(mappings) # TODO: eventually everything after this will be removed b/c of AJAX save tags = template_tags(template_id=cms_template.id, user=user) if int(_forms.getunicode('save')) == 2: from core import cms for f in cms_template.fileinfos_published: cms.push_to_queue( job_type=f.template_mapping.template.template_type, blog=cms_template.blog, site=cms_template.blog.site, data_integer=f.id) status += " {} files regenerated from template.".format( cms_template.fileinfos_published.count()) logger.info("Template {} edited by user {}.".format( cms_template.for_log, user.for_log)) return status
def theme_install_to_blog(installed_theme, blog): json_obj = json.loads(installed_theme.json) templates = json_obj["data"] kvs = json_obj["kv"] for t in templates: template = templates[t]["template"] table_obj = Template() for name in table_obj._meta.fields: if name not in ("id"): setattr(table_obj, name, template[name]) table_obj.theme = installed_theme table_obj.blog = blog table_obj.save() mappings = templates[t]["mapping"] for mapping in mappings: mapping_obj = TemplateMapping() for name in mapping_obj._meta.fields: if name not in ("id"): setattr(mapping_obj, name, mappings[mapping][name]) mapping_obj.template = table_obj mapping_obj.save() kv_index = {} kx = System() for kv in kvs: kv_current = kvs[kv] new_kv = kx.add_kv(**kv_current) kv_index[kv_current['id']] = new_kv.id for kv in kv_index: kv_current = kv new_kv_value = kv_index[kv] kv_to_change = KeyValue.get(KeyValue.id == new_kv_value) parent = kv_to_change.__dict__['_data']['parent'] if parent is None: continue kv_to_change.parent = kv_index[parent] kv_to_change.save() from core import cms # TODO: use purge_blog instead # for n in blog.pages(): cms.build_pages_fileinfos(blog.pages()) # for n in blog.index_templates: cms.build_indexes_fileinfos(blog.index_templates)