[Content] Added privileges checks and playlist update in DB added
authorPiotr Kosko <p.kosko@samsung.com>
Mon, 23 Mar 2015 10:37:49 +0000 (11:37 +0100)
committerRafal Galka <r.galka@samsung.com>
Wed, 1 Apr 2015 07:35:53 +0000 (16:35 +0900)
[Feature] Added privileges checks.
  Fixed playlist.name and playlist.thumbnailURI setters to update values also in DB.
  Still needed to add filesystem-based "if uri points to real file" check.

[Verification] Code compiles without errors.
  Checked in console. Setting name or thumbnailURI always update values in DB.
  TCT results: all/ passed/ failed/ timeout/ Not run
    before: 244/231/12/1/0
    after:  244/232/11/1/0

Change-Id: I9d9bf44294a56d58d54cd23fc68abf58262a013c
Signed-off-by: Piotr Kosko <p.kosko@samsung.com>
src/content/content_instance.cc
src/content/content_instance.h
src/content/content_manager.cc
src/content/content_manager.h
src/content/js/playlist.js

index e9d9a4f49fa8fa6977655589f17d79f69b803226..a43a400aff683cda8319b2a28eeac054dc2eb81e 100755 (executable)
@@ -22,7 +22,8 @@ namespace content {
 
 namespace {
 // The privileges that required in Content API
-const std::string kPrivilegeContent = "";
+const std::string kPrivilegeContentRead = "http://tizen.org/privilege/content.read";
+const std::string kPrivilegeContentWrite = "http://tizen.org/privilege/content.write";
 
 } // namespace
 
@@ -55,6 +56,10 @@ ContentInstance::ContentInstance() {
   REGISTER_SYNC("ContentPlaylist_move", ContentManagerPlaylistMove);
   REGISTER_SYNC("ContentManager_getLyrics", ContentManagerAudioGetLyrics);
 
+  REGISTER_SYNC("ContentPlaylist_getName", PlaylistGetName);
+  REGISTER_SYNC("ContentPlaylist_setName", PlaylistSetName);
+  REGISTER_SYNC("ContentPlaylist_getThumbnailUri", PlaylistGetThumbnailUri);
+  REGISTER_SYNC("ContentPlaylist_setThumbnailUri", PlaylistSetThumbnailUri);
   #undef REGISTER_SYNC
 }
 
@@ -224,6 +229,7 @@ static void changedContentCallback(media_content_error_e error,
 
 
 void ContentInstance::ContentManagerUpdate(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   int ret;
   if (ContentManager::getInstance()->isConnected()) {
     ret = ContentManager::getInstance()->update(args);
@@ -237,6 +243,7 @@ void ContentInstance::ContentManagerUpdate(const picojson::value& args, picojson
 
 void ContentInstance::ContentManagerUpdatebatch(const picojson::value& args, picojson::object& out) {
   LoggerE("entered");
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   double callbackId = args.get("callbackId").get<double>();
 
   auto cbData = std::shared_ptr<ReplyCallbackData>(new ReplyCallbackData);
@@ -272,6 +279,7 @@ void ContentInstance::ContentManagerGetdirectories(const picojson::value& args,
 
 }
 void ContentInstance::ContentManagerFind(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentRead, &out);
   CHECK_EXIST(args, "callbackId", out)
 
   double callbackId = args.get("callbackId").get<double>();
@@ -290,6 +298,7 @@ void ContentInstance::ContentManagerFind(const picojson::value& args, picojson::
 
 }
 void ContentInstance::ContentManagerScanfile(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   CHECK_EXIST(args, "callbackId", out)
   CHECK_EXIST(args, "contentURI", out)
 
@@ -309,6 +318,7 @@ void ContentInstance::ContentManagerScanfile(const picojson::value& args, picojs
 
 void ContentInstance::ContentManagerSetchangelistener(const picojson::value& args,
                                                       picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentRead, &out);
   CHECK_EXIST(args, "listenerId", out)
 
   ReplyCallbackData* cbData = new ReplyCallbackData();
@@ -327,12 +337,13 @@ void ContentInstance::ContentManagerSetchangelistener(const picojson::value& arg
 }
 
 void ContentInstance::ContentManagerUnsetchangelistener(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentRead, &out);
   if (ContentManager::getInstance()->unSetChangeListener().IsError()) {
     LoggerD("unsuccesfull deregistering of callback");
   }
 }
-
 void ContentInstance::ContentManagerGetplaylists(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentRead, &out);
   CHECK_EXIST(args, "callbackId", out)
 
   double callbackId = args.get("callbackId").get<double>();
@@ -354,6 +365,7 @@ void ContentInstance::ContentManagerGetplaylists(const picojson::value& args, pi
 
 }
 void ContentInstance::ContentManagerCreateplaylist(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   CHECK_EXIST(args, "callbackId", out)
   CHECK_EXIST(args, "name", out)
 
@@ -375,6 +387,7 @@ void ContentInstance::ContentManagerCreateplaylist(const picojson::value& args,
   common::TaskQueue::GetInstance().Queue<ReplyCallbackData>(WorkThread, CompletedCallback, cbData);
 }
 void ContentInstance::ContentManagerRemoveplaylist(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   double callbackId = args.get("callbackId").get<double>();
 
   auto cbData = std::shared_ptr<ReplyCallbackData>(new ReplyCallbackData);
@@ -395,6 +408,7 @@ void ContentInstance::ContentManagerRemoveplaylist(const picojson::value& args,
 }
 
 void ContentInstance::ContentManagerPlaylistAdd(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   int ret;
   if(ContentManager::getInstance()->isConnected()) {
     std::string playlist_id = args.get("playlistId").get<std::string>();
@@ -410,6 +424,7 @@ void ContentInstance::ContentManagerPlaylistAdd(const picojson::value& args, pic
 }
 
 void ContentInstance::ContentManagerPlaylistAddbatch(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   LoggerE("entered");
   double callbackId = args.get("callbackId").get<double>();
 
@@ -429,6 +444,7 @@ void ContentInstance::ContentManagerPlaylistAddbatch(const picojson::value& args
 
 
 void ContentInstance::ContentManagerPlaylistGet(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentRead, &out);
   LoggerE("entered");
   double callbackId = args.get("callbackId").get<double>();
 
@@ -447,6 +463,7 @@ void ContentInstance::ContentManagerPlaylistGet(const picojson::value& args, pic
 }
 
 void ContentInstance::ContentManagerPlaylistRemove(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   int ret;
   if(ContentManager::getInstance()->isConnected()) {
     std::string playlist_id = args.get("playlistId").get<std::string>();
@@ -462,6 +479,7 @@ void ContentInstance::ContentManagerPlaylistRemove(const picojson::value& args,
 }
 
 void ContentInstance::ContentManagerPlaylistRemovebatch(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   LoggerE("entered");
   double callbackId = args.get("callbackId").get<double>();
 
@@ -481,6 +499,7 @@ void ContentInstance::ContentManagerPlaylistRemovebatch(const picojson::value& a
 
 
 void ContentInstance::ContentManagerPlaylistSetorder(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   LoggerE("entered");
   double callbackId = args.get("callbackId").get<double>();
 
@@ -499,6 +518,7 @@ void ContentInstance::ContentManagerPlaylistSetorder(const picojson::value& args
 }
 
 void ContentInstance::ContentManagerPlaylistMove(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
   LoggerE("entered");
   double callbackId = args.get("callbackId").get<double>();
 
@@ -534,6 +554,62 @@ void ContentInstance::ContentManagerAudioGetLyrics(const picojson::value& args,
   }
 }
 
+void ContentInstance::PlaylistGetName(const picojson::value& args, picojson::object& out) {
+  int ret;
+  CHECK_EXIST(args, "id", out)
+  int id = static_cast<int>(args.get("id").get<double>());
+  std::string name;
+  ret = ContentManager::getInstance()->getPlaylistName(id, &name);
+  if(ret != MEDIA_CONTENT_ERROR_NONE) {
+    ReportError(ContentManager::getInstance()->convertError(ret), &out);
+  } else {
+    ReportSuccess(picojson::value(name),out);
+  }
+}
+
+void ContentInstance::PlaylistSetName(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
+  int ret;
+  CHECK_EXIST(args, "id", out)
+  CHECK_EXIST(args, "name", out)
+  int id = static_cast<int>(args.get("id").get<double>());
+  std::string name = args.get("name").get<std::string>();
+  ret = ContentManager::getInstance()->setPlaylistName(id, name);
+  if(ret != MEDIA_CONTENT_ERROR_NONE) {
+    ReportError(ContentManager::getInstance()->convertError(ret), &out);
+  } else {
+    ReportSuccess(out);
+  }
+}
+
+void ContentInstance::PlaylistGetThumbnailUri(const picojson::value& args, picojson::object& out) {
+  int ret;
+  CHECK_EXIST(args, "id", out)
+  int id = static_cast<int>(args.get("id").get<double>());
+  std::string uri;
+  ret = ContentManager::getInstance()->getThumbnailUri(id, &uri);
+  if(ret != MEDIA_CONTENT_ERROR_NONE) {
+    ReportError(ContentManager::getInstance()->convertError(ret), &out);
+  } else {
+    ReportSuccess(picojson::value(uri),out);
+  }
+}
+
+void ContentInstance::PlaylistSetThumbnailUri(const picojson::value& args, picojson::object& out) {
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeContentWrite, &out);
+  int ret;
+  CHECK_EXIST(args, "id", out)
+  CHECK_EXIST(args, "uri", out)
+  int id = static_cast<int>(args.get("id").get<double>());
+  std::string uri = args.get("uri").get<std::string>();
+  ret = ContentManager::getInstance()->setThumbnailUri(id, uri);
+  if(ret != MEDIA_CONTENT_ERROR_NONE) {
+    ReportError(ContentManager::getInstance()->convertError(ret), &out);
+  } else {
+    ReportSuccess(out);
+  }
+}
+
 #undef CHECK_EXIST
 
 } // namespace content
index 2483ac6b224101d5df2450b19354d1236f4ca0cc..2626d37d4bec6c6b836317ea322168146893928e 100755 (executable)
@@ -54,7 +54,10 @@ class ContentInstance : public common::ParsedInstance {
   void ContentManagerPlaylistMove(const picojson::value& args, picojson::object& out);
   void ContentManagerAudioGetLyrics(const picojson::value& args, picojson::object& out);
 
-//
+  void PlaylistGetName(const picojson::value& args, picojson::object& out);
+  void PlaylistSetName(const picojson::value& args, picojson::object& out);
+  void PlaylistGetThumbnailUri(const picojson::value& args, picojson::object& out);
+  void PlaylistSetThumbnailUri(const picojson::value& args, picojson::object& out);
 };
 
 typedef struct _ReplyCallbackData{
index 463d7d70e3ad84857b3362fb0d83a371a74799e5..a21345dc7ea418b9b5f9c272e32fae0519d0251d 100755 (executable)
@@ -24,6 +24,11 @@ using namespace common;
 namespace extension {
 namespace content {
 
+namespace {
+static const std::string uri_prefix = "file://";
+static const std::string uri_absolute_prefix = "file:///";
+}
+
 const std::map<std::string, media_content_orientation_e> orientationMap = {
     {"NORMAL", MEDIA_CONTENT_ORIENTATION_NORMAL},
     {"FLIP_HORIZONTAL", MEDIA_CONTENT_ORIENTATION_HFLIP},
@@ -1141,6 +1146,155 @@ int ContentManager::getLyrics(const picojson::value& args, picojson::object& res
   return ret;
 }
 
+media_playlist_h getPlaylistHandle(int id)
+{
+  LoggerD("Entered");
+  media_playlist_h playlist_handle = nullptr;
+  int ret_code = media_playlist_get_playlist_from_db(id, &playlist_handle);
+  if(MEDIA_CONTENT_ERROR_NONE != ret_code ||
+      playlist_handle == nullptr) {
+    LoggerE("could not get playlist handle for id: %d", id);
+    return nullptr;
+  }
+
+  return playlist_handle;
+}
+
+void destroyMediaPlaylistHandle(media_playlist_h& playlist_handle)
+{
+  LoggerD("Entered");
+  if(playlist_handle) {
+    int ret_code = media_playlist_destroy(playlist_handle);
+    playlist_handle = nullptr;
+
+    if(MEDIA_CONTENT_ERROR_NONE != ret_code) {
+      LoggerE("media_playlist_destroy failed");
+    }
+  }
+}
+
+int ContentManager::getPlaylistName(int id, std::string* result) {
+  LoggerD("Entered");
+  media_playlist_h playlist_handle = getPlaylistHandle(id);
+  PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
+
+  char* tmp_playlist_name = nullptr;
+  const int ret_code = media_playlist_get_name(playlist_handle, &tmp_playlist_name);
+
+  if(MEDIA_CONTENT_ERROR_NONE != ret_code) {
+    LoggerE("media_playlist_get_name failed");
+    return TIZEN_ERROR_UNKNOWN;
+  }
+
+  std::string playlist_name;
+  if(tmp_playlist_name) {
+    playlist_name = tmp_playlist_name;
+    free(tmp_playlist_name);
+    tmp_playlist_name = nullptr;
+  }
+
+  *result = playlist_name;
+  return MEDIA_CONTENT_ERROR_NONE;
+}
+
+int updatePlaylistInDB(media_playlist_h playlist_handle)
+{
+  LoggerD("Entered");
+  int ret_code = media_playlist_update_to_db(playlist_handle);
+  if(MEDIA_CONTENT_ERROR_NONE != ret_code) {
+    LoggerE("media_playlist_update_to_db failed");
+    return TIZEN_ERROR_UNKNOWN;
+  }
+  return MEDIA_CONTENT_ERROR_NONE;
+}
+
+int ContentManager::setPlaylistName(int id, const std::string& name)
+{
+  LoggerD("Entered");
+  if(name.empty()) {
+    LoggerE("Cannot set empty playlist name!");
+    return MEDIA_CONTENT_ERROR_INVALID_PARAMETER;
+  }
+
+  media_playlist_h playlist_handle = getPlaylistHandle(id);
+  PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
+
+  const int ret_code = media_playlist_set_name(playlist_handle, name.c_str());
+  if(MEDIA_CONTENT_ERROR_NONE != ret_code) {
+    LoggerE("media_playlist_set_name failed");
+    //Setting name that is used by other playlist does not return bad error code here.
+    //MEDIA_CONTENT_ERROR_INVALID_OPERATION is being returned in updatePlaylistInDB
+    return TIZEN_ERROR_UNKNOWN;
+  }
+
+  int ret = updatePlaylistInDB(playlist_handle);
+  if (MEDIA_CONTENT_ERROR_NONE != ret) {
+    LoggerE("Error while updating playlist: %d", ret);
+    if (MEDIA_CONTENT_ERROR_INVALID_OPERATION == ret) {
+      //We could fetch list of playlists and check if other playlist is using this
+      //name, but that seems to be to much work in synchronous method
+      LoggerE("Playlist name: %s is probably already used", name.c_str());
+    }
+    return ret;
+  }
+  return MEDIA_CONTENT_ERROR_NONE;
+}
+
+int ContentManager::getThumbnailUri(int id, std::string* result)
+{
+  LoggerD("Entered");
+  media_playlist_h playlist_handle = getPlaylistHandle(id);
+  PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
+
+  char* tmp_playlist_thb_path = nullptr;
+  const int ret_code = media_playlist_get_thumbnail_path(playlist_handle, &tmp_playlist_thb_path);
+
+  if(MEDIA_CONTENT_ERROR_NONE != ret_code) {
+    LoggerE("media_playlist_get_name failed");
+    return TIZEN_ERROR_UNKNOWN;
+  }
+
+  std::string playlist_thb_path;
+  if(tmp_playlist_thb_path) {
+    playlist_thb_path = tmp_playlist_thb_path;
+    free(tmp_playlist_thb_path);
+    tmp_playlist_thb_path = nullptr;
+  }
+
+  *result = playlist_thb_path;
+  return MEDIA_CONTENT_ERROR_NONE;
+}
+
+int ContentManager::setThumbnailUri(int id, const std::string& thb_uri)
+{
+  LoggerD("Entered");
+
+  //Allow setting empty URI, unfortunately Core API does not allow to set empty
+  //path so we need to set one empty space. This is probably issue of Core API.
+  if(!thb_uri.empty() && " " != thb_uri) {
+    /// TODO what if uri holds virtual path
+    if(thb_uri.find(uri_absolute_prefix) != 0) {
+      LoggerE("thumbnail URI is not valid: [%s]", thb_uri.c_str());
+      return MEDIA_CONTENT_ERROR_INVALID_PARAMETER;
+    }
+
+    std::string absoulte_path = thb_uri.substr(uri_prefix.length());
+    /// TODO check if uri points an existing file
+  }
+
+  media_playlist_h playlist_handle = getPlaylistHandle(id);
+  PlaylistUniquePtr playlist_ptr(playlist_handle, destroyMediaPlaylistHandle);
+
+  const int ret_code = media_playlist_set_thumbnail_path(playlist_handle,
+                                                         thb_uri.c_str());
+  if(MEDIA_CONTENT_ERROR_NONE != ret_code) {
+    LoggerE("media_playlist_set_thumbnail_path failed");
+    return TIZEN_ERROR_UNKNOWN;
+  }
+
+  int ret = updatePlaylistInDB(playlist_handle);
+  return ret;
+}
 
 PlatformResult ContentManager::convertError(int err) {
   switch (err) {
index d78877f0e018328b57f981217b642489821a6f63..507044329efdf636d61fc8f0e575f6bd5e7bd0dc 100755 (executable)
@@ -19,6 +19,9 @@
 namespace extension {
 namespace content {
 
+typedef std::unique_ptr<std::remove_pointer<media_playlist_h>::type,
+    void (*)(media_playlist_h&)> PlaylistUniquePtr;
+
 void ContentToJson(media_info_h info, picojson::object& o);
 
 class ContentManager {
@@ -51,6 +54,12 @@ class ContentManager {
   void playlistSetOrder(const std::shared_ptr<ReplyCallbackData>& user_data);
   void playlistMove(const std::shared_ptr<ReplyCallbackData>& user_data);
 
+  int getPlaylistName(int id, std::string* result);
+  int setPlaylistName(int id, const std::string& name);
+
+  int getThumbnailUri(int id, std::string* result);
+  int setThumbnailUri(int id, const std::string& thb_uri);
+
 //playlistSetOrder
   common::PlatformResult convertError(int err);
  private:
index e75cbf270f523a5a806fc6231b6181e9055e687f..141a4245289c5055477b8016b01efc7525d74eae 100644 (file)
@@ -5,9 +5,7 @@
 function Playlist(data) {
   var editableAttributes = ['name', 'thumbnailURI'];
   var id;
-  var name;
   var numberOfTracks;
-  var thumbnailURI = null;
 
   Object.defineProperties(this, {
     editableAttributes: {
@@ -28,11 +26,20 @@ function Playlist(data) {
     },
     name: {
       get: function() {
-        return name;
+        var result = native_.callSync('ContentPlaylist_getName', {'id' : Number(id)});
+        if (native_.isFailure(result)) {
+          throw native_.getErrorObject(result);
+        }
+        return native_.getResultObject(result);
       },
       set: function(v) {
         if (!type_.isNull(v)) {
-          name = converter_.toString(v, false);
+          var name = converter_.toString(v, false);
+          var result = native_.callSync('ContentPlaylist_setName',
+                  {'id' : Number(id), 'name' : name});
+          if (native_.isFailure(result)) {
+            throw native_.getErrorObject(result);
+          }
         }
       },
       enumerable: true
@@ -50,10 +57,26 @@ function Playlist(data) {
     },
     thumbnailURI: {
       get: function() {
-        return thumbnailURI;
+        var result = native_.callSync('ContentPlaylist_getThumbnailUri', {'id' : Number(id)});
+        if (native_.isFailure(result)) {
+          throw native_.getErrorObject(result);
+        }
+        var res = native_.getResultObject(result);
+        //CoreAPI not support empty thumbnail, so one space must be used instead null thumbnail
+        return res === " " ? null : res;
       },
       set: function(v) {
-        thumbnailURI = converter_.toString(v, true);
+        var thumbnailURI = converter_.toString(v, true);
+        if (type_.isNullOrUndefined(thumbnailURI)) {
+          //CoreAPI not support empty thumbnail, so one space must be used instead null thumbnail
+          thumbnailURI = " ";
+        }
+        //TODO probably thumbnailURI should be converted here to absolute uri in case of virtual
+        var result = native_.callSync('ContentPlaylist_setThumbnailUri',
+                {'id' : Number(id), 'uri' : thumbnailURI});
+        if (native_.isFailure(result)) {
+          throw native_.getErrorObject(result);
+        }
       },
       enumerable: true
     },