sharkey-web 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +24 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.md +19 -0
  5. data/README.md +188 -0
  6. data/Rakefile +8 -0
  7. data/bin/sharkey-web +9 -0
  8. data/config.ru +3 -0
  9. data/lib/sharkey.rb +12 -0
  10. data/lib/sharkey/app.rb +526 -0
  11. data/lib/sharkey/importerexporter.rb +79 -0
  12. data/lib/sharkey/models.rb +295 -0
  13. data/lib/sharkey/public/css/loading.gif +0 -0
  14. data/lib/sharkey/public/css/magicsuggest.css +232 -0
  15. data/lib/sharkey/public/css/nprogress.css +74 -0
  16. data/lib/sharkey/public/css/styles.css +263 -0
  17. data/lib/sharkey/public/css/ui.fancytree.css +545 -0
  18. data/lib/sharkey/public/data/sentences.txt +5 -0
  19. data/lib/sharkey/public/fonts/Quadrata.eot +0 -0
  20. data/lib/sharkey/public/fonts/Quadrata.svg +613 -0
  21. data/lib/sharkey/public/fonts/Quadrata.ttf +0 -0
  22. data/lib/sharkey/public/fonts/Quadrata.woff +0 -0
  23. data/lib/sharkey/public/fonts/Quadrata.zip +0 -0
  24. data/lib/sharkey/public/images/loader.gif +0 -0
  25. data/lib/sharkey/public/images/sharkey-logo.png +0 -0
  26. data/lib/sharkey/public/images/sharkey.png +0 -0
  27. data/lib/sharkey/public/js/ajaxmanager.js +67 -0
  28. data/lib/sharkey/public/js/keybindings.js +92 -0
  29. data/lib/sharkey/public/js/lib/bootstrap.min.js +6 -0
  30. data/lib/sharkey/public/js/lib/jquery-1.9.1.min.js +5 -0
  31. data/lib/sharkey/public/js/lib/jquery-ui.js +16150 -0
  32. data/lib/sharkey/public/js/lib/jquery.bootstrap-autohidingnavbar.js +213 -0
  33. data/lib/sharkey/public/js/lib/jquery.fancytree-all.js +6424 -0
  34. data/lib/sharkey/public/js/lib/jquery.tagcloud.js +92 -0
  35. data/lib/sharkey/public/js/lib/magicsuggest.js +1468 -0
  36. data/lib/sharkey/public/js/lib/mousetrap.min.js +9 -0
  37. data/lib/sharkey/public/js/lib/nprogress.js +476 -0
  38. data/lib/sharkey/public/js/page-add-link-autofill.js +102 -0
  39. data/lib/sharkey/public/js/page-add-link.js +156 -0
  40. data/lib/sharkey/public/js/page-categories.js +348 -0
  41. data/lib/sharkey/public/js/page-edit-link.js +103 -0
  42. data/lib/sharkey/public/js/page-links.js +54 -0
  43. data/lib/sharkey/public/js/page-settings.js +93 -0
  44. data/lib/sharkey/public/js/page-tagcloud.js +35 -0
  45. data/lib/sharkey/public/js/page-tags.js +287 -0
  46. data/lib/sharkey/public/js/scripts.js +147 -0
  47. data/lib/sharkey/public/themes/amelia/style.css +7 -0
  48. data/lib/sharkey/public/themes/bootstrap/style.css +5785 -0
  49. data/lib/sharkey/public/themes/cerulean/style.css +7 -0
  50. data/lib/sharkey/public/themes/cosmo/style.css +7 -0
  51. data/lib/sharkey/public/themes/cyborg/style.css +7 -0
  52. data/lib/sharkey/public/themes/darkly/style.css +7 -0
  53. data/lib/sharkey/public/themes/facebook-like/README.md +6 -0
  54. data/lib/sharkey/public/themes/facebook-like/style.css +6085 -0
  55. data/lib/sharkey/public/themes/flatly/style.css +7 -0
  56. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.eot +0 -0
  57. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.svg +229 -0
  58. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.ttf +0 -0
  59. data/lib/sharkey/public/themes/fonts/glyphicons-halflings-regular.woff +0 -0
  60. data/lib/sharkey/public/themes/holo-like/README.md +5 -0
  61. data/lib/sharkey/public/themes/holo-like/style.css +5997 -0
  62. data/lib/sharkey/public/themes/journal/style.css +7 -0
  63. data/lib/sharkey/public/themes/lumen/style.css +7 -0
  64. data/lib/sharkey/public/themes/readable/style.css +7 -0
  65. data/lib/sharkey/public/themes/simplex/style.css +7 -0
  66. data/lib/sharkey/public/themes/slate/style.css +7 -0
  67. data/lib/sharkey/public/themes/spacelab/style.css +7 -0
  68. data/lib/sharkey/public/themes/superhero/style.css +7 -0
  69. data/lib/sharkey/public/themes/united/style.css +7 -0
  70. data/lib/sharkey/public/themes/yeti/style.css +7 -0
  71. data/lib/sharkey/setting.rb +74 -0
  72. data/lib/sharkey/version.rb +5 -0
  73. data/lib/sharkey/views/404.slim +4 -0
  74. data/lib/sharkey/views/about.slim +7 -0
  75. data/lib/sharkey/views/add_link.slim +157 -0
  76. data/lib/sharkey/views/categories.slim +101 -0
  77. data/lib/sharkey/views/category.slim +22 -0
  78. data/lib/sharkey/views/centered.slim +49 -0
  79. data/lib/sharkey/views/dashboard.slim +107 -0
  80. data/lib/sharkey/views/dashboard_index.slim +26 -0
  81. data/lib/sharkey/views/edit_link.slim +121 -0
  82. data/lib/sharkey/views/help.slim +74 -0
  83. data/lib/sharkey/views/keybindings.slim +58 -0
  84. data/lib/sharkey/views/link.slim +30 -0
  85. data/lib/sharkey/views/links.slim +22 -0
  86. data/lib/sharkey/views/navbar.slim +68 -0
  87. data/lib/sharkey/views/settings.slim +74 -0
  88. data/lib/sharkey/views/settings_index.slim +220 -0
  89. data/lib/sharkey/views/single_category.slim +37 -0
  90. data/lib/sharkey/views/single_link.slim +95 -0
  91. data/lib/sharkey/views/single_tag.slim +33 -0
  92. data/lib/sharkey/views/tag.slim +22 -0
  93. data/lib/sharkey/views/tagcloud.slim +54 -0
  94. data/lib/sharkey/views/tags.slim +99 -0
  95. data/sharkey-web.gemspec +44 -0
  96. metadata +324 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f53d2ea02c3428a8b79fa0d255f81d79df9e5961
4
+ data.tar.gz: b318484b07be8b692ea824d7e472304feb27c160
5
+ SHA512:
6
+ metadata.gz: b70b6a52387f1bd8f730d277c3eb2239609b6267bf87dc6a2eb958c220317dd351c0128cd2d3893c2f4c3b98125ec955a0bcb71c25a6ee5477dcc92f24052650
7
+ data.tar.gz: 3b16133fe035b6f9a219873c2db09d2056d330e0ccf2d522c86b9eaa6fa14efad4c1fed3ffd85f6431c7ad88fe33f6baecc83b4ae8019e44f6ccab936c70136d
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+ *.db
24
+ settings.yml
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Dependencies on `sharkey.gemspec`
4
+ gemspec
@@ -0,0 +1,19 @@
1
+ Copyright (C) <2014> by <Alexandre Dantas>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,188 @@
1
+ # Sharkey
2
+
3
+ `sharkey` is a cute web-based personal bookmarking service.
4
+
5
+ * **Bookmarking** means it _saves your links_, allows you to _tag them_, arrange
6
+ in _categories_ and, most important of all, _keep track of what you should visit later_.
7
+ * **Personal** means _you control your own data_. Everything is stored on your
8
+ computer and _you can import/export/delete as you please_. It is like the anti-social
9
+ cousin of [Delicious][delicious].
10
+ * **Web-based** means it runs on your browser. _You don't need an internet connection_,
11
+ though! It doesn't mean you access a site to use it - it runs on _your computer_.
12
+ It just happens that I prefer to make sites instead of designing windows and buttons
13
+ and stuff.
14
+ * **Cute** means it has a nice appearance. It comes with _lots of themes_ and you can
15
+ even customize it _with your own themes_ too.
16
+
17
+ Here's some screenshots for you:
18
+
19
+ <!-- Add Screenshots -->
20
+
21
+ As you can see, `sharkey` is a great tool for [tab hoarders][hoard].
22
+
23
+ ### Why would you do that?
24
+
25
+ If you know me (which I bet you don't) the following sentence will seem familiar:
26
+
27
+ <blockquote>
28
+ <em>Nah, I won't close this tab... I might need it later.
29
+ Let's open another one instead</em>
30
+ <footer>
31
+ - <cite>Me, Always</cite>
32
+ </footer>
33
+ </blockquote>
34
+
35
+ Being like this for years leaves a nasty trail - lots of Firefox crashes,
36
+ everything getting slow as fuark, computer-restarting paranoia, etc... <br />
37
+ And I bet within those 400+ tabs there are _some_ repeated.
38
+
39
+ Unfortunately most bookmarking services lacked one or another feature;
40
+ some were beautiful but didn't had many things; others had 'em but didn't
41
+ allow you to control your own data; others were super ugly and some
42
+ were very complicated to install.
43
+
44
+ Well, `sharkey` is an attempt to gather the best things from them.
45
+
46
+ You should probably [check out the live demo][live]. <br />
47
+ Be warned, though, that since it's completely public it might have some
48
+ nasty spam or _even worse things_. <br />
49
+ But `sharkey` allows you to easily destroy all data so it shouldn't be
50
+ that much of an issue.
51
+
52
+ ### How to use
53
+
54
+ Hey champ, you have to install it first.
55
+
56
+ ### How to install
57
+
58
+ You can easily fetch and install `sharkey` via _Ruby Gems_. <br />
59
+ Look at that:
60
+
61
+ ```bash
62
+ $ gem install sharkey-web
63
+ ```
64
+
65
+ That command takes a while because it install all it's dependencies.
66
+ You can also [grab the gem file here][gem] if you want.
67
+
68
+ But, hey, if you run that command _right now_ it will probably fail.
69
+ That's because you need...
70
+
71
+ ### ...a dependency
72
+
73
+ `sharkey` requires `sqlite3` - and not just the library that comes with
74
+ your distro, no; it needs the fancy development package.
75
+
76
+ If you're on Ubuntu/Debian the following command should suffice:
77
+
78
+ ```bash
79
+ $ sudo apt-get install libsqlite3-dev
80
+ ```
81
+
82
+ Other systems may have different names but you get the
83
+ picture - install SQLite!
84
+
85
+ If you're on Windows... Tough luck, I have no idea what you could do. <br />
86
+ If you ever figure it out, please contact me so I can replace this
87
+ shameful paragraph. <br />
88
+ S-sorry, senpai.
89
+
90
+ ### How to use... again
91
+
92
+ Alright, so you've installed the dependencies and now you have `sharkey`!
93
+
94
+ Why don't you run it?
95
+
96
+ ```bash
97
+ $ sharkey-web
98
+ ```
99
+
100
+ This will create the `sharkey`'s web server, launch it on the background
101
+ and call your web browser. It should open a new tab (yet another...!) with
102
+ that cute interface of the screenshots above.
103
+
104
+ To kill the process, simply run:
105
+
106
+ ```bash
107
+ $ sharkey-web --kill
108
+ ```
109
+
110
+ Anyway, go play with it!
111
+
112
+ Still reading? Go there, I can wait!
113
+
114
+ ### And Then...
115
+
116
+ Back, already? ...okay
117
+
118
+ The rest of the document is just some boring technical talk, you should
119
+ probably just stick with the _Help_ session on the `sharkey` web interface.
120
+
121
+ Anyway, good talking to you. See ya!
122
+
123
+ ## Development
124
+
125
+ Great, now that that they're gone, let's go to what _really_ matters!
126
+
127
+ `sharkey` is essentially:
128
+
129
+ * A [Ruby][ruby] program;
130
+ * That is a web application, made with [Sinatra][sinatra];
131
+ * Encapsulated on a [Vegas][vegas] executable;
132
+ * That has a GUI made with HTML5/CSS3/Javascript;
133
+ * With a [Bootstrap][bootstrap] layout ([and Bootswatch themes][bootswatch]);
134
+ * Made dynamic and fluid with [jQuery][jquery];
135
+ * And featuring with tons of plugins (see below);
136
+
137
+ Now, between you and me, this was my first attempt on developing a web
138
+ application. It was a project to learn several web technologies and how to
139
+ integrate them. But don't go around telling anyone this!
140
+
141
+ ## Credits
142
+
143
+ Besides the links above, I have to point out which tools I've used
144
+ behind the scenes.
145
+
146
+ ### Tools
147
+
148
+ * [tux: A console with direct access to Sinatra's internal stuff](https://github.com/cldwalker/tux). I used it all the time to directly access the database and simulate some GET/POST requests.
149
+ * [Mousetrap: a nice keyboard helper](http://craig.is/killing/mice). Thanks to it one can use `sharkey` real fast through keyboard shortcuts.
150
+ * [jquery.tagcloud](https://github.com/addywaddy/jquery.tagcloud.js), a simple plugin to create... tag clouds.
151
+ * [Bootstrap Auto-Hiding Navbar](https://github.com/istvan-ujjmeszaros/bootstrap-autohidingnavbar) - pretty self-explanatory. I like how when you scroll down the navbar hides and when you scroll up it pops up again.
152
+
153
+ ### Links
154
+
155
+ While developing `sharkey` I bumped into several dead-ends.
156
+ These are the people/links who helped me a lot:
157
+
158
+ * [Sinatra Recipes: DataMapper](http://recipes.sinatrarb.com/p/models/data_mapper?#article)
159
+ * [Sam Stern: Making a Simple, Database-Driven website with Sinatra and Heroku](http://samuelstern.wordpress.com/2012/11/28/making-a-simple-database-driven-website-with-sinatra-and-heroku/)
160
+ * [wooptoot: File Upload with Sinatra](http://www.wooptoot.com/file-upload-with-sinatra)
161
+ * [Random Ruby Thoughts: Upload Files in Sinatra](http://alfuken.tumblr.com/post/874428235/upload-and-download-files-in-sinatra)
162
+ * [The Bastards Book of Ruby: Parsing HTML with Nokogiri](http://ruby.bastardsbook.com/chapters/html-parsing/)
163
+ * [Unheap: showcase of lots and lots of Javascript stuff](http://www.unheap.com/)
164
+
165
+ ### Notes
166
+
167
+ * When running this under development (with `rake preview`) the requests
168
+ seems _mother-fricking slow_; that's because of `shotgun` and it's tendency
169
+ to restart the application on each request.
170
+
171
+ ## License
172
+
173
+ The whole code is released under the *MIT-license*.
174
+
175
+ Check file `LICENSE.md` for details on what you can and
176
+ cannot do with it.
177
+
178
+ [delicious]: https://delicious.com/
179
+ [hoard]: http://www.urbandictionary.com/define.php?term=Tab-Hoarder
180
+ [gem]: http://link
181
+ [live]: http://saruman.link/sharkey/
182
+ [sinatra]: http://
183
+ [ruby]:
184
+ [vegas]:
185
+ [bootstrap]:
186
+ [bootswatch]:
187
+ [jquery]:
188
+
@@ -0,0 +1,8 @@
1
+ # See default bundler take tasks with `rake -T`
2
+ require "bundler/gem_tasks"
3
+
4
+ desc 'Previews Sinatra app, automatically updating on file changes'
5
+ task :preview do
6
+ `shotgun -Ilib config.ru`
7
+ end
8
+
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Main executable for Sharkey
4
+
5
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/sharkey')
6
+ require 'vegas'
7
+
8
+ Vegas::Runner.new(Sharkey::App, 'sharkey')
9
+
@@ -0,0 +1,3 @@
1
+ require File.join(File.dirname(__FILE__), 'lib', 'sharkey')
2
+
3
+ run Sharkey::App.new
@@ -0,0 +1,12 @@
1
+ # Starting point for Sharkey
2
+ #
3
+ # This file actually does nothing - simply requires everything
4
+ # we need to work
5
+
6
+ require 'sharkey/version'
7
+
8
+ module Sharkey
9
+
10
+ autoload :App, 'sharkey/app'
11
+ end
12
+
@@ -0,0 +1,526 @@
1
+ # Application file for Sharkey
2
+ #
3
+ # Here we define the actions that Sinatra needs to take (routes)
4
+
5
+ require 'sinatra'
6
+ require 'slim'
7
+ require 'data_mapper'
8
+
9
+ # For making date/time user-friendly
10
+ require 'date'
11
+ require 'chronic_duration'
12
+
13
+ # To import Sharkey::Links and get page's Titles
14
+ require 'nokogiri'
15
+ require 'metainspector'
16
+
17
+ # Create and initialize the databases
18
+ require 'sharkey/models'
19
+
20
+ require 'sharkey/setting'
21
+ require 'sharkey/importerexporter'
22
+
23
+ require 'json'
24
+
25
+ module Sharkey
26
+ class App < Sinatra::Application
27
+
28
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
29
+ # Initializing the server
30
+
31
+ # Global in-app settings
32
+ # (not related to Sinatra per se)
33
+ Sharkey::Setting.initialize
34
+
35
+ # Helper functions that are accessible inside
36
+ # every place
37
+ helpers do
38
+ def delete_link id
39
+ link = Sharkey::Link.get id
40
+ return if not link
41
+
42
+ # Before deleting the link, we must remove
43
+ # all Sharkey::Tag associations
44
+ link.taggings.destroy if link.taggings
45
+ link.categorization.destroy if link.categorization
46
+
47
+ link.reload
48
+ link.destroy
49
+ end
50
+
51
+ def delete_tag id
52
+ tag = Sharkey::Tag.get id
53
+ return if not tag
54
+
55
+ tag.taggings.destroy if tag.taggings
56
+
57
+ tag.reload
58
+ tag.destroy
59
+ end
60
+
61
+ def delete_category id
62
+ category = Sharkey::Category.get id
63
+ return if not category
64
+
65
+ # * `category.parent` is a pointer to
66
+ # another Category
67
+ # * `category.categoryParent` is a pointer
68
+ # to the RELATIONSHIP to another Category
69
+
70
+ # Removing ties to other Categories...
71
+ if category.parent
72
+ category.parent.remove_child category
73
+ end
74
+ if category.categoryParent
75
+ category.categoryParent.destroy
76
+ end
77
+
78
+ if not category.childs.empty?
79
+ category.childs.each { |child| category.remove_child(child) }
80
+ end
81
+
82
+ # ...and to other Links...
83
+ if category.categorizations
84
+ category.categorizations.destroy
85
+ end
86
+
87
+ # ...and finally to itself
88
+ category.reload
89
+ category.destroy
90
+ end
91
+
92
+ def random_sentence
93
+ @sentences ||= File.readlines(File.join(File.dirname(__FILE__), '/public/data/sentences.txt'))
94
+ @sentences.sample
95
+ end
96
+
97
+ # Converts `datetime` object into a relative date String
98
+ # (like "2 days, 8 hours, 15 minutes, 2 seconds")
99
+ def relative_date datetime
100
+ return '' if datetime.nil?
101
+
102
+ # Now we'll calculate a short, human-friendly
103
+ # version of the full date (like '2 days, 5 hours')
104
+ added_secs = datetime.strftime('%s').to_i
105
+ now_secs = DateTime.now.strftime('%s').to_i
106
+
107
+ ChronicDuration.output(now_secs - added_secs)
108
+ end
109
+
110
+ # Converts `datetime` object into a formatted date String
111
+ # suited for HTML5's <time datetime=""> attributes!
112
+ def formatted_date datetime
113
+ return '' if datetime.nil?
114
+
115
+ datetime.strftime '%Y-%m-%d %H:%m'
116
+ end
117
+ end
118
+
119
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
120
+ # Standalone pages
121
+
122
+ get '/' do
123
+ slim(:dashboard_index,
124
+ :layout => :dashboard,
125
+ :locals => { page: "home" })
126
+ end
127
+
128
+ get '/settings' do
129
+ slim(:settings_index,
130
+ :layout => :settings,
131
+ :locals => { page: "settings" })
132
+ end
133
+
134
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
135
+ # Deleting stuff
136
+
137
+ delete '/link/:id' do
138
+ delete_link(params[:id])
139
+
140
+ # If this is an AJAX request, we don't need
141
+ # to redirect anywhere!
142
+ # The JavaScript is responsible for updating
143
+ # the page, not us!
144
+ redirect back unless request.xhr?
145
+ end
146
+
147
+ # Extra parameters:
148
+ #
149
+ # - destroy_links If should also destroy links with this tag
150
+ #
151
+ delete '/tag/:id' do
152
+
153
+ # Welp, here we go!
154
+ # Send a DELETE request for each link
155
+ if (params[:destroy_links])
156
+ links = Sharkey::Link.by_tag(params[:id])
157
+
158
+ links.each { |link| delete_link link.id }
159
+ end
160
+
161
+ delete_tag params[:id]
162
+
163
+ # If this is an AJAX request, we don't need
164
+ # to redirect anywhere!
165
+ # The JavaScript is responsible for updating
166
+ # the page, not us!
167
+ redirect back unless request.xhr?
168
+ end
169
+
170
+ # Extra parameters:
171
+ #
172
+ # - destroy_links If should also destroy links with this category
173
+ #
174
+ delete '/category/:id' do
175
+
176
+ # Welp, here we go!
177
+ # Send a DELETE request for each link
178
+ if (params[:destroy_links] == 'true')
179
+ links = Sharkey::Link.by_category(params[:id])
180
+
181
+ links.each { |link| delete_link link.id }
182
+ end
183
+
184
+ delete_category params[:id]
185
+
186
+ # If this is an AJAX request, we don't need
187
+ # to redirect anywhere!
188
+ # The JavaScript is responsible for updating
189
+ # the page, not us!
190
+ redirect back unless request.xhr?
191
+ end
192
+
193
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
194
+ # Deleting too much stuff (Caution!)
195
+
196
+ delete '/all-links' do
197
+ Sharkey::Tagging.destroy
198
+ Sharkey::Link.destroy
199
+ redirect to '/'
200
+ end
201
+
202
+ delete '/all-tags' do
203
+ Sharkey::Tagging.destroy
204
+ Sharkey::Tag.destroy
205
+ redirect to '/'
206
+ end
207
+
208
+ delete '/all-categories' do
209
+ Sharkey::Categorization.destroy
210
+ Sharkey::CategoryParent.destroy
211
+ Sharkey::CategoryChild.destroy
212
+ Sharkey::Category.destroy
213
+ redirect to '/'
214
+ end
215
+
216
+ delete '/everything' do
217
+ # No need to be cautious
218
+ FileUtils.rm_f Sharkey::DATABASE_FILE
219
+ redirect to '/'
220
+ end
221
+
222
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
223
+ # Individual pages
224
+ get '/link/:id' do
225
+ the_link = Sharkey::Link.get(params[:id])
226
+ redirect to '/' if not the_link
227
+
228
+ slim(:link,
229
+ :layout => :dashboard,
230
+ locals: { page: "link", link: the_link })
231
+ end
232
+
233
+ get '/tag/:id' do
234
+ the_tag = Sharkey::Tag.get(params[:id])
235
+ redirect to '/' if not the_tag
236
+
237
+ slim(:tag,
238
+ :layout => :dashboard,
239
+ locals: { page: "tag", tag: the_tag })
240
+ end
241
+
242
+ get '/category/:id' do
243
+ the_category = Sharkey::Category.get(params[:id])
244
+ redirect to '/' if not the_category
245
+
246
+ slim(:category,
247
+ :layout => :dashboard,
248
+ locals: { page: "category", category: the_category })
249
+ end
250
+
251
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
252
+ # Creating things
253
+
254
+ post '/link' do
255
+ Sharkey::Link.create_link(params[:title],
256
+ params[:url],
257
+ params[:added_at],
258
+ params[:tags],
259
+ params[:category],
260
+ params[:comment])
261
+
262
+ # If AJAX request, don't redirect anywhere!
263
+ redirect back unless request.xhr?
264
+ end
265
+
266
+ post '/links' do
267
+
268
+ params[:url].split.each do |url|
269
+
270
+ Sharkey::Link.create_link(nil,
271
+ url,
272
+ nil,
273
+ params[:tags] || [],
274
+ params[:category],
275
+ "")
276
+ end
277
+ redirect back unless request.xhr?
278
+ end
279
+
280
+ post '/category' do
281
+ new_category = Sharkey::Category.first_or_create(name: params[:name]);
282
+
283
+ parent_category = Sharkey::Category.get(params[:parent])
284
+ # Silently fail if invalid ID was given
285
+ if (parent_category)
286
+ parent_category.add_child(new_category)
287
+ end
288
+
289
+ # If this is an AJAX request, we'll return a JSON
290
+ # string with information on the recently-added Category
291
+ if request.xhr?
292
+ return "{ \"id\": \"#{new_category.id}\", \"name\": \"#{new_category.name}\" }"
293
+ else
294
+ redirect back
295
+ end
296
+ end
297
+
298
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
299
+ # Updating things
300
+ #
301
+ # Unfortunately I couldn't use the PUT method - even when
302
+ # adding a hidden '<input>' and all that crap.
303
+ # It ain't the order of Sinatra routes also... So I fell
304
+ # back to good old POST with different URL.
305
+
306
+ post '/update/link/:id' do
307
+ return 404 if not params[:id]
308
+
309
+ Sharkey::Link.update_link(params[:id],
310
+ params[:title],
311
+ params[:url],
312
+ params[:tags],
313
+ params[:category],
314
+ params[:comment])
315
+
316
+ # If AJAX request, don't redirect anywhere!
317
+ redirect back unless request.xhr?
318
+ end
319
+
320
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
321
+ # Pages that list things
322
+
323
+ get '/links' do
324
+ # Creating an instance variable
325
+ # (visible inside all Views)
326
+ @links = Sharkey::Link.all
327
+
328
+ slim(:links,
329
+ :layout => :dashboard,
330
+ :locals => { page: "links" })
331
+ end
332
+
333
+ get '/tags' do
334
+ # Creating an instance variable
335
+ # (visible inside all Views)
336
+ @tags = Sharkey::Tag.all
337
+
338
+ # MagicSuggest, the jQuery plugin, uses this to give
339
+ # suggestions on Tag input fields.
340
+ #
341
+ # If request is AJAX, return a JSON
342
+ # array with all existing tags
343
+ if request.xhr?
344
+ return @tags.sort.to_json
345
+ end
346
+
347
+ # Now, the user can send other values to
348
+ # specify how they will get sorted
349
+ case params[:sort]
350
+ when 'name'
351
+ @tags = @tags.sort_by { |t| t.name }
352
+
353
+ when 'count'
354
+ @tags = @tags.sort_by { |t| t.taggings.count } .reverse
355
+
356
+ when 'id'
357
+ @tags = @tags.sort_by { |t| t.id }
358
+ end
359
+
360
+ slim(:tags,
361
+ :layout => :dashboard,
362
+ :locals => { page: "tags" })
363
+ end
364
+
365
+ get '/categories' do
366
+
367
+ # Let's start by showing all Categories
368
+ # WITHOUT parents.
369
+ # Then, recursively show their children
370
+ @categories = Sharkey::Category.orphans
371
+
372
+ # Now, the user can send other values to
373
+ # specify how they will get sorted
374
+ case params[:sort]
375
+ when 'name'
376
+ @categories = @categories.sort_by { |t| t.name }
377
+
378
+ when 'count'
379
+ @categories = @categories.sort_by { |t| t.categorizations.count } .reverse
380
+
381
+ when 'id'
382
+ @categories = @categories.sort_by { |t| t.id }
383
+ end
384
+
385
+ slim(:categories,
386
+ :layout => :dashboard,
387
+ :locals => { page: "categories" })
388
+ end
389
+
390
+ get '/favorites' do
391
+ @links = Sharkey::Link.all(favorite: true)
392
+
393
+ slim(:links,
394
+ :layout => :dashboard,
395
+ :locals => { page: "favorites" })
396
+ end
397
+
398
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
399
+ # Misc. pages
400
+
401
+ post '/debug' do
402
+ slim("#{params.inspect}")
403
+ end
404
+
405
+ post '/setting' do
406
+ # HACK
407
+ # BIG EXCEPTION are <select> elements, who fuck up
408
+ # the entire standard
409
+ if (params[:theme])
410
+ Sharkey::Setting['theme'] = params[:theme]
411
+ Sharkey::Setting.save
412
+ redirect back
413
+ end
414
+
415
+ # Error for non-existing setting
416
+ return 500 unless Sharkey::Setting[params[:name]]
417
+ return 500 unless params[:value]
418
+
419
+ Sharkey::Setting[params[:name]] = params[:value]
420
+ Sharkey::Setting.save
421
+
422
+ if request.xhr?
423
+ return 200
424
+ else
425
+ redirect back
426
+ end
427
+ end
428
+
429
+ # Import Sharkey::Links from Bookmark HTML files
430
+ # (eg. Firefox, Delicious, etc)
431
+ #
432
+ # POST requests automatically creates a temporary
433
+ # file for us.
434
+ #
435
+ post '/import' do
436
+
437
+ # If we got no temporary file from POST let's just
438
+ # ignore this request
439
+ unless (params[:file] and params[:file][:tempfile])
440
+ redirect back
441
+ end
442
+
443
+ ImporterExporter::import params['file'][:tempfile]
444
+ redirect to '/'
445
+ end
446
+
447
+ get '/tagcloud' do
448
+ @tags = Sharkey::Tag.all
449
+
450
+ slim(:tagcloud,
451
+ :layout => :dashboard,
452
+ :locals => { page: "tagcloud" })
453
+ end
454
+
455
+ # Surprise me!
456
+ get '/random' do
457
+ the_link = Sharkey::Link.all.sample
458
+
459
+ slim(:link,
460
+ :layout => :dashboard,
461
+ locals: { page: "link", link: the_link })
462
+ end
463
+
464
+ # Toggles the favorite state of the Link with :id
465
+ #
466
+ # Returns the item's favorite state _after_ the change.
467
+ post '/favorite/:id' do
468
+ the_link = Sharkey::Link.get params[:id]
469
+ return 502 if not the_link
470
+
471
+ the_link.toggle_favorite
472
+ return "{ \"isFavorite\": #{the_link.favorite} }"
473
+ end
474
+
475
+ # Increase the visit count of a link
476
+ post '/visit/:id' do
477
+ the_link = Sharkey::Link.get params[:id]
478
+ return 502 if not the_link
479
+
480
+ the_link.visit
481
+ return "{ \"visitCount\": #{the_link.visit_count} }"
482
+ end
483
+
484
+ get '/help' do
485
+ slim(:help,
486
+ :layout => :centered,
487
+ locals: { page: "help" })
488
+ end
489
+
490
+ get '/about' do
491
+ slim(:about,
492
+ :layout => :centered,
493
+ locals: { page: "about" })
494
+ end
495
+
496
+ # AJAX requested metadata from an URL
497
+ # Let's return, beyb
498
+ get '/metadata' do
499
+ return 404 if not params[:url]
500
+
501
+ begin
502
+ page = MetaInspector.new(params[:url], :allow_redirections => :all)
503
+
504
+ hash = {
505
+ 'pageTitle' => "#{page.title}",
506
+ 'pageDescription' => "#{page.description}"
507
+ }
508
+ return hash.to_json
509
+
510
+ rescue
511
+ # This error-handling sucks, perhaps I should
512
+ # return error values according to what actually
513
+ # happened on the server
514
+ return 503
515
+ end
516
+ end
517
+
518
+ # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
519
+ # Error-handling
520
+
521
+ not_found do
522
+ slim(:'404', :locals => { url: request.fullpath })
523
+ end
524
+ end
525
+ end
526
+