This is strange - or not as Richtext is a son of a b**ch in general. I want to track if a document (resp. the richtext items) have attachments or not to set other fields in my backend document. I created a static Java method to compute the stuff. The method is called from the postSaveDocument event of my datasource.
This is the method:
/**
* Set flag fields if attachments exist or not
*
* #param xspdoc
*/
public static void setAttachments(final Document doc, final boolean post) {
try {
if (doc.hasItem("audioFile")) {
doc.replaceItemValue("audioHasFile", "1");
} else {
doc.removeItem("audioHasFile");
}
if (doc.hasItem("audioCase")) {
doc.replaceItemValue("audioHasCase", "1");
} else {
doc.removeItem("audioHasCase");
}
if (doc.hasItem("audioTrackliste")) {
doc.replaceItemValue("audioHasTrackliste", "1");
} else {
doc.removeItem("audioHasTrackliste");
}
if (post)
doc.save();
} catch (Exception e) {
e.printStackTrace();
}
}
The problem is: everytime I add an attachment to one of the RTF items on my Xpage (via Fileupload control), save the document with the simple action, the item e.g. "audioHasFile" is set to "1". Bene!
If I then reopen the document, delete the attachment (via the Filedownload control trashicon) and save the document again, the backend doesn't recognize that the attachment has gone and the item e.g. "audioHasFile" is not removed but still holds the value "1" which was set before.
Only if a re-open the document in my Xpage (from a View panel) and save it again, the field is removed as the backend now recognizes that there is no attachment.
I know what you are thinking: the lack of an attachment doesn't mean that theres is no item for it - wrong! I also tried to check the type of the Richtext item via getType == 1 (Item.ATTACHMENT) - no luck.
Info: I deliver the Document parameter via currentDocument.getDocument(true) - so I am dealing with the synchronized backend document here.
To be clear: it's not a question of testing in general but a problem of timing.
Any idea how to solve this? Thank you in advance! :)
UPDATE: This is the solution that works:
/**
* Set flag fields if attachments exist or not
*
* #param xspdoc
*/
public static void setAttachments(final DominoDocument doc) {
try {
doc.replaceItemValue("audioHasFile", doc.getAttachmentList("audioFile").size() > 0 ? "1" : "");
doc.replaceItemValue("audioHasTrackliste", doc.getAttachmentList("audioTrackliste").size() > 0 ? "1" : "");
doc.replaceItemValue("audioHasCase", doc.getAttachmentList("audioCase").size() > 0 ? "1" : "");
// key
doc.replaceItemValue("audioKey", doc.getItemValueString("audioTitle").toLowerCase().replaceAll("\\s+",""));
doc.save();
} catch (Exception e) {
e.printStackTrace();
}
}
Try to wrap your Document in NotesXspDocument:
NotesXspDocument xspDoc;
xspDoc = com.ibm.xsp.model.domino.wrapped.DominoDocument.wrap(doc.getParentDatabase().getFilePath(), doc, null, null, false, null);
if (xspDoc.getAttachmentList("FieldName").size() > 0){
//
}
Related
I work on university project in java. I have to download attachments from new emails using GMAIL API.
I successfully connected to gmail account using OAuth 2.0 authorization.
private static final List<String> SCOPES = Collections.singletonList(GmailScopes.GMAIL_READONLY);
I tried to get unseen mails using
ListMessagesResponse listMessageResponse = service.users().messages().list(user).setQ("is:unseen").execute();
listMessageResponse is not null but when I call method .getResultSizeEstimate() it returns 0
also I tried to convert listMessageResponse to List < Message > (I guess this is more usable) using
List<Message> list = listMessageResponse.getMessages();
But list launches NullPointerException
Then tried to get each attachment with
for(Message m : list) {
List<MessagePart> part = m.getPayload().getParts();
for(MessagePart p: part) {
if(p.getFilename()!=null && p.getFilename().length()>0) {
System.out.println(p.getFilename()); // Just to check attachment filename
}
}
}
Is my approach correct (if not how to fix it) and how should I download those attachments.
EDIT 1:
Fixed q parameter, I mistakenly wrote is:unseen instead of is:unread.
Now app reaches unread mails successfully.
(For example there was two unread mails and both successfully reached, I can get theirs IDs easy).
Now this part trows NullPointerException
List<MessagePart> part = m.getPayload().getParts();
Both messages have attachments and m is not null (I get ID with .getID())
Any ideas how to overcome this and download attachment?
EDIT 2:
Attachments Downloading part
for(MessagePart p : parts) {
if ((p.getFilename() != null && p.getFilename().length() > 0)) {
String filename = p.getFilename();
String attId = p.getBody().getAttachmentId();
MessagePartBody attachPart;
FileOutputStream fileOutFile = null;
try {
attachPart = service.users().messages().attachments().get("me", p.getPartId(), attId).execute();
byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
fileOutFile = new FileOutputStream(filename); // Or any other dir
fileOutFile.write(fileByteArray);
fileOutFile.close();
}catch (IOException e) {
System.out.println("IO Exception processing attachment: " + filename);
} finally {
if (fileOutFile != null) {
try {
fileOutFile.close();
} catch (IOException e) {
// probably doesn't matter
}
}
}
}
}
Downloading working like charm, tested app with different type of emails.
Only thing left is to change label of unread message (that was reached by app) to read. Any tips how to do it?
And one tiny question:
I want this app to fetch mails on every 10 minutes using TimerTask abstract class. Is there need for manual "closing" of connection with gmail or that's done automatically after run() method iteration ends?
#Override
public void run(){
// Some fancy code
service.close(); // Something like that if even exists
}
I don't think ListMessagesResponse ever becomes null. Even if there are no messages that match your query, at least resultSizeEstimate will get populated in the resulting response: see Users.messages: list > Response.
I think you are using the correct approach, just that there is no message that matches your query. Actually, I never saw is:unseen before. Did you mean is:unread instead?
Update:
When using Users.messages: list only the id and the threadId of each message is populated, so you cannot access the message payload. In order to get the full message resource, you have to use Users.messages: get instead, as you can see in the referenced link:
Note that each message resource contains only an id and a threadId. Additional message details can be fetched using the messages.get method.
So in this case, after getting the list of messages, you have to iterate through the list, and do the following for each message in the list:
Get the message id via m.getId().
Once you have retrieved the message id, use it to call Gmail.Users.Messages.Get and get the full message resource. The retrieved message should have all fields populated, including payload, and you should be able to access the corresponding attachments.
Code sample:
List<Message> list = listMessageResponse.getMessages();
for(Message m : list) {
Message message = service.users().messages().get(user, m.getId()).execute();
List<MessagePart> part = message.getPayload().getParts();
// Rest of code
}
Reference:
Class ListMessagesResponse
Users.messages: list > Response
I have a ListView in Android that contains Orders. When you click on a specific order you can choose whether to remove it or not. When the list contains >1 items, the removed item does not appear on the ListView anymore. However, when the list size is 1 and you remove the only order left, the order does get removed from the list but not from the ListView. So you can still see it on the screen, but if you try to open it an error message is shown "Can't open this order.".
When you return to the Home screen and reopen the ListView, the order is properly removed, and an empty list is shown. However, I'm not sure why this is happening. Here is some sample code:
method {
VerkoopOrder orderToBeSaved = CurrentOrder;
UUID CurrentID = CurrentOrder.getId();
orderToBeSaved.setId(null);
String Result = OrderHelper.SaveOrder(orderToBeSaved, APIKey);
JSONObject json = new JSONObject(Result);
String res = json.getString("nummer");
if (Result != null) {
Messager.showMessage(getString(R.string.Saved), getString(R.string.OrderSavedAs) + " " + res, true, this);
DeleteCurrentOrder(APIKey, CurrentID);
UnsavedOrdersActivity.UnsavedOrderAdapter.notifyDataSetChanged();
}
}
public void DeleteCurrentOrder(String APIKey, UUID OrderId) {
try {
OrderScanPreference orderScanPreference = OrderScanPreference.GetCurrentSavedPreference(this, getString(R.string.OrderScanUserPreference));
String finalAPIKey = APIKey;
try {
for (UnsavedOrderPreference unsavedOrderPreference : orderScanPreference.unsavedOrderPreferences) {
if (unsavedOrderPreference.APIAdministrationToken.equals(finalAPIKey)) {
unsavedOrderPreference.UnsavedOrders.removeIf(r -> r.getId().equals(OrderId)); //Order gets removed from the list!
}
}
orderScanPreference.Save(this, getString(R.string.OrderScanUserPreference));
} catch (Throwable throwable) {
//TODO
}
} catch (Exception ex) {
Log.d("Exception: ", ex.toString());
//TODO
}
}
This code was written by a colleague but he left the company a few weeks ago, so I have to finish his project. Let me know if you require more information.
I placed a check to see if the list is empty or not. If it is, it reassigns the Adapter to the ListView so the list gets cleared completely.
Not a great fix, so I won't accept this as the answer yet. Only if there are no better answers soon I'll accept this.
I have two methods. The first retrieves a list of results from a search method in another class.
/* 2 - Retrieve list of results */
qmitResultsList = QMITSearchUtil.execute(URL, keyword);
/* 3 - Show results */
populateTable(qmitResultsList, tableView)
The second, populateTable() adds all the items to the table at once by calling:
ObservableList<QMITResult> dataPriority = FXCollections.observableArrayList(
qmitResultsList
);
tableView.setItems(dataPriority);
My goal is to add each new element to the TableView as it is being processed in real-time. For example, instead of processing and returning the entire list in the first method, QMITSearchUtil.execute(), I would like to update the UI with each result that is returned, one at a time. How can this be accomplished? I've tried a few ways, using a Platform.runLater() hack for example, with no success...
I discovered the answer to my question. I first define the ObservableList for my TableView:
ObservableList<QMITResult> dataPriority = FXCollections.observableArrayList();
Then I pass that into the execute() method that runs the background thread:
private void execute(String URL, String keyword, ObservableList<QMITResult> dataPriority) throws Exception {
/* Download HTML page and create list of URLs from relevant links */
Elements links = getLinkList(URL);
List<QMITResult> qmitResults = new ArrayList<>();
new Thread(() -> {
for (Element link : links) {
try {
/* Create a list of formatted URLs to loop through */
String linkText = link.toString();
String titleText = link.text();
String formattedLink = StringUtils.substringBetween(linkText, "<a href=\"", "\"").replace("\\", "/");
System.out.println(titleText);
System.out.println(formattedLink);
/* Create Word Document for each link and parse for keyword */
QMITResult qmitResultNode = null;
try {
qmitResultNode = parseDocument(keyword, formattedLink, titleText);
} catch (Exception e) {
e.printStackTrace();
}
qmitResults.add(qmitResultNode);
dataPriority.add(qmitResultNode);
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
tableView.setItems(dataPriority);
}
The result is that while the list is being formed each TableView item is being individually published without blocking the main UI thread. They come one at a time.
In crawler4j we can override a function boolean shouldVisit(WebUrl url) and control whether that particular url should be allowed to be crawled by returning 'true' and 'false'.
But can we add URL(s) at runtime ? if yes , what are ways to do that ?
Currently I can add URL(s) at beginning of program using addSeed(String url) function before the start(BasicCrawler.class, numberOfCrawlers) in CrawlController class and if I try to add new url using addSeed(String url), it gives error. Here is error image .
Any help will be appreciative and please let me know if any more detail about project is required to answer the question .
You can do this.
Use public void schedule(WebURL url) to add URLs to the crawler frontier which is a member of the Frontier.java class. But for this you need to have your url of type WebURL. If you want to make a WebURL out of your string. Please have a look at the addSeed() (below code) which is in the CrawlController.java class to see how it has converted the string (url) into a WebURL.
Also use the existing frontier instance.
Hope this helps..
public void addSeed(String pageUrl, int docId) {
String canonicalUrl = URLCanonicalizer.getCanonicalURL(pageUrl);
if (canonicalUrl == null) {
logger.error("Invalid seed URL: " + pageUrl);
return;
}
if (docId < 0) {
docId = docIdServer.getDocId(canonicalUrl);
if (docId > 0) {
// This URL is already seen.
return;
}
docId = docIdServer.getNewDocID(canonicalUrl);
} else {
try {
docIdServer.addUrlAndDocId(canonicalUrl, docId);
} catch (Exception e) {
logger.error("Could not add seed: " + e.getMessage());
}
}
WebURL webUrl = new WebURL();
webUrl.setURL(canonicalUrl);
webUrl.setDocid(docId);
webUrl.setDepth((short) 0);
if (!robotstxtServer.allows(webUrl)) {
logger.info("Robots.txt does not allow this seed: " + pageUrl);
} else {
frontier.schedule(webUrl); //method that adds URL to the frontier at run time
}
}
Presumably you can implement this function however you like, and have it depend on a list of URLs that should not be crawled. The implementation of shouldVisit is then going to involve asking if a given URL is in your list of forbidden URLs (or permitted URLs), and returning true or false on that basis.
How do you set up a Test Blob Image using the yaml structure?
Also, what is the database structure for a BLOB file? (MySQL)
I have experienced the same kind of problem a while ago on a project. However as I could not find a way to solve this with the fixtures (as the database stores the blob object as a string as Pere explained above), I created a workaround to at least solve this problem in a test-case-scenario. I created the following file /app/job/Bootstrap.java:
import play.test.*;
import play.jobs.*;
import play.db.DB;
import models.*;
import java.util.List;
#OnApplicationStart
public class Bootstrap extends Job {
public void doJob() {
// Load default data if the database is empty
if(Item.count() == 0) {
Fixtures.loadModels("my_fixtures.yml");
List<Item> allItems = Item.findAll();
for (Item a: allItems){
DB.execute("UPDATE `Item` SET image='item_" + a.name.toLowerCase() + ".png|image/png' WHERE id=" + a.getId());
}
}
}
}
The first thing I do is filling the database with initial data if there are no 'Item' already stored in the database.
The second thing is iterating over all the 'Item' which play! just stored in the database, which are read from the "my_fixtures.yml" file. Here for each item the string field will get updated as shown in the example above.
I know this is not exactly the answer to question in the OP, but it gives some kind idea to work around this issue..
EDIT: In the example given above I assume that the pictures are uploaded manually to your attachment folder as given in your application.conf, and that each image name is like: "item_<item_name_in_lowercase>" with a ".png" extension
Well, play is quite weird on that point.
The blob is not saved into the database but in a upload folder defined in your application.conf. It is the path toward the file that is saved in the database.
I cannot check it right now, but I seem to recall they are saved as textuel representations (VARCHAR, TEXT)
The blob is saved in the file system, by default under "data/attachments" if I recall correctly, but you can change that in the configuration (application.conf)
In the database, it's stored as a String (varchar in most DB) with two components: the name and the mime type. It looks like:
12345asbcdefghi12345abcdfed|image/jpeg
The first part is the name of the file. When you upload a file Play generates a unique UUID as name to avoid collision. Yes, this means you are loosing the original name. (note: now I'm having doubts on the name part, I would swear it is lost, but I may be wrong!)
The second part (after the |) is the myme type. Play uses a magic-myme library to automatically detect it.
You can see the code here.
Here is a modified version of Unji's answer that loads the images from a folder in conf, please note that I have removed all the import statements:
/**
* A job executed when the application starts.
*/
#OnApplicationStart
public class Bootstrap extends Job {
/**
* Loads the initial data if there are no
* WebAdministrators at the database.
* <p>
* It loads images on the post with the following criteria:
* <ol>
* <li>file loaction: /conf/initialMedia/</li>
* <li>file name: {post.title.toCamelCase()}-{i}.jpg</li>
* </ol>
* Where i must start in 0.
* </p>
*/
#Override
public void doJob() {
// Check if the database is empty
if(WebAdministrator.count() == 0) {
Logger.info("Loading Initial Data.");
Fixtures.loadModels("initial-data.yml");
List<Post> posts = Post.findAll();
for (Post post: posts) {
Logger.info("Looking for files for post: [" + post.title + "]");
for (int i=0; true; i++) {
VirtualFile vf = VirtualFile.fromRelativePath("/conf/initialMedia/"
+ JavaExtensions.camelCase(post.title) + "-" + i + ".jpg");
File imageFile = vf.getRealFile();
if (imageFile.exists()) {
try {
Blob blobImage = new Blob();
blobImage.set(new FileInputStream(imageFile), MimeTypes.getContentType(imageFile.getName()));
MediaItem mediaItem = new Image(blobImage);
mediaItem.save();
post.mediaItems.add(mediaItem);
post.save();
Logger.info("File: [%s] Loaded", imageFile.getAbsolutePath());
} catch (FileNotFoundException e) {
// this should never happen.
}
} else {
Logger.info("Media Loaded for post [%s]: %d files.", post.title, i);
break;
}
}
}
}
}
}