Downloading OneDrive documents as PDF

As part of automating a build process I needed a way to download the product documentation from OneDrive and save as PDF files for shipping with product. The result is this PowerShell script with two cmdlets, one that takes an array or OneDrive files to download, and one that converts the downloaded DocX files to PDF (using Word). Here is the script (also on my GitHub):


Posted in Development, Tutorials | Tagged , , , , , | Leave a comment

A drag-and-drop GUI made with PowerShell

The script below shows how to create a simple WinForms GUI in PowerShell, where you can drag and drop files and folders and then process these with PowerShell commands when you click the button. I’ve pieced this together using various online source and my own trial and error, and figured it might be useful as a base for others who need to create an interface.


I probably won’t win any design prices with this one :-)

Tip: If you are unsure of control names or how to use properties on a control, remember that you can always launch Visual Studio with a WinForms project and then inspect the Properties window and Form1.Designer.cs file for “inspiration”.

Here is the script (or get it at GitHub).

Posted in Development, Tutorials | Tagged , , , | Leave a comment

Think twice before uploading assets to Sharepoint Online

Here’s a heads up if you are uploading assets such as CSS files to SharePoint Online. In a solution I’m working on we have an “Alternate CSS” file that we upload to the master page gallery. It contains the following line:

Nothing special there. Everything was fine until one day when the icons stopped loading. Debugging led me to the CSS file, in which above line had been changed:

Notice that “//” has been added to the url. When I saw this I assumed I had made some error. I checked my original files and upload procedures, but could find nothing wrong. Provisioning the file again fixed the problem everyone was happy.

But a month or so later it happened again. Could SharePoint be modifying the file? We contacted Microsoft support and asked if they knew anything about it. They confirmed that they do indeed modify files in SharePoint Online through a time job that runs before site colection updates. Basically their response was: Deal with it.

So how *do* we deal with this? What we did was to replace “//” with “https://”. So far this seems to be working, but we are aware that it might break at any time. To be really sure you will want to skip @import statements (and other URL:s) in files you upload to SharePoint. But we don’t know what kind of files Microsoft takes the liberty to modify, nor if they process all locations or just the master page gallery. The only safe solution is probably to skip SharePoint altogether and move the files to a separate web server that you are in control of.

What we have to remember is that SharePoint Online is a cloud service, and as such, the operator is in complete control of changes. There are a lot of hidden rules and jobs going on behind the scenes in SharePoint Online that customers are not aware of. Microsoft does not necessarily tell us when changes happen or what the implications will be. And they might change the rules at any time. So we need to be cautious, even when dealing with with supported features such as Alternate CSS.

Posted in Development | Tagged , , , , , , | Leave a comment

Converting a CSV file to RegX with PowerShell

If you need to convert a CSV file with terms to a RegX file for use in your .Net project, you can use this simple PowerShell script:

The script assumes your CSV file contains two columns, “Name” and “Value”, such as this:

You can use Excel to export such files. You may need to change the -Delimiter parameter in the script to match your locale (Why? See my post on working with CSV files in Excel).

Posted in Development, Tips | Tagged , , , | Leave a comment

Working with enterprise custom fields in Project Online

I was tasked with with writing a utility for setting Enterprise Custom Fields on User Resources in Microsoft Project Online. The fields got their values from Lookup Tables. Since is was Project Online, the client side object model, CSOM, had to be used. Without previous experience of MS Project, this proved to be tricky.

Here I present a C# class that should help anyone in the same situation to get started. It is a simple console application that show various ways to interact with MS Project, and in particular how to extract available custom field values and and set on user resources. Below are some interesting snippets of the code showing how to work with Project and CSOM. The complete class can do more and is available at the bottom as well as on GitHub.


Project Online is a (very) customized SharePoint site collection. We can use same techniques when working with it as with any SharePoint Online site. As always, we need to begin with getting a context. In this case we request a ProjectContext which is just a normal SharePoint context object with additional properties:


As always in CSOM, we need to explicitly load all resources we need to access:


Listing enterprise custom fields and lookup table entries is simply a matter of iterating over the respective properties:


A user resources is not the same as the user object. It seems that we can’t trust that all users emails are synced to Project Online. To get around this, I load user resource using the login name instead of just the email, by simply appending the claims prefix to the email:


Now we can get the custom fields from the user resource. Note that fieldValue is a string array, because it can be multi valued.


To set a custom field with the value of a lookup table we need to understand the internal structure:

  • Each value in both the custom fields and lookup tables have a GUID, an internal name and a display name (also called simply name or full value). (The internal name is actually a concatenation of the GUID and a word like “custom” or “entry”.)
  • A custom field may be bound to a lookup table. We use the internal name of the lookup table entries to set such custom fields.
  • Since these are custom fields, the compiler is not aware of them. In order to set such a field we need to access it using an [indexer] together with the internal name.
  • Since field values can be multi-valued, we need to set it using a string array.
  • Finally, to persist the changes we need to update the EnterpriseResources collection, because this is where the resources are stored.

Now that we know all this, it is actually quite easy to set the field! Assuming that we already have the internal names of the custom field and lookup table entry we wish to set it to:


Putting all of this together, I made a class that can be used to read and write custom fields, and also shows how to list a bunch of information from Project sites and its users. Use it as a template to make your own solution. You need to modify it to use your own login information and GUID:s before you can use it. Again, the full Visual Studio solution can be downloaded from GitHub.


Posted in Development, Tutorials | Tagged , , , , | 1 Comment

See what Boostrap screen mode you are currently viewing

Working with a responsive framework such as Bootstrap, you build websites to display differently depending on the screen size. Bootstrap defines four screen widths that determine what “mode” you are in (xs, sm, md and lg). But it’s not always obvious what mode you are viewing. For this I put together a small piece of CSS that can be dropped into any project during development. It will display a label in the top left corner that shows the mode you are currently viewing. Here is a demo. Resize the window to see the label change!

screen mode label

This is the CSS. The screen sizes are based on Bootstrap 3 but can easily be changes to any kind of framework.

Note: The screen widths above are hard coded pixel sizes, so this CSS does not depend on any framework. To the right of each @media I have written the corresponsing Bootstrap LESS variable (@screen-xx). Replace the ###px with the variable to make sure it is always correct.

Posted in Development, Tips | Tagged , , , , | Leave a comment

Why image renditions are slow in SharePoint Online

Image renditions are supposed to be a great way to speed up SharePoint. But if you’ve ever tried to use them in SharePoint Online you have probably noticed that the images load very slow. It’s usually even faster to get the original high resolution image instead. If you look at the network requests, you’ll see that most of the time the browser is just waiting for the server to respond. Actually downloading the image data takes no time at all. But why?

This is what I think is happening: Image renditions rely on front end caching. But SharePoint Online is a huge farm with lots and lots of front ends. When you request an image rendition it will be generated and cached. But the next time you make a request for the same image rendition, the probability that you get a cache hit is very small. You will probably be served by another front end server, and even if you are served by the same server, it handles so many requests that your image rendition has already been dropped from the cache.

Thanks to my friend Dan Andersson for helping me figure out this.

Posted in Development | Tagged , | 2 Comments

Edit SharePoint property bags with SpPropertyBag.js bookmarklet

SpPropertyBag.js is a drop-in JavaScript for editing SharePoint web properties (the “property bag”). Simply paste the script into the JavaScript console and run it to open a dialog where you can view, edit, add, and remove properties:


Even easier is to use it as a bookmarklet in your web browser that you can click when you need access to the web properties! Simply paste this code into a new link in your browser’s toolbar:

Here is the full script. It is also available for forking at GitHub.


Posted in Development | Tagged , , , | 5 Comments

16 years with Flash Renamer – A development retrospective

Of all the applications, web services and games I have made or been part of, Flash Renamer is my darling. In case you don’t know, Flash Renamer is a Windows utility for batch renaming files and folders. It’s my own creation and reflects much of my own needs and way of working, while still trying to suit a broad user base. I started working on it a whopping 16 years ago, a respectable age for any software. Like all software published on my website, it is purely a hobby project that I developed in my spare time. I wanted to write a little bit about the story of how Flash Renamer came into being and developed through its versions, as well as some more general reflections on software development.

Flash Renamer 6.73

The current version of Flash Renamer.

The start of it all

It was early 1998 and I had discovered mp3:s. I was fascinated by this new way of storing “CD quality” music and had started to download music to build up a little collection of music that I liked (mostly euro dance :-)). I was however quite picky with the file names, and put a lot of thought into how to best name the files. They should convey as much information as possible, yet still look good and be easy to read. Bare in mind that this was before the ID3 metadata tag had become mainstream, or even supported by applications.

I quickly discovered that naming the files by hand was quite tedious though. So, I did a web search to try and find a solution. But I could only find one batch rename program (“THE Rename“). Unfortunately it did not start on my computer for some reason.

I knew a little programming, but I didn’t use any particular programming language at that time. But I had gotten Visual Basic 4 on a magazine cover disc and I liked how easy it was to make graphical Windows programs with it. I had cobbled up some simple applications with it before, and, for some reason I can’t remember, decided to make a batch rename application for myself.

The origins of Flash Renamer hints at one of the great things about knowing how to program a computer: if something does not exist that you need, you can make it yourself!

Origin of the name and interface

Finding a good name for a new product is hard, so I usually just give my new projects a name based on its purpose until I can figure out something better. In this case I simply named the new program Batch Renamer. Included in Visual Studio was a set of stock icons. For some reason I though the lightning bolt icon looked cool and used it for the program. The name Flash Renamer is simply derived from this randomly chosen icon! (This was before Macromedia/Adobe Flash become popular and pretty much hijacked Flash as a brand name. Luckily I have not had any visits from Adobe lawyers!)

Flash Renamer has a distinctive user interface layout that makes is stand out from most other batch renamers I’ve seen. I’ve always been satisfied with it, and based on user feedback it appears to be appreciated. What’s interesting is that this layout actually dates back to this very first version. Of course, it has been heavily updated and tweaked throughout the years, but the basic design clearly stems from the very beginning. Pure luck or a stroke of genius… who knows :-)

Flash Renamer Beta

One of the earliest surviving version of Flash Renamer. The layout is a mess, but the basic ideas are there.

First public version

In March of 1999 I made Flash Renamer 1.0 available as freeware on my personal homepage.

Flash Renamer 1.0

Flash Renamer 1.0. Now all the pieces had fallen into place. It’s a little base, but you can easily see the heritage when compared to the current version.

During the next couple of months I updated the program several times to make it look and handle better, and of course ironing out bugs. It’s hard to remember, but I probably submitted it to a few software sites during this time, such as TuCows and NoNags. I remember getting 5 stars out of 6 at NoNags. (Later on, version 3 got 6 stars!).

Flash Renamer 1.3

Flash Renamer 1.3 improved the look and feel.

The first versions could rename both files and folders, including subfolders, and came with three rename functions: Find and Replace, Convert Case and Set Attributes. These pretty much represents what I needed to do with my mp3:s, namely replace underscores with spaces, set the casing to title case and remove write protection :-)

The early versions

1½ year after the first version I released Flash Renamer 2.0. I added support for reading ID3 tags in mp3 files using code I wrote myself. In retrospect it does not sound impressive, but I was fairly proud of myself at that time, especially considering the limited documentation and that Visual Basic is not exactly known for its ease of binary processing. This was a time when mp3 was rising in popularity, but before it had become a household name. Adding mp3 support was probably a very good decision from a strategic point of view, although I mostly added it because I wanted it myself :-)

Flash Renamer 2.0

Flash Renamer 2.0. You could “design” your mp3 filenames using “tags” that were replaced with ID3 metadata during renaming. The concept of tags was later expanded and used throughout Flash Renamer.

Version two also included the Add & Crop function, a command line interface and a real user manual, to name a few.

Version 3.0 came out half a year later and was quite a big upgrade, adding several new rename functions, simple shell integration. Now the program also got its logo, courtesy of Ola Zandelin.

Flash Renamer Logo

The Flash Renamer logo

One cool feature was the file type detection, which scanned the files for magic numbers to try and figure out the correct file extension. This came about because people were uploading (usually not very legal) files to free web site hosts. In order to try and circumvent restrictions and risk of detection, they changed the file extensions from for example mp3 to a permitted file type such as gif.

Version 3.1 finally replaced the wording on the button that started the rename operation. Up until now it had said Execute, but I realized that this might not be that appropriate. I changed it to simply say Rename, which is also much more telling of the button’s action. Wording is hard. You want to convey the message in a way that is understandable to everyone without losing exactness. I think programmers tend to use too much technical terms, reducing the usability of the software. Other examples from Flash Renamer include Crop which became Remove, and Directory, which took me a lot of weighing back and forth before I finally decided to replace all occurrences of the word with Folder. And let’s not forget the function that can add a number sequence to filenames, which I first called Enum… Oh, and the Zero Padding functionality, which I first called Number Normalizing because it sounded cool, not realizing that it was actually a completely irrelevant mathematical term :-)

Flash Renamer 3.0

Flash Renamer 3.0. The tab control is getting tight with all the new functions.

Going shareware

At this time, me and some friends had started a company together, called RL Vision, and tried to market a paint application called ArtGem, inspired by the classic Amiga application Deluxe Paint (DPaint). In order to drive more traffic to our homepage I decided to move Flash Renamer from my private homepage to our company website. ArtGem eventually failed, and the company was disbanded, but I kept the RL Vision name and website as my personal software outlet.

I was fairly pleased with Flash Renamer at this point. It was a competent and stable application. It satisfied my own needs and I had gotten some praise from users. I had some ideas on how to make it better, but felt my time was limited and I was not sure I wanted to invest more in the program. At this time there had emerged a few other file rename software. At least one of them was shareware. Even though ArtGem had sold poorly, perhaps Flash Renamer would do better? I decided to give it a try and turned Flash Renamer from freeware to shareware. This was quite easy, considering that we already had the infrastructure for selling software in place after selling ArtGem.

This decision gave me the motivation to update Flash Renamer. Version 4, the first shareware version, was a big update. I replaced the rather odd file listing that used a treeview with a listview similar to Windows Explorer. This also worked as a file browser, making it fast and easy to navigate your folder structures. I also added support for viewing images as thumbnails and for reading ID3v2 metadata tags, both using third party library. Together with many other smaller features and enhancements, this version turned Flash Renamer into an application with a much more professional look and feel.

Flash Renamer 4.0

Flash Renamer 4.0 was a big update that really improved the interface. The name/logo in the top left was not the pretties though…

In version 3 I had a “simulation mode” feature that allowed you to run the rename operation without actually renaming the files. Doing so, you could inspect the log output to make sure that the new filenames were ok. With the new listview I took this to the next level, adding a preview column where you would instantly see the new filenames next to the original! This was quite tricky, since Visual Basic does not support threading. Generating filename previews take time, and would cause the UI to freeze meanwhile. I solved this by running the preview on a timer, only processing a few files at a time. Neat!

Most shareware have some limitation or annoyance to make you register. My solution was to open a window on top of the main window at startup. This window displayed a counter, counting up to the total number of files you had renamed using the program. You could not continue until the counter had finished. Thus, the more you used the program, to longer it took to start. I thought this was quite a fair way to annoy people into register, without having to limit any other functionality. (Later versions removed this counter though.)

Flash Renamer Shareware Reminder

The shareware “nag window”

I am a terrible businessman. From the very beginning, I promised free lifetime upgrades when buying Flash Renamer. Part of this was of course as an incentive to buy the product. But just as much was the simple fact that this is how I would like other software to work as well. I understand that “lifetime” is difficult for most businesses. But some software vendors seem to have made it into a business model in itself to constantly get people to pay for upgrades, and I really hate that.

Buying Flash Renamer also gives you free technical support by email. But the truth is that I help anyone who emails me just as much, no matter if they are registered or not :-). But this is probably a good strategy, because often people ask questions on how to use a program for their situation, before they actually buy a product.

After the failure with ArtGem, I did not expect Flash Renamer to sell much. But to my surprise, without any marketing, it started to sell a copy every now and then. The earnings are not anywhere near enough to make a living out of, but it is very satisfying knowing that you made a product on your own that people are prepared to pay for.

Useful functionality or bloatware?

I continued working on Flash Renamer, adding new functionality and tweaking the program to perfection. Some, like reading Exif tags, were a result of user requests and/or market study. But most new development stem from my own experience of using the program. I am a firm believer that dogfooding is the way to success.

One new function was batch replace. Find and replace had been a function since the first version, but realized that you often have a set of terms that you always want to replace to clean up your filenames. So the batch replace function is basically a list of find/replace terms. Inception?

Flash Renamer 5.0

Flash Renamer 5.0. This screenshot also shows the “Visual Assist” utility, that helps the user to pick places and select texts. This is one of my favorite enhancements, as it is a good example of how you can make an application easier to use.

In version 5 I added a shell extension, hooking into Window Explorer to allow the user to right click on files and send them directly to be renamed by preset in Flash Renamer. It was a very convenient integration IMO. But the development was oh so problem ridden. Let me just say that I don’t recommend anyone to build a shell extension menu unless you really know your way around the Windows API. And I especially don’t recommend anyone doing it in VB6 :-) It was so much trouble to build, debug and maintain. Then 64-bit Windows got its breakthrough, and 32-bit shell extensions because useless. At that point I decided to stop supporting it.

Flash Renamer Shell Extension

The Flash Renamer shell extension menu in Windows Explorer

Version 6 included ID3 tag writing functionality for mp3:s. This brings up the interesting topic of what you want your application to be? At its core, Flash Renamer modifies filenames. How far from this core can (or should) you stray without losing your identity and risk becoming bloatware? Flash Renamer has the ability to change file’s dates and attributes too. I would argue that this is fairly close to the core, as the filename, date and attributes are all properties of a file. But writing ID3 tags? That’s pretty far from the core. But you could argue that you are modifying the content inside the ID3 tags to suit your needs, which is pretty close to the idea of modifying filenames to suit your needs. But that’s a slippery slope, as using this argument we can easily motivate lots of other operations. For example, why not let Flash Renamer open and modify the content of text files too then? In fact, I had this idea, but decided to fork Flash Renamer into another product called Replace Genius. That was probably a wise choice. Back to the ID3 writer. The biggest motivation for this was the fact that much of user base (and I :-)) use Flash Renamer to manage mp3:s, and ID3 is an essential part of this. Thus I believe that writing ID3 tags is an added value to Flash Renamer.

Flash Renamer 6.0

Flash Renamer 6.0. Instead of adding a third row to the tab control, I sorted the functions into categories (the round buttons) that changed the tabs.

Scripting is another interesting feature. It allows anyone to make their own custom rename functionality to suite to their specific needs, through a simple VBScript interface. There is something really attractive about opening up your software to users and other developers. But is it worth the work? When choosing what functionality to implement in a piece of software you always have to weigh it against how useful it will be for the end users. If only a few people can be expected to find it useful, then it is hard to motivate implementing it. Scripting in a sense is a way to enable the program to implement such odd functionality without cluttering the interface too much with features that no one uses. But you can also argue that scripting in itself is such an odd functionality. Even though I believe that the user base of Flash Renamer in general is quite computer savvy, I don’t think that that many have the skills or motivation to actually construct their own scripts. Probably only a few people have ever used the scripting functionality. But I still like it for some reason :-) And it has also allowed me to implement some odd functionality that I thought was neat, but not neat enough to motivate a new function.

The future of Flash Renamer

At this point I am pretty much done with Flash Renamer. There are many things I can think of that would be cool to add to the program. But all essential features are there. Sometimes you have to say stop. Letting go of Flash Renamer means more time to other projects (and life).

Continuing developing a VB6 application today does not feel meaningful. It is tempting to re-write Flash Renamer in another language. Like all programmers I think that I could make it so much better in all kinds of ways. But the motivation is not there. Why re-do something that works and fulfills all my personal needs? Perhaps if there had been a “real” market for such an application, but there isn’t.

I will however continue to support the program for as long as it sells, and then some more. I hate software vendors who drop products and bail out of responsibility. I am working on open sourcing all my freeware products, and when the time comes I will also make sure that Flash Renamer is open sourced.

User feedback

One of the most grateful things with making software is the user feedback you get. Of course, much of this feedback is bug reports and feature requests, but I also get letter from people thanking me for my work. I’m always happy to hear from my users, and I make sure to answer all letters, even if only with a simple “thank you”. I remember one of the earliest mails I got when the first versions of Flash Renamer had just been released. This guy from Japan wrote to me to report a bug. Imagine that. Half way across the world someone completely unknown to me had found my personal website, downloaded my program and was using it. That felt pretty cool at the time, for many reasons.

Most new functionality in the later version of Flash Renamer has been a combination of user feedback and my own ideas and needs. Flash Renamer gets a lot of feature requests from user, which I guess is expected since all users have their own specific needs. While I appreciate the feedback, I can rarely implement such requests. I have to distill the feature into a more general functionality that can appeal a broader user base. It saddens me to usually write back thanking users for their suggestions, but having to tell them that I most likely will not be able to implement it.

Visual Basic is dead, long live Visual Basic

When I started programming Flash Renamer, Microsoft Visual Basic 6 was the coolest thing in town, at least if you could stand being ridiculed by “real” programmers who used C/C++. Not so anymore. Microsoft decided that .Net was the future, and simply stopped development of classic Visual Basic in order to get people to switch. Sure, there was VB.Net, but that was a completely new thing, basically only sharing some language syntax with its predecessor. Upgrading was not a simple task, if even impossible, and most people seemed to do like me and ignore the “VB7”.

Working with an obsolete technology has been difficult at times. The most obvious problem was the new “XP themes” introduces in Windows XP. Getting your program to fully use these was problematic, and required many tricks and hacks. I must however hand it to Microsoft that they are good at backwards compatibility. Even though Flash Renamer is built with ancient technology, it still works, even in Windows 10! Old software tends to “just work” in Windows. This is so much better IMO than companies like Apple and Google, who seems to have no problems throwing out “old” technology. Developers need to constantly keep up with changes or go out of business. It’s no wonder that Microsoft is the choice of large corporations, and not Apple.

Choosing a programming language

VB6 was my main programming language for a long time. But eventually I had to face the fact that I needed to find something else. It took me a long time before doing so though. Given how bad VB6 developers had been treated, I did not want to end up like that again. I wanted a high level language with a readable and productive syntax, sustainable in the long term with a large community, preferably cross platform, producing native applications and with a good IDE for easy GUI development. I finally settled on C#. It did not fulfill all my wishes, but it was the best I could find. I’m not sure I would have chosen differently today, but there sure are many more choices out there today, especially with all the new web based technology. In the end it is nearly impossible to choose the “best” language for the future. In the world of development, change is rapid. Even if you have a favorite programming language, developers more and more need to be open to new technologies in order to survive on the market.

The world of open source, available libraries and development communities has exploded compared to when I began working on Flash Renamer. Today you often put together new software or services by piecing together various components. You don’t have to write as much code yourself anymore. And if you do, the amount of documentation and help available online is astounding. If I had to redo Flash Renamer today, that would probably be the biggest change. Less code, more puzzling to make things work together.

On being a software developer

I hardly earn any money to speak of from my software. In the end, the number of hours spent on developing Flash Renamer far exceeds what I’ve earned. It’s very hard to earn a living on shareware unless you have a major hit. For me, development is a passion. I don’t do it to earn money, but because I really enjoy coding and sharing my work. So much that I spend my free time after work doing it. I think this is a quite common theme amongst developers. Just think of all the open source and freeware products available today and how much this has contributed to the computer landscape ever since it begun.

I am a reasonably good programmer today. I am not one of those with an innate talent for programming though; my talent is more based on experience. Developing applications at home has definitely helped me with that experience, and thus helped my professional career. On that note, when seeking a job, having something of your own to show is worth a lot!

Developing a product on your own requires more than just programming (although it usually starts out with just programming). You need to design graphical user interfaces and consider usability issues, think about software architecture, create web sites, writing copy and manuals, handle business tasks, give user support and so on. Such a wide range of tasks is something you would never get to do in a larger organization. It is much work, but can also be rewarding and develop you as a person.

The downside of being a one (wo)man team is of course that you can’t be an expert on everything, and there is no one there to give immediate feedback or help when you need it. You have to make all the design choices yourself, and that is very hard. Designing and tweaking the user interface, deciding on how to implement a feature, making architectural choices, taking into account all possible user needs and circumstances. These kinds of decisions are what tends to take the most time for me when developing something. Programming is actually pretty fast and easy if you know what exactly what you need to do.

Closing thought

Sometimes I wonder how many files “my little utility” has renamed. I’m sure it’s tens of millions, perhaps even billions? But more importantly, how many hours of manual labor has it saved people all over the world!? Thinking of it like that makes it clear even if it is just a fairly unknown program with a small user base, the amount of saved work is not *that* insignificant. It feels good knowing I made the world at least a little bit better :-)

Flash Renamer About Window

My current copy of Flash Renamer has performed roughly 80,000 renames since I installed it about 3 years ago.

Posted in Articles | Tagged , , , , , , , , , , , , , , , , | 1 Comment