My Word add-in programmatically makes some changes to the user's document, sends that document to a remote endpoint, and then needs to undo those changes.
I don't see any references to the undo stack in the Javascript API documentation, though I've seen some mentions in online questions that seem to indicate that the undo stack is inaccessible via the API. Is this the case? Is there no way to perform an automated undo on these changes?
I've thought of a few workarounds, but none seem viable.
Copying the contents prior to the changes and then pasting it over the altered content won't work as any Content Controls seem to not persist in the pasted content.
Another potential solution would be to save a copy of the document, make the changes to this copy, send the copy and then delete it afterwards, all invisibly to the user. I don't see any functionality in the API that would allow for this though.
Failing all this, I'll need to track all the changes and then re-create the initial state one change at a time, which will be challenging. Any simpler solutions at all would be very much appreciated.
Related
I'm wondering what are the best ways to delete items from a subset in a restful way. I got users and series, each user has his own list of series (watching, completed, etc). For example, if we want to get a list from a user we can do it with: GET /users/:id_user/series
If we want to delete a serie from the list of that user (but we don't want to delete the serie itself), how should it be?
I thought about the possibility of using DELETE /users/:id_user/series/:id_serie, but I'm not sure if it's the correct way for this case (maybe PATCH?).
I got another case, we got series and reviews. We can get the reviews like this: GET /series/:serie_id/reviews. In the other case we didn't want to delete the serie itself when deleting from a user list of series, but in this case we want to delete the review because its existence depends on the serie. So I guess in this case DELETE /series/:serie_id/reviews/:review_id is correct.
Is this difference important in order to choose the rest operation to delete the object/item from the subset?
How would you do it on the web?
You'd follow a link to a form, with input controls. You might have a something like a dropdown if you wanted to delete one series at a time, or lots of check boxes if you wanted to support a bulk delete. The user would provide input, hit the submit button, and the browser would create an application/x-www-form-urlencoded document and send it to the server.
What method would be used? Normally POST, because we are intending an edit to some resource on the server.
What resource would we be editing? Well, in trutch, it could be anything -- the server would include that information in the form metadata, so the client can just do what it is told.
So where should the server tell it to submit the form? Again, it could be anywhere... but a useful approach is to think about what resource in the client's cache is being updated. Because if we send the request to that resource, we get intelligent cache invalidation "for free".
So on the web, we would expect to see:
POST /users/:id_user/series
Does it have to be POST? On the HTML web, maybe it does, because the ubiquitous client of the web is a browser, not an editor.
It is okay to use POST.
But a perfectly valid alternative would be to edit the local copy of /users/:id_user/series, and then send back to the server a complete copy of the new version (PUT) or a patch-document describing the edits (PATCH). Notice that with both of these choices, the target uri is still /user/:id_user/series, so we still get the cache invalidation magic.
Creating a new resource in your model just to have something to DELETE is probably the wrong idea.
There are cases where an edit, or a delete, will necessarily impact more than one resource.
There are some specific circumstances when you can get the right magic cache invalidation with two resources (for instance, delete one document, and send back an updated copy of another).
But we don't, today, have a general purpose cache invalidation mechanism. (Closest thing I've been able to find is this, which seems to have stalled out in 2012.
I could not think of how to best phrase my question, so what you think I'm asking is probably not what I'm asking. Pretend that I've got three working apps, but the needs of this fourth one are a little different, so I'm asking for advice / best practices, not how to write an SQL statement, code a listview, customize a viewcell, or how to bind to a datamodel. I've got all that.
The TLDR version:
What I need is a sane way to know when the user is done entering his data so I can push it to my server.
I know that there are people out there who have solved this problem, so I'm soliciting your advice. If you also did not want to do a save on every keystroke, I'm especially interested in your recommendations.
Here's the slightly more detailed version:
I'm writing a companion app for part of our desktop software, which uses a proprietary data server to store its data that I can access from the mobile device. In my previous apps, I have a screenful of controls. The user enters/modifies data, and I know to save to my local sqlite table when they exit the screen. It's basically a transactional model, and I only upload the data to our server when they press a button to do so. Works great.
However, for this app, instead of entering a screenful of information, the user is just entering numbers in a list. Think inventory: user has a list of products and quantities, and they update the quantities to match what they've got. The user will not leave the data entry screen often, and is likely to turn off the device, or kick the app out of memory without "going back", so my datamodels can have unstored data.
I have entry fields in a listview. I need to push the entered data to my sqlite table, then on to our proprietary data server. I would have thought to do it from the property setter in my datamodel, but sadly that gets called when the datamodel is initially bound, AND on every keystroke.
I know that there are lots of people who have coded data entry in a listview, I've read all the posts to figure out how make the viewcells work. I am primarily using Entry controls, and I have tried using TextChanged, Completed, and Unfocused. Each one has some sort of issue that makes it an undesirable solution "out of the box". I've got some ideas and know how to solves parts of my problem, but am hoping someone out there has a much better solution than my collection of little hacks.
Obviously there's a ton of details (and complexity) I'm leaving out. I am using a view - viewmodel - model architecture, but am not using MVVM or any similar framework. My solution needs to be in "vanilla" Xamarin Forms.
Thank you so much for your help!
I would comment but I don't have enough reputation, so here it goes.
Is there a specific reason a button can not be added to the page (below the list view), so that the user can press it when they are done taking "inventory"? I'm assuming this is part of the complexity/details you are leaving out.
Without a button, you could make the data save/push happen using an Unfocused event on each entry field. If the user filled in a value for each entry control, the unfocus event could call the method to save the data. The code-behind would get a bit cluttered and it could be messy if you have a long list of entry controls, but it would get the job done until you find (or create) or better way.
Hope this helps.
Is there a way to 'watch' for submissions to all, or a specific depot, in Perforce? I was looking at p4 reviews but I'm not sure if it's what I need.
I want to make a piece of middleware that will send a payload to an internal service whenever someone makes a submission but I need some sort of way to watch for those changes. Something like p4 changes but in real-time so a new line is printed to the terminal whenever a change is made.
What am I best off using for this?
So, the review daemon (and by extension reviews on your user) is the recommended way to watch for changes, BUT, it sounds like you want more customization here...
So, if you go down a rabbit hole too far and decide you just want basic reviews, this article: http://answers.perforce.com/articles/KB/2920 details what needs to be setup.
Having said that, a way to do this would be triggers on the server. Check out p4 triggers. You can catch a submit in several places. Before it's properly committed to the server, after it's officially committed, etc.
What you can do is setup a trigger that runs a script and either checks in a file to another part of the depot with processed payload from the changelist, or email a set of folks, whatever you want. Triggers can run arbitrary scripts (with all the security issues that can bring of course).
Our Main branch was apparently just deleted and there's no record of why. (The branch still appears in Source Control Explorer - When I view the history of the branch it's empty). When I get latest on the branch it deletes everything locally. We have numerous children branches that all appear to be fine, but Main is now empty with no record of how/why. Anybody have any idea how we can figure out what happened and recover it? We have a child branch that should be a duplicate so we should be OK, but we'd really like to figure out what happened!
What may have happened
There are a few things I can think of, the most logical in this case is that someone issued a tf destroy $/project/Branch/* /recursive, that would have the observed effect.
It could also be that someone has renamed the branch, that would not be visible in the history per se, unless you turn on the "Show Deleted Items" option in the options of the Team Foundation Source control options.
Your Application Tier's version control cache may have become corrupted, the chance of this happening is very slim, but it may have caused this. Ensure you have a good backup of your databases even if this may seem the case, if it isn't you're going to need the database backup and the older it is, the more unlikely it is data marked for deletion will still be there.
How can you find out what happened?
Check the tbl_command in the Project Collection Database or access the hidden _oi activity log page on the web access server. You may be able to find the command that caused the deletion.
If that doesn't tell you, analyze the transaction logs of the SQL Server (if your server is configured to keep these).
What to do now?!
Make a backup of your TFS server or secure the ones you have if you haven't done so
If the version control cache is the culprit clearing it (on Application Tier machines) may solve your problem, the cache location shows on the TFS Admin Control panel:
Best way to go about this, is to stop the TFS server temporarily and then delete the contents of this folder.
There seem to be a few ways out:
Forget about it, take the contents of the most up-to-date branch and use that to repopulate the missing data. Just add them to the empty folder, check them in and then re-merge all other branches and resolve all conflicts.
Pro: Fast
Con: you loose history, resolving conflicts will be a horrible task.
Restore the project collection database to a previous point in time (warning! may require restore of all project collections to a previous point in time)
Pro: You get all your history back
Con: You loose changes made since the last known good backup, takes alot of work, will impact all projects in the same collection, possibly all projects on the same server.
Restore the whole server to temporary server and restore the collection with the missing data to the last known good configuration. Use a tool like OpsHub or Team Foundation Migration Toolkit to replay the changes since the disaster.
Pro: You get back to the most up to date point in time
Con: Takes a lot of time and expertise in TFS Migration
Restore the collection database and use the transaction logs to replay as much of the changes to the collection , then skip the transactions that perform the destroy. Be careful though, usually the destroy action marks files as deleted, but a job does the actual deletion in the background.
Pro: You get back to the most up to date point in time
Con: Takes a lot of time and expertise in SQL
Contact Microsoft Support and get a Field expert in the house. They may be able to restore the deletion if it was done without immediately triggering the cleanup job.
Pro: You will get back into the best state possible
Con: it will be costly
Whatever you do, make sure you have a backup of your current situation, that allows you to try different tactics, should your first attempts fail.
Consider splitting the project collection to allow other projects to continue working. You will end up in a situation were this one project ends up in an isolated Project Collection on its own, but it will allow you to move forward quickly.
OK - this is one for the record books, because inexplicably the project reappeared later in the day. All of it's history is back as well. I would have thought that perhaps the DBAs here did a database restore, but that's not possible since all of the checkins that have been happening all day are still there.
So if this happens to you in the future, just cross your fingers and wait a few hours!
p.s. I did look in the SQL logs but couldn't find anything. Bizarre!
I am in the process of designing a REST API for a project we are working on. That is, I am writing the specifications which will be implemented later on.
I am having troubles to think into nouns/resources instead of actions/verbs. Without going into too much project specifics, we are writing an API around SVN. For example, take the action which commits changes to the SVN server. In our project, we have multiple definitions/versions of the commit action:
simply commit all changed files
commit a list of changed files (a subset, instead of the whole set of changed files)
...
(1) How would you design the URL? The first question is, how do I describe the commit action as a noun/resource instead of a verb?
Some would say:
POST/PUT http://server.com/api/revision/commit
Should it be a POST or a PUT? I am not really creating a commit resource, so it's not a POST. However, I am not really changing a commit resource, so it's not a PUT. Actually, it's not a resource, it's an action. Once the action is executed, it's gone, there is no resource to be created, changed or kept for later reference.
That said, it must be a resource, so the URL should probably be like this
POST http://server.com/api/revision/commitment
It's also a POST, since we are creating a commitment. We are not changing anything, so no PUT. Also note that I changed commit into commitment, to reflect the fact that we are dealing with resources.
Does this make sense? To me it doesn't, it drives me nuts. I want to execute an action, not create a resource which resembles the action. But anyway.
That said, going further, I just created a commitment resource. So logically, I should be able to retrieve it later on:
GET http://server.com/api/revision/commitment/:id
But there is no commitment resource! I was forced to make one in order to be RESTful. head explodes
So, how do you really specify actions on resources in a REST API? I am not talking about the kind of actions which create a resource (create user, ...), but about the kind of actions which manipulate a resource or act on a resource (commit revision, ...).
(2) Then, secondly, in case of the second definition (see above), how do we specify the subset of changed files? Through parameters or in some structure (for example JSON array) in the BODY? Which one is preferred? Are there any general rules?
Thanks all!
Sometimes it's easier to back up in a URL design than go forward. It seems to me like what you're calling a "commitment" is actually a revision itself. svn commit basically means, "please accept these differences from my currently selected revision as a new (child) revision". Therefore, you need to identify that currently selected revision (in order to remain stateless) and then append to it in a meaningful way:
POST http://server.com/api/revisions/16/children/
That is, POST an entity which encapsulates the differences from revision 16. The server could then respond with 201 Created, plus Location: /api/revisions/23/ (or /api/revisions/16/children/1, which redirects to the former).
Then, not only have you provided for the creation of new revisions, but also most likely added a useful list of the direct children of a given revision.
Stop thinking in actions ;D
First of all put your SVN to be file based and not based on a database, that will help in thinking.
If you think about it you only have 3 restfull (well in fact 4 perhaps) operations on your repository: svn add + svn commit to put a new file under version controll. This translates into a PUT on the folder into which you want to add and commit the file. svnroot:/project/folder in fact as the "id" of the file is known you actually can PUT directly on the URL of the file: svnroot:/project/folder/file.c
You want to commit a change, that translates to a POST on the resource (not the folder but the actual existing file in that folder). POST svnroot:/project/folder/file.c
If you want to remove a file it obvioulsy is a DELETE.
If you want to know what revision number a certain file has use STATUS.
Start thinking in terms of what actually is happening, not how the verbs are called (co, ci, add, update etc.), what happening is that files are 'created' in the repository, thats a PUT, or modified, thats a POST or deleted/removed thats a DELETE or the status is queried that is STATUS.
If you retrive the file it is obviously a GET.
So, that above is an actuall existing resource. Now you could start inventing more features, give the file a size and an author or last commiter or a commit message! Now you need to work on URLs, for which no real resource exists. Some call this virtual resources.
Just add "/authors" etc. to the end of the resource URLs and use POST to add an author and GET to read the authors. (Matter of taste, you as well could use PUT, would perhaps be more clean according to the REST paradigm)