diff --git a/.scrutinizer.yml b/.scrutinizer.yml
index dc1e7400..ffef81b6 100644
--- a/.scrutinizer.yml
+++ b/.scrutinizer.yml
@@ -1,6 +1,7 @@
filter:
excluded_paths:
- '3rdparty/*'
+ - 'js/3rdparty/*'
imports:
- javascript
diff --git a/3rdparty/js/js_tpl.js b/3rdparty/js/js_tpl.js
deleted file mode 100644
index 3afdb8bb..00000000
--- a/3rdparty/js/js_tpl.js
+++ /dev/null
@@ -1,36 +0,0 @@
-
-// Simple JavaScript Templating
-// John Resig - http://ejohn.org/ - MIT Licensed
-(function(){
- var cache = {};
-
- this.tmpl = function tmpl(str, data){
- // Figure out if we're getting a template, or if we need to
- // load the template - and be sure to cache the result.
- var fn = !/\W/.test(str) ?
- cache[str] = cache[str] ||
- tmpl(document.getElementById(str).innerHTML) :
-
- // Generate a reusable function that will serve as a template
- // generator (and which will be cached).
- new Function("obj",
- "var p=[],print=function(){p.push.apply(p,arguments);};" +
-
- // Introduce the data as local variables using with(){}
- "with(obj){p.push('" +
-
- // Convert the template into pure JavaScript
- str
- .replace(/[\r\t\n]/g, " ")
- .split("<&").join("\t")
- .replace(/((^|&>)[^\t]*)'/g, "$1\r")
- .replace(/\t=(.*?)&>/g, "',$1,'")
- .split("\t").join("');")
- .split("&>").join("p.push('")
- .split("\r").join("\\'")
- + "');}return p.join('');");
-
- // Provide some basic currying to the user
- return data ? fn( data ) : fn;
- };
-})();
diff --git a/3rdparty/js/tag-it.js b/3rdparty/js/tag-it.js
deleted file mode 100644
index d69e32db..00000000
--- a/3rdparty/js/tag-it.js
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
-* jQuery UI Tag-it!
-*
-* @version v2.0 (06/2011)
-*
-* Copyright 2011, Levy Carneiro Jr.
-* Released under the MIT license.
-* http://aehlke.github.com/tag-it/LICENSE
-*
-* Homepage:
-* http://aehlke.github.com/tag-it/
-*
-* Authors:
-* Levy Carneiro Jr.
-* Martin Rehfeld
-* Tobias Schmidt
-* Skylar Challand
-* Alex Ehlke
-*
-* Maintainer:
-* Alex Ehlke - Twitter: @aehlke
-*
-* Dependencies:
-* jQuery v1.4+
-* jQuery UI v1.8+
-*/
-(function($) {
-
- $.widget('ui.tagit', {
- options: {
- itemName : 'item',
- fieldName : 'tags',
- availableTags : [],
- tagSource : null,
- removeConfirmation: false,
- caseSensitive : true,
- placeholderText : null,
- // When enabled, quotes are not neccesary
- // for inputting multi-word tags.
- allowSpaces: false,
-
- // The below options are for using a single field instead of several
- // for our form values.
- //
- // When enabled, will use a single hidden field for the form,
- // rather than one per tag. It will delimit tags in the field
- // with singleFieldDelimiter.
- //
- // The easiest way to use singleField is to just instantiate tag-it
- // on an INPUT element, in which case singleField is automatically
- // set to true, and singleFieldNode is set to that element. This
- // way, you don't need to fiddle with these options.
- singleField: false,
-
- singleFieldDelimiter: ',',
-
- // Set this to an input DOM node to use an existing form field.
- // Any text in it will be erased on init. But it will be
- // populated with the text of tags as they are created,
- // delimited by singleFieldDelimiter.
- //
- // If this is not set, we create an input node for it,
- // with the name given in settings.fieldName,
- // ignoring settings.itemName.
- singleFieldNode: null,
-
- // Optionally set a tabindex attribute on the input that gets
- // created for tag-it.
- tabIndex: null,
-
-
- // Event callbacks.
- onTagAdded : null,
- onTagRemoved: null,
- onTagClicked: null
- },
-
-
- _create: function() {
- // for handling static scoping inside callbacks
- var that = this;
-
- // There are 2 kinds of DOM nodes this widget can be instantiated on:
- // 1. UL, OL, or some element containing either of these.
- // 2. INPUT, in which case 'singleField' is overridden to true,
- // a UL is created and the INPUT is hidden.
- if (this.element.is('input')) {
- this.tagList = $('
').insertAfter(this.element);
- this.options.singleField = true;
- this.options.singleFieldNode = this.element;
- this.element.css('display', 'none');
- } else {
- this.tagList = this.element.find('ul, ol').andSelf().last();
- }
-
- this._tagInput = $('').addClass('ui-widget-content');
- if (this.options.tabIndex) {
- this._tagInput.attr('tabindex', this.options.tabIndex);
- }
- if (this.options.placeholderText) {
- this._tagInput.attr('placeholder', this.options.placeholderText);
- }
- this.options.tagSource = this.options.tagSource || function(search, showChoices) {
- var filter = search.term.toLowerCase();
- var choices = $.grep(that.options.availableTags, function(element) {
- // Only match autocomplete options that begin with the search term.
- // (Case insensitive.)
- return (element.toLowerCase().indexOf(filter) === 0);
- });
- showChoices(that._subtractArray(choices, that.assignedTags()));
- };
-
- this.tagList
- .addClass('tagit')
- .addClass('ui-widget ui-widget-content ui-corner-all')
- // Create the input field.
- .append($('').append(this._tagInput))
- .click(function(e) {
- var target = $(e.target);
- if (target.hasClass('tagit-label')) {
- that._trigger('onTagClicked', e, target.closest('.tagit-choice'));
- } else {
- // Sets the focus() to the input field, if the user
- // clicks anywhere inside the UL. This is needed
- // because the input field needs to be of a small size.
- that._tagInput.focus();
- }
- });
-
- // Add existing tags from the list, if any.
- this.tagList.children('li').each(function() {
- if (!$(this).hasClass('tagit-new')) {
- that.createTag($(this).html(), $(this).attr('class'));
- $(this).remove();
- }
- });
-
- // Single field support.
- if (this.options.singleField) {
- if (this.options.singleFieldNode) {
- // Add existing tags from the input field.
- var node = $(this.options.singleFieldNode);
- var tags = node.val().split(this.options.singleFieldDelimiter);
- node.val('');
- $.each(tags, function(index, tag) {
- that.createTag(tag);
- });
- } else {
- // Create our single field input after our list.
- this.options.singleFieldNode = this.tagList.after('');
- }
- }
-
- // Events.
- this._tagInput
- .keydown(function(event) {
- // Backspace is not detected within a keypress, so it must use keydown.
- if (event.which == $.ui.keyCode.BACKSPACE && that._tagInput.val() === '') {
- var tag = that._lastTag();
- if (!that.options.removeConfirmation || tag.hasClass('remove')) {
- // When backspace is pressed, the last tag is deleted.
- that.removeTag(tag);
- } else if (that.options.removeConfirmation) {
- tag.addClass('remove ui-state-highlight');
- }
- } else if (that.options.removeConfirmation) {
- that._lastTag().removeClass('remove ui-state-highlight');
- }
-
- // Comma/Space/Enter are all valid delimiters for new tags,
- // except when there is an open quote or if setting allowSpaces = true.
- // Tab will also create a tag, unless the tag input is empty, in which case it isn't caught.
- if (
- event.which == $.ui.keyCode.COMMA ||
- event.which == $.ui.keyCode.ENTER ||
- (
- event.which == $.ui.keyCode.TAB &&
- that._tagInput.val() !== ''
- ) ||
- (
- event.which == $.ui.keyCode.SPACE &&
- that.options.allowSpaces !== true &&
- (
- $.trim(that._tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
- (
- $.trim(that._tagInput.val()).charAt(0) == '"' &&
- $.trim(that._tagInput.val()).charAt($.trim(that._tagInput.val()).length - 1) == '"' &&
- $.trim(that._tagInput.val()).length - 1 !== 0
- )
- )
- )
- ) {
- event.preventDefault();
- that.createTag(that._cleanedInput());
-
- // The autocomplete doesn't close automatically when TAB is pressed.
- // So let's ensure that it closes.
- that._tagInput.autocomplete('close');
- }
- }).blur(function(e){
- //If autocomplete is enabled and suggestion was clicked, don't add it
- if (that.options.tagSource && that._tagInput.data('autocomplete-open')) {
- that._cleanedInput();
- } else {
- that.createTag(that._cleanedInput());
- }
- });
-
-
- // Autocomplete.
- if (this.options.availableTags || this.options.tagSource) {
- this._tagInput.autocomplete({
- source: this.options.tagSource,
- open: function(){that._tagInput.data('autocomplete-open', true)},
- close: function(){that._tagInput.data('autocomplete-open', false)},
- select: function(event, ui) {
- that.createTag(ui.item.value);
- // Preventing the tag input to be updated with the chosen value.
- return false;
- }
- });
- }
- },
-
- _cleanedInput: function() {
- // Returns the contents of the tag input, cleaned and ready to be passed to createTag
- return $.trim(this._tagInput.val().replace(/^"(.*)"$/, '$1'));
- },
-
- _lastTag: function() {
- return this.tagList.children('.tagit-choice:last');
- },
-
- assignedTags: function() {
- // Returns an array of tag string values
- var that = this;
- var tags = [];
- if (this.options.singleField) {
- tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
- if (tags[0] === '') {
- tags = [];
- }
- } else {
- this.tagList.children('.tagit-choice').each(function() {
- tags.push(that.tagLabel(this));
- });
- }
- return tags;
- },
-
- _updateSingleTagsField: function(tags) {
- // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
- $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter));
- },
-
- _subtractArray: function(a1, a2) {
- var result = [];
- for (var i = 0; i < a1.length; i++) {
- if ($.inArray(a1[i], a2) == -1) {
- result.push(a1[i]);
- }
- }
- return result;
- },
-
- tagLabel: function(tag) {
- // Returns the tag's string label.
- if (this.options.singleField) {
- return $(tag).children('.tagit-label').text();
- } else {
- return $(tag).children('input').val();
- }
- },
-
- _isNew: function(value) {
- var that = this;
- var isNew = true;
- this.tagList.children('.tagit-choice').each(function(i) {
- if (that._formatStr(value) == that._formatStr(that.tagLabel(this))) {
- isNew = false;
- return;
- }
- });
- return isNew;
- },
-
- _formatStr: function(str) {
- if (this.options.caseSensitive) {
- return str;
- }
- return $.trim(str.toLowerCase());
- },
-
- createTag: function(value, additionalClass) {
- that = this;
- // Automatically trims the value of leading and trailing whitespace.
- value = $.trim(value);
-
- if (!this._isNew(value) || value === '') {
- return false;
- }
-
- var label = $(this.options.onTagClicked ? '' : '').text(value);
-
- // Create tag.
- var tag = $('')
- .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
- .addClass(additionalClass)
- .append(label);
-
- // Button for removing the tag.
- var removeTagIcon = $('')
- .addClass('ui-icon ui-icon-close');
- var removeTag = $('\xd7') // \xd7 is an X
- .addClass('close')
- .append(removeTagIcon)
- .click(function(e) {
- // Removes a tag when the little 'x' is clicked.
- that.removeTag(tag);
- });
- tag.append(removeTag);
-
- // Unless options.singleField is set, each tag has a hidden input field inline.
- if (this.options.singleField) {
- var tags = this.assignedTags();
- tags.push(value);
- this._updateSingleTagsField(tags);
- } else {
- var escapedValue = label.html();
- tag.append('');
- }
-
- this._trigger('onTagAdded', null, tag);
-
- // Cleaning the input.
- this._tagInput.val('');
-
- // insert tag
- this._tagInput.parent().before(tag);
- },
-
- removeTag: function(tag, animate) {
- if (typeof animate === 'undefined') { animate = true; }
-
- tag = $(tag);
-
- this._trigger('onTagRemoved', null, tag);
-
- if (this.options.singleField) {
- var tags = this.assignedTags();
- var removedTagLabel = this.tagLabel(tag);
- tags = $.grep(tags, function(el){
- return el != removedTagLabel;
- });
- this._updateSingleTagsField(tags);
- }
- // Animate the removal.
- if (animate) {
- tag.fadeOut('fast').hide('blind', {direction: 'horizontal'}, 'fast', function(){
- tag.remove();
- }).dequeue();
- } else {
- tag.remove();
- }
- this._trigger('onTagFinishRemoved', null, tag);
- },
-
- removeAll: function() {
- // Removes all tags. Takes an optional `animate` argument.
- var that = this;
- this.tagList.children('.tagit-choice').each(function(index, tag) {
- that.removeTag(tag, false);
- });
- }
-
- });
-
-})(jQuery);
\ No newline at end of file
diff --git a/COPYING b/COPYING
new file mode 100644
index 00000000..2def0e88
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,661 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
\ No newline at end of file
diff --git a/README.md b/README.md
index 174c5bd3..86fb2f7c 100644
--- a/README.md
+++ b/README.md
@@ -13,5 +13,27 @@ Just clone this repo into one of your apps directory.
Status :
---------
-App is functionnal and should be stable, but will probably hit a rewrite using appframework soon.
+Rewrite by [Stefan Klemm] aka ganomi (https://github.com/ganomi)
+* This is a refactored / rewritten version of the bookmarks app using the app framework
+* Dependency Injection for user and db is used througout the controllers
+* The Routing features a consistent rest api
+* The Routing provides some legacy routes, so that for exampe the Android Bookmarks App still works.
+* Merged all the changes from https://github.com/owncloud/bookmarks/pull/68 and added visual fixes. App uses the App Framework Styling on the Client side now.
+
+There is a publicly available api that provides access to bookmarks per user. (This is usefull in connection with the Wordpress Plugin https://github.com/mario-nolte/oc2wp-bookmarks)
+
+Public Rest Api (JSON Formatting):
+---------
+Example Url:
+
+../apps/bookmarks/public/rest/v1/bookmark?user=username&password=password&tags[]=firsttag&tags[]=anothertag&select[]=description&conjunction=AND&sortby=description
+
+Parameters:
+
+* user is a mandatory parameter. This will return all bookmarks from the specific user marked as public. (not yet possible!)
+* by providing the users password all bookmarks will be returned
+* tags[] can take multiple arguments and they are used to filter the requested bookmarks by tags
+* conjunction (default = or) sets the tag filter to OR or to AND
+* select[] takes multiple arguments. By default only url and title per bookmark are returned. Further you can select any attribute of the bookmarks table and also the attribute "tags"
+* sortby takes and attribute that results will be sorted by descending.
\ No newline at end of file
diff --git a/addBm.php b/addBm.php
deleted file mode 100644
index 5cf1447f..00000000
--- a/addBm.php
+++ /dev/null
@@ -1,69 +0,0 @@
-.
-*
-*/
-
-
-
-// Check if we are a user
-OCP\User::checkLoggedIn();
-OCP\App::checkAppEnabled('bookmarks');
-
-// Prep screen if we come from the bookmarklet
-$url ='';
-if(isset($_GET['url'])) {
- $url = $_GET['url'];
-}
-if(!isset($_GET['title']) || trim($_GET['title']) == '') {
- $datas = OC_Bookmarks_Bookmarks::getURLMetadata($url);
- $title = isset($datas['title']) ? $datas['title'] : '';
-}
-else{
- $title = $_GET['title'];
-}
-
-
-OCP\Util::addscript('bookmarks/3rdparty', 'tag-it');
-OCP\Util::addscript('bookmarks', 'addBm');
-OCP\Util::addStyle('bookmarks', 'bookmarks');
-OCP\Util::addStyle('bookmarks/3rdparty', 'jquery.tagit');
-
-
-
-$bm = array('title'=> $title,
- 'url'=> $url,
- 'tags'=> array(),
- 'desc'=>'',
- 'is_public'=>0,
-);
-
-//Find All Tags
-$qtags = OC_Bookmarks_Bookmarks::findTags(array(), 0, 400);
-$tags = array();
-foreach($qtags as $tag) {
- $tags[] = $tag['tag'];
-}
-
-$tmpl = new OCP\Template( 'bookmarks', 'addBm', 'base' );
-$tmpl->assign('requesttoken', OC_Util::callRegister());
-$tmpl->assign('bookmark', $bm);
-$tmpl->assign('tags', json_encode($tags));
-$tmpl->printPage();
diff --git a/ajax/delBookmark.php b/ajax/delBookmark.php
deleted file mode 100644
index c8bc4680..00000000
--- a/ajax/delBookmark.php
+++ /dev/null
@@ -1,37 +0,0 @@
-.
-*
-*/
-
-// Check if we are a user
-OCP\JSON::checkLoggedIn();
-OCP\JSON::callCheck();
-
-OCP\JSON::checkAppEnabled('bookmarks');
-OCP\JSON::callCheck();
-
-$id = $_POST['id'];
-if (!OC_Bookmarks_Bookmarks::deleteUrl($id)) {
- OC_JSON::error();
- exit();
-}
-
-OCP\JSON::success();
diff --git a/ajax/delTag.php b/ajax/delTag.php
deleted file mode 100644
index 1afcaba8..00000000
--- a/ajax/delTag.php
+++ /dev/null
@@ -1,36 +0,0 @@
-.
-*
-*/
-
-// Check if we are a user
-OCP\JSON::checkLoggedIn();
-OCP\JSON::callCheck();
-OCP\JSON::checkAppEnabled('bookmarks');
-
-if(isset($_POST['old_name'])) {
- OC_Bookmarks_Bookmarks::deleteTag($_POST['old_name']);
- OCP\JSON::success();
- exit();
-}
-
-OC_JSON::error();
-exit();
diff --git a/ajax/editBookmark.php b/ajax/editBookmark.php
deleted file mode 100644
index a026daaa..00000000
--- a/ajax/editBookmark.php
+++ /dev/null
@@ -1,57 +0,0 @@
-
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
-*
-* You should have received a copy of the GNU Affero General Public
-* License along with this library. If not, see .
-*
-*/
-
-// Check if we are a user
-OCP\JSON::checkLoggedIn();
-OCP\JSON::callCheck();
-
-OCP\JSON::checkAppEnabled('bookmarks');
-
-// Check if it is a valid URL
-if (filter_var($_POST['url'], FILTER_VALIDATE_URL) === FALSE) {
- OC_JSON::error();
- exit();
-}
-
-// If we go the dialog form submit
-if(isset($_POST['url'])) {
- $title = isset($_POST['title']) ? $_POST['title'] : '';
- $tags = isset($_POST['item']['tags']) ? $_POST['item']['tags'] : array();
- $pub = isset($_POST['is_public']) ? true : false;
-
- if(isset($_POST['record_id']) && is_numeric($_POST['record_id']) ) { //EDIT
- $id = OC_Bookmarks_Bookmarks::editBookmark($_POST['record_id'], $_POST['url'], $_POST['title'], $tags, $_POST['description'], $pub);
- }
- else {
- if(isset($_POST['from_own'])) {
- $datas = OC_Bookmarks_Bookmarks::getURLMetadata($_POST['url']);
- if(isset($datas['title'])) $title = $datas['title'];
- }
- $id = OC_Bookmarks_Bookmarks::addBookmark($_POST['url'], $title, $tags, $_POST['description'], $pub);
- }
- $bm = OC_Bookmarks_Bookmarks::findOneBookmark($id);
- OCP\JSON::success(array('item'=>$bm));
- exit();
-}
-OC_JSON::error();
-exit();
diff --git a/ajax/getInfos.php b/ajax/getInfos.php
deleted file mode 100644
index 710e72e7..00000000
--- a/ajax/getInfos.php
+++ /dev/null
@@ -1,37 +0,0 @@
-.
-*
-*/
-
-// Check if we are a user
-OCP\JSON::checkLoggedIn();
-OCP\JSON::checkAppEnabled('bookmarks');
-
-$req_type=isset($_GET['type']) ? $_GET['type'] : '';
-
-if($req_type == 'url_info' && $_GET['url']) {
- $datas = OC_Bookmarks_Bookmarks::getURLMetadata($_GET['url']);
- $title = isset($datas['title']) ? $datas['title'] : '';
- OCP\JSON::success(array('title' => $title));
- exit();
-}
-
-OC_JSON::error();
-exit();
diff --git a/ajax/import.php b/ajax/import.php
deleted file mode 100644
index 670dde46..00000000
--- a/ajax/import.php
+++ /dev/null
@@ -1,30 +0,0 @@
-t('No file provided for import');
-}elseif (isset($_FILES['bm_import'])) {
- $file = $_FILES['bm_import']['tmp_name'];
- if($_FILES['bm_import']['type'] =='text/html') {
- $error = OC_Bookmarks_Bookmarks::importFile($file);
- if( empty($errors) ) {
- OCP\JSON::success();
- //force charset as not set by OC_JSON
- header('Content-Type: application/json; charset=utf-8');
- exit();
- }
- } else {
- $error[]= $l->t('Unsupported file type for import');
- }
-}
-
-OC_JSON::error(array('data'=>$error));
-//force charset as not set by OC_JSON
-header('Content-Type: application/json; charset=utf-8');
-exit();
diff --git a/ajax/recordClick.php b/ajax/recordClick.php
deleted file mode 100644
index 537d9e19..00000000
--- a/ajax/recordClick.php
+++ /dev/null
@@ -1,39 +0,0 @@
-.
-*
-*/
-
-// Check if we are a user
-OCP\JSON::checkLoggedIn();
-OCP\JSON::callCheck();
-OCP\JSON::checkAppEnabled('bookmarks');
-
-$query = OCP\DB::prepare('
- UPDATE `*PREFIX*bookmarks`
- SET `clickcount` = `clickcount` + 1
- WHERE `user_id` = ?
- AND `url` LIKE ?
- ');
-
-$params=array(OCP\USER::getUser(), htmlspecialchars_decode($_POST["url"]));
-$bookmarks = $query->execute($params);
-
-header( "HTTP/1.1 204 No Content" );
diff --git a/ajax/renameTag.php b/ajax/renameTag.php
deleted file mode 100644
index e707bec4..00000000
--- a/ajax/renameTag.php
+++ /dev/null
@@ -1,41 +0,0 @@
-.
-*
-*/
-
-//no apps or filesystem
-$RUNTIME_NOSETUPFS=true;
-
-
-
-// Check if we are a user
-OCP\JSON::checkLoggedIn();
-OCP\JSON::callCheck();
-OCP\JSON::checkAppEnabled('bookmarks');
-
-if(isset($_POST['old_name']) && isset($_POST['new_name']) && $_POST['new_name'] != '') {
- OC_Bookmarks_Bookmarks::renameTag($_POST['old_name'], $_POST['new_name']);
- OCP\JSON::success();
- exit();
-}
-
-OC_JSON::error();
-exit();
diff --git a/ajax/updateList.php b/ajax/updateList.php
deleted file mode 100644
index 109d699f..00000000
--- a/ajax/updateList.php
+++ /dev/null
@@ -1,51 +0,0 @@
-
-*
-* This library is free software; you can redistribute it and/or
-* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
-* License as published by the Free Software Foundation; either
-* version 3 of the License, or any later version.
-*
-* This library is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
-*
-* You should have received a copy of the GNU Affero General Public
-* License along with this library. If not, see .
-*
-*/
-
-// Check if we are a user
-OCP\JSON::checkLoggedIn();
-OCP\JSON::checkAppEnabled('bookmarks');
-
-$req_type= isset($_GET['type']) ? $_GET['type'] : 'bookmark';
-
-if($req_type == 'rel_tags') {
- $tags = OC_Bookmarks_Bookmarks::analyzeTagRequest(isset($_GET['tag']) ? $_GET['tag'] : '');
- $qtags = OC_Bookmarks_Bookmarks::findTags($tags);
- OCP\JSON::success(array('data' => $qtags));
-
-}
-else { // type == bookmark
- $filterTag = OC_Bookmarks_Bookmarks::analyzeTagRequest(isset($_GET['tag']) ? $_GET['tag'] : '');
-
- $offset = isset($_GET['page']) ? intval($_GET['page']) * 10 : 0;
-
- $sort = isset($_GET['sort']) ? ($_GET['sort']) : 'bookmarks_sorting_recent';
- if($sort == 'bookmarks_sorting_clicks') {
- $sqlSortColumn = 'clickcount';
- } else {
- $sqlSortColumn = 'lastmodified';
- }
- $bookmarks = OC_Bookmarks_Bookmarks::findBookmarks($offset, $sqlSortColumn, $filterTag, true);
- OCP\JSON::success(array('data' => $bookmarks));
-
-}
diff --git a/appinfo/app.php b/appinfo/app.php
index a13ad50f..d333a717 100644
--- a/appinfo/app.php
+++ b/appinfo/app.php
@@ -1,22 +1,34 @@
-* Copyright (c) 2011 Arthur Schiwon
-* This file is licensed under the Affero General Public License version 3 or
-* later.
-* See the COPYING-README file.
-*/
+ * ownCloud - bookmarks
+ *
+ * This file is licensed under the Affero General Public License version 3 or
+ * later. See the COPYING file.
+ * @author Marvin Thomas Rabe
+ * @author Arthur Schiwon
+ * @author Stefan Klemm
+ * @copyright (c) 2011, Marvin Thomas Rabe
+ * @copyright (c) 2011, Arthur Schiwon
+ * @copyright (c) 2014, Stefan Klemm
+ */
-OC::$CLASSPATH['OC_Bookmarks_Bookmarks'] = 'bookmarks/lib/bookmarks.php';
-OC::$CLASSPATH['OC_Search_Provider_Bookmarks'] = 'bookmarks/lib/search.php';
+namespace OCA\Bookmarks\AppInfo;
-$l = new OC_l10n('bookmarks');
-OCP\App::addNavigationEntry( array( 'id' => 'bookmarks_index',
- 'order' => 70, 'href' => OCP\Util::linkTo( 'bookmarks', 'index.php' ),
- 'icon' => OCP\Util::imagePath( 'bookmarks', 'bookmarks.svg' ),
- 'name' => $l->t('Bookmarks')
+\OCP\App::addNavigationEntry(array(
+ // the string under which your app will be referenced in owncloud
+ 'id' => 'bookmarks',
+ // sorting weight for the navigation. The higher the number, the higher
+ // will it be listed in the navigation
+ 'order' => 10,
+ // the route that will be shown on startup
+ 'href' => \OCP\Util::linkToRoute('bookmarks.web_view.index'),
+ // the icon that will be shown in the navigation
+ // this file needs to exist in img/
+ 'icon' => \OCP\Util::imagePath('bookmarks', 'bookmarks.svg'),
+ // the title of your application. This will be used in the
+ // navigation or on the settings page of your app
+ 'name' => \OC_L10N::get('bookmarks')->t('Bookmarks')
));
-OCP\Util::addscript('bookmarks', 'bookmarksearch');
-
-OC_Search::registerProvider('OC_Search_Provider_Bookmarks');
+\OC_Search::registerProvider('OCA\Bookmarks\Controller\Lib\Search');
diff --git a/appinfo/application.php b/appinfo/application.php
new file mode 100644
index 00000000..de9ae796
--- /dev/null
+++ b/appinfo/application.php
@@ -0,0 +1,82 @@
+
+ * @author Arthur Schiwon
+ * @author Stefan Klemm
+ * @copyright (c) 2011, Marvin Thomas Rabe
+ * @copyright (c) 2011, Arthur Schiwon
+ * @copyright (c) 2014, Stefan Klemm
+ */
+
+namespace OCA\Bookmarks\AppInfo;
+
+use \OCP\AppFramework\App;
+use \OCA\Bookmarks\Controller\WebViewController;
+use OCA\Bookmarks\Controller\Rest\TagsController;
+use OCA\Bookmarks\Controller\Rest\BookmarkController;
+use OCA\Bookmarks\Controller\Rest\PublicController;
+
+class Application extends App {
+
+ public function __construct(array $urlParams = array()) {
+ parent::__construct('bookmarks', $urlParams);
+
+ $container = $this->getContainer();
+
+ /**
+ * Controllers
+ * @param OC\AppFramework\Utility\SimpleContainer $c The Container instance
+ * that handles the request
+ */
+ $container->registerService('WebViewController', function($c) {
+ return new WebViewController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('ServerContainer')->getURLGenerator(),
+ $c->query('ServerContainer')->getDb()
+ );
+ });
+
+ $container->registerService('BookmarkController', function($c) {
+ return new BookmarkController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('ServerContainer')->getDb()
+ );
+ });
+
+ $container->registerService('TagsController', function($c) {
+ return new TagsController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('ServerContainer')->getDb()
+ );
+ });
+
+ $container->registerService('PublicController', function($c) {
+ return new PublicController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('ServerContainer')->getDb(),
+ $c->query('ServerContainer')->getUserManager()
+ );
+ });
+
+
+ /**
+ * Core
+ */
+ $container->registerService('UserId', function() {
+ return \OCP\User::getUser();
+ });
+ }
+
+}
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 033b6a8f..382364ce 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -4,9 +4,10 @@
BookmarksBookmark manager for ownCloudAGPL
- Arthur Schiwon, Marvin Thomas Rabe
+ Arthur Schiwon, Marvin Thomas Rabe, Stefan Klemm
- 4.93
+ 4.93true166042
-
+ 0.4
+
\ No newline at end of file
diff --git a/appinfo/routes.php b/appinfo/routes.php
new file mode 100644
index 00000000..72bf0040
--- /dev/null
+++ b/appinfo/routes.php
@@ -0,0 +1,46 @@
+
+ * @copyright (c) 2014, Stefan Klemm
+ */
+
+namespace OCA\Bookmarks\AppInfo;
+
+/**
+ * Create your routes in here. The name is the lowercase name of the controller
+ * without the controller part, the stuff after the hash is the method.
+ * e.g. page#index -> PageController->index()
+ *
+ * The controller class has to be registered in the application.php file since
+ * it's instantiated in there
+ */
+$application = new Application();
+
+$application->registerRoutes($this, array('routes' => array(
+ //Web Template Route
+ array('name' => 'web_view#index', 'url' => '/', 'verb' => 'GET'),
+ array('name' => 'web_view#bookmarklet', 'url' => '/bookmarklet', 'verb' => 'GET'),
+ //Session Based and CSRF secured Routes
+ array('name' => 'bookmark#get_bookmarks', 'url' => '/bookmark', 'verb' => 'GET'),
+ array('name' => 'bookmark#new_bookmark', 'url' => '/bookmark', 'verb' => 'POST'),
+ array('name' => 'bookmark#edit_bookmark', 'url' => '/bookmark/{id}', 'verb' => 'PUT'),
+ array('name' => 'bookmark#delete_bookmark', 'url' => '/bookmark/{id}', 'verb' => 'DELETE'),
+ array('name' => 'bookmark#click_bookmark', 'url' => '/bookmark/click', 'verb' => 'POST'),
+ array('name' => 'bookmark#export_bookmark', 'url' => '/bookmark/export', 'verb' => 'GET'),
+ array('name' => 'bookmark#import_bookmark', 'url' => '/bookmark/import', 'verb' => 'POST'),
+ array('name' => 'tags#full_tags', 'url' => '/tag', 'verb' => 'GET'),
+ array('name' => 'tags#rename_tag', 'url' => '/tag', 'verb' => 'POST'),
+ array('name' => 'tags#delete_tag', 'url' => '/tag', 'verb' => 'DELETE'),
+ //Public Rest Api
+ array('name' => 'public#return_as_json', 'url' => '/public/rest/v1/bookmark', 'verb' => 'GET'),
+ //Legacy Routes
+ array('name' => 'bookmark#legacy_get_bookmarks', 'url' => '/ajax/updateList.php', 'verb' => 'POST'),
+ array('name' => 'bookmark#legacy_edit_bookmark', 'url' => '/ajax/editBookmark.php', 'verb' => 'POST'),
+ array('name' => 'bookmark#legacy_delete_bookmark', 'url' => '/ajax/delBookmark.php', 'verb' => 'POST'),
+)));
diff --git a/appinfo/version b/appinfo/version
deleted file mode 100644
index bd73f470..00000000
--- a/appinfo/version
+++ /dev/null
@@ -1 +0,0 @@
-0.4
diff --git a/controller/lib/bookmarks.php b/controller/lib/bookmarks.php
new file mode 100644
index 00000000..0a9fcb46
--- /dev/null
+++ b/controller/lib/bookmarks.php
@@ -0,0 +1,582 @@
+.
+ *
+ */
+/**
+ * This class manages bookmarks
+ */
+
+namespace OCA\Bookmarks\Controller\Lib;
+
+use \OCP\IDb;
+
+class Bookmarks {
+
+ /**
+ * @brief Finds all tags for bookmarks
+ * @param $userId UserId
+ * @param IDb $db Database Interface
+ * @param filterTags array of tag to look for if empty then every tag
+ * @param offset integer offset
+ * @param limit integer of item to return
+ * @return Found Tags
+ */
+ public static function findTags($userId, IDb $db, $filterTags = array(), $offset = 0, $limit = 10) {
+ $params = array_merge($filterTags, $filterTags);
+ array_unshift($params, $userId);
+ $not_in = '';
+ if (!empty($filterTags)) {
+ $exist_clause = " AND exists (select 1 from `*PREFIX*bookmarks_tags`
+ `t2` where `t2`.`bookmark_id` = `t`.`bookmark_id` and `tag` = ?) ";
+
+ $not_in = ' AND `tag` not in (' . implode(',', array_fill(0, count($filterTags), '?')) . ')' .
+ str_repeat($exist_clause, count($filterTags));
+ }
+ $sql = 'SELECT tag, count(*) as nbr from *PREFIX*bookmarks_tags t ' .
+ ' WHERE EXISTS( SELECT 1 from *PREFIX*bookmarks bm where t.bookmark_id = bm.id and user_id = ?) ' .
+ $not_in .
+ ' GROUP BY `tag` ORDER BY `nbr` DESC ';
+
+ $query = $db->prepareQuery($sql, $limit, $offset);
+ $tags = $query->execute($params)->fetchAll();
+ return $tags;
+ }
+
+ /**
+ * @brief Finds Bookmark with certain ID
+ * @param $id BookmarkId
+ * @param $userId UserId
+ * @param IDb $db Database Interface
+ * @return Specific Bookmark
+ */
+ public static function findUniqueBookmark($id, $userId, IDb $db) {
+ $CONFIG_DBTYPE = \OCP\Config::getSystemValue('dbtype', 'sqlite');
+ if ($CONFIG_DBTYPE == 'pgsql') {
+ $group_fct = 'array_agg(`tag`)';
+ } else {
+ $group_fct = 'GROUP_CONCAT(`tag`)';
+ }
+ $sql = "SELECT *, (select $group_fct from `*PREFIX*bookmarks_tags` where `bookmark_id` = `b`.`id`) as `tags`
+ FROM `*PREFIX*bookmarks` `b`
+ WHERE `user_id` = ? AND `id` = ?";
+ $query = $db->prepareQuery($sql);
+ $result = $query->execute(array($userId, $id))->fetchRow();
+ $result['tags'] = explode(',', $result['tags']);
+ return $result;
+ }
+
+ /**
+ * @brief Check if an URL is bookmarked
+ * @param $url Url of a possible bookmark
+ * @param $userId UserId
+ * @param IDb $db Database Interface
+ * @return boolean if the url is already bookmarked
+ */
+ public static function bookmarkExists($url, $userId, IDb $db) {
+ $enc_url = htmlspecialchars_decode($url);
+ $sql = "SELECT id from `*PREFIX*bookmarks` where `url` = ? and `user_id` = ?";
+ $query = $db->prepareQuery($sql);
+ $result = $query->execute(array($enc_url, $userId))->fetchRow();
+ if ($result) {
+ return $result['id'];
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @brief Finds all bookmarks, matching the filter
+ * @param $userid UserId
+ * @param IDb $db Database Interface
+ * @param int $offset offset
+ * @param string $sqlSortColumn result with this column
+ * @param string|array $filters filters can be: empty -> no filter, a string -> filter this, a string array -> filter for all strings
+ * @param bool $filterTagOnly true, filter affects only tags, else filter affects url, title and tags
+ * @param int $limit limit of items to return (default 10) if -1 or false then all items are returned
+ * @param bool $public check if only public bookmarks should be returned
+ * @param array $requestedAttributes select all the attributes that should be returned. default is * + tags
+ * @param string $tagFilterConjunction select wether the filterTagOnly should filter with an AND or an OR conjunction
+ * @return Collection of specified bookmarks
+ */
+ public static function findBookmarks(
+ $userid, IDb $db, $offset, $sqlSortColumn, $filters, $filterTagOnly, $limit = 10, $public = false, $requestedAttributes = null, $tagFilterConjunction = "and") {
+
+ $CONFIG_DBTYPE = \OCP\Config::getSystemValue('dbtype', 'sqlite');
+ if (is_string($filters)) {
+ $filters = array($filters);
+ }
+
+ $toSelect = '*';
+ $tableAttributes = array('id', 'url', 'title', 'user_id', 'description',
+ 'public', 'added', 'lastmodified', 'clickcount',);
+
+ $returnTags = true;
+
+ if ($requestedAttributes != null) {
+
+ $key = array_search('tags', $requestedAttributes);
+ if ($key == false) {
+ $returnTags = false;
+ } else {
+ unset($requestedAttributes[$key]);
+ }
+
+ $toSelect = implode(",", array_intersect($tableAttributes, $requestedAttributes));
+ }
+
+ if ($CONFIG_DBTYPE == 'pgsql') {
+ $sql = "SELECT " . $toSelect . " FROM (SELECT *, (select array_to_string(array_agg(`tag`),',')
+ from `*PREFIX*bookmarks_tags` where `bookmark_id` = `b2`.`id`) as `tags`
+ FROM `*PREFIX*bookmarks` `b2`
+ WHERE `user_id` = ? ) as `b` WHERE true ";
+ } else {
+ $sql = "SELECT " . $toSelect . ", (SELECT GROUP_CONCAT(`tag`) from `*PREFIX*bookmarks_tags`
+ WHERE `bookmark_id` = `b`.`id`) as `tags`
+ FROM `*PREFIX*bookmarks` `b`
+ WHERE `user_id` = ? ";
+ }
+
+ $params = array($userid);
+
+ if ($public) {
+ $sql .= ' AND public = 1 ';
+ }
+
+ Bookmarks::findBookmarksBuildFilter($sql, $params, $filters, $filterTagOnly, $tagFilterConjunction, $CONFIG_DBTYPE);
+
+ if (!in_array($sqlSortColumn, $tableAttributes)) {
+ $sqlSortColumn = 'lastmodified';
+ }
+ $sql .= " ORDER BY " . $sqlSortColumn . " DESC ";
+ if ($limit == -1 || $limit === false) {
+ $limit = null;
+ $offset = null;
+ }
+
+ $query = $db->prepareQuery($sql, $limit, $offset);
+ $results = $query->execute($params)->fetchAll();
+ $bookmarks = array();
+ foreach ($results as $result) {
+ if ($returnTags) {
+ $result['tags'] = explode(',', $result['tags']);
+ } else {
+ unset($result['tags']);
+ }
+ $bookmarks[] = $result;
+ }
+ return $bookmarks;
+ }
+
+ private static function findBookmarksBuildFilter(&$sql, &$params, $filters, $filterTagOnly, $tagFilterConjunction, $CONFIG_DBTYPE) {
+ $tagOrSearch = false;
+ $connectWord = 'AND';
+
+ if ($tagFilterConjunction == 'or') {
+ $tagOrSearch = true;
+ $connectWord = 'OR';
+ }
+
+ if ($filterTagOnly) {
+ if ($tagOrSearch) {
+ $sql .= 'AND (';
+ } else {
+ $sql .= 'AND';
+ }
+ $exist_clause = " exists (SELECT `id` FROM `*PREFIX*bookmarks_tags`
+ `t2` WHERE `t2`.`bookmark_id` = `b`.`id` AND `tag` = ?) ";
+ $sql .= str_repeat($exist_clause . $connectWord, count($filters));
+ if ($tagOrSearch) {
+ $sql = rtrim($sql, 'OR');
+ $sql .= ')';
+ } else {
+ $sql = rtrim($sql, 'AND');
+ }
+ $params = array_merge($params, $filters);
+ } else {
+ if ($CONFIG_DBTYPE == 'mysql') { //Dirty hack to allow usage of alias in where
+ $sql .= ' HAVING true ';
+ }
+ foreach ($filters as $filter) {
+ if ($CONFIG_DBTYPE == 'mysql') {
+ $sql .= ' AND lower( concat(url,title,description,IFNULL(tags,\'\') )) like ? ';
+ } else {
+ $sql .= ' AND lower(url || title || description || ifnull(tags,\'\') ) like ? ';
+ }
+ $params[] = '%' . strtolower($filter) . '%';
+ }
+ }
+ }
+
+ /**
+ * @brief Delete bookmark with specific id
+ * @param $userId UserId
+ * @param IDb $db Database Interface
+ * @param $id Bookmark ID to delete
+ * @return boolean Success of operation
+ */
+ public static function deleteUrl($userId, IDb $db, $id) {
+ $user = $userId;
+
+ $query = $db->prepareQuery("
+ SELECT `id` FROM `*PREFIX*bookmarks`
+ WHERE `id` = ?
+ AND `user_id` = ?
+ ");
+
+ $result = $query->execute(array($id, $user));
+ $id = $result->fetchOne();
+ if ($id === false) {
+ return false;
+ }
+
+ $query = $db->prepareQuery("
+ DELETE FROM `*PREFIX*bookmarks`
+ WHERE `id` = ?
+ ");
+
+ $query->execute(array($id));
+
+ $query = $db->prepareQuery("
+ DELETE FROM `*PREFIX*bookmarks_tags`
+ WHERE `bookmark_id` = ?
+ ");
+
+ $query->execute(array($id));
+ return true;
+ }
+
+ /**
+ * @brief Rename a tag
+ * @param $userId UserId
+ * @param IDb $db Database Interface
+ * @param string $old Old Tag Name
+ * @param string $new New Tag Name
+ * @return boolean Success of operation
+ */
+ public static function renameTag($userId, IDb $db, $old, $new) {
+ $user_id = $userId;
+ $CONFIG_DBTYPE = \OCP\Config::getSystemValue('dbtype', 'sqlite');
+
+
+ if ($CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3') {
+ // Update tags to the new label unless it already exists a tag like this
+ $query = $db->prepareQuery("
+ UPDATE OR REPLACE `*PREFIX*bookmarks_tags`
+ SET `tag` = ?
+ WHERE `tag` = ?
+ AND exists( select `b`.`id` from `*PREFIX*bookmarks` `b`
+ WHERE `b`.`user_id` = ? AND `bookmark_id` = `b`.`id`)
+ ");
+
+ $params = array(
+ $new,
+ $old,
+ $user_id,
+ );
+
+ $query->execute($params);
+ } else {
+
+ // Remove potentialy duplicated tags
+ $query = $db->prepareQuery("
+ DELETE FROM `*PREFIX*bookmarks_tags` as `tgs` WHERE `tgs`.`tag` = ?
+ AND exists( SELECT `id` FROM `*PREFIX*bookmarks` WHERE `user_id` = ?
+ AND `tgs`.`bookmark_id` = `id`)
+ AND exists( SELECT `t`.`tag` FROM `*PREFIX*bookmarks_tags` `t` where `t`.`tag` = ?
+ AND `tgs`.`bookmark_id` = `t`.`bookmark_id`");
+
+ $params = array(
+ $new,
+ $user_id,
+ );
+
+ $query->execute($params);
+
+
+ // Update tags to the new label unless it already exists a tag like this
+ $query = $db->prepareQuery("
+ UPDATE `*PREFIX*bookmarks_tags`
+ SET `tag` = ?
+ WHERE `tag` = ?
+ AND exists( SELECT `b`.`id` FROM `*PREFIX*bookmarks` `b`
+ WHERE `b`.`user_id` = ? AND `bookmark_id` = `b`.`id`)
+ ");
+
+ $params = array(
+ $new,
+ $old,
+ $user_id,
+ $old,
+ );
+
+ $query->execute($params);
+ }
+
+
+ return true;
+ }
+
+ /**
+ * @brief Delete a tag
+ * @param $userid UserId
+ * @param IDb $db Database Interface
+ * @param string $old Tag Name to delete
+ * @return boolean Success of operation
+ */
+ public static function deleteTag($userid, IDb $db, $old) {
+
+ // Update the record
+ $query = $db->prepareQuery("
+ DELETE FROM `*PREFIX*bookmarks_tags`
+ WHERE `tag` = ?
+ AND exists( SELECT `id` FROM `*PREFIX*bookmarks` WHERE `user_id` = ? AND `bookmark_id` = `id`)
+ ");
+
+ $params = array(
+ $old,
+ $userid,
+ );
+
+ $result = $query->execute($params);
+ return $result;
+ }
+
+ /**
+ * Edit a bookmark
+ * @param $userid UserId
+ * @param IDb $db Database Interface
+ * @param int $id The id of the bookmark to edit
+ * @param string $url The url to set
+ * @param string $title Name of the bookmark
+ * @param array $tags Simple array of tags to qualify the bookmark (different tags are taken from values)
+ * @param string $description A longer description about the bookmark
+ * @param boolean $is_public True if the bookmark is publishable to not registered users
+ * @return null
+ */
+ public static function editBookmark($userid, IDb $db, $id, $url, $title, $tags = array(), $description = '', $is_public = false) {
+
+ $is_public = $is_public ? 1 : 0;
+ $user_id = $userid;
+
+ // Update the record
+ $query = $db->prepareQuery("
+ UPDATE `*PREFIX*bookmarks` SET
+ `url` = ?, `title` = ?, `public` = ?, `description` = ?,
+ `lastmodified` = UNIX_TIMESTAMP()
+ WHERE `id` = ?
+ AND `user_id` = ?
+ ");
+
+ $params = array(
+ htmlspecialchars_decode($url),
+ htmlspecialchars_decode($title),
+ $is_public,
+ htmlspecialchars_decode($description),
+ $id,
+ $user_id,
+ );
+
+ $result = $query->execute($params);
+
+ // Abort the operation if bookmark couldn't be set
+ // (probably because the user is not allowed to edit this bookmark)
+ if ($result == 0)
+ exit();
+
+
+ // Remove old tags
+ $sql = "DELETE FROM `*PREFIX*bookmarks_tags` WHERE `bookmark_id` = ?";
+ $query = $db->prepareQuery($sql);
+ $query->execute(array($id));
+
+ // Add New Tags
+ self::addTags($db, $id, $tags);
+
+ return $id;
+ }
+
+ /**
+ * Add a bookmark
+ * @param $userid UserId
+ * @param IDb $db Database Interface
+ * @param string $url
+ * @param string $title Name of the bookmark
+ * @param array $tags Simple array of tags to qualify the bookmark (different tags are taken from values)
+ * @param string $description A longer description about the bookmark
+ * @param boolean $public True if the bookmark is publishable to not registered users
+ * @return int The id of the bookmark created
+ */
+ public static function addBookmark($userid, IDb $db, $url, $title, $tags = array(), $description = '', $is_public = false) {
+ $public = $is_public ? 1 : 0;
+ $enc_url = htmlspecialchars_decode($url);
+ // Change lastmodified date if the record if already exists
+ $sql = "SELECT * from `*PREFIX*bookmarks` WHERE `url` = ? AND `user_id` = ?";
+ $query = $db->prepareQuery($sql, 1);
+ $result = $query->execute(array($enc_url, $userid));
+ if ($row = $result->fetchRow()) {
+ $params = array();
+ $title_str = '';
+ if (trim($title) != '') { // Do we replace the old title
+ $title_str = ' , title = ?';
+ $params[] = $title;
+ }
+ $desc_str = '';
+ if (trim($description) != '') { // Do we replace the old description
+ $desc_str = ' , description = ?';
+ $params[] = $description;
+ }
+ $sql = "UPDATE `*PREFIX*bookmarks` SET `lastmodified` = "
+ . "UNIX_TIMESTAMP() $title_str $desc_str WHERE `url` = ? and `user_id` = ?";
+ $params[] = $enc_url;
+ $params[] = $userid;
+ $query = $db->prepareQuery($sql);
+ $query->execute($params);
+ return $row['id'];
+ } else {
+ $query = $db->prepareQuery("
+ INSERT INTO `*PREFIX*bookmarks`
+ (`url`, `title`, `user_id`, `public`, `added`, `lastmodified`, `description`)
+ VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), ?)
+ ");
+
+ $params = array(
+ $enc_url,
+ htmlspecialchars_decode($title),
+ $userid,
+ $public,
+ $description,
+ );
+ $query->execute($params);
+
+ $b_id = $db->getInsertId('*PREFIX*bookmarks');
+
+ if ($b_id !== false) {
+ self::addTags($db, $b_id, $tags);
+ return $b_id;
+ }
+ }
+ }
+
+ /**
+ * @brief Add a set of tags for a bookmark
+ * @param IDb $db Database Interface
+ * @param int $bookmarkID The bookmark reference
+ * @param array $tags Set of tags to add to the bookmark
+ * @return null
+ * */
+ private static function addTags(IDb $db, $bookmarkID, $tags) {
+ $sql = 'INSERT INTO `*PREFIX*bookmarks_tags` (`bookmark_id`, `tag`) select ?, ? ';
+ $dbtype = \OCP\Config::getSystemValue('dbtype', 'sqlite');
+
+ if ($dbtype === 'mysql') {
+ $sql .= 'from dual ';
+ }
+ $sql .= 'where not exists(select * from oc_bookmarks_tags where bookmark_id = ? and tag = ?)';
+
+ $query = $db->prepareQuery($sql);
+ foreach ($tags as $tag) {
+ $tag = trim($tag);
+ if (empty($tag)) {
+ //avoid saving white spaces
+ continue;
+ }
+ $params = array($bookmarkID, $tag, $bookmarkID, $tag);
+ $query->execute($params);
+ }
+ }
+
+ /**
+ * @brief Import Bookmarks from html formatted file
+ * @param $user User imported Bookmarks should belong to
+ * @param IDb $db Database Interface
+ * @param $file Content to import
+ * @return null
+ * */
+ public static function importFile($user, IDb $db, $file) {
+ libxml_use_internal_errors(true);
+ $dom = new \domDocument();
+
+ $dom->loadHTMLFile($file);
+ $links = $dom->getElementsByTagName('a');
+
+ // Reintroduce transaction here!?
+ foreach ($links as $link) {
+ $title = $link->nodeValue;
+ $ref = $link->getAttribute("href");
+ $tag_str = '';
+ if ($link->hasAttribute("tags"))
+ $tag_str = $link->getAttribute("tags");
+ $tags = explode(',', $tag_str);
+
+ $desc_str = '';
+ if ($link->hasAttribute("description"))
+ $desc_str = $link->getAttribute("description");
+
+ self::addBookmark($user, $db, $ref, $title, $tags, $desc_str);
+ }
+
+ return array();
+ }
+
+ /**
+ * @brief Load Url and receive Metadata (Title)
+ * @param $url Url to load and analyze
+ * @return array Metadata for url;
+ * */
+ public static function getURLMetadata($url) {
+ //allow only http(s) and (s)ftp
+ $protocols = '/^[hs]{0,1}[tf]{0,1}tp[s]{0,1}\:\/\//i';
+ //if not (allowed) protocol is given, assume http
+ if (preg_match($protocols, $url) == 0) {
+ $url = 'http://' . $url;
+ }
+ $metadata = array();
+ $metadata['url'] = $url;
+ $page = \OC_Util::getUrlContent($url);
+ if ($page) {
+ if (preg_match("/(.*)<\/title>/sUi", $page, $match) !== false)
+ if (isset($match[1])) {
+ $metadata['title'] = html_entity_decode($match[1], ENT_QUOTES, 'UTF-8');
+ //Not the best solution but....
+ $metadata['title'] = str_replace('™', chr(153), $metadata['title']);
+ $metadata['title'] = str_replace('‐', '‐', $metadata['title']);
+ $metadata['title'] = str_replace('–', '–', $metadata['title']);
+ }
+ }
+ return $metadata;
+ }
+
+ /**
+ * @brief Seperate Url String at comma charachter
+ * @param $line String of Tags
+ * @return array Array of Tags
+ * */
+ public static function analyzeTagRequest($line) {
+ $tags = explode(',', $line);
+ $filterTag = array();
+ foreach ($tags as $tag) {
+ if (trim($tag) != '')
+ $filterTag[] = trim($tag);
+ }
+ return $filterTag;
+ }
+
+}
diff --git a/controller/lib/exportresponse.php b/controller/lib/exportresponse.php
new file mode 100644
index 00000000..30b5f69b
--- /dev/null
+++ b/controller/lib/exportresponse.php
@@ -0,0 +1,26 @@
+addHeader("Cache-Control", "private");
+ $this->addHeader("Content-Type", " application/stream");
+ $this->addHeader("Content-Length", strlen($returnstring));
+ $this->addHeader("Content-Disposition", "attachment; filename=" . $export_name);
+ $this->returnstring = $returnstring;
+ }
+
+ public function render() {
+ return $this->returnstring;
+ }
+
+}
\ No newline at end of file
diff --git a/controller/lib/helper.php b/controller/lib/helper.php
new file mode 100644
index 00000000..25fe849a
--- /dev/null
+++ b/controller/lib/helper.php
@@ -0,0 +1,16 @@
+ 0) {
+use \OCA\Bookmarks\Controller\Lib\Bookmarks;
+
+class Search extends \OCP\Search\Provider{
+
+ function search($query) {
+ $results = array();
+
+ if (substr_count($query, ' ') > 0) {
$search_words = explode(' ', $query);
- }else{
+ } else {
$search_words = $query;
}
- $bookmarks = OC_Bookmarks_Bookmarks::searchBookmarks($search_words);
- $l = new OC_l10n('bookmarks'); //resulttype can't be localized, javascript relies on that type
- foreach($bookmarks as $bookmark) {
- $results[]=new OC_Search_Result($bookmark['title'], $bookmark['title'], $bookmark['url'], (string) $l->t('Bookm.'), null);
+ $db = \OC::$server->getDb();
+ $user = \OCP\User::getUser();
+
+ $bookmarks = Bookmarks::findBookmarks($user, $db, 0, 'id', $search_words, false);
+ $l = new \OC_l10n('bookmarks'); //resulttype can't be localized, javascript relies on that type
+ foreach ($bookmarks as $bookmark) {
+ $results[] = new \OC_Search_Result($bookmark['title'], $bookmark['title'], $bookmark['url'], (string) $l->t('Bookm.'));
}
return $results;
}
+
}
diff --git a/controller/rest/bookmarkcontroller.php b/controller/rest/bookmarkcontroller.php
new file mode 100644
index 00000000..557a0bfa
--- /dev/null
+++ b/controller/rest/bookmarkcontroller.php
@@ -0,0 +1,254 @@
+
+ * @copyright Stefan Klemm 2014
+ */
+
+namespace OCA\Bookmarks\Controller\Rest;
+
+use \OCP\IRequest;
+use \OCP\AppFramework\ApiController;
+use \OCP\AppFramework\Http\JSONResponse;
+use \OCP\AppFramework\Http;
+use \OCP\IDb;
+use \OCA\Bookmarks\Controller\Lib\Bookmarks;
+use \OCA\Bookmarks\Controller\Lib\ExportResponse;
+
+class BookmarkController extends ApiController {
+
+ private $userId;
+ private $db;
+
+ public function __construct($appName, IRequest $request, $userId, IDb $db) {
+ parent::__construct($appName, $request);
+ $this->userId = $userId;
+ $this->db = $db;
+ $this->request = $request;
+ }
+
+ /**
+ * @NoAdminRequired
+ */
+ public function legacyGetBookmarks($type = "bookmark", $tag = '', $page = 0, $sort = "bookmarks_sorting_recent") {
+ return $this->getBookmarks($type, $tag, $page, $sort);
+ }
+
+ /**
+ * @NoAdminRequired
+ */
+ public function getBookmarks($type = "bookmark", $tag = '', $page = 0, $sort = "bookmarks_sorting_recent") {
+
+ if ($type == 'rel_tags') {
+ $tags = Bookmarks::analyzeTagRequest($tag);
+ $qtags = Bookmarks::findTags($this->userId, $this->db, $tags);
+ return new JSONResponse(array('data' => $qtags, 'status' => 'success'));
+ } else { // type == bookmark
+ $filterTag = Bookmarks::analyzeTagRequest($tag);
+
+ $offset = $page * 10;
+
+ if ($sort == 'bookmarks_sorting_clicks') {
+ $sqlSortColumn = 'clickcount';
+ } else {
+ $sqlSortColumn = 'lastmodified';
+ }
+ $bookmarks = Bookmarks::findBookmarks($this->userId, $this->db, $offset, $sqlSortColumn, $filterTag, true);
+ return new JSONResponse(array('data' => $bookmarks, 'status' => 'success'));
+ }
+ }
+
+ /**
+ * @NoAdminRequired
+ */
+ public function newBookmark($url = "", $item = array(), $from_own = 0, $title = "", $is_public = false, $description = "") {
+
+ // Check if it is a valid URL
+ if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) {
+ return new JSONResponse(array('status' => 'error'), Http::STATUS_BAD_REQUEST);
+ }
+
+ $tags = isset($item['tags']) ? $item['tags'] : array();
+
+ if ($from_own == 0) {
+ $datas = Bookmarks::getURLMetadata($url);
+ if (isset($datas['title'])) {
+ $title = $datas['title'];
+ }
+ }
+ $id = Bookmarks::addBookmark($this->userId, $this->db, $url, $title, $tags, $description, $is_public);
+ $bm = Bookmarks::findUniqueBookmark($id, $this->userId, $this->db);
+ return new JSONResponse(array('item' => $bm, 'status' => 'success'));
+ }
+
+ /**
+ @NoAdminRequired
+ *
+ * @param int $id
+ * @param bool $is_public Description
+ * @return \OCP\AppFramework\Http\TemplateResponse
+ */
+ //TODO id vs record_id?
+ public function legacyEditBookmark($id = null, $url = "", $item = array(), $title = "", $is_public = false, $record_id = null, $description = "") {
+ if ($id == null) {
+ return $this->newBookmark($url, $item, false, $title, $is_public, $description);
+ } else {
+ return $this->editBookmark($id, $url, $item, $title, $is_public, $record_id, $description);
+ }
+ }
+
+ /**
+ @NoAdminRequired
+ *
+ * @param int $id
+ * @param bool $is_public Description
+ * @return \OCP\AppFramework\Http\TemplateResponse
+ */
+ public function editBookmark($id = null, $url = "", $item = array(), $title = "", $is_public = false, $record_id = null, $description = "") {
+
+ // Check if it is a valid URL
+ if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) {
+ return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
+ }
+
+ if ($record_id == null) {
+ return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
+ }
+
+ $tags = isset($item['tags']) ? $item['tags'] : array();
+
+ if (is_numeric($record_id)) {
+ $id = Bookmarks::editBookmark($this->userId, $this->db, $record_id, $url, $title, $tags, $description, $is_public = false);
+ }
+
+ $bm = Bookmarks::findUniqueBookmark($id, $this->userId, $this->db);
+ return new JSONResponse(array('item' => $bm, 'status' => 'success'));
+ }
+
+ /**
+ @NoAdminRequired
+ *
+ * @param int $id
+ * @param bool $is_public Description
+ * @return \OCP\AppFramework\Http\JSONResponse
+ */
+ public function legacyDeleteBookmark($id = -1) {
+ return $this->deleteBookmark($id);
+ }
+
+ /**
+ @NoAdminRequired
+ *
+ * @param int $id
+ * @param bool $is_public Description
+ * @return \OCP\AppFramework\Http\JSONResponse
+ */
+ public function deleteBookmark($id = -1) {
+ if ($id == -1) {
+ return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
+ }
+
+ if (!Bookmarks::deleteUrl($this->userId, $this->db, $id)) {
+ return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
+ } else {
+ return new JSONResponse(array('status' => 'success'), Http::STATUS_OK);
+ }
+ }
+
+ /**
+ @NoAdminRequired
+ *
+ * @param string $url
+ * @return \OCP\AppFramework\Http\JSONResponse
+ */
+ public function clickBookmark($url = "") {
+
+ // Check if it is a valid URL
+ if (filter_var($url, FILTER_VALIDATE_URL) === FALSE) {
+ return new JSONResponse(array(), Http::STATUS_BAD_REQUEST);
+ }
+
+ $query = $this->db->prepareQuery('
+ UPDATE `*PREFIX*bookmarks`
+ SET `clickcount` = `clickcount` + 1
+ WHERE `user_id` = ?
+ AND `url` LIKE ?
+ ');
+
+ $params = array($this->userId, htmlspecialchars_decode($url));
+ $query->execute($params);
+
+ return new JSONResponse(array('status' => 'success'), Http::STATUS_OK);
+ }
+
+ /**
+ @NoAdminRequired
+ *
+ * @return \OCP\AppFramework\Http\JSONResponse
+ */
+ public function importBookmark() {
+
+ $l = new \OC_l10n('bookmarks');
+
+ $full_input = $this->request->getUploadedFile("bm_import");
+
+ if (empty($full_input)) {
+ \OCP\Util::writeLog('bookmarks', "No file provided for import", \OCP\Util::WARN);
+ $error = array();
+ $error[] = $l->t('No file provided for import');
+ } else {
+ $error = array();
+ $file = $full_input['tmp_name'];
+ if ($full_input['type'] == 'text/html') {
+ $error = Bookmarks::importFile($this->userId, $this->db, $file);
+ if (empty($error)) {
+ return new JSONResponse(array('status' => 'success'));
+ }
+ } else {
+ $error[] = $l->t('Unsupported file type for import');
+ }
+ }
+
+ return new JSONResponse(array('status' => 'error', 'data' => $error));
+ }
+
+ /**
+ @NoAdminRequired
+ *
+ * @return \OCP\AppFramework\Http\JSONResponse
+ */
+ public function exportBookmark() {
+
+ $file = <<
+
+
+Bookmarks
+
'.htmlspecialchars($bm['description'], ENT_QUOTES, 'UTF-8');
- $file .= "\n";
-}
-$user_name = trim(OCP\User::getDisplayName()) != '' ?
- OCP\User::getDisplayName() : OCP\User::getUser();
-$export_name = '"ownCloud Bookmarks ('.$user_name.') ('.date('Y-m-d').').html"';
-header("Cache-Control: private");
-header("Content-Type: application/stream");
-header("Content-Length: ".strlen($file));
-header("Content-Disposition: attachment; filename=".$export_name);
-
-echo $file;
-exit;
diff --git a/img/loading.gif b/img/loading.gif
new file mode 100644
index 00000000..5b33f7e5
Binary files /dev/null and b/img/loading.gif differ
diff --git a/index.php b/index.php
deleted file mode 100644
index a436d59f..00000000
--- a/index.php
+++ /dev/null
@@ -1,42 +0,0 @@
-.
-*
-*/
-
-
-
-// Check if we are a user
-OCP\User::checkLoggedIn();
-OCP\App::checkAppEnabled('bookmarks');
-
-OCP\App::setActiveNavigationEntry( 'bookmarks_index' );
-
-OCP\Util::addscript('bookmarks', 'settings');
-OCP\Util::addscript('bookmarks', 'bookmarks');
-OCP\Util::addStyle('bookmarks', 'bookmarks');
-
-OCP\Util::addscript('bookmarks/3rdparty', 'tag-it');
-OCP\Util::addscript('bookmarks/3rdparty', 'js_tpl');
-OCP\Util::addStyle('bookmarks/3rdparty', 'jquery.tagit');
-
-$tmpl = new OCP\Template( 'bookmarks', 'list', 'user' );
-$tmpl->assign('req_tag', isset($_GET['tag']) ? $_GET['tag'] : '');
-$tmpl->printPage();
diff --git a/js/3rdparty/js_tpl.js b/js/3rdparty/js_tpl.js
new file mode 100644
index 00000000..62d98ba9
--- /dev/null
+++ b/js/3rdparty/js_tpl.js
@@ -0,0 +1,33 @@
+
+// Simple JavaScript Templating
+// John Resig - http://ejohn.org/ - MIT Licensed
+(function () {
+ var cache = {};
+
+ this.tmpl = function tmpl(str, data) {
+ // Figure out if we're getting a template, or if we need to
+ // load the template - and be sure to cache the result.
+ var fn = !/\W/.test(str) ?
+ cache[str] = cache[str] ||
+ tmpl(document.getElementById(str).innerHTML) :
+ // Generate a reusable function that will serve as a template
+ // generator (and which will be cached).
+ new Function("obj",
+ "var p=[],print=function(){p.push.apply(p,arguments);};" +
+ // Introduce the data as local variables using with(){}
+ "with(obj){p.push('" +
+ // Convert the template into pure JavaScript
+ str
+ .replace(/[\r\t\n]/g, " ")
+ .split("<&").join("\t")
+ .replace(/((^|&>)[^\t]*)'/g, "$1\r")
+ .replace(/\t=(.*?)&>/g, "',$1,'")
+ .split("\t").join("');")
+ .split("&>").join("p.push('")
+ .split("\r").join("\\'")
+ + "');}return p.join('');");
+
+ // Provide some basic currying to the user
+ return data ? fn(data) : fn;
+ };
+})();
diff --git a/js/3rdparty/tag-it.js b/js/3rdparty/tag-it.js
new file mode 100644
index 00000000..dc67e8bb
--- /dev/null
+++ b/js/3rdparty/tag-it.js
@@ -0,0 +1,365 @@
+/*
+ * jQuery UI Tag-it!
+ *
+ * @version v2.0 (06/2011)
+ *
+ * Copyright 2011, Levy Carneiro Jr.
+ * Released under the MIT license.
+ * http://aehlke.github.com/tag-it/LICENSE
+ *
+ * Homepage:
+ * http://aehlke.github.com/tag-it/
+ *
+ * Authors:
+ * Levy Carneiro Jr.
+ * Martin Rehfeld
+ * Tobias Schmidt
+ * Skylar Challand
+ * Alex Ehlke
+ *
+ * Maintainer:
+ * Alex Ehlke - Twitter: @aehlke
+ *
+ * Dependencies:
+ * jQuery v1.4+
+ * jQuery UI v1.8+
+ */
+(function ($) {
+
+ $.widget('ui.tagit', {
+ options: {
+ itemName: 'item',
+ fieldName: 'tags',
+ availableTags: [],
+ tagSource: null,
+ removeConfirmation: false,
+ caseSensitive: true,
+ placeholderText: null,
+ // When enabled, quotes are not neccesary
+ // for inputting multi-word tags.
+ allowSpaces: false,
+ // The below options are for using a single field instead of several
+ // for our form values.
+ //
+ // When enabled, will use a single hidden field for the form,
+ // rather than one per tag. It will delimit tags in the field
+ // with singleFieldDelimiter.
+ //
+ // The easiest way to use singleField is to just instantiate tag-it
+ // on an INPUT element, in which case singleField is automatically
+ // set to true, and singleFieldNode is set to that element. This
+ // way, you don't need to fiddle with these options.
+ singleField: false,
+ singleFieldDelimiter: ',',
+ // Set this to an input DOM node to use an existing form field.
+ // Any text in it will be erased on init. But it will be
+ // populated with the text of tags as they are created,
+ // delimited by singleFieldDelimiter.
+ //
+ // If this is not set, we create an input node for it,
+ // with the name given in settings.fieldName,
+ // ignoring settings.itemName.
+ singleFieldNode: null,
+ // Optionally set a tabindex attribute on the input that gets
+ // created for tag-it.
+ tabIndex: null,
+ // Event callbacks.
+ onTagAdded: null,
+ onTagRemoved: null,
+ onTagClicked: null
+ },
+ _create: function () {
+ // for handling static scoping inside callbacks
+ var that = this;
+
+ // There are 2 kinds of DOM nodes this widget can be instantiated on:
+ // 1. UL, OL, or some element containing either of these.
+ // 2. INPUT, in which case 'singleField' is overridden to true,
+ // a UL is created and the INPUT is hidden.
+ if (this.element.is('input')) {
+ this.tagList = $('
').insertAfter(this.element);
+ this.options.singleField = true;
+ this.options.singleFieldNode = this.element;
+ this.element.css('display', 'none');
+ } else {
+ this.tagList = this.element.find('ul, ol').andSelf().last();
+ }
+
+ this._tagInput = $('').addClass('ui-widget-content');
+ if (this.options.tabIndex) {
+ this._tagInput.attr('tabindex', this.options.tabIndex);
+ }
+ if (this.options.placeholderText) {
+ this._tagInput.attr('placeholder', this.options.placeholderText);
+ }
+ this.options.tagSource = this.options.tagSource || function (search, showChoices) {
+ var filter = search.term.toLowerCase();
+ var choices = $.grep(that.options.availableTags, function (element) {
+ // Only match autocomplete options that begin with the search term.
+ // (Case insensitive.)
+ return (element.toLowerCase().indexOf(filter) === 0);
+ });
+ showChoices(that._subtractArray(choices, that.assignedTags()));
+ };
+
+ this.tagList
+ .addClass('tagit')
+ .addClass('ui-widget ui-widget-content ui-corner-all')
+ // Create the input field.
+ .append($('').append(this._tagInput))
+ .click(function (e) {
+ var target = $(e.target);
+ if (target.hasClass('tagit-label')) {
+ that._trigger('onTagClicked', e, target.closest('.tagit-choice'));
+ } else {
+ // Sets the focus() to the input field, if the user
+ // clicks anywhere inside the UL. This is needed
+ // because the input field needs to be of a small size.
+ that._tagInput.focus();
+ }
+ });
+
+ // Add existing tags from the list, if any.
+ this.tagList.children('li').each(function () {
+ if (!$(this).hasClass('tagit-new')) {
+ that.createTag($(this).html(), $(this).attr('class'));
+ $(this).remove();
+ }
+ });
+
+ // Single field support.
+ if (this.options.singleField) {
+ if (this.options.singleFieldNode) {
+ // Add existing tags from the input field.
+ var node = $(this.options.singleFieldNode);
+ var tags = node.val().split(this.options.singleFieldDelimiter);
+ node.val('');
+ $.each(tags, function (index, tag) {
+ that.createTag(tag);
+ });
+ } else {
+ // Create our single field input after our list.
+ this.options.singleFieldNode = this.tagList.after('');
+ }
+ }
+
+ // Events.
+ this._tagInput
+ .keydown(function (event) {
+ // Backspace is not detected within a keypress, so it must use keydown.
+ if (event.which == $.ui.keyCode.BACKSPACE && that._tagInput.val() === '') {
+ var tag = that._lastTag();
+ if (!that.options.removeConfirmation || tag.hasClass('remove')) {
+ // When backspace is pressed, the last tag is deleted.
+ that.removeTag(tag);
+ } else if (that.options.removeConfirmation) {
+ tag.addClass('remove ui-state-highlight');
+ }
+ } else if (that.options.removeConfirmation) {
+ that._lastTag().removeClass('remove ui-state-highlight');
+ }
+
+ // Comma/Space/Enter are all valid delimiters for new tags,
+ // except when there is an open quote or if setting allowSpaces = true.
+ // Tab will also create a tag, unless the tag input is empty, in which case it isn't caught.
+ if (
+ event.which == $.ui.keyCode.COMMA ||
+ event.which == $.ui.keyCode.ENTER ||
+ (
+ event.which == $.ui.keyCode.TAB &&
+ that._tagInput.val() !== ''
+ ) ||
+ (
+ event.which == $.ui.keyCode.SPACE &&
+ that.options.allowSpaces !== true &&
+ (
+ $.trim(that._tagInput.val()).replace(/^s*/, '').charAt(0) != '"' ||
+ (
+ $.trim(that._tagInput.val()).charAt(0) == '"' &&
+ $.trim(that._tagInput.val()).charAt($.trim(that._tagInput.val()).length - 1) == '"' &&
+ $.trim(that._tagInput.val()).length - 1 !== 0
+ )
+ )
+ )
+ ) {
+ event.preventDefault();
+ that.createTag(that._cleanedInput());
+
+ // The autocomplete doesn't close automatically when TAB is pressed.
+ // So let's ensure that it closes.
+ that._tagInput.autocomplete('close');
+ }
+ }).blur(function (e) {
+ //If autocomplete is enabled and suggestion was clicked, don't add it
+ if (that.options.tagSource && that._tagInput.data('autocomplete-open')) {
+ that._cleanedInput();
+ } else {
+ that.createTag(that._cleanedInput());
+ }
+ });
+
+
+ // Autocomplete.
+ if (this.options.availableTags || this.options.tagSource) {
+ this._tagInput.autocomplete({
+ source: this.options.tagSource,
+ open: function () {
+ that._tagInput.data('autocomplete-open', true)
+ },
+ close: function () {
+ that._tagInput.data('autocomplete-open', false)
+ },
+ select: function (event, ui) {
+ that.createTag(ui.item.value);
+ // Preventing the tag input to be updated with the chosen value.
+ return false;
+ }
+ });
+ }
+ },
+ _cleanedInput: function () {
+ // Returns the contents of the tag input, cleaned and ready to be passed to createTag
+ return $.trim(this._tagInput.val().replace(/^"(.*)"$/, '$1'));
+ },
+ _lastTag: function () {
+ return this.tagList.children('.tagit-choice:last');
+ },
+ assignedTags: function () {
+ // Returns an array of tag string values
+ var that = this;
+ var tags = [];
+ if (this.options.singleField) {
+ tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
+ if (tags[0] === '') {
+ tags = [];
+ }
+ } else {
+ this.tagList.children('.tagit-choice').each(function () {
+ tags.push(that.tagLabel(this));
+ });
+ }
+ return tags;
+ },
+ _updateSingleTagsField: function (tags) {
+ // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
+ $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter));
+ },
+ _subtractArray: function (a1, a2) {
+ var result = [];
+ for (var i = 0; i < a1.length; i++) {
+ if ($.inArray(a1[i], a2) == -1) {
+ result.push(a1[i]);
+ }
+ }
+ return result;
+ },
+ tagLabel: function (tag) {
+ // Returns the tag's string label.
+ if (this.options.singleField) {
+ return $(tag).children('.tagit-label').text();
+ } else {
+ return $(tag).children('input').val();
+ }
+ },
+ _isNew: function (value) {
+ var that = this;
+ var isNew = true;
+ this.tagList.children('.tagit-choice').each(function (i) {
+ if (that._formatStr(value) == that._formatStr(that.tagLabel(this))) {
+ isNew = false;
+ return;
+ }
+ });
+ return isNew;
+ },
+ _formatStr: function (str) {
+ if (this.options.caseSensitive) {
+ return str;
+ }
+ return $.trim(str.toLowerCase());
+ },
+ createTag: function (value, additionalClass) {
+ that = this;
+ // Automatically trims the value of leading and trailing whitespace.
+ value = $.trim(value);
+
+ if (!this._isNew(value) || value === '') {
+ return false;
+ }
+
+ var label = $(this.options.onTagClicked ? '' : '').text(value);
+
+ // Create tag.
+ var tag = $('')
+ .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
+ .addClass(additionalClass)
+ .append(label);
+
+ // Button for removing the tag.
+ var removeTagIcon = $('')
+ .addClass('ui-icon ui-icon-close');
+ var removeTag = $('\xd7') // \xd7 is an X
+ .addClass('close')
+ .append(removeTagIcon)
+ .click(function (e) {
+ // Removes a tag when the little 'x' is clicked.
+ that.removeTag(tag);
+ });
+ tag.append(removeTag);
+
+ // Unless options.singleField is set, each tag has a hidden input field inline.
+ if (this.options.singleField) {
+ var tags = this.assignedTags();
+ tags.push(value);
+ this._updateSingleTagsField(tags);
+ } else {
+ var escapedValue = label.html();
+ tag.append('');
+ }
+
+ this._trigger('onTagAdded', null, tag);
+
+ // Cleaning the input.
+ this._tagInput.val('');
+
+ // insert tag
+ this._tagInput.parent().before(tag);
+ },
+ removeTag: function (tag, animate) {
+ if (typeof animate === 'undefined') {
+ animate = true;
+ }
+
+ tag = $(tag);
+
+ this._trigger('onTagRemoved', null, tag);
+
+ if (this.options.singleField) {
+ var tags = this.assignedTags();
+ var removedTagLabel = this.tagLabel(tag);
+ tags = $.grep(tags, function (el) {
+ return el != removedTagLabel;
+ });
+ this._updateSingleTagsField(tags);
+ }
+ // Animate the removal.
+ if (animate) {
+ tag.fadeOut('fast').hide('blind', {direction: 'horizontal'}, 'fast', function () {
+ tag.remove();
+ }).dequeue();
+ } else {
+ tag.remove();
+ }
+ this._trigger('onTagFinishRemoved', null, tag);
+ },
+ removeAll: function () {
+ // Removes all tags. Takes an optional `animate` argument.
+ var that = this;
+ this.tagList.children('.tagit-choice').each(function (index, tag) {
+ that.removeTag(tag, false);
+ });
+ }
+
+ });
+
+})(jQuery);
\ No newline at end of file
diff --git a/js/addBm.js b/js/addBm.js
deleted file mode 100644
index 25ffa859..00000000
--- a/js/addBm.js
+++ /dev/null
@@ -1,85 +0,0 @@
-(function($){
- $.bookmark_dialog = function(el, options){
- // To avoid scope issues, use 'base' instead of 'this'
- // to reference this class from internal events and functions.
- var base = this;
-
- // Access to jQuery and DOM versions of element
- base.$el = $(el);
- base.el = el;
-
- // Add a reverse reference to the DOM object
- base.$el.data('bookmark_dialog', base);
-
- base.form_submit = function search_form_submit(event)
- {
- event.preventDefault();
- $.ajax({
- type: 'POST',
- url: $(this).attr('action'),
- data: $(this).serialize(),
- success: function(data){
- if(data.status == 'success'){
- base.options['on_success'](data);
- } else { // On failure
-
- }
- }
- });
- return false;
- }
- base.setTitle = function (str) {
- base.$el.find('.title').val(str);
- }
-
- base.init = function(){
- base.options = $.extend({},$.bookmark_dialog.defaultOptions, options);
- base.$el.find('form').bind('submit.addBmform',base.form_submit);
- // Init Tagging thing
- base.$el.find('.tags').tagit({
- allowSpaces: true,
- availableTags: fullTags,
- placeholderText: t('bookmark', 'Tags')
- });
-
- if(base.options['record']) { //Fill the form if it's an edit
- record = base.options['record'];
- base.$el.find('.record_id').val(record.id);
- base.$el.find('.title').val(record.title);
- base.$el.find('.url_input').val(record.url);
- base.$el.find('.desc').val(record.description);
- tagit_elem = base.$el.find('.tags');
- if(record.tags) {
- for(var i=0;i 0) {
+ ajaxCallCount--;
+ updateLoadingAnimation();
+ }
+}
+
+function updateLoadingAnimation() {
+ if (ajaxCallCount === 0) {
+ $("#add_form_loading").css("visibility", "hidden");
+ } else {
+ $("#add_form_loading").css("visibility", "visible");
+ }
+}
+
+$(function () {
+ $(".submit").click(function () {
+ increaseAjaxCallCount();
+ var dataString = 'url=' + $("input#url").val() + '&description=' +
+ $("textarea#description").val() + '&title=' + $("input#title").val();
+ $.ajax({
+ type: "POST",
+ url: "bookmark",
+ data: dataString,
+ complete: function () {
+ decreaseAjaxCallCount();
+ },
+ success: function (data) {
+ if (data.status === 'success') {
+ $('#bookmarklet_form').html("");
+ OC.dialogs.alert(
+ t("bookmarks", "Bookmark added. You can close the window now."),
+ t("bookmarks", "Bookmark added successfully"), closeWindow, true);
+ } else {
+ OC.dialogs.alert(t("bookmarks", "Some Error happened."),
+ t("bookmarks", "Error"), null, true);
+ }
+ }
+ });
+ return false;
+ });
+});
+
+function closeWindow() {
+ window.close();
+}
\ No newline at end of file
diff --git a/js/bookmarks.js b/js/bookmarks.js
index 4234f109..7ffe0894 100644
--- a/js/bookmarks.js
+++ b/js/bookmarks.js
@@ -1,17 +1,20 @@
-var bookmarks_page = 0;
-var bookmarks_loading = false;
+var bookmarksPage = 0;
+var bookmarksLoading = false;
var dialog;
-var bookmarks_sorting = 'bookmarks_sorting_recent';
+var bookmarksSorting = 'bookmarks_sorting_recent';
var fullTags = [];
-$(document).ready(function() {
+var ajaxCallCount = 0;
+
+$(document).ready(function () {
+ getTags();
watchUrlField();
$('#bm_import').change(attachSettingEvent);
$('#add_url').on('keydown keyup change click', watchUrlField);
- $('#settingsbtn').on('click keydown', toggleSettings);
+ $('#app-settings').on('click keydown', toggleSettings);
$('#bm_export').click(exportBm);
- $('#firstrun_setting').click(function(){
- if(! $('#bookmark_settings').hasClass('open')){
- $('#settingsbtn').click();
+ $('#emptycontent-setting').click(function () {
+ if (!$('#app-settings').hasClass('open')) {
+ $('#app-settings').click();
}
});
$('.bookmarks_list').scroll(updateOnBottom).empty();
@@ -24,40 +27,71 @@ $(document).ready(function() {
getBookmarks();
});
+function getTags() {
+ jQuery.ajax({
+ url: 'tag',
+ success: function (result) {
+ fullTags = result;
+ },
+ async: false
+ });
+}
-var formatString = (function() {
- var replacer = function(context) {
- return function(s, name) {
+var formatString = (function () {
+ var replacer = function (context) {
+ return function (s, name) {
return context[name];
};
};
- return function(input, context) {
+ return function (input, context) {
return input.replace(/\{(\w+)\}/g, replacer(context));
};
})();
-function watchClickInSetting(e){
- if($('#bookmark_settings').find($(e.target)).length == 0){
+function increaseAjaxCallCount() {
+ ajaxCallCount++;
+ if (ajaxCallCount - 1 === 0) {
+ updateLoadingAnimation();
+ }
+}
+
+function decreaseAjaxCallCount() {
+ if (ajaxCallCount > 0) {
+ ajaxCallCount--;
+ updateLoadingAnimation();
+ }
+}
+
+function updateLoadingAnimation() {
+ if (ajaxCallCount === 0) {
+ $("#add_form_loading").css("visibility", "hidden");
+ } else {
+ $("#add_form_loading").css("visibility", "visible");
+ }
+}
+
+function watchClickInSetting(e) {
+ if ($('#app-settings').find($(e.target)).length === 0) {
toggleSettings();
}
}
function checkURL(url) {
- if(url.substring(0, 3) === "htt") {
+ if (url.substring(0, 3) === "htt") {
return url;
}
- return "http://"+url;
+ return "http://" + url;
}
function toggleSettings() {
- if( $('#bookmark_settings').hasClass('open')) { //Close
- $('#bookmark_settings').switchClass( "open", "" );
+ if ($('#app-settings').hasClass('open')) { //Close
+ $('#app-settings').switchClass("open", "");
$('body').unbind('click', watchClickInSetting);
}
- else {
- $('#bookmark_settings').switchClass( "", "open");
- $('body').bind('click',watchClickInSetting);
+ else {
+ $('#app-settings').switchClass("", "open");
+ $('body').bind('click', watchClickInSetting);
}
}
function addFilterTag(event) {
@@ -66,33 +100,34 @@ function addFilterTag(event) {
}
function updateTagsList(tag) {
- html = tmpl("tag_tmpl", tag);
+ var html = tmpl("tag_tmpl", tag);
$('.tag_list').append(html);
}
function filterTagsChanged()
{
- $('#bookmarkFilterTag').val($('#tag_filter input:hidden').val());
+ $('#bookmarkFilterTag').val($('#tag_filter input').val());
$('.bookmarks_list').empty();
- bookmarks_page = 0;
+ bookmarksPage = 0;
getBookmarks();
}
function getBookmarks() {
- if(bookmarks_loading) {
+ if (bookmarksLoading) {
//have patience :)
return;
}
- bookmarks_loading = true;
+ increaseAjaxCallCount();
+ bookmarksLoading = true;
//Update Rel Tags if first page
- if(bookmarks_page == 0) {
+ if (bookmarksPage === 0) {
$.ajax({
type: 'GET',
- url: OC.filePath('bookmarks', 'ajax', 'updateList.php'),
- data: { type:'rel_tags', tag: $('#bookmarkFilterTag').val(), page:bookmarks_page, sort:bookmarks_sorting },
- success: function(tags){
+ url: 'bookmark',
+ data: {type: 'rel_tags', tag: $('#bookmarkFilterTag').val(), page: bookmarksPage, sort: bookmarksSorting},
+ success: function (tags) {
$('.tag_list').empty();
- for(var i in tags.data) {
+ for (var i in tags.data) {
updateTagsList(tags.data[i]);
}
$('.tag_list .tag_edit').click(renameTag);
@@ -105,17 +140,20 @@ function getBookmarks() {
}
$.ajax({
type: 'GET',
- url: OC.filePath('bookmarks', 'ajax', 'updateList.php'),
- data: { type:'bookmark', tag: $('#bookmarkFilterTag').val(), page:bookmarks_page, sort:bookmarks_sorting },
- success: function(bookmarks){
+ url: 'bookmark',
+ data: {type: 'bookmark', tag: $('#bookmarkFilterTag').val(), page: bookmarksPage, sort: bookmarksSorting},
+ complete: function () {
+ decreaseAjaxCallCount();
+ },
+ success: function (bookmarks) {
if (bookmarks.data.length) {
- bookmarks_page += 1;
+ bookmarksPage += 1;
}
$('.bookmark_link').unbind('click', recordClick);
$('.bookmark_delete').unbind('click', delBookmark);
$('.bookmark_edit').unbind('click', editBookmark);
- for(var i in bookmarks.data) {
+ for (var i in bookmarks.data) {
updateBookmarksList(bookmarks.data[i]);
}
checkEmpty();
@@ -124,74 +162,59 @@ function getBookmarks() {
$('.bookmark_delete').click(delBookmark);
$('.bookmark_edit').click(editBookmark);
- bookmarks_loading = false;
+ bookmarksLoading = false;
if (bookmarks.data.length) {
- updateOnBottom()
+ updateOnBottom();
}
}
});
}
-
-function createEditDialog(record){
- var oc_dialog= $('#edit_dialog form').clone().dialog({
- width : 620,
- height: 350,
- title: t('bookmarks', 'Edit bookmark'),
- modal: true,
- close : function(event, ui) {
- $(this).dialog('destroy').remove();
- }
- });
-
- $('.ui-dialog').bookmark_dialog({
- on_success: function(){
- oc_dialog.dialog('destroy').remove();
- filterTagsChanged();
- },
- record: record
- });
-}
-
-function watchUrlField(){
+function watchUrlField() {
var form = $('#add_form');
var el = $('#add_url');
var button = $('#bookmark_add_submit');
form.unbind('submit');
- if(! acceptUrl(el.val()) ) {
- form.bind('submit',function(e){e.preventDefault()});
+ if (!acceptUrl(el.val())) {
+ form.bind('submit', function (e) {
+ e.preventDefault();
+ });
button.addClass('disabled');
}
- else{
+ else {
button.removeClass('disabled');
- form.bind('submit',addBookmark);
+ form.bind('submit', addBookmark);
}
}
function acceptUrl(url) {
- return url.replace(/^\s+/g,'').replace(/\s+$/g,'') != '';
+ return url.replace(/^\s+/g, '').replace(/\s+$/g, '') !== '';
}
function addBookmark(event) {
event.preventDefault();
- url = $('#add_url').val();
+ var url = $('#add_url').val();
//If trim is empty
- if(! acceptUrl(url) ) {
+ if (!acceptUrl(url)) {
return;
}
-
+
$('#add_url').val('');
- bookmark = { url: url, description:'', title:'', from_own: '1', added_date: new Date()};
+ var bookmark = {url: url, description: '', title: '', from_own: 0, added_date: new Date()};
+ increaseAjaxCallCount();
$.ajax({
type: 'POST',
- url: OC.filePath('bookmarks', 'ajax', 'editBookmark.php'),
+ url: 'bookmark',
data: bookmark,
- success: function(data){
- if (data.status == 'success') {
+ complete: function () {
+ decreaseAjaxCallCount();
+ },
+ success: function (data) {
+ if (data.status === 'success') {
// First remove old BM if exists
$('.bookmark_single').filterAttr('data-id', data.item.id).remove();
-
- bookmark = $.extend({}, bookmark, data.item);
+
+ var bookmark = $.extend({}, bookmark, data.item);
updateBookmarksList(bookmark, 'prepend');
checkEmpty();
watchUrlField();
@@ -200,113 +223,116 @@ function addBookmark(event) {
});
}
-function delBookmark(event) {
+function delBookmark() {
var record = $(this).parent().parent();
- $.ajax({
- type: 'POST',
- url: OC.filePath('bookmarks', 'ajax', 'delBookmark.php'),
- data: { id: record.data('id') },
- success: function(data){
- if (data.status == 'success') {
- record.remove();
- checkEmpty();
- }
+ OC.dialogs.confirm(t('bookmarks', 'Are you sure you want to remove this bookmark?'),
+ t('bookmarks', 'Warning'), function (answer) {
+ if (answer) {
+ $.ajax({
+ type: 'DELETE',
+ url: 'bookmark/' + record.data('id'),
+ success: function (data) {
+ if (data.status === 'success') {
+ record.remove();
+ checkEmpty();
+ }
+ }
+ });
}
});
}
function checkEmpty() {
- if($('.bookmarks_list').children().length == 0) {
- $("#firstrun").show();
+ if ($('.bookmarks_list').children().length === 0) {
+ $("#emptycontent").show();
$("#bm_export").addClass('disabled');
$('.bookmarks_list').hide();
} else {
- $("#firstrun").hide();
+ $("#emptycontent").hide();
$("#bm_export").removeClass('disabled');
$('.bookmarks_list').show();
}
}
-function editBookmark(event) {
- if($('.bookmark_single_form').length){
+function editBookmark() {
+ if ($('.bookmark_single_form').length) {
$('.bookmark_single_form .reset').click();
}
var record = $(this).parent().parent();
- bookmark = record.data('record');
- html = tmpl("item_form_tmpl", bookmark);
-
+ var bookmark = record.data('record');
+ var html = tmpl("item_form_tmpl", bookmark);
+
record.after(html);
record.hide();
- rec_form = record.next().find('form');
+ var rec_form = record.next().find('form');
rec_form.find('.bookmark_form_tags ul').tagit({
- allowSpaces: true,
- availableTags: fullTags,
- placeholderText: t('bookmarks', 'Tags')
- });
- rec_form.bind('submit',submitBookmark);
- rec_form.find('.reset').bind('click',cancelBookmark);
+ allowSpaces: true,
+ availableTags: fullTags,
+ placeholderText: t('bookmarks', 'Tags')
+ });
+
+ rec_form.find('.reset').bind('click', cancelBookmark);
+ rec_form.bind('submit', function (event) {
+ event.preventDefault();
+ var form_values = $(this).serialize();
+ $.ajax({
+ type: 'PUT',
+ url: $(this).attr('action') + "/" + this.elements['record_id'].value,
+ data: form_values,
+ success: function (data) {
+ if (data.status === 'success') {
+ //@TODO : do better reaction than reloading the page
+ filterTagsChanged();
+ } else { // On failure
+ //@TODO : show error message?
+ }
+ }
+ });
+ });
}
function cancelBookmark(event) {
event.preventDefault();
- rec_form = $(this).closest('form').parent();
+ var rec_form = $(this).closest('form').parent();
rec_form.prev().show();
rec_form.remove();
}
-function submitBookmark(event) {
- event.preventDefault();
- form_values = $(this).serialize();
- $.ajax({
- type: 'POST',
- url: $(this).attr('action'),
- data: form_values,
- success: function(data){
- if(data.status == 'success'){
- //@TODO : do better reaction than reloading the page
- filterTagsChanged();
- } else { // On failure
- //@TODO : show error message?
- }
- }
- });
-}
-
function updateBookmarksList(bookmark, position) {
position = typeof position !== 'undefined' ? position : 'append';
- bookmark = $.extend({title:'', description:'', added_date: new Date('now'), tags:[] }, bookmark);
- tags = bookmark.tags;
+ bookmark = $.extend({title: '', description: '', added_date: new Date('now'), tags: []}, bookmark);
+ var tags = bookmark.tags;
var taglist = '';
- for ( var i=0, len=tags.length; i' + escapeHTML(tags[i]) + ' ';
}
- if(!hasProtocol(bookmark.url)) {
+ if (!hasProtocol(bookmark.url)) {
bookmark.url = 'http://' + bookmark.url;
}
-
- if(bookmark.added) {
- bookmark.added_date.setTime(parseInt(bookmark.added)*1000);
+
+ if (bookmark.added) {
+ bookmark.added_date.setTime(parseInt(bookmark.added) * 1000);
}
- if(! bookmark.title)
- bookmark.title ='';
+ if (!bookmark.title)
+ bookmark.title = '';
- html = tmpl("item_tmpl", bookmark);
- if(position == "prepend") {
+ var html = tmpl("item_tmpl", bookmark);
+ if (position === "prepend") {
$('.bookmarks_list').prepend(html);
} else {
$('.bookmarks_list').append(html);
}
- line = $('div[data-id="'+ bookmark.id +'"]');
+ var line = $('div[data-id="' + bookmark.id + '"]');
line.data('record', bookmark);
- if(taglist != '') {
+ if (taglist !== '') {
line.append('
' + taglist + '
');
}
line.find('a.bookmark_tag').bind('click', addFilterTag);
line.find('.bookmark_link').click(recordClick);
line.find('.bookmark_delete').click(delBookmark);
line.find('.bookmark_edit').click(editBookmark);
-
+
}
function updateOnBottom() {
@@ -320,50 +346,51 @@ function updateOnBottom() {
}
}
-function recordClick(event) {
+function recordClick() {
$.ajax({
type: 'POST',
- url: OC.filePath('bookmarks', 'ajax', 'recordClick.php'),
+ url: 'bookmark/click',
data: 'url=' + encodeURIComponent($(this).attr('href'))
});
}
function hasProtocol(url) {
- var regexp = /(ftp|http|https|sftp)/;
- return regexp.test(url);
+ var regexp = /(ftp|http|https|sftp)/;
+ return regexp.test(url);
}
-function renameTag(event) {
- if($('input[name="tag_new_name"]').length) return; // Do nothing if a tag is currenlty edited
- tag_el = $(this).closest('li');
- tag_el.append('');
- var form = tag_el.find('form');
- tag_el.find('.tags_actions').hide();
- tag_name = tag_el.find('.tag').hide().text();
- tag_el.find('input').val(tag_name).focus().bind('blur',function() {
+function renameTag() {
+ if ($('input[name="tag_new_name"]').length)
+ return; // Do nothing if a tag is currenlty edited
+ var tagElement = $(this).closest('li');
+ tagElement.append('');
+ var form = tagElement.find('form');
+ //tag_el.find('.tags_actions').hide();
+ var tagName = tagElement.find('.tag').hide().text();
+ tagElement.find('input').val(tagName).focus().bind('blur', function () {
form.trigger('submit');
});
- form.bind('submit',submitTagName);
+ form.bind('submit', submitTagName);
}
function submitTagName(event) {
event.preventDefault();
- tag_el = $(this).closest('li')
- new_tag_name = tag_el.find('input').val();
- old_tag_name = tag_el.find('.tag').show().text();
- tag_el.find('.tag_edit').show();
- tag_el.find('.tags_actions').show();
- tag_el.find('input').unbind('blur');
- tag_el.find('form').unbind('submit').remove();
+ var tagElement = $(this).closest('li');
+ var newTagName = tagElement.find('input').val();
+ var oldTagName = tagElement.find('.tag').show().text();
+ //tag_el.find('.tag_edit').show();
+ //tag_el.find('.tags_actions').show();
+ tagElement.find('input').unbind('blur');
+ tagElement.find('form').unbind('submit').remove();
- if(new_tag_name != old_tag_name && new_tag_name != '') {
+ if (newTagName !== oldTagName && newTagName !== '') {
//submit
$.ajax({
type: 'POST',
- url: OC.filePath('bookmarks', 'ajax', 'renameTag.php'),
- data: { old_name: old_tag_name, new_name: new_tag_name},
- success: function(bookmarks){
- if (bookmarks.status =='success') {
+ url: 'tag',
+ data: {old_name: oldTagName, new_name: newTagName},
+ success: function (bookmarks) {
+ if (bookmarks.status === 'success') {
filterTagsChanged();
}
}
@@ -371,18 +398,18 @@ function submitTagName(event) {
}
}
-function deleteTag(event){
- tag_el = $(this).closest('li');
+function deleteTag() {
+ var tag_el = $(this).closest('li');
var old_tag_name = tag_el.find('.tag').show().text();
OC.dialogs.confirm(t('bookmarks', 'Are you sure you want to remove this tag from every entry?'),
- t('bookmarks', 'Warning'), function(answer) {
- if(answer) {
+ t('bookmarks', 'Warning'), function (answer) {
+ if (answer) {
$.ajax({
- type: 'POST',
- url: OC.filePath('bookmarks', 'ajax', 'delTag.php'),
- data: { old_name: old_tag_name},
- success: function(bookmarks){
- if (bookmarks.status =='success') {
+ type: 'DELETE',
+ url: 'tag',
+ data: {old_name: old_tag_name},
+ success: function (bookmarks) {
+ if (bookmarks.status === 'success') {
filterTagsChanged();
}
}
diff --git a/js/bookmarksearch.js b/js/bookmarksearch.js
deleted file mode 100644
index e8f5363c..00000000
--- a/js/bookmarksearch.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Copyright (c) 2012 David Iwanowitsch
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-$(document).ready(function(){
- OC.search.customResults['Bookm.'] = function(row,item){
- var a=row.find('a');
- a.attr('target','_blank');
- a.click(recordClick);
- }
-});
-
-function recordClick(event) {
- var jsFileLocation = $('script[src*=bookmarksearch]').attr('src');
- jsFileLocation = jsFileLocation.replace('js/bookmarksearch.js', '');
- $.ajax({
- type: 'POST',
- url: jsFileLocation + 'ajax/recordClick.php',
- data: 'url=' + encodeURI($(this).attr('href')),
- });
-}
diff --git a/js/full_tags.php b/js/full_tags.php
deleted file mode 100644
index 931c71aa..00000000
--- a/js/full_tags.php
+++ /dev/null
@@ -1,23 +0,0 @@
-
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-// Set the content type to Javascript
-header("Content-type: text/javascript");
-
-// Disallow caching
-header("Cache-Control: no-cache, must-revalidate");
-header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
-
-$qtags = OC_Bookmarks_Bookmarks::findTags(array(), 0, 400);
-$tags = array();
-foreach($qtags as $tag) {
- $tags[] = $tag['tag'];
-}
-
-?>
-fullTags = ;
\ No newline at end of file
diff --git a/js/settings.js b/js/settings.js
index fff97f8b..226c84fe 100644
--- a/js/settings.js
+++ b/js/settings.js
@@ -3,38 +3,39 @@ function attachSettingEvent(event) {
fileUpload($(this).closest('form'), $('#upload'));
}
-function exportBm(e) {
+function exportBm() {
window.location = $(this).attr('href');
}
-function fileUpload(form, result_div) {
-
+function fileUpload(form, resultDiv) {
+
var uploadEventHandler = function () {
var data = {};
- try{
+ try {
data = $.parseJSON(iframe.contents().text());
- }catch (e){}
- if(!data) {
- result_div.text(t('bookmark', 'Import error'));
+ } catch (e) {
+ }
+ if (!data) {
+ resultDiv.text(t('bookmark', 'Import error'));
return;
}
- if(data.status == 'error') {
- list = $("
").addClass('setting_error_list');
+ if (data.status == 'error') {
+ var list = $("
").addClass('setting_error_list');
console.log(data);
- $.each(data.data,function(index, item){
- list.append($( "" ).text(item));
+ $.each(data.data, function (index, item) {
+ list.append($("").text(item));
});
- result_div.html(list);
+ resultDiv.html(list);
} else {
- result_div.text(t('bookmark', 'Import completed successfully.'));
+ resultDiv.text(t('bookmark', 'Import completed successfully.'));
getBookmarks();
}
};
-
+
// Create the iframe...
var iframe;
- if($('#upload_iframe').length === 1)
- iframe = $('#upload_iframe')
+ if ($('#upload_iframe').length === 1)
+ iframe = $('#upload_iframe');
else {
iframe = $('').attr({
id: 'upload_iframe',
@@ -43,7 +44,7 @@ function fileUpload(form, result_div) {
height: '0',
border: '0',
style: 'display:none'
- }).bind('load',uploadEventHandler);
+ }).bind('load', uploadEventHandler);
form.append(iframe);
}
@@ -58,5 +59,5 @@ function fileUpload(form, result_div) {
// Submit the form...
form.submit();
- result_div.text(t('bookmark', 'Uploading...'));
+ resultDiv.text(t('bookmark', 'Uploading...'));
}
\ No newline at end of file
diff --git a/l10n/da.php b/l10n/da.php
index b2cf7d7f..b2854e8f 100644
--- a/l10n/da.php
+++ b/l10n/da.php
@@ -4,12 +4,12 @@ $TRANSLATIONS = array(
"Unsupported file type for import" => "Filtypen understøttes ikke for import",
"Bookmarks" => "Bogmærker",
"Tags" => "Mærker",
-"Filter by tag" => "Filtrér efter mærke",
-"Edit bookmark" => "Redigér bogmærker",
+"Filter by tag" => "Filtrer efter tag",
+"Edit bookmark" => "Rediger bogmærker",
"Are you sure you want to remove this tag from every entry?" => "Er du sikker på at du vil fjerne dette flag fra alle poster?",
"Warning" => "Advarsel",
"Import error" => "Fejl ved import",
-"Import completed successfully." => "Importen blev fuldført.",
+"Import completed successfully." => "Importer fuldført.",
"Uploading..." => "Uploader...",
"Bookm." => "Bogm.",
"Add a bookmark" => "Tilføj bogmærke",
@@ -25,13 +25,13 @@ $TRANSLATIONS = array(
"Add to ownCloud" => "Tilføj til ownCloud",
"Address" => "Adresse",
"Add" => "Tilføj",
-"Related Tags" => "Relaterede mærker",
+"Related Tags" => "Relaterede Tags",
"Settings" => "Indstillinger",
"You have no bookmarks" => "Du har ingen bogmærker",
"You can also try to import a bookmark file" => "Du kan også prøve at importere en bogmærke-fil",
"Bookmarklet" => "Bookmarklet",
"Export & Import" => "Eksport & import",
-"Export" => "Eksportér",
-"Import" => "Importér"
+"Export" => "Exporter",
+"Import" => "Importer"
);
$PLURAL_FORMS = "nplurals=2; plural=(n != 1);";
diff --git a/l10n/hr.php b/l10n/hr.php
index 21c13d80..fe275796 100644
--- a/l10n/hr.php
+++ b/l10n/hr.php
@@ -1,7 +1,6 @@
"Zabilješke",
-"Warning" => "Upozorenje",
"Close" => "Zatvori",
"Save" => "Snimi",
"Delete" => "Obriši",
diff --git a/lib/bookmarks.php b/lib/bookmarks.php
deleted file mode 100644
index e843b1f9..00000000
--- a/lib/bookmarks.php
+++ /dev/null
@@ -1,467 +0,0 @@
-.
- *
- */
-
-/**
- * This class manages bookmarks
- */
-class OC_Bookmarks_Bookmarks{
-
- /**
- * @brief Finds all tags for bookmarks
- * @param filterTags array of tag to look for if empty then every tag
- * @param offset integer offset
- * @param limit integer of item to return
- */
- public static function findTags($filterTags = array(), $offset = 0, $limit = 10){
- $params = array_merge($filterTags, $filterTags);
- array_unshift($params, OCP\USER::getUser());
- $not_in = '';
- if(!empty($filterTags) ) {
- $exist_clause = " AND exists (select 1 from `*PREFIX*bookmarks_tags`
- `t2` where `t2`.`bookmark_id` = `t`.`bookmark_id` and `tag` = ?) ";
-
- $not_in = ' AND `tag` not in ('. implode(',', array_fill(0, count($filterTags), '?') ) .')'.
- str_repeat($exist_clause, count($filterTags));
- }
- $sql = 'SELECT tag, count(*) as nbr from *PREFIX*bookmarks_tags t '.
- ' WHERE EXISTS( SELECT 1 from *PREFIX*bookmarks bm where t.bookmark_id = bm.id and user_id = ?) '.
- $not_in.
- ' GROUP BY `tag` ORDER BY `nbr` DESC ';
-
- $query = OCP\DB::prepare($sql, $limit, $offset);
- $tags = $query->execute($params)->fetchAll();
- return $tags;
- }
-
- public static function findOneBookmark($id) {
- $CONFIG_DBTYPE = OCP\Config::getSystemValue( 'dbtype', 'sqlite' );
- if($CONFIG_DBTYPE == 'pgsql') {
- $group_fct = 'array_agg(`tag`)';
- }
- else {
- $group_fct = 'GROUP_CONCAT(`tag`)';
- }
- $sql = "SELECT *, (select $group_fct from `*PREFIX*bookmarks_tags` where `bookmark_id` = `b`.`id`) as `tags`
- FROM `*PREFIX*bookmarks` `b`
- WHERE `user_id` = ? AND `id` = ?";
- $query = OCP\DB::prepare($sql);
- $result = $query->execute(array(OCP\USER::getUser(), $id))->fetchRow();
- $result['tags'] = explode(',', $result['tags']);
- return $result;
- }
-
- /**
- * @brief Finds all bookmarks, matching the filter
- * @param offset integer offset
- * @param sqlSortColumn string result with this column
- * @param filters can be: empty -> no filter, a string -> filter this, a string array -> filter for all strings
- * @param filterTagOnly boolean true, filter affects only tags, else filter affects url, title and tags
- * @param limit integer of item to return (default 10) if -1 or false then all item are returned
- * @return void
- */
- public static function findBookmarks($offset, $sqlSortColumn, $filters, $filterTagOnly, $limit = 10) {
- $CONFIG_DBTYPE = OCP\Config::getSystemValue( 'dbtype', 'sqlite' );
- if(is_string($filters)) $filters = array($filters);
- if(! in_array($sqlSortColumn, array('id', 'url', 'title', 'user_id',
- 'description', 'public', 'added', 'lastmodified','clickcount',))) {
- $sqlSortColumn = 'bookmarks_sorting_recent';
- }
- $params=array(OCP\USER::getUser());
-
- if($CONFIG_DBTYPE == 'pgsql') {
- $sql = "SELECT * FROM (SELECT *, (select array_to_string(array_agg(`tag`),',') from `*PREFIX*bookmarks_tags` where `bookmark_id` = `b2`.`id`) as `tags`
- FROM `*PREFIX*bookmarks` `b2`
- WHERE `user_id` = ? ) as `b` WHERE true ";
- }
- else {
- $sql = "SELECT *, (SELECT GROUP_CONCAT(`tag`) from `*PREFIX*bookmarks_tags` WHERE `bookmark_id` = `b`.`id`) as `tags`
- FROM `*PREFIX*bookmarks` `b`
- WHERE `user_id` = ? ";
- }
-
- if($filterTagOnly) {
- $exist_clause = " AND exists (SELECT `id` FROM `*PREFIX*bookmarks_tags`
- `t2` WHERE `t2`.`bookmark_id` = `b`.`id` AND `tag` = ?) ";
- $sql .= str_repeat($exist_clause, count($filters));
- $params = array_merge($params, $filters);
- } else {
- if($CONFIG_DBTYPE == 'mysql') { //Dirty hack to allow usage of alias in where
- $sql .= ' HAVING true ';
- }
- foreach($filters as $filter) {
- if($CONFIG_DBTYPE == 'mysql')
- $sql .= ' AND lower( concat(url,title,description,tags )) like ? ';
- else
- $sql .= ' AND lower(url || title || description || tags ) like ? ';
- $params[] = '%' . strtolower($filter) . '%';
- }
- }
-
- $sql .= " ORDER BY ".$sqlSortColumn." DESC ";
- if($limit == -1 || $limit === false) {
- $limit = null;
- $offset = null;
- }
-
- $query = OCP\DB::prepare($sql, $limit, $offset);
- $results = $query->execute($params)->fetchAll();
- $bookmarks = array();
- foreach($results as $result){
- $result['tags'] = explode(',', $result['tags']);
- $bookmarks[] = $result;
- }
- return $bookmarks;
- }
-
- public static function deleteUrl($id)
- {
- $user = OCP\USER::getUser();
-
- $query = OCP\DB::prepare("
- SELECT `id` FROM `*PREFIX*bookmarks`
- WHERE `id` = ?
- AND `user_id` = ?
- ");
-
- $result = $query->execute(array($id, $user));
- $id = $result->fetchOne();
- if ($id === false) {
- return false;
- }
-
- $query = OCP\DB::prepare("
- DELETE FROM `*PREFIX*bookmarks`
- WHERE `id` = ?
- ");
-
- $result = $query->execute(array($id));
-
- $query = OCP\DB::prepare("
- DELETE FROM `*PREFIX*bookmarks_tags`
- WHERE `bookmark_id` = ?
- ");
-
- $result = $query->execute(array($id));
- return true;
- }
-
- public static function renameTag($old, $new)
- {
- $user_id = OCP\USER::getUser();
- $CONFIG_DBTYPE = OCP\Config::getSystemValue( 'dbtype', 'sqlite' );
-
-
- if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ) {
- // Update tags to the new label unless it already exists a tag like this
- $query = OCP\DB::prepare("
- UPDATE OR REPLACE `*PREFIX*bookmarks_tags`
- SET `tag` = ?
- WHERE `tag` = ?
- AND exists( select `b`.`id` from `*PREFIX*bookmarks` `b` WHERE `b`.`user_id` = ? AND `bookmark_id` = `b`.`id`)
- ");
-
- $params=array(
- $new,
- $old,
- $user_id,
- );
-
- $result = $query->execute($params);
- } else {
-
- // Remove potentialy duplicated tags
- $query = OCP\DB::prepare("
- DELETE FROM `*PREFIX*bookmarks_tags` as `tgs` WHERE `tgs`.`tag` = ?
- AND exists( SELECT `id` FROM `*PREFIX*bookmarks` WHERE `user_id` = ? AND `tgs`.`bookmark_id` = `id`)
- AND exists( SELECT `t`.`tag` FROM `*PREFIX*bookmarks_tags` `t` where `t`.`tag` = ? AND `tgs`.`bookmark_id` = `t`.`bookmark_id`");
-
- $params=array(
- $new,
- $user_id,
- );
-
- $result = $query->execute($params);
-
-
- // Update tags to the new label unless it already exists a tag like this
- $query = OCP\DB::prepare("
- UPDATE `*PREFIX*bookmarks_tags`
- SET `tag` = ?
- WHERE `tag` = ?
- AND exists( SELECT `b`.`id` FROM `*PREFIX*bookmarks` `b` WHERE `b`.`user_id` = ? AND `bookmark_id` = `b`.`id`)
- ");
-
- $params=array(
- $new,
- $old,
- $user_id,
- $old,
- );
-
- $result = $query->execute($params);
- }
-
-
- return true;
- }
-
- public static function deleteTag($old)
- {
- $user_id = OCP\USER::getUser();
-
- // Update the record
- $query = OCP\DB::prepare("
- DELETE FROM `*PREFIX*bookmarks_tags`
- WHERE `tag` = ?
- AND exists( SELECT `id` FROM `*PREFIX*bookmarks` WHERE `user_id` = ? AND `bookmark_id` = `id`)
- ");
-
- $params=array(
- $old,
- $user_id,
- );
-
- $result = $query->execute($params);
- return $result;
- }
-
- /**
- * get a string corresponding to the current time depending
- * of the OC database system
- * @return string
- */
- protected static function getNowValue() {
- $CONFIG_DBTYPE = OCP\Config::getSystemValue( "dbtype", "sqlite" );
- if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ) {
- $_ut = "strftime('%s','now')";
- } elseif($CONFIG_DBTYPE == 'pgsql') {
- $_ut = 'date_part(\'epoch\',now())::integer';
- } else {
- $_ut = "UNIX_TIMESTAMP()";
- }
- return $_ut;
- }
-
- /**
- * Edit a bookmark
- * @param int $id The id of the bookmark to edit
- * @param string $url
- * @param string $title Name of the bookmark
- * @param array $tags Simple array of tags to qualify the bookmark (different tags are taken from values)
- * @param string $description A longer description about the bookmark
- * @param boolean $is_public True if the bookmark is publishable to not registered users
- * @return null
- */
- public static function editBookmark($id, $url, $title, $tags = array(), $description='', $is_public=false) {
-
- $is_public = $is_public ? 1 : 0;
- $user_id = OCP\USER::getUser();
-
- // Update the record
- $query = OCP\DB::prepare("
- UPDATE `*PREFIX*bookmarks` SET
- `url` = ?, `title` = ?, `public` = ?, `description` = ?,
- `lastmodified` = ".self::getNowValue() ."
- WHERE `id` = ?
- AND `user_id` = ?
- ");
-
- $params=array(
- htmlspecialchars_decode($url),
- htmlspecialchars_decode($title),
- $is_public,
- htmlspecialchars_decode($description),
- $id,
- $user_id,
- );
-
- $result = $query->execute($params);
-
- // Abort the operation if bookmark couldn't be set
- // (probably because the user is not allowed to edit this bookmark)
- if ($result == 0) exit();
-
-
- // Remove old tags
- $sql = "DELETE FROM `*PREFIX*bookmarks_tags` WHERE `bookmark_id` = ?";
- $query = OCP\DB::prepare($sql);
- $query->execute(array($id));
-
- // Add New Tags
- self::addTags($id, $tags);
- }
-
- /**
- * Add a bookmark
- * @param string $url
- * @param string $title Name of the bookmark
- * @param array $tags Simple array of tags to qualify the bookmark (different tags are taken from values)
- * @param string $description A longer description about the bookmark
- * @param boolean $is_public True if the bookmark is publishable to not registered users
- * @return int The id of the bookmark created
- */
- public static function addBookmark($url, $title, $tags=array(), $description='', $is_public=false) {
- $is_public = $is_public ? 1 : 0;
- $enc_url = htmlspecialchars_decode($url);
- $_ut = self::getNowValue();
- // Change lastmodified date if the record if already exists
- $sql = "SELECT * from `*PREFIX*bookmarks` WHERE `url` = ? AND `user_id` = ?";
- $query = OCP\DB::prepare($sql, 1);
- $result = $query->execute(array($enc_url, OCP\USER::getUser()));
- if ($row = $result->fetchRow()){
- $params = array();
- $title_str = '';
- if(trim($title) != '') { // Do we replace the old title
- $title_str = ' , title = ?';
- $params[] = $title;
- }
- $desc_str = '';
- if(trim($title) != '') { // Do we replace the old description
- $desc_str = ' , description = ?';
- $params[] = $description;
- }
- $sql = "UPDATE `*PREFIX*bookmarks` SET `lastmodified` = $_ut $title_str $desc_str WHERE `url` = ? and `user_id` = ?";
- $params[] = $enc_url;
- $params[] = OCP\USER::getUser();
- $query = OCP\DB::prepare($sql);
- $query->execute($params);
- return $row['id'];
- }
- $query = OCP\DB::prepare("
- INSERT INTO `*PREFIX*bookmarks`
- (`url`, `title`, `user_id`, `public`, `added`, `lastmodified`, `description`)
- VALUES (?, ?, ?, ?, $_ut, $_ut, ?)
- ");
-
- $params=array(
- $enc_url,
- htmlspecialchars_decode($title),
- OCP\USER::getUser(),
- $is_public,
- $description,
- );
- $query->execute($params);
-
- $b_id = OCP\DB::insertid('*PREFIX*bookmarks');
-
- if($b_id !== false) {
- self::addTags($b_id, $tags);
- return $b_id;
- }
- }
-
- /**
- * Add a set of tags for a bookmark
- *
- * @param int $bookmarkId The bookmark reference
- * @param array $tags Set of tags to add to the bookmark
- * @return null
- **/
- private static function addTags($bookmarkId, $tags) {
- $sql = 'INSERT INTO `*PREFIX*bookmarks_tags` (`bookmark_id`, `tag`) select ?, ? ';
- $dbtype = OCP\Config::getSystemValue( 'dbtype', 'sqlite' );
-
- if ($dbtype === 'mysql') {
- $sql .= 'from dual ';
- }
- $sql .= 'where not exists(select * from oc_bookmarks_tags where bookmark_id = ? and tag = ?)';
-
- $query = OCP\DB::prepare($sql);
- foreach ($tags as $tag) {
- $tag = trim($tag);
- if(empty($tag)) {
- //avoid saving white spaces
- continue;
- }
- $params = array($bookmarkId, $tag, $bookmarkId, $tag);
- $query->execute($params);
- }
- }
-
- /**
- * Simple function to search for bookmark. call findBookmarks
- * @param array $search_words Set of words to look for in bookmars fields
- * @return array An Array of bookmarks
- **/
- public static function searchBookmarks($search_words) {
- return self::findBookmarks(0, 'id', $search_words, false);
- }
-
- public static function importFile($file){
- libxml_use_internal_errors(true);
- $dom = new domDocument();
-
- $dom->loadHTMLFile($file);
- $links = $dom->getElementsByTagName('a');
-
- OCP\DB::beginTransaction();
- foreach($links as $link) {
- $title = $link->nodeValue;
- $ref = $link->getAttribute("href");
- $tag_str = '';
- if($link->hasAttribute("tags"))
- $tag_str = $link->getAttribute("tags");
- $tags = explode(',', $tag_str);
-
- $desc_str = '';
- if($link->hasAttribute("description"))
- $desc_str = $link->getAttribute("description");
-
- self::addBookmark($ref, $title, $tags,$desc_str );
- }
- OCP\DB::commit();
- return array();
- }
-
- public static function getURLMetadata($url) {
- //allow only http(s) and (s)ftp
- $protocols = '/^[hs]{0,1}[tf]{0,1}tp[s]{0,1}\:\/\//i';
- //if not (allowed) protocol is given, assume http
- if(preg_match($protocols, $url) == 0) {
- $url = 'http://' . $url;
- }
- $metadata['url'] = $url;
- $page = OC_Util::getUrlContent($url);
- if($page) {
- if(preg_match( "/(.*)<\/title>/sUi", $page, $match ) !== false)
- if(isset($match[1])) {
- $metadata['title'] = html_entity_decode($match[1], ENT_QUOTES , 'UTF-8');
- //Not the best solution but....
- $metadata['title'] = str_replace('™', chr(153), $metadata['title']);
- $metadata['title'] = str_replace('‐', '‐', $metadata['title']);
- $metadata['title'] = str_replace('–', '–', $metadata['title']);
- }
- }
- return $metadata;
- }
-
- public static function analyzeTagRequest($line) {
- $tags = explode(',', $line);
- $filterTag = array();
- foreach($tags as $tag){
- if(trim($tag) != '')
- $filterTag[] = trim($tag);
- }
- return $filterTag;
- }
-}
-
diff --git a/templates/addBm.php b/templates/addBm.php
deleted file mode 100644
index a9783997..00000000
--- a/templates/addBm.php
+++ /dev/null
@@ -1,46 +0,0 @@
-
\ No newline at end of file
diff --git a/templates/addBookmarklet.php b/templates/addBookmarklet.php
new file mode 100644
index 00000000..ce7c8992
--- /dev/null
+++ b/templates/addBookmarklet.php
@@ -0,0 +1,59 @@
+
+
+
+
\ No newline at end of file
diff --git a/templates/js_tpl.php b/templates/js_tpl.php
index ea2d94d9..ce083c04 100644
--- a/templates/js_tpl.php
+++ b/templates/js_tpl.php
@@ -1,64 +1,64 @@
diff --git a/templates/list.php b/templates/list.php
deleted file mode 100644
index e0b6c52a..00000000
--- a/templates/list.php
+++ /dev/null
@@ -1,66 +0,0 @@
-
- * Copyright (c) 2011 Arthur Schiwon
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-function bookmarklet(){
- $l = new OC_l10n('bookmarks');
- $blet = "javascript:(function(){var a=window,b=document,c=encodeURIComponent,e=c(document.title),d=a.open('";
- $blet .= OCP\Util::linkToAbsolute('bookmarks', 'addBm.php');
- $blet .= "?output=popup&url='+c(b.location)+'&title='+e,'bkmk_popup','left='+((a.screenX||a.screenLeft)+10)+',top='+((a.screenY||a.screenTop)+10)+',height=400px,width=550px,resizable=1,alwaysRaised=1');a.setTimeout(function(){d.focus()},300);})();";
- $help_msg = $l->t('Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:');
- return '
-
-
-
diff --git a/templates/main.php b/templates/main.php
new file mode 100644
index 00000000..4d810bd3
--- /dev/null
+++ b/templates/main.php
@@ -0,0 +1,82 @@
+
+ * Copyright (c) 2011 Arthur Schiwon
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+$bookmarkleturl = $_['bookmarkleturl'];
+$bookmarkletscript = bookmarklet($bookmarkleturl);
+
+function bookmarklet($bookmarkleturl) {
+ $l = new OC_l10n('bookmarks');
+ $blet = "javascript:(function(){var a=window,b=document,c=encodeURIComponent,e=c(document.title),d=a.open('";
+ $blet .= $bookmarkleturl;
+ $blet .= "?output=popup&url='+c(b.location)+'&title='+e,'bkmk_popup','left='+((a.screenX||a.screenLeft)+10)+',top='+((a.screenY||a.screenTop)+10)+',height=400px,width=550px,resizable=1,alwaysRaised=1');a.setTimeout(function(){d.focus()},300);})();";
+ $help_msg = $l->t('Drag this to your browser bookmarks and click it, when you want to bookmark a webpage quickly:');
+ $output = '