RefineryCMS: apply bootstrap styles to navigation menu - css

I have upgraded Refinery CMS to the newest version (2.1.0), where there is a new approach in rendering the navigation menu :
(in partial _header.html.erb)
<%= Refinery::Pages::MenuPresenter.new(refinery_menu_pages, self).to_html %>
The older version of the same partial :
<%= render(:partial => "/refinery/menu", :locals => {
:dom_id => 'menu',
:css => 'menu'
}) %>
How could I add bootstrap styles to the navbar using MenuPresenter?

It can be done, but the solution is not pretty because the Menu Presenter in Refinery 2.1 doesn't support all the right CSS options out of the box. But with a bit of perseverance, this is roughly what to do:
Firstly, create a new blank file here: config/initializers/refinery/monkey_patch_menu_presenter.rb
In this patch file, paste in the contents of this updated version of the menu presenter (published October 2013): menu_presenter.rb
Next, based on the instructions in section 5 of the menu presenter guide, in your app/helpers/application_helper.rb file, add a new method called navigation_menu:
def navigation_menu
presenter = Refinery::Pages::MenuPresenter.new(refinery_menu_pages, self)
presenter.css = "navbar-inner"
presenter.menu_tag = :div
presenter.list_tag_css = "nav"
presenter.selected_css = "active"
presenter.first_css = ""
presenter.last_css = ""
presenter.max_depth = 0 # prevents dropdown menus, which don't render correctly
presenter
end
Finally, in your app/views/refinery/_header.html.erb file (use $ bundle exec rake refinery:override view=refinery/_header if it doesn't exist), replace the call for:
<%= Refinery::Pages::MenuPresenter.new(refinery_menu_pages, self).to_html %>
with:
<div class="navbar">
<%= navigation_menu.to_html %>
</div>
Ensure that you have the loaded the Bootstrap CSS/JS files and have wrapped the whole page in a <div class="container"> element. Then restart your application for the patch to take affect and hopefully you'll see a familiar bootstrap navigation bar.
Good luck!
Martyn.

Here a version of above menu_presenter.rb that renders sub-menus as well
(This if for Bootstrap 3, RefineryCMS 2.1.1):
require 'active_support/core_ext/string'
require 'active_support/configurable'
require 'action_view/helpers/tag_helper'
require 'action_view/helpers/url_helper'
module Refinery
module Pages
class MenuPresenter
include ActionView::Helpers::TagHelper
include ActionView::Helpers::UrlHelper
include ActiveSupport::Configurable
config_accessor :roots, :menu_tag, :list_tag, :list_item_tag, :css, :dom_id,
:max_depth, :selected_css, :first_css, :last_css, :list_tag_css,
:link_tag_css
self.dom_id = 'menu'
self.css = "collapse navbar-collapse"
self.menu_tag = :div
self.list_tag = :ul
self.list_item_tag = :li
self.selected_css = 'active'
self.first_css = :first
self.last_css = :last
self.list_tag_css = "nav navbar-nav"
def roots
config.roots.presence || collection.roots
end
attr_accessor :context, :collection
delegate :output_buffer, :output_buffer=, :to => :context
def initialize(collection, context)
#collection = collection
#context = context
end
def to_html
render_menu(roots) if roots.present?
end
private
def render_menu(items)
content_tag(menu_tag, :id => dom_id, :class => css) do
render_menu_items(items)
end
end
def render_menu_items(menu_items)
if menu_items.present?
content_tag(list_tag, :class => list_tag_css) do
menu_items.each_with_index.inject(ActiveSupport::SafeBuffer.new) do |buffer, (item, index)|
buffer << render_menu_item(item, index)
end
end
end
end
def render_menu_items_children(menu_items)
if menu_items.present?
content_tag(list_tag, :class => 'dropdown-menu') do
menu_items.each_with_index.inject(ActiveSupport::SafeBuffer.new) do |buffer, (item, index)|
buffer << render_menu_item(item, index)
end
end
end
end
def render_menu_item_link_dropdown(menu_item)
link_to( menu_item.title, context.refinery.url_for(menu_item.url), class: "dropdown-toggle", data: {toggle:"dropdown", target: "#"})
end
def render_menu_item_link(menu_item)
link_to(menu_item.title, context.refinery.url_for(menu_item.url), :class => link_tag_css)
end
def render_menu_item(menu_item, index)
content_tag(list_item_tag, :class => menu_item_css(menu_item, index)) do
buffer = ActiveSupport::SafeBuffer.new
# Check for sub menu
menu_item_children(menu_item).empty? ? buffer << render_menu_item_link(menu_item) : buffer << render_menu_item_link_dropdown(menu_item)
buffer << render_menu_items_children(menu_item_children(menu_item))
buffer
end
end
# Determines whether any item underneath the supplied item is the current item according to rails.
# Just calls selected_item? for each descendant of the supplied item
# unless it first quickly determines that there are no descendants.
def descendant_item_selected?(item)
item.has_children? && item.descendants.any?(&method(:selected_item?))
end
def selected_item_or_descendant_item_selected?(item)
selected_item?(item) || descendant_item_selected?(item)
end
# Determine whether the supplied item is the currently open item according to Refinery.
def selected_item?(item)
path = context.request.path
path = path.force_encoding('utf-8') if path.respond_to?(:force_encoding)
# Ensure we match the path without the locale, if present.
if %r{^/#{::I18n.locale}/} === path
path = path.split(%r{^/#{::I18n.locale}}).last.presence || "/"
end
# First try to match against a "menu match" value, if available.
return true if item.try(:menu_match).present? && path =~ Regexp.new(item.menu_match)
# Find the first url that is a string.
url = [item.url]
url << ['', item.url[:path]].compact.flatten.join('/') if item.url.respond_to?(:keys)
url = url.last.match(%r{^/#{::I18n.locale.to_s}(/.*)}) ? $1 : url.detect{|u| u.is_a?(String)}
# Now use all possible vectors to try to find a valid match
[path, URI.decode(path)].include?(url) || path == "/#{item.original_id}"
end
def menu_item_css(menu_item, index)
css = []
css << selected_css if selected_item_or_descendant_item_selected?(menu_item)
css << "dropdown" unless menu_item_children(menu_item).empty?
css << first_css if index == 0
css << last_css if index == menu_item.shown_siblings.length
css.reject(&:blank?).presence
end
def menu_item_children(menu_item)
within_max_depth?(menu_item) ? menu_item.children : []
end
def within_max_depth?(menu_item)
!max_depth || menu_item.depth < max_depth
end
end
end
end

Related

Deep Linking In Roku SDK using RSS Feeds

Having trouble getting deep linking to work in my Roku channel.
I've done a lot of reading on the Roku SDK (particularly their Deep Linking Doc) and a variety of forums. However, I'm just not making any headway.
Currently, my channel uses the Simple Grid with Details template, which serves video content via RSS files.
Here's the beginning of my main.brs file:
sub Main(args as Dynamic) as Void
showSGScreen(args)
if (args.mediaType <> invalid) and (args.contentID <> invalid)
if (args.mediaType = "movie" or args.mediaType = "episode" or args.mediaType = "short-form" or args.mediaType = "series" or args.mediaType = "special")
'play content directly
else
'deep linking issue such as a contentID not matching any content in the partner's catalog
'display an appropriate error message for the user
end if
else
'launch channel normally
end if
end sub
Sub RunUserInterface()
screen = CreateObject("roSGScreen")
scene = screen.CreateScene("HomeScene")
port = CreateObject("roMessagePort")
screen.SetMessagePort(port)
screen.Show()
oneRow = GetApiArray1()
twoRow = GetApiArray2()
threeRow = GetApiArray3()
fourRow = GetApiArray4()
list = [
{
TITLE : "Watch Our Services Live"
ContentList : oneRow
}
{
TITLE : "Without Ceasing: A Series On Prayer"
ContentList : twoRow
}
{
TITLE : "The Least Of These: Hope, Help, Heal"
ContentList : threeRow
}
{
TITLE : "The Bible: Learning To Live It"
ContentList : fourRow
}
]
scene.gridContent = ParseXMLContent(list)
while true
msg = wait(0, port)
print "------------------"
print "msg = "; msg
end while
if screen <> invalid then
screen.Close()
screen = invalid
end if
End Sub
Function ParseXMLContent(list As Object)
RowItems = createObject("RoSGNode","ContentNode")
for each rowAA in list
'for index = 0 to 1
row = createObject("RoSGNode","ContentNode")
row.Title = rowAA.Title
for each itemAA in rowAA.ContentList
item = createObject("RoSGNode","ContentNode")
' We don't use item.setFields(itemAA) as doesn't cast
streamFormat to proper value
for each key in itemAA
item[key] = itemAA[key]
end for
row.appendChild(item)
end for
RowItems.appendChild(row)
end for
return RowItems
End Function
Function GetApiArray1()
url = CreateObject("roUrlTransfer")
url.SetUrl("http://media.genepensiero.com/roku/rss/livestream.rss")
rsp = url.GetToString()
responseXML = ParseXML(rsp)
responseXML = responseXML.GetChildElements()
responseArray = responseXML.GetChildElements()
result = []
for each xmlItem in responseArray
if xmlItem.getName() = "item"
itemAA = xmlItem.GetChildElements()
if itemAA <> invalid
item = {}
for each xmlItem in itemAA
item[xmlItem.getName()] = xmlItem.getText()
if xmlItem.getName() = "media:content"
item.stream = {url : xmlItem.url}
item.url = xmlItem.getAttributes().url
item.streamFormat = "hls"
mediaContent = xmlItem.GetChildElements()
for each mediaContentItem in mediaContent
if mediaContentItem.getName() =
"media:thumbnail"
item.HDPosterUrl =
mediaContentItem.getattributes().url
item.hdBackgroundImageUrl =
mediaContentItem.getattributes().url
end if
end for
end if
end for
result.push(item)
end if
end if
end for
return result
End Function
Then an example of my RSS feed is:
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<title>Bright Light Church</title>
<link />
<description>Live and Archived Bible Studies</description>
<language>en-us</language>
<pubDate>Wed, 26 Apr 2017 12:00:00 PT</pubDate>
<category>TV & Film</category>
<image>
<title>The Bible</title>
<url>http://media.genepensiero.com/roku/withoutceasing.jpg</url>
<width>-1</width>
<height>-1</height>
</image>
<item>
<title>Prayer Pressure</title>
<link>http://media.calvaryhanford.com/psalms/prayerpressure.mp4</link>
<description>David brings big requests to God and receives a big
revelation.</description>
<pubDate>Sun, 05 Mar 2017 10:15:00 PT</pubDate>
<guid isPermaLink="false">ch001</guid>
<media:content channels="2" type="special" isDefault="true" id="ch001" url="http://media.calvaryhanford.com/psalms/prayerpressure.mp4">
<media:description>David brings big requests to God and receives a big
revelation.</media:description>
<media:keywords>church, bible study, expository sermon</media:keywords>
<media:thumbnail url="http://media.genepensiero.com/roku/withoutceasing.jpg" />
<media:title>Prayer Pressure</media:title>
</media:content>
</item>
When I use the Deep Link Tester it just opens my channel to the home screen, it doesn't launch the specific video.
Any insight would be appreciated.
You don't seem to be doing anything with the contentID and mediaType, the app will not deeplink to content automatically (or magically). You'll have to match the received contentId to an item in your API, and navigate to or play that item (depending on the mediaType), all from within your app.

Updated version of file is not applied

I have file, which should be updated after updating instance attributes
It looks like this:
controller.rb
def update
if UpdateNewspaperDesign.new(newspaper_design, newspaper_design_params).call
#render response
end
end
and
class UpdateNewspaperDesign
def initialize(newspaper_design, params)
#newspaper_design = newspaper_design
#params = params
end
def call
newspaper_design.assign_attributes(params)
File.open(File.join(Rails.root, 'tmp', "newspaper-design-#{Time.now.to_i}.css"), 'w') do |file|
file.write(css(newspaper_design))
newspaper_design.css = file
end
newspaper_design.save
end
private
attr_reader :newspaper_design, :params
def css(newspaper_design)
NewspaperDesignCssCompiler.new(newspaper_design).call
end
end
And then I include css in layout
= stylesheet_link_tag newspaper.design.css_url, media: 'all'
As I see in console: css url is updated, but the url, which is inserted in layout - is not update.
Replace File with Tempfile
- this is how I fixed it

Conditional classname and text (Rails)

I'm pretty new to Rails and trying some basic stuff like conditional classes.
On the 'show' view I have an element that changes styling depending on the stock availability, but also the text changes accordingly.
People keep saying the controller should be as small as possible, but placing this conditional in the view also feels dirty. Is this really the best way?
Current controller:
def show
#tyre = Tyres::Tyre.find_by_id(params[:id])
if #tyre.in_stock
#availability = I18n.t("products.filter.other.in_stock")
#availability_class = 'i-check-circle color--success'
else
#availability = I18n.t("products.filter.other.not_in_stock")
#availability_class = 'i-cross-circle color--important'
end
end
Edit:
Controller:
def show
#tyre = Tyres::Tyre.find_by_id(params[:id])
if #tyre.in_stock
#availability_append = ".in_stock"
else
#availability_append = ".not_in_stock"
end
#availability = I18n.t("products.filter.other#{#availability_append}")
end
View:
.xs-12.description__status
%i{class: (#tyre.in_stock? ? 'i-check-circle color--success' : 'i-cross-circle color--important')}
= #availability
You can clean your controller tyres_controller.rb (i suppose) method,
def show
#tyre = Tyre.find(params[:id]) # I believe you have a model named 'tyre'
end
Then, there will be a file named tyres_helper.rb in your myproject/app/helpers/. Put the following code there,
def tyre_availability(tyre) # it'll return an array with two values, first one is class name, second one is localized value
if tyre.in_stock
return 'i-check-circle color--success', I18n.t("products.filter.other.in_stock")
else
return 'i-cross-circle color--important', I18n.t("products.filter.other.not_in_stock")
end
end
and, in the view you can use,
.xs-12.description__status
%i{:class => tyre_availability(#tyre)[0]}
= tyre_availability(#tyre)[1]

Watir Webdriver <script> Element exists?

I'm attempting to determine after a page loads whether text within a script may be located using Watir WebDriver. I am using Watir-WebDriver to automate our test effort. I cannot figure out how to locate the element and verify the value. Any help?
<script><!--
...
s.events="event9"
...
//--></script>
So, I guess I'm wondering is it possible to search for text within a HTML script using watir webdriver?
Thank you in advance.
UPDATE: Below is the script.
require "rubygems"
require "watir-webdriver"
require "watir-webdriver-performance"
require "rspec"
require "headless"
include Watir
require 'logger'
#path store file: script, data file, logs
path = File.dirname(__FILE__)
#create log file
name_log = 'TEST_0001_bsro_validation_suite'
file = File.open(path + '/logs/' + name_log + '_logFile.log', File::WRONLY | File::APPEND | File::CREAT)
logger = Logger.new(file)
logger.info("=> TEST: 0004_bsro_validation")
#open internet browser
browser = Watir::Browser.new :ff
#go to rebrand website with login info. this may need to be removed.
test_site = 'http://*****:*****#fcac-rebrand.laughlin.com/'
browser.goto(test_site)
load_secs = browser.performance.summary[:response_time]
logger.info("=> Page Load Time: #{load_secs}")
zipcode_input = browser.text_field(:id => 'universal-selectorZip')
# select year; progressive selection
year_select = browser.select_list(:id => 'universal-year')
browser.select_list(:id => 'universal-year', :disabled => 'disabled').wait_while_present
if year_select.exists?
year_select.select '2010'
else
logger.info("=> ERROR: Year Select Not Available")
end
# select make; progressive selection
make_select = browser.select_list(:id => 'universal-make')
browser.select_list(:id => 'universal-make', :disabled => 'disabled').wait_while_present
if make_select.exists?
make_select.select 'Volkswagen'
else
logger.info("=> ERROR: Make Select Not Available")
end
# select model; progressive selection
model_select = browser.select_list(:id => 'universal-model')
browser.select_list(:id => 'universal-model', :disabled => 'disabled').wait_while_present
if model_select.exists?
model_select.select 'Jetta'
else
logger.info("=> ERROR: Model Select Not Available")
end
# select submodel; progressive selection
submodel_select = browser.select_list(:id => 'universal-submodel')
browser.select_list(:id => 'universal-submodel', :disabled => 'disabled').wait_while_present
if submodel_select.exists?
submodel_select.select '2.0T TDI Sedan'
else
logger.info("=> ERROR: Submodel Select Not Available")
end
# input zip code; progressive selection
if zipcode_input.exists?
zipcode_input.set '53202'
else
logger.info("=> ERROR: ZIP Code Select Not Available")
end
browser.button(:id => 'universal-submit-tires-quote').click
browser.script.html.include? "event49"
browser.close
I should add that the event is event49 not 9 in this case. Thanks!
Zeljko has the right approach. However, it would fail if/when there are multiple scripts on the page and the one you want is not first.
If there are multiple script elements, you will have to iterate over them to see if one of the scripts has the value.
browser.scripts.any?{ |s| s.html.include? "event9" }
#=> true
This should do it:
browser.script.html.include? "event9"
#=> true

Get HTTP response body as string (BubbleWrap for RubyMotion)

Using RubyMotion (for the first time!), I want to use Twitter's search API to retrieve some recent tweets for some users so have put together the class below.
The value of tweets is always an empty array. I suspect that BW::HTTP.get(url) spawns its own thread which is causing the issue.
Really, I just want twitter_search_results to return response.body.to_str but I am not sure how to do this.
How do I use RubyMotion (or BubbleWrap) to put an array of Tweet objects into my UIViewController?
class TweetsController
def initialize
#twitter_accounts = %w(dhh google)
#tweets = []
end
def tweets
twitter_search_results
puts #tweets.count
#tweets
end
def create_tweets(response)
BW::JSON.parse(response)["results"].each do |result|
#tweets << Tweet.new(result)
end
end
def twitter_search_results
query = #twitter_accounts.map{ |account| "from:#{account}" }.join(" OR ")
url = "http://search.twitter.com/search.json?q=#{query}"
BW::HTTP.get(url) do |response|
create_tweets(response.body.to_str)
end
end
end
class TwitterViewController < UIViewController
def viewDidLoad
super
self.view.backgroundColor = UIColor.blueColor
#table = UITableView.alloc.initWithFrame(self.view.bounds)
self.view.addSubview #table
#table.dataSource = self
#tweets_controller = TweetsController.new
end
def initWithNibName(name, bundle: bundle)
super
self.tabBarItem = UITabBarItem.alloc.initWithTitle(
"Twitter",
image: UIImage.imageNamed('twitter.png'),
tag: 1)
self
end
def tableView(tableView, numberOfRowsInSection: section)
#tweets_controller.tweets.length
end
def tableView(tableView, cellForRowAtIndexPath: indexPath)
#reuse_id = "Tweet"
cell = UITableViewCell.alloc.initWithStyle(UITableViewCellStyleDefault, reuseIdentifier:#reuse_id)
cell.textLabel.text = #tweets_controller.tweets[indexPath.row].text
return cell
end
end
class Tweet
attr_reader :created_at, :from_user, :text
def initialize(tweet_result)
#created_at = tweet_result["created_at"]
#from_user = tweet_result["from_user"]
#text = tweet_result["text"]
end
end
Full controller code below. I've also put the project on GitHub
class TweetsController
def initialize
#twitter_accounts = %w(dhh google)
#tweets = []
create_tweets
end
def tweets
#tweets
end
def create_tweets
json_data = twitter_search_results.dataUsingEncoding(NSUTF8StringEncoding)
e = Pointer.new(:object)
dict = NSJSONSerialization.JSONObjectWithData(json_data, options:0, error: e)
dict["results"].each do |result|
p result.class
p result
#tweets << Tweet.new(result)
end
end
def twitter_search_results
query = #twitter_accounts.map{ |account| "from:#{account}" }.join(" OR ")
url_string = "http://search.twitter.com/search.json?q=#{query}"
url_string_escaped = url_string.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)
url = NSURL.URLWithString(url_string_escaped)
request = NSURLRequest.requestWithURL(url)
response = nil
error = nil
data = NSURLConnection.sendSynchronousRequest(request, returningResponse: response, error: error)
raise "BOOM!" unless (data.length > 0 && error.nil?)
json = NSString.alloc.initWithData(data, encoding: NSUTF8StringEncoding)
end
end
the issue here is asynchronicity. you're almost there, I think, but the create_tweets method is not called before puts #tweets. In this case, I would recommend using a notification, because I think they are good ;-)
TweetsReady = 'TweetsReady' # constants are nice
NSNotificationCenter.defaultCenter.postNotificationName(TweetsReady, object:#tweets)
In your controller, register for this notification in `viewWillAppear` and unregister in `viewWillDisappear`
NSNotificationCenter.defaultCenter.addObserver(self, selector: 'tweets_ready:', name: TweetsReady, object:nil) # object:nil means 'register for all events, not just ones associated with 'object'
# ...
NSNotificationCenter.defaultCenter.removeObserver(self, name:TweetsReady, object:nil)
and you tweets_ready method should implement your UI changes.
def tweets_ready(notification)
#table.reloadData
end

Resources