From ae81b9dcd2c61ba673aadcc866a364890e0394ad Mon Sep 17 00:00:00 2001 From: ganomi Date: Tue, 25 Nov 2014 22:43:48 +0100 Subject: [PATCH] This is a refactored / rewritten version of the bookmarks app using the app frameworks design and controller features. 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. There is a publicly available api that provides access to bookmarks per user. (This is usefull in connection with the WP Plugin https://github.com/mario-nolte/oc2wp-bookmarks) --- 3rdparty/js/js_tpl.js | 36 - 3rdparty/js/tag-it.js | 378 ---------- COPYING | 661 ++++++++++++++++++ README.md | 24 +- addBm.php | 69 -- ajax/delBookmark.php | 37 - ajax/delTag.php | 36 - ajax/editBookmark.php | 57 -- ajax/getInfos.php | 37 - ajax/import.php | 30 - ajax/recordClick.php | 39 -- ajax/renameTag.php | 41 -- ajax/updateList.php | 51 -- appinfo/app.php | 44 +- appinfo/application.php | 82 +++ appinfo/info.xml | 7 +- appinfo/routes.php | 46 ++ appinfo/version | 1 - controller/lib/bookmarks.php | 582 +++++++++++++++ controller/lib/exportresponse.php | 26 + controller/lib/helper.php | 16 + {lib => controller/lib}/search.php | 30 +- controller/rest/bookmarkcontroller.php | 254 +++++++ controller/rest/publiccontroller.php | 81 +++ controller/rest/tagscontroller.php | 66 ++ controller/webviewcontroller.php | 59 ++ .../css => css/3rdparty}/jquery.tagit.css | 0 css/bookmarks.css | 540 +++++++------- export.php | 54 -- img/loading.gif | Bin 0 -> 1849 bytes index.php | 42 -- js/3rdparty/js_tpl.js | 33 + js/3rdparty/tag-it.js | 365 ++++++++++ js/addBm.js | 85 --- js/bookmarklet.js | 55 ++ js/bookmarks.js | 361 +++++----- js/bookmarksearch.js | 23 - js/full_tags.php | 23 - js/settings.js | 37 +- l10n/da.php | 12 +- l10n/hr.php | 1 - lib/bookmarks.php | 467 ------------- templates/addBm.php | 46 -- templates/addBookmarklet.php | 59 ++ templates/js_tpl.php | 114 +-- templates/list.php | 66 -- templates/main.php | 82 +++ templates/settings.php | 42 +- tests/bootstrap.php | 4 +- tests/lib_bookmark_test.php | 114 ++- tests/phpunit.xml | 2 +- tests/publiccontroller_test.php | 37 + 52 files changed, 3222 insertions(+), 2232 deletions(-) delete mode 100644 3rdparty/js/js_tpl.js delete mode 100644 3rdparty/js/tag-it.js create mode 100644 COPYING delete mode 100644 addBm.php delete mode 100644 ajax/delBookmark.php delete mode 100644 ajax/delTag.php delete mode 100644 ajax/editBookmark.php delete mode 100644 ajax/getInfos.php delete mode 100644 ajax/import.php delete mode 100644 ajax/recordClick.php delete mode 100644 ajax/renameTag.php delete mode 100644 ajax/updateList.php create mode 100644 appinfo/application.php create mode 100644 appinfo/routes.php delete mode 100644 appinfo/version create mode 100644 controller/lib/bookmarks.php create mode 100644 controller/lib/exportresponse.php create mode 100644 controller/lib/helper.php rename {lib => controller/lib}/search.php (60%) create mode 100644 controller/rest/bookmarkcontroller.php create mode 100644 controller/rest/publiccontroller.php create mode 100644 controller/rest/tagscontroller.php create mode 100644 controller/webviewcontroller.php rename {3rdparty/css => css/3rdparty}/jquery.tagit.css (100%) delete mode 100644 export.php create mode 100644 img/loading.gif delete mode 100644 index.php create mode 100644 js/3rdparty/js_tpl.js create mode 100644 js/3rdparty/tag-it.js delete mode 100644 js/addBm.js create mode 100644 js/bookmarklet.js delete mode 100644 js/bookmarksearch.js delete mode 100644 js/full_tags.php delete mode 100644 lib/bookmarks.php delete mode 100644 templates/addBm.php create mode 100644 templates/addBookmarklet.php delete mode 100644 templates/list.php create mode 100644 templates/main.php create mode 100644 tests/publiccontroller_test.php 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 @@ Bookmarks Bookmark manager for ownCloud AGPL - Arthur Schiwon, Marvin Thomas Rabe + Arthur Schiwon, Marvin Thomas Rabe, Stefan Klemm - 4.93 + 4.93 true 166042 - + 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 @@ +<?php + +namespace OCA\Bookmarks\Controller\Lib; + +use OCP\AppFramework\Http\Response; + +class ExportResponse extends Response { + + private $returnstring; + + public function __construct($returnstring) { + $user_name = trim(\OCP\User::getDisplayName()) != '' ? + \OCP\User::getDisplayName() : \OCP\User::getUser(); + $export_name = '"ownCloud Bookmarks (' . $user_name . ') (' . date('Y-m-d') . ').html"'; + $this->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 @@ +<?php + +namespace OCA\Bookmarks\Controller\Lib; + +class Helper { + + static function getDomainWithoutExt($name) { + $pos = strripos($name, '.'); + if ($pos === false) { + return $name; + } else { + return substr($name, 0, $pos); + } + } + +} diff --git a/lib/search.php b/controller/lib/search.php similarity index 60% rename from lib/search.php rename to controller/lib/search.php index b19b3bd7..aaaf9193 100644 --- a/lib/search.php +++ b/controller/lib/search.php @@ -1,4 +1,5 @@ <?php + /** * ownCloud - bookmarks plugin * @@ -20,24 +21,31 @@ * */ -class OC_Search_Provider_Bookmarks extends OC_Search_Provider{ - function search($query) { - $l=OC_L10N::get('bookmarks'); - $results=array(); +namespace OCA\Bookmarks\Controller\Lib; - $search_words=array(); - if(substr_count($query, ' ') > 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 @@ +<?php + +/** + * ownCloud - bookmarks + * + * This file is licensed under the Affero General Public License version 3 or + * later. See the COPYING file. + * + * @author Stefan Klemm <mail@stefan-klemm.de> + * @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 = <<<EOT +<!DOCTYPE NETSCAPE-Bookmark-file-1> +<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"> +<!-- This is an automatically generated file. +It will be read and overwritten. +Do Not Edit! --> +<TITLE>Bookmarks +

    Bookmarks

    +

    +EOT; + $bookmarks = Bookmarks::findBookmarks($this->userId, $this->db, 0, 'id', array(), true, -1); + foreach ($bookmarks as $bm) { + $title = $bm['title']; + if (trim($title) === '') { + $url_parts = parse_url($bm['url']); + $title = isset($url_parts['host']) ? OCA\Bookmarks\Controller\Lib\Helper::getDomainWithoutExt($url_parts['host']) : $bm['url']; + } + $file .= '

    '; + $file .= htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . ''; + if ($bm['description']) + $file .= '
    ' . htmlspecialchars($bm['description'], ENT_QUOTES, 'UTF-8'); + $file .= "\n"; + } + + return new ExportResponse($file); + } + +} diff --git a/controller/rest/publiccontroller.php b/controller/rest/publiccontroller.php new file mode 100644 index 00000000..18f23e3c --- /dev/null +++ b/controller/rest/publiccontroller.php @@ -0,0 +1,81 @@ +db = $db; + $this->userManager = $userManager; + } + + /** + * @CORS + * @NoAdminRequired + * @NoCSRFRequired + * @PublicPage + */ + public function returnAsJson($user, $password = null, $tags = array(), $conjunction = "or", $select = null, $sortby = "") { + + if ($user == null || $this->userManager->userExists($user) == false) { + return $this->newJsonErrorMessage("User could not be identified"); + } + + if ($tags[0] == "") { + $tags = array(); + } + + $public = true; + + if ($password != null) { + $public = false; + } + + + if (!$public && !$this->userManager->checkPassword($user, $password)) { + + $msg = 'REST API accessed with wrong password'; + \OCP\Util::writeLog('bookmarks', $msg, \OCP\Util::WARN); + + return $this->newJsonErrorMessage("Wrong password for user " . $user); + } + + $attributesToSelect = array('url', 'title'); + + if ($select != null) { + $attributesToSelect = array_merge($attributesToSelect, $select); + $attributesToSelect = array_unique($attributesToSelect); + } + + $output = Bookmarks::findBookmarks($user, $this->db, 0, $sortby, $tags, true, -1, $public, $attributesToSelect, $conjunction); + + if (count($output) == 0) { + $output["status"] = 'error'; + $output["message"] = "No results from this query"; + return new JSONResponse($output); + } + + return new JSONResponse($output); + } + + public function newJsonErrorMessage($message) { + $output = array(); + $output["status"] = 'error'; + $output["message"] = $message; + return new JSONResponse($output); + } + +} diff --git a/controller/rest/tagscontroller.php b/controller/rest/tagscontroller.php new file mode 100644 index 00000000..6fd757be --- /dev/null +++ b/controller/rest/tagscontroller.php @@ -0,0 +1,66 @@ +userId = $userId; + $this->db = $db; + } + + /** + * @NoAdminRequired + */ + public function deleteTag($old_name = "") { + + if ($old_name == "") { + return new JSONResponse(array(), Http::STATUS_BAD_REQUEST); + } + + Bookmarks::deleteTag($this->userId, $this->db, $old_name); + return new JSONResponse(array('status' => 'success')); + } + + /** + * @NoAdminRequired + */ + public function renameTag($old_name = "", $new_name = "") { + + if ($old_name == "" || $new_name == "") { + return new JSONResponse(array(), Http::STATUS_BAD_REQUEST); + } + + Bookmarks::renameTag($this->userId, $this->db, $old_name, $new_name); + return new JSONResponse(array('status' => 'success')); + } + + /** + * @NoAdminRequired + */ + public function fullTags() { + + header("Cache-Control: no-cache, must-revalidate"); + header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); + + $qtags = Bookmarks::findTags($this->userId, $this->db, array(), 0, 400); + $tags = array(); + foreach ($qtags as $tag) { + $tags[] = $tag['tag']; + } + + return new JSONResponse($tags); + } + +} diff --git a/controller/webviewcontroller.php b/controller/webviewcontroller.php new file mode 100644 index 00000000..ba44d0d9 --- /dev/null +++ b/controller/webviewcontroller.php @@ -0,0 +1,59 @@ + + * @copyright Stefan Klemm 2014 + */ + +namespace OCA\Bookmarks\Controller; + +use \OCP\IRequest; +use \OCP\AppFramework\Http\TemplateResponse; +use \OCP\AppFramework\Controller; +use \OCP\IDb; +use \OCA\Bookmarks\Controller\Lib\Bookmarks; + +class WebViewController extends Controller { + + private $userId; + private $urlgenerator; + private $db; + + public function __construct($appName, IRequest $request, $userId, $urlgenerator, IDb $db) { + parent::__construct($appName, $request); + $this->userId = $userId; + $this->urlgenerator = $urlgenerator; + $this->db = $db; + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function index() { + $bookmarkleturl = $this->urlgenerator->getAbsoluteURL('index.php/apps/bookmarks/bookmarklet'); + $params = array('user' => $this->userId, 'bookmarkleturl' => $bookmarkleturl); + return new TemplateResponse('bookmarks', 'main', $params); + } + + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function bookmarklet($url = "", $title = "") { + $bookmarkExists = Bookmarks::bookmarkExists($url, $this->userId, $this->db); + $description = ""; + if ($bookmarkExists != false){ + $bookmark = Bookmarks::findUniqueBookmark($bookmarkExists, $this->userId, $this->db); + $description = $bookmark['description']; + } + $params = array('url' => $url, 'title' => $title, 'description' => $description, 'bookmarkExists' => $bookmarkExists); + return new TemplateResponse('bookmarks', 'addBookmarklet', $params); // templates/main.php + } + +} diff --git a/3rdparty/css/jquery.tagit.css b/css/3rdparty/jquery.tagit.css similarity index 100% rename from 3rdparty/css/jquery.tagit.css rename to css/3rdparty/jquery.tagit.css diff --git a/css/bookmarks.css b/css/bookmarks.css index bb833924..0f75b98e 100644 --- a/css/bookmarks.css +++ b/css/bookmarks.css @@ -1,341 +1,322 @@ -/* LEGACY STUFF - remove on switch to app-navigation and app-content */ -#leftcontent, .leftcontent { - position:relative; overflow:auto; width:256px; height:100%; - background:#f8f8f8; border-right:1px solid #ddd; - -moz-box-sizing:border-box; box-sizing:border-box; -} -#leftcontent li, .leftcontent li { background:#f8f8f8; padding:.5em .8em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-transition:background-color 200ms; -moz-transition:background-color 200ms; -o-transition:background-color 200ms; transition:background-color 200ms; } -#leftcontent li:hover, #leftcontent li:active, #leftcontent li.active, .leftcontent li:hover, .leftcontent li:active, .leftcontent li.active { background:#eee; } -#leftcontent li.active, .leftcontent li.active { font-weight:bold; } -#leftcontent li:hover, .leftcontent li:hover { color:#333; background:#ddd; } -#leftcontent a { height:100%; display:block; margin:0; padding:0 1em 0 0; float:left; } -#rightcontent, .rightcontent { position:fixed; top:89px; left: 336px; overflow:auto } - -#controls + .leftcontent{ - top: 44px; -} - - - - - -#content { overflow: auto; height: 100%; } - -#firstrun .bkm_hint { display: block; font-weight: normal; /*font-size: 0.5em; */ } -#firstrun .title { font-weight:bold; } - -#distance { - width:1px; - height:50%; - margin-bottom:-13.75em; /* half of container's height */ - float:left; -} - -#firstrun_message { - margin:0 auto; - position:relative; /* puts container in front of distance */ - text-align:left; - height:27.5em; - width:45em; - clear:left; -} - -#firstrun_setting { font-size: 100%; text-decoration: underline} - input.disabled, input.disabled:hover, input.disabled:focus { - cursor: not-allowed; - background-color: #ddd; - color: gray; + cursor: not-allowed; + background-color: #ddd; + color: gray; } #settingsbtn { background: transparent; color:#666; - border-bottom: 1px solid #ddd; border-top: 1px solid #fff; } + border-bottom: 1px solid #ddd; border-top: 1px solid #fff; } #settingsbtn:hover { background:#dbdbdb !important; color:#999; - border-bottom: 1px solid #CCCCCC; border-top: 1px solid #D4D4D4; } -#settingsbtn img { width: 18px; height: 18px; margin: 10px; } + border-bottom: 1px solid #CCCCCC; border-top: 1px solid #D4D4D4; } +#settingsbtn img { width: 18px; height: 18px; margin: 10px; } -#rightcontent { - padding-top: 5px; - top: 1em !important; - overflow: visible; -} .bookmarks_headline { - font-size: large; - font-weight: bold; - margin-left: 2em; - padding: 2.5ex 0.5ex; + font-size: large; + font-weight: bold; + margin-left: 2em; + padding: 2.5ex 0.5ex; } .bookmarks_menu { - margin-left: 1.5em; - padding: 0.5ex; + margin-left: 1.5em; + padding: 0.5ex; } .bookmarks_list, #firstrun { - overflow: auto; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding-top: 3em; -/* width: 100%; */ - height: 100%; - position: fixed; - left: 336px; - right:0; + overflow: auto; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 2em; + /* width: 100%; */ + height: 100%; + /* position: fixed; */ + left: 336px; + right:0; } #add_form { - padding-bottom: 2em; + padding-bottom: 2em; +} + +#add_form_loading { + visibility: hidden; + float: right; + margin: 8px; } #add_form input { - margin: 0.3em; + margin: 0.3em; } #add_url { - width: 12em; + width: 91.5%; } .bookmarks_addBml { - text-decoration: underline; + text-decoration: underline; } .bookmarks_label { - width: 7em; - display: inline-block; - text-align: right; + width: 7em; + display: inline-block; + text-align: right; } .bookmarks_input { - width: 8em; + width: 8em; } .bookmark_actions { - position: absolute; - right: 1em; - bottom:0.8em; - display: none; + position: absolute; + right: 1em; + top: 12px; + display: none; } .bookmark_actions span { margin: 0 0.4em; } -.bookmark_actions img { opacity: 0.3; } +.bookmark_actions img { opacity: 0.3;} .bookmark_actions img:hover { opacity: 1; cursor: pointer; } .bookmark_edit img {opacity:0.3;} .bookmark_edit img:hover {cursor: pointer; opacity: 1;} .bookmark_single { - position: relative; - padding: 0.2em 1em; - min-height: 3em; - border-bottom: 1px solid #DDD; - -webkit-transition:background-color 500ms; -moz-transition:background-color 500ms; -o-transition:background-color 500ms; transition:background-color 500ms; + position: relative; + padding: 0.2em 1em; + min-height: 3em; + border-bottom: 1px solid #DDD; + -webkit-transition:background-color 500ms; -moz-transition:background-color 500ms; -o-transition:background-color 500ms; transition:background-color 500ms; } .bookmark_single:hover { - background-color:#f8f8f8; + background-color:#f8f8f8; } .bookmark_single:hover .bookmark_actions { - display: block; + display: block; } .bookmark_title { - font-weight: bold; - display: block; - margin-right: 0.8em; - margin-top:0.7em; + font-weight: bold; + display: block; + margin-right: 0.8em; + margin-top:0.7em; } .bookmark_title a { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - display: inline-block; - max-width: 24%; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + display: inline-block; + max-width: 22%; } .bookmark_single:hover .bookmark_title .bookmark_edit_btn { - display:inline-block; + display:inline-block; } .bm_view_list .bookmark_tags { - position: absolute; - top: 0.5em; - right: 6em; - text-align: right; + position: absolute; + top: 0.5em; + right: 6em; + text-align: right; } .bookmark_tags { - left:25%; - position: relative; + left:25%; + position: relative; + width: 75%; } .bookmark_tag { - display: inline-block; - color: white; - margin: 0 0.2em; - padding: 0 0.4em; - background-color: #1D2D44; - border-radius: 0.4em; - opacity: 0.2; + display: inline-block; + color: white; + margin: 0.2em 0.2em; + padding: 0 0.4em; + background-color: #1D2D44; + border-radius: 0.4em; + opacity: 0.2; } .bookmark_tag:hover { opacity: 0.5; } .loading_meta { - display: none; - margin-left: 5px; + display: none; + margin-left: 5px; } .addBm { - background: none repeat scroll 0 0 #F8F8F8; - border-radius: 0.5em 0.5em 0.5em 0.5em; - color: #555555; - margin: 1em; - padding: 0.5em 1em; - text-shadow: 0 1px 0 #FFFFFF; - width: 500px; - + background: none repeat scroll 0 0 #F8F8F8; + border-radius: 0.5em 0.5em 0.5em 0.5em; + color: #555555; + margin: 1em; + padding: 0.5em 1em; + text-shadow: 0 1px 0 #FFFFFF; + width: 500px; + } .bm_desc { - width:90%; + width:90%; } .addBm h1 { - font-weight: bold; - border-bottom: 1px solid #BABABA; + font-weight: bold; + border-bottom: 1px solid #BABABA; } .addBm fieldset > ul > li { - margin-top: 1em; + margin-top: 1em; } .addBm label { - display:block; - width:100%; + display:block; + width:100%; } .addBm .is_public_label { - display: inline; - + display: inline; + } .addBm fieldset input[type="text"], .addBm textarea { - width:100%; + width:100%; } .addBm textarea{ - min-width:250px; + min-width:250px; } .addBm .close_btn { - height: 18px; - margin: -20px 0 0; - padding: 1px; - float:right; - width: 19px; + height: 18px; + margin: -20px 0 0; + padding: 1px; + float:right; + width: 19px; } .addBm .submit { - float: right; + float: right; } .addBm ul.tagit { background:white; } .addBm input.ui-autocomplete-input, #tag_filter input.ui-autocomplete-input, .bookmark_form_tags input.ui-autocomplete-input{ - box-shadow:none; + box-shadow:none; } -#leftcontent > ul > li, .leftcontent li { - padding: 0.2em; - padding-left:1em; -} -#leftcontent > label { - margin-top: 1em; - margin-bottom: 0.7em; - display: block; - padding-left: 1em; -} - -#leftcontent .tag_list > li .tag , #leftcontent .share_list > li .tag{ - background: none repeat scroll 0 0 #DEE7F8; - border: 1px solid #CAD8F3; - border-radius: 6px 6px 6px 6px; - color: #555555; - font-weight: normal; - float:left; - padding: 0.3em; - position: relative; -} - ul.tagit li.tagit-new { - padding-left: 0em; + padding-left: 0em; +} + +#tag_select_label{ + display: block; + text-align: center; + margin-top: 10px; + cursor: default; +} + +li.tag_list { + padding-left: 3px; + padding-right: 3px; + overflow: hidden; +} + +li.tag_list a { + display: inline !important; + padding: 5px 11px 5px 11px !important; + border: 1px solid transparent; +} + +li.tag_list li:hover > a { + background: none repeat scroll 0 0 #dee7f8 !important; + border: 1px solid #cad8f3; + border-radius: 6px; } .tag_list em , .share_list em{ - float:right; - display:block; + float:right; + display:block; } -.tags_actions { - display:none; - margin-left: 0.5em; - float:right; -} -li:hover .tags_actions { - display:block; -} -li:hover em { display : none; } -#tag_filter li, #tag_filter li:hover{ - background: none repeat scroll 0 0 #F8F8F8; +.tags_actions { + margin-left: 0.5em; + display: inline; + line-height: 44px; +} + +.tags_actions > span { + display:none; + float:right; +} +li:hover > .tags_actions > span { + display:inline; +} +li:hover > .tags_actions > em { display : none; } + +#tag_filter li, #tag_filter li:hover { + background: none repeat scroll 0 0 #F8F8F8; } #tag_filter .tagit { - margin: 0.3em; + margin: 0.3em; } #tag_filter ul.tagit > li.tagit-new { - padding:0; + padding:0; } #tag_filter ul.tagit li.tagit-choice { - background: none repeat scroll 0 0 #DEE7F8; - padding: 0.2em 18px 0.2em 0.5em; + background: none repeat scroll 0 0 #DEE7F8; + padding: 0.2em 18px 0.2em 0.5em; + width: auto; } #tag_filter a { - display: inline; - float: none; - margin: 0; - padding: 0; + display: inline; + float: none; + margin: 0; + padding: 0; } + +#tag_filter ul li:hover > a { + background-color: transparent; +} + #tag_filter ul.tagit li.tagit-choice .close{ - margin-top: -8px; - cursor: pointer; margin-top: -8px; - position: absolute; - right: 0.1em; - top: 50%; + cursor: pointer; + margin-top: -8px; + right: -5px; + top: 50%; + padding-left: 0px !important; +} + +#tag_filter ul.tagit li.tagit-choice .close span{ + text-align: right; +} + +ul.tagit li.tagit-choice .close .text-icon { + display: block !important; } .bookmark_desc{ - height: 1.5em; - padding-left:1em; - white-space:nowrap; - text-overflow:ellipsis; - overflow:hidden; - display:block; - float:left; - position: absolute; - left: 25%; - width:40%; - top: 0.7em; - font-weight:500; - z-index: -10; + height: 1.5em; + padding-left:1em; + white-space:nowrap; + text-overflow:ellipsis; + overflow:hidden; + display:block; + float:left; + position: absolute; + left: 25%; + width:40%; + top: 0.7em; + font-weight:500; + /* z-index: -10; */ } .bookmark_date, .bookmark_submit { - font-size:small; - position: absolute; - right: 6em; - color:gray; -/* margin-top:2.5em; */ - margin-right: 0.2em; - top:0.8em; + font-size:small; + position: absolute; + right: 6em; + color:gray; + /* margin-top:2.5em; */ + margin-right: 0.2em; + top:0.8em; } /* .bm_view_img .bookmark_single:hover .bookmark_url{ - display:block; + display:block; } .bm_view_img .bookmark_single .bookmark_url{ - display:block; + display:block; } */ .bookmark_single:hover .bookmark_url { display: inline; } @@ -343,137 +324,112 @@ li:hover em { display : none; } .bookmark_url { display: none; } .bookmark_form_title { - float: left; - width:25%; + float: left; + width:25%; } .bookmark_form_url { - float:left; - width:25%; + float:left; + width:25%; } .bookmark_form_tags { - width:30%; - float:left; + width:30%; + float:left; } .bookmark_form_desc { - width:50%; + width:50%; + padding-top:100px; } .bookmark_form_desc textarea { - margin-top:-3.4em; + margin-top:-3.4em; } .bookmark_form_title input, .bookmark_form_url input { - width: 89%; + width: 89%; } .bookmark_form_desc textarea { - width: 94.5%; + width: 94.5%; } .bookmark_single .bookmark_edit_btn { - visibility:hidden; - width: 16px; + visibility:hidden; + width: 16px; } .bookmark_single:hover .bookmark_edit_btn { - visibility: visible; + visibility: visible; } .bookmark_single:focus .bookmark_edit_btn { - visibility: visible; - opacity:1; + visibility: visible; + opacity:1; } .bookmark_single_form { - padding: 20px 8px; - border-bottom: 1px solid #DDD; + padding: 20px 8px; + border-bottom: 1px solid #DDD; } .bookmark_single_form .tagit{ - width: 80%; - box-shadow: 0 1px 1px #FFFFFF, 0 1px 0 #BBBBBB inset; - margin: 3px; - height:6.4em; + /* width: 80%; */ + box-shadow: 0 1px 1px #FFFFFF, 0 1px 0 #BBBBBB inset; + /* margin: 3px; */ + /* height:6.4em; */ } .bookmark_form_submit { - margin-left: 4px; + margin-left: 4px; } .bookmark_form_submit .reset { - float: right; + float: right; } .bm_view_img .bookmark_actions { - bottom: 0.7em; - display: block; - position: absolute; - right: 1em; - top: auto; + bottom: 0.7em; + display: block; + position: absolute; + right: 1em; + top: auto; } -#bookmark_settings { - -moz-box-sizing: border-box; - background: none repeat scroll 0 0 #EEEEEE; - border-right: 1px solid #CCCCCC; - border-top: 1px solid #CCCCCC; - bottom: 0; - height: 2.8em; - margin: 0; - overflow: visible; - padding: 0; - position: fixed; - width: 20em; - z-index: 2; -} - -#leftcontent #bookmark_settings li{ - padding: 0; - background-color:transparent; -} -#leftcontent #bookmark_settings li:hover{ - background-color:transparent; -} - -#bookmark_settings .controls { - height: 2.8em; - width: 100%; -} - -#bookmark_settings .controls > li:last-child button { - margin-right: 0.3em; -} -#bookmark_settings .controls button { - height: 2.4em; - margin: 0.15em 0 0 0.15em; - padding: 0.2em 0.1em 0; - width: 2.4em; -} - -#bookmark_settings.open { - height: auto; -} #bm_setting_panel { - background-color: #eee; - padding: 1em; - height: 100%; -/* display:none; */ + background-color: #eee; + padding: 1em; + height: 100%; + /* display:none; */ } /* .open #bm_setting_panel { display:block; } */ #bm_setting_panel legend{ - margin-top: 0.5em; + margin-top: 0.5em; +} + +#bookmarklet_hint { + margin-bottom: 6px; } #bm_import { - opacity: 0; - position: absolute; + opacity: 0; + position: absolute; + width: 90px; } + +#bm_import:hover ~ button#bm_import_submit { + background-color: rgba(250, 250, 250, 0.9); + color: #333; +} + +#import_bookmark { + margin-top: 10px; +} + #import_bookmark .personalblock { - margin-top: 1em; -} -#leftcontent a.bookmarklet { - margin-top: 5px; - padding: 0 5px 2px; + margin-top: 1em; } +#app-navigation a.bookmarklet { + margin-top: 5px; + padding: 0 5px 2px; +} \ No newline at end of file diff --git a/export.php b/export.php deleted file mode 100644 index 3878995b..00000000 --- a/export.php +++ /dev/null @@ -1,54 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -// Check if we are a user -OCP\User::checkLoggedIn(); -OCP\App::checkAppEnabled('bookmarks'); - -function getDomainWithoutExt($name){ - $pos = strripos($name, '.'); - if($pos === false){ - return $name; - }else{ - return substr($name, 0, $pos); - } -} - -$file = << - - -Bookmarks -

    Bookmarks

    -

    -EOT; -$bookmarks = OC_Bookmarks_Bookmarks::findBookmarks(0, 'id', array(), true, -1); -foreach($bookmarks as $bm) { - $title = $bm['title']; - if(trim($title) ===''){ - $url_parts = parse_url($bm['url']); - $title = isset($url_parts['host']) ? getDomainWithoutExt($url_parts['host']) : $bm['url']; - } - $file .= '

    '; - $file .= htmlspecialchars($title, ENT_QUOTES, 'UTF-8').''; - if($bm['description']) - $file .= '
    '.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 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4 GIT binary patch literal 1849 zcma*odr(tX9tZI2z31lM+(&YVk%mZ}5P~KlG2s=WSbGzm0!x7^P##Mnh7t-jP!X0Q zk_SQ}Po-L1tlDK;6l?(>v)e5ZBQx4|Y-Q?nr@Px3?9h(3ZWr3^tj=`TP57gKr87N$ zp2wWee1GRRCwo_xahnw)5cxNPJbCg2L6DV|6`#+yw6v6!mDS$f9-JvFD^n;GQ&UrZ zzh5jCkByB101O60U0q#p_1BM>Cv-vP?&s4@g_((4_1L=L$(a91)0=J91Gas#R{McE znYG^9*0A5YZ>#;~+Wkn(W5B0^yELIYLP!K}mB~<)AM@1&nqekynuaEGqPrzoH|KodRXJy)%+w_fu3nE5>@Bd_b zqC$EQ;{c`T&?EsNO|igL9gC7Ygxv?aQUEXMq?~>wg{EyW;VcJ37CUF#HjrT=KQO_* zS>M9yydXk18D(+QDJ1>r);Lav_uYKp$T?4vr{Q$lTo&pKv^?(>L-)G2*lwH!Ah7k? z7oH<8h-(KTKt5V6$8gF)C7Io&P5=SjTh)=zV=E2EUhQZP##L8S{d%UK>>+y82>+FV+#^BzW7u3F)Bb>=lYQ%%j`F>ASe zo*cw@V#u6T`A2He;70mR(V&iV&-7{qP~=SRf&jm9-T{*ZeZ}$rd0#6c&fLG^xJcf5 z+p<`wJYgW+_s*V{uI$nMB;%8`S_3>PfGOj3Rq}@Cx^+j?rk92fANSFDBYnOqQ>Vdj z)(|$AhP4t&Lb=Gvo2#3Gl%9<=Gv`Mz?Po@P4iLF!x}GUWJICDlFk-hS^Whyh7x~VH z@0vD1>HYD4&e+~yzS*-sFR{9`{QEEZO1zg7>R&7cHts-6j!xHVdA8eI+ZlVzd%`es zJT@$#GX(gvCJ1oJN%yLBK}{V=V;seo;!w|Yte!W1%5qLNFWqvZW>h&IiH+oPT=b@E zPhGzv5=(Un*X>v`>%8h_nj^NdYcE6NHS_ifkCV$*D)Tqrbu`s;<=t<4 zAHNqNV?6(g<1PY-w@#I-WYFViz?9TrkMr)u0g`O`u|>T;k|2sV*YF^punvT;$SuTy{j3Gv)yqD!R_CF>yR)MzmmYS5v+~R zXAdD%ng9?df;wd8GxR#%3O+gz};Vo;)sK%Bj-q>Oq%R7JU-KD?vYu>#2UjaDo z&8$>5xW~?KPD_#XFToU1hIb*VOMidUr6iYiO0N|i-7s`T8!cFT`rN!^1Pt78J93i6 z5HI1wIM$94m{3SLDvISDe6$ZG1;eq_D9RTaaC>=cO{@Bs>$IlPCPJJ$h$)-3vzNUQ6OsN#_zWxey!_9%hxwH2_dEJi=yY|1c7nDm2_Lm!Cof8-R_+9UkS zcBE(o47yE)oMR(Q=dp1a2wTX5KvvGyLqlWTa7V&!A*|w|)ax~1_~aJ0=_Lilg*0iQk7#ZD EAHN$8j{pDw literal 0 HcmV?d00001 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 @@ -<form class="addBm" method="post" action="<?php print_unescaped(OCP\Util::linkTo('bookmarks', 'ajax/editBookmark.php'));?>"> - <?php if(!isset($embedded) || !$embedded):?> - <script type="text/javascript" src="<?php print_unescaped(OC_Helper::linkTo('bookmarks/js', 'full_tags.php'));?>"></script> - - <h1><?php p($l->t('Add a bookmark'));?></h1> - <div class="close_btn"> - <a href="javascript:self.close()" class="ui-icon ui-icon-closethick"> - <?php p($l->t('Close'));?> - </a> - </div> - <?php endif;?> - <fieldset class="bm_desc"> - <ul> - <li> - <input type="text" name="title" class="title" value="<?php p($_['bookmark']['title']); ?>" - placeholder="<?php p($l->t('The title of the page'));?>" /> - </li> - - <li> - <input type="text" name="url" class="url_input" value="<?php p($_['bookmark']['url']); ?>" - placeholder="<?php p($l->t('The address of the page'));?>" /> - </li> - - <li> - <ul class="tags" > - <?php foreach($_['bookmark']['tags'] as $tag):?> - <li><?php p($tag);?></li> - <?php endforeach;?> - </ul> - </li> - - <li> - <textarea name="description" class="desc" value="<?php p($_['bookmark']['desc']); ?>" - placeholder="<?php p($l->t('Description of the page'));?>"></textarea> - </li> - - <li> - <input type="submit" class="submit" value="<?php p($l->t("Save"));?>" /> - <input type="hidden" class="record_id" value="" name="record_id" /> - <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>"> - </li> - - </ul> - - </fieldset> -</form> \ 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 @@ +<?php +OCP\Util::addscript('bookmarks', 'bookmarklet'); +OCP\Util::addStyle('bookmarks', 'bookmarks'); + +$bookmarkExists = $_['bookmarkExists']; +?> +<div id="bookmarklet_form"> + <form class="addBm" action=""> + <script type="text/javascript" src="tag"></script> + + <h1 style="display: block; float: left"><?php p($l->t('Add a bookmark')); ?></h1> + <span style="display: inline; float: right"><div id="add_form_loading" style="margin: 3px;"><img src="<?php print_unescaped(OCP\image_path("bookmarks", "loading.gif")); ?>"> </div></span> + + <div style="color: red; clear: both; visibility: <?php + if ($bookmarkExists == false) { + print_unescaped('hidden'); + } + ?>"> + <strong> + <?php p($l->t('This URL is already bookmarked! Overwrite?')); ?> + </strong> + </div> + + <fieldset class="bm_desc"> + <ul> + <li> + <input id="title" type="text" name="title" class="title" value="<?php p($_['title']); ?>" + placeholder="<?php p($l->t('The title of the page')); ?>" /> + </li> + + <li> + <input id="url" type="text" name="url" class="url_input" value="<?php p($_['url']); ?>" + placeholder="<?php p($l->t('The address of the page')); ?>" /> + </li> + + <li> + <ul class="tags" > + <?php foreach ($_['bookmark']['tags'] as $tag): ?> + <li><?php p($tag); ?></li> + <?php endforeach; ?> + </ul> + </li> + + <li> + <textarea id="description" name="description" class="desc" + placeholder="<?php p($l->t('Description of the page')); ?>"><?php p($_['description']); ?></textarea> + </li> + + <li> + <input type="submit" class="submit" value="<?php p($l->t("Save")); ?>" /> + <input type="hidden" class="record_id" value="" name="record_id" /> + <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>"> + </li> + + </ul> + + </fieldset> + </form> +</div> \ 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 @@ <script type="text/html" id="item_tmpl"> - <div class="bookmark_single" data-id="<&= id &>"> - <p class="bookmark_actions"> - <span class="bookmark_delete"> - <img class="svg" src="<?php print_unescaped(OCP\image_path("", "actions/delete.svg"));?>" - title="<?php p($l->t('Delete'));?>"> - </span>  - </p> - <p class="bookmark_title"> - <a href="<&= checkURL(encodeURI(url)) &>" target="_blank" class="bookmark_link" rel="noreferrer"> - <&= escapeHTML(title == '' ? encodeURI(url) : title ) &> - </a> - <span class="bookmark_edit bookmark_edit_btn"> - <img class="svg" src="<?php print_unescaped(OCP\image_path("", "actions/rename.svg"));?>" title="<?php p($l->t('Edit'));?>"> - </span> - </p> - <span class="bookmark_desc"><&= escapeHTML(description)&> </span> - <span class="bookmark_date"><&= formatDate(added_date) &></span> - </div> + <div class="bookmark_single" data-id="<&= id &>"> + <p class="bookmark_actions"> + <span class="bookmark_delete"> + <img class="svg" src="<?php print_unescaped(OCP\image_path("", "actions/delete.svg")); ?>" + title="<?php p($l->t('Delete')); ?>"> + </span>  + </p> + <p class="bookmark_title"> + <a href="<&= checkURL(encodeURI(url)) &>" target="_blank" class="bookmark_link" rel="noreferrer"> + <&= escapeHTML(title == '' ? encodeURI(url) : title ) &> + </a> + <span class="bookmark_edit bookmark_edit_btn"> + <img class="svg" src="<?php print_unescaped(OCP\image_path("", "actions/rename.svg")); ?>" title="<?php p($l->t('Edit')); ?>"> + </span> + </p> + <span class="bookmark_desc"><&= escapeHTML(description)&> </span> + <span class="bookmark_date"><&= formatDate(added_date) &></span> + </div> </script> <script type="text/html" id="item_form_tmpl"> - <div class="bookmark_single_form" data-id="<&= id &>"> - <form method="post" action="<?php p(OCP\Util::linkTo('bookmarks', 'ajax/editBookmark.php'));?>" > - <input type="hidden" name="record_id" value="<&= id &>" /> - <p class="bookmark_form_title"> - <input type="text" name="title" placeholder="<?php p($l->t('The title of the page'));?>" - value="<&= escapeHTML(title) &>"/> - </p> - <p class="bookmark_form_url"> - <input type="text" name="url" placeholder="<?php p($l->t('The address of the page'));?>" - value="<&= encodeURI(url)&>"/> - </p> - <div class="bookmark_form_tags"><ul> - <& for ( var i = 0; i < tags.length; i++ ) { &> - <li><&= escapeHTML(tags[i]) &></li> - <& } &> - </ul></div> - <p class="bookmark_form_desc"> - <textarea name="description" placeholder="<?php p($l->t('Description of the page'));?>" - ><&= escapeHTML(description) &></textarea> - </p> - <p class="bookmark_form_submit"> - <button class="reset" ><?php p($l->t('Cancel'));?></button> - <input type="submit" class="primary" value="<?php p($l->t('Save'));?>"> - </p> - </form> - </div> + <div class="bookmark_single_form" data-id="<&= id &>"> + <form method="post" action="bookmark" > + <input type="hidden" name="record_id" value="<&= id &>" /> + <p class="bookmark_form_title"> + <input type="text" name="title" placeholder="<?php p($l->t('The title of the page')); ?>" + value="<&= escapeHTML(title) &>"/> + </p> + <p class="bookmark_form_url"> + <input type="text" name="url" placeholder="<?php p($l->t('The address of the page')); ?>" + value="<&= encodeURI(url)&>"/> + </p> + <div class="bookmark_form_tags"><ul> + <& for ( var i = 0; i < tags.length; i++ ) { &> + <li><&= escapeHTML(tags[i]) &></li> + <& } &> + </ul></div> + <p class="bookmark_form_desc"> + <textarea name="description" placeholder="<?php p($l->t('Description of the page')); ?>" + ><&= escapeHTML(description) &></textarea> + </p> + <p class="bookmark_form_submit"> + <button class="reset" ><?php p($l->t('Cancel')); ?></button> + <input type="submit" class="primary" value="<?php p($l->t('Save')); ?>"> + </p> + </form> + </div> </script> <script type="text/html" id="tag_tmpl"> - <li><a href="" class="tag"><&= escapeHTML(tag) &></a> - <p class="tags_actions"> - <span class="tag_edit"> - <img class="svg" src="<?php print_unescaped(OCP\image_path("", "actions/rename.svg"));?>" - title="<?php p($l->t('Edit'));?>"> - </span> - <span class="tag_delete"> - <img class="svg" src="<?php print_unescaped(OCP\image_path("", "actions/delete.svg"));?>" - title="<?php p($l->t('Delete'));?>"> - </span> - </p> - <em><&= nbr &></em> - </li> + <li><a href="" class="tag"><&= escapeHTML(tag) &></a> + <div class="tags_actions"> + <span class="tag_delete"> + <img class="svg" src="<?php print_unescaped(OCP\image_path("", "actions/delete.svg")); ?>" + title="<?php p($l->t('Delete')); ?>"> + </span> + <span class="tag_edit"> + <img class="svg" src="<?php print_unescaped(OCP\image_path("", "actions/rename.svg")); ?>" + title="<?php p($l->t('Edit')); ?>"> + </span> + <em><&= nbr &></em> + </div> + </li> </script> 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 @@ -<?php -/** - * Copyright (c) 2011 Marvin Thomas Rabe <mrabe@marvinrabe.de> - * Copyright (c) 2011 Arthur Schiwon <blizzz@arthur-schiwon.de> - * 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 '<div class="bkm_hint">'.$help_msg.'</div><br /><a class="button bookmarklet" href="' . $blet . '">' . $l->t('Add to ownCloud') . '</a>'; -} -?> - -<div id="leftcontent"> - - <form id="add_form"> - <input type="text" id="add_url" value="" placeholder="<?php p($l->t('Address')); ?>"/> - <input type="submit" value="<?php p($l->t('Add')); ?>" id="bookmark_add_submit" /> - </form> - - <p id="tag_filter"> - <input type="text" value="<?php p($_['req_tag']); ?>"/> - </p> - <input type="hidden" id="bookmarkFilterTag" value="<?php p($_['req_tag']); ?>" /> - - <label><?php p($l->t('Related Tags')); ?></label> - <ul class="tag_list"> - </ul> - - -<div id="bookmark_settings" class=""> - <ul class="controls"> - <li id="settingsbtn" title="<?php p($l->t('Settings')); ?>"> - <img class="svg" src="<?php print_unescaped(OCP\Util::imagePath('core', 'actions/settings.svg')); ?>" - alt="<?php p($l->t('Settings')); ?>" /> - </li> - </ul> - <div id="bm_setting_panel"> - <?php require 'settings.php';?> - </div> -</div> - -</div> -<div id="rightcontent" class="rightcontent"> - <div id="firstrun" style="display: none;"> - <div id="distance"></div> - <div id="firstrun_message"> - <p class="title"><?php - p($l->t('You have no bookmarks')); - $embedded = true; - - print_unescaped(bookmarklet());?></p><br/><br /> - - <div class="bkm_hint"><a href="#" id="firstrun_setting"><?php p($l->t('You can also try to import a bookmark file'));?></a></div> - </div> - </div> - <div class="bookmarks_list"></div> -</div> -<script type="text/javascript" src="<?php print_unescaped(OC_Helper::linkTo('bookmarks/js', 'full_tags.php'));?>"></script> - -<?php require 'js_tpl.php';?> 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 @@ +<?php +script('bookmarks', 'settings'); +script('bookmarks', 'bookmarks'); +style('bookmarks', 'bookmarks'); + +script('bookmarks', '3rdparty/tag-it'); +script('bookmarks', '3rdparty/js_tpl'); +style('bookmarks', '3rdparty/jquery.tagit'); + +/** + * Copyright (c) 2011 Marvin Thomas Rabe <mrabe@marvinrabe.de> + * Copyright (c) 2011 Arthur Schiwon <blizzz@arthur-schiwon.de> + * 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 = '<div id="bookmarklet_hint" class="bkm_hint">' . $help_msg . '</div><a class="button bookmarklet" href="' . $blet . '">' . $l->t('Add to ownCloud') . '</a>'; + return $output; +} +?> + +<div id="app-navigation"> + <ul id="navigation-list"> + <li> + <form id="add_form"> + <input type="text" id="add_url" value="" placeholder="<?php p($l->t('Address')); ?>"/> + <input type="submit" value="<?php p($l->t('Add')); ?>" id="bookmark_add_submit" /> + <div id="add_form_loading"><img src="<?php print_unescaped(OCP\image_path("bookmarks", "loading.gif")); ?>"> </div> + </form> + <p id="tag_filter" class="open"> + <input type="text" value="<?php p($_['req_tag']); ?>"/> + + + </p> + <input type="hidden" id="bookmarkFilterTag" value="<?php p($_['req_tag']); ?>" /> + <label id="tag_select_label"><?php p($l->t('Filterable Tags')); ?></label> + </li> + <li class="tag_list"> + </li> + </ul> + + <div id="app-settings"> + <div id="app-settings-header"> + <button class="settings-button generalsettings" data-apps-slide-toggle="#app-settings-content" tabindex="0"></button> + </div> + <div id="app-settings-content"> + + + <?php require 'settings.php'; ?> + </div> + </div> + +</div> +<div id="app-content"> + <div id="emptycontent" style="display: none;"> + <p class="title"><?php + p($l->t('You have no bookmarks')); + $embedded = true; + print_unescaped($bookmarkletscript); + ?></p> + <br/><br/> + + + <div class="bkm_hint"> + <a href="#" id="firstrun_setting"> + <?php p($l->t('You can also import a bookmark file')); ?> + </a></div> + </div> + <div class="bookmarks_list"></div> +</div> + +<?php +require 'js_tpl.php'; diff --git a/templates/settings.php b/templates/settings.php index 8d036398..51adcfcf 100644 --- a/templates/settings.php +++ b/templates/settings.php @@ -8,27 +8,29 @@ /** @var array $_ */ ?> -<fieldset class="personalblock"> - <legend><strong><?php p($l->t('Bookmarklet'));?></strong></legend> - <?php print_unescaped(bookmarklet());?><br /> -</fieldset> +<ul> + <li><strong><?php p($l->t('Bookmarklet')); ?></strong></li> + <?php print_unescaped($bookmarkletscript); ?><br /> +</ul> +<form id="import_bookmark" action="bookmark/import" method="post" enctype="multipart/form-data"> + <ul> + <li> + </li> + <li> + <?php if (isset($_['error'])): ?> + <h3><?php p($_['error']['error']); ?></h3> + <p><?php p($_['error']['hint']); ?></p> + <?php endif; ?> -<form id="import_bookmark" action="<?php print_unescaped(OCP\Util::linkTo( "bookmarks", "ajax/import.php" ));?>" - method="post" enctype="multipart/form-data"> - <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>"/> - <fieldset class="personalblock"> - <?php if(isset($_['error'])): ?> - <h3><?php p($_['error']['error']); ?></h3> - <p><?php p($_['error']['hint']); ?></p> - <?php endif; ?> - - <legend><strong><?php p($l->t('Export & Import'));?></strong></legend> - <input type="button" id="bm_export" href="<?php print_unescaped(OCP\Util::linkTo('bookmarks', 'export.php')) ;?>" value="<?php p($l->t('Export')); ?>" /> - <input type="file" id="bm_import" name="bm_import" size="5"> - <button type="button" name="bm_import_btn" id="bm_import_submit"><?php p($l->t('Import')); ?></button> - <div id="upload"></div> + <legend><strong><?php p($l->t('Export & Import')); ?></strong></legend> + <input type="button" id="bm_export" href="bookmark/export?requesttoken=<?php p($_['requesttoken']) ?>" value="<?php p($l->t('Export')); ?>" /> + <input type="file" id="bm_import" name="bm_import" size="5"> + <input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>" id="requesttoken"> + <button type="button" name="bm_import_btn" id="bm_import_submit"><?php p($l->t('Import')); ?></button> + <div id="upload"></div> - - </fieldset> + + </li> + </ul> </form> diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 57bf2fa0..2ab592a1 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -5,9 +5,9 @@ $RUNTIME_NOAPPS = true; define('PHPUNIT_RUN', 1); -require_once __DIR__.'/../../../lib/base.php'; +require_once __DIR__ . '/../../../lib/base.php'; -if(!class_exists('PHPUnit_Framework_TestCase')) { +if (!class_exists('PHPUnit_Framework_TestCase')) { require_once('PHPUnit/Autoload.php'); } diff --git a/tests/lib_bookmark_test.php b/tests/lib_bookmark_test.php index dcd8d08b..b85bd183 100644 --- a/tests/lib_bookmark_test.php +++ b/tests/lib_bookmark_test.php @@ -1,28 +1,104 @@ <?php OC_App::loadApp('bookmarks'); + +use OCA\Bookmarks\Controller\Lib\Bookmarks; + class Test_LibBookmarks_Bookmarks extends PHPUnit_Framework_TestCase { - function testAddBM() { - $this->assertCount(0, OC_Bookmarks_Bookmarks::findBookmarks(0, 'id', array(), true, -1)); - OC_Bookmarks_Bookmarks::addBookmark( - 'http://owncloud.org', 'Owncloud project', array('oc', 'cloud'), 'An Awesome project'); - $this->assertCount(1, OC_Bookmarks_Bookmarks::findBookmarks(0, 'id', array(), true, -1)); - } + private $userid; + private $db; - function testFindTags() { -// $uid=uniqid(); - $this->assertEquals(OC_Bookmarks_Bookmarks::findTags(), array()); + protected function setUp() { + $this->userid = \OCP\User::getUser(); + $this->db = \OC::$server->getDb(); + } - OC_Bookmarks_Bookmarks::addBookmark( - 'http://owncloud.org', 'Owncloud project', array('oc', 'cloud'), 'An Awesome project'); - $this->assertEquals(array(0=>array('tag' => 'cloud', 'nbr'=>1), 1=>array('tag' => 'oc', 'nbr'=>1)), - OC_Bookmarks_Bookmarks::findTags()); - } + function testAddBookmark() { + $this->cleanDB(); + $this->assertCount(0, Bookmarks::findBookmarks($this->userid, $this->db, 0, 'id', array(), true, -1)); + Bookmarks::addBookmark($this->userid, $this->db, 'http://owncloud.org', 'Owncloud project', array('oc', 'cloud'), 'An Awesome project'); + $this->assertCount(1, Bookmarks::findBookmarks($this->userid, $this->db, 0, 'id', array(), true, -1)); + } - protected function tearDown() { - $query = OC_DB::prepare('DELETE FROM *PREFIX*bookmarks WHERE `user_id` = \'\' '); - $query->execute(); - } + function testFindBookmarks() { + $this->cleanDB(); + Bookmarks::addBookmark($this->userid, $this->db, "http://www.google.de", "Google", array("one"), "PrivateNoTag", false); + Bookmarks::addBookmark($this->userid, $this->db, "http://www.heise.de", "Heise", array("one", "two"), "PrivatTag", false); + Bookmarks::addBookmark($this->userid, $this->db, "http://www.golem.de", "Golem", array("one"), "PublicNoTag", true); + Bookmarks::addBookmark($this->userid, $this->db, "http://www.9gag.com", "9gag", array("two", "three"), "PublicTag", true); + $outputPrivate = Bookmarks::findBookmarks($this->userid, $this->db, 0, "", array(), true, -1, false); + $this->assertCount(4, $outputPrivate); + $outputPrivateFiltered = Bookmarks::findBookmarks($this->userid, $this->db, 0, "", array("one"), true, -1, false); + $this->assertCount(3, $outputPrivateFiltered); + $outputPublic = Bookmarks::findBookmarks($this->userid, $this->db, 0, "", array(), true, -1, true); + $this->assertCount(2, $outputPublic); + $outputPublicFiltered = Bookmarks::findBookmarks($this->userid, $this->db, 0, "", array("two"), true, -1, true); + $this->assertCount(1, $outputPublicFiltered); + } -} \ No newline at end of file + function testFindBookmarksSelectAndOrFilteredTags() { + $this->cleanDB(); + $secondUser = $this->userid . "andHisClone435"; + Bookmarks::addBookmark($this->userid, $this->db, "http://www.google.de", "Google", array("one"), "PrivateNoTag", false); + Bookmarks::addBookmark($this->userid, $this->db, "http://www.heise.de", "Heise", array("one", "two"), "PrivatTag", false); + Bookmarks::addBookmark($this->userid, $this->db, "http://www.golem.de", "Golem", array("four"), "PublicNoTag", true); + Bookmarks::addBookmark($this->userid, $this->db, "http://www.9gag.com", "9gag", array("two", "three"), "PublicTag", true); + Bookmarks::addBookmark($secondUser, $this->db, "http://www.google.de", "Google", array("one"), "PrivateNoTag", false); + Bookmarks::addBookmark($secondUser, $this->db, "http://www.heise.de", "Heise", array("one", "two"), "PrivatTag", false); + Bookmarks::addBookmark($secondUser, $this->db, "http://www.golem.de", "Golem", array("four"), "PublicNoTag", true); + Bookmarks::addBookmark($secondUser, $this->db, "http://www.9gag.com", "9gag", array("two", "three"), "PublicTag", true); + $resultSetOne = Bookmarks::findBookmarks($this->userid, $this->db, 0, 'lastmodified', array('one', 'three'), true, -1, false, array('url', 'title'), 'or'); + $this->assertEquals(3, count($resultSetOne)); + $resultOne = $resultSetOne[0]; + $this->assertFalse(isset($resultOne['lastmodified'])); + $this->assertFalse(isset($resultOne['tags'])); + } + + function testFindTags() { + $this->cleanDB(); + $this->assertEquals(Bookmarks::findTags($this->userid, $this->db), array()); + Bookmarks::addBookmark($this->userid, $this->db, 'http://owncloud.org', 'Owncloud project', array('oc', 'cloud'), 'An Awesome project'); + $this->assertEquals(array(0 => array('tag' => 'cloud', 'nbr' => 1), 1 => array('tag' => 'oc', 'nbr' => 1)), Bookmarks::findTags($this->userid, $this->db)); + } + + function testFindUniqueBookmark() { + $this->cleanDB(); + $id = Bookmarks::addBookmark($this->userid, $this->db, "http://www.heise.de", "Heise", array("one", "two"), "PrivatTag", false); + $bookmark = Bookmarks::findUniqueBookmark($id, $this->userid, $this->db); + $this->assertEquals($id, $bookmark['id']); + $this->assertEquals("Heise", $bookmark['title']); + } + + function testEditBookmark() { + $this->cleanDB(); + $id = Bookmarks::addBookmark($this->userid, $this->db, "http://www.heise.de", "Heise", array("one", "two"), "PrivatTag", false); + Bookmarks::editBookmark($this->userid, $this->db, $id, "http://www.google.de", "NewTitle", array("three")); + $bookmark = Bookmarks::findUniqueBookmark($id, $this->userid, $this->db); + $this->assertEquals("NewTitle", $bookmark['title']); + $this->assertEquals("http://www.google.de", $bookmark['url']); + $this->assertEquals(1, count($bookmark['tags'])); + } + + function testDeleteBookmark() { + $this->cleanDB(); + Bookmarks::addBookmark($this->userid, $this->db, "http://www.google.de", "Google", array("one"), "PrivateNoTag", false); + $id = Bookmarks::addBookmark($this->userid, $this->db, "http://www.heise.de", "Heise", array("one", "two"), "PrivatTag", false); + $this->assertNotEquals(false, Bookmarks::bookmarkExists("http://www.google.de", $this->userid, $this->db)); + $this->assertNotEquals(false, Bookmarks::bookmarkExists("http://www.heise.de", $this->userid, $this->db)); + Bookmarks::deleteUrl($this->userid, $this->db, $id); + $this->assertFalse(Bookmarks::bookmarkExists("http://www.heise.de", $this->userid, $this->db)); + } + + protected function tearDown() { + $this->cleanDB(); + } + + function cleanDB() { + $query1 = OC_DB::prepare('DELETE FROM *PREFIX*bookmarks'); + $query1->execute(); + $query2 = OC_DB::prepare('DELETE FROM *PREFIX*bookmarks_tags'); + $query2->execute(); + } + +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 22d2b0d9..8a5fcc88 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -5,7 +5,7 @@ timeoutForSmallTests="900" timeoutForMediumTests="900" timeoutForLargeTests="900" - > +> <testsuite name='ownCloud - Bookmarks App Tests'> <directory suffix='test.php'>.</directory> </testsuite> diff --git a/tests/publiccontroller_test.php b/tests/publiccontroller_test.php new file mode 100644 index 00000000..d49be2c3 --- /dev/null +++ b/tests/publiccontroller_test.php @@ -0,0 +1,37 @@ +<?php + +OC_App::loadApp('bookmarks'); + +use \OCA\Bookmarks\Controller\Rest\PublicController; + +class Test_PublicController_Bookmarks extends PHPUnit_Framework_TestCase { + + private $userid; + private $request; + private $db; + private $userManager; + private $publicController; + + protected function setUp() { + $this->userid = "testuser"; + $this->request = \OC::$server->getRequest(); + $this->db = \OC::$server->getDb(); + $this->userManager = \OC::$server->getUserManager(); + $this->publicController = new PublicController("bookmarks", $this->request, $this->db, $this->userManager); + } + + function testPublicQueryNoUser() { + $output = $this->publicController->returnAsJson(null, "apassword", null); + $data = $output->getData(); + $status = $data['status']; + $this->assertEquals($status, 'error'); + } + + function testPublicQueryWrongUser() { + $output = $this->publicController->returnAsJson("cqc43dr4rx3x4xatr4", "apassword", null); + $data = $output->getData(); + $status = $data['status']; + $this->assertEquals($status, 'error'); + } + +}