How can I programmatically register a file with Redmine?

1

I'd like to add a file to a Redmine server without going through the graphical interface. I'm making the files available to the Redmine server by a separate svn checkout process, so I just need to be able to add the files to Redmine's own database.

Ideally, I'd like a solution that could be run like this:

./redmine-register-file /path/to/my/file.ext "with optional description"

I believe the relevant part of the interface is found in redmine/apps/views/files/new.html.erb. It's accessed by index.html.erb, which has a small portion I believe is relevant:

<div class="contextual">
<%= link_to(l(:label_attachment_new), new_project_file_path(@project), :class => 'icon icon-add') if User.current.allowed_to?(:manage_files, @project) %>
</div>

Here is the complete contents of new.html.erb:

<h2><%=l(:label_attachment_new)%></h2>

<%= error_messages_for 'attachment' %>
<%= form_tag(project_files_path(@project), :multipart => true, :class => "tabular") do %>
<div class="box">

<% if @versions.any? %>
<p><label for="version_id"><%=l(:field_version)%></label>
<%= select_tag "version_id", content_tag('option', '') +
                             options_from_collection_for_select(@versions, "id", "name") %></p>
<% end %>

<p><label><%=l(:label_attachment_plural)%></label><%= render :partial => 'attachments/form' %></p>
</div>
<%= submit_tag l(:button_add) %>
<% end %>

I don't know Ruby that well at all (anything beyond print name.reverse is beyond me), but I know that all of those colons indicate selectors. What information can I glean from the standard interface that will help me in my task, and what might a complete solution look like?


Closer to a solution:

Redmine uses a MySQL database to store its file registrations. The database is called redmine_production, and uses the following schema:

mysql> SHOW COLUMNS FROM redmine_production.attachments;
+----------------+--------------+------+-----+---------+----------------+
| Field          | Type         | Null | Key | Default | Extra          |
+----------------+--------------+------+-----+---------+----------------+
| id             | int(11)      | NO   | PRI | NULL    | auto_increment |
| container_id   | int(11)      | YES  | MUL | NULL    |                |
| container_type | varchar(30)  | YES  |     | NULL    |                |
| filename       | varchar(255) | NO   |     |         |                |
| disk_filename  | varchar(255) | NO   |     |         |                |
| filesize       | int(11)      | NO   |     | 0       |                |
| content_type   | varchar(255) | YES  |     |         |                |
| digest         | varchar(40)  | NO   |     |         |                |
| downloads      | int(11)      | NO   |     | 0       |                |
| author_id      | int(11)      | NO   | MUL | 0       |                |
| created_on     | datetime     | YES  | MUL | NULL    |                |
| description    | varchar(255) | YES  |     | NULL    |                |
+----------------+--------------+------+-----+---------+----------------+
12 rows in set (0.00 sec)

Some more Ruby source

Perhaps this will be of use: attachment.rb

Sean Allred

Posted 2013-08-21T14:35:36.187

Reputation: 910

Answers

1

For those who come by this by the Redmine thread I created: I tried to post the solution there as well, but the SPAM filter would not let me follow-up on the thread. It is probably because there was no intermediate response (*tear*).


It took a bit of time, but I figured it out. (I made a test instance of Redmine so as to not mess anything up with our production instance, so these values are default and should work for anybody trying to do this.)


A short synopsis of the process:

  1. Find the internal ID of the project you wish to add the files to.
  2. Determine file statistics.
  3. Insert file into database.

There is in fact no database validation going on, so even (2) is optional. As such, I won't go over how to get that information. (There are ample resources even on this site as to how to get this information.) The rest, however, requires a tiny bit of knowledge of how Redmine has its databases set up.


To start this, connect to your production MySQL database. (This is usually, if not always, redmine_production. You can list all MySQL databases with the command SHOW DATABASES;.)

Now, find the ID of your project you wish to add the file to. In the columns listing above, this will be inserted as container_id.

mysql> SELECT * FROM projects;
+----+----------------+-------------+----------+-----------+-----------+---------------------+---------------------+----------------+--------+------+------+
| id | name           | description | homepage | is_public | parent_id | created_on          | updated_on          | identifier     | status | lft  | rgt  |
+----+----------------+-------------+----------+-----------+-----------+---------------------+---------------------+----------------+--------+------+------+
|  1 | git-helloworld | NULL        |          |         1 |      NULL | 2012-01-01 13:00:00 | 2012-01-01 13:00:00 | git-helloworld |      1 |    1 |    2 |
|  2 | bzr-helloworld | NULL        |          |         1 |      NULL | 2012-01-01 13:00:00 | 2012-01-01 13:00:00 | bzr-helloworld |      1 |    1 |    2 |
|  3 | hg-helloworld  | NULL        |          |         1 |      NULL | 2012-01-01 13:00:00 | 2012-01-01 13:00:00 | hg-helloworld  |      1 |    1 |    2 |
|  4 | svn-helloworld | NULL        |          |         1 |      NULL | 2012-01-01 13:00:00 | 2012-01-01 13:00:00 | svn-helloworld |      1 |    1 |    2 |
+----+----------------+-------------+----------+-----------+-----------+---------------------+---------------------+----------------+--------+------+------+
4 rows in set (0.00 sec)

In this example, we want to add the files to git-helloworld, so our ID is 1. So, to add a file to the database, we will execute the SQL command:

INSERT INTO attachments (container_id, container_type,
                         filename, disk_filename, digest) VALUES (
       1, 'Project',
       'Some File Name', 'file-name-on-disk', '0123456789abcdef');

A few notes about these fields:

  • container_id: The ID of the project you wish to include the file in
  • container_type: The type of container this is; for this purpose, it is always Project
  • filename: The string to display as the file name
  • disk_filename: The actual path of the file, relative to /var/www/redmine/files/
  • digest: A string representing the MD5 checksum of the file.

Some not required, but recommended fields:

  • filesize: The size of the file as an integer. I assume this is meant to be in bytes or kilobytes, but I don't know if it matters.
  • author_id: A user to associate the file with. For my purposes, I'm going to use admin. Note that you can get a full list of Redmine users with SELECT * FROM users;.
  • description: A file description. (This is the same optional description used in the interface.)
  • content_type: I would assume this is a MIME content type.
  • created_on: The date this file was created on.

For the explicit types of all of these, refer to the columns listing in the original post.

Next, ensure that the path in disk_filename actually exists relative to your redmine/files/ directory. (Note this means that you can actually organize it!) You don't have to do this, but you will obviously get a 404 if the file isn't there.

After that, you should be good to go!

proof

Sean Allred

Posted 2013-08-21T14:35:36.187

Reputation: 910