mirror of
https://github.com/owncloud/bookmarks.git
synced 2024-11-28 04:24:09 +01:00
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)
This commit is contained in:
parent
df24129839
commit
ae81b9dcd2
36
3rdparty/js/js_tpl.js
vendored
36
3rdparty/js/js_tpl.js
vendored
@ -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;
|
||||
};
|
||||
})();
|
378
3rdparty/js/tag-it.js
vendored
378
3rdparty/js/tag-it.js
vendored
@ -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 = $('<ul></ul>').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 = $('<input type="text">').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($('<li class="tagit-new"></li>').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('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '">');
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ? '<a class="tagit-label"></a>' : '<span class="tagit-label"></span>').text(value);
|
||||
|
||||
// Create tag.
|
||||
var tag = $('<li></li>')
|
||||
.addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
|
||||
.addClass(additionalClass)
|
||||
.append(label);
|
||||
|
||||
// Button for removing the tag.
|
||||
var removeTagIcon = $('<span></span>')
|
||||
.addClass('ui-icon ui-icon-close');
|
||||
var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \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('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.itemName + '[' + this.options.fieldName + '][]">');
|
||||
}
|
||||
|
||||
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);
|
661
COPYING
Normal file
661
COPYING
Normal file
@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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
|
||||
<http://www.gnu.org/licenses/>.
|
24
README.md
24
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.
|
69
addBm.php
69
addBm.php
@ -1,69 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Arthur Schiwon
|
||||
* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// 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();
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Arthur Schiwon
|
||||
* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// 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();
|
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Arthur Schiwon
|
||||
* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// 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();
|
@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin - edit bookmark script
|
||||
*
|
||||
* @author Golnaz Nilieh
|
||||
* @copyright 2011 Golnaz Nilieh <golnaz.nilieh@gmail.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// 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();
|
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Brice Maron
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// 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();
|
@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Check if we are a user
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::callCheck();
|
||||
OCP\JSON::checkAppEnabled('bookmarks');
|
||||
|
||||
$l = new OC_l10n('bookmarks');
|
||||
if(empty($_FILES)) {
|
||||
OCP\Util::writeLog('bookmarks',"No file provided for import", \OCP\Util::WARN);
|
||||
$error[]= $l->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();
|
@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Arthur Schiwon
|
||||
* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// 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" );
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Arthur Schiwon
|
||||
* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
//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();
|
@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Arthur Schiwon
|
||||
* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
|
||||
* @copyright 2012 David Iwanowitsch <david at unclouded dot de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// 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));
|
||||
|
||||
}
|
@ -1,22 +1,34 @@
|
||||
<?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.
|
||||
*/
|
||||
* ownCloud - bookmarks
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later. See the COPYING file.
|
||||
* @author Marvin Thomas Rabe <mrabe@marvinrabe.de>
|
||||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
* @author Stefan Klemm <mail@stefan-klemm.de>
|
||||
* @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');
|
||||
|
82
appinfo/application.php
Normal file
82
appinfo/application.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks
|
||||
*
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later. See the COPYING file.
|
||||
* @author Marvin Thomas Rabe <mrabe@marvinrabe.de>
|
||||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
* @author Stefan Klemm <mail@stefan-klemm.de>
|
||||
* @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();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -4,9 +4,10 @@
|
||||
<name>Bookmarks</name>
|
||||
<description>Bookmark manager for ownCloud</description>
|
||||
<licence>AGPL</licence>
|
||||
<author>Arthur Schiwon, Marvin Thomas Rabe</author>
|
||||
<author>Arthur Schiwon, Marvin Thomas Rabe, Stefan Klemm</author>
|
||||
<standalone/>
|
||||
<require>4.93</require>
|
||||
<requiremin>4.93</requiremin>
|
||||
<shipped>true</shipped>
|
||||
<ocsid>166042</ocsid>
|
||||
</info>
|
||||
<version>0.4</version>
|
||||
</info>
|
46
appinfo/routes.php
Normal file
46
appinfo/routes.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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 (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'),
|
||||
)));
|
@ -1 +0,0 @@
|
||||
0.4
|
582
controller/lib/bookmarks.php
Normal file
582
controller/lib/bookmarks.php
Normal file
@ -0,0 +1,582 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Arthur Schiwon
|
||||
* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* 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>(.*)<\/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;
|
||||
}
|
||||
|
||||
}
|
26
controller/lib/exportresponse.php
Normal file
26
controller/lib/exportresponse.php
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
16
controller/lib/helper.php
Normal file
16
controller/lib/helper.php
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
254
controller/rest/bookmarkcontroller.php
Normal file
254
controller/rest/bookmarkcontroller.php
Normal file
@ -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</TITLE>
|
||||
<H1>Bookmarks</H1>
|
||||
<DL><p>
|
||||
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 .= '<DT><A HREF="' . $bm['url'] . '" TAGS="' . implode(',', $bm['tags']) . '">';
|
||||
$file .= htmlspecialchars($title, ENT_QUOTES, 'UTF-8') . '</A>';
|
||||
if ($bm['description'])
|
||||
$file .= '<DD>' . htmlspecialchars($bm['description'], ENT_QUOTES, 'UTF-8');
|
||||
$file .= "\n";
|
||||
}
|
||||
|
||||
return new ExportResponse($file);
|
||||
}
|
||||
|
||||
}
|
81
controller/rest/publiccontroller.php
Normal file
81
controller/rest/publiccontroller.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace OCA\Bookmarks\Controller\Rest;
|
||||
|
||||
use \OCP\AppFramework\ApiController;
|
||||
use \OCP\IRequest;
|
||||
use \OCP\IDb;
|
||||
use \OCP\AppFramework\Http\JSONResponse;
|
||||
use \OC\User\Manager;
|
||||
use OCA\Bookmarks\Controller\Lib\Bookmarks;
|
||||
|
||||
class PublicController extends ApiController {
|
||||
|
||||
private $db;
|
||||
private $userManager;
|
||||
|
||||
public function __construct($appName, IRequest $request, IDb $db, Manager $userManager) {
|
||||
parent::__construct(
|
||||
$appName, $request);
|
||||
|
||||
$this->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);
|
||||
}
|
||||
|
||||
}
|
66
controller/rest/tagscontroller.php
Normal file
66
controller/rest/tagscontroller.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace OCA\Bookmarks\Controller\Rest;
|
||||
|
||||
use \OCA\Bookmarks\Controller\Lib\Bookmarks;
|
||||
use \OCP\AppFramework\Http\JSONResponse;
|
||||
use \OCP\AppFramework\Http;
|
||||
use \OCP\AppFramework\ApiController;
|
||||
use \OCP\IRequest;
|
||||
use \OCP\IDb;
|
||||
|
||||
class TagsController 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
}
|
59
controller/webviewcontroller.php
Normal file
59
controller/webviewcontroller.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?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;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
54
export.php
54
export.php
@ -1,54 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2012 Brice Maron < brice __At__ bmaron dot net >
|
||||
* 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 = <<<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</TITLE>
|
||||
<H1>Bookmarks</H1>
|
||||
<DL><p>
|
||||
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 .= '<DT><A HREF="'.$bm['url'].'" TAGS="'.implode(',', $bm['tags']).'">';
|
||||
$file .= htmlspecialchars($title, ENT_QUOTES, 'UTF-8').'</A>';
|
||||
if($bm['description'])
|
||||
$file .= '<DD>'.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;
|
BIN
img/loading.gif
Normal file
BIN
img/loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
42
index.php
42
index.php
@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Arthur Schiwon
|
||||
* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// 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();
|
33
js/3rdparty/js_tpl.js
vendored
Normal file
33
js/3rdparty/js_tpl.js
vendored
Normal file
@ -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;
|
||||
};
|
||||
})();
|
365
js/3rdparty/tag-it.js
vendored
Normal file
365
js/3rdparty/tag-it.js
vendored
Normal file
@ -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 = $('<ul></ul>').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 = $('<input type="text">').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($('<li class="tagit-new"></li>').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('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '">');
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ? '<a class="tagit-label"></a>' : '<span class="tagit-label"></span>').text(value);
|
||||
|
||||
// Create tag.
|
||||
var tag = $('<li></li>')
|
||||
.addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
|
||||
.addClass(additionalClass)
|
||||
.append(label);
|
||||
|
||||
// Button for removing the tag.
|
||||
var removeTagIcon = $('<span></span>')
|
||||
.addClass('ui-icon ui-icon-close');
|
||||
var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \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('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.itemName + '[' + this.options.fieldName + '][]">');
|
||||
}
|
||||
|
||||
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);
|
85
js/addBm.js
85
js/addBm.js
@ -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<record.tags.length;i++) {
|
||||
tagit_elem.tagit('createTag', record.tags[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
base.init();
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
$.bookmark_dialog.defaultOptions = {
|
||||
on_success: function(){},
|
||||
bookmark_record: undefined
|
||||
};
|
||||
|
||||
$.fn.bookmark_dialog = function(options){
|
||||
return this.each(function(){
|
||||
(new $.bookmark_dialog(this, options));
|
||||
});
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
|
||||
$(document).ready(function() {
|
||||
$('body').bookmark_dialog({
|
||||
'on_success': function(){
|
||||
self.close();
|
||||
}
|
||||
});
|
||||
});
|
55
js/bookmarklet.js
Normal file
55
js/bookmarklet.js
Normal file
@ -0,0 +1,55 @@
|
||||
var ajaxCallCount = 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 () {
|
||||
$(".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();
|
||||
}
|
361
js/bookmarks.js
361
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<len; ++i ){
|
||||
if(tags[i] != '')
|
||||
for (var i = 0, len = tags.length; i < len; ++i) {
|
||||
if (tags[i] !== '')
|
||||
taglist = taglist + '<a class="bookmark_tag" href="#">' + escapeHTML(tags[i]) + '</a> ';
|
||||
}
|
||||
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('<p class="bookmark_tags">' + taglist + '</p>');
|
||||
}
|
||||
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('<form><input name="tag_new_name" type="text"></form>');
|
||||
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('<form><input name="tag_new_name" type="text"></form>');
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,23 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2012 David Iwanowitsch <david at unclouded dot de>
|
||||
* 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')),
|
||||
});
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2013 Lukas Reschke <lukas@statuscode.ch>
|
||||
* 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 = <?php echo json_encode($tags);?>;
|
@ -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 = $("<ul></ul>").addClass('setting_error_list');
|
||||
if (data.status == 'error') {
|
||||
var list = $("<ul></ul>").addClass('setting_error_list');
|
||||
console.log(data);
|
||||
$.each(data.data,function(index, item){
|
||||
list.append($( "<li></li>" ).text(item));
|
||||
$.each(data.data, function (index, item) {
|
||||
list.append($("<li></li>").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 = $('<iframe></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...'));
|
||||
}
|
12
l10n/da.php
12
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);";
|
||||
|
@ -1,7 +1,6 @@
|
||||
<?php
|
||||
$TRANSLATIONS = array(
|
||||
"Bookmarks" => "Zabilješke",
|
||||
"Warning" => "Upozorenje",
|
||||
"Close" => "Zatvori",
|
||||
"Save" => "Snimi",
|
||||
"Delete" => "Obriši",
|
||||
|
@ -1,467 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - bookmarks plugin
|
||||
*
|
||||
* @author Arthur Schiwon
|
||||
* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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>(.*)<\/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;
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
59
templates/addBookmarklet.php
Normal file
59
templates/addBookmarklet.php
Normal file
@ -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>
|
@ -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>
|
||||
|
@ -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';?>
|
82
templates/main.php
Normal file
82
templates/main.php
Normal file
@ -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';
|
@ -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>
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
timeoutForSmallTests="900"
|
||||
timeoutForMediumTests="900"
|
||||
timeoutForLargeTests="900"
|
||||
>
|
||||
>
|
||||
<testsuite name='ownCloud - Bookmarks App Tests'>
|
||||
<directory suffix='test.php'>.</directory>
|
||||
</testsuite>
|
||||
|
37
tests/publiccontroller_test.php
Normal file
37
tests/publiccontroller_test.php
Normal file
@ -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');
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user