Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117750801
kolab_notes.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
50 KB
Referenced Files
None
Subscribers
None
kolab_notes.php
View Options
<?php
/**
* Kolab notes module
*
* Adds simple notes management features to the web client
*
* @version @package_version@
* @author Thomas Bruederli <bruederli@kolabsys.com>
*
* Copyright (C) 2014-2015, Kolab Systems AG <contact@kolabsys.com>
*
* 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/>.
*/
class
kolab_notes
extends
rcube_plugin
{
public
$task
=
'?(?!login|logout).*'
;
public
$allowed_prefs
=
array
(
'kolab_notes_sort_col'
);
public
$rc
;
private
$ui
;
private
$lists
;
private
$folders
;
private
$cache
=
array
();
private
$message_notes
=
array
();
private
$bonnie_api
=
false
;
/**
* Required startup method of a Roundcube plugin
*/
public
function
init
()
{
$this
->
require_plugin
(
'libkolab'
);
$this
->
rc
=
rcube
::
get_instance
();
// proceed initialization in startup hook
$this
->
add_hook
(
'startup'
,
array
(
$this
,
'startup'
));
}
/**
* Startup hook
*/
public
function
startup
(
$args
)
{
// the notes module can be enabled/disabled by the kolab_auth plugin
if
(
$this
->
rc
->
config
->
get
(
'kolab_notes_disabled'
,
false
)
||
!
$this
->
rc
->
config
->
get
(
'kolab_notes_enabled'
,
true
))
{
return
;
}
$this
->
register_task
(
'notes'
);
// load plugin configuration
$this
->
load_config
();
// load localizations
$this
->
add_texts
(
'localization/'
,
$args
[
'task'
]
==
'notes'
&&
(!
$args
[
'action'
]
||
$args
[
'action'
]
==
'dialog-ui'
));
$this
->
rc
->
load_language
(
$_SESSION
[
'language'
],
array
(
'notes.notes'
=>
$this
->
gettext
(
'navtitle'
)));
// add label for task title
if
(
$args
[
'task'
]
==
'notes'
)
{
$this
->
add_hook
(
'storage_init'
,
array
(
$this
,
'storage_init'
));
// register task actions
$this
->
register_action
(
'index'
,
array
(
$this
,
'notes_view'
));
$this
->
register_action
(
'fetch'
,
array
(
$this
,
'notes_fetch'
));
$this
->
register_action
(
'get'
,
array
(
$this
,
'note_record'
));
$this
->
register_action
(
'action'
,
array
(
$this
,
'note_action'
));
$this
->
register_action
(
'list'
,
array
(
$this
,
'list_action'
));
$this
->
register_action
(
'dialog-ui'
,
array
(
$this
,
'dialog_view'
));
$this
->
register_action
(
'print'
,
array
(
$this
,
'print_note'
));
if
(!
$this
->
rc
->
output
->
ajax_call
&&
in_array
(
$args
[
'action'
],
array
(
'dialog-ui'
,
'list'
)))
{
$this
->
load_ui
();
}
}
else
if
(
$args
[
'task'
]
==
'mail'
)
{
$this
->
add_hook
(
'storage_init'
,
array
(
$this
,
'storage_init'
));
$this
->
add_hook
(
'message_compose'
,
array
(
$this
,
'mail_message_compose'
));
if
(
in_array
(
$args
[
'action'
],
array
(
'show'
,
'preview'
,
'print'
)))
{
$this
->
add_hook
(
'message_load'
,
array
(
$this
,
'mail_message_load'
));
$this
->
add_hook
(
'template_object_messagebody'
,
array
(
$this
,
'mail_messagebody_html'
));
}
// add 'Append note' item to message menu
if
(
$this
->
api
->
output
->
type
==
'html'
&&
$_REQUEST
[
'_rel'
]
!=
'note'
)
{
$this
->
api
->
add_content
(
html
::
tag
(
'li'
,
array
(
'role'
=>
'menuitem'
),
$this
->
api
->
output
->
button
(
array
(
'command'
=>
'append-kolab-note'
,
'label'
=>
'kolab_notes.appendnote'
,
'type'
=>
'link'
,
'classact'
=>
'icon appendnote active'
,
'class'
=>
'icon appendnote disabled'
,
'innerclass'
=>
'icon note'
,
))),
'messagemenu'
);
$this
->
api
->
output
->
add_label
(
'kolab_notes.appendnote'
,
'kolab_notes.editnote'
,
'kolab_notes.deletenotesconfirm'
,
'kolab_notes.entertitle'
,
'save'
,
'delete'
,
'cancel'
,
'close'
);
$this
->
include_script
(
'notes_mail.js'
);
}
}
if
(!
$this
->
rc
->
output
->
ajax_call
&&
!
$this
->
rc
->
output
->
env
[
'framed'
])
{
$this
->
load_ui
();
}
// get configuration for the Bonnie API
$this
->
bonnie_api
=
libkolab
::
get_bonnie_api
();
// notes use fully encoded identifiers
kolab_storage
::
$encode_ids
=
true
;
}
/**
* Hook into IMAP FETCH HEADER.FIELDS command and request MESSAGE-ID
*/
public
function
storage_init
(
$p
)
{
$p
[
'fetch_headers'
]
=
trim
(
$p
[
'fetch_headers'
]
.
' MESSAGE-ID'
);
return
$p
;
}
/**
* Load and initialize UI class
*/
private
function
load_ui
()
{
if
(!
$this
->
ui
)
{
require_once
(
$this
->
home
.
'/kolab_notes_ui.php'
);
$this
->
ui
=
new
kolab_notes_ui
(
$this
);
$this
->
ui
->
init
();
}
}
/**
* Read available calendars for the current user and store them internally
*/
private
function
_read_lists
(
$force
=
false
)
{
// already read sources
if
(
isset
(
$this
->
lists
)
&&
!
$force
)
return
$this
->
lists
;
// get all folders that have type "task"
$folders
=
kolab_storage
::
sort_folders
(
kolab_storage
::
get_folders
(
'note'
));
$this
->
lists
=
$this
->
folders
=
array
();
// find default folder
$default_index
=
0
;
foreach
(
$folders
as
$i
=>
$folder
)
{
if
(
$folder
->
default
)
$default_index
=
$i
;
}
// put default folder on top of the list
if
(
$default_index
>
0
)
{
$default_folder
=
$folders
[
$default_index
];
unset
(
$folders
[
$default_index
]);
array_unshift
(
$folders
,
$default_folder
);
}
foreach
(
$folders
as
$folder
)
{
$item
=
$this
->
folder_props
(
$folder
);
$this
->
lists
[
$item
[
'id'
]]
=
$item
;
$this
->
folders
[
$item
[
'id'
]]
=
$folder
;
$this
->
folders
[
$folder
->
name
]
=
$folder
;
}
}
/**
* Get a list of available folders from this source
*/
public
function
get_lists
(&
$tree
=
null
)
{
$this
->
_read_lists
();
// attempt to create a default folder for this user
if
(
empty
(
$this
->
lists
))
{
$folder
=
array
(
'name'
=>
'Notes'
,
'type'
=>
'note'
,
'default'
=>
true
,
'subscribed'
=>
true
);
if
(
kolab_storage
::
folder_update
(
$folder
))
{
$this
->
_read_lists
(
true
);
}
}
$folders
=
array
();
foreach
(
$this
->
lists
as
$id
=>
$list
)
{
if
(!
empty
(
$this
->
folders
[
$id
]))
{
$folders
[]
=
$this
->
folders
[
$id
];
}
}
// include virtual folders for a full folder tree
if
(!
is_null
(
$tree
))
{
$folders
=
kolab_storage
::
folder_hierarchy
(
$folders
,
$tree
);
}
$delim
=
$this
->
rc
->
get_storage
()->
get_hierarchy_delimiter
();
$lists
=
array
();
foreach
(
$folders
as
$folder
)
{
$list_id
=
$folder
->
id
;
$imap_path
=
explode
(
$delim
,
$folder
->
name
);
// find parent
do
{
array_pop
(
$imap_path
);
$parent_id
=
kolab_storage
::
folder_id
(
join
(
$delim
,
$imap_path
));
}
while
(
count
(
$imap_path
)
>
1
&&
!
$this
->
folders
[
$parent_id
]);
// restore "real" parent ID
if
(
$parent_id
&&
!
$this
->
folders
[
$parent_id
])
{
$parent_id
=
kolab_storage
::
folder_id
(
$folder
->
get_parent
());
}
$fullname
=
$folder
->
get_name
();
$listname
=
$folder
->
get_foldername
();
// special handling for virtual folders
if
(
$folder
instanceof
kolab_storage_folder_user
)
{
$lists
[
$list_id
]
=
array
(
'id'
=>
$list_id
,
'name'
=>
$fullname
,
'listname'
=>
$listname
,
'title'
=>
$folder
->
get_title
(),
'virtual'
=>
true
,
'editable'
=>
false
,
'rights'
=>
'l'
,
'group'
=>
'other virtual'
,
'class'
=>
'user'
,
'parent'
=>
$parent_id
,
);
}
else
if
(
$folder
->
virtual
)
{
$lists
[
$list_id
]
=
array
(
'id'
=>
$list_id
,
'name'
=>
$fullname
,
'listname'
=>
$listname
,
'virtual'
=>
true
,
'editable'
=>
false
,
'rights'
=>
'l'
,
'group'
=>
$folder
->
get_namespace
(),
'parent'
=>
$parent_id
,
);
}
else
{
if
(!
$this
->
lists
[
$list_id
])
{
$this
->
lists
[
$list_id
]
=
$this
->
folder_props
(
$folder
);
$this
->
folders
[
$list_id
]
=
$folder
;
}
$this
->
lists
[
$list_id
][
'parent'
]
=
$parent_id
;
$lists
[
$list_id
]
=
$this
->
lists
[
$list_id
];
}
}
return
$lists
;
}
/**
* Search for shared or otherwise not listed folders the user has access
*
* @param string Search string
* @param string Section/source to search
* @return array List of notes folders
*/
protected
function
search_lists
(
$query
,
$source
)
{
if
(!
kolab_storage
::
setup
())
{
return
array
();
}
$this
->
search_more_results
=
false
;
$this
->
lists
=
$this
->
folders
=
array
();
// find unsubscribed IMAP folders that have "event" type
if
(
$source
==
'folders'
)
{
foreach
((
array
)
kolab_storage
::
search_folders
(
'note'
,
$query
,
array
(
'other'
))
as
$folder
)
{
$this
->
folders
[
$folder
->
id
]
=
$folder
;
$this
->
lists
[
$folder
->
id
]
=
$this
->
folder_props
(
$folder
);
}
}
// search other user's namespace via LDAP
else
if
(
$source
==
'users'
)
{
$limit
=
$this
->
rc
->
config
->
get
(
'autocomplete_max'
,
15
)
*
2
;
// we have slightly more space, so display twice the number
foreach
(
kolab_storage
::
search_users
(
$query
,
0
,
array
(),
$limit
*
10
)
as
$user
)
{
$folders
=
array
();
// search for note folders shared by this user
foreach
(
kolab_storage
::
list_user_folders
(
$user
,
'note'
,
false
)
as
$foldername
)
{
$folders
[]
=
new
kolab_storage_folder
(
$foldername
,
'note'
);
}
if
(
count
(
$folders
))
{
$userfolder
=
new
kolab_storage_folder_user
(
$user
[
'kolabtargetfolder'
],
''
,
$user
);
$this
->
folders
[
$userfolder
->
id
]
=
$userfolder
;
$this
->
lists
[
$userfolder
->
id
]
=
$this
->
folder_props
(
$userfolder
);
foreach
(
$folders
as
$folder
)
{
$this
->
folders
[
$folder
->
id
]
=
$folder
;
$this
->
lists
[
$folder
->
id
]
=
$this
->
folder_props
(
$folder
);
$count
++;
}
}
if
(
$count
>=
$limit
)
{
$this
->
search_more_results
=
true
;
break
;
}
}
}
return
$this
->
get_lists
();
}
/**
* Derive list properties from the given kolab_storage_folder object
*/
protected
function
folder_props
(
$folder
)
{
if
(
$folder
->
get_namespace
()
==
'personal'
)
{
$norename
=
false
;
$editable
=
true
;
$rights
=
'lrswikxtea'
;
$alarms
=
true
;
}
else
{
$alarms
=
false
;
$rights
=
'lr'
;
$editable
=
false
;
if
((
$myrights
=
$folder
->
get_myrights
())
&&
!
PEAR
::
isError
(
$myrights
))
{
$rights
=
$myrights
;
if
(
strpos
(
$rights
,
't'
)
!==
false
||
strpos
(
$rights
,
'd'
)
!==
false
)
$editable
=
strpos
(
$rights
,
'i'
);
}
$info
=
$folder
->
get_folder_info
();
$norename
=
$readonly
||
$info
[
'norename'
]
||
$info
[
'protected'
];
}
$list_id
=
$folder
->
id
;
return
array
(
'id'
=>
$list_id
,
'name'
=>
$folder
->
get_name
(),
'listname'
=>
$folder
->
get_foldername
(),
'editname'
=>
$folder
->
get_foldername
(),
'editable'
=>
$editable
,
'rights'
=>
$rights
,
'norename'
=>
$norename
,
'parentfolder'
=>
$folder
->
get_parent
(),
'subscribed'
=>
(
bool
)
$folder
->
is_subscribed
(),
'default'
=>
$folder
->
default
,
'group'
=>
$folder
->
default
?
'default'
:
$folder
->
get_namespace
(),
'class'
=>
trim
(
$folder
->
get_namespace
()
.
(
$folder
->
default
?
' default'
:
''
)),
);
}
/**
* Get the kolab_calendar instance for the given calendar ID
*
* @param string List identifier (encoded imap folder name)
* @return object kolab_storage_folder Object nor null if list doesn't exist
*/
public
function
get_folder
(
$id
)
{
// create list and folder instance if necesary
if
(!
$this
->
lists
[
$id
])
{
$folder
=
kolab_storage
::
get_folder
(
kolab_storage
::
id_decode
(
$id
));
if
(
$folder
->
type
)
{
$this
->
folders
[
$id
]
=
$folder
;
$this
->
lists
[
$id
]
=
$this
->
folder_props
(
$folder
);
}
}
return
$this
->
folders
[
$id
];
}
/******* UI functions ********/
/**
* Render main view of the tasklist task
*/
public
function
notes_view
()
{
$this
->
ui
->
init
();
$this
->
ui
->
init_templates
();
$this
->
rc
->
output
->
set_pagetitle
(
$this
->
gettext
(
'navtitle'
));
$this
->
rc
->
output
->
send
(
'kolab_notes.notes'
);
}
/**
* Deliver a rediced UI for inline (dialog)
*/
public
function
dialog_view
()
{
// resolve message reference
if
(
$msgref
=
rcube_utils
::
get_input_value
(
'_msg'
,
rcube_utils
::
INPUT_GPC
,
true
))
{
$storage
=
$this
->
rc
->
get_storage
();
list
(
$uid
,
$folder
)
=
explode
(
'-'
,
$msgref
,
2
);
if
(
$message
=
$storage
->
get_message_headers
(
$msgref
))
{
$this
->
rc
->
output
->
set_env
(
'kolab_notes_template'
,
array
(
'_from_mail'
=>
true
,
'title'
=>
$message
->
get
(
'subject'
),
'links'
=>
array
(
kolab_storage_config
::
get_message_reference
(
kolab_storage_config
::
get_message_uri
(
$message
,
$folder
),
'note'
)),
));
}
}
$this
->
ui
->
init_templates
();
$this
->
rc
->
output
->
send
(
'kolab_notes.dialogview'
);
}
/**
* Handler to retrieve note records for the given list and/or search query
*/
public
function
notes_fetch
()
{
$search
=
rcube_utils
::
get_input_value
(
'_q'
,
rcube_utils
::
INPUT_GPC
,
true
);
$list
=
rcube_utils
::
get_input_value
(
'_list'
,
rcube_utils
::
INPUT_GPC
);
$data
=
$this
->
notes_data
(
$this
->
list_notes
(
$list
,
$search
),
$tags
);
$this
->
rc
->
output
->
command
(
'plugin.data_ready'
,
array
(
'list'
=>
$list
,
'search'
=>
$search
,
'data'
=>
$data
,
'tags'
=>
array_values
(
$tags
)
));
}
/**
* Convert the given note records for delivery to the client
*/
protected
function
notes_data
(
$records
,
&
$tags
)
{
$config
=
kolab_storage_config
::
get_instance
();
$tags
=
$config
->
apply_tags
(
$records
);
$config
->
apply_links
(
$records
);
foreach
(
$records
as
$i
=>
$rec
)
{
unset
(
$records
[
$i
][
'description'
]);
$this
->
_client_encode
(
$records
[
$i
]);
}
return
$records
;
}
/**
* Read note records for the given list from the storage backend
*/
protected
function
list_notes
(
$list_id
,
$search
=
null
)
{
$results
=
array
();
// query Kolab storage
$query
=
array
();
// full text search (only works with cache enabled)
if
(
strlen
(
$search
))
{
$words
=
array_filter
(
rcube_utils
::
normalize_string
(
mb_strtolower
(
$search
),
true
));
foreach
(
$words
as
$word
)
{
if
(
strlen
(
$word
)
>
2
)
{
// only words > 3 chars are stored in DB
$query
[]
=
array
(
'words'
,
'~'
,
$word
);
}
}
}
$this
->
_read_lists
();
if
(
$folder
=
$this
->
get_folder
(
$list_id
))
{
foreach
(
$folder
->
select
(
$query
,
empty
(
$query
))
as
$record
)
{
// post-filter search results
if
(
strlen
(
$search
))
{
$matches
=
0
;
$contents
=
mb_strtolower
(
$record
[
'title'
]
.
(
$this
->
is_html
(
$record
)
?
strip_tags
(
$record
[
'description'
])
:
$record
[
'description'
])
);
foreach
(
$words
as
$word
)
{
if
(
mb_strpos
(
$contents
,
$word
)
!==
false
)
{
$matches
++;
}
}
// skip records not matching all search words
if
(
$matches
<
count
(
$words
))
{
continue
;
}
}
$record
[
'list'
]
=
$list_id
;
$results
[]
=
$record
;
}
}
return
$results
;
}
/**
* Handler for delivering a full note record to the client
*/
public
function
note_record
()
{
$data
=
$this
->
get_note
(
array
(
'uid'
=>
rcube_utils
::
get_input_value
(
'_id'
,
rcube_utils
::
INPUT_GPC
),
'list'
=>
rcube_utils
::
get_input_value
(
'_list'
,
rcube_utils
::
INPUT_GPC
),
));
// encode for client use
if
(
is_array
(
$data
))
{
$this
->
_client_encode
(
$data
);
}
$this
->
rc
->
output
->
command
(
'plugin.render_note'
,
$data
);
}
/**
* Get the full note record identified by the given UID + Lolder identifier
*/
public
function
get_note
(
$note
)
{
if
(
is_array
(
$note
))
{
$uid
=
$note
[
'uid'
]
?:
$note
[
'id'
];
$list_id
=
$note
[
'list'
];
}
else
{
$uid
=
$note
;
}
// deliver from in-memory cache
$key
=
$list_id
.
':'
.
$uid
;
if
(
$this
->
cache
[
$key
])
{
return
$this
->
cache
[
$key
];
}
$result
=
false
;
$this
->
_read_lists
();
if
(
$list_id
)
{
if
(
$folder
=
$this
->
get_folder
(
$list_id
))
{
$result
=
$folder
->
get_object
(
$uid
);
}
}
// iterate over all calendar folders and search for the event ID
else
{
foreach
(
$this
->
folders
as
$list_id
=>
$folder
)
{
if
(
$result
=
$folder
->
get_object
(
$uid
))
{
$result
[
'list'
]
=
$list_id
;
break
;
}
}
}
if
(
$result
)
{
// get note tags
$result
[
'tags'
]
=
$this
->
get_tags
(
$result
[
'uid'
]);
// get note links
$result
[
'links'
]
=
$this
->
get_links
(
$result
[
'uid'
]);
}
return
$result
;
}
/**
* Helper method to encode the given note record for use in the client
*/
private
function
_client_encode
(&
$note
)
{
foreach
(
$note
as
$key
=>
$prop
)
{
if
(
$key
[
0
]
==
'_'
||
$key
==
'x-custom'
)
{
unset
(
$note
[
$key
]);
}
}
foreach
(
array
(
'created'
,
'changed'
)
as
$key
)
{
if
(
is_object
(
$note
[
$key
])
&&
$note
[
$key
]
instanceof
DateTime
)
{
$note
[
$key
.
'_'
]
=
$note
[
$key
]->
format
(
'U'
);
$note
[
$key
]
=
$this
->
rc
->
format_date
(
$note
[
$key
]);
}
}
// clean HTML contents
if
(!
empty
(
$note
[
'description'
])
&&
$this
->
is_html
(
$note
))
{
$note
[
'html'
]
=
$this
->
_wash_html
(
$note
[
'description'
]);
}
// convert link URIs references into structs
if
(
array_key_exists
(
'links'
,
$note
))
{
foreach
((
array
)
$note
[
'links'
]
as
$i
=>
$link
)
{
if
(
strpos
(
$link
,
'imap://'
)
===
0
&&
(
$msgref
=
kolab_storage_config
::
get_message_reference
(
$link
,
'note'
)))
{
$note
[
'links'
][
$i
]
=
$msgref
;
}
}
}
return
$note
;
}
/**
* Handler for client-initiated actions on a single note record
*/
public
function
note_action
()
{
$action
=
rcube_utils
::
get_input_value
(
'_do'
,
rcube_utils
::
INPUT_POST
);
$note
=
rcube_utils
::
get_input_value
(
'_data'
,
rcube_utils
::
INPUT_POST
,
true
);
$success
=
$silent
=
false
;
switch
(
$action
)
{
case
'new'
:
case
'edit'
:
if
(
$success
=
$this
->
save_note
(
$note
))
{
$refresh
=
$this
->
get_note
(
$note
);
}
break
;
case
'move'
:
$uids
=
explode
(
','
,
$note
[
'uid'
]);
foreach
(
$uids
as
$uid
)
{
$note
[
'uid'
]
=
$uid
;
if
(!(
$success
=
$this
->
move_note
(
$note
,
$note
[
'to'
])))
{
$refresh
=
$this
->
get_note
(
$note
);
break
;
}
}
break
;
case
'delete'
:
$uids
=
explode
(
','
,
$note
[
'uid'
]);
foreach
(
$uids
as
$uid
)
{
$note
[
'uid'
]
=
$uid
;
if
(!(
$success
=
$this
->
delete_note
(
$note
)))
{
$refresh
=
$this
->
get_note
(
$note
);
break
;
}
}
break
;
case
'changelog'
:
$data
=
$this
->
get_changelog
(
$note
);
if
(
is_array
(
$data
)
&&
!
empty
(
$data
))
{
$rcmail
=
$this
->
rc
;
$dtformat
=
$rcmail
->
config
->
get
(
'date_format'
)
.
' '
.
$this
->
rc
->
config
->
get
(
'time_format'
);
array_walk
(
$data
,
function
(&
$change
)
use
(
$lib
,
$rcmail
,
$dtformat
)
{
if
(
$change
[
'date'
])
{
$dt
=
rcube_utils
::
anytodatetime
(
$change
[
'date'
]);
if
(
$dt
instanceof
DateTime
)
{
$change
[
'date'
]
=
$rcmail
->
format_date
(
$dt
,
$dtformat
);
}
}
});
$this
->
rc
->
output
->
command
(
'plugin.note_render_changelog'
,
$data
);
}
else
{
$this
->
rc
->
output
->
command
(
'plugin.note_render_changelog'
,
false
);
}
$silent
=
true
;
break
;
case
'diff'
:
$silent
=
true
;
$data
=
$this
->
get_diff
(
$note
,
$note
[
'rev1'
],
$note
[
'rev2'
]);
if
(
is_array
(
$data
))
{
$this
->
rc
->
output
->
command
(
'plugin.note_show_diff'
,
$data
);
}
else
{
$this
->
rc
->
output
->
command
(
'display_message'
,
$this
->
gettext
(
'objectdiffnotavailable'
),
'error'
);
}
break
;
case
'show'
:
if
(
$rec
=
$this
->
get_revison
(
$note
,
$note
[
'rev'
]))
{
$this
->
rc
->
output
->
command
(
'plugin.note_show_revision'
,
$this
->
_client_encode
(
$rec
));
}
else
{
$this
->
rc
->
output
->
command
(
'display_message'
,
$this
->
gettext
(
'objectnotfound'
),
'error'
);
}
$silent
=
true
;
break
;
case
'restore'
:
if
(
$this
->
restore_revision
(
$note
,
$note
[
'rev'
]))
{
$refresh
=
$this
->
get_note
(
$note
);
$this
->
rc
->
output
->
command
(
'display_message'
,
$this
->
gettext
(
array
(
'name'
=>
'objectrestoresuccess'
,
'vars'
=>
array
(
'rev'
=>
$note
[
'rev'
]))),
'confirmation'
);
$this
->
rc
->
output
->
command
(
'plugin.close_history_dialog'
);
}
else
{
$this
->
rc
->
output
->
command
(
'display_message'
,
$this
->
gettext
(
'objectrestoreerror'
),
'error'
);
}
$silent
=
true
;
break
;
}
// show confirmation/error message
if
(
$success
)
{
$this
->
rc
->
output
->
show_message
(
'successfullysaved'
,
'confirmation'
);
}
else
if
(!
$silent
)
{
$this
->
rc
->
output
->
show_message
(
'errorsaving'
,
'error'
);
}
// unlock client
$this
->
rc
->
output
->
command
(
'plugin.unlock_saving'
);
if
(
$refresh
)
{
$this
->
rc
->
output
->
command
(
'plugin.update_note'
,
$this
->
_client_encode
(
$refresh
));
}
}
/**
* Update an note record with the given data
*
* @param array Hash array with note properties (id, list)
* @return boolean True on success, False on error
*/
private
function
save_note
(&
$note
)
{
$this
->
_read_lists
();
$list_id
=
$note
[
'list'
];
if
(!
$list_id
||
!(
$folder
=
$this
->
get_folder
(
$list_id
)))
return
false
;
// moved from another folder
if
(
$note
[
'_fromlist'
]
&&
(
$fromfolder
=
$this
->
get_folder
(
$note
[
'_fromlist'
])))
{
if
(!
$fromfolder
->
move
(
$note
[
'uid'
],
$folder
->
name
))
return
false
;
unset
(
$note
[
'_fromlist'
]);
}
// load previous version of this record to merge
if
(
$note
[
'uid'
])
{
$old
=
$folder
->
get_object
(
$note
[
'uid'
]);
if
(!
$old
||
PEAR
::
isError
(
$old
))
return
false
;
// merge existing properties if the update isn't complete
if
(!
isset
(
$note
[
'title'
])
||
!
isset
(
$note
[
'description'
]))
$note
+=
$old
;
}
// generate new note object from input
$object
=
$this
->
_write_preprocess
(
$note
,
$old
);
// email links and tags are handled separately
$links
=
$object
[
'links'
];
$tags
=
$object
[
'tags'
];
unset
(
$object
[
'links'
]);
unset
(
$object
[
'tags'
]);
$saved
=
$folder
->
save
(
$object
,
'note'
,
$note
[
'uid'
]);
if
(!
$saved
)
{
rcube
::
raise_error
(
array
(
'code'
=>
600
,
'type'
=>
'php'
,
'file'
=>
__FILE__
,
'line'
=>
__LINE__
,
'message'
=>
"Error saving note object to Kolab server"
),
true
,
false
);
$saved
=
false
;
}
else
{
// save links in configuration.relation object
$this
->
save_links
(
$object
[
'uid'
],
$links
);
// save tags in configuration.relation object
$this
->
save_tags
(
$object
[
'uid'
],
$tags
);
$note
=
$object
;
$note
[
'list'
]
=
$list_id
;
$note
[
'tags'
]
=
(
array
)
$tags
;
// cache this in memory for later read
$key
=
$list_id
.
':'
.
$note
[
'uid'
];
$this
->
cache
[
$key
]
=
$note
;
}
return
$saved
;
}
/**
* Move the given note to another folder
*/
function
move_note
(
$note
,
$list_id
)
{
$this
->
_read_lists
();
$tofolder
=
$this
->
get_folder
(
$list_id
);
$fromfolder
=
$this
->
get_folder
(
$note
[
'list'
]);
if
(
$fromfolder
&&
$tofolder
)
{
return
$fromfolder
->
move
(
$note
[
'uid'
],
$tofolder
->
name
);
}
return
false
;
}
/**
* Remove a single note record from the backend
*
* @param array Hash array with note properties (id, list)
* @param boolean Remove record irreversible (mark as deleted otherwise)
* @return boolean True on success, False on error
*/
public
function
delete_note
(
$note
,
$force
=
true
)
{
$this
->
_read_lists
();
$list_id
=
$note
[
'list'
];
if
(!
$list_id
||
!(
$folder
=
$this
->
get_folder
(
$list_id
)))
{
return
false
;
}
$status
=
$folder
->
delete
(
$note
[
'uid'
],
$force
);
if
(
$status
)
{
$this
->
save_links
(
$note
[
'uid'
],
null
);
$this
->
save_tags
(
$note
[
'uid'
],
null
);
}
return
$status
;
}
/**
* Render the template for printing with placeholders
*/
public
function
print_note
()
{
$uid
=
rcube_utils
::
get_input_value
(
'_uid'
,
rcube_utils
::
INPUT_GET
);
$list
=
rcube_utils
::
get_input_value
(
'_list'
,
rcube_utils
::
INPUT_GET
);
$this
->
note
=
$this
->
get_note
(
array
(
'uid'
=>
$uid
,
'list'
=>
$list
));
// encode for client use
if
(
is_array
(
$this
->
note
))
{
$this
->
_client_encode
(
$this
->
note
);
}
$this
->
rc
->
output
->
set_pagetitle
(
$this
->
note
[
'title'
]);
$this
->
rc
->
output
->
add_handlers
(
array
(
'noteheader'
=>
array
(
$this
,
'print_note_header'
),
'notebody'
=>
array
(
$this
,
'print_note_body'
),
));
$this
->
include_script
(
'notes.js'
);
$this
->
rc
->
output
->
send
(
'kolab_notes.print'
);
}
public
function
print_note_header
()
{
$tags
=
array_map
(
array
(
'rcube'
,
'Q'
),
(
array
)
$this
->
note
[
'tags'
]);
$tags
=
implode
(
' '
,
$tags
);
return
html
::
tag
(
'h1'
,
array
(
'id'
=>
'notetitle'
),
rcube
::
Q
(
$this
->
note
[
'title'
]))
.
html
::
div
(
array
(
'id'
=>
'notetags'
,
'class'
=>
'tagline'
),
$tags
)
.
html
::
div
(
'dates'
,
html
::
label
(
null
,
rcube
::
Q
(
$this
->
gettext
(
'created'
)))
.
html
::
span
(
array
(
'id'
=>
'notecreated'
),
rcube
::
Q
(
$this
->
note
[
'created'
]))
.
html
::
label
(
null
,
rcube
::
Q
(
$this
->
gettext
(
'changed'
)))
.
html
::
span
(
array
(
'id'
=>
'notechanged'
),
rcube
::
Q
(
$this
->
note
[
'changed'
]))
);
}
public
function
print_note_body
()
{
return
isset
(
$this
->
note
[
'html'
])
?
$this
->
note
[
'html'
]
:
rcube
::
Q
(
$this
->
note
[
'description'
]);
}
/**
* Provide a list of revisions for the given object
*
* @param array $note Hash array with note properties
* @return array List of changes, each as a hash array
*/
public
function
get_changelog
(
$note
)
{
if
(
empty
(
$this
->
bonnie_api
))
{
return
false
;
}
list
(
$uid
,
$mailbox
,
$msguid
)
=
$this
->
_resolve_note_identity
(
$note
);
$result
=
$uid
&&
$mailbox
?
$this
->
bonnie_api
->
changelog
(
'note'
,
$uid
,
$mailbox
,
$msguid
)
:
null
;
if
(
is_array
(
$result
)
&&
$result
[
'uid'
]
==
$uid
)
{
return
$result
[
'changes'
];
}
return
false
;
}
/**
* Return full data of a specific revision of a note record
*
* @param mixed $note UID string or hash array with note properties
* @param mixed $rev Revision number
*
* @return array Note object as hash array
*/
public
function
get_revison
(
$note
,
$rev
)
{
if
(
empty
(
$this
->
bonnie_api
))
{
return
false
;
}
list
(
$uid
,
$mailbox
,
$msguid
)
=
$this
->
_resolve_note_identity
(
$note
);
// call Bonnie API
$result
=
$this
->
bonnie_api
->
get
(
'note'
,
$uid
,
$rev
,
$mailbox
,
$msguid
);
if
(
is_array
(
$result
)
&&
$result
[
'uid'
]
==
$uid
&&
!
empty
(
$result
[
'xml'
]))
{
$format
=
kolab_format
::
factory
(
'note'
);
$format
->
load
(
$result
[
'xml'
]);
$rec
=
$format
->
to_array
();
if
(
$format
->
is_valid
())
{
$rec
[
'rev'
]
=
$result
[
'rev'
];
return
$rec
;
}
}
return
false
;
}
/**
* Get a list of property changes beteen two revisions of a note object
*
* @param array $$note Hash array with note properties
* @param mixed $rev Revisions: "from:to"
*
* @return array List of property changes, each as a hash array
*/
public
function
get_diff
(
$note
,
$rev1
,
$rev2
)
{
if
(
empty
(
$this
->
bonnie_api
))
{
return
false
;
}
list
(
$uid
,
$mailbox
,
$msguid
)
=
$this
->
_resolve_note_identity
(
$note
);
// call Bonnie API
$result
=
$this
->
bonnie_api
->
diff
(
'note'
,
$uid
,
$rev1
,
$rev2
,
$mailbox
,
$msguid
);
if
(
is_array
(
$result
)
&&
$result
[
'uid'
]
==
$uid
)
{
$result
[
'rev1'
]
=
$rev1
;
$result
[
'rev2'
]
=
$rev2
;
// convert some properties, similar to self::_client_encode()
$keymap
=
array
(
'summary'
=>
'title'
,
'lastmodified-date'
=>
'changed'
,
);
// map kolab object properties to keys and values the client expects
array_walk
(
$result
[
'changes'
],
function
(&
$change
,
$i
)
use
(
$keymap
)
{
if
(
array_key_exists
(
$change
[
'property'
],
$keymap
))
{
$change
[
'property'
]
=
$keymap
[
$change
[
'property'
]];
}
if
(
$change
[
'property'
]
==
'created'
||
$change
[
'property'
]
==
'changed'
)
{
if
(
$old_
=
rcube_utils
::
anytodatetime
(
$change
[
'old'
]))
{
$change
[
'old_'
]
=
$this
->
rc
->
format_date
(
$old_
);
}
if
(
$new_
=
rcube_utils
::
anytodatetime
(
$change
[
'new'
]))
{
$change
[
'new_'
]
=
$this
->
rc
->
format_date
(
$new_
);
}
}
// compute a nice diff of note contents
if
(
$change
[
'property'
]
==
'description'
)
{
$change
[
'diff_'
]
=
libkolab
::
html_diff
(
$change
[
'old'
],
$change
[
'new'
]);
if
(!
empty
(
$change
[
'diff_'
]))
{
unset
(
$change
[
'old'
],
$change
[
'new'
]);
$change
[
'diff_'
]
=
preg_replace
(
array
(
'!^.*<body[^>]*>!Uims'
,
'!</body>.*$!Uims'
),
''
,
$change
[
'diff_'
]);
$change
[
'diff_'
]
=
preg_replace
(
"!</(p|li|span)>
\n
!"
,
'</
\\
1>'
,
$change
[
'diff_'
]);
}
}
});
return
$result
;
}
return
false
;
}
/**
* Command the backend to restore a certain revision of a note.
* This shall replace the current object with an older version.
*
* @param array $note Hash array with note properties (id, list)
* @param mixed $rev Revision number
*
* @return boolean True on success, False on failure
*/
public
function
restore_revision
(
$note
,
$rev
)
{
if
(
empty
(
$this
->
bonnie_api
))
{
return
false
;
}
list
(
$uid
,
$mailbox
,
$msguid
)
=
$this
->
_resolve_note_identity
(
$note
);
$folder
=
$this
->
get_folder
(
$note
[
'list'
]);
$success
=
false
;
if
(
$folder
&&
(
$raw_msg
=
$this
->
bonnie_api
->
rawdata
(
'note'
,
$uid
,
$rev
,
$mailbox
)))
{
$imap
=
$this
->
rc
->
get_storage
();
// insert $raw_msg as new message
if
(
$imap
->
save_message
(
$folder
->
name
,
$raw_msg
,
null
,
false
))
{
$success
=
true
;
// delete old revision from imap and cache
$imap
->
delete_message
(
$msguid
,
$folder
->
name
);
$folder
->
cache
->
set
(
$msguid
,
false
);
$this
->
cache
=
array
();
}
}
return
$success
;
}
/**
* Helper method to resolved the given note identifier into uid and mailbox
*
* @return array (uid,mailbox,msguid) tuple
*/
private
function
_resolve_note_identity
(
$note
)
{
$mailbox
=
$msguid
=
null
;
if
(!
is_array
(
$note
))
{
$note
=
$this
->
get_note
(
$note
);
}
if
(
is_array
(
$note
))
{
$uid
=
$note
[
'uid'
]
?:
$note
[
'id'
];
$list
=
$note
[
'list'
];
}
else
{
return
array
(
null
,
$mailbox
,
$msguid
);
}
if
(
$folder
=
$this
->
get_folder
(
$list
))
{
$mailbox
=
$folder
->
get_mailbox_id
();
// get object from storage in order to get the real object uid an msguid
if
(
$rec
=
$folder
->
get_object
(
$uid
))
{
$msguid
=
$rec
[
'_msguid'
];
$uid
=
$rec
[
'uid'
];
}
}
return
array
(
$uid
,
$mailbox
,
$msguid
);
}
/**
* Handler for client requests to list (aka folder) actions
*/
public
function
list_action
()
{
$action
=
rcube_utils
::
get_input_value
(
'_do'
,
rcube_utils
::
INPUT_GPC
);
$list
=
rcube_utils
::
get_input_value
(
'_list'
,
rcube_utils
::
INPUT_GPC
,
true
);
$success
=
$update_cmd
=
false
;
if
(
empty
(
$action
))
{
$action
=
rcube_utils
::
get_input_value
(
'action'
,
rcube_utils
::
INPUT_GPC
);
}
switch
(
$action
)
{
case
'form-new'
:
case
'form-edit'
:
$this
->
_read_lists
();
$this
->
ui
->
list_editform
(
$action
,
$this
->
lists
[
$list
[
'id'
]],
$this
->
folders
[
$list
[
'id'
]]);
exit
;
case
'new'
:
$list
[
'type'
]
=
'note'
;
$list
[
'subscribed'
]
=
true
;
$folder
=
kolab_storage
::
folder_update
(
$list
);
if
(
$folder
===
false
)
{
$save_error
=
$this
->
gettext
(
kolab_storage
::
$last_error
);
}
else
{
$success
=
true
;
$update_cmd
=
'plugin.update_list'
;
$list
[
'id'
]
=
kolab_storage
::
folder_id
(
$folder
);
$list
[
'_reload'
]
=
true
;
}
break
;
case
'edit'
:
$this
->
_read_lists
();
$oldparent
=
$this
->
lists
[
$list
[
'id'
]][
'parentfolder'
];
$newfolder
=
kolab_storage
::
folder_update
(
$list
);
if
(
$newfolder
===
false
)
{
$save_error
=
$this
->
gettext
(
kolab_storage
::
$last_error
);
}
else
{
$success
=
true
;
$update_cmd
=
'plugin.update_list'
;
$list
[
'newid'
]
=
kolab_storage
::
folder_id
(
$newfolder
);
$list
[
'_reload'
]
=
$list
[
'parent'
]
!=
$oldparent
;
// compose the new display name
$delim
=
$this
->
rc
->
get_storage
()->
get_hierarchy_delimiter
();
$path_imap
=
explode
(
$delim
,
$newfolder
);
$list
[
'name'
]
=
kolab_storage
::
object_name
(
$newfolder
);
$list
[
'editname'
]
=
rcube_charset
::
convert
(
array_pop
(
$path_imap
),
'UTF7-IMAP'
);
$list
[
'listname'
]
=
$list
[
'editname'
];
}
break
;
case
'delete'
:
$this
->
_read_lists
();
$folder
=
$this
->
get_folder
(
$list
[
'id'
]);
if
(
$folder
&&
kolab_storage
::
folder_delete
(
$folder
->
name
))
{
$success
=
true
;
$update_cmd
=
'plugin.destroy_list'
;
}
else
{
$save_error
=
$this
->
gettext
(
kolab_storage
::
$last_error
);
}
break
;
case
'search'
:
$this
->
load_ui
();
$results
=
array
();
foreach
((
array
)
$this
->
search_lists
(
rcube_utils
::
get_input_value
(
'q'
,
rcube_utils
::
INPUT_GPC
),
rcube_utils
::
get_input_value
(
'source'
,
rcube_utils
::
INPUT_GPC
))
as
$id
=>
$prop
)
{
$editname
=
$prop
[
'editname'
];
unset
(
$prop
[
'editname'
]);
// force full name to be displayed
// let the UI generate HTML and CSS representation for this calendar
$html
=
$this
->
ui
->
folder_list_item
(
$id
,
$prop
,
$jsenv
,
true
);
$prop
+=
(
array
)
$jsenv
[
$id
];
$prop
[
'editname'
]
=
$editname
;
$prop
[
'html'
]
=
$html
;
$results
[]
=
$prop
;
}
// report more results available
if
(
$this
->
driver
->
search_more_results
)
{
$this
->
rc
->
output
->
show_message
(
'autocompletemore'
,
'notice'
);
}
$this
->
rc
->
output
->
command
(
'multi_thread_http_response'
,
$results
,
rcube_utils
::
get_input_value
(
'_reqid'
,
rcube_utils
::
INPUT_GPC
));
return
;
case
'subscribe'
:
$success
=
false
;
if
(
$list
[
'id'
]
&&
(
$folder
=
$this
->
get_folder
(
$list
[
'id'
])))
{
if
(
isset
(
$list
[
'permanent'
]))
$success
|=
$folder
->
subscribe
(
intval
(
$list
[
'permanent'
]));
if
(
isset
(
$list
[
'active'
]))
$success
|=
$folder
->
activate
(
intval
(
$list
[
'active'
]));
// apply to child folders, too
if
(
$list
[
'recursive'
])
{
foreach
((
array
)
kolab_storage
::
list_folders
(
$folder
->
name
,
'*'
,
'node'
)
as
$subfolder
)
{
if
(
isset
(
$list
[
'permanent'
]))
(
$list
[
'permanent'
]
?
kolab_storage
::
folder_subscribe
(
$subfolder
)
:
kolab_storage
::
folder_unsubscribe
(
$subfolder
));
if
(
isset
(
$list
[
'active'
]))
(
$list
[
'active'
]
?
kolab_storage
::
folder_activate
(
$subfolder
)
:
kolab_storage
::
folder_deactivate
(
$subfolder
));
}
}
}
break
;
}
$this
->
rc
->
output
->
command
(
'plugin.unlock_saving'
);
if
(
$success
)
{
$this
->
rc
->
output
->
show_message
(
'successfullysaved'
,
'confirmation'
);
if
(
$update_cmd
)
{
$this
->
rc
->
output
->
command
(
$update_cmd
,
$list
);
}
}
else
{
$error_msg
=
$this
->
gettext
(
'errorsaving'
)
.
(
$save_error
?
': '
.
$save_error
:
''
);
$this
->
rc
->
output
->
show_message
(
$error_msg
,
'error'
);
}
}
/**
* Hook to add note attachments to message compose if the according parameter is present.
* This completes the 'send note by mail' feature.
*/
public
function
mail_message_compose
(
$args
)
{
if
(!
empty
(
$args
[
'param'
][
'with_notes'
]))
{
$uids
=
explode
(
','
,
$args
[
'param'
][
'with_notes'
]);
$list
=
$args
[
'param'
][
'notes_list'
];
foreach
(
$uids
as
$uid
)
{
if
(
$note
=
$this
->
get_note
(
array
(
'uid'
=>
$uid
,
'list'
=>
$list
)))
{
$data
=
$this
->
note2message
(
$note
);
$args
[
'attachments'
][]
=
array
(
'name'
=>
abbreviate_string
(
$note
[
'title'
],
50
,
''
),
'mimetype'
=>
'message/rfc822'
,
'data'
=>
$data
,
'size'
=>
strlen
(
$data
),
);
if
(
empty
(
$args
[
'param'
][
'subject'
]))
{
$args
[
'param'
][
'subject'
]
=
$note
[
'title'
];
}
}
}
unset
(
$args
[
'param'
][
'with_notes'
],
$args
[
'param'
][
'notes_list'
]);
}
return
$args
;
}
/**
* Lookup backend storage and find notes associated with the given message
*/
public
function
mail_message_load
(
$p
)
{
if
(!
$p
[
'object'
]->
headers
->
others
[
'x-kolab-type'
])
{
$this
->
message_notes
=
$this
->
get_message_notes
(
$p
[
'object'
]->
headers
,
$p
[
'object'
]->
folder
);
}
}
/**
* Handler for 'messagebody_html' hook
*/
public
function
mail_messagebody_html
(
$args
)
{
$html
=
''
;
foreach
(
$this
->
message_notes
as
$note
)
{
$html
.=
html
::
a
(
array
(
'href'
=>
$this
->
rc
->
url
(
array
(
'task'
=>
'notes'
,
'_list'
=>
$note
[
'list'
],
'_id'
=>
$note
[
'uid'
])),
'class'
=>
'kolabnotesref'
,
'rel'
=>
$note
[
'uid'
]
.
'@'
.
$note
[
'list'
],
'target'
=>
'_blank'
,
),
rcube
::
Q
(
$note
[
'title'
]));
}
// prepend note links to message body
if
(
$html
)
{
$this
->
load_ui
();
$args
[
'content'
]
=
html
::
div
(
'kolabmessagenotes boxinformation'
,
$html
)
.
$args
[
'content'
];
}
return
$args
;
}
/**
* Determine whether the given note is HTML formatted
*/
private
function
is_html
(
$note
)
{
// check for opening and closing <html> or <body> tags
return
(
preg_match
(
'/<(html|body)(
\s
+[a-z]|>)/'
,
$note
[
'description'
],
$m
)
&&
strpos
(
$note
[
'description'
],
'</'
.
$m
[
1
].
'>'
)
>
0
);
}
/**
* Build an RFC 822 message from the given note
*/
private
function
note2message
(
$note
)
{
$message
=
new
Mail_mime
(
"
\r\n
"
);
$message
->
setParam
(
'text_encoding'
,
'8bit'
);
$message
->
setParam
(
'html_encoding'
,
'quoted-printable'
);
$message
->
setParam
(
'head_encoding'
,
'quoted-printable'
);
$message
->
setParam
(
'head_charset'
,
RCUBE_CHARSET
);
$message
->
setParam
(
'html_charset'
,
RCUBE_CHARSET
);
$message
->
setParam
(
'text_charset'
,
RCUBE_CHARSET
);
$message
->
headers
(
array
(
'Subject'
=>
$note
[
'title'
],
'Date'
=>
$note
[
'changed'
]->
format
(
'r'
),
));
if
(
$this
->
is_html
(
$note
))
{
$message
->
setHTMLBody
(
$note
[
'description'
]);
// add a plain text version of the note content as an alternative part.
$h2t
=
new
rcube_html2text
(
$note
[
'description'
],
false
,
true
,
0
,
RCUBE_CHARSET
);
$plain_part
=
rcube_mime
::
wordwrap
(
$h2t
->
get_text
(),
$this
->
rc
->
config
->
get
(
'line_length'
,
72
),
"
\r\n
"
,
false
,
RCUBE_CHARSET
);
$plain_part
=
trim
(
wordwrap
(
$plain_part
,
998
,
"
\r\n
"
,
true
));
// make sure all line endings are CRLF
$plain_part
=
preg_replace
(
'/
\r
?
\n
/'
,
"
\r\n
"
,
$plain_part
);
$message
->
setTXTBody
(
$plain_part
);
}
else
{
$message
->
setTXTBody
(
$note
[
'description'
]);
}
return
$message
->
getMessage
();
}
private
function
save_links
(
$uid
,
$links
)
{
$config
=
kolab_storage_config
::
get_instance
();
return
$config
->
save_object_links
(
$uid
,
(
array
)
$links
);
}
/**
* Find messages assigned to specified note
*/
private
function
get_links
(
$uid
)
{
$config
=
kolab_storage_config
::
get_instance
();
return
$config
->
get_object_links
(
$uid
);
}
/**
* Get note tags
*/
private
function
get_tags
(
$uid
)
{
$config
=
kolab_storage_config
::
get_instance
();
$tags
=
$config
->
get_tags
(
$uid
);
$tags
=
array_map
(
function
(
$v
)
{
return
$v
[
'name'
];
},
$tags
);
return
$tags
;
}
/**
* Find notes assigned to specified message
*/
private
function
get_message_notes
(
$message
,
$folder
)
{
$config
=
kolab_storage_config
::
get_instance
();
$result
=
$config
->
get_message_relations
(
$message
,
$folder
,
'note'
);
foreach
(
$result
as
$idx
=>
$note
)
{
$result
[
$idx
][
'list'
]
=
kolab_storage
::
folder_id
(
$note
[
'_mailbox'
]);
}
return
$result
;
}
/**
* Update note tags
*/
private
function
save_tags
(
$uid
,
$tags
)
{
$config
=
kolab_storage_config
::
get_instance
();
$config
->
save_tags
(
$uid
,
$tags
);
}
/**
* Process the given note data (submitted by the client) before saving it
*/
private
function
_write_preprocess
(
$note
,
$old
=
array
())
{
$object
=
$note
;
// TODO: handle attachments
// convert link references into simple URIs
if
(
array_key_exists
(
'links'
,
$note
))
{
$object
[
'links'
]
=
array_map
(
function
(
$link
){
return
is_array
(
$link
)
?
$link
[
'uri'
]
:
strval
(
$link
);
},
$note
[
'links'
]);
}
else
{
$object
[
'links'
]
=
$old
[
'links'
];
}
// clean up HTML content
$object
[
'description'
]
=
$this
->
_wash_html
(
$note
[
'description'
]);
$is_html
=
true
;
// try to be smart and convert to plain-text if no real formatting is detected
if
(
preg_match
(
'!<body><(?:p|pre)>(.*)</(?:p|pre)></body>!Uims'
,
$object
[
'description'
],
$m
))
{
if
(!
preg_match
(
'!<(a|b|i|strong|em|p|span|div|pre|li|img)(
\s
+[a-z]|>)!im'
,
$m
[
1
],
$n
)
||
(
$n
[
1
]
!=
'img'
&&
!
strpos
(
$m
[
1
],
'</'
.
$n
[
1
].
'>'
))
)
{
// $converter = new rcube_html2text($m[1], false, true, 0);
// $object['description'] = rtrim($converter->get_text());
$object
[
'description'
]
=
html_entity_decode
(
preg_replace
(
'!<br(
\s
+/)>!'
,
"
\n
"
,
$m
[
1
]));
$is_html
=
false
;
}
}
// Add proper HTML header, otherwise Kontact renders it as plain text
if
(
$is_html
)
{
$object
[
'description'
]
=
'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">'
.
"
\n
"
.
str_replace
(
'<head>'
,
'<head><meta name="qrichtext" content="1" />'
,
$object
[
'description'
]);
}
// copy meta data (starting with _) from old object
foreach
((
array
)
$old
as
$key
=>
$val
)
{
if
(!
isset
(
$object
[
$key
])
&&
$key
[
0
]
==
'_'
)
$object
[
$key
]
=
$val
;
}
// make list of categories unique
if
(
is_array
(
$object
[
'tags'
]))
{
$object
[
'tags'
]
=
array_unique
(
array_filter
(
$object
[
'tags'
]));
}
unset
(
$object
[
'list'
],
$object
[
'tempid'
],
$object
[
'created'
],
$object
[
'changed'
],
$object
[
'created_'
],
$object
[
'changed_'
]);
return
$object
;
}
/**
* Sanity checks/cleanups HTML content
*/
private
function
_wash_html
(
$html
)
{
// Add header with charset spec., washtml cannot work without that
$html
=
'<html><head>'
.
'<meta http-equiv="Content-Type" content="text/html; charset='
.
RCUBE_CHARSET
.
'" />'
.
'</head><body>'
.
$html
.
'</body></html>'
;
// clean HTML with washtml by Frederic Motte
$wash_opts
=
array
(
'show_washed'
=>
false
,
'allow_remote'
=>
1
,
'charset'
=>
RCUBE_CHARSET
,
'html_elements'
=>
array
(
'html'
,
'head'
,
'meta'
,
'body'
,
'link'
),
'html_attribs'
=>
array
(
'rel'
,
'type'
,
'name'
,
'http-equiv'
),
);
// initialize HTML washer
$washer
=
new
rcube_washtml
(
$wash_opts
);
$washer
->
add_callback
(
'form'
,
array
(
$this
,
'_washtml_callback'
));
$washer
->
add_callback
(
'a'
,
array
(
$this
,
'_washtml_callback'
));
// Remove non-UTF8 characters
$html
=
rcube_charset
::
clean
(
$html
);
$html
=
$washer
->
wash
(
$html
);
// remove unwanted comments (produced by washtml)
$html
=
preg_replace
(
'/<!--[^>]+-->/'
,
''
,
$html
);
return
$html
;
}
/**
* Callback function for washtml cleaning class
*/
public
function
_washtml_callback
(
$tagname
,
$attrib
,
$content
,
$washtml
)
{
switch
(
$tagname
)
{
case
'form'
:
$out
=
html
::
div
(
'form'
,
$content
);
break
;
case
'a'
:
// strip temporary link tags from plain-text markup
$attrib
=
html
::
parse_attrib_string
(
$attrib
);
if
(!
empty
(
$attrib
[
'class'
])
&&
strpos
(
$attrib
[
'class'
],
'x-templink'
)
!==
false
)
{
// remove link entirely
if
(
strpos
(
$attrib
[
'href'
],
html_entity_decode
(
$content
))
!==
false
)
{
$out
=
$content
;
break
;
}
$attrib
[
'class'
]
=
trim
(
str_replace
(
'x-templink'
,
''
,
$attrib
[
'class'
]));
}
$out
=
html
::
a
(
$attrib
,
$content
);
break
;
default
:
$out
=
''
;
}
return
$out
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Sat, Apr 4, 2:42 AM (4 d, 15 h ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ba/52/7aad1619d5e59dfe7537eba41f9a
Default Alt Text
kolab_notes.php (50 KB)
Attached To
Mode
rRPK roundcubemail-plugins-kolab
Attached
Detach File
Event Timeline