sharkey-web 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
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
+