def reset(): """Reset links """ _links[_threading.get_id()] = {} for lng in _lang.langs(False): put(lng, _router.current_url(lang=lng))
def _on_f_set(self, field_name: str, value, **kwargs): """Hook """ if field_name == 'alias': from . import _api if value is None: value = '' if not isinstance(value, str): raise RuntimeError('str or None expected') value = value.strip() if not self.is_new: # Sanitize CHANGED alias for EXISTING term term = _api.find(self.model).eq('alias', value).first() if not term or term != self: value = _api.sanitize_alias(self.model, value, self.language) else: # Sanitize alias for NEW term value = _api.sanitize_alias(self.model, value, self.language) elif field_name == 'language': # Check if language code is correct if value not in lang.langs(): raise ValueError( "Language '{}' is not supported".format(value)) return super()._on_f_set(field_name, value, **kwargs)
def build_translations(pkg_name: str): """Compile translations """ # Manage with recursive calls if pkg_name in _building_translations: return _building_translations.append(pkg_name) # Build dependencies for dep_pkg_name in package_info.requires_plugins(pkg_name): dep_pkg_name = 'plugins.' + dep_pkg_name if lang.is_package_registered(dep_pkg_name): build_translations(dep_pkg_name) output_file = path.join(assets_dst('assetman'), 'translations.json') # Prepare data structure if path.exists(output_file): data = util.load_json(output_file) else: data = {'langs': {}, 'translations': {}} # Update languages information data['langs'] = lang.langs() # Build translations structure for lang_code in lang.langs(): if lang_code not in data['translations']: data['translations'][lang_code] = {} logger.info('Compiling translations for {} ({})'.format( pkg_name, lang_code)) data['translations'][lang_code][ pkg_name] = lang.get_package_translations(pkg_name, lang_code) # Create output directory output_dir = path.dirname(output_file) if not path.exists(output_dir): makedirs(output_dir, 0o755, True) # Write translations to teh file with open(output_file, 'wt', encoding='utf-8') as f: logger.debug("Writing translations into '{}'".format(output_file)) f.write(json.dumps(data))
def _on_setup_widgets(self): """Hook """ self.add_widget(_widget.select.Checkboxes( uid='setting_days_of_week', weight=10, label=_lang.t('content_digest@days_of_week'), int_keys=True, items=[ (0, _lang.t('pytsite.lang@weekday_monday')), (1, _lang.t('pytsite.lang@weekday_tuesday')), (2, _lang.t('pytsite.lang@weekday_wednesday')), (3, _lang.t('pytsite.lang@weekday_thursday')), (4, _lang.t('pytsite.lang@weekday_friday')), (5, _lang.t('pytsite.lang@weekday_saturday')), (6, _lang.t('pytsite.lang@weekday_sunday')), ], default='0', )) self.add_widget(_widget.select.DateTime( uid='setting_day_time', weight=20, label=_lang.t('content_digest@time'), default='08:00', datepicker=False, h_size='col-sm-2 col-lg-1' )) self.add_widget(_widget.input.Integer( uid='setting_entities_number', weight=30, label=_lang.t('content_digest@entities_number'), min=1, default=10, h_size='col-sm-2 col-lg-1' )) self.add_widget(_content.widget.ModelCheckboxes( uid='setting_models', weight=40, label=_lang.t('content_digest@content_models'), filter=lambda entity: entity.has_field('views_count'), )) w = 50 for lang_code in _lang.langs(): self.add_widget(_widget.input.Text( uid='setting_mail_subject_{}'.format(lang_code), weight=w, label=_lang.t('content_digest@mail_subject', {'lang': _lang.lang_title(lang_code)}), default=_lang.t('content_digest@default_mail_subject', language=lang_code) )) w += 1 super()._on_setup_widgets()
def _generate_feeds(): # For each language we have separate feed for lng in lang.langs(): # Generate RSS feed for each model for model in reg.get('content.rss_models', ()): filename = 'rss-{}'.format(model) _api.generate_rss(model, filename, lng, length=reg.get('content.feed_length', 20))
def _on_after_save(self, first_save: bool = False, **kwargs): """Hook """ from . import _api # Recalculate tags weights if first_save and self.has_field('tags'): for t in self.tags: weight = 0 for model in _api.get_models().keys(): try: weight += _api.find(model, language=self.language).inc('tags', [t]).count() except odm.error.FieldNotDefined: pass try: auth.switch_user_to_system() t.f_set('weight', weight).save(fast=True) finally: auth.restore_user() # Update localization entities references # For each language except current one for lng in lang.langs(False): # Get localization ref for lng localization = self.f_get('localization_' + lng) # If localization is set if isinstance(localization, Content): # If localized entity hasn't reference to this entity, set it if localization.f_get('localization_' + self.language) != self: localization.f_set('localization_' + self.language, self).save() # If localization is not set elif localization is None: # Clear references from localized entities f = _api.find(self.model, language=lng).eq('localization_' + self.language, self) for referenced in f.get(): referenced.f_set('localization_' + self.language, None).save() # Notify content status change if self.has_field('status') and self.has_field('prev_status') and self.status != self.prev_status: self.content_on_status_change() events.fire('*****@*****.**', entity=self) events.fire('content@entity.{}.save'.format(self.model), entity=self)
def _on_setup_widgets(self): """Hook """ # Application names w = 10 for l in lang.langs(): self.add_widget(widget.input.Text( uid='setting_app_name_' + l, weight=w, label=lang.t('settings@application_name', {'lang': lang.lang_title(l)}, l), default=lang.t('app_name'), )) w += 1 self.add_widget(widget.input.Text( uid='setting_home_title_' + l, label=lang.t('settings@home_page_title', {'lang': lang.lang_title(l)}, l), weight=w, )) w += 1 self.add_widget(widget.input.Text( uid='setting_home_description_' + l, label=lang.t('settings@home_page_description', {'lang': lang.lang_title(l)}, l), weight=w, )) w += 1 self.add_widget(widget.input.Tokens( uid='setting_home_keywords_' + l, label=lang.t('settings@home_page_keywords', {'lang': lang.lang_title(l)}, l), weight=w, )) w += 1 # Links self.add_widget(widget.input.StringList( uid='setting_links', weight=200, label=lang.t('settings@links'), add_btn_label=lang.t('settings@add_link'), rules=validation.rule.Url(), )) # It is important to call super method AFTER super()._on_setup_widgets()
def _on_f_set(self, field_name: str, value, **kwargs): """Hook """ if field_name == 'language': if value not in lang.langs(): raise ValueError("Language '{}' is not supported".format(value)) if value == 'en': self.f_set('language_db', 'english') elif value == 'ru': self.f_set('language_db', 'russian') else: self.f_set('language_db', 'none') elif field_name == 'status': if value not in self.content_statuses(): raise ValueError("'{}' is invalid content status for model '{}'".format(value, self.model)) self.f_set('prev_status', self.f_get('status')) return super()._on_f_set(field_name, value, **kwargs)
def odm_ui_m_form_setup_widgets(self, frm: form.Form): """Hook """ # Parent from ._widget import TermSelect frm.add_widget( TermSelect( uid='_parent', model=self.model, exclude=self if not self.is_new else None, exclude_descendants=True, label=self.t('parent'), append_none_item=not self.get_field('_parent').is_required, value=self.parent, )) # Title if self.has_field('title'): frm.add_widget( widget.input.Text( uid='title', label=lang.t('taxonomy@title'), value=self.title, required=self.get_field('title').is_required, )) # Alias if self.has_field('alias'): frm.add_widget( widget.input.Text( uid='alias', label=lang.t('taxonomy@alias'), value=self.f_get('alias'), )) # Weight if self.has_field('weight'): frm.add_widget( widget.input.Integer(uid='weight', label=lang.t('taxonomy@weight'), value=self.weight, h_size='col-sm-3 col-md-2 col-lg-1')) # Image if self.has_field('image'): frm.add_widget( file_ui.widget.ImagesUpload( uid='image', label=lang.t('taxonomy@image'), required=self.get_field('image').is_required, value=self.image, )) # Language if self.has_field('language'): lng = lang.get_current() if self.is_new else self.language frm.add_widget( widget.static.Text( uid='language', label=lang.t('taxonomy@language'), text=lang.lang_title(lng), value=lng, hidden=len(lang.langs()) == 1, ))
def _setup_fields(self, **kwargs): """Hook """ skip = kwargs.get('skip', []) # Publish time self.define_field(odm.field.DateTime('publish_time', default=datetime.now())) # Author self.define_field(auth_storage_odm.field.User('author', is_required=True)) # Localizations self.define_field(odm.field.String('language', default=lang.get_current())) self.define_field(odm.field.String('language_db', is_required=True)) for lng in lang.langs(): self.define_field(odm.field.Ref('localization_' + lng, model=self.model)) # Title if 'title' not in skip: self.define_field(odm.field.String('title', is_required=True)) # Description if 'description' not in skip: self.define_field(odm.field.String('description')) # Body if 'body' not in skip: self.define_field(odm.field.String('body', is_required=True, strip_html=False)) # Images if 'images' not in skip: self.define_field(file_storage_odm.field.Images('images')) # Tags if 'tags' not in skip: self.define_field(tag.field.Tags('tags')) # External links if 'ext_links' not in skip: self.define_field(odm.field.UniqueStringList('ext_links')) # Video links if 'video_links' not in skip: self.define_field(odm.field.UniqueStringList('video_links')) # Views counter if 'views_count' not in skip: self.define_field(odm.field.Integer('views_count')) # Comments counter if 'comments_count' not in skip: self.define_field(odm.field.Integer('comments_count')) # Likes counter if 'likes_count' not in skip: self.define_field(odm.field.Integer('likes_count')) # Bookmarks count if 'bookmarks_count' not in skip: self.define_field(odm.field.Integer('bookmarks_count')) # Status if 'status' not in skip: self.define_field(odm.field.String('prev_status', is_required=True, default=self.content_statuses()[0])) self.define_field(odm.field.String('status', is_required=True, default=self.content_statuses()[0])) # Options if 'options' not in skip: self.define_field(odm.field.Dict('options'))
def load(self): """Load the theme """ from plugins import assetman # Check for requirements try: package_info.check_requirements(self._package_name) except package_info.error.Error as e: raise RuntimeError('Error while loading theme {}: {}'.format(self._package_name, e)) # Create translations directory lang_dir = path.join(self._path, 'res', 'lang') if not path.exists(lang_dir): makedirs(lang_dir, 0o755, True) # Create translation stub files for lng in lang.langs(): lng_f_path = path.join(lang_dir, '{}.yml'.format(lng)) if not path.exists(lng_f_path): with open(lng_f_path, 'wt'): pass # Register translation resources lang.register_package(self._package_name) # Register template resources tpl_path = path.join(self._path, 'res', 'tpl') if not path.exists(tpl_path): makedirs(tpl_path, 0o755, True) tpl.register_package(self._package_name) # Register assetman resources assets_path = path.join(self._path, 'res', 'assets') if not path.exists(assets_path): makedirs(assets_path, 0o755, True) assetman.register_package(self._package_name) # Load required plugins for pn, pv in self._requires['plugins'].items(): plugman.load(pn, VersionRange(pv)) # Load theme's module try: self._module = import_module(self._package_name) if hasattr(self._module, 'theme_load') and callable(self._module.theme_load): self._module.theme_load() # theme_load_{env.type}() hook env_type = reg.get('env.type') hook_names = ['theme_load_{}'.format(env_type)] if env_type == 'wsgi': hook_names.append('theme_load_uwsgi') for hook_name in hook_names: if hasattr(self._module, hook_name): getattr(self._module, hook_name)() logger.debug("Theme '{}' successfully loaded".format(self._package_name)) except Exception as e: raise _error.ThemeLoadError("Error while loading theme package '{}': {}".format(self._package_name, e)) # Compile assets if not reg.get('theme.compiled'): assetman.setup() assetman.build(self._package_name) reg.put('theme.compiled', True) self._is_loaded = True return self
def odm_ui_m_form_setup_widgets(self, frm: form.Form): """Hook """ from . import widget as _content_widget # Title if self.has_field('title'): f = self.get_field('title') # type: odm.field.String frm.add_widget(widget.input.Text( uid='title', label=self.t('title'), required=f.is_required, min_length=f.min_length, max_length=f.max_length, value=self.title, )) # Description if self.has_field('description'): f = self.get_field('description') # type: odm.field.String frm.add_widget(widget.input.Text( uid='description', label=self.t('description'), required=self.get_field('description').is_required, min_length=f.min_length, max_length=f.max_length, value=self.description, )) # Images if self.has_field('images'): frm.add_widget(file_ui.widget.ImagesUpload( uid='images', label=self.t('images'), value=self.f_get('images'), max_file_size=reg.get('content.max_image_size', 5), max_files=reg.get('content.max_images', 200), )) if self.get_field('images').is_required: frm.add_rule('images', validation.rule.NonEmpty()) # Video links if self.has_field('video_links'): frm.add_widget(widget.input.StringList( uid='video_links', label=self.t('video'), add_btn_label=self.t('add_link'), value=self.video_links, )) frm.add_rule('video_links', validation.rule.VideoHostingUrl()) # Body if self.has_field('body'): frm.add_widget(ckeditor.widget.CKEditor( uid='body', label=self.t('body'), value=self.f_get('body', process_tags=False), )) if self.get_field('body').is_required: frm.add_rule('body', validation.rule.NonEmpty()) # Tags if self.has_field('tags'): frm.add_widget(taxonomy.widget.TokensInput( uid='tags', weight=250, model='tag', label=self.t('tags'), value=self.tags, required=self.get_field('tags').is_required, )) # External links if self.has_field('ext_links'): frm.add_widget(widget.input.StringList( uid='ext_links', weight=550, label=self.t('external_links'), add_btn_label=self.t('add_link'), value=self.ext_links, required=self.get_field('ext_links').is_required, )) frm.add_rule('ext_links', validation.rule.Url()) # Status if self.has_field('status'): frm.add_widget(_content_widget.StatusSelect( uid='status', entity=self, )) # Publish time if self.has_field('publish_time') and self.odm_auth_check_entity_permissions(CONTENT_PERM_SET_PUBLISH_TIME): frm.add_widget(widget.select.DateTime( uid='publish_time', label=self.t('publish_time'), value=self.publish_time, h_size='col-xs-12 col-12 col-sm-4 col-md-3', required=self.get_field('publish_time'), )) # Language lng = lang.get_current() if self.is_new else self.language frm.add_widget(widget.static.Text( uid='language', label=self.t('language'), text=lang.lang_title(lng), value=lng, hidden=False if len(lang.langs()) > 1 else True, )) # Localizations if self.has_field('localization_' + lng) and \ self.odm_auth_check_entity_permissions(CONTENT_PERM_SET_LOCALIZATION): for i, l in enumerate(lang.langs(False)): frm.add_widget(_content_widget.EntitySelect( uid='localization_' + l, label=self.t('localization', {'lang': lang.lang_title(l)}), model=self.model, ajax_url_query={'language': l}, value=self.f_get('localization_' + l) )) # Author if self.has_field('author') and auth.get_current_user().is_admin: frm.add_widget(auth_ui.widget.UserSelect( uid='author', label=self.t('author'), value=auth.get_current_user() if self.is_new else self.author, h_size='col-xs-12 col-12 col-sm-4', required=True, ))
permissions.define_permission('app.settings.manage', 'app@manage_app_settings', 'app') # Settings settings.define('app', settings_form.Form, __name__ + '@application', 'fa fa-cube', 'app.settings.manage') # Index by section route router.add_rule('/section/<string:term_alias>', 'index_by_section', 'pytsite.content@index', { 'model': 'article', 'term_field': 'section', }) # Index by tag route router.add_rule('/tag/<string:term_alias>', 'article_index_by_tag', 'pytsite.content@index', { 'model': 'article', 'term_field': 'tags', }) # Prepare language related tpl globals language_nav = {} search_input = {} for l in lang.langs(): language_nav[l] = str(widget.select.LanguageNav('language-nav', dropdown=True, language=l)) search_input[l] = str(content.widget.Search('search-article', model='article', title=lang.t('app@search', language=l), language=l)) # Tpl globals tpl.register_global('language_nav', language_nav) tpl.register_global('search_input', search_input) tpl.register_global('get_content_sections', lambda: list(content.get_sections())) tpl.register_global('get_content_pages', lambda: list(content.find('page').get()))
def parse_json(json_data: _Union[str, dict, list], defaults: dict = None, override: dict = None) -> dict: """Parse package's JSON from string """ from pytsite import lang as _lang # Load data if isinstance(json_data, str): json_data = _json.loads(json_data) # Check data type if not isinstance(json_data, dict): raise TypeError('Dict expected, got {}'.format(type(json_data))) # Check defaults if defaults is None: defaults = {} # Check name pkg_name = json_data.setdefault('name', defaults.get('name')) if not (pkg_name and isinstance(pkg_name, str)): json_data['name'] = 'Untitled' # Check version pkg_ver = json_data.setdefault('version', _semver.Version(defaults.get('version', '0.0.1'))) if not isinstance(pkg_ver, _semver.Version): json_data['version'] = _semver.Version(pkg_ver) # Check URL pkg_url = json_data.setdefault('url', defaults.get('url')) if not (pkg_url and isinstance(pkg_url, str)): json_data['url'] = 'https://plugins.pytsite.xyz' # Check description pkg_desc = json_data.setdefault('description', defaults.get('description')) if not (pkg_desc and isinstance(pkg_desc, dict)): json_data['description'] = pkg_desc = {} # Check description translations for lng in _lang.langs(): t_description = pkg_desc.get(lng) if not (t_description and isinstance(t_description, str)): json_data['description'][lng] = '' # Check author pkg_author = json_data.setdefault('author', defaults.get('author')) if not (pkg_author and isinstance(pkg_author, dict)): json_data['author'] = pkg_author = {} if not pkg_author.get('name'): json_data['author']['name'] = 'PytSite' if not pkg_author.get('email'): json_data['author']['email'] = '*****@*****.**' if not pkg_author.get('url'): json_data['author']['url'] = 'https://plugins.pytsite.xyz' # Check license info pkg_license = json_data.setdefault('license', defaults.get('license')) if not (pkg_license and isinstance(pkg_license, dict)): json_data['license'] = pkg_license = {} if not pkg_license.get('name'): json_data['license']['name'] = 'MIT' if not pkg_license.get('url'): json_data['license']['url'] = 'https://opensource.org/licenses/MIT' # Check requirements req = json_data.setdefault('requires', defaults.get('requires')) if not (req and isinstance(req, dict)): json_data['requires'] = req = {'pytsite': None, 'packages': {}, 'plugins': {}} # Check required pytsite version json_data['requires']['pytsite'] = _semver.VersionRange(req.get('pytsite', '>=0.0.1')) # Check required pip packages versions json_data['requires']['packages'] = _sanitize_req(req.get('packages', {})) # Check required plugins versions json_data['requires']['plugins'] = _sanitize_req(req.get('plugins', {})) # Override data if isinstance(override, dict): json_data = parse_json(_util.dict_merge(json_data, override)) return json_data
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_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
def as_jsonable(self, **kwargs) -> dict: """Get JSONable representation of the entity """ r = super().as_jsonable() # Publish time if self.has_field('publish_time'): r['publish_time'] = { 'w3c': util.w3c_datetime_str(self.publish_time), 'pretty_date': self.publish_date_pretty, 'pretty_date_time': self.publish_date_time_pretty, 'ago': self.publish_time_ago, } # Author if self.has_field('author') and self.author.is_public: r['author'] = self.author.as_jsonable() # Language if self.has_field('language'): r['language'] = self.language # Localizations for lng in lang.langs(): if self.has_field('localization_' + lng): ref = self.f_get('localization_' + lng) if ref: r['localization_' + lng] = ref.as_jsonable(**kwargs) # Title if self.has_field('title'): r['title'] = self.title # Description if self.has_field('description'): r['description'] = self.description # Body if self.has_field('body'): r['body'] = self.body # Images if self.has_field('images'): thumb_w = kwargs.get('images_thumb_width', 500) thumb_h = kwargs.get('images_thumb_height', 500) img_jsonable_args = { 'thumb_width': thumb_w, 'thumb_height': thumb_h, } r['images'] = [img.as_jsonable(**img_jsonable_args) for img in self.images] if self.images: r['thumbnail'] = self.images[0].get_url(width=thumb_w, height=thumb_h) # Tags if self.has_field('tags'): r['tags'] = [t.as_jsonable() for t in self.tags] # External links if self.has_field('ext_links'): r['ext_links'] = self.ext_links # Video links if self.has_field('video_links'): r['video_links'] = self.video_links # Views counter if self.has_field('views_count'): r['views_count'] = self.views_count # Comments counter if self.has_field('comments_count'): r['comments_count'] = self.comments_count # Likes counter if self.has_field('likes_count'): r['likes_count'] = self.likes_count # Bookmarks counter if self.has_field('bookmarks_count'): r['bookmarks_count'] = self.bookmarks_count # Status if self.has_field('status'): r['status'] = self.status # Options if self.has_field('options'): r['options'] = dict(self.options) return r