Folder.cpp 8.63 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

23
#include "Folder.h"
24
#include "Device.h"
25
#include "Media.h"
26

27
#include "database/SqliteTools.h"
28
#include "filesystem/IDirectory.h"
29
30
31
32
#include "filesystem/IDevice.h"
#include "utils/Filename.h"

#include <unordered_map>
33
34
35
36

namespace policy
{
    const std::string FolderTable::Name = "Folder";
37
    const std::string FolderTable::PrimaryKeyColumn = "id_folder";
38
39
40
    unsigned int Folder::* const FolderTable::PrimaryKey = &Folder::m_id;
}

41
42
43
std::shared_ptr<factory::IFileSystem> Folder::FsFactory;

Folder::Folder( DBConnection dbConnection, sqlite::Row& row )
44
45
    : m_dbConection( dbConnection )
{
46
47
48
49
    row >> m_id
        >> m_path
        >> m_parent
        >> m_lastModificationDate
50
        >> m_isBlacklisted
51
52
        >> m_deviceId
        >> m_isPresent;
53
54
}

55
Folder::Folder( const std::string& path, time_t lastModificationDate, unsigned int parent, unsigned int deviceId )
56
57
    : m_id( 0 )
    , m_path( path )
58
    , m_parent( parent )
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
59
    , m_lastModificationDate( lastModificationDate )
60
    , m_isBlacklisted( false )
61
    , m_deviceId( deviceId )
62
    , m_isPresent( true )
63
{
64
    // Don't fetch the device mountpoint from here, we don't have a DBConnection yet.
65
66
67
68
}

bool Folder::createTable(DBConnection connection)
{
69
70
71
    std::string req = "CREATE TABLE IF NOT EXISTS " + policy::FolderTable::Name +
            "("
            "id_folder INTEGER PRIMARY KEY AUTOINCREMENT,"
72
            "path TEXT,"
73
            "id_parent UNSIGNED INTEGER,"
74
            "last_modification_date UNSIGNED INTEGER,"
75
            "is_blacklisted INTEGER,"
76
            "device_id UNSIGNED INTEGER,"
77
            "is_present BOOLEAN NOT NULL DEFAULT 1,"
78
            "FOREIGN KEY (id_parent) REFERENCES " + policy::FolderTable::Name +
79
80
81
            "(id_folder) ON DELETE CASCADE,"
            "FOREIGN KEY (device_id) REFERENCES " + policy::DeviceTable::Name +
            "(id_device) ON DELETE CASCADE"
82
            ")";
83
84
85
86
87
88
89
    std::string triggerReq = "CREATE TRIGGER IF NOT EXISTS is_device_present AFTER UPDATE OF is_present ON "
            + policy::DeviceTable::Name +
            " BEGIN"
            " UPDATE " + policy::FolderTable::Name + " SET is_present = new.is_present WHERE device_id = new.id_device;"
            " END";
    return sqlite::Tools::executeRequest( connection, req ) &&
            sqlite::Tools::executeRequest( connection, triggerReq );
90
91
}

92
std::shared_ptr<Folder> Folder::create( DBConnection connection, const std::string& fullPath, time_t lastModificationDate, unsigned int parentId, Device& device, fs::IDevice& deviceFs )
93
{
94
    auto path = utils::file::removePath( fullPath, deviceFs.mountpoint() );
95
    auto self = std::make_shared<Folder>( path, lastModificationDate, parentId, device.id() );
96
    static const std::string req = "INSERT INTO " + policy::FolderTable::Name +
97
            "(path, id_parent, last_modification_date, device_id) VALUES(?, ?, ?, ?)";
98
    if ( insert( connection, self, req, path, sqlite::ForeignKey( parentId ),
99
                         lastModificationDate, device.id() ) == false )
100
        return nullptr;
101
    self->m_dbConection = connection;
102
103
    self->m_deviceMountpoint = deviceFs.mountpoint();
    self->computeFullPath();
104
105
106
    return self;
}

107
bool Folder::blacklist( DBConnection connection, const std::string& fullPath )
108
{
109
    auto folderFs = FsFactory->createDirectory( fullPath );
110
111
    if ( folderFs == nullptr )
        return false;
112
113
114
115
116
    auto deviceFs = folderFs->device();
    auto device = Device::fromUuid( connection, deviceFs->uuid() );
    if ( device == nullptr )
        device = Device::create( connection, deviceFs->uuid(), deviceFs->isRemovable() );
    auto path = utils::file::removePath( fullPath, deviceFs->mountpoint() );
117
    static const std::string req = "INSERT INTO " + policy::FolderTable::Name +
118
119
            "(path, id_parent, is_blacklisted, device_id) VALUES(?, ?, ?, ?)";
    return sqlite::Tools::insert( connection, req, path, nullptr, true, device->id() ) != 0;
120
121
}

122
123
124
125
126
void Folder::setFileSystemFactory( std::shared_ptr<factory::IFileSystem> fsFactory )
{
    FsFactory = fsFactory;
}

127
std::shared_ptr<Folder> Folder::fromPath( DBConnection conn, const std::string& fullPath )
128
{
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
    auto folderFs = FsFactory->createDirectory( fullPath );
    if ( folderFs == nullptr )
        return nullptr;
    auto deviceFs = folderFs->device();
    if ( deviceFs == nullptr )
    {
        LOG_ERROR( "Failed to get device containing an existing folder: ", fullPath );
        return nullptr;
    }
    auto device = Device::fromUuid( conn, deviceFs->uuid() );
    // We are trying to find a folder. If we don't know the device it's on, we don't know the folder.
    if ( device == nullptr )
        return nullptr;
    auto path = utils::file::removePath( fullPath, deviceFs->mountpoint() );
    const std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE path = ? AND device_id = ? "
            "AND is_blacklisted IS NULL";
    auto folder = fetch( conn, req, path, device->id() );
    if ( folder == nullptr )
        return nullptr;
    folder->m_deviceMountpoint = deviceFs->mountpoint();
    folder->computeFullPath();
    return folder;
151
152
}

153
154
155
156
157
unsigned int Folder::id() const
{
    return m_id;
}

158
const std::string& Folder::path() const
159
{
160
    return m_fullPath;
161
162
}

163
std::vector<MediaPtr> Folder::files()
164
{
165
    static const std::string req = "SELECT * FROM " + policy::MediaTable::Name +
166
        " WHERE folder_id = ?";
167
    return Media::fetchAll<IMedia>( m_dbConection, req, m_id );
168
}
169

170
std::vector<std::shared_ptr<Folder>> Folder::folders()
171
{
172
    return fetchAll( m_dbConection, m_id );
173
174
}

175
std::shared_ptr<Folder> Folder::parent()
176
{
177
    return fetch( m_dbConection, m_parent );
178
}
179
180
181
182
183

unsigned int Folder::lastModificationDate()
{
    return m_lastModificationDate;
}
184
185
186
187
188
189
190
191
192
193

bool Folder::setLastModificationDate( unsigned int lastModificationDate )
{
    static const std::string req = "UPDATE " + policy::FolderTable::Name +
            " SET last_modification_date = ? WHERE id_folder = ?";
    if ( sqlite::Tools::executeUpdate( m_dbConection, req, lastModificationDate, m_id ) == false )
        return false;
    m_lastModificationDate = lastModificationDate;
    return true;
}
194

195
unsigned int Folder::deviceId() const
196
{
197
    return m_deviceId;
198
}
199
200
201
202
203

bool Folder::isPresent() const
{
    return m_isPresent;
}
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243

void Folder::computeFullPath()
{
    if ( ( *m_deviceMountpoint.crbegin() ) != '/' )
        m_deviceMountpoint += '/';
    m_fullPath = m_deviceMountpoint + m_path;
}

std::vector<std::shared_ptr<Folder>> Folder::fetchAll( DBConnection dbConn, unsigned int parentFolderId )
{
    std::vector<std::shared_ptr<Folder>> res;
    if ( parentFolderId == 0 )
    {
        static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
                + " WHERE id_parent IS NULL AND is_blacklisted is NULL";
        res = DatabaseHelpers::fetchAll<Folder>( dbConn, req );
    }
    else
    {
        static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
                + " WHERE id_parent = ? AND is_blacklisted is NULL";
        res = DatabaseHelpers::fetchAll<Folder>( dbConn, req, parentFolderId );
    }
    std::unordered_map<unsigned int, std::string> deviceMountpoints;
    for ( auto& f : res )
    {
        auto it = deviceMountpoints.find( f->deviceId() );
        if ( it == end( deviceMountpoints ) )
        {
            auto device = Device::fetch( dbConn, f->deviceId() );
            auto deviceFs = FsFactory->createDevice( device->uuid() );
            deviceMountpoints[f->deviceId()] = deviceFs->mountpoint();
            f->m_deviceMountpoint = deviceFs->mountpoint();
        }
        else
            f->m_deviceMountpoint = it->second;
        f->computeFullPath();
    }
    return res;
}