Completed
Pull Request — master (#168)
by Franco
02:30
created

code/model/DMSDocument_Controller.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
class DMSDocument_Controller extends Controller
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
4
{
5
    /**
6
     * Mode to switch for testing. Does not return document download, just document URL.
7
     *
8
     * @var boolean
9
     */
10
    public static $testMode = false;
11
12
    private static $allowed_actions = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
The property $allowed_actions is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
13
        'index'
14
    );
15
16
    public function init()
17
    {
18
        Versioned::choose_site_stage();
19
        parent::init();
20
    }
21
22
    /**
23
     * Returns the document object from the request object's ID parameter.
24
     * Returns null, if no document found
25
     *
26
     * @param  SS_HTTPRequest $request
27
     * @return DMSDocument|null
28
     */
29
    protected function getDocumentFromID($request)
30
    {
31
        $doc = null;
0 ignored issues
show
$doc is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
32
33
        $id = Convert::raw2sql($request->param('ID'));
34
        if (strpos($id, 'version') === 0) {
35
            // Versioned document
36
            $id = $this->getDocumentIdFromSlug(str_replace('version', '', $id));
37
            $doc = DataObject::get_by_id('DMSDocument_versions', $id);
38
            $this->extend('updateVersionFromID', $doc, $request);
39
        } else {
40
            // Normal document
41
            $doc = DataObject::get_by_id('DMSDocument', $this->getDocumentIdFromSlug($id));
0 ignored issues
show
It seems like $id defined by \Convert::raw2sql($request->param('ID')) on line 33 can also be of type array; however, DMSDocument_Controller::getDocumentIdFromSlug() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
42
            $this->extend('updateDocumentFromID', $doc, $request);
43
        }
44
45
        return $doc;
46
    }
47
48
    /**
49
     * Get a document's ID from a "friendly" URL slug containing a numeric ID and slugged title
50
     *
51
     * @param  string $slug
52
     * @return int
53
     * @throws InvalidArgumentException if an invalid format is provided
54
     */
55
    protected function getDocumentIdFromSlug($slug)
56
    {
57
        $parts = (array) sscanf($slug, '%d-%s');
58
        $id = array_shift($parts);
59
        if (is_numeric($id)) {
60
            return (int) $id;
61
        }
62
        throw new InvalidArgumentException($slug . ' is not a valid DMSDocument URL');
63
    }
64
65
    /**
66
     * Access the file download without redirecting user, so we can block direct
67
     * access to documents.
68
     */
69
    public function index(SS_HTTPRequest $request)
70
    {
71
        $doc = $this->getDocumentFromID($request);
72
73
        if (!empty($doc)) {
74
            $canView = $doc->canView();
75
76
            if ($canView) {
77
                $path = $doc->getFullPath();
78
                if (is_file($path)) {
79
                    $fileBin = trim(`whereis file`);
80
                    if (function_exists('finfo_file')) {
81
                        // discover the mime type properly
82
                        $finfo = finfo_open(FILEINFO_MIME_TYPE);
83
                        $mime = finfo_file($finfo, $path);
84
                    } elseif (is_executable($fileBin)) {
85
                        // try to use the system tool
86
                        $mime = `$fileBin -i -b $path`;
87
                        $mime = explode(';', $mime);
88
                        $mime = trim($mime[0]);
89
                    } else {
90
                        // make do with what we have
91
                        $ext = $doc->getExtension();
92
                        if ($ext =='pdf') {
93
                            $mime = 'application/pdf';
94
                        } elseif ($ext == 'html' || $ext =='htm') {
95
                            $mime = 'text/html';
96
                        } else {
97
                            $mime = 'application/octet-stream';
98
                        }
99
                    }
100
101
                    if (self::$testMode) {
102
                        return $path;
103
                    }
104
105
                    // set fallback if no config nor file-specific value
106
                    $disposition = 'attachment';
107
108
                    // file-specific setting
109
                    if ($doc->DownloadBehavior == 'open') {
110
                        $disposition = 'inline';
111
                    }
112
113
                    //if a DMSDocument can be downloaded and all the permissions/privileges has passed,
114
                    //its ViewCount should be increased by 1 just before the browser sending the file to front.
115
                    $doc->trackView();
116
117
                    return $this->sendFile($path, $mime, $doc->getFilenameWithoutID(), $disposition);
118
                }
119
            }
120
        }
121
122
        if (self::$testMode) {
123
            return 'This asset does not exist.';
124
        }
125
        $this->httpError(404, 'This asset does not exist.');
126
    }
127
128
    /**
129
     * @param string $path File path
130
     * @param string $mime File mime type
131
     * @param string $name File name
132
     * @param string $disposition Content dispositon
133
     */
134
    protected function sendFile($path, $mime, $name, $disposition)
135
    {
136
        header('Content-Type: ' . $mime);
137
        header('Content-Length: ' . filesize($path), null);
138
        if (!empty($mime) && $mime != "text/html") {
139
            header('Content-Disposition: '.$disposition.'; filename="'.addslashes($name).'"');
140
        }
141
        header('Content-transfer-encoding: 8bit');
142
        header('Expires: 0');
143
        header('Pragma: cache');
144
        header('Cache-Control: private');
145
        flush();
146
        readfile($path);
147
        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method sendFile() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
148
    }
149
}
150