I have recently switched to OpenBox, so today, I will use dynamic menus to integrate Firefox bookmarks with an existing OpenBox menu.

2018.04.18 update

Please use the following code repository as these shell scripts were updated over time.

How to create a dynamic menu?

At first, you need to know the location of the OpenBox menu configuration:

~/.config/openbox/menu.xml

Study it for a while to get comfortable with its simple XML syntax, then look at the following example as it shows a code used to define the pipe menu.

<menu execute="sh ~/bin/scriptmenu.sh" id="pipe-961648" label="Script Menu"/>

Do I need to modify the OpenBox menu by hand?

No. You can use obmenu utility to edit the menu using a graphical user interface.

How sample script output should look like?

If you wonder how sample script output should look like, then skim through the following code.

<?xml version="1.0" encoding="UTF-8"?>
<openbox_pipe_menu>
  <menu id="pipe-menu-1" label="Pipe menu - 1">
    <item label="first item inside Pipe menu - 1">
      <action name="Execute">
        <execute>
          first command
        </execute>
      </action>
    </item>
  </menu>
  <item label="second item">
    <action name="Execute">
      <execute>
        second command
      </execute>
    </action>
  </item>
</openbox_pipe_menu>

To generate the Most popular websites menu, store the following shell script as the ~/bin/most_popular_websites.sh file.

Notice the used SQL query, separator, and location of the places.sqlite database file.

#!/bin/sh
# Generate "Most popular websites" OpenBox dynamic menu

# path to the sqlite3 binary
sqlite_path=`which sqlite3`

# sqlite3 parameters (define separator character)
sqlite_params="-separator ^"

# path to the places.sqlite database
bookmarks_database=`ls ~/.mozilla/firefox/*.default/places.sqlite`

# SQL query
sql_query="select p.title, p.url from moz_places as p where p.hidden=0 order by frecency desc limit 10"

# browser path
browser_path=`which iceweasel`


# header
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
echo "<openbox_pipe_menu>";

# execute and parse sql query
$sqlite_path $sqlite_params "$bookmarks_database" "$sql_query" | while IFS=^ read title url; do
  # special case for empty title
  if [ -z "$title" ]; then
    title=$url
  fi

  # escape special characters
  title=$(echo $title | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")
  url=$(echo $url | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")

  # add missing apostrophes
  title="\"$title\""

  #item
  echo "<item label=$title><action name=\"Execute\"><execute>$browser_path $url</execute></action></item>"
done

# footer
echo "</openbox_pipe_menu>"

OpenBox XML menu configuration:

<menu execute="sh ~/bin/most_popular_websites.sh" id="pipe-ff-most-popular" label="Most popular websites"/>

How to get recently visited websites?

This shell script is almost the same as the above one. Only the SQL query changed slightly to display recently visited websites. Store it as the ~/bin/recently_visited_websites.sh shell script.

#!/bin/sh
# Generate "Recently visited websites" OpenBox dynamic menu

# path to the sqlite3 binary
sqlite_path=`which sqlite3`

# sqlite3 parameters (define separator character)
sqlite_params="-separator ^"

# path to the places.sqlite database
bookmarks_database=`ls ~/.mozilla/firefox/*.default/places.sqlite`

# SQL query
sql_query="select p.title, p.url from moz_places as p where p.hidden=0 order by last_visit_date desc limit 10"

# browser path
browser_path=`which iceweasel`


# header
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
echo "<openbox_pipe_menu>";

# execute and parse sql query
$sqlite_path $sqlite_params "$bookmarks_database" "$sql_query" | while IFS=^ read title url; do
  # special case for empty title
  if [ -z "$title" ]; then
    title=$url
  fi

  # escape special characters
  title=$(echo $title | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")
  url=$(echo $url | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")

  # add missing apostrophes
  title="\"$title\""

  #item
  echo "<item label=$title><action name=\"Execute\"><execute>$browser_path $url</execute></action></item>"
done

# footer
echo "</openbox_pipe_menu>"

OpenBox XML menu configuration:

<menu execute="sh ~/bin/recently_visited_websites.sh" id="pipe-ff-recently-visited" label="Recently visited websites"/>

How to get a bookmarks menu?

This shell script is more complicated as it uses a recursive function to create a bookmarks menu. The idea is the same, but there is a lot more data to process.

Store it as ~/bin/personal_bookmarks.sh file.

#!/bin/sh
# Generate "Bookmarks" OpenBox dynamic menu

# path to the sqlite3 binary
sqlite_path=`which sqlite3`

# sqlite3 parameters (define separator character)
sqlite_params="-separator ^"

# path to the places.sqlite database
bookmarks_database=`ls ~/.mozilla/firefox/*.default/places.sqlite`

# SQL query
sql_query="select p.title, p.url from moz_places as p where p.hidden=0 order by last_visit_date desc limit 10"

# browser path
browser_path=`which iceweasel`

# root element
root_element="(select folder_id from moz_bookmarks_roots where root_name='menu')"

# process bookmarks
process_bookmarks(){
  # SQL query - bookmarks
  sql_bookmarks_query="select b.title, p.url from moz_bookmarks as b left outer join moz_places as p on b.fk=p.id where b.type = 1 and p.hidden=0 and b.title not null and parent=$1"
  $sqlite_path $sqlite_params "$bookmarks_database" "$sql_bookmarks_query" | while IFS=^ read title url; do
    # special case for empty title
    if [ -z "$title" ]; then
      title=$url
    fi

    # escape special characters
    title=$(echo $title | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")
    url=$(echo $url | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")

    # add missing apostrophes
    title="\"$title\""

    echo "<item label=$title><action name=\"Execute\"><execute>$browser_path $url</execute></action></item>"
  done
}

# process the folders function
process_folders(){
  # execute only when there is an exactly one parameter
  if [ "$#" = 1 ]; then
    # SQL query - folders
    sql_folder_query="select id, title from moz_bookmarks where parent=$1 and type=2 and (select count(*) from moz_bookmarks as b2 where b2.parent=moz_bookmarks.id)>0"

    # process folders
    $sqlite_path $sqlite_params "$bookmarks_database" "$sql_folder_query" | while IFS=^ read id title; do
      # special case for empty title
      if [ -z "$title" ]; then
        title="(no title)"
      fi

      # escape special characters
      title=$(echo $title | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")

      # add missing apostrophes
      title="\"$title\""

      # create new menu for folder
      echo "<menu id=\"ff-bookmarks-folder-$id\" label=$title>";

      # process folders inside
      process_folders $id

      # process bookmarks in current folder
      process_bookmarks $id

      echo "</menu>"
    done
  fi
}


# header
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
echo "<openbox_pipe_menu>";

# process folders
process_folders "$root_element"

# process bookmarks for root element
process_bookmarks "$root_element"

# footer
echo "</openbox_pipe_menu>"

OpenBox XML menu configuration:

<menu execute="sh ~/bin/personal_bookmarks.sh" id="pipe-ff-personal-bookmarks" label="Bookmarks"/>

How to get bookmarks from specified folders?

If you want to display bookmarks from specified folders (non-recursively), then you will need to know id values, so use the following SQL query:

$ sqlite3 -column -header ~/.mozilla/firefox/*.default/places.sqlite "select b.id,b.title,b.parent,(select b1.title from moz_bookmarks as b1 where b1.id=b.parent) as parent_name,(select count(*) from moz_bookmarks as b2 where b2.type=1 and b2.parent=b.id) as bookmarks_count from moz_bookmarks as b where b.type = 2 and length(parent_name) > 0 and  length(title) > 0 and b.parent <> 4 order by parent,id"
id          title            parent      parent_name     bookmarks_count
----------  ---------------  ----------  --------------  ---------------
7           Mozilla Firefox  2           Bookmarks Menu  4
84          OS               2           Bookmarks Menu  1
85          Personal         2           Bookmarks Menu  5
86          News             2           Bookmarks Menu  12
96          Forums           85          Personal        4
97          Testing          85          Personal        5

The following shell script (~/bin/specified_bookmarks.sh) will include bookmarks from folders (id field) 2, 84, 85.

#!/bin/sh
# Generate "Bookmarks from specified folders" OpenBox dynamic menu

# path to the sqlite3 binary
sqlite_path=`which sqlite3`

# sqlite3 parameters (define separator character)
sqlite_params="-separator ^"

# path to the places.sqlite database
bookmarks_database=`ls ~/.mozilla/firefox/*.default/places.sqlite`

# folders
folders="2,84,86"

# SQL query
sql_query="select b.title, p.url from moz_bookmarks as b left outer join moz_places as p on b.fk=p.id where p.hidden=0 and b.type = 1 and b.title is not null and p.hidden=0 and b.parent in (${folders})"

# browser path
browser_path=`which iceweasel`


# header
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
echo "<openbox_pipe_menu>";

# execute and parse sql query
$sqlite_path $sqlite_params "$bookmarks_database" "$sql_query" | while IFS=^ read title url; do
  # special case for empty title
  if [ -z "$title" ]; then
    title=$url
  fi

  # escape special characters
  title=$(echo $title | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")
  url=$(echo $url | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")

  # add missing apostrophes
  title="\"$title\""

  #item
  echo "<item label=$title><action name=\"Execute\"><execute>$browser_path $url</execute></action></item>"
done

# footer
echo "</openbox_pipe_menu>"

OpenBox XML menu configuration:

<menu execute="sh ~/bin/specified_bookmarks.sh" id="pipe-ff-specified-bookmarks" label="Personal bookmarks"/>

How to get unsorted bookmarks?

To get unsorted bookmarks, use the following shell script (sh ~/bin/unsorted_bookmarks.sh) as you need to get contents from unfiled folder.

#!/bin/sh
# Generate "Unsorted bookmarks" OpenBox dynamic menu

# path to the sqlite3 binary
sqlite_path=`which sqlite3`

# sqlite3 parameters (define separator character)
sqlite_params="-separator ^"

# path to the places.sqlite database
bookmarks_database=`ls ~/.mozilla/firefox/*.default/places.sqlite`

# root folder - "unfiled"
root_folder="select br.folder_id from moz_bookmarks_roots as br where br.root_name='unfiled'"

# SQL query
sql_query="select b.title, p.url from moz_bookmarks as b left outer join moz_places as p on b.fk=p.id where p.hidden=0 and b.type = 1 and b.title is not null and p.hidden=0 and b.parent=(${root_folder}) order by b.dateAdded limit 25"

# browser path
browser_path=`which iceweasel`


# header
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
echo "<openbox_pipe_menu>";

# execute and parse sql query
$sqlite_path $sqlite_params "$bookmarks_database" "$sql_query" | while IFS=^ read title url; do
  # special case for empty title
  if [ -z "$title" ]; then
    title=$url
  fi

  # escape special characters
  title=$(echo $title | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")
  url=$(echo $url | sed -e "s/&/\&/g" -e "s/\"/\"/g" -e "s/</\</g" -e "s/>/\>/g")

  # add missing apostrophes
  title="\"$title\""

  #item
  echo "<item label=$title><action name=\"Execute\"><execute>$browser_path $url</execute></action></item>"
done

# footer
echo "</openbox_pipe_menu>"

OpenBox XML menu configuration:

<menu execute="sh ~/bin/unsorted_bookmarks.sh" id="pipe-ff-most-popular" label="Most popular websites"/>

Notes

As you can see, you can easily apply modifications to display additional data like parent name, used protocol, date-related fields, or anything else you can compute.

ko-fi