In Alfresco Share, when you click the "Repository" icon in the toolbar, you're taken to:
/share/page/repository
I would like to change this link to take the user to their home folder, example:
/share/page/repository#filter=path|/User%2520Homes/g/gi/gillespie/patrick.j.gillespie
I figured this would be a simple change, however, I'm pulling my hair out trying to figure out how to change the link. Does anyone know what I edit to change that link?
Update: So I can update the link via the share-config-custom.xml file, changing this line:
<item type="link" id="repository">/repository</item>
But I'm not sure how to get the folder path information in there. Does anyone have any ideas?
This was surprisingly difficult, so I figured I'd post what I did in case anyone else has the same problem.
If you're not using hashed home folders, you can provide this functionality by simply updating your share-config.xml or share-config-custom.xml like so:
<item type="link" id="repository">/repository#filter=path|/User%2520Homes/{userid}</item>
However, if you're using hashed home folders, things become a little tricky. Instead of modifying any XML files, you'll instead create a web script to get the user's home folder path and then modify the share template file to replace the links to the repository. Below is a sample web script and the modifications to the template file that are needed in order to do this.
hfp.get.desc.xml
<webscript>
<shortname>Home Folder Path</shortname>
<description>Description here.</description>
<format default="json">argument</format>
<url>/demo/get-home-folder-path</url>
<authentication>user</authentication>
</webscript>
hfp.get.js
This script traverses up the node tree and puts together the folder's path.
var myPerson = person;//used people.getPerson("patrick.j.gillespie") for testing
var currentNode = myPerson.properties.homeFolder;
var myDir = currentNode.properties["{http://www.alfresco.org/model/content/1.0}name"];
var count = 0;
while (count < 100) { // prevent an infinite loop
currentNode = currentNode.parent;
if (currentNode === undefined || currentNode === null) {break;}
if (currentNode.properties === undefined) {break;}
myDir = currentNode.properties["{http://www.alfresco.org/model/content/1.0}name"] + "/" + myDir;
count++;
}
if (count === 100) { //something went wrong
myDir = "";
}
model.homeFolder = myDir;
hfp.get.json.ftl
{
"homeFolder": "${homeFolder}"
}
Finally, you'll need to modify the "alfresco-template.ftl" file, located in "share/WEB-INF/classes/alfresco/templates/org/alfresco/include". Near the bottom of the file, add the code below. It will call the above web script to fetch the home folder path, and then update the Repository links with the home folder link.
<script type="text/javascript">
var callback = {
success: function(res) {
var data = YAHOO.lang.JSON.parse(res.responseText);
var homeFolder = "";
var hfIndex = data.homeFolder.indexOf("/User Homes/");
if (hfIndex !== -1) {
homeFolder = data.homeFolder.substr(hfIndex+12);
}
var repoLinks = $("a[title='Repository']");
for (var ii = 0; ii < repoLinks.length; ii++) {
repoLinks.get(ii).href = "/share/page/repository#filter=path|/User%2520Homes/" + homeFolder;
}
}
};
var sUrl = Alfresco.constants.PROXY_URI + "demo/get-home-folder-path";
var postData = "";
var getData = "";
var request = YAHOO.util.Connect.asyncRequest('GET', sUrl+getData, callback, postData);
</script>
There may possibly be a better way, but I was unable to find it. I'll probably refine it later, and this should mostly just be used as a starting point, but I figured I'd post it in case anyone had a similar problem.
Although not super elegant, I guess you can append parameters or anchors using a Javascript onclick handler on the link. Not quite sure which webscript renders the toolbar, but that one may be a good place to put the customization.
Related
Custom Search UI will be populated when user selects Complex asset in the Advance search screen drop down(apart from Folders,Contents) where 12 fields will be displayed .So when user clicks search button ,need to read those values and redirect to the alfresco repo files(org/alfresco/slingshot/search/search.get.js).We have already customized these files(search.get.js,search.lib.js) existed in the repository to suit out logic and working fine in 4.2.2;As we are migrating to 511,so we need to change this logic in customized faceted-search.get.js to read these values.How to write this logic in customized facted-search.get.js?
It's not actually possible to read those URL hash attributes in the faceted-search.get.js file because the JavaScript controller of the WebScript does not have access to that part of the URL (it only has information about the URL and the request parameters, not the hash parameters).
The hash parameters are actually handled on the client-side by the AlfSearchList widget.
Maybe you could explain what you're trying to achieve so that I can suggest an alternative - i.e. the end goal for the user, not the specifics of the coding you're trying to achieve.
We will be reading the querystring values something like below in the .get.js file.
function getNodeRef(){
var queryString = page.url.getQueryString();
var nodeRef = "NOT FOUND";
var stringArray = queryString.split("&");
for (var t = 0; t < stringArray.length; t++) {
if (stringArray[t].indexOf('nodeRef=') > -1) {
nodeRef = stringArray[t].split('=')[1];
break;
}
}
if (nodeRef !== "NOT FOUND") {
nodeRef = nodeRef.replace("://", "/");
return nodeRef;
}
else {
throw new Error("Node Reference is not found.");
}
}
It may be help you and we will wait for Dave Drapper suggestion also.
I'm trying to create my first extension for visual studio and so far I've been following this tutorial to get me started (http://www.diaryofaninja.com/blog/2014/02/18/who-said-building-visual-studio-extensions-was-hard).
Now I have a custom menu item appearing when I click on a file in the solution explorer.
What I need now for my small project is to get the path of the file selected in the solution explorer but I can't understand how can I do that.
Any help?
---------------------------- EDIT ------------------------------
As matze said, the answer is in the link I posted. I just didn't notice it when I wrote it.
In the meanwhile I also found another possible answer in this thread: How to get the details of the selected item in solution explorer using vs package
where I found this code:
foreach (UIHierarchyItem selItem in selectedItems)
{
ProjectItem prjItem = selItem.Object as ProjectItem;
string filePath = prjItem.Properties.Item("FullPath").Value.ToString();
//System.Windows.Forms.MessageBox.Show(selItem.Name + filePath);
return filePath;
}
So, here are two ways to get the path to the selected file(s) :)
For future reference:
//In your async method load the DTE
var dte2 = await ServiceProvider.GetGlobalServiceAsync(typeof(SDTE)) as DTE2;
var selectedItems = dte2.SelectItems;
if(selectedItems.MultiSelect || selectedItems.Count > 1){ //Use either/or
for(short i = 1; i <= selectedItems.Count; i++){
//Get selected item
var selectedItem = selectedItems[i];
//Get associated project item (selectedItem.ProjectItem
//If selectedItem is a project, then selectedItem.ProjectItem will be null,
//and selectedItem.Project will not be null.
var projectItem = selectedItem.ProjectItem;
//Get project for ProjectItem
var project = projectItem.ContainingProject;
// Or get project object if selectedItem is a project
var sproject = selectedItem.Project;
//Is selectedItem a physical folder?
var isFolder = projectItem.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder;
//Else, get item's folder
var itemFolder = new FileInfo(projectItem.Properties.Item("FullPath").ToString()).Directory;
//Find config file
var configFiles itemFolder.GetFiles("web.config");
var configfile = configFiles.length > 0 ? configFiles[0] : null;
//Turn config file into ProjectItem object
var configItem = dte2.solution.FindProjectItem(configFile.FullName);
}
}
I hope someone finds this helpful...
The article you mentioned already contains a solution for that.
Look for the menuCommand_BeforeQueryStatus method in the sample code. It uses the IsSingleProjectItemSelection method to obtain an IVsHierarchy object representing the project as well as the id of the selected item. It seems that you can safely cast the hierarchy to IVsProject and use it´s GetMkDocument function to query the item´s fullpath...
IVsHierarchy hierarchy = null;
uint itemid = VSConstants.VSITEMID_NIL;
if (IsSingleProjectItemSelection(out hierarchy, out itemid))
{
IVsProject project;
if ((project = hierarchy as IVsProject) != null)
{
string itemFullPath = null;
project.GetMkDocument(itemid, out itemFullPath);
}
}
I don´t want to copy the entire code from the article into this answer, but it might be of interest how the IsSingleProjectItemSelection function obtains the selected item; so I just add some notes instead which may guide into the right direction... The method uses the GetCurrentSelection method of the global IVsMonitorSelection service to query to the current selected item.
My organization is switching to a Google Business account, and everyone needs to transfer their Drive files to their new accounts. Drive will not allow transfer of ownership between these accounts, so I've created a script to copy files and folders from the old account to the new account. (The old account's contents have been moved into a folder shared with the new account.)
Here's what I have so far:
function copyDrive() {
var originFolder = DriveApp.getFolderById(originFolderID);
var destination = DriveApp.getFolderById(destinationID);
copyFiles(originFolder, destination);
};
function copyFiles(passedFolder, targetFolder) {
var fileContents = passedFolder.getFiles();
var file;
var fileName;
while(fileContents.hasNext()) {
file = fileContents.next();
fileName = file.getName();
file.makeCopy(fileName, targetFolder);
}
copySubFolders(passedFolder, targetFolder);
};
function copySubFolders(passedFolder, targetFolder) {
var folderContents = passedFolder.getFolders();
var folder;
var folderName;
while(folderContents.hasNext()) {
folder = folderContents.next();
folderName = folder.getName();
var subFolderCopy = targetFolder.createFolder(folderName);
copyFiles(folder, subFolderCopy);
}
};
Please pardon any inelegance; I am new at this. The script actually works great and preserves the folder structure, but it times out after copying ~150 files and folders. I've been looking into how to use continuation tokens, and I've read this post closely. I think I'm stuck on a conceptual level, because I'm not sure how the continuation tokens will interact with the recursive functions I've set up. It seems like I will end up with a stack of my copySubFolders function, and they will each need their own continuation tokens. Of course they all use the same variable name for their iterators, so I really have no idea how to set that up.
Any thoughts? Sorry for posting such a helpless newbie question; I hope it will at least be an interesting problem for someone.
I think I have solved the conceptual problem, though I am getting
We're sorry, a server error occurred. Please wait a bit and try again. (line 9, file "Code")
when I try to execute it.
Basically, I set it up to only try to copy one top-level folder at a time, and for each one of those it uses the recursive functions I had before. It should save continuation tokens for that first level of folders and any files in the root folder so it can pick up in the next execution where it left off. This way, the tokens are not involved in my recursive stack of functions.
function copyDrive() {
var originFolder = DriveApp.getFolderById(originFolderID);
var destination = DriveApp.getFolderById(destinationID);
var scriptProperties = PropertiesService.getScriptProperties();
var fileContinuationToken = scriptProperties.getProperty('FILE_CONTINUATION_TOKEN');
var fileIterator = fileContinuationToken == null ?
originFolder.getFiles() : DriveApp.continueFileIterator(fileContinuationToken);
var folderContinuationToken = scriptProperties.getProperty('FOLDER_CONTINUATION_TOKEN');
var folderIterator = folderContinuationToken == null ?
originFolder.getFolders() : DriveApp.continueFolderIterator(folderContinuationToken);
try {
var rootFileName;
while(fileIterator.hasNext()) {
var rootFile = fileIterator.next();
rootFileName = rootFile.getName();
rootFile.makeCopy(rootFileName, destination);
}
var folder = folderIterator.next();
var folderName = folder.getName();
var folderCopy = folder.makeCopy(folderName, destination);
copyFiles(folder, folderCopy);
} catch(err) {
Logger.log(err);
}
if(fileIterator.hasNext()) {
scriptProperties.setProperty('FILE_CONTINUATION_TOKEN', fileIterator.getContinuationToken());
} else {
scriptProperties.deleteProperty('FILE_CONTINUATION_TOKEN');
}
if(folderIterator.hasNext()) {
scriptProperties.setProperty('FOLDER_CONTINUATION_TOKEN', folderIterator.getContinuationToken());
} else {
scriptProperties.deleteProperty('FOLDER_CONTINUATION_TOKEN');
}
};
function copyFiles(passedFolder, targetFolder) {
var fileContents = passedFolder.getFiles();
var file;
var fileName;
while(fileContents.hasNext()) {
file = fileContents.next();
fileName = file.getName();
file.makeCopy(fileName, targetFolder);
}
copySubFolders(passedFolder, targetFolder);
};
function copySubFolders(passedFolder, targetFolder) {
var subFolderContents = passedFolder.getFolders();
var subFolder;
var subFolderName;
while(folderContents.hasNext()) {
subFolder = subFolderContents.next();
subFolderName = subFolder.getName();
var subFolderCopy = targetFolder.createFolder(folderName);
copyFiles(subFolder, subFolderCopy);
}
};
I know you would like a easy, programmatic way to do this, but it may be easiest to install Google Drive for Desktop and have them right-click, copy, paste.
The idea:
Create a single folder in which the user puts every item of their Drive. (I see you have already done that.)
Share that folder with their new account. (I see you have already done that, as well.)
Sign into their new account with Drive for Desktop.
Copy the folder in Drive for Desktop and paste it right back in. Ownership gets transferred to the new account.
Just a thought.
You're going to need to store an array of folder iterators and file iterators since each folder could have a nested array of folders. If you're reusing the same folder iterator as in the accepted solution, you won't be able to resume on more top level folders.
Take a look at my answer here for a template that you can use to recursively iterate over all the files in a drive with resume functionality built-in.
I'm doing a GUI Extension of User Interface (SiteEdit) by overriding the behaviour of one of the javascript files, to add some funcionality.
The javascript file is "/Scripts/Components/ExtComponentField.js" and the target is "SiteEdit" extending:
Tridion.Web.UI.Editors.SiteEdit.Views.Content
All works well with the extension, and I have what I wanted to have, but now I'm trying to use the
settings/customconfiguration/clientconfiguration
node of the extension config, to use some initialization parameters, but there is no way to access $config element in the javascript, and Tridion.Core.Configuration.Editors["myExt"].configuration is null.
I've seen using this customconfiguration in various javascripts like "Dashboard" or "Footprints", but is it possible to have it on "Content"? am I missing something on the extension config?
I'm afraid I didn't test this but you should be able to use:
Extensions.YourExt.getConfigurationItem = function (itemName, editorName)
{
var editor = $config.Editors[editorName].configuration;
if (editor)
{
var confXml = $xml.getNewXmlDocument(editor);
var confObj = $xml.toJson(confXml);
if (confObj[itemName])
return confObj[itemName];
else
return "";
}
}
You can then use it in the following way:
$this.getConfigurationItem("YOUR_CONFIG_ITEM_NAME", "YOUR_EDITOR_NAME").toString();
In your extension configuration (below the <theme> node) you can enter your own configuration values:
<customconfiguration>
<clientconfiguration xmlns="http://www.sdltridion.com/2009/GUI/Configuration/Merge">
<YOUR_CONFIG_ITEM_NAME>The value</YOUR_CONFIG_ITEM_NAME>
Can you confirm :)
I usually use a separate JS file with the following:
Type.registerNamespace("Extensions.Namespace");
Extensions.Namespace.getEditorConfigSection = function Editor$getEditorConfigSection() {
if (this._settings === undefined) {
var editor = $config.Editors["ThisEditorName"];
if (editor && editor.configuration && !String.isNullOrEmpty(editor.configuration)) {
var configSectionXmlDoc = $xml.getNewXmlDocument(editor.configuration);
this._settings = $xml.toJson(configSectionXmlDoc.documentElement);
}
}
return this._settings;
};
and in the configuration add it in a separate group:
<cfg:group name="Extensions.Namespace" merge="always">
<cfg:fileset>
<cfg:file type="script">/Scripts/Definitions.js</cfg:file>
</cfg:fileset>
</cfg:group>
Then wherever you need it, you can add the following dependency:
<cfg:dependency>Extensions.Namespace</cfg:dependency>
Then I usually use a function like this to get a certain configuration value:
Extensions.Namespace.Something.prototype._getMyConfigValue = function Something$_getMyConfigValue() {
var configSection = Extensions.Namespace.getEditorConfigSection();
if (configSection) {
return configSection.myconfigvalue;
}
};
The code contained in the "Content" group is running inside of the IFRAME which is hosting your published web page. As you can imagine, the amount of files included there should be minimized and so quite a lot of functionality is not available.
My suggestion would be to read the configuration only in the main window and then pass along the settings that you need to the code running in the IFRAME -- through the use of the Tridion.Utils.CrossDomainMessaging utility class ($xdm).
This should be trivial, and I'm pretty sure I did it once before.
I'm trying to post data up to a server and have it bounced back to me as a file download, prompting the native browser file download box. I know the server part works just fine becasue I can post from a demo web form, but when I run the following Flex 3 code, I can't even get the request to fire.
var fileRef:FileReference = new FileReference();
private function saveXmlAsFile(event:MouseEvent):void
{
var fileRequest:URLRequest = new URLRequest();
fileRequest.method = URLRequestMethod.POST;
fileRequest.url = "http://foo.com/dataBounce";
var urlVariables:URLVariables = new URLVariables();
urlVariables.content = "Test content to return" ;
// fileRequest.contentType = "application/x-www-form-urlencoded ";
urlVariables.fileName = "test.xml";
fileRef.addEventListener(SecurityEvent.ALL, onSecurityError);
fileRef.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError2);
fileRef.addEventListener(IOErrorEvent.NETWORK_ERROR, onNetworkError);
fileRef.addEventListener(Event.COMPLETE, onComplete);
try
{
fileRef.download(fileRequest, "test.xml");
}catch(error:Error) {
model.logger.error("unable to download file");
}
}
Note, when the call to fileRef.download is called, I can't see any request being made across the network using the traditional Firebug or HTTPWatch browser tools.
EDIT: I should add that this is for < Flash Player 10, so I can't use the newer direct save as file functionality.
Any suggestions? Thanks.
You need to add fileRef.upload to trigger the upload.
Also I would move the download statement to the onComplete so the file isn't requested before it's been uploaded.
Your explanation is pretty clear, but when I look at your code, I'm feel like I'm missing something.
The code looks like you're trying to do half of the upload part and half of the download part.
I think the code you currently have posted would work to trigger a download if you set the .method value to GET. I believe you will also need to include the filename as part of the .url property.
However, to post something and then trigger a download of it, you need two separate operations - the operation to post the data and then an operation to download it, which should probably be called from the upload operation's onComplete handler.
OK, I believe I figured out one of the things that's going on.
When you don't set the URLRequest.data property, it defaults the request method to "GET".
So, the working code looks like, with the data set to the posted URL variables:
private var fileRef:FileReference;
private function saveRawHierarchy(event:MouseEvent):void
{
var fileRequest:URLRequest = new URLRequest();
fileRequest.method = URLRequestMethod.POST;
fileRequest.url = "http://foo/bounceback";
var urlVariables:URLVariables = new URLVariables();
urlVariables.content = "CONTENT HERE";
urlVariables.fileName = "newFileName.xml";
fileRequest.data = urlVariables;
fileRef = new FileReference();
fileRef.addEventListener(Event.COMPLETE, onComplete);
try
{
fileRef.download(fileRequest, "appHierarchies.xml");
}catch(error:Error) {
model.logger.error("unable to download file");
}
}
Not sure what was wrong about the request not being made before, though.