module CamaleonCms module ShortCodeHelper # Internal method def shortcodes_init @_shortcodes = [] @_shortcodes_template = {} @_shortcodes_descr = {} shortcode_add('widget', nil, "Renderize the widget content in this place. Don't forget to create and copy the shortcode of your widgets in appearance -> widgets Sample: [widget widget_key]") shortcode_add('load_libraries', lambda { |attrs, args| return args[:shortcode] unless attrs.present? cama_load_libraries(*attrs['data'].to_s.split(',')) return '' }, "Permit to load libraries on demand, sample: [load_libraries data='datepicker,tinymce']") shortcode_add('asset', lambda { |attrs, args| return args[:shortcode] unless attrs.present? url = ActionController::Base.helpers.asset_url(attrs['file']) if attrs['image'].present? ActionController::Base.helpers.image_tag(attrs['file'], class: attrs['class'], style: attrs['style']) else url end }, "Permit to generate an asset url ( add file='' asset file path, add as_path='true' to generate only the path and not the full url, add class='my_class' to setup image class, add style='height: 100px; width: 200px;...' to setup image style, add image='true' to generate the image tag with this url), sample: or [asset image='true' file='themes/my_theme/assets/img/signature.png' style='height: 50px;']") shortcode_add('data', lambda { |attrs, args| attrs.present? ? cama_shortcode_data(attrs, args) : args[:shortcode] }, "Permit to generate specific data of a post. Attributes: object: (String, defaut post) Model name: post | posttype | category | posttag | site | user |theme | navmenu id: (Integer) Post id key: (String) Post slug field: (String) Custom field key, you can add render_field='true' to render field as html element, also you can add index=2 to indicate the value in position 2 for multitple values attrs: (String) attribute name post: title | created_at | excerpt | url | link | thumb | updated_at | author_name | author_url | content posttype: title | created_at | excerpt | url | link | thumb | updated_at category: title | created_at | excerpt | url | link | thumb | updated_at posttag: title | created_at | excerpt | url | link | thumb | updated_at site: title | created_at | excerpt | url | link | thumb | updated_at user: title | created_at | excerpt | url | link | thumb | updated_at theme: title | created_at | excerpt | url | link | thumb | updated_at navmenu: title | created_at | excerpt | url | link | thumb | updated_at Note: If id and key is not present, then will be rendered for current model Sample: [data id='122' attr='title'] ==> return the title of post with id = 122 [data key='contact' attr='url'] ==> return the url of post with slug = contact [data key='contact' attr='link'] ==> return the link with title as text of the link of post with slug = contact [data object='site' attr='url'] ==> return the url of currrent site [data key='page' object='posttype' attr='url'] ==> return the url of post_type with slug = page [data field=icon index=2] ==> return the second value (multiple values) for this custom field with key=icon of current object [data key='contact' field='sub_title'] ==> return the value of the custom_field with key=sub_title registered for post.slug = contact [data object='site' field='sub_title'] ==> return the value of the custom_field with key=sub_title registered for current_site") end # add shortcode # key: shortcode key # template: template to render, if nil will render "shortcode_templates/" # Also can be a function to execute that instead a render, sample: lambda{|attrs, args| return "my custom content"; } # descr: description for shortcode def shortcode_add(key, template = nil, descr = '') @_shortcodes << key @_shortcodes_template = @_shortcodes_template.merge({ key.to_s => template }) if template.present? @_shortcodes_descr[key] = descr if descr.present? end # add or update shortcode template # key: chortcode key to add or update # template: template to render, if nil will render "shortcode_templates/" def shortcode_change_template(key, template = nil) @_shortcodes_template[key] = template end # delete shortcode # key: chortcode key to delete def shortcode_delete(key) @_shortcodes.delete(key) end # run all shortcodes in the content # content: (string) text to find a short codes # args: custom arguments to pass for short codes render, sample: {owner: my_model, a: true} # if args != Hash, this will re send as args = {owner: args} # args should be include the owner model who is doing the action to optimize DB queries # sample: do_shortcode("hello [greeting 'world']!", @my_post) ==> "hello world!" # sample: do_shortcode("hello [greeting 'world']", {attr1: 'asd', owner: @my_post}) # return rendered string def do_shortcode(content, args = {}) args = { owner: args } unless args.is_a?(Hash) content.scan(/#{cama_reg_shortcode}/) do |item| content = _cama_replace_shortcode(content, item, args) end content end # remove all shortcodes from text # Arguments # text: String that contains shortcodes # return String def cama_strip_shortcodes(text) text.gsub(/#{cama_reg_shortcode}/, '') end # render direct a shortcode # text: text that contain the shortcode # key: shortcode key # template: template to render, if nil this will render default render file # Also can be a function to execute that instead a render, sample: lambda{|attrs, args| return "my custom content"; } # render_shortcode("asda dasdasdas[owen a='1'] [bbb] sdasdas dasd as das[owen a=213]", "owen", lambda{|attrs, args| puts attrs; return "my test"; }) def render_shortcode(text, key, template = nil) text.scan(/#{cama_reg_shortcode(key)}/).each do |item| text = _cama_replace_shortcode(text, item, {}, template) end text end private # helper to replace shortcodes adding support for closed shortcodes, sample: [title]my title[/title] def _cama_replace_shortcode(content, item, args = {}, template = nil) shortcode, code, attrs = item close_code = "[/#{code}]" if content.include?(close_code) shortcode_bk = shortcode tmp_content = content[content.index(shortcode)..] shortcode = tmp_content[0..(tmp_content.index(close_code) + close_code.size - 1)] args[:shortcode_content] = shortcode.sub(shortcode_bk, '').sub(close_code, '') end args[:shortcode] = shortcode args[:code] = code content.sub(shortcode, _eval_shortcode(code, attrs, args, template)) end # create the regexpression for shortcodes # codes: (String) shortcode keys separated by | # sample: load_libraries|asset # if empty, codes will be replaced with all registered shortcodes # Return: (String) reg expression string def cama_reg_shortcode(codes = nil) # "(\\[(#{codes || (@_shortcodes || []).join("|")})(\s|\\]){0}(.*?)\\])" # doesn't support for similar names, like: [media] and [media_gallery] "(\\[(#{codes || (@_shortcodes || []).join('|')})((\s)((?!\\]).)*|)\\])" end # determine the content to replace instead the shortcode # return string def _eval_shortcode(code, attrs, args = {}, template = nil) template ||= (@_shortcodes_template[code].present? ? @_shortcodes_template[code] : "camaleon_cms/shortcode_templates/#{code}") if @_shortcodes_template[code].instance_of?(::Proc) @_shortcodes_template[code].call(_shortcode_parse_attr(attrs), args) else render template: template, locals: { attributes: _shortcode_parse_attr(attrs), args: args }, formats: [:html] end end # parse the attributes of a shortcode def _shortcode_parse_attr(text) res = {} return res unless text.present? text.scan(/(\w+)\s*=\s*"([^"]*)"(?:\s|$)|(\w+)\s*=\s*'([^']*)'(?:\s|$)|(\w+)\s*=\s*([^\s'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/).each do |item| item.each_with_index do |c, index| if c.present? res[c] = item[index + 1] break end end end res end # execute shortcode data def cama_shortcode_data(attrs, args) res = args[:shortcode] object = (attrs['object'].presence || 'post').downcase attr = attrs['attr'] || 'title' model = if attrs['id'].present? || attrs['key'].present? cama_shortcode_model_parser(object, attrs) else args[:owner] end return res unless model.present? if attrs['field'].present? # model custom fields field = model.get_field_object(attrs['field']) if attrs['render_field'].present? return render_to_string(template: "custom_fields/#{field.options['field_key']}", layout: false, locals: { object: model, field: field, field_key: attrs['field'], attibutes: attrs }) end res = if attrs['index'] begin model.the_fields(attrs['field'])[attrs['index'].to_i - 1] rescue StandardError '' end else model.the_field(attrs['field']) end return res else # model attributes case attr when 'title' res = model.the_title when 'created_at' res = model.the_created_at when 'updated_at' res = model.the_updated_at when 'excerpt' res = begin model.the_excerpt rescue StandardError '' end when 'url' res = begin model.the_url rescue StandardError '' end when 'link' res = begin model.the_link rescue StandardError '' end when 'thumb' res = case object when 'site' model.the_logo when 'user' model.the_avatar else model.the_thumb_url end else case object when 'post' case attr when 'content' res = model.try(:the_content) when 'author_name' res = model.try(:the_author).try(:the_name) when 'author_url' res = model.try(:the_author).try(:the_name) end end end end res end # return the model object according to the type def cama_shortcode_model_parser(object, attrs) model = nil case object.downcase when 'post' model = current_site.the_post(attrs['id'].to_i) if attrs['id'].present? model = current_site.the_post(attrs['key'].to_s) if attrs['key'].present? when 'posttype' model = current_site.the_post_type(attrs['id'].to_i) if attrs['id'].present? model = current_site.the_post_type(attrs['key'].to_s) if attrs['key'].present? when 'category' model = current_site.the_category(attrs['id'].to_i) if attrs['id'].present? model = current_site.the_category(attrs['key'].to_s) if attrs['key'].present? when 'posttag' model = current_site.the_tag(attrs['id'].to_i) if attrs['id'].present? model = current_site.the_tag(attrs['key'].to_s) if attrs['key'].present? when 'site' model = current_site when 'theme' model = current_theme when 'navmenu' model = current_site.nav_menu_items.find(attrs['id']) if attrs['id'].present? model = current_site.nav_menu_items.find_by_slug(attrs['key']) if attrs['key'].present? when 'user' model = current_site.the_user(attrs['id'].to_i) if attrs['id'].present? model = current_site.the_user(attrs['key'].to_s) if attrs['key'].present? end model end end end