package org.xmlrpc.android; import android.content.Context; import android.os.AsyncTask; import android.util.Xml; import com.android.volley.NetworkResponse; import com.android.volley.Request; import com.android.volley.ServerError; import com.android.volley.toolbox.RequestFuture; import com.android.volley.toolbox.StringRequest; import com.google.gson.Gson; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.StringReader; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Vector; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.SSLHandshakeException; import kr.kdev.dg1s.biowiki.BioWiki; import kr.kdev.dg1s.biowiki.datasets.CommentTable; import kr.kdev.dg1s.biowiki.models.Blog; import kr.kdev.dg1s.biowiki.models.BlogIdentifier; import kr.kdev.dg1s.biowiki.models.Comment; import kr.kdev.dg1s.biowiki.models.CommentList; import kr.kdev.dg1s.biowiki.models.FeatureSet; import kr.kdev.dg1s.biowiki.models.MediaFile; import kr.kdev.dg1s.biowiki.ui.media.MediaGridFragment.Filter; import kr.kdev.dg1s.biowiki.ui.posts.PostsListFragment; import kr.kdev.dg1s.biowiki.util.AppLog; import kr.kdev.dg1s.biowiki.util.AppLog.T; import kr.kdev.dg1s.biowiki.util.DateTimeUtils; import kr.kdev.dg1s.biowiki.util.MapUtils; import kr.kdev.dg1s.biowiki.util.NetworkUtils; public class ApiHelper { public static final Map<String, String> wikiOptionsXMLRPCParameters = new HashMap<String, String>(); /** * Regex pattern for matching the RSD link found in BioWiki sites. */ private static final Pattern rsdLink = Pattern.compile( "<link\\s*?rel=\"EditURI\"\\s*?type=\"application/rsd\\+xml\"\\s*?title=\"RSD\"\\s*?href=\"(.*?)\"", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); static { wikiOptionsXMLRPCParameters.put("software_version", "software_version"); wikiOptionsXMLRPCParameters.put("post_thumbnail", "post_thumbnail"); wikiOptionsXMLRPCParameters.put("jetpack_client_id", "jetpack_client_id"); wikiOptionsXMLRPCParameters.put("blog_public", "blog_public"); wikiOptionsXMLRPCParameters.put("home_url", "home_url"); wikiOptionsXMLRPCParameters.put("admin_url", "admin_url"); wikiOptionsXMLRPCParameters.put("login_url", "login_url"); } public static synchronized void updateWikiOptions(Blog currentBlog, Map<?, ?> blogOptions) { boolean isModified = false; Gson gson = new Gson(); String blogOptionsJson = gson.toJson(blogOptions); if (blogOptionsJson != null) { isModified |= currentBlog.bsetBlogOptions(blogOptionsJson); } // Software version if (!currentBlog.isDotcomFlag()) { Map<?, ?> sv = (HashMap<?, ?>) blogOptions.get("software_version"); String wpVersion = MapUtils.getMapStr(sv, "value"); if (wpVersion.length() > 0) { isModified |= currentBlog.bsetWpVersion(wpVersion); } } // Featured image support Map<?, ?> featuredImageHash = (HashMap<?, ?>) blogOptions.get("post_thumbnail"); if (featuredImageHash != null) { boolean featuredImageCapable = MapUtils.getMapBool(featuredImageHash, "value"); isModified |= currentBlog.bsetFeaturedImageCapable(featuredImageCapable); } else { isModified |= currentBlog.bsetFeaturedImageCapable(false); } if (isModified) { BioWiki.wpDB.saveBlog(currentBlog); } } /** * request deleted comments for passed blog and remove them from local db * * @param blog blog to check * @return count of comments that were removed from db */ public static int removeDeletedComments(Blog blog) { if (blog == null) { return 0; } XMLRPCClientInterface client = XMLRPCFactory.instantiate( blog.getUri(), blog.getHttpuser(), blog.getHttppassword()); Map<String, Object> hPost = new HashMap<String, Object>(); hPost.put("status", "trash"); Object[] params = {blog.getRemoteBlogId(), blog.getUsername(), blog.getPassword(), hPost}; int numDeleted = 0; try { Object[] result = (Object[]) client.call("wp.getComments", params); if (result == null || result.length == 0) { return 0; } Map<?, ?> contentHash; for (Object aComment : result) { contentHash = (Map<?, ?>) aComment; long commentId = Long.parseLong(contentHash.get("comment_id").toString()); if (CommentTable.deleteComment(blog.getLocalTableBlogId(), commentId)) numDeleted++; } if (numDeleted > 0) { AppLog.d(T.COMMENTS, String.format("removed %d deleted comments", numDeleted)); } } catch (XMLRPCException e) { AppLog.e(T.COMMENTS, e); } catch (IOException e) { AppLog.e(T.COMMENTS, e); } catch (XmlPullParserException e) { AppLog.e(T.COMMENTS, e); } return numDeleted; } public static CommentList refreshComments(Context context, Blog blog, Object[] commentParams) throws XMLRPCException, IOException, XmlPullParserException { if (blog == null) { return null; } XMLRPCClientInterface client = XMLRPCFactory.instantiate(blog.getUri(), blog.getHttpuser(), blog.getHttppassword()); Object[] result; result = (Object[]) client.call("wp.getComments", commentParams); if (result.length == 0) { return null; } Map<?, ?> contentHash; long commentID, postID; String authorName, content, status, authorEmail, authorURL, postTitle, pubDate; java.util.Date date; CommentList comments = new CommentList(); for (int ctr = 0; ctr < result.length; ctr++) { contentHash = (Map<?, ?>) result[ctr]; content = contentHash.get("content").toString(); status = contentHash.get("status").toString(); postID = Long.parseLong(contentHash.get("post_id").toString()); commentID = Long.parseLong(contentHash.get("comment_id").toString()); authorName = contentHash.get("author").toString(); authorURL = contentHash.get("author_url").toString(); authorEmail = contentHash.get("author_email").toString(); postTitle = contentHash.get("post_title").toString(); date = (java.util.Date) contentHash.get("date_created_gmt"); pubDate = DateTimeUtils.javaDateToIso8601(date); Comment comment = new Comment( postID, commentID, authorName, pubDate, content, status, postTitle, authorURL, authorEmail, null); comments.add(comment); } int localBlogId = blog.getLocalTableBlogId(); CommentTable.saveComments(localBlogId, comments); return comments; } /** * Discover the XML-RPC endpoint for the BioWiki API associated with the specified blog URL. * * @param urlString URL of the blog to get the XML-RPC endpoint for. * @return XML-RPC endpoint for the specified blog, or null if unable to discover endpoint. */ public static String getXMLRPCUrl(String urlString) throws SSLHandshakeException { Pattern xmlrpcLink = Pattern.compile("<api\\s*?name=\"WordPress\".*?apiLink=\"(.*?)\"", Pattern.CASE_INSENSITIVE | Pattern.DOTALL); String html = getResponse(urlString); if (html != null) { Matcher matcher = xmlrpcLink.matcher(html); if (matcher.find()) { return matcher.group(1); } } return null; // never found the rsd tag } /** * Synchronous method to fetch the String content at the specified URL. * * @param url URL to fetch contents for. * @return content of the resource, or null if URL was invalid or resource could not be retrieved. */ public static String getResponse(final String url) throws SSLHandshakeException { return getResponse(url, 3); } private static String getResponse(final String url, int maxRedirection) throws SSLHandshakeException { RequestFuture<String> requestFuture = RequestFuture.newFuture(); StringRequest stringRequest = new StringRequest(Request.Method.GET, url, requestFuture, requestFuture); BioWiki.requestQueue.add(stringRequest); try { // Wait for the response return requestFuture.get(30, TimeUnit.SECONDS); } catch (InterruptedException e) { AppLog.e(T.API, e); return null; } catch (ExecutionException e) { if (e.getCause() instanceof ServerError) { NetworkResponse networkResponse = ((ServerError) e.getCause()).networkResponse; if ((networkResponse != null) && (networkResponse.statusCode == HttpURLConnection.HTTP_MOVED_PERM || networkResponse.statusCode == HttpURLConnection.HTTP_MOVED_TEMP)) { String newUrl = networkResponse.headers.get("Location"); if (maxRedirection > 0) { return getResponse(newUrl, maxRedirection - 1); } } } if (e.getCause() != null && e.getCause().getCause() instanceof SSLHandshakeException) { throw (SSLHandshakeException) e.getCause().getCause(); } AppLog.e(T.API, e); return null; } catch (TimeoutException e) { AppLog.e(T.API, e); return null; } } /** * Returns RSD URL based on regex match * * @param urlString * @return String RSD url */ public static String getRSDMetaTagHrefRegEx(String urlString) throws SSLHandshakeException { String html = ApiHelper.getResponse(urlString); if (html != null) { Matcher matcher = rsdLink.matcher(html); if (matcher.find()) { String href = matcher.group(1); return href; } } return null; } /** * Returns RSD URL based on html tag search * * @param urlString * @return String RSD url */ public static String getRSDMetaTagHref(String urlString) throws SSLHandshakeException { // get the html code String data = ApiHelper.getResponse(urlString); // parse the html and get the attribute for xmlrpc endpoint if (data != null) { StringReader stringReader = new StringReader(data); XmlPullParser parser = Xml.newPullParser(); try { // auto-detect the encoding from the stream parser.setInput(stringReader); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { String name = null; String rel = ""; String type = ""; String href = ""; switch (eventType) { case XmlPullParser.START_TAG: name = parser.getName(); if (name.equalsIgnoreCase("link")) { for (int i = 0; i < parser.getAttributeCount(); i++) { String attrName = parser.getAttributeName(i); String attrValue = parser.getAttributeValue(i); if (attrName.equals("rel")) { rel = attrValue; } else if (attrName.equals("type")) type = attrValue; else if (attrName.equals("href")) href = attrValue; } if (rel.equals("EditURI") && type.equals("application/rsd+xml")) { return href; } // currentMessage.setLink(parser.nextText()); } break; } eventType = parser.next(); } } catch (XmlPullParserException e) { AppLog.e(T.API, e); return null; } catch (IOException e) { AppLog.e(T.API, e); return null; } } return null; // never found the rsd tag } public enum ErrorType { NO_ERROR, INVALID_CURRENT_BLOG, NETWORK_XMLRPC, INVALID_CONTEXT, INVALID_RESULT, NO_UPLOAD_FILES_CAP, CAST_EXCEPTION, TASK_CANCELLED } public interface GenericErrorCallback { public void onFailure(ErrorType errorType, String errorMessage, Throwable throwable); } public interface GenericCallback extends GenericErrorCallback { public void onSuccess(); } public static abstract class HelperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { protected String mErrorMessage; protected ErrorType mErrorType = ErrorType.NO_ERROR; protected Throwable mThrowable; protected void setError(ErrorType errorType, String errorMessage) { mErrorMessage = errorMessage; mErrorType = errorType; AppLog.e(T.API, mErrorType.name() + " - " + mErrorMessage); } protected void setError(ErrorType errorType, String errorMessage, Throwable throwable) { mErrorMessage = errorMessage; mErrorType = errorType; mThrowable = throwable; AppLog.e(T.API, mErrorType.name() + " - " + mErrorMessage, throwable); } } public static class GetPostFormatsTask extends HelperAsyncTask<java.util.List<?>, Void, Object> { private Blog mBlog; @Override protected Object doInBackground(List<?>... args) { List<?> arguments = args[0]; mBlog = (Blog) arguments.get(0); XMLRPCClientInterface client = XMLRPCFactory.instantiate(mBlog.getUri(), mBlog.getHttpuser(), mBlog.getHttppassword()); Object result = null; Object[] params = {mBlog.getRemoteBlogId(), mBlog.getUsername(), mBlog.getPassword(), "show-supported"}; try { result = client.call("wp.getPostFormats", params); } catch (ClassCastException cce) { setError(ErrorType.INVALID_RESULT, cce.getMessage(), cce); } catch (XMLRPCException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (IOException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (XmlPullParserException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } return result; } protected void onPostExecute(Object result) { if (result != null && result instanceof HashMap) { Map<?, ?> postFormats = (HashMap<?, ?>) result; if (postFormats.size() > 0) { Gson gson = new Gson(); String postFormatsJson = gson.toJson(postFormats); if (postFormatsJson != null) { if (mBlog.bsetPostFormats(postFormatsJson)) { BioWiki.wpDB.saveBlog(mBlog); } } } } } } /** * Task to refresh blog level information (WP version number) and stuff * related to the active theme (available post types, recent comments, etc). */ public static class RefreshBlogContentTask extends HelperAsyncTask<Boolean, Void, Boolean> { private static HashSet<BlogIdentifier> refreshedBlogs = new HashSet<BlogIdentifier>(); private Blog mBlog; private Context mContext; private BlogIdentifier mBlogIdentifier; private GenericCallback mCallback; public RefreshBlogContentTask(Context context, Blog blog, GenericCallback callback) { if (context == null || blog == null) { cancel(true); return; } if (!NetworkUtils.isNetworkAvailable(context)) { cancel(true); return; } mBlogIdentifier = new BlogIdentifier(blog.getUrl(), blog.getRemoteBlogId()); if (refreshedBlogs.contains(mBlogIdentifier)) { cancel(true); } else { refreshedBlogs.add(mBlogIdentifier); } mBlog = blog; mContext = context; mCallback = callback; } private void updateBlogAdmin(Map<String, Object> userInfos) { if (userInfos.containsKey("roles") && (userInfos.get("roles") instanceof Object[])) { boolean isAdmin = false; Object[] userRoles = (Object[]) userInfos.get("roles"); for (int i = 0; i < userRoles.length; i++) { if (userRoles[i].toString().equals("administrator")) { isAdmin = true; break; } } if (mBlog.bsetAdmin(isAdmin)) { BioWiki.wpDB.saveBlog(mBlog); } } } @Override protected Boolean doInBackground(Boolean... params) { boolean commentsOnly = params[0]; XMLRPCClientInterface client = XMLRPCFactory.instantiate(mBlog.getUri(), mBlog.getHttpuser(), mBlog.getHttppassword()); if (!commentsOnly) { // check the WP number if self-hosted Map<String, String> hPost = ApiHelper.wikiOptionsXMLRPCParameters; Object[] vParams = {mBlog.getRemoteBlogId(), mBlog.getUsername(), mBlog.getPassword(), hPost}; Object versionResult = null; try { versionResult = client.call("wp.getOptions", vParams); } catch (ClassCastException cce) { setError(ErrorType.INVALID_RESULT, cce.getMessage(), cce); return false; } catch (Exception e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); return false; } if (versionResult != null) { Map<?, ?> blogOptions = (HashMap<?, ?>) versionResult; ApiHelper.updateWikiOptions(mBlog, blogOptions); } // get theme post formats List<Object> args = new Vector<Object>(); args.add(mBlog); args.add(mContext); new GetPostFormatsTask().execute(args); } // Check if user is an admin Object[] userParams = {mBlog.getRemoteBlogId(), mBlog.getUsername(), mBlog.getPassword()}; try { Map<String, Object> userInfos = (HashMap<String, Object>) client.call("wp.getProfile", userParams); updateBlogAdmin(userInfos); } catch (ClassCastException cce) { setError(ErrorType.INVALID_RESULT, cce.getMessage(), cce); return false; } catch (XMLRPCException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (IOException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (XmlPullParserException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } // refresh the comments Map<String, Object> hPost = new HashMap<String, Object>(); hPost.put("number", 30); Object[] commentParams = {mBlog.getRemoteBlogId(), mBlog.getUsername(), mBlog.getPassword(), hPost}; try { ApiHelper.refreshComments(mContext, mBlog, commentParams); } catch (Exception e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); return false; } return true; } @Override protected void onPostExecute(Boolean success) { if (mCallback != null) { if (success) { mCallback.onSuccess(); } else { mCallback.onFailure(mErrorType, mErrorMessage, mThrowable); } } refreshedBlogs.remove(mBlogIdentifier); } } public static class FetchPostsTask extends HelperAsyncTask<java.util.List<?>, Boolean, Boolean> { private Callback mCallback; private String mErrorMessage; private int mPostCount; public FetchPostsTask(Callback callback) { mCallback = callback; } @Override protected Boolean doInBackground(List<?>... params) { List<?> arguments = params[0]; Blog blog = (Blog) arguments.get(0); if (blog == null) return false; boolean isPage = (Boolean) arguments.get(1); int recordCount = (Integer) arguments.get(2); boolean loadMore = (Boolean) arguments.get(3); XMLRPCClient client = new XMLRPCClient(blog.getUrl(), blog.getHttpuser(), blog.getHttppassword()); Object[] result; Object[] xmlrpcParams = {blog.getRemoteBlogId(), blog.getUsername(), blog.getPassword(), recordCount}; try { result = (Object[]) client.call((isPage) ? "wp.getPages" : "metaWeblog.getRecentPosts", xmlrpcParams); if (result != null && result.length > 0) { mPostCount = result.length; List<Map<?, ?>> postsList = new ArrayList<Map<?, ?>>(); if (!loadMore) { BioWiki.wpDB.deleteUploadedPosts( blog.getLocalTableBlogId(), isPage); } // If we're loading more posts, only save the posts at the end of the array. // NOTE: Switching to wp.getPosts wouldn't require janky solutions like this // since it allows for an offset parameter. int startPosition = 0; if (loadMore && result.length > PostsListFragment.POSTS_REQUEST_COUNT) { startPosition = result.length - PostsListFragment.POSTS_REQUEST_COUNT; } for (int ctr = startPosition; ctr < result.length; ctr++) { Map<?, ?> postMap = (Map<?, ?>) result[ctr]; postsList.add(postMap); } BioWiki.wpDB.savePosts(postsList, blog.getLocalTableBlogId(), isPage, !loadMore); } return true; } catch (XMLRPCException e) { mErrorMessage = e.getMessage(); } catch (IOException e) { mErrorMessage = e.getMessage(); } catch (XmlPullParserException e) { mErrorMessage = e.getMessage(); } return false; } @Override protected void onCancelled() { super.onCancelled(); mCallback.onFailure(ErrorType.TASK_CANCELLED, mErrorMessage, mThrowable); } @Override protected void onPostExecute(Boolean success) { if (mCallback != null) { if (success) { mCallback.onSuccess(mPostCount); } else { mCallback.onFailure(mErrorType, mErrorMessage, mThrowable); } } } public interface Callback extends GenericErrorCallback { public void onSuccess(int postCount); } } /** * Fetch a single post or page from the XML-RPC API and save/update it in the DB */ public static class FetchSinglePostTask extends HelperAsyncTask<java.util.List<?>, Boolean, Boolean> { private Callback mCallback; private String mErrorMessage; public FetchSinglePostTask(Callback callback) { mCallback = callback; } @Override protected Boolean doInBackground(List<?>... params) { List<?> arguments = params[0]; Blog blog = (Blog) arguments.get(0); if (blog == null) return false; String postId = (String) arguments.get(1); boolean isPage = (Boolean) arguments.get(2); XMLRPCClient client = new XMLRPCClient(blog.getUrl(), blog.getHttpuser(), blog.getHttppassword()); Object[] apiParams; if (isPage) { apiParams = new Object[]{ blog.getRemoteBlogId(), postId, blog.getUsername(), blog.getPassword() }; } else { apiParams = new Object[]{ postId, blog.getUsername(), blog.getPassword() }; } try { Object result = client.call((isPage) ? "wp.getPage" : "metaWeblog.getPost", apiParams); if (result != null && result instanceof Map) { Map postMap = (HashMap) result; List<Map<?, ?>> postsList = new ArrayList<Map<?, ?>>(); postsList.add(postMap); BioWiki.wpDB.savePosts(postsList, blog.getLocalTableBlogId(), isPage, true); } return true; } catch (XMLRPCException e) { mErrorMessage = e.getMessage(); } catch (IOException e) { mErrorMessage = e.getMessage(); } catch (XmlPullParserException e) { mErrorMessage = e.getMessage(); } return false; } @Override protected void onPostExecute(Boolean success) { if (mCallback != null) { if (success) { mCallback.onSuccess(); } else { mCallback.onFailure(mErrorType, mErrorMessage, mThrowable); } } } public interface Callback extends GenericErrorCallback { public void onSuccess(); } } public static class SyncMediaLibraryTask extends HelperAsyncTask<java.util.List<?>, Void, Integer> { private Callback mCallback; private int mOffset; private Filter mFilter; public SyncMediaLibraryTask(int offset, Filter filter, Callback callback) { mOffset = offset; mCallback = callback; mFilter = filter; } @Override protected Integer doInBackground(List<?>... params) { List<?> arguments = params[0]; BioWiki.currentBlog = (Blog) arguments.get(0); Blog blog = BioWiki.currentBlog; if (blog == null) { setError(ErrorType.INVALID_CURRENT_BLOG, "ApiHelper - current blog is null"); return 0; } String blogId = String.valueOf(blog.getLocalTableBlogId()); XMLRPCClientInterface client = XMLRPCFactory.instantiate(blog.getUri(), blog.getHttpuser(), blog.getHttppassword()); Map<String, Object> filter = new HashMap<String, Object>(); filter.put("number", 50); filter.put("offset", mOffset); if (mFilter == Filter.IMAGES) { filter.put("mime_type", "image/*"); } else if (mFilter == Filter.UNATTACHED) { filter.put("parent_id", 0); } Object[] apiParams = {blog.getRemoteBlogId(), blog.getUsername(), blog.getPassword(), filter}; Object[] results = null; try { results = (Object[]) client.call("wp.getMediaLibrary", apiParams); } catch (ClassCastException cce) { setError(ErrorType.INVALID_RESULT, cce.getMessage(), cce); return 0; } catch (XMLRPCException e) { prepareErrorMessage(e); return 0; } catch (IOException e) { prepareErrorMessage(e); return 0; } catch (XmlPullParserException e) { prepareErrorMessage(e); return 0; } if (blogId == null) { setError(ErrorType.INVALID_CURRENT_BLOG, "Invalid blogId"); return 0; } if (results == null) { setError(ErrorType.INVALID_RESULT, "Invalid blogId"); return 0; } Map<?, ?> resultMap; // results returned, so mark everything existing to deleted // since offset is 0, we are doing a full refresh if (mOffset == 0) { BioWiki.wpDB.setMediaFilesMarkedForDeleted(blogId); } for (Object result : results) { resultMap = (Map<?, ?>) result; MediaFile mediaFile = new MediaFile(blogId, resultMap); BioWiki.wpDB.saveMediaFile(mediaFile); } BioWiki.wpDB.deleteFilesMarkedForDeleted(blogId); return results.length; } private void prepareErrorMessage(Exception e) { // user does not have permission to view media gallery if (e.getMessage().contains("401")) { setError(ErrorType.NO_UPLOAD_FILES_CAP, e.getMessage(), e); } else { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } } @Override protected void onPostExecute(Integer result) { if (mCallback != null) { if (mErrorType == ErrorType.NO_ERROR) { mCallback.onSuccess(result); } else { mCallback.onFailure(mErrorType, mErrorMessage, mThrowable); } } } public interface Callback extends GenericErrorCallback { public void onSuccess(int results); } } public static class EditMediaItemTask extends HelperAsyncTask<List<?>, Void, Boolean> { private GenericCallback mCallback; private String mMediaId; private String mTitle; private String mDescription; private String mCaption; public EditMediaItemTask(String mediaId, String title, String description, String caption, GenericCallback callback) { mMediaId = mediaId; mCallback = callback; mTitle = title; mCaption = caption; mDescription = description; } @Override protected Boolean doInBackground(List<?>... params) { List<?> arguments = params[0]; BioWiki.currentBlog = (Blog) arguments.get(0); Blog blog = BioWiki.currentBlog; if (blog == null) { setError(ErrorType.INVALID_CURRENT_BLOG, "ApiHelper - current blog is null"); return null; } XMLRPCClientInterface client = XMLRPCFactory.instantiate(blog.getUri(), blog.getHttpuser(), blog.getHttppassword()); Map<String, Object> contentStruct = new HashMap<String, Object>(); contentStruct.put("post_title", mTitle); contentStruct.put("post_content", mDescription); contentStruct.put("post_excerpt", mCaption); Object[] apiParams = { blog.getRemoteBlogId(), blog.getUsername(), blog.getPassword(), mMediaId, contentStruct }; Boolean result = null; try { result = (Boolean) client.call("wp.editPost", apiParams); } catch (ClassCastException cce) { setError(ErrorType.INVALID_RESULT, cce.getMessage(), cce); } catch (XMLRPCException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (IOException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (XmlPullParserException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } return result; } @Override protected void onPostExecute(Boolean result) { if (mCallback != null) { if (mErrorType == ErrorType.NO_ERROR) { mCallback.onSuccess(); } else { mCallback.onFailure(mErrorType, mErrorMessage, mThrowable); } } } } public static class GetMediaItemTask extends HelperAsyncTask<List<?>, Void, MediaFile> { private Callback mCallback; private int mMediaId; public GetMediaItemTask(int mediaId, Callback callback) { mMediaId = mediaId; mCallback = callback; } @Override protected MediaFile doInBackground(List<?>... params) { List<?> arguments = params[0]; BioWiki.currentBlog = (Blog) arguments.get(0); Blog blog = BioWiki.currentBlog; if (blog == null) { setError(ErrorType.INVALID_CURRENT_BLOG, "ApiHelper - current blog is null"); return null; } String blogId = String.valueOf(blog.getLocalTableBlogId()); XMLRPCClientInterface client = XMLRPCFactory.instantiate(blog.getUri(), blog.getHttpuser(), blog.getHttppassword()); Object[] apiParams = { blog.getRemoteBlogId(), blog.getUsername(), blog.getPassword(), mMediaId }; Map<?, ?> results = null; try { results = (Map<?, ?>) client.call("wp.getMediaItem", apiParams); } catch (ClassCastException cce) { setError(ErrorType.INVALID_RESULT, cce.getMessage(), cce); } catch (XMLRPCException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (IOException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (XmlPullParserException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } if (results != null && blogId != null) { MediaFile mediaFile = new MediaFile(blogId, results); mediaFile.save(); return mediaFile; } else { return null; } } @Override protected void onPostExecute(MediaFile result) { if (mCallback != null) { if (result != null) { mCallback.onSuccess(result); } else { mCallback.onFailure(mErrorType, mErrorMessage, mThrowable); } } } public interface Callback extends GenericErrorCallback { public void onSuccess(MediaFile results); } } public static class UploadMediaTask extends HelperAsyncTask<List<?>, Void, String> { private Callback mCallback; private Context mContext; private MediaFile mMediaFile; public UploadMediaTask(Context applicationContext, MediaFile mediaFile, Callback callback) { mContext = applicationContext; mMediaFile = mediaFile; mCallback = callback; } @Override protected String doInBackground(List<?>... params) { List<?> arguments = params[0]; BioWiki.currentBlog = (Blog) arguments.get(0); Blog blog = BioWiki.currentBlog; if (blog == null) { setError(ErrorType.INVALID_CURRENT_BLOG, "current blog is null"); return null; } XMLRPCClientInterface client = XMLRPCFactory.instantiate(blog.getUri(), blog.getHttpuser(), blog.getHttppassword()); Map<String, Object> data = new HashMap<String, Object>(); data.put("name", mMediaFile.getFileName()); data.put("type", mMediaFile.getMimeType()); data.put("bits", mMediaFile); data.put("overwrite", true); Object[] apiParams = { blog.getRemoteBlogId(), blog.getUsername(), blog.getPassword(), data }; if (mContext == null) { return null; } Map<?, ?> resultMap; try { resultMap = (HashMap<?, ?>) client.call("wp.uploadFile", apiParams, getTempFile(mContext)); } catch (ClassCastException cce) { setError(ErrorType.INVALID_RESULT, cce.getMessage(), cce); return null; } catch (XMLRPCException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); return null; } catch (IOException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); return null; } catch (XmlPullParserException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); return null; } if (resultMap != null && resultMap.containsKey("id")) { return (String) resultMap.get("id"); } else { setError(ErrorType.INVALID_RESULT, "Invalid result"); } return null; } // Create a temp file for media upload private File getTempFile(Context context) { String tempFileName = "wp-" + System.currentTimeMillis(); try { context.openFileOutput(tempFileName, Context.MODE_PRIVATE); } catch (FileNotFoundException e) { return null; } return context.getFileStreamPath(tempFileName); } @Override protected void onPostExecute(String result) { if (mCallback != null) { if (result != null) { mCallback.onSuccess(result); } else { mCallback.onFailure(mErrorType, mErrorMessage, mThrowable); } } } public interface Callback extends GenericErrorCallback { public void onSuccess(String id); } } public static class DeleteMediaTask extends HelperAsyncTask<List<?>, Void, Void> { private GenericCallback mCallback; private String mMediaId; public DeleteMediaTask(String mediaId, GenericCallback callback) { mMediaId = mediaId; mCallback = callback; } @Override protected Void doInBackground(List<?>... params) { List<?> arguments = params[0]; Blog blog = (Blog) arguments.get(0); if (blog == null) { setError(ErrorType.INVALID_CONTEXT, "ApiHelper - invalid blog"); return null; } XMLRPCClientInterface client = XMLRPCFactory.instantiate(blog.getUri(), blog.getHttpuser(), blog.getHttppassword()); Object[] apiParams = new Object[]{blog.getRemoteBlogId(), blog.getUsername(), blog.getPassword(), mMediaId}; try { if (client != null) { Boolean result = (Boolean) client.call("wp.deletePost", apiParams); if (!result) { setError(ErrorType.INVALID_RESULT, "wp.deletePost returned false"); } } } catch (ClassCastException cce) { setError(ErrorType.INVALID_RESULT, cce.getMessage(), cce); } catch (XMLRPCException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (IOException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } catch (XmlPullParserException e) { setError(ErrorType.NETWORK_XMLRPC, e.getMessage(), e); } return null; } @Override protected void onPostExecute(Void v) { if (mCallback != null) { if (mErrorType == ErrorType.NO_ERROR) { mCallback.onSuccess(); } else { mCallback.onFailure(mErrorType, mErrorMessage, mThrowable); } } } } public static class GetFeatures extends AsyncTask<List<?>, Void, FeatureSet> { private Callback mCallback; public GetFeatures() { } public GetFeatures(Callback callback) { mCallback = callback; } public FeatureSet doSynchronously(List<?>... params) { return doInBackground(params); } @Override protected FeatureSet doInBackground(List<?>... params) { List<?> arguments = params[0]; Blog blog = (Blog) arguments.get(0); if (blog == null) return null; XMLRPCClientInterface client = XMLRPCFactory.instantiate(blog.getUri(), blog.getHttpuser(), blog.getHttppassword()); Object[] apiParams = new Object[]{ blog.getRemoteBlogId(), blog.getUsername(), blog.getPassword(), }; Map<?, ?> resultMap = null; try { resultMap = (HashMap<?, ?>) client.call("wpcom.getFeatures", apiParams); } catch (ClassCastException cce) { AppLog.e(T.API, "wpcom.getFeatures error", cce); } catch (XMLRPCException e) { AppLog.e(T.API, "wpcom.getFeatures error", e); } catch (IOException e) { AppLog.e(T.API, "wpcom.getFeatures error", e); } catch (XmlPullParserException e) { AppLog.e(T.API, "wpcom.getFeatures error", e); } if (resultMap != null) { return new FeatureSet(blog.getRemoteBlogId(), resultMap); } return null; } @Override protected void onPostExecute(FeatureSet result) { if (mCallback != null) mCallback.onResult(result); } public interface Callback { void onResult(FeatureSet featureSet); } } }